4、TS格式深度解析:TS的Packet结构(188字节)、PES封装、PAT/PMT表、TS在HLS中的分片延迟

聊到封装格式对延迟的影响,TS(Transport Stream)是个绕不开的话题。很多做直播的老工程师一听到TS,第一反应就是“稳”,但第二反应往往是“延迟有点高”。

我个人习惯把TS比作一个集装箱货轮——每个箱子大小固定,装得满满当当,但装卸流程复杂。你想想看,188字节一个包,这尺寸在MPEG-2时代是精心设计过的,适合在有损信道里传输。但到了今天,这个固定尺寸反而成了延迟的“隐形杀手”。

4.1 TS Packet结构:188字节的“铁律”

TS包的大小为什么是188字节?我记得刚入行时也问过这个问题。其实原因很简单:当年ATM信元是53字节,MPEG-2为了兼容性,选了188字节(4个ATM信元 + 填充)。这个尺寸一直沿用至今。

每个TS包的结构如下:

字段 长度(字节) 说明
同步字节 1 固定为0x47,用于包同步
传输错误指示符 1 bit 置1表示当前包有错误
有效载荷单元起始指示符 1 bit 标识PES或PSI数据的起始位置
传输优先级 1 bit 高优先级包优先传输
PID 13 bits 包标识符,核心字段
传输加扰控制 2 bits 加扰模式
自适应字段控制 2 bits 00=保留,01=无自适应字段,10=仅有自适应字段,11=两者都有
连续计数器 4 bits 0-15循环,用于检测丢包
自适应字段(可选) 可变 包含PCR、填充字节等
有效载荷 可变 PES数据或PSI表

关键点:PID是TS包的“身份证”。同一个视频流的所有包共享同一个PID。解复用器就是靠PID来区分音频、视频和字幕流的。

这里有个坑——自适应字段。我曾经在项目里遇到过,某个编码器在PCR(节目时钟参考)插入时,把自适应字段撑得很大,导致有效载荷只剩几个字节。你想想看,188字节的包,有效载荷只有10个字节,传输效率低得吓人。所以,自适应字段的长度直接影响有效带宽。

4.2 PES封装:从ES到TS的“中间人”

原始的音视频数据(ES,Elementary Stream)不能直接塞进TS包。中间需要一层PES(Packetized Elementary Stream)封装。PES的作用说白了就是给ES数据加个“信封”,写上时间戳、数据长度等信息。

PES包的结构:

  • PES起始码:固定为0x000001,后面跟流ID
  • PES长度:表示PES包的长度(0表示长度可变)
  • 可选字段:PTS/DTS(解码时间戳/显示时间戳)、ESCR、ES速率等
  • PES数据:实际的ES数据

个人经验:PES包的大小不是固定的。对于视频,我通常建议一个PES包包含一个视频帧。这样在解封装时,可以精确地按帧提取数据。但要注意,如果PES包太大,会占用多个TS包,增加解复用的复杂度。

嗯,这里要注意:PTS和DTS是PES里最重要的字段。PTS告诉解码器“什么时候显示这帧”,DTS告诉解码器“什么时候解码这帧”。对于B帧来说,DTS和PTS是不同的,这也是延迟的一个来源。

4.3 PAT/PMT表:TS流的“导航系统”

一个TS流里可能包含多个节目(比如多个频道)。解复用器怎么知道哪个PID对应哪个节目?这就需要PAT(节目关联表)和PMT(节目映射表)了。

PAT表:PID固定为0x0000。它列出了所有节目的编号,以及每个节目对应的PMT表的PID。

PMT表:PID由PAT指定。它列出了某个节目包含的所有流(视频、音频、字幕等),以及每个流的PID和编码类型。

我画了一张图,帮你理清它们的关系:

PAT/PMT表与TS流的关系 PAT/PMT表与TS流的关系 PAT表 PID=0x0000 PMT表 PID=0x1000 视频流 PID=0x1011 音频流 PID=0x1012 节目号=1 PMT PID=0x1000 流类型=0x1B 流类型=0x0F 💡 工作流程: 1. 解复用器先读取PID=0x0000的包,获取PAT表 2. 从PAT表中找到节目1的PMT PID=0x1000 3. 读取PID=0x1000的包,获取PMT表,找到视频PID=0x1011和音频PID=0x1012

避坑指南:我曾经遇到过一个奇葩问题——PAT表每100ms才发送一次,而PMT表每500ms才发送一次。结果解复用器启动后,要等500ms才能拿到完整的节目信息。这在直播场景下,意味着首屏延迟直接多了500ms。所以,PAT/PMT的发送间隔直接影响加入直播的延迟。我建议PAT每40ms发送一次,PMT每100ms发送一次。

4.4 TS在HLS中的分片延迟

HLS(HTTP Live Streaming)是TS最典型的应用场景。HLS把TS流切成一个个小文件(分片),每个分片通常包含2-10秒的数据。客户端通过下载这些分片来实现播放。

分片延迟是怎么来的?我拆解给你看:

  1. 编码延迟:编码器需要积累一个GOP(Group of Pictures)的数据才能开始输出。GOP越大,延迟越高。
  2. 分片生成延迟:切片器需要等待一个分片的数据全部到达,才能生成完整的TS文件。如果分片时长是6秒,那至少等6秒。
  3. 索引更新延迟:HLS的m3u8索引文件需要定期更新。通常每过一个分片时长,索引才会更新一次。
  4. 客户端下载延迟:客户端下载分片需要时间,尤其是网络不好时。

你想想看,如果每个分片是6秒,那从采集到播放,延迟轻松超过10秒。这就是为什么HLS的延迟通常比RTMP高。

核心结论:TS格式的固定包结构(188字节)和PES封装机制,决定了它在低延迟场景下不如FLV或fMP4。但TS的强项是抗丢包和兼容性——在有线电视、卫星广播等场景下,TS依然是王者。

我记得有一次做HLS低延迟优化,把分片时长从6秒砍到2秒,同时把GOP大小从60帧降到30帧。延迟从12秒降到了4秒左右。但代价是服务器压力大了3倍,因为分片数量暴增。嗯,这就是典型的“用计算换延迟”。

最后说一句:TS格式虽然“老”,但它的设计思想——固定包长、PID复用、PSI表导航——至今仍在影响很多现代协议。理解TS,就是理解流媒体封装的底层逻辑。