mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1027713 - Part 3 - Add a cubeb API to signal that the output device changed. r=kinetik
The reentrant mutex is needed so that users can call back into cubeb's API from the callback. --HG-- extra : rebase_source : 42e7c8f09d02b30b35a7f80418df5e722b52faae
This commit is contained in:
parent
42b2a87851
commit
ab8eadeff3
@ -139,6 +139,7 @@ cubeb_stream_set_volume
|
||||
cubeb_stream_set_panning
|
||||
cubeb_stream_get_current_output_device
|
||||
cubeb_stream_output_device_destroy
|
||||
cubeb_stream_register_device_changed_callback
|
||||
th_comment_clear
|
||||
th_comment_init
|
||||
th_decode_alloc
|
||||
|
@ -171,6 +171,11 @@ typedef void (* cubeb_state_callback)(cubeb_stream * stream,
|
||||
void * user_ptr,
|
||||
cubeb_state state);
|
||||
|
||||
/**
|
||||
* User supplied callback called when the underlying device changed.
|
||||
* @param user */
|
||||
typedef void (* cubeb_device_changed_callback)(void * user_ptr);
|
||||
|
||||
/** Initialize an application context. This will perform any library or
|
||||
application scoped initialization.
|
||||
@param context
|
||||
@ -311,6 +316,18 @@ int cubeb_stream_get_current_output_device(cubeb_stream * stm,
|
||||
int cubeb_stream_output_device_destroy(cubeb_stream * stream,
|
||||
cubeb_output_device * devices);
|
||||
|
||||
/**
|
||||
* Set a callback to be notified when the output device changes.
|
||||
* @param stream the stream for which to set the callback.
|
||||
* @param device_changed_callback a function called whenever the device has
|
||||
* changed. Passing NULL allow to unregister a function
|
||||
* @return CUBEB_ERROR_INVALID_PARAMETER if either stream or
|
||||
* device_changed_callback are invalid pointers.
|
||||
* @return CUBEB_OK
|
||||
*/
|
||||
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||
cubeb_device_changed_callback device_changed_callback);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
@ -34,6 +34,9 @@ struct cubeb_ops {
|
||||
cubeb_output_device ** const device);
|
||||
int (* stream_output_device_destroy)(cubeb_stream * stream,
|
||||
cubeb_output_device * device);
|
||||
int (*stream_register_device_changed_callback)(cubeb_stream * stream,
|
||||
cubeb_device_changed_callback device_changed_callback);
|
||||
|
||||
};
|
||||
|
||||
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
|
||||
|
@ -310,3 +310,19 @@ int cubeb_stream_output_device_destroy(cubeb_stream * stream,
|
||||
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||
cubeb_device_changed_callback device_changed_callback)
|
||||
{
|
||||
if (!stream || !device_changed_callback) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (stream->context->ops->stream_register_device_changed_callback) {
|
||||
return stream->context->ops->
|
||||
stream_register_device_changed_callback(stream,
|
||||
device_changed_callback);
|
||||
}
|
||||
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -1132,5 +1132,6 @@ static struct cubeb_ops const alsa_ops = {
|
||||
.stream_set_volume = alsa_stream_set_volume,
|
||||
.stream_set_panning = alsa_stream_set_panning,
|
||||
.stream_get_current_output_device = NULL,
|
||||
.stream_output_device_destroy = NULL
|
||||
.stream_output_device_destroy = NULL,
|
||||
.stream_register_device_changed_callback = NULL
|
||||
};
|
||||
|
@ -506,5 +506,6 @@ static struct cubeb_ops const audiotrack_ops = {
|
||||
.stream_set_volume = audiotrack_stream_set_volume,
|
||||
.stream_set_panning = audiotrack_stream_set_panning,
|
||||
.stream_get_current_output_device = NULL,
|
||||
.stream_output_device_destroy = NULL
|
||||
.stream_output_device_destroy = NULL,
|
||||
.stream_register_device_changed_callback = NULL
|
||||
};
|
||||
|
@ -38,6 +38,7 @@ struct cubeb_stream {
|
||||
AudioUnit unit;
|
||||
cubeb_data_callback data_callback;
|
||||
cubeb_state_callback state_callback;
|
||||
cubeb_device_changed_callback device_changed_callback;
|
||||
void * user_ptr;
|
||||
AudioStreamBasicDescription sample_spec;
|
||||
pthread_mutex_t mutex;
|
||||
@ -181,6 +182,120 @@ audiounit_get_output_device_id(AudioDeviceID * device_id)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int audiounit_install_device_changed_callback(cubeb_stream * stm);
|
||||
static int audiounit_uninstall_device_changed_callback();
|
||||
|
||||
static OSStatus
|
||||
audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
|
||||
const AudioObjectPropertyAddress * addresses,
|
||||
void * user)
|
||||
{
|
||||
cubeb_stream * stm = (cubeb_stream*) user;
|
||||
|
||||
for (UInt32 i = 0; i < address_count; i++) {
|
||||
switch(addresses[i].mSelector) {
|
||||
case kAudioHardwarePropertyDefaultOutputDevice:
|
||||
/* fall through */
|
||||
case kAudioDevicePropertyDataSource:
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
if (stm->device_changed_callback) {
|
||||
stm->device_changed_callback(stm->user_ptr);
|
||||
}
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static int
|
||||
audiounit_install_device_changed_callback(cubeb_stream * stm)
|
||||
{
|
||||
OSStatus r;
|
||||
AudioDeviceID id;
|
||||
|
||||
/* This event will notify us when the data source on the same device changes,
|
||||
* for example when the user plugs in a normal (non-usb) headset in the
|
||||
* headphone jack. */
|
||||
AudioObjectPropertyAddress alive_address = {
|
||||
kAudioDevicePropertyDataSource,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
if (audiounit_get_output_device_id(&id) != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = AudioObjectAddPropertyListener(id, &alive_address,
|
||||
&audiounit_property_listener_callback,
|
||||
stm);
|
||||
if (r != noErr) {
|
||||
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. */
|
||||
AudioObjectPropertyAddress default_device_address = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
r = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
|
||||
&default_device_address,
|
||||
&audiounit_property_listener_callback,
|
||||
stm);
|
||||
if (r != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
audiounit_uninstall_device_changed_callback()
|
||||
{
|
||||
OSStatus r;
|
||||
AudioDeviceID id;
|
||||
|
||||
AudioObjectPropertyAddress datasource_address = {
|
||||
kAudioDevicePropertyDataSource,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
if (audiounit_get_output_device_id(&id) != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = AudioObjectRemovePropertyListener(id, &datasource_address,
|
||||
&audiounit_property_listener_callback,
|
||||
NULL);
|
||||
if (r != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
AudioObjectPropertyAddress default_device_address = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
r = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
|
||||
&default_device_address,
|
||||
&audiounit_property_listener_callback,
|
||||
NULL);
|
||||
if (r != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
/* Get the acceptable buffer size (in frames) that this device can work with. */
|
||||
static int
|
||||
audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
|
||||
@ -400,10 +515,15 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre
|
||||
stm->data_callback = data_callback;
|
||||
stm->state_callback = state_callback;
|
||||
stm->user_ptr = user_ptr;
|
||||
stm->device_changed_callback = NULL;
|
||||
|
||||
stm->sample_spec = ss;
|
||||
|
||||
r = pthread_mutex_init(&stm->mutex, NULL);
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
r = pthread_mutex_init(&stm->mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
assert(r == 0);
|
||||
|
||||
stm->frames_played = 0;
|
||||
@ -488,6 +608,24 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre
|
||||
|
||||
*stream = stm;
|
||||
|
||||
/* This is needed so that AudioUnit listeners get called on this thread, and
|
||||
* not the main thread. If we don't do that, they are not called, or a crash
|
||||
* occur, depending on the OSX version. */
|
||||
AudioObjectPropertyAddress runloop_address = {
|
||||
kAudioHardwarePropertyRunLoop,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
CFRunLoopRef run_loop = NULL;
|
||||
r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
|
||||
&runloop_address,
|
||||
0, NULL, sizeof(CFRunLoopRef), &run_loop);
|
||||
|
||||
/* we dont' check the return value here, because we want to be able to play
|
||||
* even if we can't detect device changes. */
|
||||
audiounit_install_device_changed_callback(stm);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
@ -508,6 +646,8 @@ audiounit_stream_destroy(cubeb_stream * stm)
|
||||
#endif
|
||||
}
|
||||
|
||||
audiounit_uninstall_device_changed_callback();
|
||||
|
||||
r = pthread_mutex_destroy(&stm->mutex);
|
||||
assert(r == 0);
|
||||
|
||||
@ -658,7 +798,7 @@ int audiounit_stream_get_current_output_device(cubeb_stream * stm,
|
||||
cubeb_output_device ** const device)
|
||||
{
|
||||
OSStatus r;
|
||||
uint32_t size;
|
||||
UInt32 size;
|
||||
UInt32 data;
|
||||
char strdata[4];
|
||||
AudioDeviceID output_device_id;
|
||||
@ -677,7 +817,9 @@ int audiounit_stream_get_current_output_device(cubeb_stream * stm,
|
||||
|
||||
size = sizeof(UInt32);
|
||||
/* This fails with some USB headset, so simply return an empty string. */
|
||||
r = AudioObjectGetPropertyData(output_device_id, &datasource_address, 0, NULL, &size, &data);
|
||||
r = AudioObjectGetPropertyData(output_device_id,
|
||||
&datasource_address,
|
||||
0, NULL, &size, &data);
|
||||
if (r != noErr) {
|
||||
size = 0;
|
||||
data = 0;
|
||||
@ -713,6 +855,16 @@ int audiounit_stream_output_device_destroy(cubeb_stream * stream,
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||
cubeb_device_changed_callback device_changed_callback)
|
||||
{
|
||||
pthread_mutex_lock(&stream->mutex);
|
||||
stream->device_changed_callback = device_changed_callback;
|
||||
pthread_mutex_unlock(&stream->mutex);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiounit_ops = {
|
||||
.init = audiounit_init,
|
||||
.get_backend_id = audiounit_get_backend_id,
|
||||
@ -729,5 +881,6 @@ static struct cubeb_ops const audiounit_ops = {
|
||||
.stream_set_volume = audiounit_stream_set_volume,
|
||||
.stream_set_panning = audiounit_stream_set_panning,
|
||||
.stream_get_current_output_device = audiounit_stream_get_current_output_device,
|
||||
.stream_output_device_destroy = audiounit_stream_output_device_destroy
|
||||
.stream_output_device_destroy = audiounit_stream_output_device_destroy,
|
||||
.stream_register_device_changed_callback = audiounit_stream_register_device_changed_callback
|
||||
};
|
||||
|
@ -789,5 +789,6 @@ static struct cubeb_ops const opensl_ops = {
|
||||
.stream_set_volume = opensl_stream_set_volume,
|
||||
.stream_set_panning = opensl_stream_set_panning,
|
||||
.stream_get_current_output_device = NULL,
|
||||
.stream_output_device_destroy = NULL
|
||||
.stream_output_device_destroy = NULL,
|
||||
.stream_register_device_changed_callback = NULL
|
||||
};
|
||||
|
@ -750,5 +750,6 @@ static struct cubeb_ops const pulse_ops = {
|
||||
.stream_set_volume = pulse_stream_set_volume,
|
||||
.stream_set_panning = pulse_stream_set_panning,
|
||||
.stream_get_current_output_device = NULL,
|
||||
.stream_output_device_destroy = NULL
|
||||
.stream_output_device_destroy = NULL,
|
||||
.stream_register_device_changed_callback = NULL
|
||||
};
|
||||
|
@ -363,5 +363,6 @@ static struct cubeb_ops const sndio_ops = {
|
||||
.stream_set_volume = sndio_stream_set_volume,
|
||||
.stream_set_panning = sndio_stream_set_panning,
|
||||
.stream_get_current_output_device = NULL,
|
||||
.stream_output_device_destroy = NULL
|
||||
.stream_output_device_destroy = NULL,
|
||||
.stream_register_device_changed_callback = NULL
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "cubeb_resampler.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
# define LOG(...) do { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
@ -941,7 +941,8 @@ cubeb_ops const wasapi_ops = {
|
||||
/*.stream_set_volume =*/ wasapi_stream_set_volume,
|
||||
/*.stream_set_panning =*/ wasapi_stream_set_panning,
|
||||
/*.stream_get_current_output_device =*/ NULL,
|
||||
/*.stream_output_device_destroy =*/ NULL
|
||||
/*.stream_output_device_destroy =*/ NULL,
|
||||
/*.stream_register_device_changed_callback =*/ NULL
|
||||
};
|
||||
} // namespace anonymous
|
||||
|
||||
|
@ -716,5 +716,6 @@ static struct cubeb_ops const winmm_ops = {
|
||||
/*.stream_set_volume =*/ winmm_stream_set_volume,
|
||||
/*.stream_set_panning =*/ winmm_stream_set_panning,
|
||||
/*.stream_get_current_output_device =*/ NULL,
|
||||
/*.stream_output_device_destroy =*/ NULL
|
||||
/*.stream_output_device_destroy =*/ NULL,
|
||||
/*.stream_register_device_changed_callback=*/ NULL
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user