deinterlace: Add C implementation of YADIF
authorJan Schmidt <jan@centricular.com>
Thu, 2 Jan 2020 15:34:59 +0000 (02:34 +1100)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 6 May 2020 17:08:06 +0000 (17:08 +0000)
Import the YADIF deinterlacer from ffmpeg and modify
it to match the simple deinterlace scanlines structure.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/444>

gst/deinterlace/gstdeinterlace.c
gst/deinterlace/gstdeinterlace.h
gst/deinterlace/meson.build
gst/deinterlace/yadif.c [new file with mode: 0644]
gst/deinterlace/yadif.h [new file with mode: 0644]

index e6ca741e00289c5ebd88cfbbdc9859636df95ebc..1f4abdc8017b24900cf05ec25e800d6cf966ce58 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "gstdeinterlace.h"
 #include "tvtime/plugins.h"
+#include "yadif.h"
 
 #include <string.h>
 
@@ -157,6 +158,7 @@ static const GEnumValue methods_types[] = {
       "weavetff"},
   {GST_DEINTERLACE_WEAVE_BFF, "Progressive: Bottom Field First (Do Not Use)",
       "weavebff"},
+  {GST_DEINTERLACE_YADIF, "YADIF Adaptive Deinterlacer", "yadif"},
   {0, NULL, NULL},
 };
 
@@ -368,7 +370,8 @@ static const struct
   gst_deinterlace_method_scaler_bob_get_type}, {
   gst_deinterlace_method_weave_get_type}, {
   gst_deinterlace_method_weave_tff_get_type}, {
-  gst_deinterlace_method_weave_bff_get_type}
+  gst_deinterlace_method_weave_bff_get_type}, {
+  gst_deinterlace_method_yadif_get_type}
 };
 
 static void
@@ -538,6 +541,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass)
    * * weave Weave. Bad quality, do not use.
    * * weavetff Progressive: Top Field First.  Bad quality, do not use.
    * * weavebff Progressive: Bottom Field First.  Bad quality, do not use.
