Skip to content

feat: cherry pick commits from upstream flutter-webrtc - #75

Merged
Brazol merged 6 commits into
mainfrom
feat/cherry-picks-from-upstream
Jul 10, 2026
Merged

feat: cherry pick commits from upstream flutter-webrtc#75
Brazol merged 6 commits into
mainfrom
feat/cherry-picks-from-upstream

Conversation

@Brazol

@Brazol Brazol commented Jul 8, 2026

Copy link
Copy Markdown

cherry picked PRs:
Fix audio session output port override for speaker toggle
Using delete[] instead of delete
fix(android): guard muxer writes against codec-config and invalid sample buffers
fix(android): make VideoFileRenderer more tolerant on Qualcomm/Hisi encoders
chore: Refactoring the CMake binary download process for Win/Linux.
chore(android): support AGP 9 built-in Kotlin
chore(deps): bump WebRTC-SDK to 144.7559.09 and libwebrtc to m144.7559.09 (partially)
fix: call SetRecordingDevice(0) when no sourceId is supplied
feat(android): add fullScreenOnly option to requestCapturePermission (force entire-screen on API 34+)
fix(android): recreate texture surface when frame size changes
Fix Windows libwebrtc extraction through plugin symlinks
fix(desktop): map echoCancellation/noiseSuppression/autoGainControl constraints to RTCAudioOptions

Summary by CodeRabbit

  • New Features

    • Added support for full-screen-only screen capture on Android, including Android 14+ behavior.
    • Expanded platform version reporting with macOS, Windows, and Linux WebRTC versions.
  • Bug Fixes

    • Improved audio permission handling and applied user-selected audio settings more accurately.
    • Stabilized video rendering and recording to reduce frame, encoder, and shutdown issues.
    • Updated speaker output handling on Apple platforms.
  • Chores

    • Refreshed build and packaging settings across Android, Linux, Windows, and example apps.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Brazol, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3215e4a-078e-4577-a532-b865206124bd

📥 Commits

Reviewing files that changed from the base of the PR and between 90959ac and a0f1286.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • ios/stream_webrtc_flutter.podspec
  • macos/stream_webrtc_flutter.podspec
  • pubspec.yaml
  • third_party/CMakeLists.txt
📝 Walkthrough

Walkthrough

This PR syncs with flutter-webrtc v1.5.2, upgrading Gradle/Kotlin/AGP toolchains, reworking libwebrtc's manifest-driven download/extraction and link paths, adding a fullScreenOnly screen-capture option end-to-end (Dart to Android), fixing SurfaceTextureRenderer/VideoFileRenderer concurrency and encoding issues, updating audio option handling, and correcting iOS speakerphone override.

Changes

Build tooling, versioning, and dependency updates

Layer / File(s) Summary
Android Gradle/Kotlin plugin upgrade
android/build.gradle
Bumps Kotlin to 2.2.20 and AGP to 8.1.0, conditionally applies kotlin-android plugin based on built-in Kotlin detection, updates jvmTarget config for new/old Kotlin DSL, removes explicit kotlin-stdlib-jdk7 dependency.
Example app Gradle/plugin version updates
example/android/gradle.properties, example/android/gradle/wrapper/gradle-wrapper.properties, example/android/settings.gradle, example/windows/CMakeLists.txt, .gitignore
Disables builtInKotlin/newDsl flags, bumps Gradle wrapper to 8.14 and plugin versions, suppresses Windows coroutine deprecation warnings, ignores example/android/build.
Changelog and WebRTC version constants
CHANGELOG.md, lib/stream_webrtc_flutter.dart
Adds 3.0.1 changelog section and macOS/Windows/Linux WebRTC version constants.

Estimated code review effort: 2 (Simple) | ~12 minutes

libwebrtc download and linking rework

Layer / File(s) Summary
Manifest-driven download and extraction
third_party/libwebrtc_version.ini, third_party/CMakeLists.txt
Replaces hardcoded release URL with manifest (binary_version, download_url) reading, resolves OS/arch, composes asset name/URL, and normalizes extracted archive layout.
Platform-agnostic library link paths
linux/CMakeLists.txt, elinux/CMakeLists.txt, windows/CMakeLists.txt
Updates link and bundling paths to reference libwebrtc binaries at a fixed location instead of platform-specific subdirectories.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Full-screen-only screen capture option

