623237efd942c8341f82efc384a0aa5f1eb04e56
[platform/upstream/gstreamer.git] / ext / libpng / gstpngdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is distributed in the hope that it will be useful,
5  * but WITHOUT ANY WARRANTY; without even the implied warranty of
6  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
7  * Library General Public License for more details.
8  *
9  * You should have received a copy of the GNU Library General Public
10  * License along with this library; if not, write to the
11  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
12  * Boston, MA 02111-1307, USA.
13  *
14  */
15 /**
16  * SECTION:element-pngdec
17  *
18  * Decodes png images. If there is no framerate set on sink caps, it sends EOS
19  * after the first picture.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "gstpngdec.h"
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <gst/video/video.h>
31 #include <gst/gst-i18n-plugin.h>
32
33 GST_DEBUG_CATEGORY_STATIC (pngdec_debug);
34 #define GST_CAT_DEFAULT pngdec_debug
35
36 static gboolean gst_pngdec_libpng_init (GstPngDec * pngdec);
37 static gboolean gst_pngdec_libpng_clear (GstPngDec * pngdec);
38
39 static GstStateChangeReturn gst_pngdec_change_state (GstElement * element,
40     GstStateChange transition);
41
42 static gboolean gst_pngdec_sink_activate_mode (GstPad * sinkpad,
43     GstObject * parent, GstPadMode mode, gboolean active);
44 static gboolean gst_pngdec_sink_activate (GstPad * sinkpad, GstObject * parent);
45
46 static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec);
47
48 static void gst_pngdec_task (GstPad * pad);
49 static GstFlowReturn gst_pngdec_chain (GstPad * pad, GstObject * parent,
50     GstBuffer * buffer);
51 static gboolean gst_pngdec_sink_event (GstPad * pad, GstObject * parent,
52     GstEvent * event);
53 static gboolean gst_pngdec_sink_setcaps (GstPngDec * pngdec, GstCaps * caps);
54
55 static GstFlowReturn gst_pngdec_negotiate_pool (GstPngDec * dec,
56     GstCaps * caps, GstVideoInfo * info);
57
58 static GstStaticPadTemplate gst_pngdec_src_pad_template =
59 GST_STATIC_PAD_TEMPLATE ("src",
60     GST_PAD_SRC,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBA, RGB }"))
63     );
64
65 static GstStaticPadTemplate gst_pngdec_sink_pad_template =
66 GST_STATIC_PAD_TEMPLATE ("sink",
67     GST_PAD_SINK,
68     GST_PAD_ALWAYS,
69     GST_STATIC_CAPS ("image/png")
70     );
71
72 #define gst_pngdec_parent_class parent_class
73 G_DEFINE_TYPE (GstPngDec, gst_pngdec, GST_TYPE_ELEMENT);
74
75 static void
76 gst_pngdec_class_init (GstPngDecClass * klass)
77 {
78   GstElementClass *gstelement_class;
79
80   gstelement_class = (GstElementClass *) klass;
81
82   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_pngdec_change_state);
83
84   gst_element_class_add_pad_template (gstelement_class,
85       gst_static_pad_template_get (&gst_pngdec_src_pad_template));
86   gst_element_class_add_pad_template (gstelement_class,
87       gst_static_pad_template_get (&gst_pngdec_sink_pad_template));
88   gst_element_class_set_details_simple (gstelement_class, "PNG image decoder",
89       "Codec/Decoder/Image",
90       "Decode a png video frame to a raw image",
91       "Wim Taymans <wim@fluendo.com>");
92
93   GST_DEBUG_CATEGORY_INIT (pngdec_debug, "pngdec", 0, "PNG image decoder");
94 }
95
96 static void
97 gst_pngdec_init (GstPngDec * pngdec)
98 {
99   pngdec->sinkpad =
100       gst_pad_new_from_static_template (&gst_pngdec_sink_pad_template, "sink");
101   gst_pad_set_activate_function (pngdec->sinkpad,
102       GST_DEBUG_FUNCPTR (gst_pngdec_sink_activate));
103   gst_pad_set_activatemode_function (pngdec->sinkpad,
104       GST_DEBUG_FUNCPTR (gst_pngdec_sink_activate_mode));
105   gst_pad_set_chain_function (pngdec->sinkpad,
106       GST_DEBUG_FUNCPTR (gst_pngdec_chain));
107   gst_pad_set_event_function (pngdec->sinkpad,
108       GST_DEBUG_FUNCPTR (gst_pngdec_sink_event));
109   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->sinkpad);
110
111   pngdec->srcpad =
112       gst_pad_new_from_static_template (&gst_pngdec_src_pad_template, "src");
113   gst_pad_use_fixed_caps (pngdec->srcpad);
114   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->srcpad);
115
116   pngdec->buffer_out = NULL;
117   pngdec->png = NULL;
118   pngdec->info = NULL;
119   pngdec->endinfo = NULL;
120   pngdec->setup = FALSE;
121
122   pngdec->color_type = -1;
123   pngdec->width = -1;
124   pngdec->height = -1;
125   pngdec->fps_n = 0;
126   pngdec->fps_d = 1;
127
128   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
129   pngdec->in_duration = GST_CLOCK_TIME_NONE;
130
131   gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
132
133   pngdec->image_ready = FALSE;
134 }
135
136 static void
137 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
138 {
139   GST_ERROR ("%s", error_msg);
140 }
141
142 static void
143 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
144 {
145   GST_WARNING ("%s", warning_msg);
146 }
147
148 static void
149 user_info_callback (png_structp png_ptr, png_infop info)
150 {
151   GstPngDec *pngdec = NULL;
152   GstFlowReturn ret = GST_FLOW_OK;
153   GstBuffer *buffer = NULL;
154
155   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
156
157   GST_LOG ("info ready");
158
159   /* Generate the caps and configure */
160   ret = gst_pngdec_caps_create_and_set (pngdec);
161   if (ret != GST_FLOW_OK) {
162     goto beach;
163   }
164
165   if (gst_pad_check_reconfigure (pngdec->srcpad)) {
166     GstCaps *caps;
167
168     caps = gst_pad_get_current_caps (pngdec->srcpad);
169     gst_pngdec_negotiate_pool (pngdec, caps, &pngdec->vinfo);
170     gst_caps_unref (caps);
171   }
172
173   /* Allocate output buffer */
174   g_assert (pngdec->pool);
175   ret = gst_buffer_pool_acquire_buffer (pngdec->pool, &buffer, NULL);
176   if (ret != GST_FLOW_OK) {
177     GST_DEBUG_OBJECT (pngdec, "failed to acquire buffer");
178     ret = GST_FLOW_ERROR;
179     goto beach;
180   }
181
182   pngdec->buffer_out = buffer;
183
184 beach:
185   pngdec->ret = ret;
186 }
187
188 static void
189 user_endrow_callback (png_structp png_ptr, png_bytep new_row,
190     png_uint_32 row_num, int pass)
191 {
192   GstPngDec *pngdec = NULL;
193
194   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
195
196   /* FIXME: implement interlaced pictures */
197
198   /* If buffer_out doesn't exist, it means buffer_alloc failed, which 
199    * will already have set the return code */
200   if (GST_IS_BUFFER (pngdec->buffer_out)) {
201     GstVideoFrame frame;
202     GstBuffer *buffer = pngdec->buffer_out;
203     size_t offset;
204     gint width;
205     guint8 *data;
206
207     if (!gst_video_frame_map (&frame, &pngdec->vinfo, buffer, GST_MAP_WRITE)) {
208       pngdec->ret = GST_FLOW_ERROR;
209       return;
210     }
211
212     data = GST_VIDEO_FRAME_COMP_DATA (&frame, 0);
213     offset = row_num * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0);
214     GST_LOG ("got row %u, copying in buffer %p at offset %" G_GSIZE_FORMAT,
215         (guint) row_num, pngdec->buffer_out, offset);
216     width = GST_ROUND_UP_4 (png_get_rowbytes (pngdec->png, pngdec->info));
217     memcpy (data + offset, new_row, width);
218     gst_video_frame_unmap (&frame);
219     pngdec->ret = GST_FLOW_OK;
220   }
221 }
222
223 static gboolean
224 buffer_clip (GstPngDec * dec, GstBuffer * buffer)
225 {
226   gboolean res = TRUE;
227   guint64 cstart, cstop;
228
229   if ((!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) ||
230       (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer))) ||
231       (dec->segment.format != GST_FORMAT_TIME))
232     goto beach;
233
234   cstart = GST_BUFFER_TIMESTAMP (buffer);
235   cstop = GST_BUFFER_DURATION (buffer);
236
237   if ((res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME,
238               cstart, cstart + cstop, &cstart, &cstop))) {
239     GST_BUFFER_TIMESTAMP (buffer) = cstart;
240     GST_BUFFER_DURATION (buffer) = cstop - cstart;
241   }
242
243 beach:
244   return res;
245 }
246
247 static void
248 user_end_callback (png_structp png_ptr, png_infop info)
249 {
250   GstPngDec *pngdec = NULL;
251
252   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
253
254   GST_LOG_OBJECT (pngdec, "and we are done reading this image");
255
256   if (!pngdec->buffer_out)
257     return;
258
259   if (GST_CLOCK_TIME_IS_VALID (pngdec->in_timestamp))
260     GST_BUFFER_TIMESTAMP (pngdec->buffer_out) = pngdec->in_timestamp;
261   if (GST_CLOCK_TIME_IS_VALID (pngdec->in_duration))
262     GST_BUFFER_DURATION (pngdec->buffer_out) = pngdec->in_duration;
263
264   /* buffer clipping */
265   if (buffer_clip (pngdec, pngdec->buffer_out)) {
266     /* Push our buffer and then EOS if needed */
267     GST_LOG_OBJECT (pngdec, "pushing buffer with ts=%" GST_TIME_FORMAT,
268         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (pngdec->buffer_out)));
269
270     pngdec->ret = gst_pad_push (pngdec->srcpad, pngdec->buffer_out);
271   } else {
272     GST_LOG_OBJECT (pngdec, "dropped decoded buffer");
273     gst_buffer_unref (pngdec->buffer_out);
274   }
275   pngdec->buffer_out = NULL;
276   pngdec->image_ready = TRUE;
277 }
278
279 static void
280 user_read_data (png_structp png_ptr, png_bytep data, png_size_t length)
281 {
282   GstPngDec *pngdec;
283   GstBuffer *buffer;
284   GstFlowReturn ret = GST_FLOW_OK;
285   guint size;
286
287   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
288
289   GST_LOG ("reading %" G_GSIZE_FORMAT " bytes of data at offset %d", length,
290       pngdec->offset);
291
292   ret = gst_pad_pull_range (pngdec->sinkpad, pngdec->offset, length, &buffer);
293   if (ret != GST_FLOW_OK)
294     goto pause;
295
296   size = gst_buffer_get_size (buffer);
297
298   if (size != length)
299     goto short_buffer;
300
301   gst_buffer_extract (buffer, 0, data, size);
302   gst_buffer_unref (buffer);
303
304   pngdec->offset += length;
305
306   return;
307
308   /* ERRORS */
309 pause:
310   {
311     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
312         gst_flow_get_name (ret));
313     gst_pad_pause_task (pngdec->sinkpad);
314     if (ret == GST_FLOW_EOS) {
315       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
316     } else if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED) {
317       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
318           (_("Internal data stream error.")),
319           ("stream stopped, reason %s", gst_flow_get_name (ret)));
320       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
321     }
322     png_error (png_ptr, "Internal data stream error.");
323     return;
324   }
325 short_buffer:
326   {
327     gst_buffer_unref (buffer);
328     GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
329         (_("Internal data stream error.")),
330         ("Read %u, needed %" G_GSIZE_FORMAT "bytes", size, length));
331     ret = GST_FLOW_ERROR;
332     goto pause;
333   }
334 }
335
336 static GstFlowReturn
337 gst_pngdec_negotiate_pool (GstPngDec * dec, GstCaps * caps, GstVideoInfo * info)
338 {
339   GstQuery *query;
340   GstBufferPool *pool = NULL;
341   guint size, min, max, prefix, padding, alignment;
342   GstStructure *config;
343
344   /* find a pool for the negotiated caps now */
345   query = gst_query_new_allocation (caps, TRUE);
346
347   if (gst_pad_peer_query (dec->srcpad, query)) {
348     GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints");
349     /* we got configuration from our peer, parse them */
350     gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
351         &padding, &alignment, &pool);
352     size = MAX (size, info->size);
353   } else {
354     GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints");
355     size = info->size;
356     min = max = 0;
357     prefix = 0;
358     padding = 0;
359     alignment = 0;
360   }
361
362   if (pool == NULL) {
363     /* we did not get a pool, make one ourselves then */
364     pool = gst_buffer_pool_new ();
365   }
366
367   if (dec->pool)
368     gst_object_unref (dec->pool);
369   dec->pool = pool;
370
371   config = gst_buffer_pool_get_config (pool);
372   gst_buffer_pool_config_set (config, caps, size, min, max, prefix, padding,
373       alignment);
374   /* just set the option, if the pool can support it we will transparently use
375    * it through the video info API. We could also see if the pool support this
376    * option and only activate it then. */
377   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
378
379   gst_buffer_pool_set_config (pool, config);
380   /* and activate */
381   gst_buffer_pool_set_active (pool, TRUE);
382
383   gst_query_unref (query);
384
385   return GST_FLOW_OK;
386 }
387
388 static GstFlowReturn
389 gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
390 {
391   GstFlowReturn ret = GST_FLOW_OK;
392   GstCaps *caps = NULL, *res = NULL;
393   GstPadTemplate *templ = NULL;
394   gint bpc = 0, color_type;
395   png_uint_32 width, height;
396   GstVideoFormat format;
397   GstVideoInfo vinfo = { 0, };
398
399   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR);
400
401   /* Get bits per channel */
402   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
403   if (bpc > 8) {
404     /* Add alpha channel if 16-bit depth */
405     png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE);
406     png_set_swap (pngdec->png);
407   }
408
409   /* Get Color type */
410   color_type = png_get_color_type (pngdec->png, pngdec->info);
411
412 #if 0
413   /* We used to have this HACK to reverse the outgoing bytes, but the problem
414    * that originally required the hack seems to have been in ffmpegcolorspace's
415    * RGBA descriptions. It doesn't seem needed now that's fixed, but might
416    * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */
417   if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
418     png_set_bgr (pngdec->png);
419 #endif
420
421   /* Gray scale converted to RGB and upscaled to 8 bits */
422   if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
423       (color_type == PNG_COLOR_TYPE_GRAY)) {
424     GST_LOG_OBJECT (pngdec, "converting grayscale png to RGB");
425     png_set_gray_to_rgb (pngdec->png);
426     if (bpc < 8) {              /* Convert to 8 bits */
427       GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits");
428 #if PNG_LIBPNG_VER < 10400
429       png_set_gray_1_2_4_to_8 (pngdec->png);
430 #else
431       png_set_expand_gray_1_2_4_to_8 (pngdec->png);
432 #endif
433     }
434   }
435
436   /* Palette converted to RGB */
437   if (color_type == PNG_COLOR_TYPE_PALETTE) {
438     GST_LOG_OBJECT (pngdec, "converting palette png to RGB");
439     png_set_palette_to_rgb (pngdec->png);
440   }
441
442   /* Update the info structure */
443   png_read_update_info (pngdec->png, pngdec->info);
444
445   /* Get IHDR header again after transformation settings */
446
447   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
448       &bpc, &pngdec->color_type, NULL, NULL, NULL);
449
450   pngdec->width = width;
451   pngdec->height = height;
452
453   GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", pngdec->width,
454       pngdec->height);
455
456   switch (pngdec->color_type) {
457     case PNG_COLOR_TYPE_RGB:
458       GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits");
459       format = GST_VIDEO_FORMAT_RGB;
460       break;
461     case PNG_COLOR_TYPE_RGB_ALPHA:
462       GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits");
463       format = GST_VIDEO_FORMAT_RGBA;
464       break;
465     default:
466       GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL),
467           ("pngdec does not support this color type"));
468       ret = GST_FLOW_NOT_SUPPORTED;
469       goto beach;
470   }
471
472   gst_video_info_set_format (&vinfo, format, pngdec->width, pngdec->height);
473   vinfo.fps_n = pngdec->fps_n;
474   vinfo.fps_d = pngdec->fps_d;
475   vinfo.par_n = 1;
476   vinfo.par_d = 1;
477
478   if (memcmp (&vinfo, &pngdec->vinfo, sizeof (vinfo)) == 0) {
479     GST_DEBUG_OBJECT (pngdec, "video info unchanged, skip negotiation");
480     ret = GST_FLOW_OK;
481     goto beach;
482   }
483
484   pngdec->vinfo = vinfo;
485
486   caps = gst_video_info_to_caps (&pngdec->vinfo);
487
488   templ = gst_static_pad_template_get (&gst_pngdec_src_pad_template);
489
490   res = gst_caps_intersect (caps, gst_pad_template_get_caps (templ));
491
492   gst_caps_unref (caps);
493   gst_object_unref (templ);
494
495   if (!gst_pad_set_caps (pngdec->srcpad, res))
496     ret = GST_FLOW_NOT_NEGOTIATED;
497
498   /* clear pending reconfigure */
499   gst_pad_check_reconfigure (pngdec->srcpad);
500
501   GST_DEBUG_OBJECT (pngdec, "our caps %" GST_PTR_FORMAT, res);
502   gst_pngdec_negotiate_pool (pngdec, res, &pngdec->vinfo);
503
504   gst_caps_unref (res);
505
506   /* Push a newsegment event */
507   if (pngdec->need_newsegment) {
508     gst_segment_init (&pngdec->segment, GST_FORMAT_TIME);
509     gst_pad_push_event (pngdec->srcpad,
510         gst_event_new_segment (&pngdec->segment));
511     pngdec->need_newsegment = FALSE;
512   }
513
514 beach:
515   return ret;
516 }
517
518 static void
519 gst_pngdec_task (GstPad * pad)
520 {
521   GstPngDec *pngdec;
522   GstBuffer *buffer = NULL;
523   gint i = 0;
524   png_bytep *rows, inp = NULL;
525   GstFlowReturn ret = GST_FLOW_OK;
526   GstVideoFrame frame;
527
528   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
529
530   GST_LOG_OBJECT (pngdec, "read frame");
531
532   /* Let libpng come back here on error */
533   if (setjmp (png_jmpbuf (pngdec->png))) {
534     ret = GST_FLOW_ERROR;
535     goto pause;
536   }
537
538   /* Set reading callback */
539   png_set_read_fn (pngdec->png, pngdec, user_read_data);
540
541   /* Read info */
542   png_read_info (pngdec->png, pngdec->info);
543
544   pngdec->fps_n = 0;
545   pngdec->fps_d = 1;
546
547   /* Generate the caps and configure */
548   ret = gst_pngdec_caps_create_and_set (pngdec);
549   if (ret != GST_FLOW_OK) {
550     goto pause;
551   }
552
553   /* Allocate output buffer */
554   g_assert (pngdec->pool);
555   ret = gst_buffer_pool_acquire_buffer (pngdec->pool, &buffer, NULL);
556   if (ret != GST_FLOW_OK)
557     goto pause;
558
559   rows = (png_bytep *) g_malloc (sizeof (png_bytep) * pngdec->height);
560
561   if (!gst_video_frame_map (&frame, &pngdec->vinfo, buffer, GST_MAP_WRITE))
562     goto invalid_frame;
563
564   inp = GST_VIDEO_FRAME_COMP_DATA (&frame, 0);
565
566   for (i = 0; i < pngdec->height; i++) {
567     rows[i] = inp;
568     inp += GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0);
569   }
570
571   /* Read the actual picture */
572   png_read_image (pngdec->png, rows);
573   g_free (rows);
574
575   gst_video_frame_unmap (&frame);
576   inp = NULL;
577
578   /* Push the raw RGB frame */
579   ret = gst_pad_push (pngdec->srcpad, buffer);
580   buffer = NULL;
581   if (ret != GST_FLOW_OK)
582     goto pause;
583
584   /* And we are done */
585   gst_pad_pause_task (pngdec->sinkpad);
586   gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
587   return;
588
589 pause:
590   {
591     if (inp)
592       gst_video_frame_unmap (&frame);
593     if (buffer)
594       gst_buffer_unref (buffer);
595     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
596         gst_flow_get_name (ret));
597     gst_pad_pause_task (pngdec->sinkpad);
598     if (ret == GST_FLOW_EOS) {
599       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
600     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
601       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
602           (_("Internal data stream error.")),
603           ("stream stopped, reason %s", gst_flow_get_name (ret)));
604       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
605     }
606   }
607 invalid_frame:
608   {
609     GST_DEBUG_OBJECT (pngdec, "could not map video frame");
610     ret = GST_FLOW_ERROR;
611     goto pause;
612   }
613 }
614
615 static GstFlowReturn
616 gst_pngdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
617 {
618   GstPngDec *pngdec;
619   GstFlowReturn ret = GST_FLOW_OK;
620   GstMapInfo map = GST_MAP_INFO_INIT;
621
622   pngdec = GST_PNGDEC (parent);
623
624   if (G_UNLIKELY (!pngdec->setup))
625     goto not_configured;
626
627   /* Something is going wrong in our callbacks */
628   ret = pngdec->ret;
629   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
630     GST_WARNING_OBJECT (pngdec, "we have a pending return code of %d", ret);
631     goto beach;
632   }
633
634   /* Let libpng come back here on error */
635   if (setjmp (png_jmpbuf (pngdec->png))) {
636     GST_WARNING_OBJECT (pngdec, "error during decoding");
637     ret = GST_FLOW_ERROR;
638     goto beach;
639   }
640
641   pngdec->in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
642   pngdec->in_duration = GST_BUFFER_DURATION (buffer);
643
644   gst_buffer_map (buffer, &map, GST_MAP_READ);
645
646   GST_LOG_OBJECT (pngdec, "Got buffer, size=%d", (gint) map.size);
647
648   /* Progressive loading of the PNG image */
649   png_process_data (pngdec->png, pngdec->info, map.data, map.size);
650
651   if (pngdec->image_ready) {
652     if (pngdec->framed) {
653       /* Reset ourselves for the next frame */
654       gst_pngdec_libpng_clear (pngdec);
655       gst_pngdec_libpng_init (pngdec);
656       GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame");
657       png_set_progressive_read_fn (pngdec->png, pngdec,
658           user_info_callback, user_endrow_callback, user_end_callback);
659     } else {
660       GST_LOG_OBJECT (pngdec, "sending EOS");
661       pngdec->ret = gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
662     }
663     pngdec->image_ready = FALSE;
664   }
665
666   /* grab new return code */
667   ret = pngdec->ret;
668
669 beach:
670   if (G_LIKELY (map.data))
671     gst_buffer_unmap (buffer, &map);
672
673   /* And release the buffer */
674   gst_buffer_unref (buffer);
675
676   return ret;
677
678   /* ERRORS */
679 not_configured:
680   {
681     GST_LOG_OBJECT (pngdec, "we are not configured yet");
682     ret = GST_FLOW_FLUSHING;
683     goto beach;
684   }
685 }
686
687 static gboolean
688 gst_pngdec_sink_setcaps (GstPngDec * pngdec, GstCaps * caps)
689 {
690   GstStructure *s;
691   gint num, denom;
692
693   s = gst_caps_get_structure (caps, 0);
694   if (gst_structure_get_fraction (s, "framerate", &num, &denom)) {
695     GST_DEBUG_OBJECT (pngdec, "framed input");
696     pngdec->framed = TRUE;
697     pngdec->fps_n = num;
698     pngdec->fps_d = denom;
699   } else {
700     GST_DEBUG_OBJECT (pngdec, "single picture input");
701     pngdec->framed = FALSE;
702     pngdec->fps_n = 0;
703     pngdec->fps_d = 1;
704   }
705
706   return TRUE;
707 }
708
709 static gboolean
710 gst_pngdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
711 {
712   GstPngDec *pngdec;
713   gboolean res;
714
715   pngdec = GST_PNGDEC (parent);
716
717   switch (GST_EVENT_TYPE (event)) {
718     case GST_EVENT_SEGMENT:{
719       gst_event_copy_segment (event, &pngdec->segment);
720
721       GST_LOG_OBJECT (pngdec, "SEGMENT %" GST_SEGMENT_FORMAT, &pngdec->segment);
722
723       if (pngdec->segment.format == GST_FORMAT_TIME) {
724         pngdec->need_newsegment = FALSE;
725         res = gst_pad_push_event (pngdec->srcpad, event);
726       } else {
727         gst_event_unref (event);
728         res = TRUE;
729       }
730       break;
731     }
732     case GST_EVENT_FLUSH_STOP:
733     {
734       gst_pngdec_libpng_clear (pngdec);
735       gst_pngdec_libpng_init (pngdec);
736       png_set_progressive_read_fn (pngdec->png, pngdec,
737           user_info_callback, user_endrow_callback, user_end_callback);
738       pngdec->ret = GST_FLOW_OK;
739       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
740       res = gst_pad_push_event (pngdec->srcpad, event);
741       break;
742     }
743     case GST_EVENT_EOS:
744     {
745       GST_LOG_OBJECT (pngdec, "EOS");
746       gst_pngdec_libpng_clear (pngdec);
747       pngdec->ret = GST_FLOW_EOS;
748       res = gst_pad_push_event (pngdec->srcpad, event);
749       break;
750     }
751     case GST_EVENT_CAPS:
752     {
753       GstCaps *caps;
754
755       gst_event_parse_caps (event, &caps);
756       res = gst_pngdec_sink_setcaps (pngdec, caps);
757       gst_event_unref (event);
758       break;
759     }
760     default:
761       res = gst_pad_push_event (pngdec->srcpad, event);
762       break;
763   }
764
765   return res;
766 }
767
768
769 /* Clean up the libpng structures */
770 static gboolean
771 gst_pngdec_libpng_clear (GstPngDec * pngdec)
772 {
773   png_infopp info = NULL, endinfo = NULL;
774
775   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
776
777   GST_LOG ("cleaning up libpng structures");
778
779   if (pngdec->info) {
780     info = &pngdec->info;
781   }
782
783   if (pngdec->endinfo) {
784     endinfo = &pngdec->endinfo;
785   }
786
787   if (pngdec->png) {
788     png_destroy_read_struct (&(pngdec->png), info, endinfo);
789     pngdec->png = NULL;
790     pngdec->info = NULL;
791     pngdec->endinfo = NULL;
792   }
793
794   pngdec->color_type = pngdec->height = pngdec->width = -1;
795   pngdec->offset = 0;
796   pngdec->buffer_out = NULL;
797
798   pngdec->setup = FALSE;
799
800   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
801   pngdec->in_duration = GST_CLOCK_TIME_NONE;
802
803   return TRUE;
804 }
805
806 static gboolean
807 gst_pngdec_libpng_init (GstPngDec * pngdec)
808 {
809   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
810
811   if (pngdec->setup)
812     return TRUE;
813
814   GST_LOG ("init libpng structures");
815
816   /* initialize png struct stuff */
817   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
818       (png_voidp) NULL, user_error_fn, user_warning_fn);
819
820   if (pngdec->png == NULL)
821     goto init_failed;
822
823   pngdec->info = png_create_info_struct (pngdec->png);
824   if (pngdec->info == NULL)
825     goto info_failed;
826
827   pngdec->endinfo = png_create_info_struct (pngdec->png);
828   if (pngdec->endinfo == NULL)
829     goto endinfo_failed;
830
831   pngdec->setup = TRUE;
832
833   return TRUE;
834
835   /* ERRORS */
836 init_failed:
837   {
838     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
839         ("Failed to initialize png structure"));
840     return FALSE;
841   }
842 info_failed:
843   {
844     gst_pngdec_libpng_clear (pngdec);
845     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
846         ("Failed to initialize info structure"));
847     return FALSE;
848   }
849 endinfo_failed:
850   {
851     gst_pngdec_libpng_clear (pngdec);
852     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
853         ("Failed to initialize endinfo structure"));
854     return FALSE;
855   }
856 }
857
858 static GstStateChangeReturn
859 gst_pngdec_change_state (GstElement * element, GstStateChange transition)
860 {
861   GstStateChangeReturn ret;
862   GstPngDec *pngdec;
863
864   pngdec = GST_PNGDEC (element);
865
866   switch (transition) {
867     case GST_STATE_CHANGE_READY_TO_PAUSED:
868       gst_pngdec_libpng_init (pngdec);
869       pngdec->need_newsegment = TRUE;
870       pngdec->framed = FALSE;
871       pngdec->ret = GST_FLOW_OK;
872       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
873       break;
874     default:
875       break;
876   }
877
878   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
879   if (ret != GST_STATE_CHANGE_SUCCESS)
880     return ret;
881
882   switch (transition) {
883     case GST_STATE_CHANGE_PAUSED_TO_READY:
884       gst_pngdec_libpng_clear (pngdec);
885       if (pngdec->pool)
886         gst_object_unref (pngdec->pool);
887       break;
888     default:
889       break;
890   }
891
892   return ret;
893 }
894
895 /* this function gets called when we activate ourselves in pull mode.
896  * We can perform  random access to the resource and we start a task
897  * to start reading */
898 static gboolean
899 gst_pngdec_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
900     GstPadMode mode, gboolean active)
901 {
902   GstPngDec *pngdec = GST_PNGDEC (parent);
903   gboolean res;
904
905   switch (mode) {
906     case GST_PAD_MODE_PULL:
907       if (active) {
908         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_pngdec_task,
909             sinkpad);
910       } else {
911         res = gst_pad_stop_task (sinkpad);
912       }
913       break;
914     case GST_PAD_MODE_PUSH:
915       GST_DEBUG_OBJECT (pngdec, "activating push/chain function");
916       if (active) {
917         pngdec->ret = GST_FLOW_OK;
918
919         /* Let libpng come back here on error */
920         if (setjmp (png_jmpbuf (pngdec->png)))
921           goto setup_failed;
922
923         GST_LOG_OBJECT (pngdec, "setting up progressive loading callbacks");
924         png_set_progressive_read_fn (pngdec->png, pngdec,
925             user_info_callback, user_endrow_callback, user_end_callback);
926       } else {
927         GST_DEBUG_OBJECT (pngdec, "deactivating push/chain function");
928       }
929       res = TRUE;
930       break;
931     default:
932       res = FALSE;
933       break;
934   }
935   return res;
936
937 setup_failed:
938   {
939     GST_LOG_OBJECT (pngdec, "failed setting up libpng jmpbuf");
940     gst_pngdec_libpng_clear (pngdec);
941     return FALSE;
942   }
943 }
944
945 /* this function is called when the pad is activated and should start
946  * processing data.
947  *
948  * We check if we can do random access to decide if we work push or
949  * pull based.
950  */
951 static gboolean
952 gst_pngdec_sink_activate (GstPad * sinkpad, GstObject * parent)
953 {
954   GstQuery *query;
955   gboolean pull_mode;
956
957   query = gst_query_new_scheduling ();
958
959   if (!gst_pad_peer_query (sinkpad, query)) {
960     gst_query_unref (query);
961     goto activate_push;
962   }
963
964   pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
965   gst_query_unref (query);
966
967   if (!pull_mode)
968     goto activate_push;
969
970   GST_DEBUG_OBJECT (sinkpad, "activating pull");
971   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
972
973 activate_push:
974   {
975     GST_DEBUG_OBJECT (sinkpad, "activating push");
976     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
977   }
978 }