osxaudiosrc: Support a device as both input and output
authorJoseph Donofry <rubberduckie3554@gmail.com>
Mon, 6 Dec 2021 17:49:18 +0000 (12:49 -0500)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 28 Feb 2022 06:51:21 +0000 (06:51 +0000)
osxaudiodeviceprovider now probes devices more than once to determine
if the device can function as both an input AND and output device.

Previously, if the device provider detected that a device had any output
capabilities, it was treated solely as an Audio/Sink.  This causes issues
that have both input and output capabilities (for example, USB interfaces
for professional audio have both input and output channels).  Such devices
are now listed as both an Audio/Sink as well as an Audio/Source.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1385>

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

index 52a222f..aa459f7 100644 (file)
@@ -150,10 +150,40 @@ _audio_device_has_output (AudioDeviceID device_id)
 
   status = AudioObjectGetPropertyDataSize (device_id,
       &streamsAddress, 0, NULL, &propertySize);
+
   if (status != noErr) {
+    GST_WARNING ("failed getting device property: %d", (int) status);
     return FALSE;
   }
   if (propertySize == 0) {
+    GST_DEBUG ("property size was 0; device has no output channels");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static inline gboolean
+_audio_device_has_input (AudioDeviceID device_id)
+{
+  OSStatus status = noErr;
+  UInt32 propertySize;
+
+  AudioObjectPropertyAddress streamsAddress = {
+    kAudioDevicePropertyStreams,
+    kAudioDevicePropertyScopeInput,
+    kAudioObjectPropertyElementMaster
+  };
+
+  status = AudioObjectGetPropertyDataSize (device_id,
+      &streamsAddress, 0, NULL, &propertySize);
+
+  if (status != noErr) {
+    GST_WARNING ("failed getting device property: %d", (int) status);
+    return FALSE;
+  }
+  if (propertySize == 0) {
+    GST_DEBUG ("property size was 0; device has no input channels");
     return FALSE;
   }
 
@@ -197,51 +227,77 @@ _audio_system_get_devices (gint * ndevices)
   return devices;
 }
 
-static GList *
-gst_osx_audio_device_provider_probe (GstDeviceProvider * provider)
+static void
+gst_osx_audio_device_provider_probe_internal (GstOsxAudioDeviceProvider * self,
+    gboolean is_src, AudioDeviceID * osx_devices, gint ndevices,
+    GList ** devices)
 {
-  GstOsxAudioDeviceProvider *self = GST_OSX_AUDIO_DEVICE_PROVIDER (provider);
-  GList *devices = NULL;
-  GstOsxAudioDevice *device = NULL;
-  AudioDeviceID *osx_devices = NULL;
-  gint i, ndevices = 0;
 
-  osx_devices = _audio_system_get_devices (&ndevices);
+  gint i = 0;
+  GstOsxAudioDeviceType type = GST_OSX_AUDIO_DEVICE_TYPE_INVALID;
+  GstOsxAudioDevice *device = NULL;
 
-  if (ndevices < 1) {
-    GST_WARNING ("no audio output devices found");
-    goto done;
+  if (is_src) {
+    type = GST_OSX_AUDIO_DEVICE_TYPE_SOURCE;
+  } else {
+    type = GST_OSX_AUDIO_DEVICE_TYPE_SINK;
   }
 
-  GST_INFO ("found %d audio device(s)", ndevices);
-
   for (i = 0; i < ndevices; i++) {
     gchar *device_name;
-    GstOsxAudioDeviceType type = GST_OSX_AUDIO_DEVICE_TYPE_INVALID;
 
     if ((device_name = _audio_device_get_name (osx_devices[i], FALSE))) {
-      if (!_audio_device_has_output (osx_devices[i])) {
-        GST_DEBUG ("Input Device ID: %u Name: %s",
-            (unsigned) osx_devices[i], device_name);
-        type = GST_OSX_AUDIO_DEVICE_TYPE_SOURCE;
-
-      } else {
-        GST_DEBUG ("Output Device ID: %u Name: %s",
-            (unsigned) osx_devices[i], device_name);
-        type = GST_OSX_AUDIO_DEVICE_TYPE_SINK;
+      gboolean has_output = _audio_device_has_output (osx_devices[i]);
+      gboolean has_input = _audio_device_has_input (osx_devices[i]);
+
+      if (is_src && !has_input) {
+        goto cleanup;
+      } else if (!is_src && !has_output) {
+        goto cleanup;
       }
 
       device =
           gst_osx_audio_device_provider_probe_device (self, osx_devices[i],
           device_name, type);
       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_object_ref_sink (device);
-        devices = g_list_prepend (devices, device);
+        *devices = g_list_prepend (*devices, device);
       }
 
+    cleanup:
       g_free (device_name);
     }
   }
+}
+
+static GList *
+gst_osx_audio_device_provider_probe (GstDeviceProvider * provider)
+{
+  GstOsxAudioDeviceProvider *self = GST_OSX_AUDIO_DEVICE_PROVIDER (provider);
+  GList *devices = NULL;
+  AudioDeviceID *osx_devices = NULL;
+  gint ndevices = 0;
+
+  osx_devices = _audio_system_get_devices (&ndevices);
+
+  if (ndevices < 1) {
+    GST_WARNING ("no audio output devices found");
+    goto done;
+  }
+
+  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);
 
 done:
   g_free (osx_devices);