Skip to content

feat: Audio device module level mute channel method added - #79

Open
Brazol wants to merge 8 commits into
mainfrom
feat/adm-level-mute
Open

feat: Audio device module level mute channel method added#79
Brazol wants to merge 8 commits into
mainfrom
feat/adm-level-mute

Conversation

@Brazol

@Brazol Brazol commented Jul 24, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features
    • Added iOS/macOS APIs to set and query ADM-level microphone mute without stopping the audio engine.
    • When applicable, supports “speaking while muted” activity detection.
    • Added capability reporting for ADM microphone mute support (unsupported on web).
  • Bug Fixes
    • Improved reliability by serializing ADM microphone/recording operations per factory.
    • Made audio suspend/resume idempotent to avoid audio getting stuck inactive.
    • Added clearer fail-fast errors when required audio hardware is unavailable.
  • Documentation
    • Clarified stereo playout interactions with muted-speaker activity and voice processing bypass behavior.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ADM-level microphone mute APIs, Apple-specific serial ADM queues, and idempotent audio suspend/resume handling. Web stubs expose unsupported APIs, while iOS/macOS handlers validate ADM availability and marshal recording, mute, stereo, and lifecycle operations through each factory’s queue.

Changes

Microphone mute control

Layer / File(s) Summary
Dart microphone mute API
lib/src/native/native_peer_connection_factory.dart, lib/src/web/native_peer_connection_factory.dart, lib/src/helper.dart, lib/src/native/ios/audio_management.dart, CHANGELOG.md
Adds platform support detection, mute setter/query APIs, web unsupported stubs, and documentation for mute behavior alongside muted-talker detection and stereo playout constraints.
Apple ADM operation queue
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/.../NativePeerConnectionFactory.h, ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m, macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/.../NativePeerConnectionFactory.h, macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m, CHANGELOG.md
Adds per-factory serial ADM queues, audioSuspended state, and queued ADM shutdown to order audio operations.
Native ADM handlers
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m, macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
Routes recording, mute, voice-processing, stereo, refresh, suspend, and resume operations through admQueue, validates missing ADM instances, and applies idempotent suspension state handling.

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

Sequence Diagram(s)

sequenceDiagram
  participant Dart
  participant MethodChannel
  participant NativePeerConnectionFactory
  participant RTCAudioDeviceModule
  Dart->>MethodChannel: invoke appleAdmSetMicrophoneMuted with factoryId and muted
  MethodChannel->>NativePeerConnectionFactory: resolve factory and enqueue request
  NativePeerConnectionFactory->>RTCAudioDeviceModule: set microphone mute state
  RTCAudioDeviceModule-->>NativePeerConnectionFactory: return ADM result
  NativePeerConnectionFactory-->>Dart: return success or error
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding ADM-level mute support and a new method-channel API.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/adm-level-mute

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

🤖 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
`@android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java`:
- Around line 1585-1597: Update the admSetMicrophoneMuted executor task to use
the same lifecycle serialization/guard as disposePeerConnectionFactoryHandler,
preventing adm.setMicrophoneMute from running after factory removal or racing
with nf.dispose()/adm.release(). Wrap the guarded ADM operation in try/catch and
post result.error with the caught failure on mainHandler; post
result.success(null) only after a successful mute operation.
🪄 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 Plus

Run ID: f4b87652-5afc-481a-a19d-b4a805c27b49

📥 Commits

Reviewing files that changed from the base of the PR and between d7098ba and ccbe9e0.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • lib/src/native/native_peer_connection_factory.dart
  • lib/src/web/native_peer_connection_factory.dart
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m

Comment thread android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java Outdated

@renefloor renefloor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Some minor things:

Issues

  1. iOS/macOS: rapid toggles can be applied out of order � FlutterWebRTCPlugin.m:1970 (ios), :1729 (macos)

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) is a concurrent queue, so two blocks have no ordering guarantee. setMicrophoneMuted(true) immediately followed by setMicrophoneMuted(false) (push-to-talk, a fast UI toggle, a debounce that fires twice) can land in reverse � and the mic then stays in the wrong state indefinitely, with both Dart futures resolving successfully. Android is not affected: executor is Executors.newSingleThreadExecutor() (MethodCallHandlerImpl.java:138), so calls are serialized there.

A mute toggle is much more order-sensitive than the start/stop-recording handlers this was copied from. Worth a dedicated serial queue for ADM mute ops:

