controller: port to new controller location and api
[platform/upstream/gstreamer.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., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, 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 -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 gboolean
91 gst_agingtv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
92     GstCaps * outcaps)
93 {
94   GstAgingTV *filter = GST_AGINGTV (btrans);
95   GstVideoInfo info;
96
97   if (!gst_video_info_from_caps (&info, incaps))
98     goto invalid_caps;
99
100   filter->info = info;
101
102   return TRUE;
103
104   /* ERRORS */
105 invalid_caps:
106   {
107     GST_ERROR_OBJECT (filter, "could not parse caps");
108     return GST_FLOW_ERROR;
109   }
110 }
111
112 static void
113 coloraging (guint32 * src, guint32 * dest, gint video_area, gint * c)
114 {
115   guint32 a, b;
116   gint i;
117   gint c_tmp = *c;
118
119   c_tmp -= (gint) (fastrand ()) >> 28;
120   if (c_tmp < 0)
121     c_tmp = 0;
122   if (c_tmp > 0x18)
123     c_tmp = 0x18;
124
125   for (i = 0; i < video_area; i++) {
126     a = *src++;
127     b = (a & 0xfcfcfc) >> 2;
128     *dest++ =
129         a - b + (c_tmp | (c_tmp << 8) | (c_tmp << 16)) +
130         ((fastrand () >> 8) & 0x101010);
131   }
132   *c = c_tmp;
133 }
134
135
136 static void
137 scratching (scratch * scratches, gint scratch_lines, guint32 * dest, gint width,
138     gint height)
139 {
140   gint i, y, y1, y2;
141   guint32 *p, a, b;
142   scratch *scratch;
143
144   for (i = 0; i < scratch_lines; i++) {
145     scratch = &scratches[i];
146
147     if (scratch->life) {
148       scratch->x = scratch->x + scratch->dx;
149
150       if (scratch->x < 0 || scratch->x > width * 256) {
151         scratch->life = 0;
152         break;
153       }
154       p = dest + (scratch->x >> 8);
155       if (scratch->init) {
156         y1 = scratch->init;
157         scratch->init = 0;
158       } else {
159         y1 = 0;
160       }
161       scratch->life--;
162       if (scratch->life) {
163         y2 = height;
164       } else {
165         y2 = fastrand () % height;
166       }
167       for (y = y1; y < y2; y++) {
168         a = *p & 0xfefeff;
169         a += 0x202020;
170         b = a & 0x1010100;
171         *p = a | (b - (b >> 8));
172         p += width;
173       }
174     } else {
175       if ((fastrand () & 0xf0000000) == 0) {
176         scratch->life = 2 + (fastrand () >> 27);
177         scratch->x = fastrand () % (width * 256);
178         scratch->dx = ((int) fastrand ()) >> 23;
179         scratch->init = (fastrand () % (height - 1)) + 1;
180       }
181     }
182   }
183 }
184
185 static void
186 dusts (guint32 * dest, gint width, gint height, gint * dust_interval,
187     gint area_scale)
188 {
189   gint i, j;
190   gint dnum;
191   gint d, len;
192   guint x, y;
193
194   if (*dust_interval == 0) {
195     if ((fastrand () & 0xf0000000) == 0) {
196       *dust_interval = fastrand () >> 29;
197     }
198     return;
199   }
200   dnum = area_scale * 4 + (fastrand () >> 27);
201
202   for (i = 0; i < dnum; i++) {
203     x = fastrand () % width;
204     y = fastrand () % height;
205     d = fastrand () >> 29;
206     len = fastrand () % area_scale + 5;
207     for (j = 0; j < len; j++) {
208       dest[y * width + x] = 0x101010;
209       y += dy[d];
210       x += dx[d];
211
212       if (y >= height || x >= width)
213         break;
214
215       d = (d + fastrand () % 3 - 1) & 7;
216     }
217   }
218   *dust_interval = *dust_interval - 1;
219 }
220
221 static void
222 pits (guint32 * dest, gint width, gint height, gint area_scale,
223     gint * pits_interval)
224 {
225   gint i, j;
226   gint pnum, size, pnumscale;
227   guint x, y;
228
229   pnumscale = area_scale * 2;
230   if (*pits_interval) {
231     pnum = pnumscale + (fastrand () % pnumscale);
232
233     *pits_interval = *pits_interval - 1;
234   } else {
235     pnum = fastrand () % pnumscale;
236
237     if ((fastrand () & 0xf8000000) == 0) {
238       *pits_interval = (fastrand () >> 28) + 20;
239     }
240   }
241   for (i = 0; i < pnum; i++) {
242     x = fastrand () % (width - 1);
243     y = fastrand () % (height - 1);
244
245     size = fastrand () >> 28;
246
247     for (j = 0; j < size; j++) {
248       x = x + fastrand () % 3 - 1;
249       y = y + fastrand () % 3 - 1;
250
251       if (y >= height || x >= width)
252         break;
253
254       dest[y * width + x] = 0xc0c0c0;
255     }
256   }
257 }
258
259 static void
260 gst_agingtv_get_property (GObject * object, guint prop_id,
261     GValue * value, GParamSpec * pspec)
262 {
263   GstAgingTV *agingtv = GST_AGINGTV (object);
264
265   GST_OBJECT_LOCK (agingtv);
266   switch (prop_id) {
267     case PROP_SCRATCH_LINES:
268       g_value_set_uint (value, agingtv->scratch_lines);
269       break;
270     case PROP_COLOR_AGING:
271       g_value_set_boolean (value, agingtv->color_aging);
272       break;
273     case PROP_PITS:
274       g_value_set_boolean (value, agingtv->pits);
275       break;
276     case PROP_DUSTS:
277       g_value_set_boolean (value, agingtv->dusts);
278       break;
279     default:
280       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
281   }
282   GST_OBJECT_UNLOCK (agingtv);
283 }
284
285 static void
286 gst_agingtv_set_property (GObject * object, guint prop_id,
287     const GValue * value, GParamSpec * pspec)
288 {
289   GstAgingTV *agingtv = GST_AGINGTV (object);
290
291   switch (prop_id) {
292     case PROP_SCRATCH_LINES:
293       agingtv->scratch_lines = g_value_get_uint (value);
294       break;
295     case PROP_COLOR_AGING:
296       agingtv->color_aging = g_value_get_boolean (value);
297       break;
298     case PROP_PITS:
299       agingtv->pits = g_value_get_boolean (value);
300       break;
301     case PROP_DUSTS:
302       agingtv->dusts = g_value_get_boolean (value);
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306   }
307 }
308
309 static gboolean
310 gst_agingtv_start (GstBaseTransform * trans)
311 {
312   GstAgingTV *agingtv = GST_AGINGTV (trans);
313
314   agingtv->coloraging_state = 0x18;
315   agingtv->dust_interval = 0;
316   agingtv->pits_interval = 0;
317
318   memset (agingtv->scratches, 0, sizeof (agingtv->scratches));
319
320   return TRUE;
321 }
322
323 static GstFlowReturn
324 gst_agingtv_transform (GstBaseTransform * trans, GstBuffer * in,
325     GstBuffer * out)
326 {
327   GstAgingTV *agingtv = GST_AGINGTV (trans);
328   GstVideoFrame in_frame, out_frame;
329   gint area_scale;
330   GstClockTime timestamp, stream_time;
331   gint width, height, stride, video_size;
332   guint32 *src, *dest;
333
334   timestamp = GST_BUFFER_TIMESTAMP (in);
335   stream_time =
336       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
337
338   GST_DEBUG_OBJECT (agingtv, "sync to %" GST_TIME_FORMAT,
339       GST_TIME_ARGS (timestamp));
340
341   if (GST_CLOCK_TIME_IS_VALID (stream_time))
342     gst_object_sync_values (GST_OBJECT (agingtv), stream_time);
343
344   if (!gst_video_frame_map (&in_frame, &agingtv->info, in, GST_MAP_READ))
345     goto invalid_in;
346
347   if (!gst_video_frame_map (&out_frame, &agingtv->info, out, GST_MAP_WRITE))
348     goto invalid_out;
349
350
351   width = GST_VIDEO_FRAME_WIDTH (&in_frame);
352   height = GST_VIDEO_FRAME_HEIGHT (&in_frame);
353   stride = GST_VIDEO_FRAME_PLANE_STRIDE (&in_frame, 0);
354   video_size = stride * height;
355
356   src = GST_VIDEO_FRAME_PLANE_DATA (&in_frame, 0);
357   dest = GST_VIDEO_FRAME_PLANE_DATA (&out_frame, 0);
358
359   area_scale = width * height / 64 / 480;
360   if (area_scale <= 0)
361     area_scale = 1;
362
363   if (agingtv->color_aging)
364     coloraging (src, dest, video_size, &agingtv->coloraging_state);
365   else
366     memcpy (dest, src, video_size);
367
368   scratching (agingtv->scratches, agingtv->scratch_lines, dest, width, height);
369   if (agingtv->pits)
370     pits (dest, width, height, area_scale, &agingtv->pits_interval);
371   if (area_scale > 1 && agingtv->dusts)
372     dusts (dest, width, height, &agingtv->dust_interval, area_scale);
373
374   gst_video_frame_unmap (&in_frame);
375   gst_video_frame_unmap (&out_frame);
376
377   return GST_FLOW_OK;
378
379   /* ERRORS */
380 invalid_in:
381   {
382     GST_DEBUG_OBJECT (agingtv, "invalid input frame");
383     return GST_FLOW_ERROR;
384   }
385 invalid_out:
386   {
387     GST_DEBUG_OBJECT (agingtv, "invalid output frame");
388     gst_video_frame_unmap (&in_frame);
389     return GST_FLOW_ERROR;
390   }
391 }
392
393 static void
394 gst_agingtv_class_init (GstAgingTVClass * klass)
395 {
396   GObjectClass *gobject_class = (GObjectClass *) klass;
397   GstElementClass *gstelement_class = (GstElementClass *) klass;
398   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
399
400   gobject_class->set_property = gst_agingtv_set_property;
401   gobject_class->get_property = gst_agingtv_get_property;
402
403   g_object_class_install_property (gobject_class, PROP_SCRATCH_LINES,
404       g_param_spec_uint ("scratch-lines", "Scratch Lines",
405           "Number of scratch lines", 0, SCRATCH_MAX, DEFAULT_SCRATCH_LINES,
406           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
407
408   g_object_class_install_property (gobject_class, PROP_COLOR_AGING,
409       g_param_spec_boolean ("color-aging", "Color Aging",
410           "Color Aging", DEFAULT_COLOR_AGING,
411           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
412
413   g_object_class_install_property (gobject_class, PROP_PITS,
414       g_param_spec_boolean ("pits", "Pits",
415           "Pits", DEFAULT_PITS,
416           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
417
418   g_object_class_install_property (gobject_class, PROP_DUSTS,
419       g_param_spec_boolean ("dusts", "Dusts",
420           "Dusts", DEFAULT_DUSTS,
421           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
422
423   gst_element_class_set_details_simple (gstelement_class, "AgingTV effect",
424       "Filter/Effect/Video",
425       "AgingTV adds age to video input using scratches and dust",
426       "Sam Lantinga <slouken@devolution.com>");
427
428   gst_element_class_add_pad_template (gstelement_class,
429       gst_static_pad_template_get (&gst_agingtv_sink_template));
430   gst_element_class_add_pad_template (gstelement_class,
431       gst_static_pad_template_get (&gst_agingtv_src_template));
432
433   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_agingtv_set_caps);
434   trans_class->transform = GST_DEBUG_FUNCPTR (gst_agingtv_transform);
435   trans_class->start = GST_DEBUG_FUNCPTR (gst_agingtv_start);
436 }
437
438 static void
439 gst_agingtv_init (GstAgingTV * agingtv)
440 {
441   agingtv->scratch_lines = DEFAULT_SCRATCH_LINES;
442   agingtv->color_aging = DEFAULT_COLOR_AGING;
443   agingtv->pits = DEFAULT_PITS;
444   agingtv->dusts = DEFAULT_DUSTS;
445 }