Ffmpeg分佈式視頻轉碼問題總結

本文主要聊一聊雲原生時代分佈式轉碼系統實施過程中碰到的一些問題。 聊問題之前簡單介紹一下我們的分佈式轉碼方案。

雲原生分佈式轉碼

在計算資源招之即來的雲計算時代,正在重構着軟件架構的方方面面。

對軟件架構師或者運維管理者影響比較大的一個點便是不需要在做容量規劃,不需要提前評估爲了應對某個活動應該準備多少臺機器,這個特點也深刻影響軟件架構的設計。

分佈式轉碼方案

在之前的文章中有說到視頻轉碼主要分爲3個步驟:

  1. 切片:將輸入的視頻進行切片,切分成一個個較小的視頻片段
  2. 轉碼:將一個個小的視頻片段下發到不同的機器上進行轉碼,並行執行,充分利用多個實例的計算能力
  3. 合併:將轉碼後的小視頻片段合併成一個視頻

image.png


         切片                  轉碼                  合併
輸入視頻 ------> (n個)轉碼任務 ------> (n個)轉碼結果 -----> 輸出視頻


爲什麼要切片?

爲了加快轉碼速度。 視頻轉碼是一個非常耗時的操作,讓不同的機器並行轉碼不同的視頻片段,可以充分利用大規模計算資源來加快最耗時的轉碼流程。

舉一個例子,假設1臺4核8G的機器能夠提供2倍速的轉碼速度,

  • 轉碼1個小時的視頻則需要30分鐘;
  • 如果將1個小時視頻分成2個片段(每段30分鐘),就能夠並行在2臺機器上執行,那麼每個片段分別只需要15分鐘就能夠轉碼完成;
  • 如果將1個小時視頻分成4個片段(每段15分鐘),就能夠並在在4臺機器上執行,那麼每個片段分別只需要7.5分鐘就轉碼完成;

理論上對視頻進行合理的切片加上充足的計算資源,能夠極大的提高轉碼的速度。 雖然這種分佈式切片轉碼方案優勢這麼明顯,但是在實踐過程中也發現了不少問題。 想借此文跟大家探討一下我們碰到的部分問題以及解決方案,看看大家有沒有更好的方案。 要是能夠給大家帶來一些幫助、少踩兩個坑,目的就達到了。

碰到的問題

  1. 不聊工程上的問題,工程上的問題都比較好解決
  2. 主要聊ffmpeg切片、轉碼、合併過程中所遇到的問題
  3. 知識儲備的原因,有些ffmpeg底層的原理可能會一筆帶過。。

m3u8轉碼後有雜音

對於m3u8轉碼,我們在切片環節直接用ts文件作爲切片,轉碼後再合併爲最終的視頻文件。

這麼做的好處是大大縮短了切片的時間,如果用ffmpeg 進行切片需要將文件讀取到內存再切成一個個小視頻; 而直接用ts文件作爲切片的好處是大大縮短了切片這個環節的耗時,相當於純文本處理,把m3u8文件中的ts文件地址解析出來就行。

問題現象

m3u8轉碼後生成的視頻有輕微的雜音,而原m3u8文件中

問題解釋

因爲採樣率的原因,ts文件中的音視頻流並不是完全對其的。如:

直接播放m3u8文件時,會將所有的ts文件都看成一個整體,即將每個ts片段中的視頻流和音頻流都連接起來的,所以播放時沒有雜音。

以上圖第一個ts爲例,將ts文件轉碼爲mp4後,缺失的音頻流將默認會補齊,這就造成了雜音的出現。

解決方案

將音視頻流分離,單獨進行轉碼。

  1. 對於視頻流,仍然採用切成小視頻片段的方式進行轉碼
  2. 對於音頻流,轉碼不耗資源,就不再進行切片,而是對整個音頻流進行轉碼

image.png

這樣m3u8轉碼出來就不會有雜音了。

附對應的切片跟合併命令

# 切片命令
ffmpeg -i input.mp4 -map 0:v -f segment -segment_time 15 -reset_timestamps 1 -c copy segment%d.mp4 -map 0:a -c:v vframes 1 -c:a copy audio.mp4 -y

