Skip to content

Conversation

@Brazol
Copy link
Contributor

@Brazol Brazol commented Oct 22, 2025

resolves FLU-293

This PR fixes an issue where screen sharing was not stopped correctly when canceled using system UI on Android or iOS. It also improves the iOS broadcast extension handling, waiting for a broadcast picker selection, with actually starting to screen share.

Summary by CodeRabbit

  • Bug Fixes

    • Screen sharing now reliably stops when canceled via system UI on Android and iOS.
    • iOS broadcast extension handling improved by waiting for picker selection before starting screen sharing.
  • New Features

    • Native event integration for screen sharing start/stop and improved background handling for active calls.
    • Added a lightweight notification manager for iOS broadcast events.
  • Chores

    • Bumped stream_webrtc_flutter to ^1.0.13 and added rxdart dependency.

@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Walkthrough

Adds native WebRTC event plumbing for screen-share start/stop, iOS broadcast-extension handling, constraint-aware track enablement, subscription management for background services, a Darwin notification helper on iOS, and bumps stream_webrtc_flutter to ^1.0.13.

Changes

Cohort / File(s) Change Summary
Dependency & Config Updates
melos.yaml, packages/stream_video/pubspec.yaml, packages/stream_video_flutter/example/pubspec.yaml, packages/stream_video_flutter/pubspec.yaml, packages/stream_video_noise_cancellation/pubspec.yaml, packages/stream_video_push_notification/pubspec.yaml
Bumped stream_webrtc_flutter from ^1.0.12^1.0.13; added rxdart: ^0.28.0 to packages/stream_video_flutter/pubspec.yaml.
Changelogs
packages/stream_video/CHANGELOG.md, packages/stream_video_flutter/CHANGELOG.md
Added Unreleased entries documenting fixes for system-UI-initiated stop and iOS broadcast-extension improvements.
Native WebRTC Events & Notifier
packages/stream_video/lib/src/webrtc/rtc_media_device/rtc_media_device_notifier.dart
New abstract NativeWebRtcEvent and concrete ScreenSharingStoppedEvent/ScreenSharingStartedEvent; added nativeWebRtcEventsStream() to map native rtc events to these types and expose a broadcast stream.
RTC Track Predicate
packages/stream_video/lib/src/webrtc/rtc_track/rtc_track.dart
Added bool get isScreenShareTrack to detect screen-share (video/audio) tracks.
RtcManager / Track Flow
packages/stream_video/lib/src/webrtc/rtc_manager.dart
Added awaitNativeWebRtcEvent<T>() extension and support for iOS Broadcast Extension flow that waits for ScreenSharingStartedEvent before publishing; new helpers and cancellation/cleanup for native-event-driven publish flow; adjusted screen-share creation/publish logic.
Call-level Native Event Handling
packages/stream_video/lib/src/call/call.dart
Register native WebRTC observer; added _onNativeWebRtcEvent handling to disable local screen-share tracks on ScreenSharingStoppedEvent; enable paths now propagate TrackOption.constraints to camera/microphone/screen-share enable methods.
Call Session Unpublish Handling
packages/stream_video/lib/src/call/session/call_session.dart
_onTrackUnpublished now checks for a corresponding local screen-share track and calls setScreenShareEnabled(false) (awaited) for non-remote unpublished tracks when appropriate.
Call Connect Options / TrackOption
packages/stream_video/lib/src/call/call_connect_options.dart
TrackOption.enabled now accepts optional MediaConstraints? constraints; TrackEnabled stores constraints (removed singleton) and updates props/toString.
Background Service Subscriptions
packages/stream_video_flutter/lib/src/call_background/background_service.dart
Replaced single subscription with a Subscription manager; added RTC events subscription listening for ScreenSharingStoppedEvent and stop-notification handling for matching active calls.
iOS Darwin Notification Helper
packages/stream_video_flutter/ios/Classes/ScreenShare/ScreenShareManager.swift
New ScreenShareManager singleton wrapping CFNotificationCenter with thread-safe observed-names set, main-thread onNotification callback, observe/remove APIs and cleanup.
iOS Plugin Wiring
packages/stream_video_flutter/ios/Classes/StreamVideoFlutterPlugin.swift
Added broadcastStartedNotification / broadcastStoppedNotification constants; registered ScreenShareManager observers and mapped notifications to FlutterWebRTC plugin events (screenSharingStarted, screenSharingStopped).

