[v4l2videodecoder] Post message for number of buffers
[platform/upstream/gst-plugins-good.git] / gst / effectv / gstaging.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * EffecTV - Realtime Digital Video Effector
7  * Copyright (C) 2001-2002 FUKUCHI Kentarou
8  *
9  * AgingTV - film-aging effect.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26
27 /**
28  * SECTION:element-agingtv
29  *
30  * AgingTV ages a video stream in realtime, changes the colors and adds
31  * scratches and dust.
32  *
33  * <refsect2>
34  * <title>Example launch line</title>
35  * |[
36  * gst-launch-1.0 -v videotestsrc ! agingtv ! videoconvert ! autovideosink
37  * ]| This pipeline shows the effect of agingtv on a test stream.
38  * </refsect2>
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <string.h>
46 #include <math.h>
47
48 #include "gstaging.h"
49 #include "gsteffectv.h"
50
51 static const gint dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };
52 static const gint dy[8] = { 0, -1, -1, -1, 0, 1, 1, 1 };
53
54 enum
55 {
56   PROP_0 = 0,
57   PROP_SCRATCH_LINES,
58   PROP_COLOR_AGING,
59   PROP_PITS,
60   PROP_DUSTS
61 };
62
63 #define DEFAULT_SCRATCH_LINES 7
64 #define DEFAULT_COLOR_AGING TRUE
65 #define DEFAULT_PITS TRUE
66 #define DEFAULT_DUSTS TRUE
67
68 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
69 #define CAPS_STR GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx }")
70 #else
71 #define CAPS_STR GST_VIDEO_CAPS_MAKE ("{ xRGB, xBGR }")
72 #endif
73
74 static GstStaticPadTemplate gst_agingtv_src_template =
75 GST_STATIC_PAD_TEMPLATE ("src",
76     GST_PAD_SRC,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS (CAPS_STR)
79     );
80
81 static GstStaticPadTemplate gst_agingtv_sink_template =
82 GST_STATIC_PAD_TEMPLATE ("sink",
83     GST_PAD_SINK,
84     GST_PAD_ALWAYS,
85     GST_STATIC_CAPS (CAPS_STR)
86     );
87
88 G_DEFINE_TYPE (GstAgingTV, gst_agingtv, GST_TYPE_VIDEO_FILTER);
89
90 static void
91 coloraging (guint32 * src, guint32 * dest, gint video_area, gint * c)
92 {
93   guint32 a, b;
94   gint i;
95   gint c_tmp = *c;
96
97   c_tmp -= (gint) (fastrand ()) >> 28;
98   if (c_tmp < 0)
99     c_tmp = 0;
100   if (c_tmp > 0x18)
101     c_tmp = 0x18;
102
103   for (i = 0; i < video_area; i++) {
104     a = *src++;
105     b = (a & 0xfcfcfc) >> 2;
106     *dest++ =
107         a - b + (c_tmp | (c_tmp << 8) | (c_tmp << 16)) +
108         ((fastrand () >> 8) & 0x101010);
109   }
110   *c = c_tmp;
111 }
112
113
114 static void
115 scratching (scratch * scratches, gint scratch_lines, guint32 * dest, gint width,
116     gint height)
117 {
118   gint i, y, y1, y2;
119   guint32 *p, a, b;
120   scratch *scratch;
121
122   for (i = 0; i < scratch_lines; i++) {
123     scratch = &scratches[i];
124
125     if (scratch->life) {
126       scratch->x = scratch->x + scratch->dx;
127
128       if (scratch->x < 0 || scratch->x > width * 256) {
129         scratch->life = 0;
130         break;
131       }
132       p = dest + (scratch->x >> 8);
133       if (scratch->init) {
134         y1 = scratch->init;
135         scratch->init = 0;
136       } else {
137         y1 = 0;
138       }
139       scratch->life--;
140       if (scratch->life) {
141         y2 = height;
142       } else {
143         y2 = fastrand () % height;
144       }
145       for (y = y1; y < y2; y++) {
146         a = *p & 0xfefeff;
147         a += 0x202020;
148         b = a & 0x1010100;
149         *p = a | (b - (b >> 8));
150         p += width;
151       }
152     } else {
153       if ((fastrand () & 0xf0000000) == 0) {
154         scratch->life = 2 + (fastrand () >> 27);
155         scratch->x = fastrand () % (width * 256);
156         scratch->dx = ((int) fastrand ()) >> 23;
157         scratch->init = (fastrand () % (height - 1)) + 1;
158       }
159     }
160   }
161 }
162
163 static void
164 dusts (guint32 * dest, gint width, gint height, gint * dust_interval,
165     gint area_scale)
166 {
167   gint i, j;
168   gint dnum;
169   gint d, len;
170   guint x, y;
171
172   if (*dust_interval == 0) {
173     if ((fastrand () & 0xf0000000) == 0) {
174       *dust_interval = fastrand () >> 29;
175     }
176     return;
177   }
178   dnum = area_scale * 4 + (fastrand () >> 27);
179
180   for (i = 0; i < dnum; i++) {
181     x = fastrand () % width;
182     y = fastrand () % height;
183     d = fastrand () >> 29;
184     len = fastrand () % area_scale + 5;
185     for (j = 0; j < len; j++) {
186       dest[y * width + x] = 0x101010;
187       y += dy[d];
188       x += dx[d];
189
190       if (y >= height || x >= width)
191         break;
192
193       d = (d + fastrand () % 3 - 1) & 7;
194     }
195   }
196   *dust_interval = *dust_interval - 1;
197 }
198
199 static void
200 pits (guint32 * dest, gint width, gint height, gint area_scale,
201     gint * pits_interval)
202 {
203   gint i, j;
204   gint pnum, size, pnumscale;
205   guint x, y;
206
207   pnumscale = area_scale * 2;
208   if (*pits_interval) {
209     pnum = pnumscale + (fastrand () % pnumscale);
210
211     *pits_interval = *pits_interval - 1;
212   } else {
213     pnum = fastrand () % pnumscale;
214
215     if ((fastrand () & 0xf8000000) == 0) {
216       *pits_interval = (fastrand () >> 28) + 20;
217     }
218   }
219   for (i = 0; i < pnum; i++) {
220     x = fastrand () % (width - 1);
221     y = fastrand () % (height - 1);
222
223     size = fastrand () >> 28;
224
225     for (j = 0; j < size; j++) {
226       x = x + fastrand () % 3 - 1;
227       y = y + fastrand () % 3 - 1;
228
229       if (y >= height || x >= width)
230         break;
231
232       dest[y * width + x] = 0xc0c0c0;
233     }
234   }
235 }
236
237 static void
238 gst_agingtv_get_property (GObject * object, guint prop_id,
239     GValue * value, GParamSpec * pspec)
240 {
241   GstAgingTV *agingtv = GST_AGINGTV (object);
242
243   GST_OBJECT_LOCK (agingtv);
244   switch (prop_id) {
245     case PROP_SCRATCH_LINES:
246       g_value_set_uint (value, agingtv->scratch_lines);
247       break;
248     case PROP_COLOR_AGING:
249       g_value_set_boolean (value, agingtv->color_aging);
250       break;
251     case PROP_PITS:
252       g_value_set_boolean (value, agingtv->pits);
253       break;
254     case PROP_DUSTS:
255       g_value_set_boolean (value, agingtv->dusts);
256       break;
257     default:
258       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
259   }
260   GST_OBJECT_UNLOCK (agingtv);
261 }
262
263 static void
264 gst_agingtv_set_property (GObject * object, guint prop_id,
265     const GValue * value, GParamSpec * pspec)
266 {
267   GstAgingTV *agingtv = GST_AGINGTV (object);
268
269   switch (prop_id) {
270     case PROP_SCRATCH_LINES:
271       agingtv->scratch_lines = g_value_get_uint (value);
272       break;
273     case PROP_COLOR_AGING:
274       agingtv->color_aging = g_value_get_boolean (value);
275       break;
276     case PROP_PITS:
277       agingtv->pits = g_value_get_boolean (value);
278       break;
279     case PROP_DUSTS:
280       agingtv->dusts = g_value_get_boolean (value);
281       break;
282     default:
283       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
284   }
285 }
286
287 static gboolean
288 gst_agingtv_start (GstBaseTransform * trans)
289 {
290   GstAgingTV *agingtv = GST_AGINGTV (trans);
291
292   agingtv->coloraging_state = 0x18;
293   agingtv->dust_interval = 0;
294   agingtv->pits_interval = 0;
295
296   memset (agingtv->scratches, 0, sizeof (agingtv->scratches));
297
298   return TRUE;
299 }
300
301 static GstFlowReturn
302 gst_agingtv_transform_frame (GstVideoFilter * filter, GstVideoFrame * in_frame,
303     GstVideoFrame * out_frame)
304 {
305   GstAgingTV *agingtv = GST_AGINGTV (filter);
306   gint area_scale;
307   GstClockTime timestamp, stream_time;
308   gint width, height, stride, video_size;
309   guint32 *src, *dest;
310
311   timestamp = GST_BUFFER_TIMESTAMP (in_frame->buffer);
312   stream_time =
313       gst_segment_to_stream_time (&GST_BASE_TRANSFORM (filter)->segment,
314       GST_FORMAT_TIME, timestamp);
315
316   GST_DEBUG_OBJECT (agingtv, "sync to %" GST_TIME_FORMAT,
317       GST_TIME_ARGS (timestamp));
318
319   if (GST_CLOCK_TIME_IS_VALID (stream_time))
320     gst_object_sync_values (GST_OBJECT (agingtv), stream_time);
321
322   width = GST_VIDEO_FRAME_WIDTH (in_frame);
323   height = GST_VIDEO_FRAME_HEIGHT (in_frame);
324   stride = GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0);
325   video_size = stride * height / 4;
326
327   src = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
328   dest = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
329
330   area_scale = width * height / 64 / 480;
331   if (area_scale <= 0)
332     area_scale = 1;
333
334   if (agingtv->color_aging)
335     coloraging (src, dest, video_size, &agingtv->coloraging_state);
336   else
337     memcpy (dest, src, stride * height);
338
339   scratching (agingtv->scratches, agingtv->scratch_lines, dest, width, height);
340   if (agingtv->pits)
341     pits (dest, width, height, area_scale, &agingtv->pits_interval);
342   if (area_scale > 1 && agingtv->dusts)
343     dusts (dest, width, height, &agingtv->dust_interval, area_scale);
344
345   return GST_FLOW_OK;
346 }
347
348 static void
349 gst_agingtv_class_init (GstAgingTVClass * klass)
350 {
351   GObjectClass *gobject_class = (GObjectClass *) klass;
352   GstElementClass *gstelement_class = (GstElementClass *) klass;
353   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
354   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
355
356   gobject_class->set_property = gst_agingtv_set_property;
357   gobject_class->get_property = gst_agingtv_get_property;
358
359   g_object_class_install_property (gobject_class, PROP_SCRATCH_LINES,
360       g_param_spec_uint ("scratch-lines", "Scratch Lines",
361           "Number of scratch lines", 0, SCRATCH_MAX, DEFAULT_SCRATCH_LINES,
362           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
363
364   g_object_class_install_property (gobject_class, PROP_COLOR_AGING,
365       g_param_spec_boolean ("color-aging", "Color Aging",
366           "Color Aging", DEFAULT_COLOR_AGING,
367           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
368
369   g_object_class_install_property (gobject_class, PROP_PITS,
370       g_param_spec_boolean ("pits", "Pits",
371           "Pits", DEFAULT_PITS,
372           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
373
374   g_object_class_install_property (gobject_class, PROP_DUSTS,
375       g_param_spec_boolean ("dusts", "Dusts",
376           "Dusts", DEFAULT_DUSTS,
377           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
378
379   gst_element_class_set_static_metadata (gstelement_class, "AgingTV effect",
380       "Filter/Effect/Video",
381       "AgingTV adds age to video input using scratches and dust",
382       "Sam Lantinga <slouken@devolution.com>");
383
384   gst_element_class_add_static_pad_template (gstelement_class,
385       &gst_agingtv_sink_template);
386   gst_element_class_add_static_pad_template (gstelement_class,
387       &gst_agingtv_src_template);
388
389   trans_class->start = GST_DEBUG_FUNCPTR (gst_agingtv_start);
390
391   vfilter_class->transform_frame =
392       GST_DEBUG_FUNCPTR (gst_agingtv_transform_frame);
393 }
394
395 static void
396 gst_agingtv_init (GstAgingTV * agingtv)
397 {
398   agingtv->scratch_lines = DEFAULT_SCRATCH_LINES;
399   agingtv->color_aging = DEFAULT_COLOR_AGING;
400   agingtv->pits = DEFAULT_PITS;
401   agingtv->dusts = DEFAULT_DUSTS;
402 }