1、 写在前面:学习的时候一定要对照官方文档、结合分析工具,才能深入理解
(1)TS官方文档:http://www.telemidia.puc-rio.br/~rafaeldiniz/public_files/normas/ISO-13818/iso13818-1/ISO_IEC_13818-1_2007_PDF_version_(en).pdf
(2)推荐TS的分析工具:Elecard Stream Analyzer
(3)推荐TS分析博客:https://blog.csdn.net/cabbage2008/category_5885203.html
2、TS 全称是 MPEG-2 Transport Stream,即MPEG-2标准中的传输流。TS流广泛用于广播电视系统,比如说数字电视,以及IPTV。广播电视系统中,TS流如果发送过来后,就会由我们进行解封装和解码,然后由屏幕渲染播放。这里就有一个问题,我们看电视有很多频道,比如CCTV、地方卫视等。而同一个频道还有很多节目,就像CCTV频道下面,在同一时刻就有CCTV1-CCTV14这些节目,那么这些频道、节目、音视频码流又是如何在TS里面进行区分呢?又是如何支持随机播放呢?又是怎么完成音画同步呢?仔细想想这就是TS复杂的原因,我们在互联网中可能借用了这种封装,只是传了一路视频和音频,比如直播,所以很多字段我们并不理解,原因就是因为我们只使用了比较简单的一种场景。(音视频封装:MPTG2-TS 媒体封装实例解析和说明)
3、TS流最初的作用是传输音视频数据,应用在电视广播系统,但是在直播、点播场景时,通常需要将音视频数据以".ts"的文件格式进行落盘,本文会重点介绍".ts"的封装格式。
4、TS文件的基本单位是ts packet,在不同标准中,ts packet的大小也不相同,共有有四种大小:(封装格式--TS)
(1)188 bytes:MPEG-2标准(本文会基于MPEG-2的标准去讨论ts packet)
(2)192 bytes:188 bytes + 4 bytes时间码 --> 日本DVH-S标准
(3)204 bytes:188 bytes + 16 bytes前向纠错码(FEC) --> 美国ATSC标准
(4)208 bytes:188 bytes + 4 bytes时间码 + 16 bytes前向纠错码(FEC)
在同一个标准中ts packet的大小是固定的,便于找到帧的起止位置,易于从包丢失中恢复,适合于有误码的环境,可在网络中进行远距离的传送。这样的TS便于综合多路节目为单路节目进行复用。多个节目或者不必具有共同的时间基准。(网络流媒体(四)———TS流)
ts packet的大小为什么是固定的?
TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流。由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格式。(TS和PS的区别)
5、每一个ts packet的内部一定会有ts packet header,有可能会存在填充数据adaptation field和ts payload。ts packet header固定是4字节,ts packet payload大小不固定,如果ts packet payload不足184字节,需要填充数据adaptation field。如果ts packet payload是184字节则没有填充数据adaptation field。
6、ts packet中携带两类信息:已压缩的音视频(PES、ES)和与之相关的符号化表(PSI)包括PAT和PMT,由包的PID来识别携带的是哪种信息。
(1)PAT:Program Association Table的简称,节目关联表。是接收数据的入口点,解析TS文件的起点。PAT包含所有频道编号(PID),比如共有CCTV1-CCTV14这些频道,通过读取PAT就会获取上述所有频道的编号(PID)。通过频道编号(PID)可以找到频道对应的PMT。
(2)PMT:Program Map Table的简称,节目映射表。连接节目号和节目元素的桥梁。PMT描述频道的具体信息,一个频道对应一个PMT,有几个频道就有几个PMT表。比如CCTV1对应一个PMT,CCTV2对应另一个PMT,CCTV1-CCTV14会对应14个PMT。
(3)PES:是在每一个视频/音频帧的基础上增加了pes header,形成了PES packet(pes header + es),pes header中最重要的就是PTS、DTS信息。
(4)ES:就是音视频裸数据了,常用的音频编码格式为AAC,和MP3 。视频编码格式为H.264和HEVC。其数据结构可以分别参考相应的编码规范手册。
7、ts文件的结构如下图所示:(音视频封装:MPTG2-TS 媒体封装实例解析和说明)
PAT PMT PMT PMT DATA DATA ......DATA PAT PMT PMT PMT DATA DATA......
(1)图中的adaptation field是填充数据,为的是保证ts packet的大小为188字段。当ts payload内容不足时,或者为了在系统层插入节目时钟参考(program clock reference, PCR)才会出现。(adaptation field的内容可参考 2.1 Ts Packet(Transport Stream Packet)->5、adaptation field(填充数据) )
(2)PAT、PMT是成对出现的,有几个节目就有几个PMT,上图中是一组PAT、PMT。PAT、PMT是循环插入在文件中的,一个ts文件中有多组PAT、PMT。
(3)一个PES Pacekt(pes header + es)是一帧数据,其大小可能是大于ts packet的长度,所以一个PES packet会存储于多个ts packet中。其中只有第一个ts packet有pes header,其余ts pacekt都只有es数据,最后一个ts packet可能会因为es数据不足而加入adaptation field(填充数据)。
1、ts文件由一个个ts packet组成,在MPEG-2标准中ts packet的大小固定是188 bytes。
2、ts文件的第一个ts packet是PAT,描述所有节目。第二个ts packet是PMT,有几个节目就有几个ts packet存储PMT,然后才是音视频数据。正常情况下ts文件的第一帧一定是I帧。这样才能正常播放。
3、分析ts文件时发现,ts文件中往往不仅仅是一组PAT,PMT。这要从ts文件的生成说起,ts文件的生成主要有2种场景,一种是HLS流的落盘,另一种是其它文件转封装成ts。在HLS中,PAT和PMT是循环发送的,所以ts文件会有多组PAT、PMT。其它文件转封装成ts时,转封装的工具也会循环的写PAT、PMT。
4、正常情况下ts文件的第一组PAT、PMT后面是的第一帧数据是I帧。后续的PAT、PMT后面的帧不一定是I帧(可通过ffplay xx.ts -loglevel trace分析出来),此时通过PAT、PMT找到的音视频数据,如果不是I帧,也可以通过前面收到的sps、pps的数据进行解码,并结合前面收到的I帧还原出图像。
(ffplay不是专业分析ts的工具,PAT、PMT有些内容没有打印出来,如果有需要可以自己加日志打印)
1、每一个ts packet的内部一定会有ts packet header,有可能会存在填充数据adaptation field和ts payload。ts packet header固定是4字节,ts packet payload大小不固定。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加,第一个ts包和最后一个ts包里的adaptation field还不一样,会在“2.1 Ts Packet(Transport Stream Packet)->5、adaptation field(填充数据)”中讨论。
2、ts packet payload可以是PAT、PMT、PES、ES等数据。直播和点播场景中主要接触到的就是上述4种数据,后面会详细介绍。如果需要加密,是在ts->payload加密,不是在ts->pes->pes payload加密。
3、官方定义如下图:
4、ts header主要内容如下:
Field |
Type |
Comment |
sync_byte ( 同步字节) |
8 bit |
固定为0x47,该字段是MPEG-2 TS传送包标识符。 |
transport_error_indicator (传输误差指示符) |
1 bit |
transport_error_indicator = 0:无错误。 transport_error_indicator = 1:表示在相关的传送包中至少有一个不可纠正的错误位,只有在错误纠正之后,该位才能被重新置0。 |
payload_unit_start_indicator (有效载荷单元起始符) |
1 bit |
说明:payload_unit_start_indicator表示有效载荷单元起始符,不表示有效载荷的内容,PID才表示有效载荷的内容。要先获取PID,获取效载荷的内容,再获取payload_unit_start_indicator来获取有效载荷单元起始符。 当TS包带有PES包数据时,payload_unit_start_indicator = 1表示TS包的有效净荷以PES包的第一个字节开始。payload_unit_start_indicator = 0时表示在该传输流数据包中不应开始PES于数据包(此时开始位置可能不是PES头,直接是ES数据)。如果payload_unit_start_indicator设置为1,则只有一个PES数据包在此传输流数据包中开始。 当TS包带有PSI数据时,payload_unit_start_indicator = 1表示TS包带有PSI部分的第一个字节,即第一个字节带有指针pointer_field;payload_unit_start_indicator = 0表示TS包不带有一个PSI部分的第一个字节,即有效净荷中没有指针pointer_field。 空包时,该位置为0。 |
transport_prority (传输优先级) |
1 bit |
transport_prority = 1:关联的数据包是优先级高于具有相同PID且位未设置为1的其它数据包。 |
PID (PID) |
13 bit |
表示传送包的有效净荷中的数据的类型。 PID = 0x0000:节目关联表(program association table, PAT) PID = 0x0001:条件访问表(conditional access table, CAT) PID = 0x0002:传送流描述表(transport stream description table, TSDT) PID = 0x0003~0x000F:保留 PID = 0x0010~0x1FFE:可以分配为network PID, Program map PID(PMT), elementary PID, 或其它 PID = 0x1FFF:空包 |
adaption_field_control (自适应字段控制) |
2 bit |
表示传送流包首部是否跟随有填充数据和/或有效净荷。 adaption_field_control = 00:是保留值 adaption_field_control = 01:负载中只有有效载荷 adaption_field_control = 10:负载中只有填充数据 adaption_field_control = 11:先有填充数据,后有有效载荷 |
continuity_counter (连续计数器) |
4 bit |
随着具有相同PID TS包的增加而增加,达到最大则恢复为0。可以通过该值来判断数据包是否连续。比如音频的continuity_counter序号是1,2,3,4,5,6,但是实际收到的是1,3,4,6,则是说明2,5的包丢了。 在传输流中,重复数据包应具有与原始数据包相同的continuity_counter值。 |
5、adaptation field(填充数据)
(1)有两种情况会在ts packet中插入adaptation field(填充数据):在MPEG-2 TS中,为了传送打包后长度不足188 bytes 的ts packet,或者为了在系统层插入节目时钟参考(program clock reference, PCR),需要在ts packet中插入可变长字节的填充数据。填充数据其中一个重要作用是解决编解码器的音视频同步问题(音频、视频都可以通过pcr来设置时钟,保证音视频同步。但是很多播放器都不通过pcr来同步,都是以音频为基准来设置时钟)。通常情况下,PCR不直接改变解码器的本地时钟,而是作为参考基准来调整本地时钟,使之与PCR趋于一致。
(2)当一帧数据大于188 bytes时,会被拆分成多个ts packet存储,一般这一帧的第一个和最后一个ts packet中存在adaptation field。第一个ts packet中的adaptation field包含了上文提到的时钟参考,而最后一个ts packet中的adaptation field是为了填充ts packet使之达到188 bytes,此时的adaptation field中没有时钟参考。
(3)adaptation field官方定义如下图:
(3)adaptation field主要内容如下:
Field |
Type |
Comment |
adaptation_field_length (填充数据长度) |
8 bit |
填充数据长度标识,表示此字节后面填充数据的长度。其值在0~183 bytes。从上图的官方定义中可以看出,无论是否有填充数据,此字段一定存在。 adaptation_field_length = 0~182:adaptation_field_control = 11,既有填充数据,也有有效载荷。 adaptation_field_length = 183:adaptation_field_control = 10,只有填充数据(此时adaptation_field_length后面占183 bytes,adaptation_field_length占1 byte,ts header占4 bytes)。 |
PCR_flag (PCR标志位) |
1 bit |
PCR_flag = 0:没有PCR。 PCR_flag = 1:有PCR。 |
program_clock_reference_base (节目时钟参考PCR) |
42 bit |
音频、视频都可以通过pcr来设置时钟,保证音视频同步。但是很多播放器都不通过pcr来同步,都是以音频为基准来设置时钟。 |
stuffing_bytes (填充字节) |
Nx8 bit |
填充字节,取值0xff。只填充用,无实际意义。 |
6、ts payload如果存在则其内容是PAT、PMT、PES、ES的其中一种,ts payload也有可能不存在,此时ts packet只有ts header 和 adaptation field。ts packet是188 bytes,ts header是4 bytes,所以 ts payload最多是184 bytes。
Field |
Type |
Comment |
data_byte (数据) |
0~184 bytes |
ts payload。 |
7、内容如下
1、节目关联表,定义了当前TS流中所有的节目,其PID为0x0000,它是PSI的根节点,要查找节目(获取音视频数据)必须从PAT开始。当播放器对视频开始检索分析的时候,针对每个TS 包的header中PID数据进行判断,直到找到PAT表开始的地方进行有效数据起始分析。(网络流媒体(四)———TS流)
2、PAT官方定义如下:
3、字段说明如下:(TS科普16 节目相关表PAT)
Field |
Type |
Comment |
table_id (表id) |
8 bit |
标识一个TS PSI分段的内容是节目关联分段,条件访问分段还是节目映射分段。对于PAT置为0x00。 |
section_syntax_indicator (段语法标志位) |
1 bit |
固定为1(官方解释: The section_syntax_indicator is a 1-bit field which shall be set to '1'. )。 |
0 |
1 bit |
0。 |
reversed (保留位) |
2 bit |
固定为11(每一位都是1,占2 bit所以是11)。 |
section_length (分段长度) |
12 bit |
section_length后面直到CRC_32校验字段的字节数,其值不超过1021。 |
transport_stream_id (传输流id) |
16 bit |
其值由用户规定。该字段充当标签,标识网络内此传输流有别于任何其他多路复用流。 |
version_number (版本号) |
5 bit |
current_next_indicator 设置为‘1’时,version_number 必须为当前有效的节目相关表的版本号。current_next_indicator 设置为‘0’时,version_number 为下一个有效的节目相关表的版本号。 |
current_next_indicator (指示符) |
1 bit |
current_next_indicator = 1:发送的节目相关表为当前有效的。 current_next_indicator = 0:发送的该表尚未有效并且下一个表将生效。 |
section_number (分段号) |
8 bit |
表明该ts packet属于PAT的第几个分段,分段号从0开始。因为PAT可以描述很多PMT信息,所以长度可能比较长,ts packet的长度最多是188bytes,所以可能需要多个ts packt传输一个PAT。 |
last_section_number (最后一个分段号) |
8 bit |
PAT最后的一个分段到序号(分段原因可参考"section_number"),也可以理解为PAT的最大分段数目,一般情况都是一个PAT表由一个ts packet传输。 |
开始循环 |
||
program_number (节目编号) |
16 bit |
节目号为0x0000时表示这是NIT(network_PID),节目号为非0x0000时,表示这是PMT(program_map_PID)。 |
network_PID (网络信息表PID) |
13 bit |
NIT(网络信息表)的PID值,指定应包含的传输流包的PID。network_PID 的值由用户自定义。用于描述整个网络,如多少TS流、频点和调制方式等信息(jsmpeg系列二 基础知识 TS码流 PAT PMT)。 |
program_map_PID (节目映射表PID) |
13 bit |
PMT(节目映射表)的PID值,program_map_PID 的值由用户定义,但仅限于取表2-3 中所指定的值。 |
结束循环 |
||
CRC_32 (校验码) |
32 bit |
校验值。 |
4、获取PMT的PID:在官方定义中,通过循环(上图的红框处)获取每一个program_map_PID,但是循环次数N,却没有定义,那么N是如何确定的呢?
N = (section_length-5-4)/4 字节
分子:根据定义section_length的值是表示transport_stream_id到CRC_32的字节数,减5是transport_stream_id到last_section_number的占5字节,减4是CRC_32占4字节。
分母:循环中network_PID和program_map_PID只能存在一个,所以一个循环占4字节。
5、内容如下
1、节目映射表,该表的PID是由PAT给出的。PMT数据的信息可以理解为这个节目包含的音频和视频信息。其中包含了该路节目由那些流构成,流的类型(视频、音频、数据等)指定节目中视频、音频对应的ID,和当前频道关联在一起的其它数据PID(例如该节目的PCR所对应的PID)。
2、PMT官方定义如下:
3、字段说明如下:
Field |
Type |
Comment |
table_id (表id) |
8 bit |
标识一个TS PSI分段的内容是节目关联分段,条件访问分段还是节目映射分段。对于PMT置为0x02。 |
section_syntax_indicator (段语法标志位) |
1 bit |
固定为1。 |
0 |
1 bit |
0 |
reserved (保留位) |
2 bit |
固定为11(每一位都是1,占2 bit所以是11)。 |
section_length (分段长度) |
12 bit |
section_length后面直到CRC_32校验字段的字节数,其值不超过1021。 |
program_number (节目编号) |
16 bit |
指定program_map_PID 所适用的节目。一个节目定义必须仅在一个TS_program_map_section 内承载。这意味着节目定义从不超过1016(0x3F8)。 |
version_number (版本号) |
5 bit |
为TS_program_map_section 的版本号。当分段内承载的信息发生改变时,版本号应增1 模32。版本号涉及单个节目的定义,并因此涉及单个分段。当current_next_indicator为‘1’时,version_number 应为当前有效的TS_program_map_section 的version_number。current_next_indicator设置为‘0’时,version_number 应为下一个有效的TS_program_map_section 的version_number。 |
current_next_indicator (指示符) |
1 bit |
于‘1’时指示发送的节目相关表为当前有效的。该比特设置为‘0’时,它指示发送的该表尚未有效并且下一个表将生效。 |
section_number (分段号) |
8 bit |
固定值为0x00。 |
last_section_number (最后一个分段号) |
8 bit |
固定值为0x00。 |
reserved (保留位) |
3 bit |
固定为111(每一位都是1,占3 bit所以是111)。 |
PCR_PID (PCR_PID) |
13 bit |
指示应包含PCR字段的ts packet的PID。 |
reserved (保留位) |
4 bit |
固定为1111(每一位都是1,占4 bit所以是1111)。 |
program_info_length (节目描述信息长度) |
12 bit |
表示紧跟在program_info_length后面的N个descriptor()的总长度。 |
开始循环 |
||
descriptor() (描述信息) |
节目元素描述符,详见官方文档。 |
|
循环结束 |
||
开始循环 |
||
stream_type (流类型) |
8 bit |
流类型,标志是Video还是Audio还是Metadata或其它数据。 |
reserved (保留位) |
3 bit |
固定为111(每一位都是1,占3 bit所以是111)。 |
elementary_PID (元素PID) |
13 bit |
指定携带相关元素的ts packet的PID;如果stream_type是video,则elementary_PID表示的是视频包的pid。 |
reserved (保留位) |
4 bit |
固定为1111(每一位都是1,占4 bit所以是1111)。 |
ES_info_length (ES信息长度) |
12 bit |
表示紧跟在ES_info_length后面的N2个descriptor()的总长度。 |
开始循环 |
||
descriptor() (描述信息) |
用于描述节目内容,有视频和音频两种形式,描述了原始流的一些信息。详见下文。 |
|
结束循环 |
||
结束循环 |
||
CRC32 (校验码) |
32b |
校验值。 |
上表中的descriptors()也有很多种,具体可查阅官方文档。本文重点介绍video/audio descriptor,内容如下:
Video stream descriptor:提供了标识视频元素的编码参数的基本信息。
Field |
Type |
Comment |
descriptor_tag |
8 bit |
描述descriptor类型,共有40多种类型,可详见官方文档。比如descriptor_tag = 2是video_stream_descriptor descriptor_tag = 3是audio_stream_descriptor |
descriptor_length |
8 bit |
descriptor_length后面的字节数。 |
multiple_frame_rate_flag |
1 bit |
multiple_frame_rate_flag = 1:视频流中可能存在多个帧速率。 multiple_frame_rate_flag = 0:视频流中仅存在单个帧速率。 |
frame_rate_code |
4 bit |
帧率。 |
MPEG_1_only_flag |
1 bit |
MPEG_1_only_flag = 1:表示视频流仅包含ISO/IEC 11172-2数据。 MPEG_1_only_flag = 0:表示视频流可以包含ITU-T Rec.H.22|ISO/IEC 13818-2视频数据和约束参数ISO/IEC 11172-2视频数据。 |
constrained_parameter_flag |
1 bit |
constrained_parameter_flag = 1:表示视频流不应包含不受约束的ISO/IEC 11172-2视频数据。如果MPEG_1_only_flag设置为“0”,则constrained_parameter_flag应设置为“1”。 constrained_parameter_flag = 0:视频流可以包含受约束的参数和不受约束的ISO/IEC 11172-2视频流。 |
still_picture_flag |
1 bit |
still_picture_flag = 1:表示视频流仅包含静止图片。 still_picture_flag = 0:表示视频流可能包含运动或静止图像数据。 |
profile_and_level_indication |
8 bit |
指明profile和leve。 |
chroma_format |
2 bit |
采样格式,比如01表示4:2:0。 |
frame_rate_extension_flag |
1 bit |
frame_rate_extension_flag = 1时在 ITU-T Rec. H.262 | ISO/IEC 13818-2视屏流中,frame_rate_extension_n、frame_rate_extension_d都是非零值 无论frame_rate_extension_flag为何值ISO/IEC 11172-2视频流的两个字段均设置为零。 |
Reserved |
5 bit |
保留位,固定为11111(每一位都是1,占5 bit所以是11111)。 |
Audio stream descriptor:音频流描述符提供识别音频基本流的编码版本的基本信息。
Field |
Type |
Comment |
descriptor_tag |
8 bit |
描述descriptor类型,共有40多种类型,可详见官方文档。比如descriptor_tag = 2是video_stream_descriptor descriptor_tag = 3是audio_stream_descriptor |
descriptor_length |
8 bit |
descriptor_length后面的字节数。 |
free_format_flag |
1 bit |
free_format_flag = 1:表示音频流可能包含一个或多个bitrate_index设置为“0000”的音频帧。 free_format_flag = 0:则在音频流的任何音频帧中,bitrate_index都不是“0000”(参考ISO/IEC 13818-3的2.4.2.3)。 |
ID |
1 bit |
ID = 1:表示在音频流中的每个音频帧中ID字段设置为“0”(参见ISO/IEC 13818-3的2.4.2.3)。 |
layer |
2 bit |
与层字段相同的方式编码。此字段中指示的层应等于或高于音频流的任何音频帧中指定的最高层。 |
variable_rate_audio_indicator |
1 bit |
variable_rate_audio_indicator = 0:表示比特率字段的编码值在连续音频帧中不应改变,该连续音频帧旨在无中断地呈现。 |
reserved |
3 bit |
保留位,固定为111(每一位都是1,占3 bit所以是111)。 |
4、在PMT表中,有三个循环,循环次数分别是N,N1,N2,官方文档中没有描述这3个数的取值,那么这3个值是如何确定的呢?
(1)N = program_info_length/(1+1+descriptor_length) 字节
分子:program_info_length描述循环的总字节数。
分母:前2个加1是因为descriptor_tag,descriptor_length分别占1字节。descriptor_length描述后面的字节数,所以1个descriptor()占的字节数就是1+1+descriptor_length。
(2)N1 = (section_length-9-program_info_length-4)/(5+ES_info_length) 字节
分子:根据定义section_length的值是表示last_section_number到CRC_32的字节数,减9是last_section_number到program_info_length的占9字节,减program_info_length是紧跟在后面的循环占的字节数,减4是CRC_32占4字节。
分母:加5是stream_type到ES_info_length占的字节数,加ES_info_length是紧跟在后面的循环占的字节数。
(3)N2 = ES_info_length/(1+1+descriptor_length) 字节
分子:ES_info_length描述循环的总字节数。
分母:前2个加1是因为descriptor_tag,descriptor_length分别占1字节。descriptor_length描述后面的字节数,所以1个descriptor()占的字节数就是1+1+descriptor_length。
5、内容如下
1、pes是对编码器原始数据的第一次打包,在这个过程中将es流分组、打包、加入包头信息等操作,其中加入的最主要的信息就是dts,pts。pes流的基本单位是pes packet,pes packet由pes packet header和playload组成。
2、一般视频一帧被打包成一个pes packet,一个pes packet的长度一般都大于ts packet的188字节,所以还是要进行切分,一个pes packet会被拆分成几部分,由多个ts packet携带。此时,只有携带第一个pes packet分组的ts packet才会有pes header,后面的ts packet只有es数据和填充字节,即不是每一个携带音视频数据的ts packet都有pes的打包数据(pes header)。
3、MPEG-2规定一个pes packet必须由整数个ts packet传输,如果承载一个pes packet的最后一个ts packet没有被装满,则用 adaptation field(填充数据)进行填充,如下所示:
第一个ts packet:ts header + (adaptation field) + pes header + 部分es
第二个ts packet:ts header + 部分es
..........
最后一个ts packet:ts header + adaptation field(填充数据)+ 部分es
第一个ts packet如果有adaptation field则包含了同步时钟PCR_flag=1,最后一个ts packet的adaptation field是填充数据,没有同步时钟PCR_flag=0。
4、如果一个pes packet打包为多个ts packet,那么同一个pes的ts packet是排在一起的。
6、PES官方定义如下:
4、主要字段说明如下:
(1)pes packet header
Field |
Type |
Comment |
packet_start_code_prefix (分组起始前缀字段) |
24 bit |
固定为0x000001。 |
stream_id (流标识字段) |
8 bit |
它规定了基本流的号码和类型。 |
PES_packet_length (PES包长度) |
16 bit |
PES_packet_length后面的字节数目。值0表示PES分组长度既没有指定也没有限制。 |
PTS_DTS_flags (PTS DTS标志) |
2 bit |
PTS_DTS_flags = 10:只有PTS。 PTS_DTS_flags = 11:PTS、DTS都有。 PTS_DTS_flags = 00:PTS、DTS都没有。 禁止使用01 |
marker_bit (标志位) |
1 bit |
固定为1。 |
PTS (显示时间) |
33 bit |
显示时间戳,分为3段。 |
DTS (解码时间) |
33 bit |
解码时间戳,分为3段。 |
ES_rate (ES接收速率) |
22 bit |
指定编码器接收pes packet字节的速率。 |
trick_mode_control (特技模式控制) |
3 bit |
表示相关视频的特技方式,包括快进、慢动作、冻结帧、快速反向、慢反向等。 |
上表中stream_id的取值及含义:
(2)pes payload
Field |
Type |
Comment |
PES_packet_data_bytes (PES包数据) |
8*PES_packet_length bit |
音视频数据,由stream_id或PID指示。 |
5、pts、dts计算:在pes中pts、dts都是分成了3段,在分段读取之后需要再拼成整体。以pts为例:(dts同理)
000: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
6、内容如下
1、es才是纯真正音视频数据,裸流,不带pts,类似H264编码的原始数据NALU或者音频AAC等编码原始数据。会有nalu header或adts header。包括视频、音频、sps、pps 等。
2、I帧时,如下图所示
1、找到PAT,获取PMT的PID
通过sync_byte=0x47找到ts packet的起始位置,通过ts header中PID为0x0000找到PAT,也就是ts payload中table_id为0x00的TS包,读取PMT的PID(program_map_PID)。
2、找到PMT,获取流的PID
通过sync_byte=0x47找到ts packet的起始位置,通过ts header中PID在0x0010~0x1FFE中(不固定)确认是PMT,也就是ts payload中table_id为0x02的TS包,读取流类型(stream_type)及携带该类型流的ts packet的PID(elementary_PID)。此时就找到的音频流的PID和视频流的PID,流PID都存储于ts header中的PID字段。
3、获取音视频数据
根据ts header中的PID可以判断出ts payload携带的是音频还是视频,通过ts header中的有效载荷单元起始符(payload_unit_start_indicator),可以判断出ts packet携带的PES是否是一个PES包的第一个分包。如果是PES包的第一个分包,先要找到PES包头,提取时间戳,再跳至ES数据,这就是一帧数据的开始部分。
4、组包
ts header中的有效载荷单元起始符(payload_unit_start_indicator)为1时,就知道这是下一帧的开始了,将前面的所有ES数据组合成一帧数据。开始下一轮组帧。
1、ts packet是188 bytes,ts header是4 bytes,ts payload是0~184 bytes,adaptation field也是0~184 bytes。ts payload、adaptation field都是有可能不存在。
2、adaptation field(填充数据)是独立存在的,既不属于ts header,也不属于ts payload。ts packet的结构可以参考“2.1 Ts Packet”。
3、当一帧数据大于188 bytes时,会被拆分成多个ts packet存储,一般这一帧的第一个和最后一个ts packet中存在adaptation field。第一个ts packet中的adaptation field包含了时钟参考(PCR_flag=1),而最后一个ts packet中的adaptation field是为了填充ts packet使之达到188 bytes,此时的adaptation field中没有时钟参考(PCR_flag=0)。
4、一个ts文件中PAT、PMT是多组的,不仅仅只有文件开始处才有。从文件中间播放时可以找到的就近的PAT、PMT来找到音视频流。
5、直播、点播场景中,解封装ts时程序需要使用到的表格只有PAT、PMT;ts官方文档里还有CAT等其它表格,但是在直播、点播场景中用不到。
6、封装时一个PES包封装的是一帧数据,由于一个PES包大于188 bytes,在存储时一个PES包往往存储于整数个ts packet中。但是只有第一个ts packet含有pes packet header,后面的ts packet只有es数据。并不是所有携带音视频数据的ts packet都含有pes packet header。
7、在封装一个ts文件时,还要对PES包进行分包的操作,在解封装ts文件时,要对PES进行组包的操作。
1、使用场景:
(1)ts流主要是广电领域使用,我们看到的电视节目就是ts流封装,然后再在机顶盒解码解封装和播放。苹果HLS协议的推出,在整个苹果家族产品里面支持都非常友好,安卓阵营的主流浏览器也支持HLS协议。其中HLS协议的码流封装格式也是ts;
(2)TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流。由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格式。(TS和PS的区别)
2、优点:
(1)ts文件封装格式的优点是可以实现多路复用,即将多个音视频数据流封装到同一个TS数据流中,从而节省传输带宽。同时,由于每个TS包的长度固定,也方便数据传输和存储
(2)ts码流由于采用了固定长度的包结构,当传输误码破坏了某一ts包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。
3、缺点:
(1)封装数据多,有两方面原因,由于ts packet大小固定为188 bytes,当数据不足188 bytes时,会有调整字段,此时增加了封装数据的大小。使得文件大小变大。PAT、PMT循环插入在音视频数据中,也增加了封装数据。
(封装格式--TS)
ES–>PES:对ES进行打包和加入包头信息等操作,为码流复合做准备。
PES–>TS:将音频视频和其它数据进行复合,并打包为固定大小的packet,在丢包之后,能够及时纠正并同步,使之具有很强的抵抗传输误码的能力。
ts header中的有效载荷单元起始符(payload_unit_start_indicator)来判断,payload_unit_start_indicator=1时,就知道这是下一帧的开始了,将前面的所有ES数据组合成一帧数据。然后开始下一轮组帧。
1、有两种情况会在ts packet中插入adaptation field(填充数据):在MPEG-2 TS中,为了传送打包后长度不足188 bytes 的ts packet,或者为了在系统层插入节目时钟参考(program clock reference, PCR),需要在ts packet中插入可变长字节的填充数据。填充数据其中一个重要作用是解决编解码器的音视频同步问题(音频、视频都可以通过pcr来设置时钟,保证音视频同步。但是很多播放器都不通过pcr来同步,都是以音频为基准来设置时钟)。通常情况下,PCR不直接改变解码器的本地时钟,而是作为参考基准来调整本地时钟,使之与PCR趋于一致。
2、当一帧数据大于188 bytes时,会被拆分成多个ts packet存储,一般这一帧的第一个和最后一个ts packet中存在adaptation field。第一个ts packet中的adaptation field包含了上文提到的时钟参考,而最后一个ts packet中的adaptation field是为了填充ts packet使之达到188 bytes,此时的adaptation field中没有时钟参考。
TS和PS的区别
1、可以直接参考上述文章,也可以见“一、文件结构概述->4->ts packet的大小为什么是固定的?”是从上述文章提炼出来的内容。
1、metadata的内容由sps、pps获取,而sps、pps存储在nalu里,也就是es里(HLS协议解析(含有pes分析)_hls 解析视频_juruiyuan111的博客-CSDN博客)