實例分析:

下面會實際分析一段碼流,然後看下各個Box語法和字段,為瞭讓大傢清楚分析的先後順序關系,約定三點:

1. 分析時會從外到裡進行分析,先分析第一層的Box各個字段,再分析第二層Box各個字段,以此類推;

2. Box由於種類非常多,這裡隻分析一個標準的MP4文件的Box含義,有些特殊和不太用的Box就一筆帶過瞭,如果你萬一項目用到直接查ISO標準文檔即可,畢竟這裡不是翻譯ISO文檔;

3. Box裡面字段也不是所有的都需要關註,我們隻需要關註核心和有用的,對於一些不太用的就可以忽略不計瞭,實際解封裝我們也是提取我們想要的信息或者打包時對於不關註的字段直接賦默認值即可,還是希望讀者看完這篇文章知道如何分析MP4文件,瞭解這種思路即可;

音視頻開發免費學習地址:https://ke.qq.com/course/3202131?flowToken=1042316

(點擊鏈接免費報名,先關註,不迷路)


n 分析第一層Ftyp、Moov、Mdat、Free等Box:

用Mp4Info工具打開MP4文件後,不打開內部結構,我們先看到外邊的四大Box,現在依次分析下各個Box.

ftyp Box

簡介:

ftyp是MP4文件的第一個Box,包含瞭視頻文件使用的編碼格式、標準等,這個Box作用基本就是MP4這種封裝格式的標識,同時在一份MP4文件中隻有一個這樣的Box。ftyp box通常放在文件的開始,通過對該box解析可以讓我們的軟件(播放器、demux、解析器)知道應該使用哪種協議對這該文件解析,是後續解讀文件基礎。

實例分析:

定義:

之前文章已經知道瞭Box的基本結構,所以我們先找到ftyp的Box,然後進行逐字段分析:

Box Header:

Box Length:

十六進制:0x00 00 00 18

十進制:24

表示該Box長度為24字節

Box type:

十六進制:0x66 74 79 70

這就是”ftyp”的ASCII值,標識瞭該Box的類型

Box Data:

major brand:

十六進制:0x69 73 6F 6D

即isom,說明本文件是符合這個規范的。

minor version:

十六進制:0x00 00 00 01

十進制:1

isom的版本號,一般默認值是1

compatible brand:

十六進制:0x69 73 6F 6D 61 76 63 31

ASCII值為iosmavc1表示本文件可以兼容isoavc1等協議和標準

【相關學習資料推薦,點擊下方鏈接免費報名,先碼住不迷路~】

【免費分享】我整理瞭一些比較好的面試題、學習資料、教學視頻和學習路線圖,資料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以點擊加788280672群領取~

Moov Box

簡介:

Moov Box這個Box也是MP4文件中必須有但是隻存在一個的Box,這個Box裡面一般存的是媒體文件的元數據,這個Box本身是很簡單的,是一種Container Box,裡面的數據是子Box,自己更像是一個分界標識。

所謂的媒體元數據主要包含類似SPS PPS的編解碼參數信息,還有音視頻的時間戳等信息。對於MP4還有一個重要的采樣表stbl信息,這裡面定義瞭采樣Sample、Chunk、Track的映射關系,是MP4能夠進行隨機拖動和播放的關鍵,也是需要好好理解的部分,對於實現一些音視頻特殊操作很有幫助。

實例分析:

Box Header:

Box Length:

十六進制:0x00 01 A6 85

十進制:108165

表示該Box長度為108165字節

Box type:

十六進制:0x6D 6F 6F 76

這就是”moov”的ASCII值,標識瞭該Box的類型

Box Data:

十六進制:0x00 00 00 6C……

由於這個Box是Container Box,裡面還有好幾層子Box,所以暫時不分析,等把第一層分析完再詳細講解這個Box內部。

Mdat Box

簡介:

Mdat Box這個Box是存儲音視頻數據的Box,要從這個Box解封裝出真實的媒體數據。當然這個Box一般都會存在,但是不是必須的。

在前面的文章已經講解瞭H264的基本結構是由一系列的NALU組成。原始的NALU單元組成:

Start code + NALU header + NALU payload

但是在MP4格式文件中,H264 slice並不是以00 00 00 01 Start Code來進行分割,而是存儲在Mdat Box的Data中。

Mdat Box的格式:

Box header + Box Data

Box length + Box Type + NALU length + NALU header + Nalu Data……. NALU length + NALU header + Nalu Data

說明:

1. Mdat Box基本組成還是有頭部和數據兩部分組成,但是這裡註意如果Box length長度不夠時,要用後面8字節的擴展長度字段,Box Type還是“mdat”的ASCII碼值;

2. 真實的數據字段是一個個NALU header + NALU Data,但是沒有用NALU的分解符00 00 00 01,是在每個NALU前面加瞭長度字段,和RTP打包類似;

3. 這裡的NLAU一般不再包含SPS PPS等數據,這些數據已經放到Moov Box裡面瞭,至於是如何放到Moob Box的,下面文章會講解。這裡一般NALU 類型就是IBP幀數據以及SEI用戶增強信息;

下面我們就分析下Mdat的Box Data裡面數據是不是一個個NALU,進行驗證下:

Box Header:

Box Length:

十六進制:0x01 4F 86 AB

十進制:21989035‬

表示該Box長度為108165字節,即後面的NALU整個長度為21989035-8字節。

Box type:

十六進制:0x6D 64 61 74

這就是”mdat”的ASCII值,標識瞭該Box的類型

