媒体文件格式分析之FMP4

媒体文件格式分析之FMP4

MP4 中最基本的单元就是Box,它内部是通过一个一个独立的Box拼接而成的。所以,这里,我们先从 Box 的讲解开始,每个 Box 是由 Header 和 Data 组成的,FullBox 是 Box 的扩展,Box 结构的基础上在 Header 中增加 8bits version 和 24bits flags

1. 名词解释

2. 最小单元Box

2.1 常见的mp4文件结构(简化版)

normal_box_file.jpg

3. Mp4文件整体结构

这里,我们按照 MP4 box 的划分来进行相关的阐述。先看一张 MP4 给出的结构图:

box_structure.png

一般来说,解析媒体文件,最关心的部分是视频文件的宽高、时长、码率、编码格式、帧列表、关键帧列表,以及所对应的时戳和在文件中的位置,这些信息,在mp4中,是以特定的算法分开存放在stbl box下属的几个box中的,需要解析stbl下面所有的box,来还原媒体信息。下表是对于以上几个重要的box存放信息的说明

majorbox.jpg

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[];
}
  • 字段说明:
字段 长度 说明 默认值
major_brand 4 推荐兼容性的版本 iso6
minor_version 4 最低兼容性的版本 1
compatible_brands 列表值 所有的兼容性的版本 'iso6' 'isom' 'dash'
  • Nginx模块实现
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)

作为容器盒子,存放相关的trak及meta信息.

  • 基本格式如下:
aligned(8) class MovieExtendsBox extends Box(‘mvex’){ }

3.2.1 Movie Header Box (mvhd)

mvhd 是 moov 下的第一个 box,用来描述 media 的相关信息:

  • 基本格式如下:
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;
}
  • 字段说明:
字段 长度 说明 默认值
version 4 版本 0 or 1s
creation_time 4 创建的UTC时间。从1904年开始算起, 用秒来表示
modification_time 4 最后一次修改时间
timescale 4 文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数
duration 4 该track的时间长度,用duration和time scale值可以计算track时长s 实际时间为: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 下一个track使用的id号
  • Nginx模块实现
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 的标准盒子。它的作用是告诉解码器这是一个fMP4的文件,具体的 samples 信息内容不再放到 trak 里面,而是在每一个 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 的子一级 box 用来给 fMP4 的 sample 设置默认值。基本内容为

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 就是主要存放相关 media stream 的内容。

3.2.3.1 Track Header Box (tkhd)

tkhd 是 trak box 的子一级 box 的内容。主要是用来描述该特定 trak 的相关内容信息。其主要内容为:

  • 基本格式如下:

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;
}

  • 字段说明:
字段 长度 说明 默认值
version 4 版本
creation_time 4 创建时间,非必须 0
modification_time 4 修改时间,非必须 0
track_ID 4 指明当前描述的 track ID 1
reserved 4 保留 0
duration 4 当前 track 内容持续的时间。通常结合 timescale 进行相关计算 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 主要用来包裹相关的 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;
}

  • 字段说明:
字段 长度 说明 默认值
version 4 版本
creation_time 4 创建时间,非必须 0
modification_time 4 修改时间,非必须 0
timescale 4 文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数
duration 4 当前 track 内容持续的时间。通常结合 timescale 进行相关计算 0
lanuage 4s 表明当前 trak 的语言。因为该字段总长为 15bit,通常是和 pad 组合成为 2B 的长度。 -
(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;
}

  • 字段说明:
字段 长度 说明 默认值
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 是子属内容中,重要的容器 box,用来存放当前 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 描述信息的位置。其实本身就是一个容器,没啥内容:

  • 基本格式如下:
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 是用来设置当前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; }
    }

  • 字段说明:
字段 长度 说明 默认值
version 4 版本 0
entry_count 4 入口数 1
entry_version 4 入口数 0
entry_flags 3 入口数 0

3.3 Moof Box

3.4 Sidxs Box

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容