封装格式 -- MP4

本文详细介绍了MP4的格式,包括其发展历史、在线分析工具和MPEG-4协议。重点解析了MP4文件中的Box结构,如STBL、Stsd、Stts、Ctts、Stss等,以及如何计算关键参数,如trak box类型、播放时长、分辨率、帧率、音频采样率等。此外,还提供了如何识别MP4文件、Box类型和codec type的方法。


前言

MP4,又称为MPEG-4 Part 14,是一种多媒体容器格式(container),扩展名为.mp4

发展历史

  • 2001年,apple的QuickTime格式,.qt和.mov的后缀名。
  • 2001年,MPEG-4 Part1,把基于QuickTime的box布局的容器格式添加到了MPEG-4标准。
  • 2004年,MPEG-4标准文档把编码和容器格式分开为两部分进行说明。
    MPEG-4 Part12:定义了容器格式通用的box结构,即ISO基本媒体文件格式(ISO base media file format, ISOBMFF)。
    MPEG-4 Part14:基于Part12进行了细化,是对Part-12的一种实现,定义了用于存储MPEG-4内容的容器格式,即.mp4格式。
    两者间的关系:
    MPEG-4 part12和part14关系

MP4在线分析工具

  1. mp4parser
    界面如下:
    mp4parser界面
  2. onlinemp4parser
    界面如下:
    onlinemp4parser

MPEG-4

MPEG-4协议中不同部分描述的对象:
MPEG-4


MP4解析

MP4文件中,媒体信息储存在“moov”的box中,一个moov通常包含若干个track信息,每个track都是一个随时间变化的媒体序列,Track里面基本时间单位为sample,sample是按照时间顺序排列,为了方便存取,若干个sample会被组织成一个chunk;而媒体数据存储在mdat box中,即sample是存储在mdat box中。如下图为sample和chunk关系:
在这里插入图片描述

重要概念

Box

MP4文件有若干个box组成。
下图为box的内部结构图:
box内部结构
可以看到:

  1. Box由header和body组成,header大小为8 bytes, 主要有size和type两个栏位。
    size: 是包含box header的整个box的大小。
    !! 两种特殊情况:
    当size为1,则表示box长度超出4字节表示范围,就会用largesize(8 bytes)来表示box大小。
    当size为0,表示该box为文件的最后一个box,这种情况只存在“mdat”类型的box中。
    
    type: 通常是4个ASCII码的字符如“ftpy”,“moov”。这些box type都是mp4 spec已经预定义好的,表示特定的含义。
    如果是“uuid”,表示该box是用户自定义的拓展类型
    
  2. 规范还定义了一种Fullbox,Fullbox是box的扩展。header中加入了1字节的version和3字节的flags字段,整个header大小是12bytes。
    结构如下图:
    在这里插入图片描述
  3. box中可以嵌套box,这种box被称为container box,比如moov box。而ftyp box不是container box,因为没有包含其它box。

Track

相同类型的sample的集合,对于媒体数据来说,一个track表示一个视频序列、一个音频序列或一个字幕序列。

Sample

Mp4中多媒体数据存储的最小单位。
video sample表示一帧或者一组连续的视频帧,audio sample表示一段连续的音频(也可以说是一帧音频帧)。

Chunk

一个track的几个sample组成的单元。


BOX详解

MP4文件由许多box组成,每个box包含不同信息,这些box以树型结构的方式组织。如下图:
在这里插入图片描述
可以看到在根节点之下,主要包含三个重要节点:ftypmoovmdat

  • ftyp:file type box,文件类型。描述本文件遵从的规范版本。
    内部结构为:
    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  
    }
    
  • moov:记录了媒体数据的时空间信息
    是一个container box
    一般情况下包含一个mvhd box和若干个trak box。
  • mdat:具体的媒体数据。
    内部结构为:
    aligned(8) class MediaDataBox extends Box(‘mdat’) {  
        bit(8) data[];//e.g h264 bit stream
    }  
    

下图为具体每个box的描述:
box详细描述
需要说明的是:在 mp4 中默认写入字节序是Big-Endian的。

	大端字节序(big-endian):按照内存地址增长的方向,高位数据存储于低位内存中。
	小端字节序(little-endian):按照内存地址增长的方向,高位数据存储于高位内存中。
	如存储 0x12 34 56 78:
	-------------------------------------------------------
	地址			0x100		0x101		0x102		0x103
	big			12			34			56			78
	little		78			56			34			12

STBL

stbl box是moov中的container box,储存该MP4文件的媒体数据时空间信息,是解析mp4文件的关键
其中包括:

Stsd(sample description)

Full Box,该box记录trak的编解码信息。如编码类型、分辨率等等。
如果是H.264或者H.265的话,该box中还包含关键解码信息(SPS、PPS)的box:AVCC box 或者HVCC box。
stsd

Stts(Decompressing time to sample):

该box记录编解码时间戳与样本的关系。
关键参数:

Entry_count: 时间戳与样本对应关系的组数。
Sample_count: 遵循该对应关系的样本数量。
Sample_delta: 每个样本解码时间戳的差值,解码时间戳初始值是0。

例:
在这里插入图片描述
那说明该片源有1434个DTS相差为1000的sample。(因为entry_count为1,所以该片源只有1434个sample)

Ctts(composition time to sample):

该box记录显示时间戳(PTS)与编码时间戳(DTS)的差值,从而间接的说明显示时间戳与样本的关系,对于H.264 && H.265,只有当存在B帧的时候,DTS和PTS才会有差异,这个box才会存在。
关键参数:

Entry_count: 时间戳与样本对应关系的组数。
Sample_count:遵循该对应关系的样本数量。
Composition_offset:显示时间戳与解码时间戳的差值。

对应关系是:

pts(n) = dts(n) + composition_offset(n)

例:
ctts
那说明该片源有1334组显示时间戳和编码时间戳的差值对应关系,其中:

第一组:第一个sample的pts与dts的差值为1000,即pts(0) = dts(0) + 1000
第二组:第二个sample的pts与dts的差值为4000,即pts(1) = dts(1) + 4000
...

Stss(sync sample):

该box记录每个参考帧的样本序号。在做跳转的操作时,需要从参考帧开始解码,否则会花屏。
关键参数:

Entry_count: 参考帧的数量。
Sync_sample.n:每个参考帧的样本序号。

例:
stss
那说明该片源总共有19个关键帧(entry_count):

第一个关键帧所在sample的序号为1,
第二个关键帧所在sample的序号为32,
...

Stsz(sample size):

该box记录每个样本的大小,单位为byte。
关键参数:

Sample_count: 样本总数量。
Sample_size.n: 每个样本的大小。

例:
在这里插入图片描述
那说明该片源总共有1434个video sample,
其中第一个sample大小为1345bytes,
第二个sample大小为216bytes

Stsc(sample to chunk):

该box记录chunk和sample数量的对应关系。chunk是MP4中为方便跳转而定义的一种数据组织形式
关键参数:

Entry_count:chunk和样本数量的对应关系的组数。
First_chunk:chunk序号,从1开始。
Sample_per_chunk:该对应关系中每个chunk里面样本数量。

例:
stsc
对应关系只说明了对应关系的起始chunk序号,至于还有多少个chunk有这种对应关系,需要看下一个对应关系的frist_chunk,
比如:

Chunks.0这个对应关系:序号1的chunk里面有10个sample。
Chunks.1这个对应关系:从序号为2的chunk开始到序号为143的chunk结束,这之间的每个chunk都有10个sample。
chunks.2这个对应关系:从序号为144开始到最后一个chunk结束,这之间的每个chunk有4个sample。

Stco(chunk offset)

该box记录每个chunk在文件中的偏移位置。
关键参数:

Entry_count: chunk的数量。
Chunk_offset.n:每个chunk的偏移位置。

例:
stco
那说明该片源有144个video chunk,其中:
第一个chunk在文件开始第32个字节,
第二个chunk在文件开始第29235个字节,

注意:结合stsz和stsc两个table,就可以得到每个sample在文件中的偏移量。
比如,需要知道序号为n的sample在文件中的偏移量,那么:

  1. 通过查找stsc box,得知序号为n的sample在哪个chunk,并知道该chunk的起始sample序号start_n
  2. 通过查找stsz知道sample序号为start_n到序号为n的sample这中间的大小,如diff_size.
  3. 通过查找stco知道序号n所在chunk的偏移位置pos。
  4. 最后得出序号n的sample在文件中的偏移是 (pos + diff_size).

关键参数的计算

确定trak box类型

从moov-trak-mdia-hdlr中找到handler_type即可确认。
“soun” --> 该trak box是audio
“vide” --> 该trak box是video
例:
在这里插入图片描述

计算片源播放时长(duration)。

从moov-mvhd中找到time_scale和duration。
T = duration/time_scale。
(time_scale表示1秒内有多少个基本时间单位,而duration表示这个片源有多少个时间单位)
例:
在这里插入图片描述
Mvhd box的timescale = 90000,duration = 5381224,所以时长是 5381224/90000 ≈ 59秒