Box Data:

第一個Nalu Length:

十六進制:0x00 00 02 DA

十進制:730

說明第一個NALU的長度是730字節,下一個NALU的起始位置應該在:0x1A6A0+0x08+0x2DA即0x1A982處,這個NALU分析完,我們找下下一個NALU繼續分析下驗證下猜想。

第一個Nalu Header:

十六進制:0x06

十進制:0x06&0x1F即6

我們判斷這個NALU的類型是SEI,這裡面存儲瞭大量的用戶私有定義的數據。

第一個Nalu Data:

十六進制:0x05 FF FF D6…….

這個後面的730字節都是SEI Nalu的值。

為瞭驗證我分析的對不對,我用Elecard StreamEye交叉驗證下第一個NALU的SEI信息如下:

這跟上面分析結果是一致的,繼續按照上面的猜想分析Mdat Box的第二個NALU信息如下:

上面已經分析第二個NALU的位置是0x1A982,現在分析下第二個NALU的具體信息。

第二個Nalu Length:

十六進制:0x00 00 00 6B

十進制:107

說明第一個NALU的長度是107字節

第二個Nalu Header:

十六進制:0x65

十進制:0x65&0x1F即5,

我們判斷這個NALU的類型為I幀

第二個Nalu Data:

十六進制:0x88 84 00 4D…….

這個後面的107字節都是I幀 Nalu的值,依次類推我們就能找到Mdat Box裡面所有的NALU瞭。

為瞭驗證猜想,同樣我用Elecard StreamEye進行瞭交叉驗證,後面依次發現瞭P、B幀的NALU分片都是在Mdat Box按照上面說的方式一次排列:

Free Box

簡介:

Free Box中的內容是無關緊要的,可以被忽略即該box被刪除後,不會對播放產生任何影響。這種類型的Box也不是必須的,可有可無,類似的Box還有Sikp Box.雖然在解析是可以忽略,但是需要註意該Box的刪除對其它Box的偏移量影響,特別是當Moov Box放到Mdat Box後面的情況。

實例分析:

Box Header:

Box Length:

十六進制:0x00 00 00 3A

十進制:58

表示該Box長度為58字節

Box type:

十六進制:0x66 72 65 65

這就是”free”的ASCII值,標識瞭該Box的類型

Box Data:

十六進制:0x49 73 6F 4D……

從內容來看,這裡在Free Box的Data裡面填充瞭一些MP4打包軟件的相關信息,一般沒啥用,忽略即可。

講到這裡基本把MP4文件最外層的幾個核心Box已經講解完瞭,這些東西也沒啥難度,就是對照標準文檔理解瞭就行。下面核心講解下Moov Box裡面的各個層次的子Box,這裡面內容豐富。既有時間戳信息,也有H264裡面對SPS PPS的封裝,是進行音視頻播放和正常解碼的關鍵,同時裡面還有Stbl Box的存在,這裡面有一定的設計算法,比較精巧,非常方便實現對MP4文件的拖動和暫停等操作,需要好好理解下。


n 分析第二層Moov Box的Mvhd、Trck、Iods等 Box:

Moov Box詳解:

先用Mp4Info工具看下Moov Box的詳細內部結構,將按照從上到下從外及裡依次進行分析各個Box的字段和含義:

Mvhd Box

簡介:

這個Box也是全文件唯一的一個Box,一般處於Moov Box的第一個子Box,這個Box對整個媒體文件所包含的媒體數據(包含Video track和Audio Track等)進行全面的描述。其中包含瞭媒體的創建和修改時間,默認音量、色域、時長等信息。

實例分析:

這個是用Mp4Explorer分析的結果,下面實際對照數據分析下各個字段含義:

Box Header:

這裡需要註意下該Box的頭,因為這個Box是Full Box也就是在原來的頭後面有擴展字段即在後面加上瞭8Bit的version和24Bit的 flags字段。

Box Length:

十六進制:0x00 00 00 6C

十進制:108

表示該Box長度為108字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x6D 76 68 64

這就是”mvhd”的ASCII值,標識瞭該Box的類型

Box Version:

十六進制:0x00

實際代碼就是默認填充0,不用太糾結這個字段值;

Box Tag:

十六進制:0x00 00 00

實際代碼就是默認填充0,不用太糾結這個字段值;

Box Data:

先說明下mvhd的數據部分字段含義:

creation time:

十六進制:0xCC E2 61 6A

十進制:3437388138

這個時間就是從1904/1/1經過的秒數,可以換算下剛好是2012/12/03 22:02:18

modification time:

十六進制:0xCC E2 61 6A

十進制:3437388138

這個時間就是從1904/1/1經過的秒數,可以換算下剛好是2012/12/03 22:02:18

time scale:

十六進制:0x00 00 02 58

十進制:600

1秒的時間單位是600,即把1秒劃分為600分,這樣描述的更精確;

duration:

十六進制:0x00 02 13 77

十進制: 136055

這樣我們換算下該段MP4文件的時長是136055/600即226.75秒

rate:

十六進制:0x00 01 00 00

十進制:1.0

表示采樣原始倍速播放,一般就是1倍速進行播放該視頻;

volume:

十六進制:0x01 00

十進制:1.0

表示采樣原始音量進行播放;

reserved:

十六進制:0x00 00 00 00 00 00 00 00 00 00

一般默認0即可

matrix:

十六進制:0x00 01 00 00……00 00 00 40

這36字節也是一般用默認值,視頻的空間變換矩陣,實際使用也沒啥用,按照本例默認填寫即可;

