diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c index e6ca741e00..1f4abdc801 100644 --- a/gst/deinterlace/gstdeinterlace.c +++ b/gst/deinterlace/gstdeinterlace.c @@ -40,6 +40,7 @@ #include "gstdeinterlace.h" #include "tvtime/plugins.h" +#include "yadif.h" #include @@ -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", diff --git a/gst/deinterlace/gstdeinterlace.h b/gst/deinterlace/gstdeinterlace.h index 1f4c02ea8c..65b438a459 100644 --- a/gst/deinterlace/gstdeinterlace.h +++ b/gst/deinterlace/gstdeinterlace.h @@ -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 diff --git a/gst/deinterlace/meson.build b/gst/deinterlace/meson.build index 4a36d8ee2a..febddf3e05 100644 --- a/gst/deinterlace/meson.build +++ b/gst/deinterlace/meson.build @@ -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 index 0000000000..ded9c22c5c --- /dev/null +++ b/gst/deinterlace/yadif.c @@ -0,0 +1,197 @@ +/* + * GStreamer + * Copyright (C) 2019 Jan Schmidt + * + * Portions of this file extracted from libav + * Copyright (C) 2006 Michael Niedermayer + * + * 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 +#include + +#include +#ifdef HAVE_ORC +#include +#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 index 0000000000..b603d2e181 --- /dev/null +++ b/gst/deinterlace/yadif.h @@ -0,0 +1,28 @@ +/* + * GStreamer + * Copyright (C) 2019 Jan Schmidt + * + * 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