前言
FFMpeg讀做“FF Mpeg”,“FF”指的是“Fast Forward”,而“Mpeg”指的是Moving Picture Experts Group(動(dòng)態(tài)圖像專家組)。
根據(jù)官方介紹,F(xiàn)FMpeg是一個(gè)完整的、跨平臺(tái)的音頻和視頻錄制、轉(zhuǎn)換和流媒體解決方案。簡(jiǎn)單來說,只要涉及音視頻開發(fā),基本繞不開這個(gè)工具。
一、快速入門
FFMpeg快速入門的話,建議查看阮一峰老師的《FFmpeg 視頻處理入門教程》,里面講述了音視頻處理的一些基本概念,比如FFMpeg支持的容器、編碼格式以及編碼器;還有就是講述FFMpeg的常見用法,比如查看文件信息、轉(zhuǎn)換編碼格式、提取音頻等。
二、音視頻基礎(chǔ)知識(shí)
我自己在使用FFMpeg的時(shí)候發(fā)現(xiàn),想要把FFMpeg用得明白,一些基本的音視頻基礎(chǔ)知識(shí)的了解還是很有必要的,所以在這里做下總結(jié)。
現(xiàn)在短視頻那么火,相信大家也是常看,而一個(gè)視頻的構(gòu)成其實(shí)也不復(fù)雜,就是圖像、音頻、字幕的一個(gè)組合。
對(duì)于圖像,它有兩個(gè)概念需要區(qū)分好,分別是圖像格式和色彩空間。圖像格式就是圖片壓縮編碼以及存儲(chǔ)的方式,比如我們常見的JPEG和PNG。色彩空間是顏色的數(shù)學(xué)描述方式,根據(jù)不同的表示方法分為不同的色彩模型,最常用的色彩模型有三類,RGB(用于計(jì)算機(jī)圖形學(xué)),YUV(用于視頻系統(tǒng)), CMYK(用于彩色印刷)。(后面會(huì)經(jīng)常看到Y(jié)UV)
對(duì)于音頻,也有兩個(gè)概念比較重要,一個(gè)是采集到的原始音頻數(shù)據(jù)(比如PCM),另一個(gè)是壓縮后的音頻數(shù)據(jù),比如AAC,后面也會(huì)經(jīng)常看到。
對(duì)于字幕,常見的有三種格式,分別是srt、ssa和aas。
srt字幕即文本格式字幕,它算是最簡(jiǎn)單的字幕了,因?yàn)樗鼉H由時(shí)間和字幕內(nèi)容構(gòu)成,比如下面:
# 第一行是編號(hào),表示第幾個(gè)字幕
# 第二行是時(shí)間范圍,精確到毫秒
# 第三話就是顯示的文本內(nèi)容
0
00:00:00,000 --> 00:00:01,000
假設(shè)張三攜帶10萬美刀進(jìn)行投資
1
00:00:02,000 --> 00:00:03,000
兌換成人民幣后,銀行就多了10萬美刀的外匯
復(fù)制代碼
ssa字幕是比srt字幕更先進(jìn)的字幕文件格式,而與它比較類似的ass字幕其實(shí)就是ssa字幕的plus版本,ass字幕的實(shí)質(zhì)是SSA v4.00+,是基于SSA 4.00+編碼構(gòu)建的。下面是ass字幕的具體內(nèi)容:
# 這是從上面的srt字幕轉(zhuǎn)換得到的ass字幕
# Script Info:包含腳本的頭部和總體信息
# V4+ Styles:包含了所有樣式的定義
# Events:包含了所有腳本的事件,有字幕、注釋、圖片等
[Script Info]
; Script generated by FFmpeg/Lavc58.91.100
ScriptType: v4.00+
PlayResX: 384
PlayResY: 288
ScaledBorderAndShadow: yes
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:01.00,Default,,0,0,0,,假設(shè)張三攜帶10萬美刀進(jìn)行投資
Dialogue: 0,0:00:02.00,0:00:03.00,Default,,0,0,0,,兌換成人民幣后,銀行就多了10萬美刀的外匯
復(fù)制代碼
三、一個(gè)視頻的構(gòu)建
我之所以要用FFMpeg,源于我想通過圖片生成視頻,并加上音頻和字幕,從而構(gòu)成一個(gè)完成的視頻,所以下面我主要說說在構(gòu)建時(shí)的一些心路歷程(坑)。
3.1 項(xiàng)目結(jié)構(gòu)
本次實(shí)踐生成的音視頻都會(huì)上傳到Github,可以點(diǎn)擊這里查看:
# 項(xiàng)目結(jié)構(gòu)
$ tree -l -L 1
.
├── add_audio # 添加音頻
├── add_caption # 添加字幕
└── img_to_video # 圖片轉(zhuǎn)視頻
復(fù)制代碼
3.2 圖片生成視頻
為了方便展示,我從網(wǎng)上隨便找了一張圖片:
圖片轉(zhuǎn)視頻的命令如下:
$ ffmpeg -r 25 -i img001.jpg -vcodec libx264 -pix_fmt yuv420p one_img_to_video.mp4
...
[libx264 @ 0x7faf5b809200] i8c dc,h,v,p: 65% 19% 9% 7%
[libx264 @ 0x7faf5b809200] kb/s:8960.40
復(fù)制代碼
下面是各個(gè)參數(shù)的逐個(gè)解析:
- -r:rate,用于設(shè)定視頻幀率。視頻幀率即每秒顯示幀數(shù),常見的有30FPS、25FPS或者24FPS。本次設(shè)定為25FPS,即每秒有25張圖片。
- -i:input,即輸入源文件。
- -vcodec:video codec,即視頻的編碼格式,常見的有H.264,即libx264。
- -pix_fmt:pixel formats,即像素格式,yuv420p是上文提到的YUV中的一種。
- one_img_to_video.mp4:最后輸出的文件名。
生成之后的視頻,可以看到時(shí)長(zhǎng)非常短(0秒),這是因?yàn)閹试O(shè)定是25,但是只輸入了一張圖片,圖片數(shù)不夠,所以生成的視頻時(shí)長(zhǎng)非常短。
解決辦法有兩種:一是降低幀率(不推薦),二是增加圖片數(shù)量(推薦)。
我一開始是通過降低幀率來提高時(shí)長(zhǎng)(我的需求是同一張圖片要顯示10秒左右),因?yàn)?5FPS就是一秒25張圖片,那如果設(shè)置為0.1FPS,等同于1張圖片10秒,測(cè)試如下:
$ ffmpeg -r 0.1 -i img001.jpg -vcodec libx264 -pix_fmt yuv420p one_img_to_video_small_rate.mp4
復(fù)制代碼
通過下圖,可以看到延長(zhǎng)時(shí)長(zhǎng)的目的確實(shí)達(dá)到了,但是這種方式生成的MP4其實(shí)是有問題的,不僅剪輯軟件無法支持(比如剪映),在添加音頻、字幕的時(shí)候也非常奇怪(血的教訓(xùn))。
第二種方式是增加圖片數(shù)量,這也是我使用剪映之后發(fā)現(xiàn)的,因?yàn)?strong>與剪映拖動(dòng)圖片增加視頻長(zhǎng)度的原理是一致的:
批量增加圖片可以隨便寫個(gè)腳本就可以得到,但是圖片的數(shù)量需要計(jì)算一下,比如一個(gè)時(shí)長(zhǎng)10秒,幀率25FPS的視頻就需要 10 x 25 = 250張圖片:
# 輸入為多張圖片時(shí),可使用這種寫法
# %03d 其實(shí)就是 001、002、003...100
$ cd img_to_video
$ ffmpeg -r 25 -i img/img%03d.jpg -vcodec libx264 -pix_fmt yuv420p multi_img_to_video.mp4
復(fù)制代碼
這里可能有人會(huì)疑惑,為什么每次我都會(huì)帶上 -pix_fmt yuv420p參數(shù)?這其實(shí)也是一個(gè)坑,因?yàn)槿绻患舆@個(gè)參數(shù),有些軟件沒辦法識(shí)別生成的MP4文件,比如mac 的QuickTime Player。
原因可以從官方文檔得到,因?yàn)槲覀兩梢曨l的方式其實(shí)是通過圖像序列(一系列的圖片)的方式,對(duì)應(yīng)的編碼類型為image2,這也是為什么有時(shí)在一些文章上可以看到他們的命令比上述命令多了 -f image2參數(shù)(加不加都無所謂)。在這種編碼下,默認(rèn)的pix_fmt參數(shù)并不是yuv420p,而是通過第一張圖片得到,而JPG圖片用的都是RGB,所以最終生成的視頻無法識(shí)別。
3.2 視頻添加音頻
通過上面的方式生成的視頻是沒有聲音的,所以我們需要通過FFMpeg為其加上音頻。
有時(shí)候我們得到的音頻格式并不是MP3,而是WAV,這時(shí)我們可以通過下面的命令進(jìn)行轉(zhuǎn)換:
$ ffmpeg -i input.wav -vn -ar 44100 -ac 2 -b:a 192k output.mp3
-i: 上文也提到過,即我們的輸入文件
-vn:禁用視頻,確保沒有視頻被包括在內(nèi)
-ar:設(shè)置音頻采樣頻率。對(duì)于輸出流,它默認(rèn)設(shè)置為相應(yīng)的輸入流的頻率。對(duì)于輸入流,這個(gè)選項(xiàng)只對(duì)音頻抓取設(shè)備和原始解復(fù)用器有意義,并被映射到相應(yīng)的解復(fù)用器選項(xiàng)中。
-ac:設(shè)置音頻通道的數(shù)量。這里為2是為了確保它是立體聲(2個(gè)通道)。對(duì)于輸出流,它默認(rèn)設(shè)置為輸入音頻通道的數(shù)量。對(duì)于輸入流,這個(gè)選項(xiàng)只對(duì)音頻抓取設(shè)備和原始解復(fù)用器有意義,并被映射到相應(yīng)的解復(fù)用器選項(xiàng)中。
-b:a:將音頻比特率(audio bitrate)轉(zhuǎn)換為精確的192kbit/秒
復(fù)制代碼
上面的解釋涉及到解復(fù)用這個(gè)術(shù)語,那什么是解復(fù)用呢?當(dāng)我們打開一個(gè)多媒體文件之后,第一步就是解復(fù)用,稱之為Demux。為什么需要這一步,這一步究竟是做什么的?我們知道在一個(gè)多媒體文件中,既包括音頻也包括視頻,而且音頻和視頻都是分開進(jìn)行壓縮的,因?yàn)橐纛l和視頻的壓縮算法不一樣,既然壓縮算法不一樣,那么肯定解碼也不一樣,所以需要對(duì)音頻和視頻分別進(jìn)行解碼。雖然音頻和視頻是分開進(jìn)行壓縮的,但是為了傳輸過程的方便,還是將壓縮過的音頻和視頻捆綁在一起進(jìn)行傳輸。所以我們解碼的第一步就是將這些綁在一起的音頻和視頻流分開來,也就是傳說中的**解復(fù)用。**簡(jiǎn)單來說,解復(fù)用這一步就是將音頻流和視頻流分開,方便后續(xù)解碼。
轉(zhuǎn)換之后就可以為視頻添加音頻了,這里使用的視頻是上文生成的圖片視頻(注意添加音頻也能用wav格式,只不過我習(xí)慣用mp3)
# 拷貝視頻
$ cp img_to_video/multi_img_to_video.mp4 add_audio/input.mp4
# 添加音頻有多種方式:
# 方式一:流拷貝(不推薦)
# 這種方式?jīng)]有編解碼的過程,只有解復(fù)用,所以速度很快,目前親測(cè)不成功,不太建議
$ ffmpeg -i input.mp4 -i input.mp3 -codec copy audio_copy.mp4
# 方式二:手動(dòng)選擇特定流(不推薦,親測(cè)無效)
$ ffmpeg -i input.mp4 -i input.mp3 -map 0:v -map 1:a -c copy audio_manually.mp4
# 方式三:重新編碼(親測(cè)有效)
$ ffmpeg -i input.mp4 -i input.mp3 -c:a aac -c:v libx264 audio_recode.mp4
# 有時(shí)候我們的音頻長(zhǎng)度大于視頻長(zhǎng)度,比如本次音頻長(zhǎng)度為20s,視頻長(zhǎng)度為10s,使用上面的命令會(huì)把視頻長(zhǎng)度拉長(zhǎng)到20s
# 如果想要音頻長(zhǎng)度與視頻長(zhǎng)度保持一致,可加上 -shortest 參數(shù)
$ ffmpeg -i input.mp4 -i input.mp3 -c:a aac -c:v libx264 -shortest audio_recode_short.mp4
復(fù)制代碼
3.3 視頻添加字幕
添加完音頻后,就可以添加字幕了,關(guān)于字幕轉(zhuǎn)換工具,可以自己手寫一個(gè),也可以用現(xiàn)成的,比如下面這個(gè):
TXT to SRT Converter
使用起來也非常方便,每一行就是一行字幕,最后設(shè)置好起始時(shí)間就可以了(不一定與實(shí)際朗讀匹配):
添加srt字幕的命令如下:
# 拷貝之前生成好的視頻
$ cp add_audio/audio_recode.mp4 add_caption/input.mp4
# 添加字幕
$ ffmpeg -i input.mp4 -vf subtitles=input.srt video_with_srt.mp4
# 有時(shí)候可能會(huì)遇到下面的報(bào)錯(cuò):Too many packets buffered for output stream 0:1
# 該異常拋出的原因是有些視頻數(shù)據(jù)有問題,導(dǎo)致視頻處理過快,容器封裝時(shí)隊(duì)列溢出
# 可以通過增大容器封裝隊(duì)列大小來解決,比如設(shè)置最大封裝隊(duì)列的大小為1024
$ ffmpeg -i input.mp4 -vf subtitles=input.srt -max_muxing_queue_size 1024 video_with_srt.mp4
復(fù)制代碼
有時(shí)候我們需要自定義字幕的樣式,或者字幕的位置,這時(shí)可以先把srt字幕轉(zhuǎn)換為ass字幕,再做調(diào)整。如果你安裝了FFMpeg,一行命令就能完成轉(zhuǎn)換,如果沒有安裝,也可以用一些在線工具實(shí)現(xiàn),比如字幕醬。
FFMpeg轉(zhuǎn)換命令:
$ ffmpeg -i input.srt output.ass
復(fù)制代碼
添加ass字幕命令:
$ ffmpeg -i input.mp4 -vf "ass=output.ass" video_with_ass.mp4
復(fù)制代碼
最終效果如下:
如果想要控制字幕使用的文字、文字大小、以及顯示位置等,則需要修改[V4+ Styles]里面的內(nèi)容:
# 一共分為兩行,第一行是字段名,第二行是字段值
# Fontname:字型
# Fontsize:字體大小
# MarginL:字幕距左邊的距離,取值范圍是0-PlayResX的數(shù)值
# MarginR:字幕距右邊的距離,取值范圍是0-PlayResX的數(shù)值
# MarginV:字幕高度,取值范圍是0-PlayResY的數(shù)值
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0
復(fù)制代碼
注:其他參數(shù)的說明可參考這篇文章
假設(shè)我要把字幕大小改為20、且字幕往上移動(dòng),則對(duì)應(yīng)的改動(dòng)如下:
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,50,0
復(fù)制代碼
最后重新添加即可:
$ ffmpeg -i input.mp4 -vf "ass=new.ass" video_with_new_ass.mp4
復(fù)制代碼
最終效果如下:
寫在最后
以上就是如何用FFMpeg構(gòu)建完成視頻的全流程了,希望對(duì)大家有所幫助!






