ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

pcap2ps实战:从抓包文件提取GB28181国标PS流并播放

pcap2ps实战:从抓包文件提取GB28181国标PS流并播放 简介这份资源面向网络运维、安全分析与协议开发人员聚焦从tcpdump或Wireshark生成的pcap抓包文件中筛选并提取符合国家标准的网络数据流这一实际需求。包内共2个文件包含1个C语言源码与1个Markdown说明文档压缩包约5KB体量轻巧便于快速阅读与二次开发。源码部分承担pcap解析、协议字段识别与国标流筛选的核心逻辑说明文档则交代使用方式与实现思路二者配合可帮助读者理解抓包数据的解析流程与筛选策略。目前已有221人学习下载适合需要处理网络流量、开展协议分析或进行故障排查的技术人员参考。通过阅读源码与文档读者能够掌握从混杂协议数据中定位目标数据流的方法并可将相关思路迁移到网络监控、安全事件检测或数据格式转换等场景中。1. 从 tcpdump 或 Wireshark 抓包文件里把国标流捞出来pcap2ps 到底在解决什么手里攥着一个几百 MB 的 pcap用 Wireshark 打开满屏都是 TCP、UDP、ARP翻半天找不到一路完整的国标视频流——这是做 GB28181 对接、视频平台排障、设备联调时最常遇到的场景。pcap2ps 这类工具要干的事很明确把 tcpdump 或 Wireshark 抓下来的原始报文按 RTP 时间戳和 SSRC 重新拼回一路路 PS 流落成可以直接用 ffplay、VLC 播放的 .ps 文件。它解决的不是抓包问题而是抓完之后怎么把媒体流从信令和杂包中剥离出来的问题。适合三类人做国标平台对接、需要验证设备推流是否正常的后端工程师排查花屏、卡顿、丢包但拿不到原始流的运维以及做协议分析、想看清 PS 封装里 PES 头长什么样的协议开发者。前提是你得先有一份像样的抓包文件且知道国标流走的是 RTP over UDP 还是 TCP 被动/主动模式。2. 先搞懂国标流在 pcap 里长什么样RTP、PS、SSRC 三层关系2.1 国标流为什么不能直接另存为很多人第一次处理这个问题直觉是用 Wireshark 的导出对象 → UDP或者Follow UDP Stream把某一路的载荷存下来改后缀成 .ps 就想播放。结果 ffplay 直接报错或者黑屏。原因在于国标 GB28181 的媒体面是RTP 承载 PS 流一个 PS 流会被切成很多个 RTP 包每个 RTP 包有独立的序列号和时间戳而 Wireshark 的 UDP 导出是按五元组过滤的它会把同一路流的所有 RTP 包按抓包顺序拼起来但不会去掉每个 RTP 的 12 字节头也不会处理乱序和重传。你导出的文件里每隔一段就插着一段 RTP 头PS 解析器读到就崩。正确的层次是pcap → 过滤出目标 RTP 流 → 按 SSRC 分组 → 按 RTP 序列号排序 → 剥掉 RTP 头 → 拼接 payload → 得到连续 PS 流 → 写入 .ps 文件。pcap2ps 的核心逻辑就是这条链路缺一环都会翻车。2.2 用 tshark 先确认流的存在和特征在动手写脚本或跑工具前先用 tsharkWireshark 命令行版把 pcap 里的 RTP 流摸清楚这一步能省掉后面大量瞎猜。# 统计 pcap 里所有 RTP 流的 SSRC 和包数量确认有几路国标流 tshark -r capture.pcap -Y rtp -T fields -e rtp.ssrc -e rtp.seq -e ip.src -e udp.srcport \ | awk {print $1} | sort | uniq -c | sort -rn # 看某一路 SSRC 的 RTP 时间戳跨度判断流是否连续 tshark -r capture.pcap -Y rtp rtp.ssrc0x0a1b2c3d \ -T fields -e rtp.timestamp -e rtp.seq | head -20 # 确认 PS 流的 payload type国标常见 96动态或 98 tshark -r capture.pcap -Y rtp -T fields -e rtp.p_type | sort | uniq -c第一段命令按 SSRC 聚合输出里包数量最多的那个 SSRC 通常就是主码流第二段看时间戳是否单调递增如果时间戳跳变剧烈说明抓包期间流有中断第三段确认 payload type国标设备常用 96 表示 PS。这三步做完你心里就有数了有几路流、哪路是主码流、流是否完整。提示如果 tshark 报 rtp 过滤器无效说明你的 Wireshark 版本没启用 RTP 解析或者抓包时端口不是标准 RTP 端口。国标 RTP 端口通常在 30000 以上可以在 Wireshark 里手动 Decode As → RTP。2.3 SSRC 是分流的唯一可靠依据国标流的多路复用场景里同一个 IP、同一个端口可能承载多路 SSRC 不同的流比如主码流和子码流走同一 UDP 端口。这时候按 IP端口过滤会混流必须按 SSRC 分。SSRC 是 RTP 头里 4 字节的同步源标识国标设备一般用十六进制比如0x0a1b2c3d。在 Wireshark 里可以直接rtp.ssrc 0x0a1b2c3d过滤在 tshark 里同理。记住这个值后面提取时它就是你的流 ID。3. 用 pcap2ps 把 pcap 转成可播放 PS 流完整操作链路3.1 环境准备与工具获取思路pcap2ps 不是一个官方标准工具而是一类脚本/小工具的统称常见实现有 Python 版基于 scapy 或 dpkt和 C 版基于 libpcap。我一般用 Python 版因为改起来快、依赖少。核心依赖就两个dpkt或scapy读 pcapstruct解析 RTP 头。安装pip install dpkt scapy # 验证 dpkt 能正常读 pcap python -c import dpkt; fopen(capture.pcap,rb); print(ok)如果你拿到的是别人打包好的 pcap2ps 脚本先看它读 pcap 用的是 dpkt 还是 scapy两者 API 不同混用会报错。我踩过的坑是脚本用 dpkt 写的环境里只装了 scapy跑起来直接 ImportError还以为是 pcap 文件坏了。3.2 核心提取脚本按 SSRC 分流并剥离 RTP 头下面这段是我常用的最小可用版本逻辑清晰方便你按需改。import dpkt import struct import sys def extract_ps(pcap_file, target_ssrc, out_file): # target_ssrc 传十六进制字符串如 0a1b2c3d ssrc_int int(target_ssrc, 16) packets [] # (seq, timestamp, payload) with open(pcap_file, rb) as f: pcap dpkt.pcap.Reader(f) for ts, buf in pcap: try: eth dpkt.ethernet.Ethernet(buf) ip eth.data if not isinstance(ip.data, dpkt.udp.UDP): continue udp ip.data payload udp.data if len(payload) 12: continue # 解析 RTP 头前 12 字节 rtp_header payload[:12] version (rtp_header[0] 6) 0x03 if version ! 2: continue # 不是 RTP v2跳过 seq struct.unpack(!H, rtp_header[2:4])[0] timestamp struct.unpack(!I, rtp_header[4:8])[0] ssrc struct.unpack(!I, rtp_header[8:12])[0] if ssrc ! ssrc_int: continue # 检查是否有 CSRC 扩展有则跳过扩展头 cc rtp_header[0] 0x0F header_len 12 cc * 4 # 处理 RTP 扩展头国标一般不用但保险 if rtp_header[0] 0x10: ext_len struct.unpack(!H, payload[header_len2:header_len4])[0] header_len 4 ext_len * 4 rtp_payload payload[header_len:] packets.append((seq, timestamp, rtp_payload)) except Exception: continue # 按 RTP 序列号排序处理乱序 packets.sort(keylambda x: x[0]) with open(out_file, wb) as f: for seq, ts, data in packets: f.write(data) print(fextracted {len(packets)} packets to {out_file}) if __name__ __main__: extract_ps(sys.argv[1], sys.argv[2], sys.argv[3])逻辑说明脚本遍历 pcap 每个包先判断是不是 UDP再判断 payload 长度够不够 12 字节然后解析 RTP 头的 version、seq、timestamp、ssrc。只有 ssrc 匹配目标值才保留。关键点是按 seq 排序因为网络抓包可能乱序不排序直接拼会导致 PS 流里 PES 包顺序错乱播放器花屏。参数说明target_ssrc传十六进制字符串不带0xout_file建议用.ps后缀。运行方式python pcap2ps.py capture.pcap 0a1b2c3d output.ps3.3 验证提取结果ffplay 和 ffprobe 双保险提取完别急着交付先用 ffprobe 看流信息再用 ffplay 实际播一下。# 看 PS 流里有没有视频、音频、编码格式 ffprobe -v error -show_streams -select_streams v output.ps # 实际播放观察是否花屏、卡顿 ffplay -i output.psffprobe 输出里重点看codec_name国标常见 h264、width/height、duration。如果 ffprobe 报 Invalid data found when processing input说明 PS 头没对齐或者 RTP 头没剥干净。这时候回 Wireshark 看第一个 RTP 包的 payload 前几个字节正常 PS 流开头应该是00 00 01 BAPS 包头。如果不是说明你的 header_len 算错了多半是 CSRC 或扩展头没处理。注意有些国标设备会在 RTP payload 前加 4 字节的自定义头比如海康的私有扩展这时候标准 RTP 头解析会多剥或少剥。判断方法看 payload 开头是不是00 00 01 BA不是就手动调 header_len。4. 避坑与排查pcap2ps 提取国标流最容易翻车的 5 个点4.1 提取出来的 PS 文件播放花屏、马赛克现象ffplay 能打开但画面大面积花屏声音断断续续。原因RTP 包乱序没排序或者丢包后直接拼接导致 PES 包不连续。国标流对包序敏感一个 PES 包跨多个 RTP 包顺序错了解码器就崩。解决在脚本里按 RTP seq 排序后再写文件如果丢包严重考虑在丢包处插入空包或者用ffmpeg -fflags genpts重新生成时间戳。更彻底的办法是先用 tshark 统计丢包率丢包超过 5% 的流提取出来也没法看。4.2 tshark 过滤不到 RTP全是 UDP现象tshark -Y rtp返回空但明明有 UDP 包。原因Wireshark/tshark 默认只对标准 RTP 端口如 5004做 RTP 解析国标 RTP 端口是动态协商的不在默认列表里。解决在 Wireshark 里右键某个 UDP 包 → Decode As → 选 RTP命令行下用tshark -d udp.port30000,rtp手动指定端口解码。或者干脆不用 rtp 过滤器直接按 UDP 解析 payload自己判断 RTP 版本号。4.3 多路流混在一起提取出来是大杂烩现象提取的 PS 文件里画面来回切换或者 ffprobe 报多个视频流。原因抓包文件里有多路 SSRC脚本没按 SSRC 过滤把所有 RTP 包都写进去了。解决先用 2.2 节的 tshark 命令列出所有 SSRC确认目标 SSRC 再提取。如果确实需要合并多路那要按 SSRC 分别提取成多个 .ps 文件不能混在一个文件里。4.4 TCP 承载的国标流提取失败现象pcap 里国标流走的是 TCP脚本只处理 UDP提取为空。原因GB28181 支持 TCP 被动和 TCP 主动两种媒体传输模式TCP 模式下 RTP 包前面还有 2 字节的长度字段RFC4571。解决TCP 模式要先按 TCP 流重组再解析每个 RTP 包前的 2 字节长度。Wireshark 里可以用tcp.stream eq N过滤后 Follow TCP Stream但同样要去掉长度字段。脚本改动较大建议直接用支持 TCP 的 pcap2ps 变体或者用rtpbreak这类工具。4.5 提取的文件巨大但播放只有几秒现象output.ps 有几百 MBffplay 只播了几秒就停。原因RTP 时间戳没处理PS 流里 PTS/DTS 混乱播放器解析到某个点就卡死。或者提取时把重传包也写进去了导致数据重复。解决检查 RTP 包的 timestamp 是否单调重传包seq 重复要去重。可以在脚本里加一个 seen_seq 集合重复 seq 直接跳过。另外确认 PS 流里的 PTS 是否连续必要时用 ffmpeg 重新封装ffmpeg -i output.ps -c copy -fflags genpts fixed.ps。5. 进阶技巧批量提取、按时间切片与自动化验证5.1 一个 pcap 里批量提取所有国标流实际排障时一个 pcap 里往往有十几路流手动一个个提太慢。我一般先跑一遍统计拿到所有 SSRC再循环调用提取函数。import subprocess # 先用 tshark 拿到所有 SSRC cmd tshark -r capture.pcap -Y rtp -T fields -e rtp.ssrc result subprocess.run(cmd, shellTrue, capture_outputTrue, textTrue) ssrc_list set(line.strip() for line in result.stdout.splitlines() if line.strip()) for ssrc in ssrc_list: out fstream_{ssrc}.ps subprocess.run(fpython pcap2ps.py capture.pcap {ssrc} {out}, shellTrue) print(fdone {out})这段逻辑很直白tshark 列出所有 SSRC去重后逐个提取。参数上注意 SSRC 在 tshark 输出里可能是0x0a1b2c3d格式传给脚本前要 strip 掉0x。批量提取后用ls -lh stream_*.ps看文件大小明显偏小的那几路大概率是流不完整或者 SSRC 统计有误。5.2 按时间窗口切片只提关键片段pcap 太大时全量提取耗时且占空间。如果只关心某次卡顿发生的那 30 秒可以先用 editcap 切片再提取。# 提取从第 100 秒开始、持续 30 秒的报文 editcap -A 2024-01-01 10:00:00 -B 2024-01-01 10:00:30 capture.pcap slice.pcap # 再对 slice.pcap 做 pcap2ps python pcap2ps.py slice.pcap 0a1b2c3d slice.pseditcap 的-A/-B是绝对时间需要你知道抓包起始时间。不确定的话先用capinfos capture.pcap看时间范围。切片后再提取速度快很多适合反复调试。5.3 自动化验证用 ffprobe 返回值判断提取是否成功手动播太慢我习惯在脚本最后加一步自动校验。#!/bin/bash # verify_ps.sh for f in *.ps; do if ffprobe -v error -select_streams v -show_entries streamcodec_name -of csvp0 $f | grep -q h264; then echo $f OK else echo $f FAILED fi done这个脚本遍历所有 .ps 文件用 ffprobe 检查有没有 h264 视频流有就 OK没有就 FAILED。参数上-of csvp0让输出只有编码名方便 grep。跑完一眼就能看出哪几路提取失败再针对性回查 SSRC 和 RTP 头解析。5.4 一个我踩过的坑别信看起来对的 PS 头有次提取完文件开头确实是00 00 01 BAffprobe 也能识别但播放就是花屏。查了半天发现是 RTP 扩展头没处理干净——设备在 RTP 头后加了 4 字节扩展我的脚本按标准 12 字节剥的导致 PS 头后面多了 4 字节垃圾。后来养成习惯提取完先xxd output.ps | head -5看前 80 字节确认 PS 包头、系统头、PES 头都是标准起始码再交给播放器。这个习惯帮我省了很多来回折腾的时间。做国标流提取宁可多看一眼十六进制也别假设设备一定按标准来。希望帮到你。本文还有配套的精品资源点击获取
返回列表