• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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., 51 Franklin St, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26 
27 /**
28  * SECTION:element-optv
29  * @title: optv
30  *
31  * Traditional black-white optical animation is now resurrected as a
32  * real-time video effect. Input images are binarized and combined with
33  * various optical pattern.
34  *
35  * ## Example launch line
36  * |[
37  * gst-launch-1.0 -v videotestsrc ! optv ! videoconvert ! autovideosink
38  * ]| This pipeline shows the effect of optv on a test stream.
39  *
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 
54 enum
55 {
56   OP_SPIRAL1 = 0,
57   OP_SPIRAL2,
58   OP_PARABOLA,
59   OP_HSTRIPE
60 };
61 
62 #define GST_TYPE_OPTV_MODE (gst_optv_mode_get_type())
63 static GType
gst_optv_mode_get_type(void)64 gst_optv_mode_get_type (void)
65 {
66   static GType type = 0;
67 
68   static const GEnumValue enumvalue[] = {
69     {OP_SPIRAL1, "Maelstrom", "maelstrom"},
70     {OP_SPIRAL2, "Radiation", "radiation"},
71     {OP_PARABOLA, "Horizontal Stripes",
72         "horizontal-stripes"},
73     {OP_HSTRIPE, "Vertical Stripes", "vertical-stripes"},
74     {0, NULL, NULL},
75   };
76 
77   if (!type) {
78     type = g_enum_register_static ("GstOpTVMode", enumvalue);
79   }
80   return type;
81 }
82 
83 #define DEFAULT_MODE OP_SPIRAL1
84 #define DEFAULT_SPEED 16
85 #define DEFAULT_THRESHOLD 60
86 
87 enum
88 {
89   PROP_0,
90   PROP_MODE,
91   PROP_SPEED,
92   PROP_THRESHOLD
93 };
94 
95 static guint32 palette[256];
96 
97 #define gst_optv_parent_class parent_class
98 G_DEFINE_TYPE (GstOpTV, gst_optv, GST_TYPE_VIDEO_FILTER);
99 GST_ELEMENT_REGISTER_DEFINE (optv, "optv", GST_RANK_NONE, gst_optv_get_type ());
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
initPalette(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
setOpmap(gint8 * opmap[4],gint width,gint height)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
image_y_over(guint32 * src,guint8 * diff,gint y_threshold,gint video_area)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
gst_optv_transform_frame(GstVideoFilter * vfilter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)203 gst_optv_transform_frame (GstVideoFilter * vfilter, GstVideoFrame * in_frame,
204     GstVideoFrame * out_frame)
205 {
206   GstOpTV *filter = GST_OPTV (vfilter);
207   guint32 *src, *dest;
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_frame->buffer);
215   stream_time =
216       gst_segment_to_stream_time (&GST_BASE_TRANSFORM (vfilter)->segment,
217       GST_FORMAT_TIME, timestamp);
218 
219   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
220       GST_TIME_ARGS (timestamp));
221 
222   if (GST_CLOCK_TIME_IS_VALID (stream_time))
223     gst_object_sync_values (GST_OBJECT (filter), stream_time);
224 
225   if (G_UNLIKELY (filter->opmap[0] == NULL))
226     return GST_FLOW_NOT_NEGOTIATED;
227 
228   src = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
229   dest = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
230 
231   width = GST_VIDEO_FRAME_WIDTH (in_frame);
232   height = GST_VIDEO_FRAME_HEIGHT (in_frame);
233 
234   GST_OBJECT_LOCK (filter);
235   switch (filter->mode) {
236     default:
237     case 0:
238       p = filter->opmap[OP_SPIRAL1];
239       break;
240     case 1:
241       p = filter->opmap[OP_SPIRAL2];
242       break;
243     case 2:
244       p = filter->opmap[OP_PARABOLA];
245       break;
246     case 3:
247       p = filter->opmap[OP_HSTRIPE];
248       break;
249   }
250 
251   filter->phase -= filter->speed;
252 
253   diff = filter->diff;
254   image_y_over (src, diff, filter->threshold, width * height);
255   phase = filter->phase;
256 
257   for (y = 0; y < height; y++) {
258     for (x = 0; x < width; x++) {
259       *dest++ = palette[(((guint8) (*p + phase)) ^ *diff++) & 255];
260       p++;
261     }
262   }
263   GST_OBJECT_UNLOCK (filter);
264 
265   return GST_FLOW_OK;
266 }
267 
268 static gboolean
gst_optv_set_info(GstVideoFilter * vfilter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)269 gst_optv_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
270     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
271 {
272   GstOpTV *filter = GST_OPTV (vfilter);
273   gint i, width, height;
274 
275   width = GST_VIDEO_INFO_WIDTH (in_info);
276   height = GST_VIDEO_INFO_HEIGHT (in_info);
277 
278   for (i = 0; i < 4; i++) {
279     g_free (filter->opmap[i]);
280     filter->opmap[i] = g_new (gint8, width * height);
281   }
282   setOpmap (filter->opmap, width, height);
283 
284   g_free (filter->diff);
285   filter->diff = g_new (guint8, width * height);
286 
287   return TRUE;
288 }
289 
290 static gboolean
gst_optv_start(GstBaseTransform * trans)291 gst_optv_start (GstBaseTransform * trans)
292 {
293   GstOpTV *filter = GST_OPTV (trans);
294 
295   filter->phase = 0;
296 
297   return TRUE;
298 }
299 
300 static void
gst_optv_finalize(GObject * object)301 gst_optv_finalize (GObject * object)
302 {
303   GstOpTV *filter = GST_OPTV (object);
304 
305   if (filter->opmap[0]) {
306     gint i;
307 
308     for (i = 0; i < 4; i++) {
309       g_free (filter->opmap[i]);
310       filter->opmap[i] = NULL;
311     }
312   }
313 
314   g_free (filter->diff);
315   filter->diff = NULL;
316 
317   G_OBJECT_CLASS (parent_class)->finalize (object);
318 }
319 
320 static void
gst_optv_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)321 gst_optv_set_property (GObject * object, guint prop_id, const GValue * value,
322     GParamSpec * pspec)
323 {
324   GstOpTV *filter = GST_OPTV (object);
325 
326   GST_OBJECT_LOCK (filter);
327   switch (prop_id) {
328     case PROP_MODE:
329       filter->mode = g_value_get_enum (value);
330       break;
331     case PROP_SPEED:
332       filter->speed = g_value_get_int (value);
333       break;
334     case PROP_THRESHOLD:
335       filter->threshold = g_value_get_uint (value);
336       break;
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340   }
341   GST_OBJECT_UNLOCK (filter);
342 }
343 
344 static void
gst_optv_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)345 gst_optv_get_property (GObject * object, guint prop_id, GValue * value,
346     GParamSpec * pspec)
347 {
348   GstOpTV *filter = GST_OPTV (object);
349 
350   switch (prop_id) {
351     case PROP_MODE:
352       g_value_set_enum (value, filter->mode);
353       break;
354     case PROP_SPEED:
355       g_value_set_int (value, filter->speed);
356       break;
357     case PROP_THRESHOLD:
358       g_value_set_uint (value, filter->threshold);
359       break;
360     default:
361       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362       break;
363   }
364 }
365 
366 static void
gst_optv_class_init(GstOpTVClass * klass)367 gst_optv_class_init (GstOpTVClass * klass)
368 {
369   GObjectClass *gobject_class = (GObjectClass *) klass;
370   GstElementClass *gstelement_class = (GstElementClass *) klass;
371   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
372   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
373 
374   gobject_class->set_property = gst_optv_set_property;
375   gobject_class->get_property = gst_optv_get_property;
376 
377   gobject_class->finalize = gst_optv_finalize;
378 
379   g_object_class_install_property (gobject_class, PROP_MODE,
380       g_param_spec_enum ("mode", "Mode",
381           "Mode", GST_TYPE_OPTV_MODE, DEFAULT_MODE,
382           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
383 
384   g_object_class_install_property (gobject_class, PROP_SPEED,
385       g_param_spec_int ("speed", "Speed",
386           "Effect speed", G_MININT, G_MAXINT, DEFAULT_SPEED,
387           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
388 
389   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
390       g_param_spec_uint ("threshold", "Threshold",
391           "Luma threshold", 0, G_MAXINT, DEFAULT_THRESHOLD,
392           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
393 
394   gst_element_class_set_static_metadata (gstelement_class, "OpTV effect",
395       "Filter/Effect/Video",
396       "Optical art meets real-time video effect",
397       "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
398       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
399 
400   gst_element_class_add_static_pad_template (gstelement_class,
401       &gst_optv_sink_template);
402   gst_element_class_add_static_pad_template (gstelement_class,
403       &gst_optv_src_template);
404 
405   trans_class->start = GST_DEBUG_FUNCPTR (gst_optv_start);
406 
407   vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_optv_set_info);
408   vfilter_class->transform_frame = GST_DEBUG_FUNCPTR (gst_optv_transform_frame);
409 
410   initPalette ();
411 
412   gst_type_mark_as_plugin_api (GST_TYPE_OPTV_MODE, 0);
413 }
414 
415 static void
gst_optv_init(GstOpTV * filter)416 gst_optv_init (GstOpTV * filter)
417 {
418   filter->speed = DEFAULT_SPEED;
419   filter->mode = DEFAULT_MODE;
420   filter->threshold = DEFAULT_THRESHOLD;
421 }
422