Layer / File(s) Summary
Dart requestCapturePermission API
lib/src/helper.dart, lib/src/native/native_peer_connection_factory.dart, lib/src/web/native_peer_connection_factory.dart
Adds optional fullScreenOnly named parameter to permission methods, forwarding it in method-channel argument maps.
Android native screen capture permission flow
android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java, android/src/main/java/io/getstream/webrtc/flutter/GetUserMediaImpl.java
Extracts fullScreenOnly argument and adds overloaded methods; uses MediaProjectionConfig.createConfigForDefaultDisplay() on Android 14+ when enabled.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Video/audio rendering and encoding fixes

Layer / File(s) Summary
SurfaceTextureRenderer surface lock synchronization
android/src/main/java/io/getstream/webrtc/flutter/SurfaceTextureRenderer.java
Introduces surfaceLock to synchronize EGL surface creation/recreation with destruction, removes producer.setSize call from dimension-change path.
VideoFileRenderer encoder lifecycle refactor
android/src/main/java/io/getstream/webrtc/flutter/record/VideoFileRenderer.java
Adds encoderInitFailed flag, codec profile forcing heuristic, refactors init/teardown into helpers, reorders swap/drain, fixes buffer handling for codec-config/invalid buffers.
RGB buffer type fix
common/cpp/include/flutter_video_renderer.h
Changes rgb_buffer_ from single-value to array shared_ptr.
Audio processing options handling
common/cpp/src/flutter_media_stream.cc
Adds getAudioProcessingFlag helper, populates RTCAudioOptions from constraints, passes into CreateAudioSource, reports actual settings.
iOS speakerphone override fix
common/darwin/Classes/AudioUtils.m, ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/AudioUtils.m
Changes overrideOutputAudioPort argument to AVAudioSessionPortOverrideSpeaker.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Dart as Helper/NativePeerConnectionFactory
  participant Channel as MethodChannel
  participant MCH as MethodCallHandlerImpl
  participant GUM as GetUserMediaImpl
  participant Fragment as ScreenRequestPermissionsFragment
  participant Projection as MediaProjectionManager

  Dart->>Channel: invokeMethod("requestCapturePermission", {fullScreenOnly})
  Channel->>MCH: onMethodCall(call)
  MCH->>GUM: requestCapturePermission(result, fullScreenOnly)
  GUM->>Fragment: screenRequestPermissions(receiver, fullScreenOnly)
  Fragment->>Fragment: store FULL_SCREEN_ONLY in arguments
  Fragment->>Projection: requestStart(activity, requestCode, fullScreenOnly)
  alt fullScreenOnly && Android 14+
    Projection->>Projection: createConfigForDefaultDisplay()
  else
    Projection->>Projection: createScreenCaptureIntent()
  end
  Projection-->>Fragment: capture intent result
  Fragment-->>MCH: ResultReceiver callback
  MCH-->>Dart: result(success)
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects that the PR cherry-picks upstream flutter-webrtc changes, which matches the overall changeset.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cherry-picks-from-upstream

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/AudioUtils.m (1)

85-86: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable path still uses Core Audio constant and raw session.

Same inconsistency as the darwin implementation: line 85 uses kAudioSessionOverrideAudioRoute_None and session.session while the enable path uses AVAudioSessionPortOverrideSpeaker and session. Align the disable path for consistency.

🔧 Suggested fix for the disable path
-    success = [session.session overrideOutputAudioPort:kAudioSessionOverrideAudioRoute_None
-                                                 error:&error];
+    success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideNone
+                                        error:&error];
🤖 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 `@ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/AudioUtils.m` around
lines 85 - 86, The disable path in AudioUtils.m is inconsistent with the enable
path: it still calls session.session and uses the Core Audio constant
kAudioSessionOverrideAudioRoute_None. Update the disable branch to use the same
session object and AVAudioSessionPortOverride value style as the enable branch
in the relevant AudioUtils method, so both paths are aligned and use the modern
API consistently.
common/darwin/Classes/AudioUtils.m (1)

85-86: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable path still uses Core Audio constant and raw session.

Line 85 uses kAudioSessionOverrideAudioRoute_None (a Core Audio enum) and session.session (raw AVAudioSession), while the enable path now correctly uses AVAudioSessionPortOverrideSpeaker and session (the RTCAudioSession wrapper). Since this PR is fixing the same type-mismatch bug in the enable path, the disable path should be aligned for consistency. kAudioSessionOverrideAudioRoute_None happens to equal AVAudioSessionPortOverrideNone (both 0), but relying on that coincidence is fragile.

