Twitch如何实现转码比FFmpeg性能提升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北京大會上詳細介紹實現過程。
FFmpeg的1-in-N-out流水線。為什么它無法處理前面討論的技術問題?
FFmpeg如何以編程方式處理需要單個輸入來生成多個轉碼和(或)轉封裝輸出的實例? 我們可以通過直接剖析FFmpeg最新3.3版的源代碼,來了解其線程模型和轉碼流水線。
在頂層ffmpeg.c文件中,transcode()函數(第4544行)不斷循環并重復調用transcode_step()函數(第4478行),直到其輸入信息被完全處理,或用戶中斷執行為止。Transcode_step()函數封裝了主要的流水線,并在許多其他即時步驟之間編排諸如文件I / O、過濾、解碼和編碼等動作。
在初始設置階段,init_input_threads()(第4020行)函數被調用,并將根據輸入文件的數量,產生一些新的線程來處理這些輸入。
if (nb_input_files == 1) {
?return 0;
}
for (i = 0; i < nb_input_files; i++) {
?...
?ret = av_thread_message_queue_alloc(&f->in_thread_queue, f->thread_queue_size, sizeof(AVPacket)); ? ?// line 4033
}
在第4033行中(如上所示),我們看到產生的線程數量完全由輸入的數量決定。也就是說,這意味著FFmpeg將只使用一個線程來處理1-in-N-out的場景。
在get_input_packet()函數(第4055行)中,只有當輸入文件的數量大于1時,才會調用多線程伴隨函數get_input_packet_mt()(第4047行)。get_input_packet_mt()函數可以以非阻塞的方式從消息隊列中讀取輸入幀。否則的話,我們需要使用av_read_frame()(第4072行)來每次讀取并處理一個幀。
?if (nb_input_files > 1) {
? ? get_input_packet_mt(f, pkt);
?}
?return av_read_frame(f->ctx, pkt);
如果我們跟蹤幀數據一直到流水線結束,我們發現它進入到process_input_packet()函數(行2591)中,該函數對幀數據進行解碼并通過所有適用的過濾器進行處理。時間戳校準和字幕處理的工作也在這個函數中進行。最后,在函數返回之前,已解碼的幀被復制到每個相關的輸出流。
for (i = 0; pkt && i < nb_output_streams; i++) {
?... ?// check constraints
?do_streamcopy(ist, ost, pkt); ? ?// line 2756
}
最后,transcode_step()函數調用reap_filters()函數(第1424行)來循環遍歷每個輸出流。reap_filters()函數的for循環負責收集緩沖區中待處理的幀,并將這些幀進行解碼,然后封裝到一個輸出文件中。
// reap_filters line 1423
for (i = 0; i < nb_output_streams; i++) { // loop through all output streams
?... ?// initialize contexts and files
?OutputStream *ost = output_streams[i];
?AVFilterContext *filter = ost->filter->filter;
?AVFrame filtered_frame = ost->filtered_frame;
?while (1) { // process the video/audio frame for one output stream
? ? ... ?// frame is not already complete
? ? ret = av_buffersink_get_frame_flags(filter, filtered_frame, …);
? ? if (ret < 0) {
? ? ? ?... ?// handle errors and logs
? ? ? ?break;
? ? }
? ? switch (av_buffersink_get_type(filter)) {
? ? case AVMEDIA_TYPE_VIDEO:
? ? ? ?do_video_out(of, ost, filtered_frame, float_pts);
? ? case AVMEDIA_TYPE_AUDIO:
? ? ? ?do_audio_out(of, ost, filtered_frame);
? ? }
? ? ...
}
通過跟蹤這條流水線,我們知道這些幀是如何通過單個線程的上下文順序進行處理的,從中我們能看到一些冗余。我們可以得出結論,既然1-in-N-out的轉碼流模型對我們來說是最有價值的,那么FFmpeg僅使用單線程來輸出結果則可能并不理想。FFmpeg文檔也建議我們在實際用例中,并行地啟動多個FFmpeg實例或將更有意義。在這里,我們關鍵的一點認識是,既然此工具(FFmpeg)沒有提供多線程功能,它就無法滿足Twitch流媒體服務的嚴格需求,那么我們就無法隨心所欲地使用它。
基準測試
TwitchTranscoder是我們為解決前面討論的技術問題而開發的內部軟件。它已被廣泛運用于我們的生產中,每天24小時地處理數萬個并發直播流。
為了確定TwitchTranscoder每天在轉碼任務上的表現是否會優于FFmpeg,我們進行了一系列基本的基準測試。在我們的測試中,我們對兩個工具使用相同的Twitch直播流以及有相同預設、配置文件、比特率和其他標志的1080p60視頻文件。每個視頻源都被轉碼成我們通常使用的典型的720p60,720p30,480p30,360p30和160p30。
我們的假設是,FFmpeg對于輸入文件的轉碼速度比TwitchTranscoder要慢,甚至可能無法跟上直播的速度。
圖9,10和11中的結果比較了TwitchTranscoder與FFmpeg的執行時間。實驗表明,即使在我們處理相同及更多(除了上面指定的棧之外,還提供僅音頻轉碼,縮略圖生成等等)任務的情況下,我們的轉碼器對于離線轉碼一直有絕對優勢。
對于輸出單個版本的720p60,FFmpeg稍快,這是因為TwitchTranscoder要處理如上所述的更多任務。當版本的數量增加時,TwitchTranscoder的多線程模型表現出更大的優勢,這些優勢幫助它超越了FFmpeg。觀察Twitch完整的ABR梯度,與FFmpeg相比,TwitchTranscoder節省了65%的執行時間。
圖9:TwitchTranscoder與FFmpeg轉碼時間比較,實驗1
圖10:TwitchTranscoder與FFmpeg轉碼時間比較,實驗2
圖11:TwitchTranscoder與FFmpeg轉碼時間比較,實驗2
我們通過比較在出問題前,一臺機器上最多能夠運行多少個FFmpeg的并行實例來進行實時流轉碼測試。這里可能發生的問題包括幀丟失、視頻偽影等。在我們的生產服務器中,我們能夠支持多個通道同時進行轉碼,同時,更多的通道被轉封裝。不幸的是,運行多個FFmpeg實例會導致一系列影響轉碼輸出的錯誤,并且需要更高的CPU利用率(請參見圖12中的屏幕截圖)。
圖12:FFmpeg運行多個實例時的錯誤消息
結論
在本文中,我們將FFmpeg作為實時流RTMP- to-HLS的轉碼器進行了研究,并提供了有關如何操作該工具的信息。該解決方案部署起來很簡單,但有一些技術問題值得注意,比如段錯位、不必要的性能損失,以及缺乏支持我們產品功能的靈活性等。因此,我們實現了自己內部的轉碼器軟件棧TwitchTranscoder,它運行在一個定制的線程模型中,并可以在一個進程中輸出N個處理版本。
LiveVideoStack招募全職技術編輯和社區編輯
LiveVideoStack是專注在音視頻、多媒體開發的技術社區,通過傳播最新技術探索與應用實踐,幫助技術人員成長,解決企業應用場景中的技術難題。如果你有意為音視頻、多媒體開發領域發展做出貢獻,歡迎成為LiveVideoStack社區編輯的一員。你可以翻譯、投稿、采訪、提供內容線索等。
通過contribute@livevideostack.com聯系,或在LiveVideoStack公眾號回復『技術編輯』或『社區編輯』了解詳情。
總結
以上是生活随笔為你收集整理的Twitch如何实现转码比FFmpeg性能提升65%?(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Twitch如何实现转码器比FFmepg
- 下一篇: 语音视频社交背后技术深度解析