Copy the timestamp too. funny effects: ./gst-launch filesrc location=/opt/data/AlienS...
[platform/upstream/gst-plugins-good.git] / gst / effectv / gsteffectv.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21 #include <gst/gst.h>
22
23 #define GST_TYPE_EFFECTV \
24   (gst_effectv_get_type())
25 #define GST_EFFECTV(obj) \
26   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_EFFECTV,GstEffecTV))
27 #define GST_EFFECTV_CLASS(klass) \
28   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstEffecTV))
29 #define GST_IS_EFFECTV(obj) \
30   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_EFFECTV))
31 #define GST_IS_EFFECTV_CLASS(obj) \
32   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_EFFECTV))
33
34 typedef struct _GstEffecTV GstEffecTV;
35 typedef struct _GstEffecTVClass GstEffecTVClass;
36
37 struct _GstEffecTV
38 {
39   GstElement element;
40
41   GstPad *sinkpad, *srcpad;
42
43   gint width, height;
44   gint map_width, map_height;
45   guint32 *map;
46   gint video_width_margin;
47 };
48
49 struct _GstEffecTVClass
50 {
51   GstElementClass parent_class;
52 };
53
54 static GstElementDetails effectv_details = {
55   "EffecTV",
56   "Filter/Effect",
57   "Aply edge detect on video",
58   VERSION,
59   "Wim Taymans <wim.taymans@chello.be>",
60   "(C) 2002",
61 };
62
63
64 /* Filter signals and args */
65 enum
66 {
67   /* FILL ME */
68   LAST_SIGNAL
69 };
70
71 enum
72 {
73   ARG_0,
74 };
75
76 GST_PAD_TEMPLATE_FACTORY (effectv_src_factory,
77   "src",
78   GST_PAD_SRC,
79   GST_PAD_ALWAYS,
80   GST_CAPS_NEW (
81     "effectv_src",
82     "video/raw",
83       "format",         GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")),
84       "bpp",            GST_PROPS_INT (32),
85       "depth",          GST_PROPS_INT (32),
86       "endianness",     GST_PROPS_INT (G_BYTE_ORDER),
87       "red_mask",       GST_PROPS_INT (0xff0000),
88       "green_mask",     GST_PROPS_INT (0xff00),
89       "blue_mask",      GST_PROPS_INT (0xff),
90       "width",          GST_PROPS_INT_RANGE (16, 4096),
91       "height",         GST_PROPS_INT_RANGE (16, 4096)
92   )
93 )
94
95 GST_PAD_TEMPLATE_FACTORY (effectv_sink_factory,
96   "sink",
97   GST_PAD_SINK,
98   GST_PAD_ALWAYS,
99   GST_CAPS_NEW (
100     "effectv_src",
101     "video/raw",
102       "format",         GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")),
103       "bpp",            GST_PROPS_INT (32),
104       "depth",          GST_PROPS_INT (32),
105       "endianness",     GST_PROPS_INT (G_BYTE_ORDER),
106       "red_mask",       GST_PROPS_INT (0xff0000),
107       "green_mask",     GST_PROPS_INT (0xff00),
108       "blue_mask",      GST_PROPS_INT (0xff),
109       "width",          GST_PROPS_INT_RANGE (16, 4096),
110       "height",         GST_PROPS_INT_RANGE (16, 4096)
111   )
112 )
113
114 static GType gst_effectv_get_type (void);
115
116 static void gst_effectv_class_init (GstEffecTVClass * klass);
117 static void gst_effectv_init (GstEffecTV * filter);
118
119 static void gst_effectv_set_property (GObject * object, guint prop_id,
120                                            const GValue * value, GParamSpec * pspec);
121 static void gst_effectv_get_property (GObject * object, guint prop_id,
122                                            GValue * value, GParamSpec * pspec);
123
124 static void gst_effectv_chain (GstPad * pad, GstBuffer * buf);
125
126 static GstElementClass *parent_class = NULL;
127
128 /*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
129
130      static GType gst_effectv_get_type (void)
131 {
132   static GType effectv_type = 0;
133
134   if (!effectv_type) {
135     static const GTypeInfo effectv_info = {
136       sizeof (GstEffecTVClass), NULL,
137       NULL,
138       (GClassInitFunc) gst_effectv_class_init,
139       NULL,
140       NULL,
141       sizeof (GstEffecTV),
142       0,
143       (GInstanceInitFunc) gst_effectv_init,
144     };
145
146     effectv_type = g_type_register_static (GST_TYPE_ELEMENT, "GstEffecTV", &effectv_info, 0);
147   }
148   return effectv_type;
149 }
150
151 static void
152 gst_effectv_class_init (GstEffecTVClass * klass)
153 {
154   GObjectClass *gobject_class;
155   GstElementClass *gstelement_class;
156
157   gobject_class = (GObjectClass *) klass;
158   gstelement_class = (GstElementClass *) klass;
159
160   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
161
162   gobject_class->set_property = gst_effectv_set_property;
163   gobject_class->get_property = gst_effectv_get_property;
164 }
165
166 static GstPadConnectReturn
167 gst_effectv_sinkconnect (GstPad * pad, GstCaps * caps)
168 {
169   GstEffecTV *filter;
170
171   filter = GST_EFFECTV (gst_pad_get_parent (pad));
172
173   if (!GST_CAPS_IS_FIXED (caps))
174     return GST_PAD_CONNECT_DELAYED;
175
176   gst_caps_get_int (caps, "width", &filter->width);
177   gst_caps_get_int (caps, "height", &filter->height);
178
179   filter->map_width = filter->width / 4;
180   filter->map_height = filter->height / 4;
181   filter->video_width_margin = filter->width - filter->map_width * 4;
182
183   g_free (filter->map);
184   filter->map = (guint32 *)g_malloc (filter->map_width * filter->map_height * sizeof(guint32) * 2);
185   bzero(filter->map, filter->map_width * filter->map_height * sizeof(guint32) * 2);
186
187   if (gst_pad_try_set_caps (filter->srcpad, caps)) {
188     return GST_PAD_CONNECT_OK;
189   }
190
191   return GST_PAD_CONNECT_REFUSED;
192 }
193
194 static void
195 gst_effectv_init (GstEffecTV * filter)
196 {
197   filter->sinkpad = gst_pad_new_from_template (effectv_sink_factory (), "sink");
198   gst_pad_set_chain_function (filter->sinkpad, gst_effectv_chain);
199   gst_pad_set_connect_function (filter->sinkpad, gst_effectv_sinkconnect);
200   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
201
202   filter->srcpad = gst_pad_new_from_template (effectv_src_factory (), "src");
203   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
204
205   filter->map = NULL;
206 }
207
208 static void
209 gst_effectv_chain (GstPad * pad, GstBuffer * buf)
210 {
211   GstEffecTV *filter;
212   int x, y;
213   int r, g, b;
214   guint32 *src, *dest;
215   guint32 p, q;
216   guint32 v0, v1, v2, v3;
217   GstBuffer *outbuf;
218
219   filter = GST_EFFECTV (gst_pad_get_parent (pad));
220
221   src = (guint32 *) GST_BUFFER_DATA (buf);
222
223   outbuf = gst_buffer_new ();
224   GST_BUFFER_SIZE (outbuf) = (filter->width * filter->height * 4);
225   dest = (guint32 *) GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
226   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
227   
228   src += filter->width * 4 + 4;
229   dest += filter->width * 4 + 4;
230   
231   for (y = 1; y < filter->map_height - 1; y++) {
232     for (x = 1; x < filter->map_width - 1; x++) {
233
234       p = *src;
235       q = *(src - 4);
236
237 /* difference between the current pixel and right neighbor. */
238       r = ((p & 0xff0000) - (q & 0xff0000)) >> 16;
239       g = ((p & 0xff00) - (q & 0xff00)) >> 8;
240       b = (p & 0xff) - (q & 0xff);
241       r *= r;
242       g *= g;
243       b *= b;
244       r = r >> 5;               /* To lack the lower bit for saturated addition,  */
245       g = g >> 5;               /* devide the value with 32, instead of 16. It is */
246       b = b >> 4;               /* same as `v2 &= 0xfefeff' */
247       if (r > 127)
248         r = 127;
249       if (g > 127)
250         g = 127;
251       if (b > 255)
252         b = 255;
253       v2 = (r << 17) | (g << 9) | b;
254
255 /* difference between the current pixel and upper neighbor. */
256       q = *(src - filter->width * 4);
257       r = ((p & 0xff0000) - (q & 0xff0000)) >> 16;
258       g = ((p & 0xff00) - (q & 0xff00)) >> 8;
259       b = (p & 0xff) - (q & 0xff);
260       r *= r;
261       g *= g;
262       b *= b;
263       r = r >> 5;
264       g = g >> 5;
265       b = b >> 4;
266       if (r > 127)
267         r = 127;
268       if (g > 127)
269         g = 127;
270       if (b > 255)
271         b = 255;
272       v3 = (r << 17) | (g << 9) | b;
273
274       v0 = filter->map[(y - 1) * filter->map_width * 2 + x * 2];
275       v1 = filter->map[y * filter->map_width * 2 + (x - 1) * 2 + 1];
276       filter->map[y * filter->map_width * 2 + x * 2] = v2;
277       filter->map[y * filter->map_width * 2 + x * 2 + 1] = v3;
278       r = v0 + v1;
279       g = r & 0x01010100;
280       dest[0] = r | (g - (g >> 8));
281       r = v0 + v3;
282       g = r & 0x01010100;
283       dest[1] = r | (g - (g >> 8));
284       dest[2] = v3;
285       dest[3] = v3;
286       r = v2 + v1;
287       g = r & 0x01010100;
288       dest[filter->width] = r | (g - (g >> 8));
289       r = v2 + v3;
290       g = r & 0x01010100;
291       dest[filter->width + 1] = r | (g - (g >> 8));
292       dest[filter->width + 2] = v3;
293       dest[filter->width + 3] = v3;
294       dest[filter->width * 2] = v2;
295       dest[filter->width * 2 + 1] = v2;
296       dest[filter->width * 3] = v2;
297       dest[filter->width * 3 + 1] = v2;
298
299       src += 4;
300       dest += 4;
301     }
302     src += filter->width * 3 + 8 + filter->video_width_margin;
303     dest += filter->width * 3 + 8 + filter->video_width_margin;
304   }
305   gst_buffer_unref (buf);
306
307   gst_pad_push (filter->srcpad, outbuf);
308 }
309
310 static void
311 gst_effectv_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
312 {
313   GstEffecTV *filter;
314
315   /* it's not null if we got it, but it might not be ours */
316   g_return_if_fail (GST_IS_EFFECTV (object));
317
318   filter = GST_EFFECTV (object);
319
320   switch (prop_id) {
321     default:
322       break;
323   }
324 }
325
326 static void
327 gst_effectv_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
328 {
329   GstEffecTV *filter;
330
331   /* it's not null if we got it, but it might not be ours */
332   g_return_if_fail (GST_IS_EFFECTV (object));
333
334   filter = GST_EFFECTV (object);
335
336   switch (prop_id) {
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340   }
341 }
342
343 static gboolean
344 plugin_init (GModule * module, GstPlugin * plugin)
345 {
346   GstElementFactory *factory;
347
348   factory = gst_element_factory_new ("edgeTV", GST_TYPE_EFFECTV, &effectv_details);
349   g_return_val_if_fail (factory != NULL, FALSE);
350
351   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (effectv_src_factory));
352   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (effectv_sink_factory));
353
354   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
355
356   return TRUE;
357 }
358
359 GstPluginDesc plugin_desc = {
360   GST_VERSION_MAJOR,
361   GST_VERSION_MINOR,
362   "effectv",
363   plugin_init
364 };