Web 視頻播放的那些事兒

圖片來源: https://ultimatewebsitedesign...

本文做者:hsyhtml

背景

對於視頻的在線播放,按視頻內容的實時性能夠分爲點播(VOD)和直播(Live Streaming)。現現在在 Web 環境下須要進行視頻播放時,一般可使用 video 標籤,經過它將視頻播放的各個環節都託管給瀏覽器。前端

視頻的在線播放,站在視頻消費者這一側來看,主要的技術環節在於視頻的解碼、顯示效率,以及視頻數據的傳輸效率。Web 標準中經過 video 標籤,將這兩個環節進行解耦,開發者沒必要關心視頻數據的解碼、顯示環節,而在數據加載環節進行一些擴展。git

在點播的場景下,視頻的生產者已經預先準備好了視頻的數據內容,站在視頻消費者這一側的開發者只須要指定 video 標籤 src 屬性爲對應的資源地址便可。可是在一些複雜的需求中,好比須要細緻地控制視頻數據的預加載時機和數據量,那麼則須要對視頻的一些規格參數以及相關的技術點作進一步的瞭解。github

本文將對視頻點播場景下,對數據加載環節進行拓展所需瞭解的技術內容作簡單的介紹。web

視頻文件的參數

視頻文件有一些常見的規格參數,它們做爲視頻相關技術內容的起點,簡單地瞭解它們有助於更快的理解在此之上的內容。算法

視頻的產生

起初視頻的來源都是天然界中的事物對光的反射,被圖像採集設備按必定的頻率採集後進行保存,在須要觀看的時候,將保存的內容按必定的頻率進行播放。這個大體的流程一直延續至今,只不過因爲數字技術的成熟,視頻的內容也能夠直接經過軟件編輯生成。瀏覽器

人類能夠識別的光譜和光線變化的頻率都具備必定的範圍,低於或者高於這個範圍的變化都沒法被絕大部分人所感知。所以在保存以及播放視頻的時候,須要結合人的感官體驗以及軟硬件資源的限制對視頻的各個參數作相應的調整。緩存

幀率 Frame-rate

視頻的播放原理相似幻燈片的快速切換。安全

每一次切換顯示的畫面,稱之爲一幀(Frame)。而幀率則表示每秒切換的幀數,所以它的單位是 FPS(Frames Per Second)。幀率並不和視頻的清晰度直接相關,但它卻也是影響感官體驗的重要因素。bash

人類對畫面的切換頻率的感知度有一個範圍,通常 60FPS 左右是一個比較合適的範圍。但這並非絕對的,在須要記錄一個變化須臾之間的鏡頭時,準備足夠的幀數才能捕捉到細微的變化;當拍攝一個緩慢的鏡頭推動的效果時,幀率並不須要過高,分辨率會起到更高的做用。

幀率的選取除了須要結合播放內容,還須要結合顯示設備的刷新頻率,不然選取了太高的幀率而顯示設備不支持的話,多餘的幀也只會被丟棄。

分辨率 Resolution

在視頻播放時,顯示到屏幕上的每一幀包含的像素數量是一致的。像素是顯示設備上發光原件的最小單位,最終呈現的畫面是由若干個像素組合起來所展現的。

分辨率表示視頻每一幀包含的像素,以 水平方向的像素數量 × 垂直方向的像素數量 來表示,好比 720p = 1280 × 720 就是一個比較常見分辨率。

這裏的 p 表示的是逐行掃描(Progressive Scanning),與之對應的是 i 表示的是隔行掃描(Interlaced Scanning),見下圖

最左邊一列是逐行掃描,中間一列是隔行掃描。可見隔行掃描會丟失一些畫面信息,相反的會更快地收集畫面,畫面的像素信息即文件大小相較逐行掃描也會偏小。

當視頻的分辨率低於顯示設備的分辨率時,設備上的像素點多於視頻顯示所需的像素點,這時就會用上各式的補間算法(Interpolation Algorithm)來爲顯示設備上那些未被利用的像素點生成色值信息,以徹底點亮顯示設備的全部像素點,不然將會致使屏幕上出現黑點。

