2 * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * EffecTV - Realtime Digital Video Effector
5 * Copyright (C) 2001-2006 FUKUCHI Kentaro
7 * RippleTV - Water ripple effect.
8 * Copyright (C) 2001-2002 FUKUCHI Kentaro
10 * This combines the RippleTV and BaltanTV effects, which are
11 * very similar. BaltanTV is used if the feedback property is set
12 * to TRUE, otherwise RippleTV is used.
14 * EffecTV is free software. This library is free software;
15 * you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
25 * You should have received a copy of the GNU Library General Public
26 * License along with this library; if not, write to the
27 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
32 * SECTION:element-rippletv
34 * RippleTV does ripple mark effect on the video input. The ripple is caused
35 * by motion or random rain drops.
38 * <title>Example launch line</title>
40 * gst-launch-1.0 -v videotestsrc ! rippletv ! videoconvert ! autovideosink
41 * ]| This pipeline shows the effect of rippletv on a test stream.
52 #include "gstripple.h"
53 #include "gsteffectv.h"
55 #define DEFAULT_MODE 0
64 static gint sqrtable[256];
66 #define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type())
68 gst_rippletv_mode_get_type (void)
70 static GType type = 0;
72 static const GEnumValue enumvalue[] = {
73 {0, "Motion Detection", "motion-detection"},
79 type = g_enum_register_static ("GstRippleTVMode", enumvalue);
84 #define gst_rippletv_parent_class parent_class
85 G_DEFINE_TYPE (GstRippleTV, gst_rippletv, GST_TYPE_VIDEO_FILTER);
87 static GstStaticPadTemplate gst_rippletv_src_template =
88 GST_STATIC_PAD_TEMPLATE ("src",
91 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
94 static GstStaticPadTemplate gst_rippletv_sink_template =
95 GST_STATIC_PAD_TEMPLATE ("sink",
98 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
101 static const gint point = 16;
102 static const gint impact = 2;
103 static const gint decay = 8;
104 static const gint loopnum = 2;
111 for (i = 0; i < 128; i++) {
114 for (i = 1; i <= 128; i++) {
115 sqrtable[256 - i] = -i * i;
120 image_bgset_y (guint32 * src, gint16 * background, gint video_area)
129 for (i = 0; i < video_area; i++) {
130 R = ((*p) & 0xff0000) >> (16 - 1);
131 G = ((*p) & 0xff00) >> (8 - 2);
133 *q = (gint16) (R + G + B);
140 setBackground (GstRippleTV * filter, guint32 * src)
144 info = &GST_VIDEO_FILTER (filter)->in_info;
146 image_bgset_y (src, filter->background,
147 GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info));
148 filter->bg_is_set = TRUE;
154 image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff,
167 for (i = 0; i < video_area; i++) {
168 R = ((*p) & 0xff0000) >> (16 - 1);
169 G = ((*p) & 0xff00) >> (8 - 2);
171 v = (R + G + B) - (gint) (*q);
172 *q = (gint16) (R + G + B);
173 *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24);
182 motiondetect (GstRippleTV * filter, guint32 * src)
184 guint8 *diff = filter->diff;
190 info = &GST_VIDEO_FILTER (filter)->in_info;
192 width = GST_VIDEO_INFO_WIDTH (info);
193 height = GST_VIDEO_INFO_HEIGHT (info);
195 if (!filter->bg_is_set)
196 setBackground (filter, src);
198 image_bgsubtract_update_y (src, filter->background, filter->diff,
200 p = filter->map1 + filter->map_w + 1;
201 q = filter->map2 + filter->map_w + 1;
204 for (y = filter->map_h - 2; y > 0; y--) {
205 for (x = filter->map_w - 2; x > 0; x--) {
206 h = (gint) * diff + (gint) * (diff + 1) + (gint) * (diff + width) +
207 (gint) * (diff + width + 1);
209 *p = h << (point + impact - 8);
223 drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h)
228 x = fastrand () % (map_w - 4) + 2;
229 y = fastrand () % (map_h - 4) + 2;
230 p = map1 + y * map_w + x;
231 q = map2 + y * map_w + x;
234 *(p - map_w) = *(p - 1) = *(p + 1) = *(p + map_w) = power / 2;
235 *(p - map_w - 1) = *(p - map_w + 1) = *(p + map_w - 1) = *(p + map_w + 1) =
237 *(q - map_w) = *(q - 1) = *(q + 1) = *(q + map_w) = power / 2;
238 *(q - map_w - 1) = *(q - map_w + 1) = *(q + map_w - 1) = *(p + map_w + 1) =
243 raindrop (GstRippleTV * filter)
247 if (filter->period == 0) {
248 switch (filter->rain_stat) {
250 filter->period = (fastrand () >> 23) + 100;
251 filter->drop_prob = 0;
252 filter->drop_prob_increment = 0x00ffffff / filter->period;
253 filter->drop_power = (-(fastrand () >> 28) - 2) << point;
254 filter->drops_per_frame_max = 2 << (fastrand () >> 30); // 2,4,8 or 16
255 filter->rain_stat = 1;
258 filter->drop_prob = 0x00ffffff;
259 filter->drops_per_frame = 1;
260 filter->drop_prob_increment = 1;
261 filter->period = (filter->drops_per_frame_max - 1) * 16;
262 filter->rain_stat = 2;
265 filter->period = (fastrand () >> 22) + 1000;
266 filter->drop_prob_increment = 0;
267 filter->rain_stat = 3;
270 filter->period = (filter->drops_per_frame_max - 1) * 16;
271 filter->drop_prob_increment = -1;
272 filter->rain_stat = 4;
275 filter->period = (fastrand () >> 24) + 60;
276 filter->drop_prob_increment = -(filter->drop_prob / filter->period);
277 filter->rain_stat = 5;
281 filter->period = (fastrand () >> 23) + 500;
282 filter->drop_prob = 0;
283 filter->rain_stat = 0;
287 switch (filter->rain_stat) {
293 if ((fastrand () >> 8) < filter->drop_prob) {
294 drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
297 filter->drop_prob += filter->drop_prob_increment;
302 for (i = filter->drops_per_frame / 16; i > 0; i--) {
303 drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
306 filter->drops_per_frame += filter->drop_prob_increment;
313 gst_rippletv_transform_frame (GstVideoFilter * vfilter,
314 GstVideoFrame * in_frame, GstVideoFrame * out_frame)
316 GstRippleTV *filter = GST_RIPPLETV (vfilter);
321 gint m_w, m_h, v_w, v_h;
324 GstClockTime timestamp, stream_time;
326 timestamp = GST_BUFFER_TIMESTAMP (in_frame->buffer);
328 gst_segment_to_stream_time (&GST_BASE_TRANSFORM (vfilter)->segment,
329 GST_FORMAT_TIME, timestamp);
331 GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
332 GST_TIME_ARGS (timestamp));
334 if (GST_CLOCK_TIME_IS_VALID (stream_time))
335 gst_object_sync_values (GST_OBJECT (filter), stream_time);
337 src = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
338 dest = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
340 GST_OBJECT_LOCK (filter);
341 /* impact from the motion or rain drop */
345 motiondetect (filter, src);
349 v_w = GST_VIDEO_FRAME_WIDTH (in_frame);
350 v_h = GST_VIDEO_FRAME_HEIGHT (in_frame);
352 /* simulate surface wave */
354 /* This function is called only 30 times per second. To increase a speed
355 * of wave, iterates this loop several times. */
356 for (i = loopnum; i > 0; i--) {
357 /* wave simulation */
358 p = filter->map1 + m_w + 1;
359 q = filter->map2 + m_w + 1;
360 r = filter->map3 + m_w + 1;
361 for (y = m_h - 2; y > 0; y--) {
362 for (x = m_w - 2; x > 0; x--) {
363 h = *(p - m_w - 1) + *(p - m_w + 1) + *(p + m_w - 1) + *(p + m_w + 1)
364 + *(p - m_w) + *(p - 1) + *(p + 1) + *(p + m_w) - (*p) * 9;
367 v += h - (v >> decay);
378 /* low pass filter */
379 p = filter->map3 + m_w + 1;
380 q = filter->map2 + m_w + 1;
381 for (y = m_h - 2; y > 0; y--) {
382 for (x = m_w - 2; x > 0; x--) {
383 h = *(p - m_w) + *(p - 1) + *(p + 1) + *(p + m_w) + (*p) * 60;
393 filter->map1 = filter->map2;
399 for (y = m_h - 1; y > 0; y--) {
400 for (x = m_w - 1; x > 0; x--) {
401 /* difference of the height between two voxel. They are twiced to
402 * emphasise the wave. */
403 vp[0] = sqrtable[((p[0] - p[1]) >> (point - 1)) & 0xff];
404 vp[1] = sqrtable[((p[0] - p[m_w]) >> (point - 1)) & 0xff];
414 /* draw refracted image. The vector table is stretched. */
415 for (y = 0; y < v_h; y += 2) {
416 for (x = 0; x < v_w; x += 2) {
421 dx = CLAMP (dx, 0, (v_w - 2));
422 dy = CLAMP (dy, 0, (v_h - 2));
423 dest[0] = src[dy * v_w + dx];
427 dx = x + 1 + (h + (gint) vp[2]) / 2;
428 dx = CLAMP (dx, 0, (v_w - 2));
429 dest[1] = src[dy * v_w + dx];
431 dy = y + 1 + (v + (gint) vp[m_w * 2 + 1]) / 2;
432 dy = CLAMP (dy, 0, (v_h - 2));
433 dest[v_w] = src[dy * v_w + o_dx];
435 dest[v_w + 1] = src[dy * v_w + dx];
442 GST_OBJECT_UNLOCK (filter);
448 gst_rippletv_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
449 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
451 GstRippleTV *filter = GST_RIPPLETV (vfilter);
454 width = GST_VIDEO_INFO_WIDTH (in_info);
455 height = GST_VIDEO_INFO_HEIGHT (in_info);
457 GST_OBJECT_LOCK (filter);
458 filter->map_h = height / 2 + 1;
459 filter->map_w = width / 2 + 1;
461 /* we over allocate the buffers, as the render code does not handle clipping
463 g_free (filter->map);
464 filter->map = g_new0 (gint, (1 + filter->map_h) * filter->map_w * 3);
466 filter->map1 = filter->map;
467 filter->map2 = filter->map + filter->map_w * filter->map_h;
468 filter->map3 = filter->map + filter->map_w * filter->map_h * 2;
470 g_free (filter->vtable);
471 filter->vtable = g_new0 (gint8, (1 + filter->map_h) * filter->map_w * 2);
473 g_free (filter->background);
474 filter->background = g_new0 (gint16, width * (height + 1));
476 g_free (filter->diff);
477 filter->diff = g_new0 (guint8, width * (height + 1));
478 GST_OBJECT_UNLOCK (filter);
484 gst_rippletv_start (GstBaseTransform * trans)
486 GstRippleTV *filter = GST_RIPPLETV (trans);
488 filter->bg_is_set = FALSE;
491 filter->rain_stat = 0;
492 filter->drop_prob = 0;
493 filter->drop_prob_increment = 0;
494 filter->drops_per_frame_max = 0;
495 filter->drops_per_frame = 0;
496 filter->drop_power = 0;
502 gst_rippletv_finalize (GObject * object)
504 GstRippleTV *filter = GST_RIPPLETV (object);
506 g_free (filter->map);
509 g_free (filter->vtable);
510 filter->vtable = NULL;
512 g_free (filter->background);
513 filter->background = NULL;
515 g_free (filter->diff);
518 G_OBJECT_CLASS (parent_class)->finalize (object);
522 gst_rippletv_set_property (GObject * object, guint prop_id,
523 const GValue * value, GParamSpec * pspec)
525 GstRippleTV *filter = GST_RIPPLETV (object);
527 GST_OBJECT_LOCK (filter);
530 memset (filter->map, 0,
531 filter->map_h * filter->map_w * 2 * sizeof (gint));
535 filter->mode = g_value_get_enum (value);
538 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
541 GST_OBJECT_UNLOCK (filter);
545 gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value,
548 GstRippleTV *filter = GST_RIPPLETV (object);
552 g_value_set_enum (value, filter->mode);
555 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
561 gst_rippletv_class_init (GstRippleTVClass * klass)
563 GObjectClass *gobject_class = (GObjectClass *) klass;
564 GstElementClass *gstelement_class = (GstElementClass *) klass;
565 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
566 GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
568 gobject_class->set_property = gst_rippletv_set_property;
569 gobject_class->get_property = gst_rippletv_get_property;
571 gobject_class->finalize = gst_rippletv_finalize;
573 g_object_class_install_property (gobject_class, PROP_RESET,
574 g_param_spec_boolean ("reset", "Reset",
575 "Reset all current ripples", FALSE,
576 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
578 g_object_class_install_property (gobject_class, PROP_MODE,
579 g_param_spec_enum ("mode", "Mode",
580 "Mode", GST_TYPE_RIPPLETV_MODE, DEFAULT_MODE,
581 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
583 gst_element_class_set_static_metadata (gstelement_class, "RippleTV effect",
584 "Filter/Effect/Video",
585 "RippleTV does ripple mark effect on the video input",
586 "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
587 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
589 gst_element_class_add_static_pad_template (gstelement_class,
590 &gst_rippletv_sink_template);
591 gst_element_class_add_static_pad_template (gstelement_class,
592 &gst_rippletv_src_template);
594 trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start);
596 vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_rippletv_set_info);
597 vfilter_class->transform_frame =
598 GST_DEBUG_FUNCPTR (gst_rippletv_transform_frame);
604 gst_rippletv_init (GstRippleTV * filter)
606 filter->mode = DEFAULT_MODE;
608 /* FIXME: remove this when memory corruption after resizes are fixed */
609 gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SRC_PAD (filter));
610 gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SINK_PAD (filter));