#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
displayMode = GST_DECKLINK_MODE_2160p60;
break;
default:
- g_assert_not_reached ();
+ displayMode = (GstDecklinkModeEnum) - 1;
break;
}
return displayMode;
}
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",
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",
{
GstDecklinkOutput output;
GstDecklinkInput input;
+
+ /* Audio/video output, Audio/video input */
+ GstDecklinkDevice *devices[4];
};
DuplexModeSetOperationResult gst_decklink_configure_duplex_mode (Device *
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)
{
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);
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);
}
mode_iter->Release ();
}
+
+ capture = TRUE;
+
ret = S_OK;
}
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);
}
mode_iter->Release ();
}
+
+ output = TRUE;
+
ret = S_OK;
}
GST_WARNING ("selected device does not have config interface: 0x%08lx",
(unsigned long) ret);
} else {
- char *serial_number;
-
ret =
dev->input.
config->GetString (bmdDeckLinkConfigDeviceInformationSerialNumber,
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);
}
}
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);
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)
{
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;
}
--- /dev/null
+/*
+ * 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);
+}