PS 是两级结构:pack 头加一串 PES。 pack 头带系统时钟,一个 pack 里能装多个 PES。
5.5 整节都在拆 TS,这一节换到 PS 这一侧。 5.6.2 讲怎么按起始码把它一段段解出来。
一、PS 的两级结构
① pack 层:以起始码 00 00 01 BA 开头,带系统时钟(SCR)。
一个 pack 里可以装多个 PES。
② PES 层:起始码 00 00 01 E0 是视频、C0 到 DF 是音频。
实测构造一个 pack 头,20 字节,起始码 000001ba。
这 20 字节里:起始码 4、SCR 6、复用速率 3、填充长度 1,
剩下 6 字节是 stuffing 与保留位。
注意 PS 里 pack 头只出现一次,而不是每个 PES 前都有。 这一点跟 TS 正好相反:TS 每 188 字节就有一个头,PS 是「一大块一个头」。
二、pack 头里有什么
① 起始码 4 字节:固定 00 00 01 BA,这是 PS 的身份证。
② SCR 基值与扩展 6 字节:系统参考时钟,90 kHz。
它是 PS 版的「对表工具」,作用类似 TS 的 PCR。
③ 复用速率 3 字节:码流速率上限,全是 1 表示「可变码率」。
④ 填充长度 1 字节:后面填几个 0xFF。
PS 的 pack 头比 TS 的包头大得多(十几字节对 4 字节)。但它不是每 188 字节就要一个,而是「一个 pack 一次」。 一个 pack 通常装几百 KB,所以摊下来开销很小。
SCR 的编码方式要留意: 它是 33 位基值 + 9 位扩展,拆在 6 个字节里,还得按位拼。 拼错了不会报错,只会让同步慢慢漂——这种错最难查。
三、PS 与 TS 的根本差别
PS 假设「传输是可靠的」:没有定长包、没有连续计数、没有 PCR 的频繁插入。 它更像「把 PES 串起来存成文件」,所以 DVD 用得很顺。 一旦网络丢了一个字节,后面的解析就全乱了——这就是它不能用于直播的根本原因。
不过 PS 有一个 TS 比不了的优点:开销小。 5.4.3 算过,1 MB 有效载荷 PS 只要约 320 字节头,TS 要 22 KB。 七十倍的差距,在存储介质紧张的年代是决定性的。
还有一条:PS 天然支持「随机访问」。
因为每个 pack 都以起始码开头,播放器可以扫到任意位置往后找 00 00 01 BA,
找到就能接着解。TS 虽然也能靠 0x47 对齐,但还需要 PAT/PMT 才解得出。
四、动手 Demo:PS 码流——pack 头加一串 PES
手工构造一个 20 字节的 pack 头,把每个字段的位置标出来。 再对比一下:同样 1 MB 数据,PS 与 TS 的头开销差七十倍。
import struct
# pack 头:起始码 + SCR(6) + 复用速率(3) + 填充长度(1) + stuffing
scr = 0x0001_0000_0001 # 33 位基值
b = bytearray()
b += b"\x00\x00\x01\xBA"
b += bytes([0x44, # 01 + SCR 高 3 位 + 标志
(scr >> 27) & 0xFF, (scr >> 19) & 0xFF,
(scr >> 11) & 0xFF, (scr >> 3) & 0xFF,
((scr & 0x07) << 5) | 0x04])
b += b"\xFF\xFF\xFF" # 可变码率
b += bytes([0]) # 填充长度 0
b += bytes(6) # 补齐到 20 字节
print("pack 头 %d 字节" % len(b))
print("起始码 %s" % b[:4].hex())
print("第 5 字节 0x%02X,最高两位 %s" % (b[4], bin(b[4] >> 6)))
print()
data = 1024 * 1024
ts_over = ((data + 183) // 184) * 4
ps_over = (data // (64 * 1024) + 1) * 20
print("1 MB 数据:TS 头 %d 字节(%.2f%%),PS 头 %d 字节(%.3f%%)"
% (ts_over, ts_over * 100.0 / data, ps_over, ps_over * 100.0 / data))
实跑输出:
--------------------------------------------------------------------
PS 码流——pack 头加一串 PES
--------------------------------------------------------------------
**① PS 的两级结构**
**① pack 层**:**以起始码 `00 00 01 BA` 开头,带系统时钟(SCR)。**
**一个 pack 里可以装多个 PES。**
**② PES 层**:**起始码 `00 00 01 E0` 视频、`C0` 音频。**
实测构造一个 pack 头 20 字节
起始码 000001ba(PS 的 pack 起始)
**② 一个 pack 头里有什么**
字段 字节 作用
----------------------------------------------------
起始码 4 00 00 01 BA
SCR 基值与扩展 6 系统参考时钟,90 kHz
复用速率 3 码流速率上限
填充长度 1 后面填几个 0xFF
**PS 的 pack 头比 TS 的包头大得多**(十几字节对 4 字节)。
**但它不是每 188 字节就要一个,而是「一个 pack 一次」。**
**一个 pack 通常装几百 KB,所以摊下来开销很小。**
**③ PS 与 TS 的根本差别**
**PS 假设「传输是可靠的」**:**没有定长包、没有连续计数、没有 PCR 的频繁插入。**
**它更像「把 PES 串起来存成文件」,所以 DVD 用得很顺。**
**一旦网络丢了一个字节,后面的解析就全乱了**——**这就是它不能用于直播的根本原因。**
20 字节的 pack 头——比 TS 的 4 字节大五倍,但不是每 188 字节都付。
第 5 字节高两位是 01,这个值表示「标准 MPEG-2 pack」。
复用速率写 FF FF FF 表示可变码率,定码率时要填真实值。
30 秒实验
- 把第 5 字节的 0x44 改成 0x24,看最高两位——变成 00,表示 MPEG-1 的 pack,头长度会短很多。
- 把数据量改成 10 MB,看两种开销的百分比——比例不变,因为都是线性开销。
- 把填充长度设成 8,看总长——变成 28 字节,填充长度字段后面真的要跟 8 个 0xFF。
常见误区
- 「PS 每个 PES 前都有 pack 头」——不是,一个 pack 只有一个头。
- 「PS 的 SCR 等同于 PTS」——不是,SCR 是系统时钟,PTS 是显示时间。
- 「PS 不能随机访问」——能,扫起始码就能定位。
自测三问(点开对答案)
1. PS 的两级结构是什么? —— **pack 层(一个 pack 头带若干 PES)与 PES 层。** 2. PS 的 pack 头里 SCR 是干什么的? —— **系统参考时钟**,作用类似 TS 的 PCR,用来校准接收端时钟。 3. PS 为什么不能用于直播? —— **假设传输可靠**,丢一个字节后面全乱,没有重同步机制。- ☐ (本节完,下一节 5.6.2 PS 码流的解析流程)