decklinkvideosink: Add support for Decklink hardware keying

Add two properties (keyer-mode and keyer-level) to control the built-in hardware keyer of Decklink cards.

https://bugzilla.gnome.org/show_bug.cgi?id=773660
This commit is contained in:
Dave Johnstone 2017-08-15 10:27:03 +09:30 committed by Sebastian Dröge
parent cbf4a44426
commit 0cd1bf13e8
4 changed files with 131 additions and 1 deletions

View file

@ -168,6 +168,25 @@ gst_decklink_timecode_format_get_type (void)
return (GType) id;
}
GType
gst_decklink_keyer_mode_get_type (void)
{
static gsize id = 0;
static const GEnumValue keyermodes[] = {
{GST_DECKLINK_KEYER_MODE_OFF, "Off", "off"},
{GST_DECKLINK_KEYER_MODE_INTERNAL, "Internal", "internal"},
{GST_DECKLINK_KEYER_MODE_EXTERNAL, "External", "external"},
{0, NULL, NULL}
};
if (g_once_init_enter (&id)) {
GType tmp = g_enum_register_static ("GstDecklinkKeyerMode", keyermodes);
g_once_init_leave (&id, tmp);
}
return (GType) id;
}
GType
gst_decklink_audio_connection_get_type (void)
{
@ -298,6 +317,18 @@ static const struct
/* *INDENT-ON* */
};
static const struct
{
BMDKeyerMode keymode;
GstDecklinkKeyerMode gstkeymode;
} kmodes[] = {
/* *INDENT-OFF* */
{bmdKeyerModeOff, GST_DECKLINK_KEYER_MODE_OFF},
{bmdKeyerModeInternal, GST_DECKLINK_KEYER_MODE_INTERNAL},
{bmdKeyerModeExternal, GST_DECKLINK_KEYER_MODE_EXTERNAL}
/* *INDENT-ON* */
};
const GstDecklinkMode *
gst_decklink_get_mode (GstDecklinkModeEnum e)
{
@ -452,6 +483,25 @@ gst_decklink_timecode_format_to_enum (BMDTimecodeFormat f)
return GST_DECKLINK_TIMECODE_FORMAT_RP188ANY;
}
const BMDKeyerMode
gst_decklink_keyer_mode_from_enum (GstDecklinkKeyerMode m)
{
return kmodes[m].keymode;
}
const GstDecklinkKeyerMode
gst_decklink_keyer_mode_to_enum (BMDKeyerMode m)
{
guint i;
for (i = 0; i < G_N_ELEMENTS (kmodes); i++) {
if (kmodes[i].keymode == m)
return (GstDecklinkKeyerMode) i;
}
g_assert_not_reached ();
return GST_DECKLINK_KEYER_MODE_OFF;
}
static const BMDVideoConnection connections[] = {
(BMDVideoConnection) 0, /* auto */
bmdVideoConnectionSDI,
@ -1177,6 +1227,13 @@ init_devices (gpointer data)
"0x%08lx", (unsigned long) ret);
}
ret = decklink->QueryInterface (IID_IDeckLinkKeyer,
(void **) &devices[i].output.keyer);
/* We only warn of failure to obtain the keyer interface if the keyer
* is enabled by keyer_mode
*/
ret = iterator->Next (&decklink);
i++;

View file

