音视频封装:MPTG2-TS 媒体封装实例解析和说明

2020-11-12 11:52:50 浏览数 (1)

问题背景:

首先明确这两种格式都是音视频的封装格式,是由国际电信联盟出的具体标准,具体文档见ISO/IEC-13818。由于在安防和广电领域的使用,还有苹果在自家全系列产品的主推,导致目前还有一定的生存空间。在安防领域主要是因为GB28181-11标准规定了码流的封装格式是RTP PS流。这样导致整个安防凡是和国标相关的码流封装格式都是PS流,目前依然是安防码流的主流封装标准,一时半会还看不到有任何问题。TS流主要是广电领域使用,我们看到的电视节目就是TS流封装,然后再在机顶盒解码解封装和播放。苹果HLS协议的推出,在整个苹果家族产品里面支持都非常友好,安卓阵营的主流浏览器也支持HLS协议。其中HLS协议的码流封装格式也是TS。

平时在项目开发过程中,针对一些播放黑屏、卡顿、花屏、延时大问题,我们都需要分析码流的音视频信息,一般我们需要关注到编码层,所以封装层是绕不过去的。本文档就是要总结下这两种码流的封装格式。

学习的重点是能够了解封装字段以及含义,能够根据工具等解析文件。实际编码中,也需要能够直接用C语言去分析和封装这种码流,提取关键信息。当然后面实际开发代码,我们往往会采用一些主流的基础库,比如FFmpeg支持的就比较全,所以当我们懂这些码流封装细节时,对调用接口和分析代码都是很有帮助的。

TS解析部分:

基础概念:

TS和PS部分都有一定的应用场景,其中TS最经典的应用场景就是平时生活中的数字高清电视。TS码流如果发送过来后,就会由我们进行解封装和解码,然后由屏幕渲染播放。这里就有一个问题,我们看电视有很多频道,比如CCTV、地方卫视等。而同一个频道还有很多节目,就像CCTV频道下面,在同一时刻就有CCTV1-CCTV14这些节目,那么这些频道、节目、音视频码流又是如何在TS里面进行区分呢?又是如何支持随机播放呢?又是怎么完成音画同步呢?仔细想想这就是TS复杂的原因,我们在互联网中可能借用了这种封装,只是传了一路视频和音频,所以很多字段我们并不理解,原因就是因为我们只使用了比较简单的一种场景。PS封装格式复杂的原因同理。

TS流的单位是包,每个包的大小是188字节。我们可以称为ts packet.这个ts packet有可能是音频数据、视频数据,同时为了解决频道和节目的表示问题,还引入了两种表PAT和PMT,其中PAT表格里面包含所有的PMT信息,一个PMT对应一个频道,由于我们频道很多,所以有可能我们不止一张PMT表,所有的频道描述,又都在PAT里面。

所以我们发送的TS数据包格式顺序为:

PAT PMT PMT PMT DATA DATA ......PAT PMT PMT PMT DATA DATA......

之所以PAT和PMT间歇性发送,是因为为了独立解码,因为你不知道观众什么时候要看电视,我们保证的是观众在任何时刻打开电视都可以观看节目,而播放器只有有了PAT和PMT才能独立的解码播放,所以我们要在源源不断的码流中隔一段时间就传送一次PAT和PMT。

ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。

PES流(Packet Elemental Stream):把基本流ES分割成段,并加上相应头文件打包成形的打包基本码流。

PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。

TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)

格式部分:

无论是PS还是TS都是分层结构,其两者共同点是PES是其封装单元,PES又是以ES为单元的第一次封装,所以研究这两种封装格式,抓住ES和PES,就抓住了关键。其中由于都基于PES完成第二次封装,所以也是两种封装格式能够自由转换的关键。

备注:

1. ES就是基本流,也就是类似H264编码的原始数据NALU或者音频AAC等编码原始数据。

TS包格式:

2. TS包一般都是188字节,但是也不是绝对的,有些可以再传输包后面再增加16字节形成204字节的前向纠错码等。

3. MPEG-2规定一个PES包必须由整数个TS包传输,如果承载一个PES包的最后一个TS包没有被装满,则用填充字节进行填充。

