FFMpeg框架代码阅读
fmms
13年前
<p>内容摘要 FFmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。FFmpeg支持MPEG、 DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码. TCPMP, VLC, MPlayer等开源播放器都用到了FFmpeg。 FFmpeg主目录下主要有libavcodec、libavformat和libavutil等子目录</p> <p>1. 简介<br /> FFmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的<br /> 开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。FFmpeg支持MPEG、<br /> DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码.<br /> TCPMP, VLC, MPlayer等开源播放器都用到了FFmpeg。<br /> FFmpeg主目录下主要有libavcodec、libavformat和libavutil等子目录。其中libavcodec用<br /> 于存放各个encode/decode模块,libavformat用于存放muxer/demuxer模块,libavutil用于<br /> 存放内存操作等辅助性模块。<br /> 以flash movie的flv文件格式为例, muxer/demuxer的flvenc.c和flvdec.c文件在<br /> libavformat目录下,encode/decode的mpegvideo.c和h263de.c在libavcodec目录下。<br /> <br /> 2. muxer/demuxer与encoder/decoder定义与初始化<br /> muxer/demuxer和encoder/decoder在FFmpeg中的实现代码里,有许多相同的地方,而二者最<br /> 大的差别是muxer 和demuxer分别是不同的结构AVOutputFormat与AVInputFormat,而encoder<br /> 和decoder都是用的AVCodec 结构。<br /> <br /> muxer/demuxer和encoder/decoder在FFmpeg中相同的地方有:<br /> 二者都是在main()开始的av_register_all()函数内初始化的<br /> 二者都是以链表的形式保存在全局变量中的<br /> muxer/demuxer是分别保存在全局变量AVOutputFormat *first_oformat与<br /> AVInputFormat *first_iformat中的。<br /> encoder/decoder都是保存在全局变量AVCodec *first_avcodec中的。<br /> 二者都用函数指针的方式作为开放的公共接口<br /> <br /> demuxer开放的接口有:<br /> int (*read_probe)(AVProbeData *);<br /> int (*read_header)(struct AVFormatContext *, AVFormatParameters *ap);<br /> int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);<br /> int (*read_close)(struct AVFormatContext *);<br /> int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);<br /> <br /> muxer开放的接口有:<br /> int (*write_header)(struct AVFormatContext *);<br /> int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);<br /> int (*write_trailer)(struct AVFormatContext *);<br /> <br /> encoder/decoder的接口是一样的,只不过二者分别只实现encoder和decoder函数:<br /> int (*init)(AVCodecContext *);<br /> int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);<br /> int (*close)(AVCodecContext *);<br /> int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, uint8_t *buf, int buf_size);<br /> <br /> 仍以flv文件为例来说明muxer/demuxer的初始化。<br /> 在libavformat\allformats.c文件的av_register_all(void)函数中,通过执行<br /> REGISTER_MUXDEMUX(FLV, flv);<br /> 将支持flv 格式的flv_muxer与flv_demuxer变量分别注册到全局变量first_oformat与first_iformat链表的最后位置。<br /> 其中flv_muxer在libavformat\flvenc.c中定义如下:</p> <pre class="brush:cpp; toolbar: true; auto-links: false;">AVOutputFormat flv_muxer = { "flv", "flv format", "video/x-flv", "flv", sizeof(FLVContext), #ifdef CONFIG_LIBMP3LAME CODEC_ID_MP3, #else // CONFIG_LIBMP3LAME CODEC_ID_NONE, CODEC_ID_FLV1, flv_write_header, flv_write_packet, flv_write_trailer, .codec_tag= (const AVCodecTag*[]){flv_video_codec_ids, flv_audio_codec_ids, 0}, }</pre> <p></p> <p>AVOutputFormat结构的定义如下:</p> <pre class="brush:cpp; toolbar: true; auto-links: false;">typedef struct AVOutputFormat { const char *name; const char *long_name; const char *mime_type; const char *extensions; /**< comma separated filename extensions */ /** size of private data so that it can be allocated in the wrapper */ int priv_data_size; /* output support */ enum CodecID audio_codec; /**< default audio codec */ enum CodecID video_codec; /**< default video codec */ int (*write_header)(struct AVFormatContext *); int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); int (*write_trailer)(struct AVFormatContext *); /** can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */ int flags; /** currently only used to set pixel format if not YUV420P */ int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *); int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush); /** *list of supported codec_id-codec_tag pairs,ordered by "better choice first" * the arrays are all CODEC_ID_NONE terminated */ const struct AVCodecTag **codec_tag; /* private fields */ struct AVOutputFormat *next; } AVOutputFormat;</pre> <br /> 由AVOutputFormat结构的定义可知,flv_muxer变量初始化的第一、二个成员分别为该muxer <br /> 的名称与长名称,第三、第四个成员为所对应MIMIE Type和后缀名,第五个成员是所对应的 <br /> 私有结构的大小,第六、第七个成员为所对应的音频编码和视频编码类型ID,接下来就是三 <br /> 个重要的接口函数,该 muxer的功能也就是通过调用这三个接口实现的。 <br /> <br /> flv_demuxer在libavformat\flvdec.c中定义如下, 与flv_muxer类似,在这儿主要也是设置 <br /> 了5个接口函数,其中flv_probe接口用途是测试传入的数据段是否是符合当前文件格式,这 <br /> 个接口在匹配当前demuxer时会用到。 <br /> <pre class="brush:cpp; toolbar: true; auto-links: false;">AVInputFormat flv_demuxer = { "flv", "flv format", 0, flv_probe, flv_read_header, flv_read_packet, flv_read_close, flv_read_seek, .extensions = "flv", .value = CODEC_ID_FLV1, };</pre> <p></p> <p>在上述av_register_all(void)函数中通过执行libavcodec\allcodecs.c文件里的<br /> avcodec_register_all(void)函数来初始化全部的encoder/decoder。<br /> <br /> 因为不是每种编码方式都支持encode和decode,所以有以下三种注册方式:<br /> #define REGISTER_ENCODER(X,x) \<br /> if(ENABLE_##X##_ENCODER) register_avcodec(&x##_encoder)<br /> #define REGISTER_DECODER(X,x) \<br /> if(ENABLE_##X##_DECODER) register_avcodec(&x##_decoder)<br /> #define REGISTER_ENCDEC(X,x) REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)<br /> <br /> 如支持flv的flv_encoder和flv_decoder变量就分别是在libavcodec\mpegvideo.c和<br /> libavcodec\h263de.c中创建的。<br /> <br /> 3. 当前muxer/demuxer的匹配<br /> 在FFmpeg的文件转换过程中,首先要做的就是根据传入文件和传出文件的后缀名[FIXME]匹配<br /> 合适的demuxer和muxer。匹配上的demuxer和muxer都保存在如下所示,定义在ffmpeg.c里的<br /> 全局变量file_iformat和file_oformat中:<br /> static AVInputFormat *file_iformat;<br /> static AVOutputFormat *file_oformat;<br /> <br /> 3.1 demuxer匹配<br /> 在libavformat\utils.c中的static AVInputFormat *av_probe_input_format2(<br /> AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据<br /> ,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的<br /> 判断。其调用顺序如下:<br /> void parse_options(int argc, char **argv, const OptionDef *options, <br /> void (* parse_arg_function)(const char *));<br /> static void opt_input_file(const char *filename)<br /> int av_open_input_file(…… )<br /> AVInputFormat *av_probe_input_format(AVProbeData *pd, <br /> int is_opened)<br /> static AVInputFormat *av_probe_input_format2(……)<br /> opt_input_file函数是在保存在const OptionDef options[]数组中,用于<br /> void parse_options(int argc, char **argv, const OptionDef *options)中解析argv里的<br /> “-i” 参数,也就是输入文件名时调用的。<br /> <br /> 3.2 muxer匹配<br /> 与demuxer的匹配不同,muxer的匹配是调用guess_format函数,根据main() 函数的argv里的<br /> 输出文件后缀名来进行的。<br /> void parse_options(int argc, char **argv, const OptionDef *options, <br /> void (* parse_arg_function)(const char *));<br /> void parse_arg_file(const char *filename)<br /> static void opt_output_file(const char *filename)<br /> AVOutputFormat *guess_format(const char *short_name, <br /> const char *filename,<br /> const char *mime_type)<br /> <br /> 3.3 当前encoder/decoder的匹配<br /> 在main()函数中除了解析传入参数并初始化demuxer与muxer的parse_options( )函数以外,<br /> 其他的功能都是在av_encode( )函数里完成的。<br /> 在libavcodec\utils.c中有如下二个函数:<br /> AVCodec *avcodec_find_encoder(enum CodecID id)<br /> AVCodec *avcodec_find_decoder(enum CodecID id)<br /> 他们的功能就是根据传入的CodecID,找到匹配的encoder和decoder。<br /> <br /> 在av_encode( )函数的开头,首先初始化各个AVInputStream和AVOutputStream,然后分别调<br /> 用上述二个函数,并将匹配上的encoder与decoder分别保存在:<br /> AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec与<br /> AVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec变量。 </p> <p>4. 其他主要数据结构<br /> 4.1 AVFormatContext<br /> AVFormatContext是FFMpeg格式转换过程中实现输入和输出功能、保存相关数据的主要结构。<br /> 每一个输入和输出文件,都在如下定义的指针数组全局变量中有对应的实体。<br /> static AVFormatContext *output_files[MAX_FILES];<br /> static AVFormatContext *input_files[MAX_FILES];<br /> 对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformat<br /> 或oformat成员赋值。<br /> struct AVInputFormat *iformat;<br /> struct AVOutputFormat *oformat;<br /> 对一个AVFormatContext来说,这二个成员不能同时有值,即一个AVFormatContext不能同时<br /> 含有demuxer和muxer。在main( )函数开头的parse_options( )函数中找到了匹配的muxer和<br /> demuxer之后,根据传入的argv参数,初始化每个输入和输出的AVFormatContext结构,并保<br /> 存在相应的output_files和input_files指针数组中。在av_encode( )函数中,output_files<br /> 和input_files是作为函数参数传入后,在其他地方就没有用到了。<br /> <br /> 4.2 AVCodecContext<br /> 保存AVCodec指针和与codec相关数据,如video的width、height,audio的sample rate等。<br /> AVCodecContext中的codec_type,codec_id二个变量对于encoder/decoder的匹配来说,最为<br /> 重要。<br /> enum CodecType codec_type; /* see CODEC_TYPE_xxx */<br /> enum CodecID codec_id; /* see CODEC_ID_xxx */</p> <p>如上所示,codec_type保存的是CODEC_TYPE_VIDEO,CODEC_TYPE_AUDIO等媒体类型,<br /> codec_id保存的是CODEC_ID_FLV1,CODEC_ID_VP6F等编码方式。<br /> <br /> 以支持flv格式为例,在前述的av_open_input_file(…… ) 函数中,匹配到正确的<br /> AVInputFormat demuxer后,通过av_open_input_stream( )函数中调用AVInputFormat的<br /> read_header接口来执行flvdec.c中的flv_read_header( )函数。在flv_read_header( )函数<br /> 内,根据文件头中的数据,创建相应的视频或音频AVStream,并设置AVStream中<br /> AVCodecContext的正确的codec_type值。codec_id值是在解码过程中flv_read_packet( )函<br /> 数执行时根据每一个packet头中的数据来设置的。<br /> <br /> 4.3 AVStream<br /> AVStream结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:<br /> AVCodecContext *codec; /**< codec context */<br /> void *priv_data;<br /> 其中codec指针保存的就是上节所述的encoder或decoder结构。priv_data指针保存的是和具<br /> 体编解码流相关的数据,如下代码所示,在ASF的解码过程中,priv_data保存的就是<br /> ASFStream结构的数据。<br /> AVStream *st;<br /> ASFStream *asf_st; <br /> … …<br /> st->priv_data = asf_st;</p> <p>4.4 AVInputStream/ AVOutputStream<br /> 根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStream和AVOutputStream<br /> 结构中,在av_encode( )函数中使用。AVInputStream中还保存的有与时间有关的信息。<br /> AVOutputStream中还保存有与音视频同步等相关的信息。<br /> <br /> 4.5 AVPacket<br /> AVPacket结构定义如下,其是用于保存读取的packet数据。<br /> typedef struct AVPacket {<br /> int64_t pts; ///< presentation time stamp in time_base units<br /> int64_t dts; ///< decompression time stamp in time_base units<br /> uint8_t *data;<br /> int size;<br /> int stream_index;<br /> int flags;<br /> int duration; ///< presentation duration in time_base units (0 if not available)<br /> void (*destruct)(struct AVPacket *);<br /> void *priv;<br /> int64_t pos; ///< byte position in stream, -1 if unknown<br /> } AVPacket;<br /> <br /> 在av_encode()函数中,调用AVInputFormat的<br /> (*read_packet)(struct AVFormatContext *, AVPacket *pkt)接口,读取输入文件的一帧数<br /> 据保存在当前输入AVFormatContext的AVPacket成员中。<br /> <br /> 5. av_encode函数主要流程<br /> av_encode()函数是FFMpeg中最重要的函数,编解码和输出等大部分功能都在此函数完成,<br /> 因此有必要详细描述一下这个函数的主要流程。<br /> 1).input streams initializing<br /> 2).output streams initializing<br /> 3).encoders and decoders initializing<br /> 4).set meta data information from input file if required.<br /> 5).write output files header<br /> 6).loop of handling each frame<br /> a.read frame from input file:<br /> b.decode frame data<br /> c.encode new frame data<br /> d.write new frame to output file<br /> 7).write output files trailer<br /> 8).close each encoder and decoder </p> <p> </p> <p>本文来自CSDN博客:<a href="/misc/goto?guid=4959517417861781210">http://blog.csdn.net/xianfengdesign/archive/2009/07/17/4353928.aspx</a></p> <p>原文出处: <a href="/misc/goto?guid=4959517417957862589">http://www.yuanma.org/data/2009/0521/article_3655_1.htm</a></p>