// file scope
static dispatch_queue_t kADMMuteQueue; // dispatch_queue_create("�adm.mute", DISPATCH_QUEUE_SERIAL)

  1. iOS/macOS: a nil ADM reports success � FlutterWebRTCPlugin.m:1968

audioDeviceModule is declared nullable in NativePeerConnectionFactory.h:21, and the plugin nil-checks it elsewhere (FlutterWebRTCPlugin.m:2107, :2120). Here, [nil setMicrophoneMuted:] returns 0, which the handler reads as success � so result(nil) fires and the caller believes the mic is muted while nothing happened. For a privacy-facing mute, silent-success is the wrong failure mode; this should be a FlutterError. Android is fine � nf.adm is @nonnull final.

  1. Is the mute state re-applied after the audio unit is rebuilt? (worth verifying before merge)

Nothing stores the requested mute state, so any path that tears down and rebuilds the VPIO unit drops it. Concretely, suspendAudioPeerConnectionFactory � resumeAudioPeerConnectionFactory (FlutterWebRTCPlugin.m:1990�2035) stops recording and calls initRecording/startRecording again; audio route changes may do the same internally. If the unit property doesn't survive that, the user ends up with a hot mic after a suspend/resume cycle or a route change � a bad failure direction. Android is safe here, since setMicrophoneMute sets a sticky flag on WebRtcAudioRecord rather than a hardware property.

If it doesn't survive, the fix fits the existing pattern well � NativePeerConnectionFactory already carries wasPlayingBeforeSuspend / wasRecordingBeforeSuspend for exactly this kind of restore, so a microphoneMuted property re-applied after startRecording would slot right in.

  1. No way to read the state back

There's no isMicrophoneMuted getter, so Dart can't detect drift if #3 turns out to be real.
isVoiceProcessingEnabled / isStereoPlayouta read-side handler. Not a blocker, butcheap to add alongside.


