PCR值位于携带adaptation_field且其PCR_flag=1的TS包中,需先校验adaptation_field_control和adaptation_field_length≥6,再按大端序解析6字节为42位PCR:高33位base由前5字节拼接,低9位extension由第5字节低1位与第6字节组成,最终PCR=(base

PCR值在哪一段TS包里,怎么定位
PCR 不在每个 TS 包中出现,只存在于 program_clock_reference 字段被置位的 PES 或 PSI 包中——更准确地说,是 PID 为 0x1FFF(即 PCR-only packet)或携带 adaptation_field 且其 adaptation_field_length > 0、PCR_flag == 1 的任意 TS 包。
实际解析时不能依赖 PID 过滤,必须逐包检查 adaptation field:先读 TS header 的 adaptation_field_control 字段,若为 0b10(只有 adaptation field)或 0b11(有 payload + adaptation field),再跳过 header(固定 4 字节),读 adaptation field length 字节,然后检查第 5 字节(即 adaptation field 第一个字节)的 bit7(PCR_flag)。只有它为 1,才继续解析后续 6 字节 PCR 值。
如何从6字节中还原出完整的42-bit PCR值
TS 规范定义 PCR 由两部分拼接而成:前 33-bit 是 program_clock_reference_base(来自前 5 字节 + 第 6 字节高 1 位),后 9-bit 是 program_clock_reference_extension(来自第 6 字节低 9-bit 中的低 9-bit?不对——其实是第 6 字节低 1 位 + 第 7 字节全部 8-bit,共 9-bit)。
常见错误是直接 memcpy 6 字节当 uint64_t 用,或忽略字节序和位域拆分。正确做法是:
立即学习“C++免费学习笔记(深入)”;
- 把 TS 包中紧随 adaptation field header 后的 6 字节按网络字节序(big-endian)读取
- 前 5 字节构成 base:
(buf[0] > 7) - extension 来自 buf[4] 低 7-bit 和 buf[5] 全 8-bit:
((buf[4] & 0x01) - 最终 PCR =
(base ,单位是 27 MHz 时钟周期
为什么直接除以 27e6 算出的秒数常有毫秒级漂移
PCR 本身不是绝对时间戳,而是相对起始 PCR 的单调递增计数器;而且 MPEG-2 TS 允许 PCR 插入不连续(如复用器切换、码率调整),所以单次 PCR 值不能直接映射到 wall-clock 时间。
真正可用的是 PCR 差值:两个 PCR 之间的时间差 = (PCR2 - PCR1) / 27.0e6 秒。但要注意:
- 必须处理 42-bit 溢出:当
PCR2 ,说明发生了 wrap-around,应加 <code>1ULL - PCR 插入间隔无强制下限,有些流每 100ms 才插一次,有些则每 10ms 插一次;采样太稀疏会导致插值误差放大
- PCR 精度理论为 ~37 ns(1/27MHz),但硬件生成和传输延迟可能引入微秒级抖动,别指望 sub-millisecond 同步精度
如何用 C++ 安全提取 PCR 并避免整型溢出和未定义行为
关键点不在算法,而在数据搬运和类型安全。别用 uint32_t* 强转指针读 6 字节——小端机器上会错位;也别用 memcpy 到 uint64_t 再右移——高位未初始化字节可能触发 sanitizer 报告。
推荐写法是显式字节提取,全程用 uint64_t 累积:
uint64_t extract_pcr(const uint8_t* af_start) {
const uint8_t* p = af_start + 1; // skip adaptation_field_length
uint64_t base = ((uint64_t)p[0] << 25) |
((uint64_t)p[1] << 17) |
((uint64_t)p[2] << 9) |
((uint64_t)p[3] << 1) |
((uint64_t)(p[4] >> 7));
uint64_t ext = ((uint64_t)(p[4] & 0x01) << 8) | p[5];
return (base << 9) | ext;
}
注意所有移位操作都 cast 到 uint64_t,避免 int 提升规则导致符号扩展或截断;p[4] 和 p[5] 必须确认在缓冲区内——TS 包长度固定为 188 字节,但 adaptation field 长度可变,务必校验 af_length >= 6 再访问。
最易被忽略的是:PCR 只在 transport stream 的系统层有效,如果输入是已 demux 过的 PES 或 ES 数据,PCR 信息早已丢失——确认你解析的是原始 TS 文件或实时 TS over UDP/RTP,而不是 MP4 封装里的 H.264 流。



















