Caps fixes and general cleanup.
[platform/upstream/gst-plugins-good.git] / gst / videofilter / gstvideoflip.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21
22 /*#define DEBUG_ENABLED */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <gstvideoflip.h>
27
28 #include <stdlib.h>
29 #include <math.h>
30 #include <string.h>
31
32
33
34 /* elementfactory information */
35 static GstElementDetails videoflip_details = {
36   "Video flipper",
37   "Filter/Video",
38   "LGPL",
39   "Flips and rotates video",
40   VERSION,
41   "David Schleef <ds@schleef.org>",
42   "(C) 2003",
43 };
44
45 /* GstVideoflip signals and args */
46 enum {
47   /* FILL ME */
48   LAST_SIGNAL
49 };
50
51 enum {
52   ARG_0,
53   ARG_METHOD,
54   /* FILL ME */
55 };
56
57 static void     gst_videoflip_class_init        (GstVideoflipClass *klass);
58 static void     gst_videoflip_init              (GstVideoflip *videoflip);
59
60 static void     gst_videoflip_set_property              (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
61 static void     gst_videoflip_get_property              (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
62
63 static void gst_videoflip_planar411(GstVideofilter *videofilter, void *dest, void *src);
64 static void gst_videoflip_setup(GstVideofilter *videofilter);
65
66 static GstVideoflipClass *this_class = NULL;
67 static GstVideofilterClass *parent_class = NULL;
68 static GstElementClass *element_class = NULL;
69
70 #define GST_TYPE_VIDEOFLIP_METHOD (gst_videoflip_method_get_type())
71
72 static GType
73 gst_videoflip_method_get_type(void)
74 {
75   static GType videoflip_method_type = 0;
76   static GEnumValue videoflip_methods[] = {
77     { GST_VIDEOFLIP_METHOD_IDENTITY,    "0", "Identity (no rotation)" },
78     { GST_VIDEOFLIP_METHOD_90R,         "1", "Rotate clockwise 90 degrees" },
79     { GST_VIDEOFLIP_METHOD_180,         "2", "Rotate 180 degrees" },
80     { GST_VIDEOFLIP_METHOD_90L,         "3", "Rotate counter-clockwise 90 degrees" },
81     { GST_VIDEOFLIP_METHOD_HORIZ,       "4", "Flip horizontally" },
82     { GST_VIDEOFLIP_METHOD_VERT,        "5", "Flip vertically" },
83     { GST_VIDEOFLIP_METHOD_TRANS,       "6", "Flip across upper left/lower right diagonal" },
84     { GST_VIDEOFLIP_METHOD_OTHER,       "7", "Flip across upper right/lower left diagonal" },
85     { 0, NULL, NULL },
86   };
87   if(!videoflip_method_type){
88     videoflip_method_type = g_enum_register_static("GstVideoflipMethod",
89         videoflip_methods);
90   }
91   return videoflip_method_type;
92 }
93
94 GType
95 gst_videoflip_get_type (void)
96 {
97   static GType videoflip_type = 0;
98
99   if (!videoflip_type) {
100     static const GTypeInfo videoflip_info = {
101       sizeof(GstVideoflipClass),      NULL,
102       NULL,
103       (GClassInitFunc)gst_videoflip_class_init,
104       NULL,
105       NULL,
106       sizeof(GstVideoflip),
107       0,
108       (GInstanceInitFunc)gst_videoflip_init,
109     };
110     videoflip_type = g_type_register_static(GST_TYPE_VIDEOFILTER, "GstVideoflip", &videoflip_info, 0);
111   }
112   return videoflip_type;
113 }
114
115 static GstVideofilterFormat gst_videoflip_formats[] = {
116   /* planar */
117   { "YV12", 12, gst_videoflip_planar411, },
118   { "I420", 12, gst_videoflip_planar411, },
119   { "IYUV", 12, gst_videoflip_planar411, },
120 };
121
122 static void
123 gst_videoflip_class_init (GstVideoflipClass *klass)
124 {
125   GObjectClass *gobject_class;
126   GstElementClass *gstelement_class;
127   GstVideofilterClass *gstvideofilter_class;
128   int i;
129
130   gobject_class = (GObjectClass*)klass;
131   gstelement_class = (GstElementClass*)klass;
132   gstvideofilter_class = (GstVideofilterClass *)klass;
133
134   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_METHOD,
135       g_param_spec_enum("method","method","method",
136       GST_TYPE_VIDEOFLIP_METHOD, GST_VIDEOFLIP_METHOD_90R,
137       G_PARAM_READWRITE));
138
139   this_class = klass;
140   parent_class = g_type_class_ref(GST_TYPE_VIDEOFILTER);
141   element_class = g_type_class_ref(GST_TYPE_ELEMENT);
142
143   gobject_class->set_property = gst_videoflip_set_property;
144   gobject_class->get_property = gst_videoflip_get_property;
145
146   gstvideofilter_class->setup = gst_videoflip_setup;
147
148   for(i=0;i<G_N_ELEMENTS(gst_videoflip_formats);i++){
149     gst_videofilter_class_add_format(gstvideofilter_class,
150         gst_videoflip_formats + i);
151   }
152 }
153
154 static GstCaps *gst_videoflip_get_capslist(void)
155 {
156   GstVideofilterClass *klass;
157
158   klass = g_type_class_ref(GST_TYPE_VIDEOFILTER);
159
160   return gst_videofilter_class_get_capslist(klass);
161 }
162
163 static GstPadTemplate *
164 gst_videoflip_src_template_factory(void)
165 {
166   static GstPadTemplate *templ = NULL;
167
168   if(!templ){
169     GstCaps *caps = GST_CAPS_NEW("src","video/x-raw-yuv",
170                 "width", GST_PROPS_INT_RANGE (0, G_MAXINT),
171                 "height", GST_PROPS_INT_RANGE (0, G_MAXINT),
172                 "framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT));
173
174     caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
175
176     templ = GST_PAD_TEMPLATE_NEW("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
177   }
178   return templ;
179 }
180
181 static GstPadTemplate *
182 gst_videoflip_sink_template_factory(void)
183 {
184   static GstPadTemplate *templ = NULL;
185
186   if(!templ){
187     GstCaps *caps = GST_CAPS_NEW("sink","video/x-raw-yuv",
188                 "width", GST_PROPS_INT_RANGE (0, G_MAXINT),
189                 "height", GST_PROPS_INT_RANGE (0, G_MAXINT),
190                 "framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT));
191
192     caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
193
194     templ = GST_PAD_TEMPLATE_NEW("src", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
195   }
196   return templ;
197 }
198
199 static void
200 gst_videoflip_init (GstVideoflip *videoflip)
201 {
202   GstVideofilter *videofilter;
203
204   GST_DEBUG ("gst_videoflip_init");
205
206   videofilter = GST_VIDEOFILTER(videoflip);
207
208   videofilter->sinkpad = gst_pad_new_from_template (
209                   GST_PAD_TEMPLATE_GET (gst_videoflip_sink_template_factory),
210                   "sink");
211
212   videofilter->srcpad = gst_pad_new_from_template (
213                   GST_PAD_TEMPLATE_GET (gst_videoflip_src_template_factory),
214                   "src");
215
216   gst_videofilter_postinit(GST_VIDEOFILTER(videoflip));
217 }
218
219 static void
220 gst_videoflip_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
221 {
222   GstVideoflip *videoflip;
223
224   /* it's not null if we got it, but it might not be ours */
225   g_return_if_fail(GST_IS_VIDEOFLIP(object));
226   videoflip = GST_VIDEOFLIP(object);
227
228   GST_DEBUG ("gst_videoflip_set_property");
229   switch (prop_id) {
230     case ARG_METHOD:
231       videoflip->method = g_value_get_enum (value);
232       /* FIXME is this ok? (threading issues) */
233       gst_videoflip_setup(GST_VIDEOFILTER(videoflip));
234       break;
235     default:
236       break;
237   }
238 }
239
240 static void
241 gst_videoflip_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
242 {
243   GstVideoflip *videoflip;
244
245   /* it's not null if we got it, but it might not be ours */
246   g_return_if_fail(GST_IS_VIDEOFLIP(object));
247   videoflip = GST_VIDEOFLIP(object);
248
249   switch (prop_id) {
250     case ARG_METHOD:
251       g_value_set_enum (value, videoflip->method);
252       break;
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255       break;
256   }
257 }
258
259 static gboolean plugin_init (GModule *module, GstPlugin *plugin)
260 {
261   GstElementFactory *factory;
262
263   if(!gst_library_load("gstvideofilter"))
264     return FALSE;
265
266   /* create an elementfactory for the videoflip element */
267   factory = gst_element_factory_new("videoflip",GST_TYPE_VIDEOFLIP,
268                                    &videoflip_details);
269   g_return_val_if_fail(factory != NULL, FALSE);
270
271   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (gst_videoflip_sink_template_factory));
272   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (gst_videoflip_src_template_factory));
273
274   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
275
276   return TRUE;
277 }
278
279 GstPluginDesc plugin_desc = {
280   GST_VERSION_MAJOR,
281   GST_VERSION_MINOR,
282   "videoflip",
283   plugin_init
284 };
285
286
287 static void gst_videoflip_flip(GstVideoflip *videoflip, unsigned char *dest,
288     unsigned char *src, int sw, int sh, int dw, int dh);
289
290
291 static void gst_videoflip_setup (GstVideofilter *videofilter)
292 {
293   int from_width, from_height;
294   GstVideoflip *videoflip;
295
296   GST_DEBUG("gst_videoflip_setup");
297
298   videoflip = GST_VIDEOFLIP(videofilter);
299
300   from_width = gst_videofilter_get_input_width(videofilter);
301   from_height = gst_videofilter_get_input_height(videofilter);
302
303   if(from_width==0 || from_height==0){
304     return;
305   }
306
307   switch(videoflip->method){
308     case GST_VIDEOFLIP_METHOD_90R:
309     case GST_VIDEOFLIP_METHOD_90L:
310     case GST_VIDEOFLIP_METHOD_TRANS:
311     case GST_VIDEOFLIP_METHOD_OTHER:
312       gst_videofilter_set_output_size(videofilter, from_height, from_width);
313       break;
314     case GST_VIDEOFLIP_METHOD_IDENTITY:
315     case GST_VIDEOFLIP_METHOD_180:
316     case GST_VIDEOFLIP_METHOD_HORIZ:
317     case GST_VIDEOFLIP_METHOD_VERT:
318       gst_videofilter_set_output_size(videofilter, from_width, from_height);
319       break;
320     default:
321       g_assert_not_reached();
322       break;
323   }
324
325   GST_DEBUG ("format=%p \"%s\" from %dx%d to %dx%d",
326                 videofilter->format, videofilter->format->fourcc,
327                 from_width, from_height,
328                 videofilter->to_width, videofilter->to_height);
329
330   if(videoflip->method == GST_VIDEOFLIP_METHOD_IDENTITY){
331     GST_DEBUG ("videoflip: using passthru");
332     videofilter->passthru = TRUE;
333   }else{
334     videofilter->passthru = FALSE;
335   }
336
337   videofilter->from_buf_size = (videofilter->from_width * videofilter->from_height
338                   * videofilter->format->depth) / 8;
339   videofilter->to_buf_size = (videofilter->to_width * videofilter->to_height
340                   * videofilter->format->depth) / 8;
341
342   videofilter->inited = TRUE;
343 }
344
345 static void gst_videoflip_planar411(GstVideofilter *videofilter,
346     void *dest, void *src)
347 {
348   GstVideoflip *videoflip;
349   int sw;
350   int sh;
351   int dw;
352   int dh;
353
354   g_return_if_fail(GST_IS_VIDEOFLIP(videofilter));
355   videoflip = GST_VIDEOFLIP(videofilter);
356
357   sw = videofilter->from_width;
358   sh = videofilter->from_height;
359   dw = videofilter->to_width;
360   dh = videofilter->to_height;
361
362   GST_DEBUG ("videoflip: scaling planar 4:1:1 %dx%d to %dx%d", sw, sh, dw, dh);
363
364   gst_videoflip_flip(videoflip, dest, src, sw, sh, dw, dh);
365
366   src += sw*sh;
367   dest += dw*dh;
368
369   dh = dh>>1;
370   dw = dw>>1;
371   sh = sh>>1;
372   sw = sw>>1;
373
374   gst_videoflip_flip(videoflip, dest, src, sw, sh, dw, dh);
375
376   src += sw*sh;
377   dest += dw*dh;
378
379   gst_videoflip_flip(videoflip, dest, src, sw, sh, dw, dh);
380 }
381
382 static void
383 gst_videoflip_flip(GstVideoflip *videoflip, unsigned char *dest,
384     unsigned char *src, int sw, int sh, int dw, int dh)
385 {
386   int x,y;
387
388   switch(videoflip->method){
389     case GST_VIDEOFLIP_METHOD_90R:
390       for(y=0;y<dh;y++){
391         for(x=0;x<dw;x++){
392           dest[y*dw + x] = src[(sh - 1 - x)*sw + y];
393         }
394       }
395       break;
396     case GST_VIDEOFLIP_METHOD_90L:
397       for(y=0;y<dh;y++){
398         for(x=0;x<dw;x++){
399           dest[y*dw + x] = src[x*sw + (sw - 1 - y)];
400         }
401       }
402       break;
403     case GST_VIDEOFLIP_METHOD_180:
404       for(y=0;y<dh;y++){
405         for(x=0;x<dw;x++){
406           dest[y*dw + x] = src[(sh - 1 - y)*sw + (sw - 1 - x)];
407         }
408       }
409       break;
410     case GST_VIDEOFLIP_METHOD_HORIZ:
411       for(y=0;y<dh;y++){
412         for(x=0;x<dw;x++){
413           dest[y*dw + x] = src[y*sw + (sw - 1 - x)];
414         }
415       }
416       break;
417     case GST_VIDEOFLIP_METHOD_VERT:
418       for(y=0;y<dh;y++){
419         for(x=0;x<dw;x++){
420           dest[y*dw + x] = src[(sh - 1 - y)*sw + x];
421         }
422       }
423       break;
424     case GST_VIDEOFLIP_METHOD_TRANS:
425       for(y=0;y<dh;y++){
426         for(x=0;x<dw;x++){
427           dest[y*dw + x] = src[x*sw + y];
428         }
429       }
430       break;
431     case GST_VIDEOFLIP_METHOD_OTHER:
432       for(y=0;y<dh;y++){
433         for(x=0;x<dw;x++){
434           dest[y*dw + x] = src[(sh - 1 - x)*sw + (sw - 1 - y)];
435         }
436       }
437       break;
438     default:
439       /* FIXME */
440       break;
441   }
442 }
443
444