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, intflags);

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

#ifdefCONFIG_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結構的定義如下:

typedefstruct 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 choicefirst"

* 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的時候會用到。

AVInputFormatflv_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,所以有以下三種註冊方式:

#defineREGISTER_ENCODER(X,x) \

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

#defineREGISTER_DECODER(X,x) \

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

#defineREGISTER_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中:

staticAVInputFormat *file_iformat;

staticAVOutputFormat *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是否和傳入的文件內容匹配的判斷。其調用順序如下:

voidparse_options(int argc, char **argv, const OptionDef *options)

static voidopt_input_file(const char *filename)

static voidopt_input_file(const char *filename)

int av_open_input_file(…… )

AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)

staticAVInputFormat *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裏的輸出文件後綴名來進行的。

voidparse_options(int argc, char **argv, const OptionDef *options)

voidparse_arg_file(const char *filename)

static voidopt_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格式轉換過程中實現輸入和輸出功能、保存相關數據的主要結構。每一個輸入和輸出文件,都在如下定義的指針數組全局變量中有對應的實體。

staticAVFormatContext *output_files[MAX_FILES];

staticAVFormatContext *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_type,codec_id二個變量對於encoder/decoder的匹配來說,最爲重要。

enum CodecType codec_type; /* seeCODEC_TYPE_xxx */

enumCodecID 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數據。

typedefstruct 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)(structAVFormatContext *, AVPacket *pkt);接口,讀取輸入文件的一幀數據保存在當前輸入AVFormatContextAVPacket成員中。

av_encode函數主要流程

av_encode( )函數是FFMpeg中最重要的函數,編解碼和輸出等大部分功能都在此函數內完成,因此有必要詳細描述一下這個函數的主要流程。

1.input streamsinitializing

2.outputstreams initializing

3.encoders anddecoders initializing

4.set meta datainformation from input file if required.

5.write outputfiles header

6.loop ofhandling each frame

a.read frame frominput file:

b.decode framedata

c.encode newframe data

d.write newframe to output file

7.write outputfiles trailer

8.close eachencoder and decoder

·IV. AVCodecContext

此結構在Ffmpeg SDK中的註釋是:main external api structure其重要性可見一斑。而且在avcodec它的定義處,對其每個

成員變量,都給出了十分詳細的介紹。應該說AVCodecContext的初始化是Codec使用中最重要的一環。雖然在前面的

AVStream中已經有所提及,但是這裏還是要在說一遍。AVCodecContext作爲Avstream的一個成員結構,必須要在Avstream初

始化後(30)再對其初始化(AVStream的初始化用到AVFormatContex)。雖然成員變量比較多,但是這裏只說一下在

output_example.c中用到了,其他的請查閱avcodec.h文件中介紹。

// static AVStream*add_video_stream(AVFormatContext *oc, int codec_id)

AVCodecContext *c;

st = av_new_stream(oc, 0);

c = st->codec;

c->codec_id = codec_id;

c->codec_type = CODEC_TYPE_VIDEO;

c->bit_rate = 400000; // 400kbits/s

c->width = 352;

c->height = 288; // CIF

// 幀率做分母,秒做分子,那麼time_base也就是一幀所用時間。(時間基!)

c->time_base.den =STREAM_FRAME_RATE;

c->time_base.num = 1;

c->gop_size =12;

// here define:

// #define STREAM_PIX_FMT PIX_FMT_YUV420P

// pixel format, see PIX_FMT_xxx

// -encoding: set by user.

// -decoding: set by lavc.

c->pix_fmt = STREAM_PIX_FMT;

除了以上列出了的。還有諸如指定運動估計算法的: me_method。量化參數、最大b幀數:max_b_frames。碼率控制的參數、

差錯掩蓋error_concealment、模式判斷模式:mb_decision (這個參數蠻有意思的,可以看看avcodec.h 1566行)、

Lagrange multipler參數:lmin & lmax 和 宏塊級Lagrange multipler參數:mb_lmin & mb_lmax、constant

quantization parameter rate controlmethod: cqp等。

值得一提的是在AVCodecContext中有兩個成員數據結構:AVCodec、AVFrame。AVCodec記錄了所要使用的Codec信息並且含有

5個函數:init、encoder、close、decode、flush來完成編解碼工作(參見avcode.h 2072行)。AVFrame中主要是包含了編

碼後的幀信息,包括本幀是否是key frame、*data[4]定義的Y、Cb和Cr信息等,隨後詳細介紹。

初始化後,可以說AVCodecContext在(8)&(10)中大顯身手。先在(8)open_video()中初始化AVCodec *codec以及AVFrame*

picture:

// AVCodecContext *c;

codec = avcodec_find_encoder(c->codec_id);

……

picture =alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

後在writer_video_frame(AVFormatContext *oc, AVStream *st)中作爲一個編解碼器的主要參數被利用:

AVCodecContext *c;

c = st->codec;

……

out_size = avcodec_encode_video(c,video_outbuf, video_outbuf_size, picture);

V.AVCodec

結構AVCodec中成員變量和成員函數比較少,但是很重要。他包含了CodecID,也就是用哪個Codec、

像素格式信息。還有前面提到過的5個函數(init、encode、close、decoder、flush)。順便提一下,雖然在參考代碼

output_example.c中的編碼函數用的是avcodec_encode_video(),我懷疑在其中就是調用了AVCodec的encode函數,他們

傳遞的參數和返回值都是一致的,當然還沒有得到確認,有興趣可以看看ffmpeg源代碼。在參考代碼中,AVCodec的初始化

後的使用都是依附於AVCodecContex,前者是後者的成員。在AVCodecContext初始化後(add_video_stream()),AVCodec也

就能很好的初始化了:

//初始化

codec =avcodec_find_encoder(c->codec_id); (33)

//打開Codec

avcodec_open(c, codec) (34)

VI. AVFrame

AVFrame是個很有意思的結構,它本身是這樣定義的:

typedef struct AVFrame {

FF_COMMON_FRAME

}AVFrame;

其中,FF_COMMON_FRAME是以一個宏出現的。由於在編解碼過程中AVFrame中的數據是要經常存取的。爲了加速,要採取這樣

的代碼手段。

AVFrame是作爲一個描述“原始圖像”(也就是YUV或是RGB…還有其他的嗎?)的結構,他的頭兩個成員數據,uint8_t

*data[4],int linesize[4],第一個存放的是Y、Cb、Cr(yuv格式),linesize是啥?由這兩個數據還能提取處另外一個

數據結構:

typedef struct AVPicture {

uint8_t *data[4];

int linesize[4]; // number of bytesper line

}AVPicture ;

此外,AVFrame還含有其他一些成員數據,比如。是否key_frame、已編碼圖像書coded_picture_number、是否作爲參考幀

reference、宏塊類型 *mb_type等等(avcodec.h 446行)。

AVFrame的初始化並沒有他結構上看上去的那麼簡單。由於AVFrame還有一個承載圖像數據的任務(data[4])因此,對他分

配內存應該要小心完成。output_example.c中提供了alloc_picute()來完成這項工作。參考代碼中定義了兩個全局變量:

AVFrame *picture,*tmp_picture。(如果使用yuv420格式的那麼只用到前一個數據picture就行了,將圖像信息放入

picture中。如果是其他格式,那麼先要將yuv420格式初始化後放到tmp_picture中在轉到需求格式放入picture中。)在

open_video()打開編解碼器後初始化AVFrame:

picture =alloc_picture(c->pix_fmt, c->width, c->height);

tmp_picture =alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

static AVFrame *alloc_picture(intpix_fmt, int width, int height){

AVFrame *picture;

uint8_t *picture_buf; // think aboutwhy use uint8_t? a byte!

picture = avcodec_alloc_frame();(35)

if(!picture)

return NULL;

size = avpicture_get_size(pix_fmt,width, height); (36)

picture_buf = av_malloc(size); (37)

if(!picture_buf){

av_free(picture); (38)

return NULL;

}

avpicture_fill ( (AVPicture*)picture, picture_buf, pix_fmt, width, height); (39)

return picture;

}

從以上代碼可以看出,完成對一個AVFrame的初始化(其實也就是內存分配),基本上是有這樣一個固定模式的。至於(35)

(39)分別完成了那些工作,以及爲什麼有這樣兩步,還沒有搞清楚,需要看原代碼。我的猜測是(35)對AVFrame做了基本的

內存分配,保留了對可以提取出AVPicture的前兩個數據的內存分配到(39)來完成。

說到這裏,我們觀察到在(39)中有一個(AVPicture *)picture,AVPicture這個結構也很有用。基本上他的大小也就是要在

網絡上傳輸的包大小,我們在後面可以看到AVPacket跟AVPicture有密切的關係。

VII.AVPicture

AVPicture在參考代碼中沒有自己本身的申明和初始化過程。出現了的兩次都是作爲強制類型轉換由AVFrame中提取出來的:

// open_video() 中

avpicture_fill((AVPicture *)picture,picture_buf, pix_fmt, width, height); (40)

