Skip to content

[Fix] Prevent AVAudioEngine crash when enabling recording - #103

Merged
ipavlidakis merged 2 commits into
developfrom
iliaspavlidakis/ios-1922-avaudioengine-crash
Jul 28, 2026
Merged

[Fix] Prevent AVAudioEngine crash when enabling recording#103
ipavlidakis merged 2 commits into
developfrom
iliaspavlidakis/ios-1922-avaudioengine-crash

Conversation

@ipavlidakis

@ipavlidakis ipavlidakis commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix IOS-1922 by recreating AVAudioEngine when the input topology or voice-processing state changes.
  • Convert setVoiceProcessingEnabled errors and Objective-C exceptions into a recoverable audio-device error.
  • Retry voice-processing configuration for three total attempts, using a fresh and fully prepared engine each time.
  • Restore the previous stable speaker-only state if voice processing cannot be configured.
  • Add device-only regression coverage for repeatedly enabling recording while playout remains active.

Impact

  • Voice processing and AGC remain mandatory; recording never starts with them disabled.
  • Recovery is bounded to two delayed retries, adding at most 200 ms before failure.
  • No public API changes.

Validation

  • git diff --check
  • ninja -C src/out/verify_ios_stream_on obj/modules/audio_device/audio_device_impl/audio_engine_device.o
  • ninja -C src/out/ios_1922_check obj/modules/audio_device/audio_device_impl/audio_engine_device.o
  • ninja -C src/out/ios_1922_tests obj/sdk/sdk_unittests_sources/RTCPeerConnectionFactoryAudioEngine_xctest.o
  • Run the regression test on a physical iOS device; simulator audio tests skip the hardware-dependent path.

Summary by CodeRabbit

  • New Features
    • Added APIs to set and query audio input and output availability.
    • Exposed audio engine availability through the Objective-C interface.
  • Bug Fixes
    • Improved voice-processing recovery with bounded retries when configuration fails.
    • Prevented failed voice-processing transitions from leaving the engine in an unstable state.
    • Preserved voice processing when enabling recording during active playback.
  • Tests
    • Added coverage for repeated recording activation while playback is running.

@ipavlidakis ipavlidakis self-assigned this Jul 28, 2026
@ipavlidakis ipavlidakis added bug Something isn't working iOS labels Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 77697a3d-1cf0-447a-974b-fc686d83cac7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

The audio engine now tracks input/output availability, exposes native and Objective-C availability APIs, gates engine state queries, and logs availability. Voice-processing configuration now propagates failures through bounded engine recreation retries, rollback, and recovery. An iOS XCTest covers repeated recording transitions while playout and voice processing remain active.

Audio engine availability and public bindings

Layer / File(s) Summary
Availability state and API bridge
modules/audio_device/audio_engine_device.*, sdk/objc/api/peerconnection/RTCAudioDeviceModule.*
Availability fields gate engine state, native setter/getter APIs store and query them, and Objective-C methods expose the same controls with readiness checks and worker-thread forwarding.

Voice-processing recovery

Layer / File(s) Summary
Bounded engine recreation and validation
modules/audio_device/audio_engine_device.*, sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm
Voice-processing failures now return errors, recreate and prepare AVAudioEngine instances between bounded retries, restore stable state after exhaustion, and are covered by repeated recording-transition assertions.

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

Sequence Diagram(s)

sequenceDiagram
  participant RTCAudioDeviceModule
  participant WorkerThread
  participant AudioEngineDevice
  participant AVAudioEngine
  RTCAudioDeviceModule->>WorkerThread: setEngineAvailability(input, output)
  WorkerThread->>AudioEngineDevice: SetEngineAvailability(input, output)
  AudioEngineDevice-->>WorkerThread: availability result
  AudioEngineDevice->>AVAudioEngine: configure voice processing
  AVAudioEngine-->>AudioEngineDevice: success or native failure
  AudioEngineDevice->>AVAudioEngine: release and recreate for bounded retry
Loading

Possibly related PRs

  • GetStream/webrtc#97: Both changes reattach muted speech-activity listeners during iOS voice-processing engine handling.
  • GetStream/webrtc#101: Both add input/output engine availability controls to AudioEngineDevice.
  • GetStream/webrtc#102: Both update availability-gated engine state and expose native availability APIs.

Suggested reviewers: santhoshvai, martinmitrevski

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: avoiding AVAudioEngine crashes when recording starts.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch iliaspavlidakis/ios-1922-avaudioengine-crash

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.

