deinterlace: Added "auto" fields mode
authorVivia Nikolaidou <vivia@toolsonair.com>
Thu, 17 Mar 2016 19:21:02 +0000 (21:21 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 24 Mar 2016 12:34:11 +0000 (14:34 +0200)
The "auto" fields mode will detect the upstream and downstream framerates and
will decide to deinterlace all or only top fields.

https://bugzilla.gnome.org/show_bug.cgi?id=763869

gst/deinterlace/gstdeinterlace.c
gst/deinterlace/gstdeinterlace.h

index 6ef5c61..987c4ed 100644 (file)
@@ -199,6 +199,7 @@ gst_deinterlace_fields_get_type (void)
     {GST_DEINTERLACE_ALL, "All fields", "all"},
     {GST_DEINTERLACE_TF, "Top fields only", "top"},
     {GST_DEINTERLACE_BF, "Bottom fields only", "bottom"},
+    {GST_DEINTERLACE_FIELDS_AUTO, "Automatically detect", "auto"},
     {0, NULL, NULL},
   };
 
@@ -720,6 +721,7 @@ gst_deinterlace_init (GstDeinterlace * self)
   gst_video_info_init (&self->vinfo);
   gst_deinterlace_set_method (self, self->user_set_method_id);
   self->fields = DEFAULT_FIELDS;
+  self->user_set_fields = DEFAULT_FIELDS;
   self->field_layout = DEFAULT_FIELD_LAYOUT;
   self->locking = DEFAULT_LOCKING;
   self->ignore_obscure = DEFAULT_IGNORE_OBSCURE;
@@ -809,7 +811,7 @@ gst_deinterlace_reset (GstDeinterlace * self)
   if ((gint) self->new_mode != -1)
     self->mode = self->new_mode;
   if ((gint) self->new_fields != -1)
-    self->fields = self->new_fields;
+    self->user_set_fields = self->new_fields;
   self->new_mode = -1;
   self->new_fields = -1;
 
@@ -861,11 +863,12 @@ gst_deinterlace_set_property (GObject * object, guint prop_id,
 
       GST_OBJECT_LOCK (self);
       new_fields = g_value_get_enum (value);
-      if (self->fields != new_fields && gst_pad_has_current_caps (self->srcpad)) {
+      if (self->user_set_fields != new_fields
+          && gst_pad_has_current_caps (self->srcpad)) {
         self->reconfigure = TRUE;
         self->new_fields = new_fields;
       } else {
-        self->fields = new_fields;
+        self->user_set_fields = new_fields;
       }
       GST_OBJECT_UNLOCK (self);
       break;
@@ -904,7 +907,7 @@ gst_deinterlace_get_property (GObject * object, guint prop_id,
       g_value_set_enum (value, self->user_set_method_id);
       break;
     case PROP_FIELDS:
-      g_value_set_enum (value, self->fields);
+      g_value_set_enum (value, self->user_set_fields);
       break;
     case PROP_FIELD_LAYOUT:
       g_value_set_enum (value, self->field_layout);
@@ -2017,7 +2020,7 @@ gst_deinterlace_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
     GstCaps *caps;
 
     if ((gint) self->new_fields != -1)
-      self->fields = self->new_fields;
+      self->user_set_fields = self->new_fields;
     if ((gint) self->new_mode != -1)
       self->mode = self->new_mode;
     self->new_mode = -1;
@@ -2355,12 +2358,20 @@ gst_deinterlace_getcaps (GstDeinterlace * self, GstPad * pad, GstCaps * filter)
           NULL);
   }
 
-  if (self->fields == GST_DEINTERLACE_ALL) {
+  if (self->user_set_fields == GST_DEINTERLACE_ALL) {
     tmp2 = gst_deinterlace_caps_double_framerate (tmp2, (pad == self->sinkpad));
   }
+  if (self->user_set_fields == GST_DEINTERLACE_FIELDS_AUTO) {
+    tmp = gst_caps_copy (tmp2);
+    tmp = gst_deinterlace_caps_double_framerate (tmp, (pad == self->sinkpad));
+  }
 
   ret = gst_caps_merge (ret, tmp2);
   tmp2 = NULL;
+  if (tmp != NULL) {
+    ret = gst_caps_merge (ret, tmp);
+    tmp = NULL;
+  }
 
   /* or
    * - anything else in which case we would just passthrough again if we're
@@ -2506,8 +2517,27 @@ gst_deinterlace_setcaps (GstDeinterlace * self, GstPad * pad, GstCaps * caps)
   GstCaps *srccaps = NULL;
   GstVideoInterlaceMode interlacing_mode;
   gint fps_n, fps_d;
+  GstCaps *peercaps;
 
   gst_pad_check_reconfigure (self->srcpad);
+  peercaps = gst_pad_peer_query_caps (self->srcpad, NULL);
+
+  /* Make sure the peer caps are compatible with the template caps */
+  if (peercaps) {
+    GstCaps *tmp = gst_pad_get_pad_template_caps (self->srcpad);
+    GstCaps *tmp2 = gst_caps_intersect (peercaps, tmp);
+
+    gst_caps_unref (peercaps);
+    peercaps = NULL;
+    gst_caps_unref (tmp);
+
+    if (gst_caps_is_empty (tmp2)) {
+      gst_caps_unref (tmp2);
+      GST_ERROR_OBJECT (self, "Peer caps not compatible with template caps");
+      goto invalid_caps;
+    }
+    peercaps = tmp2;
+  }
 
   if (self->locking != GST_DEINTERLACE_LOCKING_NONE) {
     if (self->low_latency == -1)
@@ -2556,21 +2586,12 @@ gst_deinterlace_setcaps (GstDeinterlace * self, GstPad * pad, GstCaps * caps)
           "Passthrough because mode=auto and progressive caps");
       self->passthrough = TRUE;
     } else if (gst_caps_can_intersect (caps, tmp)) {
-      GstCaps *peercaps;
-
-      peercaps = gst_pad_peer_query_caps (self->srcpad, NULL);
       if (peercaps) {
-        GstCaps *templcaps = gst_pad_get_pad_template_caps (self->srcpad);
         GstCaps *allowed_caps;
         GstCaps *tmp2;
         GstStructure *s;
 
-        allowed_caps = gst_caps_intersect (templcaps, peercaps);
-        gst_caps_unref (templcaps);
-        gst_caps_unref (peercaps);
-        peercaps = allowed_caps;
         allowed_caps = gst_caps_intersect (peercaps, tmp);
-        gst_caps_unref (peercaps);
 
         tmp2 = gst_caps_copy (caps);
         s = gst_caps_get_structure (tmp2, 0);
@@ -2646,20 +2667,51 @@ gst_deinterlace_setcaps (GstDeinterlace * self, GstPad * pad, GstCaps * caps)
        * mode */
       srccaps = gst_caps_copy (caps);
       gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
-    } else if (self->fields == GST_DEINTERLACE_ALL) {
-      if (!gst_deinterlace_fraction_double (&fps_n, &fps_d, FALSE))
-        goto invalid_caps;
-
+    } else if (self->user_set_fields == GST_DEINTERLACE_FIELDS_AUTO) {
       srccaps = gst_caps_copy (caps);
+      if (peercaps) {
+        gboolean can_be_tf = FALSE;
 
-      gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, fps_n,
-          fps_d, NULL);
+        /* We already know that we are not passthrough: interlace-mode will
+         * be progressive */
+        gst_caps_set_simple (srccaps, "interlace-mode", G_TYPE_STRING,
+            "progressive", NULL);
+
+        if (gst_caps_can_intersect (peercaps, srccaps)) {
+          GST_DEBUG_OBJECT (self, "Can deinterlace top fields");
+          can_be_tf = TRUE;
+        }
+        srccaps = gst_deinterlace_caps_double_framerate (srccaps, FALSE);
+        if (!gst_caps_can_intersect (peercaps, srccaps)) {
+          if (can_be_tf) {
+            GST_DEBUG_OBJECT (self, "Will deinterlace top fields");
+            gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, fps_n,
+                fps_d, NULL);
+            self->fields = GST_DEINTERLACE_TF;
+          } else {
+            GST_DEBUG_OBJECT (self,
+                "Can't negotiate upstream and downstream caps");
+            gst_caps_unref (srccaps);
+            goto invalid_caps;
+          }
+        } else {
+          GST_DEBUG_OBJECT (self, "Deinterlacing all fields");
+          self->fields = GST_DEINTERLACE_ALL;
+        }
+      } else {
+        GST_DEBUG_OBJECT (self,
+            "No peer caps yet, falling back to deinterlacing all fields");
+        self->fields = GST_DEINTERLACE_ALL;
+        srccaps = gst_deinterlace_caps_double_framerate (srccaps, FALSE);
+      }
     } else {
+      self->fields = self->user_set_fields;
       srccaps = gst_caps_copy (caps);
+      if (self->fields == GST_DEINTERLACE_ALL)
+        srccaps = gst_deinterlace_caps_double_framerate (srccaps, FALSE);
     }
 
     /* If not passthrough, we are going to output progressive content */
