Skip to content

feat(core): pick up SFU DegradationPreference and add WebRTC mapper#1699

Open
PratimMallick wants to merge 2 commits into
developfrom
AND-1192-Support_degradation_preference
Open

feat(core): pick up SFU DegradationPreference and add WebRTC mapper#1699
PratimMallick wants to merge 2 commits into
developfrom
AND-1192-Support_degradation_preference

Conversation

@PratimMallick
Copy link
Copy Markdown
Contributor

@PratimMallick PratimMallick commented May 27, 2026

Goal

Close Closes AND-1192
Honors the DegradationPreference that the SFU now sends on PublishOption (initial publish) and VideoSender (runtime quality changes), so the WebRTC layer optimizes for the right tradeoff (framerate vs. resolution) under constrained bandwidth instead of using balanced always

Implementation

  • Protos: regenerated SFU protos to pick up the new DegradationPreference enum and degradation_preference fields on PublishOption and VideoSender (plus TrackInfo.self_sub_audio_video). Public API dump refreshed accordingly.
  • Mapper (utils/DegradationPreferenceMapper.kt): SfuDegradationPreference.toRtcDegradationPreference() maps the SFU enum to org.webrtc.RtpParameters.DegradationPreference, returning null for UNSPECIFIED so callers can keep the current value.
  • Publisher:
    • On transceiver creation, applyDegradationPreference(transceiver, publishOption) runs right after connection.addTransceiver(...) and only writes sender.parameters when the SFU set a preference and it actually differs from the current value.
    • In changePublishQuality(videoSender), the SFU-provided preference is folded into the existing changed flag, so a quality update that only flips the preference still triggers a single setParameters(...) call, while UNSPECIFIED never forces an unnecessary write.
  • Behavior preserved when missing: any UNSPECIFIED/absent value is a no-op — we never overwrite an existing preference with a default.

Testing

  1. Unit tests for new code
  2. Join a call on pronto env with multiple users, observe that new preferences come from SFU when the scalability mode changes from L1T3 to L3T3_key. Observe in the stats that resolution is not dropping as maintain_resolution was applied

☑️Contributor Checklist

General

  • I have signed the Stream CLA (required)
  • Assigned a person / code owner group (required)
  • Thread with the PR link started in a respective Slack channel (required internally)
  • PR targets the develop branch
  • PR is linked to the GitHub issue it resolves

Code & documentation

  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (KDocs, docusaurus, tutorial)
  • Tutorial starter kit updated
  • Examples/guides starter kits updated (stream-video-examples)

☑️Reviewer Checklist

  • XML sample runs & works
  • Compose sample runs & works
  • Tutorial starter kit
  • Example starter kits work
  • UI Changes correct (before & after images)
  • Bugs validated (bugfixes)
  • New feature tested and works
  • Release notes and docs clearly describe changes
  • All code we touched has new or updated KDocs
  • Check the SDK Size Comparison table in the CI logs

🎉 GIF

Please provide a suitable gif that describes your work on this pull request

Summary by CodeRabbit

  • New Features
    • Enhanced video quality management with support for degradation preference settings, allowing better control over streaming quality during network constraints.
    • Improved audio and video subscription tracking for better media stream handling.

Review Change Stack

Regenerate SFU protos to pick up the new DegradationPreference enum and
its degradation_preference fields on PublishOption and VideoSender (plus
TrackInfo.self_sub_audio_video), and refresh the public API dump.

Add toRtcDegradationPreference() converting the SFU enum to
org.webrtc.RtpParameters.DegradationPreference, returning null for
UNSPECIFIED so callers can keep the current value. Includes unit tests
covering every enum variant.

Co-authored-by: Cursor <cursoragent@cursor.com>
@PratimMallick PratimMallick requested a review from a team as a code owner May 27, 2026 14:24
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@PratimMallick PratimMallick added the pr:improvement Enhances an existing feature or code label May 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

Walkthrough

This PR extends the Stream Video Android SDK with degradation preference support. A new DegradationPreference enum is added to the SFU proto schema with five options (unspecified, balanced, maintain-framerate, maintain-resolution, and combined variants). The VideoSender, PublishOption, and TrackInfo messages are updated with degradation and track subscription fields. An internal Kotlin mapper converts SFU degradation preferences to WebRTC RtpParameters, with explicit null handling for unspecified values, accompanied by complete test coverage.

Changes

Degradation Preference Support