@ipavlidakis
ipavlidakis changed the base branch from main to develop July 28, 2026 11:14
@ipavlidakis ipavlidakis changed the title iliaspavlidakis/ios-1922-avaudioengine-crash [Fix] Prevent AVAudioEngine crash when enabling recording Jul 28, 2026

@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 (4)
modules/audio_device/audio_engine_device.h (1)

421-425: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

input_topology also fires on every MuteMode::RestartEngine mute toggle.

IsInputEnabled() returns false when mute_mode == RestartEngine && input_muted, so a mute/unmute in that mode flips prev.IsInputEnabled() != next.IsInputEnabled() while output stays enabled. Those transitions previously took the cheaper restart path; they now tear down and rebuild the whole graph (plus, on failure, the new retry/recovery path). If only genuine record enable/disable should recreate, consider keying on input_enabled/input_enabled_persistent_mode rather than the mute-aware helper.

♻️ Possible narrowing
-      bool input_topology = prev.IsOutputEnabled() && next.IsOutputEnabled() &&
-          prev.IsInputEnabled() != next.IsInputEnabled();
+      bool input_topology = prev.IsOutputEnabled() && next.IsOutputEnabled() &&
+          (prev.input_enabled != next.input_enabled ||
+           prev.input_available != next.input_available);
🤖 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 `@modules/audio_device/audio_engine_device.h` around lines 421 - 425, Update
the input_topology condition to detect only genuine input enablement changes,
using the persistent input configuration fields such as input_enabled or
input_enabled_persistent_mode instead of mute-aware IsInputEnabled(). Preserve
recreation for record enable/disable while keeping RestartEngine mute/unmute
transitions on the existing cheaper restart path.
sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm (2)

199-208: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Loop does not assert the regression's distinguishing signal.

The iteration only checks recording/playing/VP flags, which would also pass if VP recovery silently fell back. Consider asserting audioDeviceModule.isEngineRunning and, since VP is mandatory per the PR objectives, that initAndStartRecording never returns the VP error code, so a degraded-but-running graph still fails the test.

🤖 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 `@sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm` around
lines 199 - 208, Strengthen the loop in the audio engine regression test by
asserting audioDeviceModule.isEngineRunning after initAndStartRecording, and
verify that initAndStartRecording never returns the VP error code. Keep the
existing recording, voice-processing, stop, and playing assertions so degraded
VP fallback is detected while preserving the current lifecycle checks.

178-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test mutates the shared AVAudioSession without restoring it, and re-asserts a stale sessionError.

The category/mode change to PlayAndRecord/voiceChat persists after this test and can affect the other tests in this class (execution order is not guaranteed). Also, sessionError is never reset between calls; Cocoa only guarantees the error out-param is meaningful when the call returns NO, so asserting XCTAssertNil(sessionError) after a successful call is not contract-guaranteed. Capture the previous category/mode in setUp/restore in tearDown (or addTeardownBlock:) and reset sessionError = nil before each call.

🤖 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 `@sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm` around
lines 178 - 216, Update the test setup and cleanup around the shared
AVAudioSession to capture its original category and mode in setUp (or an
addTeardownBlock) and restore them after the test, including deactivating the
session as needed. In the test’s AVAudioSession configuration calls, reset
sessionError to nil immediately before each setCategory, setActive, and
restoration call, then assert the result and error for that specific call.
modules/audio_device/audio_engine_device.mm (1)

2154-2155: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

100 ms usleep blocks the ADM control thread.

Each retry blocks thread_ synchronously (up to 200 ms here, more if the recovery path recurses, and macOS start already sleeps another 100 ms per start attempt). Since ModifyEngineState is reached via BlockingCall from the Objective-C layer, this stalls the caller too. Consider whether the delay can be shortened or the retry rescheduled via PostDelayedTask.

🤖 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 `@modules/audio_device/audio_engine_device.mm` around lines 2154 - 2155,
Replace the synchronous usleep delay in the AVAudioEngine recovery/retry path
with a non-blocking delayed task, such as PostDelayedTask, so ModifyEngineState
and its BlockingCall caller are not stalled. Preserve the existing retry
ordering and delay semantics while ensuring subsequent engine-state work runs
asynchronously on the appropriate thread; if rescheduling is not viable, reduce
the blocking delay to the minimum required interval.
🤖 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 `@modules/audio_device/audio_engine_device.mm`:
- Around line 2180-2201: Update the VP recovery path around recovery_state and
ApplyDeviceEngineState so recovery cannot re-enter the same voice-processing
retry block. Use an explicit recovery guard flag or construct recovery_state
with input/voice processing disabled while retaining the required speaker-only
state, then preserve the existing release and error-result handling.

