}
GType
+gst_decklink_duplex_mode_get_type (void)
+{
+ static gsize id = 0;
+ static const GEnumValue types[] = {
+ {GST_DECKLINK_DUPLEX_MODE_HALF, "Half-Duplex", "half"},
+ {GST_DECKLINK_DUPLEX_MODE_FULL, "Full-Duplex", "full"},
+ {0, NULL, NULL}
+ };
+
+ if (g_once_init_enter (&id)) {
+ GType tmp = g_enum_register_static ("GstDecklinkDuplexMode", types);
+ g_once_init_leave (&id, tmp);
+ }
+
+ return (GType) id;
+}
+
+GType
gst_decklink_timecode_format_get_type (void)
{
static gsize id = 0;
static const struct
{
+ BMDDuplexMode mode;
+ GstDecklinkDuplexMode gstmode;
+} duplex_modes[] = {
+ /* *INDENT-OFF* */
+ {bmdDuplexModeHalf, GST_DECKLINK_DUPLEX_MODE_HALF},
+ {bmdDuplexModeFull, GST_DECKLINK_DUPLEX_MODE_FULL},
+ /* *INDENT-ON* */
+};
+
+enum DuplexModeSetOperationResult
+{
+ DUPLEX_MODE_SET_UNSUPPORTED,
+ DUPLEX_MODE_SET_SUCCESS,
+ DUPLEX_MODE_SET_FAILURE
+};
+
+static const struct
+{
BMDTimecodeFormat format;
GstDecklinkTimecodeFormat gstformat;
} tcformats[] = {
return GST_DECKLINK_TIMECODE_FORMAT_RP188ANY;
}
+const BMDDuplexMode
+gst_decklink_duplex_mode_from_enum (GstDecklinkDuplexMode m)
+{
+ return duplex_modes[m].mode;
+}
+
+const GstDecklinkDuplexMode
+gst_decklink_duplex_mode_to_enum (BMDDuplexMode m)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (duplex_modes); i++) {
+ if (duplex_modes[i].mode == m)
+ return duplex_modes[i].gstmode;
+ }
+ g_assert_not_reached ();
+ return GST_DECKLINK_DUPLEX_MODE_HALF;
+}
+
const BMDKeyerMode
gst_decklink_keyer_mode_from_enum (GstDecklinkKeyerMode m)
{
GstDecklinkInput input;
};
+DuplexModeSetOperationResult gst_decklink_configure_duplex_mode (Device *
+ device, BMDDuplexMode duplex_mode);
+DuplexModeSetOperationResult
+gst_decklink_configure_duplex_mode_pair_device (Device * device,
+ BMDDuplexMode duplex_mode);
+Device *gst_decklink_find_device_by_persistent_id (int64_t persistent_id);
+gboolean gst_decklink_device_has_persistent_id (Device * device,
+ int64_t persistent_id);
+
class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback
{
private:
return NULL;
}
+ if (!is_audio) {
+ GstDecklinkVideoSink *videosink = (GstDecklinkVideoSink *) (sink);
+ if (gst_decklink_configure_duplex_mode (device,
+ videosink->duplex_mode) == DUPLEX_MODE_SET_FAILURE) {
+ return NULL;
+ }
+ }
+
g_mutex_lock (&output->lock);
if (is_audio && !output->audiosink) {
output->audiosink = GST_ELEMENT_CAST (gst_object_ref (sink));
return NULL;
}
+ if (!is_audio) {
+ GstDecklinkVideoSrc *videosrc = (GstDecklinkVideoSrc *) (src);
+ if (gst_decklink_configure_duplex_mode (device,
+ videosrc->duplex_mode) == DUPLEX_MODE_SET_FAILURE) {
+ return NULL;
+ }
+ }
g_mutex_lock (&input->lock);
input->input->SetVideoInputFrameMemoryAllocator (new
GStreamerDecklinkMemoryAllocator);
g_mutex_unlock (&input->lock);
}
+/**
+ * Probes if duplex-mode is supported and sets it accordingly. I duplex-mode is not supported
+ * but this device is part of a pair (Duo2- and Quad2-Cards) and Half-Dupley-Mode is requested,
+ * the parent device is also checked and configured accordingly.
+ *
+ * If
+ * - full-duplex-mode is requsted and the device does not support it *or*
+ * - half-duplex-mode is requested and there is not parent-device *or*
+ * - half-duplex-mode is requested and neither the device nor the parent device does support setting
+ * the duplex-mode, DUPLEX_MODE_SET_UNSUPPORTED is returnded.
+ * If the device does support duplex-mode and setting it succeeded, DUPLEX_MODE_SET_SUCCESS is rerturned.
+ * If
+ * - the device does support duplex-mode and setting it failed *or*
+ * - the Device reported a pair-device that does not exist in the system,
+ * DUPLEX_MODE_SET_FAILURE is returned.
+ */
+DuplexModeSetOperationResult
+gst_decklink_configure_duplex_mode (Device * device, BMDDuplexMode duplex_mode)
+{
+ HRESULT result;
+ bool duplex_supported;
+ int64_t paired_device_id;
+
+ GstDecklinkInput *input = &device->input;
+
+ result =
+ input->attributes->GetFlag (BMDDeckLinkSupportsDuplexModeConfiguration,
+ &duplex_supported);
+ if (result != S_OK) {
+ duplex_supported = false;
+ }
+
+ if (!duplex_supported) {
+ if (duplex_mode == bmdDuplexModeFull) {
+ GST_DEBUG ("Device does not support Full-Duplex-Mode");
+ return DUPLEX_MODE_SET_UNSUPPORTED;
+ } else if (duplex_mode == bmdDuplexModeHalf) {
+ result =
+ input->attributes->GetInt (BMDDeckLinkPairedDevicePersistentID,
+ &paired_device_id);
+
+ if (result == S_OK) {
+ GST_DEBUG ("Device does not support Half-Duplex-Mode but the Device is "
+ "a Part of a Device-Pair, trying to set Half-Duplex-Mode "
+ "on the Parent-Device");
+
+ Device *pair_device =
+ gst_decklink_find_device_by_persistent_id (paired_device_id);
+ if (pair_device == NULL) {
+ GST_ERROR ("Device reported as Pair-Device does not exist");
+ return DUPLEX_MODE_SET_FAILURE;
+ }
+ return gst_decklink_configure_duplex_mode_pair_device (pair_device,
+ duplex_mode);
+ } else {
+ GST_DEBUG ("Device does not support Half-Duplex-Mode");
+ return DUPLEX_MODE_SET_SUCCESS;
+ }
+ } else
+ g_assert_not_reached ();
+ } else {
+ GST_DEBUG ("Setting duplex-mode of Device");
+ result = input->config->SetInt (bmdDeckLinkConfigDuplexMode, duplex_mode);
+
+ if (result == S_OK) {
+ GST_DEBUG ("Duplex mode set successful");
+ return DUPLEX_MODE_SET_SUCCESS;
+ } else {
+ GST_ERROR ("Setting duplex mode failed");
+ return DUPLEX_MODE_SET_FAILURE;
+ }
+ }
+}
+
+DuplexModeSetOperationResult
+gst_decklink_configure_duplex_mode_pair_device (Device * device,
+ BMDDuplexMode duplex_mode)
+{
+ HRESULT result;
+ bool duplex_supported;
+
+ GstDecklinkInput *input = &device->input;
+
+ result =
+ input->attributes->GetFlag (BMDDeckLinkSupportsDuplexModeConfiguration,
+ &duplex_supported);
+ if (result != S_OK) {
+ duplex_supported = false;
+ }
+
+ if (!duplex_supported) {
+ GST_DEBUG ("Pair-Device does not support Duplex-Mode");
+ return DUPLEX_MODE_SET_UNSUPPORTED;
+ }
+
+ GST_DEBUG ("Setting duplex-mode of Pair-Device");
+ result = input->config->SetInt (bmdDeckLinkConfigDuplexMode, duplex_mode);
+
+ if (result == S_OK) {
+ GST_DEBUG ("Duplex mode set successful");
+ return DUPLEX_MODE_SET_SUCCESS;
+ } else {
+ GST_ERROR ("Setting duplex mode failed");
+ return DUPLEX_MODE_SET_FAILURE;
+ }
+}
+
+gboolean
+gst_decklink_device_has_persistent_id (Device * device, int64_t persistent_id)
+{
+ HRESULT result;
+ int64_t this_device_persistent_id;
+
+ GstDecklinkInput *input = &device->input;
+
+ result =
+ input->attributes->GetInt (BMDDeckLinkPersistentID,
+ &this_device_persistent_id);
+ return (result == S_OK) && (this_device_persistent_id == persistent_id);
+}
+
+Device *
+gst_decklink_find_device_by_persistent_id (int64_t persistent_id)
+{
+ GST_DEBUG ("Searching Device by persistent ID %" G_GINT64_FORMAT,
+ persistent_id);
+
+ for (guint index = 0; index < devices->len; index++) {
+ Device *device = (Device *) g_ptr_array_index (devices, index);
+
+ if (gst_decklink_device_has_persistent_id (device, persistent_id)) {
+ GST_DEBUG ("Found matching Device %u", index);
+ return device;
+ }
+ }
+
+ return NULL;
+}
+
G_DEFINE_TYPE (GstDecklinkClock, gst_decklink_clock, GST_TYPE_SYSTEM_CLOCK);
static GstClockTime gst_decklink_clock_get_internal_time (GstClock * clock);
PROP_MODE,
PROP_DEVICE_NUMBER,
PROP_VIDEO_FORMAT,
+ PROP_DUPLEX_MODE,
PROP_TIMECODE_FORMAT,
PROP_KEYER_MODE,
PROP_KEYER_LEVEL,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT)));
+ g_object_class_install_property (gobject_class, PROP_DUPLEX_MODE,
+ g_param_spec_enum ("duplex-mode", "Duplex mode",
+ "Certain DeckLink devices such as the DeckLink Quad 2 and the "
+ "DeckLink Duo 2 support configuration of the duplex mode of "
+ "individual sub-devices."
+ "A sub-device configured as full-duplex will use two connectors, "
+ "which allows simultaneous capture and playback, internal keying, "
+ "and fill & key scenarios."
+ "A half-duplex sub-device will use a single connector as an "
+ "individual capture or playback channel.",
+ GST_TYPE_DECKLINK_DUPLEX_MODE, GST_DECKLINK_DUPLEX_MODE_HALF,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT)));
+
g_object_class_install_property (gobject_class, PROP_TIMECODE_FORMAT,
g_param_spec_enum ("timecode-format", "Timecode format",
"Timecode format type to use for playback",
self->mode = GST_DECKLINK_MODE_NTSC;
self->device_number = 0;
self->video_format = GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV;
+ self->duplex_mode = bmdDuplexModeHalf;
/* VITC is legacy, we should expect RP188 in modern use cases */
self->timecode_format = bmdTimecodeRP188Any;
self->caption_line = 0;
break;
}
break;
+ case PROP_DUPLEX_MODE:
+ self->duplex_mode =
+ gst_decklink_duplex_mode_from_enum ((GstDecklinkDuplexMode)
+ g_value_get_enum (value));
+ break;
case PROP_TIMECODE_FORMAT:
self->timecode_format =
gst_decklink_timecode_format_from_enum ((GstDecklinkTimecodeFormat)
case PROP_VIDEO_FORMAT:
g_value_set_enum (value, self->video_format);
break;
+ case PROP_DUPLEX_MODE:
+ g_value_set_enum (value,
+ gst_decklink_duplex_mode_to_enum (self->duplex_mode));
+ break;
case PROP_TIMECODE_FORMAT:
g_value_set_enum (value,
gst_decklink_timecode_format_to_enum (self->timecode_format));
PROP_DEVICE_NUMBER,
PROP_BUFFER_SIZE,
PROP_VIDEO_FORMAT,
+ PROP_DUPLEX_MODE,
PROP_TIMECODE_FORMAT,
PROP_OUTPUT_STREAM_TIME,
PROP_SKIP_FIRST_TIME,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT)));
+ g_object_class_install_property (gobject_class, PROP_DUPLEX_MODE,
+ g_param_spec_enum ("duplex-mode", "Duplex mode",
+ "Certain DeckLink devices such as the DeckLink Quad 2 and the "
+ "DeckLink Duo 2 support configuration of the duplex mode of "
+ "individual sub-devices."
+ "A sub-device configured as full-duplex will use two connectors, "
+ "which allows simultaneous capture and playback, internal keying, "
+ "and fill & key scenarios."
+ "A half-duplex sub-device will use a single connector as an "
+ "individual capture or playback channel.",
+ GST_TYPE_DECKLINK_DUPLEX_MODE, GST_DECKLINK_DUPLEX_MODE_HALF,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT)));
+
g_object_class_install_property (gobject_class, PROP_TIMECODE_FORMAT,
g_param_spec_enum ("timecode-format", "Timecode format",
"Timecode format type to use for input",
self->device_number = 0;
self->buffer_size = DEFAULT_BUFFER_SIZE;
self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO;
+ self->duplex_mode = bmdDuplexModeHalf;
self->timecode_format = bmdTimecodeRP188Any;
self->no_signal = FALSE;
self->output_stream_time = DEFAULT_OUTPUT_STREAM_TIME;
("Format %d not supported", self->video_format), (NULL));
break;
}
+ case PROP_DUPLEX_MODE:
+ self->duplex_mode =
+ gst_decklink_duplex_mode_from_enum ((GstDecklinkDuplexMode)
+ g_value_get_enum (value));
break;
case PROP_TIMECODE_FORMAT:
self->timecode_format =
case PROP_VIDEO_FORMAT:
g_value_set_enum (value, self->video_format);
break;
+ case PROP_DUPLEX_MODE:
+ g_value_set_enum (value,
+ gst_decklink_duplex_mode_to_enum (self->duplex_mode));
+ break;
case PROP_TIMECODE_FORMAT:
g_value_set_enum (value,
gst_decklink_timecode_format_to_enum (self->timecode_format));