關於補間算法,這裏有一小段視頻能夠做爲參考 Resizing Images

由於那些像素上的信息是算法生成的,因此當視頻的分辨率明顯小於顯示設備的分辨率時,這樣的像素點就會變得過多,從而致使感官上的清晰度降低。而若是視頻的清晰度超過設備的分辨率上限,則多出的信息會被丟去,所以也不會呈現更好的效果。

因此視頻分辨率的選擇,須要同時結合顯示設備的分辨率。

比特率 Bitrate

比特率的單位是 bit/s,表示視頻每秒長度中包含的比特數。

目前的視頻的畫面採集設備能夠收集很是大量的像素信息,視頻的做者爲了方便對視頻進行分發,須要將原始視頻進行壓縮轉碼。

比特率的大小,受到單位時間內的視頻文件的體積所影響,而影響視頻文件的體積的因素爲:原始視頻內容、轉碼選取的分辨率、幀率以及轉碼所採用的編碼方案(Codec)。

所以比特率並非一個和視頻清晰度直接關聯的參數。

若是採用的是離線播放的話,那麼比特率將不是一個重要的參數。而若是採用在線點播的方式觀看視頻時,視頻的比特率則成了必需要考量的重要指標。

比特率表示爲了顯示一秒的畫面所需傳輸的比特數。它能夠方便的和帶寬作比較。在線點播時,須要保證在有限的帶寬條件下,每秒傳輸儘量多的比特,這些比特須要保證畫面的傳遞不會出現問題。

視頻格式

由於計算機只能按照既定的程序邏輯來執行,因此視頻數據須要按預先制定好的格式進行整理後才能保存設備上。在格式的制定中,主要保存兩類信息:

  1. 視頻元數據
  2. 視頻主體數據

元數據包含對主體數據的一些描述信息,好比會記錄視頻的大小、分辨率、音頻和視頻的編碼方案等。

視頻元數據和主體數據如何組合到一塊兒進行保存,須要容器格式來指定,常見的容器格式包括 MP四、AVI 等。對於視頻來講通常會選擇 MP4 做爲容器格式,由於它被各個系統和設備普遍支持。

針對視頻主體數據,則須要另外的參數來指定,一般稱之爲編碼方案。不一樣的編碼方案會視頻文件的體積和最終的播放效果之間作取捨。

視頻數據分段加載

上文已經簡單介紹了一些視頻相關的參數。在點播場景下,視頻的生產者已經完成了視頻的生產工做,視頻內容採用某一種編碼方案進行編碼,並和其餘一些信息一塊兒,以某個容器格式進行保存。站在視頻消費者角度,爲了儘快地可以觀看到視頻的內容,確定不能採用加載完整個視頻數據後才進行播放的形式,所以從技術上必須支持對視頻數據進行分段地加載以及解碼播放。

文章的開頭已經提到,Web 標準中已經將解碼播放和數據加載的環節進行了解耦,所以做爲開發者只須要實現對視頻數據的分段加載。

在具體瞭解分段加載的技術細節以前,能夠經過一個簡單的例子來感覺整個分段加載的流程。

以 HLS 爲例的演示

這個例子將以 HLS 協議進行展開。HLS 協議只是衆多視頻數據分段加載協議中的一種,關於它的一些細節將在下一節進行介紹。先經過運行一個例子來對數據分段加載有一個具體的感覺。

經過下面這條命令來啓動這個例子,在此以前請確保電腦上已經安裝了 ffmpeg 以及所處網絡的通暢:

wget -qO- https://gist.githubusercontent.com/hsiaosiyuan0/412a4ca26d00ff7c45318227e7599c3d/raw/de5e3c467800d1f2315e74ba71f0eb6c6760a7a0/hls.sh | bash

這段命令會加載並執行一段網絡上的腳本。這段腳本將會執行下面的工做:

  1. 下載一段網絡上的視頻
  2. 利用 ffmpeg 對這段視頻進行壓縮和分段處理
  3. 生成 m3u8 文件用於對這些分段文件進行描述和索引
  4. 生成一個用於演示的 html 文件,在其中將使用 video 標籤完成視頻點播的功能
  5. 經過 svrx 來啓動一個本地的 Web 服務器

