當前位置:
首頁 > 最新 > Revvel如何將視頻轉碼速度提升幾十倍?

Revvel如何將視頻轉碼速度提升幾十倍?

作者:Greg Femec,Revvel資深軟體開發主管(Principle Development Lead)

聽譯:王鴻蒙

責編:Ant

Serverless系統架構具有自動擴容、按需付費、無需伺服器部署和運維以及高可用性和容錯性的特點。AWS Lambda是著名的Serverless雲服務提供商,在AWS Lambda上的一個典型Serverless應用往往通過事件驅動的方式去觸發對預定義函數的調用。事件源可以有很多種,主要分為3類:

數據狀態變化,例如S3對象的新增、刪除。

API請求,即通過特定的HTTP請求來觸發函數的執行。

資源狀態變化,如AWS雲組件的相關配置發生變化。

AWS Lambda典型的應用包括:

網站:靜態網站,複雜網站應用,Flask和Express網站架構

後台:應用和服務,移動App,物聯網(IoT)

數據處理:實時數據處理,MapReduce,批處理

聊天機器人:聊天邏輯

Amazon Alexa:語音驅動的應用, Alexa 技能工具包

IT自動化:策略引擎, 擴展服務, 架構管理

在視頻編碼處理中,並行編碼演算法有多種,根據並行級別可以分為GoP 級、幀級、Slice 級和宏塊行級。視頻編碼的這種特點使得它可以很好的利用AWS Lamda的並行處理的特性。Revvel團隊在之前構建視頻轉碼服務平台過程中遇到過許多挑戰,主要體現在以下幾個方面:

大量的待轉碼視頻

無法提前預測轉碼業務所需的時間及工作量

視頻源來自於不同的合作夥伴,不同的拍攝設備,導致其長短,解析度、碼率多種多樣

不同的輸入格式

輸出格式多樣, 多種封裝格式

典型的無交錯(逐行掃描)MP4

HLS(TS塊)

DASH(片段化MP4)

各種DRM協議

在早期Revvel團隊使用了SaaS方案。但是由於視頻的來源不同,我們很難在這個方案中獲得對於視頻轉碼更高的控制權。同時成本效益並不高,尤其是新增轉碼格式的邊際成本並未隨著用量增大而顯著降低。

我們也在AWS EC2上嘗試建立自己的視頻轉碼服務,成本得到了一定的控制,視頻的控制權也增大,但隨之而來的是運維方面的挑戰。這些挑戰表現在:

可擴展性。用戶希望能夠快速上傳並分享視頻,因此我們希望避免任何任務排隊,以快速響應用戶請求。但是這裡涉及到冷啟動問題。從購買計算資源直至計算資源可用,要花費大量的時間啟動實例,下載軟體,安裝更新,下載所需文件。

資源浪費。假設我們有一個可以同時運行多個任務的高性能實例,但目前卻只有一個比較耗時的任務在運行,比如高清(HD)視頻轉碼,這在我們當前的配置下通常會花費好幾個小時。我們無法利用該實例上多餘的計算能力,也無法中止轉碼,把任務移交到另外一台機器來回收多餘的計算資源。另外,在我們構建轉碼服務的時候,EC2按小時計費,如果我們啟動多個實例處理視頻,每個實例運行了20分鐘,每個實例仍然需要支付一小時的費用。(從2017年9月份開始,EC2可以按秒計費。)

我們為什麼使用Serverless?

Serverless的好處明顯:沒有冷啟動問題,可以快速擴容縮容,沒有資源浪費,運維問題更少,並發量更大(AWS Lambda默認並發執行上限為1000,我們調整為2000,但這並不是硬性限制),以及靈活的資源配置(可以快速重新對資源進行分配)。

Serverless下轉碼的挑戰

輸入的視頻文件往往較大(有些有數百GB),我們不希望等文件下載完成才開始處理,並且我們在Lambda中的容器也沒有這麼大的存儲空間。另外,視頻轉碼工具一般假定輸入輸出為完整的視頻文件,無法進行塊級的處理。同時,如我們之前提到的,HD視頻轉碼往往耗時好幾個小時,而且轉碼一旦開始,很難暫停和重啟。

我們的解決方案

我們的高效視頻轉碼解決方案使用類似於MapReduce的分治法,把視頻切成5秒(時間可以調整)的小塊,在map中進行轉碼,在reduce中進行合併,輸出我們需要的格式, 比如無交錯視頻(Progressive MP4)和MPEG-DASH。對於HLS,某些情況下我們可以直接使用map的結果作為TS塊,有時也做一些後期處理,尤其是需要加入DRM時。同時需要避免在本地磁碟上緩存完整的視頻大文件。

