FFmpeg 是什么
FFmpeg 是非常強(qiáng)大的多媒體編解碼框架,堪稱多媒體處理的瑞士軍刀,是集錄制,轉(zhuǎn)碼,流的完整的跨平臺多媒體解決方案。本身涵蓋了大量的多媒體處理工具。
視頻知識簡述
- 圖像由空間中二維平鋪的不同顏色的像素點(diǎn)組成
- 視頻則是在時間上連續(xù)的圖片連成的產(chǎn)物。是三維的數(shù)據(jù)
- 音頻則是在時間上振幅與頻率的數(shù)據(jù),是一維的數(shù)據(jù)
而我們常說的視頻,其實是包含的視頻,音頻甚至還有字幕的一個整體。因為視頻本身是三維的數(shù)據(jù),在計算中直接存儲空間非常大,通常無法接受,所以,會對其進(jìn)行一些編碼和壓縮,這個過程通常是有損的,但卻極大了降低了它的存儲空間。

視頻相關(guān)的幾個概念
- 幀(Frame)視頻中單位時間上一張靜止的畫面,是視頻的最小單位
- 幀速率(FPS):每秒播放圖片的數(shù)量
- 碼率(Bit Rate):視頻文件在單位時間內(nèi)使用的數(shù)據(jù)流量,決定視頻的質(zhì)量和大小,單位是 kb/s 或者 Mb/s。
視頻流
視頻數(shù)據(jù)相比位圖數(shù)據(jù)多了一維,但其本身還是由像素點(diǎn)組成。
- 經(jīng)過壓縮算法壓縮的流數(shù)據(jù),稱為編碼流,如H264碼流
- 未經(jīng)壓縮的流數(shù)據(jù),即解碼后的流數(shù)據(jù),稱為原始流。即由一幅幅在時間上連續(xù)的圖像組成的數(shù)據(jù),也稱YUV流
編碼、壓縮在流媒體領(lǐng)域是一項非常重要的技術(shù):從H264碼流到YUV流的過程稱為解碼,反之稱為編碼。
視頻幀
幀是流的基本元素,即視頻流中的圖像。在原始流里,各幀都一樣。但經(jīng)過編碼壓縮后的幀,通常分為下面幾種:
- I幀,英文全寫Intra Picture,又稱幀內(nèi)編碼幀,俗稱關(guān)鍵幀。擁有完整的圖像信息。一般來說I幀不需要依賴前后幀信息,可獨(dú)立進(jìn)行解碼。
- P幀,英文全寫predictive-frame,又稱前向預(yù)測編碼幀,也有幀間預(yù)測編碼幀。顧名思義,P幀需要依賴前面的I幀或者P幀才能進(jìn)行編解碼,因為一般來說,P幀存儲的是當(dāng)前幀畫面與前一幀(前一幀可能是I幀也可能是P幀)的差別,較專業(yè)的說法是壓縮了時間冗余信息,或者說提取了運(yùn)動特性。P幀的壓縮率約在20左右,幾乎所有的H264編碼流都帶有大量的P幀。
- B幀,英文全寫bi-directional interpolatedprediction frame,又稱 雙向預(yù)測內(nèi)插編碼幀,簡稱雙向預(yù)測編碼幀。B幀非常特殊,它存儲的是本幀與前后幀的差別,因此帶有B幀的視頻在解碼時的邏輯會更復(fù)雜些,CPU開銷會更大。因此,不是所有的視頻都帶有B幀,筆者目前還沒有接觸過帶B幀的視頻?!菊业綆幀視頻一定要珍藏起來好好研究!】
- IDR幀,英文全寫Instantaneous Decoding Refresh,翻譯過來是即時解碼刷新。聽上去,這類幀并不是名詞概念,倒像是個動詞?IDR幀是一種特殊的I幀,它是為了服務(wù)于編解碼而提出的概念,IDR幀的作用是立刻刷新,使錯誤不致傳播,從IDR幀開始,重新算一個新的序列開始編碼
注意:I、P、B幀,并不是依據(jù)視頻幀數(shù)據(jù)內(nèi)部的元素的不同來區(qū)分的,從解碼后的幀本身而言,它們沒有任何區(qū)別。僅僅是在編碼時,對幀處理的方式不同而已。
視頻封閉格式及視頻編碼格式
首先視頻文件與視頻編碼格式是兩個概念。
- 視頻封閉格式 即是我們常見的視頻文件(如mp4,avi),它是一個文件容器。它的作用是將壓縮編碼后的視頻和音頻等數(shù)據(jù)盡可能緊湊的排布。它內(nèi)部可以包含視頻,音頻,字幕等數(shù)據(jù)。包括的視頻,音頻,字幕可能有各自不同的編碼格式。
- 視頻編碼格式(如mpeg4,h264)是視頻數(shù)據(jù)的編碼方式。包括視頻制式、壓縮率、以及相關(guān)的編碼方式
視頻文件通常結(jié)構(gòu)
一般包括以下部分:
- header 標(biāo)記文件類型,音視頻碼流的基本屬性信息
- index 索引表,每個frame有對應(yīng)的offset,size,timestamp
- stream 真正的音視頻流數(shù)據(jù)
FFmpeg 處理視頻的工作流程
FFmpeg 處理視頻轉(zhuǎn)碼時的常規(guī)過程如下:

