1. 数字视频压缩MPEG-2标准
MPEG-2是MPEG(Moving Picture Experts Group,运动图像专家组)组织制定的视频和音频有损压缩标准之一,它的正式名称为“基于数字存储媒体运动图像和语音的压缩标准”。与MPEG-1标准相比,MPEG-2标准具有更高的图像质量、更多的图像格式和传输码率的图像压缩标准。MPEG-2标准不是MPEG-1的简单升级,而是在传输和系统方面做了更加详细的规定和进一步的完善。它是针对标准数字电视和高清晰电视在各种应用下的压缩方案,编码率从3 Mbit/s~100 Mbit/s。
MPEG-2标准特别适用于广播质量的数字电视的编码和传送,被用于无线数字电视、DVB(Digital Video Broadcasting,数字视频广播)、数字卫星电视、DVD(Digital Video Disk,数字化视频光盘)等技术中。
如上图,MPEG2标准DVB数据发送、传输、接收的流程。
2. MPEG-2传输流系统层
在MPEG-2标准中,有两种不同类型的码流输出到信道,一种是节目码流(PS:program stream),适合没有误差产生的媒体存储,比如,DVD等存储介质;另外一种是传输流(TS:Transport Stream),适用于有信道噪声产生的传输,可在网络中进行远距离的传送。这样的TS便于综合多路节目为单路节目进行复用。多个节目或者不必具有共同的时间基准。由于MPEG-2要求这些包由传送,则在MPEG-2 TS中传送包的大小定义为4*47B=188B长度。另外,这样的TS作为一个固定长度包大小,便于找到帧的起止位置,易于从包丢失中恢复,适合于有误码的环境,但是与PS相比难于生产与复用。
TS 全称是 MPEG 2 Transport Stream,即MPEG-2 标准中的传输流。于此同时,我们常规使用的PS 是MPEG-2标准中的节目流。TS广泛用于广播电视系统,比如说数字电视,以及IPTV。
3. PS和TS的区别
通常我们开发中往往会遇到,PS、RTP、 TS、MP4这四种封装最为常见。
其中:从发布者范畴来看,MP4 规范上为MPEG-4标准,而PS、TS属于MPEG-2标准、RTP属于国际电信联盟ITU-T的RFC系列标准。
从使用范围来看, MP4主要适用于文件的存储。 TS、RTP适用于数据流的传输,不具有存储属性。PS既有存储属性又可以做实时流的传输。
从复杂度来看MP4封装最为复杂、其次PS、再次RTP和TS。
种类 | MP4 | PS | TS | RTP |
本地保存 | 支持,可以文件级别获取码流属性 | 支持,可以GOP级别获取码流属性 | 安卓、window支持直接播放。 IOS必须配合m3u8文件构成流媒体服务器方可预览 | 不支持。文件级别流属性参数不具备 |
实时传输 | 支持,但是必须是前置打包的机制。 | 支持,属性只能获取GOP级别的参数。 海康在文件头增加流媒体属性私有信息用于辅助 | 支持,配合m3u8文件构成流媒体服务器 | 支持。必须配合RTSP协议双方获取文件属性。 |
复杂度 | 高 | 高 | 简单 | 简单 |
4. TS包层
TS文件的基本存储单位为TS包,每一个包大小固定为188个字节。TS标准中有的地方是支持204字节的,204字节就是188个字节后面增加,16个字节的RS冗余校验信息。 一般默认188个字节。
每一个TS包的内部包含4个字节的TsPacketHeader。184字节的负载数据TsPacketPayloadData。
当TsPacketPayloadData中的实际有效数据不足184的时候,需要填充adaptation field数据,则按照如下格式。其中,填充规则按照4.1.2节描述。