# 合併視頻命令
ffmpeg -f concat -safe 0 -i concat.txt -c copy concat.mp4

# 合併視頻與音頻命令
ffmpeg -i concat.mp4 -i audio.mp4 -map 0:v -map 1:a -c:v copy -c:a copy output.mp4 -y

轉碼後視頻變長&音畫不同步

問題現象

轉碼後的視頻長度較輸入視頻長度變長了。 如輸入視頻是3600s,輸出視頻可能是3601s,多了1s;而且輸入視頻時長越長,誤差越大。

問題解釋

與上一個問題「m3u8轉碼後有雜音」的原因類似:

  1. 因爲採樣率的原因,切出來的視頻片段中的音視頻流時長不完全一致
  2. 對視頻片段進行轉碼時取最長的流時長作爲輸出後的視頻片段的時長
  3. 在合併環節直接將所有轉碼後的視頻片段拼接在一起,所以輸出的視頻時長變長了

解決方案

與上一個問題「m3u8轉碼後有雜音」的解決方案類似:將音視頻流分別抽離進行轉碼,最後在合併環節將音視頻流合在一起。 這樣避免了在切片環節音視頻流時長互相影響。

m3u8文件切片起止時間不準

問題現象

舉一個例子,從a.m3u8的第10s開始切5s視頻出來,命令如下:

ffmpeg -ss 10 -t 5 -i a.m3u8 -c copy out.mp4

實際輸出的out.mp4不一定從a.m3u8第10s開始的。

問題解釋

在切片環節已經不準確了,合併出來的視頻肯定也是不準的,所以我們要在切片環節把這個問題解決掉。

這裏我引入最近大火的ChatGPT的回答: m3u8格式的視頻不支持隨機訪問,而且由於ts文件的長度不固定,ffmpeg很難精確定位到seek目標所在位置。

image.png

解決方案

我們是怎麼解決的呢,可以參考ChatGPT給出來的4種解決方案的第4個方案:

  1. 將包含切片起止時間的最小ts文件集合篩選出來
  2. 將最小ts文件集合轉封裝爲mp4
  3. 重新計算出在mp4上實際的起止時間,進行切片

想必大家肯定有很多疑問,我們一步一步來解釋一下。

  1. 如何將包含切片起止時間的最小ts片段集合篩選出來?

    m3u8文件中的每一個ts文件都標明瞭該ts文件的時長,我們可以藉助這個將最小的ts文件集合篩選出來

  2. 爲什麼要轉封裝爲mp4?

    因爲mp4支持隨機訪問

  3. m3u8轉封裝爲mp4會不會出現m3u8中的音視頻編碼不支持mp4的情況?

    不會。可以參考維基百科:https://en.wikipedia.org/wiki/Comparison_of_video_container_formats

  4. 如何重新計算出在mp4上的實際起止時間?

    參考問題1的回答,可以通過每個ts文件的時間,計算出最終在mp4上實際的起止時間

PS:ChatGPT真的太強大了,要是ChatGPT早點出來會少走很多彎路。

輸入視頻的音視頻編碼不規範

問題現象

在切片環節切片失敗

問題解釋

用戶輸入的視頻文件編碼不規範:

  1. 視頻編碼正常,音頻編碼不規範。如將pcm編碼的音頻流封裝到了mp4格式中
  2. 視頻編碼與音頻編碼均不規範。大概率是原視頻被強行改了後綴,如將a.mp4改成a.mxf

因爲切片環節設計到重新封裝的操作,將1個mp4切成多個mp4就需要重新封裝。 在重新封裝時就會報錯,不同的容器格式支持不同的音視頻編碼,可以參考維基百科:https://en.wikipedia.org/wiki/Comparison_of_video_container_formats

有意思的是用戶意識不到他們的視頻文件有問題,因爲音視頻流都能夠正常解碼(即能夠正常播放)。

解決方案

