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., 59 Temple Place - Suite 330,
28 * Boston, MA 02111-1307, 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 -v videotestsrc ! rippletv ! ffmpegcolorspace ! autovideosink
41 * ]| This pipeline shows the effect of rippletv on a test stream.
52 #include "gstripple.h"
53 #include "gsteffectv.h"
55 #include <gst/video/video.h>
56 #include <gst/controller/gstcontroller.h>
58 #define DEFAULT_MODE 0
67 static gint sqrtable[256];
69 #define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type())
71 gst_rippletv_mode_get_type (void)
73 static GType type = 0;
75 static const GEnumValue enumvalue[] = {
76 {0, "Motion Detection", "motion-detection"},
82 type = g_enum_register_static ("GstRippleTVMode", enumvalue);
88 GST_BOILERPLATE (GstRippleTV, gst_rippletv, GstVideoFilter,
89 GST_TYPE_VIDEO_FILTER);
91 static GstStaticPadTemplate gst_rippletv_src_template =
92 GST_STATIC_PAD_TEMPLATE ("src",
95 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx ";"
96 GST_VIDEO_CAPS_xBGR "; " GST_VIDEO_CAPS_xRGB)
99 static GstStaticPadTemplate gst_rippletv_sink_template =
100 GST_STATIC_PAD_TEMPLATE ("sink",
103 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx ";"
104 GST_VIDEO_CAPS_xBGR "; " GST_VIDEO_CAPS_xRGB)
107 static const gint point = 16;
108 static const gint impact = 2;
109 static const gint decay = 8;
110 static const gint loopnum = 2;
117 for (i = 0; i < 128; i++) {
120 for (i = 1; i <= 128; i++) {
121 sqrtable[256 - i] = -i * i;
126 image_bgset_y (guint32 * src, gint16 * background, gint video_area)
135 for (i = 0; i < video_area; i++) {
136 R = ((*p) & 0xff0000) >> (16 - 1);
137 G = ((*p) & 0xff00) >> (8 - 2);
139 *q = (gint16) (R + G + B);
146 setBackground (GstRippleTV * filter, guint32 * src)
148 image_bgset_y (src, filter->background, filter->width * filter->height);
149 filter->bg_is_set = TRUE;
155 image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff,
168 for (i = 0; i < video_area; i++) {
169 R = ((*p) & 0xff0000) >> (16 - 1);
170 G = ((*p) & 0xff00) >> (8 - 2);
172 v = (R + G + B) - (gint) (*q);
173 *q = (gint16) (R + G + B);
174 *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24);
183 motiondetect (GstRippleTV * filter, guint32 * src)
185 guint8 *diff = filter->diff;
186 gint width = filter->width;
190 if (!filter->bg_is_set)
191 setBackground (filter, src);
193 image_bgsubtract_update_y (src, filter->background, filter->diff,
194 filter->width * filter->height);
195 p = filter->map1 + filter->map_w + 1;
196 q = filter->map2 + filter->map_w + 1;
197 diff += filter->width + 2;
199 for (y = filter->map_h - 2; y > 0; y--) {
200 for (x = filter->map_w - 2; x > 0; x--) {
201 h = (gint) * diff + (gint) * (diff + 1) + (gint) * (diff + width) +
202 (gint) * (diff + width + 1);
204 *p = h << (point + impact - 8);
218 drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h)
223 x = fastrand () % (map_w - 4) + 2;
224 y = fastrand () % (map_h - 4) + 2;
225 p = map1 + y * map_w + x;
226 q = map2 + y * map_w + x;
229 *(p - map_w) = *(p - 1) = *(p + 1) = *(p + map_w) = power / 2;
230 *(p - map_w - 1) = *(p - map_w + 1) = *(p + map_w - 1) = *(p + map_w + 1) =
232 *(q - map_w) = *(q - 1) = *(q + 1) = *(q + map_w) = power / 2;
233 *(q - map_w - 1) = *(q - map_w + 1) = *(q + map_w - 1) = *(p + map_w + 1) =
238 raindrop (GstRippleTV * filter)
242 if (filter->period == 0) {
243 switch (filter->rain_stat) {
245 filter->period = (fastrand () >> 23) + 100;
246 filter->drop_prob = 0;
247 filter->drop_prob_increment = 0x00ffffff / filter->period;
248 filter->drop_power = (-(fastrand () >> 28) - 2) << point;
249 filter->drops_per_frame_max = 2 << (fastrand () >> 30); // 2,4,8 or 16
250 filter->rain_stat = 1;
253 filter->drop_prob = 0x00ffffff;
254 filter->drops_per_frame = 1;
255 filter->drop_prob_increment = 1;
256 filter->period = (filter->drops_per_frame_max - 1) * 16;
257 filter->rain_stat = 2;
260 filter->period = (fastrand () >> 22) + 1000;
261 filter->drop_prob_increment = 0;
262 filter->rain_stat = 3;
265 filter->period = (filter->drops_per_frame_max - 1) * 16;
266 filter->drop_prob_increment = -1;
267 filter->rain_stat = 4;
270 filter->period = (fastrand () >> 24) + 60;
271 filter->drop_prob_increment = -(filter->drop_prob / filter->period);
272 filter->rain_stat = 5;
276 filter->period = (fastrand () >> 23) + 500;
277 filter->drop_prob = 0;
278 filter->rain_stat = 0;
282 switch (filter->rain_stat) {
288 if ((fastrand () >> 8) < filter->drop_prob) {
289 drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
292 filter->drop_prob += filter->drop_prob_increment;
297 for (i = filter->drops_per_frame / 16; i > 0; i--) {
298 drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
301 filter->drops_per_frame += filter->drop_prob_increment;
308 gst_rippletv_transform (GstBaseTransform * trans, GstBuffer * in,
311 GstRippleTV *filter = GST_RIPPLETV (trans);
313 GstFlowReturn ret = GST_FLOW_OK;
320 GstClockTime timestamp, stream_time;
322 timestamp = GST_BUFFER_TIMESTAMP (in);
324 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
326 GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
327 GST_TIME_ARGS (timestamp));
329 if (GST_CLOCK_TIME_IS_VALID (stream_time))
330 gst_object_sync_values (G_OBJECT (filter), stream_time);
332 src = (guint32 *) GST_BUFFER_DATA (in);
333 dest = (guint32 *) GST_BUFFER_DATA (out);
335 GST_OBJECT_LOCK (filter);
336 /* impact from the motion or rain drop */
340 motiondetect (filter, src);
342 /* simulate surface wave */
343 width = filter->map_w;
344 height = filter->map_h;
346 /* This function is called only 30 times per second. To increase a speed
347 * of wave, iterates this loop several times. */
348 for (i = loopnum; i > 0; i--) {
349 /* wave simulation */
350 p = filter->map1 + width + 1;
351 q = filter->map2 + width + 1;
352 r = filter->map3 + width + 1;
353 for (y = height - 2; y > 0; y--) {
354 for (x = width - 2; x > 0; x--) {
355 h = *(p - width - 1) + *(p - width + 1) + *(p + width - 1) + *(p +
357 + *(p - width) + *(p - 1) + *(p + 1) + *(p + width) - (*p) * 9;
360 v += h - (v >> decay);
371 /* low pass filter */
372 p = filter->map3 + width + 1;
373 q = filter->map2 + width + 1;
374 for (y = height - 2; y > 0; y--) {
375 for (x = width - 2; x > 0; x--) {
376 h = *(p - width) + *(p - 1) + *(p + 1) + *(p + width) + (*p) * 60;
386 filter->map1 = filter->map2;
392 for (y = height - 1; y > 0; y--) {
393 for (x = width - 1; x > 0; x--) {
394 /* difference of the height between two voxel. They are twiced to
395 * emphasise the wave. */
396 vp[0] = sqrtable[((p[0] - p[1]) >> (point - 1)) & 0xff];
397 vp[1] = sqrtable[((p[0] - p[width]) >> (point - 1)) & 0xff];
405 height = filter->height;
406 width = filter->width;
409 /* draw refracted image. The vector table is stretched. */
410 for (y = 0; y < height; y += 2) {
411 for (x = 0; x < width; x += 2) {
424 dest[0] = src[dy * width + dx];
428 dx = x + 1 + (h + (gint) vp[2]) / 2;
433 dest[1] = src[dy * width + dx];
435 dy = y + 1 + (v + (gint) vp[filter->map_w * 2 + 1]) / 2;
440 dest[width] = src[dy * width + i];
442 dest[width + 1] = src[dy * width + dx];
446 dest += filter->width;
449 GST_OBJECT_UNLOCK (filter);
455 gst_rippletv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
458 GstRippleTV *filter = GST_RIPPLETV (btrans);
459 GstStructure *structure;
460 gboolean ret = FALSE;
462 structure = gst_caps_get_structure (incaps, 0);
464 GST_OBJECT_LOCK (filter);
465 if (gst_structure_get_int (structure, "width", &filter->width) &&
466 gst_structure_get_int (structure, "height", &filter->height)) {
468 filter->map_h = filter->height / 2 + 1;
469 filter->map_w = filter->width / 2 + 1;
472 g_free (filter->map);
473 filter->map = g_new0 (gint, filter->map_h * filter->map_w * 3);
475 filter->map1 = filter->map;
476 filter->map2 = filter->map + filter->map_w * filter->map_h;
477 filter->map3 = filter->map + filter->map_w * filter->map_h * 2;
480 g_free (filter->vtable);
481 filter->vtable = g_new0 (gint8, filter->map_h * filter->map_w * 2);
483 if (filter->background)
484 g_free (filter->background);
485 filter->background = g_new0 (gint16, filter->width * filter->height);
488 g_free (filter->diff);
489 filter->diff = g_new0 (guint8, filter->width * filter->height);
493 GST_OBJECT_UNLOCK (filter);
499 gst_rippletv_start (GstBaseTransform * trans)
501 GstRippleTV *filter = GST_RIPPLETV (trans);
503 filter->bg_is_set = FALSE;
506 filter->rain_stat = 0;
507 filter->drop_prob = 0;
508 filter->drop_prob_increment = 0;
509 filter->drops_per_frame_max = 0;
510 filter->drops_per_frame = 0;
511 filter->drop_power = 0;
517 gst_rippletv_finalize (GObject * object)
519 GstRippleTV *filter = GST_RIPPLETV (object);
522 g_free (filter->map);
526 g_free (filter->vtable);
527 filter->vtable = NULL;
529 if (filter->background)
530 g_free (filter->background);
531 filter->background = NULL;
534 g_free (filter->diff);
537 G_OBJECT_CLASS (parent_class)->finalize (object);
541 gst_rippletv_set_property (GObject * object, guint prop_id,
542 const GValue * value, GParamSpec * pspec)
544 GstRippleTV *filter = GST_RIPPLETV (object);
546 GST_OBJECT_LOCK (filter);
549 memset (filter->map, 0,
550 filter->map_h * filter->map_w * 2 * sizeof (gint));
554 filter->mode = g_value_get_enum (value);
557 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560 GST_OBJECT_UNLOCK (filter);
564 gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value,
567 GstRippleTV *filter = GST_RIPPLETV (object);
571 g_value_set_enum (value, filter->mode);
574 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
580 gst_rippletv_base_init (gpointer g_class)
582 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
584 gst_element_class_set_details_simple (element_class, "RippleTV effect",
585 "Filter/Effect/Video",
586 "RippleTV does ripple mark effect on the video input",
587 "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
588 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
590 gst_element_class_add_pad_template (element_class,
591 gst_static_pad_template_get (&gst_rippletv_sink_template));
592 gst_element_class_add_pad_template (element_class,
593 gst_static_pad_template_get (&gst_rippletv_src_template));
597 gst_rippletv_class_init (GstRippleTVClass * klass)
599 GObjectClass *gobject_class = (GObjectClass *) klass;
600 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
602 gobject_class->set_property = gst_rippletv_set_property;
603 gobject_class->get_property = gst_rippletv_get_property;
605 gobject_class->finalize = gst_rippletv_finalize;
607 g_object_class_install_property (gobject_class, PROP_RESET,
608 g_param_spec_boolean ("reset", "Reset",
609 "Reset all current ripples", FALSE,
610 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
612 g_object_class_install_property (gobject_class, PROP_MODE,
613 g_param_spec_enum ("mode", "Mode",
614 "Mode", GST_TYPE_RIPPLETV_MODE, DEFAULT_MODE,
615 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
617 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_rippletv_set_caps);
618 trans_class->transform = GST_DEBUG_FUNCPTR (gst_rippletv_transform);
619 trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start);
625 gst_rippletv_init (GstRippleTV * filter, GstRippleTVClass * klass)
627 filter->mode = DEFAULT_MODE;
629 gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SRC_PAD (filter));
630 gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SINK_PAD (filter));