Skip to content

Commit 3681657

Browse files
authored
Audio recording perms checks (#200)
Check recording perm & AVAudioSession.category when enabling input for device rendering mode to avoid crashes.
1 parent 7c6fa10 commit 3681657

File tree

2 files changed

+407
-84
lines changed

2 files changed

+407
-84
lines changed

modules/audio_device/audio_engine_device.h

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ enum AudioEngineErrorCode {
9494

9595
// Voice processing errors
9696
kAudioEngineVoiceProcessingError = -8000,
97-
kAudioEngineAGCError = -8001
97+
kAudioEngineAGCError = -8001,
98+
99+
// Permission and session errors
100+
kAudioEngineErrorInsufficientDevicePermission = -9000,
101+
kAudioEngineErrorAudioSessionInvalidCategory = -9001
98102
};
99103

100104
class FineAudioBuffer;
@@ -124,8 +128,6 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
124128
bool output_available = true;
125129
bool input_available = true;
126130

127-
// Output will be enabled when input is enabled
128-
bool input_follow_mode = true;
129131
bool input_enabled_persistent_mode = false;
130132

131133
bool input_muted = true;
@@ -150,7 +152,6 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
150152
return input_enabled == rhs.input_enabled && input_running == rhs.input_running &&
151153
output_enabled == rhs.output_enabled && output_running == rhs.output_running &&
152154
input_available == rhs.input_available && output_available == rhs.output_available &&
153-
input_follow_mode == rhs.input_follow_mode &&
154155
input_enabled_persistent_mode == rhs.input_enabled_persistent_mode &&
155156
input_muted == rhs.input_muted && is_interrupted == rhs.is_interrupted &&
156157
render_mode == rhs.render_mode && mute_mode == rhs.mute_mode &&
@@ -165,27 +166,64 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
165166

166167
bool operator!=(const EngineState& rhs) const { return !(*this == rhs); }
167168

168-
bool IsOutputInputLinked() const { return input_follow_mode && voice_processing_enabled; }
169+
// AUDIO STATE LOGIC
170+
//
171+
// Device Mode:
172+
// - Output follows input only when voice_processing_enabled=true (for AEC)
173+
// - Input respects mute mode restrictions (RestartEngine + input_muted)
174+
// - Independent operation when voice processing is disabled
175+
//
176+
// Manual Mode:
177+
// - Bidirectional coupling: if ANY component is enabled/running, BOTH are considered
178+
// enabled/running
179+
// - No mute mode restrictions (manual control bypasses automatic muting)
180+
// - Required for AVAudioEngine graph connectivity (input must connect to output)
181+
//
182+
// All modes respect availability flags (input_available/output_available)
169183

170184
bool IsOutputEnabled() const {
171-
bool result = IsOutputInputLinked() ? (IsInputEnabled() || output_enabled) : output_enabled;
172-
return output_available && result;
185+
if (!output_available) return false;
186+
187+
switch (render_mode) {
188+
case RenderMode::Device:
189+
return voice_processing_enabled ? (IsInputEnabled() || output_enabled) : output_enabled;
190+
case RenderMode::Manual:
191+
return output_enabled || input_enabled || input_enabled_persistent_mode;
192+
}
173193
}
174194

175195
bool IsOutputRunning() const {
176-
bool result = IsOutputInputLinked() ? (IsInputRunning() || output_running) : output_running;
177-
return output_available && result;
196+
if (!output_available) return false;
197+
198+
switch (render_mode) {
199+
case RenderMode::Device:
200+
return voice_processing_enabled ? (IsInputRunning() || output_running) : output_running;
201+
case RenderMode::Manual:
202+
return output_running || input_running;
203+
}
178204
}
179205

180206
bool IsInputEnabled() const {
181-
bool result = !(mute_mode == MuteMode::RestartEngine && input_muted) &&
182-
(input_enabled || input_enabled_persistent_mode);
183-
return input_available && result;
207+
if (!input_available) return false;
208+
209+
switch (render_mode) {
210+
case RenderMode::Device:
211+
return !(mute_mode == MuteMode::RestartEngine && input_muted) &&
212+
(input_enabled || input_enabled_persistent_mode);
213+
case RenderMode::Manual:
214+
return input_enabled || input_enabled_persistent_mode || output_enabled;
215+
}
184216
}
185217

186218
bool IsInputRunning() const {
187-
bool result = !(mute_mode == MuteMode::RestartEngine && input_muted) && input_running;
188-
return input_available && result;
219+
if (!input_available) return false;
220+
221+
switch (render_mode) {
222+
case RenderMode::Device:
223+
return !(mute_mode == MuteMode::RestartEngine && input_muted) && input_running;
224+
case RenderMode::Manual:
225+
return input_running || output_running;
226+
}
189227
}
190228

191229
bool IsAnyEnabled() const { return IsInputEnabled() || IsOutputEnabled(); }
@@ -413,7 +451,6 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
413451

414452
EngineState engine_state_ RTC_GUARDED_BY(thread_);
415453

416-
bool IsMicrophonePermissionGranted();
417454
int32_t ModifyEngineState(std::function<EngineState(EngineState)> state_transform);
418455

419456
int32_t ApplyDeviceEngineState(EngineStateUpdate state);
@@ -441,6 +478,13 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
441478
std::vector<std::string> input_device_labels_;
442479
#endif
443480

481+
bool IsMicrophonePermissionGranted();
482+
bool EnsureMicrophonePermissionSync();
483+
484+
#if !TARGET_OS_OSX
485+
bool IsAudioSessionCategoryValid(NSString* category, bool is_input_enabled,
486+
bool is_output_enabled);
487+
#endif
444488
void DebugAudioEngine();
445489

446490
void StartRenderLoop();

0 commit comments

Comments
 (0)