Repository navigation
MCP Tools Media
The media bundle answers where a call's media endpoints came from, why a
stream has no dialog, and what lies under a quality score, and it exports a
call's audio.
Load only these tools with --mcp-tools media, or combine bundles as
Choosing which tools load describes.
Every tool on this page follows the rules every tool
follows, and each section states only
what its tool does differently. The MCP tool reference lists
the other bundles and finds any tool by name.
| Tool | Parameters | Returns |
|---|---|---|
reconcile_orphans |
limit?, cursor?, max_response_bytes?
|
Why each RTP stream with no dialog lacks one: a relay named the endpoint but no signaling arrived, SDP named it but no dialog claims it, or nothing named it at all |
media_diagnostics |
call_id |
The facts under the MOS: QoS marking, jitter grounding, delay provenance, silence, and what the far end reported |
explain_attribution |
call_id |
Where each of a call's media endpoints came from and how much that path is worth — asked directly, HMAC-verified, plain secret, port-gated only, or the parties' own SDP |
export_audio |
call_id, filename
|
Writes a call's RTP audio to a WAV in --mcp-file-root; needs the server started with --retain-audio
|
Start here when you have a capture and no particular suspect. These answer "what is in it", "how much", and "is the server even seeing traffic".
An orphaned stream is media with no dialog. rtp_stats names which streams
have none. This tool says why each one has none, which is the part an incident
needs.
| Name | Type | Legal values | If omitted |
|---|---|---|---|
limit? |
u32? | Ceiling is --mcp-max-rows (1000 by default), and higher clamps to it. 0 means the default. |
50 rows. |
cursor? |
string? | The previous response's next_cursor, verbatim (<RFC 3339>|0xSSRC@src>dst, the shape the rtp_stats sweep uses). A malformed timestamp half fails with invalid_params. |
Starts at the earliest orphan. |
max_response_bytes? |
u64? | Bytes for the whole response. Ceiling is --mcp-max-response-bytes (1048576 unless the operator changed it). Higher clamps to it, 0 means the ceiling. Past it the page ends early at a whole row with truncated_by: "bytes". |
The ceiling. |
Returns orphans, returned, total_orphans (every orphan in the store,
whatever limit and cursor say), truncated, truncated_by (present only
when a bound cut the page), next_cursor (null on the final page),
relay_was_consulted, relay_control, capture_identity and
schema_version. Rows run oldest first, by first packet, ties broken by
0xSSRC@src>dst. Each orphans row carries ssrc, src, dst,
named_endpoint, asserted_by, reason and note.
relay_was_consulted reads every orphan in the store, not only the page, so a
relay assertion on a later page still counts.
relay_control has one row per relay control socket this capture read
(--rtpproxy-control), and is empty when the capture read none. Read it when
the proxy is intermittently slow to answer SIP: retried commands mean the
proxy did not hear the relay's answer in time.
| Field | Meaning |
|---|---|
relay |
The control socket, address:port
|
implementation |
rtpproxy |
commands |
Command datagrams seen, retries included |
retried_commands |
Commands that repeated a cookie already seen. rtpproxy answers a repeated cookie from its reply cache, so the same cookie twice is the proxy sending the command again because it did not hear an answer in time |
retried_after_answer |
Of those, retries sent after the relay's answer was already on the wire: the answer got lost, or arrived late, on its way back to the proxy. The rest went out before the capture showed any answer |
sipnab remembers the 4,096 most recent cookies per socket, so a retry of an older one does not count. A headless run prints the same counts as a warning at the end when the proxy retried any command.
Each orphan carries one of three verdicts in reason, and each points at a
different missing piece:
| verdict | what it means |
|---|---|
relay-asserted-but-no-dialog |
a relay named this endpoint and no captured dialog claims it — the signaling is missing, not the media |
signaled-but-no-dialog |
SDP named it and no dialog holds it — either the capture missed the dialog, or its SDP arrived before capture started |
never-named |
nothing in this capture named this endpoint at all |
Read never-named together with relay_was_consulted. With
relay_was_consulted: false, sipnab asked no relay about the endpoint, so the
verdict is an absence of evidence, not evidence that no relay allocated it. To
get a relay's answer, query the relay itself (see the relay
bundle).
The example runs against tests/pcap-samples/codec-negotiation.pcap.
Its SIP is on port 5080, outside the default --portrange 5060-5061, so with
default settings the capture yields four streams and no dialogs:
You have a call-id. These explain what happened to that one call -- its signaling, its media, and the specific negotiations that tend to break.
"The MOS is 3.6 — why?" rtp_stats gives the score. This gives the facts
underneath it, and each one says what kind of number it is.
| Name | Type | Legal values | If omitted |
|---|---|---|---|
call_id |
string | A Call-ID a dialog carries, or one a media relay named for its streams. | Required — the call fails. |
Read applicable first. It is false when no RTP stream belongs to the
dialog, and the response then holds only call_id, reason,
capture_identity and schema_version. applicable: false means no media
for this call reached the capture point. It does not mean the media was healthy.
dialog_seen says whether the capture holds the call's SIP dialog. On a
media-relay host it is false and dialog_absent says why, as described
under rtp_stats.
Otherwise streams carries one entry per stream, each with five blocks:
| Block | Answers | The honesty flag |
|---|---|---|
qos |
Which queue the sender asked the network to put this media in |
marking_observed — false for a HEP-fed stream, where sipnab saw no IP header. dscp: 0 means observed best effort, a real and frequently wrong marking |
jitter |
The interarrival jitter, and whether it is a measurement |
grounded — false when the stream supplied no clock rate and sipnab fell back to a default. Jitter is an RTP-timestamp difference divided by that rate, so a wrong divisor gives a different quantity, not a rough one. An ungrounded stream reports no measured_ms at all |
delay |
The one-way delay behind the published MOS |
assumed — true when neither the operator nor any RTCP supplied one |
silence |
Comfort-noise frames and detected silence periods | -- (counts, not estimates) |
endpoint_reported |
What the far end said over RTCP | The whole block is the flag. Omitted entirely when no RTCP arrived |
Read endpoint_reported separately from the other blocks. RTCP is not
authenticated, and a report describes the path from the sender to the
reporter. On a mid-path capture that is a different segment from the one
sipnab watches, so the far end's figures and sipnab's can differ on a healthy
call. Nothing under this key feeds the MOS.
qos.remarked_to appears only when the stream's last packet carries a different
code point from its first, which means a device on the path (an SBC or a policy
boundary) rewrote the marking. A stream whose marking did not change omits it.
The example runs against
tests/pcap-samples/sip-rtp-g711.pcap:
// media_diagnostics { "call_id": "1-1966@10.0.2.20" }
{
"schema_version": 1,
"call_id": "1-1966@10.0.2.20",
"applicable": true,
"streams": [
{
"ssrc": "0x343da99b",
"src": "10.0.2.15:27942",
"dst": "10.0.2.20:6000",
"codec": "PCMU",
"packets": 425,
"qos": {
"marking_observed": true,
"dscp": 0,
"name": "CS0 / default (best effort)",
"expedited": false
},
"jitter": {
"grounded": true,
"clock_basis": "rfc3551",
"clock_rate_hz": 8000,
"measured_ms": 0.0054046519599899685
},
"delay": { "source": "assumed", "assumed": true, "one_way_ms": 100.0 },
"silence": { "cn_frames": 0, "periods": 0, "total_ms": 0 }
}
],
"capture_identity": {
"instance": "11c00618df002918a6c463-2",
"node": "sbc-1",
"dialog_generation": 13,
"stream_generation": 2
},
"dialog_seen": true,
"source_exhausted": true,
"source_stopped_early": false
}To read that answer:
-
qos.dscp: 0means the sender marked the media best effort, so the network queues it with bulk traffic. -
jitter.grounded: truemeans the jitter figure is a measurement: payload type 0 has the clock rate that RFC 3551 Table 4 fixes. -
delay.assumed: truemeans the delay behind the MOS is the default 100 ms, not a value this capture showed, because the capture carries no RTCP that reports a round trip.
clock_basis has three values: rfc3551 (a static payload type), rtpmap (a
dynamic one an SDP named), and assumed (neither, and the reason grounded is
false).
Every finding a tool reports points back at captured bytes. These retrieve those bytes, prove which frame they came from, and package them so someone else can check the work.
Parameters: call_id (string, required) — a Call-ID, as list_dialogs
returns it.
An agent that reports "media anchored at <addr>" cannot otherwise say whether
that came from SDP the two parties exchanged, from a relay sipnab asked, or
from a mirrored datagram anybody could have sent. Those carry very different
weight in an incident review and every other surface renders them identically.
delivery_trust, strongest first:
| value | meaning |
|---|---|
asked |
sipnab asked the relay over its control socket; no third party could answer |
hmac-verified |
delivered over HEP, authenticated with a token covering the datagram |
plain-secret |
delivered over HEP with a shared secret; the token does not cover the datagram, so anyone who captures one can replay it |
port-gated-only |
accepted because it arrived on the expected port and for no other reason — the source is not authenticated |
not-relay-asserted |
the parties' own claim in SDP |
The authentication answer comes from the run's HEP authentication configuration, not from a per-packet record. Ingest drops any datagram that fails authentication, so every relay datagram in the store arrived under that configuration. With no authentication configured, the tool reports the weakest reading.
The configured posture is the HEP listener's, so it covers only what the
listener delivered (input_origin: hep). A relay datagram sipnab read off a
capture interface (input_origin: wire) never reached the listener: it is
port-gated-only on every run, --hep-auth-mode hmac included. A run with
-d and -L holds both kinds side by side.
--hep-parse does not change this. A HEP datagram it unwraps off a capture
interface or a file reports input_origin: hep, because the HEP sender wrote
the addresses inside it, but it reached no listener and nothing checked its
secret. It is port-gated-only too.
The endpoints are the call's streams, so the tool answers on a media-relay host
that never saw the SIP dialog. dialog_seen is then false and
dialog_absent says why, as described under rtp_stats.
The example runs against
tests/pcap-samples/sip-rtp-g711.pcap:
// explain_attribution { "call_id": "1-1966@10.0.2.20" }
{
"call_id": "1-1966@10.0.2.20",
"endpoints": [
{
"address": "10.0.2.15",
"port": 27942,
"asserted_by": "signaled",
"input_origin": "wire",
"observed_at": "2016-11-26T14:52:59.670743+00:00",
"delivery_trust": "not-relay-asserted",
"delivery_note": "the parties' own claim in SDP, not a relay's statement about its allocation"
},
…
],
"unauthenticated_endpoints": 0,
"dialog_seen": true,
"schema_version": 1,
…
}Getting the result out of sipnab and into the next tool, the next team, or a bug report someone else can reproduce.
Writes one call's RTP audio to a WAV in the configured root. Fails when the call carries no audio it can decode, rather than writing an empty file.
Requires --retain-audio on the server command line. Without it sipnab
holds no audio payload in memory. The run keeps whole calls, inside
the [limits] max_audio_bytes budget
(Audio retention budget). A
file missing the start of a call says PARTIAL in its summary with the count
and the reason. Without the flag the tool refuses,
and its refusal reports the media it measured and names the flag — a capture
setting, not a finding that the call was silent.
| Name | Type | Legal values | If omitted |
|---|---|---|---|
call_id |
string | A Call-ID whose streams carry audio sipnab can decode. The capture need not hold the dialog: a media-relay host exports the streams the relay named. | Required — the call fails. |
filename |
string | A bare filename inside --mcp-file-root, under the same rule export_capture applies. sipnab writes a WAV whatever extension you give it. |
Required — the call fails. |
Returns path, summary, dialog_seen and schema_version, plus
dialog_absent when dialog_seen is false (see rtp_stats):
The example runs against tests/pcap-samples/sip-rtp-g711.pcap, whose capture
holds only one direction of the call's media:
sipnab --mcp -N -I tests/pcap-samples/sip-rtp-g711.pcap --retain-audio --mcp-file-root /var/spool/sipnab-exports// export_audio { "call_id": "1-1966@10.0.2.20", "filename": "call.wav" }
{
"schema_version": 1,
"path": "/var/spool/sipnab-exports/call.wav",
"summary": "Exported 8.5s of mu-law audio (425 frames, PCMU/8000Hz) to /var/spool/sipnab-exports/call.wav — PARTIAL: media in only ONE direction reached this capture; no stream going the other way was observed. That may be a one-way call, or a capture point that only sees one leg -- this file cannot tell you which.",
"dialog_seen": true,
"source_exhausted": true,
"source_stopped_early": false
}sipnab never writes over an existing file. A filename that is already taken
fails with invalid_params (-32602) and data.reason: "already_exists".
Choose another name.
Without --retain-audio the refusal arrives as internal_error (-32603). It
reports what sipnab measured, so it does not read as a silent call, and names
the remedy:
writing /var/spool/sipnab-exports/call2.wav: No audio payload retained:
sipnab measured 425 RTP packet(s) of PCMU on 1 decodable stream, but kept none
of their payload, so there is nothing to decode. This is a statement about
what this run kept, not a finding that the call was silent. Most often audio
payload retention was off — start the server with --retain-audio to hold
payload for export. With retention already on, the other causes are a
--snaplen that truncated the payload away before sipnab saw it, or packets
that carried no payload at all.
Website · Repository · Issues · Generated from docs/ — edit there, not here.
Getting started
- Installation
- First CLI Triage
- Glossary
- Cookbook
- Runnable Examples
- Real World Captures
- Troubleshooting
- Tuning Capture
- Encapsulations
- Rtpengine Relays
- Backers
Using the TUI
CLI & automation
- CLI Reference
- Error Messages
- Filter DSL
- Output Formats
- SIP Response Codes
- SIP Methods
- SIP Header Fields
- SIP Parameters
- SIP Lint Rules
- MOS and Codecs
Configuration
Integrations (API & MCP)
- REST API
- Prometheus Metrics
- Authentication
- MCP
- MCP Deployment
- MCP Across an Estate
- MCP Tools
- MCP Tools Core
- MCP Tools Signaling
- MCP Tools Captures
- MCP Tools Security
- MCP Tools Media
- MCP Tools Relay
- MCP Tools TFPS
- MCP Tools Server
- MCP Tools vCon
- MCP Tools TLS
- MCP Protocol
- Uprobe Walkthrough
- Capture SIP over TLS
- WASM Plugins
- vCon Export
- vCon Capture Stack
- G729 Decoding
Your SIP server
- Use OpenSIPS as the SIP Server
- Run sipnab beside OpenSIPS
- Use Kamailio as the SIP Server
- Run sipnab beside Kamailio
- Use Asterisk as the PBX
- Run sipnab beside Asterisk
- Use FreeSWITCH in the Voice Stack
- Run sipnab beside FreeSWITCH
Add to your voice stack
- Add a vCon Server
- Send vCons to a vCon Server
- Send vCons to vcon store
- Add TFPS
- Control TFPS from sipnab
- Ban SIP Scanners with fail2ban
- Feed fail2ban from sipnab
- Add rtpengine
- Name rtpengine Media with sipnab
- Add rtpproxy
- Name rtpproxy Media with sipnab
- Add Homer
- Connect sipnab to Homer
- Add Prometheus and Grafana
- Add sipnab Metrics to Prometheus
Development & internals
- Internals Index
- Internals Subsystem Guide
- Internals Invariants
- Internals Testing
- Internals Walkthroughs
- Internals Build CI Release
- Internals Profiling
- Internals Domain Primer
- Library API
- Benchmarks
- Fault Model
- Threat Model
- Assurance Case
- Architecture
- Internals Threading
- Internals TUI Testing
- Internals Zero Copy Payloads
- Internals Uprobe Capture
- Internals Rtpengine Control Plane
- Internals Relay Control Decoding
- Internals vCon Exporter
- Internals Operator Guide Notes
- Internals Postmortems
- Internals Code Map
- Internals Lint Rules
- Internals Encapsulation Decoding
- Internals MCP Design Notes
- Internals Metrics And Output Notes