2026/10/27 3:47:50

TS码流分析实战:从188字节包到EPG字幕排查与脚本化检查

TS码流分析实战:从188字节包到EPG字幕排查与脚本化检查 简介这是一款面向数字电视与流媒体开发者的TS码流分析工具以树形结构呈现PAT、PMT、SDT、EIT及Subtitle的PES包解析结果节点层级与各SI包的数据结构基本对应便于初学者结合实例理解TS结构。相比同类软件它额外解析了LCN与Subtitle数据并能仿真显示Subtitle图片、模拟搜台过程与EPG展示双击EIT表或eventid可查看事件详情解析结果还可导出为网页格式且解析码流大小不受限制。资源包共30个文件以gif界面素材、js脚本、exe主程序及c源码、html页面为主压缩后约161KB体积轻巧、解压即用。目前已有1916人学习下载作者后续还将公布源码并支持teletext解析适合需要快速上手DTV原理、排查码流问题的工程师与新人参考。1. 拿到一段 TS 码流先别急着找播放器手头有一份从现场抓回来的传输流文件播放器能放但画面偶尔卡一下、EPG 里节目名是空的、字幕死活不出来——这种时候继续用播放器“看”是没用的得把码流拆开看结构。TS 分析工具就是干这个的把 188 字节一个的包逐层剥开PAT 里有哪些节目、PMT 里挂了哪些流、PCR 打在哪个 PID 上、PSI/SI 表重复周期是多少全部摊在表格里。它适合做前端信源排查、机顶盒适配联调、录制文件合规检查的从业者也适合刚接触 MPEG-TS 想搞懂“包”到底长什么样的新手。下面按我实际拆包的顺序把这份工具能干什么、参数怎么看、哪里容易翻车讲清楚。2. TS 包结构与 PSI/SI 解析工具到底在拆什么2.1 188 字节固定包与 PID 复用MPEG-TS 的底层单位是固定 188 字节的包包头 4 字节里塞了同步字节 0x47、传输错误指示、单元起始指示、优先级、PID 和加扰控制。工具打开文件后第一件事就是按 0x47 对齐切包如果同步字节对不上要么文件不是纯 TS可能混了 192 字节的 M2TS 或带长度前缀的格式要么抓包时丢了字节。对齐之后按 PID 分流同一个 PID 上的包再按 payload_unit_start_indicator 判断是不是一个新 PES 或新表的起点。这一步看着简单实际决定了后面所有分析的可信度。我见过有人拿一个 192 字节封装的录制文件直接丢进去工具报了一堆 PID 但全是乱的原因就是每 192 字节里有 4 字节时间戳前缀没剥掉。常见做法是先确认封装格式纯 TS 才走 188 对齐M2TS 要先去掉前缀再分析。2.2 PAT/PMT 两级映射与节目号PAT 在 PID 0x0000 上给出 program_number 到 PMT PID 的映射PMT 再给出这个节目下每条基本流的 stream_type 和 elementary_PID。工具里通常有一栏“节目列表”点开某个节目能看到视频、音频、字幕各自的 PID 和类型。排查“有图像没声音”时先看 PMT 里音频流在不在、stream_type 对不对再看这个 PID 上有没有实际包。表典型 PID关键字段排查用途PAT0x0000program_number、PMT PID节目是否存在、PMT 指向是否正确PMT由 PAT 指定stream_type、elementary_PID、PCR_PID音视频字幕流是否齐全、PCR 挂在哪CAT0x0001CA_descriptor是否加扰、条件接收系统标识SDT0x0011service_name、service_type节目名是否为空、业务类型EIT0x0012event_name、起止时间EPG 是否完整、时间是否错位这张表是我排查时的第一参照。PAT 里节目号对不上 PMT机顶盒就会显示“无节目”PMT 里 PCR_PID 指向一个不存在的流解码器时钟就稳不住表现是画面周期性顿一下。2.3 表重复周期与 CRC 校验PSI/SI 表不是只发一次而是按周期重复工具一般会统计每张表的间隔。PAT/PMT 通常 100ms 到 500ms 一次EIT 可能几秒一次。如果工具显示某张表只出现一次或者间隔忽大忽小说明复用器调度有问题机顶盒换台时可能来不及收全表。CRC32 校验也是必看项表尾 CRC 不对严谨的解码器会直接丢弃整张表表现就是“表在但节目出不来”。2.4 用工具导出表结构做比对多数 TS 分析工具支持把解析出的表导出成文本或 CSV我一般会导两份一份是正常信源的一份是问题信源的然后逐字段 diff。下面是一段用 Python 读取导出 CSV 做字段比对的示例工具本身不提供脚本接口时导出后自己比对最省事。import csv def load_pmt(path): # 读取工具导出的 PMT 字段表按 PID 建索引 result {} with open(path, newline, encodingutf-8) as f: for row in csv.DictReader(f): pid row[elementary_PID].strip() result[pid] { stream_type: row[stream_type].strip(), pcr_pid: row[PCR_PID].strip(), } return result good load_pmt(pmt_ok.csv) bad load_pmt(pmt_bad.csv) # 找出问题信源缺失或类型变化的流 for pid, info in good.items(): if pid not in bad: print(f缺失流 PID{pid} type{info[stream_type]}) elif bad[pid][stream_type] ! info[stream_type]: print(f类型变化 PID{pid} {info[stream_type]} - {bad[pid][stream_type]})这段脚本的逻辑很直白以正常信源的 PMT 为基准逐 PID 检查问题信源是否缺流、stream_type 是否被改。参数上要注意导出的列名要和工具实际表头一致不同工具对 PCR_PID 的列名可能是PCR_PID或pcr_pid读之前先看一眼 CSV 头。stream_type 是十六进制字符串还是十进制整数也要统一否则比对全是假差异。3. EPG 与字幕流排查从 EIT 到 PES 的落地检查3.1 EIT 表结构与节目名缺失EPG 信息主要落在 EIT 上present/following 表描述当前和下一个节目schedule 表描述未来若干小时的节目。工具里能看到 EIT 的 service_id、table_id_extension、事件起止时间和名称。节目名为空最常见的原因是 SDT 里 service_name 没填或者 EIT 的 service_id 和 PAT 里的 program_number 对不上机顶盒按 service_id 去匹配就匹配不到。排查顺序我一般这样走先看 SDT 有没有、service_name 是不是空串再看 EIT 的 service_id 集合是否覆盖了所有节目最后看事件时间是不是用了错误的时区偏移导致 EPG 显示“已结束”。工具里如果有时间轴视图直接看事件块有没有重叠或空洞比逐条读表快得多。3.2 字幕流识别stream_type 与描述符字幕在 TS 里可能是 DVB 字幕stream_type 0x06 加 subtitling_descriptor、Teletext0x06 加 teletext_descriptor也可能是烧录在视频里的。工具里看到 0x06 的私有数据流别急着当成字幕先看 PMT 里这条流挂了什么描述符。subtitling_descriptor 里会给出 language、subtitling_type 和 composition/ancillary PID 的对应关系字幕的显示数据在 PES 里按段传输。stream_type描述符含义常见问题0x06subtitling_descriptorDVB 字幕语言标签缺失、PES 段不完整0x06teletext_descriptor图文电视机顶盒不支持该类型0x03/0x04无MPEG 音频采样率与声道数不符0x1B无H.264 视频SPS/PPS 未随流重复字幕不出来的典型链路是PMT 里有字幕流但 PES 里 segment 的 page_id 和 descriptor 对不上或者字幕只在节目切换时发一次、之后不再重复。工具里看 PES 负载的十六进制能确认段头是否完整。3.3 用 ffprobe 交叉验证流信息TS 分析工具给的是表级视图ffprobe 给的是解码器视角两边对一下能快速定位是“表错了”还是“流坏了”。下面这条命令把每个流的类型、PID、编码格式打出来。ffprobe -v error -show_streams -show_format input.ts重点看输出里的index、codec_type、codec_name和id对应 PID。如果工具里 PMT 显示有字幕流ffprobe 却只列出视频和音频说明字幕 PES 没有被正确识别问题多半在描述符或段封装而不是 PMT 本身。反过来ffprobe 能识别但机顶盒不显示就要回到 EIT/SDT 和机顶盒的兼容性上查。3.4 抓包时长与表完整性分析用的码流样本太短是新手常犯的错。只抓 5 秒EIT schedule 根本收不全工具里 EPG 一片空白然后误判信源有问题。我一般要求样本至少覆盖一个完整表周期EPG 排查建议 60 秒以上换台场景要包含切换前后的片段。工具里如果有“表出现次数”统计直接看 EIT 的条数够不够。4. 避坑与排查五条血泪记录4.1 同步字节对不上就断定文件损坏现象工具打开后 PID 列表为空或全是乱码。原因文件不是 188 字节纯 TS可能是 192 字节封装或带了 RTP 头。解决先用十六进制查看器确认每 188 或 192 字节处是否有 0x47M2TS 先剥 4 字节前缀RTP 封装先解出负载再分析。4.2 只看 PMT 不看实际包数现象PMT 里音视频流齐全但播放就是黑屏。原因表声明了流对应 PID 上却没有实际包或者包极少。解决在工具里看每个 PID 的包计数和码率占比声明了但计数为 0 的流就是“空挂”要回到复用配置查。4.3 CRC 错误被忽略现象表能解析出来但机顶盒行为异常。原因表尾 CRC32 不对严谨设备直接丢表。解决工具里开启 CRC 校验列任何一张表 CRC 错都要当成硬故障处理常见于复用器拼接表时截断。4.4 把 PCR 抖动当成网络问题现象画面周期性卡顿网络抓包看不出丢包。原因PCR_PID 上的 PCR 间隔超标或抖动过大解码器缓冲被掏空。解决工具里看 PCR 间隔统计DVB 建议不超过 40ms超了先查复用器调度和 PCR 校正。4.5 字幕语言标签缺失导致不显示现象字幕流存在机顶盒字幕菜单里却没有选项。原因subtitling_descriptor 里 language 字段为空或用了非标编码。解决在工具里展开描述符逐字节看补上合法的三字母语言码重新复用后再验证。5. 进阶把分析结论固化成可复用的检查脚本工具用熟了之后重复性的表检查可以脚本化。思路是让工具导出表结构脚本按固定规则打分PAT/PMT 是否存在、CRC 是否全对、每个声明流的包计数是否大于阈值、PCR 间隔是否超标、EIT 覆盖时长是否足够。下面是一个打分脚本的骨架规则阈值按自己业务调整。def check_stream(report): score 100 issues [] # 表存在性 if not report.get(pat_present): score - 30; issues.append(PAT 缺失) if not report.get(pmt_present): score - 30; issues.append(PMT 缺失) # CRC if report.get(crc_errors, 0) 0: score - 20; issues.append(fCRC 错误 {report[crc_errors]} 处) # 空挂流 for pid, cnt in report.get(pid_counts, {}).items(): if cnt 0: score - 10; issues.append(fPID {pid} 无包) # PCR 间隔单位 ms if report.get(pcr_max_gap_ms, 0) 40: score - 15; issues.append(PCR 间隔超标) # EPG 覆盖时长单位秒 if report.get(eit_span_s, 0) 3600: score - 10; issues.append(EIT 覆盖不足 1 小时) return score, issues参数说明pid_counts来自工具导出的每 PID 包计数pcr_max_gap_ms取工具统计的最大 PCR 间隔eit_span_s是 EIT 事件的最晚结束时间减最早开始时间。阈值不是死的直播信源和录制文件要求不同录制文件可以放宽 PCR 间隔直播信源要卡严。脚本输出的分数只用来快速筛最终判断还得回到工具里看具体表。我自己的习惯是每次拿到新样本先跑一遍这个打分低于 70 分的直接进人工细查省得在明显有硬伤的流上浪费时间。从那以后我每次做信源验收都强制走一遍“对齐检查 → 表完整性 → 流计数 → PCR 间隔 → EPG 覆盖”这条链翻车次数明显少了。希望帮到你。本文还有配套的精品资源点击获取