Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / gst / effectv / gstop.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  * OpTV - Optical art meets real-time video effect.
8  * Copyright (C) 2004-2005 FUKUCHI Kentaro
9  *
10  * EffecTV is free software. This library is free software;
11  * 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-optv
29  *
30  * Traditional black-white optical animation is now resurrected as a
31  * real-time video effect. Input images are binarized and combined with
32  * various optical pattern.
33  *
34  * <refsect2>
35  * <title>Example launch line</title>
36  * |[
37  * gst-launch -v videotestsrc ! optv ! videoconvert ! autovideosink
38  * ]| This pipeline shows the effect of optv on a test stream.
39  * </refsect2>
40  */
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <math.h>
47 #include <string.h>
48
49 #include "gstop.h"
50 #include "gsteffectv.h"
51
52 #include <gst/video/video.h>
53 #include <gst/controller/gstcontroller.h>
54
55 enum
56 {
57   OP_SPIRAL1 = 0,
58   OP_SPIRAL2,
59   OP_PARABOLA,
60   OP_HSTRIPE
61 };
62
63 #define GST_TYPE_OPTV_MODE (gst_optv_mode_get_type())
64 static GType
65 gst_optv_mode_get_type (void)
66 {
67   static GType type = 0;
68
69   static const GEnumValue enumvalue[] = {
70     {OP_SPIRAL1, "Maelstrom", "maelstrom"},
71     {OP_SPIRAL2, "Radiation", "radiation"},
72     {OP_PARABOLA, "Horizontal Stripes",
73         "horizontal-stripes"},
74     {OP_HSTRIPE, "Vertical Stripes", "vertical-stripes"},
75     {0, NULL, NULL},
76   };
77
78   if (!type) {
79     type = g_enum_register_static ("GstOpTVMode", enumvalue);
80   }
81   return type;
82 }
83
84 #define DEFAULT_MODE OP_SPIRAL1
85 #define DEFAULT_SPEED 16
86 #define DEFAULT_THRESHOLD 60
87
88 enum
89 {
90   PROP_0,
91   PROP_MODE,
92   PROP_SPEED,
93   PROP_THRESHOLD
94 };
95
96 static guint32 palette[256];
97
98 #define gst_optv_parent_class parent_class
99 G_DEFINE_TYPE (GstOpTV, gst_optv, GST_TYPE_VIDEO_FILTER);
100
101 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
102 #define CAPS_STR GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx }")
103 #else
104 #define CAPS_STR GST_VIDEO_CAPS_MAKE ("{ xBGR, xRGB }")
105 #endif
106
107 static GstStaticPadTemplate gst_optv_src_template =
108 GST_STATIC_PAD_TEMPLATE ("src",
109     GST_PAD_SRC,
110     GST_PAD_ALWAYS,
111     GST_STATIC_CAPS (CAPS_STR)
112     );
113
114 static GstStaticPadTemplate gst_optv_sink_template =
115 GST_STATIC_PAD_TEMPLATE ("sink",
116     GST_PAD_SINK,
117     GST_PAD_ALWAYS,
118     GST_STATIC_CAPS (CAPS_STR)
119     );
120
121 static void
122 initPalette (void)
123 {
124   gint i;
125   guint8 v;
126
127   for (i = 0; i < 112; i++) {
128     palette[i] = 0;
129     palette[i + 128] = 0xffffff;
130   }
131   for (i = 0; i < 16; i++) {
132     v = 16 * (i + 1) - 1;
133     palette[i + 112] = (v << 16) | (v << 8) | v;
134     v = 255 - v;
135     palette[i + 240] = (v << 16) | (v << 8) | v;
136   }
137 }
138
139 static void
140 setOpmap (gint8 * opmap[4], gint width, gint height)
141 {
142   gint i, j, x, y;
143 #ifndef PS2
144   gdouble xx, yy, r, at, rr;
145 #else
146   gfloat xx, yy, r, at, rr;
147 #endif
148   gint sci;
149
150   sci = 640 / width;
151   i = 0;
152   for (y = 0; y < height; y++) {
153     yy = (gdouble) (y - height / 2) / width;
154     for (x = 0; x < width; x++) {
155       xx = (gdouble) x / width - 0.5;
156 #ifndef PS2
157       r = sqrt (xx * xx + yy * yy);
158       at = atan2 (xx, yy);
159 #else
160       r = sqrtf (xx * xx + yy * yy);
161       at = atan2f (xx, yy);
162 #endif
163
164       opmap[OP_SPIRAL1][i] = ((guint)
165           ((at / G_PI * 256) + (r * 4000))) & 255;
166
167       j = r * 300 / 32;
168       rr = r * 300 - j * 32;
169       j *= 64;
170       j += (rr > 28) ? (rr - 28) * 16 : 0;
171       opmap[OP_SPIRAL2][i] = ((guint)
172           ((at / G_PI * 4096) + (r * 1600) - j)) & 255;
173
174       opmap[OP_PARABOLA][i] =
175           ((guint) (yy / (xx * xx * 0.3 + 0.1) * 400)) & 255;
176       opmap[OP_HSTRIPE][i] = x * 8 * sci;
177       i++;
178     }
179   }
180 }
181
182 /* Taken from effectv/image.c */
183 /* Y value filters */
184 static void
185 image_y_over (guint32 * src, guint8 * diff, gint y_threshold, gint video_area)
186 {
187   gint i;
188   gint R, G, B, v;
189   guint8 *p = diff;
190
191   for (i = video_area; i > 0; i--) {
192     R = ((*src) & 0xff0000) >> (16 - 1);
193     G = ((*src) & 0xff00) >> (8 - 2);
194     B = (*src) & 0xff;
195     v = y_threshold * 7 - (R + G + B);
196     *p = (guint8) (v >> 24);
197     src++;
198     p++;
199   }
200 }
201
202 static GstFlowReturn
203 gst_optv_transform (GstBaseTransform * trans, GstBuffer * in, GstBuffer * out)
204 {
205   GstOpTV *filter = GST_OPTV (trans);
206   guint32 *src, *dest;
207   GstVideoFrame in_frame, out_frame;
208   gint8 *p;
209   guint8 *diff;
210   gint x, y, width, height;
211   GstClockTime timestamp, stream_time;
212   guint8 phase;
213
214   timestamp = GST_BUFFER_TIMESTAMP (in);
215   stream_time =
216       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
217
218   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
219       GST_TIME_ARGS (timestamp));
220
221   if (GST_CLOCK_TIME_IS_VALID (stream_time))
222     gst_object_sync_values (G_OBJECT (filter), stream_time);
223
224   if (G_UNLIKELY (filter->opmap[0] == NULL))
225     return GST_FLOW_NOT_NEGOTIATED;
226
227   if (!gst_video_frame_map (&in_frame, &filter->info, in, GST_MAP_READ))
228     goto invalid_in;
229
230   if (!gst_video_frame_map (&out_frame, &filter->info, out, GST_MAP_WRITE))
231     goto invalid_out;
232
233   src = GST_VIDEO_FRAME_PLANE_DATA (&in_frame, 0);
234   dest = GST_VIDEO_FRAME_PLANE_DATA (&out_frame, 0);
235
236   width = GST_VIDEO_FRAME_WIDTH (&in_frame);
237   height = GST_VIDEO_FRAME_HEIGHT (&in_frame);
238
239   GST_OBJECT_LOCK (filter);
240   switch (filter->mode) {
241     default:
242     case 0:
243       p = filter->opmap[OP_SPIRAL1];
244       break;
245     case 1:
246       p = filter->opmap[OP_SPIRAL2];
247       break;
248     case 2:
249       p = filter->opmap[OP_PARABOLA];
250       break;
251     case 3:
252       p = filter->opmap[OP_HSTRIPE];
253       break;
254   }
255
256   filter->phase -= filter->speed;
257
258   diff = filter->diff;
259   image_y_over (src, diff, filter->threshold, width * height);
260   phase = filter->phase;
261
262   for (y = 0; y < height; y++) {
263     for (x = 0; x < width; x++) {
264       *dest++ = palette[(((guint8) (*p + phase)) ^ *diff++) & 255];
265       p++;
266     }
267   }
268   GST_OBJECT_UNLOCK (filter);
269
270   gst_video_frame_unmap (&in_frame);
271   gst_video_frame_unmap (&out_frame);
272
273   return GST_FLOW_OK;
274
275   /* ERRORS */
276 invalid_in:
277   {
278     GST_DEBUG_OBJECT (filter, "invalid input frame");
279     return GST_FLOW_ERROR;
280   }
281 invalid_out:
282   {
283     GST_DEBUG_OBJECT (filter, "invalid output frame");
284     gst_video_frame_unmap (&in_frame);
285     return GST_FLOW_ERROR;
286   }
287 }
288
289 static gboolean
290 gst_optv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
291     GstCaps * outcaps)
292 {
293   GstOpTV *filter = GST_OPTV (btrans);
294   GstVideoInfo info;
295   gint i, width, height;
296
297   if (!gst_video_info_from_caps (&info, incaps))
298     goto invalid_caps;
299
300   filter->info = info;
301
302   width = GST_VIDEO_INFO_WIDTH (&info);
303   height = GST_VIDEO_INFO_HEIGHT (&info);
304
305   for (i = 0; i < 4; i++) {
306     if (filter->opmap[i])
307       g_free (filter->opmap[i]);
308     filter->opmap[i] = g_new (gint8, width * height);
309   }
310   setOpmap (filter->opmap, width, height);
311
312   if (filter->diff)
313     g_free (filter->diff);
314   filter->diff = g_new (guint8, width * height);
315
316   return TRUE;
317
318   /* ERRORS */
319 invalid_caps:
320   {
321     GST_DEBUG_OBJECT (filter, "invalid caps received");
322     return FALSE;
323   }
324 }
325
326 static gboolean
327 gst_optv_start (GstBaseTransform * trans)
328 {
329   GstOpTV *filter = GST_OPTV (trans);
330
331   filter->phase = 0;
332
333   return TRUE;
334 }
335
336 static void
337 gst_optv_finalize (GObject * object)
338 {
339   GstOpTV *filter = GST_OPTV (object);
340
341   if (filter->opmap[0]) {
342     gint i;
343
344     for (i = 0; i < 4; i++) {
345       if (filter->opmap[i])
346         g_free (filter->opmap[i]);
347       filter->opmap[i] = NULL;
348     }
349   }
350
351   if (filter->diff)
352     g_free (filter->diff);
353   filter->diff = NULL;
354
355   G_OBJECT_CLASS (parent_class)->finalize (object);
356 }
357
358 static void
359 gst_optv_set_property (GObject * object, guint prop_id, const GValue * value,
360     GParamSpec * pspec)
361 {
362   GstOpTV *filter = GST_OPTV (object);
363
364   GST_OBJECT_LOCK (filter);
365   switch (prop_id) {
366     case PROP_MODE:
367       filter->mode = g_value_get_enum (value);
368       break;
369     case PROP_SPEED:
370       filter->speed = g_value_get_int (value);
371       break;
372     case PROP_THRESHOLD:
373       filter->threshold = g_value_get_uint (value);
374       break;
375     default:
376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377       break;
378   }
379   GST_OBJECT_UNLOCK (filter);
380 }
381
382 static void
383 gst_optv_get_property (GObject * object, guint prop_id, GValue * value,
384     GParamSpec * pspec)
385 {
386   GstOpTV *filter = GST_OPTV (object);
387
388   switch (prop_id) {
389     case PROP_MODE:
390       g_value_set_enum (value, filter->mode);
391       break;
392     case PROP_SPEED:
393       g_value_set_int (value, filter->speed);
394       break;
395     case PROP_THRESHOLD:
396       g_value_set_uint (value, filter->threshold);
397       break;
398     default:
399       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400       break;
401   }
402 }
403
404 static void
405 gst_optv_class_init (GstOpTVClass * klass)
406 {
407   GObjectClass *gobject_class = (GObjectClass *) klass;
408   GstElementClass *gstelement_class = (GstElementClass *) klass;
409   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
410
411   gobject_class->set_property = gst_optv_set_property;
412   gobject_class->get_property = gst_optv_get_property;
413
414   gobject_class->finalize = gst_optv_finalize;
415
416   g_object_class_install_property (gobject_class, PROP_MODE,
417       g_param_spec_enum ("mode", "Mode",
418           "Mode", GST_TYPE_OPTV_MODE, DEFAULT_MODE,
419           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
420
421   g_object_class_install_property (gobject_class, PROP_SPEED,
422       g_param_spec_int ("speed", "Speed",
423           "Effect speed", G_MININT, G_MAXINT, DEFAULT_SPEED,
424           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
425
426   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
427       g_param_spec_uint ("threshold", "Threshold",
428           "Luma threshold", 0, G_MAXINT, DEFAULT_THRESHOLD,
429           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430
431   gst_element_class_set_details_simple (gstelement_class, "OpTV effect",
432       "Filter/Effect/Video",
433       "Optical art meets real-time video effect",
434       "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
435       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
436
437   gst_element_class_add_pad_template (gstelement_class,
438       gst_static_pad_template_get (&gst_optv_sink_template));
439   gst_element_class_add_pad_template (gstelement_class,
440       gst_static_pad_template_get (&gst_optv_src_template));
441
442   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_optv_set_caps);
443   trans_class->transform = GST_DEBUG_FUNCPTR (gst_optv_transform);
444   trans_class->start = GST_DEBUG_FUNCPTR (gst_optv_start);
445
446   initPalette ();
447 }
448
449 static void
450 gst_optv_init (GstOpTV * filter)
451 {
452   filter->speed = DEFAULT_SPEED;
453   filter->mode = DEFAULT_MODE;
454   filter->threshold = DEFAULT_THRESHOLD;
455 }