decklinkvideosink: Add 3G-SDI Level A output support
authorEric Knapp <emkman99@gmail.com>
Thu, 2 Jun 2022 14:32:28 +0000 (10:32 -0400)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 7 Jun 2022 07:07:42 +0000 (10:07 +0300)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2544>

subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json
subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h

index 3a4fde1..770045b 100644 (file)
                         "type": "GstDecklinkKeyerMode",
                         "writable": true
                     },
+                    "mapping-format": {
+                        "blurb": "3G-SDI Mapping Format (Level A/B)",
+                        "conditionally-available": false,
+                        "construct": true,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "default (0)",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstDecklinkMappingFormat",
+                        "writable": true
+                    },
                     "mode": {
                         "blurb": "Video Mode to use for playback",
                         "conditionally-available": false,
                     }
                 ]
             },
+            "GstDecklinkMappingFormat": {
+                "kind": "enum",
+                "values": [
+                    {
+                        "desc": "Default, don't change mapping format",
+                        "name": "default",
+                        "value": "0"
+                    },
+                    {
+                        "desc": "Level A",
+                        "name": "level-a",
+                        "value": "1"
+                    },
+                    {
+                        "desc": "Level B",
+                        "name": "level-b",
+                        "value": "2"
+                    }
+                ]
+            },
             "GstDecklinkModes": {
                 "kind": "enum",
                 "values": [
index 3f79deb..3467647 100644 (file)
@@ -202,6 +202,36 @@ gst_decklink_profile_id_get_type (void)
   return (GType) id;
 }
 
+/**
+ * GstDecklinkMappingFormat:
+ * @GST_DECKLINK_MAPPING_FORMAT_DEFAULT: Don't change the mapping format
+ * @GST_DECKLINK_MAPPING_FORMAT_LEVEL_A: Level A
+ * @GST_DECKLINK_MAPPING_FORMAT_LEVEL_B: Level B
+ *
+ * 3G-SDI mapping format (SMPTE ST 425-1:2017)
+ *
+ * Since: 1.22
+ */
+GType
+gst_decklink_mapping_format_get_type (void)
+{
+  static gsize id = 0;
+  static const GEnumValue mappingformats[] = {
+    {GST_DECKLINK_MAPPING_FORMAT_DEFAULT, "Default, don't change mapping format",
+      "default"},
+    {GST_DECKLINK_MAPPING_FORMAT_LEVEL_A, "Level A", "level-a"},
+    {GST_DECKLINK_MAPPING_FORMAT_LEVEL_B, "Level B", "level-b"},
+    {0, NULL, NULL}
+  };
+
+  if (g_once_init_enter (&id)) {
+    GType tmp = g_enum_register_static ("GstDecklinkMappingFormat", mappingformats);
+    g_once_init_leave (&id, tmp);
+  }
+
+  return (GType) id;
+}
+
 GType
 gst_decklink_timecode_format_get_type (void)
 {
@@ -386,6 +416,13 @@ enum ProfileSetOperationResult
   PROFILE_SET_FAILURE
 };
 
+enum MappingFormatSetOperationResult
+{
+  MAPPING_FORMAT_SET_UNSUPPORTED,
+  MAPPING_FORMAT_SET_SUCCESS,
+  MAPPING_FORMAT_SET_FAILURE
+};
+
 enum DuplexModeSetOperationResult
 {
   DUPLEX_MODE_SET_UNSUPPORTED,
@@ -867,6 +904,8 @@ struct _Device
 
 static ProfileSetOperationResult gst_decklink_configure_profile (Device *
     device, GstDecklinkProfileId profile_id);
+static MappingFormatSetOperationResult gst_decklink_configure_mapping_format (Device *
+    device, GstDecklinkMappingFormat mapping_format);
 
 class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback
 {
@@ -1718,6 +1757,10 @@ gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio)
             videosink->profile_id) == PROFILE_SET_FAILURE) {
       return NULL;
     }
+    if (gst_decklink_configure_mapping_format (device,
+            videosink->mapping_format) == MAPPING_FORMAT_SET_FAILURE) {
+      return NULL;
+    }
   }
 
   g_mutex_lock (&output->lock);
@@ -1905,6 +1948,49 @@ gst_decklink_configure_profile (Device * device,
   }
 }
 
+static MappingFormatSetOperationResult
+gst_decklink_configure_mapping_format (Device * device,
+    GstDecklinkMappingFormat mapping_format)
+{
+  HRESULT res;
+
+  bool level_a_output;
+  switch (mapping_format) {
+    case GST_DECKLINK_MAPPING_FORMAT_LEVEL_A:
+      level_a_output = true;
+      break;
+    case GST_DECKLINK_MAPPING_FORMAT_LEVEL_B:
+      level_a_output = false;
+      break;
+    default:
+    case GST_DECKLINK_MAPPING_FORMAT_DEFAULT:
+      return MAPPING_FORMAT_SET_SUCCESS;
+  }
+
+  // Make sure Level A is supported
+  bool supports_level_a_output = false;
+  res = device->output.attributes->GetFlag(BMDDeckLinkSupportsSMPTELevelAOutput,
+    &supports_level_a_output);
+  if (res != S_OK || !supports_level_a_output) {
+    if (level_a_output) {
+      GST_DEBUG ("Device does not support Level A mapping format");
+      return MAPPING_FORMAT_SET_UNSUPPORTED;
+    } else {
+      // Level B is the only supported option
+      return MAPPING_FORMAT_SET_SUCCESS;
+    }
+  }
+
+  res = device->input.config->SetFlag(bmdDeckLinkConfigSMPTELevelAOutput, level_a_output);
+  if (res == S_OK) {
+    GST_DEBUG ("Successfully set mapping format");
+    return MAPPING_FORMAT_SET_SUCCESS;
+  } else {
+    GST_ERROR ("Failed to set mapping format");
+    return MAPPING_FORMAT_SET_FAILURE;
+  }
+}
+
 G_DEFINE_TYPE (GstDecklinkClock, gst_decklink_clock, GST_TYPE_SYSTEM_CLOCK);
 
 static GstClockTime gst_decklink_clock_get_internal_time (GstClock * clock);