Sequence Diagram(s)

sequenceDiagram
    participant System as System UI
    participant Native as Native Platform
    participant ScreenMgr as ScreenShareManager (iOS)
    participant Plugin as StreamVideoFlutterPlugin
    participant RtcNotifier as RtcMediaDeviceNotifier
    participant Rtc as RtcManager
    participant Call as Call
    participant BG as BackgroundService

    System->>Native: user stops/starts broadcast
    Native->>ScreenMgr: post Darwin notification
    ScreenMgr->>Plugin: onNotification(name)
    Plugin->>RtcNotifier: postEvent("screenSharingStopped"/"screenSharingStarted")
    RtcNotifier->>Rtc: nativeWebRtcEventsStream -> ScreenSharingStopped/Started
    Rtc->>Call: emit NativeWebRtcEvent
    Call->>Call: _onNativeWebRtcEvent -> disable/enable local screen-share track
    Rtc->>BG: event also observed
    BG->>BG: find matching callCid -> stop notification service
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing focused review:
    • iOS broadcast-extension wait/timeout/cancellation in rtc_manager.dart.
    • Subscription lifecycle and correctness in background_service.dart (Subscription manager usage and cancellation).
    • Concurrency, CFNotification callback safety, and main-thread dispatch in ScreenShareManager.swift.
    • Correct mapping and naming consistency of native event keys ("screenSharingStarted", "screenSharingStopped") across plugin, notifier, and native layers.
    • Propagation and usage of MediaConstraints via TrackOption into enable/publish paths.

Possibly related PRs

Suggested reviewers

  • renefloor

Poem

🐰 I hopped where events take flight,
A Darwin ping cut off the light.
Native whispers reached the Call,
I nudged the track — "stop sharing, all." 🎥✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is minimal and does not follow the required template structure. While it does provide the linked issue reference (FLU-293) and briefly explains the fix, it lacks nearly all required sections including Goal, Implementation details, UI Changes, Testing, and the contributor/reviewer checklists. The description offers only a high-level summary without the structured detail specified in the template. The description is largely incomplete relative to the repository's expectations. Add a properly structured description following the template provided in the repository. At minimum, include Goal, Implementation details, and Testing sections that explain why the change was made, how it was implemented (including the native event handling flow and iOS broadcast extension improvements), and how the changes can be tested. The contributor checklist items, including PR linking to the issue and Slack notification, should also be completed.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "fix(llc,ui): fixing screen share cancelation" directly relates to the primary change in the pull request, which is fixing screen sharing cancellation when triggered by system UI on Android and iOS. The scope tags indicate low-level communication and UI changes, which align with the implementation involving both native platform handling and Dart-level event management. The title is concise and specific enough for reviewers to understand the main objective, though there is a minor spelling inconsistency ("cancelation" vs. standard "cancellation").
Linked Issues Check ✅ Passed The changes comprehensively address the FLU-293 requirements. The implementation introduces native WebRTC event handling to respond to ScreenSharingStoppedEvent, disabling screen sharing when the system UI cancels it on Android/iOS [call.dart, call_session.dart]. For iOS, it improves broadcast extension handling by waiting for ScreenSharingStartedEvent before publishing the screen share track [rtc_manager.dart]. Additionally, the Swift implementation creates a robust ScreenShareManager for handling Darwin notifications [ScreenShareManager.swift, StreamVideoFlutterPlugin.swift], and background service properly stops notifications when screen sharing stops [background_service.dart]. Platform-specific logic and event-driven architecture align with the issue's requirements for robust, system-UI-aware screen share cancellation.
Out of Scope Changes Check ✅ Passed All changes in the pull request are directly related to the FLU-293 objective of implementing screen share cancellation from system UI. The native event handling infrastructure (ScreenSharingStoppedEvent, ScreenSharingStartedEvent), platform-specific implementations (ScreenShareManager.swift, native event listeners), and application-level handling (call.dart, background_service.dart) all contribute to resolving the stated requirements. Even supporting changes like the isScreenShareTrack getter and constraint-aware TrackOption updates serve the primary objective. The dependency updates to stream_webrtc_flutter may provide underlying support for this functionality. No extraneous changes are present.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/screen-share-cancelation-handling

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.

