Skip to content

Commit a58a087

Browse files
committed
macOS device patch 1
Format Log device name Default patch 1 Engine start bug workaround Change sleep time Restart engine only if stopped Patch Default device update count Recreate on device change
1 parent a1bb19a commit a58a087

File tree

4 files changed

+129
-107
lines changed

4 files changed

+129
-107
lines changed

modules/audio_device/audio_engine_device.h

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
140140
uint32_t output_device_id = 0; // kAudioObjectUnknown
141141
uint32_t input_device_id = 0; // kAudioObjectUnknown
142142

143-
uint32_t default_output_device_id = 0; // Track default device
144-
uint32_t default_input_device_id = 0;
143+
uint32_t default_output_device_update_count = 0; // Track default switch count
144+
uint32_t default_input_device_update_count = 0;
145145

146146
bool operator==(const EngineState& rhs) const {
147147
return input_enabled == rhs.input_enabled && input_running == rhs.input_running &&
@@ -155,8 +155,8 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
155155
voice_processing_agc_enabled == rhs.voice_processing_agc_enabled &&
156156
advanced_ducking == rhs.advanced_ducking && ducking_level == rhs.ducking_level &&
157157
output_device_id == rhs.output_device_id && input_device_id == rhs.input_device_id &&
158-
default_output_device_id == rhs.default_output_device_id &&
159-
default_input_device_id == rhs.default_input_device_id;
158+
default_output_device_update_count == rhs.default_output_device_update_count &&
159+
default_input_device_update_count == rhs.default_input_device_update_count;
160160
}
161161

162162
bool operator!=(const EngineState& rhs) const { return !(*this == rhs); }
@@ -191,9 +191,21 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
191191
return IsOutputInputLinked() ? input_running : input_running && output_running;
192192
}
193193

194-
bool IsOutputDefaultDevice() const { return output_device_id == 0; }
194+
bool IsOutputDefaultDevice() const {
195+
#if TARGET_OS_OSX
196+
return output_device_id == kAudioObjectUnknown;
197+
#else
198+
return output_device_id == 0;
199+
#endif
200+
}
195201

196-
bool IsInputDefaultDevice() const { return input_device_id == 0; }
202+
bool IsInputDefaultDevice() const {
203+
#if TARGET_OS_OSX
204+
return input_device_id == kAudioObjectUnknown;
205+
#else
206+
return input_device_id == 0;
207+
#endif
208+
}
197209
};
198210

199211
explicit AudioEngineDevice(bool voice_processing_bypassed);
@@ -356,29 +368,35 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
356368
bool DidUpdateInputDevice() const { return prev.input_device_id != next.input_device_id; }
357369

358370
bool DidUpdateDefaultOutputDevice() const {
359-
return prev.default_output_device_id != next.default_output_device_id;
371+
return prev.default_output_device_update_count != next.default_output_device_update_count;
360372
}
361373

362374
bool DidUpdateDefaultInputDevice() const {
363-
return prev.default_input_device_id != next.default_input_device_id;
375+
return prev.default_input_device_update_count != next.default_input_device_update_count;
364376
}
365377

366378
bool DidUpdateMuteMode() const { return prev.mute_mode != next.mute_mode; }
367379

368380
bool IsEngineRestartRequired() const {
369-
return DidUpdateAudioGraph() || DidUpdateOutputDevice() || DidUpdateInputDevice() ||
381+
return DidUpdateAudioGraph() ||
370382
// Voice processing enable state updates
371-
DidUpdateVoiceProcessingEnabled() ||
372-
// Handle default device updates
373-
(DidUpdateDefaultOutputDevice() && next.IsOutputDefaultDevice()) ||
374-
(DidUpdateDefaultInputDevice() && next.IsInputDefaultDevice());
383+
DidUpdateVoiceProcessingEnabled();
375384
}
376385

377-
// Special case to re-create engine when switching from Speaker & Mic ->
378-
// Speaker only.
379386
bool IsEngineRecreateRequired() const {
380-
return (prev.IsOutputEnabled() && next.IsOutputEnabled()) &&
381-
(prev.IsInputEnabled() && !next.IsInputEnabled());
387+
// Device id specified
388+
bool device = DidUpdateOutputDevice() || DidUpdateInputDevice();
389+
390+
// Default device updated
391+
bool default_device = (DidUpdateDefaultOutputDevice() && next.IsOutputDefaultDevice()) ||
392+
(DidUpdateDefaultInputDevice() && next.IsInputDefaultDevice());
393+
394+
// Special case to re-create engine when switching from Speaker & Mic ->
395+
// Speaker only.
396+
bool special_case = (prev.IsOutputEnabled() && next.IsOutputEnabled()) &&
397+
(prev.IsInputEnabled() && !next.IsInputEnabled());
398+
399+
return device || default_device || special_case;
382400
}
383401

