音视频封装:手撕MP4二进制解析MP4格式

前言

现在的MP4是在互联网中最常见的格式了,MP4的好处例如跨平台,不仅可以在PC播放还可以在Android,IOS等平台中播放,基本上系统自带的播放器都支持该视频格式的视频文件播放。 ❞

因为工作中需要在WEB端支持实时流的播放,提到WEB端现在比 较火的就是VUE了,既然使用VUE了,必然少不了HTML5,那就可以使用HTML5的video标签来做视频的播放,如何渲染就完全交给浏览器了。❞

想使用HTML5的video标签来播放视频,实时的数据需要是FMP4(Fragment MP4),想整明白FMP4的封装流程就需要知道MP4的封装格式和流程。所以本文的主要目的就是了解MP4的组成,以及MP4格式的分析手段。

粉丝福利,博主耗时2个月整理了一份详细的音视频开发学习教程,涵盖了音视频开发FFmmpeg、流媒体客户端、流媒体服务器、WebRTC、Android NDK开发、IOS音视频开发等等全栈技术栈,并提供了配套的免费领取C++音视频学习资料包、技术视频/代码,内容包括(FFmpeg ,WebRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs流媒体服务器,音视频通话等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

MP4分析工具&资料

可视化媒体文件分析工具:

mediainfo:https://mediaarea.net/zh-CN/MediaInfo/Download

mp4box:https://gpac.github.io/mp4box.js/test/filereader.html

Bento4: https://www.bento4.com/downloads/

16进制编辑工具:

windows/linux/:macOS通用工具:Hexinator https://www.hexinator.com/

H264白皮书;

MP4标准;

MP4的组成

1、MP4的结构

mp4由N个box组成,每个Box都长的一样,都由size,type,data三部分组成,如下图 所示:

  • 其中size和type是固定的,各占用4字节。
  • size是整个box的大小,表示从存放size的第一个字节开始到data的最后一个字节的大小。
  • type是用来描述该box的类型;
  • data就是整个box的数据了;

更多情况下box和box是嵌套的,嵌套的box结构如下图所示:

2、MP4的box类型列表

注:参考了众多翻译版的box列表,中和了一下做个记录对国人友好一些。

在互联网的视频点播中,如果希望MP4文件被快速打开,则需要把moov放在mdat的前面,如果放在后面,需要将MP4文件下载完成后才可以播放。

在实际生成MP4文件时,通常无法预先得知moov中存放的一些关键信息,此时如果想把moov放到mdat前面。通常的做法是:在保存MP4结束时把moov移动到mdat前面。

「一级」

「二级」

「三级」

「四级」

「五级」

「六级」

「必选」

「描述」

type

文件类型

pdin

下载进度信息

moov

音视频数据的元数据信息

mvhd

文件头

trak

流的track(轨道)

tkhd

流信息的track头

tref

track参考容器

elst

edit list 容器

mdia

track里的media信息

mdhd

media信息的头

hdlr

media信息的句柄

minf

media信息容器

vmhd

视频media头(只存在于视频的track)

smhd

音频media头(只存在于音频的track)

hmhd

提示media头(只存在于提示的track)

dinf

数据信息容器

dref

数据参考容器,track中media的参考信息

stbl

采样表容器,track中media的参考信息

stsd

采样表叔(codec类型与初始化信息)

stts

采样时间(decoding)

ctts

采样时间(composition)

stsc

chunk采样,数据片段信息

stsz

采样大小

stz2

采样大小详细描述

stco

chunk偏移信息,数据偏移信息

co64

64位chunk偏移信息

stts

同步采样表

stsh

采样同步表

padb

采样padding

stdp

采样退化优先描述

sdtp

独立于可支配采样描述

sdgp

采样组

sgpd

采样组描述

subs

子采样信息

mvex

视频扩展容器

mehd

视频扩展容器头

trex

track扩展信息

ipmc

IPMP控制容器

moof

视频分片

mfhd

视频分片头

traf

track分片

tfhd

track分片头

trun

track分片run信息

sdtp

独立和可支配的采样

sbgp

采样组

subs

子采样信息

mfra

视频分片访问控制信息

tfra

track分片访问控制信息

mfro

拼分片访问控制偏移量

mdat

media数据容器

free

空闲区域

skip

空闲区域

udata

用户数据

cprt

copyright信息

meta

元数据

hdlr

定义元数据句柄

dinf

数据信息容器

dref

元数据的源参考信息

ipmc

IPMP控制容器

iloc

所在位置信息容器

ipro

所在位置信息容器

sinf

计划信息保护容器

frma

原格式容器

imif

IPMP信息容器

schm

计划类型容器

schi

计划信息容器

iinf

容器所在项目信息

xml

XML容器

bxml

binary XML容器

pitm

主要参考容器

fiin

文件发送信息

paen

partition入口

fpar

文件片段容器

fecr

FEC Reservoir

segr

文件发送session组信息

gitn

组ID转名称信息

tsel

track选择信息

meco

追加的metadata信息

mere

metabox关系

meta

metadata(元数据)

styp

分段类型

sidx

分段索引

ssix

子分段索引

prft

生产者参考时间

MP4重要组成格式解析

本次使用的一个MP4文件信息如下:


(base) zhenghui@zh-pc:~/视频$ ffprobe 1.mp4 
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '1.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: mp42mp41isomiso2
    creation_time   : 2023-03-26T04:28:11.000000Z
    encoder         : x264
  Duration: 00:00:07.71, start: 0.000000, bitrate: 839 kb/s
  Stream #0:0(und): Video: h264 (High 4:4:4 Predictive) (avc1 / 0x31637661), yuv444p(tv, bt709), 2560x1440 [SAR 1:1 DAR 16:9], 705 kb/s, 60 fps, 60 tbr, 6k tbn, 120 tbc (default)
    Metadata:
      creation_time   : 2023-03-26T04:28:11.000000Z
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
  Stream #0:1(und): Audio: mp3 (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 129 kb/s (default)
    Metadata:
      creation_time   : 2023-03-26T04:28:11.000000Z
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]
(base) zhenghui@zh-pc:~/视频$ 

