FFmpeg全名是Fast Forward MPEG(Moving Picture Experts Group),為開源的影音多媒體處理框架,可以進行影音的解碼、編碼、編碼轉換、混合、抽取、串流和濾鏡,無論影音格式是從哪個地方出來的,從過去到現在的影音格式它幾乎都能夠支援。當然,我們也可以用它來將連續的圖片編碼成影片。
影片是由一個一個影格(frame)組成,一個影格就是一張靜態圖片。在製作影片的時候就是先繪製出每個時間點的靜態圖片,最後再將這些靜態圖片(設數量為#{{N}}#)組合起來,並定義每張圖片的播放持續時間(設持續時間為#{{D}}#),最後生成的影片之FPS(frames per second)就會是每張圖片的播放持續時間的倒數(#{{1 \over D}}#),影片長度則是靜態圖片的數量乘上每張圖片的播放持續時間(#{{ N \times D }}#)。
FPS是影片幀率(Frame Rate)的常用單位,在使用FFmpeg編碼影片的時候,可以在-i
參數之前使用-r
參數來設定-i
參數所輸入的影像資源的幀率,也可以在-i
參數之後使用-r
參數設定輸出影片的幀率。
假設我們在使用FFmpeg的時候,於-i
參數前將-r
參數的值設為#{{r_1}}#,又於-i
參數後將-r
參數的值設為#{{r_2}}#,則最終生成的影片長度會去參考#{{r_1}}#,影片幀率則是會參考#{{r_2}}#。若#{{ r_1 \gt r_2 }}#,輸出的影片會自動丟幀(例如#{{r_1 = 30, r_2 = 15}}#,原先一秒要播放30張圖,變成一秒播放15張圖,並把每張圖的播放時間變成兩倍);若#{{ r_1 \lt r_2 }}#,輸出的影片會自動克隆(clone)出不足的幀數(例如#{{r_1 = 30, r_2 = 60}}#,原先一秒要播放30張圖,變成一秒播放60張圖,並把張圖的播放時間縮短為一半,多出來的30張圖就是把原先那秒內的每張圖都各複製一份出來)。
要使用FFmpeg將連續的圖片轉成影片,可分為三種情形來探討。
圖片檔名有連續不中斷的數字編號
可以先下載這個範例,才能一邊看說明一邊試做,會更容易理解。將範例解壓縮後,會得到330張連續的靜態圖片。檔名格式為woodman.號碼.png
,其中的號碼
是連續不中斷的,從1開始計數,不補零。
要將這些圖片串接成影片,FFmpeg指令可以這樣撰寫:
ffmpeg -r 30 -i woodman.%d.png 輸出的影片檔案路徑
以上指令,可以將圖片woodman.1.png
到woodman.330.png
,串接為幀率為30FPS、長度為11秒的影片。這邊要注意的是,在Windows的命令提示字元下,%
字元需要使用%
字元來跳脫,也就是要連續使用兩個%
來表示一個%
字元,因此在Windows的命令提示字元下,指令應寫成:
ffmpeg -r 30 -i woodman.%%d.png 輸出的影片檔案路徑
若是圖片檔名中的編號是有補零的,假設補零補到至少兩個位數,則FFmpeg指令可以這樣撰寫:
ffmpeg -r 30 -i woodman.%02d.png 輸出的影片檔案路徑
依此類推,若是補零補到至少三個位數,就要把%02d
改成%03d
。當然也是一樣,在Windows的命令提示字元下,要連續使用兩個%
來表示一個%
字元。
在-i
參數前加上-start_number
參數,可以設定起始的圖片檔案編號。例如:
ffmpeg -r 30 -start_number 30 -i woodman.%d.png 輸出的影片檔案路徑
以上指令,可以將圖片woodman.30.png
到woodman.330.png
,串接為幀率為30FPS、長度為10秒的影片。
圖片檔名有經過字典排序
如果連續的圖片檔案的檔名是image-a.png
、image-b.png
、image-c.png
、……,或者是image001.png
、image002.png
、image004.png
(003
雖然被跳號了,但無所謂)、image005.png
、……。
要將這些圖片串接成影片,FFmpeg指令可以這樣撰寫:
ffmpeg -r 30 -pattern_type glob -i "image*.png" 輸出的影片檔案路徑
注意,在-i
參數前加上-pattern_type glob
,-i
的路徑中一定要包含星號*
,並且要使用單引號'
或是雙引號"
括起來。
圖片檔名無順序
如果連續的圖片檔案的檔名並無順序,就要使用FFmpeg的-f concat
參數來實現串接了。
在-i
參數前加上-f concat
,可以讓-i
輸入一個文字檔案,這個文字檔案的撰寫方式如下:
# this is a comment file '/path/to/abc.png' file /path/to/987.png file /path/to/123.png
檔案的讀取順序是從上到下,檔案路徑若要加引號括起來,只能夠使用單引號。假設這個文字檔案的檔名為concat.txt
,則以下的FFmpeg指令:
ffmpeg -r 1 -f concat -safe 0 -i concat.txt -r 30 輸出的影片檔案路徑
以上指令,可以將圖片/path/to/abc.png
、/path/to/987.png
、/path/to/123.png
,串接為幀率為30FPS、長度為3秒的影片。-safe 0
參數用來關閉路徑的保護功能,讓-i
輸入的文字檔中的路徑可以使用絕對路徑。若使用相對路徑,則是相對於文字檔的路徑,並不是相對於目前的工作目錄。
若不想要另外新增文字檔案的話,在Linux作業系統下,指令也可以寫成這樣:
ffmpeg -r 1 -f concat -safe 0 -i <(echo "file '/path/to/abc.png'"; echo "file /path/to/987.png"; echo "file /path/to/123.png") -r 30 輸出的影片檔案路徑
直接在指令中產生文字檔案的內容餵給FFmpeg服用,而像這種<(...)
的指令用法稱為「行程替換」(process substitution)。Linux作業系統會自動將<(...)
替換為/dev/fd/
目錄下的檔案,檔案內容則是由<(...)
括號內的指令輸出的。由於這樣的用法會導致文字檔的相對路徑是相對於/dev/fd/
目錄,因此勢必得使用絕對路徑來連結圖片檔案才行。