In `@sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm`:
- Around line 586-605: Update the five RTCAudioEngineAvailability constructions
in engineAvailability to use braced aggregate initialization, replacing
parenthesized boolean arguments with {false, false} or {input_available,
output_available} as appropriate. Preserve the existing availability and
error-handling branches.

---

Nitpick comments:
In `@modules/audio_device/audio_engine_device.h`:
- Around line 421-425: Update the input_topology condition to detect only
genuine input enablement changes, using the persistent input configuration
fields such as input_enabled or input_enabled_persistent_mode instead of
mute-aware IsInputEnabled(). Preserve recreation for record enable/disable while
keeping RestartEngine mute/unmute transitions on the existing cheaper restart
path.

In `@modules/audio_device/audio_engine_device.mm`:
- Around line 2154-2155: Replace the synchronous usleep delay in the
AVAudioEngine recovery/retry path with a non-blocking delayed task, such as
PostDelayedTask, so ModifyEngineState and its BlockingCall caller are not
stalled. Preserve the existing retry ordering and delay semantics while ensuring
subsequent engine-state work runs asynchronously on the appropriate thread; if
rescheduling is not viable, reduce the blocking delay to the minimum required
interval.

In `@sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm`:
- Around line 199-208: Strengthen the loop in the audio engine regression test
by asserting audioDeviceModule.isEngineRunning after initAndStartRecording, and
verify that initAndStartRecording never returns the VP error code. Keep the
existing recording, voice-processing, stop, and playing assertions so degraded
VP fallback is detected while preserving the current lifecycle checks.
- Around line 178-216: Update the test setup and cleanup around the shared
AVAudioSession to capture its original category and mode in setUp (or an
addTeardownBlock) and restore them after the test, including deactivating the
session as needed. In the test’s AVAudioSession configuration calls, reset
sessionError to nil immediately before each setCategory, setActive, and
restoration call, then assert the result and error for that specific call.
🪄 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: fdd417bf-c9cd-48e8-9047-9dbee05ea03c

📥 Commits

Reviewing files that changed from the base of the PR and between 20df2b7 and 62430fc.

📒 Files selected for processing (5)
  • modules/audio_device/audio_engine_device.h
  • modules/audio_device/audio_engine_device.mm
  • sdk/objc/api/peerconnection/RTCAudioDeviceModule.h
  • sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm
  • sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm

Comment thread modules/audio_device/audio_engine_device.mm

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🧹 Nitpick comments (4)
modules/audio_device/audio_engine_device.h (1)

421-425: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

input_topology also fires on every MuteMode::RestartEngine mute toggle.

IsInputEnabled() returns false when mute_mode == RestartEngine && input_muted, so a mute/unmute in that mode flips prev.IsInputEnabled() != next.IsInputEnabled() while output stays enabled. Those transitions previously took the cheaper restart path; they now tear down and rebuild the whole graph (plus, on failure, the new retry/recovery path). If only genuine record enable/disable should recreate, consider keying on input_enabled/input_enabled_persistent_mode rather than the mute-aware helper.

♻️ Possible narrowing
-      bool input_topology = prev.IsOutputEnabled() && next.IsOutputEnabled() &&
-          prev.IsInputEnabled() != next.IsInputEnabled();
+      bool input_topology = prev.IsOutputEnabled() && next.IsOutputEnabled() &&
+          (prev.input_enabled != next.input_enabled ||
+           prev.input_available != next.input_available);
🤖 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 `@modules/audio_device/audio_engine_device.h` around lines 421 - 425, Update
the input_topology condition to detect only genuine input enablement changes,
using the persistent input configuration fields such as input_enabled or
input_enabled_persistent_mode instead of mute-aware IsInputEnabled(). Preserve
recreation for record enable/disable while keeping RestartEngine mute/unmute
transitions on the existing cheaper restart path.
sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm (2)

199-208: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Loop does not assert the regression's distinguishing signal.

The iteration only checks recording/playing/VP flags, which would also pass if VP recovery silently fell back. Consider asserting audioDeviceModule.isEngineRunning and, since VP is mandatory per the PR objectives, that initAndStartRecording never returns the VP error code, so a degraded-but-running graph still fails the test.

🤖 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 `@sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm` around
lines 199 - 208, Strengthen the loop in the audio engine regression test by
asserting audioDeviceModule.isEngineRunning after initAndStartRecording, and
verify that initAndStartRecording never returns the VP error code. Keep the
existing recording, voice-processing, stop, and playing assertions so degraded
VP fallback is detected while preserving the current lifecycle checks.

178-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test mutates the shared AVAudioSession without restoring it, and re-asserts a stale sessionError.

The category/mode change to PlayAndRecord/voiceChat persists after this test and can affect the other tests in this class (execution order is not guaranteed). Also, sessionError is never reset between calls; Cocoa only guarantees the error out-param is meaningful when the call returns NO, so asserting XCTAssertNil(sessionError) after a successful call is not contract-guaranteed. Capture the previous category/mode in setUp/restore in tearDown (or addTeardownBlock:) and reset sessionError = nil before each call.

🤖 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 `@sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm` around
lines 178 - 216, Update the test setup and cleanup around the shared
AVAudioSession to capture its original category and mode in setUp (or an
addTeardownBlock) and restore them after the test, including deactivating the
session as needed. In the test’s AVAudioSession configuration calls, reset
sessionError to nil immediately before each setCategory, setActive, and
restoration call, then assert the result and error for that specific call.
modules/audio_device/audio_engine_device.mm (1)

2154-2155: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

100 ms usleep blocks the ADM control thread.

Each retry blocks thread_ synchronously (up to 200 ms here, more if the recovery path recurses, and macOS start already sleeps another 100 ms per start attempt). Since ModifyEngineState is reached via BlockingCall from the Objective-C layer, this stalls the caller too. Consider whether the delay can be shortened or the retry rescheduled via PostDelayedTask.

🤖 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 `@modules/audio_device/audio_engine_device.mm` around lines 2154 - 2155,
Replace the synchronous usleep delay in the AVAudioEngine recovery/retry path
with a non-blocking delayed task, such as PostDelayedTask, so ModifyEngineState
and its BlockingCall caller are not stalled. Preserve the existing retry
ordering and delay semantics while ensuring subsequent engine-state work runs
asynchronously on the appropriate thread; if rescheduling is not viable, reduce
the blocking delay to the minimum required interval.
🤖 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 `@modules/audio_device/audio_engine_device.mm`:
- Around line 2180-2201: Update the VP recovery path around recovery_state and
ApplyDeviceEngineState so recovery cannot re-enter the same voice-processing
retry block. Use an explicit recovery guard flag or construct recovery_state
with input/voice processing disabled while retaining the required speaker-only
state, then preserve the existing release and error-result handling.

In `@sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm`:
- Around line 586-605: Update the five RTCAudioEngineAvailability constructions
in engineAvailability to use braced aggregate initialization, replacing
parenthesized boolean arguments with {false, false} or {input_available,
output_available} as appropriate. Preserve the existing availability and
error-handling branches.

---

Nitpick comments:
In `@modules/audio_device/audio_engine_device.h`:
- Around line 421-425: Update the input_topology condition to detect only
genuine input enablement changes, using the persistent input configuration
fields such as input_enabled or input_enabled_persistent_mode instead of
mute-aware IsInputEnabled(). Preserve recreation for record enable/disable while
keeping RestartEngine mute/unmute transitions on the existing cheaper restart
path.

In `@modules/audio_device/audio_engine_device.mm`:
- Around line 2154-2155: Replace the synchronous usleep delay in the
AVAudioEngine recovery/retry path with a non-blocking delayed task, such as
PostDelayedTask, so ModifyEngineState and its BlockingCall caller are not
stalled. Preserve the existing retry ordering and delay semantics while ensuring
subsequent engine-state work runs asynchronously on the appropriate thread; if
rescheduling is not viable, reduce the blocking delay to the minimum required
interval.

In `@sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm`:
- Around line 199-208: Strengthen the loop in the audio engine regression test
by asserting audioDeviceModule.isEngineRunning after initAndStartRecording, and
verify that initAndStartRecording never returns the VP error code. Keep the
existing recording, voice-processing, stop, and playing assertions so degraded
VP fallback is detected while preserving the current lifecycle checks.
- Around line 178-216: Update the test setup and cleanup around the shared
AVAudioSession to capture its original category and mode in setUp (or an
addTeardownBlock) and restore them after the test, including deactivating the
session as needed. In the test’s AVAudioSession configuration calls, reset
sessionError to nil immediately before each setCategory, setActive, and
restoration call, then assert the result and error for that specific call.
🪄 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: fdd417bf-c9cd-48e8-9047-9dbee05ea03c

📥 Commits

Reviewing files that changed from the base of the PR and between 20df2b7 and 62430fc.