使用的二进制工具:

使用Hexinator时,建议拖动窗口,缩小或者放大到每行16个字节的大小,这样看起来比较好看,分析起来也比较好分析

moov解析

moov定义了MP4文件的音视频数据的元数据信息,从上表中可以看到,在整个MP4文件中,仅存在一个moov,而moov中又存在众多嵌套的子box.

例如下图用MP4Box.js的一个工具打开的mp4,呈现

下图是使用MP4 Reader软件打开的

可见,mp4文件的moov中包含mvhd、trak、udata、而trak中又包含tkhd、edts、mdia、udata。是一层套一层的包含着众多box,最终组成一个大的moov box。

使用MP4Box.js分析该Mp4后,点击moov时,可以看到结果如下:

  • type: moov
  • size:4690
  • start:32

如果想弄懂怎么解析出来的,我们需要使用二进制工具打开:

如上图所示,我们再结合下图的结构图。

可知前4个字节是表示的整个box的size,第5-8个字节是type,然后后面就是data了。

看前8个字节:

00 00 12 52     6D 6F 6F 76

0x00001252(hex) = 4690(dec) 0x6D(hex) = 109(dec) = m(根据ascii码表可得知) 0x6F(hex) = 111(dec) = o(根据ascii码表可得知) 0x6F(hex) = 118(dec) = v(根据ascii码表可得知)

所以可分析出来这个box的一些信息:

  • 大小有:4690个字节
  • 类型为:moov

至于为什么start是32,因为这个box是从第32个字节开始的。

看下图更为清晰:

使用MP4 Reader可以显示的更友好:

根据解析的结果可以看到,该box的类型为moov,大小为4690字节。

都说了moov中嵌套了众多box,那么继续往下看:

紧接着的16进制数据是:

0000006C  6D766864

长度:0x0000006C(hex)=108(dec) 类型:0x6D766864(hex)=mvhd(ascii码)

「为什么是嵌套的,而不是并列的:」

因为mvhd还在moov的4690字节范围内,所以是嵌套在moov内的一个box.

所以moov中第一个box是mvhd

使用Hexinator可以用鼠标选中108个字节:

想都不用想108个字节后,肯定又是一个box;

可见108个字节过后的8个16进制数据为:

00 00 0A 74   74 72 61 6B

长度:0x00000A74(hex) = 2676(dec)字节 类型:0x7472616B(hex) = trak

「trck的类型可能为音频或者视频的trck,这里暂不分析,过后会仔细分析。」

可以从00 00 0A 74的第一个字节开始选中2676个字节:

可以看到2676字节过后的8个字节:

00 00 07 2D   74 72 61 6B

box大小:0x00 00 07 2D(1837)字节 box类型:0x74 72 61 6B(trck)

继续选中1837字节:

接下来的8字节为:

00 00 00 3D   75 64 74 61

box大小:0x00 00 00 3D(61) box类型:0x75 64 74 61(udta)