pre-defined:

十六進制:0x00 00 00 00……

這24字節一般也是默認00;

next track id:

十六進制:0x00 00 00 03

這個值表示如果要增加下一個Track時,需要的編號是3,那同時也就說明本文件裡面有2個Track,實際發現剛好是2個Track;

Iods Box

簡介:

這個Box也是非必須Box,不算核心Box,實際也是24字節的固定值,解析時直接跳過即可。封裝直接給填寫固定24字節即可,註意該Box也是Full Box意味著Header裡面有1字節的Version和3字節的Flag字段。

定義的內容應該是Audio和Video ProfileLevel方面的描述。但是現在沒有用。

實例分析:

實現參考:

從這裡開始我們將從上到下從外到裡解析Track Box,這個Box最復雜也最重要,需要理解好,特別是內部子Stbl Box。其中Trak Box有兩個一個是視頻Video Track,一個是音頻Audio Track,如果視頻的Track和音頻的Track 的Trck Box下面的子Box是一致的,則不講,如果有差異會在講完該視頻的Box,然後緊接著講解音頻 Track的該Box對應含義,閱讀時註意。

Trak Box

簡介:

Trak Box定義瞭媒體文件中媒體中一個Track的信息,視頻有Video Track,音頻有Audio Track,媒體文件中可以有多個Track,每個Track具有自己獨立的時間和空間的信息,可以進行獨立操作。

每個Track Box都需要有一個Tkhd Box和Mdia Box,其它的Box都是可選擇的:

實例分析:

Trak Box這裡自身隻是一個分界符,屬於Container Box,所以它隻有Box Header。

Box Length:

十六進制:0x00 01 47 C2

十進制:83906

表示該Box長度為83906字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x6D 76 68 64

這就是”trak”的ASCII值,標識瞭該Box的類型

同樣我們發現下面一級子Box就是Tkhd和Mdia,然後分別計算Box的大小是92和83806,二者之和剛好就是父Box Trak的大小92+83806即83898再加上Trak自身的8字節即83896字節。

下面分析thhd和mdia兩個Box以及子Box詳細內容。


n 分析第三層Tkhd、 Mdia等Box:

Tkhd Box

簡介:

該Box描述瞭該Track的媒體整體信息包括時長、圖像的寬度和高度等,實際比較重要,同時該Box是Full Box即Box Header後面有Box Header Version一字節和Box Header Flag三字節字段。

實例分析:

用Mp4Explprer已經分析出來各個字段,下面可以進行實際驗證下:

Box Header:

這裡需要註意下該Box的頭,以為這個Box是Full Box也就是在原來的頭後面有擴展字段即在後面加上瞭8Bit的Version和24Bit的 flags字段。

Box Length:

十六進制:0x00 00 00 5C

十進制:92

表示該Box長度為92字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x74 6B 68 64

這就是”tkhd”的ASCII值,標識瞭該Box的類型

Box Version:

十六進制:0x00

實際代碼就是默認填充0,不用太糾結這個字段值;

Box Tag:

十六進制:0x00 00 01

該Box實際代碼就是默認填充01,不用太糾結這個字段值;

Box Data:

先說明下mvhd的數據部分字段含義:

creation time:

十六進制:0xCC E2 61 6A

十進制:3437388138

這個時間就是從1904/1/1經過的秒數,可以換算下剛好是2012/12/03 22:02:18

表示該Track生效並且用在這個影片中的時間就是2012/12/03 22:02:18。

modification time:

十六進制:0xCC E2 61 6C

十進制:3437388140

這個時間就是從1904/1/1經過的秒數,可以換算下剛好是2012/12/03 22:02:20

track ID:

十六進制:0x00 00 00 01

十進制:1

這是該Track的唯一標識。

reserve:

十六進制:0x00 00 00 00

默認為0;

duration:

十六進制:0x00 02 13 77

十進制: 136055

這樣我們換算下該段MP4文件的時長是136055/600即226.75秒

reserved:

十六進制:0x00 00 00 00 00 00 00 00 00 00

8字節,一般默認0即可

layer:

十六進制:0x00 00

十進制:0

視頻層,默認值為0,值小的在最上層

alternate group:

十六進制:0x00 00

一般默認0即可

volume:

十六進制:0x00 00

十進制:0

由於是Video Track,所以這個值肯定是0;

reserved:

十六進制:0x00 00

一般默認0即可

matrix:

十六進制:0x00 01 00 00……40 00 00 00

這36字節也是一般用默認值,視頻的空間變換矩陣,實際使用也沒啥用,按照本例默認填寫即可;

width:

十六進制:0x03 00 00 00

十進制:768.00

表示該視頻圖像的寬度是768;

height:

十六進制:0x01 B0 00 00

十進制:432.00

表示該視頻圖像的高度是432;

視頻Track的分析完我們看下音頻Track的Tkhd Box:

這個基本定義和上面是一樣的,就是音頻的Volume字段為1.0表示按照原始音頻的音量進行播放,其它已經分析不再贅述。

Mdia Box

簡介:

這個Box也是Container Box,裡面包含子Box,一般必須有Mdhd Box、Hdlr Box、Minf Box。基本就是當前Track媒體頭信息和媒體句柄以及媒體信息。

Box自身非常簡單,就是一個標識而已,最復雜的還是裡面包含的子Box.

實例分析:

Mdia Box這裡自身隻是一個分界符,屬於Container Box,所以它隻有Box Header。

Box Length:

十六進制:0x00 01 47 5E

十進制:83806

表示該Box長度為83806字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x6D 64 69 61

這就是”Mdia”的ASCII值,標識瞭該Box的類型

同樣我們發現下面一級子Box就是Mdhd、Hdlr、Minf,然後分別計算Box的大小是0x20即32、0x37即55、0x146FF即83711,三者之和的大小32+55+83711即83798再加上Mdia本身的8字節即83806字節。


n 分析第四層Mdhd、Hdlr、Minf等Box:

Mdhd Box

簡介:

這個Box是Full Box,意味著Box Header有Version和Flag字段,該Box裡面主要定義瞭該Track的媒體頭信息,其中我們最關心的兩個字段是Time scale和Duration,分別表示瞭該Track的時間戳和時長信息,這個時間戳信息也是PTS和DTS的單位。

實例分析:

這是用Mp4Explorer工具分析的結果,再從二進制原始數據交叉驗證下:

Box Header:

這裡需要註意下該Box的頭,以為這個Box是Full Box也就是在原來的頭後面有擴展字段即在後面加上瞭8Bit的Version和24Bit的 flags字段。

Box Length:

十六進制:0x00 00 00 20

十進制:32

表示該Box長度為32字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x6D 64 68 64

這就是”mdhd”的ASCII值,標識瞭該Box的類型

Box Version:

十六進制:0x00

實際代碼就是默認填充0,不用太糾結這個字段值;

Box Tag:

十六進制:0x00 00 00

該Box的默認填充0,不用太糾結這個字段值;

Box Data:

先說明下mdhd的數據部分字段含義:

creation time:

十六進制:0xCC E2 61 6A

十進制:3437388138

這個時間就是從1904/1/1經過的秒數,可以換算下剛好是2012/12/03 22:02:18

表示該Track生效並且用在這個影片中

modification time:

十六進制:0xCC E2 61 6C

十進制:3437388140

這個時間就是從1904/1/1經過的秒數,可以換算下剛好是2012/12/03 22:02:20

表示當前Track的修訂時間

timescale:

十六進制:0x00 00 75 30

十進制:30000

表示當前Track的時間戳單位是30000;

duration:

十六進制:0x00 67 CD 6C

十進制:6802796

表示當前Track的時長是:6802796/30000即226.75秒,這個和前面計算的時長是一致的;

language:

十六進制:0x0D 09

最高位為0,後面15代表三個字符,可以參考標準ISO639-2/T查看代表的預研,這個碼查下來代表的china。

quality:

十六進制:0x00 00

媒體的回放質量,這裡也沒有明確規定,一般默認值00即可;

音頻Track對照分析,隻分析Box Data不一樣的部分:

timescale:

十六進制:0x00 00 5D C0

十進制:24000

表示當前Audio Track的時間戳單位是24000;

duration:

十六進制:0x00 52 CC 00

十進制:5426176

表示當前Track的時長是:5426176/24000即226.095秒,這個和前面計算的時長是一致的;

Hdlr Box

簡介:

這個Box是Full Box,意味著Box Header有Version和Flag字段,該Box解釋瞭媒體的播放過程信息,用來設置不同Track的處理方式,標識瞭該Track的類型。

實例分析:

我們能看到當前Track的類型是vido Track,標識瞭該Track描述瞭視頻Track的媒體元數據。

該Box是Full Box,所以Box Header的頭部數據已經在前面其它Box多次介紹,這裡後面遇到類似就忽略不計瞭,我們主要看各個Box Data部分。

Box Data:

先說明下mdhd的數據部分字段含義:

handle type :

十六進制:0x00 00 00 00

默認值一般填0即可

hand sub type:

十六進制:0x76 69 64 65

表示當前的Track類型是vide即表示的視頻track;

reserved:

十六進制:0x00 00 00 00 00 00 00 00 00 00 00 00

12字節的0x00,一般默認值即可

component name:

十六進制:0x47 50 41 43 …..65 72

給這個視頻Track起的名字是GPAC ISO Video Handler

音頻Track對照分析,隻分析Box Data不一樣的部分:

Minf Box

簡介:

這個Box是我認為Moov Box裡面最重要最復雜的Box,內部還有子Box,我們還是從上到下從外到內的分析各個Box。該Box建立瞭時間到真實音視頻Sample的映射關系,對於音視頻數據操作時很有幫助的。

同時該Box是Container Box,下面一般含有三大必須的子Box:

媒體信息頭Box: Vmhd Box或者Smhd Box;

數據信息Box:Dinf Box

采樣表Box:Stbl Box

實例分析:

minf作為Container Box,也就是臨界符,所以僅僅包含8字節的頭部數據就不進行分析瞭,參考前面的Box分析結果即可,其中該Box和子Box長度為:0x00 01 46 FF即83711,然後分析第一級子Box的長度:

Vmhd Box Size:0x00 00 00 14即20;

Dinf Box Size:0x00 00 00 24即36;

Stbl Box Size:0x00 01 46 BF即83647;

我們將三者之和加起來20 + 36 +83467即83703再加上Minf Box的本身8字節,剛好為83711。


n 分析第五層Vmhd、Smhd、Dinf、Stbl等Box:

Vmhd Box

簡介:

這個Box的大小是固定的,如果是Vmhd則大小就是24字節,如果是Smhd則大小是16字節,一般都是用默認值填充。

同時該Box為Full Box意味著Box Header多瞭四字節的version和Flag字段:

實例分析:

Box Header:

前面已經分析很多Full Box的Box Header,此處不再分析,參考前面即可:

Box Data:

上面已經框出來瞭,就不用再分析這兩個字段,下面參考下代碼實現即可:

音頻Track對照分析,隻分析Box Data不一樣的部分:

Balance:

十進制:0.0

音頻的均衡是用來控制計算機的兩個揚聲器的聲音混合效果,一般值是0。

Dinf Box

簡介:

這個Box也是一個container box,一般用來定位媒體信息。一般會包含一個Dref Box即data reference box,Dref下面會有若幹個Url Box或者也叫 Urn Box,這些Box組成一個表,用來定位Track的數據。

Track可以被分成若幹個段,每一段都可以根據Url或者Urn指向的地址來獲取數據,sample描述中會用這些片段的序號將這些片段組成一個完整的track,一般情況下當數據完全包含在文件中,Url和Urn Box的字符串是空的。

這個Box存在的意義就是允許MP4文件的媒體數據分開最後還能進行恢復合並操作,但是實際上,Track的數據都保存在文件中,所以該字段的重要性還體現不出來。

實例分析:

Dinf Box也作為Container Box,也就是臨界符,所以僅僅包含12字節的頭部數據就不進行分析瞭,參考前面的Box分析結果即可,真實數據還是要看子Dref Box。

Dinf Box的長度是0x24即36字節,其子Dref Box的長度是0x1C即28字節,所以:

28+8剛好就是Dinf的長度,下面分析Dref Box.

Dref Box

簡介:

由於Track可以分為多個段,所以該Box是用來定義當前Track各個段的URl。

實例分析:

Box Header:

Box Length:

十六進制:0x00 00 00 1C

十進制:28

表示該Box長度為28字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x64 72 65 66

這就是”dref”的ASCII值,標識瞭該Box的類型

Box Version:

十六進制:0x00

實際代碼就是默認填充0;

Box Tag:

十六進制:0x00 00 00

該Box的默認填充0,不用太糾結這個字段值;

Box Data:

entry count:

十六進制:0x00 00 00 01

十進制:1

說明下面的url 列表數量是1,隻有一個Url Box.

Url Box:

十六進制:0x00 00 00 0C 75 72 6C 20 00 00 01

這裡直接把Url Box作為Dref 的Box Data 進行分析。

Url Box Header Length:

十六進制:0x00 00 00 0C

十進制:12

表明當前Url Box的大小為12字節

Url Box Header Type:

十六進制:0x75 72 6C 20

當前Box 的url 的ASCII碼,表明當前Box的類型

Url Box Header Version:

十六進制:0x00

默認值,一般填充0即可;

Url Box Header Tag:

十六進制:0x00 00 01

值為1則表明“url”中的字符串為空,表示track數據已包含在文件中,所以Url的Url Box Data部分為空。


下面我們分析最關鍵的Stbl Box,看下到底是怎樣把媒體數據的Sample和時間進行映射的,再看下Sample-Trunk-Track的三者關系。

Stbl Box

簡介:

我們知道Sample是媒體數據的存儲單元,其中存儲在Media的chunk中,在分析Mdat Box的H264 NALU打包時已經體現出來瞭,這種關系如下表所示

其中每個Sample的時間和位置、編解碼信息、和chunk關系等都是由Stbl Box來描述的,該Box又稱為采樣參數列表即Sample Table。

Stbl Box包含瞭Track中media媒體的所有時間和索引,利用這個容器的Sample信息,就可以定位Sample的媒體時間、類型、大小以及和其相鄰的Sample。同時該Box是必須在Trak Box中存在的。

其一般要包含下列子Box:

采樣描述容器: Sample Description即Stsd Box

采樣時間容器: Time To Sample 即Stts Box

采樣同步容器: Sync Sample即Stss

Chunk采樣容器: Sample To Chunk即Stsc

采樣大小容器: Sample Size即Stsz

Chunk偏移容器:Chunk Offest即Stco

實例分析:

Stbl Box同樣是Container Box,所以這裡也僅僅是8字節的分解符,這裡Box Header部分不在分析,其大小為0x00 01 46 BF即83647現在我們分析其下一級子Box的長度:

Stsd Box:0x00 00 00 A7即167

Stts Box: 0x00 00 00 18即24

Ctts Box: 0x00 00 D3 58即3381

Stss Box: 0x00 00 00 80即128

Stsc Box: 0x00 00 00 34即52

Stsz Box: 0x00 00 6A 44即27204

Stco Box: 0x00 00 07 A8即1960


n 分析第六層Stsd、Stts、Ctts、Stss、Stsc、Stsz、Stco等Box:

Stsd Box

簡介:

該Box存儲瞭編碼類型和初始化解碼器需要的信息,與特定的Track Type有關,根於不同的Track使用不一樣的編碼標準。

box header和version字段後會有一個entry count字段,根據entry的個數,每個entry會有type信息,如“vide”、“sund”等,根據type不同sample description會提供不同的信息,例如對於video track,會有“VisualSampleEntry”類型信息,對於audio track會有“AudioSampleEntry”類型信息。視頻的編碼類型、寬高、長度,音頻的聲道、采樣等信息都會出現在這個box中。

這個Box也是Full Box,對於Video Track裡面而言,H264的SPS PPS就存在該Box裡面,對於解碼非常重要。

實例分析:

Box Header:

Box Length:

十六進制:0x00 00 00 A7

十進制:167

表示該Box長度為28字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x73 74 73 64

這就是”stsd”的ASCII值,標識瞭該Box的類型

Box Version:

十六進制:0x00

實際代碼就是默認填充0;

Box Tag:

十六進制:0x00 00 00

該Box的默認填充0,不用太糾結這個字段值;

Box Data

sample description entry count:

十六進制:00 00 00 01

說明後面跟著的隻有一個sample description entry,本質還是Box,對於編碼類型是H264數據的這裡的子Box則為Avc1 Box,我們繼續進行分析:

Avc1 Box:

簡介:

當前為Video Track,所以VisualSampleEntry為Avc1 Box,內部含有SPS PPS 等音視頻解碼信息。

實例分析:

Box Header:

Box Length:

十六進制:0x00 00 00 97

十進制:151

表示該Box長度為28字節,包含瞭Bod Header和Box Data

Box type:

十六進制:0x61 76 63 31

這就是”avc1”的ASCII值,標識瞭該Box的類型

Box Data:

Box Data的字段已經在上面框出來瞭,對於默認值的就不一一列舉瞭,現在看下視頻的寬和高:

width:

十六機制:0x03 00

十進制:768

表示視頻圖像的寬度是768,這跟前面分析的結論是一致的;

higth:

十六機制:0x01 B0

表示視頻的高度是432,這跟前面分析的結論是一致的;

其它基本都是默認值,就不過多分析,現在分析Avcc Box:

AVCC Box

簡介:

該Box則包含瞭真實的SPS PPS等信息,包含著音視頻編解碼參數:

實例分析:

該Box的頭部就不用分析瞭,和前面的Box一致,我們分析Box Data即可。

Box Data:

configuration version:

十六進制:0x01

一般默認就是0x01

avc profile indication:

十六進制:0x4D

十進制:77

代表H.264的編碼算法采用Main級別,說明含有B幀

profile compatibility:

十六進制:0x40

十進制:64

avc level indication:

十六進制:0x1E

十進制:30

length size minus one:

十六進制:0xFF

二進制:1111 1111

這裡後面的bit位為11即3再加1表示每個Packet的長度用幾個字節表示。

number of sps:

十六進制:0xE1

二進制:1110 0001

十進制:1

表示後面隻有一個SPS.

sps length:

十六進制:0x00 15

十進制:21

說明後面的SPS長度是21字節;

sps:

十六進制:0x67 4D…..16 CF

SPS的實際內容

number of pps:

十六進制:0x01

二進制:0000 0001

十進制:1

表示後面隻有一個PPS

sps length:

十六進制:0x00 05

十進制:5

說明後面的PPS長度是5字節;

pps:

十六進制:0x68 E9 BB 3C 80

PPS的實際內容

為瞭驗證AVCC Box的內容我們用Elcard StreamEye進行瞭交叉驗證下:

Btrt Box

簡介:

該Box是非必須Box,描述瞭解碼器需要的緩存空間大小和碼率描述信息。解碼器如果收到該Box,可以在解碼時作為參考。

實例分析:

Box Data:

buffer size db:

十六進制:0x00 00 7D 46

十進制:32070

告訴decoder開辟緩沖區大小32070字節

max bitrate:

十六進制:0x00 12 BD D0

十進制:1228240

該路視頻的最大碼率是1228240bps即1228kb/s

avg bitrate:

十六進制:0x00 0A 61 08

十進制:680200

該路視頻的平均碼率是680kb/s

這個數據用MediInfo進行交叉驗證瞭下結果正確:

分析完瞭Video Track的Stsd Box,下面分析音頻的Stsd Box,音頻的STSD的Box Data主要由mp4a和esds Box嵌套組成,裡面包含瞭通道個數,采樣率、音頻AAC編碼級別等信息,所以我們隻分析音頻的這兩個嵌套Box即可,先分析mp4a:

各個字段已經圈出來瞭,然後對照代碼看下各個字段解釋:

mp4a box 這個box實際就是繼承瞭audio sample entry box裡面的關鍵信息:BoxData:

channelcount:

十六進制:0x00 02

十進制:2

音頻通道個數是2;

samplesize:

十六進制:0x00 10

十進制:16

表示音頻采樣sample的大小為16用兩個字節表示,一般默認;

samplerate:

十六進制:0x5D C0 00 00

十進制:24000,需要整體右移16bit進行計算

表示音頻采樣率為240000Hz

在Box Data的Sample後面還有一個子Esds Box,現在詳解下:

Esds Box

簡介:

這個Box裡面主要是將音頻的編碼信息和音頻碼率信息放到該Box裡面,所以解碼音頻時非常關鍵。

實例分析:

Box Header:

該Box同樣是Full Box,所以不做分析,主要分析下Box Data。

Box Data:

參考鏈接:

https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio

Stts Box

簡介:

這個Box是sample number和解碼時間DTS之間的映射表,通過這個表格,我們可以找到任何時間的sample。Stts Box這個表格有兩項值,其中一項是連續的樣點數目即sample count和樣點相對時間差值即sample delta即表格中每個條目提供瞭在同一個時間偏移量裡面連續的sample序號以及sample偏移量。當然這裡的相對時間差單位是由該Track的Mdhd Box描述的時間單位,這裡值為3000,可以參考上文Mdhd Box的解析。

當然用這個表格隻是能找到當前時間的sample的序號,但是該sample的長度和指針還要靠其它表格獲取。

Sample delta簡單可以理解為采樣點sample的duration,所有duration相加應該為總的Track的時長。

大傢把這個時間理解為該幀數據當前的解碼時間即可,計算公式:

DT(n+1) = DT(n) + STTS(n)

實例分析:

