Goryu serves a JSON snapshot of every active or recently active stream on
127.0.0.1:<status_port>. The port is loopback only and is not reachable from
other hosts. Any request returns the snapshot:
curl http://127.0.0.1:8081/status
The status thread never calls libsrt. Session threads sample srt_bstats() and
srt_group_data() at most once per second and write the results into shared
state; the status thread only copies that state (see
Architecture).
The response is compact (no pretty-printing). Pipe it through jq or
python3 -m json.tool to read it by eye.
Top-level fields:
pid,startedAtMs,updatedAtMs: process identity and snapshot timinglastError: the most recent process-level error, if anyactiveStreamIds: streamids with a currently connected inputstreamStates: one entry per known stream (see below); entries can briefly outliveactiveStreamIdswhile a session is tearing down
Each streamStates[] entry:
streamId,inputActive,outputConnected,retryFailuresforwardedPackets,forwardedBytes,lastPacketAt,lastInputPacketAt: Goryu-tracked forwarding progressinput: everything SRT reports about the (possibly bonded) input — see belowoutput: everything SRT reports about the single downstream connection (output is never bonded) — see belowlastErrorAt,lastError: most recent per-stream error, if any
input fields:
bonded:truewhen the encoder connected as a bonded socket group,falsefor an ordinary single-link SRT connection. Decided once when the session is accepted. A bonded group with only one leg connected is stillbonded: true.peer:{ "ip", "port" }, the encoder's address as seen by Goryu, for a plain input;nullfor a bonded input, where each leg carries its own address inlegs[](the connection that first opened the group may no longer be a member later, so no single peer is reported). A plain input has an emptylegsarray, sopeeris where its address shows up.recvPacketsTotal,recvUniquePacketsTotal, andrecvDropTotal: group-level counters from SRT's bonded group socket.recvPacketsTotal,recvLossTotal, andretransTotalcan benull: SRT does not provide a deduplicated group-level loss or receive-retransmission counter, and it does not always populaterecvPacketsTotalfor a group.recvUniquePacketsTotalis the reliable deduplicated total for group-level throughput. Use the per-leg values below for loss and retransmission telemetry; Goryu does not sum them because that would double-count packets recovered on another leg.rttMs: RTT reported for the input group socket as a wholelatencyMs: negotiated SRT buffering latency for the input, i.e. the actual value SRT settled on after the handshake, not just what was requested. For a non-bonded input this comes straight fromsrt_bstats(msRcvTsbPdDelay, the same underlying valueSRTO_LATENCYreports). A bonded group socket's ownsrt_bstatsnever fills that field in, so for a real bonded input this is derived as the maxlatencyMsacrosslegs[]instead (see below) — no extra libsrt call needed either way.bandwidthMbps,recvRateMbps,belatedTotal,belatedAvgMs,undecryptTotal,reorderDistance,rcvBufMs: estimated capacity, actual receive rate, packets dropped for arriving past their delivery deadline (count + average lateness), decryption failures, reordering, and receive-buffer occupancy (ms) — all read straight from the samesrt_bstatscall as the fields above. Only populated for a non-bonded input (nullotherwise): likelatencyMs, a bonded group's ownsrt_bstatsnever fills these in, and there was no reasonable per-leg aggregation for most of them (unlike latency, where "max across legs" has a clear meaning) — checklegs[]for the real per-leg numbers on a bonded stream instead.legs: one entry per individual bonded connection currently in (or recently in) the input group, read directly fromsrt_group_data()+srt_bstats()on each member socket — this is real per-leg telemetry from libsrt, not something Goryu derives or combines:ip,port: the leg's peer (encoder-side) address, as seen by Goryustate:pending|idle|running|brokenrttMs,recvPacketsTotal,recvUniquePacketsTotal,recvLossTotal,recvDropTotal,retransTotal: stats for that leg onlylatencyMs: the negotiated latency for that individual leg's own socket (msRcvTsbPdDelayfrom the member's ownsrt_bstats, not the group), so if an encoder ever negotiates a different value per leg it's visible herebandwidthMbps,recvRateMbps,belatedTotal,belatedAvgMs,undecryptTotal,reorderDistance,rcvBufMs: the same set of extra stats asinput.*above, but for that leg's own socket specifically — reliably populated per leg even though the group-levelinput.*versions are not
output fields:
sentPacketsTotal,sendLossTotal,sendDropTotal,retransTotal: send-side counters for the downstream connectionrttMs: RTT for the downstream connectionlatencyMs: negotiated latency on the downstreamsrt://connection Goryu makes to the output (fromsrt_bstats'msRcvTsbPdDelay, same value asinput.latencyMs); Goryu's defaultbuild_srt_uri()requestslatency=200here vs.latency=240on the bonded input listenerbandwidthMbps,sendRateMbps,undecryptTotal,sndBufMs: capacity, actual send rate, decryption failures, and send-buffer occupancy (ms) — the output socket is never a group, so these are populated after the first successful output stats sample while connected
Example response for a stream bonded over two legs (reformatted here for readability — the actual response is one compact line):
{
"pid": 12345,
"startedAtMs": 1783569708552,
"updatedAtMs": 1783569763437,
"lastError": null,
"activeStreamIds": ["camera1/main"],
"streamStates": [
{
"streamId": "camera1/main",
"inputActive": true,
"outputConnected": true,
"retryFailures": 0,
"forwardedPackets": 28,
"forwardedBytes": 36848,
"lastPacketAt": 1783569742530,
"lastInputPacketAt": 1783569742529,
"input": {
"bonded": true,
"peer": null,
"recvPacketsTotal": null,
"recvUniquePacketsTotal": 22,
"recvLossTotal": 0,
"recvDropTotal": 0,
"retransTotal": 0,
"rttMs": null,
"latencyMs": 240,
"bandwidthMbps": null,
"recvRateMbps": null,
"belatedTotal": null,
"belatedAvgMs": null,
"undecryptTotal": null,
"reorderDistance": null,
"rcvBufMs": null,
"legs": [
{
"ip": "10.0.1.20",
"port": 51072,
"state": "running",
"rttMs": 24.6,
"latencyMs": 240,
"recvPacketsTotal": 26,
"recvUniquePacketsTotal": 26,
"recvLossTotal": 0,
"recvDropTotal": 0,
"retransTotal": 0,
"bandwidthMbps": 12.3,
"recvRateMbps": 0.47,
"belatedTotal": 0,
"belatedAvgMs": 0.0,
"undecryptTotal": 0,
"reorderDistance": 0,
"rcvBufMs": 221
},
{
"ip": "10.0.2.31",
"port": 39356,
"state": "running",
"rttMs": 31.2,
"latencyMs": 240,
"recvPacketsTotal": 25,
"recvUniquePacketsTotal": 25,
"recvLossTotal": 1,
"recvDropTotal": 0,
"retransTotal": 0,
"bandwidthMbps": 9.8,
"recvRateMbps": 0.46,
"belatedTotal": 2,
"belatedAvgMs": 6.4,
"undecryptTotal": 0,
"reorderDistance": 1,
"rcvBufMs": 235
}
]
},
"output": {
"sentPacketsTotal": 21,
"sendLossTotal": 0,
"sendDropTotal": 0,
"retransTotal": 0,
"rttMs": 0.043,
"latencyMs": 200,
"bandwidthMbps": 11.8,
"sendRateMbps": 0.42,
"undecryptTotal": 0,
"sndBufMs": 1
},
"lastErrorAt": 0,
"lastError": null
}
]
}