GST_FLOW_WRONG_STATE -> GST_FLOW_FLUSHING
[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, 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         &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     alignment = 0;
359   }
360
361   if (pool == NULL) {
362     /* we did not get a pool, make one ourselves then */
363     pool = gst_buffer_pool_new ();
364   }
365
366   if (dec->pool)
367     gst_object_unref (dec->pool);
368   dec->pool = pool;
369
370   config = gst_buffer_pool_get_config (pool);
371   gst_buffer_pool_config_set (config, caps, size, min, max, prefix, alignment);
372   /* just set the option, if the pool can support it we will transparently use
373    * it through the video info API. We could also see if the pool support this
374    * option and only activate it then. */
375   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
376
377   gst_buffer_pool_set_config (pool, config);
378   /* and activate */
379   gst_buffer_pool_set_active (pool, TRUE);
380
381   gst_query_unref (query);
382
383   return GST_FLOW_OK;
384 }
385
386 static GstFlowReturn
387 gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
388 {
389   GstFlowReturn ret = GST_FLOW_OK;
390   GstCaps *caps = NULL, *res = NULL;
391   GstPadTemplate *templ = NULL;
392   gint bpc = 0, color_type;
393   png_uint_32 width, height;
394   GstVideoFormat format;
395   GstVideoInfo vinfo = { 0, };
396
397   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR);
398
399   /* Get bits per channel */
400   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
401   if (bpc > 8) {
402     /* Add alpha channel if 16-bit depth */
403     png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE);
404     png_set_swap (pngdec->png);
405   }
406
407   /* Get Color type */
408   color_type = png_get_color_type (pngdec->png, pngdec->info);
409
410 #if 0
411   /* We used to have this HACK to reverse the outgoing bytes, but the problem
412    * that originally required the hack seems to have been in ffmpegcolorspace's
413    * RGBA descriptions. It doesn't seem needed now that's fixed, but might
414    * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */
415   if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
416     png_set_bgr (pngdec->png);
417 #endif
418
419   /* Gray scale converted to RGB and upscaled to 8 bits */
420   if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
421       (color_type == PNG_COLOR_TYPE_GRAY)) {
422     GST_LOG_OBJECT (pngdec, "converting grayscale png to RGB");
423     png_set_gray_to_rgb (pngdec->png);
424     if (bpc < 8) {              /* Convert to 8 bits */
425       GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits");
426 #if PNG_LIBPNG_VER < 10400
427       png_set_gray_1_2_4_to_8 (pngdec->png);
428 #else
429       png_set_expand_gray_1_2_4_to_8 (pngdec->png);
430 #endif
431     }
432   }
433
434   /* Palette converted to RGB */
435   if (color_type == PNG_COLOR_TYPE_PALETTE) {
436     GST_LOG_OBJECT (pngdec, "converting palette png to RGB");
437     png_set_palette_to_rgb (pngdec->png);
438   }
439
440   /* Update the info structure */
441   png_read_update_info (pngdec->png, pngdec->info);
442
443   /* Get IHDR header again after transformation settings */
444
445   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
446       &bpc, &pngdec->color_type, NULL, NULL, NULL);
447
448   pngdec->width = width;
449   pngdec->height = height;
450
451   GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", pngdec->width,
452       pngdec->height);
453
454   switch (pngdec->color_type) {
455     case PNG_COLOR_TYPE_RGB:
456       GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits");
457       format = GST_VIDEO_FORMAT_RGB;
458       break;
459     case PNG_COLOR_TYPE_RGB_ALPHA:
460       GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits");
461       format = GST_VIDEO_FORMAT_RGBA;
462       break;
463     default:
464       GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL),
465           ("pngdec does not support this color type"));
466       ret = GST_FLOW_NOT_SUPPORTED;
467       goto beach;
468   }
469
470   gst_video_info_set_format (&vinfo, format, pngdec->width, pngdec->height);
471   vinfo.fps_n = pngdec->fps_n;
472   vinfo.fps_d = pngdec->fps_d;
473   vinfo.par_n = 1;
474   vinfo.par_d = 1;
475
476   if (memcmp (&vinfo, &pngdec->vinfo, sizeof (vinfo)) == 0) {
477     GST_DEBUG_OBJECT (pngdec, "video info unchanged, skip negotiation");
478     ret = GST_FLOW_OK;
479     goto beach;
480   }
481
482   pngdec->vinfo = vinfo;
483
484   caps = gst_video_info_to_caps (&pngdec->vinfo);
485
486   templ = gst_static_pad_template_get (&gst_pngdec_src_pad_template);
487
488   res = gst_caps_intersect (caps, gst_pad_template_get_caps (templ));
489
490   gst_caps_unref (caps);
491   gst_object_unref (templ);
492
493   if (!gst_pad_set_caps (pngdec->srcpad, res))
494     ret = GST_FLOW_NOT_NEGOTIATED;
495
496   /* clear pending reconfigure */
497   gst_pad_check_reconfigure (pngdec->srcpad);
498
499   GST_DEBUG_OBJECT (pngdec, "our caps %" GST_PTR_FORMAT, res);
500   gst_pngdec_negotiate_pool (pngdec, res, &pngdec->vinfo);
501
502   gst_caps_unref (res);
503
504   /* Push a newsegment event */
505   if (pngdec->need_newsegment) {
506     gst_segment_init (&pngdec->segment, GST_FORMAT_TIME);
507     gst_pad_push_event (pngdec->srcpad,
508         gst_event_new_segment (&pngdec->segment));
509     pngdec->need_newsegment = FALSE;
510   }
511
512 beach:
513   return ret;
514 }
515
516 static void
517 gst_pngdec_task (GstPad * pad)
518 {
519   GstPngDec *pngdec;
520   GstBuffer *buffer = NULL;
521   gint i = 0;
522   png_bytep *rows, inp = NULL;
523   GstFlowReturn ret = GST_FLOW_OK;
524   GstVideoFrame frame;
525
526   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
527
528   GST_LOG_OBJECT (pngdec, "read frame");
529
530   /* Let libpng come back here on error */
531   if (setjmp (png_jmpbuf (pngdec->png))) {
532     ret = GST_FLOW_ERROR;
533     goto pause;
534   }
535
536   /* Set reading callback */
537   png_set_read_fn (pngdec->png, pngdec, user_read_data);
538
539   /* Read info */
540   png_read_info (pngdec->png, pngdec->info);
541
542   pngdec->fps_n = 0;
543   pngdec->fps_d = 1;
544
545   /* Generate the caps and configure */
546   ret = gst_pngdec_caps_create_and_set (pngdec);
547   if (ret != GST_FLOW_OK) {
548     goto pause;
549   }
550
551   /* Allocate output buffer */
552   g_assert (pngdec->pool);
553   ret = gst_buffer_pool_acquire_buffer (pngdec->pool, &buffer, NULL);
554   if (ret != GST_FLOW_OK)
555     goto pause;
556
557   rows = (png_bytep *) g_malloc (sizeof (png_bytep) * pngdec->height);
558
559   if (!gst_video_frame_map (&frame, &pngdec->vinfo, buffer, GST_MAP_WRITE))
560     goto invalid_frame;
561
562   inp = GST_VIDEO_FRAME_COMP_DATA (&frame, 0);
563
564   for (i = 0; i < pngdec->height; i++) {
565     rows[i] = inp;
566     inp += GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0);
567   }
568
569   /* Read the actual picture */
570   png_read_image (pngdec->png, rows);
571   g_free (rows);
572
573   gst_video_frame_unmap (&frame);
574   inp = NULL;
575
576   /* Push the raw RGB frame */
577   ret = gst_pad_push (pngdec->srcpad, buffer);
578   buffer = NULL;
579   if (ret != GST_FLOW_OK)
580     goto pause;
581
582   /* And we are done */
583   gst_pad_pause_task (pngdec->sinkpad);
584   gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
585   return;
586
587 pause:
588   {
589     if (inp)
590       gst_video_frame_unmap (&frame);
591     if (buffer)
592       gst_buffer_unref (buffer);
593     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
594         gst_flow_get_name (ret));
595     gst_pad_pause_task (pngdec->sinkpad);
596     if (ret == GST_FLOW_EOS) {
597       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
598     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
599       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
600           (_("Internal data stream error.")),
601           ("stream stopped, reason %s", gst_flow_get_name (ret)));
602       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
603     }
604   }
605 invalid_frame:
606   {
607     GST_DEBUG_OBJECT (pngdec, "could not map video frame");
608     ret = GST_FLOW_ERROR;
609     goto pause;
610   }
611 }
612
613 static GstFlowReturn
614 gst_pngdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
615 {
616   GstPngDec *pngdec;
617   GstFlowReturn ret = GST_FLOW_OK;
618   GstMapInfo map = GST_MAP_INFO_INIT;
619
620   pngdec = GST_PNGDEC (parent);
621
622   if (G_UNLIKELY (!pngdec->setup))
623     goto not_configured;
624
625   /* Something is going wrong in our callbacks */
626   ret = pngdec->ret;
627   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
628     GST_WARNING_OBJECT (pngdec, "we have a pending return code of %d", ret);
629     goto beach;
630   }
631
632   /* Let libpng come back here on error */
633   if (setjmp (png_jmpbuf (pngdec->png))) {
634     GST_WARNING_OBJECT (pngdec, "error during decoding");
635     ret = GST_FLOW_ERROR;
636     goto beach;
637   }
638
639   pngdec->in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
640   pngdec->in_duration = GST_BUFFER_DURATION (buffer);
641
642   gst_buffer_map (buffer, &map, GST_MAP_READ);
643
644   GST_LOG_OBJECT (pngdec, "Got buffer, size=%d", (gint) map.size);
645
646   /* Progressive loading of the PNG image */
647   png_process_data (pngdec->png, pngdec->info, map.data, map.size);
648
649   if (pngdec->image_ready) {
650     if (pngdec->framed) {
651       /* Reset ourselves for the next frame */
652       gst_pngdec_libpng_clear (pngdec);
653       gst_pngdec_libpng_init (pngdec);
654       GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame");
655       png_set_progressive_read_fn (pngdec->png, pngdec,
656           user_info_callback, user_endrow_callback, user_end_callback);
657     } else {
658       GST_LOG_OBJECT (pngdec, "sending EOS");
659       pngdec->ret = gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
660     }
661     pngdec->image_ready = FALSE;
662   }
663
664   /* grab new return code */
665   ret = pngdec->ret;
666
667 beach:
668   if (G_LIKELY (map.data))
669     gst_buffer_unmap (buffer, &map);
670
671   /* And release the buffer */
672   gst_buffer_unref (buffer);
673
674   return ret;
675
676   /* ERRORS */
677 not_configured:
678   {
679     GST_LOG_OBJECT (pngdec, "we are not configured yet");
680     ret = GST_FLOW_FLUSHING;
681     goto beach;
682   }
683 }
684
685 static gboolean
686 gst_pngdec_sink_setcaps (GstPngDec * pngdec, GstCaps * caps)
687 {
688   GstStructure *s;
689   gint num, denom;
690
691   s = gst_caps_get_structure (caps, 0);
692   if (gst_structure_get_fraction (s, "framerate", &num, &denom)) {
693     GST_DEBUG_OBJECT (pngdec, "framed input");
694     pngdec->framed = TRUE;
695     pngdec->fps_n = num;
696     pngdec->fps_d = denom;
697   } else {
698     GST_DEBUG_OBJECT (pngdec, "single picture input");
699     pngdec->framed = FALSE;
700     pngdec->fps_n = 0;
701     pngdec->fps_d = 1;
702   }
703
704   return TRUE;
705 }
706
707 static gboolean
708 gst_pngdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
709 {
710   GstPngDec *pngdec;
711   gboolean res;
712
713   pngdec = GST_PNGDEC (parent);
714
715   switch (GST_EVENT_TYPE (event)) {
716     case GST_EVENT_SEGMENT:{
717       gst_event_copy_segment (event, &pngdec->segment);
718
719       GST_LOG_OBJECT (pngdec, "SEGMENT %" GST_SEGMENT_FORMAT, &pngdec->segment);
720
721       if (pngdec->segment.format == GST_FORMAT_TIME) {
722         pngdec->need_newsegment = FALSE;
723         res = gst_pad_push_event (pngdec->srcpad, event);
724       } else {
725         gst_event_unref (event);
726         res = TRUE;
727       }
728       break;
729     }
730     case GST_EVENT_FLUSH_STOP:
731     {
732       gst_pngdec_libpng_clear (pngdec);
733       gst_pngdec_libpng_init (pngdec);
734       png_set_progressive_read_fn (pngdec->png, pngdec,
735           user_info_callback, user_endrow_callback, user_end_callback);
736       pngdec->ret = GST_FLOW_OK;
737       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
738       res = gst_pad_push_event (pngdec->srcpad, event);
739       break;
740     }
741     case GST_EVENT_EOS:
742     {
743       GST_LOG_OBJECT (pngdec, "EOS");
744       gst_pngdec_libpng_clear (pngdec);
745       pngdec->ret = GST_FLOW_EOS;
746       res = gst_pad_push_event (pngdec->srcpad, event);
747       break;
748     }
749     case GST_EVENT_CAPS:
750     {
751       GstCaps *caps;
752
753       gst_event_parse_caps (event, &caps);
754       res = gst_pngdec_sink_setcaps (pngdec, caps);
755       gst_event_unref (event);
756       break;
757     }
758     default:
759       res = gst_pad_push_event (pngdec->srcpad, event);
760       break;
761   }
762
763   return res;
764 }
765
766
767 /* Clean up the libpng structures */
768 static gboolean
769 gst_pngdec_libpng_clear (GstPngDec * pngdec)
770 {
771   png_infopp info = NULL, endinfo = NULL;
772
773   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
774
775   GST_LOG ("cleaning up libpng structures");
776
777   if (pngdec->info) {
778     info = &pngdec->info;
779   }
780
781   if (pngdec->endinfo) {
782     endinfo = &pngdec->endinfo;
783   }
784
785   if (pngdec->png) {
786     png_destroy_read_struct (&(pngdec->png), info, endinfo);
787     pngdec->png = NULL;
788     pngdec->info = NULL;
789     pngdec->endinfo = NULL;
790   }
791
792   pngdec->color_type = pngdec->height = pngdec->width = -1;
793   pngdec->offset = 0;
794   pngdec->buffer_out = NULL;
795
796   pngdec->setup = FALSE;
797
798   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
799   pngdec->in_duration = GST_CLOCK_TIME_NONE;
800
801   return TRUE;
802 }
803
804 static gboolean
805 gst_pngdec_libpng_init (GstPngDec * pngdec)
806 {
807   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
808
809   if (pngdec->setup)
810     return TRUE;
811
812   GST_LOG ("init libpng structures");
813
814   /* initialize png struct stuff */
815   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
816       (png_voidp) NULL, user_error_fn, user_warning_fn);
817
818   if (pngdec->png == NULL)
819     goto init_failed;
820
821   pngdec->info = png_create_info_struct (pngdec->png);
822   if (pngdec->info == NULL)
823     goto info_failed;
824
825   pngdec->endinfo = png_create_info_struct (pngdec->png);
826   if (pngdec->endinfo == NULL)
827     goto endinfo_failed;
828
829   pngdec->setup = TRUE;
830
831   return TRUE;
832
833   /* ERRORS */
834 init_failed:
835   {
836     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
837         ("Failed to initialize png structure"));
838     return FALSE;
839   }
840 info_failed:
841   {
842     gst_pngdec_libpng_clear (pngdec);
843     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
844         ("Failed to initialize info structure"));
845     return FALSE;
846   }
847 endinfo_failed:
848   {
849     gst_pngdec_libpng_clear (pngdec);
850     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
851         ("Failed to initialize endinfo structure"));
852     return FALSE;
853   }
854 }
855
856 static GstStateChangeReturn
857 gst_pngdec_change_state (GstElement * element, GstStateChange transition)
858 {
859   GstStateChangeReturn ret;
860   GstPngDec *pngdec;
861
862   pngdec = GST_PNGDEC (element);
863
864   switch (transition) {
865     case GST_STATE_CHANGE_READY_TO_PAUSED:
866       gst_pngdec_libpng_init (pngdec);
867       pngdec->need_newsegment = TRUE;
868       pngdec->framed = FALSE;
869       pngdec->ret = GST_FLOW_OK;
870       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
871       break;
872     default:
873       break;
874   }
875
876   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
877   if (ret != GST_STATE_CHANGE_SUCCESS)
878     return ret;
879
880   switch (transition) {
881     case GST_STATE_CHANGE_PAUSED_TO_READY:
882       gst_pngdec_libpng_clear (pngdec);
883       if (pngdec->pool)
884         gst_object_unref (pngdec->pool);
885       break;
886     default:
887       break;
888   }
889
890   return ret;
891 }
892
893 /* this function gets called when we activate ourselves in pull mode.
894  * We can perform  random access to the resource and we start a task
895  * to start reading */
896 static gboolean
897 gst_pngdec_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
898     GstPadMode mode, gboolean active)
899 {
900   GstPngDec *pngdec = GST_PNGDEC (parent);
901   gboolean res;
902
903   switch (mode) {
904     case GST_PAD_MODE_PULL:
905       if (active) {
906         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_pngdec_task,
907             sinkpad);
908       } else {
909         res = gst_pad_stop_task (sinkpad);
910       }
911       break;
912     case GST_PAD_MODE_PUSH:
913       GST_DEBUG_OBJECT (pngdec, "activating push/chain function");
914       if (active) {
915         pngdec->ret = GST_FLOW_OK;
916
917         /* Let libpng come back here on error */
918         if (setjmp (png_jmpbuf (pngdec->png)))
919           goto setup_failed;
920
921         GST_LOG_OBJECT (pngdec, "setting up progressive loading callbacks");
922         png_set_progressive_read_fn (pngdec->png, pngdec,
923             user_info_callback, user_endrow_callback, user_end_callback);
924       } else {
925         GST_DEBUG_OBJECT (pngdec, "deactivating push/chain function");
926       }
927       res = TRUE;
928       break;
929     default:
930       res = FALSE;
931       break;
932   }
933   return res;
934
935 setup_failed:
936   {
937     GST_LOG_OBJECT (pngdec, "failed setting up libpng jmpbuf");
938     gst_pngdec_libpng_clear (pngdec);
939     return FALSE;
940   }
941 }
942
943 /* this function is called when the pad is activated and should start
944  * processing data.
945  *
946  * We check if we can do random access to decide if we work push or
947  * pull based.
948  */
949 static gboolean
950 gst_pngdec_sink_activate (GstPad * sinkpad, GstObject * parent)
951 {
952   GstQuery *query;
953   gboolean pull_mode;
954
955   query = gst_query_new_scheduling ();
956
957   if (!gst_pad_peer_query (sinkpad, query)) {
958     gst_query_unref (query);
959     goto activate_push;
960   }
961
962   pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
963   gst_query_unref (query);
964
965   if (!pull_mode)
966     goto activate_push;
967
968   GST_DEBUG_OBJECT (sinkpad, "activating pull");
969   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
970
971 activate_push:
972   {
973     GST_DEBUG_OBJECT (sinkpad, "activating push");
974     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
975   }
976 }