封装格式之MP4

Posted 落樱弥城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了封装格式之MP4相关的知识,希望对你有一定的参考价值。

  摘要:MP4格式是比较常用的视频封装格式,本文主要描述mp4格式的具体描述以及相关的分析工具。
  关键字:MP4

1 MP4格式简介

  ISO/IEC 14496-12:2004:Information technology — Coding of audio-visual objects — Part 12:ISO base media file format
  ISO/IEC 14496-14:2003: Information technology — Coding of audio-visual objects — Part 14:MP4 file format

  MP4文件格式即ISO/IEC 14496-14:2003,是冲QuickTime文件格式发展而来,是ISO/IEC 14496-12:2004(ISO Base Media File format)的一个具体实例。MP4的实现有两个版本:第一个版本是ISO/IEC 14496-1:2001(MPEG-4 Part 1 (Systems), First edition),发布于2001年;第二个版本ISO/IEC 14496-14:2003(MPEG-4 Part 14 (MP4 file format), Second edition)发布于2003年对前者进行了改进。

  MP4文件格式是一种标准的数字多媒体容器格式,主要用来存储数字音频和数字视频数据,支持多种音频编码数据,视频编码数据以及其他需要嵌入的额外数据,也可以存储静态图像和字幕。MP4 文件可以包含格式标准定义的元数据,还可以包含可扩展元数据平台 (XMP) 元数据。另外,MP4文件的扩展名一般为.mp4.m4a, .m4p, .m4b, .m4r, .m4v也是MP4格式的扩展名,只是不同场景使用不同的扩展名(比如m4a通常用于仅仅包含音频流的文件)。

  需要注意的是下面的文件格式严格的说应该是ISO Base Media File Format的描述,MP4只是其中的一个实现而已。比如3GP,mov等格式ISO Base Media File Format的实现,都是以box存储数据的方式,而MP4有两个版本的实现mp41和mp42。

2 MP4文件格式基本单元

  MP4文件格式中的所有数据都是通过Box(或者Atom)描述,Box是可以嵌套的。Box由Box Header和Box Body描述,Box Header描述Box的大小类型以及一些描述符(网络字节序,大端),Box Body就是当前Box内含的数据。标准中Box定义的伪代码如下:

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;
    
 
  • size:32bit描述Box的大小,size=sizeof(Box Header) + sizeof(Box Body)。针对不同情况有不同的扩展方式:
    • size==1:真实的大小为64bit的largesize
    • size==0:表明当前Box是文件的最后一个Box,Box Body就是文件剩下的内容;
  • type:描述当前Box的类型(MP4中如果找到无法识别的Box会忽略掉),标准格式是4个字符,比如moov,如果是用户定义的类型的话固定为uuid,而类型由usertype描述。

  另一种Box的扩展————FullBox,在原来Box的基础上添加了Box的版本和flag描述:

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;
 
  • version:描述当前Box的版本;
  • flags:顾名思义,标志符描述当前Box。

  可以在mp4ra.org中查看已经注册的Box类型。另外可以用MP4Infomp4explorerOnline MP4 file parser来解析MP4的Box。

3 一些重要的Box

  ISO媒体文件通过Box索引和存储数据,并且数据和索引是分开存放的,从下面的图中能够看到trak存储了索引信息,而具体的数据存储在mdat中。
  下面描述的Box都是基于Box的基本格式,描述Box Body的详细内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rEtTZK1m-1668182367238)(imgs/ios.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bco3veMt-1668182367239)(imgs/allbox.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PmpDYpU6-1668182367239)(imgs/example.png)]

3.1 ftyp

