mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-13 12:51:16 +00:00
decklink: add support for HDR output and input
Supports PQ and HLG static metadata. Support for HDR is queried from the device and selectively enabled when supported. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7214>
This commit is contained in:
parent
455b6a33b2
commit
0699dd510d
7 changed files with 821 additions and 97 deletions
File diff suppressed because one or more lines are too long
|
@ -353,12 +353,12 @@ gst_decklink_audio_channels_get_type (void)
|
|||
return (GType) id;
|
||||
}
|
||||
|
||||
#define NTSC 10, 11, false, "bt601"
|
||||
#define PAL 12, 11, true, "bt601"
|
||||
#define NTSC_WS 40, 33, false, "bt601"
|
||||
#define PAL_WS 16, 11, true, "bt601"
|
||||
#define HD 1, 1, true, "bt709"
|
||||
#define UHD 1, 1, true, "bt2020"
|
||||
#define NTSC 10, 11, false
|
||||
#define PAL 12, 11, true
|
||||
#define NTSC_WS 40, 33, false
|
||||
#define PAL_WS 16, 11, true
|
||||
#define HD 1, 1, true
|
||||
#define UHD 1, 1, true
|
||||
|
||||
static const GstDecklinkMode modes[] = {
|
||||
{bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC}, // default is ntsc
|
||||
|
@ -757,6 +757,25 @@ gst_decklink_video_format_from_type (BMDPixelFormat pf)
|
|||
return GST_VIDEO_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
GstVideoColorRange
|
||||
gst_decklink_pixel_format_to_range (BMDPixelFormat pf)
|
||||
{
|
||||
switch (pf) {
|
||||
case bmdFormat8BitYUV:
|
||||
case bmdFormat10BitYUV:
|
||||
case bmdFormat8BitARGB:
|
||||
case bmdFormat8BitBGRA:
|
||||
case bmdFormat10BitRGB:
|
||||
case bmdFormat10BitRGBXLE:
|
||||
case bmdFormat10BitRGBX:
|
||||
return GST_VIDEO_COLOR_RANGE_16_235;
|
||||
case bmdFormat12BitRGB:
|
||||
case bmdFormat12BitRGBLE:
|
||||
return GST_VIDEO_COLOR_RANGE_0_255;
|
||||
default:
|
||||
return GST_VIDEO_COLOR_RANGE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const BMDTimecodeFormat
|
||||
gst_decklink_timecode_format_from_enum (GstDecklinkTimecodeFormat f)
|
||||
|
@ -868,7 +887,6 @@ gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f,
|
|||
switch (f) {
|
||||
case bmdFormat8BitYUV: /* '2vuy' */
|
||||
gst_structure_set (s, "format", G_TYPE_STRING, "UYVY",
|
||||
"colorimetry", G_TYPE_STRING, mode->colorimetry,
|
||||
"chroma-site", G_TYPE_STRING, "mpeg2", NULL);
|
||||
break;
|
||||
case bmdFormat10BitYUV: /* 'v210' */
|
||||
|
@ -898,30 +916,69 @@ gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f,
|
|||
}
|
||||
|
||||
GstCaps *
|
||||
gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f,
|
||||
gboolean input)
|
||||
gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDDisplayModeFlags mode_flags, BMDPixelFormat f,
|
||||
BMDDynamicRange dynamic_range, gboolean input)
|
||||
{
|
||||
GstCaps *caps;
|
||||
GstStructure *generic;
|
||||
const char *format;
|
||||
|
||||
caps = gst_caps_new_empty ();
|
||||
caps =
|
||||
gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e, f,
|
||||
input));
|
||||
generic = gst_decklink_mode_get_structure (e, f, input);
|
||||
format = gst_structure_get_string (generic, "format");
|
||||
|
||||
if (g_strcmp0 (format, "UYVY") == 0 || g_strcmp0 (format, "v210") == 0) {
|
||||
if (mode_flags & bmdDisplayModeColorspaceRec601) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt601", NULL);
|
||||
caps = gst_caps_merge_structure (caps, s);
|
||||
}
|
||||
|
||||
if (mode_flags & bmdDisplayModeColorspaceRec709) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt709", NULL);
|
||||
caps = gst_caps_merge_structure (caps, s);
|
||||
}
|
||||
|
||||
if (mode_flags & bmdDisplayModeColorspaceRec2020) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2020", NULL);
|
||||
caps = gst_caps_merge_structure (caps, s);
|
||||
}
|
||||
|
||||
if (dynamic_range & bmdDynamicRangeHDRStaticPQ) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2100-pq", NULL);
|
||||
caps = gst_caps_merge_structure (caps, s);
|
||||
}
|
||||
|
||||
if (dynamic_range & bmdDynamicRangeHDRStaticHLG) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2100-hlg", NULL);
|
||||
caps = gst_caps_merge_structure (caps, s);
|
||||
}
|
||||
} else {
|
||||
caps = gst_caps_merge_structure (caps, generic);
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input)
|
||||
gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e,
|
||||
BMDDisplayModeFlags mode_flags, BMDDynamicRange dynamic_range,
|
||||
gboolean input)
|
||||
{
|
||||
GstCaps *caps;
|
||||
guint i;
|
||||
|
||||
caps = gst_caps_new_empty ();
|
||||
for (i = 1; i < G_N_ELEMENTS (formats); i++)
|
||||
caps =
|
||||
gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e,
|
||||
formats[i].format, input));
|
||||
for (i = 1; i < G_N_ELEMENTS (formats); i++) {
|
||||
GstCaps *format_caps =
|
||||
gst_decklink_mode_get_caps (e, mode_flags, formats[i].format,
|
||||
dynamic_range, input);
|
||||
caps = gst_caps_merge (caps, format_caps);
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
@ -931,12 +988,19 @@ gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input)
|
|||
{
|
||||
int i;
|
||||
GstCaps *caps;
|
||||
GstStructure *s;
|
||||
BMDDisplayModeFlags mode_flags =
|
||||
bmdDisplayModeColorspaceRec601 | bmdDisplayModeColorspaceRec709 |
|
||||
bmdDisplayModeColorspaceRec2020;
|
||||
BMDDynamicRange dynamic_range =
|
||||
(BMDDynamicRange) (bmdDynamicRangeSDR | bmdDynamicRangeHDRStaticPQ |
|
||||
bmdDynamicRangeHDRStaticHLG);
|
||||
|
||||
caps = gst_caps_new_empty ();
|
||||
for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) {
|
||||
s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i, f, input);
|
||||
caps = gst_caps_merge_structure (caps, s);
|
||||
GstCaps *format_caps =
|
||||
gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, mode_flags, f,
|
||||
dynamic_range, input);
|
||||
caps = gst_caps_merge (caps, format_caps);
|
||||
}
|
||||
|
||||
return caps;
|
||||
|
@ -952,8 +1016,8 @@ gst_decklink_mode_get_template_caps (gboolean input)
|
|||
for (i = 1; i < (int) G_N_ELEMENTS (modes); i++)
|
||||
caps =
|
||||
gst_caps_merge (caps,
|
||||
gst_decklink_mode_get_caps_all_formats ((GstDecklinkModeEnum) i,
|
||||
input));
|
||||
gst_decklink_mode_get_caps_all_formats ((GstDecklinkModeEnum) i, -1,
|
||||
(BMDDynamicRange) -1, input));
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
@ -962,6 +1026,9 @@ const GstDecklinkMode *
|
|||
gst_decklink_find_mode_and_format_for_caps (GstCaps * caps,
|
||||
BMDPixelFormat * format)
|
||||
{
|
||||
BMDDisplayModeFlags mode_flags =
|
||||
bmdDisplayModeColorspaceRec601 | bmdDisplayModeColorspaceRec709 |
|
||||
bmdDisplayModeColorspaceRec2020;
|
||||
int i;
|
||||
GstCaps *mode_caps;
|
||||
|
||||
|
@ -971,7 +1038,8 @@ gst_decklink_find_mode_and_format_for_caps (GstCaps * caps,
|
|||
|
||||
for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) {
|
||||
mode_caps =
|
||||
gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, *format, FALSE);
|
||||
gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, mode_flags, *format,
|
||||
(BMDDynamicRange) -1, FALSE);
|
||||
if (gst_caps_can_intersect (caps, mode_caps)) {
|
||||
gst_caps_unref (mode_caps);
|
||||
return gst_decklink_get_mode ((GstDecklinkModeEnum) i);
|
||||
|
@ -1558,12 +1626,18 @@ gst_decklink_com_thread (gpointer data)
|
|||
static GOnce devices_once = G_ONCE_INIT;
|
||||
static GPtrArray *devices; /* array of Device */
|
||||
|
||||
enum SupportedFlags {
|
||||
SUPPORT_NONE = 0,
|
||||
SUPPORT_FORMAT_DETECTION = (1 << 0),
|
||||
SUPPORT_HDR = (1 << 1),
|
||||
SUPPORT_COLORSPACE = (1 << 2),
|
||||
};
|
||||
|
||||
static GstDecklinkDevice *
|
||||
gst_decklink_device_new (const gchar * model_name, const gchar * display_name,
|
||||
const gchar * serial_number, gint64 persistent_id,
|
||||
gboolean supports_format_detection, GstCaps * video_caps,
|
||||
guint max_channels, gboolean video, gboolean capture, guint device_number)
|
||||
enum SupportedFlags supported, GstCaps * video_caps, guint max_channels,
|
||||
gboolean video, gboolean capture, guint device_number)
|
||||
{
|
||||
GstDevice *ret;
|
||||
gchar *name;
|
||||
|
@ -1606,7 +1680,11 @@ gst_decklink_device_new (const gchar * model_name, const gchar * display_name,
|
|||
|
||||
if (capture)
|
||||
gst_structure_set (properties, "supports-format-detection", G_TYPE_BOOLEAN,
|
||||
supports_format_detection, NULL);
|
||||
(supported & SUPPORT_FORMAT_DETECTION) != SUPPORT_NONE, NULL);
|
||||
|
||||
gst_structure_set (properties, "supports-hdr", G_TYPE_BOOLEAN,
|
||||
(supported & SUPPORT_HDR) != SUPPORT_NONE, "supports-colorspace",
|
||||
G_TYPE_BOOLEAN, (supported & SUPPORT_COLORSPACE) != SUPPORT_NONE, NULL);
|
||||
|
||||
if (serial_number)
|
||||
gst_structure_set (properties, "serial-number", G_TYPE_STRING,
|
||||
|
@ -1679,7 +1757,8 @@ init_devices (gpointer data)
|
|||
gchar *display_name = NULL;
|
||||
gchar *serial_number = NULL;
|
||||
gint64 persistent_id = 0;
|
||||
gboolean supports_format_detection = 0;
|
||||
enum SupportedFlags supported = SUPPORT_NONE;
|
||||
BMDDynamicRange dynamic_range = (BMDDynamicRange) 0;
|
||||
gint64 max_channels = 2;
|
||||
GstCaps *video_input_caps = gst_caps_new_empty ();
|
||||
GstCaps *video_output_caps = gst_caps_new_empty ();
|
||||
|
@ -1690,6 +1769,59 @@ init_devices (gpointer data)
|
|||
g_mutex_init (&dev->output.lock);
|
||||
g_cond_init (&dev->output.cond);
|
||||
|
||||
ret = decklink->QueryInterface (IID_IDeckLinkProfileAttributes,
|
||||
(void **) &dev->input.attributes);
|
||||
dev->output.attributes = dev->input.attributes;
|
||||
if (ret != S_OK) {
|
||||
GST_WARNING ("selected device does not have attributes interface: "
|
||||
"0x%08lx", (unsigned long) ret);
|
||||
} else {
|
||||
bool tmp_bool = false;
|
||||
int64_t tmp_int = 2;
|
||||
int64_t tmp_int_persistent_id = 0;
|
||||
|
||||
dev->input.attributes->GetInt (BMDDeckLinkMaximumAudioChannels, &tmp_int);
|
||||
max_channels = tmp_int;
|
||||
dev->input.attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection,
|
||||
&tmp_bool);
|
||||
GST_INFO ("device %d supports format detection %u", i, tmp_bool);
|
||||
if (tmp_bool)
|
||||
supported = (enum SupportedFlags) ((guint32) supported | SUPPORT_FORMAT_DETECTION);
|
||||
|
||||
dev->input.attributes->GetFlag (BMDDeckLinkSupportsColorspaceMetadata,
|
||||
&tmp_bool);
|
||||
GST_INFO ("device %d supports Colorspace Metadata %u", i, tmp_bool);
|
||||
if (tmp_bool)
|
||||
supported = (enum SupportedFlags) ((guint32) supported | SUPPORT_COLORSPACE);
|
||||
dev->input.attributes->GetFlag (BMDDeckLinkSupportsHDRMetadata,
|
||||
&tmp_bool);
|
||||
GST_INFO ("device %d supports HDR %u", i, tmp_bool);
|
||||
if (tmp_bool)
|
||||
supported = (enum SupportedFlags) ((guint32) supported | SUPPORT_HDR);
|
||||
|
||||
if (supported & SUPPORT_HDR) {
|
||||
ret = dev->input.attributes->GetInt (BMDDeckLinkSupportedDynamicRange,
|
||||
&tmp_int);
|
||||
if (ret == S_OK)
|
||||
dynamic_range = (BMDDynamicRange) tmp_int;
|
||||
}
|
||||
|
||||
ret =
|
||||
dev->input.attributes->GetInt (BMDDeckLinkPersistentID,
|
||||
&tmp_int_persistent_id);
|
||||
if (ret == S_OK) {
|
||||
persistent_id = tmp_int_persistent_id;
|
||||
dev->output.persistent_id = persistent_id;
|
||||
dev->input.persistent_id = persistent_id;
|
||||
GST_DEBUG ("device %d has persistent id %" G_GINT64_FORMAT, i, persistent_id);
|
||||
} else {
|
||||
persistent_id = i;
|
||||
dev->output.persistent_id = i;
|
||||
dev->input.persistent_id = i;
|
||||
GST_DEBUG ("device %d does not have persistent id. Value set to %d", i, i);
|
||||
}
|
||||
}
|
||||
|
||||
ret = decklink->QueryInterface (IID_IDeckLinkInput,
|
||||
(void **) &dev->input.input);
|
||||
if (ret != S_OK) {
|
||||
|
@ -1712,10 +1844,54 @@ init_devices (gpointer data)
|
|||
|
||||
mode_enum =
|
||||
gst_decklink_get_mode_enum_from_bmd (mode->GetDisplayMode ());
|
||||
if (mode_enum != (GstDecklinkModeEnum) - 1)
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps,
|
||||
gst_decklink_mode_get_generic_structure (mode_enum));
|
||||
if (mode_enum != (GstDecklinkModeEnum) - 1) {
|
||||
GstStructure *generic = gst_decklink_mode_get_generic_structure (mode_enum);
|
||||
BMDDisplayModeFlags flags = mode->GetFlags ();
|
||||
|
||||
if ((supported & SUPPORT_COLORSPACE) ||
|
||||
(flags & bmdDisplayModeColorspaceRec601)) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt601",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if ((supported & SUPPORT_COLORSPACE) ||
|
||||
(flags & bmdDisplayModeColorspaceRec709)) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt709",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if ((supported & SUPPORT_COLORSPACE) ||
|
||||
(flags & bmdDisplayModeColorspaceRec2020)) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2020",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if (dynamic_range & bmdDynamicRangeHDRStaticPQ) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2100-pq",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if (dynamic_range & bmdDynamicRangeHDRStaticHLG) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2100-hlg",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
gst_clear_structure (&generic);
|
||||
}
|
||||
|
||||
mode->GetName ((COMSTR_T *) & name);
|
||||
CONVERT_COM_STRING (name);
|
||||
|
@ -1759,10 +1935,54 @@ init_devices (gpointer data)
|
|||
|
||||
mode_enum =
|
||||
gst_decklink_get_mode_enum_from_bmd (mode->GetDisplayMode ());
|
||||
if (mode_enum != (GstDecklinkModeEnum) - 1)
|
||||
video_output_caps =
|
||||
gst_caps_merge_structure (video_output_caps,
|
||||
gst_decklink_mode_get_generic_structure (mode_enum));
|
||||
if (mode_enum != (GstDecklinkModeEnum) - 1) {
|
||||
GstStructure *generic = gst_decklink_mode_get_generic_structure (mode_enum);
|
||||
BMDDisplayModeFlags flags = mode->GetFlags ();
|
||||
|
||||
if ((supported & SUPPORT_COLORSPACE) ||
|
||||
(flags & bmdDisplayModeColorspaceRec601)) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt601",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if ((supported & SUPPORT_COLORSPACE) ||
|
||||
(flags & bmdDisplayModeColorspaceRec601)) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt709",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if ((supported & SUPPORT_COLORSPACE) ||
|
||||
(flags & bmdDisplayModeColorspaceRec2020)) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2020",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if (dynamic_range & bmdDynamicRangeHDRStaticPQ) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2100-pq",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
|
||||
if (dynamic_range & bmdDynamicRangeHDRStaticHLG) {
|
||||
GstStructure *s = gst_structure_copy (generic);
|
||||
gst_structure_set (s, "colorimetry", G_TYPE_STRING, "bt2100-hlg",
|
||||
NULL);
|
||||
video_input_caps =
|
||||
gst_caps_merge_structure (video_input_caps, s);
|
||||
}
|
||||
gst_clear_structure (&generic);
|
||||
}
|
||||
|
||||
mode->GetName ((COMSTR_T *) & name);
|
||||
CONVERT_COM_STRING (name);
|
||||
|
@ -1800,39 +2020,6 @@ init_devices (gpointer data)
|
|||
}
|
||||
}
|
||||
|
||||
ret = decklink->QueryInterface (IID_IDeckLinkProfileAttributes,
|
||||
(void **) &dev->input.attributes);
|
||||
dev->output.attributes = dev->input.attributes;
|
||||
if (ret != S_OK) {
|
||||
GST_WARNING ("selected device does not have attributes interface: "
|
||||
"0x%08lx", (unsigned long) ret);
|
||||
} else {
|
||||
bool tmp_bool = false;
|
||||
int64_t tmp_int = 2;
|
||||
int64_t tmp_int_persistent_id = 0;
|
||||
|
||||
dev->input.attributes->GetInt (BMDDeckLinkMaximumAudioChannels, &tmp_int);
|
||||
dev->input.attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection,
|
||||
&tmp_bool);
|
||||
supports_format_detection = tmp_bool;
|
||||
max_channels = tmp_int;
|
||||
|
||||
ret =
|
||||
dev->input.attributes->GetInt (BMDDeckLinkPersistentID,
|
||||
&tmp_int_persistent_id);
|
||||
if (ret == S_OK) {
|
||||
persistent_id = tmp_int_persistent_id;
|
||||
dev->output.persistent_id = persistent_id;
|
||||
dev->input.persistent_id = persistent_id;
|
||||
GST_DEBUG ("device %d has persistent id %" G_GINT64_FORMAT, i, persistent_id);
|
||||
} else {
|
||||
persistent_id = i;
|
||||
dev->output.persistent_id = i;
|
||||
dev->input.persistent_id = i;
|
||||
GST_DEBUG ("device %d does not have persistent id. Value set to %d", i, i);
|
||||
}
|
||||
}
|
||||
|
||||
decklink->GetModelName ((COMSTR_T *) & model_name);
|
||||
if (model_name)
|
||||
CONVERT_COM_STRING (model_name);
|
||||
|
@ -1843,22 +2030,22 @@ init_devices (gpointer data)
|
|||
if (capture) {
|
||||
dev->devices[0] =
|
||||
gst_decklink_device_new (model_name, display_name, serial_number,
|
||||
persistent_id, supports_format_detection, video_input_caps,
|
||||
max_channels, TRUE, TRUE, i);
|
||||
persistent_id, supported, video_input_caps, max_channels, TRUE, TRUE,
|
||||
i);
|
||||
dev->devices[1] =
|
||||
gst_decklink_device_new (model_name, display_name, serial_number,
|
||||
persistent_id, supports_format_detection, video_input_caps,
|
||||
max_channels, FALSE, TRUE, i);
|
||||
persistent_id, supported, video_input_caps, max_channels, FALSE, TRUE,
|
||||
i);
|
||||
}
|
||||
if (output) {
|
||||
dev->devices[2] =
|
||||
gst_decklink_device_new (model_name, display_name, serial_number,
|
||||
persistent_id, supports_format_detection, video_output_caps,
|
||||
max_channels, TRUE, FALSE, i);
|
||||
persistent_id, supported, video_output_caps, max_channels, TRUE,
|
||||
FALSE, i);
|
||||
dev->devices[3] =
|
||||
gst_decklink_device_new (model_name, display_name, serial_number,
|
||||
persistent_id, supports_format_detection, video_output_caps,
|
||||
max_channels, FALSE, FALSE, i);
|
||||
persistent_id, supported, video_output_caps, max_channels, FALSE,
|
||||
FALSE, i);
|
||||
}
|
||||
|
||||
if (model_name)
|
||||
|
|
|
@ -50,6 +50,11 @@
|
|||
::MultiByteToWideChar(CP_ACP, 0, (char*)_s, -1, s, _length); \
|
||||
g_free(_s); \
|
||||
} G_STMT_END
|
||||
#define REFIID_FORMAT "08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
|
||||
#define REFIID_ARGS(_id) \
|
||||
(guint32) (_id).Data1, (_id).Data2, (_id).Data3, \
|
||||
(_id).Data4[0], (_id).Data4[1], (_id).Data4[2], (_id).Data4[3], \
|
||||
(_id).Data4[4], (_id).Data4[5], (_id).Data4[6], (_id).Data4[7]
|
||||
#elif defined(__APPLE__)
|
||||
#include "osx/DeckLinkAPI.h"
|
||||
|
||||
|
@ -70,6 +75,12 @@
|
|||
g_free(_s); \
|
||||
} G_STMT_END
|
||||
#define WINAPI
|
||||
#define REFIID_FORMAT "02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X"
|
||||
#define REFIID_ARGS(_id) \
|
||||
(_id).byte0, (_id).byte1, (_id).byte2, (_id).byte3, \
|
||||
(_id).byte4, (_id).byte5, (_id).byte6, (_id).byte7, \
|
||||
(_id).byte8, (_id).byte9, (_id).byte10, (_id).byte11, \
|
||||
(_id).byte12, (_id).byte13, (_id).byte14, (_id).byte15
|
||||
#else /* Linux */
|
||||
#include "linux/DeckLinkAPI.h"
|
||||
|
||||
|
@ -79,6 +90,12 @@
|
|||
/* While this is a const char*, the string still has to be freed */
|
||||
#define FREE_COM_STRING(s) free(s);
|
||||
#define WINAPI
|
||||
#define REFIID_FORMAT "02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X"
|
||||
#define REFIID_ARGS(_id) \
|
||||
(_id).byte0, (_id).byte1, (_id).byte2, (_id).byte3, \
|
||||
(_id).byte4, (_id).byte5, (_id).byte6, (_id).byte7, \
|
||||
(_id).byte8, (_id).byte9, (_id).byte10, (_id).byte11, \
|
||||
(_id).byte12, (_id).byte13, (_id).byte14, (_id).byte15
|
||||
#endif /* G_OS_WIN32 */
|
||||
|
||||
void decklink_element_init (GstPlugin * plugin);
|
||||
|
@ -395,6 +412,7 @@ enum _BMDKeyerMode
|
|||
};
|
||||
|
||||
const BMDPixelFormat gst_decklink_pixel_format_from_type (GstDecklinkVideoFormat t);
|
||||
GstVideoColorRange gst_decklink_pixel_format_to_range (BMDPixelFormat pf);
|
||||
const gint gst_decklink_bpp_from_type (GstDecklinkVideoFormat t);
|
||||
const GstDecklinkVideoFormat gst_decklink_type_from_video_format (GstVideoFormat f);
|
||||
GstVideoFormat gst_decklink_video_format_from_type (BMDPixelFormat pf);
|
||||
|
@ -416,13 +434,12 @@ struct _GstDecklinkMode {
|
|||
int par_n;
|
||||
int par_d;
|
||||
gboolean tff;
|
||||
const gchar *colorimetry;
|
||||
};
|
||||
|
||||
const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e);
|
||||
const GstDecklinkModeEnum gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode);
|
||||
const BMDVideoConnection gst_decklink_get_connection (GstDecklinkConnectionEnum e);
|
||||
GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f, gboolean input);
|
||||
GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDDisplayModeFlags flags, BMDPixelFormat f, BMDDynamicRange dynamic_range, gboolean input);
|
||||
GstCaps * gst_decklink_mode_get_template_caps (gboolean input);
|
||||
|
||||
typedef struct _GstDecklinkOutput GstDecklinkOutput;
|
||||
|
@ -494,7 +511,7 @@ void gst_decklink_release_nth_input (gint n, gint64 persistent_id
|
|||
|
||||
const GstDecklinkMode * gst_decklink_find_mode_for_caps (GstCaps * caps);
|
||||
const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format);
|
||||
GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input);
|
||||
GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, BMDDisplayModeFlags flags, BMDDynamicRange dynamic_range, gboolean input);
|
||||
GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input);
|
||||
|
||||
#define GST_TYPE_DECKLINK_DEVICE gst_decklink_device_get_type()
|
||||
|
|
|
@ -216,7 +216,7 @@ public:
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *)
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, LPVOID *)
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
@ -253,21 +253,46 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class GstDecklinkVideoFrame:public IDeckLinkVideoFrame
|
||||
class GstDecklinkVideoFrame:public IDeckLinkVideoFrame, public IDeckLinkVideoFrameMetadataExtensions
|
||||
{
|
||||
public:
|
||||
GstDecklinkVideoFrame (GstVideoFrame * frame):
|
||||
running_time(0), running_time_duration(0), sync_buffer(0), m_frame(0),
|
||||
m_dframe (0), m_ancillary (0), m_timecode (0), m_refcount (1)
|
||||
have_light_level(FALSE), have_mastering_info(FALSE), m_dframe (0),
|
||||
m_ancillary (0), m_timecode (0), m_refcount (1)
|
||||
{
|
||||
m_frame = g_new0 (GstVideoFrame, 1);
|
||||
*m_frame = *frame;
|
||||
memset (&light_level, 0, sizeof (light_level));
|
||||
memset (&mastering_info, 0, sizeof (mastering_info));
|
||||
memset (&colorimetry, 0, sizeof (colorimetry));
|
||||
}
|
||||
|
||||
GstDecklinkVideoFrame (IDeckLinkMutableVideoFrame * dframe):
|
||||
running_time(0), running_time_duration(0), sync_buffer(0), m_frame(0),
|
||||
m_dframe (dframe), m_ancillary (0), m_timecode (0), m_refcount (1)
|
||||
have_light_level(FALSE), have_mastering_info(FALSE), m_dframe (dframe),
|
||||
m_ancillary (0), m_timecode (0), m_refcount (1)
|
||||
{
|
||||
memset (&light_level, 0, sizeof (light_level));
|
||||
memset (&mastering_info, 0, sizeof (mastering_info));
|
||||
memset (&colorimetry, 0, sizeof (colorimetry));
|
||||
}
|
||||
|
||||
void SetColorimetry (GstVideoColorimetry *colorimetry)
|
||||
{
|
||||
this->colorimetry = *colorimetry;
|
||||
}
|
||||
|
||||
void SetLightLevel (GstVideoContentLightLevel * ll)
|
||||
{
|
||||
light_level = *ll;
|
||||
have_light_level = TRUE;
|
||||
}
|
||||
|
||||
void SetMastringInfo (GstVideoMasteringDisplayInfo * mdi)
|
||||
{
|
||||
memcpy (&mastering_info, mdi, sizeof (*mdi));
|
||||
have_mastering_info = TRUE;
|
||||
}
|
||||
|
||||
virtual long STDMETHODCALLTYPE GetWidth (void)
|
||||
|
@ -305,7 +330,14 @@ public:
|
|||
}
|
||||
virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags (void)
|
||||
{
|
||||
return m_dframe ? m_dframe->GetFlags () : bmdFrameFlagDefault;
|
||||
BMDFrameFlags flags = m_dframe ? m_dframe->GetFlags () : bmdFrameFlagDefault;
|
||||
|
||||
if (have_mastering_info || have_light_level ||
|
||||
colorimetry.transfer == GST_VIDEO_TRANSFER_ARIB_STD_B67) {
|
||||
flags |= bmdFrameContainsHDRMetadata;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
virtual HRESULT STDMETHODCALLTYPE GetBytes (void **buffer)
|
||||
{
|
||||
|
@ -363,11 +395,161 @@ public:
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *)
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, LPVOID * ret)
|
||||
{
|
||||
GST_LOG ("frame queryinterface: %" REFIID_FORMAT, REFIID_ARGS (iid));
|
||||
if (memcmp (&iid, &IID_IDeckLinkVideoFrameMetadataExtensions, sizeof (iid))
|
||||
== 0) {
|
||||
AddRef ();
|
||||
*ret = (LPVOID *) static_cast<IDeckLinkVideoFrameMetadataExtensions *>(this);
|
||||
return S_OK;
|
||||
}
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t* value)
|
||||
{
|
||||
GST_LOG ("frame meta get int for 0x%x", metadataID);
|
||||
|
||||
switch (metadataID) {
|
||||
case bmdDeckLinkFrameMetadataColorspace: {
|
||||
switch (colorimetry.matrix) {
|
||||
case GST_VIDEO_COLOR_MATRIX_BT601:
|
||||
*value = bmdColorspaceRec601;
|
||||
return S_OK;
|
||||
case GST_VIDEO_COLOR_MATRIX_BT709:
|
||||
*value = bmdColorspaceRec709;
|
||||
return S_OK;
|
||||
case GST_VIDEO_COLOR_MATRIX_BT2020:
|
||||
*value = bmdColorspaceRec2020;
|
||||
return S_OK;
|
||||
default:
|
||||
GST_DEBUG ("no mapping from video color matrix 0x%x to BMD", colorimetry.matrix);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc:
|
||||
switch (colorimetry.transfer) {
|
||||
case GST_VIDEO_TRANSFER_BT601:
|
||||
case GST_VIDEO_TRANSFER_BT709:
|
||||
case GST_VIDEO_TRANSFER_BT2020_10:
|
||||
if (have_mastering_info && have_mastering_info)
|
||||
*value = 1;
|
||||
else
|
||||
*value = 0;
|
||||
return S_OK;
|
||||
case GST_VIDEO_TRANSFER_SMPTE2084:
|
||||
*value = 2;
|
||||
return S_OK;
|
||||
case GST_VIDEO_TRANSFER_ARIB_STD_B67:
|
||||
*value = 3;
|
||||
return S_OK;
|
||||
default:
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
virtual HRESULT STDMETHODCALLTYPE GetFloat (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ double* value)
|
||||
{
|
||||
GST_LOG ("frame meta get float for 0x%x", metadataID);\
|
||||
|
||||
switch (metadataID) {
|
||||
case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.display_primaries[0].x / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.display_primaries[0].y / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.display_primaries[1].x / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.display_primaries[1].y / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.display_primaries[2].x / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.display_primaries[2].y / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRWhitePointX:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.white_point.x / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRWhitePointY:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.white_point.y / 50000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.max_display_mastering_luminance * 65535.0 / 10000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance:
|
||||
if (have_mastering_info) {
|
||||
*value = (double) mastering_info.min_display_mastering_luminance * 6.55350 / 10000.0;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel:
|
||||
if (have_light_level) {
|
||||
*value = (double) light_level.max_content_light_level;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
case bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel:
|
||||
if (have_light_level) {
|
||||
*value = (double) light_level.max_frame_average_light_level;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
default:
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
virtual HRESULT STDMETHODCALLTYPE GetFlag (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ bool* value)
|
||||
{
|
||||
GST_LOG ("frame meta get flag for 0x%x", metadataID);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
virtual HRESULT STDMETHODCALLTYPE GetString (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ COMSTR_T* value)
|
||||
{
|
||||
GST_LOG ("frame meta get string for 0x%x", metadataID);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
virtual HRESULT STDMETHODCALLTYPE GetBytes (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ void* buffer /* optional */, /* in, out */ uint32_t* bufferSize)
|
||||
{
|
||||
GST_LOG ("frame meta get bytes for 0x%x", metadataID);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
virtual ULONG STDMETHODCALLTYPE AddRef (void)
|
||||
{
|
||||
ULONG ret;
|
||||
|
@ -394,10 +576,15 @@ public:
|
|||
GstBuffer *sync_buffer;
|
||||
private:
|
||||
GstVideoFrame * m_frame;
|
||||
gboolean have_light_level;
|
||||
gboolean have_mastering_info;
|
||||
IDeckLinkMutableVideoFrame *m_dframe;
|
||||
IDeckLinkVideoFrameAncillary *m_ancillary;
|
||||
GstDecklinkTimecode *m_timecode;
|
||||
int m_refcount;
|
||||
GstVideoContentLightLevel light_level;
|
||||
GstVideoMasteringDisplayInfo mastering_info;
|
||||
GstVideoColorimetry colorimetry;
|
||||
|
||||
virtual ~ GstDecklinkVideoFrame () {
|
||||
if (m_frame) {
|
||||
|
@ -923,7 +1110,6 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
if (!gst_video_info_from_caps (&info, caps))
|
||||
return FALSE;
|
||||
|
||||
|
||||
g_mutex_lock (&self->output->lock);
|
||||
if (self->output->video_enabled) {
|
||||
if (self->info.finfo->format == info.finfo->format &&
|
||||
|
@ -932,6 +1118,11 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
// for mode selection below in auto mode
|
||||
GST_DEBUG_OBJECT (self, "Nothing relevant has changed");
|
||||
self->info = info;
|
||||
self->have_light_level =
|
||||
gst_video_content_light_level_from_caps (&self->light_level, caps);
|
||||
self->have_mastering_info =
|
||||
gst_video_mastering_display_info_from_caps (&self->mastering_info,
|
||||
caps);
|
||||
g_mutex_unlock (&self->output->lock);
|
||||
return TRUE;
|
||||
} else {
|
||||
|
@ -1012,6 +1203,10 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
}
|
||||
|
||||
self->info = info;
|
||||
self->have_light_level =
|
||||
gst_video_content_light_level_from_caps (&self->light_level, caps);
|
||||
self->have_mastering_info =
|
||||
gst_video_mastering_display_info_from_caps (&self->mastering_info, caps);
|
||||
g_mutex_lock (&self->output->lock);
|
||||
self->output->mode = mode;
|
||||
self->output->video_enabled = TRUE;
|
||||
|
@ -1028,6 +1223,52 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static BMDDisplayModeFlags
|
||||
display_mode_flags (GstDecklinkVideoSink * self, GstDecklinkModeEnum e)
|
||||
{
|
||||
BMDDisplayModeFlags display_flags =
|
||||
bmdDisplayModeColorspaceRec601 | bmdDisplayModeColorspaceRec709 |
|
||||
bmdDisplayModeColorspaceRec2020;
|
||||
|
||||
if (self->output && self->output->output) {
|
||||
const GstDecklinkMode *gst_mode = gst_decklink_get_mode (e);
|
||||
IDeckLinkDisplayMode *display_mode = nullptr;
|
||||
bool supports_colorspace = false;
|
||||
|
||||
self->output->attributes->GetFlag (BMDDeckLinkSupportsColorspaceMetadata,
|
||||
&supports_colorspace);
|
||||
|
||||
if (!supports_colorspace) {
|
||||
self->output->output->GetDisplayMode (gst_mode->mode, &display_mode);
|
||||
if (display_mode) {
|
||||
display_flags = display_mode->GetFlags ();
|
||||
display_mode->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return display_flags;
|
||||
}
|
||||
|
||||
static BMDDynamicRange
|
||||
device_dynamic_range (GstDecklinkVideoSink * self)
|
||||
{
|
||||
BMDDynamicRange range =
|
||||
(BMDDynamicRange) (bmdDynamicRangeSDR | bmdDynamicRangeHDRStaticPQ |
|
||||
bmdDynamicRangeHDRStaticHLG);
|
||||
|
||||
if (self->output && self->output->attributes) {
|
||||
gint64 tmp_int = 0;
|
||||
HRESULT ret =
|
||||
self->output->attributes->GetInt (BMDDeckLinkSupportedDynamicRange,
|
||||
&tmp_int);
|
||||
if (ret == S_OK)
|
||||
range = (BMDDynamicRange) tmp_int;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_decklink_video_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
|
||||
{
|
||||
|
@ -1038,15 +1279,21 @@ gst_decklink_video_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
|
|||
&& self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
|
||||
mode_caps = gst_decklink_mode_get_template_caps (FALSE);
|
||||
else if (self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
|
||||
mode_caps = gst_decklink_mode_get_caps_all_formats (self->mode, FALSE);
|
||||
mode_caps =
|
||||
gst_decklink_mode_get_caps_all_formats (self->mode,
|
||||
display_mode_flags (self, self->mode), device_dynamic_range (self),
|
||||
FALSE);
|
||||
else if (self->mode == GST_DECKLINK_MODE_AUTO)
|
||||
mode_caps =
|
||||
gst_decklink_pixel_format_get_caps (gst_decklink_pixel_format_from_type
|
||||
(self->video_format), FALSE);
|
||||
else
|
||||
else {
|
||||
mode_caps =
|
||||
gst_decklink_mode_get_caps (self->mode,
|
||||
gst_decklink_pixel_format_from_type (self->video_format), FALSE);
|
||||
display_mode_flags (self, self->mode),
|
||||
gst_decklink_pixel_format_from_type (self->video_format),
|
||||
device_dynamic_range (self), FALSE);
|
||||
}
|
||||
mode_caps = gst_caps_make_writable (mode_caps);
|
||||
/* For output we support any framerate and only really care about timestamps */
|
||||
gst_caps_map_in_place (mode_caps, reset_framerate, NULL);
|
||||
|
@ -1654,6 +1901,7 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
const guint8 *indata;
|
||||
gint i, src_stride, dest_stride, stride;
|
||||
IDeckLinkMutableVideoFrame *dframe;
|
||||
GstVideoColorimetry colorimetry;
|
||||
|
||||
ret = self->output->output->CreateVideoFrame (self->info.width,
|
||||
self->info.height, self->info.stride[0], format, bmdFrameFlagDefault,
|
||||
|
@ -1677,13 +1925,16 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
indata += src_stride;
|
||||
outdata += dest_stride;
|
||||
}
|
||||
colorimetry = vframe.info.colorimetry;
|
||||
gst_video_frame_unmap (&vframe);
|
||||
|
||||
// Takes ownership of the frame
|
||||
frame = new GstDecklinkVideoFrame (dframe);
|
||||
frame->SetColorimetry (&colorimetry);
|
||||
} else {
|
||||
// Takes ownership of the frame
|
||||
frame = new GstDecklinkVideoFrame (&vframe);
|
||||
frame->SetColorimetry (&vframe.info.colorimetry);
|
||||
}
|
||||
|
||||
tc_meta = gst_buffer_get_video_time_code_meta (buffer);
|
||||
|
@ -1696,6 +1947,11 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
g_free (tc_str);
|
||||
}
|
||||
|
||||
if (self->have_light_level)
|
||||
frame->SetLightLevel (&self->light_level);
|
||||
if (self->have_mastering_info)
|
||||
frame->SetMastringInfo (&self->mastering_info);
|
||||
|
||||
write_vbi (self, buffer, format, frame, tc_meta);
|
||||
|
||||
frame->running_time = running_time;
|
||||
|
@ -1739,7 +1995,7 @@ gst_decklink_video_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
frame = (GstDecklinkVideoFrame *) g_queue_peek_head (self->pending_frames);
|
||||
GST_ERROR_OBJECT (self, "attempting preroll");
|
||||
GST_DEBUG_OBJECT (self, "attempting preroll");
|
||||
flow_ret =
|
||||
gst_base_sink_do_preroll (bsink,
|
||||
GST_MINI_OBJECT_CAST (frame->sync_buffer));
|
||||
|
|
|
@ -80,6 +80,11 @@ struct _GstDecklinkVideoSink
|
|||
|
||||
gboolean initial_sync;
|
||||
GQueue *pending_frames;
|
||||
|
||||
gboolean have_light_level;
|
||||
GstVideoContentLightLevel light_level;
|
||||
gboolean have_mastering_info;
|
||||
GstVideoMasteringDisplayInfo mastering_info;
|
||||
};
|
||||
|
||||
struct _GstDecklinkVideoSinkClass
|
||||
|
|
|
@ -188,6 +188,11 @@ typedef struct
|
|||
GstDecklinkModeEnum mode;
|
||||
BMDPixelFormat format;
|
||||
GstVideoTimeCode *tc;
|
||||
GstVideoColorimetry colorimetry;
|
||||
gboolean have_light_level;
|
||||
GstVideoContentLightLevel light_level;
|
||||
gboolean have_mastering_info;
|
||||
GstVideoMasteringDisplayInfo mastering_info;
|
||||
gboolean no_signal;
|
||||
} CaptureFrame;
|
||||
|
||||
|
@ -828,6 +833,33 @@ gst_decklink_video_src_update_time_mapping (GstDecklinkVideoSrc * self,
|
|||
}
|
||||
}
|
||||
|
||||
static BMDDisplayModeFlags
|
||||
display_mode_flags (GstDecklinkVideoSrc * self, const GstDecklinkMode * gst_mode,
|
||||
gboolean fixed)
|
||||
{
|
||||
BMDDisplayModeFlags display_flags =
|
||||
bmdDisplayModeColorspaceRec601 | bmdDisplayModeColorspaceRec709 |
|
||||
bmdDisplayModeColorspaceRec2020;
|
||||
|
||||
if (self->input && self->input->input) {
|
||||
IDeckLinkDisplayMode *display_mode = nullptr;
|
||||
bool supports_colorspace = false;
|
||||
|
||||
self->input->attributes->GetFlag (BMDDeckLinkSupportsColorspaceMetadata,
|
||||
&supports_colorspace);
|
||||
|
||||
if (!supports_colorspace || fixed) {
|
||||
self->input->input->GetDisplayMode (gst_mode->mode, &display_mode);
|
||||
if (display_mode) {
|
||||
display_flags = display_mode->GetFlags ();
|
||||
display_mode->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return display_flags;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_decklink_video_src_got_frame (GstElement * element,
|
||||
IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode,
|
||||
|
@ -903,6 +935,8 @@ gst_decklink_video_src_got_frame (GstElement * element,
|
|||
GstVideoTimeCodeFlags flags = GST_VIDEO_TIME_CODE_FLAGS_NONE;
|
||||
guint field_count = 0;
|
||||
guint skipped_frames = 0;
|
||||
IDeckLinkVideoFrameMetadataExtensions *frame_metadata = nullptr;
|
||||
HRESULT dk_ret;
|
||||
|
||||
while (gst_vec_deque_get_length (self->current_frames) >=
|
||||
self->buffer_size) {
|
||||
|
@ -948,6 +982,155 @@ gst_decklink_video_src_got_frame (GstElement * element,
|
|||
f.mode = mode;
|
||||
f.format = frame->GetPixelFormat ();
|
||||
f.no_signal = no_signal;
|
||||
|
||||
bmode = gst_decklink_get_mode (mode);
|
||||
/* these are defaults for the display mode, metadata may override these */
|
||||
BMDDisplayModeFlags mode_flags = display_mode_flags (self, bmode, FALSE);
|
||||
if (mode_flags & bmdDisplayModeColorspaceRec601) {
|
||||
gst_video_colorimetry_from_string (&f.colorimetry, "bt601");
|
||||
}
|
||||
if (mode_flags & bmdDisplayModeColorspaceRec709) {
|
||||
gst_video_colorimetry_from_string (&f.colorimetry, "bt709");
|
||||
}
|
||||
if (mode_flags & bmdDisplayModeColorspaceRec2020) {
|
||||
gst_video_colorimetry_from_string (&f.colorimetry, "bt2020");
|
||||
}
|
||||
f.colorimetry.range = gst_decklink_pixel_format_to_range (f.format);
|
||||
|
||||
dk_ret =
|
||||
frame->QueryInterface (IID_IDeckLinkVideoFrameMetadataExtensions,
|
||||
(LPVOID *) &frame_metadata);
|
||||
if (frame_metadata && dk_ret == S_OK) {
|
||||
gint64 colorspace;
|
||||
dk_ret =
|
||||
frame_metadata->GetInt (bmdDeckLinkFrameMetadataColorspace,
|
||||
&colorspace);
|
||||
GST_LOG_OBJECT (self, "ret %x colorspace 0x%" G_GINT64_FORMAT,
|
||||
(gint) dk_ret, colorspace);
|
||||
|
||||
if (dk_ret == S_OK) {
|
||||
switch (colorspace) {
|
||||
case bmdColorspaceRec601:
|
||||
f.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
|
||||
f.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_BT601;
|
||||
break;
|
||||
case bmdColorspaceRec709:
|
||||
f.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
|
||||
f.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
|
||||
break;
|
||||
case bmdColorspaceRec2020:
|
||||
f.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
|
||||
f.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_BT2020_12;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->GetFlags () & bmdFrameContainsHDRMetadata) {
|
||||
double max_cll, max_fll, x, y;
|
||||
gint64 tf;
|
||||
|
||||
f.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
|
||||
f.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_BT2020_12;
|
||||
|
||||
dk_ret =
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel, &max_cll);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel, &max_fll);
|
||||
GST_LOG_OBJECT (self, "ret %x maxcll %f maxfll %f", (gint) dk_ret, max_cll,
|
||||
max_fll);
|
||||
if (dk_ret == S_OK) {
|
||||
f.have_light_level = TRUE;
|
||||
f.light_level.max_content_light_level = (guint16) max_cll;
|
||||
f.light_level.max_frame_average_light_level = (guint16) max_fll;
|
||||
}
|
||||
|
||||
dk_ret =
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX, &x);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY, &y);
|
||||
f.mastering_info.display_primaries[0].x = (guint16) (x * 50000.0);
|
||||
f.mastering_info.display_primaries[0].y = (guint16) (y * 50000.0);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX, &x);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY, &y);
|
||||
f.mastering_info.display_primaries[1].x = (guint16) (x * 50000.0);
|
||||
f.mastering_info.display_primaries[1].y = (guint16) (y * 50000.0);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX, &x);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY, &y);
|
||||
f.mastering_info.display_primaries[2].x = (guint16) (x * 50000.0);
|
||||
f.mastering_info.display_primaries[2].y = (guint16) (y * 50000.0);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (bmdDeckLinkFrameMetadataHDRWhitePointX, &x);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (bmdDeckLinkFrameMetadataHDRWhitePointY, &y);
|
||||
f.mastering_info.white_point.x = (guint16) (x * 50000.0);
|
||||
f.mastering_info.white_point.y = (guint16) (y * 50000.0);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance, &x);
|
||||
dk_ret |=
|
||||
frame_metadata->GetFloat (
|
||||
bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance, &y);
|
||||
f.mastering_info.max_display_mastering_luminance = (guint32) (x * 10000.0 / 65535.0);
|
||||
f.mastering_info.min_display_mastering_luminance = (guint32) (y * 10000.0 / 6.5535);
|
||||
GST_LOG_OBJECT (self, "ret 0x%x mastering_info "
|
||||
"R:%u,%u G:%u,%u B:%u,%u W:%u,%u", dk_ret,
|
||||
f.mastering_info.display_primaries[0].x,
|
||||
f.mastering_info.display_primaries[0].y,
|
||||
f.mastering_info.display_primaries[1].x,
|
||||
f.mastering_info.display_primaries[1].y,
|
||||
f.mastering_info.display_primaries[2].x,
|
||||
f.mastering_info.display_primaries[2].y,
|
||||
f.mastering_info.white_point.x, f.mastering_info.white_point.y);
|
||||
if (dk_ret == S_OK)
|
||||
f.have_mastering_info = TRUE;
|
||||
|
||||
dk_ret =
|
||||
frame_metadata->GetInt (
|
||||
bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc, &tf);
|
||||
GST_LOG_OBJECT (self, "ret %x transfer func 0x%" G_GINT64_FORMAT,
|
||||
(gint) dk_ret, tf);
|
||||
|
||||
if (dk_ret == S_OK) {
|
||||
/* as specified in CTA 861.3-A */
|
||||
switch (tf) {
|
||||
case 0x0: /* traditional gamma, SDR luminance range */
|
||||
case 0x1: /* traditional gamma, HDR luminance range */
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_BT2020_12;
|
||||
break;
|
||||
case 0x2: /* PQ */
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE2084;
|
||||
break;
|
||||
case 0x3: /* HLG */
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_ARIB_STD_B67;
|
||||
break;
|
||||
default:
|
||||
f.colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
frame_metadata->Release ();
|
||||
}
|
||||
frame_metadata = nullptr;
|
||||
|
||||
if (dtc != NULL) {
|
||||
uint8_t hours, minutes, seconds, frames;
|
||||
HRESULT res;
|
||||
|
@ -960,7 +1143,6 @@ gst_decklink_video_src_got_frame (GstElement * element,
|
|||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Got timecode %02d:%02d:%02d:%02d",
|
||||
hours, minutes, seconds, frames);
|
||||
bmode = gst_decklink_get_mode (mode);
|
||||
if (bmode->interlaced)
|
||||
flags =
|
||||
(GstVideoTimeCodeFlags) (flags |
|
||||
|
@ -1406,6 +1588,37 @@ retry:
|
|||
}
|
||||
}
|
||||
|
||||
if (!gst_video_colorimetry_is_equal (&f.colorimetry,
|
||||
&self->caps_colorimetry)) {
|
||||
caps_changed = TRUE;
|
||||
self->caps_colorimetry = f.colorimetry;
|
||||
}
|
||||
|
||||
if (f.have_light_level != self->caps_have_light_level ||
|
||||
!gst_video_content_light_level_is_equal (&f.light_level,
|
||||
&self->caps_light_level)) {
|
||||
caps_changed = TRUE;
|
||||
self->caps_have_light_level = f.have_light_level;
|
||||
if (f.have_light_level) {
|
||||
self->caps_light_level = f.light_level;
|
||||
} else {
|
||||
memset (&self->caps_light_level, 0, sizeof (self->caps_light_level));
|
||||
}
|
||||
}
|
||||
|
||||
if (f.have_mastering_info != self->caps_have_light_level ||
|
||||
!gst_video_mastering_display_info_is_equal (&f.mastering_info,
|
||||
&self->caps_mastering_info)) {
|
||||
caps_changed = TRUE;
|
||||
self->caps_have_light_level = f.have_mastering_info;
|
||||
if (f.have_mastering_info) {
|
||||
memcpy (&self->caps_mastering_info, &f.mastering_info,
|
||||
sizeof (f.mastering_info));
|
||||
} else {
|
||||
memset (&self->caps_mastering_info, 0, sizeof (self->caps_mastering_info));
|
||||
}
|
||||
}
|
||||
|
||||
/* 1 ns error can be just a rounding error, so that's OK. The Decklink
|
||||
* drivers give us a really steady stream time, so anything above 1 ns can't
|
||||
* be a rounding error and is therefore something to worry about */
|
||||
|
@ -1433,11 +1646,27 @@ retry:
|
|||
|
||||
g_mutex_unlock (&self->lock);
|
||||
if (caps_changed) {
|
||||
char *colorimetry;
|
||||
const GstDecklinkMode *gst_mode = gst_decklink_get_mode (f.mode);
|
||||
|
||||
self->last_cc_vbi_line = -1;
|
||||
self->last_afd_bar_vbi_line = -1;
|
||||
self->last_cc_vbi_line_field2 = -1;
|
||||
self->last_afd_bar_vbi_line_field2 = -1;
|
||||
caps = gst_decklink_mode_get_caps (f.mode, f.format, TRUE);
|
||||
GST_LOG_OBJECT (self, "mode flags 0x%x",
|
||||
display_mode_flags (self, gst_mode, TRUE));
|
||||
caps = gst_decklink_mode_get_caps (f.mode,
|
||||
display_mode_flags (self, gst_mode, TRUE), f.format,
|
||||
bmdDynamicRangeSDR, TRUE);
|
||||
colorimetry = gst_video_colorimetry_to_string (&self->caps_colorimetry);
|
||||
if (colorimetry)
|
||||
gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, colorimetry,
|
||||
NULL);
|
||||
g_free (colorimetry);
|
||||
if (f.have_light_level)
|
||||
gst_video_content_light_level_add_to_caps (&f.light_level, caps);
|
||||
if (f.have_mastering_info)
|
||||
gst_video_mastering_display_info_add_to_caps (&f.mastering_info, caps);
|
||||
gst_video_info_from_caps (&self->info, caps);
|
||||
gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps);
|
||||
gst_element_post_message (GST_ELEMENT_CAST (self),
|
||||
|
@ -1481,6 +1710,21 @@ retry:
|
|||
return flow_ret;
|
||||
}
|
||||
|
||||
static BMDDynamicRange
|
||||
device_dynamic_range (GstDecklinkVideoSrc * self)
|
||||
{
|
||||
BMDDynamicRange range = bmdDynamicRangeSDR;
|
||||
|
||||
if (self->input && self->input->attributes) {
|
||||
gint64 tmp_int = 0;
|
||||
HRESULT ret = self->input->attributes->GetInt (BMDDeckLinkSupportedDynamicRange, &tmp_int);
|
||||
if (ret == S_OK)
|
||||
range = (BMDDynamicRange) tmp_int;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
|
||||
{
|
||||
|
@ -1488,10 +1732,19 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
|
|||
GstCaps *caps;
|
||||
|
||||
if (self->mode != GST_DECKLINK_MODE_AUTO) {
|
||||
caps = gst_decklink_mode_get_caps (self->mode, self->caps_format, TRUE);
|
||||
} else if (self->caps_mode != GST_DECKLINK_MODE_AUTO) {
|
||||
const GstDecklinkMode *gst_mode = gst_decklink_get_mode (self->mode);
|
||||
BMDDynamicRange dynamic_range = device_dynamic_range (self);
|
||||
caps =
|
||||
gst_decklink_mode_get_caps (self->caps_mode, self->caps_format, TRUE);
|
||||
gst_decklink_mode_get_caps (self->mode,
|
||||
display_mode_flags (self, gst_mode, FALSE), self->caps_format,
|
||||
dynamic_range, TRUE);
|
||||
} else if (self->caps_mode != GST_DECKLINK_MODE_AUTO) {
|
||||
const GstDecklinkMode *gst_mode = gst_decklink_get_mode (self->caps_mode);
|
||||
BMDDynamicRange dynamic_range = device_dynamic_range (self);
|
||||
caps =
|
||||
gst_decklink_mode_get_caps (self->caps_mode,
|
||||
display_mode_flags (self, gst_mode, FALSE), self->caps_format,
|
||||
dynamic_range, TRUE);
|
||||
} else {
|
||||
caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
|
||||
}
|
||||
|
|
|
@ -60,6 +60,12 @@ struct _GstDecklinkVideoSrc
|
|||
GstDecklinkModeEnum caps_mode;
|
||||
gint aspect_ratio_flag; /* -1 when unknown, 0 not set, 1 set */
|
||||
BMDPixelFormat caps_format;
|
||||
GstVideoColorimetry colorimetry;
|
||||
GstVideoColorimetry caps_colorimetry;
|
||||
gboolean caps_have_light_level;
|
||||
GstVideoContentLightLevel caps_light_level;
|
||||
gboolean caps_have_mastering_info;
|
||||
GstVideoMasteringDisplayInfo caps_mastering_info;
|
||||
GstDecklinkConnectionEnum connection;
|
||||
gint device_number;
|
||||
gint64 persistent_id;
|
||||
|
|
Loading…
Reference in a new issue