osxaudio: Implement unique-id property on elements

The actual value is stored on GstCoreAudio now, which involved a lot
of moving code around due to the strange layering in the plugin.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5274>
This commit is contained in:
Nirbheek Chauhan 2024-07-31 00:09:21 +05:30 committed by GStreamer Marge Bot
parent 81e3f7b4a4
commit 314d67b8cc
10 changed files with 203 additions and 146 deletions

View file

@ -93,15 +93,19 @@ gst_osx_audio_device_provider_init (GstOsxAudioDeviceProvider * provider)
static GstOsxAudioDevice *
gst_osx_audio_device_provider_probe_device (GstOsxAudioDeviceProvider *
provider, AudioDeviceID device_id, const gchar * device_name,
GstOsxAudioDeviceType type, gboolean is_default)
GstOsxAudioDeviceType type)
{
GstOsxAudioDevice *device = NULL;
GstCoreAudio *core_audio;
gboolean is_src = type == GST_OSX_AUDIO_DEVICE_TYPE_SOURCE ? TRUE : FALSE;
core_audio = gst_core_audio_new (NULL);
core_audio->is_src = type == GST_OSX_AUDIO_DEVICE_TYPE_SOURCE ? TRUE : FALSE;
core_audio->device_id = device_id;
core_audio->is_default = is_default;
core_audio = g_object_new (GST_TYPE_CORE_AUDIO,
"is-src", is_src, "device", device_id, NULL);
if (!gst_core_audio_select_device (core_audio)) {
GST_ERROR ("CoreAudio device coult not be selected");
goto done;
}
if (!gst_core_audio_open (core_audio)) {
GST_ERROR ("CoreAudio device could not be opened");
@ -118,69 +122,6 @@ done:
return device;
}
static inline gchar *
_audio_device_get_cfstring_prop (AudioDeviceID device_id, gboolean output,
AudioObjectPropertyElement prop_id)
{
OSStatus status = noErr;
UInt32 propertySize = 0;
CFStringRef prop_val;
gchar *result = NULL;
AudioObjectPropertyAddress propAddress = {
prop_id,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMain
};
propAddress.mScope = output ? kAudioDevicePropertyScopeOutput :
kAudioDevicePropertyScopeInput;
/* Get the length of the device name */
status = AudioObjectGetPropertyDataSize (device_id,
&propAddress, 0, NULL, &propertySize);
if (status != noErr) {
goto beach;
}
/* Get the requested property */
status = AudioObjectGetPropertyData (device_id,
&propAddress, 0, NULL, &propertySize, &prop_val);
if (status != noErr) {
goto beach;
}
/* 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:
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
_audio_device_has_output (AudioDeviceID device_id)
{
@ -361,53 +302,42 @@ _stop_audio_device_watcher (GstOsxAudioDeviceProvider * self)
static void
gst_osx_audio_device_provider_probe_internal (GstOsxAudioDeviceProvider * self,
gboolean is_src, AudioDeviceID * osx_devices, gint ndevices,
GList ** devices)
AudioDeviceID * osx_devices, gint ndevices, GList ** devices)
{
gint i = 0;
GstOsxAudioDeviceType type = GST_OSX_AUDIO_DEVICE_TYPE_INVALID;
GstOsxAudioDevice *device = NULL;
for (int i = 0; i < ndevices; i++) {
char *device_name;
GstOsxAudioDevice *device;
if (is_src) {
type = GST_OSX_AUDIO_DEVICE_TYPE_SOURCE;
} else {
type = GST_OSX_AUDIO_DEVICE_TYPE_SINK;
}
AudioDeviceID default_device = _audio_system_get_default_device (!is_src);
for (i = 0; i < ndevices; i++) {
gchar *device_name;
if ((device_name = _audio_device_get_name (osx_devices[i], FALSE))) {
gboolean has_output = _audio_device_has_output (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) {
goto cleanup;
} else if (!is_src && !has_output) {
goto cleanup;
}
device_name = gst_core_audio_device_get_prop (osx_devices[i],
kAudioObjectPropertyName);
if (!device_name)
continue;
if (_audio_device_has_input (osx_devices[i])) {
device =
gst_osx_audio_device_provider_probe_device (self, osx_devices[i],
device_name, type, is_default);
device_name, GST_OSX_AUDIO_DEVICE_TYPE_SOURCE);
if (device) {
if (is_src) {
GST_DEBUG ("Input Device ID: %u Name: %s",
(unsigned) osx_devices[i], device_name);
} else {
GST_DEBUG ("Output Device ID: %u Name: %s",
(unsigned) osx_devices[i], device_name);
}
GST_DEBUG ("Input Device ID: %u Name: %s", (unsigned) osx_devices[i],
device_name);
gst_object_ref_sink (device);
*devices = g_list_prepend (*devices, device);
}
cleanup:
g_free (device_name);
}
if (_audio_device_has_output (osx_devices[i])) {
device =
gst_osx_audio_device_provider_probe_device (self, osx_devices[i],
device_name, GST_OSX_AUDIO_DEVICE_TYPE_SINK);
if (device) {
GST_DEBUG ("Output Device ID: %u Name: %s", (unsigned) osx_devices[i],
device_name);
gst_object_ref_sink (device);
*devices = g_list_prepend (*devices, device);
}
}
g_free (device_name);
}
}
@ -428,10 +358,8 @@ gst_osx_audio_device_provider_probe (GstDeviceProvider * provider)
GST_INFO ("found %d audio device(s)", ndevices);
gst_osx_audio_device_provider_probe_internal (self, TRUE, osx_devices,
ndevices, &devices);
gst_osx_audio_device_provider_probe_internal (self, FALSE, osx_devices,
ndevices, &devices);
gst_osx_audio_device_provider_probe_internal (self, osx_devices, ndevices,
&devices);
done:
g_free (osx_devices);
@ -618,10 +546,9 @@ gst_osx_audio_device_new (AudioDeviceID device_id, const gchar * device_name,
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);
if (core_audio->unique_id != NULL) {
gst_structure_set (props, "unique-id", G_TYPE_STRING,
core_audio->unique_id, NULL);
}
switch (type) {

View file

@ -121,7 +121,6 @@ gst_osx_audio_ring_buffer_class_init (GstOsxAudioRingBufferClass * klass)
static void
gst_osx_audio_ring_buffer_init (GstOsxAudioRingBuffer * ringbuffer)
{
ringbuffer->core_audio = gst_core_audio_new (GST_OBJECT (ringbuffer));
}
static void

View file

@ -88,7 +88,8 @@ enum
{
ARG_0,
ARG_DEVICE,
ARG_VOLUME
ARG_VOLUME,
ARG_UNIQUE_ID
};
#define DEFAULT_VOLUME 1.0
@ -176,6 +177,14 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
g_object_class_install_property (gobject_class, ARG_DEVICE,
g_param_spec_int ("device", "Device ID", "Device ID of output device",
0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, ARG_UNIQUE_ID,
g_param_spec_string ("unique-id", "Unique ID",
"Unique persistent ID for the input device",
NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
#endif
gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_query);
@ -257,6 +266,7 @@ gst_osx_audio_sink_change_state (GstElement * element,
GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SINK (osxsink)->ringbuffer);
if (ringbuffer->core_audio->device_id != osxsink->device_id) {
osxsink->device_id = ringbuffer->core_audio->device_id;
osxsink->unique_id = ringbuffer->core_audio->unique_id;
g_object_notify (G_OBJECT (osxsink), "device");
}
break;
@ -279,6 +289,9 @@ gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
case ARG_DEVICE:
g_value_set_int (value, sink->device_id);
break;
case ARG_UNIQUE_ID:
g_value_set_string (value, sink->unique_id);
break;
#endif
case ARG_VOLUME:
g_value_set_double (value, sink->volume);
@ -493,16 +506,11 @@ gst_osx_audio_sink_create_ringbuffer (GstAudioBaseSink * sink)
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
(void *) gst_osx_audio_sink_io_proc);
ringbuffer->core_audio = g_object_new (GST_TYPE_CORE_AUDIO,
"is-src", FALSE, "device", osxsink->device_id, NULL);
ringbuffer->core_audio->osxbuf = GST_OBJECT (ringbuffer);
ringbuffer->core_audio->element =
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
ringbuffer->core_audio->is_src = FALSE;
/* By default the coreaudio instance created by the ringbuffer
* has device_id==kAudioDeviceUnknown. The user might have
* selected a different one here
*/
if (ringbuffer->core_audio->device_id != osxsink->device_id)
ringbuffer->core_audio->device_id = osxsink->device_id;
return GST_AUDIO_RING_BUFFER (ringbuffer);
}

View file

@ -82,6 +82,7 @@ struct _GstOsxAudioSink
GstAudioBaseSink sink;
AudioDeviceID device_id;
const char *unique_id;
AudioUnit audiounit;
double volume;

View file

@ -76,7 +76,8 @@ enum
enum
{
ARG_0,
ARG_DEVICE
ARG_DEVICE,
ARG_UNIQUE_ID,
};
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
@ -151,6 +152,14 @@ gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass)
g_param_spec_int ("device", "Device ID", "Device ID of input device",
0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, ARG_UNIQUE_ID,
g_param_spec_string ("unique-id", "Unique ID",
"Unique persistent ID for the input device",
NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gstaudiobasesrc_class->create_ringbuffer =
GST_DEBUG_FUNCPTR (gst_osx_audio_src_create_ringbuffer);
@ -168,6 +177,7 @@ gst_osx_audio_src_init (GstOsxAudioSrc * src)
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
src->device_id = kAudioDeviceUnknown;
src->unique_id = NULL;
}
static void
@ -196,6 +206,9 @@ gst_osx_audio_src_get_property (GObject * object, guint prop_id,
case ARG_DEVICE:
g_value_set_int (value, src->device_id);
break;
case ARG_UNIQUE_ID:
g_value_set_string (value, src->unique_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -225,6 +238,7 @@ gst_osx_audio_src_change_state (GstElement * element, GstStateChange transition)
GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SRC (osxsrc)->ringbuffer);
if (ringbuffer->core_audio->device_id != osxsrc->device_id) {
osxsrc->device_id = ringbuffer->core_audio->device_id;
osxsrc->unique_id = ringbuffer->core_audio->unique_id;
g_object_notify (G_OBJECT (osxsrc), "device");
}
break;
@ -314,16 +328,11 @@ gst_osx_audio_src_create_ringbuffer (GstAudioBaseSrc * src)
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
(void *) gst_osx_audio_src_io_proc);
ringbuffer->core_audio = g_object_new (GST_TYPE_CORE_AUDIO,
"is-src", TRUE, "device", osxsrc->device_id, NULL);
ringbuffer->core_audio->osxbuf = GST_OBJECT (ringbuffer);
ringbuffer->core_audio->element =
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
ringbuffer->core_audio->is_src = TRUE;
/* By default the coreaudio instance created by the ringbuffer
* has device_id==kAudioDeviceUnknown. The user might have
* selected a different one here
*/
if (ringbuffer->core_audio->device_id != osxsrc->device_id)
ringbuffer->core_audio->device_id = osxsrc->device_id;
return GST_AUDIO_RING_BUFFER (ringbuffer);
}

