diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp --- a/media/libcubeb/src/cubeb_audiounit.cpp +++ b/media/libcubeb/src/cubeb_audiounit.cpp @@ -594,20 +594,20 @@ audiounit_get_input_device_id(AudioDevic return CUBEB_OK; } static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume); static int audiounit_stream_set_volume(cubeb_stream * stm, float volume); static int -audiounit_reinit_stream(cubeb_stream * stm, bool is_started) +audiounit_reinit_stream(cubeb_stream * stm) { auto_lock context_lock(stm->context->mutex); - if (is_started) { + if (!stm->shutdown) { audiounit_stream_stop_internal(stm); } { auto_lock lock(stm->mutex); float volume = 0.0; int vol_rv = audiounit_stream_get_volume(stm, &volume); @@ -622,32 +622,30 @@ audiounit_reinit_stream(cubeb_stream * s audiounit_stream_set_volume(stm, volume); } // Reset input frames to force new stream pre-buffer // silence if needed, check `is_extra_input_needed()` stm->frames_read = 0; // If the stream was running, start it again. - if (is_started) { + if (!stm->shutdown) { audiounit_stream_start_internal(stm); } } return CUBEB_OK; } static OSStatus audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_count, const AudioObjectPropertyAddress * addresses, void * user) { cubeb_stream * stm = (cubeb_stream*) user; stm->switching_device = true; - // Note if the stream was running or not - bool was_running = !stm->shutdown; LOG("(%p) Audio device changed, %d events.", stm, address_count); for (UInt32 i = 0; i < address_count; i++) { switch(addresses[i].mSelector) { case kAudioHardwarePropertyDefaultOutputDevice: { LOG("Event[%d] - mSelector == kAudioHardwarePropertyDefaultOutputDevice", i); // Allow restart to choose the new default stm->output_device = nullptr; @@ -666,19 +664,20 @@ audiounit_property_listener_callback(Aud if (stm->is_default_input) { LOG("It's the default input device, ignore the event"); return noErr; } // Allow restart to choose the new default. Event register only for input. stm->input_device = nullptr; } break; - case kAudioDevicePropertyDataSource: - LOG("Event[%d] - mSelector == kAudioHardwarePropertyDataSource", i); - break; + case kAudioDevicePropertyDataSource: { + LOG("Event[%d] - mSelector == kAudioHardwarePropertyDataSource", i); + return noErr; + } } } for (UInt32 i = 0; i < address_count; i++) { switch(addresses[i].mSelector) { case kAudioHardwarePropertyDefaultOutputDevice: case kAudioHardwarePropertyDefaultInputDevice: case kAudioDevicePropertyDeviceIsAlive: @@ -691,17 +690,17 @@ audiounit_property_listener_callback(Aud break; } } } // Use a new thread, through the queue, to avoid deadlock when calling // Get/SetProperties method from inside notify callback dispatch_async(stm->context->serial_queue, ^() { - if (audiounit_reinit_stream(stm, was_running) != CUBEB_OK) { + if (audiounit_reinit_stream(stm) != CUBEB_OK) { stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); LOG("(%p) Could not reopen the stream after switching.", stm); } stm->switching_device = false; }); return noErr; } @@ -752,27 +751,16 @@ audiounit_install_device_changed_callbac } r = audiounit_add_listener(stm, output_dev_id, kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback); if (r != noErr) { PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource", r); return CUBEB_ERROR; } - - /* This event will notify us when the default audio device changes, - * for example when the user plugs in a USB headset and the system chooses it - * automatically as the default, or when another device is chosen in the - * dropdown list. */ - r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice", r); - return CUBEB_ERROR; - } } if (stm->input_unit) { /* This event will notify us when the data source on the input device changes. */ AudioDeviceID input_dev_id; r = audiounit_get_input_device_id(&input_dev_id); if (r != noErr) { return CUBEB_ERROR; @@ -780,78 +768,112 @@ audiounit_install_device_changed_callbac r = audiounit_add_listener(stm, input_dev_id, kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback); if (r != noErr) { PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource", r); return CUBEB_ERROR; } - /* This event will notify us when the default input device changes. */ - r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice", r); - return CUBEB_ERROR; - } - /* Event to notify when the input is going away. */ AudioDeviceID dev = stm->input_device ? reinterpret_cast(stm->input_device) : audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT); r = audiounit_add_listener(stm, dev, kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); if (r != noErr) { PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive", r); return CUBEB_ERROR; } } return CUBEB_OK; } static int +audiounit_install_system_changed_callback(cubeb_stream * stm) +{ + OSStatus r; + + if (stm->output_unit) { + /* This event will notify us when the default audio device changes, + * for example when the user plugs in a USB headset and the system chooses it + * automatically as the default, or when another device is chosen in the + * dropdown list. */ + r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); + if (r != noErr) { + LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r); + return CUBEB_ERROR; + } + } + + if (stm->input_unit) { + /* This event will notify us when the default input device changes. */ + r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice, + kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); + if (r != noErr) { + LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r); + return CUBEB_ERROR; + } + } + + return CUBEB_OK; +} + +static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm) { OSStatus r; if (stm->output_unit) { AudioDeviceID output_dev_id; r = audiounit_get_output_device_id(&output_dev_id); if (r != noErr) { return CUBEB_ERROR; } r = audiounit_remove_listener(stm, output_dev_id, kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback); if (r != noErr) { return CUBEB_ERROR; } - - r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - return CUBEB_ERROR; - } } if (stm->input_unit) { AudioDeviceID input_dev_id; r = audiounit_get_input_device_id(&input_dev_id); if (r != noErr) { return CUBEB_ERROR; } r = audiounit_remove_listener(stm, input_dev_id, kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback); if (r != noErr) { return CUBEB_ERROR; } - + } + return CUBEB_OK; +} + +static int +audiounit_uninstall_system_changed_callback(cubeb_stream * stm) +{ + OSStatus r; + + if (stm->output_unit) { + r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); + if (r != noErr) { + return CUBEB_ERROR; + } + } + + if (stm->input_unit) { r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); + kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); if (r != noErr) { return CUBEB_ERROR; } } return CUBEB_OK; } /* Get the acceptable buffer size (in frames) that this device can work with. */ @@ -1764,16 +1786,22 @@ audiounit_setup_stream(cubeb_stream * st if (stm->input_unit && stm->output_unit) { // According to the I/O hardware rate it is expected a specific pattern of callbacks // for example is input is 44100 and output is 48000 we expected no more than 2 // out callback in a row. stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate); } + r = audiounit_install_device_changed_callback(stm); + if (r != CUBEB_OK) { + LOG("(%p) Could not install the device change callback.", stm); + return r; + } + return CUBEB_OK; } static int audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * /* stream_name */, cubeb_devid input_device, @@ -1838,31 +1866,37 @@ audiounit_stream_init(cubeb * context, } if (r != CUBEB_OK) { LOG("(%p) Could not setup the audiounit stream.", stm); audiounit_stream_destroy(stm); return r; } - r = audiounit_install_device_changed_callback(stm); + r = audiounit_install_system_changed_callback(stm); if (r != CUBEB_OK) { LOG("(%p) Could not install the device change callback.", stm); return r; } *stream = stm; LOG("Cubeb stream (%p) init successful.", stm); return CUBEB_OK; } static void audiounit_close_stream(cubeb_stream *stm) { stm->mutex.assert_current_thread_owns(); + + int r = audiounit_uninstall_device_changed_callback(stm); + if (r != CUBEB_OK) { + LOG("(%p) Could not uninstall the device changed callback", stm); + } + if (stm->input_unit) { AudioUnitUninitialize(stm->input_unit); AudioComponentInstanceDispose(stm->input_unit); } audiounit_destroy_input_linear_buffer(stm); if (stm->output_unit) { @@ -1873,31 +1907,29 @@ audiounit_close_stream(cubeb_stream *stm cubeb_resampler_destroy(stm->resampler); } static void audiounit_stream_destroy(cubeb_stream * stm) { stm->shutdown = true; + int r = audiounit_uninstall_system_changed_callback(stm); + if (r != CUBEB_OK) { + LOG("(%p) Could not uninstall the device changed callback", stm); + } + auto_lock context_lock(stm->context->mutex); audiounit_stream_stop_internal(stm); { auto_lock lock(stm->mutex); audiounit_close_stream(stm); } -#if !TARGET_OS_IPHONE - int r = audiounit_uninstall_device_changed_callback(stm); - if (r != CUBEB_OK) { - LOG("(%p) Could not uninstall the device changed callback", stm); - } -#endif - assert(stm->context->active_streams >= 1); stm->context->active_streams -= 1; LOG("Cubeb stream (%p) destroyed successful.", stm); stm->~cubeb_stream(); free(stm); } @@ -1914,20 +1946,20 @@ audiounit_stream_start_internal(cubeb_st r = AudioOutputUnitStart(stm->output_unit); assert(r == 0); } } static int audiounit_stream_start(cubeb_stream * stm) { + auto_lock context_lock(stm->context->mutex); stm->shutdown = false; stm->draining = false; - auto_lock context_lock(stm->context->mutex); audiounit_stream_start_internal(stm); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); LOG("Cubeb stream (%p) started successfully.", stm); return CUBEB_OK; } @@ -1943,19 +1975,19 @@ audiounit_stream_stop_internal(cubeb_str r = AudioOutputUnitStop(stm->output_unit); assert(r == 0); } } static int audiounit_stream_stop(cubeb_stream * stm) { + auto_lock context_lock(stm->context->mutex); stm->shutdown = true; - auto_lock context_lock(stm->context->mutex); audiounit_stream_stop_internal(stm); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); LOG("Cubeb stream (%p) stopped successfully.", stm); return CUBEB_OK; }