從Mp4Explorer分析來看,這裡的表項隻有一項,含義是每個sample delta值為6796個sample。從這個表格能得出兩項信息是不是固定幀率和幀率計算以及Track 時長。:

1. 這個所有的sample的Sample delta值一樣,說明是固定幀率,同時幀率為30000/1001即29.97。

2. 其次可以計算出該Video Track的時長為6796*1001即6802796,這個值能在mdhd裡面已經分析過瞭,那麼換算成時間時長就是6802796/30000即226.75秒;

再次驗證下這兩個結論:

Header:

其中該Box是Full Box,後面有四字節的version和flag字段,其中flag默認值用0,這裡分析略過。

Box Data:

entry count:

十六進制:0x00 00 00 01

十進制:1

說明下面的sample count和sample delta組成的二元組信息隻有一對;

sample count:

十六進制:0x00 00 1A BC

十進制:6796

值為sample delta的sample個數為6796個

sample delta:

十六進制:0x00 00 03 E9

十進制:1001

Sample的delta值為1001,這樣我們計算出幀率就是30000/1001即29.75幀/s,這是一個比較重要的信息。

根據stts box可以計算出每個sample的dts,其中sample delta為該sample的dts相對於上一個sample的差值,比如entry_count=1,sample_count=5,sample_delta=1024時,5個sample的dts將依次為0 1024 2048 3072 4096。

然後分析下音頻Track的Stts box:

1. 說明音頻的Sample個數是5299個,然後每個Sample的對應DTS差值的1024;

2. 隻有一項說明所有幀的音頻DTS差值都相等,則(5299*1024)/24000即226.09秒,和分析結果一致;

Stts Box分析瞭每個幀的DTS信息,得到瞭時間到幀的映射關系,但是我們還沒有得到PTS的信息,因為存在B幀的情況,這裡我們分析Ctts Box.,但是對於音頻Track而言是沒有Ctts Box的。

Ctts Box

簡介:

該Box保存瞭每個sample的composition time和decode time之間的差值,這裡通過Composition Time就可以計算出Sample的PTS。

當然在這篇文章中,不存在B幀情況下PTS是等於DTS的,則不含B幀的視頻文件是沒有Ctts Box的,同樣音頻也沒有Ctts Box。

實例分析:

這裡我們看到實際的二元組信息為6761,也就是說具有相同Sample offset值的sample個數存在大於1的情況,所以這裡的6761小於stts裡面分析出來的幀格式6796,這裡需要理解好。

Box Header:

該Box同樣是Full Box,所以不做分析,主要分析下Box Data。

Box Data:

entry count:

十六進制:0x00 00 1A 69

十進制:6761

說明下面的sample count和sample offset組成的二元組信息有6791個,所以我們分析前五組即可。

sample count:

十六進制:0x00 00 00 01

十進制:1

具有相同的sample offset連續sample個數為1

sample offset:

十六進制:0x00 00 07 D2

十進制:2002

這樣我們結合stts進行分析即可以得到前面5個sample的DTS和PTS:

這樣我們就知道對應sample的解碼和顯示順序,為瞭驗證猜想我用碼流分析工具實際解碼播放看瞭下解碼PTS的大小關系:

解碼:0-1-2-3-4

顯示:0-3-2-4-1

隻有真正瞭理解這塊才真正理解瞭stts和ctts對於DTS和PTS的計算方法,這是實現按順序解碼和播放關鍵也是音視頻同步的核心內容。

Stss Box

以前的文章已經講過I幀是播放的起始位置,隻有編碼器拿到第一個I幀才能渲染出第一幅畫面。所以後續的一些特殊隨機操作,高標清切換時都需要找I幀,隻有隨機找到I幀才能完成這些特殊操作。

其中該Box就是存儲瞭那些Sample是I關鍵幀,很顯然音頻Track也是不存在這個Box的。

實例分析:

通過Mp4Explor分析工具可以得出是I幀的Sample序號,下面看Box的實際數據:

Box Header:

該Box同樣是Full Box,所以不做分析,主要分析下Box Data。

Box Data:

entry count:

十六進制:0x00 00 00 1C

十進制:28

說明本文件大概有28個關鍵幀

sample count:

十六進制:0x00 00 00 01

十進制:1

繼續分析則關鍵幀的Sample序號如下:

Stsc Box

簡介:

之前講解瞭MP4中存在Track-Trunk-Sample概念,講到現在Track和Sample已經都講到瞭,但是還沒有講解Trunk和Sample的映射關系,這個Box就是說明那些Sample可以劃分為一個Trunk。

媒體數據被分為若幹個chunk, chunk可以有不同的大小,同一個chunk中的樣點sample也允許有不同的大小;通過本表可以定位一個樣點的chunk位置,同時該Box裡面的Box Data裡面有三個字段,分別是first chunk、sample per chunk、sample description index。

1. fist chunk: 具有相同采樣點sample和sample_description_index的chunk中,第一個chunk的索引值,也就是說該chunk索引值一直到下一個索引值之間的所有chunk都具有相同的sample個數,同時這些sample的描述description也一樣;

2. samples per chunk: 上面所有chunk的sample個數

3. sample description index: 描述采樣點的采樣描述項的索引值,范圍為1到樣本描述表中的表項數目;

這 3 個字段實際上決定瞭一個 MP4 中有多少個 chunks,每個 chunks 有多少個 samples。

實例分析:

這說明三元組信息(first chunk、sample per chunk、sample desciption index)的個數隻有3個,其中的意義:

第一個chunk(1-2),每個chunk的sample的sample個數有15個,同時這些sample description index都是1;

第二個到四百八十六(2-496)這裡面的所有chunk,每個chunk的sample個數有14個,同時這些sample description index都是1;

第486即最後一個chunk的sample個數有5個,同時這些sample description index都是1;

從上面的表中我們可以得出下面信息:

1. Chunk總數是486個;

2. Sample的總個數是1*15 + (486-2)*14 + 1*5 即 6796個sample,這和Stts Box分析出來的結果是一致的;

Box Data:

entry count:

十六進制:0x00 00 00 03

十進制:3

說明本文件三元組信息為3個,循環解析三次即可;

first chunk:

十六進制:0x00 00 00 01

十進制:1

具有相同sample個數和description的chunk起始索引是1;

sample per chunk:

十六進制:0x00 00 00 0F

十進制:15

每個chunk具有的sample的個數是15;

sample description index:

十六進制:0x00 00 00 01

十進制:1

這些sample的description index都是1,一般用默認值即可;

其它三元組信息分析大同小異,這裡不再贅述。

順便看下音頻Track的各個音頻Sample和chunk的映射關系:

Stsz Box:

前面分析瞭sample的PTS、DTS等,也分析瞭chunk裡面sample的信息,但是沒有分析sample的大小,這是我們在文件讀取和解析Sample的關鍵。這裡給出每個Sample的Size即包含的字節數。

包含瞭媒體中全部sample的數目和一張給出每個sample大小的表。這個box相對來說體積是比較大的。

實例分析:

從上到下就是6796個Sample的大小。

Box Header:

此Box是Full Box,同樣含有version和flag字段,其中flag為0。

Box Data:

sample size:

十六進制:0x00 00 00 00

十進制:0

說明sample的具體大小在表後面項目中entry size;

sample count:

十六進制:0x00 00 1A 8C

十進制:6796

說明本Track的大小為6789個

entry size:

十六進制:0x00 00 03 4D

十進制:845

說明第一個sample大小為845字節

現在我們分析瞭sample的大小,也分析瞭sample和chunk的映射關系,現在分析最後一個Stbl的子Box,該Box將給出每個Trunk在文件的偏移量。

分析下音頻Track 的Stsz Box各個Sample的大小:

Stco Box

簡介:

該Box存儲瞭Chunk Offset,表示瞭每個Chunk在文件中的位置,這樣我們就能找到瞭chunk在文件的偏移量,然後根據其它表的關聯關系就可以讀取每個Sample的大小。

實例分析:

這張表中我們發現瞭有chunk的數量是486個,這和Stsc Box分析得出的結論是一致的。同時給出瞭每個chunk的在文件的偏移量。

Box Header:

此Box是Full Box,同樣含有version和flag字段,其中flag為0。

Box Data:

entry count:

十六進制:0x00 00 01 E6

十進制:486

說明本文件的chunk數量總和是486。

Chunk offset:

十六進制:0x00 01 A6 A5

十進制:108197

說明本文件第一個chunk的偏移量是6575字節,我們再分析瞭後面四個chunk的偏移量:

我們發現是和上面工具分析的結果是一致的。

音頻Track的Stco Box順便分析下:

stco 有兩種形式,如果你的視頻過大的話,就有可能造成 chunk offset 超過 32bit 的限制。所以,這裡針對大 Video 額外創建瞭一個 co64 的 Box。它的功效等價於 stco,也是用來表示 sample 在 mdat box 中的位置。隻是,裡面 chunk_offset 是 64bit 的。

H.264數據打包MP4文件步驟:

1. 構造Ftyp Box,這是MP4文件的基本標識,基本按照規范構造即可;

2. 構造Mdat Box,這裡面都是音視頻媒體數據,將NALU數據封裝成一個個Sample,從上到下排列起來即可;

3. 構造Moov Box,這個Box,先構造除瞭stbl的其它Box,這樣從子Box,一直按照層次構造上來,這些Box的基本信息是明確的,個別信息通過解析SPS、PPS是可以明確知道的,所以上文已經明確瞭這些Box的字段,所以構造起來並不難;

4. 構造Moov Box的Stbl部分,這部分要封裝:

SPS、PPS的NALU數據

也要構造時間戳PTS和DTS信息

Sample的大小和數量

Chunk的數量和在文件偏移量

Sample和Chunk的映射關系等,

需要仔細處理,一旦構造錯誤都會播放失敗;

5. 其中Moov到底是放到Mdat Box前面還是後面,需要在封裝打包前確定後,因為會牽一發動全身,裡面的信息都依賴在具體文件的位置,至於前面後面區別見下篇文章;

6. 再填充一些你感興趣的非必要信息Box,至此組裝成整個MP4文件即可;

MP4的解封裝代碼比較長,看瞭幾個開源項目都有上萬行的代碼量,不過隻要嚴格按照ISO規范進行書寫,問題不會太大,當然該關心H265、Av1等編碼格式的打包,其實流程基本差不多,後面會慢慢介紹給大傢,但是這篇是基礎。

總結:

截止到這裡我們分析完瞭所有核心Box的內容,包含瞭Box的作用,語法結構,各個字段值。同時用多種工具進行瞭交叉驗證分析的結果。當然Box的總數量要達到70多個,其它都不是必須Box,如果後面用到會繼續分享出來。

音視頻解封裝:MP4核心Box詳解及H264&AAC打包方案 – 資料 – 我愛音視頻網 – 構建全國最權威的音視頻技術交流分享論壇