我們使用的工具包括AWS Lambda(實時縮放、並行處理的能力)、S3(支持分段上傳,按範圍請求數據)、FFmpeg(轉碼領域的瑞士軍刀)以及Python(AWS Lambda對Python有很好的支持,Revvel團隊熟悉並大量使用該語言,並可以對C代碼進行很好的整合)。下面對Serverless轉碼的架構圖進行簡要說明:

圖:轉碼架構圖

我們的輸入和輸出處理基於S3存儲。首先,我們從不同的存儲位置獲取視頻源文件,統一存儲在S3上,執行map(圖中所示Chunk Lambda Function)開始進行處理。嚴格來講,在這裡我們可以並行運行任意數量的map,視頻文件中的每組5秒數據塊可以被分別獲取,這些5秒數據塊的計算相互獨立,每一個的處理都不依賴於其他的5秒數據塊,數據塊的分發和執行只取決於我們現有的CPU總核數,以及並發的Lambda function執行數量。所以,這就允許我們一次運行多組程序來處理ts塊,而這步處理的結果將被存儲於另外的S3存儲桶中。下一步,我們繼續在這些文件塊上進行reduce。通過對文件塊進行相對簡單的reduce操作,並將他們合併,可以生成MP4文件;如果要生成DRM文件則要更加複雜的過程,圖中所示即是生成DRM後的HLS文件的例子,而DASH文件的生成過程與之類似。

下面我們講解塊函數(Chunk Function)操作中的細節。

塊函數(Chunk Function)工作說明

之所以叫塊函數,是因為我們要通過這個函數生成ts文件塊。如果我們想生成一個文件塊,首先就需要解碼輸入視頻中的一小部分,然後將其轉碼到想要得到的解析度/碼率,最後,將生成的TS文件塊上傳到S3中。這裡面臨如下兩個主要的挑戰。

圖:塊函數(Chunk Funtion)工作流程圖

解碼的挑戰與解決方案

從上面可以看到,我們不再需要下載整個文件,而只是對其中一小部分進行操作即可。因此,我們的挑戰主要來自於如何在這種情況下實現解碼。通常,我們使用FFmpeg幫助我們完成大部分解碼操作。FFmpeg支持有大小範圍請求的HTTP請求,FFmpeg常常讀若干位元組後前後跳轉,產生大量的HTTP請求來對相同文件塊反覆讀取,造成巨大的性能消耗。為此,我們在Lambda function中對S3進行了緩存,做法是在Lambda里啟動一個HTTP服務,代理所有FFmpeg對S3的讀取請求。我們要做的是從S3中得到比FFmpeg請求更大的文件塊,並將其緩存到內存中,避免反覆對相同文件塊的反覆造成的開銷。另外,由於視頻處理整體是從前往後的,所以在FFmpeg處理一個塊時,我們也會主動預取下一個數據塊,以免出現CPU等待IO造成的性能損失。

FFmpeg的挑戰

首先, 動態鏈接的FFmepg在lambda中運行不是很穩定,所以我們目前基本都是使用的靜態鏈接。雖然文件尺寸更大,但仍在Lambda的限制之內。

另外,創建進程也可能出現問題。在Lambda里使用fork創建進程時,子進程會繼承父進程的所有屬性,這裡面也包括了運行著我們代碼的lambda沙箱,因此在子進程里常常會意外發現一些自己並未創建過的東西。在我們的實踐中,在Python代碼里fork的FFmpeg進程繼承了沙箱中的某些文件描述符,特別是標準輸入,這偶爾會造成一些bug,我們可以將文件描述符關閉來解決這些問題。另外,如果前後執行多個Lambda function,容器可能會被重用,這意味著之前創建的進程會一直保持到後續的執行過程。因此,lambda function即使在執行錯誤的情況下也要正確清理現場。

合併函數(Merge Function)

圖:精簡合併函數(Merge Function)工作流程圖

當我們的文件經過map步驟處理後,形成小的ts文件塊進入到精簡合併操作步驟。其中,比較常用的就是生成MP4文件。在這個過程中,合併函數讀入.ts的文件塊,合併為MP4文件,然後上傳到S3中。這裡遇到的問題主要集中在文件輸出上。

輸出文件的挑戰

相對於輸入文件,輸出的視頻文件往往很大,lambda沒有足夠的臨時空間存儲。FFmpeg雖然支持FTP輸出,但在輸出MP4時,它輸出大部分數據之後,還會跳轉到文件頭插入一些信息。這個時候我們就要引入S3的文件分段上傳功能。我們在Lambda function中啟動一個定製的FTP服務,類似適配器,將FTP輸入適配到S3的文件分段上傳功能中。由於S3不需要按順序上傳,每段大小也不需要相同,因此我們可以從第二段開始上傳,最後再上傳包含文件頭的第一段。

連接函數(Concat Function)

圖:連接函數(Concat Function)工作流程圖

如上圖所示,我們看一個輸出DRM後的HLS格式文件的例子。圖中,我們讀入.ts的文件塊,在reduce過程中進行加密,並將加密文件上傳到S3中。我們將其合併以提高緩存效率,因為CDN中為列表中每個HLS只創建1個對象。這裡的問題主要集中在執行方面。

執行的挑戰

我們面臨處理時間方面的挑戰。舉例說明,當我們的AES加密樣本需要重新打包ts流,由於視頻較長,無法在lambda限制的5分鐘內完成。我們的解決方案是將lambda function進行菊鏈連接,這樣如果前一個lambda function沒有處理完,就把當前上傳狀態序列化給下一個lambda function繼續處理。這裡要注意的是,上傳和下載速度不一定是對稱的,當上傳速度成為瓶頸時,需要限制下載速度。我們曾經遇到過下載數據過多而來不及上傳導致的內存不足,所以當你進行類似的流式數據處理時,要加上一些背壓(backpressure)。

下面我們講一下部署的問題。

部署和CI/CD流水線

圖:部署和CI/CD流水線

考慮到可重現性,我們通常選擇在Docker容器中創建我們的lambda function。創建lambda function所用的git sha和腳本哈希值會成為zip文件名的一部分,這是我們對於不可變基礎架構理念的一種探索。但我們生成的新zip文件實際上不會立即替代現有的lambda function,而會生成一個新版本。我們可以對新版本進行測試,確保它可正常工作。當我們足夠自信代碼沒有問題的時候,我們會更新產品環境中的lambda function的別名,使它指向新版本,這是新代碼才開始在產品環境中運行。雖然是老生常談,我們感覺,Serverless中很棒的一點就是,它強制你把代碼分拆成API定義良好的小片段,這也基本保證了你的代碼一開始就是可測試的。

下面,我們對Lambda在使用中給一些建議:

注意容器復用的問題。除了注意清理多餘的進程以外,一般還要及時清理磁碟的臨時空間或內存空間。我們還用到了S3高速緩存和FTP適配器,這些最終會綁定到一個埠上,如果你使用的靜態埠,程序結束時要及時釋放,或者像我們一樣使用隨機埠。

日誌問題。如果多進程出現問題,lambda沙盒提供了很多linux的實用工具可用來調試,比如spawn、ps、top,在你的函數里調用他們,日誌會出現在CloudWatch里,就像一個虛擬機一樣。

運行時間可能變化。準備好重試超時的函數,尤其是網路受限的函數,這些函數可能只是恰巧超時而已。

成果展示

使用情況統計數據。我們現在已處理15萬小時的已轉碼視頻,並擁有4億次Lambda Function調用。

成本顯著降低過去6個月,我們每個月平均支付6000美元的Lambda使用費,而在按需分配的EC2上擁有類似能力(2000計算核心、C4系列等)需要每月花費6萬美元。

速度大幅度提升此前2小時的視頻在EC2上轉碼需要4-6個小時,而在Lambda上則不超過10分鐘。

幾點思考

局限性。一是lambda function有5分鐘的運行時間限制,想生成比如4k 60fps的視頻就很難完成。二是在解碼視頻文件中部的5秒文件塊時,由於我們沒有讀取之前的幀,所以我們需要視頻源格式支持高效的跳轉,我們有大量的視頻提供方以及工作室,還沒有遇到過不能高效跳轉的情況,但從理論上說,有可能存在這樣的視頻格式。三是Lambda上的CPU每小時的價格要高於EC2。四是解碼開銷。其實解碼工作中很多步驟是重複的。比如,很多步驟都要解碼2個音頻流、1個視頻流,讀取文件頭裡的元數據等。

未來計劃。一是支持更多種輸出格式。我們也很期待iOS和Android早日統一標準,這樣我們提供一個視頻源即可。二是提升對較小視頻文件處理的效率。

Revvel團隊簡介

Revvel團隊的前身是前Hulu CEO Jason Kilar和CTO Richard Tom創立的短視頻創業團隊Vessel。Verizon在2016年11月收購了Vessel團隊來服務與它在數字媒體業務方面的戰略。Revvel團隊主要專註在下一代互聯網電視產品和服務。它和雅虎和收購的AOL一起隸屬於Verizon旗下新成立的名為「Oath」的新公司。Revvel總部位於舊金山,其在北京的研發團隊位於清華科技園附近,專註在大數據平台,搜索和個性化推薦技術,移動和網頁開發,以及多媒體編碼技術等領域。

LiveVideoStack招募全職技術編輯和社區編輯

LiveVideoStack是專註在音視頻、多媒體開發的技術社區,通過傳播最新技術探索與應用實踐,幫助技術人員成長,解決企業應用場景中的技術難題。如果你有意為音視頻、多媒體開發領域發展做出貢獻,歡迎成為LiveVideoStack社區編輯的一員。你可以翻譯、投稿、採訪、提供內容線索等。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 LiveVideoStack 的精彩文章:

TAG:LiveVideoStack |