FFMpeg框架程式碼閱讀 - [3DTV]

helloxchen發表於2010-11-04

FFMpeg框架程式碼閱讀 - [3DTV]

:轉載時請以超連結形式標明文章原始出處和作者資訊及本宣告
http://leezen.blogbus.com/logs/15774588.html

簡介
FFmpeg是一個集錄制、轉換、音/影片編碼解碼功能為一體的完整的開源解決方案。FFmpeg的開發是基於Linux作業系統,但是可以在大多數作業系統中編譯和使用。FFmpeg支援MPEGDivXMPEG4AC3DVFLV40多種編碼,AVIMPEGOGGMatroskaASF90多種解碼.
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的初始化。
在libavformatallformats.c檔案的av_register_all(void)函式中,透過執行
REGISTER_MUXDEMUX(FLV, flv);
將支援flv 格式的flv_muxerflv_demuxer變數分別註冊到全域性變數first_oformatfirst_iformat連結串列的最後位置。
其中flv_muxer在libavformatflvenc.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在libavformatflvdec.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)函式中透過執行libavcodecallcodecs.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變數就分別是在libavcodecmpegvideo.c和libavcodech263de.c中建立的。
當前muxer/demuxer的匹配
在FFmpeg的檔案轉換過程中,首先要做的就是根據傳入檔案和傳出檔案的字尾名匹配合適的demuxer和muxer。
匹配上的demuxer和muxer都儲存在如下所示,定義在ffmpeg.c裡的全域性變數file_iformatfile_oformat中:
static AVInputFormat *file_iformat;
static AVOutputFormat *file_oformat;
1. demuxer匹配
在libavformatutils.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( )函式里完成的。
在libavcodecutils.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結構都是封裝在AVInputStreamAVOutputStream結構中,在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成員中。
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
[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1040906/,如需轉載,請註明出處,否則將追究法律責任。

相關文章