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 "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; " disabled because we can't handle animated gifs */
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
gst_gdk_pixbuf_dec_sink_setcaps(GstGdkPixbufDec * filter,GstCaps * caps)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 *
gst_gdk_pixbuf_dec_get_capslist(GstCaps * filter)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
gst_gdk_pixbuf_dec_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)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
gst_gdk_pixbuf_dec_class_init(GstGdkPixbufDecClass * klass)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_static_pad_template (gstelement_class,
184 &gst_gdk_pixbuf_dec_src_template);
185 gst_element_class_add_static_pad_template (gstelement_class,
186 &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
gst_gdk_pixbuf_dec_init(GstGdkPixbufDec * filter)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 filter->packetized = FALSE;
219 }
220
221 static gboolean
gst_gdk_pixbuf_dec_setup_pool(GstGdkPixbufDec * filter,GstVideoInfo * info)222 gst_gdk_pixbuf_dec_setup_pool (GstGdkPixbufDec * filter, GstVideoInfo * info)
223 {
224 GstCaps *target;
225 GstQuery *query;
226 GstBufferPool *pool;
227 GstStructure *config;
228 guint size, min, max;
229
230 target = gst_pad_get_current_caps (filter->srcpad);
231 if (!target)
232 return FALSE;
233
234 /* try to get a bufferpool now */
235 /* find a pool for the negotiated caps now */
236 query = gst_query_new_allocation (target, TRUE);
237
238 if (!gst_pad_peer_query (filter->srcpad, query)) {
239 /* not a problem, we use the query defaults */
240 GST_DEBUG_OBJECT (filter, "ALLOCATION query failed");
241 }
242
243 if (gst_query_get_n_allocation_pools (query) > 0) {
244 /* we got configuration from our peer, parse them */
245 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
246 } else {
247 pool = NULL;
248 size = info->size;
249 min = max = 0;
250 }
251
252 gst_query_unref (query);
253
254 if (pool == NULL) {
255 /* we did not get a pool, make one ourselves then */
256 pool = gst_buffer_pool_new ();
257 }
258
259 /* and configure */
260 config = gst_buffer_pool_get_config (pool);
261 gst_buffer_pool_config_set_params (config, target, size, min, max);
262 gst_buffer_pool_set_config (pool, config);
263
264 if (filter->pool) {
265 gst_buffer_pool_set_active (filter->pool, FALSE);
266 gst_object_unref (filter->pool);
267 }
268 filter->pool = pool;
269
270 /* and activate */
271 gst_buffer_pool_set_active (filter->pool, TRUE);
272
273 gst_caps_unref (target);
274
275 return TRUE;
276 }
277
278 static GstFlowReturn
gst_gdk_pixbuf_dec_flush(GstGdkPixbufDec * filter)279 gst_gdk_pixbuf_dec_flush (GstGdkPixbufDec * filter)
280 {
281 GstBuffer *outbuf;
282 GdkPixbuf *pixbuf;
283 int y;
284 guint8 *out_pix;
285 guint8 *in_pix;
286 int in_rowstride, out_rowstride;
287 GstFlowReturn ret;
288 GstCaps *caps = NULL;
289 gint width, height;
290 gint n_channels;
291 GstVideoFrame frame;
292
293 pixbuf = gdk_pixbuf_loader_get_pixbuf (filter->pixbuf_loader);
294 if (pixbuf == NULL)
295 goto no_pixbuf;
296
297 width = gdk_pixbuf_get_width (pixbuf);
298 height = gdk_pixbuf_get_height (pixbuf);
299
300 if (GST_VIDEO_INFO_FORMAT (&filter->info) == GST_VIDEO_FORMAT_UNKNOWN) {
301 GstVideoInfo info;
302 GstVideoFormat fmt;
303 GList *l;
304
305 GST_DEBUG ("Set size to %dx%d", width, height);
306
307 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
308 switch (n_channels) {
309 case 3:
310 fmt = GST_VIDEO_FORMAT_RGB;
311 break;
312 case 4:
313 fmt = GST_VIDEO_FORMAT_RGBA;
314 break;
315 default:
316 goto channels_not_supported;
317 }
318
319
320 gst_video_info_init (&info);
321 gst_video_info_set_format (&info, fmt, width, height);
322 info.fps_n = filter->in_fps_n;
323 info.fps_d = filter->in_fps_d;
324 caps = gst_video_info_to_caps (&info);
325
326 filter->info = info;
327
328 gst_pad_set_caps (filter->srcpad, caps);
329 gst_caps_unref (caps);
330
331 gst_gdk_pixbuf_dec_setup_pool (filter, &info);
332
333 for (l = filter->pending_events; l; l = l->next)
334 gst_pad_push_event (filter->srcpad, l->data);
335 g_list_free (filter->pending_events);
336 filter->pending_events = NULL;
337 }
338
339 ret = gst_buffer_pool_acquire_buffer (filter->pool, &outbuf, NULL);
340 if (ret != GST_FLOW_OK)
341 goto no_buffer;
342
343 GST_BUFFER_TIMESTAMP (outbuf) = filter->last_timestamp;
344 GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
345
346 in_pix = gdk_pixbuf_get_pixels (pixbuf);
347 in_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
348
349 gst_video_frame_map (&frame, &filter->info, outbuf, GST_MAP_WRITE);
350 out_pix = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
351 out_rowstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
352
353 for (y = 0; y < height; y++) {
354 memcpy (out_pix, in_pix, width * GST_VIDEO_FRAME_COMP_PSTRIDE (&frame, 0));
355 in_pix += in_rowstride;
356 out_pix += out_rowstride;
357 }
358
359 gst_video_frame_unmap (&frame);
360
361 GST_DEBUG ("pushing... %" G_GSIZE_FORMAT " bytes",
362 gst_buffer_get_size (outbuf));
363 ret = gst_pad_push (filter->srcpad, outbuf);
364
365 if (ret != GST_FLOW_OK)
366 GST_DEBUG_OBJECT (filter, "flow: %s", gst_flow_get_name (ret));
367
368 return ret;
369
370 /* ERRORS */
371 no_pixbuf:
372 {
373 GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL), ("error geting pixbuf"));
374 return GST_FLOW_ERROR;
375 }
376 channels_not_supported:
377 {
378 GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
379 ("%d channels not supported", n_channels));
380 return GST_FLOW_ERROR;
381 }
382 no_buffer:
383 {
384 GST_DEBUG ("Failed to create outbuffer - %s", gst_flow_get_name (ret));
385 return ret;
386 }
387 }
388
389 static gboolean
gst_gdk_pixbuf_dec_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)390 gst_gdk_pixbuf_dec_sink_event (GstPad * pad, GstObject * parent,
391 GstEvent * event)
392 {
393 GstFlowReturn res = GST_FLOW_OK;
394 gboolean ret = TRUE, forward = TRUE;
395 GstGdkPixbufDec *pixbuf;
396
397 pixbuf = GST_GDK_PIXBUF_DEC (parent);
398
399 switch (GST_EVENT_TYPE (event)) {
400 case GST_EVENT_CAPS:
401 {
402 GstCaps *caps;
403
404 gst_event_parse_caps (event, &caps);
405 ret = gst_gdk_pixbuf_dec_sink_setcaps (pixbuf, caps);
406 forward = FALSE;
407 break;
408 }
409 case GST_EVENT_EOS:
410 if (pixbuf->pixbuf_loader != NULL) {
411 gdk_pixbuf_loader_close (pixbuf->pixbuf_loader, NULL);
412 res = gst_gdk_pixbuf_dec_flush (pixbuf);
413 g_object_unref (G_OBJECT (pixbuf->pixbuf_loader));
414 pixbuf->pixbuf_loader = NULL;
415 /* as long as we don't have flow returns for event functions we need
416 * to post an error here, or the application might never know that
417 * things failed */
418 if (res != GST_FLOW_OK && res != GST_FLOW_FLUSHING
419 && res != GST_FLOW_EOS && res != GST_FLOW_NOT_LINKED) {
420 GST_ELEMENT_FLOW_ERROR (pixbuf, res);
421 forward = FALSE;
422 ret = FALSE;
423 }
424 }
425 break;
426 case GST_EVENT_FLUSH_STOP:
427 g_list_free_full (pixbuf->pending_events,
428 (GDestroyNotify) gst_event_unref);
429 pixbuf->pending_events = NULL;
430 /* Fall through */
431 case GST_EVENT_SEGMENT:
432 {
433 const GstSegment *segment;
434 GstSegment output_segment;
435 guint32 seqnum;
436
437 gst_event_parse_segment (event, &segment);
438 if (segment->format == GST_FORMAT_BYTES)
439 pixbuf->packetized = FALSE;
440 else
441 pixbuf->packetized = TRUE;
442
443 if (segment->format != GST_FORMAT_TIME) {
444 seqnum = gst_event_get_seqnum (event);
445 gst_event_unref (event);
446 gst_segment_init (&output_segment, GST_FORMAT_TIME);
447 event = gst_event_new_segment (&output_segment);
448 gst_event_set_seqnum (event, seqnum);
449 }
450
451 if (pixbuf->pixbuf_loader != NULL) {
452 gdk_pixbuf_loader_close (pixbuf->pixbuf_loader, NULL);
453 g_object_unref (G_OBJECT (pixbuf->pixbuf_loader));
454 pixbuf->pixbuf_loader = NULL;
455 }
456 break;
457 }
458 default:
459 break;
460 }
461 if (forward) {
462 if (!gst_pad_has_current_caps (pixbuf->srcpad) &&
463 GST_EVENT_IS_SERIALIZED (event)
464 && GST_EVENT_TYPE (event) > GST_EVENT_CAPS
465 && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP
466 && GST_EVENT_TYPE (event) != GST_EVENT_EOS) {
467 ret = TRUE;
468 pixbuf->pending_events = g_list_prepend (pixbuf->pending_events, event);
469 } else {
470 ret = gst_pad_event_default (pad, parent, event);
471 }
472 } else {
473 gst_event_unref (event);
474 }
475 return ret;
476 }
477
478 static GstFlowReturn
gst_gdk_pixbuf_dec_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)479 gst_gdk_pixbuf_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
480 {
481 GstGdkPixbufDec *filter;
482 GstFlowReturn ret = GST_FLOW_OK;
483 GError *error = NULL;
484 GstClockTime timestamp;
485 GstMapInfo map;
486
487 filter = GST_GDK_PIXBUF_DEC (parent);
488
489 timestamp = GST_BUFFER_TIMESTAMP (buf);
490
491 if (GST_CLOCK_TIME_IS_VALID (timestamp))
492 filter->last_timestamp = timestamp;
493
494 GST_LOG_OBJECT (filter, "buffer with ts: %" GST_TIME_FORMAT,
495 GST_TIME_ARGS (timestamp));
496
497 if (filter->pixbuf_loader == NULL)
498 filter->pixbuf_loader = gdk_pixbuf_loader_new ();
499
500 gst_buffer_map (buf, &map, GST_MAP_READ);
501
502 GST_LOG_OBJECT (filter, "Writing buffer size %d", (gint) map.size);
503 if (!gdk_pixbuf_loader_write (filter->pixbuf_loader, map.data, map.size,
504 &error))
505 goto error;
506
507 if (filter->packetized == TRUE) {
508 gdk_pixbuf_loader_close (filter->pixbuf_loader, NULL);
509 ret = gst_gdk_pixbuf_dec_flush (filter);
510 g_object_unref (filter->pixbuf_loader);
511 filter->pixbuf_loader = NULL;
512 }
513
514 gst_buffer_unmap (buf, &map);
515 gst_buffer_unref (buf);
516
517 return ret;
518
519 /* ERRORS */
520 error:
521 {
522 GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
523 ("gdk_pixbuf_loader_write error: %s", error->message));
524 g_error_free (error);
525 gst_buffer_unmap (buf, &map);
526 gst_buffer_unref (buf);
527 return GST_FLOW_ERROR;
528 }
529 }
530
531 static GstStateChangeReturn
gst_gdk_pixbuf_dec_change_state(GstElement * element,GstStateChange transition)532 gst_gdk_pixbuf_dec_change_state (GstElement * element,
533 GstStateChange transition)
534 {
535 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
536 GstGdkPixbufDec *dec = GST_GDK_PIXBUF_DEC (element);
537
538 switch (transition) {
539 case GST_STATE_CHANGE_READY_TO_PAUSED:
540 /* default to single image mode, setcaps function might not be called */
541 dec->in_fps_n = 0;
542 dec->in_fps_d = 1;
543 gst_video_info_init (&dec->info);
544 break;
545 default:
546 break;
547 }
548
549 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
550 if (ret == GST_STATE_CHANGE_FAILURE)
551 return ret;
552
553 switch (transition) {
554 case GST_STATE_CHANGE_PAUSED_TO_READY:
555 dec->in_fps_n = 0;
556 dec->in_fps_d = 0;
557 if (dec->pool) {
558 gst_buffer_pool_set_active (dec->pool, FALSE);
559 gst_object_replace ((GstObject **) & dec->pool, NULL);
560 }
561 g_list_free_full (dec->pending_events, (GDestroyNotify) gst_event_unref);
562 dec->pending_events = NULL;
563 if (dec->pixbuf_loader != NULL) {
564 gdk_pixbuf_loader_close (dec->pixbuf_loader, NULL);
565 g_object_unref (G_OBJECT (dec->pixbuf_loader));
566 dec->pixbuf_loader = NULL;
567 }
568 break;
569 default:
570 break;
571 }
572
573 return ret;
574 }
575