FFmpeg名稱中的mpeg來自視頻編碼標(biāo)準MPEG,而前綴FF是Fast Forward的首字母縮寫。
目錄
一 FFmpeg的主體結(jié)構(gòu)
二 FFmpeg命令行工具的使用
三 FFmpeg API的介紹與使用
一 FFmpeg的主體結(jié)構(gòu)
默認的編譯會生成4
個可執(zhí)行文件和8
個靜態(tài)庫??蓤?zhí)行文件包括用于轉(zhuǎn)碼
、推流
、Dump媒體文件的ffmpeg
、用于播放媒體文件的ffplay
、 用于獲取媒體文件信息的ffprobe
,以及作為簡單流媒體服務(wù)器的ffserver
。
8個靜態(tài)庫其實就是FFmpeg的8個模塊,具體包括如下內(nèi)容。
AVUtil
核心工具庫,該模塊是最基礎(chǔ)
的模塊之一,下面的許多
其他模塊都會依賴該庫做一些基本的音視頻處理操作。AVFormat
文件格式和協(xié)議庫,該模塊是最重要的模塊之一,封 裝了Protocol層和Demuxer、Muxer層,使得協(xié)議和格式對于開發(fā)者來說是透明的。AVCodec
編解碼庫,該模塊也是最重要的模塊之一,封裝了 Codec層,但是有一些Codec是具備自己的License的,F(xiàn)Fmpeg是不會默認添加像libx264、FDK-AAC、lame等庫的,但是FFmpeg就像一個平臺 一樣,可以將其他的第三方的Codec以插件的方式添加進來,然后為開 發(fā)者提供統(tǒng)一的接口。AVFilter
音視頻濾鏡庫,該模塊提供了包括音頻特效和視頻特效的處理,在使用FFmpeg的API進行編解碼的過程中,直接使用該模塊為音視頻數(shù)據(jù)做特效處理是非常方便同時也非常高效的一種方式。AVDevice
輸入輸出設(shè)備庫,比如,需要編譯出播放聲音或者視頻的工具ffplay
,就需要確保該模塊是打開的,同時也需要libSDL的預(yù)先編譯,因為該設(shè)備模塊播放聲音與播放視頻使用的都是libSDL庫。SwrRessample
該模塊可用于音頻重采樣
,可以對數(shù)字音頻進行聲道數(shù)、數(shù)據(jù)格式、采樣率等多種基本信息的轉(zhuǎn)換。SWScale
該模塊是將圖像進行格式轉(zhuǎn)換
的模塊,比如,可以將 YUV的數(shù)據(jù)轉(zhuǎn)換為RGB的數(shù)據(jù)。PostProc
該模塊可用于進行后期處理,當(dāng)我們使用AVFilter的時候需要打開該模塊的開關(guān),因為Filter中會使用到該模塊的一些基礎(chǔ)函數(shù)。
比如AAC編碼,常見的有兩種封裝格式
- 一種是ADTS格式的流,是AAC定義在MPEG2里面的格式
- 另外一種是封裝在MPEG4里面的格式,這種格式會在每一幀前面拼接一個用聲道、采樣率等信息組成的頭。
AAC
的bit stream filter
常常應(yīng)用在編碼
的過程中。
與音頻的AAC編碼格式相對應(yīng)的是視頻中的H264編碼
,它也有兩種封裝格式
- 一種是 MP4封裝的格式
- 一種是裸的H264格式(一般稱為annexb封裝格式)
FFmpeg中也提供了對應(yīng)的bit stream filter
,稱H264_mp4toannexb
,可以將MP4封裝格式的H264數(shù)據(jù)包轉(zhuǎn)換為annexb封裝格式的H264數(shù)據(jù) (其實就是裸的H264的數(shù)據(jù))包。
H264
的bit stream filter
常常應(yīng)用于視頻解碼過程中。
二 FFmpeg命令行工具的使用
ffmpeg
是進行媒體文件轉(zhuǎn)碼的命令行工具
ffprobe
是用于查看媒體 文件頭信息的工具
ffplay
則是用于播放媒體文件的工具
2.1 ffprobe
1.首先用ffprobe查看一個音頻的文件
ffprobe ~/Desktop/32037.mp3
2.輸出格式信息format_name、時間長度duration、文件 大小size、比特率bit_rate、流的數(shù)目nb_streams等。
ffprobe -show_format 32037.mp4
3.以JSON格式的形式輸出具體每一個流最詳細
的信息
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ǔ),外加渲染音視頻 的庫libSDL來構(gòu)建的媒體文件播放器。
業(yè)界內(nèi)開源的ijkPlayer
其實就是基于ffplay
進行改造的播放器,當(dāng)然其做了硬件解碼以及很多兼容性的工作。
音視頻同步
在 ffplay中音畫同步的實現(xiàn)方式其實有三種。分別是
- 以
音頻
為主時間軸 作為同步源 - 以
視頻
為主時間軸作為同步源 - 以
外部時鐘
為主時間軸作為同步源
并且在ffplay
中默認的對齊方式也是以音頻
為基準進行對齊的。
首先要聲明的是,播放器接收到的視頻幀或者音頻幀,內(nèi)部都會有時間戳(PTS時鐘)
來標(biāo)識它實際應(yīng)該在什么時刻進行展示。
實際的對齊策略如下:比較視頻當(dāng)前的播放時間和音頻當(dāng)前的播放時間
- 如果視頻播放
過快
,則通過加大延遲或者重復(fù)播放來降低視頻播放速度; - 如果視頻播
放慢
了,則通過減小延遲或者丟幀來追趕音頻播放的時間點。
關(guān)鍵就在于音視頻時間的比較以及延遲的計算,當(dāng)然在比較的過程中會設(shè) 置一個閾值(Threshold)
,若超過預(yù)設(shè)的閾值就應(yīng)該做調(diào)整(丟幀渲染 或者重復(fù)渲染),這就是整個對齊策略。
2.3 ffmpeg
ffmpeg
就是強大的媒體文件轉(zhuǎn)換工具。它可以轉(zhuǎn)換任何格式的媒體文件,并且還可以用自己的AudioFilter
以及VideoFilter
進行處理和編輯。
- 從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
- 將兩路聲音進行合并,比如要給一段聲音加上背景音樂
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
- 將一個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ù)語
容器/文件(Conainer/File)
即特定格式的多媒體文件,比如MP4
、flv
、mov
等。媒體流(Stream)
表示時間軸上的一段連續(xù)數(shù)據(jù),如一段聲音數(shù) 據(jù)、一段視頻數(shù)據(jù)或一段字幕數(shù)據(jù),可以是壓縮的,也可以是非壓縮的,壓縮的數(shù)據(jù)需要關(guān)聯(lián)特定的編解碼器
。數(shù)據(jù)幀/數(shù)據(jù)包(Frame/Packet)
通常,一個媒體流是由大量的數(shù)據(jù)幀組成的,對于壓縮數(shù)據(jù),幀
對應(yīng)著編解碼器
的最小處理單元,分屬于不同媒體流的數(shù)據(jù)幀交錯存儲于容器之中。編解碼器
編解碼器是以幀
為單位實現(xiàn)壓縮數(shù)據(jù)
和原始數(shù)據(jù)
之間的相互轉(zhuǎn)換的。
3.2 名詞介紹
-
AVFormatContext
就是對容器或者說媒體文件層次的一個抽象。 -
AVStream
對流的抽象 -
AVCodecContext
與AVCodec
對編解碼格式以及編解碼器的抽象 -
AVPacket
與AVFrame
對于編碼器或者解碼器的輸入輸出部分,也就是壓縮數(shù)據(jù)以及原始數(shù)據(jù)的抽象。 -
AVFilter
對于音視頻的處理肯定是針對于原始數(shù)據(jù)的處理,也就是針對于AVFrame
的處理。
3.3 實例
接下來介紹一個解碼的實例,該實例實現(xiàn)的功能非常單一,就是把一個視頻文件解碼成單獨的音頻PCM文件和視頻YUV文件。
- 引用頭文件
- 注冊協(xié)議、格式與編解碼器
avformat_network_init();
av_register_all();
- 打開媒體文件源,并設(shè)置超時回調(diào)
- 尋找各個流,并且打開對應(yīng)的解碼器
- 初始化解碼后數(shù)據(jù)的結(jié)構(gòu)體
分配出解碼之后的數(shù)據(jù)所存放的內(nèi)存空間,以及進行格式轉(zhuǎn)換需要用到的對象 - 讀取流內(nèi)容并且解碼
打開了解碼器之后,就可以讀取一部分流中的數(shù)據(jù)(壓縮數(shù)據(jù)
),然后將壓縮數(shù)據(jù)作為解碼器
的輸入,解碼器將其解碼為原始數(shù)據(jù)
(裸數(shù)據(jù)
),之后就可以將原始數(shù)據(jù)寫入文件了。 - 處理解碼后的裸數(shù)據(jù)
解碼之后會得到裸數(shù)據(jù),音頻就是PCM
數(shù)據(jù),視頻就是YUV
數(shù)據(jù) - 關(guān)閉所有資源
四 FFmpeg源碼結(jié)構(gòu)
4.1 libavformat
AVFormatContext是API層直接接觸到的結(jié)構(gòu)體,它會進行格式的封 裝與解封裝。
4.2 libavcodec
該結(jié)構(gòu)體包含的就是與實際的編解碼
有關(guān)的部分。
3.3 FFmpeg通用API分析
3.3.1 av_register_all
所以該函數(shù)的內(nèi)部實現(xiàn)會先調(diào)用avcodec_register_all
來注冊所有config.h里面開放的編解碼器,然后會注冊所有的Muxer
和Demuxer
(也就是封裝格式),最后注冊所有的Protocol
(即協(xié)議層的東西)。
3.3.2 av_find_codec
這里面其實包含了兩部分的內(nèi)容:一部分是尋找解碼器
,一部分是尋找編碼器
。
3.3.3 avcodec_open2
該函數(shù)是打開編解碼器(Codec)的函數(shù),無論是編碼過程還是解碼過程,都會用到該函數(shù)。
3.4 調(diào)用FFmpeg解碼時用到的函數(shù)分析
avformat_open_input
根據(jù)所提供的文件路徑判斷文件的格 式,其實就是通過這一步來決定使用的到底是哪一個Demuxer
。
avformat_find_stream_info
該方法的作用就是把所有Stream
的MetaData
信息填充好。
av_read_frame
使用該方法讀取出來的數(shù)據(jù)是AVPacket
。
對于音頻流
,一個AVPacket
可能包含多
個AVFrame
,但是對于視頻流
,一個AVPacket
只包含一
個AVFrame
,該函數(shù)最終只會返回一個AVPacket
結(jié)構(gòu)體。
avcodec_decode
該方法包含了兩部分內(nèi)容:一部分是解碼視頻
,一部分是解碼音頻
,解碼
是會委托給對應(yīng)的解碼器來實施的。
avformat_close_input
該函數(shù)負責(zé)釋放對應(yīng)的資源。
3.5 調(diào)用FFmpeg編碼時用到的函數(shù)分析
avformat_alloc_output_context2
該函數(shù)內(nèi)部需要調(diào)用方法avformat_alloc_context來分配一個 AVFormatContext
結(jié)構(gòu)體。
avio_open2
編碼的階段了,開發(fā)者需要將手動封裝好的AVFrame
結(jié)構(gòu)體,作為avcodec_encode_video
方法的輸入,將其編碼成為AVPacket
,然后調(diào)用av_write_frame
方法輸出到媒體文件中。
本文參考音視頻開發(fā)進階指南