關(guān)于ffmpeg
關(guān)于ffmpeg,我們首先需要知道它是什么,ffmpeg官網(wǎng)對(duì)ffmpeg進(jìn)行了定義:

一個(gè)針對(duì)音視頻的錄制/轉(zhuǎn)換/串流傳輸?shù)目缙脚_(tái)解決方案。

當(dāng)然,在about頁(yè)面介紹得更加詳細(xì),可以說(shuō)ffmpeg是針對(duì)音視頻領(lǐng)域的全能操作框架。在現(xiàn)如今的互聯(lián)網(wǎng)世界中,關(guān)于音視頻的操作處理,或直接或間接都會(huì)依賴到這個(gè)框架(當(dāng)然也有其他音視頻框架)。
怎樣使用ffmpeg
ffmpeg有兩種使用方法:
- 自定義開(kāi)發(fā)使用
- 命令行使用
因?yàn)閒fmpeg的音視頻處理能力都封裝在對(duì)應(yīng)的庫(kù)文件中,因此我們?cè)倏梢栽谧约旱拇a工程中嵌入對(duì)應(yīng)的庫(kù)文件,然后就可以調(diào)用它的基礎(chǔ)能力,通過(guò)各種組合實(shí)現(xiàn)我們想要的功能,這就是自定義開(kāi)發(fā)使用。
而且ffmpeg自身也會(huì)把這些功能庫(kù)統(tǒng)一封裝并套殼,打包成一個(gè)可執(zhí)行文件,可以接受各種輸入命令,比如在windows上就是ffmpeg.exe文件,我們可以下載這個(gè)可執(zhí)行文件,然后再命令行窗口執(zhí)行對(duì)應(yīng)的命令行代碼。
命令行
我們以windows平臺(tái)為例,在官網(wǎng)下載最新的可執(zhí)行文件,不出意外,你會(huì)下載到三個(gè)可執(zhí)行文件,分別是:
- ffmpeg,用于音視頻進(jìn)行修改(錄制,采集,裁剪,轉(zhuǎn)換等)處理的能力
- ffprobe,用于分析音視頻文件的相關(guān)信息
- ffplay,發(fā)播放音視頻
把文件所在路徑添加到系統(tǒng)路徑即可在命令行窗口進(jìn)行調(diào)用。
比如查看媒體文件的一些信息
ffprobe -show_streams sample.mp4 // 顯示sample.mp4內(nèi)部的流的信息
比如視頻文件的格式轉(zhuǎn)換
// mp4轉(zhuǎn)換為avi
ffmpeg -i sample.mp4 -c copy output.avi
命令行的參數(shù)和組合形式非常多,具體可以參考官網(wǎng) Command Line Tools Documentation。
也可以先看看阮一峰 FFmpeg 視頻處理入門(mén)教程,我認(rèn)為他的教程都非常詳細(xì)且易懂
自定義開(kāi)發(fā)
自定義開(kāi)發(fā)則是撇開(kāi)了ffmpeg的殼,只獲取其內(nèi)部功能實(shí)現(xiàn)的庫(kù)文件,把庫(kù)文件集成到我們工程中,通過(guò)調(diào)用ffmpeg提供的API來(lái)實(shí)現(xiàn)我們自己的功能,這個(gè)會(huì)涉及到了c/cpp開(kāi)發(fā)。
關(guān)于自定義開(kāi)發(fā)更多細(xì)節(jié)就不舉例了,這不是本文的重點(diǎn),而且后面會(huì)專門(mén)在ffmpeg的基礎(chǔ)上進(jìn)行功能開(kāi)發(fā)。
小結(jié)
相對(duì)而言命令行的方式更簡(jiǎn)單一些,因?yàn)槊钚邢喈?dāng)于已經(jīng)提前封裝了一些功能實(shí)現(xiàn)的邏輯代碼,自定義開(kāi)發(fā)的使用復(fù)雜的多,因?yàn)樽远x開(kāi)發(fā)則需要自己實(shí)現(xiàn)邏輯。但是自定義開(kāi)發(fā)比較靈活,而且更適合精細(xì)化的,自定義程度較高的那些需求,而且更容易讓我們理解音視頻處理的一些過(guò)程。
其實(shí)這兩種使用方式?jīng)]有本質(zhì)區(qū)別,命令行在library之上增加了中間層,把一些功能實(shí)現(xiàn)凝結(jié)為幾個(gè)命令參數(shù)以達(dá)到簡(jiǎn)化開(kāi)發(fā)的目的。而自定義則直接拿開(kāi)中間層,直接接觸library。

ffmpeg的功能模塊

libavcodec提供音視頻的編解碼能力libavformat提供處理各種音視頻容器格式的能力,比如封裝,解封裝libavutil提供一些實(shí)用的功能函數(shù)libavfilter提供音視頻流的濾鏡效果libavdevice提供操作輸入和輸出設(shè)備的能力。例如,從攝像頭獲取視頻數(shù)據(jù)以及將視頻數(shù)據(jù)輸出到屏幕libswresample實(shí)現(xiàn)音頻混合和重采樣能力libswscale實(shí)現(xiàn)視頻幀的顏色轉(zhuǎn)換和縮放能力
如果使用命令行模式,一般會(huì)把上述的庫(kù)全部打包起來(lái),而如果是自定義開(kāi)發(fā)則是按需使用,比如如果不涉及設(shè)備操作可以不要libavdevice,不需要濾鏡功能可以不要libavfilter...
這些支持庫(kù)都是跨平臺(tái)的,既可以是動(dòng)態(tài)庫(kù)也可以是靜態(tài)庫(kù),
ffmpeg的一些基礎(chǔ)概念
time_scale
time_scale 可以稱作時(shí)間刻度,具體的含義是把一秒鐘分為多少個(gè)刻度。
time_scale = 10 // 表示把一秒鐘分為10份,10個(gè)刻度,每個(gè)刻度代表1/10秒
time_scale = 1000 // 表示把一秒鐘分為1000份,1000個(gè)刻度,每個(gè)刻度代表1/1000秒
time_base
time_base是ffmpeg中一個(gè)非常重要的概念,一般稱作時(shí)間基,或者可以說(shuō)是ffmpeg中的時(shí)間基礎(chǔ)單位。
現(xiàn)實(shí)世界所使用的時(shí)間基礎(chǔ)單位一般是是秒,因?yàn)槊雽?duì)于普通人而言已經(jīng)足夠精確了,使用毫秒或者微秒毫無(wú)必要,但是在ffmpeg中不是以秒為基礎(chǔ)單位,而是把秒分為若干份,以一份作為ffmpeg中的時(shí)間基礎(chǔ)單位。
time_base = 1/24 //表示把1s分為24份,以1/24作為ffmpeg的時(shí)間的基礎(chǔ)單位。
time_base = 1/1000 //表示把1s分為1000份,以1/1000作為ffmpeg的時(shí)間的基礎(chǔ)單位。
ffmpeg中大量的時(shí)間表示(AVStram,AVPacket,AVFrame中的PTS,DTS),都是以time_base作為基礎(chǔ)單位的,而不是現(xiàn)實(shí)時(shí)間。
PTS/DTS/pts_time
-
DTS(Decoding Time Stamp,解碼時(shí)間戳)
- 指示解碼器應(yīng)該在什么時(shí)間點(diǎn)開(kāi)始解碼該幀(解碼順序)
-
PTS(Presentation Time Stamp,顯示時(shí)間戳)
- 指示幀應(yīng)該在什么時(shí)間點(diǎn)被呈現(xiàn)給用戶。PTS 是在解碼后確定的時(shí)間戳,用于視頻幀或音頻幀的渲染或播放順序。
-
pts_time
- 當(dāng)前媒體幀的顯示時(shí)間,單位為秒
DTS,PTS一般都是以time_base為單位的。
關(guān)于pts,pts_time,time_base之間的計(jì)算方式
time_base = 1/75;
Frame pts pts_time
0 0 0 x 1/75 = 0.00 (表示該幀在第0秒顯示)
1 3 3 x 1/75 = 0.04 (表示該幀在第0.04秒顯示)
2 6 6 x 1/75 = 0.08 (表示該幀在第0.08秒顯示)
3 9 9 x 1/75 = 0.12 (表示該幀在第0.12秒顯示)
AVStream
在ffmpeg中,讀取或者寫(xiě)入視頻文件時(shí),內(nèi)部會(huì)構(gòu)建一個(gè)數(shù)據(jù)結(jié)構(gòu)AVFormatContext,這個(gè)結(jié)構(gòu)中包含了視頻的相關(guān)信息,其中一個(gè)信息就是AVStram數(shù)組,每一個(gè)AVStream都包含了一種數(shù)據(jù),比如音頻和視頻分別代表一個(gè)AVStram結(jié)構(gòu),我們可以從中獲取比如時(shí)長(zhǎng),幀數(shù),編解碼器信息,time_base等

