ffmpeg 架构分析

来源:互联网 发布:ae mac 破解补丁 编辑:程序博客网 时间:2024/05/22 13:48

1. 简介

FFmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg

开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。FFmpeg支持MPEG

DivXMPEG4AC3DVFLV40多种编码,AVIMPEGOGGMatroskaASF90多种解码.

TCPMP, VLC, MPlayer等开源播放器都用到了FFmpeg

FFmpeg主目录下主要有libavcodeclibavformatlibavutil等子目录。其中libavcodec

于存放各个encode/decode模块,libavformat用于存放muxer/demuxer模块,libavutil用于

存放内存操作等辅助性模块。

flash movieflv文件格式为例, muxer/demuxerflvenc.cflvdec.c文件

libavformat目录下,encode/decodempegvideo.ch263de.clibavcodec目录下。

 

2. muxer/demuxerencoder/decoder定义与初始化

muxer/demuxerencoder/decoderFFmpeg中的实现代码里,有许多相同的地方,而二者最

大的差别是muxer 和demuxer分别是不同的结构AVOutputFormatAVInputFormat,而encoder

decoder都是用的AVCodec 结构。

 

muxer/demuxerencoder/decoderFFmpeg中相同的地方有:

    二者都是在main()开始的av_register_all()函数内初始化的

    二者都是以链表的形式保存在全局变量中的

        muxer/demuxer是分别保存在全局变量AVOutputFormat *first_oformat

        AVInputFormat *first_iformat中的。

        encoder/decoder都是保存在全局变量AVCodec *first_avcodec中的。

    二者都用函数指针的方式作为开放的公共接口

   

demuxer开放的接口有:

    int (*read_probe)(AVProbeData *);

    int (*read_header)(struct AVFormatContext *, AVFormatParameters *ap);

    int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);

    int (*read_close)(struct AVFormatContext *);

    int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);

   

muxer开放的接口有:

    int (*write_header)(struct AVFormatContext *);

    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);

    int (*write_trailer)(struct AVFormatContext *);

 

encoder/decoder的接口是一样的,只不过二者分别只实现encoderdecoder函数:

    int (*init)(AVCodecContext *);

    int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);

    int (*close)(AVCodecContext *);

    int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, uint8_t *buf, int buf_size);

 

flv文件为例来说明muxer/demuxer的初始化。

libavformat\allformats.c文件的av_register_all(void)函数中,通过执行

REGISTER_MUXDEMUX(FLV, flv);

将支持flv 格式的flv_muxerflv_demuxer变量分别注册到全局变量first_oformatfirst_iformat链表的最后位置。

其中flv_muxerlibavformat\flvenc.c中定义如下:

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},

}

AVOutputFormat结构的定义如下:

typedef struct AVOutputFormat {

    const char *name;

    const char *long_name;

    const char *mime_type;

    const char *extensions;

   

    int priv_data_size;

   

    enum CodecID audio_codec;

    enum CodecID video_codec;

 

    int (*write_header)(struct AVFormatContext *);

    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);

    int (*write_trailer)(struct AVFormatContext *);

   

    int flags;

   

    int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);

    int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush);

 

   

    const struct AVCodecTag **codec_tag;

   

    struct AVOutputFormat *next;

} AVOutputFormat;

AVOutputFormat结构的定义可知,flv_muxer变量初始化的第一、二个成员分别为该muxer

的名称与长名称,第三、第四个成员为所对应MIME Type和后缀名,第五个成员是所对应的

私有结构的大小,第六、第七个成员为所对应的音频编码和视频编码类型ID,接下来就是三

个重要的接口函数,该 muxer的功能也就是通过调用这三个接口实现的

 

flv_demuxerlibavformat\flvdec.c中定义如下, 与flv_muxer类似,在这儿主要也是设置

5个接口函数,其中flv_probe接口用途是测试传入的数据段是否是符合当前文件格式,这

个接口在匹配当前demuxer时会用到。

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,

};

 

在上述av_register_all(void)函数中通过执行libavcodec\allcodecs.c文件里的

avcodec_register_all(void)函数来初始化全部的encoder/decoder

 

因为不是每种编码方式都支持encodedecode,所以有以下三种注册方式

