背景
ARC(高德車機雲控平台)是一個基於車載設備業務深度定制的雲控平台,通過該平台我們能夠實現遠程使用不同類型的車載設備。為了讓遠程使用者像在本地一樣使用車載設備,需要將車載設備的畫面及時的傳回給使用者。因此,畫面傳輸能力是ARC平台的一個核心組件。
起初我們采用行業內普遍在用的畫面傳輸開源方案(minicap)。該方案獲取到屏幕數據后壓縮生成JPG圖像,逐幀傳輸到Web端進行展示。由於車機性能比手機差很多,壓縮圖片消耗CPU性能大,在部分低端車機設備上壓縮圖片能消耗80%左右的CPU,容易使設備使用出現卡頓。同時圖像壓縮率不算很高,傳輸消耗帶寬大,在低帶寬下造成用戶看到的畫面過度延遲。
因此,我們需要一個解決方案能夠平衡傳回的畫面質量和車機端的CPU資源消耗。本文將小結本次雲控平台畫面傳輸的視頻流方案。
思路方法
基於圖像數據的基本傳輸鏈路,為了能夠不消耗設備端CPU資源,首先想到了圖像不進行壓縮,先傳輸到服務端進行處理。但是經過調研,車機的USB帶寬傳輸根本無法滿足高清圖像不壓縮進行傳輸,高清原始數據非常大,基本1秒只能傳輸三幀左右的數據。
另一個思路是采用設備端的硬件編碼器減少CPU資源的消耗。經過調研Android 4.1開始基本都自帶了H264視頻編碼器。因此,決定嘗試采用視頻流的方案,在設備端通過硬件編碼器編碼成視頻流,通過服務端轉發到Web端進行解碼展示。
實現方案
整個實現方案可以分為以下三個部分:
-
設備端:負責畫面的獲取和編碼。
-
服務端:處理視頻流的傳輸和控制。
-
Web端:視頻流的解碼和展示。
畫面的獲取和編碼
圖像畫面的獲取直接采用的是Android的Virtual Display。編碼方式有多種實現方法:
由於Java方案只能支持Android 5.0以上機器, 而目前車載市場Android 4.x的占比還比較大,無法忽略。因此只能使用cpp的方案,最低可兼容Android 4.3版本。
視頻流的傳輸和控制
Web端最常見的直播方案是rtmp/hls/flvjs等。但是這些方案最低都有1-3s的延遲。對於一般直播平台沒有影響,但是對於有實時交互場景的雲控平台,要求達到毫秒級延遲。所以,最終決定采用H264裸流通過Socket傳輸的方案,設備端編碼H264視頻流直接傳輸到Web端進行播放。
同時,為了提高使用體驗,對視頻流的傳輸增加了彈性控制。通過在服務端加入緩存隊列用來監控前端帶寬負載情況,根據帶寬狀況自動調節幀率和碼率,優先保證使用者的流暢感。
Web端展示和解碼
Web端展示使用media source extensiton(MSE) + fragment mp4的方案, 把H264裸流封裝成fragment mp4后,通過MSE api進行解碼播放, 具體實現是參考了開源的Jmuxer方案。
丟幀和補幀
默認情況下Android Virtual Display產生的最大幀率是60fps,而我們肉眼30fps就能感覺流暢。為了能夠節省帶寬,我們定義了視頻流最大輸出幀率是30fps。在網絡帶寬較差的情況下,我們還能夠降低幀率來最大限度的避免延遲。同時,Android MediaCodec不支持控制幀率,幀率是由每秒送入的幀數量決定的。因此,我們需要通過實現丟幀來進行幀率控制。
Win7硬件解碼器沒有低延遲模式,需要大概10幀左右數據才能開始播放,而VirtualDisplay是畫面有變化才會產生圖像幀,因此需要實現補幀來消除解碼延遲。
我們通過創建一個EglSurface對丟幀補幀進行處理,通過時間間隔控制eglTexture繪入EglSurface的頻度進行丟幀,通過重復繪入最后一幀數據進行補幀。
總結
該方案在ARC平台上的使用,在保證傳輸質量的同時,有效的提升了使用者操作的流暢感。該方案理論上也可以應用於其他類似的雲控平台上,如果不需要支持Android 4.x設備,采用Java層API來獲取視頻流數據,則可以降低開發和適配成本。