View file

@ -73,6 +73,7 @@ struct _GstOsxAudioSrc
GstAudioBaseSrc src;
AudioDeviceID device_id;
const char *unique_id;
};
struct _GstOsxAudioSrcClass

View file

@ -23,6 +23,7 @@
#include "gstosxcoreaudio.h"
#include "gstosxcoreaudiocommon.h"
#include <CoreAudio/CoreAudio.h>
GST_DEBUG_CATEGORY (osx_coreaudio_debug);
#define GST_CAT_DEFAULT osx_coreaudio_debug
@ -35,11 +36,24 @@ G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
#include "gstosxcoreaudiohal.c"
#endif
enum
{
PROP_0,
PROP_DEVICE,
PROP_IS_SRC,
};
static void gst_core_audio_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_core_audio_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
gst_core_audio_finalize (GObject * object)
{
GstCoreAudio *core_audio = GST_CORE_AUDIO (object);
g_mutex_clear (&core_audio->timing_lock);
g_free (core_audio->unique_id);
G_OBJECT_CLASS (gst_core_audio_parent_class)->finalize (object);
}
@ -49,6 +63,19 @@ gst_core_audio_class_init (GstCoreAudioClass * klass)
{
GObjectClass *object_klass = G_OBJECT_CLASS (klass);
object_klass->finalize = gst_core_audio_finalize;
object_klass->set_property = gst_core_audio_set_property;
object_klass->get_property = gst_core_audio_get_property;
g_object_class_install_property (object_klass, PROP_DEVICE,
g_param_spec_int ("device", "Device ID", "Device ID of input device",
0, G_MAXINT, 0,
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_klass, PROP_IS_SRC,
g_param_spec_boolean ("is-src", "Is source", "Is a source device",
FALSE,
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
@ -56,6 +83,7 @@ gst_core_audio_init (GstCoreAudio * core_audio)
{
core_audio->is_passthrough = FALSE;
core_audio->device_id = kAudioDeviceUnknown;
core_audio->unique_id = NULL;
core_audio->is_src = FALSE;
core_audio->audiounit = NULL;
core_audio->cached_caps = NULL;
@ -69,6 +97,44 @@ gst_core_audio_init (GstCoreAudio * core_audio)
g_mutex_init (&core_audio->timing_lock);
}
static void
gst_core_audio_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstCoreAudio *self = GST_CORE_AUDIO (object);
switch (prop_id) {
case PROP_IS_SRC:
self->is_src = g_value_get_boolean (value);
break;
case PROP_DEVICE:
self->device_id = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_core_audio_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstCoreAudio *self = GST_CORE_AUDIO (object);
switch (prop_id) {
case PROP_IS_SRC:
g_value_set_boolean (value, self->is_src);
break;
case PROP_DEVICE:
g_value_set_int (value, self->device_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
_is_outer_scope (AudioUnitScope scope, AudioUnitElement element)
{
@ -128,17 +194,6 @@ _host_time_to_ns (GstCoreAudio * core_audio, uint64_t host_time)
* Public API *
*************************/
GstCoreAudio *
gst_core_audio_new (GstObject * osxbuf)
{
GstCoreAudio *core_audio;
core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
core_audio->osxbuf = osxbuf;
core_audio->cached_caps = NULL;
return core_audio;
}
gboolean
gst_core_audio_close (GstCoreAudio * core_audio)
{
@ -368,7 +423,14 @@ gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume)
gboolean
gst_core_audio_select_device (GstCoreAudio * core_audio)
{
return gst_core_audio_select_device_impl (core_audio);
gboolean ret = gst_core_audio_select_device_impl (core_audio);
if (core_audio->device_id != kAudioDeviceUnknown)
core_audio->unique_id =
gst_core_audio_device_get_prop (core_audio->device_id,
kAudioDevicePropertyDeviceUID);
return ret;
}
void

View file

@ -91,6 +91,7 @@ struct _GstCoreAudio
gboolean is_src;
gboolean is_passthrough;
AudioDeviceID device_id;
char *unique_id;
gboolean is_default;
gboolean cached_caps_valid; /* thread-safe flag */
GstCaps *cached_caps;
@ -129,8 +130,6 @@ GType gst_core_audio_get_type (void);
void gst_core_audio_init_debug (void);
GstCoreAudio * gst_core_audio_new (GstObject *osxbuf);
gboolean gst_core_audio_open (GstCoreAudio *core_audio);
gboolean gst_core_audio_close (GstCoreAudio *core_audio);

View file

@ -559,3 +559,51 @@ gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
channel_desc->mCoordinates[2]);
}
}
char *
gst_core_audio_device_get_prop (AudioDeviceID device_id,
AudioObjectPropertyElement prop_id)
{
OSStatus status = noErr;
UInt32 propertySize = 0;
CFStringRef prop_val;
gchar *result = NULL;
AudioObjectPropertyAddress propAddress = {
prop_id,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMain
};
propAddress.mScope = kAudioObjectPropertyScopeGlobal;
/* Get the length of the device name */
status = AudioObjectGetPropertyDataSize (device_id,
&propAddress, 0, NULL, &propertySize);
if (status != noErr) {
goto beach;
}
/* Get the requested property */
status = AudioObjectGetPropertyData (device_id,
&propAddress, 0, NULL, &propertySize, &prop_val);
if (status != noErr) {
goto beach;
}
/* 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:
return result;
}

View file

@ -69,4 +69,7 @@ AudioChannelLabel gst_audio_channel_position_to_core_audio (GstAudioChannelPosit
GstAudioChannelPosition gst_core_audio_channel_label_to_gst (AudioChannelLabel label, int channel, gboolean warn);
char * gst_core_audio_device_get_prop (AudioDeviceID device_id,
AudioObjectPropertyElement prop_id);
G_END_DECLS