📒 Files selected for processing (5)
  • modules/audio_device/audio_engine_device.h
  • modules/audio_device/audio_engine_device.mm
  • sdk/objc/api/peerconnection/RTCAudioDeviceModule.h
  • sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm
  • sdk/objc/unittests/RTCPeerConnectionFactoryAudioEngine_xctest.mm
🛑 Comments failed to post (1)
sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm (1)

586-605: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Determine the C++ standard used for the ObjC++ SDK targets.
rg -n --hidden -g '!*.md' -e 'cxx_std|-std=c\+\+|cflags_cc|std:c\+\+' BUILD.gn build_overrides/ build/config/compiler/BUILD.gn 2>/dev/null | head -50
fd -H -t f 'BUILD.gn' sdk/objc | head

Repository: GetStream/webrtc

Length of output: 217


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate relevant source/header/build files =="
fd -H -t f 'RTCAudioEngineAvailability|RTCAudioEngine' sdk/objc . 2>/dev/null | head -100
fd -H -t f 'BUILD.gn' sdk/objc . 2>/dev/null | head -100

echo
echo "== inspect SDK source/imports and availability definitions =="
rg -n --hidden -g '!*.md' -e 'RTCAudioEngineAvailability|AudioEngineAvailability|class RTCAudioEngineAvailability|struct RTCAudioEngineAvailability|typedef .*RTCAudioEngineAvailability|using RTCAudioEngineAvailability' . 2>/dev/null | head -200

echo
echo "== candidate files with line counts =="
for f in $(fd -H -t f 'RTCAudioEngineAvailability|RTCAudioEngine.*\.h|RTCAudioEngine.*\.mm' sdk/objc . 2>/dev/null); do
  echo "--- $f"
  wc -l "$f"
done

echo
echo "== inspect BUILD.gn cxx settings near matches =="
grep -n -C 2 'cflags_cc\|cxx_std\|-std=c++\|std:c++\|objc' BUILD.gn sdk -R 2>/dev/null | head -200

Repository: GetStream/webrtc

Length of output: 12863


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== inspect peerconnection header/implementation around availability =="
sed -n '45,90p' sdk/objc/api/peerconnection/RTCAudioDeviceModule.h
sed -n '540,615p' sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm

echo
echo "== inspect sdk objc common config =="
sed -n '45,95p' sdk/BUILD.gn

echo
echo "== inspect root config cflags/cc around lines 300-435 =="
sed -n '295,435p' BUILD.gn

echo
echo "== search for cxx_default / cxx17/14/20 in root and sdk build =="
rg -n --hidden -g '!*.md' -e 'cxx17|cxx14|cxx20|cxx_default|standard_library|target_cpu|cxx_std|c++17|c++14|c++20|RTCaudioEngineAvailability|RTCAudioEngineState' BUILD.gn sdk -R 2>/dev/null | head -200

echo
echo "== locate all aggregate T(...) style direct init for ObjC availability/flags structs =="
python3 - <<'PY'
import pathlib, re
for p in list(pathlib.Path("sdk/objc/api/peerconnection").glob("RTCAudioDeviceModule.h"))+list(pathlib.Path("sdk/objc/api/peerconnection").glob("RTCAudioDeviceModule.mm")):
    text=p.read_text()
    for m in re.finditer(r'\bRTC_OBJC_TYPE\((\w+)\)\(\s*\W?\s*\w+\s*,\s*\w+\s*\)\s*;?\s*', text):
        print(f"{p}:{text[:m.start()].count(chr(10))+1}: {m.group(0).replace(chr(10),' ')}")
PY

Repository: GetStream/webrtc

Length of output: 9933


Use braced aggregate initialization for RTCAudioEngineAvailability.

RTCAudioEngineAvailability is a plain aggregate with bool fields, so T(a7, b) is only valid direct initialization in C++20; pre-C++20 ObjC++ targets can reject it. Replace the five occurrences with {false, false} or {input_available, output_available}.

🤖 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 `@sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm` around lines 586 - 605,
Update the five RTCAudioEngineAvailability constructions in engineAvailability
to use braced aggregate initialization, replacing parenthesized boolean
arguments with {false, false} or {input_available, output_available} as
appropriate. Preserve the existing availability and error-handling branches.

@ipavlidakis
ipavlidakis requested a review from santhoshvai July 28, 2026 13:19
@ipavlidakis
ipavlidakis merged commit 466cd5f into develop Jul 28, 2026
1 check passed
@ipavlidakis
ipavlidakis deleted the iliaspavlidakis/ios-1922-avaudioengine-crash branch July 28, 2026 13:35
santhoshvai added a commit to GetStream/react-native-webrtc that referenced this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working iOS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants