decklink: Add simple device provider for Decklink devices
authorSebastian Dröge <sebastian@centricular.com>
Thu, 21 Nov 2019 14:59:29 +0000 (16:59 +0200)
committerSebastian Dröge <slomo@coaxion.net>
Tue, 3 Dec 2019 09:16:22 +0000 (09:16 +0000)
sys/decklink/gstdecklink.cpp
sys/decklink/gstdecklink.h
sys/decklink/gstdecklinkdeviceprovider.cpp [new file with mode: 0644]
sys/decklink/gstdecklinkdeviceprovider.h [new file with mode: 0644]
sys/decklink/meson.build

index 0fed8b0194d479fbae3f58bc75cb2ca2f1d119df..25493ce2a9e0a336b18c91fb5fea04a592783e02 100644 (file)
@@ -29,6 +29,7 @@
 #include "gstdecklinkvideosink.h"
 #include "gstdecklinkaudiosrc.h"
 #include "gstdecklinkvideosrc.h"
+#include "gstdecklinkdeviceprovider.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug);
 #define GST_CAT_DEFAULT gst_decklink_debug
@@ -481,7 +482,7 @@ gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode)
       displayMode = GST_DECKLINK_MODE_2160p60;
       break;
     default:
-      g_assert_not_reached ();
+      displayMode = (GstDecklinkModeEnum) - 1;
       break;
   }
   return displayMode;
@@ -621,8 +622,7 @@ gst_decklink_caps_get_pixel_format (GstCaps * caps, BMDPixelFormat * format)
 }
 
 static GstStructure *
-gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f,
-    gboolean input)
+gst_decklink_mode_get_generic_structure (GstDecklinkModeEnum e)
 {
   const GstDecklinkMode *mode = &modes[e];
   GstStructure *s = gst_structure_new ("video/x-raw",
@@ -633,6 +633,16 @@ gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f,
       mode->interlaced ? "interleaved" : "progressive",
       "framerate", GST_TYPE_FRACTION, mode->fps_n, mode->fps_d, NULL);
 
+  return s;
+}
+
+static GstStructure *
+gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f,
+    gboolean input)
+{
+  const GstDecklinkMode *mode = &modes[e];
+  GstStructure *s = gst_decklink_mode_get_generic_structure (e);
+
   if (input && mode->interlaced) {
     if (mode->tff)
       gst_structure_set (s, "field-order", G_TYPE_STRING, "top-field-first",
@@ -801,6 +811,9 @@ struct _Device
 {
   GstDecklinkOutput output;
   GstDecklinkInput input;
+
+  /* Audio/video output, Audio/video input */
+  GstDecklinkDevice *devices[4];
 };
 
 DuplexModeSetOperationResult gst_decklink_configure_duplex_mode (Device *
@@ -1261,6 +1274,76 @@ gst_decklink_com_thread (gpointer data)
 static GOnce devices_once = G_ONCE_INIT;
 static GPtrArray *devices;      /* array of Device */
 
+
+static GstDecklinkDevice *
+gst_decklink_device_new (const gchar * model_name, const gchar * display_name,
+    const gchar * serial_number, gboolean supports_format_detection,
+    GstCaps * video_caps, guint max_channels, gboolean video, gboolean capture,
+    guint device_number)
+{
+  GstDevice *ret;
+  gchar *name;
+  const gchar *device_class;
+  GstCaps *caps = NULL;
+  GstStructure *properties;
+
+  if (capture)
+    device_class = video ? "Video/Source/Hardware" : "Audio/Source/Hardware";
+  else
+    device_class = video ? "Video/Sink/Hardware" : "Audio/Sink/Hardware";
+
+  name =
+      g_strdup_printf ("%s (%s %s)", display_name,
+      video ? "Video" : "Audio", capture ? "Capture" : "Output");
+
+  if (video) {
+    caps = gst_caps_ref (video_caps);
+  } else {
+    static GstStaticCaps audio_caps =
+        GST_STATIC_CAPS
+        ("audio/x-raw, format={S16LE,S32LE}, channels={2, 8, 16}, rate=48000, "
+        "layout=interleaved");
+    GstCaps *max_channel_caps =
+        gst_caps_new_simple ("audio/x-raw", "channels", GST_TYPE_INT_RANGE, 2,
+        max_channels, NULL);
+
+    caps =
+        gst_caps_intersect (gst_static_caps_get (&audio_caps),
+        max_channel_caps);
+    gst_caps_unref (max_channel_caps);
+  }
+  properties = gst_structure_new_empty ("properties");
+
+  gst_structure_set (properties,
+      "device-number", G_TYPE_UINT, device_number,
+      "model-name", G_TYPE_STRING, model_name,
+      "display-name", G_TYPE_STRING, display_name,
+      "max-channels", G_TYPE_UINT, max_channels, NULL);
+
+  if (capture)
+    gst_structure_set (properties, "supports-format-detection", G_TYPE_BOOLEAN,
+        supports_format_detection, NULL);
+
+  if (serial_number)
+    gst_structure_set (properties, "serial-number", G_TYPE_STRING,
+        serial_number, NULL);
+
+  ret = GST_DEVICE (g_object_new (GST_TYPE_DECKLINK_DEVICE,
+          "display-name", name,
+          "device-class", device_class, "caps", caps, "properties", properties,
+          NULL));
+
+  g_free (name);
+  gst_caps_unref (caps);
+  gst_structure_free (properties);
+
+  GST_DECKLINK_DEVICE (ret)->video = video;
+  GST_DECKLINK_DEVICE (ret)->capture = capture;
+  GST_DECKLINK_DEVICE (ret)->device_number = device_number;
+
+  return GST_DECKLINK_DEVICE (ret);
+}
+
 static gpointer
 init_devices (gpointer data)
 {
@@ -1294,6 +1377,15 @@ init_devices (gpointer data)
   ret = iterator->Next (&decklink);
   while (ret == S_OK) {
     Device *dev;
+    gboolean capture = FALSE;
+    gboolean output = FALSE;
+    gchar *model_name = NULL;
+    gchar *display_name = NULL;
+    gchar *serial_number = NULL;
+    gboolean supports_format_detection = 0;
+    gint64 max_channels = 2;
+    GstCaps *video_input_caps = gst_caps_new_empty ();
+    GstCaps *video_output_caps = gst_caps_new_empty ();
 
     dev = g_new0 (Device, 1);
 
@@ -1319,6 +1411,14 @@ init_devices (gpointer data)
         GST_DEBUG ("Input %d supports:", i);
         while ((ret = mode_iter->Next (&mode)) == S_OK) {
           char *name;
+          GstDecklinkModeEnum mode_enum;
+
+          mode_enum =
+              gst_decklink_get_mode_enum_from_bmd (mode->GetDisplayMode ());
+          if (mode_enum != (GstDecklinkModeEnum) - 1)
+            video_input_caps =
+                gst_caps_merge_structure (video_input_caps,
+                gst_decklink_mode_get_generic_structure (mode_enum));
 
           mode->GetName ((COMSTR_T *) & name);
           CONVERT_COM_STRING (name);
@@ -1332,6 +1432,9 @@ init_devices (gpointer data)
         }
         mode_iter->Release ();
       }
+
+      capture = TRUE;
+
       ret = S_OK;
     }
 
@@ -1355,6 +1458,14 @@ init_devices (gpointer data)
         GST_DEBUG ("Output %d supports:", i);
         while ((ret = mode_iter->Next (&mode)) == S_OK) {
           char *name;
+          GstDecklinkModeEnum mode_enum;
+
+          mode_enum =
+              gst_decklink_get_mode_enum_from_bmd (mode->GetDisplayMode ());
+          if (mode_enum != (GstDecklinkModeEnum) - 1)
+            video_output_caps =
+                gst_caps_merge_structure (video_output_caps,
+                gst_decklink_mode_get_generic_structure (mode_enum));
 
           mode->GetName ((COMSTR_T *) & name);
           CONVERT_COM_STRING (name);
@@ -1368,6 +1479,9 @@ init_devices (gpointer data)
         }
         mode_iter->Release ();
       }
+
+      output = TRUE;
+
       ret = S_OK;
     }
 
@@ -1377,8 +1491,6 @@ init_devices (gpointer data)
       GST_WARNING ("selected device does not have config interface: 0x%08lx",
           (unsigned long) ret);
     } else {
-      char *serial_number;
-
       ret =
           dev->input.
           config->GetString (bmdDeckLinkConfigDeviceInformationSerialNumber,
@@ -1388,7 +1500,6 @@ init_devices (gpointer data)
         dev->output.hw_serial_number = g_strdup (serial_number);
         dev->input.hw_serial_number = g_strdup (serial_number);
         GST_DEBUG ("device %d has serial number %s", i, serial_number);
-        FREE_COM_STRING (serial_number);
       }
     }
 
@@ -1398,8 +1509,55 @@ init_devices (gpointer data)
     if (ret != S_OK) {
       GST_WARNING ("selected device does not have attributes interface: "
           "0x%08lx", (unsigned long) ret);
+    } else {
+      bool tmp_bool = false;
+      int64_t tmp_int = 2;
+
+      dev->input.attributes->GetInt (BMDDeckLinkMaximumAudioChannels,
+          &tmp_int);
+      dev->input.attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection,
+          &tmp_bool);
+      supports_format_detection = tmp_bool;
+      max_channels = tmp_int;
+    }
+
+    decklink->GetModelName ((COMSTR_T *) & model_name);
+    if (model_name)
+      CONVERT_COM_STRING (model_name);
+    decklink->GetDisplayName ((COMSTR_T *) & display_name);
+    if (display_name)
+      CONVERT_COM_STRING (display_name);
+
+    if (capture) {
+      dev->devices[0] =
+          gst_decklink_device_new (model_name, display_name, serial_number,
+          supports_format_detection, video_input_caps, max_channels, TRUE, TRUE,
+          i);
+      dev->devices[1] =
+          gst_decklink_device_new (model_name, display_name, serial_number,
+          supports_format_detection, video_input_caps, max_channels, FALSE,
+          TRUE, i);
+    }
+    if (output) {
+      dev->devices[2] =
+          gst_decklink_device_new (model_name, display_name, serial_number,
+          supports_format_detection, video_output_caps, max_channels, TRUE,
+          FALSE, i);
+      dev->devices[3] =
+          gst_decklink_device_new (model_name, display_name, serial_number,
+          supports_format_detection, video_output_caps, max_channels, FALSE,
+          FALSE, i);
     }
 
