ISO/IEC 14496是MPEG专家组制定的MPEG-4标准,分为多个部分(仍在更新)。
参考:https://en.wikipedia.org/wiki/Category:ISO/IEC_14496
第一部分(ISO/IEC 14496-1):系统:描述视频和音频数据流的控制、同步以及混合方式(即混流Multiplexing,简写为MUX)。
第二部分(ISO/IEC 14496-2):视频:定义一个对各种视觉信息(包括自然视频、静止纹理、计算机合成图形等等)的编解码器。(例如XviD编码就属于MPEG-4 Part 2)
第三部分(ISO/IEC 14496-3):音频:定义一个对各种音频信号进行编码的编解码器的集合。包括高级音频编码(Advanced Audio Coding,缩写为AAC)的若干变形和其他一些音频/语音编码工具。
第四部分(ISO/IEC 14496-4):一致性:定义对本标准其他的部分进行一致性测试的程序。
第五部分(ISO/IEC 14496-5):参考软件:提供用于演示功能和说明本标准其他部分功能的软件。
第六部分(ISO/IEC 14496-6):多媒体传输集成框架(DMIF for Delivery Multimedia Integration Framework)
第七部分(ISO/IEC 14496-7):优化的参考软件:提供对实现进行优化的例子(这里的实现指的是第五部分)。
第八部分(ISO/IEC 14496-8):在IP网络上传输:定义在IP网络上传输MPEG-4内容的方式。
第九部分(ISO/IEC 14496-9):参考硬件:提供用于演示怎样在硬件上实现本标准其他部分功能的硬件设计方案。
第十部分(ISO/IEC 14496-10):高级视频编码或称高级视频编码(Advanced Video Coding,缩写为AVC):定义一个视频编解码器(codec)。AVC和XviD都属于MPEG-4编码,但由于AVC属于MPEG-4 Part 10,在技术特性上比属于MPEG-4 Part2的XviD要先进。另外,它和ITU-T H.264标准是一致的,故又称为H.264。
第十二部分(ISO/IEC 14496-12):基于ISO的媒体文件格式:定义一个存储媒体内容的文件格式。
第十三部分(ISO/IEC 14496-13):知识产权管理和保护(IPMP for Intellectual Property Management and Protection)拓展。
第十四部分(ISO/IEC 14496-14):MPEG-4文件格式:定义基于第十二部分的用于存储MPEG-4内容的视频文件格式。
第十五部分(ISO/IEC 14496-15):AVC文件格式:定义基于第十二部分的用于存储第十部分的视频内容的文件格式。
第十六部分(ISO/IEC 14496-16):动画框架扩展(AFX : Animation Framework eXtension)。
第十七部分(ISO/IEC 14496-17):同步文本字幕格式。
第十八部分(ISO/IEC 14496-18):字体压缩和流式传输(针对开放字体格式Open Font Format)。
第十九部分(ISO/IEC 14496-19):合成材质流(Synthesized Texture Stream)。
第二十部分(ISO/IEC 14496-20):简单场景表示(LASeR for Lightweight Scene Representation。
第二十一部分(ISO/IEC 14496-21):用于描绘(Rendering)的MPEG-J拓展。
第二十二部分(ISO/IEC 14496-22):开放字体格式(Open Font Format)。
第二十三部分(ISO/IEC 14496-23):符号化音乐表示(Symbolic Music Representation)。
第二十四部分(ISO/IEC 14496-24):音频与系统交互作用(Audio and systems interaction)。
第二十五部分(ISO/IEC 14496-25):3D图形压缩模型(3D Graphics Compression Model)。
第二十六部分(ISO/IEC 14496-26):音频一致性检查:定义测试音频数据与ISO/IEC 14496-3是否一致的方法(Audio conformance)。
第二十七部分(ISO/IEC 14496-27):3D图形一致性检查:定义测试3D图形数据与ISO/IEC 14496-11:2005, ISO/IEC 14496-16:2006, ISO/IEC 14496-21:2006,和ISO/IEC 14496-25:2009是否一致的方法(3D Graphics conformance)。
MP4是在“ISO/IEC 14496-14”标准文件中定义的一种多媒体容器格式,它是MPEG4 (ISO/IEC 14496)标准的一部分。是“ISO/IEC 14496-12(MPEG-4 Part 12 ISO base media file format)”标准中所定义的媒体格式的一种实现。
本篇文章主要参考的标准文档如下:
MP4是一种描述较为全面的容器格式,被认为可以在其中嵌入任何形式的数据,以及各种编码的音视频等,不过我们常见的大部分的MP4文件存放的AVC(H.264)或MPEG-4(Part 2)编码的视频和AAC编码的音频。
MP4的结构就像俄罗斯的套娃有很多box套box,也可以理解为一棵Box树。下面这张图是常见的box的树结构图,可以用来大致了解MP4文件的构造:
通过上面的介绍,我们了解了MP4格式就是由一个个的box组合成的box树,所有的数据都包含在box里,下面来了解一下box的基本结构。一个box是由box header和box里面包含的数据组成的,如下图所示
整个box以box header开头,box header中包含了box的大小(size)和类型(type)等信息。其中,size指明了整个box所占用的大小,包括header部分,如果box很大(例如存放具体视频数据的mdat box),超过了uint32的最大数值,size就被设置为1,并用接下来的8位uint64的largesize来存放大小。box中的字节序为网络字节序,也就是大端字节序(Big-Endian)。
box根据header部分包含的信息的不同可以分为box和full box,如下图所示:
其中box和full box在ISO_IEC_14496-12_2015文档中的定义为
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;
}
}
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;
}
full box中的version是一个用来指定该box的文件的格式的整数
flags 是一个标志图
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
}
1.major_brand: 是一个标识符,如mp42
2.minor_version: 是一个major brand 的次版本标识
3.compatible_brands:是一个list,一直到box的结尾
下面来分析一个示例文件
box的类型为ftyp box大小为24个字节,其中major_brand和minor_version都是mp42。
aligned(8) class MediaDataBox extends Box(‘mdat’)
{
bit(8) data[];
}
aligned(8) class FreeSpaceBox extends Box(free_type)
{
unsigned int(8) data[];
}
aligned(8) class MovieBox extends Box(‘moov’)
{
}
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;
// Unity matrix
template int(32)[9] matrix = { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };
bit(32)[6] pre_defined = 0;
unsigned int(32) next_track_ID;
}
1.version:用来指定该box的版本,取值为0或1,一般为0
2.creation_time:用来指定创建时间,单位为相对于UTC时间1904-01-01零点的秒数
3.modification_time:用来指定最后修改时间
4.timescale:用来指定文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数
5.duration:用来指定该track的时间长度,用duration和time scale值可以计算track时长,比如audio track的time scale = 8000, duration = 560128,时长为70.016,video track的time scale = 600, duration = 42000,时长为70
6.rate:用来指定推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,即[16.16] 格式,该值为1.0(0x00010000)表示正常前向播放
7.volume:用来指定推荐的音量,与rate类似,[8.8] 格式,1.0(0x0100)表示最大音量
8.matrix:用来指定视频变换矩阵
9.next_track_ID:用来指定下一个track使用的id号
下面来分析一个示例文件
aligned(8) class TrackBox extends 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;
// unity matrix
template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };
unsigned int(32) width;
unsigned int(32) height;
}
1.creation_time :指定创建时间(相对于UTC时间1904-01-01零点的秒数)
2.modification_time:指定修改时间
3.track_ID: 指定track的id号,不能重复且不能为0
4.reserved: 保留位
5.duration: 指定track的时长
6.reserved: 保留位
7.layer:指定视频层,默认为0,值小的在上层
8.alternate_group:指定rack分组信息,默认为0表示该track未与其他track有群组关系
9.volume: 指定[8.8] 格式的音量信息,如果为音频track,1.0(0x0100)表示最大音量;否则为0
10.reserved: 保留位
11.matrix:指定视频变换矩阵
12.width:宽,为 [16.16] 格式值,与sample描述中的实际画面大小比值,用于播放时的展示宽高
13.height:高
aligned(8) class MediaBox extends Box(‘mdia’)
{
}
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;
}
1.creation time:创建时间(相对于UTC时间1904-01-01零点的秒数)
2.modification time:修改时间
3.time scale:同前表
4.duration: track的时长
5.language:媒体语言码。最高位为0,后面15位为3个字符(见ISO 639-2/T标准中定义)
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;
}
1.handler_type: 在media box中,该值为4个字符:
“vide”— video track
“soun”— audio track
“hint”— hint track
2.name :human‐readable name for the track type
aligned(8) class MediaInformationBox extends Box(‘minf’)
{
}
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};
}
1.graphicsmode: 视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成
2.opcolor:{red,green,blue}
aligned(8) class SoundMediaHeaderBox extends FullBox(‘smhd’, version = 0, 0)
{
template int(16) balance = 0;
const unsigned int(16) reserved = 0;
}
1.balance:立体声平衡,[8.8] 格式值,一般为0,-1.0表示全部左声道,1.0表示全部右声道
aligned(8) class DataInformationBox extends Box(‘dinf’)
{
}
aligned(8) class DataEntryUrlBox (bit(24) flags) extends FullBox(‘url ’, version = 0, flags)
{
string location;
}
aligned(8) class DataEntryUrnBox (bit(24) flags) extends FullBox(‘urn ’, version = 0, flags)
{
string name;
string location;
}
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;
}
}
1.entry_version: 用来指明当前 entry 的格式
2.entry_flags: 其值不是固定的,但是有一个特殊的值, 0x000001 用来表示当前 media 的数据和 moov 包含的数据一致
“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”包含了编码的信息,其存储的信息随媒体类型不同而不同。
stbl box在标准文档中的定义如下
aligned(8) class SampleTableBox extends Box(‘stbl’)
{
}
aligned(8) abstract class SampleEntry (unsigned int(32) format) extends Box(format)
{
const unsigned int(8)[6] reserved = 0;
unsigned int(16) data_reference_index;
}
aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type) extends FullBox('stsd', version, 0)
{
int i ;
unsigned int(32) entry_count;
for (i = 1 ; i <= entry_count ; i++)
{
SampleEntry(); // an instance of a class derived from SampleEntry
}
}
视频track的stsd
[stsd] size=12+149
entry-count = 1
[avc1] size=8+137
data_reference_index = 1
width = 720
height = 576
compressor =
[avcC] size=8+51
Configuration Version = 1
Profile = High
Profile Compatibility = 0
Level = 40
NALU Length Size = 4
Sequence Parameter = [67 64 00 28 ac d1 00 b4 12 6c 08 40 00 00 03 00 40 00 00 0c b8 08 00 16 e3 40 00 5b 8d e4 93 00 f8 c1 88 90]
Picture Parameter = [68 eb ef 2c]
音频track的stsd
[stsd] size=12+79
entry-count = 1
[mp4a] size=8+67
data_reference_index = 1
channel_count = 2
sample_size = 16
sample_rate = 48000
[esds] size=12+27
[ESDescriptor] size=2+25
es_id = 0
stream_priority = 31
[DecoderConfig] size=2+17
stream_type = 5
object_type = 64
up_stream = 0
buffer_size = 531
max_bitrate = 129336
avg_bitrate = 125368
DecoderSpecificInfo = 11 90
[Descriptor:06] size=2+1
aligned(8) class CompositionOffsetBox extends FullBox(‘ctts’, version, 0)
{
unsigned int(32) entry_count;
int i;
if (version==0)
{
for (i=0; i < entry_count; i++)
{
unsigned int(32) sample_count;
unsigned int(32) sample_offset;
}
}
else if (version == 1)
{
for (i=0; i < entry_count; i++)
{
unsigned int(32) sample_count;
signed int(32) sample_offset;
}
}
}
aligned(8) class TimeToSampleBox extends FullBox(’stts’, version = 0, 0)
{
unsigned int(32) entry_count;
int i;
for (i=0; i < entry_count; i++)
{
unsigned int(32) sample_count;
unsigned int(32) sample_delta;
}
}
aligned(8) class SampleSizeBox extends FullBox(‘stsz’, version = 0, 0)
{
unsigned int(32) sample_size;
unsigned int(32) sample_count;
if (sample_size==0)
{
for (i=1; i <= sample_count; i++)
{
unsigned int(32) entry_size;
}
}
}
aligned(8) class SampleToChunkBox extends FullBox(‘stsc’, version = 0, 0)
{
unsigned int(32) entry_count;
for (i=1; i <= entry_count; i++)
{
unsigned int(32) first_chunk;
unsigned int(32) samples_per_chunk;
unsigned int(32) sample_description_index;
}
}
aligned(8) class SyncSampleBox extends FullBox(‘stss’, version = 0, 0)
{
unsigned int(32) entry_count;
int i;
for (i=0; i < entry_count; i++)
{
unsigned int(32) sample_number;
}
}
aligned(8) class ChunkOffsetBox extends FullBox(‘stco’, version = 0, 0)
{
unsigned int(32) entry_count;
for (i=1; i <= entry_count; i++)
{
unsigned int(32) chunk_offset;
}
}
aligned(8) class ChunkLargeOffsetBox extends FullBox(‘co64’, version = 0, 0)
{
unsigned int(32) entry_count;
for (i=1; i <= entry_count; i++)
{
unsigned int(64) chunk_offset;
}
}
stsc中找到该帧在哪个chunk中-->stco找到每个chunk的offset-->stsz计算出该帧所在的chunk前面所有帧大小,进而计算出该帧在文件中的offset