FFMpeg框架代码阅读

简介
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等子目录。其中libavcodec用于存放各个encode/decode模块,libavformat用于存放muxer/demuxer模块,libavutil用于存放内存操作等常用模块。
以flash movie的flv文件格式为例, muxer/demuxer的flvenc.c和flvdec.c文件在libavformat目录下,encode/decode的mpegvideo.c和h263de.c在libavcodec目录下。
 
muxer/demuxer与encoder/decoder定义与初始化
muxer/demuxer和encoder/decoder在FFmpeg中的实现代码里,有许多相同的地方,而二者最大的差别是muxer和demuxer分别是不同的结构AVOutputFormat与AVInputFormat,而encoder和decoder都是用的AVCodec结构。
 
muxer/demuxer和encoder/decoder在FFmpeg中相同的地方有:
l          二者都是在main()开始的av_register_all()函数内初始化的。
l          二者都是以链表的形式保存在全局变量中的。
muxer/demuxer是分别保存在全局变量AVOutputFormat *first_oformat与AVInputFormat *first_iformat中的。
encoder/decoder都是保存在全局变量AVCodec *first_avcodec中的。
l          二者都用函数指针的方式作为开放的公共接口。
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的接口都是一样的,只不过二者分别只实现encoder和decoder函数: 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_muxer在libavformat/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; /**< 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;

AVOutputFormat结构的定义可知, flv_muxer变量初始化的第一、第二个成员分别为该muxer的名称与长名称,第三、第四个成员为所对应MIMIE Type和后缀名,第五个成员是所对应的私有结构的大小,第六、第七个成员为所对应的音频编码和视频编码类型ID,接下来就是三个重要的接口函数,该muxer的功能也就是通过调用这三个接口实现的。
 
flv_demuxer在libavformat/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。
 
因为不是每种编码方式都支持encode和decode,所以有以下三种注册方式:
#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)
 
如支持flv的 flv_encoderflv_decoder变量就分别是在libavcodec/mpegvideo.c和libavcodec/h263de.c中创建的。
 
当前muxer/demuxer的匹配
在FFmpeg的文件转换过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的demuxer和muxer。
匹配上的demuxer和muxer都保存在如下所示,定义在ffmpeg.c里的全局变量 file_iformatfile_oformat中:
static AVInputFormat *file_iformat;
static AVOutputFormat *file_oformat;
 
1.          demuxer匹配
在libavformat/utils.c中的 static AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的判断。其调用顺序如下:
void parse_options(int argc, char **argv, const OptionDef *options)   
static void opt_input_file(const char *filename)
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” 参数,也就是输入文件名时调用的。
 
2.          muxer匹配
与demuxer的匹配不同,muxer的匹配是调用 guess_format函数,根据 main( ) 函数的argv里的输出文件后缀名来进行的。
void parse_options(int argc, char **argv, const OptionDef *options)
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)
 
当前encoder/decoder的匹配
main( )函数中除了解析传入参数并初始化demuxer与muxer的 parse_options( )函数以外,其他的功能都是在 av_encode( )函数里完成的。
在libavcodec/utils.c中有如下二个函数。
              AVCodec *avcodec_find_encoder(enum CodecID id)
              AVCodec *avcodec_find_decoder(enum CodecID id)
他们的功能就是根据传入的CodecID,找到匹配的encoder和decoder。
 
av_encode( )函数的开头,首先初始化各个 AVInputStreamAVOutputStream,然后分别调用上述二个函数,并将匹配上的encoder与decoder分别保存在 AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codecAVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec变量中。
 
  
 
其他主要数据结构
1.         AVFormatContext
AVFormatContext是FFMpeg格式转换过程中实现输入和输出功能、保存相关数据的主要结构。每一个输入和输出文件,都在如下定义的指针数组全局变量中有对应的实体。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的 iformatoformat成员赋值。
       struct AVInputFormat *iformat;
        struct AVOutputFormat *oformat;
对一个 AVFormatContext来说,二个成员不能同时有值,即一个 AVFormatContext不能同时含有demuxer和muxer。
main( )函数开头的 parse_options( )函数中找到了匹配的muxer和demuxer之后,根据传入的argv参数,初始化每个输入和输出的 AVFormatContext结构,并保存在相应的 output_filesinput_files指针数组中。
av_encode( )函数中, output_filesinput_files是作为函数参数传入后,在其他地方就没有用到了。
 
2.          AVCodecContext
保存 AVCodec指针和与codec相关的数据,如video的width、height,audio的sample rate等。 AVCodecContext中的 codec_typecodec_id二个变量对于encoder/decoder的匹配来说,最为重要。
           enum CodecType codec_type; /* see CODEC_TYPE_xxx */
              enum CodecID codec_id; /* see CODEC_ID_xxx */
如上所示, 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( )函数中调用 AVInputFormatread_header接口来执行flvdec.c中的 flv_read_header( )函数。在 flv_read_header( )函数内,根据文件头中的数据,创建相应的视频或音频 AVStream,并设置 AVStreamAVCodecContext的正确的 codec_type值。 codec_id值是在解码过程中 flv_read_packet( )函数执行时根据每一个packet头中的数据来设置的。
 
3.          AVStream
AVStream结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:
             AVCodecContext *codec; /**< codec context */
                 void *priv_data;
其中 codec指针保存的就是上节所述的encoder或decoder结构。 priv_data指针保存的是和具体编解码流相关的数据,如下代码所示,在ASF的解码过程中, priv_data保存的就是 ASFStream结构的数据。
     AVStream *st;
ASFStream *asf_st;   
     … …
st->priv_data = asf_st;
 
4.          AVInputStream/ AVOutputStream
根据输入和输出流的不同,前述的 AVStream结构都是封装在 AVInputStream和  AVOutputStream结构中,在 av_encode( )函数中使用。
AVInputStream中还保存的有与时间有关的信息。
AVOutputStream中还保存有与音视频同步等相关的信息。
 
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成员中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值