summaryrefslogtreecommitdiff
path: root/sys/decklink
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2019-11-21 16:59:29 +0200
committerSebastian Dröge <slomo@coaxion.net>2019-12-03 09:16:22 +0000
commit8ba4d1a480843b063136774e6c889e17cb5d8d69 (patch)
tree6308b5402750eb85c7fb89d9f93fe0a0177aed88 /sys/decklink
parentf3e4aa1a18933dbafa1c09e8cfa022df6090873e (diff)
downloadgstreamer-plugins-bad-8ba4d1a480843b063136774e6c889e17cb5d8d69.tar.gz
decklink: Add simple device provider for Decklink devices
Diffstat (limited to 'sys/decklink')
-rw-r--r--sys/decklink/gstdecklink.cpp203
-rw-r--r--sys/decklink/gstdecklink.h29
-rw-r--r--sys/decklink/gstdecklinkdeviceprovider.cpp91
-rw-r--r--sys/decklink/gstdecklinkdeviceprovider.h47
-rw-r--r--sys/decklink/meson.build1
5 files changed, 362 insertions, 9 deletions
diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp
index 0fed8b019..25493ce2a 100644
--- a/sys/decklink/gstdecklink.cpp
+++ b/sys/decklink/gstdecklink.cpp
@@ -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;
}
diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h
index 8778d8f7f..ad8e42701 100644
--- a/sys/decklink/gstdecklink.h
+++ b/sys/decklink/gstdecklink.h
@@ -41,11 +41,11 @@
#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
index 000000000..c884c4281
--- /dev/null
+++ b/sys/decklink/gstdecklinkdeviceprovider.cpp
@@ -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
index 000000000..0bd684def
--- /dev/null
+++ b/sys/decklink/gstdecklinkdeviceprovider.h
@@ -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_ */
diff --git a/sys/decklink/meson.build b/sys/decklink/meson.build
index 4688e7366..abffb633f 100644
--- a/sys/decklink/meson.build
+++ b/sys/decklink/meson.build
@@ -4,6 +4,7 @@ decklink_sources = [
'gstdecklinkvideosink.cpp',
'gstdecklinkaudiosrc.cpp',
'gstdecklinkvideosrc.cpp',
+ 'gstdecklinkdeviceprovider.cpp',
]
if get_option('decklink').disabled()