FFmpeg庫的基本組成
- libavcodec: 提供各種音頻,視頻,字幕等編碼和解碼功能。
- libavformat: 用于音視頻封裝格式的生成和解析, 包括獲取解碼所需信息以生成解碼上下文結(jié)構(gòu)和讀取音視頻幀等功能。
- libavutil: 包括了hash器,解碼器和各類工具函數(shù)。
- libavfilter: 音視頻濾鏡庫,提供了音頻特效和視頻特效的處理。
- libavdevice: 提供了訪問捕獲設(shè)備和回放設(shè)備的接口。
- libswresample: 用于混音和音頻重采樣,對(duì)數(shù)字音頻進(jìn)行聲道數(shù),數(shù)據(jù)格式 采樣率等多種基本信息的轉(zhuǎn)換。
- libswscale: 用于視頻場(chǎng)景比例縮放、色彩映射轉(zhuǎn)換。比如YUV轉(zhuǎn)換為RGB的數(shù)據(jù)
- libpostproc:用于后期效果處理,當(dāng)使用avfilter的時(shí)候需要打開該模塊的開關(guān),因?yàn)樗鼤?huì)使用該模塊的一些移除函數(shù)。
ffmpeg中的結(jié)構(gòu)體
結(jié)構(gòu)體很多,最關(guān)鍵的可以分為以下幾類
解協(xié)議(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLcontext主要用于存儲(chǔ)音視頻使用的協(xié)議類型以及狀態(tài)。URLProtocol存儲(chǔ)輸入音視頻使用的封裝格式。每一種協(xié)議都對(duì)應(yīng)一個(gè)URLProtocol結(jié)構(gòu)。
解封裝(flv、avi、rmvb、mp4)
AVFormatContext主要存儲(chǔ)音視頻封裝格式中包含的信息。
AVInputFormat存儲(chǔ)輸入音視頻使用的封裝格式。每種音視頻的封裝格式都對(duì)應(yīng)一個(gè)AVInputFormat結(jié)構(gòu)。
解碼(h264,mpeg2,aac,mp3)
每個(gè)AVStream存儲(chǔ)一個(gè)視頻流/音頻流的相關(guān)數(shù)據(jù),每個(gè)AVStream對(duì)應(yīng)一個(gè)AVCodecContext,存儲(chǔ)該視頻流/音頻流使用解碼方式的相關(guān)數(shù)據(jù);每個(gè)AVcodecContext對(duì)應(yīng)一個(gè)AVCodec,包含該音頻流/視頻流對(duì)應(yīng)的解碼器,每種解碼器都對(duì)應(yīng)一個(gè)AVcodec結(jié)構(gòu)。
存數(shù)據(jù)
視頻的每個(gè)結(jié)構(gòu)一般是存一幀,音頻可能有好幾幀。
解碼前數(shù)據(jù)格式:AVPacket;
- 音頻的話一個(gè)AVPacket中可能有好幾個(gè)AVFrame
- 視頻的話一個(gè)AVPacket中只有一個(gè)個(gè)AVFrame
解碼后數(shù)據(jù)格式:AVFrame;

注冊(cè)協(xié)議、格式與編解碼器
使用FFmpeg的API首先要調(diào)用FFmpeg的注冊(cè)協(xié)議,格式與編解碼器的方法。確保所有的格式和編解碼器都被注冊(cè)到了FFmpeg框架中。
av_register_all();
這個(gè)方法里會(huì)還會(huì)調(diào)用 avfilter_register_all 和 avcodec_register_all avformat_register_all
如果需要用到網(wǎng)絡(luò)的操作,也要注冊(cè)網(wǎng)絡(luò)協(xié)議
avformat_network_init();
ffmpeg API
利用FFmpeg解碼的全部過程:
0.注冊(cè)協(xié)議、格式與編解碼器
1.打開媒體文件
2.尋找各個(gè)流,并且打開對(duì)應(yīng)的解碼器
3.初始化解碼后數(shù)據(jù)的結(jié)構(gòu)體
4.讀取流內(nèi)容并且解碼
5.處理解碼后的裸數(shù)據(jù)
6.關(guān)閉所有資源
通用API解析
1.av_register_all
編譯靜態(tài)庫的時(shí)候有一些編譯條件,通過enable,disable進(jìn)行了了一些開關(guān)的控制,其中就包括協(xié)議或者一些編解碼器,上邊提到的注冊(cè)協(xié)議,就是注冊(cè)這里開開的也就是enable的部分。
2.av_find_codec
這里包含了兩部分內(nèi)容,一部分是尋找解碼器,另一部分是尋找編碼器,av_register_all中已經(jīng)把編碼器和解碼器都存放到一個(gè)鏈表中了,澤庫其實(shí)就是從這個(gè)構(gòu)造的鏈表中進(jìn)行遍歷通過Codec的ID或者name進(jìn)行條件匹配,最終返回對(duì)應(yīng)的Codec。
3.avcodec_open2
該函數(shù)是打開編解碼器的函數(shù),無論編碼,還是解碼,都會(huì)用到。輸入?yún)?shù)有三個(gè)
- AVCodecContext
- av_find_codec尋找出來的編解碼器
- 第三個(gè)一般傳NULL
該函數(shù)的具體實(shí)現(xiàn)文件是如何找到的?需要看第一部中如何注冊(cè)的??赡軙?huì)調(diào)用x264或者lame庫中的API。
3.avcodec_close
就是avcodec_open的一個(gè)逆過程,找到對(duì)應(yīng)的實(shí)現(xiàn)文件的close指針?biāo)赶虻暮瘮?shù)。然后調(diào)用對(duì)應(yīng)的第三方庫的API來關(guān)閉對(duì)應(yīng)的編碼庫。
調(diào)用FFmpeg解碼時(shí)用到的函數(shù)
1.avformat_open_input
通過文件路徑判斷文件格式,根據(jù)格式選擇Demuxer(解軌器)。然后對(duì)應(yīng)的關(guān)鍵生命周期的方法,read_header、read_packet、read_seek、read_close都會(huì)使用找到的解軌器中的函數(shù)指針指定的函數(shù)。
其中read_header會(huì)構(gòu)建好AVStream的結(jié)構(gòu)體,以便后續(xù)傳參使用。
2.avformat_find_stream_info
它的作用就是把所有stream的metaData信息填充好。利用選擇好的Demuxer中的read_packet函數(shù)讀取一段數(shù)據(jù)進(jìn)行解碼。它
3.av_read_frame
該方法讀取出來的數(shù)據(jù)是AVPacket(數(shù)據(jù)包)。這個(gè)函數(shù)的實(shí)現(xiàn)首先會(huì)委托到Demuxer的read_packet方法中去。
對(duì)于音頻流一個(gè)AVPacket可能包含多個(gè)AVFrame,但是對(duì)于視頻流一個(gè)AVPacket只包含一個(gè)AVFrame。該函數(shù)最終就返回一個(gè)AVPacket結(jié)構(gòu)體
4.avcodec_decode
該方法包含兩部分內(nèi)容
- 解碼音頻
- 解碼視頻
解碼其實(shí)是委托給對(duì)應(yīng)的解碼器來實(shí)施的,在打開解碼器的時(shí)候就找到了對(duì)應(yīng)的解碼器的實(shí)現(xiàn)。解碼器中會(huì)有對(duì)應(yīng)的生命周期的函數(shù)的實(shí)現(xiàn),最重要的就是 - init 打開解碼器
- decoder 解碼
- close 關(guān)閉解碼器
5.avformat_close_input
該函數(shù)負(fù)責(zé)釋放對(duì)應(yīng)的資源,首先就會(huì)調(diào)用對(duì)應(yīng)的Demuxer的read_close方法,然后釋放掉AVFormatContext,最后關(guān)閉文件或者遠(yuǎn)程連接。
調(diào)用FFmpeg編碼時(shí)用到的函數(shù)
1.avformat_alloc_output_context2
該函數(shù)內(nèi)部會(huì)調(diào)用avformat_alloc_context來分配一個(gè)AVFormatContext結(jié)構(gòu)體,然后找到注冊(cè)的Muxer和Demuxer,也就是封裝格式部分,去找到對(duì)應(yīng)的封裝格式。如果找不到返回對(duì)應(yīng)的錯(cuò)誤提示。
2.avio_open2
首先調(diào)用函數(shù)ffurl_open,構(gòu)造出URLContext結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中包含了URLProtocol(需要去第一步register_protocol中已經(jīng)注冊(cè)的協(xié)議鏈表中尋找);接著會(huì)調(diào)用avio_alloc_context方法,分配出AVIOContext結(jié)構(gòu)體,并將上一步構(gòu)造出來的URLProtocol傳遞進(jìn)來;然后把上一步分配出來AVIOContext結(jié)構(gòu)體賦值給AVFormatContext的屬性,其實(shí)這就是圖3-2表示的結(jié)構(gòu)了。而該過程恰好是上面所分析的avformat_open_input函數(shù)的實(shí)現(xiàn)過程的一個(gè)逆過程。之前就提到過,編碼過程和解碼過程從邏輯上來講本來就是一個(gè)逆過程,所以在FFmpeg的實(shí)現(xiàn)過程中它們也是一個(gè)逆過程。
后面的步驟也都是解碼的一個(gè)逆過程,解碼過程中的av_f ind_stream_info對(duì)應(yīng)到這里就是avformat_new_stream和avformat_write_header。avformat_new_stream函數(shù)會(huì)將音頻流或者視頻流的信息填充好,分配出AVStream結(jié)構(gòu)體,在音頻流中分配聲道、采樣率、表示格式、編碼器等信息,在視頻流中分配寬、高、幀率、表示格式、編碼器等信息;avformat_write_header函數(shù)與解碼過程中的read_header恰好是一個(gè)逆過程,因此這里將不再介紹。接下來就是編碼的階段了,開發(fā)者需要將手動(dòng)封裝好的AVFrame結(jié)構(gòu)體,作為avcodec_encode_video方法的輸入,將其編碼成為AVPacket,然后調(diào)用av_write_frame方法輸出到媒體文件中。而av_write_frame方法會(huì)將編碼后的AVPacket結(jié)構(gòu)體作為Muxer中的write_packet生命周期方法的輸入,write_packet函數(shù)會(huì)加上自己封裝格式的頭信息,然后調(diào)用協(xié)議層寫到本地文件或者網(wǎng)絡(luò)服務(wù)器上。最后一步就是av_write_trailer,該函數(shù)有一個(gè)非常大的坑,如果沒有執(zhí)行write_header操作,就直接執(zhí)行write_trailer操作,程序會(huì)直接崩潰(即Crash掉),所以必須保證這兩個(gè)函數(shù)成對(duì)出現(xiàn)。write_trailer函數(shù)的實(shí)現(xiàn)會(huì)把沒有輸出的AVPacket全部丟給協(xié)議層去做輸出,然后會(huì)調(diào)用Muxer的write_trailer生命周期方法,對(duì)于不同的格式寫出的尾部也不盡相同,這里不再逐一介紹。