到此,我们已经分析得到了如下结果:moov中嵌套了一个mvhd box,两个trck box(可能是视频也可能是音频,过后再分析,一个udta box

8字节(moov的type和size)+108字节(mvhd)+2676字节(trck)+1837字节(trck)+61字节(udta)

刚好为前面得出的moov的总大小4690字节

mvhd解析

mvhd box是MPEG-4(MP4)容器格式中的一个存放音视频文件头(Movie Header)的部分,mvhd中包含了整个媒体文件的信息,包括媒体文件的持续时间、时间刻度、音频和视频轨道数量,播放速度等信息。

前面我们分析过,mvhd的大小为0x0000006C(108)字节,解析方式如下表所示:

mvhd参数表:

字段

长度(字节)

描述

box大小

4

Movie Header box的字节数

box类型

4

mvhd

版本

1

Movie Header box的版本,取值为0或1;取值决定了生成时间、修订时间、duration按照什么方式进行解析。为0时:按照32位/4字节的方式;为1时:按照64位/8字节的方式;

标志

3

扩展的Movie Header box标致

生成时间

4/8

Movie box的起始时间。基准时间是:1904-1-1 0:00 AM

修订时间

4/8

Movie box的修订时间。基准时间是:1904-1-1 0:00 AM

timescale

4

时间计算单位,这是一个全局的timescale。mp4也支持每个track单独的timescale。换算方式:duration/timescale=xx秒

duration

4/8

记录整个文件的播放时长;在fMP4的时候,该值需为0;

播放速度

4

1.0为正常播放速度(16.16的浮点表示)

播放音量

2

1.0为最大音量(8.8的浮点表示)

保留

10

矩阵结构

36

矩阵定义了该Movie中两个坐标空间的映射关系

预定义

24

下一个Track ID

4

下一个待添加track的ID值。如果为0就不是一个有效的值。

按照mvhd参数表来填充解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x0000006C

108

box类型(4)

0x6D766864

mvhd

版本(1)

0x00

0

标志(3)

0x000000

0

生成时间(4/8)

0xE0457A5B

3762649691

修订时间(4/8)

0xE0457A65

3762649701

timescale(4)

0x00001770

6000

duration(4/8)

0x0000B49D

46237

播放速度(4)

0x00010000

1.0

播放音量(2)

0x0100

1.0

保留(10)

0x00000000000000000000

矩阵结构(36)

0x000100000000000000000000000000000001000000000000000000000000000040000000

预定义(24)

0x000000000000000000000000000000000000000000000000

下一个Track ID(4)

0x00000003

3

说明:例如播放速度为1.0,那么怎么得到的呢,计算方式如下:播放速度是以16.16浮点来表示的,播放速度占4个字节, 0x00010000 转成 二进制为:0000 0000 0000 0001 0000 0000 0000 0000 再转成16.16的定点小数:0000 0000 0000 0001 . 0000 0000 0000 0000 再转成10进制小数:1.0

mvhd box的下一个box是trak box,下面开始解析trak box的解析。

trak解析

每个媒体文件中可包含多个track,每个track都是独立的,各自保存各自的媒体信息,例如音频track,视频track。

从mvhd box后面的8个字节,我们可以分析出来下一个box的大小和类型

00 00 0A 74 74 72 61 6B

长度:0x00000A74(hex) = 2676(dec)字节 类型:0x7472616B(hex) = trak

可知从00 00 0A 74开始的第2676字节全是该track box的数据

如下图所示,所标出的2676个字节就是第一个track的所有数据

继续往下读8字节:

00 00 00 5C 74 6B 68 64

前4个字节:0x0000005C(92) 后4个字节:0x746B6864(tkhd)

说明这又是一个box,类型为tkhd,该box的长度为92字节

下图所标的92字节就是整个tkhd box的数据:

跳过92字节后的8字节为:

00 00 00 24 65 64 74 73

前4个字节:0x00000024(36) 后4个字节:0x65647473(edts)

说明这又是一个box,长度为36,类型为edts的box。如下图,所标的36字节就是edts的全部数据:

继续跳过36字节,读取8字节:

00 00 09 93 6D 64 69 61

前4个字节:0x00000993(2451) 后4个字节:0x6D646961(mdia)

这是一个长度为2451,类型为mdia的box;标出2451后:

跳过2451字节后,我们再读取8字节数据:

00 00 00 59 75 64 74 61

前4个字节:0x00000059(89) 后4个字节:0x75647461(udta)

可见这是一个长度为89的udta box。

跳过89字节后,再读取8字节:

00 00 07 2D 74 72 61 6B

前4个字节:0x0000072D(1837) 后4个字节:0x7472616B(trak)

可知这是遇到的第2个trak box了,所以这里先忽略。所以到udta box就结束了第一个trak box中嵌套的所有三级box。

「总结一下:」

  • 第1个trak box一共2676字节;
  • tkhd占用92字节
  • edts占用36字节
  • mdia占用2451字节
  • udta占用89字节

所以8(trak的size和type)+92(tkhd)+36(edts)+2451(mdia)+89(udta)=2676字节;

接下来我们来分析track中的第一个box :tkhd box

tkhd解析

tkhd是“Track Header”的缩写。

每个track中必须包含tkhd,切只能而且必须包含1个tkhd box;tkhd用来表述这个轨道的一些基本信息参数,例如:

如果是视频的话就会包含宽和高,如果是音频的话就会包含采样率等信息。

「tkhd参数表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

有效的标志如下:
0x0001: track生效,为0时该track不启用;
0x0002: track被用在Movie中;(播放时会用到)
0x0004: track被用在Movie预览中;(预览模式会用到)
0x0008: 表示宽度和高度字段不以像素单位表示
「注意:解析出来该值需要做些转换才可知结果。」

生成时间

4/8

Movie box的起始时间。基准时间是:1904-1-1 0:00 AM

修订时间

4/8

Movie box的修订时间。基准时间是:1904-1-1 0:00 AM

trackID

4

该track的ID,唯一的

保留

4

duration

4/8

trak的duration,在媒体文件的时间戳中,与trak的edts list的时间戳建立关联,然后进行时间戳计算,得到对应track的播放时间坐标。但是在fMP4时,该值需要设置为0

保留

8

layer

2

视频曾,默认为0,值小的在上层

alternate group

2

track分组信息,默认为0,表示该track未与其他track有群组关系

音量

2

播放此track的音量。1.0时为正常音量

保留

2

矩阵结构

36

该矩阵定义了该track中两个坐标空间的映射关系

宽度

4

如果该track是video track,该值表示视频图像的宽度(16.16浮点表示)

高度

4

如果该track是video track,该值表示视频图像的高度(16.16浮点表示)

视频trak->tkhd

前面我们分析过tkhd的长度为92字节

同样为了方便查看,继续标出来92字节:

然后按照字节数填表:

「视频tkhd解析表:」

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x0000005C

92

box类型(4)

0x746B6864

tkhd

版本(1)

0x00

0

标志(3)

0x000007

7

生成时间(4/8)

0xE0457A5B

3762649691

修订时间(4/8)

0xE0457A65

3762649701

trackID(4)

0x00000001

1

保留(4)

0x00000000

duration(4/8)

0x0000B478

46200

保留(8)

0x0000000000000000

layer(2)

0x0000

0

alternate group(2)

0x0000

0

音量(2)

0x0000

0

保留(2)

0x0000

矩阵结构(36)

0x000100000000000000000000000000000001000000000000000000000000000040000000

宽度(4)

0x0A000000

2560.0

高度(4)

0x05A00000

1440.0

重点说明:

「1、为什么标志是7(0x000007),代表是什么意思:」

在回顾下上面表格中介绍的:有效的标志如下:

  • 0x0001: track生效,为0时该track不启用;
  • 0x0002: track被用在Movie中;(播放时会用到)
  • 0x0004: track被用在Movie预览中;(预览模式会用到)
  • 0x0008: 表示宽度和高度字段不以像素单位表示

可以计算一下:0x00 00 07 = 0x0001 | 0x0002 | 0x0004;所以这三种是有效的。

「2、高度和宽度如何计算的:」

高度和宽度是16.16浮点运算的。拿宽度0x0A000000举例:

0x0A000000 (十六进制)= 转成二进制并用点分开16.16 --> 0x0000 1010 0000 0000 . 0000 0000 0000 0000 二进制转10进制:2560.0

宽度和高度都是有效的,可见这是一个视频track。还记得我们这个分析中有2个trck吗?我们猜另一个应该就是音频trck了,再看下音频trck.

音频trak->tkhd

本文所分析的mp4文件的第二个trak位于2676后的box就是第二个trak box

按照解析视频的方法,来解析音频:「音频tkhd解析表:」

「字段(所占字节数)」 「十六进制数据」 「转换后结果」
box大小(4) 0x0000005C 92
box类型(4) 0x746B6864 tkhd
版本(1) 0x00 0
标志(3) 0x000007 7
生成时间(4/8) 0xE0457A5B 3762649691
修订时间(4/8) 0xE0457A65 3762649701
trackID(4) 0x00000002 2
保留(4) 0x00000000
duration(4/8) 0x0000B49D 46237
保留(8) 0x0000000000000000
layer(2) 0x0000 0
alternate group(2) 0x0000 0
音量(2) 0x0100 1.0
保留(2) 0x0000
矩阵结构(36) 0x000100000000000000000000000000000001000000000000000000000000000040000000
宽度(4) 0x00000000 0.0
高度(4) 0x00000000 0.0

可见,与视频tkhd不同的地方有:trackID、duration、音量、矩阵结构、宽度、高度

宽度和高度对于音频来说是无意义的,所以音频中为0.0

mdia解析

mdia box位于trak box内,按照前面所分析的结果来说,mdia在edts之后,所以我们跳过edts box直接到mdia box;

老样子,读取8字节数据:

00 00 09 93 6D 64 69 61

前四个字节:0x00000993(2451) 后四个字节:0x6D646961(mdia)

可见这是一个长度为2451字节的mdia box;

为什么这个box这么大,因为mdia box中包含的box特别多,例如下面这些是必须要包含的:

  • mdhd:media信息头
  • hdlr:media信息的句柄
  • minf:media信息容器

mdia box解析:

如下图所示就是mdia box的开始位置:

取前8字节数据:

00 00 09 93 6D 64 69 61

前四位:0x00000993(2451) 后四位:0x6D646961(mdia)

说明我们找的没错,这个box就是长度为2451字节的mdia box;

接下来可以根据前面的经验继续往下找了:

mdhd:

00 00 00 20 6D 64 68 64

前四位:0x00000020(32) 后四位:0x6D646864(mdhd)

跳过32字节继续:

00 00 00 2D 68 64 6C 72

前四位:0x0000002D(45) 后四位:0x68646C72(hdlr)

跳过45字节继续:

00 00 09 3E 6D 69 6E 66

前四位:0x0000093E(2366) 后四位:0x6D696E66(minf)

跳过2366字节继续就到了udta,所以就上面这些就是mdia box的所有内容了。

8(mdia)+32(mdhd)+45(hdlr)+2366(minf)= 2451字节,计算结果和前面分析的是吻合的,所以是正确的。

mdhd解析

mdhd box是用来描述Media的Header,每个track都有一个mdhd。

「mdhd容器参数表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

生成时间

4

Movie box的起始时间。基准时间是:1904-1-1 0:00 AM

修订时间

4

Movie box的修订时间。基准时间是:1904-1-1 0:00 AM

timescale

4

时间计算单位,这个地方的timescale是每个track单独的timescale

duration

4

该track的duration

语言

2

媒体的语言码

质量

2

媒体的回放质量

上面已经得到了mdhd的长度为32。

所以就这一点:

按照前面的经验,继续填表法:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000020

32

box类型(4)

0x6D646864

mdhd

版本(1)

0x00

0

标志(3)

0x000000

0

生成时间(4/8)

0xE0457A5B

修订时间(4/8)

0xE0457A65

timescale(4)

0x00001770

6000

duration(4)

0x0000B478

46200

语言(2)

0x55C4

21956

质量(2)

0x0000

0

根据这个表,我们可以计算出来整个视频track的时长,duration/timescale就是时长,单位为秒:46200/6000=7.7秒

hdlr解析

hdlr box中存放着该track的媒体类型,如果多个track的话,例如就可以区分出来哪个是音频track,哪个是视频track等。「hdlr容器参数表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

handle的预定义

4

handler的预定义

handle的子类型

4

用来描述media handler或data handler的类型。

如果component type是mhlr,这个字段定义了数据的类型,例如:video是vide类型的track,soun是sound的track类型。如果component type是dhlr,这个字段定义了数据引用的类型,例如:alis是文件的别名。

保留

12

保留字段,默认为0

component name

可变

这个component的名字,就是生成此media的media handler。该字段的长度可以为0。

前面我们分析过,hdlr占用45字节的长度:

填表:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x0000002D

45

box类型(4)

0x68646C72

hdlr

版本(1)

0x00

0

标志(3)

0x000000

0

handle的预定义(4)

0x00000000

0

handle的子类型(4)

0x76696465

vide

保留(12)

0x000000000000000000000000

0

component name(可变)

0x566964656F48616E646C657200

VideoHandler'\0'

可以看到这是一个video的track。对应的组件名称为:VideoHandler,并且以0x00结尾。

minf解析

前面我们分析过minf的长度有2366字节。在minf box中包含着很多与音视频采样等信息相关的重要容器。

例如重要的容器:

  • vmhd::视频meida头(Video Media Information Header)
  • smhd:音频media头(Sound Media Information Header)
  • dinf:数据信息参考容器(Data Information)
  • stbl:采样表容器(Sample Table)

下面逐步来解析完这些重要的容器。

vmhd解析

vmhd box中描述了一些与视频track编码无关的通用信息。

「vmhd参数表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

图形模式

2

传输模式,传输模式指定的布尔值

Opcolor

6

颜色值,RGB颜色值

读取8字节:

00 00 00 14 76 6D 68 64

前四个字节:0x00000014(20) 后四个字节:0x766D6864(vmhd)

下图所标就是vmhd整个box的数据了:

填表解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000014

20

box类型(4)

0x766D6864

vmhd

版本(1)

0x00

0

标志(3)

0x000001

1

图形模式(2)

0x0000

0

Opcolor(6)

0x000000000000

0

smhd解析

smhd中描述了音频的Header的相关内容。

「smhd参数表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

均衡

2

音频的均衡用来控制计算的两个扬声器混合效果,一般是0

保留

2

保留字段,默认为0

smhd在第二个track中,所以需要你去另外的track中去找。

例如我的:

取出前8个字节分析:

00 00 00 10 73 6D 68 64

前4个字节:0x00000010(16) 后4个字节:0x736D6864(smhd)

所标处的16个字节就是smhd的数据

继续填表:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000010

16

box类型(4)

0x736D6864

smhd

版本(1)

0x00

0

标志(3)

0x000000

0

均衡(2)

0x0000

0

保留(2)

0x0000

0

好像vmhd和smhd中的内容也没有太多值得参考的价值。

dinf解析

dinf也是必须要包含的一个box,dinf中包含着一个dref box。dinf存放着音视频media的参考信息。

00 00 00 24 64 69 6E

前4个字节:0x00000024(36) 后4个字节:0x64696E66(dinf)

可知dinf box一共占了36个字节。

继续读取8字节:

00 00 00 1C 64 72 65 66

前4个字节:0x0000001C(28) 后4个字节:0x64726566(dref)

dref占用了28字节。

dref解析

「dref参考表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

条目数目

4

data reference(数据参考)的数目

数据参考

---

每个data reference就像容器的格式一样,包含下面的数据成员

尺寸

4

这个box字节数

类型

4

如url/urn等类型

版本

1

这个data reference的版本

数据

可变

data reference信息

dref的内容如下:

继续填表解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x0000001C

28

box类型(4)

0x64726566

dref

版本(1)

0x00

0

标志(3)

0x000000

0

条目数目(4)

0x00000001

1

数据参考(---)

尺寸(4)

0x0000000C

12

类型(4)

0x75726C20

url

版本(1)

0x00

0

数据(可变)

0x000001

1

stbl解析

stbl称为采样列表Box(Sample Table Box)。

在这里需要首先了解下标准中的一些定义:

  • track(轨道):表示一些sample的集合,对于媒体文件数据来说,用来表示一个视频或者音频数据序列。
  • chunk(块):一个track中几个连续的sample组成的集合成为chunk(块);同一个chunk内的sample是连续的;在fMP4格式中,则使用run来表达类似的意思。
  • sample(采样):与一个时间戳相关的所有数据。对于video的话,一般对应视频中的一个帧;对于audio的话,一般对应音频中的一段压缩的音频数据。

一般同一个track中不可能有多个sample具有相同的时间戳。

简洁一些:

  • sample是一个媒体流的基本单元,例如视频流的一个sample代表实际的NAL数据。
  • chunk是数据存储的基本单位,chunk是一系列sample数据的集合,一个chunk中包含多个sample。

先看看下都包含了哪些内容:

读取8字节:

00 00 08 FE 73 74 62 6C

前4个字节:0x000008FE(2302) 后4个字节:0x7374626C(stbl) 说明我们读取到了stbl box,大小为2302个字节。

显然整个stbl box有2302个字节,因为现在分析的这些都在stbl box内,所以继续读取8个字节:

00 00 00 D2 73 74 73 64

前4个字节:0x000000D2(210) 后4个字节:0x73747364(stsd) 说明我们读取到了stsd box,大小为210个字节。

跳过210字节继续读取8字节:

00 00 00 18 73 74 74 73

前4个字节:0x00000018(24) 后4个字节:0x73747473(stts) 说明我们读取到了stts box,大小为24个字节。

跳过24字节继续读取8字节:

00 00 00 14 73 74 73 73

前4个字节:0x00000014(20) 后4个字节:0x73747373(stss) 说明我们读取到了stss box,大小为20个字节。

跳过20字节继续读取8字节:

00 00 00 28 73 74 73 63

前4个字节:0x00000028(40) 后4个字节:0x773747363(stsc) 说明我们读取到了stsc box,大小为40个字节。

跳过40字节继续读取8字节:

00 00 07 4C 73 74 73 7A

前4个字节:0x0000074C(1868) 后4个字节:0x7374737A(stsz) 说明我们读取到了stsz box,大小为1868个字节。

跳过1868字节继续读取8字节:

00 00 00 84 73 74 63 6F

前4个字节:0x00000084(132) 后4个字节:0x7374636F(stco) 说明我们读取到了stco box,大小为132个字节。

跳过32字节继续读取8字节:

00 00 00 59 75 64 74 61

前4个字节:0x00000059(59) 后4个字节:0x75647461(udata)

发现我们去读的字节数已经超出了stbl的总大小,所以这个udata是外层的box,并不是stbl box内所包含的,所以忽略。

12.1. stsd解析

全称:Sample description

stbl box内,所以继续读取8个字节:

00 00 00 D2 73 74 73 64

前4个字节:0x000000D2(210) 后4个字节:0x73747364(stsd) 说明我们读取到了stsd box,大小为210个字节。

「stsd参考表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

stsd

版本

1

该box的版本

标志

3

该box的标志

条目数目

4

data reference(数据参考)的数目

数据参考表

---

每个data reference就像容器的格式一样,包含下面的数据成员

数据参考表box大小

4

数据存储格式

4

例如我的就是avc1

保留

6

数据参考索引

2

版本

2

修订级别

2

厂商

4

用来描述生成压缩数据的压缩程序的开发人员

时间质量

4

描述时间压缩程度

空间质量

4

描述空间压缩程度

宽度

2

描述图像的宽度

高度

2

描述图像的高度

水平分辨率

4

描述图像的水平分辨率

垂直分辨率

4

描述体香的垂直分辨率

数据大小

4

Frame count

2

描述每个样本中存储了多少帧压缩数据

压缩器名字大小

1

压缩器名字大小

填充

31

填充0x00

压缩器名字(Compressor name)

4

描述图像的压缩格式的名字,例如"jpeg"

深度

2

描述压缩图像的像素深度

颜色表ID

2

描述使用的颜色表

我们读取到了stsd box,大小为210个字节。

读取到0x61766331时,发现又是一个box,类型为avc1.

继续填表解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x000000D2

210

box类型(4)

0x73747364

stsd

版本(1)

0x00

0

标志(3)

0x000000

0

条目数目(4)

0x00000001

1

数据参考表(---)

数据参考表大小(4)

0x000000C2

194

数据存储格式 (4)

0x61766331

avc1

保留(6)

0x000000000000

0

数据参考索引(2)

0x0001

1

版本(2)

0x0000

修订级别(2)

0x0000

厂商(4)

0x00000000

时间质量(4)

0x00000000

空间质量(4)

0x00000000

宽度(2)

0x0A00

2560

高度(2)

0x05A0

1440

水平分辨率(4)

0x00480000

垂直分辨率(4)

0x00480000

数据大小(4)

0x00000000

Frame count(2)

0x0001

1

压缩器名字大小(1)

0x00

0

填充(31)

0x00000000000000000000000000000000000000000000000000000000000000

0

深度(2)

0x0018

24

颜色表ID(2)

0xFFFF

65535

avcC:

「avcC参考表:」

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

stsd

版本

1

版本号

AVC解码器配置记录

----

配置AVC解码器的记录

avcC填表解析:继续填表解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000035

53

box类型(4)

0x61766343

avcC

版本(1)

0x01

1

AVC解码器配置记录

0xF40033FFE1001
D67F400339196
802800B5B016A0
202028000003000
800000303C478C19
501000568CE02F192

SPS,PPS

AVC解码器参数解析:

1、由十六进制转成二进制:

1111010000000000001100111111111111100001000000000001110101100111111101000000000000110011100100011001011010000000001010000000000010110101101100000001011010100000001000000010000000101000000000000000000000000011000000000000100000000000000000000000001100000011110001000111100011000001100101010000000100000000000001010110100011001110000000101111000110010010

解析SPS和PPS需要运用H264的知识点啦:h264的sps,pps解析可以尝试去了解H.264白皮书《Rec. ITU-T H.264 (03/2010)》的44页这本标准

「sps数据的定义与解析:」

 

此处稍复杂,暂时放一下,之后单独来写。

12.2. stts解析

stts全程:Time-to-Sample

存储sample与时间的映射表。

跳过210字节继续读取8字节:

00 00 00 18 73 74 74 73

前4个字节:0x00000018(24) 后4个字节:0x73747473(stts) 说明我们读取到了stts box,大小为24个字节。

stts参数表:

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

映射表数量

4

映射表

---

映射表的格式:

Field

Bytes

Sample count

4

Sample Duration

4

stts解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000018

24

box类型(4)

0x73747473

stts

版本(1)

0x00

0

标志(3)

0x000000

0

映射表数量(4)

0x00000001

1个映射表

映射表(---)

0x000001CE00000064

映射表的格式:

Sample count(4 bytes)

Sample Duration(4 bytes)

0x000001CE(462)

0x00000064(100)

12.3. stss解析

stss全称:Sync sample

同步sample表,存放关键帧列表

跳过24字节继续读取8字节:

00 00 00 14 73 74 73 73

前4个字节:0x00000014(20) 后4个字节:0x73747373(stss) 说明我们读取到了stss box,大小为20个字节。

stss参数表:

「字段」

「长度(字节)」

「描述」

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

同步表数量

4

同步表

---

同步表结构:

「Entry Number」

Sample

「Number」

Sample1

「Number」

Sample2

「Number」

Sample3

「Number」

Sample4

「Number」

Sample5

「Number」

Sample6

stss解析:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000014

20

box类型(4)

0x73747373

stss

版本(1)

0x00

0

标志(3)

0x000000

0

同步表数量(4)

0x00000001

1个同步表,说明只有1个关键帧

同步表(---)

0x00000001

同步表的格式:

「Entry Number」

Sample

0x00000001(1)

Sample1

12.4. stsc解析

stsc全称:Sample to chunk

存储sample 与 chunk之间的映射关系。

跳过20字节继续读取8字节:

00 00 00 28 73 74 73 63

前4个字节:0x00000028(40) 后4个字节:0x773747363(stsc) 说明我们读取到了stsc box,大小为40个字节。

stsc参数表:

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

sample-to-chunk表数量

4

sample-to-chunk表

---

sample-to-chunk表结构:

字段

长度(字节)

描述

First chunk

4

第一个chunk

Samples per chunk

4

每块样本数

Sample description ID

4

样本描述ID

stsc解析表:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000028

40

box类型(4)

0x73747363

stsc

版本(1)

0x00

0

标志(3)

0x000000

0

sample-to-chunk表数量(4)

0x00000002

2个

sample-to-chunk表(---)

0x00000001

sample-to-chunk表:

First chunk

Samples per chunk

Sample description ID

0x00000001(1)

0x00000010(16)

0x00000001(1)

0x0000001D(29)

0x0000000E(14)

0x00000001(1)

12.5. stsz解析

stsz全称:Sample size

stsz box中存储了每个sample的大小。

stsz参数表:

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

样本个数

4

样本个数

sample-size表数量

4

sample-size表

---

sample-size表结构:

「Sample size」

「Sample」

Size

Sample 1

Size

Sample 2

Size

Sample 3

跳过40字节继续读取8字节:

00 00 07 4C 73 74 73 7A

前4个字节:0x0000074C(1868) 后4个字节:0x7374737A(stsz) 说明我们读取到了stsz box,大小为1868个字节。

stsz解析表:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x0000074C

1868

box类型(4)

0x7374737A

stsz

版本(1)

0x00

0

标志(3)

0x000000

0

样本个数(4)

0x00000000

0

sample-size表数量(4)

0x000001CE

462个

sample-size表(---)

0x00000001

如何算出sample-size表的结束字符,或者说怎么算出sample-size表所占的字符大小:sample-size表数量=462

462*4=1848

我们从0x000001CE往后标1848个字节就是总体的sample-size表:

可以看出,标出来的结尾就到了stco了,说明我们计算的是正确的。

sample-size表解析:

「Sample size」

0x00094566(607590)

0x00000091(145)

0x00000011(17)

0x00000011(17)

....

0x00000011(17)

12.6. stco解析

stco全称:Chunk offset atom

stco box中通过offset定义了每个chunk到文件开头的位置。

stco参数表:

字段

长度(字节)

描述

box大小

4

该box的大小(字节)

box类型

4

该box的类型

版本

1

该box的版本

标志

3

该box的标志

Chunk offset表数量

4

Chunk offset表

---

Chunk offset表结构:

「Chunk offset」

「Chunk」

「offset」

「Chunk」 1

「offset」

「Chunk」 2

「offset」

「Chunk」 3

跳过1868字节继续读取8字节:

00 00 00 84 73 74 63 6F

前4个字节:0x00000084(132) 后4个字节:0x7374636F(stco) 说明我们读取到了stco box,大小为132个字节。

stco解析表:

「字段(所占字节数)」

「十六进制数据」

「转换后结果」

box大小(4)

0x00000084

132

box类型(4)

0x7374636F

stco

版本(1)

0x00

0

标志(3)

0x000000

0

Chunk offset表数量(4)

0x0000001D

29个

Chunk offset表(---)

0x00000001

我们计算下Chunk offset列表的大小:29*4=116字节,

Chunk offset表解析:

「Chunk offset」

「0x0000127A」

「0x0009798C」

「0x000989E2」

...

...

..

粉丝福利,博主耗时2个月整理了一份详细的音视频开发学习教程,涵盖了音视频开发FFmmpeg、流媒体客户端、流媒体服务器、WebRTC、Android NDK开发、IOS音视频开发等等全栈技术栈,并提供了配套的免费领取C++音视频学习资料包、技术视频/代码,内容包括(FFmpeg ,WebRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs流媒体服务器,音视频通话等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

猜你喜欢

转载自blog.csdn.net/m0_73443478/article/details/143209238