天天看點

流的操作(一)視訊轉音頻引發的血案

轉發自白狼棧

有些小夥伴看文章非常細心,對于上一節課不經意提到的一些邊緣細節都比較在意,比如 -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是怎麼轉碼的一文介紹的轉碼流程就很容易了解了。

你應該已經猜到了,類似的我們還可以去除視訊流、字幕流等。

  1. -an 去除音頻流
  2. -vn 去除視訊流
  3. -sn 去除字幕流
  4. -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那麼厲害,那如果我們不手動指定,它能自動幫我們選擇合适的解碼器處理嗎?

非常可以。

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)) 
......      

繼續閱讀