Twitch如何实现转码器比FFmepg性能提升65%?(上)
文 / Jeff Gong, Sahil Dhanju, Chih-Chiang Lu, Yueshi Shen
譯 / 王鴻蒙
編者按:超過220萬創作者在Twitch發布海量的視頻,這對實時轉碼業務造成了巨大壓力,Twitch團隊通過優化多線程的轉碼服務以及Intel QuickSync的支持,實現了比FFmepg性能提升65%,并降低80%總體擁有成本。Twitch團隊通過博客介紹了這一實現,LiveVideoStack對本文進行了摘譯,點擊『閱讀原文』訪問英文博客。同時,Yueshi Shen將在12月8-9日的ArchSummit 2017北京大會上詳細介紹實現過程。
背景介紹
Twitch是全球領先的視頻游戲、電子競技和其他新興創意內容的流媒體直播平臺。每個月,超過220萬獨創內容創作者在我們的網站上直播或上傳視頻。高峰期,Twitch同時處理成千上萬的并發直播視頻流,并將其傳送給世界各地的觀眾。
圖1描述了我們的直播視頻CDN架構,它為全球提供數以萬計的并發直播流。
圖一
與許多其他實時流服務一樣,Twitch接收直播者通過RTMP上傳的實時消息流。RTMP是一種用于在互聯網上傳輸視頻流和音頻流的協議,主要用于點對點通信。為了將我們的直播內容觸達無數觀眾,Twitch使用HTTP實時流媒體協議(HLS),HLS是一種基于HTTP的流媒體通信協議,現在大多數視頻網站都使用該技術。
在實時流處理流水線內,轉碼模塊負責將輸入的RTMP流轉換為具有多個版本(如1080p、720p等)的HLS格式。這些版本具有不同的碼率,使得具有不同下載帶寬的觀眾能夠以盡可能最好的質量來獲取實時視頻流。圖2描述了我們的實時視頻CDN中的轉碼模塊的輸入和輸出。
圖2: 轉碼模塊的輸入和輸出
在這篇文章中,我們將討論:
FFmpeg如何滿足大部分實時轉碼要求
FFmpeg無法提供哪些功能
Twitch為什么建立自己的內部轉碼器軟件棧
直接使用FFmpeg
FFmpeg是一個流行的開源軟件項目,旨在記錄、處理和流式傳輸視頻和音頻。它被云編碼服務廣泛部署,以用于文件轉碼,也可用于實時流轉封裝和轉碼。
假設我們正在使用RTMP協議以6mbps和1080p60(1920×1080,每秒60幀的幀速率)接收使用最廣泛的H.264視頻壓縮標準文件。我們想要生成4個HLS版本:
1080p60 HLS / H.264
720p60 HLS / H.264
720p30 HLS / H.264
480p30 HLS / H.264
一個解決方案是運行4個獨立的FFmpeg實例,每個實例都處理一個版本。在這里,我們將所有即時解碼刷新(IDR)的時間間隔設置為2秒,并關閉場景變化檢測,從而滿足HLS標準所要求的所有版本輸出的HLS片段完全時間對齊的要求。
FFmpeg 1-in-1-out示例命令:
ffmpeg -i <input file or RTMP stream> -c:v libx264 -x264opts keyint=<IDR interval>:no-scenecut -s <resolution> -r <fps> -b:v <target bitrate> -profile:v <profile> -c:a aac -sws_flags <scaler algorithm> -hls_list_size <number of playlist entries> <output file or playlist>.m3u8
注意:
“<>”中的所有參數都來自用戶的輸入。其中一些在下面會更詳細地描述
c:v用來指定要使用的視頻編解碼器,我們的例子中使用的是libx264
x264opts用來表示libx264的選項。在這里,IDR間隔應該是你想要的FPS的2倍,所以720p60會產生一個120的IDR間隔,而720p30需要一個60的IDR間隔。No-scenecut用來禁用場景變化檢測
s用來指定視頻大小。可以是“寬x高”的形式或尺寸縮寫的名稱
r用來指定FPS
b:v用來指定目標視頻比特率,當有帶寬限制或要求時,該功能非常有用;另外,b:a用于音頻
profile是指H.264的配置文件
sws_flags決定應該使用哪種縮放算法
hls_list_size用于確定播放列表中的最大片段數(例如,等于6時表示實時流傳輸,將其設置為0則表示具有所有片段的播放列表)。段持續時間(可選的hls_timeflag)將與IDR間隔相同,在我們的例子中是2秒。
由于H.264是有損壓縮標準,轉碼將不可避免地導致視頻質量下降。而且,編碼在計算上是非常“昂貴”的過程,特別是對于高分辨率和高幀速率的視頻。考慮到這兩個約束條件,相對理想的情況是對源RTMP中的最原始版本進行復合而不是轉碼,以節省計算能力并保持視頻質量。
在上面的例子中,如果我們想要將輸入的1080p60 RTMP源碼復合成HLS,我們實際上可以使用上面的命令,而不用指定大小或目標FPS,以及編解碼器的副本(避免對源碼進行解碼和重新編碼):
ffmpeg -i <input file or RTMP stream> -c:v copy -c:a copy -hls_list_size <number of playlist entries> <output file or playlist>.m3u8
轉封裝源碼流是一種有效的技術,但可能會導致輸出HLS失去規范兼容性,從而在某些設備上無法正常播放。 我們將在下一節闡釋該問題的性質及其影響。
另一方面,FFmpeg具有接收1個輸入和產生N個輸出的功能,我們用下面的FFmpeg命令來演示。
FFmpeg 1-in-N-out示例命令(使用主配置文件、x264快速預置和雙線性縮放算法):
-c:v libx264 -x264opts keyint=120:no-scenecut -s 1920x1080 -r 60 -b:v <target bitrate> -profile:v main -preset veryfast -c:a libfdk_aac -sws_flags bilinear -hls_list_size <number of playlist entries> <output file or playlist>.m3u8 \
-c:v libx264 -x264opts keyint=120:no-scenecut -s 1280x720 -r 60 -b:v <target bitrate> -profile:v main -preset veryfast -c:a libfdk_aac -sws_flags bilinear -hls_list_size <number of playlist entries> <output file or playlist>.m3u8 \
-c:v libx264 -x264opts keyint=60:no-scenecut -s 1280x720 -r 30 -b:v <target bitrate> -profile:v main -preset veryfast -c:a libfdk_aac -sws_flags bilinear -hls_list_size <number of playlist entries> <output file or playlist>.m3u8 \
-c:v libx264 -x264opts keyint=60:no-scenecut -s 852x480 -r 30 -b:v <target bitrate> -profile:v main -preset veryfast -c:a libfdk_aac -sws_flags bilinear -hls_list_size <number of playlist entries> <output file or playlist>.m3u8
如果我們想復合最原始版本,而轉碼其余的版本,我們可以用先前指定的編解碼器副本替換第一個輸出配置:
-c:v copy -c:a copy -hls_list_size <number of playlist entries> <output file or playlist>.m3u8
注意:
使用上面的命令,我們可以從一個輸入文件轉碼出多個版本。 每個“\”表示新的一行,我們可以指定一個不同的標志組合,以及一個唯一的輸出名稱。 每個命令都是相對獨立的,可以使用任何其他的標志組合。
這里的每個命令的主要區別在于s和rflags,本文前面已經解釋過了。
在單個FFmpeg實例中運行以下多個轉碼的一個替代方法是運行多個實例,即并行地為每個期望的輸出運行一個實例。 1-in-N-out的FFmpeg是一個消耗計算資源較低的過程,我們將在稍后做出解釋。
幾個技術問題
上一節演示了如何使用FFmpeg為直播流生成HLS。雖然很有用,但是一些技術方面的問題使FFmpeg成為一個不太理想的解決方案。
復合和轉碼
圖3: HLS版本和片段,對齊跨多個版本的片段
在HLS中,一個版本由一系列片段組成,每個片段以一個IDR幀開始。HLS規范要求版本的相應片段的IDR幀必須對齊,以便它們具有相同的演示時間戳(PTS)。只有這樣,當觀眾的網絡狀況發生變化時,HLS自適應比特率(ABR)播放器才能在這些版本之間無縫地切換(見圖3)。
圖4:復合版本和轉碼版本的片段之間的不對齊
如果我們對源代碼和其他版本進行轉碼,我們將得到完美的時間對齊的HLS片段,因為我們強制FFmpeg精確地每2秒編碼一次IDR。 但是,我們無法控制源RTMP比特流中的IDR間隔,這完全由播放軟件的配置決定。如果我們將源代碼進行復合,那么復合的和轉碼的版本的片段就不能保證對齊(見圖4)。這種不對齊可能會導致播放問題。例如,我們注意到,Chromecast在收到含有未對齊片段的HLS流時會不斷顯示播放暫停。
對于具有可變IDR間隔的源RTMP流,我們希望輸出的HLS看起來如圖5那樣對齊:
圖5:復合版本和轉碼版本的對齊分段
但是,在1-in-1-out 和1-in-N-out的FFmpeg實例中,與N個輸出版本相對應的N個編碼器是獨立的。如上所述,除非所有的版本都被轉碼(即最原始的版本也將被轉碼而非復合),否則結果IDR不會被對齊(見圖4)。
軟件性能
如圖2中所討論的,我們的RTMP-HLS轉碼器接收1個流的輸入并產生N個流的輸出(N = HLS版本的數量,例如,圖5中的N = 4)。實現這種輸出的最簡單方法是創建N個獨立的1-in-1-out轉碼器,每個轉碼器產生1個輸出流。上面介紹的FFmpeg解決方案使用了這個方法,生成了N個FFmpeg的實例。
在1-in-1-out轉碼器中有3個組件,即解碼器,縮放器和編碼器(見圖6)。因此,對于N個FFmpeg實例,我們將對應有N個解碼器,N個縮放器和N個編碼器。
?
圖6:剖析視頻轉碼器
由于N個解碼器都是相同的,所以理想情況下,轉碼器應消除冗余的N-1個解碼器,并將來自這個唯一解碼器的解碼圖像發送到N個下游縮放器和編碼器(見圖7)。
圖7:僅有1個視頻解碼器的1-in-N-out視頻轉碼器
上面我們談到了解碼器冗余。現在,我們來看看四個版本的例子。
1080p60 HLS/H.264
720p60 HLS/H.264
720p30 HLS/H.264
480p30 HLS/H.264
這個例子中的兩個轉碼版本720p60和720p30可以共享一個縮放器。實驗證明,縮放在轉碼過程中是一個計算量非常大的步驟。 避免不必要的重復縮放過程可以顯著優化我們的轉碼器的性能。 圖8描繪了整合720p60和720p30版本的縮放器的線程模型。
圖8:共享縮放器的720p60和720p30的線程模型
除了解碼器和縮放器共享之外,更重要的特性是使用多線程。由于編碼器和縮放器在計算上都非常昂貴,因此利用現代多核CPU架構同時處理多個版本是非常重要的。從我們的實驗中,我們發現多線程對于實現更高密度的工作非常有用,對于某些特定的應用程序(如4K)也非常重要。
自定義功能
FFmpeg是一種多功能的視頻處理軟件,支持標準的ABR轉碼工作流的各種視頻/音頻格式。但是,它不能處理Twitch操作中特有的一些技術要求。例如,
1)幀率下采樣器
典型的傳入比特流具有60fps(每秒幀數),我們將它們轉碼為30fps,以獲得較低的比特率版本(例如,720p30,480p30等)。另一方面,由于Twitch是一個全球平臺,我們經常收到50fps的流量,這些流量大部分來自PAL國家。在這種情況下,較低比特率的版本應該下采樣到25fps,而不是30fps。
簡單地刪除每一個第二幀在這時并不是一個很好的解決方案。對于兩種不同類型的傳入比特流,我們的下采樣器需要有不同的表現。一種具有低于60fps的固定幀率,另一種具有不規則幀丟失,平均幀率低于60fps。
2)元數據插入
某些信息需要被插入到HLS比特流中以增強用戶體驗。通過構建我們自己的轉碼器和播放器,Twitch可以控制完整的端到端攝取 - 轉碼 - CDN播放流水線。這允許我們將專有的元數據結構插入到轉碼器輸出中,最終由我們的播放器進行解析,并用于產生Twitch特有的效果。
LiveVideoStack招募全職技術編輯和社區編輯
LiveVideoStack是專注在音視頻、多媒體開發的技術社區,通過傳播最新技術探索與應用實踐,幫助技術人員成長,解決企業應用場景中的技術難題。如果你有意為音視頻、多媒體開發領域發展做出貢獻,歡迎成為LiveVideoStack社區編輯的一員。你可以翻譯、投稿、采訪、提供內容線索等。
通過contribute@livevideostack.com聯系,或在LiveVideoStack公眾號回復『技術編輯』或『社區編輯』了解詳情。
總結
以上是生活随笔為你收集整理的Twitch如何实现转码器比FFmepg性能提升65%?(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Netflix如何节省92%视频编码成本
- 下一篇: Twitch如何实现转码比FFmpeg性