Skip to content

feat(realtime): startBitrateKbps connect option — seed the publisher's initial bandwidth estimate - #193

Open
ilay-decart wants to merge 2 commits into
mainfrom
api-1486-start-bitrate-seed-option
Open

feat(realtime): startBitrateKbps connect option — seed the publisher's initial bandwidth estimate#193
ilay-decart wants to merge 2 commits into
mainfrom
api-1486-start-bitrate-seed-option

Conversation

@ilay-decart

@ilay-decart ilay-decart commented Aug 2, 2026

Copy link
Copy Markdown

What

Adds an opt-in startBitrateKbps realtime connect option that seeds the browser publisher's initial bandwidth estimate by munging x-google-start-bitrate onto 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.

await client.realtime.connect(stream, { model, onRemoteStream, startBitrateKbps: 1100 });

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: pure mungeStartBitrate(sdp, kbps) (idempotent; never touches audio/rtx/red payloads) + installStartBitrateMunge — a scoped global RTCPeerConnection patch, 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 on disconnect(), and the uninstaller never clobbers a foreign patch. Argless setLocalDescription() passes through untouched; environments without a global RTCPeerConnection log a warning and no-op.
  • Plumbed client.ts (zod option, 0–10 000, JSDoc) → stream-session.tsmedia-channel.ts.
  • Default off ⇒ byte-identical behavior for existing users.

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 --noEmit and 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 startBitrateKbps realtime 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.ts injects x-google-start-bitrate into video codec fmtp lines (and adds fmtp for codecs like VP8 that lack one) via a scoped global RTCPeerConnection patch 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 in client.ts (0–10 000) and passed through stream-sessionmedia-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.

…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>
@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 6ff98b3

@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 2207a5f. Configure here.

if (g.RTCPeerConnection === StartBitratePC) {
g.RTCPeerConnection = OriginalPC;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

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>
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