媒體文件格式分析之FMP4
MP4 中最基本的單元就是Box,它內(nèi)部是通過一個(gè)一個(gè)獨(dú)立的Box拼接而成的罢绽。所以,這里,我們先從 Box 的講解開始,每個(gè) Box 是由 Header 和 Data 組成的寓调,F(xiàn)ullBox 是 Box 的擴(kuò)展,Box 結(jié)構(gòu)的基礎(chǔ)上在 Header 中增加 8bits version 和 24bits flags
1. 名詞解釋
2. 最小單元Box
2.1 常見的mp4文件結(jié)構(gòu)(簡(jiǎn)化版)
3. Mp4文件整體結(jié)構(gòu)
這里锄码,我們按照 MP4 box 的劃分來進(jìn)行相關(guān)的闡述夺英。先看一張 MP4 給出的結(jié)構(gòu)圖:
一般來說,解析媒體文件滋捶,最關(guān)心的部分是視頻文件的寬高痛悯、時(shí)長(zhǎng)、碼率重窟、編碼格式载萌、幀列表、關(guān)鍵幀列表巡扇,以及所對(duì)應(yīng)的時(shí)戳和在文件中的位置扭仁,這些信息,在mp4中厅翔,是以特定的算法分開存放在stbl box下屬的幾個(gè)box中的乖坠,需要解析stbl下面所有的box,來還原媒體信息刀闷。下表是對(duì)于以上幾個(gè)重要的box存放信息的說明
3.1 File Type Box (ftyp)
通常放在MP4文件的開頭瓤帚,告訴解碼器基本的解碼版本和兼容格式描姚。
- 基本格式如下:
aligned(8) class FileTypeBox
extends Box(‘ftyp’) {
unsigned int(32) major_brand;
unsigned int(32) minor_version;
unsigned int(32) compatible_brands[];
}
- 字段說明:
字段 | 長(zhǎng)度 | 說明 | 默認(rèn)值 |
---|---|---|---|
major_brand | 4 | 推薦兼容性的版本 | iso6 |
minor_version | 4 | 最低兼容性的版本 | 1 |
compatible_brands | 列表值 | 所有的兼容性的版本 | 'iso6' 'isom' 'dash' |
- Nginx模塊實(shí)現(xiàn)
ngx_int_t
ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b)
{
u_char *pos;
pos = ngx_rtmp_mp4_start_box(b, "ftyp");
/* major brand */
ngx_rtmp_mp4_box(b, "iso6");
/* minor version */
ngx_rtmp_mp4_field_32(b, 1);
/* compatible brands */
ngx_rtmp_mp4_box(b, "isom");
ngx_rtmp_mp4_box(b, "iso6");
ngx_rtmp_mp4_box(b, "dash");
ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK;
}
3.2 Movie Box (moov)
作為容器盒子,存放相關(guān)的trak及meta信息.
- 基本格式如下:
aligned(8) class MovieExtendsBox extends Box(‘mvex’){ }
3.2.1 Movie Header Box (mvhd)
mvhd 是 moov 下的第一個(gè) box戈次,用來描述 media 的相關(guān)信息:
- 基本格式如下:
aligned(8) class MovieHeaderBox extends FullBox(‘mvhd’, version, 0) {
if (version==1) {
unsigned int(64) creation_time;
unsigned int(64) modification_time;
unsigned int(32) timescale;
unsigned int(64) duration;
} else { // version==0
unsigned int(32) creation_time;
unsigned int(32) modification_time;
unsigned int(32) timescale;
unsigned int(32) duration;
}
template int(32) rate = 0x00010000; // typically 1.0
template int(16) volume = 0x0100; // typically, full volume
const bit(16) reserved = 0;
const unsigned int(32)[2] reserved = 0;
template int(32)[9] matrix =
{ 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };
// Unity matrix
bit(32)[6] pre_defined = 0;
unsigned int(32) next_track_ID;
}
- 字段說明:
字段 | 長(zhǎng)度 | 說明 | 默認(rèn)值 |
---|---|---|---|
version | 4 | 版本 | 0 or 1s |
creation_time | 4 | 創(chuàng)建的UTC時(shí)間轩勘。從1904年開始算起, 用秒來表示 | 無 |
modification_time | 4 | 最后一次修改時(shí)間 | 無 |
timescale | 4 | 文件媒體在1秒時(shí)間內(nèi)的刻度值,可以理解為1秒長(zhǎng)度的時(shí)間單元數(shù) | 無 |
duration | 4 | 該track的時(shí)間長(zhǎng)度怯邪,用duration和time scale值可以計(jì)算track時(shí)長(zhǎng)s | 實(shí)際時(shí)間為:duration/timescale = xx 秒 |
rate | 4 | 推薦播放速率 | 0x00010000 |
volume | 2 | 音量大小 | 0x0100 為最大值 |
reserved | 10 | 保留字段 | 0 |
matrixs | 4 * 9 | 視頻變換矩陣 | {0x00010000,0,0,0,0x0001s0000,0,0,0,0x40000000} |
next_track_ID | 4 | 下一個(gè)track使用的id號(hào) |
- Nginx模塊實(shí)現(xiàn)
static ngx_int_t
ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b)
{
u_char *pos;
pos = ngx_rtmp_mp4_start_box(b, "mvhd");
/* version */
ngx_rtmp_mp4_field_32(b, 0);
/* creation time */
ngx_rtmp_mp4_field_32(b, 0);
/* modification time */
ngx_rtmp_mp4_field_32(b, 0);
/* timescale */
ngx_rtmp_mp4_field_32(b, 1000);
/* duration */
ngx_rtmp_mp4_field_32(b, 0);
/* reserved */
ngx_rtmp_mp4_field_32(b, 0x00010000);
ngx_rtmp_mp4_field_16(b, 0x0100);
ngx_rtmp_mp4_field_16(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_write_matrix(b, 1, 0, 0, 1, 0, 0);
/* reserved */
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
/* next track id */
ngx_rtmp_mp4_field_32(b, 1);
ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK;
}
3.2.2 Movie Extends Box (mvex)(fMP4專有)
mvex 是 fMP4 的標(biāo)準(zhǔn)盒子绊寻。它的作用是告訴解碼器這是一個(gè)fMP4的文件,具體的 samples 信息內(nèi)容不再放到 trak 里面悬秉,而是在每一個(gè) moof 中澄步。基本格式為:
aligned(8) class MovieExtendsHeaderBox extends FullBox(‘mehd’, version, 0) { if (version==1) {
unsigned int(64) fragment_duration;
} else { // version==0
unsigned int(32) fragment_duration;
}
}
3.2.2.1 Track Extends Box (trex)(fMP4專有)
trex 是 mvex 的子一級(jí) box 用來給 fMP4 的 sample 設(shè)置默認(rèn)值和泌〈甯祝基本內(nèi)容為
aligned(8) class TrackExtendsBox extends FullBox(‘trex’, 0, 0){
unsigned int(32) track_ID;
unsigned int(32) default_sample_description_index;
unsigned int(32) default_sample_duration;
unsigned int(32) default_sample_size;
unsigned int(32) default_sample_flags
}
3.2.3 Track Box (trak)
trak box 就是主要存放相關(guān) media stream 的內(nèi)容。
3.2.3.1 Track Header Box (tkhd)
tkhd 是 trak box 的子一級(jí) box 的內(nèi)容武氓。主要是用來描述該特定 trak 的相關(guān)內(nèi)容信息梯皿。其主要內(nèi)容為:
- 基本格式如下:
aligned(8) class TrackHeaderBox
extends FullBox(‘tkhd’, version, flags){
if (version==1) {
unsigned int(64) creation_time;
unsigned int(64) modification_time;
unsigned int(32) track_ID;
const unsigned int(32) reserved = 0;
unsigned int(64) duration;
} else { // version==0
unsigned int(32) creation_time;
unsigned int(32) modification_time;
unsigned int(32) track_ID;
const unsigned int(32) reserved = 0;
unsigned int(32) duration;
}
const unsigned int(32)[2] reserved = 0;
template int(16) layer = 0;
template int(16) alternate_group = 0;
template int(16) volume = {if track_is_audio 0x0100 else 0}; const unsigned int(16) reserved = 0;
template int(32)[9] matrix=
{ 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };
// unity matrix
unsigned int(32) width;
unsigned int(32) height;
}
- 字段說明:
字段 | 長(zhǎng)度 | 說明 | 默認(rèn)值 |
---|---|---|---|
version | 4 | 版本 | |
creation_time | 4 | 創(chuàng)建時(shí)間,非必須 | 0 |
modification_time | 4 | 修改時(shí)間县恕,非必須 | 0 |
track_ID | 4 | 指明當(dāng)前描述的 track ID | 1 |
reserved | 4 | 保留 | 0 |
duration | 4 | 當(dāng)前 track 內(nèi)容持續(xù)的時(shí)間东羹。通常結(jié)合 timescale 進(jìn)行相關(guān)計(jì)算 | 0 |
reserved | 12 | 保留字段 | 0 |
reserved | 2 | 保留字段 | 0 |
alternate_group | 2 | 保留字段 | 0 |
volume | 2 | 保留字段 | if track_is_audio 0x0100 else 0 |
reserved | 2 | 保留字段 | 0 |
matrix | 9 * 4 | matrix | b, 1, 0, 0, 1, 0, 0 , width , height |
3.2.3.2 Media Box (media)
mdia 主要用來包裹相關(guān)的 media 信息。
(1) Media Header Box (mdhd)
- 基本格式如下:
aligned(8) class MediaHeaderBox extends FullBox(‘mdhd’, version, 0) { if (version==1) {
unsigned int(64) creation_time;
unsigned int(64) modification_time;
unsigned int(32) timescale;
unsigned int(64) duration;
} else { // version==0
unsigned int(32) creation_time;
unsigned int(32) modification_time;
unsigned int(32) timescale;
unsigned int(32) duration;
}
bit(1) pad = 0;
unsigned int(5)[3] language; // ISO-639-2/T language code unsigned int(16) pre_defined = 0;
}
- 字段說明:
字段 | 長(zhǎng)度 | 說明 | 默認(rèn)值 |
---|---|---|---|
version | 4 | 版本 | |
creation_time | 4 | 創(chuàng)建時(shí)間忠烛,非必須 | 0 |
modification_time | 4 | 修改時(shí)間属提,非必須 | 0 |
timescale | 4 | 文件媒體在1秒時(shí)間內(nèi)的刻度值,可以理解為1秒長(zhǎng)度的時(shí)間單元數(shù) | 無 |
duration | 4 | 當(dāng)前 track 內(nèi)容持續(xù)的時(shí)間美尸。通常結(jié)合 timescale 進(jìn)行相關(guān)計(jì)算 | 0 |
lanuage | 4s | 表明當(dāng)前 trak 的語言冤议。因?yàn)樵撟侄慰傞L(zhǎng)為 15bit,通常是和 pad 組合成為 2B 的長(zhǎng)度师坎。 | - |
(2) Handler Reference Box(hdlr)
- 基本格式如下:
aligned(8) class HandlerBox extends FullBox(‘hdlr’, version = 0, 0) {
unsigned int(32) pre_defined = 0;
unsigned int(32) handler_type;
const unsigned int(32)[3] reserved = 0;
string name;
}
- 字段說明:
字段 | 長(zhǎng)度 | 說明 | 默認(rèn)值 |
---|---|---|---|
version | 4 | 版本 | |
pre_defined | 4 | 版本 | 0 |
handler_type | 4 | 是代指具體 trak 的處理類型 | 0 |
reserved | 4 * 3 | reserved | 0 |
data | string | reserved | "VideoHandler" or "SoundHandler" |
- handler_type 類型如下:
vide : Video track
soun : Audio track
hint : Hint track
meta : Timed Metadata track
auxv : Auxiliary Video track
3.2.3.3 Media Information Box (minf)
minf 是子屬內(nèi)容中求类,重要的容器 box,用來存放當(dāng)前 track 的基本描述信息屹耐。
(1) Video Media Header Box(vmhd)
- 基本格式如下:
aligned(8) class VideoMediaHeaderBox
extends FullBox(‘vmhd’, version = 0, 1) {
template unsigned int(16) graphicsmode = 0; // copy, see below
template unsigned int(16)[3] opcolor = {0, 0, 0};
}
(2) Sound Media Header Box(smhd)
- 基本格式如下:
aligned(8) class SoundMediaHeaderBox
extends FullBox(‘smhd’, version = 0, 0) {
template int(16) balance = 0;
const unsigned int(16) reserved = 0;
}
(3) Data Information Box(dinf)
dinf 是用來說明在 trak 中尸疆,media 描述信息的位置。其實(shí)本身就是一個(gè)容器惶岭,沒啥內(nèi)容:
- 基本格式如下:
aligned(8) class SoundMediaHeaderBox
extends FullBox(‘smhd’, version = 0, 0) {
template int(16) balance = 0;
const unsigned int(16) reserved = 0;
}
(4) Data Reference Box(dref)
dref 是用來設(shè)置當(dāng)前Box描述信息的 data_entry寿弱。
- 基本格式如下:
aligned(8) class DataReferenceBox
extends FullBox(‘dref’, version = 0, 0) {
unsigned int(32) entry_count;
for (i=1; i <= entry_count; i++) {
DataEntryBox(entry_version, entry_flags) data_entry; }
}
- 字段說明:
字段 | 長(zhǎng)度 | 說明 | 默認(rèn)值 |
---|---|---|---|
version | 4 | 版本 | 0 |
entry_count | 4 | 入口數(shù) | 1 |
entry_version | 4 | 入口數(shù) | 0 |
entry_flags | 3 | 入口數(shù) | 0 |