计算视频或音频的播放时长(duration).

从moov-mvhd-trak-mdia-mdhd中找到time_scale和duration。
T = duration / time_scale
例如:
在这里插入图片描述
则 T(video) = 16659643 / 24000 = 694.1518 s。

计算视频的分辨率(resolution)。

从moov-(vide)trak-tkhd中找到视频的length和width既可。
例:
在这里插入图片描述
则视频分辨率为 1280*720。
对于AVC和HEVC编码格式,可以从
moov-(vide)trak-mdia-minf-stbl-stsd-avc1或者moov-(vide)trak-mdia-minf-stbl-stsd-hvc1中找分辨率。
在这里插入图片描述

计算视频的帧率(framerate)

从moov-(vide)trak-mdia-mdhd中找到timescale
例:
在这里插入图片描述
从moov-(vide)trak-mdia-minf-stbl-stts找到每个sample的时间差值
例:
在这里插入图片描述
则帧率为f = time_scale/ sample_delta = 24000 / 1000 = 24fps。

计算音频采样率(sample rate)。

从moov-(aud)trak-tkhd中找到timescale
例:
在这里插入图片描述
则音频采样率是 44100 Hz

计算video的dts和pts。

  1. 从moov-(vide)trak-mdia-minf-stbl-stts中找到sample_count和sample_delta。
    则dts就是从0开始,步长为sample_dalta的等差数列。
  2. 从moov-(vide)trak-mdia-minf-stbl-ctts中找到entry_count以及sample_count和composition_offset。
    则每一帧的pts就是 [该帧dts] 与 [composition_offset] 的和。
!!注意:
1. 计算出来的dts和pts要除以moov-trak-mdia-mdhd中的timescale,单位才是秒
2. 在多个box中都存在timescale参数,表示意义是相同的(都表示1秒内有多少个基本时间单位),但使用场景不同。
moov-mvhd中的timescale用于与moov-mvhd中的duration配合,计算总播放时长
moov-trak-mdia-mdhd中的timescale用于进行dts和pts的单位转化以及计算视频时长和音频时长。。

例:
Stts:
在这里插入图片描述
Ctts(composition time-to-sample, 创作时间与样本的转化关系):
在这里插入图片描述
那么,

DTS : 0     1000  2000  3000  4000  5000
CTTS: 1000  4000  1000  -999  0     2000
PTS : 1000  5000  3000  2001  4000  7000
type: I1    P1    B1    B2    B3    P2

注意:
如果某个sample_count > 1,说明有连续若干帧的pts与dts的差值是一样的。
如果没有ctts box,那么说明该视频没有B帧,解码顺序和显示顺序是一致的,DTS == PTS。
示例代码:

else if (box_type_equa(uint32_to_str(bh.type, sbuffer), "ctts"))// 存在ctts,说明dts与pts不相等
{
    ...
    uint32_t i = 0, j = 0, num = 0, pos = 16;
    for (i = 0; i < entry_cnt; i++) //entry_cnt从stts中获取
    {
        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++) //sample_cnt从ctts中获取
        {
            //通过dts与offset的和计算得到pts
            PushBack_Array(pts_array, At_Array(dts_array, num++) + sample_offset);
            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));
        }
    ...
}

文件中Sample数量。

  1. 从moov-trak-mdia-minf-stbl-stsz中的sample_count得到。
  2. 从moov-trak-mdia-minf-stbl-stts中将所有entry的sample_count相加得到。
  3. 从moov-trak-mdia-minf-stbl-ctts中将所有entry的sample_count相加得到。
  4. 从moov-trak-mdia-minf-stbl-stsc中将所有entry中每个chunk中的sample数量相加得到。

文件中Chunk数量。

  1. moov-trak-mdia-minf-stbl-stco中的entry_count就是chunk数量。
  2. 从moov-trak-mdia-minf-stbl-stsc中最后一个entry的frist_chunk就是chunk数量。

如何probe一个数据流为MP4类型?

数据流开头第5个字节到第8个字节是’ftyp’、‘moov’、'mdat’和’pnot’之一,则说明该数据流为MP4。

如何判断一个box是普通box还是full box?

规范中有规定具体哪些box是普通box,哪些是full box。
常见普通box有:ftyp,moov,trak
常见full box有:mvhd、tkhd

如何判断该mp4文件中的video/audio codec type?

stsd box的container box的type就是code type 4cc,
比如下面:
在这里插入图片描述
code type 4cc为"avc1",codec type是H264。
又比如:
在这里插入图片描述
code type 4cc为"mp4a",codec type是aac。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值