+    if (model_name)
+      FREE_COM_STRING (model_name);
+    if (display_name)
+      FREE_COM_STRING (display_name);
+    if (serial_number)
+      FREE_COM_STRING (serial_number);
+    gst_caps_unref (video_input_caps);
+    gst_caps_unref (video_output_caps);
+
     ret = decklink->QueryInterface (IID_IDeckLinkKeyer,
         (void **) &dev->output.keyer);
 
@@ -1420,6 +1578,35 @@ init_devices (gpointer data)
   return NULL;
 }
 
+GList *
+gst_decklink_get_devices (void)
+{
+  guint i;
+  GList *l = NULL;
+
+  g_once (&devices_once, init_devices, NULL);
+
+  for (i = 0; i < devices->len; i++) {
+    Device *device = (Device *) g_ptr_array_index (devices, i);
+
+    if (device->devices[0])
+      l = g_list_prepend (l, device->devices[0]);
+
+    if (device->devices[1])
+      l = g_list_prepend (l, device->devices[1]);
+
+    if (device->devices[2])
+      l = g_list_prepend (l, device->devices[2]);
+
+    if (device->devices[3])
+      l = g_list_prepend (l, device->devices[3]);
+  }
+
+  l = g_list_reverse (l);
+
+  return l;
+}
+
 GstDecklinkOutput *
 gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio)
 {
@@ -1817,6 +2004,10 @@ plugin_init (GstPlugin * plugin)
       GST_TYPE_DECKLINK_AUDIO_SRC);
   gst_element_register (plugin, "decklinkvideosrc", GST_RANK_NONE,
       GST_TYPE_DECKLINK_VIDEO_SRC);
+
+  gst_device_provider_register (plugin, "decklinkdeviceprovider",
+      GST_RANK_PRIMARY, GST_TYPE_DECKLINK_DEVICE_PROVIDER);
+
   return TRUE;
 }
 
index 8778d8f7f90844d6ee8d80414d9a8cc0054f39dd..ad8e427015b9094ee382db4818a5a289200c7cf9 100644 (file)
 #define COMSTR_T BSTR
 /* MinGW does not have comsuppw.lib, so no _com_util::ConvertBSTRToString */
 # ifdef __MINGW32__
-#  define CONVERT_COM_STRING(s) BSTR _s = (BSTR)s; s = (char*) malloc(100); wcstombs(s, _s, 100); ::SysFreeString(_s);
+#  define CONVERT_COM_STRING(s) G_STMT_START { BSTR _s = (BSTR)s; s = (char*) malloc(100); wcstombs(s, _s, 100); ::SysFreeString(_s); } G_STMT_END
 #  define FREE_COM_STRING(s) free(s);
 # else
-#  define CONVERT_COM_STRING(s) BSTR _s = (BSTR)s; s = _com_util::ConvertBSTRToString(_s); ::SysFreeString(_s);
-#  define FREE_COM_STRING(s) delete[] s;
+#  define CONVERT_COM_STRING(s) G_STMT_START { BSTR _s = (BSTR)s; s = _com_util::ConvertBSTRToString(_s); ::SysFreeString(_s); } G_STMT_END
+#  define FREE_COM_STRING(s) G_STMT_START { delete[] s; } G_STMT_END
 # endif /* __MINGW32__ */
 #else
 #define COMSTR_T const char*
@@ -288,4 +288,27 @@ const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * ca
 GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input);
 GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input);
 
