osxaudio: Add some device provider properties

Add is-default and unique-id properties to the device provider.

unique-id is particularly useful for recognising the device again
as it's stable for a device across reboots and replugs.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5274>
This commit is contained in:
Jan Schmidt 2023-09-04 02:43:19 +10:00 committed by GStreamer Marge Bot
parent e76581fe93
commit 81e3f7b4a4
4 changed files with 94 additions and 24 deletions

View file

@ -93,7 +93,7 @@ gst_osx_audio_device_provider_init (GstOsxAudioDeviceProvider * provider)
static GstOsxAudioDevice * static GstOsxAudioDevice *
gst_osx_audio_device_provider_probe_device (GstOsxAudioDeviceProvider * gst_osx_audio_device_provider_probe_device (GstOsxAudioDeviceProvider *
provider, AudioDeviceID device_id, const gchar * device_name, provider, AudioDeviceID device_id, const gchar * device_name,
GstOsxAudioDeviceType type) GstOsxAudioDeviceType type, gboolean is_default)
{ {
GstOsxAudioDevice *device = NULL; GstOsxAudioDevice *device = NULL;
GstCoreAudio *core_audio; GstCoreAudio *core_audio;
@ -101,6 +101,7 @@ gst_osx_audio_device_provider_probe_device (GstOsxAudioDeviceProvider *
core_audio = gst_core_audio_new (NULL); core_audio = gst_core_audio_new (NULL);
core_audio->is_src = type == GST_OSX_AUDIO_DEVICE_TYPE_SOURCE ? TRUE : FALSE; core_audio->is_src = type == GST_OSX_AUDIO_DEVICE_TYPE_SOURCE ? TRUE : FALSE;
core_audio->device_id = device_id; core_audio->device_id = device_id;
core_audio->is_default = is_default;
if (!gst_core_audio_open (core_audio)) { if (!gst_core_audio_open (core_audio)) {
GST_ERROR ("CoreAudio device could not be opened"); GST_ERROR ("CoreAudio device could not be opened");
@ -118,42 +119,66 @@ done:
} }
static inline gchar * static inline gchar *
_audio_device_get_name (AudioDeviceID device_id, gboolean output) _audio_device_get_cfstring_prop (AudioDeviceID device_id, gboolean output,
AudioObjectPropertyElement prop_id)
{ {
OSStatus status = noErr; OSStatus status = noErr;
UInt32 propertySize = 0; UInt32 propertySize = 0;
gchar *device_name = NULL; CFStringRef prop_val;
AudioObjectPropertyScope prop_scope; gchar *result = NULL;
AudioObjectPropertyAddress deviceNameAddress = { AudioObjectPropertyAddress propAddress = {
kAudioDevicePropertyDeviceName, prop_id,
kAudioDevicePropertyScopeOutput, kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMain kAudioObjectPropertyElementMain
}; };
prop_scope = output ? kAudioDevicePropertyScopeOutput : propAddress.mScope = output ? kAudioDevicePropertyScopeOutput :
kAudioDevicePropertyScopeInput; kAudioDevicePropertyScopeInput;
deviceNameAddress.mScope = prop_scope;
/* Get the length of the device name */ /* Get the length of the device name */
status = AudioObjectGetPropertyDataSize (device_id, status = AudioObjectGetPropertyDataSize (device_id,
&deviceNameAddress, 0, NULL, &propertySize); &propAddress, 0, NULL, &propertySize);
if (status != noErr) { if (status != noErr) {
goto beach; goto beach;
} }
/* Get the name of the device */ /* Get the requested property */
device_name = (gchar *) g_malloc (propertySize);
status = AudioObjectGetPropertyData (device_id, status = AudioObjectGetPropertyData (device_id,
&deviceNameAddress, 0, NULL, &propertySize, device_name); &propAddress, 0, NULL, &propertySize, &prop_val);
if (status != noErr) { if (status != noErr) {
g_free (device_name); goto beach;
device_name = NULL;
} }
/* Convert to UTF-8 C String */
CFIndex prop_len = CFStringGetLength (prop_val);
CFIndex max_size =
CFStringGetMaximumSizeForEncoding (prop_len, kCFStringEncodingUTF8) + 1;
result = g_malloc (max_size);
if (!CFStringGetCString (prop_val, result, max_size, kCFStringEncodingUTF8)) {
g_free (result);
result = NULL;
}
CFRelease (prop_val);
beach: beach:
return device_name; return result;
}
static inline gchar *
_audio_device_get_name (AudioDeviceID device_id, gboolean output)
{
return _audio_device_get_cfstring_prop (device_id, output,
kAudioObjectPropertyName);
}
static inline gchar *
_audio_device_get_uid (AudioDeviceID device_id, gboolean output)
{
return _audio_device_get_cfstring_prop (device_id, output,
kAudioDevicePropertyDeviceUID);
} }
static inline gboolean static inline gboolean
@ -210,6 +235,35 @@ _audio_device_has_input (AudioDeviceID device_id)
return TRUE; return TRUE;
} }
static inline AudioDeviceID
_audio_system_get_default_device (gboolean output)
{
OSStatus status = noErr;
UInt32 propertySize = sizeof (AudioDeviceID);
AudioDeviceID device_id = kAudioDeviceUnknown;
AudioObjectPropertySelector prop_selector;
prop_selector = output ? kAudioHardwarePropertyDefaultOutputDevice :
kAudioHardwarePropertyDefaultInputDevice;
AudioObjectPropertyAddress defaultDeviceAddress = {
prop_selector,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMain
};
status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
&defaultDeviceAddress, 0, NULL, &propertySize, &device_id);
if (status != noErr) {
GST_ERROR ("failed getting default output device: %d", (int) status);
}
GST_DEBUG ("Default device id: %u", (unsigned) device_id);
return device_id;
}
static inline AudioDeviceID * static inline AudioDeviceID *
_audio_system_get_devices (gint * ndevices) _audio_system_get_devices (gint * ndevices)
{ {
@ -320,12 +374,15 @@ gst_osx_audio_device_provider_probe_internal (GstOsxAudioDeviceProvider * self,
type = GST_OSX_AUDIO_DEVICE_TYPE_SINK; type = GST_OSX_AUDIO_DEVICE_TYPE_SINK;
} }
AudioDeviceID default_device = _audio_system_get_default_device (!is_src);
for (i = 0; i < ndevices; i++) { for (i = 0; i < ndevices; i++) {
gchar *device_name; gchar *device_name;
if ((device_name = _audio_device_get_name (osx_devices[i], FALSE))) { if ((device_name = _audio_device_get_name (osx_devices[i], FALSE))) {
gboolean has_output = _audio_device_has_output (osx_devices[i]); gboolean has_output = _audio_device_has_output (osx_devices[i]);
gboolean has_input = _audio_device_has_input (osx_devices[i]); gboolean has_input = _audio_device_has_input (osx_devices[i]);
gboolean is_default = (default_device == osx_devices[i]);
if (is_src && !has_input) { if (is_src && !has_input) {
goto cleanup; goto cleanup;
@ -335,7 +392,7 @@ gst_osx_audio_device_provider_probe_internal (GstOsxAudioDeviceProvider * self,
device = device =
gst_osx_audio_device_provider_probe_device (self, osx_devices[i], gst_osx_audio_device_provider_probe_device (self, osx_devices[i],
device_name, type); device_name, type, is_default);
if (device) { if (device) {
if (is_src) { if (is_src) {
GST_DEBUG ("Input Device ID: %u Name: %s", GST_DEBUG ("Input Device ID: %u Name: %s",
@ -553,10 +610,20 @@ gst_osx_audio_device_new (AudioDeviceID device_id, const gchar * device_name,
const gchar *element_name = NULL; const gchar *element_name = NULL;
const gchar *klass = NULL; const gchar *klass = NULL;
GstCaps *template_caps, *caps; GstCaps *template_caps, *caps;
GstStructure *props = gst_structure_new_empty ("properties");
g_return_val_if_fail (device_id > 0, NULL); g_return_val_if_fail (device_id > 0, NULL);
g_return_val_if_fail (device_name, NULL); g_return_val_if_fail (device_name, NULL);
gst_structure_set (props, "is-default", G_TYPE_BOOLEAN,
core_audio->is_default, NULL);
gchar *uid = _audio_device_get_uid (device_id, !core_audio->is_src);
if (uid != NULL) {
gst_structure_set (props, "unique-id", G_TYPE_STRING, uid, NULL);
g_free (uid);
}
switch (type) { switch (type) {
case GST_OSX_AUDIO_DEVICE_TYPE_SOURCE: case GST_OSX_AUDIO_DEVICE_TYPE_SOURCE:
element_name = "osxaudiosrc"; element_name = "osxaudiosrc";
@ -582,8 +649,8 @@ gst_osx_audio_device_new (AudioDeviceID device_id, const gchar * device_name,
} }
gstdev = g_object_new (GST_TYPE_OSX_AUDIO_DEVICE, "device-id", gstdev = g_object_new (GST_TYPE_OSX_AUDIO_DEVICE, "device-id",
device_id, "display-name", device_name, "caps", caps, "device-class", device_id, "display-name", device_name, "caps", caps,
klass, NULL); "properties", props, "device-class", klass, NULL);
gstdev->element = element_name; gstdev->element = element_name;

View file

@ -91,6 +91,7 @@ struct _GstCoreAudio
gboolean is_src; gboolean is_src;
gboolean is_passthrough; gboolean is_passthrough;
AudioDeviceID device_id; AudioDeviceID device_id;
gboolean is_default;
gboolean cached_caps_valid; /* thread-safe flag */ gboolean cached_caps_valid; /* thread-safe flag */
GstCaps *cached_caps; GstCaps *cached_caps;
gint stream_idx; gint stream_idx;

View file

@ -1224,13 +1224,12 @@ gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
gboolean output = !core_audio->is_src; gboolean output = !core_audio->is_src;
gboolean res = FALSE; gboolean res = FALSE;
/* Find the ID of the default output device */
AudioDeviceID default_device_id = _audio_system_get_default_device (output);
/* Here we decide if selected device is valid or autoselect /* Here we decide if selected device is valid or autoselect
* the default one when required */ * the default one when required */
if (device_id == kAudioDeviceUnknown) { if (device_id == kAudioDeviceUnknown) {
AudioDeviceID default_device_id;
/* Find the ID of the default output device */
default_device_id = _audio_system_get_default_device (output);
if (default_device_id != kAudioDeviceUnknown) { if (default_device_id != kAudioDeviceUnknown) {
device_id = default_device_id; device_id = default_device_id;
@ -1304,8 +1303,10 @@ gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
} }
} }
if (res) if (res) {
core_audio->device_id = device_id; core_audio->device_id = device_id;
core_audio->is_default = (device_id == default_device_id);
}
return res; return res;
} }

View file

@ -126,6 +126,7 @@ static gboolean
gst_core_audio_select_device_impl (GstCoreAudio * core_audio) gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
{ {
/* No device selection in iOS */ /* No device selection in iOS */
core_audio->is_default = TRUE;
return TRUE; return TRUE;
} }