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.pngwoodman.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.pngwoodman.330.png,串接為幀率為30FPS、長度為10秒的影片。

圖片檔名有經過字典排序

如果連續的圖片檔案的檔名是image-a.pngimage-b.pngimage-c.png、……,或者是image001.pngimage002.pngimage004.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/目錄,因此勢必得使用絕對路徑來連結圖片檔案才行。