feat(realtime): startBitrateKbps connect option — seed the publisher's initial bandwidth estimate - #193
feat(realtime): startBitrateKbps connect option — seed the publisher's initial bandwidth estimate#193ilay-decart wants to merge 2 commits into
Conversation
…s initial bandwidth estimate Browsers start the WebRTC send-side bandwidth estimate at ~300 kbps and ramp over several seconds, so realtime sessions begin at reduced input resolution even on links with ample capacity. The new opt-in startBitrateKbps connect option munges x-google-start-bitrate onto every video codec's fmtp (VP8/VP9/H264/H265/AV1; fmtp line added when a codec has none) on both the local offer and the remote answer, seeding the estimator so its startup probes (3x/6x of the start value) validate full-resolution bandwidth within the first probe round. Implementation: a scoped global RTCPeerConnection patch installed for the media channel's lifetime (LiveKit reconnects create fresh PCs that must be seeded too) and uninstalled on disconnect; never clobbers a foreign patch on restore; argless setLocalDescription passes through untouched; no-op without a global RTCPeerConnection or for 0/undefined. Default off = byte-identical behavior. 10 new unit tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
commit: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 2207a5f. Configure here.
| if (g.RTCPeerConnection === StartBitratePC) { | ||
| g.RTCPeerConnection = OriginalPC; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Overlapping patches leak global PC
Medium Severity
Overlapping installStartBitrateMunge calls nest subclasses on the global RTCPeerConnection. The older uninstaller no-ops because the global is no longer its class, and the newer one restores that older subclass instead of the true original, so after every session ends the global stays patched and later page WebRTC still gets SDP munged.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 2207a5f. Configure here.
Field finding (live Chrome sessions, 250ms stats traces): libwebrtc re-initializes the send-side estimate to x-google-start-bitrate on EVERY applied description carrying the param. Munging renegotiations - e.g. LiveKit's track-publish round ~0.5s after join - therefore RESETS an already-converged estimator back down to the seed (measured: est 5301 kbps -> 1106 kbps at the second negotiation, exactly the seed value), recreating the slow ramp the option exists to eliminate and, on h264-simulcast publishers, landing at the layer-activation moment. The patched PC now munges only its first offer/answer pair; later negotiations pass through untouched. Fresh peer connections (LiveKit reconnects) get their own first-pair seeding. Regression test pins all three behaviors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>


What
Adds an opt-in
startBitrateKbpsrealtime connect option that seeds the browser publisher's initial bandwidth estimate by mungingx-google-start-bitrateonto every video codec's fmtp lines (VP8/VP9/H264/H265/AV1 — an fmtp line is added when a codec has none, e.g. VP8) on both the local offer and the remote answer.Why
libwebrtc starts its send-side estimate at ~300 kbps and ramps over several seconds, so sessions begin at reduced camera resolution even on links with plenty of headroom — the input (and therefore the generated output) stays soft for most of a short session. Seeding moves the estimator's starting point, and its startup probes scale from it (3×/6×, then ×2 rounds —
probe_controller.cc), so full-resolution bandwidth is validated within the first probe round on capable links. Values above ~1100 kbps measurably degrade startup on very weak (<1 Mbps) uplinks in our test-rig sweeps, so roll out behind a flag/canary.How
realtime/start-bitrate.ts: puremungeStartBitrate(sdp, kbps)(idempotent; never touches audio/rtx/red payloads) +installStartBitrateMunge— a scoped globalRTCPeerConnectionpatch, since livekit-client owns its peer connections and exposes no SDP hook. Installed for the media channel's lifetime (LiveKit full reconnects create fresh PCs that must be seeded too), uninstalled ondisconnect(), and the uninstaller never clobbers a foreign patch. ArglesssetLocalDescription()passes through untouched; environments without a globalRTCPeerConnectionlog a warning and no-op.client.ts(zod option, 0–10 000, JSDoc) →stream-session.ts→media-channel.ts.Tests
10 new unit tests (
tests/start-bitrate.unit.test.ts): per-codec munge incl. VP8 fmtp insertion, audio/rtx/red untouched, idempotency, identity for 0/video-free SDPs, offer+answer munging via a fake PC, argless pass-through, log-once, uninstall/foreign-patch safety, missing-global no-op. Full suite: 304/304;tsc --noEmitand biome clean.Internal ref: API-1486.
🤖 Generated with Claude Code
Note
Medium Risk
Touches the core WebRTC publish path via a global RTCPeerConnection monkey-patch; mis-tuned values can hurt weak uplinks, though the feature is off by default.
Overview
Adds an opt-in
startBitrateKbpsrealtime connect option so publishers can raise libwebrtc’s initial send-side bandwidth estimate instead of the default ~300 kbps cold start, which should reduce time spent at reduced camera input resolution on capable uplinks.When set,
start-bitrate.tsinjectsx-google-start-bitrateinto video codec fmtp lines (and adds fmtp for codecs like VP8 that lack one) via a scoped globalRTCPeerConnectionpatch for the media channel lifetime—only the first offer/answer pair is munged so later LiveKit renegotiations do not reset a converged estimator; reconnects get a fresh seed. The option is validated inclient.ts(0–10 000) and passed throughstream-session→media-channel; omitting it leaves behavior unchanged.Unit tests cover SDP munging, renegotiation pass-through, uninstall/foreign-patch safety, and edge cases.
Reviewed by Cursor Bugbot for commit 6ff98b3. Bugbot is set up for automated code reviews on this repo. Configure here.