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