Layer / File(s) Summary
Proto schema definition for degradation preferences
stream-video-android-core/src/main/proto/video/sfu/models/models.proto, stream-video-android-core/src/main/proto/video/sfu/event/events.proto
New DegradationPreference enum with five preference levels (unspecified, balanced, maintain-framerate, maintain-resolution, and combined) is introduced. VideoSender, PublishOption, and TrackInfo messages extended with degradation_preference and self_sub_audio_video fields to carry preference metadata across the SFU protocol.
Generated public API surface
stream-video-android-core/api/stream-video-android-core.api
Public API signatures updated to expose the DegradationPreference enum with standard enum helpers (fromValue, values, getEntries). Constructor and accessor methods for VideoSender, PublishOption, and TrackInfo updated to include the new degradation preference and track subscription parameters and getters.
SFU-to-WebRTC degradation preference mapper and tests
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/utils/DegradationPreferenceMapper.kt, stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/utils/DegradationPreferenceMapperTest.kt
Internal mapper extension converts SFU degradation preferences to WebRTC RtpParameters.DegradationPreference values. UNSPECIFIED input maps explicitly to null to allow callers to preserve existing behavior. Tests verify correct bidirectional mapping for balanced, maintain-framerate, maintain-resolution, and combined variants.

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

pr:improvement

Suggested reviewers

  • aleksandar-apostolov

Poem

📹 A rabbit hops through preference trees,
Balancing frames with graceful ease,
Degradation mapped from SFU to WebRTC's heart,
Schema extended, mapper complete—a unified art! 🎬

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding support for SFU DegradationPreference and its WebRTC mapper integration.
Description check ✅ Passed The description includes all major sections (Goal, Implementation, Testing) with detailed explanations of proto changes, mapper logic, publisher modifications, and test strategy.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch AND-1192-Support_degradation_preference

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
stream-video-android-core/src/main/proto/video/sfu/models/models.proto (1)

229-229: 💤 Low value

Consider adding a documentation comment for the new field.