4.1 TS包文件头模块
一共四个字节,8个标记。如下图
结构体描述如下面的表:
Ts header结构
sync_byte | 8b | 同步字节,固定为0x47, 采用固定宏TS_SYNC_0X47替代。每一次PAT和PMT和每一帧数据处理的时候都要添。 |
transport_error_indicator | 1b | 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0。 |
payload_unit_start_indicator | 1b | 负载开始标志位,用来表示TS包的有效净荷带有PES包或者PSI数据的情况,占位1bit;另若此值为1,且负载为PSI数据时,则在TS头后,负载起始字节会有1个调整字节point_field; |
transport_priority | 1b | 传输优先级,0为低优先级,1为高优先级,通常取0 |
pid: | 13b | pid值(Packet ID号码,唯一的号码对应不同的包) 指示有效负载中的数据类型,占位13bit;0x0000代表PAT,0x0001代表CAT,0x0002-0x000F保留,0x1FFF表示空包; |
transport_scrambling_control | 2b | 传输加扰控制,00表示未加密 |
adaptation_field_control | 2b | 调整字段标志,表示此TS首部是否跟随调整字段还是负载数据,占位2bit,其中00位保留,01表示无调整字段,只有有效负载数据,10表示只有调整字段,无有效负载,11表示有调整字段,且其后跟有有效负载;空分组此字段应为01; |
continuity_counter | 4b | 固定为11,递增计数器,从0-f,起始值不一定取0,但必须是连续的 |
以上4个字节是必须存在的,但还有一些特殊的。当adaptation_field_control==1x时,会出现Adaptation field字段,Adaptation field字段如下。
Adaptation field字段
adaptation_field_length | 8b | 调整字段长度标示,标示此字节后面调整字段的长度,占位8bit;值为0时,表示在TS分组中插入一个调整字节,后面没有调整字段,紧跟着的是有效负载;adaptation_field_control == ‘11’时,此值在0~182之间,adaptation_field_control == ‘10’时,此值为183,若字段没这么长则填充0xFF字段; |
以下字段都是在adaptation_field_length>0是才会出现的 | ||
discontinuity_indicator | 1b | 不连续状态指示符,占位1bit,置位1时表示此TS分组的不连续状态为真; |
random_access_indicator | 1b | 随机访问指示符,占位1bit; |
elementary_stream_priority_indicator | 1b | 原始流数据优先级指示符,占位1bit,置位1表示此原始流数据比相同PID的TS包中的其他原始流优先级高; |
PCR_flag | 1b | PCR标志位,占位1bit,置位1表示调整字段中包含PCR字段,置位0则没有PCR字段; |
OPCR_flag | 1b | OPCR标志位,占位1bit,置位1表示调整字段中包含OPCR字段,置位0则没有OPCR字段; |
splicing_point_flag | 1b | splice_countdown标志位,占位1bit,置位1表示调整字段中包含splice_countdown字段,置位0则没有splice_countdown字段; |
transport_private_data_flag | 1b | transport_private_data标志位,占位1bit,置位1时表示调整字段中含有1个或者多个私有数据字节,置位0则无此字节; |
adaptation_field_extension_flag | 1b | 调整字段扩展标志位,占位1bit,置位1表示含有调整字段扩展字段,置位0则无扩展字段; |
以上8个bit是标识符,后面是根据标识符的值来确定的字段,顺序如下:
@PCR字段:当PCR_flag == 1时,此字段才存在,占位48bit,依次顺序为:
program_clock_reference_base字段:占位33bit;
reserved字段:占位6bit;
program_clock_reference_extension字段:占位9bit;
@OPCR字段:当OPCR_flag == 1时,此字段才存在,占位48bit,依次顺序为:
original_program_clock_reference_base字段:占位33bit;
reserved字段:占位6bit;
original_program_clock_reference_extension字段:占位9bit;
@splice_countdown字段:当splicing_point_flag == 1时此字段存在,占位8bit;
@transport_private_data字段:私有数据字段,当transport_private_data_flag==1时此字段存在,占位N*8bit,字节顺序为:
transport_private_data_length:表明私有数据的字节长度,占位8bit;
private_data_byte:私有数据,长度由前面的长度字段确定;
@adaptation_field_extension字段:调整字段扩展字段,占用长度不确定,当adaptation_field_extension_flag == 1时此字段存在,字段中也有3个标志位,来确定一些字段存不存在,其具体字节顺序如下:
adaptation_field_extension_length:调整字段扩展字段的长度,占位8bit;
ltw_flag:ltw字段标志位,置位1时表示此字段存在,占位1bit;
piecewise_rate_flag:piecewise_rate字段标志位,置位1时此字段存在,占位1bit;
seamless_splice_flag:seamless_splice标志位,置位1时此字段存在,占位1bit;
Reserved:保留字段,占位5bit;
Ltw字段:当ltw_flag == 1时此字段存在,占位16bit,其由以下两个字段组成
ltw_valid_flag:占位1bit,当ltw_valid_flag == 1时,ltw_offset才有效;
ltw_offset:占位15bit;
piecewise_rate字段:当piecewise_rate_flag == 1时此字段存在,占位24bit,其字节顺序如下:
reserved字段:保留字段,占位2bit;
piecewise_rate字段:占位22bit;此字段只有在当ltw_flag == 1和ltw_valid_flag == 1时才有定义,有定义时此字段是一个正整数;
seamless_splice字段:当seamless_splice_flag == 1时此字段存在,占位40bit;字节顺序依次为:
splice_type字段:占位4bit;标识delay和rate值;
DTS_next_AU[32..30]:占位3bit;
marker_bit字段:占位1bit;
DTS_next_AU[29..15]字段:占位15bit;
marker_bit:占位1bit;
DTS_next_AU[14..0]:占位15bit;
marker_bit:占位1bit;
stuffing_byte:填充字段,固定为0xFF;
Payload_bytes:有效负载字段,字节来自PES包,PSI部分等;
注意:
当TS包带有PES包数据时,payload_unit_start_indicator具有以下的特点:置为1,标识TS包的有效净荷以PES包的第一个字节开始,即此TS包为PES包的起始包,且此TS分组中有且只有一个PES包的起始字段;置为0,表示TS包不是PES包的起始包,是后面的数据包。
当 TS包带有PSI数据时,payload_unit_start_indicator具有以下特点:置为1,表示TS包中带有PSI数据分段的第一个字节,即这个TS包是PSI Section的起始包,则此TS包的负载的第一个字节带有pointer_field;置为0,表示TS包不带有PSI Section的第一个字节,即此TS包不是PSI的起始包,即在有效负载中没有point_field,有效负载的开始就是PSI的数据内容。point_field的定义将在下面的PSI节中进行介绍;对于空包的包,payload_unit_start_indicator应该置为0。
例如:若TS包载荷为PAT,则当接收到的TS包的payload_unit_start_indicator为1时,表明这个TS包包含了PAT头信息,从这个包里面解析出section_length和continuity_counter,然后继续收集后面的payload_unit_start_indicator = 0的TS包,并判断continuity_counter的连续性,不断读出TS包中的净载荷(也就是PAT数据),用section_length作为收集TS包结束条件。
TS包头之后,就是负载payload的内容了,里面可以是PES分组的数据,也可以是PSI信息,PSI信息主要由PAT,PMT,CAT等,在这里主要介绍PAT和PMT两种信息表;由上所描述信息可知,payload的类型是由PID来确定的,一般PID==0x0000则payload为PAT,PID== 0x0001,则payload为CAT,而PMT的PID则是在PAT中进行指定的;
PSI还有可能有一个特殊的字段:
Point_field字段:跟在包头之后,占位8bit,属于有效负载,表示从此字段开始到负载中PSI Section的第一个字节之间的字节数;当payload_unit_start_indicator == 1时,此字段才存在;若point_field == 0x00,则表示此字节后跟着的就是PSI Section的起始字节;此字段是在有效负载中的,计入有效负载的长度;
其中PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。下表给出了一些表的PID值,这些值是固定的,不允许用于更改。
类型 | PID 值 |
PAT | 0x0000 |
CAT | 0x0001 |
TSDT | 0x0002 |
EIT,ST | 0x0012 |
RST,ST | 0x0013 |
TDT,TOT,ST | 0x0014 |
表1 pid 类型表
下面以一个TS流的其中一个Packet中的Packet Header为例进行说明:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | … |
Packet(十六进制) | 4 | 7 | 0 | 7 | e | 5 | 1 | 2 | … | ||||||||||||||||||||||||
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | … |
Packet Header 信息 | 1 sync_byte=0x47 | 2 | 3 | 4 | 5 PID=0x07e5 | 6 | 7 | 8 | …
|
表2 header 例程数据表
@ sync_byte=01000111, 就是0x47,这是DVB TS规定的同步字节,固定是0x47.
@ transport_error_indicator=0, 表示当前包没有发生传输错误.
@ payload_unit_start_indicator=0, 含义参考ISO13818-1标准文档
@ transport_priority=0, 表示当前包是低优先级.
@ PID=00111 11100101即0x07e5, Video PID
@ transport_scrambling_control=00, 表示节目没有加密
@ adaptation_field_control=01 即0x01,具体含义请参考ISO13818-1
@ continuity_counte=0010 即0x02,表示当前传送的相同类型的包是第3个
ts层的内容是通过PID值来标识的,主要内容包括:PAT表、PMT表、音频流、视频流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音视频流了。PAT表的PID值固定为0。PAT表和PMT表需要定期插入ts流,因为用户随时可能加入ts流,这个间隔比较小,通常每隔几个视频帧就要加入PAT和PMT。PAT和PMT表是必须的,还可以加入其它表如SDT(业务描述表)等。
ts流最早应用于数字电视领域,其格式非常复杂包含的配置信息表多达十几个,视频格式主要是mpeg2。苹果公司发明的http live stream流媒体是基于ts文件的,不过他大大简化了传统的ts流,只需要2个最基本的配置表PAT和PMT,再加上音视频内容就可以了,hls流媒体视频编码的主要格式为h264/mpeg4,音频为aac/mp3。
4.2 TS包填充模块
adaption为自适应填充功能,主要目的为填充凑齐不足188字节的TS包。
图6 TS adaptation填充结构
@ adaptation_field_length:8bit,自适应域长度,后面的字节数
@ flag :1bit,取0x50表示包含PCR或0x40表示不包含PCR
@ PCR:1bit,Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。
@ stuffing_bytes:填充字节,取值0xff
自适应区的长度要包含传输错误指示符标识的一个字节。pcr是节目时钟参考,pcr、dts、pts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr。如果没有字段,ipad是可以播放的,但vlc无法播放。打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。
5. ES层数据
es层就是音视频裸数据了,常用的音频编码格式为AAC,和MP3 。视频编码格式为H.264和HEVC。
其数据结构可以分别参考相应的编码规范手册。
6. PES包层
pes层是在每一个视频/音频帧上加入了时间戳等信息,pes包内容很多,我们只留下最常用的。
如图7,按照苹果协议定义规范,PES内容可以是, PMT项数据,PAT项数据, 也可以是正常的音视频数据。
6.1 PAT格式
PAT全称Program Association Table,节目关联表,定义了当前TS流中所有的节目,其PID为0x0000,它是PSI的根节点,当播放器对视频开始检索分析的时候,针对每个TS 包的header中pid成员进行判定,直到找到PAT表开始的地方进行有效数据起始分析。
table_id | 8b | PAT表固定为0x00 |
section_syntax_indicator | 1b | 固定为1 |
zero | 1b | 固定为0 |
reserved | 2b | 固定为11 |
section_length | 12b | 后面数据的长度 |
transport_stream_id | 16b | 传输流ID,固定为0x0001 |
reserved | 2b | 固定为11 |
version_number | 5b | 版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1b | 固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT表 |
section_number | 8b | 固定为0x00 |
last_section_number | 8b | 固定为0x00 |
开始循环 |
|
|
program_number | 16b | 节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT |
reserved | 3b | 固定为111 |
PID | 13b | 节目号对应内容的PID值 |
结束循环 |
|
|
CRC32 | 32b | 前面数据的CRC32校验码 |
表3 PAT 例程数据表
PAT结构根据表3 PAT 例程数据表加上我们节目数量为1的时候以得知PAT的数据结构如下:
因此PAT长度为16个字节,而payloadSize为184个字节, 那么adaptFileLen填充为168个长度。
根据表3的描述,有如下的例程。
通过一段TS流中一个Packet分析PAT表,这里我们分析一段TS流其中一个Packet的Packet Data部分:
首先给出一个数据包,其数据如下:
Packet Header | Packet Data |
0x47 0x40 0x00 0x10 | 0000 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff…… ff ff |
分析Packet Header如下表所示:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | … |
Packet(十六进制) | 4 | 7 | 4 | 0 | 0 | 0 | 1 | 0 | … | ||||||||||||||||||||||||
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | … |
Packet Header Bits | 1 sync_byte=0x47 | 2 | 3 | 4 | 5 PID=0x0000 | 6 | 7 | 8 | … |
根据包头数据格式,我们可以知晓整个数据包的属性,列表如下:
sync_byte | 0x47 | 固定同步字节 |
transport_error_indicator | “0” | 没有传输错误 |
payload_unit_start_indicator | “1” | 在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。即上面数据中红色部分不属于有效数据包。 |
transport_priority | “0” | 传输优先级低 |
PID | 0x0000 | PID=0x0000说明数据包是PAT表信息 |
transport_scrambling_control | “00” | 未加密 |
adaptation_field_control | “01” | 附加区域控制 |
continuity_counte | “0000” | 包递增计数器 |
如上表所示,我们可以知道,首先Packet的Packet Data是PAT信息表,因为其PID为0,并且在包头后需要除去一个字节才是有效数据(payload_unit_start_indicator="1")。这样,Packet Data就应该是“00 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff …… ff ff”。
Packet Data分析 | |||||||||||||||||||||||||
第n个字节 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | … | ||||
Packet Data(除去开头的0x00) | 00 | b0 | 11 | 00 | 01 | c1 | 00 | 00 | 00 | 00 | e0 | 1f | 00 | 01 | e1 | 00 | 24 | ac | 48 | 84 | … | ||||
字段名 | 位 | 具体值 | 次序 | 说明 | |||||||||||||||||||||
table_id | 8 | 0000 | 第1个字节 0000 0000B(0x00) | PAT的table_id只能是0x00 | |||||||||||||||||||||
section_syntax_indicator | 1 | 1 |
第2、3个字节 1011 0000 0001 0001B(0xb0 11) | 段语法标志位,固定为1 | |||||||||||||||||||||
zero | 1 | 0 |
| ||||||||||||||||||||||
reserved | 2 | 11 |
| ||||||||||||||||||||||
section_length | 12 | 0000 0001 0001B=0x011=17 | 段长度为17字节 | ||||||||||||||||||||||
transport_stream_id | 16 | 0x0001 | 第4、5个字节 0x00 0x01 |
| |||||||||||||||||||||
reserved | 2 | 11 |
第6个字节 1100 0001B(0xc1) |
| |||||||||||||||||||||
version_number | 5 | 00000 | 一旦PAT有变化,版本号加1 | ||||||||||||||||||||||
current_next_indicator | 1 | 1 | 当前传送的PAT表可以使用,若为0则要等待下一个表 | ||||||||||||||||||||||
section_number | 8 | 0x00 | 第7个字节0x00 |
| |||||||||||||||||||||
last_section_number | 8 | 0x00 | 第8个字节 0x00 |
| |||||||||||||||||||||
开始循环 | |||||||||||||||||||||||||
program_number | 16 | 0x0000-第一次 | 2个字节(0x00 00) | 节目号 | |||||||||||||||||||||
reserved | 3 | 111 |
2个字节 1110 0000 0001 1111B(0xe0 1f) |
| |||||||||||||||||||||
network_id(节目号为0时) program_map_PID(节目号为其他时) | 13 | 0 0000 0001 1111B=31 -第一次 | 节目号为0x0000时,表示这是NIT,PID=0x001f,即31 节目号为0x0001时,表示这是PMT,PID=0x100,即256 | ||||||||||||||||||||||
结束循环 | |||||||||||||||||||||||||
CRC_32 | 32 | -- | 4个字节 |
|
由以上几个表可以分析出PAT表和PMT表有着内在的联系。也就是之前提到的。PAT表描述了当前流的NIT(Network Information Table,网络信息表)中的PID、当前流中有多少不同类型的PMT表及每个PMT表对应的频道号。
6.2 PMT格式
PMT 全称Program Map Table,节目映射表,PMT数据的信息可以理解为这个节目包含的音频和视频信息。海康目前用它来填充海康自己内部的私有信息。
table_id | 8b | PMT表取值随意,0x02 |
section_syntax_indicator | 1b | 固定为1 |
zero | 1b | 固定为0 |
reserved | 2b | 固定为11 |
section_length | 12b | 后面数据的长度 |
program_number | 16b | 频道号码,表示当前的PMT关联到的频道,取值0x0001 |
reserved | 2b | 固定为11 |
version_number | 5b | 版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1b | 固定为1 |
section_number | 8b | 固定为0x00 |
last_section_number | 8b | 固定为0x00 |
reserved | 3b | 固定为111 |
PCR_PID | 13b | PCR(节目参考时钟)所在TS分组的PID,指定为视频PID |
reserved | 4b | 固定为1111 |
program_info_length | 12b | 节目描述信息,指定为0x000表示没有 |
开始循环 |
|
|
stream_type | 8b | 流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03 |
reserved | 3b | 固定为111 |
elementary_PID | 13b | 与stream_type对应的PID |
reserved | 4b | 固定为1111 |
ES_info_length | 12b | 描述信息,指定为0x000表示没有 |
结束循环 |
|
|
CRC32 | 32b | 前面数据的CRC32校验码 |
表4 PMT数据表
通过一段TS流中一个Packet分析PMT表,通过分析一段TS流的数据包Packet来学习PMT表。下面给出了一段TS流数据中的一个Packet(十六进制数)。
Packet Header | Packet Data |
0x47 0x43 0xe8 0x12 | 00 02 b0 12 00 01 c1 00 00 e3 e9 f0 00 1b e3 e9 f0 00 f0 af b4 4f ff ff…… ff ff |
首先解析Packet Header,分析如下:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | … | |||
Packet(十六进制) | 4 | 7 | 4 | 3 | e | 8 | 1 | 2 | … | ||||||||||||||||||||||||||
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | … | ||
Packet Header Bits | 1 sync_byte=0x47 | 2 | 3 | 4 | 5 PID=0x03e8 | 6 | 7 | 8 | … | ||||||||||||||||||||||||||
Packet Header分析 | |||||||||||||||||||||||||||||||||||
| Packet Header:0x47 0x40 0x00 0x10 | ||||||||||||||||||||||||||||||||||
1 | sync_byte | 0x47 | 固定同步字节 | ||||||||||||||||||||||||||||||||
2 | transport_error_indicator | “0” | 没有传输错误 | ||||||||||||||||||||||||||||||||
3 | payload_unit_start_indicator | “1” | 在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。 | ||||||||||||||||||||||||||||||||
4 | transport_priority | “0” | 传输优先级低 | ||||||||||||||||||||||||||||||||
5 | PID | 0x03e8 | PID=0x03e8说明数据包是PMT表信息 | ||||||||||||||||||||||||||||||||
6 | transport_scrambling_control | “00” | 未加密 | ||||||||||||||||||||||||||||||||
7 | adaptation_field_control | “01” | 附加区域控制 | ||||||||||||||||||||||||||||||||
8 | continuity_counte | “0010” | 包递增计数器 |
因为payload_unit_start_indicator=‘1’,在解析数据包的时候需要去除Packet Data的第一个字节。下面是对Packet Data的详细解析:
PMT表的Packet Data分析 | |||||||||||||||||||||||||
第n个字节 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | … | ||||
Packet Data | 02 | b0 | 12 | 00 | 01 | c1 | 00 | 00 | e3 | e9 | f0 | 00 | 1b | e3 | e9 | f0 | 00 | f0 | 1b | e3 | … | ||||
字段名 | 位数 | 具体值 | 次序 | 说明 | |||||||||||||||||||||
table_id | 8 | 0x02 | 第1个字节 |
| |||||||||||||||||||||
section_syntax_indicator | 1 | 1B |
第2、3个字节 1011 0000 0001 0010B=0xb012 | 段语法标志 | |||||||||||||||||||||
zero | 1 | 0B |
| ||||||||||||||||||||||
reserved | 2 | 11B=0x03 |
| ||||||||||||||||||||||
section_length | 12 | 0000 0001 0010B=0x12 | 段长度,从program_number开始,到CRC_32(含)的字节总数 | ||||||||||||||||||||||
program_number | 16 | 0x0001 | 第4、5个字节 0x00 01 | 频道号码,表示当前的PMT关联到的频道 | |||||||||||||||||||||
reserved | 2 | 11B=0x03 |
第6个字节 1100 0001B=0xc1 |
| |||||||||||||||||||||
version_number |
5 |
00000B=0x00 | 版本号码,如果PMT内容有更新,则它会递增1通知解复用程序需要重新接收节目信息 | ||||||||||||||||||||||
current_next_indicator | 1 | 1B=0x01 | 当前未来标志符 | ||||||||||||||||||||||
section_number | 8 | 0x00 | 第7个字节0x00 | 当前段号码 | |||||||||||||||||||||
last_section_number | 8 | 0x00 | 第8个字节 0x00 | 最后段号码,含义和PAT中的对应字段相同 | |||||||||||||||||||||
reserved | 3 | 111B=0x07 | 第9、10个字节 1110 0011 1110 1001B=0xe3e9 |
| |||||||||||||||||||||
PCR_PID | 13 | 000111110B=0x3e9 | PCR(节目参考时钟)所在TS分组的PID | ||||||||||||||||||||||
reserved | 4 | 1111B=0x0f |
第11、12个字节 1111 0000 0000 0000=0xf000 |
| |||||||||||||||||||||
program_info_length |
12 |
000000000000B=0x000 | 节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes)紧接着就是频道内部包含的节目类型和对应的PID号码了 | ||||||||||||||||||||||
stream_type | 8 | 0x1b | 第13个字节 0x1b | 流类型,标志是Video还是Audio还是其他数据 | |||||||||||||||||||||
reserved | 3 | 111B=0x07 | 第14、15个字节 1110 0011 1110 1001B=0xe3e9 |
| |||||||||||||||||||||
elementary_PID | 13 | 000111110 1001=0x3e9 | 该节目中包括的视频流,音频流等对应的TS分组的PID | ||||||||||||||||||||||
reserved | 4 | 1111B=0x0f | 第16、17个字节 1111 0000 0000 0000B=0xf000 |
| |||||||||||||||||||||
es_info_length | 12 | 0000 0000 0000=0x000 |
| ||||||||||||||||||||||
CRC | 32 | —— | —— |
|
6.3 音视频数据封装
音视频数据是在每一个视频/音频帧上加入了时间戳等信息,pes包内容很多,我们只留下最常用的。
pes start code | 3B | 开始码,固定为0x000001 |
stream id | 1B | 音频取值(0xc0-0xdf),通常为0xc0 |
pes packet length | 2B | 后面pes数据的长度,0表示长度不限制, |
flag | 1B | 通常取值0x80,表示数据不加密、无优先级、备份的数据 |
flag | 1B | 取值0x80表示只含有pts,取值0xc0表示含有pts和dts |
pes data length | 1B | 后面数据的长度,取值5或10 |
pts | 5B | 33bit值 |
dts | 5B | 33bit值 |
pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要,音频数据的pts和dts相同,所以只需要pts。有pts和dts两种时间戳是B帧引起的,I帧和P帧的pts等于dts。如果一个视频没有B帧,则pts永远和dts相同。从文件中顺序读取视频帧,取出的帧顺序和dts顺序相同。dts算法比较简单,初始值 + 增量即可,pts计算比较复杂,需要在dts的基础上加偏移量。
音频的pes中只有pts(同dts),视频的I、P帧两种时间戳都要有,视频B帧只要pts(同dts)。打包pts和dts就需要知道视频帧类型,但是通过容器格式我们是无法判断帧类型的,必须解析h.264内容才可以获取帧类型。
时钟配置,MPEG2-TS 中规定系统采用45K的基准时钟。而我们DSP进程封装功能为90K(兼容RTP、PS封装目的)。因此需要进行转换。
假如帧率为25帧每秒, 那么每一帧的系统间隔时间为40ms。按照TS封装45K的采样率基准时钟来计算, 需要1800个时钟(40 * 45)。
假如音频采样率为16K,单个点耗时1/16ms即62.5us,那么1024个点为一帧一共64ms(62.5us * 1024),,则转为45K下为2880个时钟,(0.0625*45*1024)。