ts包解析必须先搜索0x47同步字节而非硬切188字节,因起始位置可能偏移;找到后需验证连续包结构,再按188字节解析,并重点检查sync_byte、payload_unit_start_indicator和pid字段。

TS包固定188字节,但起始同步字节可能错位
读取MPEG-TS文件最常踩的坑是直接按188字节切分——0x47 同步字节未必出现在每个188字节块开头。真实文件里可能存在填充、误码或封装器写入偏差,导致第一个0x47 出现在偏移 1、2、3… 处。必须先做同步字节搜索,而不是硬对齐。
实操建议:
- 用
fread或std::ifstream::read一次性读入至少 200 字节缓冲区(覆盖一个完整包+偏移余量) - 在缓冲区中用
memchr(buf, 0x47, len)找第一个0x47,若未找到则滑动窗口再读 - 找到后检查后续第188字节是否也是
0x47(验证连续包结构),否则继续向后搜索 - 确认同步位置后,从此处开始按188字节严格解析,丢弃前面所有字节
TS包头字段解析:重点看sync_byte、payload_unit_start_indicator和PID
TS包头共4字节,但真正影响解析逻辑的是三个字段:sync_byte(固定0x47)、payload_unit_start_indicator(bit 6,指示PES/PSI起始)、PID(13-bit,标识流类型)。其他字段如transport_error_indicator、adaptation_field_control 在调试阶段可暂忽略,但上线前必须检查transport_error_indicator == 1 的包并跳过。
示例提取逻辑(C++17):
uint8_t header[4]; // 假设已定位到包起始 memcpy(header, buf + offset, 4); uint16_t pid = ((header[1] & 0x1F) <p>注意:<code>PID</code> 是区分音视频/PCR/PSI的关键,常见值:<code>0x0000</code>(PAT)、<code>0x0001</code>(CAT)、<code>0x0010</code>(PMT)、<code>0x0100</code>~<code>0x0FFF</code>(通常为音视频流)</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a> <p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p> </div> <a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div> <h3>遇到<code>adaptation_field_control == 0x02</code>或<code>0x03</code>时必须跳过适配域</h3> <p>TS包不一定携带有效载荷;当<code>adaptation_field_control</code>为<code>0x02</code>(仅适配域)或<code>0x03</code>(适配域+载荷)时,包头后紧跟一个长度字节<code>adaptation_field_length</code>,之后才是PES或PSI数据。不跳过会导致后续<code>PES_header</code>解析失败(比如把适配域里的<code>0x000001</code>当成PES起始码)。</p> <p>处理步骤:</p>
- 从header[3]提取
adaptation_field_control:低2位,即header[3] & 0x03 - 若为2或3,读取下一个字节
adapt_len,然后跳过1 + adapt_len字节才进入payload区 - 特别注意:当
adapt_len == 0时,表示“空适配域”,此时仍要跳过该字节,但payload从下一位开始
解析PES包时别直接信任pes_packet_length
PES_packet_length字段(PES头中第4-5字节)在TS中经常为0,表示长度未知——这是MPEG-TS的合法行为,尤其对实时流或可变码率视频。依赖它做内存分配或边界判断必然崩溃。
正确做法是结合TS层信息:
- 一个PES包可能跨越多个TS包,靠
payload_unit_start_indicator定位PES起始 - 用
PES_header_data_length(PES头中第8字节)确定当前PES头长度,再从剩余空间提取实际数据 - 数据结束由下一个带
payload_unit_start_indicator==1且相同PID的TS包决定,或文件末尾 - 若需完整PES,必须缓存连续PID的TS包payload,直到收到下一个start indicator或超长阈值(如500KB)后强制flush
真正容易被忽略的是PCR时间戳提取:它藏在PID为0x0000(PAT)或0x0010(PMT)所指向的视频PID的适配域中,且格式复杂(42-bit,含33-bit base + 9-bit extension),不专门处理就拿不到准确解码时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










