decklink: Correctly set top-field-first/bottom-field-first

First of all, all the HD and UHD modes should be top-field-first, as
also returned by the Decklink mode iterator API.

Then we should include the caps field "field-order" in the caps of the
source (not the sink due to negotiation problems with optional fields).

And finally we should set the TFF flag on interlaced buffers that are
top-field-first.
This commit is contained in:
Sebastian Dröge 2016-11-28 17:17:43 +02:00
parent aec46143bb
commit fdf1a57953
4 changed files with 82 additions and 24 deletions

View file

@ -194,8 +194,8 @@ gst_decklink_audio_connection_get_type (void)
#define NTSC 10, 11, false, "bt601" #define NTSC 10, 11, false, "bt601"
#define PAL 12, 11, true, "bt601" #define PAL 12, 11, true, "bt601"
#define HD 1, 1, false, "bt709" #define HD 1, 1, true, "bt709"
#define UHD 1, 1, false, "bt2020" #define UHD 1, 1, true, "bt2020"
static const GstDecklinkMode modes[] = { static const GstDecklinkMode modes[] = {
{bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC}, // default is ntsc {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC}, // default is ntsc
@ -467,7 +467,7 @@ gst_decklink_caps_get_pixel_format (GstCaps * caps, BMDPixelFormat * format)
} }
static GstStructure * static GstStructure *
gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f) gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f, gboolean input)
{ {
const GstDecklinkMode *mode = &modes[e]; const GstDecklinkMode *mode = &modes[e];
GstStructure *s = gst_structure_new ("video/x-raw", GstStructure *s = gst_structure_new ("video/x-raw",
@ -478,6 +478,13 @@ gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f)
mode->interlaced ? "interleaved" : "progressive", mode->interlaced ? "interleaved" : "progressive",
"framerate", GST_TYPE_FRACTION, mode->fps_n, mode->fps_d, NULL); "framerate", GST_TYPE_FRACTION, mode->fps_n, mode->fps_d, NULL);
if (input && mode->interlaced) {
if (mode->tff)
gst_structure_set (s, "field-order", G_TYPE_STRING, "top-field-first", NULL);
else
gst_structure_set (s, "field-order", G_TYPE_STRING, "bottom-field-first", NULL);
}
switch (f) { switch (f) {
case bmdFormat8BitYUV: /* '2vuy' */ case bmdFormat8BitYUV: /* '2vuy' */
gst_structure_set (s, "format", G_TYPE_STRING, "UYVY", gst_structure_set (s, "format", G_TYPE_STRING, "UYVY",
@ -509,19 +516,19 @@ gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f)
} }
GstCaps * GstCaps *
gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f) gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f, gboolean input)
{ {
GstCaps *caps; GstCaps *caps;
caps = gst_caps_new_empty (); caps = gst_caps_new_empty ();
caps = caps =
gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e, f)); gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e, f, input));
return caps; return caps;
} }
GstCaps * GstCaps *
gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e) gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input)
{ {
GstCaps *caps; GstCaps *caps;
guint i; guint i;
@ -530,13 +537,13 @@ gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e)
for (i = 1; i < G_N_ELEMENTS (formats); i++) for (i = 1; i < G_N_ELEMENTS (formats); i++)
caps = caps =
gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e, gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e,
formats[i].format)); formats[i].format, input));
return caps; return caps;
} }
GstCaps * GstCaps *
gst_decklink_pixel_format_get_caps (BMDPixelFormat f) gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input)
{ {
int i; int i;
GstCaps *caps; GstCaps *caps;
@ -544,7 +551,7 @@ gst_decklink_pixel_format_get_caps (BMDPixelFormat f)
caps = gst_caps_new_empty (); caps = gst_caps_new_empty ();
for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) { for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) {
s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i, f); s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i, f, input);
caps = gst_caps_merge_structure (caps, s); caps = gst_caps_merge_structure (caps, s);
} }
@ -552,7 +559,7 @@ gst_decklink_pixel_format_get_caps (BMDPixelFormat f)
} }
GstCaps * GstCaps *
gst_decklink_mode_get_template_caps (void) gst_decklink_mode_get_template_caps (gboolean input)
{ {
int i; int i;
GstCaps *caps; GstCaps *caps;
@ -561,7 +568,7 @@ gst_decklink_mode_get_template_caps (void)
for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) for (i = 1; i < (int) G_N_ELEMENTS (modes); i++)
caps = caps =
gst_caps_merge (caps, gst_caps_merge (caps,
gst_decklink_mode_get_caps_all_formats ((GstDecklinkModeEnum) i)); gst_decklink_mode_get_caps_all_formats ((GstDecklinkModeEnum) i, input));
return caps; return caps;
} }
@ -578,7 +585,7 @@ gst_decklink_find_mode_and_format_for_caps (GstCaps * caps,
return NULL; return NULL;
for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) { for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) {
mode_caps = gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, *format); mode_caps = gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, *format, FALSE);
if (gst_caps_can_intersect (caps, mode_caps)) { if (gst_caps_can_intersect (caps, mode_caps)) {
gst_caps_unref (mode_caps); gst_caps_unref (mode_caps);
return gst_decklink_get_mode ((GstDecklinkModeEnum) i); return gst_decklink_get_mode ((GstDecklinkModeEnum) i);
@ -937,10 +944,33 @@ init_devices (gpointer data)
GST_WARNING ("selected device does not have input interface: 0x%08x", GST_WARNING ("selected device does not have input interface: 0x%08x",
ret); ret);
} else { } else {
IDeckLinkDisplayModeIterator *mode_iter;
devices[i].input.device = decklink; devices[i].input.device = decklink;
devices[i].input. devices[i].input.
input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i]. input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i].
input)); input));
if ((ret =
devices[i].input.input->GetDisplayModeIterator (&mode_iter)) ==
S_OK) {
IDeckLinkDisplayMode *mode;
GST_DEBUG ("Input %d supports:", i);
while ((ret = mode_iter->Next (&mode)) == S_OK) {
const char *name;
mode->GetName (&name);
GST_DEBUG (" %s mode: 0x%08x width: %ld height: %ld"
" fields: 0x%08x flags: 0x%08x", name,
(int) mode->GetDisplayMode (), mode->GetWidth (),
mode->GetHeight (), (int) mode->GetFieldDominance (),
(int) mode->GetFlags ());
mode->Release ();
}
mode_iter->Release ();
}
ret = S_OK;
} }
ret = decklink->QueryInterface (IID_IDeckLinkOutput, ret = decklink->QueryInterface (IID_IDeckLinkOutput,
@ -949,11 +979,34 @@ init_devices (gpointer data)
GST_WARNING ("selected device does not have output interface: 0x%08x", GST_WARNING ("selected device does not have output interface: 0x%08x",
ret); ret);
} else { } else {
IDeckLinkDisplayModeIterator *mode_iter;
devices[i].output.device = decklink; devices[i].output.device = decklink;
devices[i].output.clock = devices[i].output.clock =
gst_decklink_clock_new ("GstDecklinkOutputClock"); gst_decklink_clock_new ("GstDecklinkOutputClock");
GST_DECKLINK_CLOCK_CAST (devices[i].output.clock)->output = GST_DECKLINK_CLOCK_CAST (devices[i].output.clock)->output =
&devices[i].output; &devices[i].output;
if ((ret =
devices[i].output.output->GetDisplayModeIterator (&mode_iter)) ==
S_OK) {
IDeckLinkDisplayMode *mode;
GST_DEBUG ("Output %d supports:", i);
while ((ret = mode_iter->Next (&mode)) == S_OK) {
const char *name;
mode->GetName (&name);
GST_DEBUG (" %s mode: 0x%08x width: %ld height: %ld"
" fields: 0x%08x flags: 0x%08x", name,
(int) mode->GetDisplayMode (), mode->GetWidth (),
mode->GetHeight (), (int) mode->GetFieldDominance (),
(int) mode->GetFlags ());
mode->Release ();
}
mode_iter->Release ();
}
ret = S_OK;
} }
ret = decklink->QueryInterface (IID_IDeckLinkConfiguration, ret = decklink->QueryInterface (IID_IDeckLinkConfiguration,

View file

@ -160,8 +160,8 @@ struct _GstDecklinkMode {
const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e); const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e);
const GstDecklinkModeEnum gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode); const GstDecklinkModeEnum gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode);
const BMDVideoConnection gst_decklink_get_connection (GstDecklinkConnectionEnum e); const BMDVideoConnection gst_decklink_get_connection (GstDecklinkConnectionEnum e);
GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f); GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f, gboolean input);
GstCaps * gst_decklink_mode_get_template_caps (void); GstCaps * gst_decklink_mode_get_template_caps (gboolean input);
typedef struct _GstDecklinkOutput GstDecklinkOutput; typedef struct _GstDecklinkOutput GstDecklinkOutput;
struct _GstDecklinkOutput { struct _GstDecklinkOutput {
@ -227,7 +227,7 @@ void gst_decklink_release_nth_input (gint n, GstElement * src, gb
const GstDecklinkMode * gst_decklink_find_mode_for_caps (GstCaps * caps); const GstDecklinkMode * gst_decklink_find_mode_for_caps (GstCaps * caps);
const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format); const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format);
GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e); GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input);
GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f); GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input);
#endif #endif

