參考鏈接
- FFmpeg中mp4的demuxer(mov.c)代碼閱讀 - 簡書
- mp4文件格式解析 - 簡書
- mp4封裝格式各box類型講解及IBP幀計算_青丶空゛的博客-CSDN博客
- 5分鐘入門MP4文件格式 - 程序猿小卡 - 博客園
- ?關于M4A文件的隨機訪問 - 云+社區 - 騰訊云
MP4文件格式相關內容
- MP4文件由許多box組成,每個box包含不同的信息, 這些box以樹形結構的方式組織。
- 以下是主要box的簡要說明:
- 根節點之下,主要包含三個節點:ftyp、moov、mdat。
- ftyp:文件類型。描述遵從的規范的版本。
- moov box:媒體的metadata信息,保存了音視頻數據的時空信息。
- mdat:具體的媒體數據。
- 說明:在 mp4 中默認寫入字節序是 Big-Endian的。
2. mp4文件基本信息
分析mp4文件的工具:
- mp4box.js:一個在線解析mp4的工具。
- bento4:包含mp4dump、mp4edit、mp4encrypt等工具。
- MP4Box:類似于bento4,包含很全面的工具。
- mp4info.exe: windows平臺圖形界面展示mp4基本信息的工具。
- mvhd針對整個影片,tkhd針對單個track,mdhd針對媒體,vmhd針對視頻,smhd針對音頻,可以認為是從 寬泛 > 具體,前者一般是從后者推導出來的。?
mp4文件基本信息
- audio信息:
- smplrate:sample rate(采樣率)。
- channel:通道個數。
- bitrate:比特率。
- audiosamplenum:音頻sample的個數。
- video信息:
- width、height:視頻的寬/高。
- bitrate:比特率(碼率),秒為單位。等于視頻總的大小/時長。
- frames:視頻幀數。
- fps:幀率(frame per second)。
- total_time:時間長度,ms為單位。等于duration/timescale。
- timescale:時間的粒度,1000表示1000個單位為1s。
- duration:時間粒度的個數。
- videosamplenum:視頻sample的個數。
3. 封裝格式重要概念
box
- mp4文件由若干個box組成。下面是box結構的一個示意圖。
- box由header和body組成,header指明box的size和type。size是包含box header的整個box的大小。
- box type,通常是4個ASCII碼的字符如“ftyp”、“moov”等,這些box type都是已經預定義好的,表示固定的含義。如果是“uuid”,表示該box為用戶自定義擴展類型,如果box type是未定義的,應該將其忽略。
- 如果header中的size為1,則表示box長度需要更多的bits位來描述,在后面會有一個64bits位的largesize用來描述box的長度。如果size為0,表示該box為文件的最后一個box,文件結尾(同樣只存在于“mdat”類型的box中)。
- 只有“mdat”類型的box才可能會用到large size
- size后面緊跟著的32位為box type,一般是4個字符,如“ftyp”、“moov”等,這些box type都是預定義好的,分別表示固定的意義。如果是“uuid”,表示box為用戶的擴展類型,如果未定義box type 需要將其忽略
- box中可以包含box,這種box稱為container box。
- box分為兩種,Box和Fullbox。FullBox 是 Box 的擴展,Header 中增加了version 和 flags字段,分別定義如下:
aligned(8) class Box (unsigned int(32) boxtype,optional unsigned int(8)[16] extended_type) {unsigned int(32) size;unsigned int(32) type = boxtype;if (size==1) {unsigned int(64) largesize;} else if (size==0) {// box extends to end of file}if (boxtype==‘uuid’) {unsigned int(8)[16] usertype = extended_type;}
}
- FullBox有version和flags字段,
aligned(8) class FullBox(unsigned int(32) boxtype, unsigned int(8) v, bit(24) f)
extends Box(boxtype) {unsigned int(8) version = v;bit(24) flags = f;
}
MP4box
ftyp box
- 該box有且只有1個,并且只能被包含在文件層,而不能被其他box包含。該box應該被放在文件的最開始,指示該MP4文件應用的相關信息。
- “ftyp” body依次包括1個32位的major brand(4個字符),1個32位的minor version(整數)和1個以32位(4個字符)為單位元素的數組compatible brands。這些都是用來指示文件應用級別的信息。
- major_brand:比如常見的 isom、mp41、mp42、avc1、qt等。它表示“最好”基于哪種格式來解析當前的文件。舉例,major_brand 是 A,compatible_brands 是 A1,當解碼器同時支持 A、A1 規范時,最好使用A規范來解碼當前媒體文件,如果不支持A規范,但支持A1規范,那么,可以使用A1規范來解碼;
- minor_version:提供 major_brand 的說明信息,比如版本號,不得用來判斷媒體文件是否符合某個標準/規范;
- compatible_brands:文件兼容的brand列表。比如 mp41 的兼容 brand 為 isom。通過兼容列表里的 brand 規范,可以將文件 部分(或全部)解碼出來;
- 在實際使用中,不能把 isom 做為 major_brand,而是需要使用具體的brand(比如mp41),因此,對于 isom,沒有定義具體的文件擴展名、mime type。
- 下面是常見的幾種brand,以及對應的文件擴展名、mime type,更多brand可以參考?這里?。
- MP4封裝格式介紹及解析_tiankong19999的博客-CSDN博客_mp4封裝
補充
關于AVC/AVC1
- 在討論 MP4 規范時,提到AVC,有的時候指的是“AVC文件格式”,有的時候指的是"AVC壓縮標準(H.264)",這里簡單做下區分。
- AVC文件格式:基于 ISO基礎文件格式 衍生的,使用的是AVC壓縮標準,可以認為是MP4的擴展格式,對應的brand 通常是 avc1,在MPEG-4 PART 15 中定義。
- AVC壓縮標準(H.264):在MPEG-4 Part 10中定義。
- ISO基礎文件格式(Base Media File Format) 在 MPEG-4 Part 12 中定義。
FREE(可選的)
- free是可選的,如果存在,則通常出現在moov與mdat之間,即moov-free-mdat。
- free中的數據通常為全0,其作用相當于占位符,在實時拍攝視頻,moov數據增多時分配給moov使用。
- 因為設備錄制視頻時并不能預先知道視頻數據大小,如果moov在mdat之前,隨著拍攝mdat的數據會增加,moov數據也會增多,如果沒有free預留的空間,則要不停的向后移動mdat數據以騰出moov空間。
-
???“free”中的內容是無關緊要的,可以被忽略。該box被刪除后,不會對播放產生任何影響。
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的值即為單位為秒的媒體時長。
- 創建時間、修改時間、時間度量標尺、可播放時長等信息
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | |
creation time | 4 | 創建時間(相對于UTC時間1904-01-01零點的秒數) |
modification time | 4 | 修改時間 |
time scale | 4 | 文件媒體在1秒時間內的刻度值,可以理解為1秒長度的時間單元數 |
duration | 4 | 該 track的時間長度,用duration和time scale值可以計算track時長,比如audio track的time scale = 8000, duration = 560128,時長為70.016,video track的time scale = 600, duration = 42000,時長為70 |
rate | 4 | 推薦播放速率,高16位和低16位分別為小數點整數部分和小數部分,即[16.16] 格式,該值為1.0(0x00010000)表示正常前向播放 |
volume | 2 | 與rate類似,[8.8] 格式,1.0(0x0100)表示最大音量 |
reserved | 10 | 保留位 |
matrix | 36 | 視頻變換矩陣,一般忽略不計 |
pre-defined | 24 | |
next track id | 4 | 下一個track使用的id號 |
補充
- ?timescale:一秒包含的時間單位(整數)。舉個例子,如果timescale等于1000,那么,一秒包含1000個時間單位(后面track等的時間,都要用這個來換算,比如track的duration為10,000,那么,track的實際時長為10,000/1000=10s);
- next_track_ID:32位整數,非0,一般可以忽略不計。當要添加一個新的track到這個影片時,可以使用的track id,必須比當前已經使用的track id要大。也就是說,添加新的track時,需要遍歷所有track,確認可用的track id;
trak box (Track Box)
- trak也是一個container box,其子box包含了該track的媒體數據引用和描述。一個MP4文件中的媒體可以包含多個track,且至少有一個track,這些track之間彼此獨立,有自己的時間和空間信息。“trak”必須包含一個“tkhd”和一個“mdia”,此外還有很多可選的box(略)
- track表示一些sample集合,對于媒體數據來說,track表示一個視頻或者音頻序列
- 一系列子box描述了每個媒體軌道的具體信息
- hint track并不包含媒體數據,而是包含將一些其他數據track打包成流媒體的指示信息
- sample對于非hint track來說,video sample 表示視頻幀,或者一組連續視頻幀,audio sample即為一段連續的壓縮音頻,統稱為sample,對于hint track,sample定義了一個或者多個流媒體的格式
- sample table指明sample的時序和物理布局的表
- chunk 一個track的幾個sample組成的單元
- MP4文件中 媒體內容在moov的box中,一個moov包含多個track,每個track就是一個隨時間變化的媒體序列,track里每個時間單位是一個sample,sample是按照時間順序排列。注意,一幀音頻可以分解為多個音頻sample,所以音頻一般用sample作為單位,而不用幀
tkhd(track header box)
- tkhd 描述的該track的,如果是視頻會有寬、高信息、 還有文件創建時間、修改時間等。
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | 按位或操作結果值,預定義如下:0x000001 track_enabled,否則該track不被播放;0x000002 track_in_movie,表示該track在播放中被引用;0x000004 track_in_preview,表示該track在預覽時被引用。一般該值為7,如果一個媒體所有track均未設置track_in_movie和track_in_preview,將被理解為所有track均設置了這兩項;對于hint track,該值為0 |
creation time | 4 | 創建時間(相對于UTC時間1904-01-01零點的秒數) |
modification time | 4 | 修改時間 |
track id | 4 | id號,不能重復且不能為0 |
reserved | 4 | 保留位 |
duration | 4 | track的時間長度;當前track的完整時長(需要除以timescale得到具體秒數) |
reserved | 8 | 保留位 |
layer | 2 | 視頻層,默認為0,值小的在上層;視頻軌道的疊加順序,數字越小越靠近觀看者,比如1比2靠上,0比1靠上 |
alternate group | 2 | track分組信息,默認為0表示該track未與其他track有群組關系;當前track的分組ID,alternate_group值相同的track在同一個分組里面。同個分組里的track,同一時間只能有一個track處于播放狀態。當alternate_group為0時,表示當前track沒有跟其他track處于同個分組。一個分組里面,也可以只有一個track |
volume | 2 | [8.8] 格式,如果為音頻track,1.0(0x0100)表示最大音量;否則為0 |
reserved | 2 | 保留位 |
matrix | 36 | 視頻變換矩陣 |
width | 4 | 寬 |
height | 4 | 高,均為 [16.16] 格式值,與sample描述中的實際畫面大小比值,用于播放時的展示寬高 |
補充
- flags:按位或操作獲得,默認值是7(0x000001 | 0x000002 | 0x000004),表示這個track是啟用的、用于播放的 且 用于預覽的。
- Track_enabled:值為0x000001,表示這個track是啟用的,當值為0x000000,表示這個track沒有啟用;
- Track_in_movie:值為0x000002,表示當前track在播放時會用到;
- Track_in_preview:值為0x000004,表示當前track用于預覽模式;
mdia (Track Media Structure)??
- mdia box 描述了這條音視頻軌/流(trak)的媒體數據樣本的主要信息,對播放器來說是一個很重要的box
- “mdia”也是個container box,其子box的結構和種類還是比較復雜的。先來看一個“mdia”的實例結構樹圖。
- ?總 體來說,“mdia”定義了track媒體類型以及sample數據,描述sample信息。一般“mdia”包含一個“mdhd”,一個“hdlr”和 一個“minf”,其中“mdhd”為media header box,“hdlr”為handler reference box,“minf”為media information box。下面依次看一下這幾個box的結構。
mdhd (Media Header Box)
- 當前音/視頻軌/流(trak)的總體信息, 該box中有duration字段和timescale字段,duration/timescale的值即為當前流的時長。
- hdlr box用來指定該流的類型
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | |
creation time | 4 | 創建時間(相對于UTC時間1904-01-01零點的秒數) |
modification time | 4 | 修改時間 |
time scale | 4 | 同前表 |
duration | 4 | track的時間長度 |
language | 2 | 媒體語言碼。最高位為0,后面15位為3個字符(見ISO 639-2/T標準中定義) |
pre-defined | 2 |
?Handler Reference Box(hdlr)
- “hdlr”解釋了媒體的播放過程信息,該box也可以被包含在meta box(meta)中。“hdlr”結構如下表。
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | |
pre-defined | 4 | |
handler type | 4 | 在media box中,該值為4個字符:“vide”— video track“soun”— audio track“hint”— hint track |
reserved | 12 | |
name | 不定 | track type name,以‘\0’結尾的字符串 |
- handler_type的取值包括:
- vide(0x76 69 64 65),video track;
- soun(0x73 6f 75 6e),audio track;
- hint(0x68 69 6e 74),hint track;
- name為utf8字符串,對handler進行描述,比如 L-SMASH Video Handler(參考?這里)。
- “hdlr”的字節實例如下圖,各字段已經用顏色區分開:
- 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)等等?
Media Information Box(minf)
- ???“minf” 存儲了解釋track媒體數據的handler-specific信息,media handler用這些信息將媒體時間映射到媒體數據并進行處理。“minf”中的信息格式和內容與媒體類型以及解釋媒體數據的media handler密切相關,其他media handler不知道如何解釋這些信息。“minf”是一個container box,其實際內容由子box說明。
- ???一 般情況下,“minf”包含一個header box,一個“dinf”和一個“stbl”,其中,header box根據track type(即media handler type)分為“vmhd”、“smhd”、“hmhd”和“nmhd”,“dinf”為data information box,“stbl”為sample table box。下面分別介紹。
- ???下圖為“minf”部分字節實例,其中紅色為box header,藍色為“smhd”,綠色為“dinf”,黃色為一部分“stbl”。
Media Information Header Box(vmhd、smhd、hmhd、nmhd)
- Video Media Header Box(vmhd)?
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | |
graphics mode | 4 | 視頻合成模式,為0時拷貝原始圖像,否則與opcolor進行合成 |
opcolor | 2×3 | {red,green,blue} |
- Sound Media Header Box(smhd)?
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | |
balance | 2 | 立體聲平衡,[8.8] 格式值,一般為0,-1.0表示全部左聲道,1.0表示全部右聲道 |
reserved | 2 |
- Hint Media Header Box(hmhd)? 略
- Null Media Header Box(nmhd)? 非視音頻媒體使用該box,略
Data Information Box(dinf)
- ???“dinf”解釋如何定位媒體信息,是一個container box。“dinf”一般包含一個“dref”,即data reference box;“dref”下會包含若干個“url”或“urn”,這些box組成一個表,用來定位track數據。簡單的說,track可以被分成若干段,每 一段都可以根據“url”或“urn”指向的地址來獲取數據,sample描述中會用這些片段的序號將這些片段組成一個完整的track。一般情況下,當 數據被完全包含在文件中時,“url”或“urn”中的定位字符串是空的。
- ?“dref”的字節結構如下表。?
字段 | 字節數 | 意義 |
box size | 4 | box大小 |
box type | 4 | box類型 |
version | 1 | box版本,0或1,一般為0。(以下字節數均按version=0) |
flags | 3 | |
entry count | 4 | “url”或“urn”表的元素個數 |
“url”或“urn”列表 | 不定 |
- ???“url”或“urn”都是box,“url”的內容為字符串(location string),“urn”的內容為一對字符串(name string and location string)。當“url”或“urn”的box flag為1時,字符串均為空。
- ???下 面是一個“dinf”的字節實例圖。其中黃色為“dinf”的box header,由紅色部分我們知道包含的“url”或“urn”個數為1,紅色后面為“url”box的內容。紫色為“url”的box header(根據box type我們知道是個“url”),綠色為box flag,值為1,說明“url”中的字符串為空,表示track數據已包含在文件中。
Sample Table Box(stbl)
- ???“stbl”幾乎是普通的MP4文件中最復雜的一個box了,首先需要回憶一下sample的概念。sample是媒體數據存儲的單位,存儲在media的chunk中,chunk和sample的長度均可互不相同,如下圖所示。
- ???“stbl” 包含了關于track中sample所有時間和位置的信息,以及sample的編解碼等信息。利用這個表,可以解釋sample的時序、類型、大小以及在各自存儲容器中的位置。“stbl”是一個container box,其子box包括:sample description box(stsd)、time to sample box(stts)、sample size box(stsz或stz2)、sample to chunk box(stsc)、chunk offset box(stco或co64)、composition time to sample box(ctts)、sync sample box(stss)等。
- ???“stsd”必不可少,且至少包含一個條目,該box包含了data reference box進行sample數據檢索的信息。沒有“stsd”就無法計算media sample的存儲位置。“stsd”包含了編碼的信息,其存儲的信息隨媒體類型不同而不同。
Sample Description Box(stsd)
- 給出視頻、音頻的編碼、寬高、音量等信息,以及每個sample中包含多少個frame???
- 存儲了編碼類型和初始化解碼器需要的信息。有與特定的track-type相關的信息,相同的track-type也會存在不同信息的情況如使用不一樣的編碼標準。
- 結構如下:
- box header和version字段后會有一個entry count字段,根據entry的個數,每個entry會有type信息,如“vide”、“sund”等,根據type不同sample description會提供不同的信息,例如對于video track,會有“VisualSampleEntry”類型信息,對于audio track會有“AudioSampleEntry”類型信息。
- 視頻的編碼類型、寬高、長度,音頻的聲道、采樣等信息都會出現在這個box中。
Time To Sample Box(stts)
- ???結構如下:
- “stts” 存儲了sample的duration,描述了sample時序的映射方法,我們通過它可以找到任何時間的sample。“stts”可以包含一個壓縮的 表來映射時間和sample序號,用其他的表來提供每個sample的長度和指針。表中每個條目提供了在同一個時間偏移量里面連續的sample序號,以 及samples的偏移量。遞增這些偏移量,就可以建立一個完整的time to sample表。
Sample Size Box(stsz)
- 每個sample的size(單位是字節)? ?,根據 sample_size 字段,可以知道當前track包含了多少個sample(或幀)。
- 結構如下:
- “stsz” 定義了每個sample的大小,包含了媒體中全部sample的數目和一張給出每個sample大小的表。這個box相對來說體積是比較大的。
- 有兩種不同的box類型,stsz、stz2。
stsz:
- sample_size:默認的sample大小(單位是byte),通常為0。如果sample_size不為0,那么,所有的sample都是同樣的大小。如果sample_size為0,那么,sample的大小可能不一樣。
- sample_count:當前track里面的sample數目。如果 sample_size==0,那么,sample_count 等于下面entry的條目;
- entry_size:單個sample的大小(如果sample_size==0的話)
stz2:
- field_size:entry表中,每個entry_size占據的位數(bit),可選的值為4、8、16。4比較特殊,當field_size等于4時,一個字節上包含兩個entry,高4位為entry[i],低4位為entry[i+1];
- sample_count:等于下面entry的條目;
- entry_size:sample的大小。
Sample To Chunk Box(stsc)
- ???結構如下:
- 用chunk組織sample可以方便優化數據獲取,一個thunk包含一個或多個sample。“stsc”中用一個表描述了sample與chunk的映射關系,查看這張表就可以找到包含指定sample的thunk,從而找到這個sample。
Sync Sample Box(stss)
- 結構如下:
??
- “stss” 確定media中的關鍵幀。對于壓縮媒體數據,關鍵幀是一系列壓縮序列的開始幀,其解壓縮時不依賴以前的幀,而后續幀的解壓縮將依賴于這個關鍵幀。 “stss”可以非常緊湊的標記媒體內的隨機存取點,它包含一個sample序號表,表內的每一項嚴格按照sample的序號排列,說明了媒體中的哪一個 sample是關鍵幀。如果此表不存在,說明每一個sample都是一個關鍵幀,是一個隨機存取點。
Chunk Offset Box(stco)
- thunk在文件中的偏移???
- 結構如下:
- “stco” 定義了每個thunk在媒體流中的位置。位置有兩種可能,32位的和64位的,后者對非常大的電影很有用。在一個表中只會有一種可能,這個位置是在整個文 件中的,而不是在任何box中的,這樣做就可以直接在文件中找到媒體數據,而不用解釋box。需要注意的是一旦前面的box有了任何改變,這張表都要重新 建立,因為位置信息已經改變了。
- 針對小文件、大文件,有兩種不同的box類型,分別是stco、co64,它們的結構是一樣的,只是字段長度不同。
- chunk_offset 指的是在文件本身中的 offset,而不是某個box內部的偏移。
- 在構建mp4文件的時候,需要特別注意 moov 所處的位置,它對于chunk_offset 的值是有影響的。有一些MP4文件的 moov 在文件末尾,為了優化首幀速度,需要將 moov 移到文件前面,此時,需要對 chunk_offset 進行改寫。
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的差值,
- stts包含了DTS到sample number的映射表,主要用來推導每個幀(sample)的時長。
- 那么此樣本數據的dts為 :? ?0 ? 1000 2000 3000 4000 ···
- entry_count:stts 中包含的entry條目數;
- sample_count:單個entry中,具有相同時長(duration 或 sample_delta)的連續sample的個數。
- sample_delta:sample的時長(以timescale為計量)
ctts(Composition Time to Sample Box)
- Composition Time 構成時間目前我直接理解的PTS。。
- ctts 有每個sample的構成時間(Composition Time)和解碼時間(DTS)之間的差值(CTTS)即圖中的composition_offset。
- 如果不存在ctts,則代表該流不存在B幀,那么PTS就直接等于DTS。
- 幀解碼到渲染的時間差值,通常用在B幀的場景,對于存在B幀的視頻來說,ctts就需要存在了。當PTS、DTS不相等時,就需要ctts了,公式為 CT(n) = DT(n) + CTTS(n) 。
-
對于只有I幀、P幀的視頻來說,解碼順序、渲染順序是一致的,此時,ctts沒必要存在。
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幀),跳轉時,需要從關鍵幀開始解碼,否則會花屏。
- 哪些sample是關鍵幀
- mp4文件中,關鍵幀所在的sample序號。如果沒有stss的話,所有的sample中都是關鍵幀。
- entry_count:entry的條目數,可以認為是關鍵幀的數目;
- sample_number:關鍵幀對應的sample的序號;(從1開始計算)