//write_video_frame 中

// AVPacket pkt;

if(oc->oformat->flags &AVFMT_RAWPICTURE){

……

pkt.size = sizeof(AVPicture); (41)

}

在(40)中,實際上是對AVFrame的data[4]、linesize[4]分配內存。由於這兩個數據大小如何分配確實需要有pix_fmt、

width、height來確定。如果輸出文件格式就是RAW 圖片(如YUV和RGB),AVPacket作爲將編碼後數據寫入文件的基本數據

單元,他的單元大小、數據都是由AVPacket來的。

總結起來就是,AVPicture的存在有以下原因,AVPicture將Picture的概念從Frame中提取出來,就只由Picture(圖片)本

身的信息,亮度、色度和行大小。而Frame還有如是否是key frame之類的信息。這樣的類似“分級”是整個概念更加清晰。

VIII.AVPacket

AVPacket的存在是作爲寫入文件的基本單元而存在的。我們可能會認爲直接把編碼後的比特流寫入文件不就可以了,爲什麼

還要麻煩設置一個AVPacket結構。在我看來這樣的編碼設置是十分有必要的,特別是在做視頻實時傳輸,同步、邊界問題可

以通過AVPacket來解決。AVPacket的成員數據有兩個時間戳、數據data(通常是編碼後數據)、大小size等等(參見

avformat.h 48行)。講AVPacket的用法就不得不提到編解碼函數,因爲AVPacket的好些信息只有在編解碼後才能的知。在

參考代碼中(ouput_example.c 從362到394行),做的一個判斷分支。如果輸出文件格式是RAW圖像(即YUV或RGB)那麼就

沒有編碼函數,直接寫入文件(因爲程序本身生成一個YUV文件),這裏的代碼雖然在此看來沒什麼價值,但是如果是解碼

函數解出yuv文件(或rgb)那麼基本的寫文件操作就是這樣的:

if(oc->oformat->flags &AVFMT_RAWPICTURE) {

AVPacket pkt; // 這裏沒有用指針!

av_init_packet(&pkt);

pkt.flags |= PKT_FLAG_KEY // rawpicture 中,每幀都是key frame?

pkt.stream_index = st->index;

pkt.data = (uint8_t *)picture;

pkt.size = sizeof(AVPicture);

ret = av_write_frame(oc, &pkt);

}

輸出非raw picture,編碼後:

else{

// video_outbuf &video_outbuf_size在open_video() 中初始化

out_size = avcodec_encode_video(c,video_outbuf, video_outbuf_size, picture); (42)

if(out_size > 0){

AVPacket pkt;

av_init_packet(&pkt); (43)

pkt.pts=av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);(44)

if(c->coded_frame->key_frame)

pkt.flags |= PKT_FLAG_KEY;

pkt.stream_index= st->index;

pkt.data= video_outbuf;

pkt.size= out_size;

/* write the compressed frame in themedia file */

ret = av_write_frame(oc, &pkt);(45)

} else {

ret = 0;

}

if (ret != 0) {

fprintf(stderr, "Error whilewriting video frame\n");

exit(1);

}

其中video_outbuf和video_outbuf_size在open_video()裏的初始化是這樣的:

video_outbuf = NULL;

// 輸出不是raw picture,而確實用到編碼codec

if( !(oc->oformat->flags &AVFMT_RAWPICTURE)){

video_outbuf_size = 200000;

video_outbuf =av_malloc(video_outbuf_size);

}

(43)是AVPacket結構的初始化函數。(44)比較難理解,而且爲什麼會有這樣的一些時間戳我也沒有搞明白。其他的AVPacket

成員數據的賦值比較容易理解,要注意的是video_outbuf和video_outbuf_size的初始化問題,由於在參考代碼中初始化和

使用不在同一函數中,所以比較容易忽視。(45)是寫文件函數,AVFormatContext* oc中含有文件名等信息,返回值ret因該

是一共寫了多少數據信息,如果返回0則說明寫失敗。(42)和(45)作爲比較重要的SDK函數,後面還會介紹的。.

IX. Conclusion

以上分析了FFMpeg中比較重要的數據結構。下面的這個生成關係理一下思路:(->表示 派生出)

AVFormatContext->AVStream->AVCodecContext->AVCodec

|

AVOutputFormat or AVInputFormat

AVFrame->AVPicture….>AVPacket

二.FFMpeg 中的函數:

在前一部分的分析中我們已經看到FFMpeg SDK提供了許多初始化函數和編碼函數。我們要做的就是對主要數據結構正確的初

始化,以及正確使用相應的編解碼函數以及讀寫(I/O)操作函數。作爲一個整體化的代碼SDK,FFMpeg有一些他自己的標準

化使用過程。比如函數av_register_all(); 就是一個最開始就該調用的“註冊函數”,他初始化了libavcodec,“註冊”

了所有的的codec和視頻文件格式(format)。下面,我沿着參考代碼(ouput_example.c)的脈絡,介紹一下相關函數。

/******************************************************************

main()

******************************************************************/

1. av_register_all ();

usage: initialize ibavcoded, andregister all codecs and formats

每個使用FFMpeg SDK的工程都必須調用的函數。進行codec和format的註冊,然後才能使用。聲明在allformats.c中,都是

宏有興趣看看。

2. AVOutputFormat guess_format(constchar *short_name, const char *filename, const char *mime_type)

usage: 通過文件後綴名,猜測文件格式,其實也就是要判斷使用什麼編碼器(or解碼器)。

AVOutputFormat *fmt;

fmt = guess_format(NULL, filename,NULL);

3. AVFormatContext*av_alloc_format_context(void)

usage: allocate the output mediacontext.實際是初始化AVFormatContext的成員數據AVClass:

AVFormatContext *ic;

ic->av_class =&av_format_context_class;

//where

// format_to_name, options arepointer to function

static const AVClassav_format_context_class = {“AVFormatContext”, format_to_name, options};

4. static AVStream*add_video_stream(AVFormatContext *ox, int codec_id);

AVStream *video_st;

video_st = add_video_stream(oc,fmt->video_codec);

5. intav_set_parameters(AVFormatContext *s, AVFormatParameters *ap)

usage: set the output parameters(must be done even if no parameters).

AVFormatContext *oc;

// if failed, return integer smallerthan zero

av_set_parameters(oc, NULL);

6. void dump_format(AVFormatContext*ic, int index, const char *url, int is_output);

usage: 這一步會用有效的信息把 AVFormatContext 的流域(streams field)填滿。作爲一個可調試的診斷,我們會將這

些信息全盤輸出到標準錯誤輸出中,不過你在一個應用程序的產品中並不用這麼做:

dump_format(oc, 0, filename, 1); // 也就是指明AVFormatContext中的事AVOutputFormat,還是 //AVInputFormat

7. static voidopen_video(AVFormatContext *oc, AVStream *st)

open_video(oc, video_st);

8. intav_write_header(AVFormatContext *s)

usage: allocate the stream privatedata and writer the stream header to an output media file. param s media

file

handle, return 0 if OK, AVERROR_xxxif error.

write the stream header, if any

av_write_header(oc);

9. static voidwrite_video_frame(AVFormatContext *oc, AVStream *st)

write_video_frame(oc, video_st);

10. static voidclose_video(AVFormatContext *oc, AVStream *st)

// close each codec

close_video(oc, video_st);

11. intav_write_trailer(AVFormatContext *s)

usage: write the trailer, if any.Write the stream trailer to an output media file and free the file private

data.

av_write_trailer(oc);

12. void av_freep(void *arg)

usage: free the streams. Freesmemory and sets the pointer to NULL. arg pointer to the pointer which should

be freed .

av_freep(&oc->streams[i]->codec);

av_freeep(&oc->streams[s]);

13. int url_fclose(ByteIOContext*s);

usage: close the output file

url_fclose(&oc->pb);

·14. void av_free(void *ptr)

usage: free the stream. Free memorywhich has been allocated with av_malloc(z)() or av_realloc().

av_free(oc);

/******************************************************************

******************************************************************

add_video_stream()

AVCodecContext *c

AVStream *st

******************************************************************/

******************************************************************

1.AVStream*av_new_stream(AVFormatContext *s, int id)

usage: add a new stream to a mediafile. s: media file handle, id: file format dependent stream id

st = av_new_stream(oc, 0);

/******************************************************************

******************************************************************

open_video()

AVCodecContext *c

AVCodec *codec

AVFrame *picture, *tmp_picture

uint8_t *video_output

int frame_count, video_outbuf_size;

******************************************************************

******************************************************************/

1 AVCodec *avcodec_find_encoder(enumCodecID id)

usage: find the codec of encoder byCodecID. 在前面main中的guess_format()就已經開始爲此準備了。

codec =avcodec_find_encoder(c->codec_id);

2 int avcodec_open(AVCodecContext*avctx, AVCodec *codec);

usage: opens / inits the AVCodecContext.打開失敗的話,返回值小於零。

avcodec_open(c, codec);

3 void *av_malloc(unsigned in size);

usage: you can redefine av_mallocand av_free in your project to use your memory allocator. You do not need

to suppress this file because thelinker will do it automatically

Memory allocation of size byte withalignment suitable for all memory accesses (including vectors if

available on the CPU). av_malloc(0)must return a non NULL pointer.

video_outbuf_size = 200000;

video_outbuf =avmalloc(video_outbuf_size);

4 static AVFrame *alloc_picture(intpix_fmt, int width, int height)

picture =alloc_picture(c->pix_fmt, c->width, c->height);

/******************************************************************

******************************************************************

******************************************************************

alloc_picture()

AVFrame *picture

uint8_t *picture_buf

int size

******************************************************************

******************************************************************/

******************************************************************

1. avcodec_alloc_frame()

usage: initialize AVFrame* picture

picture = avcodec_alloc_frame()

2. int avpicture_get_size(intpix_fmt, int width, int height)

usage: 根據像素格式和視頻分辨率獲得picture存儲大小。

size = avpicture_get_size(pix_fmt,width, height);

picture_buf = av_malloc(size)

3. int avpicture_fill(AVPicture*picture, uint8_t *ptr, int pix_fmt, int width, int height)

usage: Picture field are filled with‘ptr’ addresses, also return size。用ptr中的內容根據文件格式(YUV…)

和分辨率填充picture。這裏由於是在初始化階段,所以填充的可能全是零。

avpicture_fill((AVPicture*)picture,picture_buf, pix_fmt, width, height);

/******************************************************************

******************************************************************

write_video_frame()

int out_size, ret;

AVCodecContext *c;

static struct SwsContext*img_convert_ctx

******************************************************************

******************************************************************/

1 struct SwsContext*sws_getContext(int srcW, ……)

usage: 轉變raw picture格式的獲取context函數,比如下面的代碼就是將其他格式的(如yuv422)轉爲yuv420,就要將

context 保存在img_convert_ctx中,然後再利用後面的介紹函數做轉化。

img_convert_ctx =sws_getContext(c->width, c->height,

PIX_FMT_YUV420P,

c->width, c->height,

c->pix_fmt,

sws_flags, NULL, NULL, NULL);

2 int sws_scale(struct SwsContext*ctx, uing8_t* src[], int srcStride[], int srcSliceY, int srcSliceH,

uint8_t* dst[], int dstStride[]);

usage: 根據SwsContext保存的目標文件context將src(source)轉爲dst(destination)。

sws_scale(img_convert_ctx,tmp_picture->data, tmp_picture->linesize, 0, c->height,picture->data, picture-

>linesize);

4. intavcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size, constAVFrame *pict);

usage: 根據AVCodecContext將pict編碼到buf中,buf_size是buf的大小。

out_size = avcodec_encode_video(c,video_outbuf, video_outbuf_size, picture);

5 static inline voidav_init_packet(AVPacket *pkt)

usage: initialize optional fields ofa packet.初始化AVPacket。

AVPacket pkt;

av_init_packet(&pkt)

6 int64_t av_rescale_q(int64_t a,AVRational bq, AVRational cq)

usage: 校準時間基(maybe)

pkt.pts =av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);

7 int av_write_frame(AVFormatContext*s, AVPacket *pkt)

usage: write a packet to an outputmedia file . pkt: the packet, which contains the stream_index,

buf/buf_size, dts/pts, …if errorreturn<0, if OK =0, if end of stream wanted =1

ret = av_write_frame(oc, &pke);

/******************************************************************

******************************************************************

static voidclose_video(AVFormatContext *oc, AVStream *st)

{

avcodec_close(st->codec);

av_free(picture->data[0]);

av_free(picture);

if (tmp_picture) {

av_free(tmp_picture->data[0]);

av_free(tmp_picture);

}

av_free(video_outbuf);

}

/******************************************************************

******************************************************************

講初始化過的,特別是分配過內存的,都要釋放。

注:標定紅色的函數爲需要編程人員自己按照實際應用情況編寫的,藍色的是FFMpegSDK中定義的函數。我們會分析函數的

作用和調用方法。

由於時間關係,寫得很亂。希望沒有給你帶來太大困擾,這份不成熟的文檔能對你有一點點地幫助。chinavideo版上有很多

大牛,我想做拋磚引玉者,希望以後能有更好的文檔出現。我也是ffmpeg的初學者,如果大家有什麼問題也希望能在版上一

起討論。

 

 

轉自:http://weimou66.iteye.com/blog/1417162

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章