index baa7879..f0aef72 100644 (file)
@@ -174,6 +174,14 @@ typedef enum {
 GType gst_decklink_profile_id_get_type (void);
 
 typedef enum {
+  GST_DECKLINK_MAPPING_FORMAT_DEFAULT,
+  GST_DECKLINK_MAPPING_FORMAT_LEVEL_A, /* bmdDeckLinkConfigSMPTELevelAOutput = true */
+  GST_DECKLINK_MAPPING_FORMAT_LEVEL_B, /* bmdDeckLinkConfigSMPTELevelAOutput = false */
+} GstDecklinkMappingFormat;
+#define GST_TYPE_DECKLINK_MAPPING_FORMAT (gst_decklink_mapping_format_get_type ())
+GType gst_decklink_mapping_format_get_type (void);
+
+typedef enum {
   GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1, /*bmdTimecodeRP188VITC1 */
   GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2, /*bmdTimecodeRP188VITC2 */
   GST_DECKLINK_TIMECODE_FORMAT_RP188LTC, /*bmdTimecodeRP188LTC */
index a64c046..1657740 100644 (file)
@@ -232,6 +232,14 @@ private:
   gint m_refcount;
 };
 
+/**
+ * GstDecklinkMappingFormat:
+ * @GST_DECKLINK_MAPPING_FORMAT_DEFAULT: Don't change the mapping format
+ * @GST_DECKLINK_MAPPING_FORMAT_LEVEL_A: Level A
+ * @GST_DECKLINK_MAPPING_FORMAT_LEVEL_B: Level B
+ *
+ * Since: 1.22
+ */
 enum
 {
   PROP_0,
@@ -245,6 +253,7 @@ enum
   PROP_HW_SERIAL_NUMBER,
   PROP_CC_LINE,
   PROP_AFD_BAR_LINE,
+  PROP_MAPPING_FORMAT,
 };
 
 static void gst_decklink_video_sink_set_property (GObject * object,
@@ -407,6 +416,20 @@ gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass)
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
 
+  /**
+   * GstDecklinkVideoSink:mapping-format
+   *
+   * Specifies the 3G-SDI mapping format to use (SMPTE ST 425-1:2017).
+   *
+   * Since: 1.22
+   */
+  g_object_class_install_property (gobject_class, PROP_MAPPING_FORMAT,
+      g_param_spec_enum ("mapping-format", "3G-SDI Mapping Format",
+          "3G-SDI Mapping Format (Level A/B)",
+          GST_TYPE_DECKLINK_MAPPING_FORMAT, GST_DECKLINK_MAPPING_FORMAT_DEFAULT,
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+              G_PARAM_CONSTRUCT)));
+
   templ_caps = gst_decklink_mode_get_template_caps (FALSE);
   templ_caps = gst_caps_make_writable (templ_caps);
   /* For output we support any framerate and only really care about timestamps */
@@ -422,6 +445,8 @@ gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass)
 
   GST_DEBUG_CATEGORY_INIT (gst_decklink_video_sink_debug, "decklinkvideosink",
       0, "debug category for decklinkvideosink element");
+
+  gst_type_mark_as_plugin_api(GST_TYPE_DECKLINK_MAPPING_FORMAT, (GstPluginAPIFlags)0);
 }
 
 static void
@@ -435,6 +460,7 @@ gst_decklink_video_sink_init (GstDecklinkVideoSink * self)
   self->timecode_format = bmdTimecodeRP188Any;
   self->caption_line = 0;
   self->afd_bar_line = 0;
+  self->mapping_format = GST_DECKLINK_MAPPING_FORMAT_DEFAULT;
 
   gst_base_sink_set_max_lateness (GST_BASE_SINK_CAST (self), 20 * GST_MSECOND);
   gst_base_sink_set_qos_enabled (GST_BASE_SINK_CAST (self), TRUE);
@@ -490,6 +516,9 @@ gst_decklink_video_sink_set_property (GObject * object, guint property_id,
     case PROP_AFD_BAR_LINE:
       self->afd_bar_line = g_value_get_int (value);
       break;
+    case PROP_MAPPING_FORMAT:
+      self->mapping_format = (GstDecklinkMappingFormat) g_value_get_enum (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -538,6 +567,9 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id,
     case PROP_AFD_BAR_LINE:
       g_value_set_int (value, self->afd_bar_line);
       break;
+    case PROP_MAPPING_FORMAT:
+      g_value_set_enum (value, self->mapping_format);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
index 555ec8e..4255a8a 100644 (file)
@@ -75,6 +75,7 @@ struct _GstDecklinkVideoSink
   guint16 cdp_hdr_sequence_cntr;
 
   gint afd_bar_line;
+  GstDecklinkMappingFormat mapping_format;
 };
 
 struct _GstDecklinkVideoSinkClass