500 个序列全部到齐,却报 74.77% loss。已用独立解析和源码核对。
先看实战:工具读数与原始证据
适合作为 SIP/RTP 排查入口。对当前版本,建议同时检查原始流指标、抓包完整性与音频内容。
10% 丢包被 problems 命中,analyze 和单呼叫 Issues 仍写无问题。
静音流得到 4.358 分,导出后左声道 80000 个采样全为零。
切换实验场景
展开当前样本的实测数据与自动分析原文
全部为本地合成 PCAP,实际运行 Sipnab 后取得结果。未连接生产电话系统,也未进行网卡实时抓包。数字固定于 v0.5.159。
听一听导出的媒体
通过 Sipnab 的 MCP export_audio 实际生成。左声道为 A→B,右声道为 B→A;下方都是合成测试音。
正常双向
左:A→B,440Hz;右:B→A,660Hz。两个方向各 10 秒。
下载 WAV · 8kHz / 16bit / 双声道
10% 丢包
左侧每 10 包少 1 包。重建音频不等于接收端实际 PLC 播放。
下载 WAV · 8kHz / 16bit / 双声道
左声道全静音
左声道 RMS=0,右声道有音;两个流的 MOS 都约为 4.358。
下载 WAV · 8kHz / 16bit / 双声道
双声道文件即使左侧静音,右侧仍会有声音。音频验证使用解码后的 RMS 与样本数;未做主观听感评分。
1. 它是什么,边界在哪里
【官方文档+源码】Sipnab 是 Rust 实现的 SIP/RTP 捕获和分析工具,不是电话交换机,也不是 SIP 呼叫发起器。它把 Call-ID、SIP 状态和 SDP 中的媒体地址/端口关联到 RTP 流;也能在缺少可匹配信令时保留孤立媒体流。[S1][S3]
从一个呼叫的生命周期理解它:
- 取得输入:离线 PCAP/PCAPNG、网卡抓包或 HEP 镜像。
- 解封装与重组:链路/IP/传输层解析,必要时处理 IP 分片和 TCP SIP 分帧。
- 整理信令:解析 SIP,按呼叫上下文维护状态、消息、响应码、时间指标和 SDP offer/answer。
- 关联媒体:利用协商信息关联 RTP;观察 SSRC、payload type、序列号、时间戳、到达时间、包数、丢包和抖动。
- 读取结果:TUI、CLI/JSON、REST、MCP 和 Prometheus 使用共享分析存储,但具体字段和诊断覆盖必须逐个输出入口核对。
【能力已公开,未实测】HEP、REST、Prometheus、TLS/SRTP 解密、安全检测、多腿关联、PCAPNG、复杂封装、Opus 等。MIT OR Apache-2.0 双许可由同版本 Cargo.toml 和发布包内两份许可证确认。GitHub 仓库摘要的单一许可证标签不完整。[S1][S2][S3]
2. 本机怎么使用
已完成的本地安装
本机为 macOS ARM64,使用官方 sipnab-0.5.159-aarch64-apple-darwin.tar.gz。未编译 Rust,未安装新的全局包,二进制和音频插件仅放在 sipnab-lab/bin/。发布包 SHA-256 与 GitHub Release API 中的 digest 相同:
843315ed3e5caaa16991fab712da7bf8a385fcbdc798ebb3a56fafe4967bb5ad这证明下载内容与该次 GitHub 发布记录相符,不等于独立签名审计。本次未执行 gh attestation verify。版本详情、下载 URL、时间和校验结果在 evidence/release.json 与 evidence/install-verification.json。[S2]
cd '/path/to/sipnab-lab'
./bin/sipnab --version
./bin/sipnab --no-config -I fixtures/baseline.pcap--no-config 忽略宿主已有配置,使本实验使用相同默认值。离线 -I 模式不需要 sudo。这里的 shell 命令以本实验目录为工作目录。[S4]
其他机器的安装方法(文档路径,未逐平台实测)
| 环境 | 方法 | 适用条件 |
|---|---|---|
| macOS | brew install NormB/tap/sipnab |
官方 Homebrew tap;使用后以 sipnab --version 核对真实版本 |
| Linux x86_64/ARM64 | 从 Release 选择匹配架构的 deb/rpm 或 tar.gz | GNU 包要求 glibc 2.36+ 与 libpcap;不要用 macOS 二进制 |
| Linux 老发行版 | 选择对应 unknown-linux-musl 包 |
发布说明标注静态版本;不含 TUI 音频播放 |
| 源码构建 | cargo build --release --features full |
本版本文档要求 Rust 1.97+、libpcap 开发头文件;默认源码特性集不等于完整发布包 |
本次 --version 实际列出 native,tui,audio,tls,hep,api,mcp,mcp-http,metrics,plugins,vcon。macOS 抓实时网卡需要 BPF 访问权限;Linux 需要相应抓包权限。[S1][S2][S4]
3. 十分钟上手:先看信令,再看媒体
TUI 操作路线
| 所在位置 | 按键 | 用途 |
|---|---|---|
| Call List | ↑/↓ 或 j/k → Enter | 选择呼叫并进入 SIP 时序图 |
| Call Flow | j/k;Enter;Esc | 逐条读消息;查看原始消息;返回 |
| Call Flow | d | 切换 SDP 展示,核对 c=、m=audio、codec、ptime |
| Call Flow | m → 移到后续消息 → M | 标记起点、看时间差、清除标记 |
| Call List | Tab | 进入 RTP Streams |
| Call Flow | r | 进入该呼叫的 RTP 流列表 |
| RTP Streams | Enter | 看单流详情:丢包、抖动、MOS、RTT/延迟假设 |
| RTP/Stream Detail | F2 | 保存 WAV;本次实际 WAV 导出使用 MCP |
| 多个视图 | F1 | 查询当前视图快捷键;某些按键随视图而变 |
| Call List | q,然后 y | 退出;本次已实际走通 |
【本地实测】本次使用丢包样本走通 Call List → Enter → Call Flow → r → RTP Streams → Enter → Stream Detail → 退出。终端记录 results/tui-session.ansi 显示 450 包、丢失 50 包、10.00% loss、MOS 2.2(Bad),并显示 delay 100ms assumed。其他表中按键依据官方 walkthrough,未逐键实测。[S5][L4]
批量命令:知道每一种输出是什么
# 全部呼叫、每条一个 JSON 对象,便于脚本读取
./bin/sipnab --no-config -N -I fixtures/loss-10pct.pcap \
--json-dialogs --no-cli-print > results/my-calls.ndjson
# 筛出诊断别名命中的呼叫;还要检查原始指标
./bin/sipnab --no-config -N -I fixtures/loss-10pct.pcap \
--problems --json-dialogs --no-cli-print
# 单通电话报告
./bin/sipnab --no-config -N -I fixtures/loss-10pct.pcap \
--call-report 'loss-10pct@sipnab-lab.invalid' \
--markdown --no-cli-print
# 精确过滤:这个版本的 DSL 使用 rtp.loss(百分数)
./bin/sipnab --no-config -N -I fixtures/loss-10pct.pcap \
--filter 'rtp.loss > 5' --json-dialogs --no-cli-print
# 缺一个媒体方向、信令失败
./bin/sipnab --no-config -N -I fixtures/one-way.pcap \
--one-way --report --no-cli-print
./bin/sipnab --no-config -N -I fixtures/busy-486.pcap \
--filter "state == 'Failed'" --report --no-cli-print-I 是大写字母 I;-N 是非交互;--json 是每条 SIP 消息一个对象,--json-dialogs 才是每个 dialog 一个对象;--no-cli-print 防止普通消息输出混入结构化报告。本次的 per-dialog NDJSON 没有 MOS 字段,MCP rtp_stats 才提供本报告使用的 MOS。[S4][S6][L1]
过滤有两层:BPF 决定哪些包进入分析,--filter 决定哪些呼叫显示。如果 BPF 只写 port 5060,RTP 动态端口会被提前丢弃。--portrange 则是 SIP 识别端口范围,不能把它当 RTP 媒体范围。[S4][S6]
4. 六组实战设计与结果
【实验设定】无真实号码、无生产连接、无网卡抓包。Python 直接生成 PCAP 字节,不发送 UDP。地址使用文档地址 192.0.2.10/20,Call-ID 使用 .invalid。信令走 UDP/5060,媒体走 UDP/4000 与 5000;PT=0、PCMU/8000、ptime=20ms,SSRC 分别 0x11111111 / 0x22222222。两个方向基准音分别为 440/660 Hz。没有 RTCP,MOS 使用 Sipnab 默认的 100ms 单向路径延迟假设。
正常呼叫时间线:INVITE 0ms → 100 Trying 50ms → 180 Ringing 200ms → 200 OK 500ms → ACK 510ms → 首个 RTP 600/602ms → BYE 11000ms → 200 OK 11020ms。每个方向 500 包、每包 160 个采样点,合计 10 秒音频;Sipnab 的 duration_sec=11.02 是该 dialog 的观察跨度,不能直接当接通后的通话时长。
| 样本 | 注入条件 | A/B 到达包数 | A→B 真实缺包 | Sipnab A→B loss | A→B MOS | --problems |
|---|---|---|---|---|---|---|
| baseline | 无故障 | 500/500 | 0 | 0.00% | 4.358 | 未命中 |
| loss-10pct | A→B 每 10 包去掉第 6 包 | 450/500 | 50 | 10.00% | 2.228 | 命中 |
| jitter-reorder | A→B 奇数索引包额外延迟 120ms,保留全部包 | 500/500 | 0 | 74.77% | 1.000 | 命中 |
| one-way | 删除 B→A 全部媒体,保留双向 SIP | 500/0 | A→B 为 0 | 0.00% | 4.358 | 命中 |
| silent-payload | A→B 的 160 字节音频载荷全部为 0xFF | 500/500 | 0 | 0.00% | 4.358 | 未命中 |
| busy-486 | 486 Busy Here + ACK,不建立媒体 | 0/0 | 不适用 | 不适用 | 不适用 | 命中 |
表中 MOS 是 Sipnab 的模型估计,没有做人工主观 MOS、PESQ 或 POLQA。乱序行的 MOS 受错误丢包输入污染,不能当听感结论;单向流的高 MOS 只描述存在的那个流,不替缺失方向评分。[L1][L2]
发现 A:乱序统计为何出错
独立脚本从 PCAP 解析 RTP 序列:500 个唯一序列、1000–1499 连续齐全、0 重复、247 次序列回退;每个相邻逻辑包的 RTP 时间戳增量仍为 160。Wireshark 4.6.7 同样显示 500 包、0 丢包,并标记乱序问题。[L2]
【源码核对】src/rtp/stream.rs:811–831 以 last_seq + 1 计算缺口,较小前向 gap 立即累加到 lost_packets,随后无条件把 last_seq 更新为新到达包的序号;该更新路径没有在迟到包补齐时扣回。这解释本样本中的累加高估。[S7]
同样本还产生 Ptime asymmetry: 10 ms vs 20 ms 提示,但两端 SDP 都声明 20ms,且真实 RTP 逻辑时间戳都是每包 160/8000=20ms。这条提示也不能据此归因为真实重分包或中间设备。 抖动读数(Sipnab 最终 98.88ms、Wireshark mean 47.875ms)口径和乱序处理不同,不把它们直接判为同一指标不一致。
发现 B:自动摘要不是全覆盖质量检查
loss-10pct 的原始流指标已准确显示 10%,--problems 与 MCP find_problems 都选中这通电话,但 --analyze 与 call-report 的 Issues 没有对应提示。[L1]
【源码核对】该版本 analysis.rs 的 FindingKind 枚举没有纯 RTP loss/jitter 类别;call_report.rs 的 Issues 只依据 diagnosis.hints 是否为空。DSL problems 则直接包含 rtp.loss > loss_bad_pct 与 rtp.jitter > jitter_bad_ms。[S8][S9]
用法:同时保留 per-stream 指标与筛选结果;不要把文本中的 No problems found 或进程退出码 0 当作业务质量合格。退出码主要反映执行是否成功,而非呼叫有无故障。[S4]
发现 C:传输正常仍可以是静音
silent-payload 的两个方向均被正确关联,MOS 都约 4.358,one_way_audio=false。MCP 导出的 8kHz、16bit、双声道、10秒 WAV 左声道 80000 个采样全部为零,右声道正常。测得 RMS 为 0 / 6344.33。[L3]
【解释】本模型的输入是网络侧丢包、抖动、codec 损伤参数和路径延迟,不检查词语是否说出来、TTS 是否正确生成、音量是否合理。CN/PT13 的静默跟踪也不能等同于识别所有 PCMU 零载荷。高 MOS、双向都有 RTP、200 OK 都不能代替媒体内容检查。[S7][S10]
对单向与 486 的正确解读
one-way 被标记 one_way_audio=true;这证明本抓包视点缺少反向媒体。真实排障仍需排除镜像漏采、非对称路由、direct media、hold/sendonly、VAD/舒适噪声等情形,不能单凭这一点断定接收端真的无声。
busy-486 的状态为 Failed,最终响应 486;它是合法的忙线结果,不必然是网络或软件故障。本实验还验证 SIP 失败时无媒体不会被自动理解为“已经接通但无媒体”。
5. MCP 实战与音频导出
【本地实测】使用临时 stdio 子进程,执行 initialize → tools/list → capture_status → rtp_stats → find_problems;在三个保留载荷的进程中执行 export_audio。实测工具列表共 65 个。没有把它注册到任何常驻 AI 客户端,也没有打开 HTTP MCP 监听端口。[L3]
# 临时 MCP 进程;它等待 JSON-RPC 请求,不是普通文本输出命令
./bin/sipnab --no-config -I fixtures/baseline.pcap --mcp --quiet \
--retain-audio --mcp-file-root ./results/audio
# 已提供的客户端脚本完成协议握手与查询;运行后自动结束子进程
python3 scripts/run_mcp.py关键调用参数:
{"name":"rtp_stats","arguments":{"call_id":"baseline@sipnab-lab.invalid"}}
{"name":"export_audio","arguments":{"call_id":"baseline@sipnab-lab.invalid","filename":"baseline.wav"}}这是 tools/call 的 params 内容,不是完整 JSON-RPC 握手。完整实现见 scripts/mcp_probe.py,实际请求响应见 results/*-mcp-transcript.json。
操作要点:
- 先看
capture_status.source_exhausted=true和source_stopped_early=false,避免把尚未读完的文件当全量结果。 - 核对
orphaned_stream_count、undecodable/snapped frames、端口过滤与 retention cap;它们描述本次捕获是否完整。 --retain-audio才让批量 MCP 进程保存载荷;没有载荷留存,导出失败不能解释为原通话没音频。- 音频文件限定到
--mcp-file-root。本次实测已有同名文件会被拒绝覆盖;脚本对固定样本重跑复用现有 WAV 和原始 export_audio 回执。 --json-dialogs、MCPrtp_stats、TUI 是不同输出表面;先检查真实 schema 再写解析脚本。mos_grounded说明模型参数的来源资格,不等于人工听测。- 这三个 WAV 是从观测到的 RTP 重建的双声道测试音,不是终端原生录音;丢包填补、抖动缓冲、PLC 和实际播放效果可能不同。
6. 用到 FreeSWITCH / AI 语音链路时,怎么安排
【工程建议,未连接生产】推荐先收齐一个已知问题呼叫的信令与媒体 PCAP,在工单中记录抓包点、Call-ID、起止时间、两条腿/通道标识、codec 与 ptime。把 Sipnab 作为离线第一遍分析,再用 Wireshark 与音频工具复核。
- 呼叫建立慢:看 INVITE→100/180/200 的时间,不把 180 的 PDD、接通 setup、首个媒体时延混为一个指标。检查 SIP 重传及最终错误码。
- 只一侧听不到:先确定抓包点应当可见双向媒体,再比对 SDP 的 c=/m=audio 与实际四元组;查 NAT、SBC 媒体锚点、direct media 和 ACL。只镜像 HEP 信令时,不会凭空得到完整 RTP 质量。
- TTS 卡顿/断续:分别看生成侧 PCM、送入交换机的媒体、交换机出侧 RTP、接收端反馈;Sipnab 可观察其中 SIP/RTP 段,不能直接证明 TTS 首包慢、应用排队或模型生成出错。
- 双向包正常但无声:导出对应方向的 WAV,查 RMS、连续静音段、样本数与音频时长;需要判断说话内容时再加 VAD/ASR。先确认 G.711 PCMU/PCMA、采样率和转码配置。
- 多中心/SBC 多腿:保存各抓包点原始证据。Call-ID 可能被 B2BUA 改写,扩展关联需依据可见头字段和业务标识核对;不能把同一 call-id 就等同于所有交换机内部 UUID。
实时抓包命令模板(本次未执行;按授权接口和实际地址改写):
# Linux 上两端 SIP 与媒体都经同一个可观察地址时的示例
# 文档地址 192.0.2.10 必须替换为真实且获授权的抓包对象
sudo sipnab --no-config -d eth0 -O call.pcap 'host 192.0.2.10 and udp'如果信令和媒体 IP 不同,应把已知的相关地址纳入 BPF;如果是 SIP/TCP 则不能只抓 UDP。macOS 先用 tcpdump -D 确认真实接口名。使用适当捕获窗口并保留退出时的丢包统计;抓取范围过窄会失去证据,过大又可能使本机抓包掉包。[S4]
TLS/SRTP:普通密文 PCAP 不会自动变成明文。官方提供 keylog、特定 RSA 交换、SDES/SRTP、DTLS-SRTP 等路径,要求对应构建能力和可用密钥;RSA 私钥路径不适用于 ECDHE/PFS。uprobes 属于 Linux 相关路径,本次没有实测。内容可见性应在抓包设计时确认,而不是分析失败后推定“没有信令/媒体”。[S11]
7. 如何完整复现
cd '/path/to/sipnab-lab'
python3 scripts/generate_fixtures.py
python3 scripts/run_cli.py
python3 scripts/run_mcp.py
python3 scripts/verify_results.py生成器和验证器只使用 Python 标准库。run_cli.py 中的 tshark 路径是本机 Wireshark.app 路径,其他系统需要按安装路径改写。Sipnab 二进制仅适用于本机的 macOS ARM64。已有 WAV 会保留;需要改变样本时应使用新的输出目录并同步生成新的导出回执。
检查包数/关联/序列完整性之外,还保存了 38 个初始 CLI 命令的返回码、stdout 与 stderr。进程均正常退出;“命令成功”与“诊断正确”分开评价。附加筛选和多核复核记录在 results/extra-checks.json。本任务不修补上游源码,也未向上游发送 issue。
实验限制:仅短呼叫、合成音、UDP/IPv4/PCMU;没有实网、长期压测、真实人声、主观音质、TCP 重传、复杂 NAT、SRTP、Opus、多个交换机或网络单向时延测量。独立核对证明的是当前文件中的包与载荷,不能证明生产接收端的播放体验。
8. 来源与证据台账
所有在线文档按 v0.5.159 固定,读取日期 2026-09-08。官方文档与源码来自同一项目,不能当成彼此独立的第三方背书;实战结论用本地原始 PCAP 和 Wireshark 交叉检查。
| ID | 来源 | 支持内容 |
|---|---|---|
| S1 | 官方 README | 产品、功能、构建特性和运行要求 |
| S2 | v0.5.159 Release、Cargo.toml | 发布日期、下载包、校验、MIT OR Apache-2.0 |
| S3 | 架构 | 输入、处理链、DialogStore/StreamStore、输出共享 |
| S4 | 安装、CLI | 安装、命令、默认阈值、退出码、输入过滤 |
| S5 | TUI walkthrough、按键 | TUI 操作路径 |
| S6 | 输出格式、DSL | 消息/呼叫 JSON、过滤方式 |
| S7 | stream.rs | 累计 gap、last_seq 更新、CN 跟踪 |
| S8 | analysis.rs、dsl.rs | 自动分析与 problems 筛选覆盖差异 |
| S9 | call_report.rs | Issues 依赖 diagnosis.hints |
| S10 | MOS 文档、quality.rs | 简化 E-model、codec 参数、延迟假设与适用边界 |
| S11 | TLS 捕获 | 解密条件;本次未实测 |
| S12 | MCP、工具说明 | stdio、查询、音频留存与导出 |
| L1 | verified-summary(本地文件)、命令记录(本地文件) | 六组实测、Sipnab 原始输出 |
| L2 | 生成器(本地文件)、独立验证器(本地文件)、乱序 tshark 输出(本地文件) | 已知输入、序列去重/缺失、独立解析 |
| L3 | MCP 工具 schema(本地文件)、results/*-mcp-*.json、results/audio/*.wav |
真正调用、真正导出、声道 RMS |
| L4 | TUI 终端记录(本地文件) | 实际交互与单流显示结果 |
工作区组织参考 [[projects/ai-learning]](知识日期 2026-08-30),只用于交付约定;Sipnab 的事实均以本次资料和实战为准。