@Brazol Brazol marked this pull request as ready for review October 28, 2025 10:08
@Brazol Brazol requested a review from a team as a code owner October 28, 2025 10:08
@codecov
Copy link

codecov bot commented Oct 28, 2025

Codecov Report

❌ Patch coverage is 11.90476% with 111 lines in your changes missing coverage. Please review.
✅ Project coverage is 4.84%. Comparing base (09cfc71) to head (6fb1db6).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/stream_video/lib/src/call/call.dart 15.21% 39 Missing ⚠️
...kages/stream_video/lib/src/webrtc/rtc_manager.dart 0.00% 32 Missing ⚠️
...er/lib/src/call_background/background_service.dart 0.00% 19 Missing ⚠️
...tc/rtc_media_device/rtc_media_device_notifier.dart 33.33% 10 Missing ⚠️
...tream_video/lib/src/call/session/call_session.dart 0.00% 5 Missing ⚠️
...tream_video/lib/src/call/call_connect_options.dart 50.00% 3 Missing ⚠️
...ream_video/lib/src/webrtc/rtc_track/rtc_track.dart 0.00% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #1085      +/-   ##
========================================
+ Coverage   4.82%   4.84%   +0.01%     
========================================
  Files        591     591              
  Lines      40402   40500      +98     
========================================
+ Hits        1949    1961      +12     
- Misses     38453   38539      +86     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@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: 6

🧹 Nitpick comments (3)
packages/stream_video_flutter/lib/src/call_background/background_service.dart (1)

244-276: Reduce notification churn: update only on meaningful changes

The service is updated on every CallState emission. This is noisy and can be expensive. Cache the last built NotificationOptions and update only if changed (deep equality or selective fields), or debounce.

Example:

