对于FFmpeg的使用中,理解各个结构体的作用至关重要。首先在使用之前,avdevice_register_all()和 avformat_network_init()来分别对设备和网络进行初始化
解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)
- AVIOContext是FFmpeg中用于执行输入输出操作的抽象层,提供协议层之上的抽象接口,它负责处理数据流的读写和定位,而不需要知道底层数据的来源,因此可以处理本地,网络,内存缓冲区多个流来源。这个结构体包含了读写缓冲区的指针,缓冲区的大小,当前读写位置,和读写位置的指针(这些具体的指针指向集体的协议实现)等
- 在古早版本的FFmpeg中UP用于定义和注册不同的底层协议(如file\http\rtp\tcp\udp等),每个这样的结构体中都包含了针对该协议的一组回调函数(如url_open\url_read\url_read\url_write\url_seek\url_close等)。但是在现代的FFmpeg中已经更多的整合和抽象进入AC中了。
- 同样的在古早版本中,UC是UP的一个具体实例,用于存贮某个已经打开协议连接的上下文信息。但是在新版的FFmpeg中这个结构也已经被整合到了AC中
解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。
- avformat_alloc_contex()申请一个AVFormatContext内存
- avformat_free_contex 释放该结构里的所用东西以及该结构本身
- avformat_close_input() 关闭解复用器,关闭后就不需要使用avfoemat_free_contex来进行释放,常用
- avformat_open_input 打开输入视频文件
- avformat_find_stream_info 获取音视频文件信息
- afvv_read_frame 读取音视频包
- avformat_seek_file 定位文件,拖拉进度条,根据pts
- av_seek_frame 定位文件,根据比例
- 在阅读雷博士写的播放器的时候能够轻易地发现这个结构体是一个很重要的结构体,它不仅是用于解封装到解码中间,在其他地方也是可以看到的。但是在这个结构体中实际上并没有什么很复杂的信息。只有类似音视频流的个数,音视频流,时长,比特率之类的东西。以及显示音视频流(文件)的源信息
解码(h264,mpeg2,aac,mp3)
- avcodec_alloc_contex3() 分配解码器上下文
- avcodec_find_decoder() 根据ID查找解码器
- avcodec_find_decoder_by_name() 根据名字查找解码器。不同厂家所制造的解码器ID相同,但是名字可以不同
- avcodec_open2 打开编解码器
- avcodecd_send_packet() 发送编码数据包
- avcodec_recieve_frame() 接收解码器数据
- avcodec_free_contex 释放解码器上下文包含
- avcodec_close 关闭解码器
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。
AVStream
- 这个结构体可以理解成为媒体文件中的轨道信息。
AVCodecContex
- 这个结构体在编码和解码中都有应用,用于存储编解码器的上下文实例
- 解码过程,用于存储和接收编码器配置,打开一个媒体文件时候FFmpeg会从文件头部解析出视频流的编码参数,包括像视频宽度高度,像素格式,时间基准,音视频采样率等。另外配置编码器选项,跳帧策略,线程数,错误隐藏策略,是否允许多线程解码。传递压缩数据和接收原始数据: 会把压缩后的 AVPacket (例如一个H.264 NALU)传递给 AVCodecContext 对应的解码器(通过 avcodec_send_packet)。解码器处理后,会从 AVCodecContext 对应的解码器中读取解码后的 AVFrame (例如YUV像素数据或PCM采样数据,通过 avcodec_receive_frame)。
- 编码过程。首先还是设置编码参数,包括输入原始视频的尺寸和格式,编码器的时间基准,B帧最大数量等。配置编码选项,例如一些预设和调优,编码速度,编码模式等。传递原始数据和接收压缩数据: 你会把原始的 AVFrame (例如YUV像素数据或PCM采样数据)传递给 AVCodecContext 对应的编码器(通过 avcodec_send_frame)。编码器处理后,你会从 AVCodecContext 对应的编码器中读取压缩后的 AVPacket (例如H.264 NALU或AAC帧,通过 avcodec _receive_packet)。
- 这个接口一般是应用程序和底层编解码算法之间的桥梁,FFmpeg由此提供了统一的接口来配置不同的编解码器。有关于其他参数的解析还是看一下雷博士的博客。
AVCodec
- AVCodec代表了一个具体的已经注册的编码器算法,它是一个只读的结构体,包含着这个编码器本身的静态通用信息,和能力。以及只想内部实现函数的指针。一般通过avcodec_find_decoder或者avcodec_find_encoder来获取这样一个指针。可以理解这是一个解码算法所提供的指针。
- 要注意AC的存储是以一个全局的注册列表形式(可能是一个链表或是其他的数据结构)实现的。由于FFmpeg时支持多种音视频编解码的这些编解码器在编译时可以被静态链接,作为共享库进行动态加载,允许 FFmpeg 在运行时动态地查找和选择合适的编解码器。
- AVCodec 定义了编解码器的抽象接口(例如 decode, encode, init, close 等函数指针)。具体的编解码器实现(例如 libx264、libvpx、libfdk_aac 等第三方库或 FFmpeg 内部实现)则通过 AVCodec 结构体将自己注册到 FFmpeg 中。这种设计使得 FFmpeg 核心库无需知道所有编解码器的具体实现细节,只需通过统一的 AVCodec 接口来调用它们。
存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧
解码前数据:AVPacket
- 这个结构体比较简单,都是一些时间戳,大小之类的数据,
解码后数据:AVFrame
- AF中包含了多个码流参数,其中又以data数组为核心,主要是存储原始数据。在data数组中对于packed和planar数据的存储格式是不一样的。
- 在AVPictureType结构体中IBP较为常见,但是要注意S\SI\BI\BP帧类型,S 帧是一种特殊的编码帧,它本身是帧间预测的(像 P 帧),但它的预测信息(运动矢量、残差)可以被后续的 I 帧或 P 帧作为参考。SI 帧是一种特殊的 I 帧。它所有的宏块都是帧内编码的(像 I 帧一样),所以它是一个独立的帧,不依赖其他帧进行预测。 它的主要作用也是提供随机访问点,但它通常是为了更快速、更鲁棒的切换而设计。与普通的 I 帧相比,SI 帧可能在编码方式或解码端处理上有一些特定优化,以确保在它这里可以立即开始一个新的解码序列。BI 帧是一种非常特殊的帧,它包含的宏块全部是帧内编码的(就像 I 帧),但它却像 B 帧一样,既可以被向前参考,也可以被向后参考。SP 帧是一种特殊的 P 帧。它的大部分宏块都是帧间预测的(像 P 帧),但它的预测信息(运动矢量、残差)可以被后续的 I 帧或 P 帧作为参考。
- qscale_table,网上绝大部分关于这个结构体的论述都是搬得雷博士的,需要注意宏块在视频帧指的是一个矩形块,而QP值其实是QP_step的一个索引,规定了宏块的采样步数,这个索引表是可以被查到的。
- 另外以及像运动矢量和运动估计参考帧都比较容易理解
在内存中packet和frame是如何进入队列,并等待被利用的呢
- 首先packet的拷贝有两种,深拷贝和浅拷贝,packet内部会初始化一个buffer指针,通过引用这个指针实现对数据包的引用。以浅拷贝为例,由于指针固定,无法精准保证释放的时候别的部分没有利用这部分数据空间,因此引入计数器,引用+1.释放—1,如果引用计数为0,那么就可以释放这片空间了。Frame的机理也是一样的
- av_packet—alloc,ava_packet_free。申请和释放空间
- av_init_packet 初始化AVpacket字段
- av_new_packet为buffer分配内存,计数初始化为1
- av_packet_ref 增加计数
- av_packet_unref 减少计数
- av_pakcet_move_ref 转移计数
- av_packet_clone 申请结构体,并且计数加1
接下来是以雷博士做的SIMPLEST_FFMPEG_PLAYER为例(注意是第一版),在上面添加注释,详细分析每个语句的作用,以及视频播放器的工作流程。
1 | /** |