stsz (Sample Size Boxes):
- 顧名思義,樣本大小.
?
?stsc (Sample To Chunk Box):
- 媒體數據的樣本是被打包進chunks(塊)的,chunks和樣本(samples)的大小不固定,該box用于說明chunks關聯樣本的信息。
- 每個thunk中包含幾個sample
- entry_count:有多少個表項(每個表項,包含first_chunk、samples_per_chunk、sample_description_index信息);
- first_chunk 該入口第一個chunks的索引(index).
- samples_per_chunk 樣本數量/chunks.
- sample_description_index:指向 stsd 中 sample description 的索引值(參考stsd小節);
-
sample 以 chunk 為單位分成多個組。chunk的size可以是不同的,chunk里面的sample的size也可以是不同的
前面描述比較抽象,這里看個例子,這里表示的是:
- 序號1~15的chunk,每個chunk包含15個sample;
- 序號16的chunk,包含30個sample;
- 序號17以及之后的chunk,每個chunk包含28個sample;
- 以上所有chunk中的sample,對應的sample description的索引都是1;
first_chunk | samples_per_chunk | sample_description_index |
---|---|---|
1 | 15 | 1 |
16 | 30 | 1 |
17 | 28 | 1 |
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中描述)。
- 實際媒體數據。我們最終解碼播放的數據都在這里面
- 該box包含于文件層,可以有多個,也可以沒有(當媒體數據全部為外部文件引用時),用來存儲媒體數據。數據直接跟在box type字段后面,具體數據結構的意義需要參考metadata(主要在sample table中描述)。
?
mehd(Movie Extends Header Box)
- mehd是可選的,用來聲明影片的完整時長(fragment_duration)。如果不存在,則需要遍歷所有的fragment,來獲得完整的時長。對于fmp4的場景,fragment_duration一般沒辦法提前預知。
trex(Track Extends Box)
- 用來給 fMP4 的 sample 設置各種默認值,比如時長、大小等
-
字段含義如下:
- track_id:對應的 track 的 ID,比如video track、audio track 的ID;
- default_sample_description_index:sample description 的默認 index(指向stsd);
- default_sample_duration:sample 默認時長,一般為0;
- default_sample_size:sample 默認大小,一般為0;
- default_sample_flags:sample 的默認flag,一般為0;
- 老版本規范里,前6位都是保留位,新版規范里,只有前4位是保留位。is_leading 含義不是很直觀,下一小節會專門講解下。
- reserved:4 bits,保留位;
- is_leading:2 bits,是否 leading sample,可能的取值包括:
- 0:當前 sample 不確定是否 leading sample;(一般設為這個值)
- 1:當前 sample 是 leading sample,并依賴于 referenced I frame 前面的 sample,因此無法被解碼;
- 2:當前 sample 不是 leading sample;
- 3:當前 sample 是 leading sample,不依賴于 referenced I frame 前面的 sample,因此可以被解碼;
- sample_depends_on:2 bits,是否依賴其他sample,可能的取值包括:
- 0:不清楚是否依賴其他sample;
- 1:依賴其他sample(不是I幀);
- 2:不依賴其他sample(I幀);
- 3:保留值;
- sample_is_depended_on:2 bits,是否被其他sample依賴,可能的取值包括:
- 0:不清楚是否有其他sample依賴當前sample;
- 1:其他sample可能依賴當前sample;
- 2:其他sample不依賴當前sample;
- 3:保留值;
- sample_has_redundancy:2 bits,是否有冗余編碼,可能的取值包括:
- 0:不清楚是否存在冗余編碼;
- 1:存在冗余編碼;
- 2:不存在冗余編碼;
- 3:保留值;
- sample_padding_value:3 bits,填充值;
- sample_is_non_sync_sample:1 bits,不是關鍵幀;
- sample_degradation_priority:16 bits,降級處理的優先級(一般針對如流傳過程中出現的問題);
is_leading
- 為方便講解,下面的 leading frame 對應 leading sample,referenced frame 對應 referenced samle。
- 以 H264編碼 為例,H264 中存在 I幀、P幀、B幀。由于 B幀 的存在,視頻幀的 解碼順序、渲染順序 可能不一致。
- mp4文件的特點之一,就是支持隨機位置播放。比如,在視頻網站上,可以拖動進度條快進。
- 很多時候,進度條定位的那個時刻,對應的不一定是 I幀。為了能夠順利播放,需要往前查找最近的一個 I幀,如果可能的話,從最近的 I幀 開始解碼播放(也就是說,不一定能從前面最近的I幀播放)。
- 將上面描述的此刻定位到的幀,稱作 leading frame。leading frame 前面最近的一個 I 幀,叫做 referenced frame。
- 回顧下 is_leading 為 1 或 3 的情況,同樣都是 leading frame,什么時候可以解碼(decodable),什么時候不能解碼(not decodable)?
- 我沒看懂
1、is_leading 為 1 的例子: 如下所示,幀2(leading frame) 解碼依賴 幀1、幀3(referenced frame)。在視頻流里,從 幀2 往前查找,最近的 I幀 是 幀3。哪怕已經解碼了 幀3,幀2 也解不出來。
2、is_leading 為 3 的例子: 如下所示,此時,幀2(leading frame)可以解碼出來。
moof
- moof是個container box,相關 metadata 在內嵌box里,比如 mfhd、 tfhd、trun 等。
mfhd(Movie Fragment Header Box)
- 結構比較簡單,sequence_number 為 movie fragment 的序列號。根據 movie fragment 產生的順序,從1開始遞增。
traf(Track Fragment Box)
- 對 fmp4 來說,數據被氛圍多個 movie fragment。一個 movie fragment 可包含多個track fragment(每個 track 包含0或多個 track fragment)。
- 每個 track fragment 中,可以包含多個該 track 的 sample。 每個 track fragment 中,包含多個 track run,每個 track run 代表一組連續的 sample。
tfhd(Track Fragment Header Box)
- tfhd 用來設置 track fragment 中 的 sample 的 metadata 的默認值。
- sample_description_index、default_sample_duration、default_sample_size 沒什么好講的,這里只講解下 tf_flags、base_data_offset。
- 首先是 tf_flags,不同 flag 的值如下(同樣是求按位求或) :
- 0x000001 base‐data‐offset‐present:存在 base_data_offset 字段,表示 數據位置 相對于整個文件的 基礎偏移量。
- 0x000002 sample‐description‐index‐present:存在 sample_description_index 字段;
- 0x000008 default‐sample‐duration‐present:存在 default_sample_duration 字段;
- 0x000010 default‐sample‐size‐present:存在 default_sample_size 字段;
- 0x000020 default‐sample‐flags‐present:存在 default_sample_flags 字段;
- 0x010000 duration‐is‐empty:表示當前時間段不存在sample,default_sample_duration 如果存在則為0 ,;
- 0x020000 default‐base‐is‐moof:如果 base‐data‐offset‐present 為1,則忽略這個flag。如果 base‐data‐offset‐present 為0,則當前 track fragment 的 base_data_offset 是從 moof 的第一個字節開始計算;
- sample 位置計算公式為 base_data_offset + data_offset,其中,data_offset 每個 sample 單獨定義。如果未顯式提供 base_data_offset,則 sample 的位置的通常是基于 moof 的相對位置。
- 舉個例子,比如 tf_flags 等于 57,表示 存在 base_data_offset、default_sample_duration、default_sample_flags。
- base_data_offset 為 1263 (ftyp、moov 的size 之和為 1263)。
?trun(Track Fragment Run Box)
- 前面聽過,track run 表示一組連續的 sample,其中:
- sample_count:sample 的數目;
- data_offset:數據部分的偏移量;
- first_sample_flags:可選,針對當前 track run中 第一個 sample 的設置;
- tr_flags 如下,大同小異:
- 0x000001 data‐offset‐present:存在 data_offset 字段;
- 0x000004 first‐sample‐flags‐present:存在 first_sample_flags 字段,這個字段的值,只會覆蓋第一個 sample 的flag設置;當 first_sample_flags 存在時,sample_flags 則不存在;
- 0x000100 sample‐duration‐present:每個 sample 都有自己的 sample_duration,否則使用默認值;
- 0x000200 sample‐size‐present:每個 sample 都有自己的 sample_size,否則使用默認值;
- 0x000400 sample‐flags‐present:每個 sample 都有自己的 sample_flags,否則使用默認值;
- 0x000800 sample‐composition‐time‐offsets‐present:每個 sample 都有自己的 sample_composition_time_offset;
- 0x000004 first‐sample‐flags‐present,覆蓋第一個sample的設置,這樣就可以把一組sample中的第一個幀設置為關鍵幀,其他的設置為非關鍵幀;
- 舉例如下,tr_flags 為 2565。此時,存在 data_offset 、first_sample_flags、sample_size、sample_composition_time_offset。
補充
- moofbox,這個box是視頻分片的描述信息。并不是MP4文件必須的部分,但在我們常見的可在線播放的MP4格式文件中(例如Silverlight Smooth Streaming中的ismv文件)確是重中之重。
- mfra box,一般在文件末尾,媒體的索引文件,可通過查詢直接定位所需時間點的媒體數據。
- 附:Smooth Streaming中ismv文件結構,文件分為了多個Fragments,每個Fragment中包含moof和mdat。這樣的結構符合漸進式播放需求。(mdat及其描述信息逐步傳輸,收齊一個Fragment便可播放其中的mdat)。
- mp4和fmp4的區別
- fMP4 跟普通 mp4 基本文件結構是一樣的。普通mp4用于點播場景,fmp4通常用于直播場景。
- 它們有以下差別:
- 普通mp4的時長、內容通常是固定的。fMP4 時長、內容通常不固定,可以邊生成邊播放;
- 普通mp4完整的metadata都在moov里,需要加載完moov box后,才能對mdat中的媒體數據進行解碼渲染;
- fMP4中,媒體數據的metadata在moof box中,moof 跟 mdat (通常)結對出現。moof 中包含了sample duration、sample size等信息,因此,fMP4可以邊生成邊播放;
- 怎么判斷mp4文件是普通mp4,還是fMP4呢?一般可以看下是否存在存在mvex(Movie Extends Box)。
- 當存在mvex時,表示當前文件是fmp4(非嚴謹)。此時,sample相關的metadata不在moov里,需要通過解析moof box來獲得。
- 它們有以下差別:
- sample:
- video sample 即為一幀或者一組連續的視頻幀
- audio sample 即為一段連續的音頻
- sample table 指明sample時序和物理布局的表