2. 数字电视传输流基础:MPEG-2系统层、TS包结构、PID映射
各位好。这一章我们聊聊数字电视的“地基”——MPEG-2系统层。说白了,就是TS流是怎么组织的。我当年刚接触这个的时候,对着188字节的包看了半天,心想这玩意儿怎么就能把视频、音频、字幕全塞进去?后来才明白,核心就三件事:系统层封装、固定长度小包、还有PID这个“身份证”。
2.1 MPEG-2系统层:就是给视频配个“快递单”
MPEG-2标准不光定义了视频怎么压缩(那是视频层的事),还定义了压缩后的数据怎么打包、怎么和音频同步、怎么在信道里传输。这就是系统层(ISO/IEC 13818-1)的活。
我习惯把系统层想象成一个快递分拣中心:
- ES(基本流):没包装的原始货——压缩好的视频或者音频帧。
- PES(打包基本流):套了一层塑料袋,贴上时间戳和长度标签。
- TS(传输流):把PES切碎,分装进188字节的标准小纸箱,每个纸箱上贴PID条码。
- PS(节目流):另一种包装方式,给光盘存储用的,咱们做数字电视传输不常用。
你想想看,如果直接拿PES在信道里传,万一丢了一个包,整个帧都废了。TS包把数据切得很碎,丢几个包还能纠错恢复。这是设计上的第一个精巧之处。
2.2 TS包结构:188字节里的“微缩世界”
TS包固定188字节。不多不少,就是188。嗯,这里要注意——有些系统加了16字节的RS纠错码变成204字节,但那是在信道编码层加的。系统层永远是188字节。
来看一下TS包长什么样:
| 字段 | 比特数 | 作用 |
|---|---|---|
| 同步字节(Sync Byte) | 8 | 固定0x47,包起始标记 |
| 传输错误指示器 | 1 | 1表示此包有不可纠正的错误,一般信道解码器设的 |
| 有效载荷单元起始指示器 | 1 | 1表示此包携带PES或PSI的第一个字节 |
| 传输优先级 | 1 | 1为高优先级,极少用 |
| PID | 13 | 包标识符,最核心的字段 |
| 传输加扰控制 | 2 | 00表示未加扰 |
| 自适应字段控制 | 2 | 01只有有效载荷,10只有自适应字段,11两者都有 |
| 连续性计数器 | 4 | 0-15循环,用于检测包丢失 |
| 自适应字段(可选) | 可变 | 填充字节或PCR时间戳 |
| 有效载荷数据 | 可变 | 切碎的PES数据或PSI/SI表数据 |
我记得在项目中曾经遇到过一个问题:码流解析器突然报连续性计数器跳变,一度以为信道丢包。排查了半天才发现是上层复用器的一个bug,它在插入空包时没有递增计数器。从那以后,我养成了一个习惯——调试时先把连续性计数器打出来看一遍,这比看PID还管用。
调试TS流时,先用逻辑分析仪或者软件工具抓住一段码流,手工对比连续的几个包的同步字节0x47和连续性计数器变化。如果遇到连续几个包PID相同但计数器不连续,基本可以断定复用器有问题或者信道有丢包。
2.3 PID:TS流的“灵魂身份证”
PID(Packet Identifier)一共13位,从0到8191。每个TS包都带着一个PID。你只要看到PID,就知道这个包里装的是哪一路视频、哪一路音频、还是系统信息表。
PID映射不是随便分配的,有固定的保留值和动态分配两种:
- 0x0000:PAT(节目关联表),整个TS流的“根目录”
- 0x0001:CAT(条件接收表),CA系统的入口
- 0x0010 - 0x1FFE:动态分配,PMT(节目映射表)、视频、音频、数据等
- 0x1FFF:空包(填充包),用于码率适配
为什么说PID是灵魂?你想想看,一个TS流里可能同时跑着10个电视频道,每个频道又可能有高清主视频、标清备播视频、两路立体声音频、一路外语声道、一路字幕。这些数据流全部混在一起,靠什么区分?就是靠PID。解码器拿到TS包,先看PID,然后决定丢给哪个解码器去处理。
2.4 PID映射与PMT:一张信息表把PID串联起来
光有PID还不够,解码器怎么知道哪个PID对应哪个节目?这就需要服务信息(SI)和节目特定信息(PSI)了。
PSI里有四张表:PAT、PMT、CAT、NIT(网络信息表,可选)。其中PAT是所有表的入口——它告诉解码器:“每个节目对应的PMT在哪个PID上”。PMT又告诉解码器:“这个节目的视频在PID X,音频在PID Y,字幕在PID Z”。
我来画个关系图:
你看这个流程:ES→PES→切碎→加TS包头→分配PID。解码器收到TS流后,第一步是先找PID=0x0000的包,拿到PAT,然后根据PAT找到PMT,再根据PMT拿到具体视频和音频的PID。整个路径清晰得像一道光。
2.5 实战:怎么从TS流里刨出视频PID
假设你拿到了一个TS流文件,想手动看一眼它的节目信息。怎么做?
- 找到TS包的起始位置:扫描同步字节0x47,每个188字节检查一次。连续3个包起始都是0x47,基本可以确认同步了。
- 抓取PID=0x0000的包:包头的第2和第3字节就是PID(注意是13位,分布在两个字节里)。
- 解析PAT表数据:PAT使用table_id 0x00,里面会列出program_number和对应的PMT_PID。
- 根据PMT_PID抓取PMT包:PMT里会列出stream_type(0x1B表示H.264视频,0x0F表示AAC音频)和对应的elementary_PID。
- 恭喜,你现在已经知道哪个PID装的是视频了。
第一次写PAT解析时,我忘了PSI表的数据可能跨多个TS包。PAT或PMT数据大于184字节时,会占用连续多个TS包,通过有效载荷单元起始指示器和pointer_field来拼接。如果你漏了这个机制,解析出来的表数据会是残缺的。
再说一个容易忽略的点:PID 0x1FFF是空包。复用器为了适配信道码率,会插入大量空包。这些包除了PID是0x1FFF外,其他字段几乎没意义。解码器看到这个PID,直接丢掉就完事了。我自己写码流分析工具时,会先把空包滤掉,只看有效数据包,效率高很多。
2.6 小结一下今天的内容
MPEG-2系统层说白了就三层:ES是原材料,PES是半成品,TS是成品小包。PID是每个TS包的“身份证”,解码器靠它分拣数据。PAT和PMT这两张表则是PID的“户口本”,记录了所有节目和流的对应关系。
下个实战环节我们就要真的动手分析TS包了。我建议你先找一段标准的TS流,在十六进制编辑器里打开,手动找几个包看PID和连续性计数器找找手感。纸上得来终觉浅,绝知此事要躬行。