From 70ff21342117866ee939f9f7597ee487bae31757 Mon Sep 17 00:00:00 2001 From: Martin Eikermann Date: Wed, 11 Jun 2008 11:12:14 +0000 Subject: gst/deinterlace2/: Add a deinterlacer plugin based on the tvtime/DScaler deinterlacer, which was relicensed to LGPL f... Original commit message from CVS: Based on a patch by: Martin Eikermann * gst/deinterlace2/Makefile.am: * gst/deinterlace2/gstdeinterlace2.c: (gst_deinterlace2_method_get_type), (gst_deinterlace2_fields_get_type), (gst_deinterlace2_field_layout_get_type), (gst_deinterlace2_base_init), (gst_deinterlace2_class_init), (gst_deinterlace2_init), (gst_deinterlace2_set_method), (gst_deinterlace2_set_property), (gst_deinterlace2_get_property), (gst_deinterlace2_finalize), (gst_deinterlace2_pop_history), (gst_deinterlace2_head_history), (gst_deinterlace2_push_history), (gst_deinterlace2_deinterlace_scanlines), (gst_deinterlace2_chain), (gst_deinterlace2_setcaps), (gst_deinterlace2_sink_event), (gst_deinterlace2_change_state), (gst_deinterlace2_src_event), (gst_deinterlace2_src_query), (gst_deinterlace2_src_query_types), (plugin_init): * gst/deinterlace2/gstdeinterlace2.h: * gst/deinterlace2/tvtime/greedy.c: (copy_scanline), (deinterlace_greedy_packed422_scanline_mmxext), (dscaler_greedyl_get_method): * gst/deinterlace2/tvtime/greedyh.asm: * gst/deinterlace2/tvtime/greedyh.c: (deinterlace_frame_di_greedyh), (dscaler_greedyh_get_method), (greedyh_init), (greedyh_filter_mmx), (greedyh_filter_3dnow), (greedyh_filter_sse): * gst/deinterlace2/tvtime/greedyh.h: * gst/deinterlace2/tvtime/greedyhmacros.h: * gst/deinterlace2/tvtime/mmx.h: * gst/deinterlace2/tvtime/plugins.h: * gst/deinterlace2/tvtime/speedtools.h: * gst/deinterlace2/tvtime/speedy.c: (multiply_alpha), (clip255), (comb_factor_packed422_scanline_mmx), (diff_factor_packed422_scanline_c), (diff_factor_packed422_scanline_mmx), (diff_packed422_block8x8_mmx), (diff_packed422_block8x8_c), (packed444_to_packed422_scanline_c), (packed422_to_packed444_scanline_c), (packed422_to_packed444_rec601_scanline_c), (vfilter_chroma_121_packed422_scanline_mmx), (vfilter_chroma_121_packed422_scanline_c), (vfilter_chroma_332_packed422_scanline_mmx), (vfilter_chroma_332_packed422_scanline_c), (kill_chroma_packed422_inplace_scanline_mmx), (kill_chroma_packed422_inplace_scanline_c), (invert_colour_packed422_inplace_scanline_mmx), (invert_colour_packed422_inplace_scanline_c), (mirror_packed422_inplace_scanline_c), (interpolate_packed422_scanline_c), (convert_uyvy_to_yuyv_scanline_mmx), (convert_uyvy_to_yuyv_scanline_c), (interpolate_packed422_scanline_mmx), (interpolate_packed422_scanline_mmxext), (blit_colour_packed422_scanline_c), (blit_colour_packed422_scanline_mmx), (blit_colour_packed422_scanline_mmxext), (blit_colour_packed4444_scanline_c), (blit_colour_packed4444_scanline_mmx), (blit_colour_packed4444_scanline_mmxext), (small_memcpy), (speedy_memcpy_c), (speedy_memcpy_mmx), (speedy_memcpy_mmxext), (blit_packed422_scanline_c), (blit_packed422_scanline_mmx), (blit_packed422_scanline_mmxext), (composite_colour4444_alpha_to_packed422_scanline_c), (composite_colour4444_alpha_to_packed422_scanline_mmxext), (composite_packed4444_alpha_to_packed422_scanline_c), (composite_packed4444_alpha_to_packed422_scanline_mmxext), (composite_packed4444_to_packed422_scanline_c), (composite_packed4444_to_packed422_scanline_mmxext), (composite_alphamask_to_packed4444_scanline_c), (composite_alphamask_to_packed4444_scanline_mmxext), (composite_alphamask_alpha_to_packed4444_scanline_c), (premultiply_packed4444_scanline_c), (premultiply_packed4444_scanline_mmxext), (blend_packed422_scanline_c), (blend_packed422_scanline_mmxext), (quarter_blit_vertical_packed422_scanline_mmxext), (quarter_blit_vertical_packed422_scanline_c), (subpix_blit_vertical_packed422_scanline_c), (a8_subpix_blit_scanline_c), (myround), (init_RGB_to_YCbCr_tables), (init_YCbCr_to_RGB_tables), (rgb24_to_packed444_rec601_scanline_c), (rgba32_to_packed4444_rec601_scanline_c), (packed444_to_rgb24_rec601_scanline_c), (packed444_to_nonpremultiplied_packed4444_scanline_c), (aspect_adjust_packed4444_scanline_c), (setup_speedy_calls), (speedy_get_accel): * gst/deinterlace2/tvtime/speedy.h: * gst/deinterlace2/tvtime/sse.h: * gst/deinterlace2/tvtime/tomsmocomp.c: (Fieldcopy), (deinterlace_frame_di_tomsmocomp), (dscaler_tomsmocomp_get_method), (tomsmocomp_init), (tomsmocomp_filter_mmx), (tomsmocomp_filter_3dnow), (tomsmocomp_filter_sse): * gst/deinterlace2/tvtime/tomsmocomp.h: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoop0A.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopBottom.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA8.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA2.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA6.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH2.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopTop.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVA.inc: * gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVAH.inc: * gst/deinterlace2/tvtime/tomsmocomp/StrangeBob.inc: * gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll.inc: * gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll2.inc: * gst/deinterlace2/tvtime/tomsmocomp/WierdBob.inc: * gst/deinterlace2/tvtime/vfir.c: (deinterlace_line), (deinterlace_scanline_vfir), (copy_scanline), (dscaler_vfir_get_method): * gst/deinterlace2/tvtime/x86-64_macros.inc: Add a deinterlacer plugin based on the tvtime/DScaler deinterlacer, which was relicensed to LGPL for GStreamer and in theory provides better and faster results than the simple deinterlace element. Fixes bug #163578. Ported to GStreamer 0.10 but still not enabled or included in the build system by default because of bad artefacts caused by a bug somewhere and as it can be only build on x86/amd64 ATM and requires special CFLAGS. Will be fixed soon. --- gst/deinterlace2/gstdeinterlace2.c | 893 +++++++++++++++++++++++++++++++++++++ 1 file changed, 893 insertions(+) create mode 100644 gst/deinterlace2/gstdeinterlace2.c (limited to 'gst/deinterlace2/gstdeinterlace2.c') diff --git a/gst/deinterlace2/gstdeinterlace2.c b/gst/deinterlace2/gstdeinterlace2.c new file mode 100644 index 00000000..450a31c9 --- /dev/null +++ b/gst/deinterlace2/gstdeinterlace2.c @@ -0,0 +1,893 @@ +/* + * GStreamer + * Copyright (C) 2005 Martin Eikermann + * Copyright (C) 2008 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdeinterlace2.h" +#include +#include + +#include "tvtime/plugins.h" +#include "tvtime/speedy.h" + +GST_DEBUG_CATEGORY_STATIC (deinterlace2_debug); +#define GST_CAT_DEFAULT (deinterlace2_debug) + +/* Object signals and args */ +enum +{ + LAST_SIGNAL +}; + +/* Arguments */ +enum +{ + ARG_0, + ARG_METHOD, + ARG_FIELDS, + ARG_FIELD_LAYOUT +}; + +#define GST_TYPE_DEINTERLACE2_METHOD (gst_deinterlace2_method_get_type ()) +static GType +gst_deinterlace2_method_get_type (void) +{ + static GType deinterlace2_method_type = 0; + + static const GEnumValue method_types[] = { + {GST_DEINTERLACE2_TOM, "Toms Motion Compensation", "tomsmc"}, + {GST_DEINTERLACE2_GREEDY_H, "Greedy High Motion", "greedyh"}, + {GST_DEINTERLACE2_GREEDY_L, "Greedy Low Motion", "greedyl"}, + {GST_DEINTERLACE2_VFIR, "Vertical Blur", "vfir"}, + {0, NULL, NULL}, + }; + + if (!deinterlace2_method_type) { + deinterlace2_method_type = + g_enum_register_static ("GstDeinterlace2Methods", method_types); + } + return deinterlace2_method_type; +} + +#define GST_TYPE_DEINTERLACE2_FIELDS (gst_deinterlace2_fields_get_type ()) +static GType +gst_deinterlace2_fields_get_type (void) +{ + static GType deinterlace2_fields_type = 0; + + static const GEnumValue fields_types[] = { + {GST_DEINTERLACE2_ALL, "All fields", "all"}, + {GST_DEINTERLACE2_TF, "Top fields only", "top"}, + {GST_DEINTERLACE2_BF, "Bottom fields only", "bottom"}, + {0, NULL, NULL}, + }; + + if (!deinterlace2_fields_type) { + deinterlace2_fields_type = + g_enum_register_static ("GstDeinterlace2Fields", fields_types); + } + return deinterlace2_fields_type; +} + +#define GST_TYPE_DEINTERLACE2_FIELD_LAYOUT (gst_deinterlace2_field_layout_get_type ()) +static GType +gst_deinterlace2_field_layout_get_type (void) +{ + static GType deinterlace2_field_layout_type = 0; + + static const GEnumValue field_layout_types[] = { + {GST_DEINTERLACE2_LAYOUT_AUTO, "Auto detection", "auto"}, + {GST_DEINTERLACE2_LAYOUT_TFF, "Top field first", "tff"}, + {GST_DEINTERLACE2_LAYOUT_BFF, "Bottom field first", "bff"}, + {0, NULL, NULL}, + }; + + if (!deinterlace2_field_layout_type) { + deinterlace2_field_layout_type = + g_enum_register_static ("GstDeinterlace2FieldLayout", + field_layout_types); + } + return deinterlace2_field_layout_type; +} + +static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2")) + ); + +static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2")) + ); + +static void gst_deinterlace2_finalize (GObject * object); + +static void gst_deinterlace2_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_deinterlace2_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_deinterlace2_setcaps (GstPad * pad, GstCaps * caps); + +static gboolean gst_deinterlace2_sink_event (GstPad * pad, GstEvent * event); + +static GstFlowReturn gst_deinterlace2_chain (GstPad * pad, GstBuffer * buffer); + +static GstStateChangeReturn gst_deinterlace2_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_deinterlace2_src_event (GstPad * pad, GstEvent * event); + +static gboolean gst_deinterlace2_src_query (GstPad * pad, GstQuery * query); + +static const GstQueryType *gst_deinterlace2_src_query_types (GstPad * pad); + +static void gst_deinterlace2_deinterlace_scanlines (GstDeinterlace2 * object); + +GST_BOILERPLATE (GstDeinterlace2, gst_deinterlace2, GstElement, + GST_TYPE_ELEMENT); + +static void +gst_deinterlace2_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_templ)); + + gst_element_class_set_details_simple (element_class, + "Deinterlacer", + "Filter/Video", + "Deinterlace Methods ported from DScaler/TvTime", + "Martin Eikermann , " + "Sebastian Dröge "); +} + +static void +gst_deinterlace2_class_init (GstDeinterlace2Class * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + GstElementClass *element_class = (GstElementClass *) klass; + + gobject_class->set_property = gst_deinterlace2_set_property; + gobject_class->get_property = gst_deinterlace2_get_property; + gobject_class->finalize = gst_deinterlace2_finalize; + + g_object_class_install_property (gobject_class, ARG_METHOD, + g_param_spec_enum ("method", + "Method", + "Deinterlace Method", + GST_TYPE_DEINTERLACE2_METHOD, + GST_DEINTERLACE2_GREEDY_H, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + g_object_class_install_property (gobject_class, ARG_FIELDS, + g_param_spec_enum ("fields", + "fields", + "Fields to use for deinterlacing", + GST_TYPE_DEINTERLACE2_FIELDS, + GST_DEINTERLACE2_ALL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + + g_object_class_install_property (gobject_class, ARG_FIELDS, + g_param_spec_enum ("tff", + "tff", + "Deinterlace top field first", + GST_TYPE_DEINTERLACE2_FIELD_LAYOUT, + GST_DEINTERLACE2_LAYOUT_AUTO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_deinterlace2_change_state); +} + +static void +gst_deinterlace2_init (GstDeinterlace2 * object, GstDeinterlace2Class * klass) +{ + object->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink"); + gst_pad_set_chain_function (object->sinkpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_chain)); + gst_pad_set_event_function (object->sinkpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_sink_event)); + gst_pad_set_setcaps_function (object->sinkpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_setcaps)); + gst_pad_set_getcaps_function (object->sinkpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_element_add_pad (GST_ELEMENT (object), object->sinkpad); + + object->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); + gst_pad_set_event_function (object->srcpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_src_event)); + gst_pad_set_query_type_function (object->srcpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_src_query_types)); + gst_pad_set_query_function (object->srcpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_src_query)); + gst_pad_set_setcaps_function (object->srcpad, + GST_DEBUG_FUNCPTR (gst_deinterlace2_setcaps)); + gst_pad_set_getcaps_function (object->srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_element_add_pad (GST_ELEMENT (object), object->srcpad); + + gst_element_no_more_pads (GST_ELEMENT (object)); + + object->cpu_feature_flags = oil_cpu_get_flags (); + + setup_speedy_calls (object->cpu_feature_flags, 0); + object->pMemcpy = speedy_memcpy; + + object->method = dscaler_tomsmocomp_get_method (); + + object->history_count = 0; + + object->field_layout = GST_DEINTERLACE2_LAYOUT_AUTO; + + object->out_buf = NULL; + object->output_stride = 0; + object->line_length = 0; + object->frame_width = 0; + object->frame_height = 0; + object->field_height = 0; + object->field_stride = 0; + + object->fields = GST_DEINTERLACE2_ALL; + + object->bottom_field = TRUE; +} + +static void +gst_deinterlace2_set_method (GstDeinterlace2 * object, + GstDeinterlace2Methods method) +{ + + switch (method) { + case GST_DEINTERLACE2_TOM: + object->method_id = method; + object->method = dscaler_tomsmocomp_get_method (); + break; + case GST_DEINTERLACE2_GREEDY_H: + object->method_id = method; + object->method = dscaler_greedyh_get_method (); + break; + case GST_DEINTERLACE2_GREEDY_L: + object->method_id = method; + object->method = dscaler_greedyl_get_method (); + break; + case GST_DEINTERLACE2_VFIR: + object->method_id = method; + object->method = dscaler_vfir_get_method (); + break; + default: + GST_WARNING ("Invalid Deinterlacer Method"); + } + + + if (object->method->deinterlace_frame == NULL) + object->method->deinterlace_frame = gst_deinterlace2_deinterlace_scanlines; + + /* TODO: if current method requires less fields in the history, + pop the diff from field_history. + */ + +} + +static void +gst_deinterlace2_set_property (GObject * _object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstDeinterlace2 *object; + + g_return_if_fail (GST_IS_DEINTERLACE2 (_object)); + object = GST_DEINTERLACE2 (_object); + + switch (prop_id) { + case ARG_METHOD: + gst_deinterlace2_set_method (object, g_value_get_enum (value)); + break; + case ARG_FIELDS: + object->fields = g_value_get_enum (value); + break; + case ARG_FIELD_LAYOUT: + object->field_layout = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + +} + +static void +gst_deinterlace2_get_property (GObject * _object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstDeinterlace2 *object; + + g_return_if_fail (GST_IS_DEINTERLACE2 (_object)); + object = GST_DEINTERLACE2 (_object); + + switch (prop_id) { + case ARG_METHOD: + g_value_set_enum (value, object->method_id); + break; + case ARG_FIELDS: + g_value_set_enum (value, object->fields); + break; + case ARG_FIELD_LAYOUT: + g_value_set_enum (value, object->field_layout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gst_deinterlace2_finalize (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static GstBuffer * +gst_deinterlace2_pop_history (GstDeinterlace2 * object) +{ + GstBuffer *buffer = NULL; + + g_assert (object->history_count > 0); + + buffer = object->field_history[object->history_count - 1].buf; + + object->history_count--; + GST_DEBUG ("pop, size(history): %d", object->history_count); + + return buffer; +} + +#if 0 +static GstBuffer * +gst_deinterlace2_head_history (GstDeinterlace2 * object) +{ + return object->field_history[object->history_count - 1].buf; +} +#endif + + +/* invariant: field with smallest timestamp is object->field_history[object->history_count-1] + +*/ + +static void +gst_deinterlace2_push_history (GstDeinterlace2 * object, GstBuffer * buffer) +{ + int i = 1; + + GstClockTime timestamp; + + GstClockTime field_diff; + + g_assert (object->history_count < MAX_FIELD_HISTORY - 2); + + for (i = MAX_FIELD_HISTORY - 1; i >= 2; i--) { + object->field_history[i].buf = object->field_history[i - 2].buf; + object->field_history[i].flags = object->field_history[i - 2].flags; + } + + if (object->field_layout == GST_DEINTERLACE2_LAYOUT_AUTO) { + GST_WARNING ("Could not detect field layout. Assuming top field first."); + object->field_layout = GST_DEINTERLACE2_LAYOUT_TFF; + } + + + if (object->field_layout == GST_DEINTERLACE2_LAYOUT_TFF) { + GST_DEBUG ("Top field first"); + object->field_history[0].buf = + gst_buffer_create_sub (buffer, object->line_length, + GST_BUFFER_SIZE (buffer) - object->line_length); + object->field_history[0].flags = PICTURE_INTERLACED_BOTTOM; + object->field_history[1].buf = buffer; + object->field_history[1].flags = PICTURE_INTERLACED_TOP; + } else { + GST_DEBUG ("Bottom field first"); + object->field_history[0].buf = buffer; + object->field_history[0].flags = PICTURE_INTERLACED_TOP; + object->field_history[1].buf = + gst_buffer_create_sub (buffer, object->line_length, + GST_BUFFER_SIZE (buffer) - object->line_length); + object->field_history[1].flags = PICTURE_INTERLACED_BOTTOM; + } + + /* Timestamps are assigned to the field buffers under the assumption that + the timestamp of the buffer equals the first fields timestamp */ + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + field_diff = GST_SECOND / (object->frame_rate_d * 2) / object->frame_rate_n; + GST_BUFFER_TIMESTAMP (object->field_history[0].buf) = timestamp + field_diff; + GST_BUFFER_TIMESTAMP (object->field_history[1].buf) = timestamp; + + object->history_count += 2; + GST_DEBUG ("push, size(history): %d", object->history_count); +} + +/* some methods support only deinterlace_/copy_scanline functions. + This funtion calls them in the right manner. */ +static void +gst_deinterlace2_deinterlace_scanlines (GstDeinterlace2 * object) +{ + + gint line = 1; + + gint cur_field_idx = object->history_count - object->method->fields_required; + + GST_INFO ("cur_field_idx: %d", cur_field_idx); + + guint8 *out_data = GST_BUFFER_DATA (object->out_buf); + + guint8 *cur_field = + GST_BUFFER_DATA (object->field_history[cur_field_idx].buf); + guint8 *last_field = NULL; + + guint8 *second_last_field = NULL; + + /* method can just handle up to 3 history fields, + bcs until now there isn't a plugin (with interp./copy scanline methods) + that uses more */ + g_assert (object->method->fields_required <= 3); + + if (object->method->fields_required >= 2) { + last_field = GST_BUFFER_DATA (object->field_history[cur_field_idx + 1].buf); + } + if (object->method->fields_required >= 3) { + second_last_field = + GST_BUFFER_DATA (object->field_history[cur_field_idx + 2].buf); + } + + if (object->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM) { + /* double the first scanline of the bottom field */ + blit_packed422_scanline (out_data, cur_field, object->frame_width); + out_data += object->output_stride; + } + + blit_packed422_scanline (out_data, cur_field, object->frame_width); + out_data += object->output_stride; + line++; + + for (; line <= object->field_height;) { + deinterlace_scanline_data_t data; + + /* interp. scanline */ + data.t0 = cur_field; + data.b0 = cur_field + object->field_stride; + + if (last_field != NULL) { + data.tt1 = last_field; + data.m1 = last_field + object->field_stride; + data.bb1 = last_field + (object->field_stride * 2); + + last_field += object->field_stride; + } + + if (second_last_field != NULL) { + data.t2 = second_last_field; + data.b2 = second_last_field + object->field_stride; + } + + /* set valid data for corner cases */ + if (line == 2) { + data.tt1 = data.bb1; + } else if (line == object->field_height) { + data.bb1 = data.tt1; + } + + object->method->interpolate_scanline (object, &data, out_data); + out_data += object->output_stride; + + /* copy a scanline */ + data.tt0 = cur_field; + data.m0 = cur_field + (object->field_stride); + data.bb0 = cur_field + (object->field_stride * 2); + cur_field += object->field_stride; + + if (last_field != NULL) { + data.t1 = last_field; + data.b1 = last_field + object->field_stride; + } + + if (second_last_field != NULL) { + data.tt2 = second_last_field; + data.m2 = second_last_field + (object->field_stride); + data.bb2 = second_last_field + (object->field_stride * 2); + second_last_field += object->field_stride; + } + + /* set valid data for corner cases */ + if (line == object->field_height) { + data.bb0 = data.tt0; + data.bb2 = data.tt2; + data.b1 = data.t1; + } + + object->method->copy_scanline (object, &data, out_data); + out_data += object->output_stride; + line++; + } + + if (object->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP) { + /* double the last scanline of the top field */ + blit_packed422_scanline (out_data, cur_field, object->frame_width); + } +} + +static GstFlowReturn +gst_deinterlace2_chain (GstPad * pad, GstBuffer * buf) +{ + //GstBuffer *out_buf = NULL; + GstDeinterlace2 *object = NULL; + + GstClockTime timestamp; + + //GstFlowReturn ret = GST_FLOW_OK; + + object = GST_DEINTERLACE2 (GST_PAD_PARENT (pad)); + + gst_deinterlace2_push_history (object, buf); + buf = NULL; + + if (object->method != NULL) { + int cur_field_idx = 0; + + /* Not enough fields in the history */ + if (object->history_count < object->method->fields_required + 1) { + /* TODO: do bob or just forward frame */ + GST_DEBUG ("HistoryCount=%d", object->history_count); + return GST_FLOW_OK; + } + + if (object->fields == GST_DEINTERLACE2_ALL) + GST_DEBUG ("All fields"); + if (object->fields == GST_DEINTERLACE2_TF) + GST_DEBUG ("Top fields"); + if (object->fields == GST_DEINTERLACE2_BF) + GST_DEBUG ("Bottom fields"); + + cur_field_idx = object->history_count - object->method->fields_required; + + if ((object->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP + && object->fields == GST_DEINTERLACE2_TF) || + object->fields == GST_DEINTERLACE2_ALL) { + GST_DEBUG ("deinterlacing top field"); + + /* create new buffer */ + object->out_buf = gst_buffer_new_and_alloc (object->frame_size); + gst_buffer_set_caps (object->out_buf, GST_PAD_CAPS (object->srcpad)); + + /* do magic calculus */ + if (object->method->deinterlace_frame != NULL) { + object->method->deinterlace_frame (object); + + buf = gst_deinterlace2_pop_history (object); + timestamp = GST_BUFFER_TIMESTAMP (buf); + gst_buffer_unref (buf); + + GST_BUFFER_TIMESTAMP (object->out_buf) = timestamp; + gst_pad_push (object->srcpad, object->out_buf); + } + } + /* no calculation done: remove excess field */ + else if (object->field_history[cur_field_idx].flags == + PICTURE_INTERLACED_TOP && object->fields == GST_DEINTERLACE2_BF) { + GST_DEBUG ("Removing unused top field"); + buf = gst_deinterlace2_pop_history (object); + gst_buffer_unref (buf); + } + + cur_field_idx = object->history_count - object->method->fields_required; + + /* deinterlace bottom_field */ + if ((object->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM + && object->fields == GST_DEINTERLACE2_BF) || + object->fields == GST_DEINTERLACE2_ALL) { + GST_DEBUG ("deinterlacing bottom field"); + + /* create new buffer */ + object->out_buf = gst_buffer_new_and_alloc (object->frame_size); + gst_buffer_set_caps (object->out_buf, GST_PAD_CAPS (object->srcpad)); + + /* do magic calculus */ + if (object->method->deinterlace_frame != NULL) { + object->method->deinterlace_frame (object); + + buf = gst_deinterlace2_pop_history (object); + timestamp = GST_BUFFER_TIMESTAMP (buf); + gst_buffer_unref (buf); + + GST_BUFFER_TIMESTAMP (object->out_buf) = timestamp; + gst_pad_push (object->srcpad, object->out_buf); + } + } + /* no calculation done: remove excess field */ + else if (object->field_history[cur_field_idx].flags == + PICTURE_INTERLACED_BOTTOM && object->fields == GST_DEINTERLACE2_TF) { + GST_DEBUG ("Removing unused bottom field"); + buf = gst_deinterlace2_pop_history (object); + gst_buffer_unref (buf); + } + + + } else { + object->out_buf = gst_deinterlace2_pop_history (object); + gst_pad_push (object->srcpad, object->out_buf); + } + GST_DEBUG ("----chain end ----\n\n"); + + return GST_FLOW_OK; +} + +static gboolean +gst_deinterlace2_setcaps (GstPad * pad, GstCaps * caps) +{ + gboolean res = TRUE; + + GstDeinterlace2 *object = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); + + GstPad *otherpad; + + GstStructure *structure; + + GstVideoFormat fmt; + + guint32 fourcc; + + otherpad = (pad == object->srcpad) ? object->sinkpad : object->srcpad; + + if (!gst_pad_accept_caps (otherpad, caps) + || !gst_pad_set_caps (otherpad, caps)) + goto caps_not_accepted; + + structure = gst_caps_get_structure (caps, 0); + + res = gst_structure_get_int (structure, "width", &object->frame_width); + res &= gst_structure_get_int (structure, "height", &object->frame_height); + res &= + gst_structure_get_fraction (structure, "framerate", &object->frame_rate_n, + &object->frame_rate_d); + res &= gst_structure_get_fourcc (structure, "format", &fourcc); + /* TODO: get interlaced, field_layout, field_order */ + if (!res) + goto invalid_caps; + + /* TODO: introduce object->field_stride */ + object->field_height = object->frame_height / 2; + + fmt = gst_video_format_from_fourcc (fourcc); + + /* TODO: only true if fields are subbuffers of interlaced frames, + change when the buffer-fields concept has landed */ + object->field_stride = + gst_video_format_get_row_stride (fmt, 0, object->frame_width) * 2; + object->output_stride = + gst_video_format_get_row_stride (fmt, 0, object->frame_width); + + /* in bytes */ + object->line_length = + gst_video_format_get_row_stride (fmt, 0, object->frame_width); + object->frame_size = + gst_video_format_get_size (fmt, object->frame_width, + object->frame_height); + + GST_DEBUG_OBJECT (object, "Set caps: %" GST_PTR_FORMAT, caps); + +done: + + gst_object_unref (object); + return res; + +invalid_caps: + res = FALSE; + GST_ERROR_OBJECT (object, "Invalid caps: %" GST_PTR_FORMAT, caps); + goto done; + +caps_not_accepted: + res = FALSE; + GST_ERROR_OBJECT (object, "Caps not accepted: %" GST_PTR_FORMAT, caps); + goto done; +} + +static gboolean +gst_deinterlace2_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + + GstDeinterlace2 *object = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); + + GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_EOS: + case GST_EVENT_NEWSEGMENT: + /* TODO: reset history */ + + /* fall through */ + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (object); + return res; +} + +static GstStateChangeReturn +gst_deinterlace2_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* TODO: reset history, clean up, etc */ + break; + case GST_STATE_CHANGE_READY_TO_NULL: + default: + break; + } + + return ret; +} + +static gboolean +gst_deinterlace2_src_event (GstPad * pad, GstEvent * event) +{ + GstDeinterlace2 *object = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); + + gboolean res; + + GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (object); + + return res; +} + +static gboolean +gst_deinterlace2_src_query (GstPad * pad, GstQuery * query) +{ + GstDeinterlace2 *object = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); + + gboolean res = FALSE; + + GST_LOG_OBJECT (object, "%s query", GST_QUERY_TYPE_NAME (query)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + { + GstClockTime min, max; + + gboolean live; + + GstPad *peer; + + if ((peer = gst_pad_get_peer (object->sinkpad))) { + if ((res = gst_pad_query (peer, query))) { + GstClockTime latency; + + gst_query_parse_latency (query, &live, &min, &max); + + GST_DEBUG ("Peer latency: min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + /* TODO: calculate our own latency from framerate + * and object->method->fields_required */ + /* add our own latency */ + + latency = + gst_util_uint64_scale (object->method->fields_required * + GST_SECOND, object->frame_rate_d, object->frame_rate_n); + + GST_DEBUG ("Our latency: min %" GST_TIME_FORMAT + ", max %" GST_TIME_FORMAT, + GST_TIME_ARGS (latency), GST_TIME_ARGS (latency)); + + min += latency; + if (max != GST_CLOCK_TIME_NONE) + max += latency; + else + max = latency; + + GST_DEBUG ("Calculated total latency : min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + gst_query_set_latency (query, live, min, max); + } + gst_object_unref (peer); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (object); + return res; +} + +static const GstQueryType * +gst_deinterlace2_src_query_types (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_LATENCY, + GST_QUERY_NONE + }; + return types; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (deinterlace2_debug, "deinterlace2", 0, + "Deinterlacer"); + + oil_init (); + + if (!gst_element_register (plugin, "deinterlace2", GST_RANK_NONE, + GST_TYPE_DEINTERLACE2)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "deinterlace2", + "Deinterlacer", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN); -- cgit v1.2.1