+   * * yadif YADIF Adaptive.
    */
   g_object_class_install_property (gobject_class, PROP_METHOD,
       g_param_spec_enum ("method",
index 1f4c02ea8cb45d74ababbf112e75cc1b67bc43db..65b438a4597c7e8a49a4d743c5c33fc3acfc6deb 100644 (file)
@@ -56,7 +56,8 @@ typedef enum
   GST_DEINTERLACE_SCALER_BOB,
   GST_DEINTERLACE_WEAVE,
   GST_DEINTERLACE_WEAVE_TFF,
-  GST_DEINTERLACE_WEAVE_BFF
+  GST_DEINTERLACE_WEAVE_BFF,
+  GST_DEINTERLACE_YADIF
 } GstDeinterlaceMethods;
 
 typedef enum
index 4a36d8ee2a7e9baaa3cd2cdf9dc28d0c9d523187..febddf3e05c02d24fc936709f1382c5ca911bf2d 100644 (file)
@@ -10,7 +10,8 @@ interlace_sources = [
   'tvtime/weave.c',
   'tvtime/linear.c',
   'tvtime/linearblend.c',
-  'tvtime/scalerbob.c'
+  'tvtime/scalerbob.c',
+  'yadif.c'
 ]
 
 orcsrc = 'tvtime'
diff --git a/gst/deinterlace/yadif.c b/gst/deinterlace/yadif.c
new file mode 100644 (file)
index 0000000..ded9c22
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Jan Schmidt <jan@centricular.com>
+ *
+ * Portions of this file extracted from libav
+ * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gst/gst.h>
+#ifdef HAVE_ORC
+#include <orc/orc.h>
+#endif
+#include "gstdeinterlacemethod.h"
+#include "yadif.h"
+
+#define GST_TYPE_DEINTERLACE_METHOD_YADIF      (gst_deinterlace_method_yadif_get_type ())
+#define GST_IS_DEINTERLACE_METHOD_YADIF(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_YADIF))
+#define GST_IS_DEINTERLACE_METHOD_YADIF_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_YADIF))
+#define GST_DEINTERLACE_METHOD_YADIF_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_YADIF, GstDeinterlaceMethodYadifClass))
+#define GST_DEINTERLACE_METHOD_YADIF(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_YADIF, GstDeinterlaceMethodYadif))
+#define GST_DEINTERLACE_METHOD_YADIF_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_YADIF, GstDeinterlaceMethodYadifClass))
+#define GST_DEINTERLACE_METHOD_YADIF_CAST(obj) ((GstDeinterlaceMethodYadif*)(obj))
+
+typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodYadif;
+typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodYadifClass;
+
+G_DEFINE_TYPE (GstDeinterlaceMethodYadif,
+    gst_deinterlace_method_yadif, GST_TYPE_DEINTERLACE_SIMPLE_METHOD);
+
+static void
+filter_scanline_yadif (GstDeinterlaceSimpleMethod * self,
+    guint8 * out, const GstDeinterlaceScanlineData * scanlines, guint size);
+
+static void
+copy_scanline (GstDeinterlaceSimpleMethod * self, guint8 * out,
+    const GstDeinterlaceScanlineData * scanlines, guint size)
+{
+  memcpy (out, scanlines->m0, size);
+}
+
+static void
+    gst_deinterlace_method_yadif_class_init
+    (GstDeinterlaceMethodYadifClass * klass)
+{
+  GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass;
+  GstDeinterlaceSimpleMethodClass *dism_class =
+      (GstDeinterlaceSimpleMethodClass *) klass;
+
+  dim_class->name = "YADIF Adaptive Deinterlacer";
+  dim_class->nick = "yadif";
+  dim_class->fields_required = 5;
+  dim_class->latency = 2;
+
+  dism_class->copy_scanline_planar_y = copy_scanline;
+  dism_class->copy_scanline_planar_u = copy_scanline;
+  dism_class->copy_scanline_planar_v = copy_scanline;
+
+  dism_class->interpolate_scanline_planar_y = filter_scanline_yadif;
+  dism_class->interpolate_scanline_planar_u = filter_scanline_yadif;
+  dism_class->interpolate_scanline_planar_v = filter_scanline_yadif;
+}
+
+static void
+gst_deinterlace_method_yadif_init (GstDeinterlaceMethodYadif * self)
+{
+}
+
+#define FFABS(a) ABS(a)
+#define FFMIN(a,b) MIN(a,b)
+#define FFMAX(a,b) MAX(a,b)
+#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c)
+#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c)
+
+#define CHECK(j)\
+    {   int score = FFABS(s->t0[x - 1 + (j)] - s->b0[x - 1 - (j)])\
+                  + FFABS(s->t0[x  +(j)] - s->b0[x  -(j)])\
+                  + FFABS(s->t0[x + 1 + (j)] - s->b0[x + 1 - (j)]);\
+        if (score < spatial_score) {\
+            spatial_score= score;\
+            spatial_pred= (s->t0[x  +(j)] + s->b0[x -(j)])>>1;\
+
+/* The is_not_edge argument here controls when the code will enter a branch
+ * which reads up to and including x-3 and x+3. */
+
+#define FILTER(start, end, is_not_edge) \
+    for (x = start;  x < end; x++) { \
+        int c = s->t0[x]; \
+        int d = (s->m1[x] + s->mp[x])>>1; \
+        int e = s->b0[x]; \
+        int temporal_diff0 = FFABS(s->m1[x] - s->mp[x]); \
+        int temporal_diff1 =(FFABS(s->t2[x] - c) + FFABS(s->b2[x] - e) )>>1; \
+        int temporal_diff2 =(FFABS(s->tp2[x] - c) + FFABS(s->bp2[x] - e) )>>1; \
+        int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); \
+        int spatial_pred = (c+e) >> 1; \
+ \
+        if (is_not_edge) {\
+            int spatial_score = FFABS(s->t0[x-1] - s->b0[x-1]) + FFABS(c-e) \
+                              + FFABS(s->t0[x+1] - s->b0[x+1]); \
+            CHECK(-1) CHECK(-2) }} }} \
+            CHECK( 1) CHECK( 2) }} }} \
+        }\
+ \
+        if (!(mode&2)) { \
+            int b = (s->tt1[x] + s->ttp[x])>>1; \
+            int f = (s->bb1[x] + s->bbp[x])>>1; \
+            int max = FFMAX3(d - e, d - c, FFMIN(b - c, f - e)); \
+            int min = FFMIN3(d - e, d - c, FFMAX(b - c, f - e)); \
+ \
+            diff = FFMAX3(diff, min, -max); \
+        } \
+ \
+        if (spatial_pred > d + diff) \
+           spatial_pred = d + diff; \
+        else if (spatial_pred < d - diff) \
+           spatial_pred = d - diff; \
+ \
+        dst[x] = spatial_pred; \
+ \
+    }
+
+static void
+filter_line_c (guint8 * dst,
+    const GstDeinterlaceScanlineData * s, int start, int end, int mode)
+{
+  int x;
+  /* The function is called for processing the middle
+   * pixels of each line, excluding 3 at each end.
+   * This allows the FILTER macro to be
+   * called so that it processes all the pixels normally.  A constant value of
+   * true for is_not_edge lets the compiler ignore the if statement. */
+  FILTER (start, end, 1)
+}
+
+#define MAX_ALIGN 8
+static void
+filter_edges (guint8 * dst,
+    const GstDeinterlaceScanlineData * s, int w, int mode)
+{
+  int x;
+  const int edge = MAX_ALIGN - 1;
+
+  /* Only edge pixels need to be processed here.  A constant value of false
+   * for is_not_edge should let the compiler ignore the whole branch. */
+  FILTER (0, 3, 0)
+      FILTER (w - edge, w - 3, 1)
+      FILTER (w - 3, w, 0)
+}
+
+static void
+filter_scanline_yadif (GstDeinterlaceSimpleMethod * self,
+    guint8 * out, const GstDeinterlaceScanlineData * s_orig, guint size)
+{
+  uint8_t *dst = out;
+  const int bpp = 1;            // Hard code 8-bit atm
+  int w = size / bpp;
+  int edge = MAX_ALIGN / bpp - 1;
+  GstDeinterlaceScanlineData s = *s_orig;
+
+  int mode = (s.tt1 == NULL || s.bb1 == NULL || s.ttp == NULL
+      || s.bbp == NULL) ? 2 : 0;
+
+  /* When starting up, some data might not yet be available, so use the current frame */
+  if (s.m1 == NULL) {
+    s.tt1 = s.ttp;
+    s.m1 = s.mp;
+    s.bb1 = s.bbp;
+  }
+  if (s.t2 == NULL)
+    s.t2 = s.tp2;
+  if (s.b2 == NULL)
+    s.b2 = s.bp2;
+
+  filter_edges (dst, &s, w, mode);
+  filter_line_c (dst, &s, 3, w - edge, mode);
+}
diff --git a/gst/deinterlace/yadif.h b/gst/deinterlace/yadif.h
new file mode 100644 (file)
index 0000000..b603d2e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Jan Schmidt <jan@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __YADIF_H__
+#define __YADIF_H__
+
+#define GST_TYPE_DEINTERLACE_YADIF (gst_deinterlace_method_yadif_get_type ())
+
+GType gst_deinterlace_method_yadif_get_type (void);
+
+#endif