隨著直播和短視頻的興起,視頻由於承擔了更大的信息量,因此現在已經是非常主流的運營/產品信息輸出方式。但由於國內各瀏覽器廠商本身的利益關係所在,他們對HTML5的Video能力做了非常多的限制,不限於:
其具體問題可參考騰訊IMWeb團隊編寫的《複雜幀動畫之行動端Video採坑實現》。
為了解決這些問題,我們透過軟解FLV的方式實現了WXInlinePlayer,其用的第三方技術和平台API如下:
同時我們也寫了WebAssembly版本的FLV Demuxer,你可以在lib/codec找到相關程式碼。
相容測試使用BrowserStack服務提供的相關機型,僅供參考:
https://eroszy.github.io/WXInlinePlayer/example/index.html
請確保你安裝過parcel / emscripten 1.38.45 / cmake 以及make,然後執行以下指令:
npm install # 初始化工程
npm update # 更新工程有关的插件。如果网络错误,改用 cnpm update
bash build.sh
最終產物會在example資料夾中。
請注意:
- 請在*nix環境下進行build,並不能保證Windows下的OpenH264的編譯
- 請確保emscripten在1.38.45版本,否則會出現wasm32錯誤
- cmake 版本需要是3.16+
<!DOCTYPE html >
< html >
< head >
< meta charset =" UTF-8 " />
< title > WXInlinePlayer </ title >
< style >
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
}
</ style >
</ head >
< body >
< canvas id =" container " width =" 800 " height =" 450 " > </ canvas >
< script src =" ./index.js " > </ script >
< script >
if ( WXInlinePlayer . isSupport ( ) ) {
WXInlinePlayer . init ( {
asmUrl : './prod.baseline.asm.combine.js' ,
wasmUrl : './prod.baseline.wasm.combine.js'
} ) ;
WXInlinePlayer . ready ( ) . then ( ( ) => {
const player = new WXInlinePlayer ( {
url : 'https://static.petera.cn/mm.flv' ,
$container : document . getElementById ( 'container' ) ,
hasVideo : true ,
hasAudio : true ,
volume : 1.0 ,
muted : false ,
autoplay : true ,
loop : true ,
isLive : false ,
chunkSize : 128 * 1024 ,
preloadTime : 5e2 ,
bufferingTime : 1e3 ,
cacheSegmentCount : 64 ,
customLoader : null
} ) ;
const { userAgent } = navigator ;
const isWeChat = / MicroMessenger / i . test ( userAgent ) ;
if ( ! isWeChat ) {
alert ( 'click to play!' ) ;
document . body . addEventListener ( 'click' , ( ) => {
player . play ( ) ;
} ) ;
}
} ) ;
}
</ script >
</ body >
</ html >
在工程根目錄,輸入指令啟動server:
npm run serve
然後輸入網址存取demo:
http://localhost:8888/example/index.html
目前執行環境是否支援WXInlinePlayer。
if ( WXInlinePlayer . isSupport ( ) ) {
console . log ( 'WXInlinePlayer support' ) ;
}
初始化WXInlinePlayer,需要傳入載入的H264解碼庫的具體位址,關於解碼庫的選擇,請參考:如何選擇解碼依賴。
if ( WXInlinePlayer . isSupport ( ) ) {
WXInlinePlayer . init ( {
asmUrl : './prod.baseline.asm.combine.js' ,
wasmUrl : './prod.baseline.wasm.combine.js'
} ) . catch ( e => {
console . log ( `WXInlinePlayer init error: ${ e } ` ) ;
} ) ;
}
WXInlinePlayer已經準備就緒,可以安全的進行初始化操作。
if ( WXInlinePlayer . isSupport ( ) ) {
WXInlinePlayer . init ( { /*.....*/ } ) ;
WXInlinePlayer . ready ( ) . then ( ( ) => {
console . log ( 'WXInlinePlayer ready' ) ;
} ) ;
}
WXInlinePlayer建構函數,相關初始化參數請參考:初始化參數。
WXInlinePlayer . ready ( ) . then ( ( ) => {
const player = new WXInlinePlayer ( { /*...*/ } ) ;
} ) ;
進行影片播放。需要注意的是由於瀏覽器限制(不包含微信及Chrome 66版本以下),高版本已經禁用了音頻自動播放,因此直接調用此方法可能不會有作用,請在click/touchstart/touchend/touchmove等事件中讓使用者主動觸發。
document . body . addEventListener ( 'click' , ( ) => {
player . play ( ) ;
} ) ;
停止整個播放器,不可被恢復(resume)。
player . stop ( ) ;
暫停目前播放。
player . pause ( ) ;
恢復由pause引起的暫停操作。
player . resume ( ) ;
取得/設定目前音量。
const volume = player . volume ( ) ; // get volume
player . volume ( volume ) ; // set volume
取得/設定靜音狀態。
const muted = player . mute ( ) ; // get mute
player . mute ( muted ) ; // set mute
銷毀播放器,釋放所有記憶體等待回收。
player . destroy ( ) ;
取得目前播放時間,請注意,可能出現負值的情況請注意處理。
player . on ( 'timeUpdate' , ( ) => {
let currentTime = player . getCurrentTime ( ) ;
currentTime = currentTime <= 0 ? 0 : currentTime ;
} ) ;
可播放時長,可理解為緩衝的時長。
player . on ( 'timeUpdate' , ( ) => {
const duration = player . getAvaiableDuration ( ) ;
} ) ;
目前有3套解碼庫,分別是:
其差別在於:
我們推薦當你播放廣告影片/行銷影片/小動畫影片等對依賴庫大小敏感的時候使用baseline.asm/baseline.wasm,而在播放點播影片/直播影片時等對依賴庫大小不敏感的時候使用all.asm/all.wasm。
在開發本機上,針對同一視頻,WXInlinePlayer與手淘、花椒等FFMpeg實現在內存佔用和CPU佔用上相差不大,WXInlinePlayer性能整體較FFMpeg方案好5-10%左右,而H265由於減少的deblock,其效能相較於FFMpeg方案好30%左右,以下為H265的播放效能比較:
WXInlinePlayer的卡頓和延遲主要來自於3個地方:
一般來說,如果在使用者網路環境較好的情況下,渲染由於使用了WebGL,很難造成瓶頸(操作很單一),其中一般會因為軟解碼效能不足造成不停卡頓及延遲。
優化因為軟解碼性能不足造成的延遲,我們一般從幾個地方著手:
目前WXInlinePlayer在中高階主機上解1280x720,碼率1024,幀率24fps的影片比較流暢。
關於以上提到的影片參數你可以透過FFmpeg查看:
ffmpeg -i " your.flv "
這裡我們給出主流平台的profile/幀率/碼率/解析度供參考:
平台 | 類型 | 清晰度 | profile | 幀率 | 碼率 | 解析度 |
---|---|---|---|---|---|---|
虎牙 | 橫螢幕 | 標清 | High | 24 | 500k | 800x450 |
虎牙 | 橫螢幕 | 高畫質 | High | 24 | 1200k | 1280x720 |
虎牙 | 垂直螢幕 | 高畫質 | Main | 16 | 1280k | 540x960 |
奇秀 | 垂直螢幕 | 標清 | High | 15 | 307k | 204x360 |
奇秀 | 垂直螢幕 | 高畫質 | High | 15 | 512k | 304x540 |
奇秀 | 垂直螢幕 | 超清 | Baseline | 15 | 1440k | 720x1280 |
抖音 | 垂直螢幕 | 預設 | High | 30 | 1600k(變化較多,僅供參考) | 720x1280 |
快手 | 垂直螢幕 | 預設 | High | 25 | 2880k(變化較多,僅供參考) | 720x1280 |
我們建議你:
WXInlinePlayer的我們常用的低延遲設定參數如下,僅供參考,實際上請依照你的直播串流/點播檔案設定調整:
{
chunkSize : 128 * 1024 ,
preloadTime : 5e2 ,
bufferingTime : 1e3 ,
cacheSegmentCount : 64 ,
}
同時,你可以使用performance事件來判斷目前的解碼效能,然後提示使用者並降級到你的後備方案(例如直接video播放/靜態圖/序列幀等):
player . on ( 'performance' , ( { averageDecodeCost , averageUnitDuration } ) => {
const prop = averageUnitDuration / averageDecodeCost ;
if ( prop >= 2.0 ) {
console . log ( 'good performance' ) ;
} else if ( prop < 2.0 && prop >= 1.0 ) {
console . log ( 'ok, thats fine' ) ;
} else {
console . log ( 'bad performance' ) ;
}
} ) ;
FFmpeg方案目前有幾個比較大的問題,第一個是解碼庫的大小,精簡後2M左右,gzip大約600k,這對於在意依賴庫大小的產品是不可接受的。其次FFmpeg的方案很難被自己優化,例如WXInlinePlayer在2.0時會做多Worker的解碼,這對於這類方案的修改成本是非常大的。
卡頓和延遲的原因比較複雜,對於WXInlinePlayer來說一般情況是解碼速度跟不上播放速度,請參考如何降低卡頓和延遲進行最佳化。
UC不管是iOS或Android都對WebAssembly/ASM.js進行了閹割,因此索性不支持了。
請使用FFmpeg或其他類似的工具,這裡給出一個簡單的指令範例:
ffmpeg -i " your.mp4 " -vcodec libx264 -acodec aac out.flv
WXInlinePlayer的FLV規範遵循金山的FLV拓展規範,如果需要進行相關的編碼,可以參考其相關的FFmpeg patch或金山編寫的編碼器。