//类型:ftype
//容器:当前文件
//强制:必需
//数量:1
aligned(8) class FileTypeBox extends Box(‘ftyp’) 
    unsigned int(32) major_brand;
    unsigned int(32) minor_version;
    unsigned int(32) compatible_brands[]; // to end of the box
 

  ftyp(File Type Box)在文件中的位置尽可能的靠前帮助解封装器识别文件的类型以及兼容版本。ftyp描述了文件的主要规范,解封装器尽量使用对应的规范解复用文件,而次要版本仅供参考,不得用于确定文件是否符合标准。 它可能允许更精确地识别主要规范,以进行检查、调试或改进解码。

  • major_brand:当前文件的主要兼容格式,解封装器最好使用改规范解复用文件;
  • minor_version:次要版本;
  • compatible_brands:当前文件兼容的规范列表。

  ftypBox的大小为32字节,主规范为isom,次版本号为512,兼容规范为isom,ios2,avc1,mp41。比如:

[ftyp] size=8+24
  major_brand = isom
  minor_version = 200
  compatible_brand = isom iso2 avc1 mp41

3.2 moov

//类型:moov
//容器:当前文件
//强制:必需
//数量:1
aligned(8) class MovieBox extends Box(‘moov’)

  moov(Movie Box)存储媒体文件的元数据,是一个容器box,即具体信息由Box中的子Box描述。一般情况下在文件中的位置接近文件头或者文件尾,大多数情况下在ftyp后面。

3.3 mdat

//类型:mdat
//容器:当前文件
//强制:非必需
//数量:任何数量
aligned(8) class MediaDataBox extends Box(‘mdat’)  
    bit(8) data[];

  mdat(Media Data Box)包含实际经过编码的媒体流数据,其具体的媒体数据类型是通过其他Box索引描述,也就是说媒体数据mdat即便没有Box Header也能正常解析。

3.4 mvhd

//类型:mvhd
//容器:moov
//强制:必需
//数量:1
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; 

  mvhd(Movie Header Box)是一个FullBox存储描述媒体文件信息的元信息,和具体的媒体数据不相关的内容。

  • version:版本,0或者1
  • creation_time:一个64位int表示创建时间(in seconds since midnight, Jan. 1, 1904, in UTC time);
  • modification_time:一个64位int表示修改时间(in seconds since midnight, Jan. 1, 1904, in UTC time);
  • timescale:1s的时间尺度,比如60表示时间间隔为 1 60 \\frac160 601s;
  • duration:表示当前媒体的时长,根据文件中的track的信息推导出来,等于时间最长的track的duration;
  • rate:推荐的播放速率,32位整数,高16位、低16位分别代表整数部分、小数部分,1.0表示正常播放速率;
  • volume:推荐的音量,16位整数,高8位、低8位分别代表整数部分、小数部分,1.0表示正常播放音量;
  • matrix:视频的转换矩阵,默认是个单位矩阵;
  • next_track_ID:表示一个值用于将要添加到此演示文稿中的下一个轨道的轨道 ID。零不是有效的轨道 ID 值。 next_track_ID的值应大于正在使用的最大 track-ID。 如果该值等于或大于0xffff(最大 32 位),并且要添加新的媒体轨道,则必须在文件中搜索未使用的轨道标识符。比如:
  [mvhd] size=12+96
    version = 0
    flags = 0 
    creation_time = 2022-11-06T03:40:48.000Z
    modification_time = 2022-11-06T03:40:48.000Z
    timescale = 600
    duration = 63250
    rate = 1
    volume = 1
    next_track_ID = 3

3.5 trak

//类型:trak
//容器:moov
//强制:必需
//数量:大于等于1
aligned(8) class TrackBox extends Box(‘trak’) 

  trak(Track)是一个容器Box,本身不包含任何内容,其含义由内部的Box定义。媒体文件中可能包含多条Track,每一条Track是独立的,都有自己的时间和空间信息,比如音频和视频可以单独分为两个Track存放,而单个视频流也可以分为多个Track存放。

3.6 tkhd