+#define GST_TYPE_DECKLINK_DEVICE gst_decklink_device_get_type()
+#define GST_DECKLINK_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECKLINK_DEVICE,GstDecklinkDevice))
+
+typedef struct _GstDecklinkDevice GstDecklinkDevice;
+typedef struct _GstDecklinkDeviceClass GstDecklinkDeviceClass;
+
+struct _GstDecklinkDeviceClass
+{
+  GstDeviceClass parent_class;
+};
+
+struct _GstDecklinkDevice
+{
+  GstDevice parent;
+  gboolean video;
+  gboolean capture;
+  guint device_number;
+};
+
+GType gst_decklink_device_get_type (void);
+
+GList * gst_decklink_get_devices (void);
+
 #endif
diff --git a/sys/decklink/gstdecklinkdeviceprovider.cpp b/sys/decklink/gstdecklinkdeviceprovider.cpp
new file mode 100644 (file)
index 0000000..c884c42
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
+ * Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdecklinkdeviceprovider.h"
+#include "gstdecklink.h"
+
+G_DEFINE_TYPE (GstDecklinkDeviceProvider, gst_decklink_device_provider,
+    GST_TYPE_DEVICE_PROVIDER);
+
+static void
+gst_decklink_device_provider_init (GstDecklinkDeviceProvider * self)
+{
+}
+
+static GList *
+gst_decklink_device_provider_probe (GstDeviceProvider * provider)
+{
+  return gst_decklink_get_devices ();
+}
+
+static void
+gst_decklink_device_provider_class_init (GstDecklinkDeviceProviderClass * klass)
+{
+  GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
+
+  dm_class->probe = GST_DEBUG_FUNCPTR (gst_decklink_device_provider_probe);
+
+  gst_device_provider_class_set_static_metadata (dm_class,
+      "Decklink Device Provider", "Hardware/Source/Sink/Audio/Video",
+      "Lists and provides Decklink devices",
+      "Sebastian Dröge <sebastian@centricular.com>");
+}
+
+G_DEFINE_TYPE (GstDecklinkDevice, gst_decklink_device, GST_TYPE_DEVICE);
+
+static void
+gst_decklink_device_init (GstDecklinkDevice * self)
+{
+}
+
+static GstElement *
+gst_decklink_device_create_element (GstDevice * device, const gchar * name)
+{
+  GstDecklinkDevice *self = GST_DECKLINK_DEVICE (device);
+  GstElement *ret = NULL;
+
+  if (self->video && self->capture) {
+    ret = gst_element_factory_make ("decklinkvideosrc", name);
+  } else if (!self->video && self->capture) {
+    ret = gst_element_factory_make ("decklinkaudiosrc", name);
+  } else if (self->video && !self->capture) {
+    ret = gst_element_factory_make ("decklinkvideosink", name);
+  } else {
+    ret = gst_element_factory_make ("decklinkaudiosink", name);
+  }
+
+  if (ret) {
+    g_object_set (ret, "device-number", self->device_number, NULL);
+  }
+
+  return ret;
+}
+
+static void
+gst_decklink_device_class_init (GstDecklinkDeviceClass * klass)
+{
+  GstDeviceClass *gst_device_class = GST_DEVICE_CLASS (klass);
+
+  gst_device_class->create_element =
+      GST_DEBUG_FUNCPTR (gst_decklink_device_create_element);
+}
diff --git a/sys/decklink/gstdecklinkdeviceprovider.h b/sys/decklink/gstdecklinkdeviceprovider.h
new file mode 100644 (file)
index 0000000..0bd684d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
+ * Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_DECKLINK_DEVICE_PROVIDER_H_
+#define _GST_DECKLINK_DEVICE_PROVIDER_H_
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_DECKLINK_DEVICE_PROVIDER gst_decklink_device_provider_get_type()
+#define GST_DECKLINK_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECKLINK_DEVICE_PROVIDER,GstDecklinkDeviceProvider))
+
+typedef struct _GstDecklinkDeviceProvider GstDecklinkDeviceProvider;
+typedef struct _GstDecklinkDeviceProviderClass GstDecklinkDeviceProviderClass;
+
+struct _GstDecklinkDeviceProviderClass
+{
+  GstDeviceProviderClass parent_class;
+};
+
+struct _GstDecklinkDeviceProvider
+{
+  GstDeviceProvider parent;
+};
+
+GType gst_decklink_device_provider_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GST_DECKLINK_DEVICE_PROVIDER_H_ */
index 4688e73668e8d8f945854f40b7e760a650602b26..abffb633f5e0ba4b556647ac1b41eefa80c81407 100644 (file)
@@ -4,6 +4,7 @@ decklink_sources = [
   'gstdecklinkvideosink.cpp',
   'gstdecklinkaudiosrc.cpp',
   'gstdecklinkvideosrc.cpp',
+  'gstdecklinkdeviceprovider.cpp',
 ]
 
 if get_option('decklink').disabled()