若是沒法運行這段腳本的話,也能夠經過這段 視頻 或者 gif 來得到演示的內容。

在演示中能夠看到瀏覽器會對分段的數據進行加載,而且當網絡環境變化後,會加載不一樣清晰度的分段內容。

HLS(HTTP Live Streaming)協議

HLS 是蘋果公司爲知足在線點播需求而推出的流媒體播放協議。由於這個協議基於 HTTP 協議,因此能夠充分利用現存的針對 HTTP 協議的技術內容和優化措施。

經過這個圖能夠發現,視頻錄製完成後須要先上傳到 Web 服務器進行分段和索引,這期間的消耗會讓用戶接受到的信息產生必定的滯後。因此雖然之爲 Live Streaming,可是它和實時(Realtime)之間仍是有一些差距,文章開頭提到的 Live Streaming 指的是實時的數據流。

蘋果公司不光制定了 HLS 協議的細節,對於協議的實施也給出了一套解決方案,能夠從 About Apple's HTTP Live Streaming Tools 中得到對於這些工具的介紹。因爲協議自己是開源的,依然可使用相似上一節例子中的 ffmpeg 來完成一些相同的工做。

HLS 協議的關鍵,同時也是數據分段加載的關鍵在於兩部分:

  1. 以什麼策略對數據進行分段
  2. 對分段的結果進行描述和索引

對於分段的策略,一般狀況下會選擇將視頻按照相同的播放時間分割成一個個小段,好比上面的例子中將視頻按 10s 來分割每個段。

而在 HLS 協議中,經過 m3u8 文件來對分段的內容進行描述和索引。就如同上面的例子同樣,視頻的製做者除了須要將視頻進行分段之外,還須要生成對這些分段進行描述的 m3u8 文件,而視頻的消費者,只要獲得 m3u8 文件,就能夠靈活的選擇分段的加載形式了。

接下來將經過進一步瞭解 m3u8 的細節,以瞭解分段的內容。

m3u8 文件

m3u 是一種保存音視頻文件播放所需信息的文件格式,m3u8 則是該格式在使用 UTF8 進行編碼時的縮寫。

m3u8 文件在 HLS 協議中起到 Playlist 的做用,相似音樂播放軟件中的「歌單」,不過歌單面向的對象是用戶,而 m3u8 則是供播放器選用。

由於是採用的 UTF8 進行的編碼,因此可使用常見的文本編輯器打開它們。下圖是上文例子中產生的 m3u8 文件的內容:

完整的 m3u8 格式的說明見 rfc8216 - HTTP Live Streaming。截圖涉及到的相關協議內容爲:

  • 文件中每行只會存在三種類型的數據:URI、空行、以 # 開頭的行,空行將和註釋一塊兒被解釋程序忽略
  • 協議中規定以 # 開頭的行能夠表示註釋或者標籤(Tag),緊接着 # 的內容爲 EXT(區分大小寫)則表示該行是標籤,不然都視爲註釋,會被解釋程序所忽略
  • 所以第一行 #EXTM3U 表示的是標籤。該標籤用以表示當前文件是對 m3u 格式的擴展,協議規定該標籤必須出如今文件開頭的第一行
  • 剩餘的以 #EXT 開頭的行都是標籤,它們被 URI 行分隔,用於修飾它們下方的 URI 資源
  • 某些標籤含有值,其值的形式爲屬性列表(Attribute Lists),屬性列表中的屬性以半角逗號(,)進行分隔,屬性由名稱和其值所組成,形式爲 AttributeName=AttributeValue。所以 #EXT-X-STREAM-INF 標籤的值即爲屬性列表
  • Playlist 分爲兩類,其中相似截圖中只存在 URI 內容的稱之爲主播放列表(Master Playlist),主播放列表中的 URI 都表示的是另外一種類型,媒體信息播放列表(Media Playlist)的位置。Media Playlist 中具體記錄了資源的分段信息:

    <img src="https://p1.music.126.net/3uNyvqTL4TZksYVL52XqWg==/109951164871804276.png" width="400" />

  • #EXT-X-STREAM-INF 標籤的做用是描述可變流(Variant Stream)的信息。其中的屬性含義大都在第一節已經介紹過了。BANDWIDTH 表示該流中各個分段的比特率的峯值,另外一個常見的 AVERAGE-BANDWIDTH 雖然沒有出現,可是它表示綜合了各個分段的比特率的平均值。客戶端會根據這兩個屬性的值、結合自身當前的帶寬來選擇合適的流下的下一個分段數據

因此在上文的例子中,經過向 video 指定一個包含三個不一樣分辨率的 Master Playlist,瀏覽器會根據自身當下的帶寬速率,自動的在三個數據流之間進行切換。當前若是沒有須要自動切換清晰度的場景,也能夠向 video 標籤直接指定一個 Media Playlist 來完成對某個特定的分辨率的流的播放。

Dynamic Adaptive Streaming over HTTP(DASH)

HLS 協議由於是蘋果公司推出的,因此能夠在蘋果公司的設備上獲得普遍的支持,在運行於這些設備上的 Safari 瀏覽器中,video 標籤能夠直接做爲 HLS 協議的客戶端來運行。

在那些不支持 HLS 的瀏覽器環境中,一般可使用另外一種相似的協議,該協議直接由 MPEG 組織開發和維護,稱之爲 MPEG-DASH,MPEG 是一個國際上專門制定視頻播放相關協議的組織,所以 DASH 協議相比 HLS 更具普遍性。

做爲另外一個基於 HTTP 協議的流媒體播放協議,DASH 具備和 HLS 類似的技術細節。例如它經過 Media Presentation Description(mpd)文件提供相似 m3u8 文件的功能。下圖是 mpd 文件內容的截圖:

DASH 協議並不被主流瀏覽器廠商直接支持,一般須要藉助額外的客戶端插件配合,好比接下來將介紹的 MSE 來完成數據的分段加載。在考慮移動端播放的狀況下,一般來講選用 HLS 協議可以適配更多的設備,這是由於支持 DASH 的播放的設備一般均可以經過 HLS 插件來對 HLS 協議提供支持,反之則否則。

Media Source Extension

MSE,Media Source Extension 是 Web 標準中制定的針對音視頻數據加載的接口,Web 應用程序能夠經過這個接口實現本身的音視頻數據的加載方案。由於 WebRTC 不是針對點播的協議,因此 MSE 成爲了點播場景下惟一的音視頻數據加載接口。

上文提到的 HLS 和 DASH 協議,在那些不原生支持它們可是支持 MSE 的瀏覽器中,均可以經過基於 MSE 實現對應的插件來完成相應的支持。

下圖是 MSE 目前的兼容性概覽,另見 Caniuse - MediaSource

圖中指的是 iPadOS,在 iOS 上目前仍是不支持的。在安卓設備上整體來講支持度比較高,因此上文提到採用 HLS 協議可以適配更多的設備,在蘋果設備上原生支持,在安卓設備上經過基於 MSE 的 HLS 插件來對其進行支持。

MSE 的目的是將音視頻的播放和數據加載進行解耦。Web 開發者沒必要介入或者干預現有的音視頻解碼和播放控制的行爲,只須要經過 MediaSource 向音視頻元素傳遞播放所需的數據便可,下圖能夠演示它們之間的關係:

音視頻元素只負責對音視頻數據進行解碼播放,應用自定義音視頻數據的加載策略並對數據進行加載,二者之間經過 MediaSource 進行交互。

AbortController

使用 MSE 是爲了自定義音視頻數據加載的策略,而數據加載策略中重要的一個功能點在於,爲了節約帶寬,須要能夠終止那些正在進行的已知無用的請求的繼續加載。而 Fetch API 裏面終止數據加載的功能須要結合 AbortController 來使用。

下面是 AbortController 目前的兼容性概覽,另見 Caniuse - AbortController

除非必要,不然徹底不用支持 IE,所以在不考慮 IE 的狀況下,它和 MSE 具備類似程度的兼容性,能夠將二者結合在一塊兒使用。

