Meta 的 FFmpeg 應用:大規模媒體處理
FFmpeg 是名副其實的媒體處理萬用工具。作為業界標準工具,它支援多種音訊與視訊編解碼器及封裝格式。它還能編排複雜的濾鏡鏈,用於媒體編輯與處理。對於使用我們應用程式的用戶來說,FFmpeg 在實現新視訊體驗以及提升現有體驗穩定性方面發揮著重要作用。
Meta 每天執行 ffmpeg(主要 CLI 應用程式)和 ffprobe(用於獲取媒體檔案屬性的公用程式)執行檔數百億次,這在處理媒體檔案時帶來了獨特的挑戰。FFmpeg 可以輕鬆地對單個檔案進行轉碼和編輯,但我們的編排工作流有額外的需求。多年來,我們不得不依賴內部開發的 FFmpeg 分支(fork)來提供直到最近才加入 FFmpeg 的功能,例如執行緒化多路編碼(threaded multi-lane encoding)和即時品質指標計算。
隨著時間推移,我們的內部分支與 FFmpeg 上游版本產生了顯著差異。與此同時,新版本的 FFmpeg 帶來了對新編解碼器和檔案格式的支援,以及穩定性的提升,這些都讓我們能更順暢地接收來自用戶的多元視訊內容。這使得我們必須同時支援最新的開源 FFmpeg 版本與我們的內部分支。這不僅導致功能集逐漸分化,也為安全地進行程式碼變更(rebase)以避免效能倒退帶來了挑戰。
由於內部分支日益陳舊,我們與 FFmpeg 開發者、FFlabs 和 VideoLAN 合作,在 FFmpeg 中開發新功能,使我們能夠完全棄用內部分支,並在所有使用場景中完全依賴上游版本。透過上游化的補丁和重構,我們填補了以往依賴內部分支的兩個重要空白:執行緒化多路轉碼和即時品質指標。
為 VOD 和直播構建更高效的多路轉碼
當用戶透過我們的應用程式上傳視訊時,我們會生成一組編碼以支援 HTTP 動態自適應串流(DASH)播放。DASH 播放允許應用程式的視訊播放器根據網路狀況等訊號動態選擇編碼。這些編碼在解析度、編解碼器、幀率和視覺品質等級上可能有所不同,但它們都是從同一個來源編碼創建的,播放器可以在它們之間即時無縫切換。
在一個非常簡單的系統中,獨立的 FFmpeg 命令列可以按順序逐一生成每條路徑的編碼。這可以透過並行運行每個命令來優化,但由於每個進程執行的重複工作,這種方式很快就會變得效率低下。
為了改善這一點,可以在單個 FFmpeg 命令列中生成多個輸出,將視訊幀解碼一次後發送到每個輸出的編碼器實例。這消除了每個命令列帶來的視訊解碼和進程啟動時間的重複開銷。鑑於我們每天處理超過 10 億個視訊上傳,每個上傳都需要多次執行 FFmpeg,降低單個進程的運算使用量將帶來顯著的效率提升。
我們的內部 FFmpeg 分支對此提供了額外的優化:並行化視訊編碼。雖然單個視訊編碼器內部通常是多執行緒的,但在使用多個編碼器時,以前的 FFmpeg 版本會針對給定的幀串行執行每個編碼器。透過並行運行所有編碼器實例,整體上可以獲得更好的並行性。
感謝包括來自 FFlabs 和 VideoLAN 在內的 FFmpeg 開發者的貢獻,更高效的執行緒機制從 FFmpeg 6.0 開始實施,並在 8.0 版本中完成最終完善。這直接受到了我們內部分支設計的影響,也是我們過去依賴內部分支的主要功能之一。這項開發引發了 FFmpeg 數十年來最複雜的重構,並為所有 FFmpeg 用戶實現了更高效的編碼。
為了完全從內部分支遷移,我們還需要在上游實現另一個功能:即時品質指標。
在直播轉碼時啟用即時品質指標
視覺品質指標能以數值呈現媒體的感知視覺品質,可用於量化壓縮造成的品質損失。這些指標分為參考性(reference)或無參考性(no-reference)指標,前者是將參考編碼與其他失真編碼進行比較。
FFmpeg 可以在編碼完成後,透過獨立的命令列使用兩個現有的編碼來計算各種視覺品質指標(如 PSNR、SSIM 和 VMAF)。這對於離線或 VOD 場景是可以接受的,但對於我們可能希望即時計算品質指標的直播場景則不適用。
為此,我們需要在每個輸出路徑使用的每個視訊編碼器之後插入一個視訊解碼器。這些解碼器為壓縮後的視訊每一幀提供位圖,以便我們與壓縮前的幀進行比較。最終,我們可以使用單個 FFmpeg 命令列即時為每條編碼路徑生成品質指標。
感謝由 FFlabs 和 VideoLAN 等 FFmpeg 開發者啟用的「環路內(in-loop)」解碼功能,從 FFmpeg 7.0 開始,我們不再需要依賴內部 FFmpeg 分支來實現這項功能。
我們在對社群影響最大時進行上游化
諸如轉碼時的即時品質指標和更高效的執行緒機制,可以為 Meta 內外的各種基於 FFmpeg 的流水線帶來效率提升,我們致力於在上游推動這些發展,以造福 FFmpeg 社群和更廣泛的業界。然而,我們內部開發的一些補丁並不適合貢獻給上游。這些補丁高度針對我們的基礎設施,不具備通用性。
FFmpeg 支援透過 NVIDIA 的 NVDEC 和 NVENC、AMD 的統一視訊解碼器 (UVD) 以及 Intel 的 Quick Sync Video (QSV) 等設備進行硬體加速解碼、編碼和過濾。每個設備都透過 FFmpeg 中標準 API 的實現來支援,從而簡化整合並減少對特定設備命令列標記的需求。我們透過這些相同的 API 增加了對 Meta 可擴展視訊處理器 (MSVP)(我們用於視訊轉碼的客製化 ASIC)的支援,使得在不同硬體平台上能使用通用工具,並將平台特定的差異降至最低。
由於 MSVP 僅在 Meta 內部的基礎設施中使用,若沒有硬體進行測試和驗證,FFmpeg 開發者將難以支援它。在這種情況下,將此類補丁保留在內部是有意義的,因為它們不會為外部帶來利益。我們承擔了隨著時間推移將內部補丁 rebase 到更新的 FFmpeg 版本的責任,並利用廣泛的驗證來確保升級過程中的穩健性和正確性。
我們對 FFmpeg 的持續承諾
憑藉更高效的多路編碼和即時品質指標,我們能夠在所有 VOD 和直播流水線中完全棄用內部的 FFmpeg 分支。得益於 FFmpeg 中的標準化硬體 API,我們能夠以最小的摩擦同時支援 MSVP ASIC 和基於軟體的流水線。
FFmpeg 經歷了超過 25 年活躍開發的考驗。那些提高資源利用率、增加對新編解碼器和功能支援以及提升穩定性的開發,為更廣泛的媒體提供了強大的支援。對於我們平台上的用戶來說,這意味著實現了新體驗並提升了現有體驗的可靠性。我們計劃繼續與開源開發者合作投資 FFmpeg,為 Meta、整個行業以及使用我們產品的人們帶來利益。
致謝
我們要感謝開源社群、我們在 FFlabs 和 VideoLAN 的合作夥伴,以及許多 Meta 工程師的貢獻,包括 Max Bykov、Jordi Cenzano Ferret、Tim Harris、Colleen Henry、Mark Shwartzman、Haixia Shi、Cosmin Stejerean、Hassene Tmar 和 Victor Loh。
分享此內容: