多媒体文件格式之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 流形成过程

  1. 将原始音视频数据压缩之后,压缩结果组成一个基本码流(ES)。
  2. 对 ES(基本码流)进行打包形成 PES。
  3. 在 PES 包中加入时间戳信息(PTS/DTS)。
  4. 将 PES 包内容分配到一系列固定长度的传输包(TS Packet)中。
  5. 在传输包中加入定时信息(PCR)。
  6. 在传输包中加入节目专用信息(PSI)。
  7. 连续输出传输包形成具有恒定比特率的 MPEG2-TS 流。

1.2 TS 流的解析过程

  1. 从复用的 MPEG2-TS 流中解析出 TS 包;
  2. 从 TS 包中获取 PAT 及对应的 PMT(PSI 中的表格);
  3. 从而获取特定节目的音视频 PID;
  4. 通过 PID 刷选出特定音视频相关的 TS 包,并解析出 PES;
  5. 从 PES 中读取到 PTS/DTS,并从 PES 中解析出基本码流 ES;
  6. 将 ES 交给解码器,获得压缩前的原始音频数据。

TS 文件分为三层:

  • TS 层:Transport Stream,是在 pes 层的基础上加入数据流的识别和传输必须的信息。
  • PES 层: Packet Elemental Stream,是在音视频数据上加了时间戳等对数据帧的说明信息。
  • ES 层:Elementary Stream,即音视频数据。

TS 文件格式如下图

image

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。
  • 当 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-1PES packet 头或者原始的ISO/IEC 11172-1packet 头中使用的填充字节大小.
    • 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 包实例分析

PES 包具体封装过程可参考:

SRS之SrsTsContext::encode_pes详解

假设封装的为通过 FLV 文件发送过来的视频包,即一个视频 tag。并且该视频 tag 的帧类型为 1(即 KeyFrame),则将该视频数据的部分数据(因为一个 TS packet 固定为 188 字节,因此对于一个视频帧,将会被分成多个包来封装)封装后的第一个 PES 包如下图所示。
image

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 的视频数据没有封装完)

image
第二个 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

参考博文

TS文件格式详解
TS流(3)——PAT,PMT

在 MPEG-2 中定义了节目特定信息(PSI),PSI 用来描述传送流的组成结构,在 MPEG-2 系统中担任及其重要的角色,在多路复用中尤为重要的是 PAT 表和 PMT 表。PAT 表给出了一路 MPEG-2 码流中有多少套节目,以及它与 PMT 表 PID 之间的对应关系;PMT 表给出了一套节目的具体组成情况与其视频、音频等 PID 对应关系。PSI 提供了使接收机能够自动配置的信息,用于对复用流中的不同节目流进行解复用和解码。PSI 表包括如下 4 种:

PAT/PMT 的具体封装过程可参考:

SRS之TS封装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 表实际数据的示例

image
以下根据该图来具体分析 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)字段的位置。

PMT 格式如下图

如下图是一段 PMT 表实际数据的示例

image

posted @ 2018-06-05 20:53  季末的天堂  阅读(3316)  评论(0编辑  收藏  举报