@@ -54,6 +54,14 @@ G_DEFINE_TYPE (GstOsxAudioDeviceProvider, gst_osx_audio_device_provider,
54
54
55
55
static GList * gst_osx_audio_device_provider_probe (GstDeviceProvider *
56
56
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 );
57
65
58
66
static void
59
67
gst_osx_audio_device_provider_class_init (GstOsxAudioDeviceProviderClass *
@@ -62,18 +70,112 @@ gst_osx_audio_device_provider_class_init (GstOsxAudioDeviceProviderClass *
62
70
GstDeviceProviderClass * dm_class = GST_DEVICE_PROVIDER_CLASS (klass );
63
71
64
72
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 ;
65
75
66
76
gst_device_provider_class_set_static_metadata (dm_class ,
67
77
"OSX Audio Device Provider" , "Source/Sink/Audio" ,
68
78
"List and monitor OSX audio source and sink devices" ,
69
79
"Hyunjun Ko <[email protected] >" );
70
80
}
71
81
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
+
72
106
static void
73
107
gst_osx_audio_device_provider_init (GstOsxAudioDeviceProvider * provider )
74
108
{
75
109
}
76
110
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
+
77
179
static GstOsxAudioDevice *
78
180
gst_osx_audio_device_provider_probe_device (GstOsxAudioDeviceProvider *
79
181
provider , AudioDeviceID device_id , const gchar * device_name ,
@@ -344,6 +446,99 @@ gst_osx_audio_device_provider_probe (GstDeviceProvider * provider)
344
446
return devices ;
345
447
}
346
448
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
+
347
542
enum
348
543
{
349
544
PROP_DEVICE_ID = 1 ,
@@ -430,6 +625,7 @@ gst_osx_audio_device_new (AudioDeviceID device_id, const gchar * device_name,
430
625
}
431
626
432
627
GstStructure * props = gst_structure_new ("osxaudiodevice-proplist" ,
628
+ "device-id" , G_TYPE_INT , device_id ,
433
629
"is-default" , G_TYPE_BOOLEAN , is_default , NULL );
434
630
gstdev = g_object_new (GST_TYPE_OSX_AUDIO_DEVICE , "device-id" ,
435
631
device_id , "display-name" , device_name , "caps" , caps , "device-class" ,
0 commit comments