☰
TS码流分析工具实战:从ffmpeg生成到tsduck排障
2026/9/26 22:36:59 网站建设 项目流程

简介:面向数字电视/DVB开发者的TS码流分析实用工具,以Tree视图直观显示PAT、PMT、SDT、EIT及Subtitle的PES包结构,帮助初学者通过实例快速掌握TS封装与SI/PSI信息组织方式。它同样适用于运维和高频排查码流问题的技术支持场景,既能用于学习,也能辅助日常调试。相比常见分析软件,它额外解析LCN和Subtitle数据,支持Subtitle图片仿真显示、搜台过程模拟与EPG仿真展示,双击EIT也可将全部表导出为parsing_result.html,便于复盘比对。解析完成后还可将PAT、PMT、SDT、NIT等表一键存成HTML网页,统一核对更为方便。Tree结构与SI包的数据结构一一对应,双击Subtitle的PES节点即可在右侧显示字幕图片,双击EIT中的eventid还能弹窗查看该事件详细数据,解析时也不受码流大小限制。资源包共30个文件(约161KB),内含可直接运行的exe、供查看结果的HTML页面、Tree控件相关JS脚本、Event.c源码文件及24张界面/树形节点示意GIF图片,适合边看效果边对照代码理解TS解析逻辑。已有1911人学习/下载,尤其适合刚接触DTV的新人和需要排查码流问题的工程师,能快速建立完整认知,少走弯路。

1. 一个好的码流分析软件,能让你少熬三个夜

做流媒体开发的人,多半经历过这种深夜:画面卡成PPT,抓回来的码流用十六进制编辑器打开,满眼0x47后面全是陌生字节,PAT、PMT、PES的概念背得头头是道,却连哪个PID丢了都定位不了。其实问题不在你,而在缺一个能把TS结构剥开的码流分析软件(ts analysis tool)——它能把传输流拆成一张张可读的表:PAT、PMT、SDT、PES、PCR,告诉你音视频在哪、丢没丢包、时间戳跳没跳。这篇不带你背协议文档,而是从生成一段.ts文件开始,一路玩到能独立排障,适合流媒体开发、播放器调试、安防对接,以及想彻底搞懂.ts分片结构的同学。

2. 先搞到一段真实TS码流:三种样本来源与一条生成命令

2.1 没有素材?用ffmpeg几秒生成一个标准的.ts文件

最常见也最可控的做法,是自己用ffmpeg把任意本地视频重新封装成TS,命令只有一行:

ffmpeg -i input.mp4 -c copy -f mpegts -mpegts_flags +resend_headers output.ts

-c copy是关键,意思是只重新封装、不重编码,几秒钟完成,而且视频编码参数和你原来的MP4完全一致,便于后续在分析工具里看到熟悉的H.264/H.265 Profile。如果没有本地MP4,也可以去网上抓一段公开视频转,但可能会被转码,结构变成SEI等,新手会多看到一些干扰项。-mpegts_flags +resend_headers是让ffmpeg周期性重发PAT/PMT表,这样分析工具在流的中间也能找到节目信息,播放器也不会因为只出现一次PAT而黑屏。

另一个更真实的来源,是直接从HLS地址里抓一段.ts分片。开发者抓取视频时最常见的问题,就是把M3U8里的分片下载下来后,用播放器一打开就卡住,然后怀疑分片损坏。实际上分片开头PAT/PMT往往只出现一次,很多播放器从中间插入就会等不到表,这时候用分析工具能看到“PAT/PMT only at beginning”之类的提示。

curl -O https://example.com/segment_0001.ts

这样拿到的分片通常只有6到10秒,非常适合新手做结构拆解。它比自造TS多了真实网络分片的割裂感:PES头可能没有严格对齐到帧边界,时间戳也可能在新分片里清零。这些在后面的避坑章里都会遇到。

2.2 抓UDP组播流:真实场景里码流的常见来源

在广电、安防、IPTV项目里,TS往往不是文件,而是UDP或RTP组播流。这时就必须用wireshark抓包。常见做法是先在网卡上抓一段时间,过滤条件是:

udp.dstport == 1234 && ip.addr == 239.1.1.1

