mp4封裝格式各box類型講解及IBP幀計算

mp4封裝格式各box類型講解及IBP幀計算


MP4文件封裝格式,對應的標準爲ISO/IEC 14496-12,即信息技術 視聽對象編碼的第12部分 ISO 基本媒體文件格式(Information technology Coding of audio-visual objects Part 12: ISO base media file format)數組

box

若是從總體上看,mp4全部的數據所有存放在 一個叫box的結構中。
box,顧名思義,能夠簡單的理解爲一個箱子 裏面能夠聽任何符合大小的東西,也能夠繼續放箱子,箱子裏面再放東西,這種箱子裏面仍然放箱子的箱子稱爲容器箱子(container box) 你能夠想象你要搬家,把你的傢俱所有放在一個個的箱子裏面,而後一個大箱子把小箱子一個個再裝箱。MP4中的 moov box 就是一種容器箱子。

網絡

box的字節序爲網絡字節序,也就是大端字節序(Big-Endian)Box由header和body組成,其中header統一指明box的大小和類型,body根據類型有不一樣的意義和做用。數據結構

box size 有三種可能:
一、一般的box開頭的4個字節(32位)爲box size,該大小包括box header和box body整個box的大小,這樣咱們就能夠在文件中定位各個box。工具

二、若是 box size爲1,則表示這個box的大小爲large size(「mdat」類型)。ui

三、若是box size爲0,表示該box爲文件的最後一個box,文件結尾即爲該box結尾。(一樣只存在於「mdat」類型的box中。)編碼

size後面緊跟的32位爲box type,通常是4個字符,如「ftyp」、「moov」等,這些box type都是已經預約義好的,分別表示固定的意義。若是是「uuid」,表示該box爲用戶擴展類型,若是box type是未定義的,應該將其忽略。3d

14496-12標準中box的都有這些類型,這張表,也能從總體上了解完各種型box的說明:code

MP4文件分析工具。orm

兩個在線的MP4 分析工具,下面內容所有以此工具來分析一份demo
online-mp4-parser
online-mp4-parser-2視頻

能夠看到這份標準的mp4視頻根路徑上有四個box -- ftypmoovuuidmdat
ftyp 指定了文件類型
moov 保存了音視頻數據的時空間信息
mdat 存放音視頻數據

下面依賴工具簡單依次分析一份普通mp4文件

ftyp box

該box有且只有1個,而且只能被包含在文件層,而不能被其餘box包含。該box應該被放在文件的最開始,指示該MP4文件應用的相關信息。

「ftyp」 body依次包括1個32位的major brand(4個字符),1個32位的minor version(整數)和1個以32位(4個字符)爲單位元素的數組compatible brands。這些都是用來指示文件應用級別的信息。

moov box

moov box 是一個 container box 該box包含了文件媒體的元數據信息,具體內容信息由子box詮釋。同File Type Box同樣,該box有且只有一個,且只被包含在文件層。通常狀況下,「moov」會緊隨「ftyp」出現。

能夠看到這個demo 中有 mvhd、trak、udta 三種 box 通常狀況下 「moov」中會包含1個「mvhd」和若干個「trak」。其中「mvhd」爲header box,通常做爲「moov」的第一個子box出現。「trak」包含了一條音、視頻軌/流/track的相關信息,也是一個container box。

該box是解析MP4文件裏面最重要的一個box,它包含了音視頻數據的編碼格式、音視頻數據樣本,chunks的大小、存儲位置也即偏移offset、時間戳單位、DTS,CTS(PTS),解碼時間、顯示時間等等...

moov box中記錄的每幀音視頻數據位置信息,實際上都在mdat box中,經過解析moov box來獲取到每幀音視頻數據具體位置後,使得播放器能方便的拖拉進度條。

mvhd box (Movie Header Box)

mvhd 描述了與具體音頻或視頻流無關的文件總體信息,其中的duration/timescale的值即爲單位爲秒的媒體時長。

trak box (Track Box)

trak也是一個container box,其子box包含了該track的媒體數據引用和描述。一個MP4文件中的媒體能夠包含多個track,且至少有一個track,這些track之間彼此獨立,有本身的時間和空間信息。「trak」必須包含一個「tkhd」和一個「mdia」,此外還有不少可選的box(略)。

tkhd(track header box)

tkhd 描述的該track的,若是是視頻會有寬、高信息、 還有文件建立時間、修改時間等。

mdia (Track Media Structure)

mdia box 描述了這條音視頻軌/流(trak)的媒體數據樣本的主要信息,對播放器來講是一個很重要的box..

mdhd (Media Header Box)

當前音/視頻軌/流(trak)的整體信息, 該box中有duration字段和timescale字段,duration/timescale的值即爲當前流的時長。

hdlr box用來指定該流的類型

stsd box的子box用於保存該流的編碼類型

avcC box指定了該流的編碼類型爲H264,儲瞭解碼所需的SPS、PPS信息。

stsc stsz stco三個box用於保存每幀視頻或音頻數據在文件中的保存位置。

stts stss ctts三個box用於保存媒體數據和時間戳的對應關係。

