流的操作(一)視頻轉音頻引發的血案
轉發自白狼棧:查看原文
有些小夥伴看文章非常細心,對於上一節課不經意提到的一些邊緣細節都比較在意,比如 -acodec、-vcodec、流複製等。其實這些都離不開我們今天要講的重點——流。
說起流,可能有很多小夥伴第一反應是流媒體,但是我們今天要說的是容器內流的類型。通過前面的介紹,相信你對容器內的音頻(audio, a)和視頻(video, v)都有了一些印象。除此之外,容器內流的類型還有字幕(subtitle, s)、附加數據(attachment, t)和普通數據(data, d)。我們重點介紹一下音頻流、視頻流和字幕流。
流的操作,指的是我們可以從輸入文件中選擇不同的流進行操作,然後輸出我們想要的結果。
舉個例子,家裡有小孩的都應該比較清楚,學校現在有很多英語的配音比賽,大屏幕播放一段視頻,學生在舞台上配音,非常形象。
在這個場景中,大屏幕上播放的視頻,其實就是無聲視頻。無聲視頻並不是把聲音調到最小,它指的是沒有音頻的視頻,這樣播放的視頻只有畫面。比方說對於前文案例一的素材視頻可以通過 -an 的命令去除音頻流,只保留視頻流即只有畫面(沒有下載的可以點擊這裡下載)。
ffmpeg -i r1ori.mp4 -an -y r1-silent.mp4
來看下結果視頻r1-silent.mp4的信息,沒有了 Stream #0:1(und): Audio 的信息。
» ffmpeg -i r1-silent.mp4 -hide_banner Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'r1-silent.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf58.20.100 Duration: 00:00:58.53, start: 0.000000, bitrate: 1687 kb/s Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 544x960, 1684 kb/s, 29.83 fps, 29.83 tbr, 11456 tbn, 59.67 tbc (default) Metadata: handler_name : VideoHandler At least one output file must be specified
現在即使你把音響抱過來,聲音加到最大播放這個視頻,也不會聽到任何聲音。
-an 即 -acodec none。a指的是audio,codec指的是解碼器,-acodec就是音頻解碼器,合起來就是不指定音頻解碼器,回顧下我們在ffmpeg是怎麼轉碼的一文介紹的轉碼流程就很容易理解了。
你應該已經猜到了,類似的我們還可以去除視頻流、字幕流等。
- -an 去除音頻流
- -vn 去除視頻流
- -sn 去除字幕流
- -dn 去除數據流
有同學可能注意到了,我們的原視頻的時長是59秒,還不到一分鐘,但是 -an 的一條命令要花上十幾秒的處理時間,太慢了,有沒有辦法優化下?
你仔細思考下,那麼慢,時間花在哪裡了?對,就是重新編碼。
這裡我們只是去除音頻流,有必要重新編碼嗎?沒有,所以如果我們可以把視頻流複製出來是不是就好了?
優化後的命令如下
ffmpeg -i r1ori.mp4 -an -vcodec copy -y r1-silent.mp4
這條命令瞬間就輸出結果了。我們添加了一個參數 -vcodec copy。-vcodec指的是視頻解碼器,v是視頻video,codec是解碼器,後跟解碼器名稱,copy複製輸入的視頻流,不作解碼處理。
同樣,如果我們想提取視頻中的音頻,或者說把視頻轉成音頻,是不是可以用下面這條命令?
ffmpeg -i r1ori.mp4 -vn -c:a copy -y r1-silent.mp3
執行該命令後發現報錯了
[mp3 @ 0x7f97a580f000] Invalid audio stream. Exactly one MP3 audio stream is required. Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
提示我們音頻流無效,原因是codec的參數錯誤。我們看下原視頻的信息
» ffmpeg -i r1ori.mp4 -hide_banner ...... Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 129 kb/s (default) ......
注意音頻流這行信息,我們發現音頻流是aac格式的,而我們要輸出的是mp3格式的,-c:a copy 參數意味着我們想要把aac格式的音頻流裝進mp3容器,這是不可行的。aac 音頻流需要一個專用的 aac 容器,mp3 音頻流需要專用的 mp3 容器。
註:aac 和 mp3 都是有損壓縮音頻編碼格式。
找到原因就好辦了,我們把輸出的mp3格式修改成aac格式
ffmpeg -i r1ori.mp4 -vn -c:a copy -y r1-silent.aac
雖然mp4容器內的音頻流大多數都是aac格式,但是,試想一下如果我們寫好程序,要針對用戶上傳的視頻提取音頻並做存儲,偏偏用戶上傳的原視頻內的音頻是mp3呢?
為了滿足這一場景,我們製作一個含mp3格式的視頻,然後再執行上面的命令試試。
1、把r1ori.mp4視頻內的音頻流轉成mp3
ffmpeg -i r1ori.mp4 -c:a libmp3lame -c:v copy -y r2.mp4
註:由於ffmpeg沒有原生的mp3編碼器,所有我們指定了外部的libmp3lame編碼庫(雖然 -c:a libmp3lame 你也可以把libmp3lame改為mp3,實際上使用的還是libmp3lame)。如果你執行上面的命令報了一個類似這樣的錯誤 ERROR: libmp3lame >= 3.98.3 not found,說明你本地的ffmpeg沒有添加–enable-libmp3lame編譯參數,可以參考這篇文章選擇對應的方式重新安裝ffmpeg;
2、提取該視頻的音頻
ffmpeg -i r2.mp4 -vn -c:a copy -y r1-silent.aac 報錯:Only AAC streams can be muxed by the ADTS muxer Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
所以,-c:a copy 不是萬能的,也就是說如果我們想讓視頻轉音頻,最好指定一種編碼器,用aac還是libmp3lame?就音質質量而言,我們更推薦libmp3lame,儘管ffmpeg自帶的最好音頻編碼器是aac。
綜上,如果你需要輸出mp3格式的音頻,你可以使用
ffmpeg -i r1ori.mp4 -vn -c:a libmp3lame -y r1-silent.mp3
如果你想輸出aac格式的音頻,你可以使用
ffmpeg -i r1ori.mp4 -vn -c:a aac -y r1-silent.aac
註:新版本的ffmpeg是支持原生aac編碼的,所以可以直接使用 -c:a aac,低版本的ffmpeg像2.x的版本原生aac編碼器是不完全支持的,必須同時指定 -strict -2 才可以使用。
以上,我們介紹了手動指定音頻解碼器,成功的將視頻轉換成了音頻。
既然ffmpeg那麼厲害,那如果我們不手動指定,它能自動幫我們選擇合適的解碼器處理嗎?
非常可以。
以mp3為例,我們試下
ffmpeg -i r1ori.mp4 -y r1-silent.mp3 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'r1ori.mp4': ...... Stream mapping: Stream #0:1 -> #0:0 (aac (native) -> mp3 (libmp3lame)) ......
注意看輸出的過程代碼中包含 Stream mapping 以及其下一行代碼,可以看出ffmpeg的確自動為我們選擇了libmp3lame解碼器。
如果原視頻有多路音頻流,又該如何操作呢?我們下節課再說。