TS包都是分为包头 ts header和ts payload有效载荷部分,其中有效负载可以填入音频、视频、和两种表类似的其它形式数据。

第一个TS包:ts header PES header 部分ES

第二个TS包:ts header 部分ES

..........

最后一个包:ts header 填充字节 部分ES

对于PAT和PMT封装的TS包,基本一个TS包188字节就够用了,但是对于视频数据,里面有I B P 帧,这些帧一个完整大小都在几千字节。所以一个帧封装成为一个PES包,由于一个TS包只有188字节,则需要进行切分,所以一个完整帧生成的TS包就是上面三种包格式,这说的数据部分,我们实际最关心的也是这部分实际音视频内容。

实例分析:

下面我们将从一个实例,讲解一个完整TS文件的TS包示例,其中会讲解PAT包 、PMT包 、音频包和视频包。

l PAT部分:

概况:

传输流包的长度必须是188字节,sync_byte必须是0x47,任何有用的数据都不会是0xFF.

PAT是Program Association Table的简称,也称为“节目关联表”。PAT定义了当前TS流中所有的节目,其中PID恒为0x00,这个是关键。唯一分配给了PAT,也是分析PMT,音视频包的开始,它是PSI信息的根节点,要查找节目必须从PAT开始。

PAT表描述了当前TS流中包含什么样的PID,当前流有多少类型的PMT表,当前流的NID表格,每个PMT对应的频道号等信息。

我们用Elecard Analyze工具先分析一下TS文件的概况:

1. 注意的这个工具分析还是要看偏移量是188字节才能算作完整一包内容,不是一行就是完整一包;

2. 其次这个工具分析出来的第一包并不是PAT,而是第二包,第一包为TS包头,所以我们还是要关注pid为0x000的包,这是分析PAT的起始点;

3. 我们用这个工具基本就可以分析出来是否含有音频,明显这个含有AAC音频,因为有AAC音频的包头。是不是含有视频,打开二进制还可以看到里面SPS PPS I 帧等进一步的信息和相应字段,所以我们又可以推到出分辨率,帧率,视频编码格式框架等信息。

我们用一个TS分析工具分析一个TS文件的PAT看下具体字段:

第一部分TS包头:

十六进制: 47 40 00 11

二进制: 0100 01111 0100 0000 0000 0000 00010001

划分: 0100 01111 0 1 0 0000000000000 00 01 0001

任何一个TS包,无论包承载内容是什么,都有一个四字节的包头,其中各个字段含义如下:

字段

字段含义

占位

属于第几字节

当前数据和分析

sync_byte

同步字节

8

第一字节

0x47:188的大小和0x47的分割符基本能划分出任何一个ts包。

transport_error_indircator

传输错误标识

1

第二字节

0:

payload_unit_start_indircation

负载单元开始标识

1

第二字节

1:该字段用来表示TS包的有效净荷带有PES包或者PSI数据的情况。 当TS包带有PES包数据时,该字段有以下特点:置为1,表示TS包的有效载荷以PES包的第一字节开始;置为0,表示TS包的开始不是PES包切分的第一个TS包。 当TS包带有PSI数据时,该字段的特点如下:置为1,表示TS包带有PSI的第一字节,即第一字节带有指针pointer_filed;置为0,表示TS包不带有PSI部分的第一字节,即有效载荷指针没有pointer_filed.对于空包,则应该置为0.

transport_priority

传输优先级

1

第二字节

0:

pid

PID

13

第二字节后五位第三字节

即为0,表示TS头后面就是PAT表自己

transport_scrambling_control

传输扰乱控制

2

第四字节

00:

adaptation_filed_control

自适应区域控制

2

第四字节

00:是保留值01:负载中只有有效载荷10:负载中只有自适应字段;11:先有自适应字段,后有有效载荷;

continuity_counter

连续计数器

4

第四字节

当同一类型的有效载荷用ts包不够传输时有用。会从0-15依次循环。一般要按照PAT PMT 视频 音频进行分类型计数。特别是在将PES分包TS时非常重要。

其中我们也可以看到和实际工具分析出来的包头信息一致:

第二部分 TS包调整字节:

十六进制:00

在除去四字节后第一个字节是调整字节,所以实际数据应该除去第一字节后的数据;

第三部分 TS包有效载荷即PSI表PAT的数据:

十六进制:00B0 0D00 01 C1 00 00 00 01 F0 00 2A B1 04 B2

二进制:

00 : 0000 0000

B0 0D: 1011 0000 0000 1100

00 01: 0000 0000 0000 0001

C1: 1100 0001

00: 0000 0000

00: 0000 0000

00 01: 0000 0001

F0 00: 1111 0000 0000 00000

2A B1 04 B2:CRC校验,用来检验数据的正确性循环冗余校验码

头部数据中PID是最重要的字段,想要码流还是表,直接根据头里面的PID进行过滤即可:

表类型

PID 值

PAT

0x0000

CAT

0x0001

TSDT

0x0002

EIT,ST

0x0012

RST,ST

0x0013

字段

字段含义

占位

属于第几字节

当前数据和分析

table_id

表ID

8

第一字节

0x00:标识一个TS PSI分段的内容是节目关联分段,条件访问分段还是节目映射分段。对于PAT置为0x00

section_synatx_indication

段同步标识

1

第二字节

1:

“0”

0值

1

第二字节

0:

reserved

保留值

2

第二字节

11:

section_length

分段长度

12

第三字节

0000 0000 1100:0x0D:十三字节。分段长度字段,这个值是包括该字段在内到CRC_32校验字段的字节数,其值不超过1021

transport_stream_id

传输流标识

16

第四、五字节

0x00 0x01:该字节充当标签,标识网络内此传输流有别于任何其他路复用流。其值由用户规定。

reserved

保留值

2

第六字节

11:

version_number

PAT的版本号

5

第六字节

00 000:PAT的版本号,如果PAT有变,则版本号加1

current_next_indicator

标识

1

第六字节

1:置为0时,表明该传送的段不能使用,下一个表分段才能有效,一般默认值用1

section_number

分段号

8

第七字节

0x00表明该TS包属于PAT的第几个分段,分段号从0开始。因为PAT可以描述很多PMT信息,所以长度可能比较长。

last_section_number

最后一个分段号

8

第八字节

0x00表明该PAT的最大分段数目,一般情况都是一个PAT表由一个TS包传输。

program_number(循环开始,N从0开始)

节目的编号

16

N 2字节

0x00 0x01

reserved

保留值

3

N 3字节

111:

network_PID

NIT表的PID值

13

N 3 N 4字节

节目号为0则用此值;

program_map_PID

PMT的ID值

13

N 3 N 4字节

1 0000 0000 0000:0x10 0x004096 其它时,则填充此值;

crc_32

CRC校验

32

最后载荷四个字节

CRC校验

总结:

1. 表格灰色部分是个循环,整个占用四字节,那到底有几个循环是怎么算出来的?

是根据section_length-6-4字节/4算来的,减去6就是section_length到last_section_number字段和,减去

4 是因为还有CRC_32占用的四字节。总长度是0x0d即(13 - 6 -4)/4;

2. 表格红色部分,是if-else if关系。取决于前面字段节目号是否从0开始;

3. PAT表我们主要就是解析怎么获取到后面的PMT的表格ID;

l PMT部分:

概况:

PMT (Program Map Table):节目映射表,该表的PID是由PAT给出的。通过该表可以得到一路节目中包含的信息。

其中包含了该路节目由那些流构成,流的类型(视频、音频、数据等),指定节目中各个流对应的ID,以及该节目的PCR所对应的PID.

1. 当前频道中包含的Video数据的PID;

2. 当前频道中包含的Audio数据的PID;

3. 和当前频道关联在一起的其它数据PID;

分析PMT表:

第一部分:TS包头:

其中我们在PAT负载的program_map_PID字段已经发现了PMT的PID值为4096,所以我们在当前的PMT的TS包头数据里面已经看到了对该值的引用。

由于四字节头基本都是一样的这里不再做说明,参考PAT的TS头即可,这里重点讲解PMT的载荷数据;

第二部分 TS包调整字节:

十六进制:00

