Skip to content

Commit 1ec928e

Browse files
committed
gstosxaudiodeviceprovider: add device change monitoring.
1 parent b00affa commit 1ec928e

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed

subprojects/gst-plugins-good/sys/osxaudio/gstosxaudiodeviceprovider.c

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ G_DEFINE_TYPE (GstOsxAudioDeviceProvider, gst_osx_audio_device_provider,
5454

5555
static GList *gst_osx_audio_device_provider_probe (GstDeviceProvider *
5656
provider);
57+
static gboolean gst_osx_audio_device_provider_start (GstDeviceProvider * provider);
58+
static void gst_osx_audio_device_provider_stop (GstDeviceProvider * provider);
59+
static OSStatus gst_osx_audio_device_change_cb(AudioObjectID inObjectID,
60+
UInt32 inNumberAddresses,
61+
const AudioObjectPropertyAddress* inAddresses,
62+
void* inClientData);
63+
static void
64+
gst_osx_audio_device_provider_update_devices (GstOsxAudioDeviceProvider * provider);
5765

5866
static void
5967
gst_osx_audio_device_provider_class_init (GstOsxAudioDeviceProviderClass *
@@ -62,18 +70,112 @@ gst_osx_audio_device_provider_class_init (GstOsxAudioDeviceProviderClass *
6270
GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
6371

6472
dm_class->probe = gst_osx_audio_device_provider_probe;
73+
dm_class->start = gst_osx_audio_device_provider_start;
74+
dm_class->stop = gst_osx_audio_device_provider_stop;
6575

6676
gst_device_provider_class_set_static_metadata (dm_class,
6777
"OSX Audio Device Provider", "Source/Sink/Audio",
6878
"List and monitor OSX audio source and sink devices",
6979
"Hyunjun Ko <[email protected]>");
7080
}
7181

82+
static OSStatus gst_osx_audio_device_change_cb(AudioObjectID inObjectID,
83+
guint32 inNumberAddresses,
84+
const AudioObjectPropertyAddress* inAddresses,
85+
void* userdata) {
86+
GstOsxAudioDeviceProvider * provider = (GstOsxAudioDeviceProvider *) userdata;
87+
88+
for (guint32 i = 0; i < inNumberAddresses; i++) {
89+
switch (inAddresses[i].mSelector) {
90+
case kAudioHardwarePropertyDefaultInputDevice:
91+
gst_osx_audio_device_provider_update_devices(provider);
92+
break;
93+
case kAudioHardwarePropertyDefaultOutputDevice:
94+
gst_osx_audio_device_provider_update_devices(provider);
95+
break;
96+
case kAudioHardwarePropertyDevices:
97+
gst_osx_audio_device_provider_update_devices(provider);
98+
break;
99+
default:
100+
break;
101+
}
102+
}
103+
return noErr;
104+
}
105+
72106
static void
73107
gst_osx_audio_device_provider_init (GstOsxAudioDeviceProvider * provider)
74108
{
75109
}
76110

111+
static gboolean
112+
gst_osx_audio_device_provider_start (GstDeviceProvider * provider)
113+
{
114+
GstOsxAudioDeviceProvider *self = GST_OSX_AUDIO_DEVICE_PROVIDER (provider);
115+
116+
// Register callbacks for the following AudioObjectIDs
117+
AudioObjectID event_ids[] = {kAudioHardwarePropertyDevices, kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice};
118+
119+
for (size_t i = 0; i < sizeof(event_ids) / sizeof(event_ids[0]); i++){
120+
AudioObjectPropertyAddress deviceListAddr = {
121+
.mSelector = event_ids[i],
122+
.mScope = kAudioObjectPropertyScopeGlobal,
123+
.mElement = kAudioObjectPropertyElementMain
124+
};
125+
126+
OSStatus err =
127+
AudioObjectAddPropertyListener(kAudioObjectSystemObject,
128+
&deviceListAddr,
129+
gst_osx_audio_device_change_cb,
130+
(void *)self);
131+
132+
if (err != noErr) {
133+
GST_ERROR("Failed to register AudioObjectAddPropertyListener(%u) %d", event_ids[i], err);
134+
return FALSE;
135+
}
136+
}
137+
138+
/* baseclass will not call probe() once it's started, but we can get
139+
* notification only add/remove or change case. To this manually */
140+
GList *devices = gst_osx_audio_device_provider_probe(provider);
141+
if (devices) {
142+
GList *iter;
143+
for (iter = devices; iter; iter = g_list_next (iter)) {
144+
gst_device_provider_device_add (provider, GST_DEVICE (iter->data));
145+
}
146+
g_list_free (devices);
147+
}
148+
149+
return TRUE;
150+
}
151+
152+
static void
153+
gst_osx_audio_device_provider_stop (GstDeviceProvider * provider)
154+
{
155+
GstOsxAudioDeviceProvider *self = GST_OSX_AUDIO_DEVICE_PROVIDER (provider);
156+
157+
// De-register callbacks for the following AudioObjectIDs
158+
AudioObjectID event_ids[] = {kAudioHardwarePropertyDevices, kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice};
159+
160+
for (size_t i = 0; i < sizeof(event_ids) / sizeof(event_ids[0]); i++){
161+
AudioObjectPropertyAddress deviceListAddr = {
162+
.mSelector = event_ids[i],
163+
.mScope = kAudioObjectPropertyScopeGlobal,
164+
.mElement = kAudioObjectPropertyElementMain
165+
};
166+
167+
OSStatus err =
168+
AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
169+
&deviceListAddr,
170+
gst_osx_audio_device_change_cb,
171+
(void *)self);
172+
173+
if (err != noErr) {
174+
GST_ERROR("Failed to de-register AudioObjectAddPropertyListener(%u) %d", event_ids[i], err);
175+
}
176+
}
177+
}
178+
77179
static GstOsxAudioDevice *
78180
gst_osx_audio_device_provider_probe_device (GstOsxAudioDeviceProvider *
79181
provider, AudioDeviceID device_id, const gchar * device_name,
@@ -344,6 +446,99 @@ gst_osx_audio_device_provider_probe (GstDeviceProvider * provider)
344446
return devices;
345447
}
346448

449+
static gboolean
450+
gst_osx_audio_device_is_in_list (GList * list, GstDevice * device)
451+
{
452+
GList *iter;
453+
GstStructure *s;
454+
AudioDeviceID device_id;
455+
gboolean device_is_default;
456+
gboolean found = FALSE;
457+
458+
s = gst_device_get_properties (device);
459+
g_assert (s);
460+
g_assert(gst_structure_get_int (s, "device-id", &device_id) == TRUE);
461+
g_assert(gst_structure_get_boolean (s, "is-default", &device_is_default) == TRUE);
462+
463+
for (iter = list; iter; iter = g_list_next (iter)) {
464+
GstStructure *other_s;
465+
AudioDeviceID other_device_id;
466+
gboolean other_device_is_default;
467+
468+
other_s = gst_device_get_properties (GST_DEVICE (iter->data));
469+
g_assert (other_s);
470+
471+
g_assert(gst_structure_get_int (other_s, "device-id", &other_device_id) == TRUE);
472+
g_assert(gst_structure_get_boolean (other_s, "is-default", &other_device_is_default) == TRUE);
473+
474+
if (device_id == other_device_id && device_is_default == other_device_is_default) {
475+
found = TRUE;
476+
}
477+
478+
gst_structure_free (other_s);
479+
if (found)
480+
break;
481+
}
482+
483+
gst_structure_free (s);
484+
485+
return found;
486+
}
487+
488+
static void
489+
gst_osx_audio_device_provider_update_devices (GstOsxAudioDeviceProvider * self)
490+
{
491+
GstDeviceProvider *provider = GST_DEVICE_PROVIDER_CAST (self);
492+
GList *prev_devices = NULL;
493+
GList *new_devices = NULL;
494+
GList *to_add = NULL;
495+
GList *to_remove = NULL;
496+
GList *iter;
497+
498+
GST_OBJECT_LOCK (self);
499+
prev_devices = g_list_copy_deep (provider->devices,
500+
(GCopyFunc) gst_object_ref, NULL);
501+
GST_OBJECT_UNLOCK (self);
502+
503+
new_devices = gst_osx_audio_device_provider_probe (provider);
504+
505+
/* Ownership of GstDevice for gst_device_provider_device_add()
506+
* and gst_device_provider_device_remove() is a bit complicated.
507+
* Remove floating reference here for things to be clear */
508+
for (iter = new_devices; iter; iter = g_list_next (iter))
509+
gst_object_ref_sink (iter->data);
510+
511+
/* Check newly added devices */
512+
for (iter = new_devices; iter; iter = g_list_next (iter)) {
513+
if (!gst_osx_audio_device_is_in_list (prev_devices, GST_DEVICE (iter->data))) {
514+
to_add = g_list_prepend (to_add, gst_object_ref (iter->data));
515+
}
516+
}
517+
518+
/* Check removed device */
519+
for (iter = prev_devices; iter; iter = g_list_next (iter)) {
520+
if (!gst_osx_audio_device_is_in_list (new_devices, GST_DEVICE (iter->data))) {
521+
to_remove = g_list_prepend (to_remove, gst_object_ref (iter->data));
522+
}
523+
}
524+
525+
for (iter = to_remove; iter; iter = g_list_next (iter))
526+
gst_device_provider_device_remove (provider, GST_DEVICE (iter->data));
527+
528+
for (iter = to_add; iter; iter = g_list_next (iter))
529+
gst_device_provider_device_add (provider, GST_DEVICE (iter->data));
530+
531+
if (prev_devices)
532+
g_list_free_full (prev_devices, (GDestroyNotify) gst_object_unref);
533+
534+
if (to_add)
535+
g_list_free_full (to_add, (GDestroyNotify) gst_object_unref);
536+
537+
if (to_remove)
538+
g_list_free_full (to_remove, (GDestroyNotify) gst_object_unref);
539+
}
540+
541+
347542
enum
348543
{
349544
PROP_DEVICE_ID = 1,
@@ -430,6 +625,7 @@ gst_osx_audio_device_new (AudioDeviceID device_id, const gchar * device_name,
430625
}
431626

432627
GstStructure *props = gst_structure_new ("osxaudiodevice-proplist",
628+
"device-id", G_TYPE_INT, device_id,
433629
"is-default", G_TYPE_BOOLEAN, is_default, NULL);
434630
gstdev = g_object_new (GST_TYPE_OSX_AUDIO_DEVICE, "device-id",
435631
device_id, "display-name", device_name, "caps", caps, "device-class",

0 commit comments

Comments
 (0)