前言
上文主要講解了 FFmpeg 相關(guān)知識,以及在 Windows 下編譯 FFmpeg 源碼,本文繼續(xù)對 FFmpeg 進(jìn)行更深入的介紹。
一、FFmpeg 基礎(chǔ)指令
主要參數(shù):
? -i 設(shè)定輸入流
? -f 設(shè)定輸出格式(format)
? -ss 開始時間
? -t 時間長度
音頻參數(shù):
? -aframes 設(shè)置要輸出的音頻幀數(shù)
? -b:a 音頻碼率
? -ar 設(shè)定采樣率
? -ac 設(shè)定聲音的Channel數(shù)
? -acodec 設(shè)定聲音編解碼器,如果用copy表示原始編解碼數(shù)據(jù)必須被拷貝。
? -an 不處理音頻
? -af 音頻過濾器
ffmpeg -i test.mp4 -b:a 192k -ar 48000 -ac 2 -acodec libmp3lame -aframes 200 out2.mp3
視頻參數(shù):
? -vframes 設(shè)置要輸出的視頻幀數(shù)
? -b 設(shè)定視頻碼率
? -b:v 視頻碼率
? -r 設(shè)定幀速率
? -s 設(shè)定畫面的寬與高
? -vn 不處理視頻
? -aspect aspect 設(shè)置橫縱比 4:3 16:9 或 1.3333 1.7777
? -vcodec 設(shè)定視頻編解碼器,如果用copy表示原始編解碼數(shù)據(jù)必須被拷貝。
? -vf 視頻過濾器
ffmpeg -i test.mp4 -vframes 300 -b:v 300k -r 30 -s 640x480 -aspect 16:9 -vcodec libx265
二、FFmpeg 應(yīng)用之視頻播放器
1、音視頻播放流程
播放一個音視頻文件的流程: 
- 解協(xié)議
- 將流媒體協(xié)議的數(shù)據(jù),解析為標(biāo)準(zhǔn)的相應(yīng)的封裝格式數(shù)據(jù)視音頻在網(wǎng)絡(luò)上傳播的時候,常常采用各種流媒體協(xié)議,例如 HTTP,RTMP,或是 MMS 等等。
- 這些協(xié)議在傳輸視音頻數(shù)據(jù)的同時,也會傳輸一些信令數(shù)據(jù)。這些信令數(shù)據(jù)包括對播放的控制(播放,暫停,停止),或者對網(wǎng)絡(luò)狀態(tài)的描述等。解協(xié)議的過程中會去除掉信令數(shù)據(jù)而只保留視音頻數(shù)據(jù)。例如,采用 RTMP 協(xié)議傳輸?shù)臄?shù)據(jù),經(jīng)過解協(xié)議操作后,輸出 FLV 格式的數(shù)據(jù)。
- 解封裝
- 將輸入的封裝格式的數(shù)據(jù),分離成為音頻流壓縮編碼數(shù)據(jù)和視頻流壓縮編碼數(shù)據(jù)
- 封裝格式種類很多,例如 MP4,MKV,RMVB,TS,F(xiàn)LV,AVI 等等,它的作用就是將已經(jīng)壓縮編碼的視頻數(shù)據(jù)和音頻數(shù)據(jù)按照一定的格式放到一起。例如,F(xiàn)LV 格式的數(shù)據(jù),經(jīng)過解封裝操作后,輸出 H.264 編碼的視頻碼流和 AAC 編碼的音頻碼流。
- 解碼
- 將視頻/音頻壓縮編碼數(shù)據(jù),解碼成為非壓縮的視頻/音頻原始數(shù)據(jù)
- 音頻的壓縮編碼標(biāo)準(zhǔn)包含 AAC,MP3,AC-3 等等,視頻的壓縮編碼標(biāo)準(zhǔn)則包含 H.264,MPEG2,VC-1 等等
- 解碼是整個系統(tǒng)中最重要也是最復(fù)雜的一個環(huán)節(jié)。
- 通過解碼,壓縮編碼的視頻數(shù)據(jù)輸出成為非壓縮的顏色數(shù)據(jù),例如 YUV420P,RGB 等等;壓縮編碼的音頻數(shù)據(jù)輸出成為非壓縮的音頻抽樣數(shù)據(jù),例如 PCM 數(shù)據(jù)。
- 音視頻同步
- 根據(jù)解封裝模塊處理過程中獲取到的參數(shù)信息,同步解碼出來的視頻和音頻數(shù)據(jù),并將視頻音頻數(shù)據(jù)送至系統(tǒng)的顯卡和聲卡播放出來。
- 視音頻封裝格式數(shù)據(jù)分為 flv、mkv、 mp4、rmvb、ts 等等格式
2、音視頻同步
音視頻同步的方式基本是確定一個時鐘(音頻時鐘、視頻時鐘、外部時鐘)作為主時鐘,非主時鐘的音頻或視頻時鐘為從時鐘。
在播放過程中,主時鐘作為同步基準(zhǔn),不斷判斷從時鐘與主時鐘的差異,調(diào)節(jié)從時鐘,使從時鐘追趕(落后時)或等待(超前時)主時鐘。
視頻同步到音頻的基本方法是:如果視頻超前音頻,則不進(jìn)行播放,以等待音頻;如果視頻落后音頻,則丟棄當(dāng)前幀直接播放下一幀,以追趕音頻。
按照主時鐘的不同種類,可以將音視頻同步模式分為如下三種:
- 音頻同步到視頻,視頻時鐘作為主時鐘。
- 視頻同步到音頻,音頻時鐘作為主時鐘。(常用)
- 音視頻同步到外部時鐘,外部時鐘作為主時鐘。

