From fdf1a57953a8e9f411180a1fe3d6e67bad669049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 28 Nov 2016 17:17:43 +0200 Subject: [PATCH] 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. --- sys/decklink/gstdecklink.cpp | 77 ++++++++++++++++++++++----- sys/decklink/gstdecklink.h | 8 +-- sys/decklink/gstdecklinkvideosink.cpp | 10 ++-- sys/decklink/gstdecklinkvideosrc.cpp | 11 ++-- 4 files changed, 82 insertions(+), 24 deletions(-) diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp index a744700636..955157b97e 100644 --- a/sys/decklink/gstdecklink.cpp +++ b/sys/decklink/gstdecklink.cpp @@ -194,8 +194,8 @@ gst_decklink_audio_connection_get_type (void) #define NTSC 10, 11, false, "bt601" #define PAL 12, 11, true, "bt601" -#define HD 1, 1, false, "bt709" -#define UHD 1, 1, false, "bt2020" +#define HD 1, 1, true, "bt709" +#define UHD 1, 1, true, "bt2020" static const GstDecklinkMode modes[] = { {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 * -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]; 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", "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) { case bmdFormat8BitYUV: /* '2vuy' */ gst_structure_set (s, "format", G_TYPE_STRING, "UYVY", @@ -509,19 +516,19 @@ gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f) } GstCaps * -gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f) +gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f, gboolean input) { GstCaps *caps; caps = gst_caps_new_empty (); 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; } GstCaps * -gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e) +gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input) { GstCaps *caps; guint i; @@ -530,13 +537,13 @@ gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e) for (i = 1; i < G_N_ELEMENTS (formats); i++) caps = gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e, - formats[i].format)); + formats[i].format, input)); return caps; } GstCaps * -gst_decklink_pixel_format_get_caps (BMDPixelFormat f) +gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input) { int i; GstCaps *caps; @@ -544,7 +551,7 @@ gst_decklink_pixel_format_get_caps (BMDPixelFormat f) caps = gst_caps_new_empty (); 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); } @@ -552,7 +559,7 @@ gst_decklink_pixel_format_get_caps (BMDPixelFormat f) } GstCaps * -gst_decklink_mode_get_template_caps (void) +gst_decklink_mode_get_template_caps (gboolean input) { int i; GstCaps *caps; @@ -561,7 +568,7 @@ gst_decklink_mode_get_template_caps (void) for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) 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; } @@ -578,7 +585,7 @@ gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, return NULL; 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)) { gst_caps_unref (mode_caps); 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", ret); } else { + IDeckLinkDisplayModeIterator *mode_iter; + devices[i].input.device = decklink; devices[i].input. input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i]. 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, @@ -949,11 +979,34 @@ init_devices (gpointer data) GST_WARNING ("selected device does not have output interface: 0x%08x", ret); } else { + IDeckLinkDisplayModeIterator *mode_iter; + devices[i].output.device = decklink; devices[i].output.clock = gst_decklink_clock_new ("GstDecklinkOutputClock"); GST_DECKLINK_CLOCK_CAST (devices[i].output.clock)->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, diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h index 3391650d67..5ae38b2815 100644 --- a/sys/decklink/gstdecklink.h +++ b/sys/decklink/gstdecklink.h @@ -160,8 +160,8 @@ struct _GstDecklinkMode { 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); -GstCaps * gst_decklink_mode_get_template_caps (void); +GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f, gboolean input); +GstCaps * gst_decklink_mode_get_template_caps (gboolean input); typedef struct _GstDecklinkOutput 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_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format); -GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e); -GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f); +GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e, gboolean input); +GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f, gboolean input); #endif diff --git a/sys/decklink/gstdecklinkvideosink.cpp b/sys/decklink/gstdecklinkvideosink.cpp index 324f86a430..f0f79e9ccb 100644 --- a/sys/decklink/gstdecklinkvideosink.cpp +++ b/sys/decklink/gstdecklinkvideosink.cpp @@ -227,7 +227,7 @@ gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass) (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | 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); /* For output we support any framerate and only really care about timestamps */ 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 && 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) - 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) mode_caps = gst_decklink_pixel_format_get_caps (gst_decklink_pixel_format_from_type - (self->video_format)); + (self->video_format), FALSE); else mode_caps = 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); /* For output we support any framerate and only really care about timestamps */ gst_caps_map_in_place (mode_caps, reset_framerate, NULL); diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp index d6c056193e..c1ea3012d2 100644 --- a/sys/decklink/gstdecklinkvideosrc.cpp +++ b/sys/decklink/gstdecklinkvideosrc.cpp @@ -203,7 +203,7 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass) DEFAULT_DROP_NO_SIGNAL_FRAMES, (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_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, 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; 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) { caps = @@ -703,6 +703,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer) CaptureFrame *f; GstCaps *caps; gboolean caps_changed = FALSE; + const GstDecklinkMode *mode; g_mutex_lock (&self->lock); 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); 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_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps); 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_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, "Outputting buffer %p with timestamp %" GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT, *buffer, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buffer)),