#define REGISTER_ENCODER(X,x) \

    if(ENABLE_##X##_ENCODER) register_avcodec(&x##_encoder)

#define REGISTER_DECODER(X,x) \

    if(ENABLE_##X##_DECODER) register_avcodec(&x##_decoder)

#define REGISTER_ENCDEC(X,x) REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)

 

如支持flvflv_encoderflv_decoder变量就分别是在libavcodec\mpegvideo.clibavcodec\h263de.c中创建的。

3. 当前muxer/demuxer的匹配

FFmpeg的文件转换过程中,首先要做的就是根据传入文件和传出文件的后缀名[FIXME]匹配合适的demuxermuxer

匹配上的demuxermuxer都保存在如下所示,定义在ffmpeg.c里的

全局变量file_iformatfile_oformat中:

    static AVInputFormat *file_iformat;

    static AVOutputFormat *file_oformat;

3.1 demuxer匹配

libavformat\utils.c中的static AVInputFormat *av_probe_input_format2(

AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据

,依次调用每个demuxerread_probe接口,来进行该demuxer是否和传入的文件内容匹配的

判断。其调用顺序如下:

void parse_options(int argc, char **argv, const OptionDef *options,

void (* parse_arg_function)(const char *));

static void opt_input_file(const char *filename)

int av_open_input_file(…… )

AVInputFormat *av_probe_input_format(AVProbeData *pd,

                                int is_opened)

static AVInputFormat *av_probe_input_format2(……)

opt_input_file函数是在保存在const OptionDef options[]数组中,用于

void parse_options(int argc, char **argv, const OptionDef *options)中解析argv里的

“-i” 参数,也就是输入文件名时调用的。

3.2 muxer匹配

demuxer的匹配不同,muxer的匹配是调用guess_format函数,根据main() 函数的argv里的

输出文件后缀名来进行的。

void parse_options(int argc, char **argv, const OptionDef *options,

          void (* parse_arg_function)(const char *));

void parse_arg_file(const char *filename)

static void opt_output_file(const char *filename)

AVOutputFormat *guess_format(const char *short_name,

                            const char *filename,

                            const char *mime_type)

3.3 当前encoder/decoder的匹配

main()函数中除了解析传入参数并初始化demuxermuxerparse_options( )函数以外,

其他的功能都是在av_encode( )函数里完成的。

libavcodec\utils.c中有如下二个函数:

    AVCodec *avcodec_find_encoder(enum CodecID id)

    AVCodec *avcodec_find_decoder(enum CodecID id)

他们的功能就是根据传入的CodecID,找到匹配的encoderdecoder

av_encode( )函数的开头,首先初始化各个AVInputStreamAVOutputStream,然后分别调

用上述二个函数,并将匹配上的encoderdecoder分别保存在:

AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec

AVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec变量。

4. 其他主要数据结构

4.1 AVFormatContext

AVFormatContextFFMpeg格式转换过程中实现输入和输出功能保存相关数据的主要结构。

每一个输入和输出文件,都在如下定义的指针数组全局变量中有对应的实体。

    static AVFormatContext *output_files[MAX_FILES];

    static AVFormatContext *input_files[MAX_FILES];

对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformat

oformat成员赋值。

    struct AVInputFormat *iformat;

    struct AVOutputFormat *oformat;

对一个AVFormatContext来说,这二个成员不能同时有值,即一个AVFormatContext不能同时

含有demuxermuxer。在main( )函数开头的parse_options( )函数中找到了匹配的muxer

demuxer之后,根据传入的argv参数,初始化每个输入和输出的AVFormatContext结构,并保

存在相应的output_filesinput_files指针数组中。在av_encode( )函数中,output_files

input_files是作为函数参数传入后,在其他地方就没有用到了。

4.2 AVCodecContext

保存AVCodec指针和与codec相关数据,如videowidthheightaudiosample rate等。

AVCodecContext中的codec_typecodec_id二个变量对于encoder/decoder的匹配来说,最为

重要

    enum CodecType codec_type;   

    enum CodecID codec_id;       

如上所示,codec_type保存的是CODEC_TYPE_VIDEOCODEC_TYPE_AUDIO等媒体类型,

codec_id保存的是CODEC_ID_FLV1CODEC_ID_VP6F等编码方式。

以支持flv格式为例,在前述的av_open_input_file(…… ) 函数中,匹配到正确的

AVInputFormat demuxer后,通过av_open_input_stream( )函数中调用AVInputFormat

read_header接口来执行flvdec.c中的flv_read_header( )函数。在flv_read_header( )函数

内,根据文件头中的数据,创建相应的视频或音频AVStream,并设置AVStream

AVCodecContext的正确的codec_type值。codec_id值是在解码过程中flv_read_packet( )

数执行时根据每一个packet头中的数据来设置的。

4.3 AVStream

AVStream结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:

    AVCodecContext *codec;

    void *priv_data;

其中codec指针保存的就是上节所述的encoderdecoder结构。priv_data指针保存的是和具

体编解码流相关的数据,如下代码所示,在ASF的解码过程中,priv_data保存的就是

ASFStream结构的数据。

    AVStream *st;

    ASFStream *asf_st; 

    … …

    st->priv_data = asf_st;

4.4 AVInputStream/ AVOutputStream

根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStreamAVOutputStream结构中,在av_encode( )函数中使用。

AVInputStream中还保存的有与时间有关的信息。

AVOutputStream中还保存有与音视频同步等相关的信息。

4.5 AVPacket

AVPacket结构定义如下,其是用于保存读取的packet数据。

typedef struct AVPacket {

    int64_t pts;            ///< presentation time stamp in time_base units

    int64_t dts;            ///< decompression time stamp in time_base units

    uint8_t *data;

    int  size;

    int  stream_index;

    int  flags;

    int  duration;        ///< presentation duration in time_base units (0 if not available)

    void (*destruct)(struct AVPacket *);

    void *priv;

    int64_t pos;          ///< byte position in stream, -1 if unknown

} AVPacket;

av_encode()函数中,调用AVInputFormat

(*read_packet)(struct AVFormatContext *, AVPacket *pkt)接口,读取输入文件的一帧数

据保存在当前输入AVFormatContextAVPacket成员中。

 


http://blog.sina.com.cn/s/blog_4c4835b001010ir1.html

1 0
原创粉丝点击