@ -156,11 +156,32 @@ typedef enum {
#define GST_TYPE_DECKLINK_TIMECODE_FORMAT (gst_decklink_timecode_format_get_type ())
GType gst_decklink_timecode_format_get_type (void);
typedef enum
{
GST_DECKLINK_KEYER_MODE_OFF,
GST_DECKLINK_KEYER_MODE_INTERNAL,
GST_DECKLINK_KEYER_MODE_EXTERNAL
} GstDecklinkKeyerMode;
#define GST_TYPE_DECKLINK_KEYER_MODE (gst_decklink_keyer_mode_get_type ())
GType gst_decklink_keyer_mode_get_type (void);
/* Enum BMDKeyerMode options of off, internal and external @@@ DJ @@@ */
typedef uint32_t BMDKeyerMode;
enum _BMDKeyerMode
{
bmdKeyerModeOff = /* 'off' */ 0,
bmdKeyerModeInternal = /* 'int' */ 1,
bmdKeyerModeExternal = /* 'ext' */ 2
};
const BMDPixelFormat gst_decklink_pixel_format_from_type (GstDecklinkVideoFormat t);
const gint gst_decklink_bpp_from_type (GstDecklinkVideoFormat t);
const GstDecklinkVideoFormat gst_decklink_type_from_video_format (GstVideoFormat f);
const BMDTimecodeFormat gst_decklink_timecode_format_from_enum (GstDecklinkTimecodeFormat f);
const GstDecklinkTimecodeFormat gst_decklink_timecode_format_to_enum (BMDTimecodeFormat f);
const BMDKeyerMode gst_decklink_keyer_mode_from_enum (GstDecklinkKeyerMode m);
const GstDecklinkKeyerMode gst_decklink_keyer_mode_to_enum (BMDKeyerMode m);
typedef struct _GstDecklinkMode GstDecklinkMode;
struct _GstDecklinkMode {
@ -187,6 +208,7 @@ struct _GstDecklinkOutput {
IDeckLink *device;
IDeckLinkOutput *output;
IDeckLinkAttributes *attributes;
IDeckLinkKeyer *keyer;
GstClock *clock;
GstClockTime clock_start_time, clock_last_time, clock_epoch;
GstClockTimeDiff clock_offset;

View file

@ -120,7 +120,9 @@ enum
PROP_MODE,
PROP_DEVICE_NUMBER,
PROP_VIDEO_FORMAT,
PROP_TIMECODE_FORMAT
PROP_TIMECODE_FORMAT,
PROP_KEYER_MODE,
PROP_KEYER_LEVEL
};
static void gst_decklink_video_sink_set_property (GObject * object,
@ -227,6 +229,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_KEYER_MODE,
g_param_spec_enum ("keyer-mode", "Keyer mode",
"Keyer mode to be enabled",
GST_TYPE_DECKLINK_KEYER_MODE,
GST_DECKLINK_KEYER_MODE_OFF,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT)));
g_object_class_install_property (gobject_class, PROP_KEYER_LEVEL,
g_param_spec_int ("keyer-level", "Keyer level",
"Keyer level", 0, 255, 255,
(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 */
@ -289,6 +305,14 @@ gst_decklink_video_sink_set_property (GObject * object, guint property_id,
gst_decklink_timecode_format_from_enum ((GstDecklinkTimecodeFormat)
g_value_get_enum (value));
break;
case PROP_KEYER_MODE:
self->keyer_mode =
gst_decklink_keyer_mode_from_enum ((GstDecklinkKeyerMode)
g_value_get_enum (value));
break;
case PROP_KEYER_LEVEL:
self->keyer_level = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -315,6 +339,13 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id,
g_value_set_enum (value,
gst_decklink_timecode_format_to_enum (self->timecode_format));
break;
case PROP_KEYER_MODE:
g_value_set_enum (value,
gst_decklink_keyer_mode_to_enum (self->keyer_mode));
break;
case PROP_KEYER_LEVEL:
g_value_set_int (value, self->keyer_level);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -387,6 +418,24 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
g_assert (mode != NULL);
};
/* enable or disable keyer */
if (self->output->keyer != NULL) {
if (self->keyer_mode == bmdKeyerModeOff) {
self->output->keyer->Disable ();
} else if (self->keyer_mode == bmdKeyerModeInternal) {
self->output->keyer->Enable (false);
self->output->keyer->SetLevel (self->keyer_level);
} else if (self->keyer_mode == bmdKeyerModeExternal) {
self->output->keyer->Enable (true);
self->output->keyer->SetLevel (self->keyer_level);
} else {
g_assert_not_reached ();
}
} else if (self->keyer_mode != bmdKeyerModeOff) {
GST_WARNING_OBJECT (self, "Failed to set keyer to mode %d",
self->keyer_mode);
}
/* The timecode_format itself is used when we embed the actual timecode data
* into the frame. Now we only need to know which of the two standards the
* timecode format will adhere to: VITC or RP188, and send the appropriate

View file

@ -53,6 +53,8 @@ struct _GstDecklinkVideoSink
gint device_number;
GstDecklinkVideoFormat video_format;
BMDTimecodeFormat timecode_format;
BMDKeyerMode keyer_mode;
gint keyer_level;
GstVideoInfo info;