1概述
ffmpeg的demuxer和muxer接口分别在AVInputFormat和AVOutputFormat两个结构体中实现,在av_register_all()函数中将两个结构分别静态初始化为两个链表,保存在全局变量:first_iformat和first_oformat两个变量中。在FFmpeg的文件转换或者打开过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的demuxer和muxer,得到合适的信息后保存在AVFormatContext中。
2相关数据结构介绍
1、AVInputFormat
该结构被称为demuxer,是音视频文件的一个解封装器,它的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 typedef struct AVInputFormat { const char *name ; const char *long_name ; int priv_data_size ; //具体文件容器格式对应的Context的大小,如: avicontext int ( *read_probe ) (AVProbeData * ) ; int ( *read_header ) ( struct AVFormatContext *, AVFormatParameters *ap ) ; int ( *read_packet ) ( struct AVFormatContext *, AVPacket *pkt ) ; int ( *read_close ) ( struct AVFormatContext * ) ; #if FF_API_READ_SEEK attribute_deprecated int ( *read_seek ) ( struct AVFormatContext *, int stream_index, int64_t timestamp, int flags ) ; #endif int64_t ( *read_timestamp ) ( struct AVFormatContext *s, int stream_index, int64_t *pos, int64_t pos_limit ) ; int flags ; const char *extensions ; int value ; int ( *read_play ) ( struct AVFormatContext * ) ; int ( *read_pause ) ( struct AVFormatContext * ) ; const struct AVCodecTag * const *codec_tag ; int ( *read_seek2 ) ( struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags ) ; #if FF_API_OLD_METADATA2 const AVMetadataConv *metadata_conv ; #endif const AVClass *priv_class ; ///< AVClass for the private context struct AVInputFormat *next; } AVInputFormat ;
对于不同的文件格式要实现相应的函数接口,这样每一种格式都有一个对应的demuxer,所有的demuxer都保存在全局变量first_iformat中。红色表示提供的接口。
2、AVOutputFormat
该结构与AVInputFormat类似也是在编译时静态初始化,组织为一个链表结构,提供了多个muxer的函数接口。
1 2 3 int ( *write_header ) ( struct AVFormatContext * ) ; int ( *write_packet ) ( struct AVFormatContext *,AVPacket *pkt ) ; int ( *write_trailer ) ( struct AVFormatContext * ) ;
对于不同的文件格式要实现相应的函数接口,这样每一种格式都有一个对应的muxer,所有的muxer都保存在全局变量first_oformat中。
3、AVFormatContext
该结构表示与程序当前运行的文件容器格式使用的上下文,着重于所有文件容器共有的属性,在运行时动态的确定其值,是AVInputFormat和AVOutputFormat的载体,但同一个结构对象只能使AVInputFormat和AVOutputFormat中的某一个有效。每一个输入和输出文件,都在
1 2 static AVFormatContext *output_files [MAX_FILES ] ; static AVFormatContext *input_files [MAX_FILES ] ;
定义的指针数组全局变量中有对应的实体。对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformat或oformat成员赋值。在转码时读写数据是通过AVFormatContext结构进行的。定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 typedef struct AVFormatContext { const AVClass *av_class ; struct AVInputFormat *iformat ; //指向具体的demuxer </span> struct AVOutputFormat *oformat ; //指向具体的muxer </span> void *priv_data ; //具体文件容器格式的Context如:</span> avicontext AVIOContext *pb ; //广义的输入输出;</span> unsigned int nb_streams ; //本次打开的文件容器中流的数量 </span> AVStream **streams ; //每个流的相关描述</span> char filename [ 1024 ] ; // input or output filename */ int64_t timestamp ; int ctx_flags ; struct AVPacketList *packet_buffer ; …… enum CodecID video_codec_id ; enum CodecID audio_codec_id ; enum CodecID subtitle_codec_id ; unsigned int max_index_size ; unsigned int max_picture_buffer ; …… struct AVPacketList *raw_packet_buffer ; struct AVPacketList *raw_packet_buffer_end ; struct AVPacketList *packet_buffer_end ; …… } AVFormatContext ;
红色部分的成员是AVFormatContext中最为重要的成员变量,这些变量的初始化是ffmpeg能正常工作的必要条件,那么,AVFormatContext是如何被初始化的呢?文件的格式是如何被探测到的呢? 首先我们来探讨:
struct AVInputFormat *iformat; //指向具体的demuxer
struct AVOutputFormat *oformat; //指向具体的muxer
void *priv_data; //具体文件容器格式的Context如:avicontext
三个成员的初始化。 在avformat_open_input()函数中调用了init_input()函数,然后用调用了av_probe_input_format()函数实现了对AVFormatContext的初始化。其调用关系如下:
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
AVInputFormat *fmt,int buf_size,AVFormatParameters *ap);
int avformat_open_input(ic_ptr, filename, fmt, &opts); static int init_input(s, filename)
av_probe_input_format(&pd, 0);
av_probe_input_format (AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的判断。与demuxer的匹配不同,muxer的匹配是调用guess_format函数,根据main( ) 函数的argv里的输出文件后缀名来进行的。至此完成了前三个重要成员的初始化,具体的做法就不在深入分析。
下面分别给出av_read_frame函数以及av_write_frame函数的基本流程。
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
->av_read_frame_internel-> av_read_packet ->iformat->read_packet(在实现中会丢弃多余信息) ->av_get_packet ->get_xxx
int av_write_frame(AVFormatContext *s, AVPacket *pkt); ->oformat->write_packet ->put_xxx 由上可见,对AVFormatContext的读写操作最终是通过ByteIOContext来实现的,这样,AVFormatContext与 URLContext就由ByteIOContext结构联系到一起了。在AVFormat结构体中有一个packet的缓冲区raw_packet_buffer,是AVPackList的指针类型,av_read_packet函数将读到的包添加至raw_packet_buffer链表末尾。