From 58dadd1ad0d4d04041e49b7d10246b0aca6f4d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=B6rner?= Date: Sun, 3 Mar 2019 19:34:06 +0100 Subject: [PATCH] decklink: implement duplex-mode property --- sys/decklink/gstdecklink.cpp | 218 ++++++++++++++++++++++++++ sys/decklink/gstdecklink.h | 9 ++ sys/decklink/gstdecklinkvideosink.cpp | 25 +++ sys/decklink/gstdecklinkvideosink.h | 1 + sys/decklink/gstdecklinkvideosrc.cpp | 24 +++ sys/decklink/gstdecklinkvideosrc.h | 1 + 6 files changed, 278 insertions(+) diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp index cbe2abcfb7..4596e45467 100644 --- a/sys/decklink/gstdecklink.cpp +++ b/sys/decklink/gstdecklink.cpp @@ -139,6 +139,24 @@ gst_decklink_video_format_get_type (void) return (GType) id; } +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) { @@ -299,6 +317,24 @@ static const struct /* *INDENT-ON* */ }; +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; @@ -495,6 +531,25 @@ gst_decklink_timecode_format_to_enum (BMDTimecodeFormat f) 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) { @@ -734,6 +789,15 @@ struct _Device 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: @@ -1319,6 +1383,14 @@ gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio) 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)); @@ -1385,6 +1457,13 @@ gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio) 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); @@ -1433,6 +1512,145 @@ gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio) 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); diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h index 3c55608ccb..bfddf77596 100644 --- a/sys/decklink/gstdecklink.h +++ b/sys/decklink/gstdecklink.h @@ -144,6 +144,13 @@ typedef enum { #define GST_TYPE_DECKLINK_VIDEO_FORMAT (gst_decklink_video_format_get_type ()) GType gst_decklink_video_format_get_type (void); +typedef enum { + GST_DECKLINK_DUPLEX_MODE_HALF, /* bmdDuplexModeHalf */ + GST_DECKLINK_DUPLEX_MODE_FULL, /* bmdDuplexModeFull */ +} GstDecklinkDuplexMode; +#define GST_TYPE_DECKLINK_DUPLEX_MODE (gst_decklink_duplex_mode_get_type ()) +GType gst_decklink_duplex_mode_get_type (void); + typedef enum { GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1, /*bmdTimecodeRP188VITC1 */ GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2, /*bmdTimecodeRP188VITC2 */ @@ -181,6 +188,8 @@ const GstDecklinkVideoFormat gst_decklink_type_from_video_format (GstVideoFormat GstVideoFormat gst_decklink_video_format_from_type (BMDPixelFormat pf); const BMDTimecodeFormat gst_decklink_timecode_format_from_enum (GstDecklinkTimecodeFormat f); const GstDecklinkTimecodeFormat gst_decklink_timecode_format_to_enum (BMDTimecodeFormat f); +const BMDDuplexMode gst_decklink_duplex_mode_from_enum (GstDecklinkDuplexMode m); +const GstDecklinkDuplexMode gst_decklink_duplex_mode_to_enum (BMDDuplexMode m); const BMDKeyerMode gst_decklink_keyer_mode_from_enum (GstDecklinkKeyerMode m); const GstDecklinkKeyerMode gst_decklink_keyer_mode_to_enum (BMDKeyerMode m); diff --git a/sys/decklink/gstdecklinkvideosink.cpp b/sys/decklink/gstdecklinkvideosink.cpp index ecf7b39b27..914b2392ee 100644 --- a/sys/decklink/gstdecklinkvideosink.cpp +++ b/sys/decklink/gstdecklinkvideosink.cpp @@ -126,6 +126,7 @@ enum PROP_MODE, PROP_DEVICE_NUMBER, PROP_VIDEO_FORMAT, + PROP_DUPLEX_MODE, PROP_TIMECODE_FORMAT, PROP_KEYER_MODE, PROP_KEYER_LEVEL, @@ -227,6 +228,20 @@ gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass) (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", @@ -284,6 +299,7 @@ gst_decklink_video_sink_init (GstDecklinkVideoSink * self) 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; @@ -320,6 +336,11 @@ gst_decklink_video_sink_set_property (GObject * object, guint property_id, 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) @@ -358,6 +379,10 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id, 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)); diff --git a/sys/decklink/gstdecklinkvideosink.h b/sys/decklink/gstdecklinkvideosink.h index 7b6108b493..07d3e9cffa 100644 --- a/sys/decklink/gstdecklinkvideosink.h +++ b/sys/decklink/gstdecklinkvideosink.h @@ -52,6 +52,7 @@ struct _GstDecklinkVideoSink GstDecklinkModeEnum mode; gint device_number; GstDecklinkVideoFormat video_format; + BMDDuplexMode duplex_mode; BMDTimecodeFormat timecode_format; BMDKeyerMode keyer_mode; gint keyer_level; diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp index a7655d813d..e34dabef3a 100644 --- a/sys/decklink/gstdecklinkvideosrc.cpp +++ b/sys/decklink/gstdecklinkvideosrc.cpp @@ -49,6 +49,7 @@ enum PROP_DEVICE_NUMBER, PROP_BUFFER_SIZE, PROP_VIDEO_FORMAT, + PROP_DUPLEX_MODE, PROP_TIMECODE_FORMAT, PROP_OUTPUT_STREAM_TIME, PROP_SKIP_FIRST_TIME, @@ -187,6 +188,20 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass) (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", @@ -253,6 +268,7 @@ gst_decklink_video_src_init (GstDecklinkVideoSrc * self) 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; @@ -319,6 +335,10 @@ gst_decklink_video_src_set_property (GObject * object, guint property_id, ("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 = @@ -365,6 +385,10 @@ gst_decklink_video_src_get_property (GObject * object, guint property_id, 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)); diff --git a/sys/decklink/gstdecklinkvideosrc.h b/sys/decklink/gstdecklinkvideosrc.h index 367b05b513..1ac960230a 100644 --- a/sys/decklink/gstdecklinkvideosrc.h +++ b/sys/decklink/gstdecklinkvideosrc.h @@ -65,6 +65,7 @@ struct _GstDecklinkVideoSrc GstVideoInfo info; GstDecklinkVideoFormat video_format; + BMDDuplexMode duplex_mode; BMDTimecodeFormat timecode_format; GstDecklinkInput *input;