decklink: implement duplex-mode property
authorPeter Körner <peter@mazdermind.de>
Sun, 3 Mar 2019 18:34:06 +0000 (19:34 +0100)
committerSebastian Dröge <slomo@coaxion.net>
Mon, 4 Mar 2019 09:34:44 +0000 (09:34 +0000)
sys/decklink/gstdecklink.cpp
sys/decklink/gstdecklink.h
sys/decklink/gstdecklinkvideosink.cpp
sys/decklink/gstdecklinkvideosink.h
sys/decklink/gstdecklinkvideosrc.cpp
sys/decklink/gstdecklinkvideosrc.h

index cbe2abc..4596e45 100644 (file)
@@ -140,6 +140,24 @@ gst_decklink_video_format_get_type (void)
 }
 
 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;
@@ -301,6 +319,24 @@ static const struct
 
 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[] = {
@@ -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);
index 3c55608..bfddf77 100644 (file)
@@ -145,6 +145,13 @@ typedef enum {
 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 */
   GST_DECKLINK_TIMECODE_FORMAT_RP188LTC, /*bmdTimecodeRP188LTC */
@@ -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);
 
index ecf7b39..914b239 100644 (file)
@@ -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));
index 7b6108b..07d3e9c 100644 (file)
@@ -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;
index a7655d8..e34dabe 100644 (file)
@@ -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));
index 367b05b..1ac9602 100644 (file)
@@ -65,6 +65,7 @@ struct _GstDecklinkVideoSrc
 
   GstVideoInfo info;
   GstDecklinkVideoFormat video_format;
+  BMDDuplexMode duplex_mode;
   BMDTimecodeFormat timecode_format;
 
   GstDecklinkInput *input;