在除去四字节后第一个字节是调整字节,所以实际数据应该除去第一字节后的数据;

第三部分 TS包有效载荷PMT:

十六进制:02 B0 17 00 01 C1 00 00 E1 00 F0 00 1B E1 00 F0 00 0F E1 01 F0 00 2F 44 B9 9B

对应二进制:

02: 0000 0010

B0 17: 1011 0000 0001 0111

00 01: 0000 0000 0000 0001

C1: 1100 0001

00 00: 0000 0000

E1 00: 1110 0001 0000 0000

F0 00: 1111 0001 0000 0000

1B E1 00 F0 00: 0001 1011 1110 0001 0000 0000 1111 0000 0000 0000

0F E1 01 F0 00: 0000 1111 1000 0001 0000 0001 1111 0000 0000 0000

2F 44 B9 9B:

PMT的格式如下:

字段解释示例如下:

字段

字段含义

占位

属于第几字节

当前数据和分析

table_id

表ID

8

第一字节

0x02:标识一个TS PSI分段的内容是节目关联分段,条件访问分段还是节目映射分段。对于PMT置为0x02

section_synatx_indication

段同步标识

1

第二字节

1:对于PMT该字段置为1

“0”

0值

1

第二字节

0:

reserved

保留值

2

第二字节

11:

section_length

分段长度

12

第二、三字节

0000 0001 0111:0x17:23:二十三字节。分段长度字段,前两位置00,这个值是包括该字段在内到CRC_32校验字段的字节数,起值不超过1021

program_number

传输流标识

16

第四、五字节

0x00 0x01:对应于PAT中的program_number

reserved

保留值

2

第六字节

11:

version_number

PMT的版本号

5

第六字节

00 000:PMT的版本号,如果字段中有关信息有变,则版本号以32为模加1。版本号是对一个节目的定义。

current_next_indicator

标识

1

第六字节

1:该字段置为1时,表示当前传送的program_map_section可用。置为0时,表明该传送的段不能使用,下一个表分段才能有效;

section_number

分段号

8

第七字节

0x00该字段一般总是置为0x00

last_section_number

最后一个分段号

8

第八字节

0x00该字段一般总是置为0x00

reserved

保留号

3

第九字节

1110 0001 0000 0000

PCR_PID

PCR值

13

第九字节第十字节

0 0001 0000 00000x100256该字段指示TS包的PID值,该TS含有该PCR字段,而PCR值对应于有节目号指定的节目。

reserved

保留值

4

第十一字节

1111

program_info_length不为0,后面进行循环层1

节目信息长度

12

第十一字节第十二字节

0000 0000 000: 表明跟随其后的对节目信息描述的字节数,也就是第一个N loop descriptors的字节数。这里是0则表示第一层循环略过;

stream_type开始循环层2

流类型

8

N 1字节(N=0)

0x1B:表明这个流是h264编码格式;表示PES流的类型。

reserved

保留值

3

N 2字节(N=0)

111:

elementary_pid

负载该PES流的TS包的PID值

13

N 2字节N 3字节(N=0)

0 0001 0000 0000:0x100:256:表明负载该PES流的PID值

reserved

保留值

4

N 4字节(N=0)

1111

es_info_length不为0,循环层3

Es流描述相关的字节数

12

N 5字节(N=0)

0000 0000 0000:表明跟随其后描述相关节目元素的字节数;否则为第二个循环的第二层循环;

stream_type

流类型

8

N 1字节(N=1)

0x0F

reserved

保留值

3

N 2字节(N=1)

111

elementary_pid

负载该PES流的TS包的PID值

13

N 2字节N 3字节(N=1)

0 0001 0000 0001:0x101257:

reserved

保留值

4

N 4字节(N=1)

1111:

es_info_length

Es流描述相关的字节数

12

N 5字节(N=1)

0000 00000000:表明跟随其后描述相关节目元素的字节数;否则为第二个循环的第二层循环;默认一般为0

crc_32

CRC校验

32

载荷最后四字节

0x2F 0x440xB9 0x9BCRC校验

总结:

1. program_info_length如果不为0,则有多少字节,则后面要跟多少字节对节目信息进行描述。

2. stream_type到es_info_length是另外一层循环,在这里面有可能还存在一层循环,就是es_info_length,不为0时要将此字节计算在之内,如果为0则一次循环需要五字节。

3. 图表中显示了红色和黄色循环了两次。

4. 这里面定义的真实码流视频和音频的PID,所以PMT是定义每路节目的音视频类型TYPE和编号PID的关键。

l PES部分:

概况:

PES即Packetized Elementary Stream(分组的ES),也就是对编码器原始数据的第一次打包,在这个过程中将ES流分组、打包、加入包头信息等操作。PES流的基本单位是PES包,PES包由包头和playload组成。一般视频一个帧被打包成一个PES包,长度一般都大于TS包的188字节,所以还是要进行切分。

分析载荷PES包:

第一部分:TS包头

PID是257,这里要看PMT负载的内容的elementary_pid字段定义。

elementary_pid其中一个就是257表示描述的是音频。

其次发现自适应填充为11,即TS包载荷先有自适应字段再有有效载荷,发现的确先有一堆0xFF填充字段。这是会影响TS包载荷的分析。

第二部分:TS包调整字节

在TS包头四个字节之后,如果发现自适应字段是11,则TS包头后面的第一个字节是自适应字段的长度,0x98即152个填充字节,不包含长度字段。

0x98后面为一个字节的调整字节0x00,接着就是151个填充字节。

第三部分:TS包有效载荷PES数据:

PES 头数据

十六进制:二进制:

00 00 01:PES的起始码,默认规定占三字节,24位

16 位字段,指出了PES 分组中跟在该字段后的字节数目。值为0 表示PES 分组长度要么没有规定要么没有限制。这种情况只允许出现在有效负载包含来源于传输流分组中某个视频基本流的字节的PES 分组中。

C0:流ID,八位0x(C0~DF)指音频,0x(E0~EF)为视频

00 19:PES包的长度,也就是一帧数据的总长度是25

80:1000 0000 这八位规定如下,一般就是默认0x80

10:默认规定00:PES加扰控制0:PES优先级0:数据定位指示符0:版权0:原始的或复制的

80:1000 0000

10:PTS_DTS_flags,10代表后面将会有PTS信息。当值为'10'时,PTS 字段应出现在PES 分组标题中;当值为'11'时,PTS 字段和DTS 字段都应出现在PES 分组标题中;当值为'00'时,PTS 字段和DTS 字段都不出现在PES分组标题中。值'01'是不允许的。000000:分别代表其他6个标志,0表示后面没有对应的信息,这会决定PES头数据还有那些字段,本例只有PTS。ESCR:1位。置'1'时表示ESCR 基础和扩展字段出现在PES 分组标题中;值为'0'表示没有ESCR 字段。ESrate:1 位。置'1'时表示ES rate 字段出现在PES 分组标题中;值为'0'表示没有ES rate 字段。DSMtrick mode:1 位。置'1'时表示有8 位特技方式字段;值为'0'表示没有该字段。Additionalinfo:1 位。附加版权信息标志字段。置'1'时表示有附加拷贝信息字段;值为'0'表示没有该字段。CRC:1 位。置'1'时表示CRC 字段出现在PES 分组标题中;值为'0'表示没有该字段。Extensionflag:1 位标志。置'1'时表示PES 分组标题中有扩展字段;值为'0'表示没有该字段。

05:0000 0101

PES头数据长度,表示后面还有0x05个字节,之后就是一帧的数据内容。PES头数据具体包含哪些内容有前面的标志位来确定,哪些信息的标志位1,就包含哪些信息。排列顺序分别是PTS DTS ESCR ES速率 DSM特技方式 附件的复制信息 前PES的CRC PES 扩展,如果还有多余的字节没用,就用填充字节0xFF填充。本例子中,PES头数据只包含PTS数据。 PES 标题数据长度字段。指出包含在PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前的字节指出了有无可选字段。

21 00 63 43 41: 0010 0001 0000 0000 0110 0011 0100 0011 0100 0001

因为PTS_DTS_flags为10,则这五个字节是PTS值,这个值很关键,影响的是时间戳,是音视频同步的关键。

0010:4位,默认规定

