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