在 AbortController 不可用的狀況下 XMLHttpRequest.abort() 是另個具備較高兼容性的方案。不過 AbortController 對資源能夠起到「分組」管理的特性,是 XMLHttpRequest 默認所不具有的。

視頻加載速率優化

針對視頻加載速率的優化,一般來講有兩個方向,分別是「動態切換清晰度」和「視頻內容預加載」,接下來將對這兩個技術點作簡單的介紹。

動態切換清晰度

動態切換清晰度指的是在視頻播放的過程當中,根據設備的當前帶寬在不一樣清晰度的流之間進行切換的功能。

在使用 HLS 協議時,服務端預先將視頻進行壓縮分段,並提供 m3u8 文件,客戶端拿到 m3u8 文件後,根據當前的帶寬動態的切換分段的加載。

好比針對同一個視頻,準備了三個不一樣清晰度的流,並分別將視頻流分隔爲3段:

720P --seg11--  --seg12-- --seg13--
480P --seg21--  --seg22-- --seg23--
360P --seg31--  --seg32-- --seg33--

最簡單的算法下,客戶端能夠先選擇中間分辨率的流 480P 來加載第一段的視頻(seg21),根據加載的時長以及 seg21 自己的大小能夠獲得當前的下載速率,若是低於 480P 正常播放所需的帶寬,好比 1500kps,則下一段視頻將從知足當前帶寬的流中選取,在上面的例子中就是加載 360P 的 seg32。

固然由於帶寬是動態變化的,且是一個估算的值,例子中僅經過一個分段就進行切換很大程度上是不合理的,這裏只是做爲演示。更爲實用的算法能夠參考 DASH 協議中的 Adaptive Bit Rate Logic

視頻內容預加載

視頻內容預加載顧名思義就是在視頻未開始播放前,就預先加載一部分視頻內容,這樣用戶點擊播放時,即刻就能得到反饋。

視頻內容預加載的算法基於兩點進行展開:

  1. 對用戶即將進行點播的行爲的預判
  2. 在不阻礙當前播放進度的前提下進行預加載

對於第一點來講,須要結合業務的需求來作具體的調整。

對於第二點來講,最簡單的算法能夠對正在播放的視頻設置一個緩衝區的安全闕值,好比 5s,若是當前正在播放的視頻其緩存區中有 5s 的內容還沒有被播放,則能夠嘗試進行下一條視頻的預加載。

因此總得來講,預加載依賴於技術上的可行性,具體的策略則須要按照實際的需求來制定。在不支持 MSE 的場景下,能夠經過 video 標籤的 preload 屬性來開啓預加載,不過預加載的時機以及預加載的數據量則都是處於託管的狀態。

MSE 和 MP4

上文對 HLS 和 DASH 協議以及 MSE 作了簡單的介紹,也對視頻加載速率的優化方式作了簡單的介紹。在使用 HLS 和 DASH 的狀況下,均可以直接選用現有的開源實現來得到客戶端的播放支持,好比 hls.jsdash.js

使用 HLS 和 DASH 協議都有一個前提條件,就是視頻文件須要按照協議的規定以及業務實際所需進行預先分段,換句話說就是須要服務端的配合。

下面將介紹一種在缺少 HLS 和 DASH 的支持下,利用 HTTP Ranges 和 MP4 容器格式加上 MSE 來完成對視頻內容分段加載的可行性。這個形式幾乎不須要服務端的額外配合,也能夠經過對這個可行性的介紹,進一步瞭解 MSE 協議以及 MP4 容器格式。

HTTP Ranges

音視頻文件,在點播場景下一般都會經過 CDN 網絡加速內容的分發。CDN 網絡中的 Web 服務器一般都會支持 HTTP Range Request 功能。經過這個功能,可以讓客戶端在預先知道文件內容的狀況下,按需取得文件的某一部分。

因此分段加載一段視頻數據的能力是 CDN 網絡默認就提供的,剩下的只是如何可以讓客戶端預知一段視頻的內容。在 HLS 或者 DASH 協議中,都是經過索引文件 m3u8 或 mpd 來實現的,客戶端經過加載這個索引文件就預知了整個視頻的分段信息和它們的相對偏移量,所以順利的完成後續的加載工做。

