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