7606e5b7baff2481c58e2a74033e71588fd4059c
[platform/upstream/gst-plugins-good.git] / gst / videoflip / gstvideoflip.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
21 /*#define DEBUG_ENABLED */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <gstvideoflip.h>
26 #include <videoflip.h>
27
28
29
30 /* elementfactory information */
31 static GstElementDetails videoflip_details = GST_ELEMENT_DETAILS (
32   "Video scaler",
33   "Filter/Effect/Video",
34   "Resizes video",
35   "Wim Taymans <wim.taymans@chello.be>"
36 );
37
38 /* GstVideoflip signals and args */
39 enum {
40   /* FILL ME */
41   LAST_SIGNAL
42 };
43
44 enum {
45   ARG_0,
46   ARG_METHOD,
47   /* FILL ME */
48 };
49
50 static void     gst_videoflip_base_init         (gpointer g_class);
51 static void     gst_videoflip_class_init        (GstVideoflipClass *klass);
52 static void     gst_videoflip_init              (GstVideoflip *videoflip);
53
54 static void     gst_videoflip_set_property              (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
55 static void     gst_videoflip_get_property              (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
56
57 static void     gst_videoflip_chain             (GstPad *pad, GstData *_data);
58 static GstCaps * gst_videoflip_get_capslist(void);
59
60 static GstElementClass *parent_class = NULL;
61
62 #define GST_TYPE_VIDEOFLIP_METHOD (gst_videoflip_method_get_type())
63
64 static GType
65 gst_videoflip_method_get_type(void)
66 {
67   static GType videoflip_method_type = 0;
68   static GEnumValue videoflip_methods[] = {
69     { GST_VIDEOFLIP_METHOD_IDENTITY,    "0", "Identity (no rotation)" },
70     { GST_VIDEOFLIP_METHOD_90R,         "1", "Rotate right 90 degrees" },
71     { GST_VIDEOFLIP_METHOD_180,         "2", "Rotate 180 degrees" },
72     { GST_VIDEOFLIP_METHOD_90L,         "3", "Rotate left 90 degrees" },
73     { GST_VIDEOFLIP_METHOD_HORIZ,       "4", "Flip horizontally" },
74     { GST_VIDEOFLIP_METHOD_VERT,        "5", "Flip vertically" },
75     { GST_VIDEOFLIP_METHOD_TRANS,       "6", "Flip across upper left/lower right diagonal" },
76     { GST_VIDEOFLIP_METHOD_OTHER,       "7", "Flip across upper right/lower left diagonal" },
77     { 0, NULL, NULL },
78   };
79   if(!videoflip_method_type){
80     videoflip_method_type = g_enum_register_static("GstVideoflipMethod",
81         videoflip_methods);
82   }
83   return videoflip_method_type;
84 }
85
86 static GstPadTemplate *
87 gst_videoflip_src_template_factory(void)
88 {
89   static GstPadTemplate *templ = NULL;
90
91   if(!templ){
92     /* well, actually RGB too, but since there's no RGB format anyway */
93     GstCaps *caps = GST_CAPS_NEW("src","video/x-raw-yuv",
94                 "width", GST_PROPS_INT_RANGE (0, G_MAXINT),
95                 "height", GST_PROPS_INT_RANGE (0, G_MAXINT),
96                 "framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT));
97
98     caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
99
100     templ = GST_PAD_TEMPLATE_NEW("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
101   }
102   return templ;
103 }
104
105 static GstPadTemplate *
106 gst_videoflip_sink_template_factory(void)
107 {
108   static GstPadTemplate *templ = NULL;
109
110   if(!templ){
111     GstCaps *caps = GST_CAPS_NEW("sink","video/x-raw-yuv",
112                 "width", GST_PROPS_INT_RANGE (0, G_MAXINT),
113                 "height", GST_PROPS_INT_RANGE (0, G_MAXINT),
114                 "framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT));
115
116     caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
117
118     templ = GST_PAD_TEMPLATE_NEW("src", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
119   }
120   return templ;
121 }
122
123 GType
124 gst_videoflip_get_type (void)
125 {
126   static GType videoflip_type = 0;
127
128   if (!videoflip_type) {
129     static const GTypeInfo videoflip_info = {
130       sizeof(GstVideoflipClass),
131       gst_videoflip_base_init,
132       NULL,
133       (GClassInitFunc)gst_videoflip_class_init,
134       NULL,
135       NULL,
136       sizeof(GstVideoflip),
137       0,
138       (GInstanceInitFunc)gst_videoflip_init,
139     };
140     videoflip_type = g_type_register_static(GST_TYPE_ELEMENT, "GstVideoflip", &videoflip_info, 0);
141   }
142   return videoflip_type;
143 }
144
145 static void
146 gst_videoflip_base_init (gpointer g_class)
147 {
148   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
149
150   gst_element_class_set_details (element_class, &videoflip_details);
151
152   gst_element_class_add_pad_template (element_class, GST_PAD_TEMPLATE_GET (gst_videoflip_sink_template_factory));
153   gst_element_class_add_pad_template (element_class, GST_PAD_TEMPLATE_GET (gst_videoflip_src_template_factory));
154 }
155 static void
156 gst_videoflip_class_init (GstVideoflipClass *klass)
157 {
158   GObjectClass *gobject_class;
159   GstElementClass *gstelement_class;
160
161   gobject_class = (GObjectClass*)klass;
162   gstelement_class = (GstElementClass*)klass;
163
164   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_METHOD,
165       g_param_spec_enum("method","method","method",
166       GST_TYPE_VIDEOFLIP_METHOD, GST_VIDEOFLIP_METHOD_90R,
167       G_PARAM_READWRITE));
168
169   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
170
171   gobject_class->set_property = gst_videoflip_set_property;
172   gobject_class->get_property = gst_videoflip_get_property;
173
174 }
175
176 static GstCaps *
177 gst_videoflip_get_capslist(void)
178 {
179   static GstCaps *capslist = NULL;
180   GstCaps *caps;
181   int i;
182
183   if (capslist){
184     gst_caps_ref(capslist);
185     return capslist;
186   }
187
188   for(i=0;i<videoflip_n_formats;i++){
189     caps = videoflip_get_caps(videoflip_formats + i);
190     capslist = gst_caps_append(capslist, caps);
191   }
192
193   gst_caps_ref(capslist);
194   return capslist;
195 }
196
197 static GstCaps *
198 gst_videoflip_sink_getcaps (GstPad *pad, GstCaps *caps)
199 {
200   GstVideoflip *videoflip;
201   GstCaps *capslist = NULL;
202   GstCaps *peercaps;
203   GstCaps *sizecaps;
204   int i;
205
206   GST_DEBUG ("gst_videoflip_src_link");
207   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
208   
209   /* get list of peer's caps */
210   if(pad == videoflip->srcpad){
211     peercaps = gst_pad_get_allowed_caps (videoflip->sinkpad);
212   }else{
213     peercaps = gst_pad_get_allowed_caps (videoflip->srcpad);
214   }
215
216   /* FIXME videoflip doesn't allow passthru of video formats it
217    * doesn't understand. */
218   /* Look through our list of caps and find those that match with
219    * the peer's formats.  Create a list of them. */
220   for(i=0;i<videoflip_n_formats;i++){
221     GstCaps *fromcaps = videoflip_get_caps(videoflip_formats + i);
222     if(gst_caps_is_always_compatible(fromcaps, peercaps)){
223       capslist = gst_caps_append(capslist, fromcaps);
224     }
225     gst_caps_unref (fromcaps);
226   }
227   gst_caps_unref (peercaps);
228
229   sizecaps = GST_CAPS_NEW("videoflip_size","video/x-raw-yuv",
230                 "width", GST_PROPS_INT_RANGE (0, G_MAXINT),
231                 "height", GST_PROPS_INT_RANGE (0, G_MAXINT),
232                 "framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT));
233
234   caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
235   gst_caps_unref (sizecaps);
236
237   return caps;
238 }
239
240
241 static GstPadLinkReturn
242 gst_videoflip_src_link (GstPad *pad, GstCaps *caps)
243 {
244   GstVideoflip *videoflip;
245   GstPadLinkReturn ret;
246   GstCaps *peercaps;
247
248   GST_DEBUG ("gst_videoflip_src_link");
249   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
250
251   if (!GST_CAPS_IS_FIXED (caps)) {
252     return GST_PAD_LINK_DELAYED;
253   }
254
255   gst_caps_debug(caps,"ack");
256
257   videoflip->format = videoflip_find_by_caps (caps);
258   g_return_val_if_fail(videoflip->format, GST_PAD_LINK_REFUSED);
259
260   gst_caps_get_int (caps, "width", &videoflip->to_width);
261   gst_caps_get_int (caps, "height", &videoflip->to_height);
262
263   GST_DEBUG ("width %d height %d",videoflip->to_width,videoflip->to_height);
264
265   peercaps = gst_caps_copy(caps);
266
267   gst_caps_set(peercaps, "width", GST_PROPS_INT_RANGE (0, G_MAXINT));
268   gst_caps_set(peercaps, "height", GST_PROPS_INT_RANGE (0, G_MAXINT));
269
270   ret = gst_pad_try_set_caps (videoflip->srcpad, peercaps);
271
272   gst_caps_unref(peercaps);
273
274   if(ret==GST_PAD_LINK_OK){
275     caps = gst_pad_get_caps (videoflip->srcpad);
276
277     gst_caps_get_int (caps, "width", &videoflip->from_width);
278     gst_caps_get_int (caps, "height", &videoflip->from_height);
279     gst_videoflip_setup(videoflip);
280   }
281
282   return ret;
283 }
284
285 static GstPadLinkReturn
286 gst_videoflip_sink_link (GstPad *pad, GstCaps *caps)
287 {
288   GstVideoflip *videoflip;
289   GstPadLinkReturn ret;
290   GstCaps *peercaps;
291
292   GST_DEBUG ("gst_videoflip_src_link");
293   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
294
295   if (!GST_CAPS_IS_FIXED (caps)) {
296     return GST_PAD_LINK_DELAYED;
297   }
298
299   videoflip->format = videoflip_find_by_caps (caps);
300   gst_caps_debug(caps,"ack");
301   g_return_val_if_fail(videoflip->format, GST_PAD_LINK_REFUSED);
302
303   gst_caps_get_int (caps, "width", &videoflip->from_width);
304   gst_caps_get_int (caps, "height", &videoflip->from_height);
305
306   gst_videoflip_setup(videoflip);
307
308   peercaps = gst_caps_copy(caps);
309
310   gst_caps_set(peercaps, "width", GST_PROPS_INT (videoflip->to_width));
311   gst_caps_set(peercaps, "height", GST_PROPS_INT (videoflip->to_height));
312
313   ret = gst_pad_try_set_caps (videoflip->srcpad, peercaps);
314
315   gst_caps_unref(peercaps);
316
317   if(ret==GST_PAD_LINK_OK){
318     caps = gst_pad_get_caps (videoflip->srcpad);
319
320     gst_caps_get_int (caps, "width", &videoflip->to_width);
321     gst_caps_get_int (caps, "height", &videoflip->to_height);
322     gst_videoflip_setup(videoflip);
323   }
324
325   return ret;
326 }
327
328 static void
329 gst_videoflip_init (GstVideoflip *videoflip)
330 {
331   GST_DEBUG ("gst_videoflip_init");
332   videoflip->sinkpad = gst_pad_new_from_template (
333                   GST_PAD_TEMPLATE_GET (gst_videoflip_sink_template_factory),
334                   "sink");
335   gst_element_add_pad(GST_ELEMENT(videoflip),videoflip->sinkpad);
336   gst_pad_set_chain_function(videoflip->sinkpad,gst_videoflip_chain);
337   gst_pad_set_link_function(videoflip->sinkpad,gst_videoflip_sink_link);
338   gst_pad_set_getcaps_function(videoflip->sinkpad,gst_videoflip_sink_getcaps);
339
340   videoflip->srcpad = gst_pad_new_from_template (
341                   GST_PAD_TEMPLATE_GET (gst_videoflip_src_template_factory),
342                   "src");
343   gst_element_add_pad(GST_ELEMENT(videoflip),videoflip->srcpad);
344   gst_pad_set_link_function(videoflip->srcpad,gst_videoflip_src_link);
345   //gst_pad_set_getcaps_function(videoflip->srcpad,gst_videoflip_getcaps);
346
347   videoflip->inited = FALSE;
348   videoflip->force_size = FALSE;
349 }
350
351
352 static void
353 gst_videoflip_chain (GstPad *pad, GstData *_data)
354 {
355   GstBuffer *buf = GST_BUFFER (_data);
356   GstVideoflip *videoflip;
357   guchar *data;
358   gulong size;
359   GstBuffer *outbuf;
360
361   GST_DEBUG ("gst_videoflip_chain");
362
363   g_return_if_fail (pad != NULL);
364   g_return_if_fail (GST_IS_PAD (pad));
365   g_return_if_fail (buf != NULL);
366
367   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
368   g_return_if_fail (videoflip->inited);
369
370   data = GST_BUFFER_DATA(buf);
371   size = GST_BUFFER_SIZE(buf);
372
373   if(videoflip->passthru){
374     gst_pad_push(videoflip->srcpad, GST_DATA (buf));
375     return;
376   }
377
378   GST_DEBUG ("gst_videoflip_chain: got buffer of %ld bytes in '%s'",size,
379                               GST_OBJECT_NAME (videoflip));
380  
381   GST_DEBUG ("size=%ld from=%dx%d to=%dx%d fromsize=%ld (should be %d) tosize=%d",
382         size,
383         videoflip->from_width, videoflip->from_height,
384         videoflip->to_width, videoflip->to_height,
385         size, videoflip->from_buf_size,
386         videoflip->to_buf_size);
387
388   g_return_if_fail (size == videoflip->from_buf_size);
389
390   outbuf = gst_buffer_new();
391   /* FIXME: handle bufferpools */
392   GST_BUFFER_SIZE(outbuf) = videoflip->to_buf_size;
393   GST_BUFFER_DATA(outbuf) = g_malloc (videoflip->to_buf_size);
394   GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf);
395
396   g_return_if_fail(videoflip->format);
397   GST_DEBUG ("format %s",videoflip->format->fourcc);
398   g_return_if_fail(videoflip->format->scale);
399
400   videoflip->format->scale(videoflip, GST_BUFFER_DATA(outbuf), data);
401
402   GST_DEBUG ("gst_videoflip_chain: pushing buffer of %d bytes in '%s'",GST_BUFFER_SIZE(outbuf),
403                       GST_OBJECT_NAME (videoflip));
404
405   gst_pad_push(videoflip->srcpad, GST_DATA (outbuf));
406
407   gst_buffer_unref(buf);
408 }
409
410 static void
411 gst_videoflip_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
412 {
413   GstVideoflip *src;
414
415   /* it's not null if we got it, but it might not be ours */
416   g_return_if_fail(GST_IS_VIDEOFLIP(object));
417   src = GST_VIDEOFLIP(object);
418
419   GST_DEBUG ("gst_videoflip_set_property");
420   switch (prop_id) {
421     case ARG_METHOD:
422       src->method = g_value_get_enum (value);
423       break;
424     default:
425       break;
426   }
427 }
428
429 static void
430 gst_videoflip_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
431 {
432   GstVideoflip *src;
433
434   /* it's not null if we got it, but it might not be ours */
435   g_return_if_fail(GST_IS_VIDEOFLIP(object));
436   src = GST_VIDEOFLIP(object);
437
438   switch (prop_id) {
439     case ARG_METHOD:
440       g_value_set_enum (value, src->method);
441       break;
442     default:
443       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
444       break;
445   }
446 }
447
448
449 static gboolean
450 plugin_init (GstPlugin *plugin)
451 {
452   return gst_element_register (plugin, "videoflip", GST_RANK_NONE, GST_TYPE_VIDEOFLIP);
453 }
454
455 GST_PLUGIN_DEFINE (
456   GST_VERSION_MAJOR,
457   GST_VERSION_MINOR,
458   "videoflip",
459   "Resizes video",
460   plugin_init,
461   VERSION,
462   GST_LICENSE,
463   GST_COPYRIGHT,
464   GST_PACKAGE,
465   GST_ORIGIN
466 )