1 FFMPEG中的重要结构体
1.1 结构体分类
FFMPEG中结构体很多。最关键的结构体可以分成以下几类:
1)应用层(提纲挈领)
AVFormatContext 结构体按名字来说,应该将其归为封装层,但是,从整体的架构上来说,它是FFMPEG中提纲挈领的最外层结构体,在音视频处理过程中,该结构体保存着所有信息。这些信息一部分由AVFormatContext的直接成员持有,另一部分由后续要介绍的这些数据结构所持有,而这些结构体都是AVFormatContext的直接成员或者间接成员。总的来说,AVFormatContext结构体作用可以类比于WebRtc中的PeerConnection,但是区别在于,WebRtc是C++的接口,PeerConnection不仅持有数据(状态信息),而且还提供方法。而FFMPEG是C语言实现,AVFormatContext持有数据,方法与其是分开的。具体关于AVFormatContext结构体的分析见FFRMPEG4.1源码分析之 AVFormatContext。
2) 协议层(http, rtsp, rtmp, mms, file)-----I/O相关结构体
协议层,处理各种协议,但我更倾向于认为其是FFMPEG的I/O处理层,提供了资源的按字节读写能力。这一层的作用:一方面根据音视频资源的URL,来识别该以什么协议来访问该资源。本地存储的文件?那么是file协议。网络资源?是http协议?rtsp协议?rtmp协议?;另一方面识别协议后,那么可以使用协议相关的方法open资源,read资源的原始比特流,向资源中write原始比特流,在资源中seek,close资源,并提供缓冲区buffer,所有的操作就像访问一个文件一样。FFMPEG这层提供了这样一个抽象,像访问文件一样去访问资源,这个概念在linux系统中普遍存在,一切皆是文件。这一层的主要结构体有下面三个URLProtocol,URLContext,AVIOContext,可以认为这3个结构体在协议层也是有上下级关系的。
URLProtocol 是这层中最底层的结构体,持有协议访问方法:每个协议都有其专属的URLProtocol结构体,在FFMPEG中以常量的形式存在,命名方式是ff_xxx_protocol,其中xxx是协议名。URLProtocol的成员函数指针族提供了上述类文件操作的所有方法,如果是网络协议,那么网络访问的所有一切也被封装在这些方法之中,可以认为URLProtocol提供了协议的访问方法。
URLContext 是协议上下文对象,是URLProtocol上一层的结构体,持有协议访问方法以及当前访问状态信息:通过持有URLProtocol对象而持有协议访问方法,并且通过持有另外一个协议相关的状态信息结构体来持有当前协议访问的状态信息。持有状态信息的这个结构体名称跟协议名相关,以Http协议为例,相应结构体名称为HttpContext。注意一点:有些相关的协议会映射到同一个状态信息的结构体上,比如http,https,httpproxy对应的URLProtocol结构体为ff_http_protocol,ff_https_protocol,ff_httpproxy_protocol,但是这3个协议对应同一个状态信息上下文结构体HttpContext。再比如file,pipe协议对应的URLProtocol结构体为ff_file_protocol,ff_pipe_protocol,二者对应同一个状态信息上下文结构体FileContext。
AVIOContext 是协议层最上一层的结构体,可以认为是协议层的public api,提纲挈领的AVFormatContext通过持有AVIOContext而具备IO访问能力。AVIOContext通过持有URLContext而持有协议访问方法以及访问状态,同时内部再提供一个读写缓冲区。注意是读写缓冲区,既可以作为读缓冲区,也可以写缓冲区,当然同时只支持读或者写。
3) 封装层(flv,avi,rmvb,mp4)
以解封装为例,协议层提供了对资源的按字节读写能力,并将字节数据存储到缓冲区中,而封装层所起作用就是从字节流中截取一个个数据帧出来,这个数据帧以AVPacket结构体来表示,这个数据帧可能是属于视频,一般是存一帧,也可能是音频,可能对应好几帧音频。这一层主要的结构体有如下几个:AVInputFormat,AVOutputFormat,AVFormatContext,AVFormatInternal。
AVInputFormat 存储输入视音频使用的封装格式,提供了按格式读取数据的方法。类似于每种协议格式对应一个URLProtocol结构体,每种输入视音频封装格式都对应一个AVInputFormat 结构体,在FFMPEG中以常量的形式存在,命名方式是ff_xxx_demuxer,其中xxx是封装格式名。AVInputFormat结构体提供了文件格式探测read_probe,读文件头read_header,写数据包read_packet,读关闭read_close等方法。注意没有read_open,因为到这一层的时候,资源肯定是打开的。
AVOutputFormat 存储输出音视频使用的封装格式,提供了将格式化的数据转成无差别的字节流的方法。类似于每种协议格式对应一个URLProtocol结构体,每种输出视音频封装格式都对应一个AVOutputFormat 结构体,在FFMPEG中以常量的形式存在,命名方式是ff_xxx_muxer,其中xxx是封装格式名。AVOutputFormat 结构体提供了写文件头write_header,写数据包write_packet,写文件尾write_trailer等方法。
AVFormatInternal 是一个封装层内部使用的对象,提供了已读取或者待写入的编码数据包AVPacket队列等状态信息。
AVFormatContext 是一个上下文对象,是AVOutputFormat/AVInputFormat上层结构体,可认为是封装层的public api,当然也如应用层所述,其作用不止如此。一方面,AVFormatContext通过持有AVOutputFormat或者是AVInputFormat从而具有按格式写数据包和按格式读取数据包的方法;另一方面,AVFormatContext通过持有AVFormatInternal,从而持有了封装/解封装的过程的状态信息。
AVPacket 是从IO层读取字节数据后经封装层包装好的编码数据包。该结构体存储了编码数据,以及描述这些数据的信息,比如pts(播放时间戳),dts(解码时间戳),size(数据带下),stream_index(所属流的序号),duration(持续时间)等等。
4) 编解码层(h264,mpeg2,aac,mp3)
以解码为例,封装层将数据提取成一个个AVPacket,包含了编码后的数据包,从概念上来说,由于音视频资源中会存在多路流,音频流,视频流,字幕流,用户自定义数据流,并且每种类型的流还可以有多个。那么封装层得到的AVPacket是其中某一路流的数据包(归属于哪路流在封装层就能确定),每路流的编解码方式都可以各不相同,因此,AVPacket需要使用其归属的那路流的解码器去解码。编解码层就是将编码后的数据包解码出来成为原始音视频数据,以AVFrame结构体来承载解码后的数据。原始音视频数据可以进一步的滤镜处理或者直接渲染出来。主要涉及的结构体有AVStream,AVCodecContext,AVCodec。
AVCodec 是编码层的核心对象,也是该层中最底层的数据结构,持有音视频数据的编解码方法:每一种编解码算法都有专属的AVCodec,编码器的命名规则为ff_xxx_encoder,解码器命名方式为 ff_xxx_decoder,但都是AVCodec结构体。比如常见的AAC编码器结构体对象为ff_aac_encoder,其持有编码器的init方法,encode2方法,close方法;AAC解码器结构体对象为ff_aac_decoder,其持有init方法,decode方法,close方法。
AVCodecContext 是编码器上下文对象,是AVCodec上一层的结构体,持有编解码方法以及当前编解码状态信息:通过持有AVCodec对象而持有编解码方法,并且通过持有另外一个编解码相关的状态信息结构体来持有当前编解码状态信息。持有状态信息的这个结构体名称跟协议名相关,以AAC编码器为例,相应结构体名称为AACEncContext;以AAC解码器为例,相应的结构体名称为AACContext。
AVStream 是编解码层最上层的数据结构,表征的是封装中的一路流。其通过持有AVCodecContext对象来持有编解码的一切。不过目前,该成员在AVStream中被声明为deprecated,AVStream通过持有AVCodecParameters对象来获取编解码相关的参数。提纲挈领的AVFormatContext通过持有AVStream数组而持有编解码的一切。
AVFrame 是AVPacket经编解码层解码后的原始音视频数据。该结构存储着原始音视频数据,以及描述原始音视频数据的信息,比如原始数据是音频数据,那么必不可少的有sample_rate(采样率),channel_layout(通道布局),format(采样格式)等等,对于视频数据,必不可少的有width(宽),height(高),format(像素格式),key_frame(是否关键帧),coded_picture_number(编码序号),display_picture_number(显示序号),pts(播放时间戳),注意可没有帧率这个参数。
1.2 结构体之间的层次关系
他们之间的对应关系如下所示: