FFmpeg名稱中的mpeg來(lái)自視頻編碼標(biāo)準(zhǔn)MPEG,而前綴FF是Fast Forward的首字母縮寫。
目錄
一 FFmpeg的主體結(jié)構(gòu)
二 FFmpeg命令行工具的使用
三 FFmpeg API的介紹與使用
一 FFmpeg的主體結(jié)構(gòu)

默認(rèn)的編譯會(huì)生成4個(gè)可執(zhí)行文件和8個(gè)靜態(tài)庫(kù)??蓤?zhí)行文件包括用于轉(zhuǎn)碼、推流、Dump媒體文件的ffmpeg、用于播放媒體文件的ffplay、 用于獲取媒體文件信息的ffprobe,以及作為簡(jiǎn)單流媒體服務(wù)器的ffserver。
8個(gè)靜態(tài)庫(kù)其實(shí)就是FFmpeg的8個(gè)模塊,具體包括如下內(nèi)容。
AVUtil核心工具庫(kù),該模塊是最基礎(chǔ)的模塊之一,下面的許多
其他模塊都會(huì)依賴該庫(kù)做一些基本的音視頻處理操作。AVFormat文件格式和協(xié)議庫(kù),該模塊是最重要的模塊之一,封 裝了Protocol層和Demuxer、Muxer層,使得協(xié)議和格式對(duì)于開(kāi)發(fā)者來(lái)說(shuō)是透明的。AVCodec編解碼庫(kù),該模塊也是最重要的模塊之一,封裝了 Codec層,但是有一些Codec是具備自己的License的,F(xiàn)Fmpeg是不會(huì)默認(rèn)添加像libx264、FDK-AAC、lame等庫(kù)的,但是FFmpeg就像一個(gè)平臺(tái) 一樣,可以將其他的第三方的Codec以插件的方式添加進(jìn)來(lái),然后為開(kāi) 發(fā)者提供統(tǒng)一的接口。AVFilter音視頻濾鏡庫(kù),該模塊提供了包括音頻特效和視頻特效的處理,在使用FFmpeg的API進(jìn)行編解碼的過(guò)程中,直接使用該模塊為音視頻數(shù)據(jù)做特效處理是非常方便同時(shí)也非常高效的一種方式。AVDevice輸入輸出設(shè)備庫(kù),比如,需要編譯出播放聲音或者視頻的工具ffplay,就需要確保該模塊是打開(kāi)的,同時(shí)也需要libSDL的預(yù)先編譯,因?yàn)樵撛O(shè)備模塊播放聲音與播放視頻使用的都是libSDL庫(kù)。SwrRessample該模塊可用于音頻重采樣,可以對(duì)數(shù)字音頻進(jìn)行聲道數(shù)、數(shù)據(jù)格式、采樣率等多種基本信息的轉(zhuǎn)換。SWScale該模塊是將圖像進(jìn)行格式轉(zhuǎn)換的模塊,比如,可以將 YUV的數(shù)據(jù)轉(zhuǎn)換為RGB的數(shù)據(jù)。PostProc該模塊可用于進(jìn)行后期處理,當(dāng)我們使用AVFilter的時(shí)候需要打開(kāi)該模塊的開(kāi)關(guān),因?yàn)镕ilter中會(huì)使用到該模塊的一些基礎(chǔ)函數(shù)。
比如AAC編碼,常見(jiàn)的有兩種封裝格式
- 一種是ADTS格式的流,是AAC定義在MPEG2里面的格式
- 另外一種是封裝在MPEG4里面的格式,這種格式會(huì)在每一幀前面拼接一個(gè)用聲道、采樣率等信息組成的頭。
AAC的bit stream filter常常應(yīng)用在編碼的過(guò)程中。
與音頻的AAC編碼格式相對(duì)應(yīng)的是視頻中的H264編碼,它也有兩種封裝格式
- 一種是 MP4封裝的格式
- 一種是裸的H264格式(一般稱為annexb封裝格式)
FFmpeg中也提供了對(duì)應(yīng)的bit stream filter,稱H264_mp4toannexb,可以將MP4封裝格式的H264數(shù)據(jù)包轉(zhuǎn)換為annexb封裝格式的H264數(shù)據(jù) (其實(shí)就是裸的H264的數(shù)據(jù))包。
H264的bit stream filter常常應(yīng)用于視頻解碼過(guò)程中。
二 FFmpeg命令行工具的使用
ffmpeg是進(jìn)行媒體文件轉(zhuǎn)碼的命令行工具
ffprobe是用于查看媒體 文件頭信息的工具
ffplay則是用于播放媒體文件的工具
2.1 ffprobe
1.首先用ffprobe查看一個(gè)音頻的文件
ffprobe ~/Desktop/32037.mp3
2.輸出格式信息format_name、時(shí)間長(zhǎng)度duration、文件 大小size、比特率bit_rate、流的數(shù)目nb_streams等。
ffprobe -show_format 32037.mp4
3.以JSON格式的形式輸出具體每一個(gè)流最詳細(xì)的信息
ffprobe -print_format json -show_streams 32037.mp4
4.顯示幀信息的命令如下:
ffprobe -show_frames sample.mp4
5.查看包信息的命令如下:
ffprobe -show_packets sample.mp4
2.2 ffplay
ffplay是以FFmpeg框架為基礎(chǔ),外加渲染音視頻 的庫(kù)libSDL來(lái)構(gòu)建的媒體文件播放器。
業(yè)界內(nèi)開(kāi)源的ijkPlayer其實(shí)就是基于ffplay進(jìn)行改造的播放器,當(dāng)然其做了硬件解碼以及很多兼容性的工作。
音視頻同步
在 ffplay中音畫同步的實(shí)現(xiàn)方式其實(shí)有三種。分別是
- 以
音頻為主時(shí)間軸 作為同步源 - 以
視頻為主時(shí)間軸作為同步源 - 以
外部時(shí)鐘為主時(shí)間軸作為同步源
并且在ffplay中默認(rèn)的對(duì)齊方式也是以音頻為基準(zhǔn)進(jìn)行對(duì)齊的。
首先要聲明的是,播放器接收到的視頻幀或者音頻幀,內(nèi)部都會(huì)有時(shí)間戳(PTS時(shí)鐘)來(lái)標(biāo)識(shí)它實(shí)際應(yīng)該在什么時(shí)刻進(jìn)行展示。
實(shí)際的對(duì)齊策略如下:比較視頻當(dāng)前的播放時(shí)間和音頻當(dāng)前的播放時(shí)間
- 如果視頻播放
過(guò)快,則通過(guò)加大延遲或者重復(fù)播放來(lái)降低視頻播放速度; - 如果視頻播
放慢了,則通過(guò)減小延遲或者丟幀來(lái)追趕音頻播放的時(shí)間點(diǎn)。
關(guān)鍵就在于音視頻時(shí)間的比較以及延遲的計(jì)算,當(dāng)然在比較的過(guò)程中會(huì)設(shè) 置一個(gè)閾值(Threshold),若超過(guò)預(yù)設(shè)的閾值就應(yīng)該做調(diào)整(丟幀渲染 或者重復(fù)渲染),這就是整個(gè)對(duì)齊策略。
2.3 ffmpeg
ffmpeg就是強(qiáng)大的媒體文件轉(zhuǎn)換工具。它可以轉(zhuǎn)換任何格式的媒體文件,并且還可以用自己的AudioFilter以及VideoFilter進(jìn)行處理和編輯。
- 從MP4文件中抽取視頻流導(dǎo)出為裸H264數(shù)據(jù)
ffmpeg -i output.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264
- 使用AAC音頻數(shù)據(jù)和H264的視頻生成MP4文件
ffmpeg -i test.aac -i test.h264 -acodec copy -bsf:a aac_adtstoasc -vcodec copy -f mp4 output.mp4
- 從WAV音頻文件中導(dǎo)出PCM裸數(shù)據(jù)
ffmpeg -i input.wav -acodec pcm_s16le -f s16le output.pcm
- 將兩路聲音進(jìn)行合并,比如要給一段聲音加上背景音樂(lè)
ffmpeg -i vocal.wav -i accompany.wav -filter_complex
amix=inputs=2:duration=shortest output.wav
- 為視頻增加水印效果
ffmpeg -i input.mp4 -i changba_icon.png -filter_complex
'[0:v][1:v]overlay=main_w-overlay_w-10:10:1[out]' -map '[out]' output.mp4
- 將一個(gè)YUV格式表示的數(shù)據(jù)轉(zhuǎn)換為JPEG格式的圖片
ffmpeg -f rawvideo -pix_fmt yuv420p -s 480*480 -i texture.yuv -f image2-vcodec mjpeg output.jpg
三 FFmpeg API的介紹與使用
3.1 術(shù)語(yǔ)
容器/文件(Conainer/File)即特定格式的多媒體文件,比如MP4、flv、mov等。媒體流(Stream)表示時(shí)間軸上的一段連續(xù)數(shù)據(jù),如一段聲音數(shù) 據(jù)、一段視頻數(shù)據(jù)或一段字幕數(shù)據(jù),可以是壓縮的,也可以是非壓縮的,壓縮的數(shù)據(jù)需要關(guān)聯(lián)特定的編解碼器。數(shù)據(jù)幀/數(shù)據(jù)包(Frame/Packet)通常,一個(gè)媒體流是由大量的數(shù)據(jù)幀組成的,對(duì)于壓縮數(shù)據(jù),幀對(duì)應(yīng)著編解碼器的最小處理單元,分屬于不同媒體流的數(shù)據(jù)幀交錯(cuò)存儲(chǔ)于容器之中。編解碼器編解碼器是以幀為單位實(shí)現(xiàn)壓縮數(shù)據(jù)和原始數(shù)據(jù)之間的相互轉(zhuǎn)換的。
3.2 名詞介紹
-
AVFormatContext就是對(duì)容器或者說(shuō)媒體文件層次的一個(gè)抽象。 -
AVStream對(duì)流的抽象 -
AVCodecContext與AVCodec對(duì)編解碼格式以及編解碼器的抽象 -
AVPacket與AVFrame對(duì)于編碼器或者解碼器的輸入輸出部分,也就是壓縮數(shù)據(jù)以及原始數(shù)據(jù)的抽象。 -
AVFilter對(duì)于音視頻的處理肯定是針對(duì)于原始數(shù)據(jù)的處理,也就是針對(duì)于AVFrame的處理。
3.3 實(shí)例
接下來(lái)介紹一個(gè)解碼的實(shí)例,該實(shí)例實(shí)現(xiàn)的功能非常單一,就是把一個(gè)視頻文件解碼成單獨(dú)的音頻PCM文件和視頻YUV文件。
- 引用頭文件
- 注冊(cè)協(xié)議、格式與編解碼器
avformat_network_init();
av_register_all();
- 打開(kāi)媒體文件源,并設(shè)置超時(shí)回調(diào)
- 尋找各個(gè)流,并且打開(kāi)對(duì)應(yīng)的解碼器
- 初始化解碼后數(shù)據(jù)的結(jié)構(gòu)體
分配出解碼之后的數(shù)據(jù)所存放的內(nèi)存空間,以及進(jìn)行格式轉(zhuǎn)換需要用到的對(duì)象 - 讀取流內(nèi)容并且解碼
打開(kāi)了解碼器之后,就可以讀取一部分流中的數(shù)據(jù)(壓縮數(shù)據(jù)),然后將壓縮數(shù)據(jù)作為解碼器的輸入,解碼器將其解碼為原始數(shù)據(jù)(裸數(shù)據(jù)),之后就可以將原始數(shù)據(jù)寫入文件了。 - 處理解碼后的裸數(shù)據(jù)
解碼之后會(huì)得到裸數(shù)據(jù),音頻就是PCM數(shù)據(jù),視頻就是YUV數(shù)據(jù) - 關(guān)閉所有資源
四 FFmpeg源碼結(jié)構(gòu)
4.1 libavformat

AVFormatContext是API層直接接觸到的結(jié)構(gòu)體,它會(huì)進(jìn)行格式的封 裝與解封裝。
4.2 libavcodec

該結(jié)構(gòu)體包含的就是與實(shí)際的編解碼有關(guān)的部分。
3.3 FFmpeg通用API分析
3.3.1 av_register_all
所以該函數(shù)的內(nèi)部實(shí)現(xiàn)會(huì)先調(diào)用avcodec_register_all來(lái)注冊(cè)所有config.h里面開(kāi)放的編解碼器,然后會(huì)注冊(cè)所有的Muxer和Demuxer(也就是封裝格式),最后注冊(cè)所有的Protocol(即協(xié)議層的東西)。
3.3.2 av_find_codec
這里面其實(shí)包含了兩部分的內(nèi)容:一部分是尋找解碼器,一部分是尋找編碼器。
3.3.3 avcodec_open2
該函數(shù)是打開(kāi)編解碼器(Codec)的函數(shù),無(wú)論是編碼過(guò)程還是解碼過(guò)程,都會(huì)用到該函數(shù)。
3.4 調(diào)用FFmpeg解碼時(shí)用到的函數(shù)分析
avformat_open_input
根據(jù)所提供的文件路徑判斷文件的格 式,其實(shí)就是通過(guò)這一步來(lái)決定使用的到底是哪一個(gè)Demuxer。
avformat_find_stream_info
該方法的作用就是把所有Stream的MetaData信息填充好。
av_read_frame
使用該方法讀取出來(lái)的數(shù)據(jù)是AVPacket。
對(duì)于音頻流,一個(gè)AVPacket可能包含多個(gè)AVFrame,但是對(duì)于視頻流,一個(gè)AVPacket只包含一個(gè)AVFrame,該函數(shù)最終只會(huì)返回一個(gè)AVPacket結(jié)構(gòu)體。
avcodec_decode
該方法包含了兩部分內(nèi)容:一部分是解碼視頻,一部分是解碼音頻,解碼是會(huì)委托給對(duì)應(yīng)的解碼器來(lái)實(shí)施的。
avformat_close_input
該函數(shù)負(fù)責(zé)釋放對(duì)應(yīng)的資源。
3.5 調(diào)用FFmpeg編碼時(shí)用到的函數(shù)分析
avformat_alloc_output_context2
該函數(shù)內(nèi)部需要調(diào)用方法avformat_alloc_context來(lái)分配一個(gè) AVFormatContext結(jié)構(gòu)體。
avio_open2
編碼的階段了,開(kāi)發(fā)者需要將手動(dòng)封裝好的AVFrame結(jié)構(gòu)體,作為avcodec_encode_video方法的輸入,將其編碼成為AVPacket,然后調(diào)用av_write_frame方法輸出到媒體文件中。