0f20f47b97666c6ae8b3176edc540f0cb4acef4e
[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
61 static GstElementClass *parent_class = NULL;
62
63 #define GST_TYPE_VIDEOFLIP_METHOD (gst_videoflip_method_get_type())
64
65 static GType
66 gst_videoflip_method_get_type(void)
67 {
68   static GType videoflip_method_type = 0;
69   static GEnumValue videoflip_methods[] = {
70     { GST_VIDEOFLIP_METHOD_IDENTITY,    "0", "Identity (no rotation)" },
71     { GST_VIDEOFLIP_METHOD_90R,         "1", "Rotate right 90 degrees" },
72     { GST_VIDEOFLIP_METHOD_180,         "2", "Rotate 180 degrees" },
73     { GST_VIDEOFLIP_METHOD_90L,         "3", "Rotate left 90 degrees" },
74     { GST_VIDEOFLIP_METHOD_HORIZ,       "4", "Flip horizontally" },
75     { GST_VIDEOFLIP_METHOD_VERT,        "5", "Flip vertically" },
76     { GST_VIDEOFLIP_METHOD_TRANS,       "6", "Flip across upper left/lower right diagonal" },
77     { GST_VIDEOFLIP_METHOD_OTHER,       "7", "Flip across upper right/lower left diagonal" },
78     { 0, NULL, NULL },
79   };
80   if(!videoflip_method_type){
81     videoflip_method_type = g_enum_register_static("GstVideoflipMethod",
82         videoflip_methods);
83   }
84   return videoflip_method_type;
85 }
86
87 static GstPadTemplate *
88 gst_videoflip_src_template_factory(void)
89 {
90   /* well, actually RGB too, but since there's no RGB format anyway */
91   GstCaps *caps = gst_caps_from_string ("video/x-raw-yuv, "
92               "width = (int) [ 0, MAX ], "
93               "height = (int) [ 0, MAX ], "
94               "framerate = (double) [ 0, MAX ]");
95
96   caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
97
98   return gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
99 }
100
101 static GstPadTemplate *
102 gst_videoflip_sink_template_factory(void)
103 {
104   GstCaps *caps = gst_caps_from_string ("video/x-raw-yuv, "
105               "width = (int) [ 0, MAX ], "
106               "height = (int) [ 0, MAX ], "
107               "framerate = (double) [ 0, MAX ]");
108
109   caps = gst_caps_intersect(caps, gst_videoflip_get_capslist ());
110
111   return gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
112 }
113
114 GType
115 gst_videoflip_get_type (void)
116 {
117   static GType videoflip_type = 0;
118
119   if (!videoflip_type) {
120     static const GTypeInfo videoflip_info = {
121       sizeof(GstVideoflipClass),
122       gst_videoflip_base_init,
123       NULL,
124       (GClassInitFunc)gst_videoflip_class_init,
125       NULL,
126       NULL,
127       sizeof(GstVideoflip),
128       0,
129       (GInstanceInitFunc)gst_videoflip_init,
130     };
131     videoflip_type = g_type_register_static(GST_TYPE_ELEMENT, "GstVideoflip", &videoflip_info, 0);
132   }
133   return videoflip_type;
134 }
135
136 static void
137 gst_videoflip_base_init (gpointer g_class)
138 {
139   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
140
141   gst_element_class_set_details (element_class, &videoflip_details);
142
143   gst_element_class_add_pad_template (element_class,
144       gst_videoflip_sink_template_factory ());
145   gst_element_class_add_pad_template (element_class,
146       gst_videoflip_src_template_factory ());
147 }
148 static void
149 gst_videoflip_class_init (GstVideoflipClass *klass)
150 {
151   GObjectClass *gobject_class;
152   GstElementClass *gstelement_class;
153
154   gobject_class = (GObjectClass*)klass;
155   gstelement_class = (GstElementClass*)klass;
156
157   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_METHOD,
158       g_param_spec_enum("method","method","method",
159       GST_TYPE_VIDEOFLIP_METHOD, GST_VIDEOFLIP_METHOD_90R,
160       G_PARAM_READWRITE));
161
162   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
163
164   gobject_class->set_property = gst_videoflip_set_property;
165   gobject_class->get_property = gst_videoflip_get_property;
166
167 }
168
169 static GstCaps *
170 gst_videoflip_get_capslist(void)
171 {
172   GstCaps *caps;
173   GstStructure *structure;
174   int i;
175
176   caps = gst_caps_new_empty ();
177   for(i=0;i<videoflip_n_formats;i++){
178     structure = videoflip_get_cap (videoflip_formats + i);
179     gst_caps_append_structure (caps, structure);
180   }
181
182   return caps;
183 }
184
185 static GstCaps *
186 gst_videoflip_sink_getcaps (GstPad *pad)
187 {
188   GstVideoflip *videoflip;
189   GstCaps *capslist = NULL;
190   GstCaps *peercaps;
191   GstCaps *sizecaps;
192   GstCaps *caps;
193   int i;
194
195   GST_DEBUG ("gst_videoflip_sink_getcaps");
196   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
197   
198   /* get list of peer's caps */
199   if(pad == videoflip->srcpad){
200     peercaps = gst_pad_get_allowed_caps (videoflip->sinkpad);
201   }else{
202     peercaps = gst_pad_get_allowed_caps (videoflip->srcpad);
203   }
204
205   /* FIXME videoflip doesn't allow passthru of video formats it
206    * doesn't understand. */
207   /* Look through our list of caps and find those that match with
208    * the peer's formats.  Create a list of them. */
209   for(i=0;i<videoflip_n_formats;i++){
210     GstCaps *fromcaps = gst_caps_new_full(videoflip_get_cap(
211           videoflip_formats + i), NULL);
212     if(gst_caps_is_always_compatible(fromcaps, peercaps)){
213       gst_caps_append(capslist, fromcaps);
214     }
215   }
216   gst_caps_free (peercaps);
217
218   sizecaps = gst_caps_from_string ("video/x-raw-yuv, "
219               "width = (int) [ 0, MAX ], "
220               "height = (int) [ 0, MAX ], "
221               "framerate = (double) [ 0, MAX ]");
222
223   caps = gst_caps_intersect(capslist, sizecaps);
224   gst_caps_free (sizecaps);
225
226   return caps;
227 }
228
229
230 static GstPadLinkReturn
231 gst_videoflip_src_link (GstPad *pad, const GstCaps *caps)
232 {
233   GstVideoflip *videoflip;
234   GstStructure *structure;
235   gboolean ret;
236
237   GST_DEBUG ("gst_videoflip_src_link");
238   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
239   
240   structure = gst_caps_get_structure (caps, 0);
241
242   videoflip->format = videoflip_find_by_caps (caps);
243   g_return_val_if_fail(videoflip->format, GST_PAD_LINK_REFUSED);
244
245   ret = gst_structure_get_int (structure, "width", &videoflip->to_width);
246   ret &= gst_structure_get_int (structure, "height", &videoflip->to_height);
247
248   if (!ret) return GST_PAD_LINK_REFUSED;
249
250   return GST_PAD_LINK_OK;
251 }
252
253 static GstPadLinkReturn
254 gst_videoflip_sink_link (GstPad *pad, const GstCaps *caps)
255 {
256   GstVideoflip *videoflip;
257   GstStructure *structure;
258   gboolean ret;
259
260   GST_DEBUG ("gst_videoflip_sink_link");
261   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
262   
263   structure = gst_caps_get_structure (caps, 0);
264
265   videoflip->format = videoflip_find_by_caps (caps);
266   g_return_val_if_fail(videoflip->format, GST_PAD_LINK_REFUSED);
267
268   ret = gst_structure_get_int (structure, "width", &videoflip->from_width);
269   ret &= gst_structure_get_int (structure, "height", &videoflip->from_height);
270
271   if (!ret) return GST_PAD_LINK_REFUSED;
272
273   return GST_PAD_LINK_OK;
274 }
275
276 static void
277 gst_videoflip_init (GstVideoflip *videoflip)
278 {
279   GST_DEBUG ("gst_videoflip_init");
280   videoflip->sinkpad = gst_pad_new_from_template (
281                   gst_videoflip_sink_template_factory(),
282                   "sink");
283   gst_element_add_pad(GST_ELEMENT(videoflip),videoflip->sinkpad);
284   gst_pad_set_chain_function(videoflip->sinkpad,gst_videoflip_chain);
285   gst_pad_set_link_function(videoflip->sinkpad,gst_videoflip_sink_link);
286   gst_pad_set_getcaps_function(videoflip->sinkpad,gst_videoflip_sink_getcaps);
287
288   videoflip->srcpad = gst_pad_new_from_template (
289                   gst_videoflip_src_template_factory(),
290                   "src");
291   gst_element_add_pad(GST_ELEMENT(videoflip),videoflip->srcpad);
292   gst_pad_set_link_function(videoflip->srcpad,gst_videoflip_src_link);
293   //gst_pad_set_getcaps_function(videoflip->srcpad,gst_videoflip_getcaps);
294
295   videoflip->inited = FALSE;
296   videoflip->force_size = FALSE;
297 }
298
299
300 static void
301 gst_videoflip_chain (GstPad *pad, GstData *_data)
302 {
303   GstBuffer *buf = GST_BUFFER (_data);
304   GstVideoflip *videoflip;
305   guchar *data;
306   gulong size;
307   GstBuffer *outbuf;
308
309   GST_DEBUG ("gst_videoflip_chain");
310
311   g_return_if_fail (pad != NULL);
312   g_return_if_fail (GST_IS_PAD (pad));
313   g_return_if_fail (buf != NULL);
314
315   videoflip = GST_VIDEOFLIP (gst_pad_get_parent (pad));
316   g_return_if_fail (videoflip->inited);
317
318   data = GST_BUFFER_DATA(buf);
319   size = GST_BUFFER_SIZE(buf);
320
321   if(videoflip->passthru){
322     gst_pad_push(videoflip->srcpad, GST_DATA (buf));
323     return;
324   }
325
326   GST_DEBUG ("gst_videoflip_chain: got buffer of %ld bytes in '%s'",size,
327                               GST_OBJECT_NAME (videoflip));
328  
329   GST_DEBUG ("size=%ld from=%dx%d to=%dx%d fromsize=%ld (should be %d) tosize=%d",
330         size,
331         videoflip->from_width, videoflip->from_height,
332         videoflip->to_width, videoflip->to_height,
333         size, videoflip->from_buf_size,
334         videoflip->to_buf_size);
335
336   g_return_if_fail (size == videoflip->from_buf_size);
337
338   outbuf = gst_buffer_new();
339   /* FIXME: handle bufferpools */
340   GST_BUFFER_SIZE(outbuf) = videoflip->to_buf_size;
341   GST_BUFFER_DATA(outbuf) = g_malloc (videoflip->to_buf_size);
342   GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf);
343
344   g_return_if_fail(videoflip->format);
345   GST_DEBUG ("format %s",videoflip->format->fourcc);
346   g_return_if_fail(videoflip->format->scale);
347
348   videoflip->format->scale(videoflip, GST_BUFFER_DATA(outbuf), data);
349
350   GST_DEBUG ("gst_videoflip_chain: pushing buffer of %d bytes in '%s'",GST_BUFFER_SIZE(outbuf),
351                       GST_OBJECT_NAME (videoflip));
352
353   gst_pad_push(videoflip->srcpad, GST_DATA (outbuf));
354
355   gst_buffer_unref(buf);
356 }
357
358 static void
359 gst_videoflip_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
360 {
361   GstVideoflip *src;
362
363   /* it's not null if we got it, but it might not be ours */
364   g_return_if_fail(GST_IS_VIDEOFLIP(object));
365   src = GST_VIDEOFLIP(object);
366
367   GST_DEBUG ("gst_videoflip_set_property");
368   switch (prop_id) {
369     case ARG_METHOD:
370       src->method = g_value_get_enum (value);
371       break;
372     default:
373       break;
374   }
375 }
376
377 static void
378 gst_videoflip_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
379 {
380   GstVideoflip *src;
381
382   /* it's not null if we got it, but it might not be ours */
383   g_return_if_fail(GST_IS_VIDEOFLIP(object));
384   src = GST_VIDEOFLIP(object);
385
386   switch (prop_id) {
387     case ARG_METHOD:
388       g_value_set_enum (value, src->method);
389       break;
390     default:
391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392       break;
393   }
394 }
395
396
397 static gboolean
398 plugin_init (GstPlugin *plugin)
399 {
400   return gst_element_register (plugin, "videoflip", GST_RANK_NONE, GST_TYPE_VIDEOFLIP);
401 }
402
403 GST_PLUGIN_DEFINE (
404   GST_VERSION_MAJOR,
405   GST_VERSION_MINOR,
406   "videoflip",
407   "Resizes video",
408   plugin_init,
409   VERSION,
410   GST_LICENSE,
411   GST_PACKAGE,
412   GST_ORIGIN
413 )