前端同窗要使用 HTML5 播放器視頻,必然會使用 video 標籤,不過大多數同窗只是使用了較簡單的功能,其實它自己擁有不凡之力有待咱們發現。html
首先,咱們先來看下 video 最基礎的用法:前端
使用 src 屬性git
<video src="http://v2v.cc/~j/theora_testsuite/320x240.ogg" controls> 你的瀏覽器不支持 <code>video</code> 標籤。 </video>
使用 source 標籤github
<video controls> <source src="foo.ogg" type="video/ogg"> <source src="foo.mp4" type="video/mp4"> Your browser does not support the <code>video</code> element. </video>
這是 MDN 關於 video 給出的基本用例。在這裏咱們簡單介紹下兩種方法的不一樣,src 只能賦予 video 一個播放地址,當瀏覽器不支持這種視頻格式的解碼時就會出現錯誤,致使視頻播放失敗。爲了解決這個問題纔有了 source 標籤,利用多個 source 標籤引入不一樣格式的視頻,從上到下解析直到遇到看上述代碼當瀏覽器不支持 ogg 格式時,瀏覽器會自動播放 foo.mp4。web
咱們會發現使用 video:src 屬性播放視頻的時候會常常出現播放失敗,咱們該怎麼作能提高視頻播放的質量?瀏覽器
這種狀況通常都使用 cdn,爲了更保險通常還會將不一樣的 cdn 廠商分爲主用 cdn 和備用 cdn。那麼問題來了,咱們怎麼利用 video 自己的特性並結合 cdn 保證咱們的視頻播放質量?網絡
<video controls> <source src="//a.com/main.mp4" type="video/mp4"> <source src="//b.com/backup.mp4" type="video/mp4"> Your browser does not support the <code>video</code> element. </video>
不難看出,source 標籤不只支持不一樣視頻格式的自動切換,也適用於相同視頻格式的失敗切換,即 main.mp4 因網絡問題沒法獲取時瀏覽器會自動切換到 backup.mp4 。ide
若是你們瀏覽視頻的時候,能夠發現不少網站的 video 是這個樣子的:網站
<video src="blob:http://abc.com/d0823f0f-2b2a-4fd6-a93a-e4c82173c107"> </video>
若是直接訪問 blob 這個地址發現並不存在。ui
這個地址就是映射 Blob 對象的 DOMString,其實 video 屬性 src 支持 Blob 的,不過新的標準是使用 srcObject 屬性來替代這一個功能,目前代碼能夠這樣寫:
const mediaSource = new MediaSource(); const video = document.createElement('video'); try { video.srcObject = mediaSource; } catch (error) { video.src = URL.createObjectURL(mediaSource); }
在這段代碼中除了 Blob 對象,還有 MediaSource 對象,讓 video 擁有不凡之力的主要因素是瀏覽器對 MediaSource 對象的支持,才讓 JavaScript 對 video 有了更大的操縱空間。關於如何使用 MediaSource 不在本文講述,你們能夠自行查閱,咱們要說的是 video 結合 MediaSource 能夠作哪些事情?
點播領域裏 mp4 是最廣泛、兼容性最好的視頻容器,不過 mp4 也有它的侷限性,好比常見的清晰度切換,咱們是沒法像youtube那樣作到無縫切換的。咱們能夠看下普通的mp4播放的網絡請求和youtube視頻播放的網絡請求的區別。
圖1.1 普通mp4的下載請求過程
圖1.2 Youtube視頻下載請求過程
這兩張圖不難看出,在默認狀況下 mp4 使用一次 http 請求全部的視頻數據,Youtube 則分次請求。固然這個描述很不專業,但確實形象。形成這種差別的是 video 不支持流式的視頻數據,Youtube 採用的是流式的視頻容器 webm,而 mp4 是非流式的。那如何解釋清楚流式的視頻數據呢,從專業的角度三言兩語很難說清楚,但用大白話翻譯過來就是流式的視頻數據支持分段獨立播放,非流式的不能夠。換句話說一個10M的視頻文件,流式的視頻能夠把0~1M的數據請求回來單獨播放,可是非流式的不能夠。
上面咱們描述了視頻格式的不一樣,接下來咱們要說的是第一張圖中的視頻加載是瀏覽器來控制的,經過給 video 的 src 屬性配置視頻地址,觸發播放以後瀏覽器就會開始下載了,JS干涉不了。而 Youtube 的視頻加載是經過JS來控制的,各位能夠再次看下第二張圖的網絡請求類型:xhr,足以證實這一點。
上面兩點搞清楚以後咱們就該說下清晰度切換的事情了。這個需求你們都不陌生,可是直接使用 mp4 格式作無縫清晰度切換,難度還挺大的。先解釋下「無縫清晰度切換」的概念:從播放一個分辨率的視頻到另外一個分辨率且保證畫面、聲音不停頓的平滑切換過程。瞭解了這個概念,你們應該知道了用 video 無縫切換 mp4 有多難。一方面,video 是不支持流式的視頻格式的,一方面,video 的加載是不受JS控制的。經過切換 video 的 src 屬性,必然會致使畫面中斷、從新請求視頻數據等。有的同窗想到說利用兩個 video 再結合 z-index 來搞,可是當你生成另外一個video去加載視頻的時候,沒法保證兩個畫面是嚴格一致的,即便將原來的畫面暫停到一個時刻,用另外一個視頻經過 currentTime 屬性與之同步,切換仍然看到畫面閃爍,基本沒法和 Youtube 無縫切換的體驗匹敵。並且還會形成更多流量的浪費,背後的緣由你們能夠研究下 mp4 容器和 webm 容器的異同,也能夠看下視頻解碼相關的文章。
還有一種方法就是將 mp4 格式通通轉碼到流式的視頻格式好比 hls、webm 等。不過這種看上去可行的方式實際上會帶來很大的成本開銷,如將大量視頻作轉碼會消耗高昂的機器資源、雙倍存儲的費用、CDN的雙倍費用等等。其實咱們也是在這種背景下研究出來新的技術問題解決清晰度無縫切換的。
首先,咱們改變對 mp4 視頻的播放流程,再也不直接使用 video 的 src 來播放,由於咱們沒有任何能夠操做的空間。video不只支持 src 屬性還支持 Blob 對象,咱們就是利用後者。播放的流程以下:
圖1.3 mp4 視頻新播放流程
而後在作清晰度切換的時候流程以下:
圖1.4 mp4視頻清晰度切換原理示意圖
使用 video 進行解碼完成播放
而後在作清晰度切換的時候流程以下:
圖1.5 mp4視頻清晰度切換流程示意圖
這個過程看上去比較繁瑣,可是全部的操做都是在瀏覽器端完成,也就是說都是JS來實現的。這樣以前說的全部成本問題都不存在,還能作到youtube相同體驗的無縫切換。若是你們也想使用這個功能不須要本身再去實現一遍上述流程,可使用以下代碼:
import Player from 'xgplayer'; import 'xgplayer-mp4'; let player = new Player({ el:document.querySelector('#mse'), url: [{src:'/mp4/',type:"video/mp4"},{src:'/mp5/',type:'video/mp4'}] }); player.emit('resourceReady', [{name: '高清', url: '/mp4/',cname:'高清'}, {name: '超清', url: '/mp5/',cname:'超清'}]);
若是對這段代碼有什麼疑惑或者想深刻了解下它背後是如何實現的能夠參考 文檔 或者 Github。
咱們平時直接使用video加載視頻,大概是這樣的:
圖2.1 video默認下載截圖
我隨便找了個視頻,你們看下視頻總長度是 02:08,在播放到 00:05 的時候,瀏覽器已經下載到 01:30 了,若是用戶終止觀看,下載的視頻就這樣被浪費掉了。固然,若是不斷的 seek 也會形成較多的流量浪費。按照咱們以前的統計在短視頻領域,用戶 seek 的頻率在 80%,因此這部分流量是能夠節省掉的。具體原理以下:
圖2.2 播放器加載視頻原理
具體實現代碼以下:
import Player from 'xgplayer'; import 'xgplayer-mp4'; const player = new Player({ id:'vs', url:'//abc.com/a/mp4', preloadTime:10 });
這樣就實現了視頻在播放過程當中永遠只預加載10秒的數據,進而保證節省流量。