然后通过wireshark的“导出原始载荷”把UDP payload存成二进制文件。注意:如果源是RTP封装,导出的payload是RTP载荷,它不保证TS包边界对齐。因为TS包固定188字节,一个RTP包可能只装一部分TS数据,也可能一个RTP包里有多个TS包。这时不能直接按188字节切,否则分析工具会疯狂报同步错误。更稳妥的做法是先用工具自动搜索0x47同步,或者用tsduck的-I rtp方式直接读UDP流,让它自己处理RTP头。

抓包过程有个血泪经验:不要用笔记本自带的WIFI网卡抓组播,很容易漏包。要指定有线口并开启混杂模式。抓包时长建议20秒以上,因为如果只抓2秒,刚好错过PAT/PMT和PCR,分析结果没有任何参考意义。

tcpdump -i eth0 -s 0 -w ts.pcap udp port 1234

tcpdump抓下来的pcap后续可以直接用tsduck的-I ip插件读取,也可以导出为裸TS。这个流程解决了很多“线上码流正常,本地却复现不了”的问题:抓的是原始报文,不是播放器转存的TS。

2.3 验证样本是不是真TS:十秒检查法

拿到TS文件后,第一步不是丢进分析工具,而是先验证它到底是不是TS,很多“分析工具报错”其实是文件本身是MP4改了后缀。先用ffprobe验证:

ffprobe -v error -show_entries stream=index,codec_type,codec_name -of csv output.ts

正常输出类似:

0,video,h264 1,audio,aac

如果输出“Invalid data found”,那多半是假TS。也可以用xxd快速看结构:

xxd -l 48 output.ts

如果0x47每隔0xbc(188)出现一次,基本可以认为包对齐。另一种情况是TS带RS纠错,每包204字节,此时0x47间隔是0xcc。你需要告诉分析工具“Packet size=204”,否则它会看到大量同步丢失,误以为文件损坏。

这一步很重要,因为后续所有PID统计、PCR检查都建立在包对齐的前提上。包没对齐,工具会自己重新找同步,但丢失最前面的几十个包,导致连续性检查结果全是假的。我一般在任何分析之前都先跑这10秒检查,省掉很多后续的翻车。

3. 用 ts analysis tool 拆开TS包:三张表看清全部结构

3.1 TS包定长188字节:同步头、PID、适配字段

TS包固定188字节,由4字节头部和184字节载荷组成。头部第1字节必须是0x47,第2到第3字节里藏着13位PID,第4字节的低4位是连续计数器(continuity_counter)。分析工具做的事,本质就是按188字节切包、读PID、再根据PID解析不同表。

当分析工具显示“同步丢失”时,不要急着怀疑工具。先确认文件是不是TS、包长是不是188/204。我曾经被一个带204字节纠错的录播文件误导,工具里显示每隔一会儿就有丢同步,其实是因为按188切包后,把每包末尾的16个RS校验字节当成了下一包的头部,自然找不到0x47。

PID列表是分析工具的首页信息,每个PID的出现次数、首次出现位置、最后出现位置都会被统计。PID 0x0000一定是PAT,0x0011是SDT,0x0012是EIT,音视频PID则是由PMT表动态告诉你的。只看一次PID列表,你就能判断这个TS是不是多重节目:如果有好几个PID都在跑,而且PAT里有多个节目号,说明是复用了一整条节目流。

3.2 用ffprobe快速看PID、时长和音视频编码

ffprobe是FFmpeg家族里的探测工具,虽然它不是专门的TS分析软件,但对“这文件是什么编码、有没有音轨”的判断非常快:

ffprobe -show_packets -read_intervals "%+#20" -show_data output.ts

-read_intervals "%+#20"表示只分析前20秒;-show_data会打印载荷,能直接看到PES头和H.264的00 00 01起始码。输出里会列出每个包的pid、pos、dts、pts、size:

packet: pos=376 bytes=188 flags=K__ pid=0x0101 dts=3600 pts=3600

newffprobe对PES层显示到包粒度,但不会直接告诉你“PID 0x0101是视频”还是“音频”。不过我们能从dts/pts增长规律猜出来:25fps视频每个PES间隔约40ms,音频间隔更密集。如果要看PAT/PMT到底写了什么,还得换tsduck这类TS专用分析工具。

3.3 PAT/PMT/SDT三张表的关系:先有PAT,再有PMT

TS结构里最核心的是三张表。PAT(Program Association Table)固定挂在PID 0x0000上,它只做一件事:告诉解析器“节目号1对应的PMT在PID 0x0100”。PMT(Program Map Table)里才是真正的内容清单:这个节目包含哪些流,每个流的PID和编码类型是什么。SDT(Service Description Table)在PID 0x0011上,提供节目名、服务商等附加信息,排障时用得少。

用tsduck的分析插件,可以直观看到这三层:

tsp -I file output.ts -P analyze -O drop

输出大致是:

PAT: Program 1: PMT PID 0x0100 PMT: Stream 0x0101: H.264/AVC video, type 0x1B Stream 0x0102: AAC audio, type 0x0F

这就是整个TS结构的骨架。视频PID和音频PID不是写死在解码器里的,而是通过PAT找到PMT,再从PMT里查出来。所以任何TS分析工具,第一步都要先解析PAT,否则后面全是盲猜。在避坑章里会提到,很多黑屏问题就是PMT信息缺失导致的。

表名PID作用关键字段
PAT0x0000节目号到PMT PID的映射program_number, PMT_PID
PMT从PAT获取流类型到PID的映射stream_type, elementary_PID
SDT0x0011服务描述服务名、服务提供商
PES音视频PID音视频访问单元PTS/DTS, stream_id

3.4 PES拿到之后:时间戳、帧边界和关键参数

TS包载荷里装的数据,分裂再拼起来就是PES包。视频PES通常包含一个视频访问单元,也就是一帧;音频PES可能包含若干帧。分析工具在这一层能显示PTS/DTS,这是判断音画同步的最重要原始数据。

用tsduck的PES插件,或者直接看ffprobe的pts字段。视频25fps时PTS增量大概是40ms,30fps是33.3ms,音频则看采样率。如果PTS序列出现大跳变,比如从36000直接跳到190000,说明码流里有拼接、GOP被修剪或者分片边界没有连续性。.ts分片在拼接分析时经常出现这类提示,但播放器本身又能播,因为多数播放器会在分片边界重置时间轴。分析工具和播放器的容忍度不同,所以解读输出时要带着场景。

PCR(Program Clock Reference)往往在音视频PID之一里,或者单独的PID。它用来给解码器恢复27MHz时钟,分析工具能算出每次PCR插入的间隔。正常PCR间隔应该在40ms以内,超过100ms,很多播放器就会出现音画不同步。第4章会专门讲怎么设置阈值,因为默认情况下不少工具只显示、不报警。

4. ts analysis tool的必调参数:不设对,分析结果会骗你

4.1 连续性检查:不开等于只看结构,不看健康度

很多通用播放器工具默认只解析帧,不检查连续计数器。连续计数器是每个PID内部4bit计数,从0到15循环。每收到一个该PID的TS包,计数器加1。如果解析器发现某个PID的CC从5直接跳到8,说明中间丢了2个包。这是检测UDP丢包最有效的手段。

tsduck里打开连续性检查:

tsp -I file output.ts -P continuity -O drop

输出样例:

PID 0x0101, packet 1234: continuity error, expected 5, got 8

注意一个细节:只有承载载荷的包,CC才会递增。如果包只有适配字段,比如PCR单独占用一个适配字段包,那么CC不递增。分析工具如果没考虑这点,就会误报。我碰到过一次,某个编码器每隔一段距离插一个纯PCR适配包,tsduck默认处理是对的,但一些自制脚本没判断adaptation_field_control,把CC不递增全算成了丢包。

如果CC错误集中出现在特定PID,而其他PID正常,说明丢包和这个PID的码率或发送时刻有关。如果所有PID都跳,大概率是抓包网卡丢包,不是码流问题。判断方法就是关掉网卡收包卸载功能再抓一次。

4.2 PCR间隔分析:音画不同步的暗礁

PCR是TS分析工具里最值得调的一个参数。默认的pcr插件会输出每个PCR的间隔,但不会主动告诉你“这个间隔是不是太长”。在命令行里加阈值:

tsp -I file output.ts -P pcr --max-interval 40 -O drop

--max-interval 40表示超过40ms就报警。为什么是40?因为25fps视频一帧正好40ms,PCR插入间隔超过一个帧周期,解码器恢复出来的时钟就开始抖。对于一些码率高的流,PCR间隔稍大也能忍,所以我一般会用100ms做宽放阈值,定位“偶尔音画不同步”时再收紧到40ms。