第2個問題在目前的分佈式轉碼方案中還沒有好的解決方案,再加上出現概率非常小(用戶手動更改文件後綴),這裏不做討論。 主要討論一下第1個問題的解決方案:在切片環節就將音頻文件轉碼了。 image.png

爲什麼能夠在切片環節對音頻轉碼?因爲音頻轉碼不耗CPU,不會明顯影響到切片環節的速度。

mxf不支持抽離音頻流

問題現象

mxf格式的視頻經過切片命令抽離出來的音頻文件時長只有1幀(0.04s)。 因爲我們切片命令裏只給了1幀圖片到音頻文件中。

問題解釋

mxf以視頻流的長度作爲整個視頻的長度,意味着音頻切片只有一幀圖像的話,整個視頻時長只有1幀的時長。 所以問題準確的描述應該爲mxf不支持以這種方式抽離音頻流。

解決方案

既然mxf不支持以這種方式抽離音頻流,那麼方案有2個:

  1. 將所有的視頻流不轉碼,都copy到音頻文件中,還是隻對音頻流進行轉碼。
  2. 看看有沒有完全包含mxf支持的音視頻編碼格式並且支持這種方式抽離音頻流的封裝格式。

方案1相當於音頻文件對輸入視頻進行了一次拷貝,如果輸入視頻特別大,那麼音頻文件也將會特別大。 我們最終採用的方案2。 有沒有這種格式呢?即支持所有mxf 音視頻編碼格式,又支持這種方式抽離音頻。 還真有。通過維基百科:https://en.wikipedia.org/wiki/Comparison_of_video_container_formats,可以發現avi格式能夠支持所有mxf支持的音視頻編碼格式;而且經過測試,也支持這種方式將音頻抽離出來。

所以對於mxf格式的視頻,在切片環節切出來的音頻文件設置爲avi格式。

ffmpeg對圖片支持不夠友好

問題現象

對於頭信息比較大的圖片再加上不能準確讀取後綴的話(如加簽訪問的場景),很大概率會解析失敗,獲取到錯誤的meta信息。 如將圖片識別出2幀。

問題解釋

ffmpeg確實對圖片支持不太友好

解決方案

將圖片下載到本地,再進行識別。 這種方案有一個缺點是耗時會非常長,如果業務方同步調用獲取信息接口有可能會超時。

阿里雲的轉碼方案

決定自研轉碼之前,我們是使用阿里雲的轉碼服務的。 上面碰到的很多問題在阿里雲轉碼服務中都不存在,這裏面固然有阿里雲在音視頻轉碼領域的積累,但是我想也跟他們的轉碼方案有很大的關係。

經過長時間的觀察,我猜阿里雲沒有使用切片-轉碼-合併這種方案,而是使用高性能服務器,不切片直接對視頻進行轉碼。 爲什麼呢?因爲上述問題中,基本上都是在切片環節出現的問題。尤其是「輸入視頻的音視頻編碼不規範」這個問題,只要使用切片-轉碼-合併這種方案,那麼百分之百會碰到跟我們的問題。而阿里雲不能使用我們這種非常規手段去解決。 因爲視頻封裝格式、音視頻編碼格式非常多,出於穩定性考慮,不可能碰到問題了再case by case的去解決。

但是使用高性能服務器,不切片直接對視頻進行轉碼這種方案固然能夠避免很多切片環節的問題,但是也很容易造成轉碼任務阻塞。舉一個例子: 在機器資源池不大的情況下,A用戶輸入了很多優先級低轉碼非常耗時的視頻,把資源池都佔滿了;而隨後B用戶輸入了1個優先級很搞的視頻,雖然優先級很高,但是也得等待A用戶正在轉碼的視頻轉完,讓出1臺機器才能執行。 這種情況對B用戶體驗就非常不好,在A用戶轉碼完成之前對B用戶來說服務是不可用的。

最後

好了,本篇將我們實施分佈式轉碼過程中所碰到的比較難解的問題以及解決方案都聊了一遍。 當然,以後還會碰新的問題,我會將其放在一個系列文章裏面討論,希望能夠給大家帶來一些幫助或者少踩一些坑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章