Fix initial video quality blurriness for all codecs - #1987
Conversation
|
size-limit report 📦
|
| }); | ||
| } else if (track.codec && isVideoCodec(track.codec)) { | ||
| // Apply start bitrate for all video codecs to prevent initial blurriness. | ||
| // Sum all encoding bitrates for simulcast (BWE needs to handle all layers combined). |
There was a problem hiding this comment.
don't we need to differentiate between SVC and non SVC here? (i.e. keeping it with first encoding only for SVC and summing for the others?)
There was a problem hiding this comment.
Theoretically yes, technically no.
For modern SVC ((Chrome M113+), they are single encoding with scalability mode, so it is fine.
for legacy SVC (Safari/RN/older Chrome): they have multiple encodings describing SVC layers. Summing the value might go beyond the target max bitrate. But given
- 1 Mbps cap - We cap at 1 Mbps anyway, so overcounting is limited
- Higher is safer - Starting slightly high is better than too low (which causes blurriness)
- Legacy SVC is rare - Modern Chrome uses single encoding; mainly Safari/RN use legacy
I fixed the code anyway.
…-bitrate libwebrtc starts every new video send stream at roughly 300 kbps and ramps up from there regardless of the encodings' maxBitrate, so the first seconds of a published track are visibly blurry (measured: QP 39-42 for ~12 s, the 2.3 Mbps ceiling reached only after ~30 s on a healthy network). Match what client-sdk-js (livekit/client-sdk-js#1987) and rust-sdks (livekit/rust-sdks#1197, #1226) already do: when a video sender is added, derive a start bitrate from its encodings — the sum of the active layers' maxBitrate, times 0.9, capped at 1 Mbps unless the track is a screen share, and no hint under 300 kbps — and declare `x-google-start-bitrate=<kbps>` on every video codec's fmtp (VP8/VP9/AV1/H264/H265) of that sender's section in the publisher's offer, matched through the `a=msid` track id. Not opt-in and no public API, as in the sibling SDKs. - `SDPMediaSection.setFmtpParameter(_:value:forPayload:)`: replaces an existing value, appends to an existing fmtp line, or inserts one for payloads that have none (VP8), keeping every other parameter verbatim. - `Transport.startBitrateKbps(targetBps:isScreenShare:)` / `startBitrateKbps(for:isScreenShare:)`: the shared formula. - `Transport.mungeVideoStartBitrate(_:kbpsBySenderId:)`, appended as the last (optional) munge of `set(localDescription:munging:)` (livekit#1068) in both single- and dual-PC negotiation, on the SDP module from livekit#1078. - `Transport.addTransceiver(with:transceiverInit:startBitrateKbps:)` records the hint against the new sender before returning, so the offer libwebrtc requests in response cannot be created without it; `remove(track:)` clears it. Both `LocalParticipant` video publish paths (primary and backup codec) pass the derived value. Tests: the formula, the encodings sum, the per-sender munge (codec case, replace/append/insert, rtx/audio/unmapped sections untouched, no-op identity), `setFmtpParameter`, and — against the shipped libwebrtc — that a send-only video section's msid carries the sender id and that the munged offer is accepted by `setLocalDescription` rather than rejected as disallowed munging. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-bitrate libwebrtc starts every new video send stream at roughly 300 kbps and ramps up from there regardless of the encodings' maxBitrate, so the first seconds of a published track are visibly blurry (measured: QP 39-42 for ~12 s, the 2.3 Mbps ceiling reached only after ~30 s on a healthy network). Match what client-sdk-js (livekit/client-sdk-js#1987) and rust-sdks (livekit/rust-sdks#1197, #1226) already do: when a video sender is added, derive a start bitrate from its encodings — the sum of the active layers' maxBitrate, times 0.9, capped at 1 Mbps unless the track is a screen share, and no hint under 300 kbps — and declare `x-google-start-bitrate=<kbps>` on every video codec's fmtp (VP8/VP9/AV1/H264/H265) of that sender's section in the publisher's offer, matched through the `a=msid` track id. Not opt-in and no public API, as in the sibling SDKs. - `SDPMediaSection.setFmtpParameter(_:value:forPayload:)`: replaces an existing value, appends to an existing fmtp line, or inserts one for payloads that have none (VP8), keeping every other parameter verbatim. - `Transport.startBitrateKbps(targetBps:isScreenShare:)` / `startBitrateKbps(for:isScreenShare:)`: the shared formula. - `Transport.mungeVideoStartBitrate(_:kbpsBySenderId:)`, appended as the last (optional) munge of `set(localDescription:munging:)` (livekit#1068) in both single- and dual-PC negotiation, on the SDP module from livekit#1078. - `Transport.addTransceiver(with:transceiverInit:startBitrateKbps:)` records the hint against the new sender before returning, so the offer libwebrtc requests in response cannot be created without it; `remove(track:)` clears it. Both `LocalParticipant` video publish paths (primary and backup codec) pass the derived value. - The backup-codec publish path now computes its encodings with `isScreenShare`, as the primary path does; it had defaulted to the camera encoding since livekit#275, which the screen-share branch of the new formula made visible. Tests: the formula, the encodings sum, the per-sender munge (codec case, replace/append/insert, rtx/audio/unmapped sections untouched, no-op identity), `setFmtpParameter`, the backup codec's screen-share encodings, and — against the shipped libwebrtc — that a send-only video section's msid carries the sender id and that the munged offer is accepted by `setLocalDescription` rather than rejected as disallowed munging. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Improve initial video quality by:
Changes
PCTransport.ts
publishUtils.ts
options.ts
Behavior
│ Setting │ Before │ After │
│ x-google-start-bitrate │ 90% of target (unbounded) │ min(90% of target, 1 Mbps) │
│ Camera degradationPreference │ maintain-resolution │ maintain-framerate │
│ Screen share degradationPreference │ maintain-resolution │ maintain-resolution │
│ Other degradationPreference │ maintain-resolution │ balanced │
Related
Aligns with rust-sdks PRs:
See Rust PR: livekit/rust-sdks#1197