PCR位于哪个PID也很关键。PMT里会有PCR_PID声明。如果PCR_PID指向视频PID或音频PID,解码器会跟着该流的时间戳走;如果声明了一个单独的数据PID,那这个PID必须定期出现。用tsduck的-P analyze能看到PCR PID: 0x0101这类信息。PCR间隔分析最怕遇到没有PCR的TS流,这时解码器会退化为“音频为主时钟”,视频帧率稍微波动就会出现音画漂移。工具不会把这种流标错,但如果配了检查参数,会提示“PCR not found”。

4.3 PTS连续性窗口:时间戳偏差的报警阈值

PTS是播放器真正用来做音画同步的,但在1分钟以上的文件里,PTS会和PCR有累积偏差。比如PCR稳定,PTS却每隔十几秒慢一点,说明编码器生成PTS时参考时钟不精确。tsduck的-P pts插件可以限制PTS与PCR的最大偏差:

tsp -I file output.ts -P pts --max-pcr-pts-offset 100 -O drop

超过100ms就告警。这个参数在做Android播放器时很实用:Media3 ExoPlayer播同一个.ts分片,有时只在特定片段出现音画异步,工具正好在对应位置报PTS/PCR偏差过大。把这段流的PCR、PTS列出来比对,往往能看到某个B帧顺序异常或Temporal Reference错乱。这类问题靠看播放器日志很难定位,靠分析工具的阈值报警就容易得多。

对于.ts分片拼成的长文件,PTS经常在新分片起始处不再连续,这是因为HLS分片的编码器或切片器重置了时间戳。此时--max-pcr-pts-offset会误报。可以按分片单独分析,或者设置工具忽略分片边界。分析工具的输出只是一组告警,我们要按场景判断是不是真故障,而不是见错就改。

4.4 常用参数速查表

参数作用建议值
continuity检测连续计数器丢包开
pcr.max-intervalPCR最大插入间隔40~100ms
pts.max-pcr-pts-offsetPTS与PCR最大偏差100ms
analyze.仅前N包快速结构判断1000包

我一般的分析套路是先前1000包看结构,再整段扫连续性,最后由具体告警位置反查PCR/PTS。如果一开始就用全量输出,几十万行日志只会把真正的问题淹没。

5. TS分析避坑:5个翻车现场与对应的排查手段

5.1 PAT/PMT只在开头出现:播放器黑屏,工具却显示结构完整

现象:抓回来一段TS,ts analysis tool显示PAT/PMT都有,PID列表正常,但Android播放器加载后黑屏,进度条一点不动。原因:工具从文件开头解析,在最初的几百包看到PAT/PMT就判定“有”,但后续不再重复。播放器从流中任意位置进入,等不到新的PAT/PMT就会判定没有节目。解决:检查PAT/PMT重复周期,ffmpeg封装时一定要加-mpegts_flags +resend_headers,编码器里PAT/PMT间隔不要超过100ms。一个好的分析工具应该能显示PAT出现次数,而不是只判断“有没有”。

5.2 CC连续但播放花屏:抓包阶段就已经错了

现象:分析工具没有报CC丢失,画面却花屏。原因:网卡上TSO/GRO把UDP包重组了,wireshark导出的payload里少了某些尾部数据,但TS包又被巧合对齐,CC计数器看起来连续。这是抓包环境造成的黑匣子。解决:抓包前关掉网卡的校验和卸载和分片卸载,Linux下命令是:

ethtool -K eth0 rx off tx off

然后重新抓包,再用ffprobe验证一次能正常解出视频帧。如果CC没错误但视频解码失败,优先怀疑抓包链路,而不是直接追编码器。

5.3 PCR间隔忽大忽小:音画不同步就是它

现象:分析工具报PCR间隔从30ms跳到200ms,播放器表现偶发音画不同步。原因:编码器插入PCR的时机不固定,或者复用器没有把PCR适配包放在音频/视频包之前。解决:用pcr插件看PCR间隔的直方图,如果最大值超过100ms且集中在I帧附近,说明PCR被大包挤占,常见的补救措施是调整复用器buffer或让编码器专门单独发送PCR包。实测中最有效的一招是把PMT里的PCR_PID指定到固定间隔的专用PID,代价是占用一点带宽,但音画同步稳定性会好很多。

