Skip to content

feat(realtime): glass-to-glass latency via LiveKit frame metadata#185

Merged
AdirAmsalem merged 5 commits into
mainfrom
livekit-frame-metadata-latency
Jul 16, 2026
Merged

feat(realtime): glass-to-glass latency via LiveKit frame metadata#185
AdirAmsalem merged 5 commits into
mainfrom
livekit-frame-metadata-latency

Conversation

@AdirAmsalem

@AdirAmsalem AdirAmsalem commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What & why

Network RTT hides the dominant cost in real-time video — model inference — so a session can report "good" while actually feeling laggy. This lets apps measure the real camera→display (glass-to-glass) latency their users actually experience, so they can surface honest connection quality and diagnose lag. It stays opt-in and diagnostic (browser-only), and replaces an earlier approach that painted a visible marker onto the video — the new measurement is invisible to end users. Startup (ttffMs) and steady-state (g2gMs) latency are surfaced on the existing connectionQuality and stats signals and drive the latency verdict instead of RTT when available.

Usage

const rt = await client.realtime.connect(stream, {
  debugQuality: true, // opt-in, diagnostic, browser-only
  onConnectionQuality: (report) => {
    console.log(report.metrics.g2gMs, report.metrics.ttffMs);
  },
});

Experimental and browser-only; React Native rejects debugQuality. g2gDropRatio remains null for now.


Note

Medium Risk
Touches LiveKit publish/subscribe setup and server capability negotiation on an experimental encoded-transform path; mis-gating frame timing could break video decode, though the change is opt-in via debugQuality and includes worker-availability guards.

Overview
Replaces the opt-in pixel-marker glass-to-glass path (visible bottom-left stamp, outgoing frame transform, pixel_latency query param) with LiveKit frame metadata: capture timestamps on publish, server propagation, and matching on remote playout via timeSyncUpdate / lookupFrameMetadata. debugQuality: true still surfaces ttffMs / g2gMs on stats and connectionQuality, but measurement is invisible and no longer wraps the outgoing stream.

When a frame-metadata worker can be created, the SDK advertises client_capabilities.frame_timing on join (instead of pixel_latency), wires the LiveKit frameMetadata worker and timestamp: true publish options, and only then enables diagnostics. If the worker fails (e.g. CSP), frame timing stays off so subscribers are not left with undecodable VP8/VP9 trailers.

g2gDropRatio is documented and implemented as null until frame IDs propagate end-to-end; seq-based drop inference is removed with the marker code. Docs and the demo page call out using stats (or polling metrics) for live g2g numbers because connectionQuality is debounced.

Reviewed by Cursor Bugbot for commit e246dd4. Bugbot is set up for automated code reviews on this repo. Configure here.

…data

Replace the visible pixel-marker glass-to-glass measurement with LiveKit
frame metadata (per-frame capture timestamps propagated through inference),
surfaced as g2gMs / ttffMs on the stats and connectionQuality signals under
the opt-in debugQuality flag. Browser-only; React Native rejects debugQuality.
@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@decartai/sdk@185

commit: e246dd4

Comment thread packages/sdk/src/realtime/media-channel.ts
TimeSyncUpdate emits `timestamp` as a DOMHighResTimeStamp relative to
performance.timeOrigin, but it was compared against the publisher's epoch
userTimestamp — making every latency negative, so all samples were dropped
and g2gMs/ttffMs stayed null whenever debugQuality was on. Add timeOrigin to
reach epoch ms. Fix the reader test, which masked this by feeding an already-
epoch timestamp; it now drives a relative timestamp like the real SDK.
Comment thread packages/sdk/src/realtime/browser/prepare-connection.ts Outdated
The server appends a packet trailer to every frame once frame_timing is
advertised; a subscriber without the frame-metadata worker can't strip it and
the VP8/VP9 decoder then fails on every frame. frameTiming was derived from
debugQuality alone and sent in the join before the worker was created, so a
worker that failed to construct left the server stamping trailers into a
workerless room (broken video, not just missing metrics). Create the worker up
front and gate frameTiming — and the diagnostics reader — on its success.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bb8152a. Configure here.

Comment thread packages/sdk/src/realtime/media-channel.ts
onConnectionQuality / the connectionQuality event are debounced to verdict
changes, so reading g2g/rtt through them goes stale between changes (the event
looked like the latency API but wasn't). Add a connectionMetrics event +
onConnectionMetrics callback that push the live ConnectionQualityMetrics every
stats tick (~1/s); keep connectionQuality for the coarse verdict and document
getConnectionQuality()?.metrics as the pull path.
…ctionMetrics

Revert the dedicated connectionMetrics event/callback (92b21cb): the continuous
g2g data is already on the `stats` event (`stats.glassToGlass`, every tick), so
a parallel channel added surface without new capability. Instead document the
`stats` event + `getConnectionQuality()` polling as the live path, and clarify
that `onConnectionQuality` / the `connectionQuality` event are debounced to
verdict changes (which is what made g2g look stale).
@AdirAmsalem
AdirAmsalem merged commit b447c4c into main Jul 16, 2026
5 checks passed
@AdirAmsalem
AdirAmsalem deleted the livekit-frame-metadata-latency branch July 16, 2026 18:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant