1. 程式人生 > >mp4封裝格式各box型別講解及IBP幀計算

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

# mp4封裝格式各box型別講解及IBP幀計算 [toc] ---- 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 就是一種容器箱子。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221157482-876035483.png) ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517220047694-1880995792.png) box的位元組序為網路位元組序,也就是大端位元組序(Big-Endian)Box由header和body組成,其中header統一指明box的大小和型別,body根據型別有不同的意義和作用。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221222022-1149265585.png) ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221234244-1367932159.png) box size 有三種可能: 1、通常的box開頭的4個位元組(32位)為box size,該大小包括box header和box body整個box的大小,這樣我們就可以在檔案中定位各個box。 2、如果 box size為1,則表示這個box的大小為large size(“mdat”型別)。 3、如果box size為0,表示該box為檔案的最後一個box,檔案結尾即為該box結尾。(同樣只存在於“mdat”型別的box中。) size後面緊跟的32位為box type,一般是4個字元,如“ftyp”、“moov”等,這些box type都是已經預定義好的,分別表示固定的意義。如果是“uuid”,表示該box為使用者擴充套件型別,如果box type是未定義的,應該將其忽略。 14496-12標準中box的都有這些型別,這張表,也能從整體上了解完各型別box的說明: ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221304336-666826564.png) MP4檔案分析工具。 兩個線上的MP4 分析工具,下面內容全部以此工具來分析一份demo [online-mp4-parser](https://www.onlinemp4parser.com/) [online-mp4-parser-2](http://mp4parser.com/) 可以看到這份標準的mp4視訊根路徑上有四個box -- **`ftyp`**、**`moov`**、**`uuid`**、**`mdat`** 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。這些都是用來指示檔案應用級別的資訊。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221540401-741531671.png) ## 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的值即為單位為秒的媒體時長。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221612363-879915535.png) ## trak box (Track Box) trak也是一個container box,其子box包含了該track的媒體資料引用和描述。一個MP4檔案中的媒體可以包含多個track,且至少有一個track,這些track之間彼此獨立,有自己的時間和空間資訊。“trak”必須包含一個“tkhd”和一個“mdia”,此外還有很多可選的box(略)。 ## tkhd(track header box) tkhd 描述的該track的,如果是視訊會有寬、高資訊、 還有檔案建立時間、修改時間等。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221646659-1564020466.png) ## mdia (Track Media Structure) mdia box 描述了這條音視訊軌/流(trak)的媒體資料樣本的主要資訊,對播放器來說是一個很重要的box.. ## mdhd (Media Header Box) 當前音/視訊軌/流(trak)的總體資訊, 該box中有duration欄位和timescale欄位,duration/timescale的值即為當前流的時長。 hdlr box用來指定該流的型別 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221713287-1814669643.png) stsd box的子box用於儲存該流的編碼型別 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221722836-245793310.png) 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) ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221745789-1345318096.png) stts 可以計算出每個sample的dts,其中sample_delta為該sample的dts相對於上一個smaple的差值, 那麼此樣本資料的dts為 : ``` 0 1000 2000 3000 4000 ··· ``` ## ctts(Composition Time to Sample Box) Composition Time 構成時間目前我直接理解的PTS。。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221806159-1861720351.png) ctts 有每個sample的構成時間(Composition Time)和解碼時間(DTS)之間的差值(CTTS)即圖中的composition_offset。 如果不存在ctts,則代表該流不存在B幀,那麼PTS就直接等於DTS。 ## timescale 最後就是關於單位,你可以看到圖中樣本的單位都是以1000為單位浮動,實際上真實DTS和PTS時間是需要除以mdia/mdhd中的timescale。這裡是30000。 有了這些,我們就可以在ctts裡面計算出pts了 : ```c 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幀),跳轉時,需要從關鍵幀開始解碼,否則會花屏。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221828969-178399218.png) ## stsz (Sample Size Boxes): 顧名思義,樣本大小. ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221839249-984000556.png) ## stsc (Sample To Chunk Box): 媒體資料的樣本是被打包進chunks(塊)的,chunks和樣本(samples)的大小不固定,該box用於說明chunks關聯樣本的資訊。 first_chunk 該入口第一個chunks的索引(index). samples_per_chunk 樣本數量/chunks. ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221855264-603502155.png) ## stco (Chunk Offset Box) 描述每個chunks相對檔案的偏移量。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221910673-1559901840.png) 如圖 第一個chunks即前10個樣本(此例), samples.1起始地址為 423257, samples.1的地址則為 423257 + 140798 = 564055, 依此類推... 有了這些即可計算出音視訊的時間和空間資訊了 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221929873-1787488885.png) ## mdat box Meida Data Box 媒體資料box 位於頂層,定義是一個位元組陣列,用來儲存媒體資料。該box數量可以為0個,也可以有多個(當媒體資料全部為外部檔案引用時),資料直接跟在box type欄位後面,具體資料結構的意義需要參考metadata(主要在sample table中描述)。 ![](https://img2020.cnblogs.com/blog/1384555/202005/1384555-20200517221946097-1793311649.png) 參考 : ISO/IEC 14496-12:201