7ef18e4a9471e189c68600ee2eee817401e10743
[platform/upstream/gst-plugins-good.git] / gst / videofilter / gstvideofilter.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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 /*#define DEBUG_ENABLED */
27 #include <gstvideofilter.h>
28
29
30
31 /* GstVideofilter signals and args */
32 enum {
33   /* FILL ME */
34   LAST_SIGNAL
35 };
36
37 enum {
38   ARG_0,
39   ARG_METHOD,
40   /* FILL ME */
41 };
42
43 static void     gst_videofilter_base_init       (gpointer g_class);
44 static void     gst_videofilter_class_init      (gpointer g_class, gpointer class_data);
45 static void     gst_videofilter_init            (GTypeInstance *instance, gpointer g_class);
46
47 static void     gst_videofilter_set_property            (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
48 static void     gst_videofilter_get_property            (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
49
50 static void     gst_videofilter_chain           (GstPad *pad, GstData *_data);
51 GstCaps * gst_videofilter_class_get_capslist(GstVideofilterClass *klass);
52 static void gst_videofilter_setup(GstVideofilter *videofilter);
53
54 static GstElementClass *parent_class = NULL;
55
56 GType
57 gst_videofilter_get_type (void)
58 {
59   static GType videofilter_type = 0;
60
61   if (!videofilter_type) {
62     static const GTypeInfo videofilter_info = {
63       sizeof(GstVideofilterClass),
64       gst_videofilter_base_init,
65       NULL,
66       gst_videofilter_class_init,
67       NULL,
68       NULL,
69       sizeof(GstVideofilter),
70       0,
71       gst_videofilter_init,
72     };
73     videofilter_type = g_type_register_static(GST_TYPE_ELEMENT,
74         "GstVideofilter", &videofilter_info, G_TYPE_FLAG_ABSTRACT);
75   }
76   return videofilter_type;
77 }
78
79 static void gst_videofilter_base_init (gpointer g_class)
80 {
81   static GstElementDetails videofilter_details = {
82     "Video scaler",
83     "Filter/Effect/Video",
84     "Resizes video",
85     "David Schleef <ds@schleef.org>"
86   };
87   GstVideofilterClass *klass = (GstVideofilterClass *) g_class;
88   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
89
90   klass->formats = g_ptr_array_new();
91
92   gst_element_class_set_details (element_class, &videofilter_details);
93 }
94
95 static void gst_videofilter_class_init (gpointer g_class, gpointer class_data)
96 {
97   GObjectClass *gobject_class;
98   GstElementClass *gstelement_class;
99   GstVideofilterClass *klass;
100
101   klass = (GstVideofilterClass *)g_class;
102   gobject_class = (GObjectClass*)klass;
103   gstelement_class = (GstElementClass*)klass;
104
105   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
106
107   gobject_class->set_property = gst_videofilter_set_property;
108   gobject_class->get_property = gst_videofilter_get_property;
109 }
110
111 static GstStructure *gst_videofilter_format_get_structure(GstVideofilterFormat *format)
112 {
113   unsigned int fourcc;
114   GstStructure *structure;
115
116   if(format->filter_func==NULL)
117     return NULL;
118
119   fourcc = GST_MAKE_FOURCC(format->fourcc[0],format->fourcc[1],format->fourcc[2],format->fourcc[3]);
120
121   if(format->depth){
122     structure = gst_structure_new ("video/x-raw-rgb",
123         "depth", G_TYPE_INT, format->depth,
124         "bpp", G_TYPE_INT, format->bpp,
125         "endianness", G_TYPE_INT, format->endianness,
126         "red_mask", G_TYPE_INT, format->red_mask,
127         "green_mask", G_TYPE_INT, format->green_mask,
128         "blue_mask", G_TYPE_INT, format->blue_mask, NULL);
129   }else{
130     structure = gst_structure_new ("video/x-raw-yuv",
131         "format", GST_TYPE_FOURCC, fourcc, NULL);
132   }
133
134   gst_structure_set(structure,
135       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
136       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
137       "framerate", GST_TYPE_DOUBLE_RANGE, 0.0, G_MAXDOUBLE,
138       NULL);
139
140   return structure;
141 }
142
143 GstCaps * gst_videofilter_class_get_capslist(GstVideofilterClass *klass)
144 {
145   GstCaps *caps;
146   GstStructure *structure;
147   int i;
148
149   caps = gst_caps_new_empty();
150   for(i=0;i<klass->formats->len;i++){
151     structure = gst_videofilter_format_get_structure(g_ptr_array_index(klass->formats,i));
152     gst_caps_append_structure (caps, structure);
153   }
154
155   return caps;
156 }
157
158 static GstCaps *
159 gst_videofilter_getcaps (GstPad *pad)
160 {
161   GstVideofilter *videofilter;
162   GstVideofilterClass *klass;
163   //GstCaps *caps;
164   GstCaps *othercaps;
165   GstPad *otherpad;
166   //int i;
167
168   GST_DEBUG("gst_videofilter_getcaps");
169   videofilter = GST_VIDEOFILTER (gst_pad_get_parent (pad));
170   
171   klass = GST_VIDEOFILTER_CLASS(G_OBJECT_GET_CLASS(videofilter));
172
173   otherpad = (pad == videofilter->srcpad) ? videofilter->sinkpad :
174     videofilter->srcpad;
175
176   othercaps = gst_pad_get_allowed_caps (otherpad);
177
178   return othercaps;
179 #if 0
180   /* FIXME videofilter doesn't allow passthru of video formats it
181    * doesn't understand. */
182   /* Look through our list of caps and find those that match with
183    * the peer's formats.  Create a list of them. */
184   /* FIXME optimize if peercaps == NULL */
185   caps = gst_caps_new_empty ();
186   for(i=0;i<klass->formats->len;i++){
187     GstCaps *icaps;
188     GstCaps *fromcaps;
189     
190     fromcaps = gst_caps_new_full (gst_videofilter_format_get_structure (
191           g_ptr_array_index (klass->formats,i)), NULL);
192
193     icaps = gst_caps_intersect (fromcaps, peercaps);
194     if(icaps != NULL){
195       gst_caps_append (caps, fromcaps);
196     } else {
197       gst_caps_free (fromcaps);
198     }
199     if(icaps) gst_caps_free (icaps);
200   }
201   gst_caps_free (peercaps);
202
203   return caps;
204 #endif
205 }
206
207 static GstPadLinkReturn
208 gst_videofilter_link (GstPad *pad, const GstCaps *caps)
209 {
210   GstVideofilter *videofilter;
211   GstStructure *structure;
212   gboolean ret;
213   int width, height;
214   double framerate;
215   GstPadLinkReturn lret;
216   GstPad *otherpad;
217
218   GST_DEBUG("gst_videofilter_src_link");
219   videofilter = GST_VIDEOFILTER (gst_pad_get_parent (pad));
220
221   otherpad = (pad == videofilter->srcpad) ? videofilter->sinkpad :
222     videofilter->srcpad;
223
224   structure = gst_caps_get_structure (caps, 0);
225
226   videofilter->format = gst_videofilter_find_format_by_structure (
227       videofilter, structure);
228   g_return_val_if_fail(videofilter->format, GST_PAD_LINK_REFUSED);
229
230   ret = gst_structure_get_int (structure, "width", &width);
231   ret &= gst_structure_get_int (structure, "height", &height);
232   ret &= gst_structure_get_double (structure, "framerate", &framerate);
233
234   if (!ret) return GST_PAD_LINK_REFUSED;
235
236   lret = gst_pad_try_set_caps (otherpad, caps);
237   if (GST_PAD_LINK_FAILED (lret)) return lret;
238
239   GST_DEBUG("width %d height %d",width,height);
240
241 #if 0
242   if (pad == videofilter->srcpad) {
243     videofilter->to_width = width;
244     videofilter->to_height = height;
245   } else {
246     videofilter->from_width = width;
247     videofilter->from_height = height;
248   }
249 #endif
250   videofilter->to_width = width;
251   videofilter->to_height = height;
252   videofilter->from_width = width;
253   videofilter->from_height = height;
254   videofilter->framerate = framerate;
255
256   gst_videofilter_setup(videofilter);
257
258   return GST_PAD_LINK_OK;
259 }
260
261 static void
262 gst_videofilter_init (GTypeInstance *instance, gpointer g_class)
263 {
264   GstVideofilter *videofilter = GST_VIDEOFILTER (instance);
265   GstPadTemplate *pad_template;
266
267   GST_DEBUG("gst_videofilter_init");
268
269   pad_template = gst_element_class_get_pad_template(GST_ELEMENT_CLASS(g_class),
270       "sink");
271   g_return_if_fail(pad_template != NULL);
272   videofilter->sinkpad = gst_pad_new_from_template(pad_template, "sink");
273   gst_element_add_pad(GST_ELEMENT(videofilter),videofilter->sinkpad);
274   gst_pad_set_chain_function(videofilter->sinkpad,gst_videofilter_chain);
275   gst_pad_set_link_function(videofilter->sinkpad,gst_videofilter_link);
276   gst_pad_set_getcaps_function(videofilter->sinkpad,gst_videofilter_getcaps);
277
278   pad_template = gst_element_class_get_pad_template(GST_ELEMENT_CLASS(g_class),
279       "src");
280   g_return_if_fail(pad_template != NULL);
281   videofilter->srcpad = gst_pad_new_from_template(pad_template, "src");
282   gst_element_add_pad(GST_ELEMENT(videofilter),videofilter->srcpad);
283   gst_pad_set_link_function(videofilter->srcpad,gst_videofilter_link);
284   gst_pad_set_getcaps_function(videofilter->srcpad,gst_videofilter_getcaps);
285
286   videofilter->inited = FALSE;
287 }
288
289 static void
290 gst_videofilter_chain (GstPad *pad, GstData *_data)
291 {
292   GstBuffer *buf = GST_BUFFER (_data);
293   GstVideofilter *videofilter;
294   guchar *data;
295   gulong size;
296   GstBuffer *outbuf;
297
298   GST_DEBUG ("gst_videofilter_chain");
299
300   g_return_if_fail (pad != NULL);
301   g_return_if_fail (GST_IS_PAD (pad));
302   g_return_if_fail (buf != NULL);
303
304   videofilter = GST_VIDEOFILTER (gst_pad_get_parent (pad));
305   //g_return_if_fail (videofilter->inited);
306
307   data = GST_BUFFER_DATA(buf);
308   size = GST_BUFFER_SIZE(buf);
309
310   if(videofilter->passthru){
311     gst_pad_push(videofilter->srcpad, GST_DATA (buf));
312     return;
313   }
314
315   GST_DEBUG ("gst_videofilter_chain: got buffer of %ld bytes in '%s'",size,
316                               GST_OBJECT_NAME (videofilter));
317  
318   GST_DEBUG("size=%ld from=%dx%d to=%dx%d fromsize=%ld (should be %d) tosize=%d",
319         size,
320         videofilter->from_width, videofilter->from_height,
321         videofilter->to_width, videofilter->to_height,
322         size, videofilter->from_buf_size,
323         videofilter->to_buf_size);
324
325   g_return_if_fail (size >= videofilter->from_buf_size);
326
327   if (size > videofilter->from_buf_size) {
328     GST_INFO("buffer size %ld larger than expected (%d)",
329         size, videofilter->from_buf_size);
330   }
331
332   outbuf = gst_pad_alloc_buffer(videofilter->srcpad, GST_BUFFER_OFFSET_NONE,
333       videofilter->to_buf_size);
334   GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf);
335   GST_BUFFER_DURATION(outbuf) = GST_BUFFER_DURATION(buf);
336
337   g_return_if_fail(videofilter->format);
338   GST_DEBUG ("format %s",videofilter->format->fourcc);
339
340   videofilter->in_buf = buf;
341   videofilter->out_buf = outbuf;
342
343   videofilter->format->filter_func(videofilter, GST_BUFFER_DATA(outbuf), data);
344
345   GST_DEBUG ("gst_videofilter_chain: pushing buffer of %d bytes in '%s'",GST_BUFFER_SIZE(outbuf),
346                       GST_OBJECT_NAME (videofilter));
347
348   gst_pad_push(videofilter->srcpad, GST_DATA (outbuf));
349
350   gst_buffer_unref(buf);
351 }
352
353 static void
354 gst_videofilter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
355 {
356   GstVideofilter *src;
357
358   /* it's not null if we got it, but it might not be ours */
359   g_return_if_fail(GST_IS_VIDEOFILTER(object));
360   src = GST_VIDEOFILTER(object);
361
362   GST_DEBUG("gst_videofilter_set_property");
363   switch (prop_id) {
364     default:
365       break;
366   }
367 }
368
369 static void
370 gst_videofilter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
371 {
372   GstVideofilter *src;
373
374   /* it's not null if we got it, but it might not be ours */
375   g_return_if_fail(GST_IS_VIDEOFILTER(object));
376   src = GST_VIDEOFILTER(object);
377
378   switch (prop_id) {
379     default:
380       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
381       break;
382   }
383 }
384
385 int gst_videofilter_get_input_width(GstVideofilter *videofilter)
386 {
387   g_return_val_if_fail(GST_IS_VIDEOFILTER(videofilter),0);
388
389   return videofilter->from_width;
390 }
391
392 int gst_videofilter_get_input_height(GstVideofilter *videofilter)
393 {
394   g_return_val_if_fail(GST_IS_VIDEOFILTER(videofilter),0);
395
396   return videofilter->from_height;
397 }
398
399 void gst_videofilter_set_output_size(GstVideofilter *videofilter,
400     int width, int height)
401 {
402   int ret;
403   GstCaps *srccaps;
404   GstStructure *structure;
405
406   g_return_if_fail(GST_IS_VIDEOFILTER(videofilter));
407
408   videofilter->to_width = width;
409   videofilter->to_height = height;
410
411   videofilter->to_buf_size = (videofilter->to_width * videofilter->to_height
412       * videofilter->format->bpp)/8;
413
414   srccaps = gst_caps_copy (gst_pad_get_negotiated_caps(videofilter->srcpad));
415   structure = gst_caps_get_structure (srccaps, 0);
416
417   gst_structure_set (structure, "width", G_TYPE_INT, width,
418       "height", G_TYPE_INT, height, NULL);
419
420   ret = gst_pad_try_set_caps (videofilter->srcpad, srccaps);
421
422   if (ret < 0) {
423     g_critical ("could not set output size");
424   }
425 }
426
427 static void gst_videofilter_setup(GstVideofilter *videofilter)
428 {
429   GstVideofilterClass *klass;
430
431   klass = GST_VIDEOFILTER_CLASS(G_OBJECT_GET_CLASS(videofilter));
432
433   if(klass->setup){
434     klass->setup(videofilter);
435   }
436
437   if(videofilter->to_width == 0){
438     videofilter->to_width = videofilter->from_width;
439   }
440   if(videofilter->to_height == 0){
441     videofilter->to_height = videofilter->from_height;
442   }
443
444   g_return_if_fail(videofilter->format != NULL);
445   g_return_if_fail(videofilter->from_width > 0);
446   g_return_if_fail(videofilter->from_height > 0);
447   g_return_if_fail(videofilter->to_width > 0);
448   g_return_if_fail(videofilter->to_height > 0);
449
450   videofilter->from_buf_size = (videofilter->from_width * videofilter->from_height *
451       videofilter->format->bpp) / 8;
452   videofilter->to_buf_size = (videofilter->to_width * videofilter->to_height *
453       videofilter->format->bpp) / 8;
454
455   videofilter->inited = TRUE;
456 }
457
458 GstVideofilterFormat *gst_videofilter_find_format_by_structure (
459     GstVideofilter *videofilter, const GstStructure *structure)
460 {
461   int i;
462   GstVideofilterClass *klass;
463   GstVideofilterFormat *format;
464   gboolean ret;
465
466   klass = GST_VIDEOFILTER_CLASS(G_OBJECT_GET_CLASS(videofilter));
467
468   g_return_val_if_fail(structure != NULL, NULL);
469
470   if (strcmp (gst_structure_get_name (structure), "video/x-raw-yuv") == 0) {
471     guint32 fourcc;
472
473     ret = gst_structure_get_fourcc (structure, "format", &fourcc);
474     if (!ret) return NULL;
475     for(i=0;i<klass->formats->len;i++){
476       guint32 format_fourcc;
477       format = g_ptr_array_index(klass->formats,i);
478       format_fourcc = GST_STR_FOURCC (format->fourcc);
479       if (format->depth == 0 && format_fourcc == fourcc) {
480         return format;
481       }
482     }
483   } else if (strcmp (gst_structure_get_name (structure), "video/x-raw-rgb")
484       == 0) {
485     int bpp;
486     int depth;
487     int endianness;
488     int red_mask;
489     int green_mask;
490     int blue_mask;
491
492     ret = gst_structure_get_int (structure, "bpp", &bpp);
493     ret &= gst_structure_get_int (structure, "depth", &depth);
494     ret &= gst_structure_get_int (structure, "endianness", &endianness);
495     ret &= gst_structure_get_int (structure, "red_mask", &red_mask);
496     ret &= gst_structure_get_int (structure, "green_mask", &green_mask);
497     ret &= gst_structure_get_int (structure, "blue_mask", &blue_mask);
498     if (!ret) return NULL;
499     for(i=0;i<klass->formats->len;i++){
500       format = g_ptr_array_index(klass->formats,i);
501       if (format->bpp == bpp && format->depth == depth &&
502           format->endianness == endianness && format->red_mask == red_mask &&
503           format->green_mask == green_mask && format->blue_mask == blue_mask) {
504         return format;
505       }
506     }
507   }
508
509   return NULL;
510 }
511
512 void gst_videofilter_class_add_format(GstVideofilterClass *videofilterclass,
513     GstVideofilterFormat *format)
514 {
515   g_ptr_array_add(videofilterclass->formats, format);
516 }
517
518 void gst_videofilter_class_add_pad_templates (GstVideofilterClass *videofilter_class)
519 {
520   GstElementClass *element_class = GST_ELEMENT_CLASS (videofilter_class);
521
522   gst_element_class_add_pad_template (element_class,
523       gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS,
524         gst_videofilter_class_get_capslist (videofilter_class)));
525
526   gst_element_class_add_pad_template (element_class,
527       gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
528         gst_videofilter_class_get_capslist (videofilter_class)));
529 }
530
531 static gboolean
532 plugin_init (GstPlugin *plugin)
533 {
534   return TRUE;
535 }
536
537 GST_PLUGIN_DEFINE (
538   GST_VERSION_MAJOR,
539   GST_VERSION_MINOR,
540   "gstvideofilter",
541   "Video filter parent class",
542   plugin_init,
543   VERSION,
544   "LGPL",
545   GST_PACKAGE,
546   GST_ORIGIN
547 )