decklink: Correctly set top-field-first/bottom-field-first
authorSebastian Dröge <sebastian@centricular.com>
Mon, 28 Nov 2016 15:17:43 +0000 (17:17 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Mon, 28 Nov 2016 15:19:26 +0000 (17:19 +0200)
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
sys/decklink/gstdecklink.h
sys/decklink/gstdecklinkvideosink.cpp
sys/decklink/gstdecklinkvideosrc.cpp

index a744700..955157b 100644 (file)
@@ -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,
index 3391650..5ae38b2 100644 (file)
@@ -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
index 324f86a..f0f79e9 100644 (file)
@@ -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);
index d6c0561..c1ea301 100644 (file)
@@ -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)),