🔧 Suggested fix for the disable path
-    success = [session.session overrideOutputAudioPort:kAudioSessionOverrideAudioRoute_None
-                                                 error:&error];
+    success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideNone
+                                        error:&error];
🤖 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 `@common/darwin/Classes/AudioUtils.m` around lines 85 - 86, The disable path in
AudioUtils.m is still using the old Core Audio enum and raw AVAudioSession
access, so align it with the enable path fix. Update the
`overrideOutputAudioPort:error:` call in the same audio session flow to use the
`RTCAudioSession` wrapper (`session`) and the modern
`AVAudioSessionPortOverrideNone` constant instead of
`kAudioSessionOverrideAudioRoute_None` and `session.session`, keeping the
speaker on/off handling consistent across both branches.
lib/src/helper.dart (1)

211-221: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass factoryId through the static helper
lib/src/helper.dart:211 still calls requestCapturePermission without a factoryId, but the Android handler rejects null/empty ids before reaching the fullScreenOnly path. The per-factory API already sends factoryId; this static entry point will keep failing for Android callers unless it is wired the same way or removed.

🤖 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 `@lib/src/helper.dart` around lines 211 - 221, The static helper
requestCapturePermission in helper.dart still omits factoryId, so Android
rejects the call before handling fullScreenOnly. Update this helper to accept
and forward factoryId through WebRTC.invokeMethod the same way the per-factory
API does, or remove the static entry point if it is no longer valid. Keep the
Android-only guard behavior intact and make sure the requestCapturePermission
signature and call sites stay consistent with the factory-based flow.
🧹 Nitpick comments (2)
android/src/main/java/io/getstream/webrtc/flutter/record/VideoFileRenderer.java (1)

353-365: 🩺 Stability & Availability | 🔵 Trivial | ⚖️ Poor tradeoff

Verify the standalone-EGL fallback can reuse the already-attempted input surface.

When setupEncoderSurface(sharedContext, "shared") fails after eglBase.createSurface(surface) succeeded but a later step threw, the catch releases only eglBase and leaves the encoder's input surface intact. The standalone retry then calls EglBase.createSurface(surface) again on the same Surface. Depending on where the shared attempt failed, that surface may already be bound to the released EGL context, causing the fallback to fail with EGL_BAD_SURFACE and burn the whole config. Please confirm the fallback actually recovers rather than silently failing to the next config.

🤖 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
`@android/src/main/java/io/getstream/webrtc/flutter/record/VideoFileRenderer.java`
around lines 353 - 365, The standalone fallback in
VideoFileRenderer.setupEncoderSurface should be able to reuse the same encoder
input surface after a failed shared-context attempt. Update the error-handling
path around the setupEncoderSurface(sharedContext, "shared") and
setupEncoderSurface(null, "standalone") retries so that a failure after
eglBase.createSurface(surface) does not leave the Surface in a state that makes
the second attempt hit EGL_BAD_SURFACE. Verify the cleanup/release logic in
setupEncoderSurface and the surrounding encoder-start flow preserves a valid
retry path and that resetVideoEncoderState/releaseVideoEncoderResources fully
recover before moving on.
common/cpp/src/flutter_media_stream.cc (1)

224-225: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused audioConstraints path. In common/cpp/src/flutter_media_stream.cc, audioConstraints is created in both audio branches but never read, so the addDefaultAudioConstraints(...) and ParseMediaConstraints(...) calls here do nothing. Drop the local variable and these calls from GetUserAudio.

🤖 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 `@common/cpp/src/flutter_media_stream.cc` around lines 224 - 225, The
GetUserAudio path in flutter_media_stream.cc is carrying an unused
audioConstraints variable, so the addDefaultAudioConstraints(...) and
ParseMediaConstraints(...) calls in the audio setup branch have no effect.
Remove that local variable and the related constraint-building calls from
GetUserAudio, and keep the audio source creation using the existing
RTCAudioSource/RTCAudioOptions flow only.
🤖 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 `@third_party/CMakeLists.txt`:
- Around line 136-137: The notice in the libwebrtc extraction check is
misleading because it implies extraction already happened; update the message in
the CMake logic around the LIBWEBRTC_THIRD_PARTY_DIR/libwebrtc existence check
to accurately state that the libwebrtc directory is missing before extraction or
unpacking begins. Keep the condition and surrounding flow unchanged, but rewrite
the message text in the relevant message(NOTICE ...) call so it matches the
current state being validated.
- Around line 122-138: The CMake download/extraction flow in the third_party
logic creates generated artifacts that should not be tracked. Update the
repository ignore rules to exclude both the downloads cache and the extracted
libwebrtc tree, specifically the paths created by the file(DOWNLOAD) and
_libwebrtc_extract_and_normalize steps, so they are not accidentally committed.
- Around line 122-138: The file(DOWNLOAD) call in the third_party CMake logic is
missing explicit transfer hardening and safe path handling. Update the download
block around the existing file(DOWNLOAD) invocation to pass TLS_VERIFY ON and an
INACTIVITY_TIMEOUT value, and quote the ZIPFILE argument so the download target
works correctly when the path contains spaces. Keep the change localized to the
download/extract flow that uses download_status and download_log.

---

Outside diff comments:
In `@common/darwin/Classes/AudioUtils.m`:
- Around line 85-86: The disable path in AudioUtils.m is still using the old
Core Audio enum and raw AVAudioSession access, so align it with the enable path
fix. Update the `overrideOutputAudioPort:error:` call in the same audio session
flow to use the `RTCAudioSession` wrapper (`session`) and the modern
`AVAudioSessionPortOverrideNone` constant instead of
`kAudioSessionOverrideAudioRoute_None` and `session.session`, keeping the
speaker on/off handling consistent across both branches.

In `@ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/AudioUtils.m`:
- Around line 85-86: The disable path in AudioUtils.m is inconsistent with the
enable path: it still calls session.session and uses the Core Audio constant
kAudioSessionOverrideAudioRoute_None. Update the disable branch to use the same
session object and AVAudioSessionPortOverride value style as the enable branch
in the relevant AudioUtils method, so both paths are aligned and use the modern
API consistently.

In `@lib/src/helper.dart`:
- Around line 211-221: The static helper requestCapturePermission in helper.dart
still omits factoryId, so Android rejects the call before handling
fullScreenOnly. Update this helper to accept and forward factoryId through
WebRTC.invokeMethod the same way the per-factory API does, or remove the static
entry point if it is no longer valid. Keep the Android-only guard behavior
intact and make sure the requestCapturePermission signature and call sites stay
consistent with the factory-based flow.

---

Nitpick comments:
In
`@android/src/main/java/io/getstream/webrtc/flutter/record/VideoFileRenderer.java`:
- Around line 353-365: The standalone fallback in
VideoFileRenderer.setupEncoderSurface should be able to reuse the same encoder
input surface after a failed shared-context attempt. Update the error-handling
path around the setupEncoderSurface(sharedContext, "shared") and
setupEncoderSurface(null, "standalone") retries so that a failure after
eglBase.createSurface(surface) does not leave the Surface in a state that makes
the second attempt hit EGL_BAD_SURFACE. Verify the cleanup/release logic in
setupEncoderSurface and the surrounding encoder-start flow preserves a valid
retry path and that resetVideoEncoderState/releaseVideoEncoderResources fully
recover before moving on.

In `@common/cpp/src/flutter_media_stream.cc`:
- Around line 224-225: The GetUserAudio path in flutter_media_stream.cc is
carrying an unused audioConstraints variable, so the
addDefaultAudioConstraints(...) and ParseMediaConstraints(...) calls in the
audio setup branch have no effect. Remove that local variable and the related
constraint-building calls from GetUserAudio, and keep the audio source creation
using the existing RTCAudioSource/RTCAudioOptions flow only.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85bc37db-4530-4134-a85a-f86121cc4bef

📥 Commits

Reviewing files that changed from the base of the PR and between 8a26844 and 90959ac.

📒 Files selected for processing (24)
  • .gitignore
  • CHANGELOG.md
  • android/build.gradle
  • android/src/main/java/io/getstream/webrtc/flutter/GetUserMediaImpl.java
  • android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java
  • android/src/main/java/io/getstream/webrtc/flutter/SurfaceTextureRenderer.java
  • android/src/main/java/io/getstream/webrtc/flutter/record/VideoFileRenderer.java
  • common/cpp/include/flutter_video_renderer.h
  • common/cpp/src/flutter_media_stream.cc
  • common/darwin/Classes/AudioUtils.m
  • elinux/CMakeLists.txt
  • example/android/gradle.properties
  • example/android/gradle/wrapper/gradle-wrapper.properties
  • example/android/settings.gradle
  • example/windows/CMakeLists.txt
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/AudioUtils.m
  • lib/src/helper.dart
  • lib/src/native/native_peer_connection_factory.dart
  • lib/src/web/native_peer_connection_factory.dart
  • lib/stream_webrtc_flutter.dart
  • linux/CMakeLists.txt
  • third_party/CMakeLists.txt
  • third_party/libwebrtc_version.ini
  • windows/CMakeLists.txt

Comment thread third_party/CMakeLists.txt
Comment thread third_party/CMakeLists.txt
@Brazol
Brazol merged commit d7098ba into main Jul 10, 2026
12 checks passed
@Brazol
Brazol deleted the feat/cherry-picks-from-upstream branch July 10, 2026 09:24
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