View file

@ -227,7 +227,7 @@ 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)));
templ_caps = gst_decklink_mode_get_template_caps (); 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 */
gst_caps_map_in_place (templ_caps, reset_framerate, NULL); gst_caps_map_in_place (templ_caps, reset_framerate, NULL);
@ -405,17 +405,17 @@ gst_decklink_video_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
if (self->mode == GST_DECKLINK_MODE_AUTO if (self->mode == GST_DECKLINK_MODE_AUTO
&& self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO) && self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
mode_caps = gst_decklink_mode_get_template_caps (); mode_caps = gst_decklink_mode_get_template_caps (FALSE);
else if (self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO) else if (self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
mode_caps = gst_decklink_mode_get_caps_all_formats (self->mode); mode_caps = gst_decklink_mode_get_caps_all_formats (self->mode, FALSE);
else if (self->mode == GST_DECKLINK_MODE_AUTO) else if (self->mode == GST_DECKLINK_MODE_AUTO)
mode_caps = mode_caps =
gst_decklink_pixel_format_get_caps (gst_decklink_pixel_format_from_type gst_decklink_pixel_format_get_caps (gst_decklink_pixel_format_from_type
(self->video_format)); (self->video_format), FALSE);
else else
mode_caps = mode_caps =
gst_decklink_mode_get_caps (self->mode, gst_decklink_mode_get_caps (self->mode,
gst_decklink_pixel_format_from_type (self->video_format)); gst_decklink_pixel_format_from_type (self->video_format), FALSE);
mode_caps = gst_caps_make_writable (mode_caps); mode_caps = gst_caps_make_writable (mode_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 */
gst_caps_map_in_place (mode_caps, reset_framerate, NULL); gst_caps_map_in_place (mode_caps, reset_framerate, NULL);

View file

@ -203,7 +203,7 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
DEFAULT_DROP_NO_SIGNAL_FRAMES, DEFAULT_DROP_NO_SIGNAL_FRAMES,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
templ_caps = gst_decklink_mode_get_template_caps (); templ_caps = gst_decklink_mode_get_template_caps (TRUE);
gst_element_class_add_pad_template (element_class, gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, templ_caps)); gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, templ_caps));
gst_caps_unref (templ_caps); gst_caps_unref (templ_caps);
@ -473,7 +473,7 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
format = self->caps_format; format = self->caps_format;
g_mutex_unlock (&self->lock); g_mutex_unlock (&self->lock);
mode_caps = gst_decklink_mode_get_caps (mode, format); mode_caps = gst_decklink_mode_get_caps (mode, format, TRUE);
if (filter) { if (filter) {
caps = caps =
@ -703,6 +703,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
CaptureFrame *f; CaptureFrame *f;
GstCaps *caps; GstCaps *caps;
gboolean caps_changed = FALSE; gboolean caps_changed = FALSE;
const GstDecklinkMode *mode;
g_mutex_lock (&self->lock); g_mutex_lock (&self->lock);
while (g_queue_is_empty (&self->current_frames) && !self->flushing) { while (g_queue_is_empty (&self->current_frames) && !self->flushing) {
@ -755,7 +756,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
g_mutex_unlock (&self->lock); g_mutex_unlock (&self->lock);
if (caps_changed) { if (caps_changed) {
caps = gst_decklink_mode_get_caps (f->mode, f->format); caps = gst_decklink_mode_get_caps (f->mode, f->format, TRUE);
gst_video_info_from_caps (&self->info, caps); gst_video_info_from_caps (&self->info, caps);
gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps); gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps);
gst_element_post_message (GST_ELEMENT_CAST (self), gst_element_post_message (GST_ELEMENT_CAST (self),
@ -799,6 +800,10 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
GST_BUFFER_DURATION (*buffer) = f->duration; GST_BUFFER_DURATION (*buffer) = f->duration;
gst_buffer_add_video_time_code_meta (*buffer, f->tc); gst_buffer_add_video_time_code_meta (*buffer, f->tc);
mode = gst_decklink_get_mode (self->mode);
if (mode->interlaced && mode->tff)
GST_BUFFER_FLAG_SET (*buffer, GST_VIDEO_BUFFER_FLAG_TFF | GST_VIDEO_BUFFER_FLAG_INTERLACED);
GST_DEBUG_OBJECT (self, GST_DEBUG_OBJECT (self,
"Outputting buffer %p with timestamp %" GST_TIME_FORMAT " and duration %" "Outputting buffer %p with timestamp %" GST_TIME_FORMAT " and duration %"
GST_TIME_FORMAT, *buffer, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buffer)), GST_TIME_FORMAT, *buffer, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buffer)),