From 1818374de29671a16db9032434c707ec5003654b Mon Sep 17 00:00:00 2001 From: Eric Knapp Date: Thu, 2 Jun 2022 10:32:28 -0400 Subject: [PATCH] decklinkvideosink: Add 3G-SDI Level A output support Part-of: --- .../docs/plugins/gst_plugins_cache.json | 32 ++++++++ .../gst-plugins-bad/sys/decklink/gstdecklink.cpp | 86 ++++++++++++++++++++++ .../gst-plugins-bad/sys/decklink/gstdecklink.h | 8 ++ .../sys/decklink/gstdecklinkvideosink.cpp | 32 ++++++++ .../sys/decklink/gstdecklinkvideosink.h | 1 + 5 files changed, 159 insertions(+) diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index 3a4fde1..770045b 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -11581,6 +11581,18 @@ "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, @@ -11955,6 +11967,26 @@ } ] }, + "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": [ diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp index 3f79deb..3467647 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp @@ -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); diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h index baa7879..f0aef72 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h @@ -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 */ diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp index a64c046..1657740 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp @@ -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; diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h index 555ec8e..4255a8a 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h @@ -75,6 +75,7 @@ struct _GstDecklinkVideoSink guint16 cdp_hdr_sequence_cntr; gint afd_bar_line; + GstDecklinkMappingFormat mapping_format; }; struct _GstDecklinkVideoSinkClass -- 2.7.4