384402
bool DidEnableManualRenderingMode() const {
@@ -398,7 +416,7 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
398416
int32_t ApplyManualEngineState(EngineStateUpdate state);
399417

400418
// AudioEngine observer methods. May be called from any thread.
401-
void ReconfigureEngine(bool is_required);
419+
void ReconfigureEngine();
402420

403421
// Device related
404422
#if TARGET_OS_OSX
@@ -407,6 +425,12 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver
407425
void* clientData);
408426
void HandleDeviceListenerEvent(AudioObjectPropertySelector selector);
409427
void UpdateAllDeviceIDs();
428+
429+
// Debounce flags for device updates
430+
rtc::scoped_refptr<PendingTaskSafetyFlag> default_device_update_safety_ =
431+
PendingTaskSafetyFlag::Create();
432+
const int kDefaultDeviceUpdateDebounceMs = 500; // Debounce delay in milliseconds
433+
410434
std::vector<AudioObjectID> input_device_ids_;
411435
std::vector<AudioObjectID> output_device_ids_;
412436
std::vector<std::string> output_device_labels_;

modules/audio_device/audio_engine_device.mm

Lines changed: 80 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -84,25 +84,17 @@
8484
channels:1
8585
interleaved:YES];
8686

87-
#if TARGET_OS_OSX
8887
// Initial engine state
8988
engine_state_.voice_processing_bypassed = voice_processing_bypassed;
90-
std::optional<AudioDeviceID> default_output_device_id =
91-
mac_audio_utils::GetDefaultOutputDeviceID();
92-
std::optional<AudioDeviceID> default_input_device_id = mac_audio_utils::GetDefaultInputDeviceID();
93-
if (default_output_device_id) {
94-
engine_state_.default_output_device_id = *default_output_device_id;
95-
}
96-
if (default_input_device_id) {
97-
engine_state_.default_input_device_id = *default_input_device_id;
98-
}
99-
#endif
10089
}
10190

10291
AudioEngineDevice::~AudioEngineDevice() {
10392
RTC_DCHECK_RUN_ON(thread_);
10493

10594
safety_->SetNotAlive();
95+
#if TARGET_OS_OSX
96+
default_device_update_safety_->SetNotAlive();
97+
#endif
10698

10799
Terminate();
108100

@@ -175,30 +167,43 @@
175167
observer_->OnDevicesUpdated();
176168
}
177169
}
178-
} else if (selector == kAudioHardwarePropertyDefaultOutputDevice) {
179-
LOGI() << "Did update default output device";
180-
std::optional<AudioDeviceID> device_id = mac_audio_utils::GetDefaultOutputDeviceID();
181-
if (device_id) {
182-
int32_t result = ModifyEngineState([device_id](EngineState state) -> EngineState {
183-
state.default_output_device_id = *device_id;
184-
return state;
185-
});
186-
if (result != 0) {
187-
LOGE() << "Failed to update default output device ID, error: " << result;
188-
}
189-
}
190-
} else if (selector == kAudioHardwarePropertyDefaultInputDevice) {
191-
LOGI() << "Did update default input device";
192-
std::optional<AudioDeviceID> device_id = mac_audio_utils::GetDefaultInputDeviceID();
193-
if (device_id) {
194-
int32_t result = ModifyEngineState([device_id](EngineState state) -> EngineState {
195-
state.default_input_device_id = *device_id;
196-
return state;
197-
});
198-
if (result != 0) {
199-
LOGE() << "Failed to update default input device ID, error: " << result;
200-
}
201-
}
170+
} else if (selector == kAudioHardwarePropertyDefaultOutputDevice ||
171+
selector == kAudioHardwarePropertyDefaultInputDevice) {
172+
// Cancel any pending updates
173+
default_device_update_safety_->SetNotAlive();
174+
default_device_update_safety_ = PendingTaskSafetyFlag::Create();
175+
176+
// Schedule a new debounced update
177+
thread_->PostDelayedTask(
178+
SafeTask(default_device_update_safety_,
179+
[this, selector] {
180+
RTC_DCHECK_RUN_ON(thread_);
181+
LOGI() << "Processing debounced default device update for selector: "
182+
<< selector;
183+
184+
if (selector == kAudioHardwarePropertyDefaultOutputDevice) {
185+
LOGI() << "Did update default output device";
186+
int32_t result = ModifyEngineState([](EngineState state) -> EngineState {
187+
state.default_output_device_update_count++;
188+
return state;
189+
});
190+
if (result != 0) {
191+
LOGE() << "Failed to update default output device update count, error: "
192+
<< result;
193+
}
194+
} else if (selector == kAudioHardwarePropertyDefaultInputDevice) {
195+
LOGI() << "Did update default input device";
196+
int32_t result = ModifyEngineState([](EngineState state) -> EngineState {
197+
state.default_input_device_update_count++;
198+
return state;
199+
});
200+
if (result != 0) {
201+
LOGE() << "Failed to update default input device update count, error: "
202+
<< result;
203+
}
204+
}
205+
}),
206+
TimeDelta::Millis(kDefaultDeviceUpdateDebounceMs));
202207
}
203208
}));
204209
}
@@ -1250,8 +1255,8 @@
12501255
// ----------------------------------------------------------------------------------------------------
12511256
// Private - Engine Related
12521257