一旦我們獲取了AVStream,我們就可以讀取該Stream所代表的數(shù)據(jù)。
AVPacket
在ffmpeg中,在視頻文件讀取到基本信息之后,就可以讀取正式數(shù)據(jù)內(nèi)容用于解碼,而這個(gè)用于解碼的數(shù)據(jù)格式就是AVPacket.
AVPakcet內(nèi)部包括待一段解碼的數(shù)據(jù)data,size,pts,dts,time_base,stream_id(表示packet讀取自哪個(gè)AVStream)等。

AVFrame
AVFrame則是解碼后的原始數(shù)據(jù),表示一個(gè)可以被使用幀,注意這并不一定是視頻幀,也可能是音頻幀,因?yàn)橐曨l和音頻都使用AVFrame的數(shù)據(jù)結(jié)構(gòu)。
AVFrame內(nèi)包含可播放數(shù)據(jù),寬,高(視頻),幀類型(視頻),采樣數(shù)(音頻),音頻格式,PTS等
一般如果是視頻,AVFrame表示得是YUV或者RGB圖片;如果是音頻,則AVFrame表示得是PCM數(shù)據(jù)。

此時(shí)的得到的數(shù)據(jù)可以直接被播放。
音視頻同步
在獲得了可用的音頻或者視頻幀之后,也并不是直接發(fā)送到對(duì)應(yīng)的設(shè)備進(jìn)行播放即可,一般需要我們進(jìn)行音視頻同步控制,因?yàn)闀?huì)出現(xiàn)音頻和視頻因?yàn)楦鞣N差異導(dǎo)致獲得可用幀的速度差別很大,比如視頻很快播放完,但是音頻才播了一點(diǎn)點(diǎn)。因此我們需要通過(guò)PTS和time_base來(lái)控制每一幀的播放速度。
至于同步方法,一般是需要建立一個(gè)時(shí)間坐標(biāo)系作為參考,然后計(jì)算每一幀的顯示時(shí)間戳是否對(duì)應(yīng)了正確的時(shí)間,否則就等待。
邏輯流程與數(shù)據(jù)格式轉(zhuǎn)換
根據(jù)上面的對(duì)一些數(shù)據(jù)類型的簡(jiǎn)單介紹,我們已經(jīng)可以得到一個(gè)音頻文件播放過(guò)程的邏輯流程了

同樣的,把可播放的原始數(shù)據(jù)保存成文件則是這個(gè)過(guò)程的逆過(guò)程,大同小異。
總結(jié)
本文主要對(duì)ffmpeg使用方法,核心模塊,ffmpeg中的一些基本概念做了介紹,后面將會(huì)站在自定義開(kāi)發(fā)的角度上,分析ffmpeg的數(shù)據(jù)結(jié)構(gòu)的詳情,和一個(gè)視頻被播放的完整過(guò)程實(shí)現(xiàn),最終會(huì)講講在Android平臺(tái)的使用方式。