From b339812c07501a79bafa0c347ba918bf541edac0 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 7 Feb 2013 15:09:51 -0800 Subject: [PATCH] yadif: Add YADIF deinterlacing filter Code copied from Libav, commit 26e4f0c70. Will eventually be moved to -ugly because underlying code is GPL. --- configure.ac | 2 + gst/yadif/Makefile.am | 22 ++ gst/yadif/gstyadif.c | 493 +++++++++++++++++++++++++++++++++++++++++++++ gst/yadif/gstyadif.h | 64 ++++++ gst/yadif/vf_yadif.c | 161 +++++++++++++++ gst/yadif/yadif.c | 109 ++++++++++ gst/yadif/yadif_template.c | 262 ++++++++++++++++++++++++ 7 files changed, 1113 insertions(+) create mode 100644 gst/yadif/Makefile.am create mode 100644 gst/yadif/gstyadif.c create mode 100644 gst/yadif/gstyadif.h create mode 100644 gst/yadif/vf_yadif.c create mode 100644 gst/yadif/yadif.c create mode 100644 gst/yadif/yadif_template.c diff --git a/configure.ac b/configure.ac index e8d4028..9f73879 100644 --- a/configure.ac +++ b/configure.ac @@ -396,6 +396,7 @@ AG_GST_CHECK_PLUGIN(videoparsers) AG_GST_CHECK_PLUGIN(videosignal) AG_GST_CHECK_PLUGIN(vmnc) AG_GST_CHECK_PLUGIN(y4m) +AG_GST_CHECK_PLUGIN(yadif) dnl *** plug-ins to exclude *** @@ -2248,6 +2249,7 @@ gst/videoparsers/Makefile gst/videosignal/Makefile gst/vmnc/Makefile gst/y4m/Makefile +gst/yadif/Makefile gst-libs/Makefile gst-libs/gst/Makefile gst-libs/gst/basecamerabinsrc/Makefile diff --git a/gst/yadif/Makefile.am b/gst/yadif/Makefile.am new file mode 100644 index 0000000..c01afc7 --- /dev/null +++ b/gst/yadif/Makefile.am @@ -0,0 +1,22 @@ +plugin_LTLIBRARIES = libgstyadif.la + +libgstyadif_la_SOURCES = gstyadif.c gstyadif.h vf_yadif.c yadif.c +libgstyadif_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstyadif_la_LIBADD = -lgstvideo-1.0 $(GST_BASE_LIBS) $(GST_LIBS) +libgstyadif_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstyadif_la_LIBTOOLFLAGS = --tag=disable-static + + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstyadif -:SHARED libgstyadif \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstyadif_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstyadif_la_CFLAGS) \ + -:LDFLAGS $(libgstyadif_la_LDFLAGS) \ + $(libgstyadif_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/yadif/gstyadif.c b/gst/yadif/gstyadif.c new file mode 100644 index 0000000..cde9184 --- /dev/null +++ b/gst/yadif/gstyadif.c @@ -0,0 +1,493 @@ +/* GStreamer + * Copyright (C) 2013 David Schleef + * Copyright (C) 2013 Rdio, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU 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 General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ +/** + * SECTION:element-gstyadif + * + * The yadif element deinterlaces video, using the YADIF deinterlacing + * filter copied from Libav. This element only handles the simple case + * of interlaced-mode=interleaved video instead of the more complex + * inverse telecine and deinterlace cases that are handled by the + * deinterlace element. + * + * + * Example launch line + * |[ + * gst-launch -v videotestsrc pattern=ball ! interlace ! yadif ! xvimagesink + * ]| + * This pipeline creates an interlaced test pattern, and then deinterlaces + * it using the yadif filter. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "gstyadif.h" + +GST_DEBUG_CATEGORY_STATIC (gst_yadif_debug_category); +#define GST_CAT_DEFAULT gst_yadif_debug_category + +/* prototypes */ + + +static void gst_yadif_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec); +static void gst_yadif_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec); +static void gst_yadif_dispose (GObject * object); +static void gst_yadif_finalize (GObject * object); + +static GstCaps *gst_yadif_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * filter); +static GstCaps *gst_yadif_fixate_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); +static gboolean gst_yadif_accept_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps); +static gboolean gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps); +static gboolean gst_yadif_query (GstBaseTransform * trans, + GstPadDirection direction, GstQuery * query); +static gboolean gst_yadif_decide_allocation (GstBaseTransform * trans, + GstQuery * query); +static gboolean gst_yadif_filter_meta (GstBaseTransform * trans, + GstQuery * query, GType api, const GstStructure * params); +static gboolean gst_yadif_propose_allocation (GstBaseTransform * trans, + GstQuery * decide_query, GstQuery * query); +static gboolean gst_yadif_transform_size (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps, + gsize * othersize); +static gboolean gst_yadif_get_unit_size (GstBaseTransform * trans, + GstCaps * caps, gsize * size); +static gboolean gst_yadif_start (GstBaseTransform * trans); +static gboolean gst_yadif_stop (GstBaseTransform * trans); +static gboolean gst_yadif_sink_event (GstBaseTransform * trans, + GstEvent * event); +static gboolean gst_yadif_src_event (GstBaseTransform * trans, + GstEvent * event); +static GstFlowReturn gst_yadif_prepare_output_buffer (GstBaseTransform * trans, + GstBuffer * input, GstBuffer ** outbuf); +static gboolean gst_yadif_copy_metadata (GstBaseTransform * trans, + GstBuffer * input, GstBuffer * outbuf); +static gboolean gst_yadif_transform_meta (GstBaseTransform * trans, + GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf); +static void gst_yadif_before_transform (GstBaseTransform * trans, + GstBuffer * buffer); +static GstFlowReturn gst_yadif_transform (GstBaseTransform * trans, + GstBuffer * inbuf, GstBuffer * outbuf); +static GstFlowReturn gst_yadif_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); + +enum +{ + PROP_0 +}; + +/* pad templates */ + +static GstStaticPadTemplate gst_yadif_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{Y42B,I420,Y444}") + ",interlace-mode=(string){interleaved,mixed,progressive}") + ); + +static GstStaticPadTemplate gst_yadif_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{Y42B,I420,Y444}") + ",interlace-mode=(string)progressive") + ); + + +/* class initialization */ + +G_DEFINE_TYPE_WITH_CODE (GstYadif, gst_yadif, GST_TYPE_BASE_TRANSFORM, + GST_DEBUG_CATEGORY_INIT (gst_yadif_debug_category, "yadif", 0, + "debug category for yadif element")); + +static void +gst_yadif_class_init (GstYadifClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseTransformClass *base_transform_class = + GST_BASE_TRANSFORM_CLASS (klass); + + /* Setting up pads and setting metadata should be moved to + base_class_init if you intend to subclass this class. */ + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), + gst_static_pad_template_get (&gst_yadif_sink_template)); + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), + gst_static_pad_template_get (&gst_yadif_src_template)); + + gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass), + "YADIF deinterlacer", "Video/Filter", + "Deinterlace video using YADIF filter", "David Schleef "); + + gobject_class->set_property = gst_yadif_set_property; + gobject_class->get_property = gst_yadif_get_property; + gobject_class->dispose = gst_yadif_dispose; + gobject_class->finalize = gst_yadif_finalize; + base_transform_class->transform_caps = + GST_DEBUG_FUNCPTR (gst_yadif_transform_caps); + if (0) + base_transform_class->fixate_caps = + GST_DEBUG_FUNCPTR (gst_yadif_fixate_caps); + if (0) + base_transform_class->accept_caps = + GST_DEBUG_FUNCPTR (gst_yadif_accept_caps); + base_transform_class->set_caps = GST_DEBUG_FUNCPTR (gst_yadif_set_caps); + if (0) + base_transform_class->query = GST_DEBUG_FUNCPTR (gst_yadif_query); + if (0) + base_transform_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_yadif_decide_allocation); + if (0) + base_transform_class->filter_meta = + GST_DEBUG_FUNCPTR (gst_yadif_filter_meta); + if (0) + base_transform_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_yadif_propose_allocation); + if (0) + base_transform_class->transform_size = + GST_DEBUG_FUNCPTR (gst_yadif_transform_size); + base_transform_class->get_unit_size = + GST_DEBUG_FUNCPTR (gst_yadif_get_unit_size); + base_transform_class->start = GST_DEBUG_FUNCPTR (gst_yadif_start); + base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_yadif_stop); + if (0) + base_transform_class->sink_event = GST_DEBUG_FUNCPTR (gst_yadif_sink_event); + if (0) + base_transform_class->src_event = GST_DEBUG_FUNCPTR (gst_yadif_src_event); + if (0) + base_transform_class->prepare_output_buffer = + GST_DEBUG_FUNCPTR (gst_yadif_prepare_output_buffer); + if (0) + base_transform_class->copy_metadata = + GST_DEBUG_FUNCPTR (gst_yadif_copy_metadata); + if (0) + base_transform_class->transform_meta = + GST_DEBUG_FUNCPTR (gst_yadif_transform_meta); + if (0) + base_transform_class->before_transform = + GST_DEBUG_FUNCPTR (gst_yadif_before_transform); + base_transform_class->transform = GST_DEBUG_FUNCPTR (gst_yadif_transform); + if (0) + base_transform_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_yadif_transform_ip); + +} + +static void +gst_yadif_init (GstYadif * yadif) +{ + + yadif->sinkpad = gst_pad_new_from_static_template (&gst_yadif_sink_template, + "sink"); + + yadif->srcpad = gst_pad_new_from_static_template (&gst_yadif_src_template, + "src"); +} + +void +gst_yadif_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + /* GstYadif *yadif = GST_YADIF (object); */ + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gst_yadif_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + /* GstYadif *yadif = GST_YADIF (object); */ + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gst_yadif_dispose (GObject * object) +{ + /* GstYadif *yadif = GST_YADIF (object); */ + + /* clean up as possible. may be called multiple times */ + + G_OBJECT_CLASS (gst_yadif_parent_class)->dispose (object); +} + +void +gst_yadif_finalize (GObject * object) +{ + /* GstYadif *yadif = GST_YADIF (object); */ + + /* clean up object here */ + + G_OBJECT_CLASS (gst_yadif_parent_class)->finalize (object); +} + + +static GstCaps * +gst_yadif_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * filter) +{ + GstCaps *othercaps; + + othercaps = gst_caps_copy (caps); + + if (direction == GST_PAD_SRC) { + GValue value = G_VALUE_INIT; + GValue v = G_VALUE_INIT; + + g_value_init (&value, GST_TYPE_LIST); + g_value_init (&v, G_TYPE_STRING); + + g_value_set_string (&v, "interleaved"); + gst_value_list_append_value (&value, &v); + g_value_set_string (&v, "mixed"); + gst_value_list_append_value (&value, &v); + g_value_set_string (&v, "progressive"); + gst_value_list_append_value (&value, &v); + + gst_caps_set_value (othercaps, "interlace-mode", &value); + g_value_reset (&value); + g_value_reset (&v); + } else { + gst_caps_set_simple (othercaps, "interlace-mode", G_TYPE_STRING, + "progressive", NULL); + } + + return othercaps; +} + +static GstCaps * +gst_yadif_fixate_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) +{ + + return NULL; +} + +static gboolean +gst_yadif_accept_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps) +{ + return TRUE; +} + +static gboolean +gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstYadif *yadif = GST_YADIF (trans); + + gst_video_info_from_caps (&yadif->video_info, incaps); + + return TRUE; +} + +static gboolean +gst_yadif_query (GstBaseTransform * trans, GstPadDirection direction, + GstQuery * query) +{ + + return TRUE; +} + +static gboolean +gst_yadif_decide_allocation (GstBaseTransform * trans, GstQuery * query) +{ + + return TRUE; +} + +static gboolean +gst_yadif_filter_meta (GstBaseTransform * trans, GstQuery * query, + GType api, const GstStructure * params) +{ + + return TRUE; +} + +static gboolean +gst_yadif_propose_allocation (GstBaseTransform * trans, + GstQuery * decide_query, GstQuery * query) +{ + + return TRUE; +} + +static gboolean +gst_yadif_transform_size (GstBaseTransform * trans, + GstPadDirection direction, + GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize) +{ + + return FALSE; +} + +static gboolean +gst_yadif_get_unit_size (GstBaseTransform * trans, GstCaps * caps, gsize * size) +{ + GstVideoInfo info; + + if (gst_video_info_from_caps (&info, caps)) { + *size = GST_VIDEO_INFO_SIZE (&info); + + return TRUE; + } + return FALSE; +} + +static gboolean +gst_yadif_start (GstBaseTransform * trans) +{ + + return TRUE; +} + +static gboolean +gst_yadif_stop (GstBaseTransform * trans) +{ + + return TRUE; +} + +static gboolean +gst_yadif_sink_event (GstBaseTransform * trans, GstEvent * event) +{ + + return TRUE; +} + +static gboolean +gst_yadif_src_event (GstBaseTransform * trans, GstEvent * event) +{ + + return TRUE; +} + +static GstFlowReturn +gst_yadif_prepare_output_buffer (GstBaseTransform * trans, + GstBuffer * input, GstBuffer ** buf) +{ + + return GST_FLOW_ERROR; +} + +static gboolean +gst_yadif_copy_metadata (GstBaseTransform * trans, + GstBuffer * input, GstBuffer * outbuf) +{ + + return TRUE; +} + +static gboolean +gst_yadif_transform_meta (GstBaseTransform * trans, + GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf) +{ + + return TRUE; +} + +static void +gst_yadif_before_transform (GstBaseTransform * trans, GstBuffer * buffer) +{ + +} + +void yadif_filter (GstYadif * yadif, int parity, int tff); + +static GstFlowReturn +gst_yadif_transform (GstBaseTransform * trans, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + GstYadif *yadif = GST_YADIF (trans); + int parity; + int tff; + + parity = 0; + tff = 0; + + if (!gst_video_frame_map (&yadif->dest_frame, &yadif->video_info, outbuf, + GST_MAP_WRITE)) + goto dest_map_failed; + + if (!gst_video_frame_map (&yadif->cur_frame, &yadif->video_info, inbuf, + GST_MAP_READ)) + goto src_map_failed; + + yadif->next_frame = yadif->cur_frame; + yadif->prev_frame = yadif->cur_frame; + + yadif_filter (yadif, parity, tff); + + gst_video_frame_unmap (&yadif->dest_frame); + gst_video_frame_unmap (&yadif->cur_frame); + return GST_FLOW_OK; + +dest_map_failed: + { + GST_ERROR_OBJECT (yadif, "failed to map dest"); + return GST_FLOW_ERROR; + } +src_map_failed: + { + GST_ERROR_OBJECT (yadif, "failed to map src"); + gst_video_frame_unmap (&yadif->dest_frame); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_yadif_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + + return GST_FLOW_OK; +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + + return gst_element_register (plugin, "yadif", GST_RANK_NONE, GST_TYPE_YADIF); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + yadif, + "YADIF deinterlacing filter", + plugin_init, VERSION, "GPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/yadif/gstyadif.h b/gst/yadif/gstyadif.h new file mode 100644 index 0000000..118f501 --- /dev/null +++ b/gst/yadif/gstyadif.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) 2013 Rdio, Inc. + * Copyright (C) 2013 David Schleef + * + * 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 _GST_YADIF_H_ +#define _GST_YADIF_H_ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_YADIF (gst_yadif_get_type()) +#define GST_YADIF(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_YADIF,GstYadif)) +#define GST_YADIF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_YADIF,GstYadifClass)) +#define GST_IS_YADIF(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_YADIF)) +#define GST_IS_YADIF_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_YADIF)) + +typedef struct _GstYadif GstYadif; +typedef struct _GstYadifClass GstYadifClass; + +struct _GstYadif +{ + GstBaseTransform base_yadif; + + GstPad *sinkpad; + GstPad *srcpad; + + int mode; + + GstVideoInfo video_info; + + GstVideoFrame prev_frame; + GstVideoFrame cur_frame; + GstVideoFrame next_frame; + GstVideoFrame dest_frame; +}; + +struct _GstYadifClass +{ + GstBaseTransformClass base_yadif_class; +}; + +GType gst_yadif_get_type (void); + +G_END_DECLS + +#endif diff --git a/gst/yadif/vf_yadif.c b/gst/yadif/vf_yadif.c new file mode 100644 index 0000000..efb7884 --- /dev/null +++ b/gst/yadif/vf_yadif.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 David Schleef + * Copyright (C) 2013 Rdio, Inc. + * Copyright (C) 2006-2010 Michael Niedermayer + * 2010 James Darnley + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Libav 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Libav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#undef NDEBUG +#include + +#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 PERM_RWP AV_PERM_WRITE | AV_PERM_PRESERVE | AV_PERM_REUSE + +#define CHECK(j)\ + { int score = FFABS(cur[mrefs-1+(j)] - cur[prefs-1-(j)])\ + + FFABS(cur[mrefs +(j)] - cur[prefs -(j)])\ + + FFABS(cur[mrefs+1+(j)] - cur[prefs+1-(j)]);\ + if (score < spatial_score) {\ + spatial_score= score;\ + spatial_pred= (cur[mrefs +(j)] + cur[prefs -(j)])>>1;\ + +#define FILTER \ + for (x = 0; x < w; x++) { \ + int c = cur[mrefs]; \ + int d = (prev2[0] + next2[0])>>1; \ + int e = cur[prefs]; \ + int temporal_diff0 = FFABS(prev2[0] - next2[0]); \ + int temporal_diff1 =(FFABS(prev[mrefs] - c) + FFABS(prev[prefs] - e) )>>1; \ + int temporal_diff2 =(FFABS(next[mrefs] - c) + FFABS(next[prefs] - e) )>>1; \ + int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); \ + int spatial_pred = (c+e) >> 1; \ + int spatial_score = FFABS(cur[mrefs - 1] - cur[prefs - 1]) + FFABS(c-e) \ + + FFABS(cur[mrefs + 1] - cur[prefs + 1]) - 1; \ + \ + CHECK(-1) CHECK(-2) }} }} \ + CHECK( 1) CHECK( 2) }} }} \ + \ + if (mode < 2) { \ + int b = (prev2[2 * mrefs] + next2[2 * mrefs])>>1; \ + int f = (prev2[2 * prefs] + next2[2 * prefs])>>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[0] = spatial_pred; \ + \ + dst++; \ + cur++; \ + prev++; \ + next++; \ + prev2++; \ + next2++; \ + } + +static void +filter_line_c (guint8 * dst, + guint8 * prev, guint8 * cur, guint8 * next, + int w, int prefs, int mrefs, int parity, int mode) +{ + int x; + guint8 *prev2 = parity ? prev : cur; + guint8 *next2 = parity ? cur : next; + +FILTER} + +#if 0 +static void +filter_line_c_16bit (guint16 * dst, + guint16 * prev, guint16 * cur, guint16 * next, + int w, int prefs, int mrefs, int parity, int mode) +{ + int x; + guint16 *prev2 = parity ? prev : cur; + guint16 *next2 = parity ? cur : next; + mrefs /= 2; + prefs /= 2; + +FILTER} +#endif + +void yadif_filter (GstYadif * yadif, int parity, int tff); +void filter_line_x86 (guint8 * dst, + guint8 * prev, guint8 * cur, guint8 * next, + int w, int prefs, int mrefs, int parity, int mode); + +void +yadif_filter (GstYadif * yadif, int parity, int tff) +{ + int y, i; + const GstVideoInfo *vi = &yadif->video_info; + const GstVideoFormatInfo *vfi = vi->finfo; + + for (i = 0; i < GST_VIDEO_FORMAT_INFO_N_COMPONENTS (vfi); i++) { + int w = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vfi, i, vi->width); + int h = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vfi, i, vi->height); + int refs = GST_VIDEO_INFO_COMP_STRIDE (vi, i); + int df = GST_VIDEO_INFO_COMP_PSTRIDE (vi, i); + guint8 *prev_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->prev_frame, i); + guint8 *cur_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->cur_frame, i); + guint8 *next_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->next_frame, i); + guint8 *dest_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->dest_frame, i); + + for (y = 0; y < h; y++) { + if ((y ^ parity) & 1) { + guint8 *prev = prev_data + y * refs; + guint8 *cur = cur_data + y * refs; + guint8 *next = next_data + y * refs; + guint8 *dst = dest_data + y * refs; + int mode = ((y == 1) || (y + 2 == h)) ? 2 : yadif->mode; + if (0) { + filter_line_c (dst, prev, cur, next, w, + y + 1 < h ? refs : -refs, y ? -refs : refs, parity ^ tff, mode); + } else { + filter_line_x86 (dst, prev, cur, next, w, + y + 1 < h ? refs : -refs, y ? -refs : refs, parity ^ tff, mode); + } + } else { + guint8 *dst = dest_data + y * refs; + guint8 *cur = cur_data + y * refs; + + memcpy (dst, cur, w * df); + } + } + } + +#if 0 + emms_c (); +#endif +} diff --git a/gst/yadif/yadif.c b/gst/yadif/yadif.c new file mode 100644 index 0000000..93b526d --- /dev/null +++ b/gst/yadif/yadif.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2006 Michael Niedermayer + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Libav 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Libav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#if 0 +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/internal.h" +#include "libavutil/mem.h" +#include "libavutil/x86/asm.h" +#include "libavcodec/x86/dsputil_mmx.h" +#include "libavfilter/yadif.h" +#endif + +//#if HAVE_INLINE_ASM + +typedef struct xmm_reg +{ + guint64 a, b; +} xmm_reg; +typedef gint64 x86_reg; +#define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v +#define DECLARE_ASM_CONST(n,t,v) static const t __attribute__((used)) __attribute__ ((aligned (n))) v + +#define ARCH_X86_64 1 +//#if ARCH_X86_64 && defined(__PIC__) +#if 1 +# define LOCAL_MANGLE(a) #a "(%%rip)" +#else +# define LOCAL_MANGLE(a) #a +#endif + +#define EXTERN_PREFIX "" +#define MANGLE(a) EXTERN_PREFIX LOCAL_MANGLE(a) + +DECLARE_ASM_CONST (16, const xmm_reg, pb_1) = { +0x0101010101010101ULL, 0x0101010101010101ULL}; + +DECLARE_ASM_CONST (16, const xmm_reg, pw_1) = { +0x0001000100010001ULL, 0x0001000100010001ULL}; + + + +#if HAVE_SSSE3_INLINE +#define COMPILE_TEMPLATE_SSE2 1 +#define COMPILE_TEMPLATE_SSSE3 1 +#undef RENAME +#define RENAME(a) a ## _ssse3 +#include "yadif_template.c" +#undef COMPILE_TEMPLATE_SSSE3 +#endif + +//#if HAVE_SSE2_INLINE +#undef RENAME +#define RENAME(a) a ## _sse2 +#include "yadif_template.c" +#undef COMPILE_TEMPLATE_SSE2 +//#endif + +#if HAVE_MMXEXT_INLINE +#undef RENAME +#define RENAME(a) a ## _mmxext +#include "yadif_template.c" +#endif + +//#endif /* HAVE_INLINE_ASM */ + +void filter_line_x86 (guint8 * dst, + guint8 * prev, guint8 * cur, guint8 * next, + int w, int prefs, int mrefs, int parity, int mode); + +void +filter_line_x86 (guint8 * dst, + guint8 * prev, guint8 * cur, guint8 * next, + int w, int prefs, int mrefs, int parity, int mode) +{ +#if 0 +#if HAVE_MMXEXT_INLINE + if (cpu_flags & AV_CPU_FLAG_MMXEXT) + yadif->filter_line = yadif_filter_line_mmxext; +#endif +#if HAVE_SSE2_INLINE + if (cpu_flags & AV_CPU_FLAG_SSE2) + yadif->filter_line = yadif_filter_line_sse2; +#endif +#if HAVE_SSSE3_INLINE + if (cpu_flags & AV_CPU_FLAG_SSSE3) + yadif->filter_line = yadif_filter_line_ssse3; +#endif +#endif + yadif_filter_line_sse2 (dst, prev, cur, next, w, prefs, mrefs, parity, mode); +} diff --git a/gst/yadif/yadif_template.c b/gst/yadif/yadif_template.c new file mode 100644 index 0000000..ef3dffc --- /dev/null +++ b/gst/yadif/yadif_template.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2006 Michael Niedermayer + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Libav 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Libav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef COMPILE_TEMPLATE_SSE2 +#define MM "%%xmm" +#define MOV "movq" +#define MOVQ "movdqa" +#define MOVQU "movdqu" +#define STEP 8 +#define LOAD(mem,dst) \ + MOV" "mem", "dst" \n\t"\ + "punpcklbw "MM"7, "dst" \n\t" +#define PSRL1(reg) "psrldq $1, "reg" \n\t" +#define PSRL2(reg) "psrldq $2, "reg" \n\t" +#define PSHUF(src,dst) "movdqa "dst", "src" \n\t"\ + "psrldq $2, "src" \n\t" +#else +#define MM "%%mm" +#define MOV "movd" +#define MOVQ "movq" +#define MOVQU "movq" +#define STEP 4 +#define LOAD(mem,dst) \ + MOV" "mem", "dst" \n\t"\ + "punpcklbw "MM"7, "dst" \n\t" +#define PSRL1(reg) "psrlq $8, "reg" \n\t" +#define PSRL2(reg) "psrlq $16, "reg" \n\t" +#define PSHUF(src,dst) "pshufw $9, "dst", "src" \n\t" +#endif + +#ifdef COMPILE_TEMPLATE_SSSE3 +#define PABS(tmp,dst) \ + "pabsw "dst", "dst" \n\t" +#else +#define PABS(tmp,dst) \ + "pxor "tmp", "tmp" \n\t"\ + "psubw "dst", "tmp" \n\t"\ + "pmaxsw "tmp", "dst" \n\t" +#endif + +#define CHECK(pj,mj) \ + MOVQU" "#pj"(%[cur],%[mrefs]), "MM"2 \n\t" /* cur[x-refs-1+j] */\ + MOVQU" "#mj"(%[cur],%[prefs]), "MM"3 \n\t" /* cur[x+refs-1-j] */\ + MOVQ" "MM"2, "MM"4 \n\t"\ + MOVQ" "MM"2, "MM"5 \n\t"\ + "pxor "MM"3, "MM"4 \n\t"\ + "pavgb "MM"3, "MM"5 \n\t"\ + "pand "MANGLE(pb_1)", "MM"4 \n\t"\ + "psubusb "MM"4, "MM"5 \n\t"\ + PSRL1(MM"5") \ + "punpcklbw "MM"7, "MM"5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\ + MOVQ" "MM"2, "MM"4 \n\t"\ + "psubusb "MM"3, "MM"2 \n\t"\ + "psubusb "MM"4, "MM"3 \n\t"\ + "pmaxub "MM"3, "MM"2 \n\t"\ + MOVQ" "MM"2, "MM"3 \n\t"\ + MOVQ" "MM"2, "MM"4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\ + PSRL1(MM"3") /* ABS(cur[x-refs +j] - cur[x+refs -j]) */\ + PSRL2(MM"4") /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\ + "punpcklbw "MM"7, "MM"2 \n\t"\ + "punpcklbw "MM"7, "MM"3 \n\t"\ + "punpcklbw "MM"7, "MM"4 \n\t"\ + "paddw "MM"3, "MM"2 \n\t"\ + "paddw "MM"4, "MM"2 \n\t" /* score */ + +#define CHECK1 \ + MOVQ" "MM"0, "MM"3 \n\t"\ + "pcmpgtw "MM"2, "MM"3 \n\t" /* if(score < spatial_score) */\ + "pminsw "MM"2, "MM"0 \n\t" /* spatial_score= score; */\ + MOVQ" "MM"3, "MM"6 \n\t"\ + "pand "MM"3, "MM"5 \n\t"\ + "pandn "MM"1, "MM"3 \n\t"\ + "por "MM"5, "MM"3 \n\t"\ + MOVQ" "MM"3, "MM"1 \n\t" /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */ + +#define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\ + hurts both quality and speed, but matches the C version. */\ + "paddw "MANGLE(pw_1)", "MM"6 \n\t"\ + "psllw $14, "MM"6 \n\t"\ + "paddsw "MM"6, "MM"2 \n\t"\ + MOVQ" "MM"0, "MM"3 \n\t"\ + "pcmpgtw "MM"2, "MM"3 \n\t"\ + "pminsw "MM"2, "MM"0 \n\t"\ + "pand "MM"3, "MM"5 \n\t"\ + "pandn "MM"1, "MM"3 \n\t"\ + "por "MM"5, "MM"3 \n\t"\ + MOVQ" "MM"3, "MM"1 \n\t" + +static void RENAME (yadif_filter_line) (guint8 * dst, guint8 * prev, + guint8 * cur, guint8 * next, int w, int prefs, int mrefs, int parity, + int mode) +{ + DECLARE_ALIGNED (16, guint8, tmp)[16 * 4]; + int x; + +#define FILTER\ + for(x=0; x>1 */\ + MOVQ" "MM"0, (%[tmp]) \n\t" /* c */\ + MOVQ" "MM"3, 16(%[tmp]) \n\t" /* d */\ + MOVQ" "MM"1, 32(%[tmp]) \n\t" /* e */\ + "psubw "MM"4, "MM"2 \n\t"\ + PABS( MM"4", MM"2") /* temporal_diff0 */\ + LOAD("(%[prev],%[mrefs])", MM"3") /* prev[x-refs] */\ + LOAD("(%[prev],%[prefs])", MM"4") /* prev[x+refs] */\ + "psubw "MM"0, "MM"3 \n\t"\ + "psubw "MM"1, "MM"4 \n\t"\ + PABS( MM"5", MM"3")\ + PABS( MM"5", MM"4")\ + "paddw "MM"4, "MM"3 \n\t" /* temporal_diff1 */\ + "psrlw $1, "MM"2 \n\t"\ + "psrlw $1, "MM"3 \n\t"\ + "pmaxsw "MM"3, "MM"2 \n\t"\ + LOAD("(%[next],%[mrefs])", MM"3") /* next[x-refs] */\ + LOAD("(%[next],%[prefs])", MM"4") /* next[x+refs] */\ + "psubw "MM"0, "MM"3 \n\t"\ + "psubw "MM"1, "MM"4 \n\t"\ + PABS( MM"5", MM"3")\ + PABS( MM"5", MM"4")\ + "paddw "MM"4, "MM"3 \n\t" /* temporal_diff2 */\ + "psrlw $1, "MM"3 \n\t"\ + "pmaxsw "MM"3, "MM"2 \n\t"\ + MOVQ" "MM"2, 48(%[tmp]) \n\t" /* diff */\ +\ + "paddw "MM"0, "MM"1 \n\t"\ + "paddw "MM"0, "MM"0 \n\t"\ + "psubw "MM"1, "MM"0 \n\t"\ + "psrlw $1, "MM"1 \n\t" /* spatial_pred */\ + PABS( MM"2", MM"0") /* ABS(c-e) */\ +\ + MOVQU" -1(%[cur],%[mrefs]), "MM"2 \n\t" /* cur[x-refs-1] */\ + MOVQU" -1(%[cur],%[prefs]), "MM"3 \n\t" /* cur[x+refs-1] */\ + MOVQ" "MM"2, "MM"4 \n\t"\ + "psubusb "MM"3, "MM"2 \n\t"\ + "psubusb "MM"4, "MM"3 \n\t"\ + "pmaxub "MM"3, "MM"2 \n\t"\ + PSHUF(MM"3", MM"2") \ + "punpcklbw "MM"7, "MM"2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\ + "punpcklbw "MM"7, "MM"3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\ + "paddw "MM"2, "MM"0 \n\t"\ + "paddw "MM"3, "MM"0 \n\t"\ + "psubw "MANGLE(pw_1)", "MM"0 \n\t" /* spatial_score */\ +\ + CHECK(-2,0)\ + CHECK1\ + CHECK(-3,1)\ + CHECK2\ + CHECK(0,-2)\ + CHECK1\ + CHECK(1,-3)\ + CHECK2\ +\ + /* if(p->mode<2) ... */\ + MOVQ" 48(%[tmp]), "MM"6 \n\t" /* diff */\ + "cmpl $2, %[mode] \n\t"\ + "jge 1f \n\t"\ + LOAD("(%["prev2"],%[mrefs],2)", MM"2") /* prev2[x-2*refs] */\ + LOAD("(%["next2"],%[mrefs],2)", MM"4") /* next2[x-2*refs] */\ + LOAD("(%["prev2"],%[prefs],2)", MM"3") /* prev2[x+2*refs] */\ + LOAD("(%["next2"],%[prefs],2)", MM"5") /* next2[x+2*refs] */\ + "paddw "MM"4, "MM"2 \n\t"\ + "paddw "MM"5, "MM"3 \n\t"\ + "psrlw $1, "MM"2 \n\t" /* b */\ + "psrlw $1, "MM"3 \n\t" /* f */\ + MOVQ" (%[tmp]), "MM"4 \n\t" /* c */\ + MOVQ" 16(%[tmp]), "MM"5 \n\t" /* d */\ + MOVQ" 32(%[tmp]), "MM"7 \n\t" /* e */\ + "psubw "MM"4, "MM"2 \n\t" /* b-c */\ + "psubw "MM"7, "MM"3 \n\t" /* f-e */\ + MOVQ" "MM"5, "MM"0 \n\t"\ + "psubw "MM"4, "MM"5 \n\t" /* d-c */\ + "psubw "MM"7, "MM"0 \n\t" /* d-e */\ + MOVQ" "MM"2, "MM"4 \n\t"\ + "pminsw "MM"3, "MM"2 \n\t"\ + "pmaxsw "MM"4, "MM"3 \n\t"\ + "pmaxsw "MM"5, "MM"2 \n\t"\ + "pminsw "MM"5, "MM"3 \n\t"\ + "pmaxsw "MM"0, "MM"2 \n\t" /* max */\ + "pminsw "MM"0, "MM"3 \n\t" /* min */\ + "pxor "MM"4, "MM"4 \n\t"\ + "pmaxsw "MM"3, "MM"6 \n\t"\ + "psubw "MM"2, "MM"4 \n\t" /* -max */\ + "pmaxsw "MM"4, "MM"6 \n\t" /* diff= MAX3(diff, min, -max); */\ + "1: \n\t"\ +\ + MOVQ" 16(%[tmp]), "MM"2 \n\t" /* d */\ + MOVQ" "MM"2, "MM"3 \n\t"\ + "psubw "MM"6, "MM"2 \n\t" /* d-diff */\ + "paddw "MM"6, "MM"3 \n\t" /* d+diff */\ + "pmaxsw "MM"2, "MM"1 \n\t"\ + "pminsw "MM"3, "MM"1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\ + "packuswb "MM"1, "MM"1 \n\t"\ +\ + ::[prev] "r"(prev),\ + [cur] "r"(cur),\ + [next] "r"(next),\ + [prefs]"r"((x86_reg)prefs),\ + [mrefs]"r"((x86_reg)mrefs),\ + [mode] "g"(mode),\ + [tmp] "r"(tmp)\ + );\ + __asm__ volatile(MOV" "MM"1, %0" :"=m"(*dst));\ + dst += STEP;\ + prev+= STEP;\ + cur += STEP;\ + next+= STEP;\ + } + + if (parity) { +#define prev2 "prev" +#define next2 "cur" + FILTER +#undef prev2 +#undef next2 + } else { +#define prev2 "cur" +#define next2 "next" + FILTER +#undef prev2 +#undef next2 + } +} + +#undef STEP +#undef MM +#undef MOV +#undef MOVQ +#undef MOVQU +#undef PSHUF +#undef PSRL1 +#undef PSRL2 +#undef LOAD +#undef PABS +#undef CHECK +#undef CHECK1 +#undef CHECK2 +#undef FILTER -- 2.7.4