Monitor in-call video quality
Video quality on Orbit is a loop with three stages, each with its own endpoint surface:- Before the call — the preflight check verifies the joiner’s devices and network path before they ever enter the room.
- During the call — each participant’s SDK reports its own connection stats, and you read a live per-room snapshot to see who is having a bad call right now.
- After the call — the QoE report aggregates the session’s samples into p50/p95/p99 latency, jitter, and packet-loss, per-participant rollups, and drop-offs, retained for 24 hours after the last sample.
Where each stage fits
All routes are under
https://api.orbit.devotel.io/api/v1. Room names are
scoped to your tenant automatically — a caller can never read another
tenant’s room snapshot.
Step 1 — Gate the join with a preflight check
Fetch the preflight configuration and run it on the joiner’s device before you let them into the room:- ice_servers — STUN entries plus short-lived TURN and TURNS (TLS) credentials, so the check validates the exact relay path the real call will use, including restrictive-NAT cases where only TURN succeeds. Credentials expire after 60 minutes; fetch preflight close to join time rather than caching it.
- signaling_url — the media signaling endpoint to probe.
- checks — the pass/fail thresholds for each check: a 150 kbps send and receive floor and a 400 ms RTT ceiling for a camera-on join, over a 10-second network test.
SERVICE_UNAVAILABLE, the video service is not
configured on the cluster — surface that as a configuration problem, not a
failed joiner.
Step 2 — Report stats during the call
The media SFU does not expose per-participant transport stats — those live only in each participant’s browser. So the integration model is: every participant’s SDK rolls up its ownRTCPeerConnection.getStats() sample and
POSTs it on a cadence (every 5–10 seconds is the usual interval).
The response carries an
advisory block: when a sample shows congestion,
the advisory names the concrete encoding the publisher should fall back to —
suggested bitrate, recommended resolution, framerate, and degradation
preference — so a constrained or mobile participant degrades
deterministically instead of freezing. Apply the advisory to the sender’s
encoding parameters immediately; pass content_hint: "screen_share" when
the stats are for a screen-share track so the screen ladder (sharpness over
framerate) is picked instead of the camera ladder.
The reporting call needs a
video:write-scoped credential — the same scope
your SDK already holds to join a room. Reads (the snapshot and report below)
accept either video:read or video:write.Roll up getStats() in the browser
The browser-side shape is a short rollup over onegetStats() pass. Pick
the outbound-rtp entry for the video sender and the candidate-pair entry for
the transport:
quality_score from your own thresholds (jitter, loss, and RTT
against the preflight bounds are a workable formula) and clamp it to the
0–5 range. The server clamps every metric to a sane range anyway, so a
buggy SDK build cannot poison the monitor with NaN or runaway values.
Step 3 — Watch the live snapshot during a call
GET /video/connection-quality/:name is the “who is having a bad call
right now” view — one row per still-reporting participant:
participants list means the room is idle or nobody has reported recently,
not that everyone is healthy.
quality_label buckets the score into excellent (4–5), good (2–3.99),
poor (above 0, below 2), and lost (0). Poll this endpoint from your
operations dashboard every 10–30 seconds while a supervised call is live.
For a single participant, GET /video/connection-quality/:name/participants/:identity/stats returns that
one row — useful for an SLA probe against a specific caller. It returns 404
when the participant is not currently reporting (never joined, left, or
their sample aged out past the live TTL).
Step 4 — Read the post-session QoE report
After the call,GET /video/connection-quality/:name/report aggregates the
session’s retained samples into the after-call view:
- Room-level percentiles —
rtt_ms,jitter_ms,packet_loss_pct, andquality_scoreeach carryp50/p95/p99over every sample in the session. The p95 and p99 tails are where “the call felt bad for a few minutes” shows up after the p50 looks fine. - Per-participant rollups — each participant gets the same percentile
block plus a
quality_score_min, aworst_quality_label, and adropped_offflag.dropped_off: truemeans the participant stopped reporting well before the session’s last sample — they left abruptly or lost connectivity;drop_off_countsums those across the room. - Retention — samples are retained for 24 hours after the last sample
of the session, then the report expires. Pull and archive any report you
need as SLA evidence within that window. With no retained samples you get
an empty-but-valid report (
sample_count: 0), not an error.
Step 5 — Wire the operations playbook
Quality telemetry earns its keep when it drives an action. The landed patterns:- Subscribe to the quality-drop webhook. When a participant’s sample
crosses into
poororlostfrom a healthier bucket, Orbit fires avideo.participant.qosevent to your tenant’s webhook endpoints. It is edge-triggered — one event per transition, not one per heartbeat — so it is safe to page on. The payload carries the room, identity, both quality labels, the raw metrics, and the same encoding advisory the reporting SDK received. - Alert on the live snapshot. Poll
GET /video/connection-quality/:nameduring supervised sessions and raise an alert when any participant sits atpoororlostfor two consecutive polls — a single transient dip is normal; a sustained one is a supervisor page. - Act, don’t just watch. The
advisoryblock on both the report response and the snapshot row names the fallback encoding. For a managed experience (telehealth, support), apply it to the participant’s sender automatically; for a meeting, surface “Bob’s connection is unstable — try audio-only” to the host. - Correlate against cost and usage. A chronically degraded room is also a billable-usage question — pair the QoE report with the room-usage analytics in Read the cost-intelligence dashboards when a tenant disputes video spend on quality grounds.
- Never block the call on monitoring. The whole surface is best-effort:
when the monitoring store is unavailable, POST and GET return 503
SERVICE_UNAVAILABLE— never 500, and never a reason to fail a join or hang up a call. Treat a 503 from these routes as “skip monitoring this interval and keep the call,” and alert on the 503 itself only if it persists.
Where to go next
- Video meetings and conferences — create and run the rooms these routes monitor.
- Room access tokens — the credentials a participant’s SDK holds to join and report.
- Read the cost-intelligence dashboards — room minutes, usage, and the margin side of video.
- Video API reference — the full video endpoint surface.