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