-    srccaps = gst_caps_make_writable (srccaps);
     gst_caps_set_simple (srccaps, "interlace-mode", G_TYPE_STRING,
         "progressive", NULL);
 
@@ -2684,18 +2736,24 @@ gst_deinterlace_setcaps (GstDeinterlace * self, GstPad * pad, GstCaps * caps)
   if (!gst_deinterlace_do_bufferpool (self, srccaps))
     goto no_bufferpool;
 
+  if (peercaps)
+    gst_caps_unref (peercaps);
   gst_caps_unref (srccaps);
 
   return TRUE;
 
 invalid_caps:
   {
+    if (peercaps)
+      gst_caps_unref (peercaps);
     GST_ERROR_OBJECT (pad, "Invalid caps: %" GST_PTR_FORMAT, caps);
     return FALSE;
   }
 set_caps_failed:
   {
     GST_ERROR_OBJECT (pad, "Failed to set caps: %" GST_PTR_FORMAT, srccaps);
+    if (peercaps)
+      gst_caps_unref (peercaps);
     if (srccaps)
       gst_caps_unref (srccaps);
     return FALSE;
index 4d783d3..eb8e37f 100644 (file)
@@ -63,7 +63,8 @@ typedef enum
 {
   GST_DEINTERLACE_ALL,         /* All (missing data is interp.) */
   GST_DEINTERLACE_TF,          /* Top Fields Only */
-  GST_DEINTERLACE_BF           /* Bottom Fields Only */
+  GST_DEINTERLACE_BF,          /* Bottom Fields Only */
+  GST_DEINTERLACE_FIELDS_AUTO  /* Automatically detect */
 } GstDeinterlaceFields;
 
 typedef enum
@@ -126,6 +127,8 @@ struct _GstDeinterlace
 
   GstDeinterlaceFields fields;
 
+  GstDeinterlaceFields user_set_fields;
+
   /* current state (differs when flushing/inverse telecine using weave) */
   GstDeinterlaceMethods method_id;
   /* property value */