pngenc: Support 8 bit grayscale
[platform/upstream/gst-plugins-good.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 void gst_pngdec_base_init (gpointer g_class);
37 static void gst_pngdec_class_init (GstPngDecClass * klass);
38 static void gst_pngdec_init (GstPngDec * pngdec);
39
40 static gboolean gst_pngdec_libpng_init (GstPngDec * pngdec);
41 static gboolean gst_pngdec_libpng_clear (GstPngDec * pngdec);
42
43 static GstStateChangeReturn gst_pngdec_change_state (GstElement * element,
44     GstStateChange transition);
45
46 static gboolean gst_pngdec_sink_activate_push (GstPad * sinkpad,
47     gboolean active);
48 static gboolean gst_pngdec_sink_activate_pull (GstPad * sinkpad,
49     gboolean active);
50 static gboolean gst_pngdec_sink_activate (GstPad * sinkpad);
51
52 static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec);
53
54 static void gst_pngdec_task (GstPad * pad);
55 static GstFlowReturn gst_pngdec_chain (GstPad * pad, GstBuffer * buffer);
56 static gboolean gst_pngdec_sink_event (GstPad * pad, GstEvent * event);
57 static gboolean gst_pngdec_sink_setcaps (GstPad * pad, GstCaps * caps);
58
59 static GstElementClass *parent_class = NULL;
60
61 GType
62 gst_pngdec_get_type (void)
63 {
64   static GType pngdec_type = 0;
65
66   if (!pngdec_type) {
67     static const GTypeInfo pngdec_info = {
68       sizeof (GstPngDecClass),
69       gst_pngdec_base_init,
70       NULL,
71       (GClassInitFunc) gst_pngdec_class_init,
72       NULL,
73       NULL,
74       sizeof (GstPngDec),
75       0,
76       (GInstanceInitFunc) gst_pngdec_init,
77     };
78
79     pngdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstPngDec",
80         &pngdec_info, 0);
81   }
82   return pngdec_type;
83 }
84
85 static GstStaticPadTemplate gst_pngdec_src_pad_template =
86     GST_STATIC_PAD_TEMPLATE ("src",
87     GST_PAD_SRC,
88     GST_PAD_ALWAYS,
89     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB)
90     );
91
92 static GstStaticPadTemplate gst_pngdec_sink_pad_template =
93 GST_STATIC_PAD_TEMPLATE ("sink",
94     GST_PAD_SINK,
95     GST_PAD_ALWAYS,
96     GST_STATIC_CAPS ("image/png")
97     );
98
99 static void
100 gst_pngdec_base_init (gpointer g_class)
101 {
102   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
103
104   gst_element_class_add_pad_template (element_class,
105       gst_static_pad_template_get (&gst_pngdec_src_pad_template));
106   gst_element_class_add_pad_template (element_class,
107       gst_static_pad_template_get (&gst_pngdec_sink_pad_template));
108   gst_element_class_set_details_simple (element_class, "PNG image decoder",
109       "Codec/Decoder/Image",
110       "Decode a png video frame to a raw image",
111       "Wim Taymans <wim@fluendo.com>");
112 }
113
114 static void
115 gst_pngdec_class_init (GstPngDecClass * klass)
116 {
117   GstElementClass *gstelement_class;
118
119   gstelement_class = (GstElementClass *) klass;
120
121   parent_class = g_type_class_peek_parent (klass);
122
123   gstelement_class->change_state = gst_pngdec_change_state;
124
125   GST_DEBUG_CATEGORY_INIT (pngdec_debug, "pngdec", 0, "PNG image decoder");
126 }
127
128 static void
129 gst_pngdec_init (GstPngDec * pngdec)
130 {
131   pngdec->sinkpad =
132       gst_pad_new_from_static_template (&gst_pngdec_sink_pad_template, "sink");
133   gst_pad_set_activate_function (pngdec->sinkpad, gst_pngdec_sink_activate);
134   gst_pad_set_activatepush_function (pngdec->sinkpad,
135       gst_pngdec_sink_activate_push);
136   gst_pad_set_activatepull_function (pngdec->sinkpad,
137       gst_pngdec_sink_activate_pull);
138   gst_pad_set_chain_function (pngdec->sinkpad, gst_pngdec_chain);
139   gst_pad_set_event_function (pngdec->sinkpad, gst_pngdec_sink_event);
140   gst_pad_set_setcaps_function (pngdec->sinkpad, gst_pngdec_sink_setcaps);
141   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->sinkpad);
142
143   pngdec->srcpad =
144       gst_pad_new_from_static_template (&gst_pngdec_src_pad_template, "src");
145   gst_pad_use_fixed_caps (pngdec->srcpad);
146   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->srcpad);
147
148   pngdec->buffer_out = NULL;
149   pngdec->png = NULL;
150   pngdec->info = NULL;
151   pngdec->endinfo = NULL;
152   pngdec->setup = FALSE;
153
154   pngdec->color_type = -1;
155   pngdec->width = -1;
156   pngdec->height = -1;
157   pngdec->bpp = -1;
158   pngdec->fps_n = 0;
159   pngdec->fps_d = 1;
160
161   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
162   pngdec->in_duration = GST_CLOCK_TIME_NONE;
163
164   gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
165
166   pngdec->image_ready = FALSE;
167 }
168
169 static void
170 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
171 {
172   GST_ERROR ("%s", error_msg);
173 }
174
175 static void
176 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
177 {
178   GST_WARNING ("%s", warning_msg);
179 }
180
181 static void
182 user_info_callback (png_structp png_ptr, png_infop info)
183 {
184   GstPngDec *pngdec = NULL;
185   GstFlowReturn ret = GST_FLOW_OK;
186   size_t buffer_size;
187   GstBuffer *buffer = NULL;
188
189   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
190
191   GST_LOG ("info ready");
192
193   /* Generate the caps and configure */
194   ret = gst_pngdec_caps_create_and_set (pngdec);
195   if (ret != GST_FLOW_OK) {
196     goto beach;
197   }
198
199   /* Allocate output buffer */
200   pngdec->rowbytes = png_get_rowbytes (pngdec->png, pngdec->info);
201   if (pngdec->rowbytes > (G_MAXUINT32 - 3)
202       || pngdec->height > G_MAXUINT32 / pngdec->rowbytes) {
203     ret = GST_FLOW_ERROR;
204     goto beach;
205   }
206   pngdec->rowbytes = GST_ROUND_UP_4 (pngdec->rowbytes);
207   buffer_size = pngdec->height * pngdec->rowbytes;
208
209   ret =
210       gst_pad_alloc_buffer_and_set_caps (pngdec->srcpad, GST_BUFFER_OFFSET_NONE,
211       buffer_size, GST_PAD_CAPS (pngdec->srcpad), &buffer);
212   if (ret != GST_FLOW_OK) {
213     goto beach;
214   }
215
216   pngdec->buffer_out = buffer;
217
218 beach:
219   pngdec->ret = ret;
220 }
221
222 static void
223 user_endrow_callback (png_structp png_ptr, png_bytep new_row,
224     png_uint_32 row_num, int pass)
225 {
226   GstPngDec *pngdec = NULL;
227
228   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
229
230   /* FIXME: implement interlaced pictures */
231
232   /* If buffer_out doesn't exist, it means buffer_alloc failed, which 
233    * will already have set the return code */
234   if (GST_IS_BUFFER (pngdec->buffer_out)) {
235     size_t offset = row_num * pngdec->rowbytes;
236
237     GST_LOG ("got row %u, copying in buffer %p at offset %" G_GSIZE_FORMAT,
238         (guint) row_num, pngdec->buffer_out, offset);
239     memcpy (GST_BUFFER_DATA (pngdec->buffer_out) + offset, new_row,
240         pngdec->rowbytes);
241     pngdec->ret = GST_FLOW_OK;
242   }
243 }
244
245 static gboolean
246 buffer_clip (GstPngDec * dec, GstBuffer * buffer)
247 {
248   gboolean res = TRUE;
249   gint64 cstart, cstop;
250
251
252   if ((!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) ||
253       (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer))) ||
254       (dec->segment.format != GST_FORMAT_TIME))
255     goto beach;
256
257   cstart = GST_BUFFER_TIMESTAMP (buffer);
258   cstop = GST_BUFFER_DURATION (buffer);
259
260   if ((res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME,
261               cstart, cstart + cstop, &cstart, &cstop))) {
262     GST_BUFFER_TIMESTAMP (buffer) = cstart;
263     GST_BUFFER_DURATION (buffer) = cstop - cstart;
264   }
265
266 beach:
267   return res;
268 }
269
270 static void
271 user_end_callback (png_structp png_ptr, png_infop info)
272 {
273   GstPngDec *pngdec = NULL;
274
275   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
276
277   GST_LOG_OBJECT (pngdec, "and we are done reading this image");
278
279   if (!pngdec->buffer_out)
280     return;
281
282   if (GST_CLOCK_TIME_IS_VALID (pngdec->in_timestamp))
283     GST_BUFFER_TIMESTAMP (pngdec->buffer_out) = pngdec->in_timestamp;
284   if (GST_CLOCK_TIME_IS_VALID (pngdec->in_duration))
285     GST_BUFFER_DURATION (pngdec->buffer_out) = pngdec->in_duration;
286
287   /* buffer clipping */
288   if (buffer_clip (pngdec, pngdec->buffer_out)) {
289     /* Push our buffer and then EOS if needed */
290     GST_LOG_OBJECT (pngdec, "pushing buffer with ts=%" GST_TIME_FORMAT,
291         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (pngdec->buffer_out)));
292
293     pngdec->ret = gst_pad_push (pngdec->srcpad, pngdec->buffer_out);
294   } else {
295     GST_LOG_OBJECT (pngdec, "dropped decoded buffer");
296     gst_buffer_unref (pngdec->buffer_out);
297   }
298   pngdec->buffer_out = NULL;
299   pngdec->image_ready = TRUE;
300 }
301
302 static void
303 user_read_data (png_structp png_ptr, png_bytep data, png_size_t length)
304 {
305   GstPngDec *pngdec;
306   GstBuffer *buffer;
307   GstFlowReturn ret = GST_FLOW_OK;
308   guint size;
309
310   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
311
312   GST_LOG ("reading %" G_GSIZE_FORMAT " bytes of data at offset %d", length,
313       pngdec->offset);
314
315   ret = gst_pad_pull_range (pngdec->sinkpad, pngdec->offset, length, &buffer);
316   if (ret != GST_FLOW_OK)
317     goto pause;
318
319   size = GST_BUFFER_SIZE (buffer);
320
321   if (size != length)
322     goto short_buffer;
323
324   memcpy (data, GST_BUFFER_DATA (buffer), size);
325
326   gst_buffer_unref (buffer);
327
328   pngdec->offset += length;
329
330   return;
331
332   /* ERRORS */
333 pause:
334   {
335     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
336         gst_flow_get_name (ret));
337     gst_pad_pause_task (pngdec->sinkpad);
338     if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
339       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
340           (_("Internal data stream error.")),
341           ("stream stopped, reason %s", gst_flow_get_name (ret)));
342       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
343     }
344     return;
345   }
346 short_buffer:
347   {
348     gst_buffer_unref (buffer);
349     GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
350         (_("Internal data stream error.")),
351         ("Read %u, needed %" G_GSIZE_FORMAT "bytes", size, length));
352     ret = GST_FLOW_ERROR;
353     goto pause;
354   }
355 }
356
357 static GstFlowReturn
358 gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
359 {
360   GstFlowReturn ret = GST_FLOW_OK;
361   GstCaps *caps = NULL, *res = NULL;
362   GstPadTemplate *templ = NULL;
363   gint bpc = 0, color_type;
364   png_uint_32 width, height;
365
366   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR);
367
368   /* Get bits per channel */
369   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
370
371   /* We don't handle 16 bits per color, strip down to 8 */
372   if (bpc == 16) {
373     GST_LOG_OBJECT (pngdec,
374         "this is a 16 bits per channel PNG image, strip down to 8 bits");
375     png_set_strip_16 (pngdec->png);
376   }
377
378   /* Get Color type */
379   color_type = png_get_color_type (pngdec->png, pngdec->info);
380
381 #if 0
382   /* We used to have this HACK to reverse the outgoing bytes, but the problem
383    * that originally required the hack seems to have been in ffmpegcolorspace's
384    * RGBA descriptions. It doesn't seem needed now that's fixed, but might
385    * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */
386   if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
387     png_set_bgr (pngdec->png);
388 #endif
389
390   /* Gray scale converted to RGB and upscaled to 8 bits */
391   if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
392       (color_type == PNG_COLOR_TYPE_GRAY)) {
393     GST_LOG_OBJECT (pngdec, "converting grayscale png to RGB");
394     png_set_gray_to_rgb (pngdec->png);
395     if (bpc < 8) {              /* Convert to 8 bits */
396       GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits");
397 #if PNG_LIBPNG_VER < 10400
398       png_set_gray_1_2_4_to_8 (pngdec->png);
399 #else
400       png_set_expand_gray_1_2_4_to_8 (pngdec->png);
401 #endif
402     }
403   }
404
405   /* Palette converted to RGB */
406   if (color_type == PNG_COLOR_TYPE_PALETTE) {
407     GST_LOG_OBJECT (pngdec, "converting palette png to RGB");
408     png_set_palette_to_rgb (pngdec->png);
409   }
410
411   /* Update the info structure */
412   png_read_update_info (pngdec->png, pngdec->info);
413
414   /* Get IHDR header again after transformation settings */
415
416   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
417       &bpc, &pngdec->color_type, NULL, NULL, NULL);
418
419   pngdec->width = width;
420   pngdec->height = height;
421
422   GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", pngdec->width,
423       pngdec->height);
424
425   switch (pngdec->color_type) {
426     case PNG_COLOR_TYPE_RGB:
427       GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits");
428       pngdec->bpp = 24;
429       break;
430     case PNG_COLOR_TYPE_RGB_ALPHA:
431       GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits");
432       pngdec->bpp = 32;
433       break;
434     default:
435       GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL),
436           ("pngdec does not support this color type"));
437       ret = GST_FLOW_NOT_SUPPORTED;
438       goto beach;
439   }
440
441   caps = gst_caps_new_simple ("video/x-raw-rgb",
442       "width", G_TYPE_INT, pngdec->width,
443       "height", G_TYPE_INT, pngdec->height,
444       "bpp", G_TYPE_INT, pngdec->bpp,
445       "framerate", GST_TYPE_FRACTION, pngdec->fps_n, pngdec->fps_d, NULL);
446
447   templ = gst_static_pad_template_get (&gst_pngdec_src_pad_template);
448
449   res = gst_caps_intersect (caps, gst_pad_template_get_caps (templ));
450
451   gst_caps_unref (caps);
452   gst_object_unref (templ);
453
454   if (!gst_pad_set_caps (pngdec->srcpad, res))
455     ret = GST_FLOW_NOT_NEGOTIATED;
456
457   GST_DEBUG_OBJECT (pngdec, "our caps %" GST_PTR_FORMAT, res);
458
459   gst_caps_unref (res);
460
461   /* Push a newsegment event */
462   if (pngdec->need_newsegment) {
463     gst_pad_push_event (pngdec->srcpad,
464         gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
465     pngdec->need_newsegment = FALSE;
466   }
467
468 beach:
469   return ret;
470 }
471
472 static void
473 gst_pngdec_task (GstPad * pad)
474 {
475   GstPngDec *pngdec;
476   GstBuffer *buffer = NULL;
477   size_t buffer_size = 0;
478   gint i = 0;
479   png_bytep *rows, inp;
480   png_uint_32 rowbytes;
481   GstFlowReturn ret = GST_FLOW_OK;
482
483   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
484
485   GST_LOG_OBJECT (pngdec, "read frame");
486
487   /* Let libpng come back here on error */
488   if (setjmp (png_jmpbuf (pngdec->png))) {
489     ret = GST_FLOW_ERROR;
490     goto pause;
491   }
492
493   /* Set reading callback */
494   png_set_read_fn (pngdec->png, pngdec, user_read_data);
495
496   /* Read info */
497   png_read_info (pngdec->png, pngdec->info);
498
499   /* Generate the caps and configure */
500   ret = gst_pngdec_caps_create_and_set (pngdec);
501   if (ret != GST_FLOW_OK) {
502     goto pause;
503   }
504
505   /* Allocate output buffer */
506   rowbytes = png_get_rowbytes (pngdec->png, pngdec->info);
507   if (rowbytes > (G_MAXUINT32 - 3) || pngdec->height > G_MAXUINT32 / rowbytes) {
508     ret = GST_FLOW_ERROR;
509     goto pause;
510   }
511   rowbytes = GST_ROUND_UP_4 (rowbytes);
512   buffer_size = pngdec->height * rowbytes;
513   ret =
514       gst_pad_alloc_buffer_and_set_caps (pngdec->srcpad, GST_BUFFER_OFFSET_NONE,
515       buffer_size, GST_PAD_CAPS (pngdec->srcpad), &buffer);
516   if (ret != GST_FLOW_OK)
517     goto pause;
518
519   rows = (png_bytep *) g_malloc (sizeof (png_bytep) * pngdec->height);
520
521   inp = GST_BUFFER_DATA (buffer);
522
523   for (i = 0; i < pngdec->height; i++) {
524     rows[i] = inp;
525     inp += rowbytes;
526   }
527
528   /* Read the actual picture */
529   png_read_image (pngdec->png, rows);
530   g_free (rows);
531
532   /* Push the raw RGB frame */
533   ret = gst_pad_push (pngdec->srcpad, buffer);
534   if (ret != GST_FLOW_OK)
535     goto pause;
536
537   /* And we are done */
538   gst_pad_pause_task (pngdec->sinkpad);
539   gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
540   return;
541
542 pause:
543   {
544     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
545         gst_flow_get_name (ret));
546     gst_pad_pause_task (pngdec->sinkpad);
547     if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
548       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
549           (_("Internal data stream error.")),
550           ("stream stopped, reason %s", gst_flow_get_name (ret)));
551       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
552     }
553   }
554 }
555
556 static GstFlowReturn
557 gst_pngdec_chain (GstPad * pad, GstBuffer * buffer)
558 {
559   GstPngDec *pngdec;
560   GstFlowReturn ret = GST_FLOW_OK;
561
562   pngdec = GST_PNGDEC (gst_pad_get_parent (pad));
563
564   GST_LOG_OBJECT (pngdec, "Got buffer, size=%u", GST_BUFFER_SIZE (buffer));
565
566   if (G_UNLIKELY (!pngdec->setup))
567     goto not_configured;
568
569   /* Something is going wrong in our callbacks */
570   ret = pngdec->ret;
571   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
572     GST_WARNING_OBJECT (pngdec, "we have a pending return code of %d", ret);
573     goto beach;
574   }
575
576   /* Let libpng come back here on error */
577   if (setjmp (png_jmpbuf (pngdec->png))) {
578     GST_WARNING_OBJECT (pngdec, "error during decoding");
579     ret = GST_FLOW_ERROR;
580     goto beach;
581   }
582
583   pngdec->in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
584   pngdec->in_duration = GST_BUFFER_DURATION (buffer);
585
586   /* Progressive loading of the PNG image */
587   png_process_data (pngdec->png, pngdec->info, GST_BUFFER_DATA (buffer),
588       GST_BUFFER_SIZE (buffer));
589
590   if (pngdec->image_ready) {
591     if (pngdec->framed) {
592       /* Reset ourselves for the next frame */
593       gst_pngdec_libpng_clear (pngdec);
594       gst_pngdec_libpng_init (pngdec);
595       GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame");
596       png_set_progressive_read_fn (pngdec->png, pngdec,
597           user_info_callback, user_endrow_callback, user_end_callback);
598     } else {
599       GST_LOG_OBJECT (pngdec, "sending EOS");
600       pngdec->ret = gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
601     }
602     pngdec->image_ready = FALSE;
603   }
604
605   /* grab new return code */
606   ret = pngdec->ret;
607
608   /* And release the buffer */
609   gst_buffer_unref (buffer);
610
611 beach:
612   gst_object_unref (pngdec);
613
614   return ret;
615
616   /* ERRORS */
617 not_configured:
618   {
619     GST_LOG_OBJECT (pngdec, "we are not configured yet");
620     ret = GST_FLOW_WRONG_STATE;
621     goto beach;
622   }
623 }
624
625 static gboolean
626 gst_pngdec_sink_setcaps (GstPad * pad, GstCaps * caps)
627 {
628   GstStructure *s;
629   GstPngDec *pngdec;
630   gint num, denom;
631
632   pngdec = GST_PNGDEC (gst_pad_get_parent (pad));
633
634   s = gst_caps_get_structure (caps, 0);
635   if (gst_structure_get_fraction (s, "framerate", &num, &denom)) {
636     GST_DEBUG_OBJECT (pngdec, "framed input");
637     pngdec->framed = TRUE;
638     pngdec->fps_n = num;
639     pngdec->fps_d = denom;
640   } else {
641     GST_DEBUG_OBJECT (pngdec, "single picture input");
642     pngdec->framed = FALSE;
643     pngdec->fps_n = 0;
644     pngdec->fps_d = 1;
645   }
646
647   gst_object_unref (pngdec);
648   return TRUE;
649 }
650
651 static gboolean
652 gst_pngdec_sink_event (GstPad * pad, GstEvent * event)
653 {
654   GstPngDec *pngdec;
655   gboolean res;
656
657   pngdec = GST_PNGDEC (gst_pad_get_parent (pad));
658
659   switch (GST_EVENT_TYPE (event)) {
660     case GST_EVENT_NEWSEGMENT:{
661       gdouble rate, arate;
662       gboolean update;
663       gint64 start, stop, position;
664       GstFormat fmt;
665
666       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
667           &start, &stop, &position);
668
669       gst_segment_set_newsegment_full (&pngdec->segment, update, rate, arate,
670           fmt, start, stop, position);
671
672       GST_LOG_OBJECT (pngdec, "NEWSEGMENT (%s)", gst_format_get_name (fmt));
673
674       if (fmt == GST_FORMAT_TIME) {
675         pngdec->need_newsegment = FALSE;
676         res = gst_pad_push_event (pngdec->srcpad, event);
677       } else {
678         gst_event_unref (event);
679         res = TRUE;
680       }
681       break;
682     }
683     case GST_EVENT_FLUSH_STOP:
684     {
685       gst_pngdec_libpng_clear (pngdec);
686       gst_pngdec_libpng_init (pngdec);
687       png_set_progressive_read_fn (pngdec->png, pngdec,
688           user_info_callback, user_endrow_callback, user_end_callback);
689       pngdec->ret = GST_FLOW_OK;
690       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
691       res = gst_pad_push_event (pngdec->srcpad, event);
692       break;
693     }
694     case GST_EVENT_EOS:
695     {
696       GST_LOG_OBJECT (pngdec, "EOS");
697       gst_pngdec_libpng_clear (pngdec);
698       pngdec->ret = GST_FLOW_UNEXPECTED;
699       res = gst_pad_push_event (pngdec->srcpad, event);
700       break;
701     }
702     default:
703       res = gst_pad_push_event (pngdec->srcpad, event);
704       break;
705   }
706
707   gst_object_unref (pngdec);
708   return res;
709 }
710
711
712 /* Clean up the libpng structures */
713 static gboolean
714 gst_pngdec_libpng_clear (GstPngDec * pngdec)
715 {
716   png_infopp info = NULL, endinfo = NULL;
717
718   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
719
720   GST_LOG ("cleaning up libpng structures");
721
722   if (pngdec->info) {
723     info = &pngdec->info;
724   }
725
726   if (pngdec->endinfo) {
727     endinfo = &pngdec->endinfo;
728   }
729
730   if (pngdec->png) {
731     png_destroy_read_struct (&(pngdec->png), info, endinfo);
732     pngdec->png = NULL;
733     pngdec->info = NULL;
734     pngdec->endinfo = NULL;
735   }
736
737   pngdec->bpp = pngdec->color_type = pngdec->height = pngdec->width = -1;
738   pngdec->offset = 0;
739   pngdec->rowbytes = 0;
740   pngdec->buffer_out = NULL;
741
742   pngdec->setup = FALSE;
743
744   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
745   pngdec->in_duration = GST_CLOCK_TIME_NONE;
746
747   return TRUE;
748 }
749
750 static gboolean
751 gst_pngdec_libpng_init (GstPngDec * pngdec)
752 {
753   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
754
755   if (pngdec->setup)
756     return TRUE;
757
758   GST_LOG ("init libpng structures");
759
760   /* initialize png struct stuff */
761   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
762       (png_voidp) NULL, user_error_fn, user_warning_fn);
763
764   if (pngdec->png == NULL)
765     goto init_failed;
766
767   pngdec->info = png_create_info_struct (pngdec->png);
768   if (pngdec->info == NULL)
769     goto info_failed;
770
771   pngdec->endinfo = png_create_info_struct (pngdec->png);
772   if (pngdec->endinfo == NULL)
773     goto endinfo_failed;
774
775   pngdec->setup = TRUE;
776
777   return TRUE;
778
779   /* ERRORS */
780 init_failed:
781   {
782     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
783         ("Failed to initialize png structure"));
784     return FALSE;
785   }
786 info_failed:
787   {
788     gst_pngdec_libpng_clear (pngdec);
789     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
790         ("Failed to initialize info structure"));
791     return FALSE;
792   }
793 endinfo_failed:
794   {
795     gst_pngdec_libpng_clear (pngdec);
796     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
797         ("Failed to initialize endinfo structure"));
798     return FALSE;
799   }
800 }
801
802 static GstStateChangeReturn
803 gst_pngdec_change_state (GstElement * element, GstStateChange transition)
804 {
805   GstStateChangeReturn ret;
806   GstPngDec *pngdec;
807
808   pngdec = GST_PNGDEC (element);
809
810   switch (transition) {
811     case GST_STATE_CHANGE_READY_TO_PAUSED:
812       gst_pngdec_libpng_init (pngdec);
813       pngdec->need_newsegment = TRUE;
814       pngdec->framed = FALSE;
815       pngdec->ret = GST_FLOW_OK;
816       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
817       break;
818     default:
819       break;
820   }
821
822   ret = parent_class->change_state (element, transition);
823   if (ret != GST_STATE_CHANGE_SUCCESS)
824     return ret;
825
826   switch (transition) {
827     case GST_STATE_CHANGE_PAUSED_TO_READY:
828       gst_pngdec_libpng_clear (pngdec);
829       break;
830     default:
831       break;
832   }
833
834   return ret;
835 }
836
837 /* this function gets called when we activate ourselves in push mode. */
838 static gboolean
839 gst_pngdec_sink_activate_push (GstPad * sinkpad, gboolean active)
840 {
841   GstPngDec *pngdec;
842
843   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (sinkpad));
844
845   pngdec->ret = GST_FLOW_OK;
846
847   if (active) {
848     /* Let libpng come back here on error */
849     if (setjmp (png_jmpbuf (pngdec->png)))
850       goto setup_failed;
851
852     GST_LOG ("setting up progressive loading callbacks");
853     png_set_progressive_read_fn (pngdec->png, pngdec,
854         user_info_callback, user_endrow_callback, user_end_callback);
855   }
856   return TRUE;
857
858 setup_failed:
859   {
860     GST_LOG ("failed setting up libpng jmpbuf");
861     gst_pngdec_libpng_clear (pngdec);
862     return FALSE;
863   }
864 }
865
866 /* this function gets called when we activate ourselves in pull mode.
867  * We can perform  random access to the resource and we start a task
868  * to start reading */
869 static gboolean
870 gst_pngdec_sink_activate_pull (GstPad * sinkpad, gboolean active)
871 {
872   if (active) {
873     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_pngdec_task,
874         sinkpad);
875   } else {
876     return gst_pad_stop_task (sinkpad);
877   }
878 }
879
880 /* this function is called when the pad is activated and should start
881  * processing data.
882  *
883  * We check if we can do random access to decide if we work push or
884  * pull based.
885  */
886 static gboolean
887 gst_pngdec_sink_activate (GstPad * sinkpad)
888 {
889   if (gst_pad_check_pull_range (sinkpad)) {
890     return gst_pad_activate_pull (sinkpad, TRUE);
891   } else {
892     return gst_pad_activate_push (sinkpad, TRUE);
893   }
894 }