various: fix pad template leaks
[platform/upstream/gstreamer.git] / ext / gdk_pixbuf / gstgdkpixbuf.c
1 /* GStreamer GdkPixbuf-based image decoder
2  * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2003 David A. 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 #include <gst/gst.h>
25 #include <gst/video/video.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <string.h>
28
29 #include "gstgdkpixbuf.h"
30 #include "gstgdkpixbufsink.h"
31 #include "pixbufscale.h"
32
33 GST_DEBUG_CATEGORY_STATIC (gst_gdk_pixbuf_debug);
34 #define GST_CAT_DEFAULT gst_gdk_pixbuf_debug
35
36 enum
37 {
38   ARG_0,
39   ARG_SILENT                    /* FIXME 0.11: remove */
40 };
41
42 static GstStaticPadTemplate gst_gdk_pixbuf_sink_template =
43     GST_STATIC_PAD_TEMPLATE ("sink",
44     GST_PAD_SINK,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("image/png; "
47         /* "image/jpeg; " disabled because we can't handle MJPEG */
48         "image/gif; "
49         "image/x-icon; "
50         "application/x-navi-animation; "
51         "image/x-cmu-raster; "
52         "image/x-sun-raster; "
53         "image/x-pixmap; "
54         "image/tiff; "
55         "image/x-portable-anymap; "
56         "image/x-portable-bitmap; "
57         "image/x-portable-graymap; "
58         "image/x-portable-pixmap; "
59         "image/bmp; "
60         "image/x-bmp; "
61         "image/x-MS-bmp; "
62         "image/vnd.wap.wbmp; " "image/x-bitmap; " "image/x-tga; "
63         "image/x-pcx; image/svg; image/svg+xml")
64     );
65
66 static GstStaticPadTemplate gst_gdk_pixbuf_src_template =
67     GST_STATIC_PAD_TEMPLATE ("src",
68     GST_PAD_SRC,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB "; " GST_VIDEO_CAPS_RGBA)
71     );
72
73 static void gst_gdk_pixbuf_set_property (GObject * object, guint prop_id,
74     const GValue * value, GParamSpec * pspec);
75 static void gst_gdk_pixbuf_get_property (GObject * object, guint prop_id,
76     GValue * value, GParamSpec * pspec);
77
78 static GstStateChangeReturn
79 gst_gdk_pixbuf_change_state (GstElement * element, GstStateChange transition);
80 static GstFlowReturn gst_gdk_pixbuf_chain (GstPad * pad, GstBuffer * buffer);
81 static gboolean gst_gdk_pixbuf_sink_event (GstPad * pad, GstEvent * event);
82
83 #ifdef enable_typefind
84 static void gst_gdk_pixbuf_type_find (GstTypeFind * tf, gpointer ignore);
85 #endif
86
87 GST_BOILERPLATE (GstGdkPixbuf, gst_gdk_pixbuf, GstElement, GST_TYPE_ELEMENT);
88
89 static gboolean
90 gst_gdk_pixbuf_sink_setcaps (GstPad * pad, GstCaps * caps)
91 {
92   GstGdkPixbuf *filter;
93   const GValue *framerate;
94   GstStructure *s;
95
96   filter = GST_GDK_PIXBUF (GST_PAD_PARENT (pad));
97   s = gst_caps_get_structure (caps, 0);
98
99   if ((framerate = gst_structure_get_value (s, "framerate")) != NULL) {
100     filter->framerate_numerator = gst_value_get_fraction_numerator (framerate);
101     filter->framerate_denominator =
102         gst_value_get_fraction_denominator (framerate);
103     GST_DEBUG_OBJECT (filter, "got framerate of %d/%d fps => packetized mode",
104         filter->framerate_numerator, filter->framerate_denominator);
105   } else {
106     filter->framerate_numerator = 0;
107     filter->framerate_denominator = 1;
108     GST_DEBUG_OBJECT (filter, "no framerate, assuming single image");
109   }
110
111   return TRUE;
112 }
113
114 static GstCaps *
115 gst_gdk_pixbuf_get_capslist (void)
116 {
117   GSList *slist;
118   GSList *slist0;
119   GstCaps *capslist = NULL;
120   GstCaps *return_caps = NULL;
121   GstCaps *tmpl_caps;
122
123   capslist = gst_caps_new_empty ();
124   slist0 = gdk_pixbuf_get_formats ();
125
126   for (slist = slist0; slist; slist = g_slist_next (slist)) {
127     GdkPixbufFormat *pixbuf_format;
128     char **mimetypes;
129     char **mimetype;
130
131     pixbuf_format = slist->data;
132     mimetypes = gdk_pixbuf_format_get_mime_types (pixbuf_format);
133
134     for (mimetype = mimetypes; *mimetype; mimetype++) {
135       gst_caps_append_structure (capslist, gst_structure_new (*mimetype, NULL));
136     }
137     g_strfreev (mimetypes);
138   }
139   g_slist_free (slist0);
140
141   tmpl_caps = gst_static_caps_get (&gst_gdk_pixbuf_sink_template.static_caps);
142   return_caps = gst_caps_intersect (capslist, tmpl_caps);
143
144   gst_caps_unref (tmpl_caps);
145   gst_caps_unref (capslist);
146   return return_caps;
147 }
148
149 static GstCaps *
150 gst_gdk_pixbuf_sink_getcaps (GstPad * pad)
151 {
152   return gst_gdk_pixbuf_get_capslist ();
153 }
154
155 static void
156 gst_gdk_pixbuf_base_init (gpointer g_class)
157 {
158   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
159
160   gst_element_class_add_static_pad_template (element_class,
161       &gst_gdk_pixbuf_src_template);
162   gst_element_class_add_static_pad_template (element_class,
163       &gst_gdk_pixbuf_sink_template);
164   gst_element_class_set_details_simple (element_class,
165       "GdkPixbuf image decoder", "Codec/Decoder/Image",
166       "Decodes images in a video stream using GdkPixbuf",
167       "David A. Schleef <ds@schleef.org>, Renato Filho <renato.filho@indt.org.br>");
168 }
169
170 /* initialize the plugin's class */
171 static void
172 gst_gdk_pixbuf_class_init (GstGdkPixbufClass * klass)
173 {
174   GObjectClass *gobject_class;
175   GstElementClass *gstelement_class;
176
177   gobject_class = (GObjectClass *) klass;
178   gstelement_class = (GstElementClass *) klass;
179
180   gobject_class->set_property = gst_gdk_pixbuf_set_property;
181   gobject_class->get_property = gst_gdk_pixbuf_get_property;
182
183   g_object_class_install_property (gobject_class, ARG_SILENT,
184       g_param_spec_boolean ("silent", "Silent",
185           "Produce verbose output ? (deprecated)", FALSE,
186           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
187
188   gstelement_class->change_state =
189       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_change_state);
190 }
191
192 static void
193 gst_gdk_pixbuf_init (GstGdkPixbuf * filter, GstGdkPixbufClass * klass)
194 {
195   filter->sinkpad =
196       gst_pad_new_from_static_template (&gst_gdk_pixbuf_sink_template, "sink");
197   gst_pad_set_setcaps_function (filter->sinkpad,
198       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_setcaps));
199   gst_pad_set_getcaps_function (filter->sinkpad,
200       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_getcaps));
201   gst_pad_set_chain_function (filter->sinkpad,
202       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_chain));
203   gst_pad_set_event_function (filter->sinkpad,
204       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_event));
205   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
206
207   filter->srcpad =
208       gst_pad_new_from_static_template (&gst_gdk_pixbuf_src_template, "src");
209   gst_pad_use_fixed_caps (filter->srcpad);
210   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
211
212   filter->last_timestamp = GST_CLOCK_TIME_NONE;
213   filter->pixbuf_loader = NULL;
214 }
215
216 static GstFlowReturn
217 gst_gdk_pixbuf_flush (GstGdkPixbuf * filter)
218 {
219   GstBuffer *outbuf;
220   GdkPixbuf *pixbuf;
221   int y;
222   guint8 *out_pix;
223   guint8 *in_pix;
224   int in_rowstride;
225   GstFlowReturn ret;
226   GstCaps *caps = NULL;
227   gint n_channels;
228
229   pixbuf = gdk_pixbuf_loader_get_pixbuf (filter->pixbuf_loader);
230   if (pixbuf == NULL)
231     goto no_pixbuf;
232
233   if (filter->image_size == 0) {
234     filter->width = gdk_pixbuf_get_width (pixbuf);
235     filter->height = gdk_pixbuf_get_height (pixbuf);
236     filter->rowstride = gdk_pixbuf_get_rowstride (pixbuf);
237     filter->image_size = filter->rowstride * filter->height;
238
239     n_channels = gdk_pixbuf_get_n_channels (pixbuf);
240     switch (n_channels) {
241       case 3:
242         caps = gst_caps_from_string (GST_VIDEO_CAPS_RGB);
243         break;
244       case 4:
245         caps = gst_caps_from_string (GST_VIDEO_CAPS_RGBA);
246         break;
247       default:
248         goto channels_not_supported;
249     }
250
251     gst_caps_set_simple (caps,
252         "width", G_TYPE_INT, filter->width,
253         "height", G_TYPE_INT, filter->height,
254         "framerate", GST_TYPE_FRACTION, filter->framerate_numerator,
255         filter->framerate_denominator, NULL);
256
257     GST_DEBUG ("Set size to %dx%d", filter->width, filter->height);
258     gst_pad_set_caps (filter->srcpad, caps);
259     gst_caps_unref (caps);
260   }
261
262   ret = gst_pad_alloc_buffer_and_set_caps (filter->srcpad,
263       GST_BUFFER_OFFSET_NONE,
264       filter->image_size, GST_PAD_CAPS (filter->srcpad), &outbuf);
265
266   if (ret != GST_FLOW_OK)
267     goto no_buffer;
268
269   GST_BUFFER_TIMESTAMP (outbuf) = filter->last_timestamp;
270   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
271
272   in_pix = gdk_pixbuf_get_pixels (pixbuf);
273   in_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
274   out_pix = GST_BUFFER_DATA (outbuf);
275
276   /* FIXME, last line might not have rowstride pixels */
277   for (y = 0; y < filter->height; y++) {
278     memcpy (out_pix, in_pix, filter->rowstride);
279     in_pix += in_rowstride;
280     out_pix += filter->rowstride;
281   }
282
283   GST_DEBUG ("pushing... %d bytes", GST_BUFFER_SIZE (outbuf));
284   ret = gst_pad_push (filter->srcpad, outbuf);
285
286   if (ret != GST_FLOW_OK)
287     GST_DEBUG_OBJECT (filter, "flow: %s", gst_flow_get_name (ret));
288
289   return ret;
290
291   /* ERRORS */
292 no_pixbuf:
293   {
294     GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL), ("error geting pixbuf"));
295     return GST_FLOW_ERROR;
296   }
297 channels_not_supported:
298   {
299     GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
300         ("%d channels not supported", n_channels));
301     return GST_FLOW_ERROR;
302   }
303 no_buffer:
304   {
305     GST_DEBUG ("Failed to create outbuffer - %s", gst_flow_get_name (ret));
306     return ret;
307   }
308 }
309
310 static gboolean
311 gst_gdk_pixbuf_sink_event (GstPad * pad, GstEvent * event)
312 {
313   GstFlowReturn res = GST_FLOW_OK;
314   gboolean ret = TRUE;
315   GstGdkPixbuf *pixbuf;
316
317   pixbuf = GST_GDK_PIXBUF (gst_pad_get_parent (pad));
318
319   switch (GST_EVENT_TYPE (event)) {
320     case GST_EVENT_EOS:
321       if (pixbuf->pixbuf_loader != NULL) {
322         gdk_pixbuf_loader_close (pixbuf->pixbuf_loader, NULL);
323         res = gst_gdk_pixbuf_flush (pixbuf);
324         g_object_unref (G_OBJECT (pixbuf->pixbuf_loader));
325         pixbuf->pixbuf_loader = NULL;
326         /* as long as we don't have flow returns for event functions we need
327          * to post an error here, or the application might never know that
328          * things failed */
329         if (res != GST_FLOW_OK && res != GST_FLOW_WRONG_STATE) {
330           GST_ELEMENT_ERROR (pixbuf, STREAM, FAILED, (NULL),
331               ("Flow: %s", gst_flow_get_name (res)));
332         }
333       }
334       break;
335     case GST_EVENT_NEWSEGMENT:
336     case GST_EVENT_FLUSH_STOP:
337       if (pixbuf->pixbuf_loader != NULL) {
338         gdk_pixbuf_loader_close (pixbuf->pixbuf_loader, NULL);
339         g_object_unref (G_OBJECT (pixbuf->pixbuf_loader));
340         pixbuf->pixbuf_loader = NULL;
341       }
342       break;
343     default:
344       break;
345   }
346
347   if (res == GST_FLOW_OK) {
348     ret = gst_pad_event_default (pad, event);
349   } else {
350     ret = FALSE;
351   }
352
353   gst_object_unref (pixbuf);
354
355   return ret;
356 }
357
358 static GstFlowReturn
359 gst_gdk_pixbuf_chain (GstPad * pad, GstBuffer * buf)
360 {
361   GstGdkPixbuf *filter;
362   GstFlowReturn ret = GST_FLOW_OK;
363   GError *error = NULL;
364   GstClockTime timestamp;
365   guint8 *data;
366   guint size;
367
368   filter = GST_GDK_PIXBUF (gst_pad_get_parent (pad));
369
370   timestamp = GST_BUFFER_TIMESTAMP (buf);
371
372   if (GST_CLOCK_TIME_IS_VALID (timestamp))
373     filter->last_timestamp = timestamp;
374
375   GST_LOG_OBJECT (filter, "buffer with ts: %" GST_TIME_FORMAT,
376       GST_TIME_ARGS (timestamp));
377
378   if (filter->pixbuf_loader == NULL)
379     filter->pixbuf_loader = gdk_pixbuf_loader_new ();
380
381   data = GST_BUFFER_DATA (buf);
382   size = GST_BUFFER_SIZE (buf);
383
384   GST_LOG_OBJECT (filter, "Writing buffer size %d", size);
385   if (!gdk_pixbuf_loader_write (filter->pixbuf_loader, data, size, &error))
386     goto error;
387
388   /* packetised mode? */
389   if (filter->framerate_numerator != 0) {
390     gdk_pixbuf_loader_close (filter->pixbuf_loader, NULL);
391     ret = gst_gdk_pixbuf_flush (filter);
392     g_object_unref (filter->pixbuf_loader);
393     filter->pixbuf_loader = NULL;
394   }
395
396   gst_buffer_unref (buf);
397   gst_object_unref (filter);
398
399   return ret;
400
401   /* ERRORS */
402 error:
403   {
404     GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
405         ("gdk_pixbuf_loader_write error: %s", error->message));
406     g_error_free (error);
407     gst_buffer_unref (buf);
408     gst_object_unref (filter);
409     return GST_FLOW_ERROR;
410   }
411 }
412
413 static void
414 gst_gdk_pixbuf_set_property (GObject * object, guint prop_id,
415     const GValue * value, GParamSpec * pspec)
416 {
417   switch (prop_id) {
418     case ARG_SILENT:
419       /* filter->silent = g_value_get_boolean (value); */
420       break;
421     default:
422       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
423       break;
424   }
425 }
426
427 static void
428 gst_gdk_pixbuf_get_property (GObject * object, guint prop_id,
429     GValue * value, GParamSpec * pspec)
430 {
431   switch (prop_id) {
432     case ARG_SILENT:
433       /* g_value_set_boolean (value, filter->silent); */
434       break;
435     default:
436       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
437       break;
438   }
439 }
440
441 static GstStateChangeReturn
442 gst_gdk_pixbuf_change_state (GstElement * element, GstStateChange transition)
443 {
444   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
445   GstGdkPixbuf *dec = GST_GDK_PIXBUF (element);
446
447   switch (transition) {
448     case GST_STATE_CHANGE_READY_TO_PAUSED:
449       /* default to single image mode, setcaps function might not be called */
450       dec->framerate_numerator = 0;
451       dec->framerate_denominator = 1;
452       break;
453     default:
454       break;
455   }
456
457   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
458   if (ret == GST_STATE_CHANGE_FAILURE)
459     return ret;
460
461   switch (transition) {
462     case GST_STATE_CHANGE_PAUSED_TO_READY:
463       dec->framerate_numerator = 0;
464       dec->framerate_denominator = 0;
465       break;
466     default:
467       break;
468   }
469
470   return ret;
471 }
472
473 #define GST_GDK_PIXBUF_TYPE_FIND_SIZE 1024
474
475 #ifdef enable_typefind
476 static void
477 gst_gdk_pixbuf_type_find (GstTypeFind * tf, gpointer ignore)
478 {
479   guint8 *data;
480   GdkPixbufLoader *pixbuf_loader;
481   GdkPixbufFormat *format;
482
483   data = gst_type_find_peek (tf, 0, GST_GDK_PIXBUF_TYPE_FIND_SIZE);
484   if (data == NULL)
485     return;
486
487   GST_DEBUG ("creating new loader");
488
489   pixbuf_loader = gdk_pixbuf_loader_new ();
490
491   gdk_pixbuf_loader_write (pixbuf_loader, data, GST_GDK_PIXBUF_TYPE_FIND_SIZE,
492       NULL);
493
494   format = gdk_pixbuf_loader_get_format (pixbuf_loader);
495
496   if (format != NULL) {
497     GstCaps *caps;
498     gchar **p;
499     gchar **mlist = gdk_pixbuf_format_get_mime_types (format);
500
501     for (p = mlist; *p; ++p) {
502       GST_DEBUG ("suggesting mime type %s", *p);
503       caps = gst_caps_new_simple (*p, NULL);
504       gst_type_find_suggest (tf, GST_TYPE_FIND_MINIMUM, caps);
505       gst_caps_free (caps);
506     }
507     g_strfreev (mlist);
508   }
509
510   GST_DEBUG ("closing pixbuf loader, hope it doesn't hang ...");
511   /* librsvg 2.4.x has a bug where it triggers an endless loop in trying
512      to close a gzip that's not an svg; fixed upstream but no good way
513      to work around it */
514   gdk_pixbuf_loader_close (pixbuf_loader, NULL);
515   GST_DEBUG ("closed pixbuf loader");
516   g_object_unref (G_OBJECT (pixbuf_loader));
517 }
518 #endif
519
520 /* entry point to initialize the plug-in
521  * initialize the plug-in itself
522  * register the element factories and pad templates
523  * register the features
524  */
525 static gboolean
526 plugin_init (GstPlugin * plugin)
527 {
528   GST_DEBUG_CATEGORY_INIT (gst_gdk_pixbuf_debug, "gdkpixbuf", 0,
529       "gdk pixbuf loader");
530
531   if (!gst_element_register (plugin, "gdkpixbufdec", GST_RANK_SECONDARY,
532           GST_TYPE_GDK_PIXBUF))
533     return FALSE;
534
535 #ifdef enable_typefind
536   gst_type_find_register (plugin, "image/*", GST_RANK_MARGINAL,
537       gst_gdk_pixbuf_type_find, NULL, GST_CAPS_ANY, NULL);
538 #endif
539
540   if (!gst_element_register (plugin, "gdkpixbufsink", GST_RANK_NONE,
541           GST_TYPE_GDK_PIXBUF_SINK))
542     return FALSE;
543
544   if (!pixbufscale_init (plugin))
545     return FALSE;
546
547   /* plugin initialisation succeeded */
548   return TRUE;
549 }
550
551
552 /* this is the structure that gst-register looks for
553  * so keep the name plugin_desc, or you cannot get your plug-in registered */
554 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
555     GST_VERSION_MINOR,
556     "gdkpixbuf",
557     "GdkPixbuf-based image decoder, scaler and sink",
558     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)