- 輸入媒體文件
- 對文件demux
- 對流進(jìn)行相應(yīng)的decode
- 使用輸出的編碼進(jìn)行encode
- 進(jìn)行Mux操作
- 輸出媒體文件
各自狀態(tài)的數(shù)據(jù)產(chǎn)物如下:

Demux
視頻中會有音頻,視頻,它們在文件有各自的編碼壓縮方式,但為了傳輸過程方便,將壓縮過的音頻和視頻捆綁在一起進(jìn)行傳輸。首先就需要將媒體文件中的各個音頻,視頻,字幕流等分開。
這一步叫Demux(解復(fù)用)??偨Y(jié)就是把不同的流從某種容器(文件)中解析出來。如上圖所示,并且,一個媒體文件中,還可以分別有不止一個音頻,視頻和字幕流。
Decode
媒體數(shù)據(jù)為了降低存儲量,針對里面的音頻,視頻,字幕等都會采取特定方式的編碼壓縮。
這一步便對各自的流采用其對應(yīng)的解碼算法進(jìn)行處理。得到原始流。
即這一步是以幀為單位實現(xiàn)壓縮數(shù)據(jù)到原始數(shù)據(jù)轉(zhuǎn)換。
Encode
這一步再按照目標(biāo)的編碼方式對流進(jìn)行編碼。即這一步是以幀為單位實現(xiàn)原始數(shù)據(jù)到壓縮數(shù)據(jù)轉(zhuǎn)換。
Mux
Demux的逆操作,把不同的流按照某種容器的規(guī)則放入容器
FFmpeg源碼實現(xiàn)的main流程主要概括
- 編解碼器注冊
- 輸入輸出格式和設(shè)備注冊
- 上下文初始化和編解碼參數(shù)讀入
- 之后進(jìn)行具體的編解碼工作
- 輸入輸出流初始化。
- 根據(jù)輸入輸出流確定需要的編解碼器,并初始化
- 寫輸出文件的各部分
FFmpeg filter
按照編解碼器的位置劃分:
- prefilters 在encode之前
- intrafilters 編碼時(是編碼器不可缺少的一部分)
- postfilters 在解碼之后
FFmpeg中filter分為:
- source filter (只有輸出)
- audio filter
- video filter
- Multimedia filter
- sink filter (只有輸入)
FFmpeg 硬件加速支持情況
平臺可用API情況

FFmpeg實現(xiàn)的API情況

VDPAU的加速方案(通過Nvidia的GPU)
因其僅有解碼部分的硬件加速,缺少編碼部分的加速。且Nvidia有單身以NVENC和NVDEC替代掉這一塊的意思,這里不多做細(xì)節(jié)介紹。細(xì)節(jié)請看文檔
支持的格式有:
- mpeg-1, mpeg-2, mpeg-4
- h264, h265
- vc-11
CUDA(NVENC/NVDEC) 加速方案(通過Nvidia的GPU)
優(yōu)點(diǎn):
- 得益于強(qiáng)大的GPU,視頻編解碼的能力強(qiáng)
缺點(diǎn): - 需要相應(yīng)的GPU設(shè)備支持
- 在多塊GPU的機(jī)器上,并發(fā)跑任務(wù)時,需要一定的GPU策略進(jìn)行分配
- 需要提高碼率才能達(dá)到與軟編解相似的圖像效果
- GPU資源浪費(fèi),一個GPU同時只能執(zhí)行一個轉(zhuǎn)碼任務(wù)
編碼支持
- NVDEC支持解碼 H.264, HEVC, MJPEG, MPEG-1/2/4, VP8/VP9, VC-1
- NVENC支持編碼 H.264, HEVC
各種型號的設(shè)計支持情況請看Video Encode and Decode GPU Support Matrix
GPU相關(guān)的轉(zhuǎn)碼命令
查看硬件支持:
ffmpeg -hwaccels
使用CUDA解碼:
ffmpeg -hwaccel cuda -i input output
使用CUVID解碼:
ffmpeg -c:v h264_cuvid -i input output
轉(zhuǎn)碼,使用NVDEC和NVENC
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input -c:v h264_nvenc -preset slow output
可以使用-hwaccel_device <id> 指定GPU。GPU id可以通過nvidia-smi查看
基于Intel QSV加速
該方案一般通過QSV來進(jìn)行加速。QSV(Quick Sync Video)即Intel的集成加速器,該方便優(yōu)缺點(diǎn)如下:
優(yōu)點(diǎn):
- 不需要額外的硬件支持
- 與現(xiàn)有軟編解模式接近,不影響現(xiàn)有架構(gòu)設(shè)計
缺點(diǎn): - 很多服務(wù)器CPU不支持,倒是常見的桌面CPU都支持
- 需要提高碼率才能達(dá)到與軟編解相似的圖像效果
查看CPU是否支持的方法如下:
- 查看CPU型號,如linux上
cat /proc/cpuinfo,如Intel(R) Celeron(R) CPU J3455 @ 1.50GHz - Google搜索,打開intel官網(wǎng)中對該型號的細(xì)節(jié)參數(shù)介紹。若支持則如下:

該方案支持h264,h265,mjpeg,mpeg2video,vp8,vp9的編解碼,和vc1的解碼。
更細(xì)節(jié)的支持情況如下:
解碼支持情況:

編碼支持情況:

一個簡單測試
- 編解碼:
- 軟編解碼 CPU 20% 內(nèi)存300M
- QSV編解碼 CPU 1% 內(nèi)存200M
- 編碼:
- 軟編碼 CPU 20%+ 內(nèi)存280M+
- QSV編碼 CPU 1% 內(nèi)存160M+
- 解碼:
- 軟解碼 CPU 2.7% 內(nèi)存40M
- QSV解碼 CPU 0.3% 內(nèi)存60M
QuickSync對于編碼解碼在CPU使用率上都有著非常不錯的提升。編碼提升效果特別明顯。
關(guān)于抽幀的各種表現(xiàn),待之后測試補(bǔ)上。TODO
基于API
- DXVA2 / D3D11VA (Windows)
- libmfx (Linux, Windows)
- VAAPI (Linux)
- libmfs (Linux)
- Intel Media SDK (Linux, Windows)
更多細(xì)節(jié)可以參考官方文檔
FFmpeg使用QSV加速的一些命令行參數(shù)可參考
常用抽幀及命令
- 抽取視頻關(guān)鍵幀(IPB幀)
- 抽取視頻場景轉(zhuǎn)換幀
- 按照時間進(jìn)行均勻抽幀
- 抽取制定時間的視頻幀
抽取視頻關(guān)鍵幀(IPB幀)
使用ffprobe提取出IPB幀的時間
ffprobe -i 666051400.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type
抽取IPB幀到j(luò)pg圖片:
# 抽取I幀
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,I)" -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg
# 抽取P幀
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,P)" -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg
# 抽取B幀
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,B)" -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg
抽取視頻場景轉(zhuǎn)換幀
# https://ffmpeg.org/ffmpeg-filters.html#select_002c-aselect
# 其中0.1表示幀為新場景的概率
ffmpeg -i 666051400.mp4 -filter:v "select='gt(scene,0.1)',showinfo" -f null - 2>&1
均勻抽幀
# -r 指定抽取的幀率,即從視頻中每秒鐘抽取圖片的數(shù)量。1代表每秒抽取一幀。
ffmpeg -i 666051400.mp4 -r 1 -q:v 2 -f image2 ./%08d.000000.jpg
抽取制定時間的幀
# 耗時0.07s -ss放前面,使用關(guān)鍵幀信息進(jìn)行索引,所以更快
ffmpeg -ss 00:00:30 -i 666051400.mp4 -vframes 1 0.jpg
# 耗時0.68s
ffmpeg -i 666051400.mp4 -ss 00:00:30 -vframes 1 0.jpg
針對抽幀方向的加速調(diào)研
抽幀流程其實與轉(zhuǎn)碼類似,一樣是輸入視頻文件,對視頻文件進(jìn)行Demux,解碼視頻流,得到原始視頻幀,然后挑選相應(yīng)的視頻幀按jpeg編碼,mux輸出到一個個文件中。
那么要加快抽幀的效率,便是想辦法加快上面這各個階段的速率
- 加快數(shù)據(jù)讀取。不確定可行性,視頻解碼整體還是接近流式讀取的情況,這塊耗時理論上本身不高,
- 加快解復(fù)用,這塊耗時占比依舊很小,理論上可優(yōu)化空間不大
- 加快解碼,解碼理論上是抽幀過程中消耗占比很大的一塊,而通過GPU或者其他一些方式加快,也算有比較大的優(yōu)化空間
- 加快選幀速度,這塊對于場景抽幀意義比較大
- 加快圖片編碼,編碼這塊也是抽幀過程中消耗占比比較大的一塊,也有通過GPU或者其他一些策略加速,算有比較大的優(yōu)化空間
- 加快圖片輸出,
抽幀會伴隨著大量的文件輸出。在有很多抽幀任務(wù)并行時,理論上會對磁盤造成大量的隨機(jī)寫入。
其他途徑優(yōu)化:
- 通過并行
- 算法優(yōu)化
- 其他
結(jié)合以上各節(jié)點(diǎn),這里列出一些進(jìn)一步的調(diào)研方向:
基于Intel QSV的加速方案調(diào)研
此方案針對加快解碼,加快編碼?;谀壳暗男畔砜矗?/p>
- 支持多種視頻格式解碼
- 支持jpeg圖片編碼
- 需要支持QSV的cpu
該方案會有非常大的優(yōu)化效果,若有足夠支持QSV的CPU機(jī)器,那么該方案可行性非常大。
待進(jìn)一步補(bǔ)充相關(guān)測試數(shù)據(jù)
基于Nvidia Cuda的加速方案調(diào)研
同上,針對解碼編碼方面嘗試加快進(jìn)行優(yōu)化。目前情況:
- 支持多種視頻格式解碼
- 不支持jpeg圖片編碼,但網(wǎng)上有其他支持cuda加速的jpeg庫,或者可以融入進(jìn)來
- 需要相應(yīng)的Nvidia GPU設(shè)備
該方案理論上也應(yīng)該會有不錯的優(yōu)化效果。但就目前的一些簡單測試來看,即使能達(dá)到好的加速效果,其成本也未必能夠接受
待進(jìn)一步補(bǔ)充相關(guān)測試數(shù)據(jù)
基于內(nèi)存文件系統(tǒng)進(jìn)行優(yōu)化
該方案針對加快數(shù)據(jù)讀取和數(shù)據(jù)輸出??紤]的點(diǎn)主要是整個抽幀過程中是否對視頻文件有不小量的隨機(jī)讀取,大量抽幀任務(wù)并行時,會有巨量的圖片輸出。這塊是否會產(chǎn)生大量的隨機(jī)寫入,降低效率。
待進(jìn)一步補(bǔ)充相關(guān)測試數(shù)據(jù)
基于關(guān)鍵幀模式優(yōu)化
該方案針對加解碼(更確切地說是減少不必要的解碼操作)
例如對于一秒一幀的均勻抽幀,我們針對這種情況,修改為,盡量抽取每一秒中的關(guān)鍵幀。做個偽的一秒一幀的均勻抽幀。這種模式不一定能為所有抽幀情況加速,但若能為大頭的需求加速也不錯
待進(jìn)一步測試驗證可行性及補(bǔ)充相關(guān)測試數(shù)據(jù)
基于庫選擇的優(yōu)化
針對解碼編碼方面嘗試加快進(jìn)行優(yōu)化。
ffmpeg中,同樣格式可能有多個編碼解碼器,比如jpeg的就會有mjpeg,libjpeg等。不同的庫實現(xiàn),性能,質(zhì)量會有一些差距,通過測試實驗,改善參數(shù),或許一定程度上能優(yōu)化性能。
待進(jìn)一步補(bǔ)充相關(guān)測試數(shù)據(jù)
基于ffmpeg參數(shù)和源碼的優(yōu)化
針對流程的優(yōu)化。如前面的單幀抽幀,-ss 參數(shù)的不同位置會影響 ffmpeg 抽取的時間。理論上,ffmpeg 本身設(shè)計是為了通用,而對于專用于抽幀的場景,ffmpeg本身可能存在一些沒必要的工作,嘗試通過參數(shù)和源碼級別來優(yōu)化,或許能省掉這部分的資源消耗。
待進(jìn)一步補(bǔ)充相關(guān)測試數(shù)據(jù)
針對其他應(yīng)用的加速調(diào)研
TODO
參考
FFmpeg官網(wǎng)
3GP/MP4 視頻文件格式解析及其播放原理(轉(zhuǎn))
FFmpeg視頻抽幀那些事
FFmpeg原理和架構(gòu)
FFmpeg 硬件加速方案概覽 (上)
FFmpeg 硬件加速方案概覽 (下)
FFmpeg wiki HWAccelIntro
視頻和視頻幀:FFMPEG+Intel QSV硬解的環(huán)境安裝篇
視頻和視頻幀:視頻和幀基礎(chǔ)知識整理