m3u8 文件的功能,基本能夠直接利用 MP4 容器格式來提供,這是由於 MP4 容器格式中定義了一個名爲 moov 盒子,它記錄了相似分段的信息,關於 MP4 容器格式的細節下一節將會介紹。

moov 根據製做視頻的軟件不一樣,有時會處於整個視頻文件的末尾,所以爲了讓客戶端播放軟件可以儘快地加載到 moov 盒子,CDN 服務提供商都會建議視頻提供者先將視頻的 moov 信息進行前移後再進行分發。好比 NOS 官網上就有相似的建議,見 Flash Player點播

可見在這個方案中,對 MP4 文件格式的瞭解成了重要的一環,接下來將會簡單介紹 MP4 文件格式。

MP4 容器格式

MP4 是一個容器格式,之因此強調它是一個容器格式,是由於它的做用是將全部用於描述視頻的信息進行有組織的統一存放。另外一個經常提到的編碼格式只是 MP4 容器包含的衆多視頻信息中的一個。

MP4 格式大體這幾個特色:

  • MP4 文件採用二進制進行編碼,在格式的制定上採用面向對象(Object Oriented)的設計
  • 視頻信息按類別進行組織,並概括到一個個盒子類型(Box Class)中,所以整個 MP4 文件由一個個盒子(Box)構成,每一個盒子都是其所對應的盒子類型的實例
  • 盒子類型以前存在嵌套的形式,以表示它們之間的包含關係

能夠經過 MP4Box.js 提供的在線工具,放入這段演示視頻,來查看 MP4 文件的結構信息:

除了在線工具之外,也能夠經過 AtomicParsley 這個命令行程序快速地獲得 MP4 文件的內部盒子結構。

使用下面的命令便可:

AtomicParsley 1.mp4 -T

上圖中,左邊 Box Tree View 展現的是文件內包含的盒子,以及這些模塊的層級關係,右邊則是當前選中的盒子上的屬性。

上圖中頂層的盒子包括 ftypfreemdatmoovmoov 包含另一些盒子,以子節點的形式進行展現。mdat 盒子中保存的是音視頻數據的主體,moov 盒子中包含了用於描述這些數據的元信息。

MP4 中衆多的盒子類型和它們的從屬關係以下圖,另見 ISO/IEC 14496-12

上圖表格中的縮進展現了盒子之間的包含關係。moov 盒子中比較重要的盒子就是 trak 盒子。MP4 格式中將畫面數據和音頻數據分開保存,通常來講一段視頻將包含一段畫面數據和一段音頻數據,它們的信息將分別經過兩個獨立的 trak 盒子來保存。

trak 盒子中主要記錄音視頻的編碼信息,以及對音視頻數據塊的索引,數據塊的主體存放在 mdat 盒子中。

所以只要客戶端獲得一段視頻的 moov 信息,那麼就能夠按需地加載 mdat 中的音視頻數據。

在瞭解盒子之間的關係以後,能夠經過下圖來簡單的瞭解盒子的數據結構:

盒子的數據總體能夠分爲 head 和 body 兩部分,這樣分類雖然不是協議內規定的,但倒是一個方便理解的方式。須要注意的是頭部中的 size 數據,它表示的是整個盒子所佔的數據大小。這樣的設計使得客戶端在快速的在盒子之間進行偏移,而且只選擇本身感受興趣的部分進行延遲解析。

若是把文件整個當作是一個大的盒子,那麼 ftypmoov 這些處於頂層的盒子則是它的子節點。盒子在其父級節點中的順序不被要求是固定的。好比 ftyp 在標準中只被要求儘早的出現,而並非必須放到起始的位置。

正是由於盒子在容器中的順序是不固定的,客戶端軟件只能經過不斷地請求頂層的盒子來獲取到 moov 盒子,能夠經過運行一小段程序來具體演示這個過程。這裏是演示代碼的源碼,能夠經過下面的命令運行該演示程序:

deno --allow-net https://gist.githubusercontent.com/hsiaosiyuan0/a7b215b53b48b9e66d2e0bad9eb8d1dd/raw/6bf88be0c81d55f620b1d94d51f5a56b55691007/locate-moov.ts

若是不方便運行這段腳本,也能夠直接查看這裏的結果演示

這段腳本中正是利用了 box 盒子數據結構的特色,從文件的起始位置開始請求第一個8個字節的內容,這也是上文提到的盒子的 head 部分,它們將包含盒子的大小以及盒子的類型,各佔4個字節。隨後的請求將不斷地累加偏移地址以請求接下來緊挨着的盒子,可是每次依然只請求盒子的頭部信息,通過幾回偏移後,將會獲得 moov 盒子的信息。

經過這個例子也能夠看出將 moov 信息前置的意義,將有效得減小爲了定位 moov 盒子的偏移請求次數。

MSE-FORMAT-ISOBMFF

到目前爲止已經介紹了 MSE,HTTP Ranges 和 MP4 容器格式。經過對 MP4 容器格式的瞭解,發現它內部的數據其實也是分塊存儲的,而且分塊的信息保存在 moov 盒子中。

那麼彷佛按照 moov 盒子的分塊信息加載分塊的數據而後藉助 Media Source 就能進行視頻的播放了,好比下面的這段代碼摘自 Google Developer - Media Source Extensions,其中最重要的一段代碼就是 appendBuffer 的調用:

應用代碼加載了分段的數據,而後調用 appendBuffer 將數據經過 Media Source 傳遞給音視頻播放器。

實際上事情要稍微複雜一些,這是由於 MP4 容器格式只是衆多容器格式中的一種,而 MSE 做爲一個通用的數據接口,它將對接各類容器格式的數據,所以它對外製定了一個通用的數據分段格式。這個分段格式被稱爲 MSE-FORMAT-ISOBMFF,它是基於 ISO base media file format, ISOBMFF 的修改版。

一般所說的 MP4,即 MPEG-4 Part 14,它和 ISOBMFF 的關係爲:

能夠看到 MP4 是 ISOBMFF 格式的拓展,將包含更多樣化的信息。而 MSE-FORMAT-ISOBMFF 是在 ISOBMFF 的基礎上引入了分段的概念。

MSE-FORMAT-ISOBMFF 下數據的分段加載形式以下:

上圖展現了 MSE-FORMAT-ISOBMFF 中的兩個主要分段類型:initialization segmentmedia segment。這些分段又由一些盒子組成。

所以爲了讓 MP4 格式的文件能夠被 MSE 播放,須要在格式上作相似下面從右往左的轉換,總體的流程爲:

最右邊是本來的視頻內容,中間虛線部分爲開發者自行編寫的內容,須要完成對原始視頻時間的下載和再組裝的任務。

轉換的過程並不涉及到對視頻數據的轉碼,只是容器格式的相互轉換,可是即便這樣整個過程依然十分繁瑣,所以藉助現有的實現能夠方便地完成這個工做。

mp4box.js 就是這樣在這方面功能比較豐富的實現。不過雖然功能比較多,可是文檔還不是很是豐富,能夠參考倉庫中的測試案例

爲了方便快速獲得一個本地可運行的演示,這裏準備了一個演示項目。在演示項目中,經過自定義的 Downloader 來對數據進行加載,加載後的數據將交給 mp4box 進行從新組裝,組裝完成後再加入到 Media Source 中傳遞給播放器。若是是要完成預加載的功能,只須要對 Downloader 按業務所需稍加修改就能夠了。

結尾

本文從視頻的常見參數開始,到一種不須要服務端額外配合的點播方式結束,介紹了 Web 環境下的視頻點播功能所涉及的一些技術點。由於這方面涉及的內容不少,加上水平和時間的限制,因此暫時只能呈現這些內容。但願它們能起到一些幫助做用,同時也期待你們的寶貴意見和建議。

引用

本文發佈自 網易雲音樂前端團隊,文章未經受權禁止任何形式的轉載。咱們一直在招人,若是你剛好準備換工做,又剛好喜歡雲音樂,那就 加入咱們
相關文章
相關標籤/搜索