decklinkvideosink: Add 3G-SDI Level A output support

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2544>
This commit is contained in:
Eric Knapp 2022-06-02 10:32:28 -04:00 committed by Sebastian Dröge
parent 91b7d3679e
commit 1818374de2
5 changed files with 159 additions and 0 deletions

View file

@ -11581,6 +11581,18 @@
"type": "GstDecklinkKeyerMode", "type": "GstDecklinkKeyerMode",
"writable": true "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": { "mode": {
"blurb": "Video Mode to use for playback", "blurb": "Video Mode to use for playback",
"conditionally-available": false, "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": { "GstDecklinkModes": {
"kind": "enum", "kind": "enum",
"values": [ "values": [

View file

@ -202,6 +202,36 @@ gst_decklink_profile_id_get_type (void)
return (GType) id; 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 GType
gst_decklink_timecode_format_get_type (void) gst_decklink_timecode_format_get_type (void)
{ {
@ -386,6 +416,13 @@ enum ProfileSetOperationResult
PROFILE_SET_FAILURE PROFILE_SET_FAILURE
}; };
enum MappingFormatSetOperationResult
{
MAPPING_FORMAT_SET_UNSUPPORTED,
MAPPING_FORMAT_SET_SUCCESS,
MAPPING_FORMAT_SET_FAILURE
};
enum DuplexModeSetOperationResult enum DuplexModeSetOperationResult
{ {
DUPLEX_MODE_SET_UNSUPPORTED, DUPLEX_MODE_SET_UNSUPPORTED,
@ -867,6 +904,8 @@ struct _Device
static ProfileSetOperationResult gst_decklink_configure_profile (Device * static ProfileSetOperationResult gst_decklink_configure_profile (Device *
device, GstDecklinkProfileId profile_id); device, GstDecklinkProfileId profile_id);
static MappingFormatSetOperationResult gst_decklink_configure_mapping_format (Device *
device, GstDecklinkMappingFormat mapping_format);
class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback 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) { videosink->profile_id) == PROFILE_SET_FAILURE) {
return NULL; return NULL;
} }
if (gst_decklink_configure_mapping_format (device,
videosink->mapping_format) == MAPPING_FORMAT_SET_FAILURE) {
return NULL;
}
} }
g_mutex_lock (&output->lock); 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); G_DEFINE_TYPE (GstDecklinkClock, gst_decklink_clock, GST_TYPE_SYSTEM_CLOCK);
static GstClockTime gst_decklink_clock_get_internal_time (GstClock * clock); static GstClockTime gst_decklink_clock_get_internal_time (GstClock * clock);

View file

@ -173,6 +173,14 @@ typedef enum {
#define GST_TYPE_DECKLINK_PROFILE_ID (gst_decklink_profile_id_get_type ()) #define GST_TYPE_DECKLINK_PROFILE_ID (gst_decklink_profile_id_get_type ())
GType gst_decklink_profile_id_get_type (void); 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 { typedef enum {
GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1, /*bmdTimecodeRP188VITC1 */ GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1, /*bmdTimecodeRP188VITC1 */
GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2, /*bmdTimecodeRP188VITC2 */ GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2, /*bmdTimecodeRP188VITC2 */

View file

@ -232,6 +232,14 @@ private:
gint m_refcount; 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 enum
{ {
PROP_0, PROP_0,
@ -245,6 +253,7 @@ enum
PROP_HW_SERIAL_NUMBER, PROP_HW_SERIAL_NUMBER,
PROP_CC_LINE, PROP_CC_LINE,
PROP_AFD_BAR_LINE, PROP_AFD_BAR_LINE,
PROP_MAPPING_FORMAT,
}; };
static void gst_decklink_video_sink_set_property (GObject * object, 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 | (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT))); 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_decklink_mode_get_template_caps (FALSE);
templ_caps = gst_caps_make_writable (templ_caps); templ_caps = gst_caps_make_writable (templ_caps);
/* For output we support any framerate and only really care about timestamps */ /* 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", GST_DEBUG_CATEGORY_INIT (gst_decklink_video_sink_debug, "decklinkvideosink",
0, "debug category for decklinkvideosink element"); 0, "debug category for decklinkvideosink element");
gst_type_mark_as_plugin_api(GST_TYPE_DECKLINK_MAPPING_FORMAT, (GstPluginAPIFlags)0);
} }
static void static void
@ -435,6 +460,7 @@ gst_decklink_video_sink_init (GstDecklinkVideoSink * self)
self->timecode_format = bmdTimecodeRP188Any; self->timecode_format = bmdTimecodeRP188Any;
self->caption_line = 0; self->caption_line = 0;
self->afd_bar_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_max_lateness (GST_BASE_SINK_CAST (self), 20 * GST_MSECOND);
gst_base_sink_set_qos_enabled (GST_BASE_SINK_CAST (self), TRUE); 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: case PROP_AFD_BAR_LINE:
self->afd_bar_line = g_value_get_int (value); self->afd_bar_line = g_value_get_int (value);
break; break;
case PROP_MAPPING_FORMAT:
self->mapping_format = (GstDecklinkMappingFormat) g_value_get_enum (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -538,6 +567,9 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id,
case PROP_AFD_BAR_LINE: case PROP_AFD_BAR_LINE:
g_value_set_int (value, self->afd_bar_line); g_value_set_int (value, self->afd_bar_line);
break; break;
case PROP_MAPPING_FORMAT:
g_value_set_enum (value, self->mapping_format);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;

View file

@ -75,6 +75,7 @@ struct _GstDecklinkVideoSink
guint16 cdp_hdr_sequence_cntr; guint16 cdp_hdr_sequence_cntr;
gint afd_bar_line; gint afd_bar_line;
GstDecklinkMappingFormat mapping_format;
}; };
struct _GstDecklinkVideoSinkClass struct _GstDecklinkVideoSinkClass