多媒体文件格式之TS
1. 综述
TS:全称为 MPEG2-TS。TS 即 "Transport Stream" 的缩写。它是分包发送的,每一个包长为 188 字节(或 204 个字节的包,在 188 个字节后加上 16 字节的 CRC 校验数据)。包的结构为,包头为 4 个字节,负载为 184 个字节。在 TS 流里可以填入很多类型的数据,如视频、音频、自定义信息等。MPEG2-TS 主要应用于实时传送的节目,比如实时广播的电视节目。MPEG2-TS 格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。简单地说,将 DVD 上的 VOB 文件的前面一截 cut 掉(或者是数据损坏了)就会导致整个文件无法解码,而电视节目是任何时候打开电视机都能解码的。
1.1 TS 流形成过程
- 将原始音视频数据压缩之后,压缩结果组成一个基本码流(ES)。
- 对 ES(基本码流)进行打包形成 PES。
- 在 PES 包中加入时间戳信息(PTS/DTS)。
- 将 PES 包内容分配到一系列固定长度的传输包(TS Packet)中。
- 在传输包中加入定时信息(PCR)。
- 在传输包中加入节目专用信息(PSI)。
- 连续输出传输包形成具有恒定比特率的 MPEG2-TS 流。
1.2 TS 流的解析过程
- 从复用的 MPEG2-TS 流中解析出 TS 包;
- 从 TS 包中获取 PAT 及对应的 PMT(PSI 中的表格);
- 从而获取特定节目的音视频 PID;
- 通过 PID 刷选出特定音视频相关的 TS 包,并解析出 PES;
- 从 PES 中读取到 PTS/DTS,并从 PES 中解析出基本码流 ES;
- 将 ES 交给解码器,获得压缩前的原始音频数据。
TS 文件分为三层:
- TS 层:Transport Stream,是在 pes 层的基础上加入数据流的识别和传输必须的信息。
- PES 层: Packet Elemental Stream,是在音视频数据上加了时间戳等对数据帧的说明信息。
- ES 层:Elementary Stream,即音视频数据。
TS 文件格式如下图
2. TS 层:Transport Stream
ts 包大小固定为 188 字节,ts 层分为三个部分:ts header、adaptation field、payload。ts header 固定 4 个字节;adaptation field 可能存在也可能不存在,主要作用是给不足 188 字节的数据做填充;payload 是 pes 数据。
2.1 TS Header
TS 包的包头提供关于传输方面的信息:同步、有无差错、有无加扰、PCR(节目参考时钟)等标志。TS 包的包头长度不固定,前 32bits(4字节)固定,后面可能跟有自适应字段(适配域)。32bits(4个字节)是最小包头。TS Header 的结构如下:
- sync_byte(8bits):同步字节,是包中的第一个字节,TS 包以固定的 8bit 的同步字节 0x47 开始,表示后面是一个 TS 分组(注:后面包中的数据不会出现 0x47 的),用于建立发送端和接收端包的同步。
- transport_error_indicator(1bit):传输错误标志位,为 1 表示在相关的传输包中至少有一个不可纠正的错误位。当被置 1 后,在错误被纠正之前不能重置为 0.
- payload_unit_start_indicator(1bit):负载起始标识,针对不同的负载,有不同的含义。
- PES:置为 1,标识 TS 包的有效载荷以 PES 包的第一个字节开始,即此 TS 包为 PES 包的起始包,且此 TS 分组中有且只有一个 PES 包的起始字段;置为 0,表示 TS 包不是 PES 包的起始包,是后面的数据包。
- PSI:置为 1,表示 TS 包中带有 PSI 数据分段的第一个字节,即这个 TS 包是 PSI Section 的起始包,则此 TS 包的负载的第一个字节带有 pointer_field,用来指示 PSI 数据在 payload 中的位置;置为 0,表示 TS 包不带有 PSI Section 的第一个字节,即此 TS 包不是 PSI 的起始包,即在有效负载中没有 pointer_filed,有效负载的开始就是 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 包结束条件。
- transport_priority(1bit):传输优先标志,为 1 表明当前 TS 包的优先级比其他具有相同 PID,但此位没有被置 1 的 TS 包高。
- PID(13bits):指示有效负载中数据的类型。PID 是识别 TS 包的重要参数,用来识别 TS 包所承载的数据。在 TS 码流生成时,每一类业务(视频,音频,数据)的基本码流均被赋予一个不同的识别号 PID,解码器借助于 PID 判断某一个 TS 包属于哪一类业务的基本码流。PID 值 0x0000~0x000F 保留。其中 0x0000 为 PAT 保留;0x0001 为 CAT 保留;0x1ffff 为分组保留,即空包。标准中定义的 PID 分配见如下表:
PID 值 | 描述 |
---|---|
0x00 | PAT(Program Association Table) |
0x01 | CAT(Conditional Access Table) |
0x02 | TSDT(Transport Stream Description Table) |
0x03 ~ 0x0f | ReservedStart ~ ReservedEnd |
0x12 | EIT,ST |
0x13 | RST,ST |
0x14 | TDT,TOT,ST |
0x10~0x1ffe | 自定义 PID,可用于 PMT 的 pid、network 的 pid 或者其他目标 |
0x1fff | 空包 |
-
| PCR 的 PID 可以选择 0、1 或者 0x10~0x1FFE 中的任意值
-
transport_scrambling_control(2bits):有效负载加密模式标志,00 表示未加密。如果传输包包头中包括调整字段,不应被加密。其他取值含义是用户自定义的。
-
adaption_field_control(2bits):调整字段标志,表示此 ts 首部是否跟随调整字段和负载数据。占 2bits,字段值含义如下:
- 00:保留;
- 01:表示无调整字段,只有有效负载数据;
- 10:表示只有调整字段,无有效负载数据;
- 11:表示有调整字段,且其后跟随有效负载数据;
- 注:空包此字段应为 10。调整字段中的前一个字节表示调整字段的长度 length,有效载荷开始的位置应再偏移 [length] 个字节。
-
continuity_counter(4bits):循环计数器,用于对传输误码进行检测。在发送端对所有的包都做 0~15 的循环计数,在接收终端,如发现循环计数器的值有中断,表明数据在传输中有丢失。
ts 层的内容是通过 PID 值来标识的,主要内容包括:PAT 表、PMT 表、音频流、视频流。解析 ts 流要先找到 PAT 表,只要找到 PAT 就可以找到 PMT,然后就可以找到音视频流了。PAT 表的和 PMT 表需要定期插入 ts 流,因为用户随时可能加入 ts 流,这个间隔比较小,通常每隔几个视频帧就要加入 PAT 和 PMT。PAT 和 PMT 表是必须的,还可以加入其它表如 SDT(业务描述表)等,不过 hls 流只要有 PAT 和 PMT 就可以播放了。
- PAT 表:主要的作用就是指明了 PMT 表的 PID 值。
- PMT 表:主要的作用就是指明了音视频流的 PID 值。
- 音频流/视频流:承载音视频内容。
2.2 adaptation field
参考博文:
TS协议解析第四部分(adaptation field)
2.2.1 adaptation field 综述
调整字段,一般在以下两种情况会在 TS 流中添加自适应段,并且此时的 TS header 中 adaptation_filed_control == 1x 时,以下字段才会存在:
- 封装 TS 数据的时候,视频或者音频数据不够 184 个字节的时候,使用该段来指明调整字段 0xFF 的长度,此时的自适应区的 PCR 标志为 0.
- 对于每一帧视频数据进行封装的时候,需要并且是必须在 TS header 之后添加自适应区间,此时,自适应区间中最重要的部分是 PCR 相关数据,PCR 主要用来实现解码端的时钟同步。此时自适应区的 PCR 标志为 1.
在 MPEG2-TS 中,为了传送打包后长度不足 188 字节(包括包头)的不完整 TS Packet,或者为了在系统层插入节目时钟参考 PCR 字段,需要在 TS 包中插入可变长度字段的调整字段。调整字段包括对较高层次的解码功能有用的相关信息,调整字段的格式基于采用若干标识符,以表示该字段的某些特定扩展是否存在。调整字段由 1B 调整字段长度、不连续指示器、随机存取器、PCR 标志符、基本数据流优先级指示器、拼接点标志符、传送专用数据标志、调整字段扩展标志以及有相应标志符的字段组成。
调整字段是一个可变长的域,是由 TS Header 中的 adaption_field_control(调整字段控制)来标识的。当利用包头的信息将各基本比特流提取出来后,调整字段提供基本比特流解码所需的同步及时序等功能,以及编辑节目所需的各种机制,如本地解码插入等。
自适应区的长度要包含传输错误指示符标识的一个字节。pcr 是节目时钟参考,pcr、dts、pts 都是对同一个系统时钟的采样值,pcr 是递增的,因此可以将其设置为 dts 值,音频数据不需要 pcr。如果没有字段,ipad 是可以播放的,但 vlc 无法播放。打包 ts 流时 PAT 和 PMT 表是没有 adaptation field 的,不够的长度直接补 0xff 即可。视频流和音频流都需要加 adaptation field,通常加在一个帧的第一个 ts 包和最后一个 ts 包里,中间的 ts 包不加。如下图所示:
2.2.2 adaptation field 句法示意图
adaptation field 层次图
adaptation field 句法图
- adaption_field_length(8bits):adaptation_field_length 的值指定了其后的 adaptation field 的字节数。值为 0 是为了在 TS Packet 中插入单个的填充字节。当 adaptation_field_control 为 '11'(表示 adaptation field 和 payload 都有)时,adaptation_field_length 的值将为 0~182 之间。当 adaptation_field_control 为 '10'(表示只有 adaptation field,没有 payload)时,adaptation_field_length 的值将为 183。对于携带有 PES Packet 的 TS Packet,当 PES packet 数据不足以填满 TS Packet 的负载时必须要有填充字节。填充是通过规定 adaptation field 的长度比 adaptation field 中 data elements 的总长度还要长来实现的,以便使负载在容纳完 PES packet 数据后还有剩余空间,在 adaptation field 中这 extra space 是使用填充字节来填满的。这是允许携带 PES packet 数据的 TS packet 进行填充的唯一的方法,对于 TS Packet 携带 PSI 可供选择的填充方法可参考 2.4.4.
- discontinuity_indicator(1bit):
- 为 1 指示当前的 TS Packet 的不连续状态为 true;为 0,则当前的 TS packet 的不连续状态为 false。这个 discontinuity_indicator 用于指示两种类型的不连续性:系统时基的不连续性和 continuity_counter(连续计数器)的不连续性。
- 通过使用指定为 PCR_PID 的 PID 的 TS Packet 中的 discontinuity_indicator 来指示系统时基的不连续性(参考 2.4.4.9)。当指定为 PCR_PID 的 PID 的 TS packet 的不连续状态为 true 时,具有相同 PID 的 TS Packet 的下一个 PCR 代表关联节目的新的系统时钟的样本。系统时基的不连续被定义为当包含一个新系统时基的 PCR 的 packet 的第一个字节到达 T-STD 输入时的时刻。在该 packet 中该 discontinuity_indicator 将会被设置为 1,指示发生系统时基的不连续。
- random_access_indicator(1bit):
- random_access_indicator 是 1bit 的字段,它指示当前的 TS packet 和后续可能具有相同 PID 的 TS packet 包含一些支持此时可以随意访问的信息。
- 当该字段被设置为 1 时,如果 PES 流类型为 1 或 2(参考 Table 2-29),则具有相同 PID 的 TS packet 的下一个 PES packet 的负载开始将会包含 video sequence header 的第一个字节,或者如果 PES 流类型为 3 或 4,则包含 audio frame 的第一个字节。
- 在视频的情况下, 包含 video sequence header 后的第一个图像的 PES packet 中将会有一个 presentation timestamp。
- 在音频的情况下,包含音频帧的第一个字节的 PES packet 中将会有一个 presentation timestamp。
- 在 PCR_PID 中,random_access_indicator 只可以在包含 PCR 字段的 TS packet 中被设置为 1.
- elementary_stream_priority_indicator(1bit):
- 该字段指示在具有相同 PID 的 packet 中,在该 TS packet 的负载内携带的 ES 数据(即音视频数据)的优先级。为 1 指示该负载比其他的 TS packet 的负载具有更高的优先级。
- 在 video 的情况下,如果 payload 包含一个或多个来自 intra-coded slice 的字节,则该字段可能会被设为 1。
- 值为 0 表示该 payload 与其他没有设置该字段为 1 的 pakcet 具有相同的优先级。
- PCR_flag(1bit):为 1 指示 adaptation field 包含两部分编码的 PCR 字段。为 0 指示 adaptation field 没有包含任何的 PCR 字段。
- OPCR_flag(1bit):为 1 指示 adaptation field 包含两部分编码的 OPCR 字段;为 0 指示 adaptation field 没有包含任何 OPCR 字段。
- splicing_point_flag(1bit):为 1 指示 splice_countdown 将会在相关 adaptation field 中指定的拼接点出现;为 0 指示 splice_countdown 在 adaptation field 中不存在。
- transport_private_data_flag(1bit):为 1 指示 adaptation field 包含一个或多个 private_data 字节;为 0 指示 adaptation field 中没有包含任何的 private_data 字节。
- adaptation_field_extension_flag(1bit):为 1 表示有 adaptation field 扩展出现;为 0 表示 adaptation field 中不存在 adaptation field 扩展。
- 当 PCR_Flag == 1 时,有如下字段:
- program_clock_reference_base(33bits):
- const1_value0(6bits):保留字段,固定为 1.
- program_clock_reference_extension(9bits):
- 节目时钟参考(PCR)是一个在两部分编码的 42bit 字段(program_clock_reference_base 和 program_clock_reference_extension)。
- 第一部分 program_clock_reference_base 是一个 33bit 字段,其值由 PCR_base 指定。
- 第二部分 program_clock_reference_extension 是一个 9bit 字段,其值由 PCR_ext 指定。
- PCR 指示包含 program_clock_reference_base 最后一个 bit 的字节在系统目标解码器的输入端的预期到达时间。
- 当 OPCR_flag == 1 时,有如下字段:
- original_program_clock_reference_base(33bits):
- const1_value2(6bits):保留字段,固定为 1.
- original_program_clock_reference_extension(9bits):
- 当 splicing_point_flag == 1 时,有如下字段:
- splice_countdown(8bits):
- 当 transport_private_data_flag == 1 时,有如下字段:
- transport_private_data_length(8bits):
- transport_private_data:[transport_private_data_length]bytes
- 当 adaptation_field_extension_flag == 1 时,有如下字段:
- adaptation_field_extension_length(8bits):
- ltw_flag(1bit):为 1 表示有 ltw_offset 字段存在。
- piecewise_rate_flag(1bit):为 1 表示有 piecewise_rate 字段存在
- seamless_splice_flag(1bit):
- const1_value1(5bits):保留字段,固定为 1.
- 当 ltw_flag == 1 时,有如下字段:
- ltw_valid_flag(1bit):
- ltw_offset(15bits):
- 当 piecewise_rate_flag == 1 时,有如下字段:
- reserved(2bits):保留字段
- piecewise_rate(22bits):
- 当 seamless_splice_flag == 1 时,有如下字段:
- splice_type(4bits):
- DTS_next_AU0(3bits):
- marker_bit0(1bit):
- DTS_next_AU1(15bits):
- marker_bit1(1bit):
- DTS_next_AU2(15bits):
- marker_bit2(1bit):
3. PES 层: Packet Elemental Stream
参考博文:
PES,TS,PS,RTP等流的打包格式解析之PES流
pes 层是在每一个视频/音频帧上加入了时间戳等信息。PES 包是由固定包头,可选包头和负载三部分组成的,其中固定包头 6 个字节,PES 固定包头中 PES_packet_length 字段为 16bits,因此一个 PES 包的最大长度为 65535 字节,也即一帧数据可被分为多个 PES 包。
PES 层大致格式如下图:
PES 层句法示意框图
PES 层详细句法图
PES 固定包头(6字节)
- packet_start_code_prefix(24bit):该字段联合下面的 stream_id 构成了一个 packet 的起始码,指示 packet 的开始。该字段固定为 0x000001。
- stream_id(8bits):PES 包中的负载流类型。一般视频为 0xe0,音频为 0xc0。
- PES_packet_length(16bits):PES 包长度,包括此字段后的可选包头和负载的长度。
PES 可选包头
- PES_scrambling_control(2bits): 加密模式,00 未加密;01 或 10 或 11 有用户定义.
- PES_priority(1bit):有效负载的优先级,该字段为 1 的 PES packet 的优先级比为 0 的 PES packet 高.
- data_alignment_indicator(1bit):数据定位指示器.
- copyright(1bit):版本信息,1 为有版权,0 无版权.
- original_or_copy(1bit):原始或备份,1 为 原始,0 为备份.
- PTS_DTS_flags(2bits):10 表示 PES 头部有 PTS 字段,11 表示有 PTS 和 DTS 字段,00 表示都没有,10 被禁止.
- ESCR_flag(1bit):1 表示 PES 头部有 ESCR 字段,0 则无该 ESCR 字段.
- ES_rate_flag(1bit):1 表示 PES 头部有 ES_rate 字段,0 表示没有.
- DSM_trick_mode_flag(1bit):1 表示有一个 8bit 的 track mode 字段,0 表示没有.
- additional_copy_info_flag(1bit):1 表示有 additional_copy_info 字段,0 表示没有.
- PES_CRC_flag(1bit):1 表示 PES 包中有 CRC 字段,0 表示没有.
- PES_extension_flag(1bit):1 表示 PES 头部中有 extension 字段存在,0 表示没有.
- PES_header_data_length(1bit):指定在 PES 包头部中可选头部字段和任意的填充字节所占的总字节数。可选字段的内容由上面的 7 个 flag 来控制的.
- 当 PTS_DTS_flags == '10' 时,有 PTS
- 当 PTS_DTS_flags == '11' 时,有 PTS 和 DTS
- PTS(presentation time)显示时间戳和DTS(decoding time stamp)解码时间戳,是用来音视频同步的,DTS/PTS是相对SCR(系统参考)的时间戳,是以 90000 为单位,PTS/DTS 到 ms 的转换公式是 PTS/90,系统时钟频率为 90Khz,所以转换到秒为 PTS/90000。
- PTS(presentation time)显示时间戳和DTS(decoding time stamp)解码时间戳,是用来音视频同步的,DTS/PTS是相对SCR(系统参考)的时间戳,是以 90000 为单位,PTS/DTS 到 ms 的转换公式是 PTS/90,系统时钟频率为 90Khz,所以转换到秒为 PTS/90000。
- 当 ESCR_flag == '1' 时,有 ESCR
- ESCR 字段占位 48bit,由 33bit 的 ESCR_base 字段和 9bit 的 ESCR_extension 字段组成.
- ESCR_extension(9bits):周期数,取值范围 0~299,循环一次,base + 1
- 当 ES_rate_flag == '1' 时,有 ES_rate
- 目标解码器接收 PES 分组字节速率,禁止为 0,占 24bits.
- 当 DSM_trick_mode_flag == '1' 时,有 track mode control
- trick_mode_control:表示哪种 trick mode 被应用于相应的视频流,占 8bits;其中 trick_mode_control 占前 3 个 bit,根据其值后面有 5 个bit的不同内容,具体可看上图。
- 当 additional_copy_info_flag == '1' 时,有 additional_copy_info
- additional_copy_info(7bits):表示和版权相关的私有数据.
- 当 PES_CRC_flag == '1' 时,有 previous_PES_packet_CRC
- previous_PES_packet_CRC(16bits):CRC 校验值
- 当 PES_extension_flag == '1' 时,有 PES 扩展字段
- PES_private_data_flag(1bit):1 表示有 PES_private_daa 存在,0 则无
- PES_private_data(128bits):私有数据
- pack_header_field_flag(1bit):1 表示有 pack_header_field
- pack_field_length(8bits):指示 pack_header() 的大小
- pack_header()
- program_packet_sequence_counter_flag(1bit):1 表示有 program_packet_sequence_counter
- program_packet_sequence_counter(7bits)
- MPEG1_MPEG2_identifier(1bit):1 指示这个 PES packet 携带的信息来自 ISO/IEC 11172-1 流;0 指示这个 PES packet 携带的信息来自节目流.
- original_stuff_length(6bits): 指定在原始的
ITU-T Rec. H.222.0 | ISO/IEC 13818-1
PES packet 头或者原始的ISO/IEC 11172-1
packet 头中使用的填充字节大小.
- P-STD_buffer_flag(1bit):1 表示有 P-STD_buffer
- PES_extension_field_length(7bits)
- stream_id_extension_flag(1bit)
- Reserved(3bits):保留字段
- PES_extension_flag_2(1bit): 1 表示有 PES_extension_field_length 和 associated 字段
- PES_private_data_flag(1bit):1 表示有 PES_private_daa 存在,0 则无
PES 包实例分析
PES 包具体封装过程可参考:
SRS之SrsTsContext::encode_pes详解
假设封装的为通过 FLV 文件发送过来的视频包,即一个视频 tag。并且该视频 tag 的帧类型为 1(即 KeyFrame),则将该视频数据的部分数据(因为一个 TS packet 固定为 188 字节,因此对于一个视频帧,将会被分成多个包来封装)封装后的第一个 PES 包如下图所示。
1. TS packet 之 TS header
ts header 固定为 4 字节。
47 41 00 30
- sync_byte(8bits):同步字节,固定为 0x47。
- transport_error_indicator(1bit):传输错误标志位,为 0。
- payload_unit_start_indicator(1bit):负载单元起始标识,为 1,对于负载为 PES,则标识 TS 包的有效负载以 PES 包的第一个字节开始,即此 TS 包为 PES 包的起始包,且此 TS 包中有且只有一个 PES 包有该起始字段。
- transport_priority(1bit):传输优先级,为 0,即优先级低。
- PID(13bits):标识负载的数据类型,为 0x100,在 SRS 中该 0x100 PID 标识为视频。
- transport_scrambling_control(2bits):传输加密模式,为 0,即不加密。
- adaption_field_control(2bits):调整字段标识,为 '11',标识同时有 adaptation field 和 payload。
- continuity_counter(4bits):循环计数器,用来对传输误码进行检测。为 0,因为这是第一个包。
2. TS packet 之 adaptation field
首先,先看 adaption_field_length 的值是否大于 0,大于 0 才表示有 adaptation field。
07
- adaption_field_length(8bits):adaptation field 的总字节数,不包括该 adaption_field_length 所占的字节,为 7 字节。
adaption_field_length 的值为 7,因此 adaptation field 的总字节数为 7 字节。
10 00 00 00 00 7E 00
- discontinuity_indicator(1bit):不连续性指示器,为 0,表示当前 TS packet 为连续状态,即 continuity_counter 没有被中断。
- random_access_indicator(1bit):随意访问指示,为 0。
- elementary_stream_priority_indicator(1bit):es 优先级,为 0,即低优先级。
- PCR_flag(1bit):为 1,表示有 pcr 信息。
- OPCR_flag(1bit):为 0,即无 OPCR。
- splicing_point_flag(1bit):为 0,即无 splice_countdown 字段。
- transport_private_data_flag(1bit):为 0,即无 private_data。
- adaptation_field_extension_flag(1bit):为 0,即无 adaptation field 扩展。
- PCR_flag == 1
- program_clock_reference_base(33bits):为 0。
- const1_value0(6bits):保留字段,固定为 1。
- program_clock_reference_extension(9bits):为 0.
3. TS packet 之 payload,即 PES
PES 包可分为三部分:pes 固定头,pes 可选头,pes 负载。
3.1 pes 之 固定头(6bytes)
00 00 01 E0 46 34
- packet_start_code_prefix(24bits):PES 包起始码,固定为 0x000001。
- stream_id(8bits):PES 包中负载的流类型,为 0xe0,表示为视频流。
- PES_packet_length(16bits):PES 包长度,包括此字段后的可选包头和有效负载的长度,为 0x4634,即 17972 字节。
3.2 pes 之 可选包头
80 C0 0A 31 00 01 2E 69 11 00 01 00 01
- const2bits(2bits):保留字段,固定为 '10'。
- PES_scrambling_control(2bits):PES 加密控制,为 '00',即不加密。
- PES_priority(1bit):PES 优先级,为 0,即优先级低。
- data_alignment_indicator(1bit):数据定位指示符,为 0。
- copyright(1bit):版权,为 0,即无版权。
- original_or_copy(1bit):原始或备份,为 0,即备份。
- PTS_DTS_flags(2bits):为 '11', 表示同时有 DTS 和 PTS。
- ESCR_flag(1bit):为 0,即 PES 包中无 ESCR 字段。
- ES_rate_flag(1bit):为 0,即 PES 头部无 ES_rate 字段。
- DSM_trick_mode_flag(1bit):为 0,即 PES 头部无 track mode。
- additional_copy_info_flag(1bit):为 0,即 PES 头部无 additional_copy_info。
- PES_CRC_flag(1bit):为 0,即 PES 头部无 CRC。
- PES_extension_flag(1bit):为 0,即 PES 头部无 PES 扩展。
- PES_header_data_length(8bits):指示 PES 头部中该字段后余下的字节数,为 0x0A,即 10 字节。
- PTS_DTS_flags == '11',同时有 DTS 和 PTS
- pts(33bits):为 0x1734,十进制为 5940(对于视频,pts=dts+cts)。下面为封装 pts 时的格式:
- constvalue0(4bits):固定为 '0011'。
- PTS [32..30](3bits)
- constvalue1(1bit):固定为 '1'
- PTS [29..15](15bits)
- constvalue1(1bit):固定为 '1'
- PTS [14..0](15bits)
- constvalue1(1bit):固定为 '1'
- dts(33bits):为 0,封装格式与 pts 一致,除了开始的 4 bits 固定为 '0001'。
3.3 pes 之 payload,即 es(音视频数据)
00 00 00 01 09 F0 00 00 01 06 ...
- fresh_nalu_header(32bits):若为 ts message 封装的第一个 pes 包,则固定为 0x00000001(针对视频数据)
- aud(16bits):nal_unit_type 为 6,固定为 0x09 0xf0。
- cont_nalu_header(24bits):固定为 0x000001,非该 ts message 的第一个 nalu 下,其余各 nalu 之间都是用 0x000001 分隔的。
- nalu header(8bits):为 0x06,低 5 bits 为表示 nalu_type,为 6,即 SEI。
- ...
下图为封装的第二个 pes 包(因为该 ts message 的视频数据没有封装完)
第二个 pes 包及该当前 ts message 封装的 pes 包都仅有两部分:固定 4 字节的 ts header,以及 payload(视频数据)。
1. ts header(4bytes)
47 01 00 11
- sync_byte(8bits):同步字节,固定为 0x47。
- transport_error_indicator(1bit):传输错误标志位,为 0。
- payload_unit_start_indicator(1bit):负载单元起始标识,为 0,表示该 TS 包不是 PES 包的起始包,是后面的数据包。
- transport_priority(1bit):传输优先级,为 0,即优先级低。
- PID(13bits):标识负载的数据类型,为 0x100,在 SRS 中该 0x100 PID 标识为视频。
- transport_scrambling_control(2bits):传输加密模式,为 0,即不加密。
- adaption_field_control(2bits):调整字段标识,为 '01',标识仅有 payload。
- continuity_counter(4bits):循环计数器,用来对传输误码进行检测。为 1,这是相同 PID 的第二个 pes 包,会自动加 1。
2. payload
接下来的数据即为视频数据,承接上一个 pes 包的视频数据。
4. ES 层:Elementary Stream
es 层指的就是音视频数据。
打包 es 层数据时 pes 头和 es 数据之间要加入一个 aud(type=9) 的 nalu,关键帧 slice 前必须要加入 sps(type=7) 和 pps(type=8) 的 nalu,而且是紧邻的。如下图所示:
4.1 视频数据
对于视频数据打包为 es 包时,是按照 Annexb 格式进行封装的。即在每一个 NALU 前都需要插入 0x00000001(4字节,首次开始打包该视频帧为 ES 时插入 4 字节)或 0x000001(3 字节,普通情况下插入该分隔符)。
the first ts message of apple sample:
annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)(AUD)
annexb 3B header, 12B nalu(nal_unit_type:6)(SEI)
annexb 3B header, 19B sps(nal_unit_type:7)(SPS)
annexb 3B header, 4B pps(nal_unit_type:8)(PPS)
annexb 3B header, 2762B nalu(nal_unit_type:5)(IDR)
the second ts message of apple ts sample:
annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)(AUD)
annexb 3B header, 21B nalu(nal_unit_type:6)(SEI)
annexb 3B header, 379B nalu(nal_unit_type:1)(non-IDR,P/B)
annexb 3B header, 406B nalu(nal_unit_type:1)(non-IDR,P/B)
5. PSI
参考博文
在 MPEG-2 中定义了节目特定信息(PSI),PSI 用来描述传送流的组成结构,在 MPEG-2 系统中担任及其重要的角色,在多路复用中尤为重要的是 PAT 表和 PMT 表。PAT 表给出了一路 MPEG-2 码流中有多少套节目,以及它与 PMT 表 PID 之间的对应关系;PMT 表给出了一套节目的具体组成情况与其视频、音频等 PID 对应关系。PSI 提供了使接收机能够自动配置的信息,用于对复用流中的不同节目流进行解复用和解码。PSI 表包括如下 4 种:
PAT/PMT 的具体封装过程可参考:
5.1 PAT(Program Association Table):节目关联表
PAT 表用 MPEG 指定的 PID (00)标明,通常用 PID=0 表示。它的主要作用是针对复用的每一路传输流,提供传输流中包含哪些节目、节目的编号以及对应节目的节目映射表(PMT)的位置,即 PMT 的 TS 包的包标识符(PID)的值,同时还提供网络信息表(NIT)的位置,即 NIT 的 TS 包的包标识符(PID)的值。
PAT 表定义了当前 TS 流中所有的节目,是 PSI 信息的根节点,要查找节目必须从 PAT 表开始。
PAT 格式如下图
如下图是一段 PAT 表实际数据的示例
以下根据该图来具体分析 PAT。
5.1.1 ts header
47 40 00 10
- sync_byte(8bytes):0x47,同步字节。
- transport_error_indicator(1bit):0, 没有传输错误。
- payload_unit_start_indicator(1bit):1, 当前 TS 包为 PAT,即表示 TS 包带有 PSI 数据,因此这里为 1,表示 TS 包带有 PSI 部分的第一个字节,即第一个字节带有指针 pointer_field。
- transport_priority(1bit):0, 传输优先级低。
- PID(13bit):0x0000,表明这是 PAT 表信息。
- transport_scrambling_control(2bits):00,Not scrambled。
- adaption_field_control(2bits):01,No adaptation_field, payload only。
- continuity_counter(4bits):0000, 包递增计数器。
5.1.2 ts payload
首先根据前面 ts header 的 payload_unit_start_indicator 是否为 1,表明具体负载数据前是否存在 pointer_field。由前可知,payload_unit_start_indicator 为 1,因此,首先会有一个 1 字节的 pointer_field 字段。
1. ts payload 之 pointer_filed
00
- pointer_field(8bits):0。 当 TS 头部中的 payload_unit_start_indicator 为 1 且负载是 PSI 信息时:其值为此字段之后到有效负载第一个字节(不含)之间的字节数。当其为 0 时,表示下一个字节即是有效负载的第一个字节。因此,这里 pointer_field 为 0,即表示下一个字节即是有效负载的第一个字节。
2. ts payload 之 有效负载:PAT
下面为 PAT 数据的开始固定的 8 个字节。
00 00 B0 0D 00 01 C1 00 00
- table_id(8bits):0。表示 PST 的类型,0x00 表示为 PAT。
- section_syntax_indicator(1bit):1。固定为 1。
- zero(1bit):'0'。固定为0.
- reserved(2bits):'11'。保留位,置 1。
- section_length(12bits):十进制为 13。高 2 bit 应为 00,表明此字段之后的整个分段的字节数,包含 CRC 32.
- trasport_stream_id(16bits):十进制为 1。用于在一个网络中从其他的多路复用中识别此传送流,其值用户自定义。
- reserved(2bits):十进制为 1。保留位,置 1.
- version_number(5bits):十进制为 0。PAT 版本号,PAT 每改变一次,版本号加 1。当 current_next_indicator 为 1 时,version_number 为当前 PAT 版本号,否则为下一可用 PAT 版本号。
- current_next_indicator(1bit):十进制为 1。其值为 1 时,当前 PAT 可用。为 0 时,当前 PAT 不可用。
- section_number(8bits):十进制为 0。当前 PAT 分段号码,PAT 第一分段号码应为 0.
- last_section_number(8bits):十进制为 0。PAT 最后分段号码。
3. PAT 之节目循环
00 01 F0 01
- program_number(16bits):0x0001, 节目号,当其为 0 时,接下来的 PID 为网络 PID。当其为 1 时,表示这是 PMT,因此接下来的 PID 为 PMT。
- reserved(3bits):固定为 '111'。
- program_map_PID(13bits):0x1001,节目号 PMT 对应内容的 PID 值。
4. PAT 之 CRC
2E 70 19 05
- CRC32(32bits):整个 PAT 的校验码,从 table_id 至 program_map_PID。
5.2 PMT(Program Map Table):节目映射表
节目映射表指明该节目包含的内容,即该节目由那些流组成,这些流的类型(音频、视频、数据),以及组成该节目的流的位置,及对应的 TS 包的 PID 值,每路节目的节目时钟参考(PCR)字段的位置。