Android視訊開發進階(part2-MP4檔案的解析)

qing的世界發表於2018-03-09

上一期文章我分享了一些視訊播放裡面的術語和基本概念。這一篇文章我會主要介紹容器(container format file)格式檔案的細節,以最常見的MP4檔案入手。然後會簡短的介紹一個標準的播放器的啟動,解析,播放流程。本篇還是以基礎知識為主,雖然很枯燥,但是對視訊開發的學習有非常大的好處,我自己個人的感受就是,如果在很多專有名字,概念都不熟悉的情況下,想要去閱讀播放器原始碼會是相當困難的事情。比如Exoplayer,谷歌的分包策略就是根據播放器的元件來分包。如果不熟悉播放器的基礎構建的話,連哪個部分的程式碼在哪個包都不知道。希望大家如果真的想進階的話還是耐心的理解好每個基礎概念。

Android視訊開發進階(part2-MP4檔案的解析)

  1. Mp4格式檔案的構成
  2. Mp4標頭檔案的構成
  3. 標準播放器的啟動流程
  4. 線上視訊播放的技術基礎(online video streaming)

1. Mp4格式檔案的構成

在上期我們大概介紹了Mp4檔案的結構

Android視訊開發進階(part2-MP4檔案的解析)

但是這樣抽象的介紹可能還是比較難理解,我們深入一些。

1.1 MP4到底是個啥?

通俗的說,MP4其實是一種格式的規範,這個規範是被ISO機構認證的,也就是說,只要你通過Codec生成了一個mp4檔案,那麼這個檔案的格式必須是按照ISO機構的規矩來。。。。既然是規範,那麼我們看看到底ISO對mp4做了什麼規範:

請大家開啟連結->ISO的mp4檔案規範

大家可能會有點懵逼,看不懂。其實這個規範很好理解,它定義了一個MP4檔案裡面,哪些資料應該放在什麼位置(以位元組為單位),哪些資料的長度是多少。我擷取了一段:

Android視訊開發進階(part2-MP4檔案的解析)

大家看,上面這一段規範定義了ftyp這個標頭檔案header所在的位置和長度(以位元組為單位)。 至於這些標頭檔案是有什麼用,我在上一篇文章大概提到過,他們屬於meta data的一部分。在本章我會更詳細的介紹。

所以說,任何容器,包括mp4都是類似的結構化檔案,只不過不同的格式檔案ISO對其有嚴格的要求,資料的擺放順序,排列等等不同而已。有興趣的同學可以對比一下rmvb,mp4,mkv這些格式的要求有什麼不同,優劣勢各是什麼。

2.Mp4標頭檔案的構成

關於mp4檔案的標頭檔案格式(meta data),蘋果官網對其進行了詳細的描述(這個介紹是基於QuickTime播放器支援的mp4檔案來介紹的,quciktime播放器對mp4的要求有些許不同,但是差別不大,我們可以忽略):

Movie Atom

我們不追究太多細節,有興趣的同學可以自己檢視,我們專注於一些基礎的資訊。

首先,在Meta Data裡面,每一個Header,標頭檔案,我們都叫他們Atom Header(不知道咋翻譯)。Atom Header分為Leaf Atom 和 Container Atom。前者代表一個連線著字串資訊的標頭檔案,後者是一個包含了若干個子Atom的標頭檔案,他們互相之間是有層級關係的(參考上圖)。每次播放器獲取了movie atom之後(moov),會根據層級關係,向下,或者向下讀取相關的其他資訊。每一個標頭檔案都會對它的子標頭檔案儲存位置的引用,所以只要根據mp4檔案的規範獲取了最頂級的標頭檔案moov,就可以順勢往下讀取其他標頭檔案了。

我們來看看mp4的標頭檔案結構

Android視訊開發進階(part2-MP4檔案的解析)

看起來很複雜,但是對於一個播放器來說,很多資訊都不是必須。我們需要知道的最重要的資訊是取樣索引表(Sample Table Atoms).對應圖中“**stbl **”這個atom header。這個索引表儲存了mp4檔案所有的取樣(sample)與視訊時間的對應關係(一般以微秒為單位),還有包括每個取樣的大小,在mp4檔案中的起始位置(以自己為單位)。

3.標準播放器的啟動流程

那麼既然我們已經知道一個容器檔案的格式規範了,播放器就可以通過解析容器的標頭檔案來控制播放(playback)了。

3.1 播放器

通常播放器由三個部分構成

  1. 讀取器(Extractor)
  2. 渲染器(TrackRenderer)
  3. 載入控制器(Load Controller)
  4. 資料來源(Source)

讀取器負責從source檔案讀取資料,載入控制器負責控制讀取資料的策略(比如說線上視訊播放的時候緩衝策略),渲染器負責接收讀取器讀取的資料,並渲染到螢幕上。

Android視訊開發進階(part2-MP4檔案的解析)

3.2 播放器的播放過程

在播放器可以把資料提交給渲染器之前,播放器需要把必需的標頭檔案全部解析並存入記憶體,比如之前說的取樣索引表。一般播放器在解析完畢後,會構建三個個表,一個存放時間對應取樣索引,一個存放取樣索引對應在mp4檔案中的起始位置(以位元組為單位),一個存放取樣索引對應大小(以位元組為單位)。以下圖為例

假設播放器需要從第1微秒開始播放,那麼需要把第1微秒的資料放入渲染器。所以會查詢下面這三個表。

Android視訊開發進階(part2-MP4檔案的解析)

通過表1,我們知道該微秒對應第1個取樣(sample),從第一個和第二個表我們知道,第1個取樣的資料範圍(在mp4檔案內)是從第0位元組到300(0+300)位元組,那麼播放器就會去讀取這個範圍的資料並且放入渲染器中進行渲染。

同時,載入器會基於當前已經快取的資料,決定是否還需要不停的讀取資料進入記憶體。一般來說每個播放器都有預設的快取值,也會有一個基準線,只有當快取足夠資料才能放進渲染器進行渲染。

最後同理,當我們拖動滑動控制器(SeekBar)想快進的時候,我們和第一步一樣,通過我們想滑動的時間獲取取樣的索引,再重新開始讀取資料。

綜上所述,播放器在正式播放視訊檔案之前,必須要把標頭檔案全部讀取並解析(這會是一段非常耗時的程式),這也是線上視訊播放的等待時間的瓶頸。在接下來的章節我會介紹自適應視訊播放(Adaptive Streaming),這個技術的發明使得了分段式mp4檔案(Fragmented Mp4)技術得以誕生,大大的減少了線上視訊播放的等待時間。

4.線上視訊播放的技術基礎(online video streaming)

線上視訊的播放其實和播放本地視訊的局別就是Extractor讀取的Source,資料來源不一樣,線上播放需要下載資料到記憶體,再交由Extractor讀取分析。但是既然是線上視訊播放,我們肯定不能把整個容器檔案下載到記憶體或者硬碟再開始解析播放。我們希望能控制下載的進度,比如我當前在看第10s的視訊內容,所以我只想快取/下載視訊內容到第20s的位置。

我們俗稱的漸進式下載(Progressive Downloading)就解決了這一難題。

說的好像是很嚇人的黑科技啊!!!!

Android視訊開發進階(part2-MP4檔案的解析)

其實就是HTTP1.1協議支援的分段式下載而已。。。。。

Android視訊開發進階(part2-MP4檔案的解析)

在HTTP請求裡面假如一個叫RANGE的header,放入起始位元組和結束位元組,就可以只下載對應部分的資料,這一header的支援也是各種下載軟體實現斷點下載的基礎。每次斷網的時候記錄下來已經下載的資料的位元組數,下次再下載的時候從位元組數+1處重新下載並且寫入原有檔案就可以了。


分割線

所以這次分享就結束啦,下一期分享我會開始進入正題,在安卓平臺裡面,對視訊播放的支援,像api啊等等,以及其變遷歷史。

Android視訊開發進階(part2-MP4檔案的解析)

週末愉快!

part3-安卓的MediaCodec API

相關文章