gdkpixbuf: re-enable already-ported gdkpixbuf element as gdkpixbufdec
[platform/upstream/gstreamer.git] / ext / gdk_pixbuf / gstgdkpixbufdec.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
25 #include <gst/gst.h>
26 #include <gst/video/video.h>
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28 #include <string.h>
29
30 #include "gstgdkpixbufdec.h"
31
32 GST_DEBUG_CATEGORY_STATIC (gdkpixbufdec_debug);
33 #define GST_CAT_DEFAULT gdkpixbufdec_debug
34
35 static GstStaticPadTemplate gst_gdk_pixbuf_dec_sink_template =
36     GST_STATIC_PAD_TEMPLATE ("sink",
37     GST_PAD_SINK,
38     GST_PAD_ALWAYS,
39     GST_STATIC_CAPS ("image/png; "
40         /* "image/jpeg; " disabled because we can't handle MJPEG */
41         "image/gif; "
42         "image/x-icon; "
43         "application/x-navi-animation; "
44         "image/x-cmu-raster; "
45         "image/x-sun-raster; "
46         "image/x-pixmap; "
47         "image/tiff; "
48         "image/x-portable-anymap; "
49         "image/x-portable-bitmap; "
50         "image/x-portable-graymap; "
51         "image/x-portable-pixmap; "
52         "image/bmp; "
53         "image/x-bmp; "
54         "image/x-MS-bmp; "
55         "image/vnd.wap.wbmp; " "image/x-bitmap; " "image/x-tga; "
56         "image/x-pcx; image/svg; image/svg+xml")
57     );
58
59 static GstStaticPadTemplate gst_gdk_pixbuf_dec_src_template =
60     GST_STATIC_PAD_TEMPLATE ("src",
61     GST_PAD_SRC,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB") "; "
64         GST_VIDEO_CAPS_MAKE ("RGBA"))
65     );
66
67 static GstStateChangeReturn
68 gst_gdk_pixbuf_dec_change_state (GstElement * element,
69     GstStateChange transition);
70 static GstFlowReturn gst_gdk_pixbuf_dec_chain (GstPad * pad, GstObject * parent,
71     GstBuffer * buffer);
72 static gboolean gst_gdk_pixbuf_dec_sink_event (GstPad * pad, GstObject * parent,
73     GstEvent * event);
74
75 #define gst_gdk_pixbuf_dec_parent_class parent_class
76 G_DEFINE_TYPE (GstGdkPixbufDec, gst_gdk_pixbuf_dec, GST_TYPE_ELEMENT);
77
78 static gboolean
79 gst_gdk_pixbuf_dec_sink_setcaps (GstGdkPixbufDec * filter, GstCaps * caps)
80 {
81   const GValue *framerate;
82   GstStructure *s;
83
84   s = gst_caps_get_structure (caps, 0);
85
86   if ((framerate = gst_structure_get_value (s, "framerate")) != NULL) {
87     filter->in_fps_n = gst_value_get_fraction_numerator (framerate);
88     filter->in_fps_d = gst_value_get_fraction_denominator (framerate);
89     GST_DEBUG_OBJECT (filter, "got framerate of %d/%d fps => packetized mode",
90         filter->in_fps_n, filter->in_fps_d);
91   } else {
92     filter->in_fps_n = 0;
93     filter->in_fps_d = 1;
94     GST_DEBUG_OBJECT (filter, "no framerate, assuming single image");
95   }
96
97   return TRUE;
98 }
99
100 static GstCaps *
101 gst_gdk_pixbuf_dec_get_capslist (GstCaps * filter)
102 {
103   GSList *slist;
104   GSList *slist0;
105   GstCaps *capslist = NULL;
106   GstCaps *return_caps = NULL;
107   GstCaps *tmpl_caps;
108
109   capslist = gst_caps_new_empty ();
110   slist0 = gdk_pixbuf_get_formats ();
111
112   for (slist = slist0; slist; slist = g_slist_next (slist)) {
113     GdkPixbufFormat *pixbuf_format;
114     char **mimetypes;
115     char **mimetype;
116
117     pixbuf_format = slist->data;
118     mimetypes = gdk_pixbuf_format_get_mime_types (pixbuf_format);
119
120     for (mimetype = mimetypes; *mimetype; mimetype++) {
121       gst_caps_append_structure (capslist, gst_structure_new_empty (*mimetype));
122     }
123     g_strfreev (mimetypes);
124   }
125   g_slist_free (slist0);
126
127   tmpl_caps =
128       gst_static_caps_get (&gst_gdk_pixbuf_dec_sink_template.static_caps);
129   return_caps = gst_caps_intersect (capslist, tmpl_caps);
130
131   gst_caps_unref (tmpl_caps);
132   gst_caps_unref (capslist);
133
134   if (filter && return_caps) {
135     GstCaps *temp;
136
137     temp = gst_caps_intersect (return_caps, filter);
138     gst_caps_unref (return_caps);
139     return_caps = temp;
140   }
141
142   return return_caps;
143 }
144
145 static gboolean
146 gst_gdk_pixbuf_dec_sink_query (GstPad * pad, GstObject * parent,
147     GstQuery * query)
148 {
149   gboolean res;
150
151   switch (GST_QUERY_TYPE (query)) {
152     case GST_QUERY_CAPS:
153     {
154       GstCaps *filter, *caps;
155
156       gst_query_parse_caps (query, &filter);
157       caps = gst_gdk_pixbuf_dec_get_capslist (filter);
158       gst_query_set_caps_result (query, caps);
159       gst_caps_unref (caps);
160
161       res = TRUE;
162       break;
163     }
164     default:
165       res = gst_pad_query_default (pad, parent, query);
166       break;
167   }
168   return res;
169 }
170
171
172 /* initialize the plugin's class */
173 static void
174 gst_gdk_pixbuf_dec_class_init (GstGdkPixbufDecClass * klass)
175 {
176   GstElementClass *gstelement_class;
177
178   gstelement_class = (GstElementClass *) klass;
179
180   gstelement_class->change_state =
181       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_dec_change_state);
182
183   gst_element_class_add_pad_template (gstelement_class,
184       gst_static_pad_template_get (&gst_gdk_pixbuf_dec_src_template));
185   gst_element_class_add_pad_template (gstelement_class,
186       gst_static_pad_template_get (&gst_gdk_pixbuf_dec_sink_template));
187   gst_element_class_set_static_metadata (gstelement_class,
188       "GdkPixbuf image decoder", "Codec/Decoder/Image",
189       "Decodes images in a video stream using GdkPixbuf",
190       "David A. Schleef <ds@schleef.org>, Renato Filho <renato.filho@indt.org.br>");
191
192   GST_DEBUG_CATEGORY_INIT (gdkpixbufdec_debug, "gdkpixbuf", 0,
193       "GdkPixbuf image decoder");
194 }
195
196 static void
197 gst_gdk_pixbuf_dec_init (GstGdkPixbufDec * filter)
198 {
199   filter->sinkpad =
200       gst_pad_new_from_static_template (&gst_gdk_pixbuf_dec_sink_template,
201       "sink");
202   gst_pad_set_query_function (filter->sinkpad,
203       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_dec_sink_query));
204   gst_pad_set_chain_function (filter->sinkpad,
205       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_dec_chain));
206   gst_pad_set_event_function (filter->sinkpad,
207       GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_dec_sink_event));
208   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
209
210   filter->srcpad =
211       gst_pad_new_from_static_template (&gst_gdk_pixbuf_dec_src_template,
212       "src");
213   gst_pad_use_fixed_caps (filter->srcpad);
214   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
215
216   filter->last_timestamp = GST_CLOCK_TIME_NONE;
217   filter->pixbuf_loader = NULL;
218 }
219
220 static gboolean
221 gst_gdk_pixbuf_dec_setup_pool (GstGdkPixbufDec * filter, GstVideoInfo * info)
222 {
223   GstCaps *target;
224   GstQuery *query;
225   GstBufferPool *pool;
226   GstStructure *config;
227   guint size, min, max;
228
229   target = gst_pad_get_current_caps (filter->srcpad);
230
231   /* try to get a bufferpool now */
232   /* find a pool for the negotiated caps now */
233   query = gst_query_new_allocation (target, TRUE);
234
235   if (!gst_pad_peer_query (filter->srcpad, query)) {
236     /* not a problem, we use the query defaults */
237     GST_DEBUG_OBJECT (filter, "ALLOCATION query failed");
238   }
239
240   if (gst_query_get_n_allocation_pools (query) > 0) {
241     /* we got configuration from our peer, parse them */
242     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
243   } else {
244     pool = NULL;
245     size = info->size;
246     min = max = 0;
247   }
248
249   if (pool == NULL) {
250     /* we did not get a pool, make one ourselves then */
251     pool = gst_buffer_pool_new ();
252   }
253
254   /* and configure */
255   config = gst_buffer_pool_get_config (pool);
256   gst_buffer_pool_config_set_params (config, target, size, min, max);
257   gst_buffer_pool_set_config (pool, config);
258
259   if (filter->pool) {
260     gst_buffer_pool_set_active (filter->pool, FALSE);
261     gst_object_unref (filter->pool);
262   }
263   filter->pool = pool;
264
265   /* and activate */
266   gst_buffer_pool_set_active (filter->pool, TRUE);
267
268   gst_caps_unref (target);
269
270   return TRUE;
271 }
272
273 static GstFlowReturn
274 gst_gdk_pixbuf_dec_flush (GstGdkPixbufDec * filter)
275 {
276   GstBuffer *outbuf;
277   GdkPixbuf *pixbuf;
278   int y;
279   guint8 *out_pix;
280   guint8 *in_pix;
281   int in_rowstride, out_rowstride;
282   GstFlowReturn ret;
283   GstCaps *caps = NULL;
284   gint width, height;
285   gint n_channels;
286   GstVideoFrame frame;
287
288   pixbuf = gdk_pixbuf_loader_get_pixbuf (filter->pixbuf_loader);
289   if (pixbuf == NULL)
290     goto no_pixbuf;
291
292   width = gdk_pixbuf_get_width (pixbuf);
293   height = gdk_pixbuf_get_height (pixbuf);
294
295   if (GST_VIDEO_INFO_FORMAT (&filter->info) == GST_VIDEO_FORMAT_UNKNOWN) {
296     GstVideoInfo info;
297     GstVideoFormat fmt;
298
299     GST_DEBUG ("Set size to %dx%d", width, height);
300
301     n_channels = gdk_pixbuf_get_n_channels (pixbuf);
302     switch (n_channels) {
303       case 3:
304         fmt = GST_VIDEO_FORMAT_RGB;
305         break;
306       case 4:
307         fmt = GST_VIDEO_FORMAT_RGBA;
308         break;
309       default:
310         goto channels_not_supported;
311     }
312
313
314     gst_video_info_init (&info);
315     gst_video_info_set_format (&info, fmt, width, height);
316     info.fps_n = filter->in_fps_n;
317     info.fps_d = filter->in_fps_d;
318     caps = gst_video_info_to_caps (&info);
319
320     filter->info = info;
321
322     gst_pad_set_caps (filter->srcpad, caps);
323     gst_caps_unref (caps);
324
325     gst_gdk_pixbuf_dec_setup_pool (filter, &info);
326   }
327
328   ret = gst_buffer_pool_acquire_buffer (filter->pool, &outbuf, NULL);
329   if (ret != GST_FLOW_OK)
330     goto no_buffer;
331
332   GST_BUFFER_TIMESTAMP (outbuf) = filter->last_timestamp;
333   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
334
335   in_pix = gdk_pixbuf_get_pixels (pixbuf);
336   in_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
337
338   gst_video_frame_map (&frame, &filter->info, outbuf, GST_MAP_WRITE);
339   out_pix = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
340   out_rowstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
341
342   for (y = 0; y < height; y++) {
343     memcpy (out_pix, in_pix, width * GST_VIDEO_FRAME_COMP_PSTRIDE (&frame, 0));
344     in_pix += in_rowstride;
345     out_pix += out_rowstride;
346   }
347
348   gst_video_frame_unmap (&frame);
349
350   GST_DEBUG ("pushing... %d bytes", gst_buffer_get_size (outbuf));
351   ret = gst_pad_push (filter->srcpad, outbuf);
352
353   if (ret != GST_FLOW_OK)
354     GST_DEBUG_OBJECT (filter, "flow: %s", gst_flow_get_name (ret));
355
356   return ret;
357
358   /* ERRORS */
359 no_pixbuf:
360   {
361     GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL), ("error geting pixbuf"));
362     return GST_FLOW_ERROR;
363   }
364 channels_not_supported:
365   {
366     GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
367         ("%d channels not supported", n_channels));
368     return GST_FLOW_ERROR;
369   }
370 no_buffer:
371   {
372     GST_DEBUG ("Failed to create outbuffer - %s", gst_flow_get_name (ret));
373     return ret;
374   }
375 }
376
377 static gboolean
378 gst_gdk_pixbuf_dec_sink_event (GstPad * pad, GstObject * parent,
379     GstEvent * event)
380 {
381   GstFlowReturn res = GST_FLOW_OK;
382   gboolean ret = TRUE, forward = TRUE;
383   GstGdkPixbufDec *pixbuf;
384
385   pixbuf = GST_GDK_PIXBUF_DEC (parent);
386
387   switch (GST_EVENT_TYPE (event)) {
388     case GST_EVENT_CAPS:
389     {
390       GstCaps *caps;
391
392       gst_event_parse_caps (event, &caps);
393       ret = gst_gdk_pixbuf_dec_sink_setcaps (pixbuf, caps);
394       forward = FALSE;
395       break;
396     }
397     case GST_EVENT_EOS:
398       if (pixbuf->pixbuf_loader != NULL) {
399         gdk_pixbuf_loader_close (pixbuf->pixbuf_loader, NULL);
400         res = gst_gdk_pixbuf_dec_flush (pixbuf);
401         g_object_unref (G_OBJECT (pixbuf->pixbuf_loader));
402         pixbuf->pixbuf_loader = NULL;
403         /* as long as we don't have flow returns for event functions we need
404          * to post an error here, or the application might never know that
405          * things failed */
406         if (res != GST_FLOW_OK && res != GST_FLOW_FLUSHING) {
407           GST_ELEMENT_ERROR (pixbuf, STREAM, FAILED, (NULL),
408               ("Flow: %s", gst_flow_get_name (res)));
409           forward = FALSE;
410           ret = FALSE;
411         }
412       }
413       break;
414     case GST_EVENT_SEGMENT:
415     case GST_EVENT_FLUSH_STOP:
416       if (pixbuf->pixbuf_loader != NULL) {
417         gdk_pixbuf_loader_close (pixbuf->pixbuf_loader, NULL);
418         g_object_unref (G_OBJECT (pixbuf->pixbuf_loader));
419         pixbuf->pixbuf_loader = NULL;
420       }
421       break;
422     default:
423       break;
424   }
425   if (forward) {
426     ret = gst_pad_event_default (pad, parent, event);
427   } else {
428     gst_event_unref (event);
429   }
430   return ret;
431 }
432
433 static GstFlowReturn
434 gst_gdk_pixbuf_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
435 {
436   GstGdkPixbufDec *filter;
437   GstFlowReturn ret = GST_FLOW_OK;
438   GError *error = NULL;
439   GstClockTime timestamp;
440   GstMapInfo map;
441
442   filter = GST_GDK_PIXBUF_DEC (parent);
443
444   timestamp = GST_BUFFER_TIMESTAMP (buf);
445
446   if (GST_CLOCK_TIME_IS_VALID (timestamp))
447     filter->last_timestamp = timestamp;
448
449   GST_LOG_OBJECT (filter, "buffer with ts: %" GST_TIME_FORMAT,
450       GST_TIME_ARGS (timestamp));
451
452   if (filter->pixbuf_loader == NULL)
453     filter->pixbuf_loader = gdk_pixbuf_loader_new ();
454
455   gst_buffer_map (buf, &map, GST_MAP_READ);
456
457   GST_LOG_OBJECT (filter, "Writing buffer size %d", (gint) map.size);
458   if (!gdk_pixbuf_loader_write (filter->pixbuf_loader, map.data, map.size,
459           &error))
460     goto error;
461
462   /* packetised mode? *//* FIXME: shouln't this be fps_d != 0, since 0/1
463    * might be packetised mode but variable framerate */
464   if (filter->in_fps_n != 0) {
465     gdk_pixbuf_loader_close (filter->pixbuf_loader, NULL);
466     ret = gst_gdk_pixbuf_dec_flush (filter);
467     g_object_unref (filter->pixbuf_loader);
468     filter->pixbuf_loader = NULL;
469   }
470
471   gst_buffer_unmap (buf, &map);
472   gst_buffer_unref (buf);
473
474   return ret;
475
476   /* ERRORS */
477 error:
478   {
479     GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
480         ("gdk_pixbuf_loader_write error: %s", error->message));
481     g_error_free (error);
482     gst_buffer_unmap (buf, &map);
483     gst_buffer_unref (buf);
484     return GST_FLOW_ERROR;
485   }
486 }
487
488 static GstStateChangeReturn
489 gst_gdk_pixbuf_dec_change_state (GstElement * element,
490     GstStateChange transition)
491 {
492   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
493   GstGdkPixbufDec *dec = GST_GDK_PIXBUF_DEC (element);
494
495   switch (transition) {
496     case GST_STATE_CHANGE_READY_TO_PAUSED:
497       /* default to single image mode, setcaps function might not be called */
498       dec->in_fps_n = 0;
499       dec->in_fps_d = 1;
500       gst_video_info_init (&dec->info);
501       break;
502     default:
503       break;
504   }
505
506   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
507   if (ret == GST_STATE_CHANGE_FAILURE)
508     return ret;
509
510   switch (transition) {
511     case GST_STATE_CHANGE_PAUSED_TO_READY:
512       dec->in_fps_n = 0;
513       dec->in_fps_d = 0;
514       if (dec->pool) {
515         gst_buffer_pool_set_active (dec->pool, FALSE);
516         gst_object_replace ((GstObject **) & dec->pool, NULL);
517       }
518       break;
519     default:
520       break;
521   }
522
523   return ret;
524 }