Minor

  • muted.boolValue on a nil NSNumber (Fluttently yields NO. Not reachable through theDart API, but the channel is directly callable, and Android is explicit about it (muted != null && muted). An explicit nil â�� error would match.

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

🤖 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
`@ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m`:
- Around line 2033-2037: Capture suspend/resume state only within the serialized
audio-manager queue blocks. In
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
lines 2033-2037, read adm.isPlaying/isRecording and assign
nf.wasPlayingBeforeSuspend/nf.wasRecordingBeforeSuspend inside the suspend
block; at lines 2064-2070, read those restoration flags and apply audio
configuration inside the resume block. Apply the equivalent changes in
macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
lines 1804-1808 and 1835-1837, using the suspend/resume queue blocks and
preserving existing behavior.
🪄 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 Plus

Run ID: 5bf7b478-360c-404e-89a6-c9022adad3e5

📥 Commits

Reviewing files that changed from the base of the PR and between ccbe9e0 and 97ca00a.

📒 Files selected for processing (6)
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h

Comment thread ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m Outdated

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

♻️ Duplicate comments (1)
android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java (1)

1585-1599: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Serialize mute operations with factory disposal and always complete the result.

The queued task can call adm.setMicrophoneMute after disposePeerConnectionFactoryHandler has removed the factory and nf.dispose() has released its ADM. Also, any exception escapes the executor task, so Dart receives neither success nor an error and the returned Future can hang.

Run the mute operation and nf.dispose()/adm.release() under the same executor or lifecycle lock, and catch failures to post result.error(...) on the main thread. This is the same unresolved issue raised in the previous review.

🤖 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/MethodCallHandlerImpl.java`
around lines 1585 - 1599, Update the admSetMicrophoneMuted handler and the
corresponding disposePeerConnectionFactoryHandler lifecycle path to serialize
mute changes with factory disposal and ADM release using the same executor or
lifecycle lock. Ensure the queued mute task cannot access a disposed
NativePeerConnectionFactory or JavaAudioDeviceModule, and catch operation
failures so the main-thread callback always completes with result.error(...)
instead of leaving the Future unresolved.
🤖 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.

Duplicate comments:
In
`@android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java`:
- Around line 1585-1599: Update the admSetMicrophoneMuted handler and the
corresponding disposePeerConnectionFactoryHandler lifecycle path to serialize
mute changes with factory disposal and ADM release using the same executor or
lifecycle lock. Ensure the queued mute task cannot access a disposed
NativePeerConnectionFactory or JavaAudioDeviceModule, and catch operation
failures so the main-thread callback always completes with result.error(...)
instead of leaving the Future unresolved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7eb569fe-bb0f-4c58-8d15-fbc4e6557693

📥 Commits

Reviewing files that changed from the base of the PR and between 97ca00a and d7169b2.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java
  • android/src/main/java/io/getstream/webrtc/flutter/NativePeerConnectionFactory.java
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • lib/src/native/native_peer_connection_factory.dart
  • lib/src/web/native_peer_connection_factory.dart
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
🚧 Files skipped from review as they are similar to previous changes (3)
  • lib/src/web/native_peer_connection_factory.dart
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m

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

🤖 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
`@macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m`:
- Around line 1753-1758: Update the muted validation in the
appleAdmSetMicrophoneMuted handler to require a non-nil CFBoolean value using
CFBooleanGetTypeID(), rather than accepting any NSNumber. Preserve the existing
appleAdmSetMicrophoneMuted FlutterError response and return for invalid
payloads, while allowing only true/false values to reach muted.boolValue.
🪄 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 Plus

Run ID: 4998f8d0-a40b-4193-858f-87fa00b29676

📥 Commits

Reviewing files that changed from the base of the PR and between b64c2cd and 0182341.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
  • lib/src/native/native_peer_connection_factory.dart
  • lib/src/web/native_peer_connection_factory.dart
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
🚧 Files skipped from review as they are similar to previous changes (4)
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
  • lib/src/web/native_peer_connection_factory.dart
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m

@Brazol

Brazol commented Jul 30, 2026

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

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

🧹 Nitpick comments (2)
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h (1)

60-63: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

audioSuspended is publicly mutable with no stated threading contract. The suspend/resume flags are only safe when accessed from admQueue, but neither header says so.

  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h#L60-L63: extend the doc comment to state that this flag must be read/written on admQueue.
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h#L60-L63: mirror the same doc note.
🤖 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/include/stream_webrtc_flutter/NativePeerConnectionFactory.h`
around lines 60 - 63, The audioSuspended property documentation lacks its
threading contract. Update the doc comment in
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
lines 60-63 and mirror the same note in
macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
lines 60-63, stating that audioSuspended must be read and written on admQueue.
lib/src/native/native_peer_connection_factory.dart (1)

234-241: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Doc note about ambiguous false no longer matches the native handler.

The iOS/macOS handlers (appleAdmIsMicrophoneMuted) now return a FlutterError when the ADM is missing rather than false, so an unqueryable ADM surfaces as a thrown error, not false. Consider dropping or rewording the "cannot distinguish" paragraph so callers don't over-defend against a case that no longer occurs.

🤖 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/native/native_peer_connection_factory.dart` around lines 234 - 241,
Update the documentation for the native microphone mute readback method around
setMicrophoneMuted/appleAdmIsMicrophoneMuted to remove or reword the paragraph
claiming that an unavailable ADM is reported as false. Document that missing or
unqueryable ADM state surfaces as a thrown FlutterError, while false represents
the native handler’s non-muted result.
🤖 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
`@ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m`:
- Around line 2112-2115: Move the audioConfigSnapshot handling into the admQueue
block that restores audio state, and guard both setAppleAudioConfiguration: and
the restore operations with nf.audioSuspended. Ensure configuration occurs
immediately before restoration within the same queued block, and does not run
when no audio was suspended.

In
`@ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m`:
- Around line 100-107: Replace the blocking dispatch_sync teardown around adm in
NativePeerConnectionFactory.m with a deadlock-safe approach: either assert the
ADM queue-specific context before synchronous execution or dispatch the
stopRecording/stopPlayout block asynchronously while retaining adm. Apply the
identical change at
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
lines 100-107 and
macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
lines 95-102, preserving exception handling.

---

Nitpick comments:
In
`@ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h`:
- Around line 60-63: The audioSuspended property documentation lacks its
threading contract. Update the doc comment in
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
lines 60-63 and mirror the same note in
macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
lines 60-63, stating that audioSuspended must be read and written on admQueue.

In `@lib/src/native/native_peer_connection_factory.dart`:
- Around line 234-241: Update the documentation for the native microphone mute
readback method around setMicrophoneMuted/appleAdmIsMicrophoneMuted to remove or
reword the paragraph claiming that an unavailable ADM is reported as false.
Document that missing or unqueryable ADM state surfaces as a thrown
FlutterError, while false represents the native handler’s non-muted result.
🪄 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 Plus

Run ID: 3002f542-de63-4bf6-b4f2-c1c30ca29cc2

📥 Commits

Reviewing files that changed from the base of the PR and between d54ec38 and 94039f5.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h
  • lib/src/helper.dart
  • lib/src/native/ios/audio_management.dart
  • lib/src/native/native_peer_connection_factory.dart
  • lib/src/web/native_peer_connection_factory.dart
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterWebRTCPlugin.m
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/include/stream_webrtc_flutter/NativePeerConnectionFactory.h

Comment on lines 2112 to 2115
NSDictionary* audioConfigSnapshot = nf.audioConfigSnapshot;
BOOL restorePlaying = nf.wasPlayingBeforeSuspend;
BOOL restoreRecording = nf.wasRecordingBeforeSuspend;
if (audioConfigSnapshot != nil) {
[AudioUtils setAppleAudioConfiguration:audioConfigSnapshot];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Audio-session reconfiguration still runs outside admQueue and even when nothing was suspended.

setAppleAudioConfiguration: executes on the calling thread ahead of the queued restore, so it is not ordered against pending ADM work, and it runs unconditionally — including when nf.audioSuspended is NO and the block restores nothing. Move it inside the queued block under the nf.audioSuspended check so configuration and restore stay atomic and idempotent.

🔧 Proposed fix
-    NSDictionary* audioConfigSnapshot = nf.audioConfigSnapshot;
-    if (audioConfigSnapshot != nil) {
-      [AudioUtils setAppleAudioConfiguration:audioConfigSnapshot];
-    }
     dispatch_async(nf.admQueue, ^{
       if (nf.audioSuspended) {
+        NSDictionary* audioConfigSnapshot = nf.audioConfigSnapshot;
+        if (audioConfigSnapshot != nil) {
+          [AudioUtils setAppleAudioConfiguration:audioConfigSnapshot];
+        }
         BOOL restorePlaying = nf.wasPlayingBeforeSuspend;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NSDictionary* audioConfigSnapshot = nf.audioConfigSnapshot;
BOOL restorePlaying = nf.wasPlayingBeforeSuspend;
BOOL restoreRecording = nf.wasRecordingBeforeSuspend;
if (audioConfigSnapshot != nil) {
[AudioUtils setAppleAudioConfiguration:audioConfigSnapshot];
}
dispatch_async(nf.admQueue, ^{
if (nf.audioSuspended) {
NSDictionary* audioConfigSnapshot = nf.audioConfigSnapshot;
if (audioConfigSnapshot != nil) {
[AudioUtils setAppleAudioConfiguration:audioConfigSnapshot];
}
BOOL restorePlaying = nf.wasPlayingBeforeSuspend;
🤖 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/FlutterWebRTCPlugin.m`
around lines 2112 - 2115, Move the audioConfigSnapshot handling into the
admQueue block that restores audio state, and guard both
setAppleAudioConfiguration: and the restore operations with nf.audioSuspended.
Ensure configuration occurs immediately before restoration within the same
queued block, and does not run when no audio was suspended.

Comment on lines +100 to +107
dispatch_sync(_admQueue, ^{
@try {
[adm stopRecording];
[adm stopPlayout];
} @catch (NSException* e) {
NSLog(@"[NativePeerConnectionFactory] stopRecording/stopPlayout failed: %@", e);
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Blocking dispatch_sync on the factory's own serial ADM queue during dispose. Both platform implementations tail the ADM queue synchronously to run the final stops, which self-deadlocks if teardown is ever reached from ADM-queue work (observer callback, completion hop, or a future refactor).

  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m#L100-L107: guard with a queue-specific key assertion, or enqueue the stops via dispatch_async while the block retains adm.
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m#L95-L102: apply the identical change.
📍 Affects 2 files
  • ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m#L100-L107 (this comment)
  • macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m#L95-L102
🤖 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/NativePeerConnectionFactory.m`
around lines 100 - 107, Replace the blocking dispatch_sync teardown around adm
in NativePeerConnectionFactory.m with a deadlock-safe approach: either assert
the ADM queue-specific context before synchronous execution or dispatch the
stopRecording/stopPlayout block asynchronously while retaining adm. Apply the
identical change at
ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
lines 100-107 and
macos/stream_webrtc_flutter/Sources/stream_webrtc_flutter/NativePeerConnectionFactory.m
lines 95-102, preserving exception handling.

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