Android播放器-解封装

  • 解封装
  • 软硬解码
  • 像素格式转换
  • 重采样
  • pts/dts
  • 同步策略

解封装

av_register_all()  // 注册所有的格式(解封装和加封装格式)
avformat_network_init() // 想支持rtsp等网络协议,初始化网络模块

avformat_open_input(...) // 打开文件并解析
avformat_find_stream_info(...) // 查找文件格式和索引

av_find_best_stream(...) // 查找对应的流

AVFormatContext 解加封装上下文,有AVStream数组
AVStream 音视频流信息
AVPacket 解封装完的数据包(视频的话,h264的间隔符00 00 01去掉了)

av_read_frame(...)

int avformat_open_input函数

确保av_register_all avformat_network_init已调用
AVFormatContext **ps 可以传入指向空指针的指针,也可以传入指向自己申请空间地址的指针
const char *url 可以是本地的文件,也可以是网络地址(http,rtsp)
AVInputFormat *fmt 一般不用,直接传null,指定封装格式(如果是频繁打开,最好指定)
AVDictionary **options 一般可以给null,可参考libavformat\options_table.h

AVFormatContext

AVIOContext *pb;  // io context 如果要自定义一些读写格式就要用到这个
char filename[1024]; // 文件名,断开重连需要用到

unsigned int nb_streams; // 数组大小
AVStream **streams; // 一般0是视频,1是音频,但不能保证

int64_t duration; // 以AV_TIME_BASE为单位,获取的总长度,不一定能获取到
int64_t bit_rate; // 比特率

void avformat_close_input(AVFormatContext **s);

关闭完之后,内部帮我们已经置零了

avformat_find_stream_info

获取视频流的信息
在一些文件中,可能没有文件信息(如flv格式、h264格式的),这时我们前面调用的avformat_open_input打开文件后,就没有参数,获取不到文件信息
我们就可以用avformat_find_stream_info,来探测一下流信息,

815,281 261,869.51 554,018.67

AVStream

AVCodecContext *codec; // 过时了
AVRational time_base; // 时间基数 一般为一百万 其实就是分数,这是了减少精度的损失,表示一秒钟有多少个,就是多少分之一秒
int64_t duration; // 转换成毫秒:
// duration*((double)time_base.num/(double)time_base.den)*1000
AVRational avg_frame_rate: // 帧率
AVCodecParameter *codecpar; // 音视频参数

enum AVMediaType codec_type; // 编码类型,表示是音频还是视频
enum AVCodecID codec_id; // 编码格式
unit32 codec_tag; // 用四个字节表示各种编码器
int format; // 指的是像素格式或音频的采样格式
int width;
int height;
uint64_t channel_layout; // 2.1声道,还是3声道
int channels; // 声道数
int sample_rate;
int frame_size; // 一帧音频的大小

写一段代码就测试一下

av_find_best_stream

获取音视频的索引
int av_find_best_stream(
AVFormatContext *ic,
enum AVMediaType type,
int wanted_stream_nb, // 默认设置-1
int related_stream, // 相关的流信息,一个文件里可能有多个节目
AVCodec **decoder_ret, // 解码器
int flags // 暂未使用的参数
)

av_read_frame

参数:
AVFormatContext *s
AVPacket *pkt // 不能传NULL,输出参数,内部会分配帧空间
返回值:
return 0 if OK
< 0 on error or end of file

AVPacket

AVBufferRef *buf; // 可能多个AVPacket共用 buf
int64_t pts; // pts * (num/den)
int64_t dts; // 根据dts顺序发给解码器
uint8_t *data;
int size;

相关函数:
AVPacket *av_packet_alloc(void) // 创建并初始化
AVPacket *av_packet_clone(const AVPacket *src) // 创建并引用计数
int av_packet_ref(AVPacket *dst, const AVPacket *src); // 增加引用
av_packet_unref(AVPacket *pkt) // 减引用
void av_packet_free(AVPacket **pkt) // 清空对象并减引用计数
void av_init_packet(AVPacket *pkt) // 设置默认值
int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size) // 自己构造AVPacket包
int av_copy_packet(AVPacket *dst, const AVPacket *src); // 不建议使用

av_seek_frame

int av_seek_frame(
AVFormatContext *s,
int stream_index, // 流索引 默认-1,即视频
int64_t timestamp, // 和pts一致,AVStream.time_base
int flags
)
stream_index:
音频不存在IBP帧,而视频有IBP帧,所以最好用视频移动到关键帧位置

flags:
#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward 往后找,就找过去的时间点
#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes 根据文件大小位置来跳,这时时间戳就是文件位置
#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes 针对frame来说,就跳到这个frame,可能会花屏
#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number 找关键帧
打赏