Skip to content

Default video degradation preference by track source, including the backup codec - #1155

Merged
hiroshihorie merged 4 commits into
mainfrom
sxian/default-degradation-preference-by-source
Aug 7, 2026
Merged

Default video degradation preference by track source, including the backup codec#1155
hiroshihorie merged 4 commits into
mainfrom
sxian/default-degradation-preference-by-source

Conversation

@xianshijing-lk

@xianshijing-lk xianshijing-lk commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Aligns Flutter with the behavior landed in client-sdk-android (livekit/client-sdk-android#991). Two related changes.

Source-based defaults

Previously every video track fell back to maintainResolution, and the preference was only applied to camera and screen share tracks at all:

if ([TrackSource.camera, TrackSource.screenShareVideo].contains(track.source)) {
  final degradationPreference = options.degradationPreference ?? DegradationPreference.maintainResolution;
  await track.setDegradationPreference(degradationPreference);
}

Now getDefaultDegradationPreference(source) resolves camera → maintainFramerate (smoother video for real-time communication), screen share → maintainResolution (clarity matters for text/UI), other → balanced, and it is applied to every video sender. Custom sources previously got whatever WebRTC derived implicitly from the native source; balanced is the preference the WebRTC spec mandates as the default and is the honest choice when the application declined to declare a motion-vs-detail intent.

An explicitly set degradationPreference still wins in all cases — the default only fills a null.

Backup codec sender

Degradation preference is a property of the sender, not of the track — a top-level field on RtpParameters, not per-encoding. publishAdditionalCodecForPublication adds a second transceiver and therefore a second sender, which was never configured, so the backup encoder resolved a preference implicitly and could adapt along a different axis than the primary.

Both senders sink from the same video source, so a diverging backup does not just degrade itself — its restriction is merged onto the shared source and affects the primary too.

setDegradationPreference now stores the resolved preference and fans out to every sender, and publishAdditionalCodecForPublication applies it to the backup sender once created. Using the track's stored resolved value means the two encoders cannot disagree.

Note simulcast is unaffected — all simulcast encodings live under one sender and already share its preference. Only the backup codec is a separate sender.

Tests

test/options/degradation_preference_test.dart covers the three source mappings. Full suite passes (379 tests), flutter analyze lib/ test/ clean, dart format clean at the repo's 120-column width.

Cross-SDK

client-sdk-js gets the backup-sender half in livekit/client-sdk-js#2040 (its source-based defaults already matched). The Rust SDK already resolves the same defaults and has no backup-codec publish path. Swift follows separately.

🤖 Generated with Claude Code

Camera tracks now default to maintaining framerate, screen share tracks to
maintaining resolution, and other sources to balanced, instead of every
video track defaulting to maintaining resolution.

The preference was also only applied to camera and screen share tracks, so
custom sources got whatever WebRTC derived implicitly. It is now applied to
every video sender.

Degradation preference is a sender-level property, and a backup codec
publishes over its own sender, so it needs the preference applied
separately. Apply the resolved preference there too, and keep every sender
in sync when the preference changes after publish.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

setDegradationPreference now fans out over simulcastCodecs, whose senders
are never cleared and can outlive their peer connection. After a full
reconnect, rePublishAllTracks reuses the same track object, so the fanout
would call setParameters on a sender from the disposed connection and the
resulting platform error aborted the whole republish. Catch and warn
instead, matching how setPublishingLayers handles the same call.
devin-ai-integration[bot]

This comment was marked as resolved.

setDegradationPreference awaits inside the loop over simulcastCodecs, and
addSimulcastTrack can insert into the map during that await when the
server requests a backup codec. Iterate over a snapshot to avoid a
ConcurrentModificationError.
@hiroshihorie
hiroshihorie merged commit 30a88de into main Aug 7, 2026
15 checks passed
@hiroshihorie
hiroshihorie deleted the sxian/default-degradation-preference-by-source branch August 7, 2026 14:14
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.

2 participants