//类型:tkhd
//容器:trak
//强制:必需
//数量:1
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;

  tkhd(Track Header)是用来存储当前Track的描述信息,一个Track只有一个tkhd。媒体轨道的轨道头标志的默认值为 7(track_enabled、track_in_movie、track_in_preview)。 如果在演示中所有轨道既没有设置 track_in_movie 也没有设置 track_in_preview,则所有轨道都应被视为在所有轨道上都设置了两个标志。Hint轨道应将轨道头标志设置为 0,以便在本地播放和预览时忽略它们。

  • version:0或者1表示版本;
  • flag:24bit的标志位:
    • Track_enabled(0x000001):表明当前Track可用,如果disable了应该设置为0;
    • Track_in_movie(0x000002):表明当前Track用于播放;
    • Track_in_preview(0x000004):表明当前Track用于预览;
  • creation_time:一个64位int表示创建时间(in seconds since midnight, Jan. 1, 1904, in UTC time);
  • modification_time:一个64位int表示修改时间(in seconds since midnight, Jan. 1, 1904, in UTC time);
  • track_ID:当前Track的ID,是唯一表示,一个文件中不应该存在两个Track_ID相同的Track;
  • reserved:预留;
  • duration:当前轨道的时长,如果有轨道的编辑列表则值为所有编辑列表持续时间之和;否则为样本持续时间之和。如果无法确认则时间设置为0xfffffff。另外时间需要根据mvhd中的time_scale计算;
  • layer:视频轨道的叠加顺序,数字越小越靠近观看者,比如1比2靠上,0比1靠上;
  • alternate_group:当前Track的分组ID,0表示当前轨道不和任何一个轨道在同一组,任何时候只能播放一个组中的一个Track。
  • volume:推荐的音量,16位整数,高8位、低8位分别代表整数部分、小数部分,1.0表示正常播放音量;
  • matrix:视频的转换矩阵,默认是个单位矩阵;
  • width:表示当前轨道视频宽度的浮点数(高16位为整数部分,低16位为小数部分),在对轨道进行操作前图像都会缩放到当前的size;
  • height:表示当前轨道视频高度的浮点数(高16位为整数部分,低16位为小数部分),在对轨道进行操作前图像都会缩放到当前的size。
//这个示例视频有两个track一个视频流一个是音频流
[tkhd] size=12+80, flags=3
    id = 1
    duration = 63250
    width = 640.000000
    height = 360.000000

[tkhd] size=12+80, flags=1
    id = 2
    duration = 63223
    width = 0.000000
    height = 0.000000

3.7 edts

//类型:edts
//容器:trak
//强制:非必需
//数量:0或1
aligned(8) class EditBox extends Box(‘edts’) 
 

  edts(Edit Box)是一个容器Box。描述了播放媒体流时的映射关系,如果为空就按照track中的时间一一映射播放,设定的话就按照elst中的时间播放。

3.8 elst

//类型:elst
//容器:edts
//强制:非必需
//数量:0或1
aligned(8) class EditListBox extends FullBox(‘elst’, version, 0) 
    unsigned int(32) entry_count;
    for (i=1; i <= entry_count; i++) 
        if (version==1) 
            unsigned int(64) segment_duration;
            int(64) media_time;
         else  // version==0
            unsigned int(32) segment_duration;
            int(32) media_time;
        
        
        int(16) media_rate_integer;
        int(16) media_rate_fraction = 0;
    
 

  elst(Edit List)是一个数组,数组中每一项存储了一定的显示规则,比如开始时间,持续时长,以及速率等。加入我们希望视频开始10s画面不懂,然后播放从第0s开始播放30s,elst应该为

Entry-count = 2
Segment-duration = 10 seconds Media-Time = -1 Media-Rate = 1
Segment-duration = 30 seconds Media-Time =  0 Media-Rate = 1
  • version:版本信息,0或者1;
  • entry_point:当前列表的项目数;
  • segment_duration:以mvhd中的time_scale表示的当前编辑项持续的时长;
  • media_time:包含此编辑段的媒体内的开始时间(以mdhd中的time_scale为单位)。 如果此字段设置为 –1,则为空编辑。 轨道中的最后一个编辑永远不会是空编辑;
  • media_rate:edit段的速率为0的话,画面停止。画面会在media_time点上停止segment_duration时间。否则这个值始终为1。

  需要注意的是虽然上面的字段中有media_rate_integermedia_rate_fraction,但是标准里只描述了media_time。通过对比二进制位我认为标准里的media_time就是media_rate_integer