1253-
void AudioEngineDevice::ReconfigureEngine(bool is_required) {
1254-
LOGI() << "ReconfigureEngine is_required: " << is_required;
1258+
void AudioEngineDevice::ReconfigureEngine() {
1259+
LOGI() << "ReconfigureEngine";
12551260

12561261
// TODO: More optimizations
12571262
// We only need to re-attach the input / output nodes with updated sample rate etc.
@@ -1264,25 +1269,6 @@
12641269
// Re-configure is only for device mode
12651270
if (current_state.render_mode != RenderMode::Device) return;
12661271

1267-
// bool is_reconfigure_required = is_required;
1268-
1269-
// #if defined(WEBRTC_IOS)
1270-
// RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession)
1271-
// sharedInstance]; const double new_sample_rate = session.sampleRate; if
1272-
// (!is_reconfigure_required && current_state.output_enabled) {
1273-
// AVAudioFormat* format = [thisoutputNode() outputFormatForBus:0];
1274-
// if (format.sampleRate != new_sample_rate) {
1275-
// is_reconfigure_required = true;
1276-
// }
1277-
// }
1278-
// #endif
1279-
1280-
// // No configuration required
1281-
// if (!is_reconfigure_required) {
1282-
// LOGI() << "ReconfigureEngine no configuration required";
1283-
// return;
1284-
// }
1285-
12861272
EngineState shutdown_state = this->engine_state_;
12871273
shutdown_state.input_enabled = false;
12881274
shutdown_state.input_running = false;
@@ -2193,41 +2179,40 @@
21932179
//
21942180
#if TARGET_OS_OSX
21952181
if (state.next.IsAnyEnabled() &&
2196-
(!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired() ||
2197-
state.DidUpdateInputDevice() || state.DidUpdateOutputDevice() ||
2198-
((state.DidUpdateDefaultOutputDevice() && state.next.IsOutputDefaultDevice()) ||
2199-
(state.DidUpdateDefaultInputDevice() && state.next.IsInputDefaultDevice())))) {
2182+
(!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired())) {
22002183
if (state.next.IsInputEnabled()) {
22012184
uint32_t input_device_id = state.next.input_device_id;
22022185
if (input_device_id == kAudioObjectUnknown) {
2203-
input_device_id = state.next.default_input_device_id;
2204-
LOGI() << "Using default input device: " << input_device_id;
2205-
}
2206-
2207-
LOGI() << "Setting input device: " << input_device_id;
2208-
AudioUnit inputUnit = inputNode().audioUnit;
2209-
OSStatus err = AudioUnitSetProperty(inputUnit, kAudioOutputUnitProperty_CurrentDevice,
2210-
kAudioUnitScope_Global, 1, &input_device_id,
2211-
sizeof(input_device_id));
2212-
if (err != noErr) {
2213-
LOGE() << "Failed to set input device: " << input_device_id;
2186+
LOGI() << "Using default input device";
2187+
} else {
2188+
auto input_device_name = mac_audio_utils::GetDeviceName(input_device_id);
2189+
LOGI() << "Setting input device: " << input_device_name.value_or("Unknown") << " ("
2190+
<< input_device_id << ")";
2191+
AudioUnit inputUnit = inputNode().audioUnit;
2192+
OSStatus err = AudioUnitSetProperty(inputUnit, kAudioOutputUnitProperty_CurrentDevice,
2193+
kAudioUnitScope_Global, 1, &input_device_id,
2194+
sizeof(input_device_id));
2195+
if (err != noErr) {
2196+
LOGE() << "Failed to set input device: " << input_device_id;
2197+
}
22142198
}
22152199
}
22162200

22172201
if (state.next.IsOutputEnabled()) {
22182202
uint32_t output_deviceId = state.next.output_device_id;
22192203
if (output_deviceId == kAudioObjectUnknown) {
2220-
output_deviceId = state.next.default_output_device_id;
2221-
LOGI() << "Using default output device: " << output_deviceId;
2222-
}
2223-
2224-
LOGI() << "Setting output device: " << output_deviceId;
2225-
AudioUnit outputUnit = outputNode().audioUnit;
2226-
OSStatus err = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice,
2227-
kAudioUnitScope_Global, 0, &output_deviceId,
2228-
sizeof(output_deviceId));
2229-
if (err != noErr) {
2230-
LOGE() << "Failed to set output device: " << output_deviceId;
2204+
LOGI() << "Using default output device";
2205+
} else {
2206+
auto output_device_name = mac_audio_utils::GetDeviceName(output_deviceId);
2207+
LOGI() << "Setting output device: " << output_device_name.value_or("Unknown") << " ("
2208+
<< output_deviceId << ")";
2209+
AudioUnit outputUnit = outputNode().audioUnit;
2210+
OSStatus err = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice,
2211+
kAudioUnitScope_Global, 0, &output_deviceId,
2212+
sizeof(output_deviceId));
2213+
if (err != noErr) {
2214+
LOGE() << "Failed to set output device: " << output_deviceId;
2215+
}
22312216
}
22322217
}
22332218
}
@@ -2290,6 +2275,13 @@
22902275
NSString* error_string = nil;
22912276

22922277
@try {
2278+
#if TARGET_OS_OSX
2279+
// Workaround for engine not starting in some cases when other apps are using voice
2280+
// processing already.
2281+
[engine_device_ prepare];
2282+
usleep(1000);
2283+
#endif
2284+
22932285
NSError* error = nil;
22942286
start_result = [engine_device_ startAndReturnError:&error];
22952287
if (!start_result && error != nil) {
@@ -2317,7 +2309,12 @@
23172309
object:engine_device_
23182310
queue:nil
23192311
usingBlock:^(NSNotification* notification) {
2320-
ReconfigureEngine(true);
2312+
LOGI() << "AVAudioEngineConfigurationChangeNotification engineIsRunning: "
2313+
<< engine_device_.running;
2314+
// Only re-configure if engine stopped.
2315+
if (!engine_device_.running) {
2316+
ReconfigureEngine();
2317+
}
23212318
}];
23222319

23232320
} else {

modules/audio_device/mac/audio_device_utils_mac.cc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,6 @@ std::vector<AudioObjectID> GetAudioObjectIDs(
167167
return device_ids;
168168
}
169169

170-
std::optional<std::string> GetDeviceName(AudioObjectID device_id) {
171-
return GetDeviceStringProperty(device_id, kAudioObjectPropertyName);
172-
}
173-
174170
std::optional<std::string> TranslateDeviceSource(AudioObjectID device_id,
175171
UInt32 source_id,
176172
bool is_input) {
@@ -248,6 +244,10 @@ std::optional<std::string> GetDeviceUniqueID(AudioObjectID device_id) {
248244
return GetDeviceStringProperty(device_id, kAudioDevicePropertyDeviceUID);
249245
}
250246

247+
std::optional<std::string> GetDeviceName(AudioObjectID device_id) {
248+
return GetDeviceStringProperty(device_id, kAudioObjectPropertyName);
249+
}
250+
251251
std::optional<std::string> GetDeviceLabel(AudioObjectID device_id,
252252
bool is_input) {
253253
std::optional<std::string> device_label;
@@ -281,15 +281,14 @@ std::optional<uint32_t> GetDeviceTransportType(AudioObjectID device_id) {
281281
kAudioObjectPropertyScopeGlobal);
282282
}
283283

284-
285284
bool IsInputDevice(AudioObjectID device_id) {
286285
auto type = GetDeviceTransportType(device_id);
287286
if (type && (*type == kAudioDeviceTransportTypeAggregate ||
288287
*type == kAudioDeviceTransportTypeVirtual ||
289288
*type == kAudioDeviceTransportTypeUnknown)) {
290289
return false;
291290
}
292-
291+
293292
std::vector<AudioObjectID> streams =
294293
GetAudioObjectIDs(device_id, kAudioDevicePropertyStreams);
295294

modules/audio_device/mac/audio_device_utils_mac.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ std::vector<AudioObjectID> GetRelatedDeviceIDs(AudioObjectID device_id);
3737

3838
std::optional<std::string> GetDeviceUniqueID(AudioObjectID device_id);
3939

40+
std::optional<std::string> GetDeviceName(AudioObjectID device_id);
41+
4042
std::optional<std::string> GetDeviceLabel(AudioObjectID device_id,
4143
bool is_input);
4244

0 commit comments

Comments
 (0)