5.4 PTS回绕被当成不连续:跨分片误报要冷静

现象:分析工具对一段6小时TS的尾部报“PTS discontinuity”,播放器却完全正常。原因:PTS是33bit计数,约26.5小时回绕一次,回绕点附近工具会看到从巨大数值直接掉到0的伪跳变。另外HLS分片边界经常不保证PTS连续,分析工具把多个分片拼在一起扫的时候也会误报。解决:分析工具要能识别回绕,比如把PTS差值按无符号数计算;分析分片长流时最好按分片边界分开。我处理这种问题的方法是先看告警处前后几十个包的PTS序列,如果它是从0x1FFFFFFFF滚回0x0,那就是回绕,不是故障。

5.5 私有流和加密流:工具只能分析到传输层

现象:分析工具到了某个PID显示“unknown”,PES长度超长或全是0xFF。原因:该PID承载的是条件接收加密流或工程私有数据,stream_type经常是0x06(private data),PES层被加密,工具没有密钥自然解不开。解决:先从PAT/PMT找到stream_type和CA描述符。如果确认是加密流,就看它的CC是否连续、PCR是否正常,传输层健康就算合格;私有流则需要核对CA私有描述符。很多开发者第一次碰到这种流,以为分析工具有bug,其实换谁都没法在不脱壳的情况下解析载荷。分析工具在这时候更像一个“传输层体检仪”,而不是破解工具。

6. 进阶:写一个几十行的PID统计脚本,把TS分析固化到工程里

6.1 一个不依赖第三方库的TS快速体检脚本

前面用现成工具,最后提倡自己写一个极简脚本,因为把“读TS包结构”这个动作变成代码,才算真正掌握了TS结构。下面这份Python脚本只做三件事:统计PID、统计同步丢失、统计连续计数器跳变。

import sys import collections def analyze_ts(filename): with open(filename, 'rb') as f: data = f.read() pids = collections.Counter() cc_seen = {} cc_errors = 0 sync_errors = 0 pos = data.find(b'\x47') # 找第一个同步字节 while pos != -1 and pos + 188 <= len(data): if data[pos] != 0x47: # 理论不会触发,防御性判断 sync_errors += 1 pos = data.find(b'\x47', pos + 1) continue pid = ((data[pos + 1] & 0x1F) << 8) | data[pos + 2] afc = (data[pos + 3] >> 4) & 0x03 cc = data[pos + 3] & 0x0F has_payload = afc in (0b10, 0b11) if has_payload: if pid in cc_seen and cc != (cc_seen[pid] + 1) % 16: cc_errors += 1 cc_seen[pid] = cc pids[pid] += 1 pos += 188 print(f"PID count: {len(pids)}") for pid, cnt in sorted(pids.items()): print(f" PID 0x{pid:04X}: {cnt} packets") print(f"Sync errors: {sync_errors}") print(f"CC errors: {cc_errors}") if __name__ == "__main__": analyze_ts(sys.argv[1])

代码逻辑不复杂:找到0x47后按188字节前进,从头部提取PID和连续计数器。只有在包真正携带载荷时,CC才递增,所以用afc in (0b10, 0b11)过滤。如果同一PID的CC没有按步进+1走,说明该PID的包在这段流里有丢失或乱序。CC的错误统计比同步错误更敏锐,因为TS同步检测只能看到边界错乱,而CC能看到包级别的空洞。

这个脚本可以直接配合命令行自动跑:

python ts_check.py output.ts

输出:

PID count: 5 PID 0x0000: 120 packets PID 0x0100: 240 packets PID 0x0101: 6120 packets PID 0x0102: 2040 packets Sync errors: 0 CC errors: 0

把这段脚本丢进CI,每次新版本码流生成后自动跑一遍,比每次手动用GUI工具快得多。如果需要分析PAT/PMT的具体映射,脚本里再加一段解析PAT包的逻辑也不难,但那是另一个层次的事。我现在接手任何陌生TS,习惯永远是先跑这个体检脚本,确认包对齐和CC无错后,再拿tsduck看PAT/PMT和PCR/PTS。这个习惯帮我避开了很多假故障,也希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询