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