三、FFplay 播放器
FFmpeg 播放器的整體框架圖 
2、ffplay 的初體驗(yàn)及快捷鍵
ffplay 是 ffmpeg 工程中提供的播放器,功能相當(dāng)?shù)膹?qiáng)大,凡是 ffmpeg 支持的視音頻格式它基本上都支持。
甚至連 VLC 不支持的一些流媒體都可以播放(比如說 RTMP),但是它的缺點(diǎn)是其不是圖形化界面的,必須通過鍵盤來操作。
通過 ffplay 播放 D:\Work\test 目錄下的 SampleVideo_1280x720_20mb.mp4 文件
ffplay -i SampleVideo_1280x720_20mb.mp4
 可以看到視頻正在被播放,按空格是暫停,左右鍵是快退和快進(jìn) 
3、ffplay 模塊劃分
- 解析輸入
- 打開碼流
- 音視頻隊列
- 音視頻解碼
- 播放控制
- 開始播放
- 停止播放
- 暫停播放
- 跳到(seek)指定位置播放
- 音頻輸出
- 視頻輸出
- ffplay 快捷鍵支持
4、ffplay 原理及流程

- av_register_all():用于注冊所有muxers、demuxers 與 protocols;
- avformat_open_input():根據(jù)傳入的 url 確定了要使用的協(xié)議 URLProtocol,比如 http 的或是 file 類型的協(xié)議;
- av_find_stream_info():獲取更多的碼流信息。比如獲取視頻幀率、視頻寬高,重新計算最大分析時長,打開解碼器解碼獲取 codec 數(shù)據(jù);
- avcodec_find_decoder():查找解碼器;
- avcodec_open():打開編解碼器;
- av_read_frame():讀取音視頻幀;
- avcodec_decode_video2():解碼一幀視頻數(shù)據(jù);
SDL 顯示 YUV 圖像的流程圖: 
- SDL_Init():主要是初始化內(nèi)存等;
- SDL_SetVideoMode():設(shè)置顯示模式,創(chuàng)建 SDL 窗口,并指定圖像尺寸及像素個數(shù);
- SDL_Surface:是一種圖片數(shù)據(jù)類型,包含了圖片的全部像素點(diǎn),以及渲染這張圖片需要的其它數(shù)據(jù);
- SDL_CreateYUVOverlay():創(chuàng)建畫布對象;
- SDL_Overlay:用于顯示YUV數(shù)據(jù);
- SDL_DisplayYUVOverlay():讓 SDL 來渲染我們給它的數(shù)據(jù);
四、FFmpeg 編解碼及轉(zhuǎn)碼
1、FFmpeg 轉(zhuǎn)碼全流程簡介
FFmpeg 常規(guī)處理流程  大流程可以劃分為輸入、輸出、轉(zhuǎn)碼、播放四大塊
其中轉(zhuǎn)碼涉及比較多的處理環(huán)節(jié),從圖中可以看出,轉(zhuǎn)碼功能在整個功能圖中占比很大,轉(zhuǎn)碼的核心功能在解碼和編碼兩個部分,但在一個可用的示例程序中,編碼解碼與輸入輸出是難以分割的。
解復(fù)用器為解碼器提供輸入,解碼器會輸出原始幀,對原始幀可進(jìn)行各種復(fù)雜的濾鏡處理,濾鏡處理后的幀經(jīng)編碼器生成編碼幀,多路流的編碼幀經(jīng)復(fù)用器輸出到輸出文件。
2、FFmpeg 轉(zhuǎn)碼步驟分析
- 解復(fù)用
- 從輸入文件中讀取編碼幀,判斷流類型,根據(jù)流類型將編碼幀送入視頻解碼器或音頻解碼器。
- 解碼
- 濾鏡
- 提供多種多樣的濾鏡,用來處理原始幀數(shù)據(jù) FFmpeg。
- 編碼
- 復(fù)用
五、FFmpeg 特效處理 libavfilter
1、libavfilter 介紹
libavfilter 是 FFmpeg 提供的濾波器類,可以用其做一些音視頻處理,如音視頻倍速、水平翻轉(zhuǎn)、裁剪、加方框、疊加文字等功能。
例如之前介紹過的音頻重采樣,視頻的像素格式轉(zhuǎn)換,本質(zhì)上也是濾波,所以 libavfilter 也可以實(shí)現(xiàn) libswresample、libswscale 提供的對音視頻格式變換的功能。
2、ffmpeg 添加水印基本流程
這里主要講述如何利用 ffmpeg 向視頻文件添加水印這一功能,文中最后會給出源代碼下載地址以及視頻下載地址,視頻除了講述添加水印的基本原理以及代碼實(shí)現(xiàn),還提到了要注意的一些地方,因?yàn)橹苯舆\(yùn)行 demo 源碼可能會有問題。
利用 ffmpeg 向視頻文件添加水印的基本原理是將視頻文件的視頻包解碼成一幀幀 “Frame”,通過 ffmpeg Filter(overlay)實(shí)現(xiàn)待添加水印與 “Frame” 的疊加,最后將疊加后的視頻幀進(jìn)行編碼并將編碼后的數(shù)據(jù)寫到輸出文件里。
基本的流程如下圖所示: 
3、ffmpeg 向視頻中添加文字
ffmpeg 支持添加文字能,具體如何將文字疊加到視頻中的每一張圖片,ffmpeg 調(diào)用了文字庫 FreeSerif.ttf。
當(dāng)我們用到 ffmpeg 添加文字功能時我們需要先下載改文字庫,下載地址是:http://www./free-serif.font,這算是前期準(zhǔn)備工作。  準(zhǔn)備工作完成以后,介紹下 ffmpeg 實(shí)現(xiàn)視頻文件添加文字功能的基本流程,流程圖如下圖所示: 
4、實(shí)戰(zhàn)測試
首先我們先截取一個 10s 的本地視頻文件
ffmpeg -ss 0 -t 10 -i SampleVideo_1280x720_20mb.flv -c copy -f flv -y SampleVideo_1280x720_20mb_10s.flv
回顯如下
D:\Work\test>ffmpeg -ss 0 -t 10 -i SampleVideo_1280x720_20mb.flv -c copy -f flv -y SampleVideo_1280x720_20mb_10s.flv
ffmpeg version 6.0-essentials_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 12.2.0 (Rev10, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libvpl --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
libavutil 58. 2.100 / 58. 2.100
libavcodec 60. 3.100 / 60. 3.100
libavformat 60. 3.100 / 60. 3.100
libavdevice 60. 1.100 / 60. 1.100
libavfilter 9. 3.100 / 9. 3.100
libswscale 7. 1.100 / 7. 1.100
libswresample 4. 10.100 / 4. 10.100
libpostproc 57. 1.100 / 57. 1.100
Input #0, flv, from 'SampleVideo_1280x720_20mb.flv':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf60.3.100
Duration: 00:01:57.31, start: 0.000000, bitrate: 1442 kb/s
Stream #0:0: Video: h264 (Main), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 1048 kb/s, 25 fps, 25 tbr, 1k tbn
Stream #0:1: Audio: aac (LC), 48000 Hz, 5.1, fltp, 383 kb/s
Output #0, flv, to 'SampleVideo_1280x720_20mb_10s.flv':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf60.3.100
Stream #0:0: Video: h264 (Main) ([7][0][0][0] / 0x0007), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 1048 kb/s, 25 fps, 25 tbr, 1k tbn
Stream #0:1: Audio: aac (LC) ([10][0][0][0] / 0x000A), 48000 Hz, 5.1, fltp, 383 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
frame= 250 fps=0.0 q=-1.0 Lsize= 1723kB time=00:00:09.98 bitrate=1414.0kbits/s speed=1.46e+03x
video:1244kB audio:467kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.769128%
D:\Work\test>

 使用 Everything 工具找到 simhei.ttf 將其拷貝到待添加水印的視頻目錄下  接下來我們在這個 10s 的視頻上面添加一個文字跑馬燈水印,執(zhí)行下面命令:
ffmpeg -i SampleVideo_1280x720_20mb_10s.flv -acodec aac -vcodec libx264 -r 30 -g 300 -vf "drawtext=fontfile=simhei.ttf:text='hello world!':x=10:y=h-80:enable=lt(mod(t\,3)\,2):fontsize=50:fontcolor=red@0.5:shadowy=2" -f flv -y SampleVideo_1280x720_20mb_wm2.flv
- -i:設(shè)定輸入流
- -acodec aac:指定音頻 aac 編碼
- -vcodec libx264:設(shè)定視頻編解碼器為 libx264
- -r 30:一秒鐘播 30 幀
- -g 300:GOP,300 個幀里面才有一個 I 幀
- -vf:視頻過濾器
- enable=lt(mod(t,3),2):播放時間對 3 求余數(shù),當(dāng)小于等于 2 的時候顯示,也就是說每 3 秒中顯示 2 秒滅 1 秒
- @0.5:透明度
- shadowy=2:陰影
執(zhí)行后回顯如下:
D:\Work\test>ffmpeg -i SampleVideo_1280x720_20mb_10s.flv -acodec aac -vcodec libx264 -r 30 -g 300 -vf "drawtext=fontfile=simhei.ttf:text='你好: hello':x=10:y=h-80:enable=lt(mod(t\,3)\,2):fontsize=50:fontcolor=red@0.5:shadowy=2" -f flv -y SampleVideo_1280x720_20mb_wm2.flv
ffmpeg version 6.0-essentials_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 12.2.0 (Rev10, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libvpl --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
libavutil 58. 2.100 / 58. 2.100
libavcodec 60. 3.100 / 60. 3.100
libavformat 60. 3.100 / 60. 3.100
libavdevice 60. 1.100 / 60. 1.100
libavfilter 9. 3.100 / 9. 3.100
libswscale 7. 1.100 / 7. 1.100
libswresample 4. 10.100 / 4. 10.100
libpostproc 57. 1.100 / 57. 1.100
Input #0, flv, from 'SampleVideo_1280x720_20mb_10s.flv':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf60.3.100
Duration: 00:00:10.01, start: 0.000000, bitrate: 1411 kb/s
Stream #0:0: Video: h264 (Main), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 1048 kb/s, 25 fps, 25 tbr, 1k tbn
Stream #0:1: Audio: aac (LC), 48000 Hz, 5.1, fltp, 383 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (aac (native) -> aac (native))
Press [q] to stop, [?] for help
Fontconfig error: Cannot load default config file: No such file: (null)
[Parsed_drawtext_0 @ 0000020b250b80c0] Using "C:/Windows/fonts\CascadiaCode.ttf"
[libx264 @ 0000020b24cce480] using SAR=1/1
[libx264 @ 0000020b24cce480] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0000020b24cce480] profile High, level 3.1, 4:2:0, 8-bit
[libx264 @ 0000020b24cce480] 264 - core 164 r3106 eaa68fa - H.264/MPEG-4 AVC codec - Copyleft 2003-2023 - http://www./x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=22 lookahead_threads=3 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=300 keyint_min=30 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, flv, to 'SampleVideo_1280x720_20mb_wm2.flv':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf60.3.100
Stream #0:0: Video: h264 ([7][0][0][0] / 0x0007), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 30 fps, 1k tbn
Metadata:
encoder : Lavc60.3.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
Stream #0:1: Audio: aac (LC) ([10][0][0][0] / 0x000A), 48000 Hz, 5.1, fltp, 341 kb/s
Metadata:
encoder : Lavc60.3.100 aac
frame= 300 fps=196 q=-1.0 Lsize= 2018kB time=00:00:09.98 bitrate=1655.5kbits/s dup=50 drop=0 speed=6.52x
video:1582kB audio:422kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.706711%
[libx264 @ 0000020b24cce480] frame I:2 Avg QP:16.30 size: 76289
[libx264 @ 0000020b24cce480] frame P:162 Avg QP:21.71 size: 7884
[libx264 @ 0000020b24cce480] frame B:136 Avg QP:27.74 size: 1392
[libx264 @ 0000020b24cce480] consecutive B-frames: 28.0% 32.7% 6.0% 33.3%
[libx264 @ 0000020b24cce480] mb I I16..4: 22.7% 32.6% 44.7%
[libx264 @ 0000020b24cce480] mb P I16..4: 0.9% 1.5% 0.2% P16..4: 28.7% 7.2% 3.6% 0.0% 0.0% skip:58.0%
[libx264 @ 0000020b24cce480] mb B I16..4: 0.1% 0.1% 0.0% B16..8: 28.1% 0.8% 0.1% direct: 0.2% skip:70.7% L0:48.3% L1:49.7% BI: 2.0%
[libx264 @ 0000020b24cce480] 8x8 transform intra:49.5% inter:54.9%
[libx264 @ 0000020b24cce480] coded y,uvDC,uvAC intra: 51.3% 66.0% 23.6% inter: 7.1% 9.2% 0.5%
[libx264 @ 0000020b24cce480] i16 v,h,dc,p: 28% 31% 11% 30%
[libx264 @ 0000020b24cce480] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 26% 22% 20% 4% 6% 6% 5% 5% 6%
[libx264 @ 0000020b24cce480] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 22% 14% 11% 8% 8% 9% 7% 12% 7%
[libx264 @ 0000020b24cce480] i8c dc,h,v,p: 51% 20% 21% 8%
[libx264 @ 0000020b24cce480] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 @ 0000020b24cce480] ref P L0: 81.7% 11.4% 5.9% 1.0%
[libx264 @ 0000020b24cce480] ref B L0: 95.0% 4.8% 0.2%
[libx264 @ 0000020b24cce480] ref B L1: 98.3% 1.7%
[libx264 @ 0000020b24cce480] kb/s:1295.36
[aac @ 0000020b250d0b80] Qavg: 1102.865

可以看到本地生成了一個 SampleVideo_1280x720_20mb_wm2.flv 文件  打開視頻可以看到左下角 hello world 閃爍效果 
|