| /* GStreamer |
| * Copyright (C) 2013 David Schleef <ds@schleef.org> |
| * Copyright (C) 2013 Rdio, Inc. <ingestions@rd.io> |
| * |
| * 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. |
| * |
| * <refsect2> |
| * <title>Example launch line</title> |
| * |[ |
| * gst-launch-1.0 -v videotestsrc pattern=ball ! interlace ! yadif ! xvimagesink |
| * ]| |
| * This pipeline creates an interlaced test pattern, and then deinterlaces |
| * it using the yadif filter. |
| * </refsect2> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/gst.h> |
| #include <gst/base/gstbasetransform.h> |
| #include <gst/video/video.h> |
| #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 gboolean gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps, |
| GstCaps * outcaps); |
| 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 GstFlowReturn gst_yadif_transform (GstBaseTransform * trans, |
| GstBuffer * inbuf, GstBuffer * outbuf); |
| |
| enum |
| { |
| PROP_0, |
| PROP_MODE |
| }; |
| |
| #define DEFAULT_MODE GST_DEINTERLACE_MODE_AUTO |
| |
| /* 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") |
| ); |
| |
| #define GST_TYPE_DEINTERLACE_MODES (gst_deinterlace_modes_get_type ()) |
| static GType |
| gst_deinterlace_modes_get_type (void) |
| { |
| static GType deinterlace_modes_type = 0; |
| |
| static const GEnumValue modes_types[] = { |
| {GST_DEINTERLACE_MODE_AUTO, "Auto detection", "auto"}, |
| {GST_DEINTERLACE_MODE_INTERLACED, "Force deinterlacing", "interlaced"}, |
| {GST_DEINTERLACE_MODE_DISABLED, "Run in passthrough mode", "disabled"}, |
| {0, NULL, NULL}, |
| }; |
| |
| if (!deinterlace_modes_type) { |
| deinterlace_modes_type = |
| g_enum_register_static ("GstYadifModes", modes_types); |
| } |
| return deinterlace_modes_type; |
| } |
| |
| |
| /* 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 <ds@schleef.org>"); |
| |
| 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); |
| base_transform_class->set_caps = GST_DEBUG_FUNCPTR (gst_yadif_set_caps); |
| 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); |
| base_transform_class->transform = GST_DEBUG_FUNCPTR (gst_yadif_transform); |
| |
| g_object_class_install_property (gobject_class, PROP_MODE, |
| g_param_spec_enum ("mode", "Deinterlace Mode", |
| "Deinterlace mode", |
| GST_TYPE_DEINTERLACE_MODES, |
| DEFAULT_MODE, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); |
| |
| } |
| |
| static void |
| gst_yadif_init (GstYadif * yadif) |
| { |
| } |
| |
| void |
| gst_yadif_set_property (GObject * object, guint property_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstYadif *yadif = GST_YADIF (object); |
| |
| switch (property_id) { |
| case PROP_MODE: |
| yadif->mode = g_value_get_enum (value); |
| break; |
| 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) { |
| case PROP_MODE: |
| g_value_set_enum (value, yadif->mode); |
| break; |
| 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_unset (&value); |
| g_value_unset (&v); |
| } else { |
| gst_caps_set_simple (othercaps, "interlace-mode", G_TYPE_STRING, |
| "progressive", NULL); |
| } |
| |
| return othercaps; |
| } |
| |
| 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_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; |
| } |
| |
| 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 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) |