-        final stateSubscription = call.state.listen((CallState value) async {
+        NotificationOptions? _last;
+        final stateSubscription = call.state.listen((CallState value) async {
           ...
-          try {
-            final updateOptions = optionsBuilder.call(call);
+          try {
+            final updateOptions = optionsBuilder.call(call);
+            if (_last != null && _last == updateOptions) return;
             ...
-            _logger.v(() => '<$callCid> [_startManagingCall] call service update result: $updateResult');
+            if (updateResult) _last = updateOptions;
+            _logger.v(() => '<$callCid> [_startManagingCall] call service update result: $updateResult');

If NotificationOptions lacks ==, consider comparing key fields or hashing.

packages/stream_video/lib/src/call/session/call_session.dart (1)

772-785: Local-unpublish handling: simplify and avoid redundant lookup

You already know the track is local (track is! RtcRemoteTrack). Re‑querying the same track via rtcManager?.getTrack(track.trackId) is redundant.

Apply this diff:

-    if (track is! RtcRemoteTrack) {
-      final localTrack = rtcManager?.getTrack(track.trackId);
-      if (localTrack != null &&
-          localTrack.isScreenShareTrack &&
-          localTrack.mediaTrack.enabled) {
+    if (track is! RtcRemoteTrack) {
+      final localTrack = track;
+      if (localTrack.isScreenShareTrack && localTrack.mediaTrack.enabled) {
         await setScreenShareEnabled(false);
       }
       return;
     }
packages/stream_video_flutter/ios/Classes/ScreenShare/ScreenShareManager.swift (1)

77-93: LGTM! Correct callback handling with main queue dispatch.

The callback implementation properly:

  • Unwraps the unmanaged observer pointer
  • Guards against nil values
  • Dispatches user callbacks to the main queue (appropriate for Flutter/UI integration)

The static CFNotificationCallback correctly bridges C-style callbacks to Swift.

Optional: Consider thread-safe callback access

The onNotification callback is a mutable var that could theoretically be set to nil between the guard check and dispatch execution. If callback mutation during notification processing is a concern, consider:

private var _onNotification: ((String) -> Void)?
private let callbackLock = NSLock()

var onNotification: ((String) -> Void)? {
    get {
        callbackLock.lock()
        defer { callbackLock.unlock() }
        return _onNotification
    }
    set {
        callbackLock.lock()
        defer { callbackLock.unlock() }
        _onNotification = newValue
    }
}

However, for typical usage patterns where the callback is set once during initialization, the current implementation is likely sufficient.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 09cfc71 and 9257fcc.

📒 Files selected for processing (16)
  • melos.yaml (1 hunks)
  • packages/stream_video/CHANGELOG.md (1 hunks)
  • packages/stream_video/lib/src/call/call.dart (5 hunks)
  • packages/stream_video/lib/src/call/session/call_session.dart (1 hunks)
  • packages/stream_video/lib/src/webrtc/rtc_manager.dart (5 hunks)
  • packages/stream_video/lib/src/webrtc/rtc_media_device/rtc_media_device_notifier.dart (2 hunks)
  • packages/stream_video/lib/src/webrtc/rtc_track/rtc_track.dart (1 hunks)
  • packages/stream_video/pubspec.yaml (1 hunks)
  • packages/stream_video_flutter/CHANGELOG.md (1 hunks)
  • packages/stream_video_flutter/example/pubspec.yaml (1 hunks)
  • packages/stream_video_flutter/ios/Classes/ScreenShare/ScreenShareManager.swift (1 hunks)
  • packages/stream_video_flutter/ios/Classes/StreamVideoFlutterPlugin.swift (2 hunks)
  • packages/stream_video_flutter/lib/src/call_background/background_service.dart (4 hunks)
  • packages/stream_video_flutter/pubspec.yaml (1 hunks)
  • packages/stream_video_noise_cancellation/pubspec.yaml (1 hunks)
  • packages/stream_video_push_notification/pubspec.yaml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/stream_video_flutter/ios/Classes/StreamVideoFlutterPlugin.swift (1)
packages/stream_video_flutter/ios/Classes/ScreenShare/ScreenShareManager.swift (1)
  • observeNotification (24-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: analyze
  • GitHub Check: build
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: stream_video_flutter
  • GitHub Check: stream_video_push_notification
  • GitHub Check: stream_video
🔇 Additional comments (16)
packages/stream_video/CHANGELOG.md (1)

1-6: Changelog entries are clear and well-documented.

The new "Unreleased" section accurately captures the two bug fixes from this PR: screen sharing not stopping via system UI cancellation on Android/iOS, and iOS broadcast extension handling improvements. Entries are concise, follow the established changelog format with emoji prefixes and platform tags, and align with the PR objectives.

packages/stream_video_flutter/pubspec.yaml (2)

25-25: New dependency added: rxdart

The rxdart package has been added as a new dependency. Based on the PR context, this is likely used for managing native WebRTC event streams and screen sharing lifecycle events.


27-27: Verify that stream_webrtc_flutter v1.0.13 includes the expected WebRTC event handling fixes.

Version 1.0.13 exists on pub.dev and was published today, making it a valid dependency update. However, no public changelog or release notes are currently available to confirm it contains the specific fixes for WebRTC event handling and Android/iOS system UI stop events mentioned in the review comment. Ensure this version actually includes the fixes you expect for screen sharing cancellation before merging.

packages/stream_video_noise_cancellation/pubspec.yaml (1)

18-18: LGTM!

Dependency version bump is consistent with the broader repository update to stream_webrtc_flutter ^1.0.13.

packages/stream_video_push_notification/pubspec.yaml (1)

26-26: LGTM!

Dependency version bump is consistent with the broader repository update to stream_webrtc_flutter ^1.0.13.

melos.yaml (1)

25-25: LGTM!

Bootstrap dependency version is correctly aligned with the version bumps across all package pubspec files.

packages/stream_video_flutter/CHANGELOG.md (1)

1-7: LGTM!

The changelog entries accurately document the screen sharing fixes for Android/iOS system UI cancellation and the iOS broadcast extension improvements mentioned in the PR objectives.

packages/stream_video_flutter/example/pubspec.yaml (1)

33-33: LGTM!

Example app dependency version correctly updated to match the main packages.

packages/stream_video/pubspec.yaml (1)

34-34: LGTM!

Core package dependency version correctly updated to stream_webrtc_flutter ^1.0.13.

packages/stream_video/lib/src/webrtc/rtc_track/rtc_track.dart (1)

42-44: LGTM!

The new isScreenShareTrack getter follows the same pattern as the existing isVideoTrack and isAudioTrack getters. The logic correctly identifies both screen share video and audio tracks, which aligns with the screen sharing improvements in this PR.

packages/stream_video/lib/src/call/call.dart (1)

2002-2015: Good: pass constraints when enabling provided local tracks

Forwarding the concrete constraints to setMicrophoneEnabled, setCameraEnabled, and setScreenShareEnabled prevents capability regressions and aligns with the new flow. LGTM.

packages/stream_video_flutter/lib/src/call_background/background_service.dart (1)

39-41: No issues found—the review comment is incorrect

The Subscriptions class is actually publicly exported from the stream_video package. The file packages/stream_video/lib/stream_video.dart explicitly exports src/utils/subscriptions.dart, making Subscriptions part of the public API. Since background_service.dart imports from package:stream_video/stream_video.dart, the symbol resolves correctly and compilation will succeed. The suggested refactor is unnecessary.

Likely an incorrect or invalid review comment.

packages/stream_video/lib/src/webrtc/rtc_media_device/rtc_media_device_notifier.dart (1)

18-28: LGTM! Clean event class hierarchy.

The abstract base class NativeWebRtcEvent with concrete implementations for screen sharing events is well-structured and follows a clear, extensible pattern.

packages/stream_video_flutter/ios/Classes/ScreenShare/ScreenShareManager.swift (3)

1-17: LGTM! Solid singleton pattern with proper thread safety.

The class structure is well-designed with appropriate use of CFNotificationCenter for Darwin notifications and NSLock for thread-safe access to shared state.


19-21: LGTM! Proper resource cleanup.

The deinit correctly removes all observers to prevent leaks.


24-75: LGTM! Thread-safe observer management.

The observer management methods are well-implemented with:

  • Proper locking to protect shared state
  • Guards against duplicate additions and non-existent removals
  • Correct use of Unmanaged pointers for CFNotificationCenter
  • Clean use of defer for guaranteed lock release

Copy link

@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: 1

♻️ Duplicate comments (2)
packages/stream_video_flutter/lib/src/call_background/background_service.dart (1)

179-208: Null-safety issue flagged in previous review

The past review identified that using ! on call.state.value.localParticipant at line 192 is unsafe even after the null check at line 186, since the check and access happen on separate lines and state could theoretically change.

packages/stream_video/lib/src/call/call.dart (1)

471-515: Null-safety issue flagged in previous review

The past review identified that using ! on state.value.localParticipant at line 488 is unsafe even after the null check at line 486. The check and the force-unwrap happen on separate accesses to state.value.localParticipant, which could theoretically change between them.

🧹 Nitpick comments (1)
packages/stream_video/lib/src/webrtc/rtc_manager.dart (1)

1181-1212: Consider timeout for iOS broadcast extension flow

When using iOS broadcast extension, the code creates a track and starts listening for ScreenSharingStartedEvent but returns immediately without a timeout. If the user cancels the broadcast picker or the event never arrives, the subscription remains active indefinitely.

Consider adding a timeout to clean up if the event doesn't arrive:

if (CurrentPlatform.isIos &&
    enabled &&
    constraints is ScreenShareConstraints &&
    constraints.useiOSBroadcastExtension) {
  final screenShareTrackResult = await createScreenShareTrack(
    constraints: constraints,
  );

  if (screenShareTrackResult.isFailure) {
    _logger.e(
      () =>
          '[ScreenSharingStartedEvent] failed to create screen share track: ${screenShareTrackResult.getErrorOrNull()}',
    );
    return screenShareTrackResult;
  }

  _startListeningToScreenShareStarted(
    screenShareTrackResult.getDataOrNull()!,
  );

  // Add timeout to clean up if broadcast never starts
  _screenSharingStartedOperation = CancelableOperation.fromFuture(
    awaitNativeWebRtcEvent<ScreenSharingStartedEvent>()
        .timeout(
          const Duration(seconds: 30),
          onTimeout: () {
            _logger.w(() => '[setScreenShareEnabled] broadcast start timeout');
            _screenSharingStartedSubscription?.cancel();
            throw TimeoutException('Broadcast picker timeout');
          },
        ),
  );

  return Future.value(
    Result.success(screenShareTrackResult.getDataOrNull()!),
  );
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9257fcc and d2383f6.

📒 Files selected for processing (6)
  • packages/stream_video/lib/src/call/call.dart (11 hunks)
  • packages/stream_video/lib/src/call/call_connect_options.dart (2 hunks)
  • packages/stream_video/lib/src/webrtc/rtc_manager.dart (9 hunks)
  • packages/stream_video/lib/src/webrtc/rtc_media_device/rtc_media_device_notifier.dart (2 hunks)
  • packages/stream_video_flutter/ios/Classes/StreamVideoFlutterPlugin.swift (2 hunks)
  • packages/stream_video_flutter/lib/src/call_background/background_service.dart (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stream_video/lib/src/webrtc/rtc_media_device/rtc_media_device_notifier.dart
🧰 Additional context used
🧬 Code graph analysis (1)
packages/stream_video_flutter/ios/Classes/StreamVideoFlutterPlugin.swift (1)
packages/stream_video_flutter/ios/Classes/ScreenShare/ScreenShareManager.swift (1)
  • observeNotification (24-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: analyze_legacy_version
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: stream_video_push_notification
  • GitHub Check: stream_video_flutter
  • GitHub Check: stream_video
  • GitHub Check: build
  • GitHub Check: analyze
🔇 Additional comments (10)
packages/stream_video_flutter/ios/Classes/StreamVideoFlutterPlugin.swift (2)

6-7: LGTM! Clear notification identifiers.

The static constants follow proper reverse-domain naming conventions for Darwin notifications and will be used consistently throughout the notification handling code.


20-35: LGTM! Threading concern from previous review is now resolved.

The notification observer setup is correct, and the callback handler properly dispatches to the main thread (line 23) before posting events to the Flutter plugin. This ensures thread-safe interaction with Flutter's method channel, addressing the concern raised in the previous review.

The use of optional chaining for FlutterWebRTCPlugin.sharedSingleton()?.postEvent(...) provides safe nil handling, and the switch statement correctly maps Darwin notifications to corresponding Flutter events.

packages/stream_video/lib/src/call/call_connect_options.dart (1)

114-151: LGTM! Constraint-aware TrackEnabled implementation

The changes correctly make TrackEnabled constraint-aware by:

  • Adding an optional constraints parameter to the factory
  • Storing constraints as a field
  • Including constraints in equality comparison via props
  • Updating toString() for better debugging

This allows different track enablement states to carry their specific constraints, which aligns well with the enhanced screen sharing and track management flow described in the PR.

packages/stream_video/lib/src/webrtc/rtc_manager.dart (2)

1223-1229: LGTM! Screen share track recreation logic

The decision to always create a new screen share track when enabling is well-justified by the comment. This ensures:

  • Correct constraint handling on iOS
  • Support for selecting different screens on web/desktop platforms

1345-1367: LGTM! Clean event awaiter and listener implementations

Both methods are well-implemented:

  • awaitNativeWebRtcEvent uses the .take(1).first pattern, which auto-cancels the subscription and avoids manual Completer management (addresses past review feedback).
  • _startListeningToScreenShareStarted properly cancels any previous subscription before creating a new one, preventing leaks.
packages/stream_video/lib/src/call/call.dart (5)

498-510: LGTM! ScreenSharingStartedEvent handling

The event handling correctly:

  • Updates participant state to reflect screen sharing is enabled
  • Updates connect options with iOS broadcast extension constraints
  • Aligns with the native-event-driven flow for iOS broadcast extensions

1964-1979: LGTM! Consistent constraint extraction pattern

The constraint extraction across all three apply methods follows a safe and consistent pattern:

  1. Check if constraints are of the expected type
  2. Cast if present, or use defaults
  3. Pass to the enable method

This properly integrates with the constraint-aware TrackEnabled state introduced in call_connect_options.dart.

Also applies to: 1989-1992, 2003-2019


2036-2049: LGTM! Constraint propagation in track setting

When setting a local track, the code correctly propagates the track's media constraints to the corresponding enable method (lines 2036-2048). This ensures the track's constraints are reflected in the call's connect options and participant state.


2829-2831: LGTM! TrackOption state updates with constraints

All three enable methods (setCameraEnabled, setMicrophoneEnabled, setScreenShareEnabled) correctly update _connectOptions with constraint-aware TrackOption instances. This maintains consistency between the actual track configuration and the stored connect options.

Also applies to: 2893-2895, 2946-2948


2932-2938: LGTM! iOS broadcast extension flow

The early return when using iOS broadcast extension is correct. Instead of immediately updating state, the code waits for the ScreenSharingStartedEvent (handled at lines 498-510), which provides a more accurate representation of when the broadcast actually starts.

Copy link

@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: 1

🧹 Nitpick comments (1)
packages/stream_video/lib/src/webrtc/rtc_manager.dart (1)

1351-1366: Auto-cancel after first start event and handle publish failures

Listener should cancel after first event to avoid duplicate publishes and should log failures.

Apply:

 void _startListeningToScreenShareStarted(
   RtcLocalTrack<ScreenShareConstraints> track,
 ) {
   _screenSharingStartedSubscription?.cancel();
-  _screenSharingStartedSubscription = RtcMediaDeviceNotifier.instance
-      .nativeWebRtcEventsStream()
-      .whereType<ScreenSharingStartedEvent>()
-      .listen((event) async {
-        _logger.i(() => '[ScreenSharingStartedEvent] received: $event');
-
-        await publishVideoTrack(
-          track: track,
-        );
-      });
+  _screenSharingStartedSubscription = RtcMediaDeviceNotifier.instance
+      .nativeWebRtcEventsStream()
+      .whereType<ScreenSharingStartedEvent>()
+      .take(1)
+      .listen((event) async {
+        _logger.i(() => '[ScreenSharingStartedEvent] received: $event');
+        final res = await publishVideoTrack(track: track);
+        if (res is Failure) {
+          _logger.e(() =>
+              '[ScreenSharingStartedEvent] publish failed: ${res.getErrorOrNull()}');
+        }
+      }, onError: (e, stk) {
+        _logger.e(() => '[ScreenSharingStartedEvent] stream error: $e');
+      });
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d2383f6 and 6fb1db6.

📒 Files selected for processing (1)
  • packages/stream_video/lib/src/webrtc/rtc_manager.dart (9 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: stream_video
  • GitHub Check: stream_video_push_notification
  • GitHub Check: stream_video_flutter
  • GitHub Check: analyze
  • GitHub Check: build
  • GitHub Check: analyze_legacy_version
🔇 Additional comments (6)
packages/stream_video/lib/src/webrtc/rtc_manager.dart (6)

109-111: Good: dedicated subscription field for screen-share start events

Field addition looks correct and scoped. Lifecycle handled below in dispose.


434-439: Good: cancel subscription on dispose

Cancelling and nulling _screenSharingStartedSubscription prevents leaks.


1221-1224: Re-enabling screen share creates a new track; verify stale entry/ID behavior

Creating a new track when one exists is fine for constraints/picker UX, but ensure the old entry in tracks doesn’t linger (trackId stability matters). If trackIds change per creation, consider removing the stale entry after successful publish to avoid duplicates.

Suggested follow-up (post-publish cleanup):

-      if (enabled && track is RtcLocalScreenShareTrack) {
+      if (enabled && track is RtcLocalScreenShareTrack) {
         // Always create a new screen share track when enabling screen sharing.
         // This ensures correct handling of constraints on iOS and supports selecting different screens on web and desktop platforms.
-        return _createAndPublishTrack(
+        final res = await _createAndPublishTrack(
           trackType: trackType,
           constraints: constraints,
         );
+        // If a new trackId is used, remove the stale entry.
+        res.fold(
+          success: (it) {
+            if (it.data.trackId != track.trackId) {
+              tracks.remove(track.trackId);
+            }
+          },
+          failure: (_) {},
+        );
+        return res;
       }

1343-1349: LGTM: minimal and leak-free awaiter

awaitNativeWebRtcEvent<T>() via whereType<T>().take(1).first is concise and auto-cancels.


1175-1210: Ensure cancellation/stop on system UI cancel (stopped event path)

If the user cancels from the system UI, confirm there’s a listener for a native “stopped/cancelled” event that mutes/unpublishes the screen-share track (and cancels any pending “started” subscription).

Would you like me to add a mirrored handler for ScreenSharingStoppedEvent that:

  • cancels _screenSharingStartedSubscription,
  • calls muteTrack/unpublishTrack for screen share (and audio),
  • and updates state/logs?

Also applies to: 1351-1366


1-7: Clean up unused imports; whereType is properly available

  • Remove CancelableOperation import (line 3): not used in this file.
  • Remove utils/future.dart import (line 22): not used in this file.
  • whereType<T>() is available from the current RxDart import and works correctly throughout the codebase.
-import 'package:async/async.dart' show CancelableOperation;
-import '../utils/future.dart';

@Brazol Brazol merged commit 1769457 into main Oct 29, 2025
14 of 15 checks passed
@Brazol Brazol deleted the fix/screen-share-cancelation-handling branch October 29, 2025 10:04
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.

3 participants