light-rtc: 理念與實踐
在與同行交流過程中,發現很多同行對 WebRTC 改動太多,導致無法升級 WebRTC 版本。而 WebRTC 開源社區的快速迭代,讓他們感到欣喜又焦慮:開源社區的迭代效果,是不是超過了他們對 WebRTC 的優化效果?我們針對特定場景優化 WebRTC 時,怎麼緊跟 WebRTC 開源社區通用的優化?
作者:阿里雲智慧技術專家 熊金水
理念
簡言之,把 WebRTC 作為 Framework 使用,而不是 Library,即:WebRTC 倉庫輕量化,核心模組插件化。
詳細的,WebRTC 作為 Framework 串聯核心模組;核心模組既可以以插件形式使用我們的實現,也可以 Fallback 到 WebRTC 的默認實現。目的是減少 WebRTC 衝突的可能性,提高升級 WebRTC 的敏捷性。
目標:一年升級一次 WebRTC,一次花費一個人月。
架構
模組拆解
WebRTC 的核心模組,包括:
音頻
- ADM 採集、APM、ACM 編碼;
- NetEQ 與解碼、AM、ADM 渲染;
影片
- 採集、編碼;
- JB、解碼、渲染;
通用
- RTP 打包與解包、FEC 生成與恢復、CC 與 Pacer、ICE、SDP 信令等。
WebRTC 在長期的演進中,API 已經具備了作為 Framework 的大部分能力。紅色的核心模組,已經基本可以插件化,如下面的 API:
倉庫管理
light-rtc 作為 WebRTC 倉庫,我們需要保留兩個 Remote,一個是 Alibaba,一個是 Google。升級 WebRTC 時,我們從 Google 上 Pull 最新程式碼, 解決衝突,然後 Push 到 Alibaba。
對插件化的模組,我們需要放到單獨的倉庫 lrtc-plugin 里,這樣有兩個好處:
- 對 light-rtc 倉庫改動少,減少與 Google 衝突的可能性;
- 更重要的,讓每個開發同學,在每次改動前,更主動、更有意識的思考,放到哪個倉庫更合適,否則容易慣性思維,直接改動 light-rtc。
對 lrtc-plugin 依賴的第三方庫,也應該以單獨的倉庫存在,並保留兩個 Remote,比如 Opus,這樣,即使修改了 Opus 源碼,仍然可以像升級 light-rtc 一樣,方便的單獨升級 Opus 版本。
模組
Codec
音頻編解碼器、影片編解碼器,是我們最常優化的部分之一:
- 新的編碼工(AV1/SCC/ROI 等)優化影片品質和頻寬;
- 解析度自適應,使不同能力(編碼能力、發送頻寬等)的發送端,發送不同解析度的碼流;
- Simulcast,為不同能力(解碼能力、顯示能力、接收頻寬等)的接收端,提供不同解析度碼流;
- SVC,提供時域/空域分層;
- 新的影片解碼實現,規避 Mac 硬解卡死等問題;
- 新的音頻編碼器,適配商用接收端;
……
這部分插件化是相對簡單的,只需要實現自己的 [Video|Audio][Encoder|Decoder]Factory
即可。以 Simulcast為例,在自己實現的 VideoEncoderFactory 里,先用 WebRTC 原始的 VideoEncoderFactory,創建多個 Encoder 對象,然後封裝到一個 Simulcast Encoder 里。
ADM
很可惜,ADM(Audio Device Module)沒有提供檢測設備插拔的功能,需要增加 Callback 介面。
另外,雖然 WebRTC 支援樣本數量的監控,但是當前只用於列印日誌,如果想在此基礎上做更多事情(如:發現採集樣本為 0 時,重啟採集),則單獨做一個 AudioSampleMoniter 的類,比較有利於擴展。
ADM 是一個適配難點,相信是困擾 RTC 同行的共同難題。不同作業系統、不同機型,都可能有不一樣的問題。例如:
- Mac 3.5mm 耳機插拔時,偶爾崩潰;
- Mac 獲取的設備 ID 在插拔後發生變化,不能做持久化;
- 聯想 X1 電腦,多次插拔後,整個 Audio 後台服務失效;
- 某些 Windows 機型採集不到聲音;
- 某些手機采音許可權問題;
……
這些修改大部分屬於 Bugfix,參考「Bugfix」章節。
APM
APM(Audio Processing Module)可能是 light-rtc 相對難處理的部分。
APM 與 NetEQ 一起,可能是 WebRTC 核心模組中,開源價值最大的部分。在我對 APM 有限的認知里,對 APM 常見的優化可能有:
- 混音後的遠端訊號,做濾波/均衡處理。這是業界不少音頻演算法的必要條件;
- 利用 Android 手機特性,優化 AECM,尤其是 Double Talk 時的效果;
- 嘯叫檢測與抑制;
- 利用機型特性,優化 AGC,提高語音音量;
……
下圖是 WebRTC APM 內部模組的數據流程圖:
從圖中可以看出,APM 其實也為插件化做了準備,但是只在近端訊號的尾部、遠端訊號的頭部。從 APM 構造函數上也可以看出來:
濾波/均衡,可以方便的實現一個 CustomProcessing 的 render_pre_processor。
其他的優化,遵循輕量化/插件化的理念,沒有現成的插件介面,我們可以創造新的插件介面,如嘯叫抑制,以及 AECM 優化的部分演算法。
但 APM 仍然會有很多沒辦法插件化的,只能修改 light-rtc 倉庫,如 AECM Double Talk 優化等。
AM
AM(Audio Mixer)的插件化,可以在不修改 light-rtc 的基礎上,玩出很多花樣:
- 播放本地文件;
- 藉助語音檢測演算法,優化語音排序,從而選出更準確的語音做混音;
- Mono 變成 Stereo,藉助 HRTF,可以在多方同時說話時提高說話人辨識度和可懂度;
- 對 RTP 方案的回放,倍速回放時變速不變調;
……
FEC
FEC(Forward Error Correction),常見的修改:
- 調參,如冗餘度、MaxFrames、Table 類型,包括固定參數和動態自適應調參兩類,已有的插件介面 WebRTC::FecControllerFactoryInterface 即可滿足;
- RSFEC,需要創造新的插件介面;
- Opus Inband FEC。WebRTC 動態配置的 Opus FEC 參數,不能很好的解決弱網時聲音卡頓問題。這時,一個辦法是把 Opus 獨立成倉庫,直接修改 Opus 編碼器。
CC
CC(Congestion Control),包含兩個方面,一個是 CC 演算法本身,一個是 CC 關聯模組。
演算法本身,可以用不同的演算法實現,如 WebRTC 默認的 goog_cc,也可以是 BBR,甚至是滿足 WebRTC::NetworkControllerFactoryInterface 介面的外部插件。
關聯模組:
- 頻寬分配:不同場景可能不一樣,如影片會議里,需要「保音頻、保螢幕」。可以通過 rtc::BitrateAllocationStrategy 實現插件化。
- Pacer 調優:對於螢幕內容,I幀往往非常大,WebRTC 的 2.5 倍的發送頻寬,會導致巨大的首幀時間。具體解法見仁見智。
……
VideoRender
Android、iOS、Mac,WebRTC 都提供了默認的實現,雖然有少量 Bug,但是基本滿足需求。
Windows 平台,早期 WebRTC 提供了 D3D 的實現,最新版已經剔除,我們可以在 lrtc-plugin 倉庫實現自己的 D3D,或者其他的渲染,如 QT OpenGL。
VideoProcess
WebRTC 並沒有提供影片前處理(如:美顏)、後處理(如:超解析度)的介面,但是我們完全可以像 rtc::BitrateAllocationStrategy 一樣,創造 VideoProcessInterface 介面, 並在 lrtc-plugin 倉庫里實現。
讓 VideoProcessInterface 同時繼承 Sink 和 Source 介面,可以方便的把多個對象串聯起來。
其他 & Bugfix
其他核心模組,如 JitterBuffer、ICE 等,目前接觸的主要是 Bugfix,還沒有發現自己訂製重寫的必要。
Bugfix,往往只能修改 light-rtc 倉庫。一方面,是盡量把 Bugfix 內聚成函數,減少對已有程式碼的修改;另一方面,盡量把 Bugfix 貢獻到開源社區(Issue Tracker),既為開源社區做了貢獻,也徹底避免了升級的衝突。
貢獻到開源社區,往往比想像的要複雜,但也更能鍛煉人。在特定場景,往往只用了 WebRTC 一部分能力,如影片 JitterBuffer,一個 Bugfix 可能只考慮到了 H264,貢獻到開源社區時,則需要同時兼顧 VP8/VP9,甚至是將來的 AV1。在這個過程中,Google 工程師會在 Code Review 中與你親密切磋,其實是非常好的鍛煉機會,進一步提高對 WebRTC 的認識。
參考
WebRTC m74 源碼
RSFEC:
- WebRTC RSFEC 詳解和剖析;
- ARTP 技術探秘之:WebRTC 中支援 RS FEC。
(以上兩篇文章之後將會在本號推送)
CC
「影片雲技術」你最值得關注的音影片技術公眾號,每周推送來自阿里雲一線的實踐技術文章,在這裡與音影片領域一流工程師交流切磋。