update for meta api change
[platform/upstream/gst-plugins-good.git] / gst / effectv / gstripple.c
1 /* GStreamer
2  * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *
4  * EffecTV - Realtime Digital Video Effector
5  * Copyright (C) 2001-2006 FUKUCHI Kentaro
6  *
7  * RippleTV - Water ripple effect.
8  * Copyright (C) 2001-2002 FUKUCHI Kentaro
9  *
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.
13  *
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.
19  *
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.
24  *
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.
29  */
30
31 /**
32  * SECTION:element-rippletv
33  *
34  * RippleTV does ripple mark effect on the video input. The ripple is caused
35  * by motion or random rain drops.
36  *
37  * <refsect2>
38  * <title>Example launch line</title>
39  * |[
40  * gst-launch -v videotestsrc ! rippletv ! videoconvert ! autovideosink
41  * ]| This pipeline shows the effect of rippletv on a test stream.
42  * </refsect2>
43  */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <math.h>
50 #include <string.h>
51
52 #include "gstripple.h"
53 #include "gsteffectv.h"
54
55 #include <gst/controller/gstcontroller.h>
56
57 #define DEFAULT_MODE 0
58
59 enum
60 {
61   PROP_0,
62   PROP_RESET,
63   PROP_MODE
64 };
65
66 static gint sqrtable[256];
67
68 #define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type())
69 static GType
70 gst_rippletv_mode_get_type (void)
71 {
72   static GType type = 0;
73
74   static const GEnumValue enumvalue[] = {
75     {0, "Motion Detection", "motion-detection"},
76     {1, "Rain", "rain"},
77     {0, NULL, NULL},
78   };
79
80   if (!type) {
81     type = g_enum_register_static ("GstRippleTVMode", enumvalue);
82   }
83   return type;
84 }
85
86 #define gst_rippletv_parent_class parent_class
87 G_DEFINE_TYPE (GstRippleTV, gst_rippletv, GST_TYPE_VIDEO_FILTER);
88
89 static GstStaticPadTemplate gst_rippletv_src_template =
90 GST_STATIC_PAD_TEMPLATE ("src",
91     GST_PAD_SRC,
92     GST_PAD_ALWAYS,
93     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
94     );
95
96 static GstStaticPadTemplate gst_rippletv_sink_template =
97 GST_STATIC_PAD_TEMPLATE ("sink",
98     GST_PAD_SINK,
99     GST_PAD_ALWAYS,
100     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
101     );
102
103 static const gint point = 16;
104 static const gint impact = 2;
105 static const gint decay = 8;
106 static const gint loopnum = 2;
107
108 static void
109 setTable (void)
110 {
111   gint i;
112
113   for (i = 0; i < 128; i++) {
114     sqrtable[i] = i * i;
115   }
116   for (i = 1; i <= 128; i++) {
117     sqrtable[256 - i] = -i * i;
118   }
119 }
120
121 static void
122 image_bgset_y (guint32 * src, gint16 * background, gint video_area)
123 {
124   gint i;
125   gint R, G, B;
126   guint32 *p;
127   gint16 *q;
128
129   p = src;
130   q = background;
131   for (i = 0; i < video_area; i++) {
132     R = ((*p) & 0xff0000) >> (16 - 1);
133     G = ((*p) & 0xff00) >> (8 - 2);
134     B = (*p) & 0xff;
135     *q = (gint16) (R + G + B);
136     p++;
137     q++;
138   }
139 }
140
141 static gint
142 setBackground (GstRippleTV * filter, guint32 * src)
143 {
144   image_bgset_y (src, filter->background,
145       GST_VIDEO_INFO_WIDTH (&filter->info) *
146       GST_VIDEO_INFO_HEIGHT (&filter->info));
147   filter->bg_is_set = TRUE;
148
149   return 0;
150 }
151
152 static void
153 image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff,
154     gint video_area)
155 {
156   gint i;
157   gint R, G, B;
158   guint32 *p;
159   gint16 *q;
160   guint8 *r;
161   gint v;
162
163   p = src;
164   q = background;
165   r = diff;
166   for (i = 0; i < video_area; i++) {
167     R = ((*p) & 0xff0000) >> (16 - 1);
168     G = ((*p) & 0xff00) >> (8 - 2);
169     B = (*p) & 0xff;
170     v = (R + G + B) - (gint) (*q);
171     *q = (gint16) (R + G + B);
172     *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24);
173
174     p++;
175     q++;
176     r++;
177   }
178 }
179
180 static void
181 motiondetect (GstRippleTV * filter, guint32 * src)
182 {
183   guint8 *diff = filter->diff;
184   gint width, height;
185   gint *p, *q;
186   gint x, y, h;
187
188   width = GST_VIDEO_INFO_WIDTH (&filter->info);
189   height = GST_VIDEO_INFO_HEIGHT (&filter->info);
190
191   if (!filter->bg_is_set)
192     setBackground (filter, src);
193
194   image_bgsubtract_update_y (src, filter->background, filter->diff,
195       width * height);
196   p = filter->map1 + filter->map_w + 1;
197   q = filter->map2 + filter->map_w + 1;
198   diff += width + 2;
199
200   for (y = filter->map_h - 2; y > 0; y--) {
201     for (x = filter->map_w - 2; x > 0; x--) {
202       h = (gint) * diff + (gint) * (diff + 1) + (gint) * (diff + width) +
203           (gint) * (diff + width + 1);
204       if (h > 0) {
205         *p = h << (point + impact - 8);
206         *q = *p;
207       }
208       p++;
209       q++;
210       diff += 2;
211     }
212     diff += width + 2;
213     p += 2;
214     q += 2;
215   }
216 }
217
218 static inline void
219 drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h)
220 {
221   gint x, y;
222   gint *p, *q;
223
224   x = fastrand () % (map_w - 4) + 2;
225   y = fastrand () % (map_h - 4) + 2;
226   p = map1 + y * map_w + x;
227   q = map2 + y * map_w + x;
228   *p = power;
229   *q = power;
230   *(p - map_w) = *(p - 1) = *(p + 1) = *(p + map_w) = power / 2;
231   *(p - map_w - 1) = *(p - map_w + 1) = *(p + map_w - 1) = *(p + map_w + 1) =
232       power / 4;
233   *(q - map_w) = *(q - 1) = *(q + 1) = *(q + map_w) = power / 2;
234   *(q - map_w - 1) = *(q - map_w + 1) = *(q + map_w - 1) = *(p + map_w + 1) =
235       power / 4;
236 }
237
238 static void
239 raindrop (GstRippleTV * filter)
240 {
241   gint i;
242
243   if (filter->period == 0) {
244     switch (filter->rain_stat) {
245       case 0:
246         filter->period = (fastrand () >> 23) + 100;
247         filter->drop_prob = 0;
248         filter->drop_prob_increment = 0x00ffffff / filter->period;
249         filter->drop_power = (-(fastrand () >> 28) - 2) << point;
250         filter->drops_per_frame_max = 2 << (fastrand () >> 30); // 2,4,8 or 16
251         filter->rain_stat = 1;
252         break;
253       case 1:
254         filter->drop_prob = 0x00ffffff;
255         filter->drops_per_frame = 1;
256         filter->drop_prob_increment = 1;
257         filter->period = (filter->drops_per_frame_max - 1) * 16;
258         filter->rain_stat = 2;
259         break;
260       case 2:
261         filter->period = (fastrand () >> 22) + 1000;
262         filter->drop_prob_increment = 0;
263         filter->rain_stat = 3;
264         break;
265       case 3:
266         filter->period = (filter->drops_per_frame_max - 1) * 16;
267         filter->drop_prob_increment = -1;
268         filter->rain_stat = 4;
269         break;
270       case 4:
271         filter->period = (fastrand () >> 24) + 60;
272         filter->drop_prob_increment = -(filter->drop_prob / filter->period);
273         filter->rain_stat = 5;
274         break;
275       case 5:
276       default:
277         filter->period = (fastrand () >> 23) + 500;
278         filter->drop_prob = 0;
279         filter->rain_stat = 0;
280         break;
281     }
282   }
283   switch (filter->rain_stat) {
284     default:
285     case 0:
286       break;
287     case 1:
288     case 5:
289       if ((fastrand () >> 8) < filter->drop_prob) {
290         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
291             filter->map_h);
292       }
293       filter->drop_prob += filter->drop_prob_increment;
294       break;
295     case 2:
296     case 3:
297     case 4:
298       for (i = filter->drops_per_frame / 16; i > 0; i--) {
299         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
300             filter->map_h);
301       }
302       filter->drops_per_frame += filter->drop_prob_increment;
303       break;
304   }
305   filter->period--;
306 }
307
308 static GstFlowReturn
309 gst_rippletv_transform (GstBaseTransform * trans, GstBuffer * in,
310     GstBuffer * out)
311 {
312   GstRippleTV *filter = GST_RIPPLETV (trans);
313   guint32 *src, *dest;
314   GstVideoFrame in_frame, out_frame;
315   gint x, y, i;
316   gint dx, dy;
317   gint h, v;
318   gint width, height;
319   gint *p, *q, *r;
320   gint8 *vp;
321   GstClockTime timestamp, stream_time;
322
323   timestamp = GST_BUFFER_TIMESTAMP (in);
324   stream_time =
325       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
326
327   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
328       GST_TIME_ARGS (timestamp));
329
330   if (GST_CLOCK_TIME_IS_VALID (stream_time))
331     gst_object_sync_values (G_OBJECT (filter), stream_time);
332
333   if (!gst_video_frame_map (&in_frame, &filter->info, in, GST_MAP_READ))
334     goto invalid_in;
335
336   if (!gst_video_frame_map (&out_frame, &filter->info, out, GST_MAP_WRITE))
337     goto invalid_out;
338
339   src = GST_VIDEO_FRAME_PLANE_DATA (&in_frame, 0);
340   dest = GST_VIDEO_FRAME_PLANE_DATA (&out_frame, 0);
341
342   GST_OBJECT_LOCK (filter);
343   /* impact from the motion or rain drop */
344   if (filter->mode)
345     raindrop (filter);
346   else
347     motiondetect (filter, src);
348
349   /* simulate surface wave */
350   width = filter->map_w;
351   height = filter->map_h;
352
353   /* This function is called only 30 times per second. To increase a speed
354    * of wave, iterates this loop several times. */
355   for (i = loopnum; i > 0; i--) {
356     /* wave simulation */
357     p = filter->map1 + width + 1;
358     q = filter->map2 + width + 1;
359     r = filter->map3 + width + 1;
360     for (y = height - 2; y > 0; y--) {
361       for (x = width - 2; x > 0; x--) {
362         h = *(p - width - 1) + *(p - width + 1) + *(p + width - 1) + *(p +
363             width + 1)
364             + *(p - width) + *(p - 1) + *(p + 1) + *(p + width) - (*p) * 9;
365         h = h >> 3;
366         v = *p - *q;
367         v += h - (v >> decay);
368         *r = v + *p;
369         p++;
370         q++;
371         r++;
372       }
373       p += 2;
374       q += 2;
375       r += 2;
376     }
377
378     /* low pass filter */
379     p = filter->map3 + width + 1;
380     q = filter->map2 + width + 1;
381     for (y = height - 2; y > 0; y--) {
382       for (x = width - 2; x > 0; x--) {
383         h = *(p - width) + *(p - 1) + *(p + 1) + *(p + width) + (*p) * 60;
384         *q = h >> 6;
385         p++;
386         q++;
387       }
388       p += 2;
389       q += 2;
390     }
391
392     p = filter->map1;
393     filter->map1 = filter->map2;
394     filter->map2 = p;
395   }
396
397   vp = filter->vtable;
398   p = filter->map1;
399   for (y = height - 1; y > 0; y--) {
400     for (x = width - 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[width]) >> (point - 1)) & 0xff];
405       p++;
406       vp += 2;
407     }
408     p++;
409     vp += 2;
410   }
411
412   width = GST_VIDEO_FRAME_WIDTH (&in_frame);
413   height = GST_VIDEO_FRAME_HEIGHT (&in_frame);
414   vp = filter->vtable;
415
416   /* draw refracted image. The vector table is stretched. */
417   for (y = 0; y < height; y += 2) {
418     for (x = 0; x < width; x += 2) {
419       h = (gint) vp[0];
420       v = (gint) vp[1];
421       dx = x + h;
422       dy = y + v;
423       if (dx < 0)
424         dx = 0;
425       if (dy < 0)
426         dy = 0;
427       if (dx >= width)
428         dx = width - 1;
429       if (dy >= height)
430         dy = height - 1;
431       dest[0] = src[dy * width + dx];
432
433       i = dx;
434
435       dx = x + 1 + (h + (gint) vp[2]) / 2;
436       if (dx < 0)
437         dx = 0;
438       if (dx >= width)
439         dx = width - 1;
440       dest[1] = src[dy * width + dx];
441
442       dy = y + 1 + (v + (gint) vp[filter->map_w * 2 + 1]) / 2;
443       if (dy < 0)
444         dy = 0;
445       if (dy >= height)
446         dy = height - 1;
447       dest[width] = src[dy * width + i];
448
449       dest[width + 1] = src[dy * width + dx];
450       dest += 2;
451       vp += 2;
452     }
453     dest += width;
454     vp += 2;
455   }
456   GST_OBJECT_UNLOCK (filter);
457
458   gst_video_frame_unmap (&in_frame);
459   gst_video_frame_unmap (&out_frame);
460
461   return GST_FLOW_OK;
462
463   /* ERRORS */
464 invalid_in:
465   {
466     GST_DEBUG_OBJECT (filter, "invalid input frame");
467     return GST_FLOW_ERROR;
468   }
469 invalid_out:
470   {
471     GST_DEBUG_OBJECT (filter, "invalid output frame");
472     gst_video_frame_unmap (&in_frame);
473     return GST_FLOW_ERROR;
474   }
475
476 }
477
478 static gboolean
479 gst_rippletv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
480     GstCaps * outcaps)
481 {
482   GstRippleTV *filter = GST_RIPPLETV (btrans);
483   GstVideoInfo info;
484   gint width, height;
485
486   if (!gst_video_info_from_caps (&info, incaps))
487     goto invalid_caps;
488
489   filter->info = info;
490
491   width = GST_VIDEO_INFO_WIDTH (&info);
492   height = GST_VIDEO_INFO_HEIGHT (&info);
493
494   GST_OBJECT_LOCK (filter);
495   filter->map_h = height / 2 + 1;
496   filter->map_w = width / 2 + 1;
497
498   if (filter->map)
499     g_free (filter->map);
500   filter->map = g_new0 (gint, filter->map_h * filter->map_w * 3);
501
502   filter->map1 = filter->map;
503   filter->map2 = filter->map + filter->map_w * filter->map_h;
504   filter->map3 = filter->map + filter->map_w * filter->map_h * 2;
505
506   if (filter->vtable)
507     g_free (filter->vtable);
508   filter->vtable = g_new0 (gint8, filter->map_h * filter->map_w * 2);
509
510   if (filter->background)
511     g_free (filter->background);
512   filter->background = g_new0 (gint16, width * height);
513
514   if (filter->diff)
515     g_free (filter->diff);
516   filter->diff = g_new0 (guint8, width * height);
517   GST_OBJECT_UNLOCK (filter);
518
519   return TRUE;
520
521   /* ERRORS */
522 invalid_caps:
523   {
524     GST_DEBUG_OBJECT (filter, "invalid caps received");
525     return FALSE;
526   }
527 }
528
529 static gboolean
530 gst_rippletv_start (GstBaseTransform * trans)
531 {
532   GstRippleTV *filter = GST_RIPPLETV (trans);
533
534   filter->bg_is_set = FALSE;
535
536   filter->period = 0;
537   filter->rain_stat = 0;
538   filter->drop_prob = 0;
539   filter->drop_prob_increment = 0;
540   filter->drops_per_frame_max = 0;
541   filter->drops_per_frame = 0;
542   filter->drop_power = 0;
543
544   return TRUE;
545 }
546
547 static void
548 gst_rippletv_finalize (GObject * object)
549 {
550   GstRippleTV *filter = GST_RIPPLETV (object);
551
552   if (filter->map)
553     g_free (filter->map);
554   filter->map = NULL;
555
556   if (filter->vtable)
557     g_free (filter->vtable);
558   filter->vtable = NULL;
559
560   if (filter->background)
561     g_free (filter->background);
562   filter->background = NULL;
563
564   if (filter->diff)
565     g_free (filter->diff);
566   filter->diff = NULL;
567
568   G_OBJECT_CLASS (parent_class)->finalize (object);
569 }
570
571 static void
572 gst_rippletv_set_property (GObject * object, guint prop_id,
573     const GValue * value, GParamSpec * pspec)
574 {
575   GstRippleTV *filter = GST_RIPPLETV (object);
576
577   GST_OBJECT_LOCK (filter);
578   switch (prop_id) {
579     case PROP_RESET:{
580       memset (filter->map, 0,
581           filter->map_h * filter->map_w * 2 * sizeof (gint));
582       break;
583     }
584     case PROP_MODE:
585       filter->mode = g_value_get_enum (value);
586       break;
587     default:
588       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
589       break;
590   }
591   GST_OBJECT_UNLOCK (filter);
592 }
593
594 static void
595 gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value,
596     GParamSpec * pspec)
597 {
598   GstRippleTV *filter = GST_RIPPLETV (object);
599
600   switch (prop_id) {
601     case PROP_MODE:
602       g_value_set_enum (value, filter->mode);
603       break;
604     default:
605       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
606       break;
607   }
608 }
609
610 static void
611 gst_rippletv_class_init (GstRippleTVClass * klass)
612 {
613   GObjectClass *gobject_class = (GObjectClass *) klass;
614   GstElementClass *gstelement_class = (GstElementClass *) klass;
615   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
616
617   gobject_class->set_property = gst_rippletv_set_property;
618   gobject_class->get_property = gst_rippletv_get_property;
619
620   gobject_class->finalize = gst_rippletv_finalize;
621
622   g_object_class_install_property (gobject_class, PROP_RESET,
623       g_param_spec_boolean ("reset", "Reset",
624           "Reset all current ripples", FALSE,
625           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
626
627   g_object_class_install_property (gobject_class, PROP_MODE,
628       g_param_spec_enum ("mode", "Mode",
629           "Mode", GST_TYPE_RIPPLETV_MODE, DEFAULT_MODE,
630           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
631
632   gst_element_class_set_details_simple (gstelement_class, "RippleTV effect",
633       "Filter/Effect/Video",
634       "RippleTV does ripple mark effect on the video input",
635       "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
636       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
637
638   gst_element_class_add_pad_template (gstelement_class,
639       gst_static_pad_template_get (&gst_rippletv_sink_template));
640   gst_element_class_add_pad_template (gstelement_class,
641       gst_static_pad_template_get (&gst_rippletv_src_template));
642
643   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_rippletv_set_caps);
644   trans_class->transform = GST_DEBUG_FUNCPTR (gst_rippletv_transform);
645   trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start);
646
647   setTable ();
648 }
649
650 static void
651 gst_rippletv_init (GstRippleTV * filter)
652 {
653   filter->mode = DEFAULT_MODE;
654
655   gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SRC_PAD (filter));
656   gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SINK_PAD (filter));
657 }