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)(str t AVFormatContext *, AVFormatParameters *ap);
int (*read_packet)(str t AVFormatContext *, AVPacket *pkt);
int (*read_close)(str t AVFormatContext *);
int (*read_seek)(str t AVFormatContext *, int stream_index, int64_t timestamp, int flags);
         muxer開放的接口有:
                   int (*write_header)(str t AVFormatContext *);
                   int (*write_packet)(str t AVFormatContext *, AVPacket *pkt);
                int (*write_trailer)(str t AVFormatContext *);
 
encoder/decoder的接口都是同樣的,只不過兩者分別只實現encoder和decoder函數:
int (*init)(AVCodecContext *);
               int (*encode)(AVCodecContext *, uint8_t *b , int b _size, void *data);
               int (*close)(AVCodecContext *);
                 int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, uint8_t *b , int b _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_a io_codec_ids, 0},
}
AVOutputFormat結構的定義以下:
typedef str t 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 a io_codec; /**< default a io codec */
    enum CodecID video_codec; /**< default video codec */
    int (*write_header)(str t AVFormatContext *);
    int (*write_packet)(str t AVFormatContext *, AVPacket *pkt);
    int (*write_trailer)(str t 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)(str t AVFormatContext *, AVFormatParameters *);
    int (*interleave_packet)(str t 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 str t AVCodecTag **codec_tag;
    /* private fields */
    str t AVOutputFormat *next;
} AVOutputFormat;
AVOutputFormat結構的定義可知, flv_muxer變量初始化的第1、第二個成員分別爲該muxer的名稱與長名稱,第3、第四個成員爲所對應MIMIE Type和後綴名,第五個成員是所對應的私有結構的大小,第6、第七個成員爲所對應的音頻編碼和視頻編碼類型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",
    .val = 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的匹配是調用 g ss_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 *g ss_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->str t AVCodec *codecAVOutputStream->AVStream *st->AVCodecContext *codec->str t AVCodec *codec變量中。
 
 
 
 
其餘主要數據結構
1.         AVFormatContext
AVFormatContext是FFMpeg格式轉換過程當中實現輸入和輸出功能、保存相關數據的主要結構。每個輸入和輸出文件,都在以下定義的指針數組全局變量中有對應的實體。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
對於輸入和輸出,由於共用的是同一個結構體,因此須要分別對該結構中以下定義的 iformatoformat成員賦值。
    str t AVInputFormat *iformat;
    str t 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,a io的sample rate等。 AVCodecContext中的 codec_type codec_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_A IO等媒體類型,
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 str t 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 (*destr t)(str t AVPacket *);
    void *priv;
    int64_t pos;           ///< byte position in stream, -1 if unknown
} AVPacket;
 
av_encode( )函數中,調用 AVInputFormat(*read_packet)(str t AVFormatContext *, AVPacket *pkt);接口,讀取輸入文件的一幀數據保存在當前輸入 AVFormatContextAVPacket成員中。
 
av_encode函數主要流程
av_encode( )函數是FFMpeg中最重要的函數,編解 碼和輸出等大部分功能都在此函數內完成,所以有必要詳細描述一下這個函數的主要流程。
1.          input streams initializing
2.          output streams initializing
3.          encoders and decoders initializing
4.          set meta data information from input file if required.
5.          write output files header
6.          loop of handling each frame
a.        read frame from input file:
b.        decode frame data
c.        encode new frame data
d.        write new frame to output file
7.          write output files trailer
8. close each encoder and decoder
相關文章
相關標籤/搜索