网络流媒体(四)———TS流
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)。
网络流媒体(四)———TS流相关推荐
- java 解析m3u8的实例_m3u8文件完整实例及TS流抓取
参考文档: http://blog.csdn.net/blueboyhi/article/details/40107683 如下 是一个华数影视的M3U8文件链接 http://chyd-sn.was ...
- 截获视频网站 ts流 视频 网络嗅探 IPTV OTT 数据流 音视频数据 码流 ts 流
忽然想把最近的技术总结一下.做ts流 IP数据流也有5年之久了.接触了大大小小的项目.例如网络嗅探 通过sniffer 获取IP包.ts 码流分析等.今天把二者结合起来可以应用到手机终端.或者工程仪表 ...
- 流媒体基础知识TS流 PS流 ES流区别
IP数据报有首部和数据两部分组成的,首部的前一部分是固定长度20字节,是所有IP数据报必须具有的.首部包括:总长度.标识.MF.DF.片偏移. 数字信号实际传送的是数据流,一般数据流包括以下三种: ( ...
- vlc源码分析(四) 调用libts接收TS流
http://www.cnblogs.com/jiayayao/p/6858253.html 代码分析前,先要了解TS流基本概念:TS流之基本概念. VLC解析TS流是通过libts库来分离的,lib ...
- TS流解析 二 *****
1.TS格式介绍 TS:全称为MPEG2-TS.TS即"Transport Stream"的缩写.它是分包发送的,每一个包长为188字节(还有192和204个字节的包).包的结构为 ...
- 基于FFmpeg接收RTSP的ts流
RTSP用于建立的控制媒体流的传输,通过wireshark抓包可以看到rtsp消息交互的过程: 1. 第一步:查询服务器端可用方法 C->S:OPTION request // 询问S有 ...
- 对TS流的一些理解TS流的结构
TS流的结构 TS流由很多个TS包组成,而每个TS包的结构包括:4B的包头,可变长的调整字段,有效净荷(payload).现在分别说一下这三部分的结构和作用. 包头 包头最小为4字节,这四字节分 ...
- ffmpeg基础三:H264,从MP4文件获取(av_bsf_get_by_name(“h264_mp4toannexb“))和从TS流获取保存H264
参考:零声学院 1.流媒体编解码流程 2.编码原理 在⾳视频传输过程中,视频⽂件的传输是⼀个极⼤的问题:⼀段分辨率为19201080,每个像 素点为RGB占⽤3个字节,帧率是25的视频,对于传输带宽的 ...
- 网络流媒体--SDP会话描述协议(RFC-4566)
1.SDP协议简介 SDP(Session Description Protocol)是为了描述多媒体会话而设计的,主要用来描述会话通告,会话邀请或者其他形式的多媒体会话初始化.为了初始化多媒体会话, ...
最新文章
- Rhel6.0部署Oracle10g报错相关问题记录
- 学编程又一火爆网站:哔哩哔哩
- java最小访问原则_Android基础进阶之EffectiveJava翻译系列(第七章:通用原则)
- Nginx在Windows平台的配置与使用
- Oracle Study之--Oracle 11gR2通过RMAN克隆数据库
- http://www.od85c.com.cn/html/,OllyDbg script for unpacking Enigma 4.xx and 5.xx
- 为什么要使用信道复用技术?常用的信道复用技术有哪些?
- lsqnonlin函数_matlab非线性最小二乘函数
- 比亚迪汉家族3月热销12359辆 汉EV单车销量破万
- java 正方形_java-确定正方形和矩形之间的关系的算法
- 《ANSYS 14.0超级学习手册》一2.5 本章小结
- [Ext JS]12.12.1 必填字段组件扩展
- Echarts数据可视化tooltip提示框,开发全解+完美注释
- java.exe 不是有效的win32_WinXP提示不是有效的Win32应用程序怎么办?
- java 27 - 7 反射之 通过反射越过泛型检查
- flash幻灯片源码
- C++string类常用函数 c++中的string常用函数用法总结
- Qt之Dialog\widget\ mainwindow的区别和布局管理器 分裂器的区别
- 初踩阿里云效代码管理
- 机器学习 - 概念学习
热门文章
- Python 多进程 multiprocessing.Pool类详解
- 中国各城市首轮感染高峰期预测!(最新更新版)
- Python学习(3)计算个人所得税
- DCOS(centos 7.4/7.6)
- Greenplum 分布键 distribute hash分布和随机分布
- 从二维到三维,可见Web3D技术的重要性,让线上3D产品展示所见即所得
- php如何除去图片水印,如何去掉图片水印,一键去除文字logo图片水印更简单
- 5.16 按照自定义序列对城市进行排序 [原创Excel教程]
- PHP网页的工作原理
- 工业生产管理-数据采集初探