000:3位PTS[32-30]

1:marker_bit

0000 0000 0110 001:PTS[29-15]

1:marker_bit

0100 0011 0100 000:PTS[14-0]

1:marker_bit

所以PTS的二进制是:00000 0000 0001 1000 1010 0001 1010 0000

这个和工具计算出来的是一致的。

PES有效载荷:

PES包头数据分析完,剩下的数据全部帧数据的一部分了,经过分析这些数据就是AAC数据头格式了。

FF F1 6C 40 02 3F FC 00 FA 00 AC 21 C2 CD 00 00 0E

用工具分析出来的AAC数据格式结果:

我们再分析一个有效载荷为视频的PES数据部分:

第一部分:TS包头

47 41 00 31 07

最关注的的PID为265,这个引用的是PMT值。

其次我们发现自适应控制是11也就是3,说明后面自适应填充数据。

第二部分:TS Header AdaptationField自适应字段(可选)

其中第一个字节0x07代表自适应的长度,为七字节。

07 50 00 0C 5B 5C 7E

前两个字节:07 50

AdaptationFieldLength(8 bits) : 7

DiscontinuityIndicator(1 bit) : 0

RandomAccessIndicator(1 bit) : 1

ElementaryStreamPriorityIndicator : 0

PCRFlag(1 bit) : 1

OPCRFlag(1 bit) : 0

SplicingPointFlag(1 bit) : 0

TrasportPrivateDataFlag(1 bit) : 0

AdaptationFieldExtensionFlag(1 bit) : 0

后六个字节: 00 0C 5B 5C 7E

PCR :00 0C 5B 5C 7E 转为10进制 == 207314046

PCR_base :00 0C 5B 5C 7E>>15 == 1619639

PCR_ext :00 0C 5B 5C 7E & 0x1ff == 126

PCR_base 转成秒 :1619639/ 90000 = 17

PCR = PCR_base * 300 PCR_ext = 1619639* 300 126= 485892000

if (PCR_flag = = '1')

{

program_clock_reference_base // 33 bits

1100 0101 1011 0101 110

Reserved // 6 bits

program_clock_reference_extension // 9 bits

}

ProgramClockReferenceBase(33 bits) : 935936

Reserved(6 bits) : 0x3f

ProgramClockReferenceExtension(9 bits) : 0

MPEG2-TS规定的系统时钟频率为27MHz.PTS就是以系统时钟的300分频为单位的计数值(规定的除以300,参考ISO-13818-1)

PTS转换成秒: 1/(27MHZ/300) = 1 / 90000

PCR(i)=PCRBase(i)∗300 PCRExt(i) PCR(i) = PCR Base(i) * 300 PCR Ext(i)

PCR(i)=PCRBase(i)∗300 PCRExt(i)

第三部分:PES数据头

00 00 01 E0 00 00 80 80 05 21 00 63 6D 71

PES头的数据是开始符是00 00 01

经过分析,下面为PES数据的头,其中发现80 80 05,即后面有5分字节即为真实的PES有效载荷数据。

00 00 01

E0

00 00

80

80

05

21 00 63 6D 71

第四部分:PES有效载荷

其中

00 00 00 01 09 F0为H264 AUD

00 00 00 01 67 4D 00 1F 95 A8 14 01 6E 84 00 00 1C 20 00 05 7E 40 10 为H264的SPS

00 00 00 01 68 EE 3C 80 为H264的PPS

00 00 00 01 65 为H264的PPS

这就是码流分析的关键。

总结:

基本上从TS流分析H264码流这块,简单的方法就是先分析PID为0x00的PAT,然后分析PMT里面对音频和视频的PID定义。

然后以定义PID分别过滤音频,然后再过滤视频,这样就完成了从TS文件到音视频数据的过滤。

1. 第一步找PAT,注重分析PMT的表的PID;

2. 第二步找PMT,分析里面是否含有音视频,音视频编码格式,音视频的PID;

3. 第三步根据音视频的PID过滤音频TS包和视频TS包

4. 拿掉TS头,PES头,后即可得到音频数据和视频数据的裸码流;

TS文件解复用示意图如下:

示例代码:

0 人点赞