简介:这是一款面向数字电视与音视频开发者的TS码流分析工具,适合初学者结合实例理解TS结构,也方便工程师排查客户问题。它以Tree形式展示PAT、PMT、SDT、EIT及Subtitle的PES包结构,与各SI包的数据结构基本对应,并额外解析了LCN与Subtitle数据,还能将Subtitle仿真显示出来。资源包共30个文件,以24个gif界面素材、2个js脚本、1个exe可执行程序为主,另含c源码、html页面与db数据文件,压缩包约161KB,体积轻巧便于携带。工具支持仿真搜台、仿真EPG,双击EIT表可将全部EIT导出为网页,点击HTML按键能把PAT、PMT、SDT、NIT存成网页格式,双击eventid可查看事件详细数据,解析码流大小不受限制。目前已有1911人学习下载,作者后续将公布源码并支持teletext解析,对学习DTV原理与PSI/SI结构有实际参考价值。
1. 码流分析软件到底在分析什么:从 TS 结构说起
做音视频开发的人,迟早会遇到一个场景:播放器黑屏、花屏、音画不同步,日志里只有一句“解码失败”,什么有效信息都没有。这时候你需要的不是加更多日志,而是把码流本身拆开看——TS 流里到底有没有 PMT、PID 对不对、PCR 是不是断了、PES 包头有没有丢。码流分析软件(ts analysis tool)就是干这个的:它把 MPEG-TS 的 188 字节包逐层剥开,让你看到 PAT、PMT、PES、ES 每一层的真实状态。
TS 结构本身不复杂,但它的设计目标是传输可靠性而非解析友好性,所以固定长度包、PID 复用、PCR 时钟恢复这些机制叠在一起,肉眼几乎不可能直接读。一个趁手的 ts analysis tool 能帮你把 PSI/SI 表还原成树状结构,把 PID 和流类型对应起来,把时间戳的连续性画出来。这篇内容适合三类人:做 IPTV/直播排障的、做播放器兼容性测试的、以及需要从 TS 里提取裸流做二次处理的。接下来我会按“TS 结构怎么理解 → 工具怎么选和怎么用 → 实际排障怎么走 → 坑在哪”的顺序讲清楚。
2. TS 结构拆解与分析工具选型:先看懂再动手
2.1 TS 包的固定结构:188 字节里有什么
MPEG-TS 的每个包固定 188 字节,这个长度不是随便定的,它和 ATM 信元、FEC 纠错块大小有历史渊源。每个包的结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| sync_byte | 1 字节 | 固定 0x47,用于包同步 |
| transport_error_indicator | 1 bit | 传输错误标志 |
| payload_unit_start_indicator | 1 bit | 负载起始标志,PES 包首包为 1 |
| transport_priority | 1 bit | 传输优先级 |
| PID | 13 bit | 包标识,区分不同流 |
| transport_scrambling_control | 2 bit | 加扰控制 |
| adaptation_field_control | 2 bit | 适配字段控制 |
| continuity_counter | 4 bit | 连续计数器,同 PID 递增 |
| adaptation_field | 可变 | 适配字段,含 PCR 等 |
| payload | 可变 | 负载数据 |
理解这张表是看懂任何 ts analysis tool 输出界面的前提。PID 是核心中的 PID——PAT 固定在 PID 0x0000,PMT 的 PID 由 PAT 指定,通常从 0x0020 开始分配。continuity_counter 是排障时最常看的字段,同一个 PID 的包如果 CC 不连续,说明中间丢包了。
2.2 主流 ts analysis tool 的能力对比与选型
常见的 TS 分析工具有几类:命令行型的 ffprobe、tsduck,图形界面型的 DVBAnalyzer、TSReader,以及集成在 Wireshark 里的 MPEG-TS 解析器。选哪个取决于你的场景:
- 快速看流基本信息:ffprobe 一条命令就够,适合 CI 里做自动化检测。
- 深入分析 PSI/SI 表和 PCR 抖动:tsduck 功能最全,支持脚本化批量处理。
- 实时抓包和协议栈关联:Wireshark 加载 TS 插件,能同时看 RTP/TS 两层。
- 可视化逐包浏览:DVBAnalyzer 这类工具适合教学和手动排查。
我一般会同时装 ffprobe 和 tsduck,前者做快速体检,后者做深度分析。下面给出两者的最小可用命令。
# 用 ffprobe 快速查看 TS 流的节目和流信息 ffprobe -v quiet -print_format json -show_programs -show_streams input.ts # 用 tsduck 分析 PSI/SI 表结构 tstables input.ts # 用 tsduck 查看 PID 统计和码率分布 tsbitrate -p input.tsffprobe的-show_programs会输出 PAT/PMT 解析后的节目列表,-show_streams给出每个 ES 流的编码格式和分辨率。tstables则把 PAT、PMT、SDT 等表逐条打印出来,适合确认 PMT 里是否缺少某个流。tsbitrate按 PID 统计码率,能快速定位哪个 PID 占用了异常带宽。
提示:ffprobe 对 TS 的解析依赖其内部 demuxer,遇到非标准流可能直接报错退出。tsduck 的容错性更好,建议作为主力分析工具。
3. 用 ts analysis tool 定位常见码流故障:从现象到 PID
3.1 黑屏无画面:先查 PAT/PMT 再看 ES
播放器黑屏但音频正常,或者音视频全无,第一步不是看解码器日志,而是确认 PSI 表是否完整。用 tstables 输出 PAT 和 PMT:
# 输出 PAT 表,确认节目号和 PMT PID tstables -t PAT input.ts # 输出 PMT 表,确认每个节目的流类型和 PID tstables -t PMT input.tsPAT 表里每个 program 对应一个 program_number 和 PMT PID。如果 PAT 为空或 PMT PID 指向的包不存在,播放器根本不知道去哪里找视频流。PMT 表里 stream_type 字段标识编码格式:0x1B 是 H.264,0x24 是 HEVC,0x03/0x04 是 MPEG-2 Audio。如果 PMT 里缺少视频流的描述条目,播放器就不会去解那个 PID。
常见故障模式:PAT 里声明了 PMT PID 0x1000,但实际流里 0x1000 的包全是空包(PID 0x1FFF 才是空包,这里指 payload 为空)。用tsbitrate -p input.ts看 0x1000 的码率是否为 0,就能确认。
3.2 花屏和卡顿:PCR 抖动与 continuity_counter 丢包
花屏和卡顿的原因通常有两个:PCR 不稳定导致解码器时钟恢复失败,或者 TS 包丢失导致 PES 不完整。先看 PCR:
# 提取 PCR 并计算抖动 tsp -i input.ts -P pcrverify --jsonpcrverify插件会输出每个 PCR 的间隔和抖动值。DVB 标准要求 PCR 间隔不超过 40ms,抖动在 ±500ns 以内。如果间隔超过 100ms,解码器缓冲会耗尽,表现为周期性卡顿。
再看 continuity_counter:
# 检测 CC 不连续的包 tsp -i input.ts -P continuity --jsoncontinuity插件会报告每个 PID 的 CC 跳变次数。如果某个视频 PID 的 CC 跳变频繁,说明传输链路丢包。这时候要区分是源端问题还是传输问题:在源端和终端同时抓包对比,如果源端 CC 连续而终端不连续,问题在传输网络。
3.3 音画不同步:PTS/DTS 与 PCR 的三角关系
音画不同步的根因通常是 PTS 和 PCR 的偏移不一致。PTS 是显示时间戳,DTS 是解码时间戳,PCR 是系统时钟参考。解码器用 PCR 恢复系统时钟,再用 PTS 决定每一帧的显示时刻。如果音频和视频的 PTS 基准相差过大,就会出现不同步。
用 tsduck 提取 PTS 序列:
# 提取视频 PID 的 PTS 并输出时间线 tsp -i input.ts -P pes --pid 0x0100 --output-file video_pes.txt # 用 ffprobe 查看音视频流的起始 PTS ffprobe -v quiet -show_entries stream=index,codec_type,start_time -of csv input.tsffprobe输出的start_time是各流第一个 PTS 换算成秒的值。正常情况下音频和视频的 start_time 差值应在 100ms 以内。如果超过 500ms,播放器即使做同步补偿也会有明显感知。
注意:PTS 是 33 位字段,约 26.5 小时回绕一次。长时间流的 PTS 分析要考虑回绕处理,否则会看到巨大的负值跳变。
4. 避坑与排查:ts analysis tool 使用中的五个血泪教训
4.1 坑一:把空包当成有效数据
现象:用 tsbitrate 看到某个 PID 码率很高,但播放器就是不出画面。
原因:PID 0x1FFF 是空包,用于填充码率。有些工具默认不排除空包,导致统计结果虚高。更隐蔽的情况是,某些复用器把空包打上了错误的 PID。
解决:分析前先过滤空包。tsduck 的tsbitrate默认排除 0x1FFF,但如果你用 Wireshark 看,需要手动加过滤条件mpeg_ts.pid != 0x1fff。另外检查 PAT 里声明的 PID 是否和实际有数据的 PID 一致。
4.2 坑二:忽略 adaptation_field 里的 PCR 标志
现象:pcrverify 报告 PCR 间隔正常,但播放器仍然卡顿。
原因:PCR 只出现在 adaptation_field 中,且需要 PCR_flag 置位。有些流在 adaptation_field 里放了其他字段但没有 PCR,工具如果只看 adaptation_field 存在与否就会误判。
解决:用tsp -P pcrverify --verbose查看每个 PCR 的具体位置和值,确认 PCR_flag 确实置位。同时检查 PCR PID 是否和 PMT 里声明的 PCR_PID 一致。
4.3 坑三:continuity_counter 在适配字段-only 包上的行为
现象:continuity 插件报告大量 CC 跳变,但实际没有丢包。
原因:当 adaptation_field_control 为 0b10(只有适配字段,无负载)时,continuity_counter 不应该递增。如果复用器错误地递增了,工具会误报。
解决:检查复用器配置,确认 adaptation_field_control=0b10 时 CC 保持不变。分析时可以用tsp -P continuity --ignore-af-only跳过这类包。
4.4 坑四:PES 包跨越多个 TS 包时的解析错误
现象:提取的 ES 流用 ffmpeg 播放时报“Invalid NAL unit”。
原因:一个 PES 包可能跨越多个 TS 包,payload_unit_start_indicator 只在第一个 TS 包置位。如果工具没有正确重组 PES,提取出的 ES 流会在包边界处断裂。
解决:用 tsduck 的tsp -P pes做 PES 重组,它会根据 PUSI 和 PES 包头长度自动拼接。不要手动从 TS payload 里抠数据。
4.5 坑五:把加密流的乱码当成编码错误
现象:PMT 里 stream_type 正常,但提取的 ES 流全是乱码。
原因:transport_scrambling_control 字段非零表示该包被加扰。如果没注意到这个字段,会误以为是编码器输出异常。
解决:用tstables -t PMT --verbose查看 PMT 里的 CA_descriptor,确认是否有条件接收系统。加扰流的分析需要先解密,这不是 ts analysis tool 能直接处理的。
5. 进阶技巧:用脚本批量分析 TS 流并生成报告
单次手动分析只能解决眼前问题,真正省时间的是把常见检查项脚本化。我一般会写一个 Python 脚本,调用 tsduck 和 ffprobe,对一批 TS 文件做自动化体检,输出结构化报告。
import subprocess import json import sys from pathlib import Path def analyze_ts(filepath): """对单个 TS 文件做基础分析,返回结构化结果""" result = {"file": str(filepath), "issues": []} # 1. 用 ffprobe 检查节目和流信息 ffprobe_cmd = [ "ffprobe", "-v", "quiet", "-print_format", "json", "-show_programs", "-show_streams", str(filepath) ] try: out = subprocess.run(ffprobe_cmd, capture_output=True, text=True, timeout=30) probe = json.loads(out.stdout) result["programs"] = len(probe.get("programs", [])) result["streams"] = len(probe.get("streams", [])) if result["programs"] == 0: result["issues"].append("PAT 为空或无法解析") except Exception as e: result["issues"].append(f"ffprobe 失败: {e}") # 2. 用 tsduck 检查 CC 连续性 cc_cmd = ["tsp", "-i", str(filepath), "-P", "continuity", "--json"] try: out = subprocess.run(cc_cmd, capture_output=True, text=True, timeout=60) cc_data = json.loads(out.stdout) for pid_info in cc_data.get("pids", []): if pid_info.get("cc_errors", 0) > 0: result["issues"].append( f"PID {pid_info['pid']} CC 错误 {pid_info['cc_errors']} 次" ) except Exception as e: result["issues"].append(f"continuity 检查失败: {e}") # 3. 用 tsduck 检查 PCR 抖动 pcr_cmd = ["tsp", "-i", str(filepath), "-P", "pcrverify", "--json"] try: out = subprocess.run(pcr_cmd, capture_output=True, text=True, timeout=60) pcr_data = json.loads(out.stdout) max_jitter = pcr_data.get("max_jitter_ns", 0) if max_jitter > 500: result["issues"].append(f"PCR 最大抖动 {max_jitter}ns 超过 500ns") except Exception as e: result["issues"].append(f"PCR 检查失败: {e}") return result if __name__ == "__main__": for ts_file in Path(sys.argv[1]).glob("*.ts"): r = analyze_ts(ts_file) status = "OK" if not r["issues"] else "FAIL" print(f"[{status}] {r['file']} programs={r.get('programs')} streams={r.get('streams')}") for issue in r["issues"]: print(f" - {issue}")这个脚本的逻辑是:先用 ffprobe 做节目级检查,确认 PAT/PMT 可解析;再用 tsduck 的 continuity 插件检查每个 PID 的 CC 错误;最后用 pcrverify 检查 PCR 抖动。三个检查覆盖了最常见的传输层故障。
参数方面,timeout设 30 到 60 秒是因为大文件分析可能较慢,但超过这个时间通常说明流本身有严重问题导致工具卡住。max_jitter_ns的阈值 500 来自 DVB 标准,如果你做的是非 DVB 场景(比如 SRT 传输的 TS),可以放宽到 1000ns。
实际用的时候,我会把这个脚本挂在 CI 里,每次构建出新流文件就自动跑一遍。曾经有一次,编码器升级后输出的流 PCR 间隔从 40ms 变成了 80ms,脚本直接报 FAIL,比等到测试同学反馈卡顿早了整整一天。这种后悔药,提前吃比事后补强。
希望帮到你。
本文还有配套的精品资源,点击获取