ffmpeg 的參數很不好下正確, 要練習一下

查詢 input file 中的 streams

$ ffmpeg -i 25020.vob 
Seems stream 0 codec frame rate differs from container frame rate: 59.94 (60000/1001) -> 29.97 (30000/1001) 
Input #0, mpeg, from '/media/49B8-E524/songs/025/25020.vob': 

Duration: 00:04:17.31, start: 4596.681567, bitrate: 6174 kb/s 

Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x480 [PAR 8:9 DAR 4:3], 9800 kb/s, 29.97 tbr, 90k tbn, 59.94 tbc 

Stream #0.1[0x80]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 

Stream #0.2[0x81]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 
At least one output file must be specified

從上面可以看到, 25020.vob 有 3 個 stream - 1 個 mpeg2video 格式的 video stream, 2 個 ac3 格式的 audio stream

Stream 前有個 n.m 的序號, 後面接著 unique ID (不一定會有). n 指的是第幾個 input, m 指的是在 input 中, 這個 stream 出現的順序. 例如我們只有一個 input, 所以上面顯示了
Input #0, mpeg, from '/media/49B8-E524/songs/025/25020.vob':
那屬於它的 stream 序號就會是 0.m. 接著, 第一個 stream 是 video, 序號是 0.0, 第二個 stream 是 audio, 序號是 0.1, 依此類推.

在 input 後可加上檔名做為輸出對像, 例如

$ ffmpeg -i 25020.vob test.vob

ffmpeg 就會從 25020.vob 抽出 audio + video 共 2 個 streams 並寫入 test.vob - 通常這樣的行為都不會是我們想要的, 而且, ffmpeg 不會將 input 的格式保持住, 所以當我們檢視 test.vob 的內容時

$ ffmpeg -i test.vob 
Seems stream 0 codec frame rate differs from container frame rate: 59.94 (60000/1001) -> 29.97 (30000/1001) 
Input #0, mpeg, from 'test.vob': 

Duration: 00:04:17.29, start: 0.500000, bitrate: 583 kb/s 

Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x480 [PAR 8:9 DAR 4:3], 104857 kb/s, 29.97 tbr, 90k tbn, 59.94 tbc 

Stream #0.1[0x1c0]: Audio: mp2, 48000 Hz, stereo, s16, 64 kb/s 
At least one output file must be specified

會發現 video stream 的 bitrate 改變了, audio stream 的壓縮方式也變了...

如果我們想把 video stream 抽出, 可以這麼做

$ ffmpeg -i 25020.vob -an -vcodec copy test.vob

-an 告訴 ffmpeg 對於 test.vob 這個 output 而言, 拿掉 input audio stream, 並且以參數 -vcodec 指定 video stream 只是簡單的 copy 到 test.vob, 不對它做任何的改變.

參數多起來, 有點亂了吧~ ffmpeg 怎麼知道那個參數是給 input 的, 哪個是給 output 的? 大致的格式如下

$ ffmpeg [input arguments] -i [input file] [output arguments] [output file]

也就是說, 參數大多都是放在 input file 跟 output file 之前. 並且, 一個 input 可以有多個 output, 一個 output 也可以有多個 input, 但, 就像先前說的, 參數順序很重要.

如果我們想把第一個 audio stream 存到 test.vob, 把第二個 audio stream 存到 test2.vob, 這時候會用到 -map 參數, 命令如下

$ ffmpeg -y -i 25020.vob -map 0.1 test.vob -map 0.2 test2.vob 
Seems stream 0 codec frame rate differs from container frame rate: 59.94 (60000/1001) -> 29.97 (30000/1001) 
Input #0, mpeg, from '25020.vob': 

Duration: 00:04:17.31, start: 4596.681567, bitrate: 6174 kb/s 

Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x480 [PAR 8:9 DAR 4:3], 9800 kb/s, 29.97 tbr, 90k tbn, 59.94 tbc 

Stream #0.1[0x80]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 

Stream #0.2[0x81]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 
Number of stream maps must match number of output streams

不成功, 說 map 數量跟 output stream 數量不合. 原因大概是出在 ffmpeg 會假設一個 video input file 的 output 至少會有一個 video stream 跟 audio stream, 但上面的命令一個輸出 (預期 2 個 input stream) 只給了一個 map (一個 input stram), 對不起來, 後面的動作也就沒法接下去了.

來做個實驗, 看看我們的想法正不正確

$ ffmpeg -y -i 25020.vob -map 0.0 -map 0.1 test.vob -map 0.0 -map 0.2 test2.vob

這個命令看起來是 OK 的, 只是在輸出中多了個 video stream 不是我們想要的. 那如果把 -map 0.0 (video stream) 換成 -map 0.2 及 -map 0.1 呢? 試試

$ ffmpeg -y -i 25020.vob -map 0.2 -map 0.1 test.vob -map 0.2 -map 0.2 test2.vob

Codec type mismatch for mapping #0.2 -> #0.0
果然, 因為我們用 map 把 input audio stream 丟給了 output video stream, 所以這個動做就失敗了, 所以, 為了要讓 output file 能正確的只包含一個 audio stream, 我們可以用 -vn 來將 output 對 video stream 要需求移除, 命令如下

$ ffmpeg -y -i 25020.vob -vn -map 0.1 test.vob -vn -map 0.2 test2.vob

嗯, 能動, 但如果看看 output file 中 audio stream 的壓縮方式, 就會發現又被從 ac3 重壓成 mp2 了, 音質很差, 處理速度也慢. 再 -acodec copy 讓 ffmpeg 不要去碰我們的 output stream

$ ffmpeg -y -i 25020.vob -vn -acodec copy -map 0.1 test.vob -vn -acodec copy -map 0.2 test2.vob

嗯, 看起來處理速度快多了 (只有 copy 嘛), 且 output file 中 stream 的壓縮方式也正確了

那當我們要組合多個檔案到一個裡呢? 這也難不倒 ffmpeg. 假設我們要從 25020.vob 取得 video stream, 並從 test.vob (我們剛才抽出的 audio stream) 取得 audio stream, 然後存到 test3.vob. 先來看看 ffmpeg 有多個 input files 時有什麼訊息

$ ffmpeg -i 25020.vob -i test.vob 
Seems stream 0 codec frame rate differs from container frame rate: 59.94 (60000/1001) -> 29.97 (30000/1001) 
Input #0, mpeg, from '25020.vob': 

Duration: 00:04:17.31, start: 4596.681567, bitrate: 6174 kb/s 

Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x480 [PAR 8:9 DAR 4:3], 9800 kb/s, 29.97 tbr, 90k tbn, 59.94 tbc 

Stream #0.1[0x80]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 

Stream #0.2[0x81]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 
Input #1, mpeg, from 'test.vob': 

Duration: 00:04:17.31, start: 0.500000, bitrate: 390 kb/s 

Stream #1.0[0x80]: Audio: ac3, 48000 Hz, stereo, s16, 384 kb/s 
At least one output file must be specified

所以 input stream 共有 4 個, 0.0 (video), 0.1 (audio), 0.2 (audio), 1.0 (audio). 好, 將之組合起來

ffmpeg -y -i 25020.vob -i test.vob -an -vcodec copy -map 0.0 -acodec copy -map 1.0 test3.vob 
Number of stream maps must match number of output streams

失敗, input stream 跟 output stream 的數量又對不上了... 這時候要用上 -newaudio 來指定 test3.vob 的 audio stream 是從新的 input file 讀進來, 並新增一個 output audio stream

ffmpeg -y -i 25020.vob -i test.vob -an -vcodec copy -map 0.0 test3.vob -acodec copy -map 1.0 -newaudio

文件上說, -newaudio 或 -newvideo 必須要接在 output file 後, 而下給這個新增的 stream 的參數則必須接在 output file 後, -newaudio/-newvideo 之前, 如上命令.

至此, 比較重要的參數順序及什麼 -map/-newaudio/-newvideo 都看過了, 其他的應用應該不會太難才是!