[v4l2videodecoder] Post message for number of buffers
[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., 51 Franklin St, Fifth Floor,
28  * Boston, MA 02110-1301, 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-1.0 -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 #define DEFAULT_MODE 0
56
57 enum
58 {
59   PROP_0,
60   PROP_RESET,
61   PROP_MODE
62 };
63
64 static gint sqrtable[256];
65
66 #define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type())
67 static GType
68 gst_rippletv_mode_get_type (void)
69 {
70   static GType type = 0;
71
72   static const GEnumValue enumvalue[] = {
73     {0, "Motion Detection", "motion-detection"},
74     {1, "Rain", "rain"},
75     {0, NULL, NULL},
76   };
77
78   if (!type) {
79     type = g_enum_register_static ("GstRippleTVMode", enumvalue);
80   }
81   return type;
82 }
83
84 #define gst_rippletv_parent_class parent_class
85 G_DEFINE_TYPE (GstRippleTV, gst_rippletv, GST_TYPE_VIDEO_FILTER);
86
87 static GstStaticPadTemplate gst_rippletv_src_template =
88 GST_STATIC_PAD_TEMPLATE ("src",
89     GST_PAD_SRC,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
92     );
93
94 static GstStaticPadTemplate gst_rippletv_sink_template =
95 GST_STATIC_PAD_TEMPLATE ("sink",
96     GST_PAD_SINK,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
99     );
100
101 static const gint point = 16;
102 static const gint impact = 2;
103 static const gint decay = 8;
104 static const gint loopnum = 2;
105
106 static void
107 setTable (void)
108 {
109   gint i;
110
111   for (i = 0; i < 128; i++) {
112     sqrtable[i] = i * i;
113   }
114   for (i = 1; i <= 128; i++) {
115     sqrtable[256 - i] = -i * i;
116   }
117 }
118
119 static void
120 image_bgset_y (guint32 * src, gint16 * background, gint video_area)
121 {
122   gint i;
123   gint R, G, B;
124   guint32 *p;
125   gint16 *q;
126
127   p = src;
128   q = background;
129   for (i = 0; i < video_area; i++) {
130     R = ((*p) & 0xff0000) >> (16 - 1);
131     G = ((*p) & 0xff00) >> (8 - 2);
132     B = (*p) & 0xff;
133     *q = (gint16) (R + G + B);
134     p++;
135     q++;
136   }
137 }
138
139 static gint
140 setBackground (GstRippleTV * filter, guint32 * src)
141 {
142   GstVideoInfo *info;
143
144   info = &GST_VIDEO_FILTER (filter)->in_info;
145
146   image_bgset_y (src, filter->background,
147       GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info));
148   filter->bg_is_set = TRUE;
149
150   return 0;
151 }
152
153 static void
154 image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff,
155     gint video_area)
156 {
157   gint i;
158   gint R, G, B;
159   guint32 *p;
160   gint16 *q;
161   guint8 *r;
162   gint v;
163
164   p = src;
165   q = background;
166   r = diff;
167   for (i = 0; i < video_area; i++) {
168     R = ((*p) & 0xff0000) >> (16 - 1);
169     G = ((*p) & 0xff00) >> (8 - 2);
170     B = (*p) & 0xff;
171     v = (R + G + B) - (gint) (*q);
172     *q = (gint16) (R + G + B);
173     *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24);
174
175     p++;
176     q++;
177     r++;
178   }
179 }
180
181 static void
182 motiondetect (GstRippleTV * filter, guint32 * src)
183 {
184   guint8 *diff = filter->diff;
185   gint width, height;
186   gint *p, *q;
187   gint x, y, h;
188   GstVideoInfo *info;
189
190   info = &GST_VIDEO_FILTER (filter)->in_info;
191
192   width = GST_VIDEO_INFO_WIDTH (info);
193   height = GST_VIDEO_INFO_HEIGHT (info);
194
195   if (!filter->bg_is_set)
196     setBackground (filter, src);
197
198   image_bgsubtract_update_y (src, filter->background, filter->diff,
199       width * height);
200   p = filter->map1 + filter->map_w + 1;
201   q = filter->map2 + filter->map_w + 1;
202   diff += width + 2;
203
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);
208       if (h > 0) {
209         *p = h << (point + impact - 8);
210         *q = *p;
211       }
212       p++;
213       q++;
214       diff += 2;
215     }
216     diff += width + 2;
217     p += 2;
218     q += 2;
219   }
220 }
221
222 static inline void
223 drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h)
224 {
225   gint x, y;
226   gint *p, *q;
227
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;
232   *p = power;
233   *q = power;
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) =
236       power / 4;
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) =
239       power / 4;
240 }
241
242 static void
243 raindrop (GstRippleTV * filter)
244 {
245   gint i;
246
247   if (filter->period == 0) {
248     switch (filter->rain_stat) {
249       case 0:
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;
256         break;
257       case 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;
263         break;
264       case 2:
265         filter->period = (fastrand () >> 22) + 1000;
266         filter->drop_prob_increment = 0;
267         filter->rain_stat = 3;
268         break;
269       case 3:
270         filter->period = (filter->drops_per_frame_max - 1) * 16;
271         filter->drop_prob_increment = -1;
272         filter->rain_stat = 4;
273         break;
274       case 4:
275         filter->period = (fastrand () >> 24) + 60;
276         filter->drop_prob_increment = -(filter->drop_prob / filter->period);
277         filter->rain_stat = 5;
278         break;
279       case 5:
280       default:
281         filter->period = (fastrand () >> 23) + 500;
282         filter->drop_prob = 0;
283         filter->rain_stat = 0;
284         break;
285     }
286   }
287   switch (filter->rain_stat) {
288     default:
289     case 0:
290       break;
291     case 1:
292     case 5:
293       if ((fastrand () >> 8) < filter->drop_prob) {
294         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
295             filter->map_h);
296       }
297       filter->drop_prob += filter->drop_prob_increment;
298       break;
299     case 2:
300     case 3:
301     case 4:
302       for (i = filter->drops_per_frame / 16; i > 0; i--) {
303         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
304             filter->map_h);
305       }
306       filter->drops_per_frame += filter->drop_prob_increment;
307       break;
308   }
309   filter->period--;
310 }
311
312 static GstFlowReturn
313 gst_rippletv_transform_frame (GstVideoFilter * vfilter,
314     GstVideoFrame * in_frame, GstVideoFrame * out_frame)
315 {
316   GstRippleTV *filter = GST_RIPPLETV (vfilter);
317   guint32 *src, *dest;
318   gint x, y, i;
319   gint dx, dy, o_dx;
320   gint h, v;
321   gint m_w, m_h, v_w, v_h;
322   gint *p, *q, *r;
323   gint8 *vp;
324   GstClockTime timestamp, stream_time;
325
326   timestamp = GST_BUFFER_TIMESTAMP (in_frame->buffer);
327   stream_time =
328       gst_segment_to_stream_time (&GST_BASE_TRANSFORM (vfilter)->segment,
329       GST_FORMAT_TIME, timestamp);
330
331   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
332       GST_TIME_ARGS (timestamp));
333
334   if (GST_CLOCK_TIME_IS_VALID (stream_time))
335     gst_object_sync_values (GST_OBJECT (filter), stream_time);
336
337   src = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
338   dest = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
339
340   GST_OBJECT_LOCK (filter);
341   /* impact from the motion or rain drop */
342   if (filter->mode)
343     raindrop (filter);
344   else
345     motiondetect (filter, src);
346
347   m_w = filter->map_w;
348   m_h = filter->map_h;
349   v_w = GST_VIDEO_FRAME_WIDTH (in_frame);
350   v_h = GST_VIDEO_FRAME_HEIGHT (in_frame);
351
352   /* simulate surface wave */
353
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;
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 + 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;
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 = 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];
405       p++;
406       vp += 2;
407     }
408     p++;
409     vp += 2;
410   }
411
412   vp = filter->vtable;
413
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) {
417       h = (gint) vp[0];
418       v = (gint) vp[1];
419       dx = x + h;
420       dy = y + v;
421       dx = CLAMP (dx, 0, (v_w - 2));
422       dy = CLAMP (dy, 0, (v_h - 2));
423       dest[0] = src[dy * v_w + dx];
424
425       o_dx = dx;
426
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];
430
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];
434
435       dest[v_w + 1] = src[dy * v_w + dx];
436       dest += 2;
437       vp += 2;
438     }
439     dest += v_w;
440     vp += 2;
441   }
442   GST_OBJECT_UNLOCK (filter);
443
444   return GST_FLOW_OK;
445 }
446
447 static gboolean
448 gst_rippletv_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
449     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
450 {
451   GstRippleTV *filter = GST_RIPPLETV (vfilter);
452   gint width, height;
453
454   width = GST_VIDEO_INFO_WIDTH (in_info);
455   height = GST_VIDEO_INFO_HEIGHT (in_info);
456
457   GST_OBJECT_LOCK (filter);
458   filter->map_h = height / 2 + 1;
459   filter->map_w = width / 2 + 1;
460
461   /* we over allocate the buffers, as the render code does not handle clipping
462    * very well */
463   g_free (filter->map);
464   filter->map = g_new0 (gint, (1 + filter->map_h) * filter->map_w * 3);
465
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;
469
470   g_free (filter->vtable);
471   filter->vtable = g_new0 (gint8, (1 + filter->map_h) * filter->map_w * 2);
472
473   g_free (filter->background);
474   filter->background = g_new0 (gint16, width * (height + 1));
475
476   g_free (filter->diff);
477   filter->diff = g_new0 (guint8, width * (height + 1));
478   GST_OBJECT_UNLOCK (filter);
479
480   return TRUE;
481 }
482
483 static gboolean
484 gst_rippletv_start (GstBaseTransform * trans)
485 {
486   GstRippleTV *filter = GST_RIPPLETV (trans);
487
488   filter->bg_is_set = FALSE;
489
490   filter->period = 0;
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;
497
498   return TRUE;
499 }
500
501 static void
502 gst_rippletv_finalize (GObject * object)
503 {
504   GstRippleTV *filter = GST_RIPPLETV (object);
505
506   g_free (filter->map);
507   filter->map = NULL;
508
509   g_free (filter->vtable);
510   filter->vtable = NULL;
511
512   g_free (filter->background);
513   filter->background = NULL;
514
515   g_free (filter->diff);
516   filter->diff = NULL;
517
518   G_OBJECT_CLASS (parent_class)->finalize (object);
519 }
520
521 static void
522 gst_rippletv_set_property (GObject * object, guint prop_id,
523     const GValue * value, GParamSpec * pspec)
524 {
525   GstRippleTV *filter = GST_RIPPLETV (object);
526
527   GST_OBJECT_LOCK (filter);
528   switch (prop_id) {
529     case PROP_RESET:{
530       memset (filter->map, 0,
531           filter->map_h * filter->map_w * 2 * sizeof (gint));
532       break;
533     }
534     case PROP_MODE:
535       filter->mode = g_value_get_enum (value);
536       break;
537     default:
538       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
539       break;
540   }
541   GST_OBJECT_UNLOCK (filter);
542 }
543
544 static void
545 gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value,
546     GParamSpec * pspec)
547 {
548   GstRippleTV *filter = GST_RIPPLETV (object);
549
550   switch (prop_id) {
551     case PROP_MODE:
552       g_value_set_enum (value, filter->mode);
553       break;
554     default:
555       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
556       break;
557   }
558 }
559
560 static void
561 gst_rippletv_class_init (GstRippleTVClass * klass)
562 {
563   GObjectClass *gobject_class = (GObjectClass *) klass;
564   GstElementClass *gstelement_class = (GstElementClass *) klass;
565   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
566   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
567
568   gobject_class->set_property = gst_rippletv_set_property;
569   gobject_class->get_property = gst_rippletv_get_property;
570
571   gobject_class->finalize = gst_rippletv_finalize;
572
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));
577
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));
582
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>");
588
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);
593
594   trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start);
595
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);
599
600   setTable ();
601 }
602
603 static void
604 gst_rippletv_init (GstRippleTV * filter)
605 {
606   filter->mode = DEFAULT_MODE;
607
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));
611 }