403 lines
13 KiB
Diff
403 lines
13 KiB
Diff
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<intptr_t>(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;
|
|
}
|
|
|