本編博客記錄桌面虛擬化移動端預研。
完整demo: https://github.com/MarkRepo/wfs.js
常見的直播方案有RTMP RTSP HLS 等等, 由於這些流都需要先傳輸到服務器,然后進行推流,延時比較大,RTMP可以優化到1s,hls延時最高,大概10s左右。
虛擬桌面要求延時能在100ms以內。經過google查找資料發現有以下幾種方案可以實現:
1. 用websocket 傳輸h264編碼數據,在瀏覽器中使用broadway開源庫進行解碼,調用html5 canvas繪制圖像。在github上有一個demo,經過測試,broadway解碼效率不高。(測試環境 chrome book)
參考: https://github.com/131/h264-live-player
2. 使用webRTC 進行點對點直播,找了一個demo,搭建了一個聊天室測試,延時效果大概在500ms左右,應該可以優化。webRTC的接口封裝的很好,只有三個接口。
demo: https://github.com/LingyuCoder/SkyRTC-demo 參考:https://segmentfault.com/a/1190000000436544
上面的demo有一個地方需要注意: 使用http服務無法獲取到視頻流,瀏覽器報錯,提示需要https服務。改成https服務之后,測試成功。
這個方案可行,但是需要自己去改webRTC的源碼,工作量比較大,所以沒有采用。
3. 使用MSE(Media Source Extension, 具體參考W3C標准)擴展實現 HTML5 video tag的流式直播。(最終采用的方案)
方案描述: 使用websocket 從服務端傳輸h264編碼數據到瀏覽器, 在瀏覽器端使用JS 解析h264數據 , 封裝成fMP4 fragment, 喂給media source 中的sourceBuffer, 瀏覽器video tag自動獲取sourceBuffer中的數據進行解碼渲染。
最后實現的demo體驗效果良好,延時能達到100ms以內,使用筆記本軟解、硬解, chrome book 軟解表現都很完美,唯獨chrome book 硬解會緩沖一幀數據,是一個瑕疵, 不過這個缺點可以在服務器端多發一幀數據解決。(見后文)
下面主要記錄預研過程中出現的重要問題和解決方案:
(1)解析h264數據,封裝fMP4 fragment。
這一步比較復雜,由於之前沒有JS開發經驗,沒有選擇自己寫,在github找了一個開源實現。參考:https://github.com/ChihChengYang/wfs.js; 根據wfs.js搭建的直播方案,主要出現三個問題(只有第一個延時是wfs.js庫的問題,其余是自己的問題):
(2)第一個是延時問題,延時很大,在3~5s左右
原因有兩個: 1. wfs.js庫中做了緩存,收到一定的數據之后才執行fMP4 fragment的封裝。
2. chrome瀏覽器的解碼器默認不是以直播流的模式解碼視頻幀,所以會在解碼的時候緩存4幀數據。
解決方法: 1. 把wfs.js庫中的緩存去掉,每來一幀數據都執行fMP4 fragment的封裝
2. 設置mvhd.duration = 0,如果有mehd的話,設置mehd.fragmentDuration = 0, 這樣chrome 會進入“low delay mode”, 不會緩存數據。
具體參考: https://stackoverflow.com/questions/36364943/frame-by-frame-decode-using-media-source-extension
https://bugs.chromium.org/p/chromium/issues/detail?id=465324
(3)第二個就是解碼問題,解碼花屏
原因: 虛擬機spice服務端使用了websokify代理(python 寫的)。首先,這個代理服務器是流式的(出現數據幀被分割和合並的現象),瀏覽器端js沒有進行數據幀邊界的解析; 第二,代理緩沖區過小,導致數據幀被分割傳輸。
解決方法:1. 修改websokify代理的接收緩沖區大小。
2. 在wfs.js庫中對收到的數據進行解析,一幀一幀的提交數據,封裝fMP4 fragment。
(4)第三是屏幕倒轉問題
原因: spice服務端發過來的h264數據就是倒的,在終端平台,是由終端處理的。
解決方法: 利用css的畫面旋轉功能,以x軸為旋轉軸, 旋轉180度。如:
<style type="text/css" media="screen">
video.rotate180{
width:100%;
height:100%;
transform:rotateX(180deg);
-moz-transform:rotateX(180deg);
-webkit-transform:rotateX(180deg);
-o-transform:rotateX(180deg);
-ms-transform:rotateX(180deg);
}
</style>
(5)關於chrome book硬解碼緩存一幀問題解決辦法
通過看chrome源碼 decoder部分,發現decoder處理幾個數據類型會直接flush緩沖區,所以可以在wfs.js每收到一幀數據,
就構造一幀這種類型的空數據,喂給video tag, 把緩沖的一幀flush出來,同時把播放時間縮短一半即可(否則會幀堵塞)。示例:
var copy2 = new Uint8Array(4);
copy2[0] = 0, copy2[1] = 0, copy2[2] = 1, copy2[3] = 10; //類型10,11 都可以,但是10可以兼容軟解
this.wfs.trigger(Event.H264_DATA_PARSING, {data: copy2});