FFMpeg SDK 開發手冊 1

helloxchen發表於2010-11-04

FFMpeg SDK 開發手冊(1)[轉載] - [3DTV]

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

FFMpeg SDK 開發手冊

FFMpeg 中比較重要的函式以及資料結構如下:

1. 資料結構:

(1) AVFormatContext

(2) AVOutputFormat

(3) AVInputFormat

(4) AVCodecContext

(5) AVCodec

(6) AVFrame

(7) AVPacket

(8) AVPicture

(9) AVStream

2. 初始化函式:

(1) av_register_all()

(2) avcodec_open()

(3) avcodec_close()

(4) av_open_input_file()

(5) av_find_input_format()

(6) av_find_stream_info()

(7) av_close_input_file()

3. 音影片編解碼函式:

(1) avcodec_find_decoder()

(2) avcodec_alloc_frame()

(3) avpicture_get_size()

(4) avpicture_fill()

(5) img_convert()

(6) avcodec_alloc_context()

(7) avcodec_decode_video()

(8) av_free_packet()

(9) av_free()

4. 檔案操作:

(1) avnew_steam()

(2) av_read_frame()

(3) av_write_frame()

(4) dump_format()

5. 其他函式:

(1) avpicture_deinterlace()

(2) ImgReSampleContext()

以下就根據,以上資料結構及函式在ffmpeg測試程式碼output_example.c中出現的前後順進行分析。在此之前還是先談一下

ffmpeg的編譯問題。在linux下的編譯比較簡單,這裡不多說了。在windows下的編譯可以參考以下網頁:

%3D1

值得一提的是,在使用編譯後的sdk進行測試時(用到ffmpeg目錄下的output_example.c)編譯過程中可能會有以下兩個問

題:

1 Output_example.c用到了snprintf.h這個標頭檔案。然而這個標頭檔案在win下和linux下有所不同。具體在win

可以用以下方法解決:

2 如果使用vc6,或是vc6的命令列進行編譯,inline可能不認。錯誤會出現在common.h檔案中,可以在common.h中加入

#ifdef _MSC_VAR

#define inline __inline

#endif

交待完畢進入正題。

一.FFMpeg 中的資料結構:

I. AVFormatContext

一般在使用ffmpeg sdk的程式碼中AVFormatContext是一個貫穿始終的資料結構,很多函式都要用到它作為引數。FFmpeg程式碼

中對這個資料結構的註釋是:format I/O context

此結構包含了一個影片流的格式內容。其中存有了AVInputFormator AVOutputFormat同一時間AVFormatContext內只能存

在其中一個),和AVStreamAVPacket這幾個重要的資料結構以及一些其他的相關資訊,比如title,author,copyright等。

還有一些可能在編解碼中會用到的資訊,諸如:duration, file_size, bit_rate等。參考avformat.h標頭檔案。

Useage:

宣告:

AVFormatContext *oc; (1)

初始化: 由於AVFormatConext結構包含許多資訊因此初始化過程是分步完成,而且有些變數如果沒有值可用,也可不初始

化。但是由於一般宣告都是用指標因此一個分配記憶體過程不可少:

oc = av_alloc_format_context(); (2)

結構中的AVInputFormat*(或AVOutputFormat*)是一定要初始化的,基本上這是編譯碼要使用什麼codec的依據所在:

oc->oformat = fmt; or oc->iformat = fmt; (3)

其中AVOutputFormat* fmtAVInputFormat* fmt。(AVInputFormat and AVOutputFormat的初始化在後面介紹。隨後在參

考程式碼output_example.c中有一行:

snprintf(oc-filename, sizeof(oc->filename), “%s”, filename); (4)

還不是十分清楚有什麼作用,估計是先要在輸出檔案中寫一些頭資訊。

在完成以上步驟後,(初始化完畢AVInputFormat*(或AVOutputFormat*)以及AVFormatContext)接下來就是要利用oc初始

化本節開始講到的AVFormatContext中的第二個重要結構。AVStream(假設已經有了宣告AVStream *video_st。參考程式碼中

用了一個函式來完成初始化,當然也可以在主函式中做,傳遞進函式的引數是oc fmt->video_codec(這個在下一節介紹

(29)):

vdeo_st = add_video_stream(oc, fmt->video_codec); (5)

此函式會在後面講到AVStream結構時分析。

AVFormatContext最後的一個設定工作是:

if( av_set_paramters(oc,NULL) < 0){ (6)

//handle error;

}

dump_format(oc, 0, filename, 1); (7)

作用就是看看先前的初始化過程中設定的引數是否符合規範,否則將報錯。

上面講的都是初始化的過程,包括AVFormatContext本身的和利用AVFormatContext初始化其他資料結構的。接下來要講講整

個的編解碼過程。我想先將ouput_example.cmain函式內的編解碼函式框架描述一下。這樣比較清晰,而且編碼者為了結

構清晰,在寫ouput_example.c的過程中也基本上在main函式中只保持AVFormatContextAVStream兩個資料結構

AVOutputFormat其實也在但是包含在AVFormatContext中了)。

// open video codec and allocate the necessary encode buffers

if(video_st)

open_video(oc, video_st); (8)

// write the stream header, if any

av_write_header(oc); (9)

// encode and decode process

for(; ;){

write_video_frame(oc, video_st); (10)

// break condition…here

}

//close codec

if(video_st)

close_video(oc, video_st); (11)

//write the trailer , if any

av_write_trailer(oc); (12)

// free the streams

for(i=0; ib_streams; i++){

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

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

}

//close the ouput file

if(!(fmt->flags & AVFMT_NOFILE)){

url_fclose(&oc->pb); (15)

}

av_free(oc); (16)

透過以上的一串程式碼,就可以清晰地看出AVFormatContex* ocAVStream* video_st是在使用ffmpeg SDK開發時貫穿始終的

兩個資料結構。以下,簡要介紹一下三個標為紅色的函式,他們是參考程式碼output_example.c開發者自行定義的函式。這樣

可以使整個程式碼結構清晰,當然你在使用ffmpeg SDK時也可以在主函式中完成對應的功能。在後面我們會專門針對這三個函

數做分析。

1. open_video(oc, video_st);

此函式主要是對影片編碼器(或解碼器)的初始化過程。初始化的資料結構為AVCodec* codecAVCodecContext* c包括用

到了的SDK函式有:

c = st->codec;

codec = avcodec_find_encoder(c->codec_id); //編碼時,找編碼器 (17)

codec = avcodec_find_decoder(c->codec_id); //解碼時,找解碼器 (18)

AVCodecContex是結構AVStream中的一個資料結構,因此在AVStream初始化後(5)直接復值給c

// internal open video codec

avcodec_open(c,codec); (19)

// allocate video stream buffer

// AVFrame *picture

// uint8_t *video_outbuf

video_outbuf_size=200000;

video_outbuf = av_maloc(video_outbuf_size); (20)

// allocate video frame buffer

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

上述三步比較容易理解,開啟影片編解碼codec、分配輸出流快取大小、分配每一幀影像快取大小。其中AVFrame也是ffmpeg

中主要資料結構之一。這一步(8)是對編解碼器的初始化過程。

2. write_video_frame(AVFormatContext *oc, AVStream *st)

這個函式中做了真正的編解碼工作,其中的函式比較複雜先列出來慢慢分析。

用到的資料結構有AVCodecContext *c, SwsContext *img_convert_ctx。其中SwsContext是用來變換影像格式的。比如

yuv422變到yuv420等,當然也用到函式,見下面列表。

fill_yuv_image(tmp_picture, frame_count, c->width, c->height); (22)

sws_scale(img_convert_ctx, tmp_picture->, tmp_picture->linesize,

0, c->height, picture->data, picture->linesize); (23)

img_convert_ctx = sws_getContxt(c->width, c->height, PIX_FMT_YUV420P, (24)

c->width, c->heigth, c->pix_fmt, sws_flags, NULL, NULL, NULL);

由於參考程式碼中做的是一個編碼。因此,它總是要求編碼器輸入的是yuv檔案,而且是yuv420格式的。就會有了以上一些處

理過程。接下來呼叫編碼器編碼,資料規則化(打包)用到AVPacket,這也是ffmpeg中一個比較不好理解的地方。

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

AVPacket pkt;

av_init_packet(&pkt); (26)

//……handle pkt process, we will analyze later

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

encode就一定會有decode。而且ffmpeg專為解碼而生,但是為什麼在參考程式碼中只用了encoder呢?個人猜想是因為

encode只是用yuv420來編碼,這樣的yuv420生成比較容易,要是用到解碼的化,還要在程式碼中附帶一個其他格式的音影片文

件。在原始碼libavcodec資料夾中有一個apiexample.c的參考程式碼,其中就做了編解碼。有空的化我會分析一下。

3. close_video(AVFormatContext *oc, AVStream *st)

avcodec_close(st->codec);

av_free(picture->data[0]);

av_free(picture);

av_free(video_outbuf);

比較容易理解,不多說了。

以上一大段雖然名為介紹AVFormatContext。但基本上把ouput_example.c的影片編碼部分的框架走了一遍,其一是想說明結

AVFormatContext的重要性,另一方面也是希望對使用FFMpeg SDK開發者有一個大致的框架。

其實,真正的一些編碼函式,記憶體分配函式在SDK中都已經封裝好了,只要搞清楚結構就能用了。而開發者要做的就是一些

初始化的過程,基本上就是針對資料結構1的初始化。

II. AVOutputFormat

雖然簡單(初始化)但是十分重要,他是編解碼器將要使用哪個codec的“指示”。在其成員資料中最重要的就是關於影片

codec的了:enum CodecID video_codec;

AVOutputFormat *fmt;

fmt = guess_format(NULL, filename, NULL); (28)

根據filename來判斷檔案格式,同時也初始化了用什麼編碼器。當然,如果是用AVInputFormat *fmt的化,就是fix用什麼

解碼器。(指定輸出序列->fix編碼器,指定輸入序列->fix解碼器?)

III. AVStream

AVStream作為繼AVFormatContext後第二個貫穿始終的結構是有其理由的。他的成員資料中有AVCodecContext這基本的上是

對所使用的Video Codec的引數進行設定的(包括bit rate、解析度等重要資訊)。同時作為“Stream”,它包含了“流”

這個概念中的一些資料,比如:幀率(r_frame_rate)、基本時間計量單位(time_base)、(需要編解碼的)首幀位置

start_time)、持續時間(duration)、幀數(nb_frames)以及一些ip資訊。當然後面的這些資訊中有些不是必須要初

始化的,但是AVCodecContex是一定要初始化的,而且就是作為初始化AVStream最重要的一個部分。我們在前面就談到了

AVStream的初始化函式(5),現在來看看他是怎麼做的:

// declaration

AVStream *video_st;

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

static AVStream *add_video_stream(AVFormatContex *oc, int codec_id){ (29)

AVCodecContext *c; // member of AVStream, which will be initialized here

AVStream *st; // temporary data, will be returned

st = av_new_stream(oc, 0); (30)

c = st->codec;

// 以下基本是針對c的初始化過程。包括位元率、解析度、GOP大小等。

……

// 以下的兩行需要注意一下,特別是使用MP4

if(!strcmp(oc->oformat->name, “mp4”) || !strcmp(oc->oformat->name, “mov”) || !strcmp(oc->oformat->name,

3gp”))

c->flags |= CODEC_FLAG_GLOBAL_HEADER;

// st傳給video_st;

return st;

}

以上程式碼中,有幾點需要注意的。一個是(30)c = st->codec是一定要做的,當然這是程式設計中最基本的問題,(30)是將st

這個AVSteam繫結到AVFormatContext* oc上。後面的c = st->codec是將c繫結到stAVCodecContext上。其二是對c的初始

化過程中,ouput_example.c裡做的是一些基本的配置,當然作為使用者的你還希望對codec加入其他的一些編解碼的條件。

可以參考avcodec.h裡關於AVCodecContext結構的介紹,註釋比較詳細的。

關於AVStream的使用在前面介紹AVFormatContext時已有所涉及,在主函式中三個編解碼函式中(8)(10)(11)中。觀察相

關的程式碼,可以發現主要還是將AVStream中的AVCodecContext提取出來,再從中提取出AVCodec結構如在(8)中:

// open_video(oc, video_st);

// AVFormatContext *oc, AVStream *st

AVCodec *codec;

AVCodecContext *c;

c = st->codec;

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

// open the codec

avcodec_open(c, codec); (32)

同樣,我們可以看到在(10)(write_video_frame())AVFrame也是做為傳遞AVCodecContext結構的載體而存在。(11

(close_video())比較簡單,不熬述。

[@more@]

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

相關文章