Other fields in TrackInfo include brief comments (e.g., // for audio tracks). Adding a comment here would clarify the purpose of self_sub_audio_video for SDK consumers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@stream-video-android-core/src/main/proto/video/sfu/models/models.proto` at
line 229, Add a brief documentation comment for the TrackInfo field
self_sub_audio_video that explains its purpose and usage (e.g., whether the
local participant is subscribed to their own audio and video streams), following
the style of other comments in TrackInfo (like "// for audio tracks") so SDK
consumers understand what true/false means and any relevant caveats.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@stream-video-android-core/api/stream-video-android-core.api`:
- Around line 17141-17144: The API changed by adding a DegradationPreference
parameter (and a new getter on TrackInfo), breaking binary compatibility for
callers of the all-args constructors and copy(...) methods for VideoSender,
PublishOption and TrackInfo; restore compatibility by adding deprecated overload
shims that match the previous JVM signatures (constructors and copy methods
without DegradationPreference) which delegate to the new constructors/copy
methods supplying a sensible default DegradationPreference, and for TrackInfo
add the legacy getter getSelf_sub_audio_video() that delegates to the new
property; ensure these shims are public and annotated/deprecated so existing
compiled consumers keep working while signaling the intended breaking change.

In
`@stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/utils/DegradationPreferenceMapperTest.kt`:
- Line 25: Update the DegradationPreferenceMapperTest class to extend the
project test harness by making it inherit from TestBase (replace the current
plain class declaration with one that subclasses TestBase) and add any necessary
import for TestBase; ensure the test class name DegradationPreferenceMapperTest
uses TestBase so it runs as a fast unit test under the repo conventions.

---

Nitpick comments:
In `@stream-video-android-core/src/main/proto/video/sfu/models/models.proto`:
- Line 229: Add a brief documentation comment for the TrackInfo field
self_sub_audio_video that explains its purpose and usage (e.g., whether the
local participant is subscribed to their own audio and video streams), following
the style of other comments in TrackInfo (like "// for audio tracks") so SDK
consumers understand what true/false means and any relevant caveats.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8a6dff3c-b795-4ea3-a731-6cc3005c0fba

📥 Commits

Reviewing files that changed from the base of the PR and between d395d7a and 7343e26.

⛔ Files ignored due to path filters (4)
  • stream-video-android-core/src/main/proto/video/sfu/event/events.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/event/events_vtproto.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/models/models.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/models/models_vtproto.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (5)
  • stream-video-android-core/api/stream-video-android-core.api
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/utils/DegradationPreferenceMapper.kt
  • stream-video-android-core/src/main/proto/video/sfu/event/events.proto
  • stream-video-android-core/src/main/proto/video/sfu/models/models.proto
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/utils/DegradationPreferenceMapperTest.kt

Comment on lines +17141 to +17144
public fun <init> (Lstream/video/sfu/models/Codec;Ljava/util/List;Lstream/video/sfu/models/TrackType;ILstream/video/sfu/models/DegradationPreference;Lokio/ByteString;)V
public synthetic fun <init> (Lstream/video/sfu/models/Codec;Ljava/util/List;Lstream/video/sfu/models/TrackType;ILstream/video/sfu/models/DegradationPreference;Lokio/ByteString;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun copy (Lstream/video/sfu/models/Codec;Ljava/util/List;Lstream/video/sfu/models/TrackType;ILstream/video/sfu/models/DegradationPreference;Lokio/ByteString;)Lstream/video/sfu/event/VideoSender;
public static synthetic fun copy$default (Lstream/video/sfu/event/VideoSender;Lstream/video/sfu/models/Codec;Ljava/util/List;Lstream/video/sfu/models/TrackType;ILstream/video/sfu/models/DegradationPreference;Lokio/ByteString;ILjava/lang/Object;)Lstream/video/sfu/event/VideoSender;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Check whether repo docs define ABI/API compatibility expectations.
rg -n --iglob '*.md' --iglob '*.kts' --iglob '*.gradle.kts' \
  'binary compat|binary compatibility|api compatibility|semver|breaking change'

# 2) Inspect this API dump around affected classes/signatures to confirm removed/changed ctor descriptors.
rg -n 'class stream/video/sfu/event/VideoSender|class stream/video/sfu/models/PublishOption|class stream/video/sfu/models/TrackInfo|fun <init> \(|fun copy \(' \
  stream-video-android-core/api/stream-video-android-core.api -A6 -B3

Repository: GetStream/stream-video-android

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="stream-video-android-core/api/stream-video-android-core.api"

echo "== AGENTS.md ABI compatibility guideline =="
sed -n '30,60p' AGENTS.md || true

echo
echo "== stream-video-android-core.api around 17141-17144 =="
sed -n '17130,17160p' "$FILE" | nl -ba -w2 -s':'

echo
echo "== stream-video-android-core.api around 17830-17833 =="
sed -n '17820,17850p' "$FILE" | nl -ba -w2 -s':'

echo
echo "== stream-video-android-core.api around 18035-18038 =="
sed -n '18025,18060p' "$FILE" | nl -ba -w2 -s':'

Repository: GetStream/stream-video-android

Length of output: 2554


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="stream-video-android-core/api/stream-video-android-core.api"

echo "== stream-video-android-core.api exact 17141-17144 =="
sed -n '17141,17144p' "$FILE"

echo
echo "== stream-video-android-core.api around 17141 (context) =="
sed -n '17135,17150p' "$FILE"

echo
echo "== stream-video-android-core.api exact 17830-17833 =="
sed -n '17830,17833p' "$FILE"

echo
echo "== stream-video-android-core.api around 17830 (context) =="
sed -n '17825,17840p' "$FILE"

echo
echo "== stream-video-android-core.api exact 18035-18038 =="
sed -n '18035,18038p' "$FILE"

echo
echo "== stream-video-android-core.api around 18035 (context) =="
sed -n '18030,18050p' "$FILE"

Repository: GetStream/stream-video-android

Length of output: 8093


Potential binary-compatibility break from generated Wire model constructor/copy signature changes

stream-video-android-core/api/stream-video-android-core.api shows updated public all-args constructor + copy(...) signatures that remove the previous JVM call sites for:

  • stream/video/sfu/event/VideoSender (added DegradationPreference), lines 17141-17144
  • stream/video/sfu/models/PublishOption (added DegradationPreference), lines 17830-17833
  • stream/video/sfu/models/TrackInfo (constructor/copy signature change + new getSelf_sub_audio_video()), lines 18035-18038

Since AGENTS.md requires Maven-shipped modules to retain binary compatibility, this likely breaks already-compiled consumers that directly call those constructors/copy methods—either add binary-compatible overload shims (deprecated) or document/bump as an intentional breaking change with migration notes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@stream-video-android-core/api/stream-video-android-core.api` around lines
17141 - 17144, The API changed by adding a DegradationPreference parameter (and
a new getter on TrackInfo), breaking binary compatibility for callers of the
all-args constructors and copy(...) methods for VideoSender, PublishOption and
TrackInfo; restore compatibility by adding deprecated overload shims that match
the previous JVM signatures (constructors and copy methods without
DegradationPreference) which delegate to the new constructors/copy methods
supplying a sensible default DegradationPreference, and for TrackInfo add the
legacy getter getSelf_sub_audio_video() that delegates to the new property;
ensure these shims are public and annotated/deprecated so existing compiled
consumers keep working while signaling the intended breaking change.

import org.webrtc.RtpParameters
import stream.video.sfu.models.DegradationPreference

class DegradationPreferenceMapperTest {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Adopt TestBase for this unit test class.

Line 25 should use the project test base for unit tests to stay consistent with the repo test harness conventions.

As per coding guidelines, "Use TestBase for fast unit tests and IntegrationTestBase for end-to-end call flows".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/utils/DegradationPreferenceMapperTest.kt`
at line 25, Update the DegradationPreferenceMapperTest class to extend the
project test harness by making it inherit from TestBase (replace the current
plain class declaration with one that subclasses TestBase) and add any necessary
import for TestBase; ensure the test class name DegradationPreferenceMapperTest
uses TestBase so it runs as a fast unit test under the repo conventions.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 12.19 MB 12.21 MB 0.02 MB 🟢
stream-video-android-ui-xml 5.68 MB 5.66 MB -0.02 MB 🚀
stream-video-android-ui-compose 6.27 MB 6.28 MB 0.02 MB 🟢

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
58.5% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Enhances an existing feature or code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant