本編博客記錄桌面虛擬化移動端預研。css
完整demo: https://github.com/MarkRepo/wfs.jshtml
常見的直播方案有RTMP RTSP HLS 等等, 因爲這些流都須要先傳輸到服務器,而後進行推流,延時比較大,RTMP能夠優化到1s,hls延時最高,大概10s左右。html5
虛擬桌面要求延時能在100ms之內。通過google查找資料發現有如下幾種方案能夠實現:python
1. 用websocket 傳輸h264編碼數據,在瀏覽器中使用broadway開源庫進行解碼,調用html5 canvas繪製圖像。在github上有一個demo,通過測試,broadway解碼效率不高。(測試環境 chrome book)git
參考: https://github.com/131/h264-live-playergithub
2. 使用webRTC 進行點對點直播,找了一個demo,搭建了一個聊天室測試,延時效果大概在500ms左右,應該能夠優化。webRTC的接口封裝的很好,只有三個接口。web
demo: https://github.com/LingyuCoder/SkyRTC-demo 參考:http://www.javashuo.com/article/p-kvxfcpva-hh.htmlchrome
上面的demo有一個地方須要注意: 使用http服務沒法獲取到視頻流,瀏覽器報錯,提示須要https服務。改爲https服務以後,測試成功。canvas
這個方案可行,可是須要本身去改webRTC的源碼,工做量比較大,因此沒有采用。segmentfault
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});