在同級的stbl的樣本表box裏面能夠查到對應的樣本 描述信息(stsd),時序信息(stts),樣本的大小信息(stsz),樣本到chunk的映射信息(stsc),chunk的位置信息(stco)等等

下面計算下PTS,來了解stbl box..

PTS和DTS的計算

I P B 幀的概念

在音視頻中,爲了提升壓縮效率,會將每幀畫面壓縮爲不一樣類型的視頻幀數據。
I幀表示關鍵幀,包含有一幀畫面的完整信息,解碼時只須要本幀數據就能夠解碼出完整的一幀畫面。
P幀表示前向參考幀,它保存了本幀與上一幀的差別信息,它不能單獨解碼,須要根據上一幀的畫面加上本幀保存的差值來獲取本幀的完整畫面。
B幀爲雙向參考幀,它解碼時須要依賴它以前和以後的幀來獲取最終的畫面
由於B幀須要依賴它後面的幀來進行解碼,因此它的解碼順序就必然和顯示順序不能保持一致,這時就須要解碼時間戳(DTS)和顯示時間戳(PTS)來共同決定一幀視頻數據什麼時候解碼,而後什麼時候顯示了。

舉個例子
一小段視頻幀序列以下 :
type : I --- B --- B --- P --- B --- B --- P
PTS : 0.33 0.67 1.00 1.33 1.67 2.00 2.33
DTS : 0.00 0.67 1.00 0.33 1.67 2.00 1.33

PTS >= DTS

根據mp4 stts和ctts 能夠獲得DTS和PTS

stts(Decoding Time to Sample Box)

stts 能夠計算出每一個sample的dts,其中sample_delta爲該sample的dts相對於上一個smaple的差值,
那麼此樣本數據的dts爲 :

0   1000 2000 3000 4000 ···

ctts(Composition Time to Sample Box)

Composition Time 構成時間目前我直接理解的PTS。。

ctts 有每一個sample的構成時間(Composition Time)和解碼時間(DTS)之間的差值(CTTS)即圖中的composition_offset。
若是不存在ctts,則表明該流不存在B幀,那麼PTS就直接等於DTS。

timescale

最後就是關於單位,你能夠看到圖中樣本的單位都是以1000爲單位浮動,實際上真實DTS和PTS時間是須要除以mdia/mdhd中的timescale。這裏是30000。

有了這些,咱們就能夠在ctts裏面計算出pts了 :

else if (box_type_equa(uint32_to_str(bh.type, sbuffer), "ctts")) {
        uint32_t version = 0;
        read_net_bytes_to_host_uint32(&box[8], &version);
        if(version != 0) {
            LOG_E("ctts unsupport version :%d ", version)
            return;
        }

        uint32_t entry_cnt = 0;
        read_net_bytes_to_host_uint32(&box[12], &entry_cnt);
        char buf[128] = {0};
        tree_childs_insert_with_val(tree, "version", uint32_to_ascii(version, buf));
        tree_childs_insert_with_val(tree, "entry_cnt", uint32_to_ascii(entry_cnt, buf));
        
        uint32_t i = 0, j = 0, num = 0, pos = 16;
        for (i = 0; i < entry_cnt; i++) {
            uint32_t sample_cnt;
            read_net_bytes_to_host_uint32(&box[pos], &sample_cnt);
            pos += 4;
        
            uint32_t sample_offset;
            read_net_bytes_to_host_uint32(&box[pos], &sample_offset);
            pos += 4;

            for (j = 0; j < sample_cnt; j++) {
                PushBack_Array(pts_array, At_Array(dts_array, num++) + sample_offset);
                float dt, pt = 0.0;
                printf("dts : %9.3f ms | pts : %9.3f ms | \n", At_Array(dts_array, num - 1) / (mdhd_time_scale * 1.0), At_Array(pts_array, num - 1) / (mdhd_time_scale * 1.0));
            }

stss (Sync Sample Box)

stss 裏面存放了關鍵幀的序號(I幀),跳轉時,須要從關鍵幀開始解碼,不然會花屏。

stsz (Sample Size Boxes):

顧名思義,樣本大小.

stsc (Sample To Chunk Box):

媒體數據的樣本是被打包進chunks(塊)的,chunks和樣本(samples)的大小不固定,該box用於說明chunks關聯樣本的信息。

first_chunk 該入口第一個chunks的索引(index).
samples_per_chunk 樣本數量/chunks.

stco (Chunk Offset Box)

描述每一個chunks相對文件的偏移量。

如圖 第一個chunks即前10個樣本(此例), samples.1起始地址爲 423257, samples.1的地址則爲 423257 + 140798 = 564055, 依此類推...

有了這些便可計算出音視頻的時間和空間信息了

mdat box

Meida Data Box 媒體數據box 位於頂層,定義是一個字節數組,用來存儲媒體數據。該box數量能夠爲0個,也能夠有多個(當媒體數據所有爲外部文件引用時),數據直接跟在box type字段後面,具體數據結構的意義須要參考metadata(主要在sample table中描述)。

參考 : ISO/IEC 14496-12:2015規範

相關文章
相關標籤/搜索