From 8ba4d1a480843b063136774e6c889e17cb5d8d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 21 Nov 2019 16:59:29 +0200 Subject: [PATCH] decklink: Add simple device provider for Decklink devices --- sys/decklink/gstdecklink.cpp | 203 ++++++++++++++++++++- sys/decklink/gstdecklink.h | 29 ++- sys/decklink/gstdecklinkdeviceprovider.cpp | 91 +++++++++ sys/decklink/gstdecklinkdeviceprovider.h | 47 +++++ sys/decklink/meson.build | 1 + 5 files changed, 362 insertions(+), 9 deletions(-) create mode 100644 sys/decklink/gstdecklinkdeviceprovider.cpp create mode 100644 sys/decklink/gstdecklinkdeviceprovider.h diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp index 0fed8b0194..25493ce2a9 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 8778d8f7f9..ad8e427015 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 0000000000..c884c42817 --- /dev/null +++ b/sys/decklink/gstdecklinkdeviceprovider.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2019 Mathieu Duponchelle + * Copyright (C) 2019 Sebastian Dröge + * + * 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 "); +} + +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 0000000000..0bd684defe --- /dev/null +++ b/sys/decklink/gstdecklinkdeviceprovider.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Mathieu Duponchelle + * Copyright (C) 2019 Sebastian Dröge + * + * 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 + +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 4688e73668..abffb633f5 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()