media_rate specifies the relative rate at which to play the media corresponding to this edit segment. If this value is 0, then the edit is specifying a ‘dwell’: the media at media-time is presented for the segment-duration. Otherwise this field shall contain the value 1.

3.9 mdia

//类型:mdia
//容器:trak
//强制:必需
//数量:1
aligned(8) class MediaBox extends Box(‘mdia’) 
 

  Media Box是一个容器Box存储当前轨道的媒体信息。

3.10 mdhd

//类型:mdhd
//容器:mdia
//强制:必需
//数量:1
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;

  mdhd(Media Header Box)存储了媒体的时长等元数据。

  • version:版本号0或者1;
  • creation_time:一个64/32位int表示修改时间(in seconds since midnight, Jan. 1, 1904, in UTC time);
  • modification_time:一个64/32位int表示修改时间(in seconds since midnight, Jan. 1, 1904, in UTC time);
  • timescale:当前媒体流1s所包含的可读,即一个刻度表示 1 t i m e s c a l e \\frac1time_scale timescale1s;
  • duration:以timescale表示的时长;
  • ```language``:当前track的媒体的语言代码。 三字符代码集参见 ISO 639-2/T。 每个字符都被打包为它的 ASCII 值和 0x60 之间的差异。 由于代码仅限于三个小写字母,因此这些值是严格的正数。

3.11 hdlr

//类型:mdia
//容器:mdia或者meta
//强制:必需
//数量:1
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;

  hdlr(Handler Reference)声明了轨道中的媒体数据呈现的过程,因此也声明了轨道中媒体的性质。 例如,视频轨道将由视频处理程序处理。此框在 Meta Box 中出现时,声明了“Meta Box”内容的结构或格式。

  • version:当前box的版本;
  • handler_type
    • hdlrmdia时为四个字符表示当前track的类型:,可取值vide,soun,hint分别表示视频,音频和hint Track;
    • metaBox中时表示当前Box的内容格式;
  • name:一个以\\0为结尾的UTF-8字符串,该字符串对track进行描述。

3.12 minf

//类型:minf
//容器:mdia
//强制:必需
//数量:1
aligned(8) class MediaInformationBox extends Box(‘minf’) 

  minf(Media Information)是一个容器Box,包含Track数据的索引等信息。

3.13 vmhd,smhd,hmhd,nmhd

//类型:vmhd,smhd,hmhd,nmhd
//容器:minf
//强制:必需
//数量:1

  vmhd,smhd,hmhd,nmhd这四个Box都是存储当前媒体数据的描述信息。只是针对不同类型的数据采用不同的box,比如视频就是vmhd,音频就是smhd

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;

vmhd

aligned(8) class SoundMediaHeaderBox extends FullBox(‘smhd’, version = 0, 0)  
    template int(16) balance = 0; 
    const unsigned int(16) reserved = 0;

vmhd

aligned(8) class HintMediaHeaderBox extends FullBox(‘hmhd’, version = 0, 0)  
    unsigned int(16) maxPDUsize;
    unsigned int(16) avgPDUsize;
    unsigned int(32) maxbitrate;
    以上是关于封装格式之MP4的主要内容,如果未能解决你的问题,请参考以下文章

音视频处理之FFmpeg封装格式20180510

100ask_imx6ull视频监控项目-流媒体方案的实现之ffmpeg

音视频处理之封装格式介绍20180225

如何从 MP4 文件中提取元数据轨道

#yyds干货盘点# FFmpeg[5] - 将视频文件转码成MP4格式(FFmpeg转封装2)

一文读懂MP4封装格式