| /* GStreamer |
| * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk> |
| * |
| * EffecTV - Realtime Digital Video Effector |
| * Copyright (C) 2001-2006 FUKUCHI Kentaro |
| * |
| * StreakTV - afterimage effector. |
| * Copyright (C) 2001-2002 FUKUCHI Kentaro |
| * |
| * This combines the StreakTV and BaltanTV effects, which are |
| * very similar. BaltanTV is used if the feedback property is set |
| * to TRUE, otherwise StreakTV is used. |
| * |
| * EffecTV is free software. 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. |
| */ |
| |
| /** |
| * SECTION:element-streaktv |
| * |
| * StreakTV makes after images of moving objects. |
| * |
| * <refsect2> |
| * <title>Example launch line</title> |
| * |[ |
| * gst-launch-1.0 -v videotestsrc ! streaktv ! videoconvert ! autovideosink |
| * ]| This pipeline shows the effect of streaktv on a test stream. |
| * </refsect2> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <math.h> |
| #include <string.h> |
| |
| #include "gststreak.h" |
| #include "gsteffectv.h" |
| |
| #define DEFAULT_FEEDBACK FALSE |
| |
| enum |
| { |
| PROP_0, |
| PROP_FEEDBACK |
| }; |
| |
| #define gst_streaktv_parent_class parent_class |
| G_DEFINE_TYPE (GstStreakTV, gst_streaktv, GST_TYPE_VIDEO_FILTER); |
| |
| static GstStaticPadTemplate gst_streaktv_src_template = |
| GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }")) |
| ); |
| |
| static GstStaticPadTemplate gst_streaktv_sink_template = |
| GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }")) |
| ); |
| |
| |
| static GstFlowReturn |
| gst_streaktv_transform_frame (GstVideoFilter * vfilter, |
| GstVideoFrame * in_frame, GstVideoFrame * out_frame) |
| { |
| GstStreakTV *filter = GST_STREAKTV (vfilter); |
| guint32 *src, *dest; |
| gint i, cf; |
| gint video_area, width, height; |
| guint32 **planetable = filter->planetable; |
| gint plane = filter->plane; |
| guint stride_mask, stride_shift, stride; |
| |
| src = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0); |
| dest = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0); |
| |
| width = GST_VIDEO_FRAME_WIDTH (in_frame); |
| height = GST_VIDEO_FRAME_HEIGHT (in_frame); |
| |
| video_area = width * height; |
| |
| GST_OBJECT_LOCK (filter); |
| if (filter->feedback) { |
| stride_mask = 0xfcfcfcfc; |
| stride = 8; |
| stride_shift = 2; |
| } else { |
| stride_mask = 0xf8f8f8f8; |
| stride = 4; |
| stride_shift = 3; |
| } |
| |
| for (i = 0; i < video_area; i++) { |
| planetable[plane][i] = (src[i] & stride_mask) >> stride_shift; |
| } |
| |
| cf = plane & (stride - 1); |
| if (filter->feedback) { |
| for (i = 0; i < video_area; i++) { |
| dest[i] = planetable[cf][i] |
| + planetable[cf + stride][i] |
| + planetable[cf + stride * 2][i] |
| + planetable[cf + stride * 3][i]; |
| planetable[plane][i] = (dest[i] & stride_mask) >> stride_shift; |
| } |
| } else { |
| for (i = 0; i < video_area; i++) { |
| dest[i] = planetable[cf][i] |
| + planetable[cf + stride][i] |
| + planetable[cf + stride * 2][i] |
| + planetable[cf + stride * 3][i] |
| + planetable[cf + stride * 4][i] |
| + planetable[cf + stride * 5][i] |
| + planetable[cf + stride * 6][i] |
| + planetable[cf + stride * 7][i]; |
| } |
| } |
| |
| plane++; |
| filter->plane = plane & (PLANES - 1); |
| GST_OBJECT_UNLOCK (filter); |
| |
| return GST_FLOW_OK; |
| } |
| |
| static gboolean |
| gst_streaktv_set_info (GstVideoFilter * vfilter, GstCaps * incaps, |
| GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info) |
| { |
| GstStreakTV *filter = GST_STREAKTV (vfilter); |
| gint i, width, height; |
| |
| width = GST_VIDEO_INFO_WIDTH (in_info); |
| height = GST_VIDEO_INFO_HEIGHT (in_info); |
| |
| g_free (filter->planebuffer); |
| |
| filter->planebuffer = g_new0 (guint32, width * height * 4 * PLANES); |
| |
| for (i = 0; i < PLANES; i++) |
| filter->planetable[i] = &filter->planebuffer[width * height * i]; |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gst_streaktv_start (GstBaseTransform * trans) |
| { |
| GstStreakTV *filter = GST_STREAKTV (trans); |
| |
| filter->plane = 0; |
| |
| return TRUE; |
| } |
| |
| static void |
| gst_streaktv_finalize (GObject * object) |
| { |
| GstStreakTV *filter = GST_STREAKTV (object); |
| |
| if (filter->planebuffer) { |
| g_free (filter->planebuffer); |
| filter->planebuffer = NULL; |
| } |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static void |
| gst_streaktv_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstStreakTV *filter = GST_STREAKTV (object); |
| |
| switch (prop_id) { |
| case PROP_FEEDBACK: |
| if (G_UNLIKELY (GST_STATE (filter) >= GST_STATE_PAUSED)) { |
| g_warning ("Changing the \"feedback\" property only allowed " |
| "in state < PLAYING"); |
| return; |
| } |
| |
| filter->feedback = g_value_get_boolean (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_streaktv_get_property (GObject * object, guint prop_id, GValue * value, |
| GParamSpec * pspec) |
| { |
| GstStreakTV *filter = GST_STREAKTV (object); |
| |
| switch (prop_id) { |
| case PROP_FEEDBACK: |
| g_value_set_boolean (value, filter->feedback); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_streaktv_class_init (GstStreakTVClass * klass) |
| { |
| GObjectClass *gobject_class = (GObjectClass *) klass; |
| GstElementClass *gstelement_class = (GstElementClass *) klass; |
| GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass; |
| GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass; |
| |
| gobject_class->set_property = gst_streaktv_set_property; |
| gobject_class->get_property = gst_streaktv_get_property; |
| |
| gobject_class->finalize = gst_streaktv_finalize; |
| |
| g_object_class_install_property (gobject_class, PROP_FEEDBACK, |
| g_param_spec_boolean ("feedback", "Feedback", |
| "Feedback", DEFAULT_FEEDBACK, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| gst_element_class_set_static_metadata (gstelement_class, "StreakTV effect", |
| "Filter/Effect/Video", |
| "StreakTV makes after images of moving objects", |
| "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, " |
| "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); |
| |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&gst_streaktv_sink_template)); |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&gst_streaktv_src_template)); |
| |
| trans_class->start = GST_DEBUG_FUNCPTR (gst_streaktv_start); |
| |
| vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_streaktv_set_info); |
| vfilter_class->transform_frame = |
| GST_DEBUG_FUNCPTR (gst_streaktv_transform_frame); |
| } |
| |
| static void |
| gst_streaktv_init (GstStreakTV * filter) |
| { |
| filter->feedback = DEFAULT_FEEDBACK; |
| } |