STOPPED->FAILED
[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 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19
20 #include "gstpngdec.h"
21
22 #include <string.h>
23 #include <gst/video/video.h>
24 #include <gst/gst-i18n-plugin.h>
25
26 static GstElementDetails gst_pngdec_details = {
27   "PNG decoder",
28   "Codec/Decoder/Image",
29   "Decode a png video frame to a raw image",
30   "Wim Taymans <wim@fluendo.com>",
31 };
32
33 GST_DEBUG_CATEGORY (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
58 static GstElementClass *parent_class = NULL;
59
60 GType
61 gst_pngdec_get_type (void)
62 {
63   static GType pngdec_type = 0;
64
65   if (!pngdec_type) {
66     static const GTypeInfo pngdec_info = {
67       sizeof (GstPngDecClass),
68       gst_pngdec_base_init,
69       NULL,
70       (GClassInitFunc) gst_pngdec_class_init,
71       NULL,
72       NULL,
73       sizeof (GstPngDec),
74       0,
75       (GInstanceInitFunc) gst_pngdec_init,
76     };
77
78     pngdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstPngDec",
79         &pngdec_info, 0);
80   }
81   return pngdec_type;
82 }
83
84 static GstStaticPadTemplate gst_pngdec_src_pad_template =
85     GST_STATIC_PAD_TEMPLATE ("src",
86     GST_PAD_SRC,
87     GST_PAD_ALWAYS,
88     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB)
89     );
90
91 static GstStaticPadTemplate gst_pngdec_sink_pad_template =
92 GST_STATIC_PAD_TEMPLATE ("sink",
93     GST_PAD_SINK,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS ("image/png")
96     );
97
98 static void
99 gst_pngdec_base_init (gpointer g_class)
100 {
101   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
102
103   gst_element_class_add_pad_template (element_class,
104       gst_static_pad_template_get (&gst_pngdec_src_pad_template));
105   gst_element_class_add_pad_template (element_class,
106       gst_static_pad_template_get (&gst_pngdec_sink_pad_template));
107   gst_element_class_set_details (element_class, &gst_pngdec_details);
108 }
109
110 static void
111 gst_pngdec_class_init (GstPngDecClass * klass)
112 {
113   GObjectClass *gobject_class;
114   GstElementClass *gstelement_class;
115
116   gobject_class = (GObjectClass *) klass;
117   gstelement_class = (GstElementClass *) klass;
118
119   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
120
121   gstelement_class->change_state = gst_pngdec_change_state;
122
123   GST_DEBUG_CATEGORY_INIT (pngdec_debug, "pngdec", 0, "PNG image decoder");
124 }
125
126
127 static void
128 gst_pngdec_init (GstPngDec * pngdec)
129 {
130   pngdec->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get
131       (&gst_pngdec_sink_pad_template), "sink");
132   gst_pad_set_activate_function (pngdec->sinkpad, gst_pngdec_sink_activate);
133   gst_pad_set_activatepush_function (pngdec->sinkpad,
134       gst_pngdec_sink_activate_push);
135   gst_pad_set_activatepull_function (pngdec->sinkpad,
136       gst_pngdec_sink_activate_pull);
137   gst_pad_set_chain_function (pngdec->sinkpad, gst_pngdec_chain);
138   gst_pad_set_event_function (pngdec->sinkpad, gst_pngdec_sink_event);
139   gst_pad_use_fixed_caps (pngdec->sinkpad);
140   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->sinkpad);
141
142   pngdec->srcpad = gst_pad_new_from_template (gst_static_pad_template_get
143       (&gst_pngdec_src_pad_template), "src");
144   gst_pad_use_fixed_caps (pngdec->srcpad);
145   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->srcpad);
146
147   /* gst_pad_set_chain_function (pngdec->sinkpad, gst_pngdec_chain); */
148
149   pngdec->buffer_out = NULL;
150   pngdec->png = NULL;
151   pngdec->info = NULL;
152   pngdec->endinfo = NULL;
153   pngdec->setup = FALSE;
154
155   pngdec->color_type = -1;
156   pngdec->width = -1;
157   pngdec->height = -1;
158   pngdec->bpp = -1;
159   pngdec->fps = 0.0;
160 }
161
162 static void
163 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
164 {
165   GST_ERROR ("%s", error_msg);
166 }
167
168 static void
169 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
170 {
171   GST_WARNING ("%s", warning_msg);
172 }
173
174 static void
175 user_info_callback (png_structp png_ptr, png_infop info)
176 {
177   GstPngDec *pngdec = NULL;
178   GstFlowReturn ret = GST_FLOW_OK;
179   gint bpc = 0;
180   png_uint_32 width, height;
181   size_t buffer_size;
182   GstBuffer *buffer = NULL;
183
184   pngdec = GST_PNGDEC (png_ptr->io_ptr);
185
186   GST_LOG ("info ready");
187
188   /* Get bits per channel */
189   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
190
191   /* We don't handle 16 bits per color, strip down to 8 */
192   if (bpc == 16) {
193     GST_LOG ("this is a 16 bits per channel PNG image, strip down to 8 bits");
194     png_set_strip_16 (pngdec->png);
195   }
196
197   /* Update the info structure */
198   png_read_update_info (pngdec->png, pngdec->info);
199
200   /* Get IHDR header again after transformation settings */
201
202   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
203       &bpc, &pngdec->color_type, NULL, NULL, NULL);
204
205   pngdec->width = width;
206   pngdec->height = height;
207
208   /* Generate the caps and configure */
209   ret = gst_pngdec_caps_create_and_set (pngdec);
210   if (ret != GST_FLOW_OK) {
211     goto beach;
212   }
213
214   /* Allocate output buffer */
215   pngdec->rowbytes = png_get_rowbytes (pngdec->png, pngdec->info);
216   buffer_size = pngdec->height * GST_ROUND_UP_4 (pngdec->rowbytes);
217   ret = gst_pad_alloc_buffer (pngdec->srcpad, GST_BUFFER_OFFSET_NONE,
218       buffer_size, GST_PAD_CAPS (pngdec->srcpad), &buffer);
219   if (ret != GST_FLOW_OK) {
220     goto beach;
221   }
222
223   pngdec->buffer_out = buffer;
224
225 beach:
226   pngdec->ret = ret;
227 }
228
229 static void
230 user_endrow_callback (png_structp png_ptr, png_bytep new_row,
231     png_uint_32 row_num, int pass)
232 {
233   GstPngDec *pngdec = NULL;
234
235   pngdec = GST_PNGDEC (png_ptr->io_ptr);
236
237   /* FIXME: implement interlaced pictures */
238
239   if (GST_IS_BUFFER (pngdec->buffer_out)) {
240     size_t offset = row_num * GST_ROUND_UP_4 (pngdec->rowbytes);
241
242     GST_LOG ("got row %d, copying in buffer %p at offset %d", row_num,
243         pngdec->buffer_out, offset);
244     memcpy (GST_BUFFER_DATA (pngdec->buffer_out) + offset, new_row,
245         pngdec->rowbytes);
246     pngdec->ret = GST_FLOW_OK;
247   } else {
248     GST_LOG ("we don't have any output buffer to write this row !");
249     pngdec->ret = GST_FLOW_ERROR;
250   }
251 }
252
253 static void
254 user_end_callback (png_structp png_ptr, png_infop info)
255 {
256   GstPngDec *pngdec = NULL;
257
258   pngdec = GST_PNGDEC (png_ptr->io_ptr);
259
260   GST_LOG ("and we are done reading this image, pushing it and setting eos");
261
262   /* Push our buffer and then EOS */
263   pngdec->ret = gst_pad_push (pngdec->srcpad, pngdec->buffer_out);
264   pngdec->ret = gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
265 }
266
267 static void
268 user_read_data (png_structp png_ptr, png_bytep data, png_size_t length)
269 {
270   GstPngDec *pngdec;
271   GstBuffer *buffer;
272   GstFlowReturn ret = GST_FLOW_OK;
273
274   pngdec = GST_PNGDEC (png_ptr->io_ptr);
275
276   GST_LOG ("reading %d bytes of data at offset %d", length, pngdec->offset);
277
278   ret = gst_pad_pull_range (pngdec->sinkpad, pngdec->offset, length, &buffer);
279   if ((ret != GST_FLOW_OK) || (GST_BUFFER_SIZE (buffer) != length))
280     goto pause;
281
282   memcpy (data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
283
284   gst_buffer_unref (buffer);
285
286   pngdec->offset += length;
287
288   return;
289
290 pause:
291   GST_LOG_OBJECT (pngdec, "pausing task, reason %s", gst_flow_get_name (ret));
292   gst_pad_pause_task (pngdec->sinkpad);
293   if (GST_FLOW_IS_FATAL (ret)) {
294     gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
295     GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
296         (_("Internal data stream error.")),
297         ("stream stopped, reason %s", gst_flow_get_name (ret)));
298   }
299 }
300
301 static GstFlowReturn
302 gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
303 {
304   GstFlowReturn ret = GST_FLOW_OK;
305   GstCaps *caps = NULL, *res = NULL;
306   GstPadTemplate *templ = NULL;
307
308   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR);
309
310   GST_LOG ("this is a %dx%d PNG image", pngdec->width, pngdec->height);
311
312   switch (pngdec->color_type) {
313     case PNG_COLOR_TYPE_RGB:
314       GST_LOG ("we have no alpha channel, depth is 24 bits");
315       pngdec->bpp = 24;
316       break;
317     case PNG_COLOR_TYPE_RGB_ALPHA:
318       GST_LOG ("we have an alpha channel, depth is 32 bits");
319       pngdec->bpp = 32;
320       break;
321     default:
322       GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL),
323           ("pngdec does not support grayscale or paletted data yet"));
324       ret = GST_FLOW_ERROR;
325       goto beach;
326   }
327
328   caps = gst_caps_new_simple ("video/x-raw-rgb",
329       "width", G_TYPE_INT, pngdec->width,
330       "height", G_TYPE_INT, pngdec->height,
331       "bpp", G_TYPE_INT, pngdec->bpp,
332       "framerate", G_TYPE_DOUBLE, pngdec->fps, NULL);
333
334   templ = gst_static_pad_template_get (&gst_pngdec_src_pad_template);
335
336   res = gst_caps_intersect (caps, gst_pad_template_get_caps (templ));
337
338   gst_caps_unref (caps);
339
340   if (!gst_pad_set_caps (pngdec->srcpad, res)) {
341     ret = GST_FLOW_ERROR;
342   }
343
344   GST_LOG ("our caps %s", gst_caps_to_string (res));
345
346   gst_caps_unref (res);
347
348 beach:
349   return ret;
350 }
351
352 static void
353 gst_pngdec_task (GstPad * pad)
354 {
355   GstPngDec *pngdec;
356   GstBuffer *buffer = NULL;
357   size_t buffer_size = 0;
358   gint i = 0, bpc = 0;
359   png_bytep *rows, inp;
360   png_uint_32 width, height, rowbytes;
361   GstFlowReturn ret = GST_FLOW_OK;
362
363   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
364
365   /* Let libpng come back here on error */
366   if (setjmp (png_jmpbuf (pngdec->png))) {
367     ret = GST_FLOW_ERROR;
368     goto pause;
369   }
370
371   /* Set reading callback */
372   png_set_read_fn (pngdec->png, pngdec, user_read_data);
373
374   /* Read info */
375   png_read_info (pngdec->png, pngdec->info);
376
377   /* Get bits per channel */
378   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
379
380   /* We don't handle 16 bits per color, strip down to 8 */
381   if (bpc == 16) {
382     GST_LOG ("this is a 16 bits per channel PNG image, strip down to 8 bits");
383     png_set_strip_16 (pngdec->png);
384   }
385
386   /* Update the info structure */
387   png_read_update_info (pngdec->png, pngdec->info);
388
389   /* Get IHDR header again after transformation settings */
390
391   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
392       &bpc, &pngdec->color_type, NULL, NULL, NULL);
393
394   pngdec->width = width;
395   pngdec->height = height;
396
397   /* Generate the caps and configure */
398   ret = gst_pngdec_caps_create_and_set (pngdec);
399   if (ret != GST_FLOW_OK) {
400     goto pause;
401   }
402
403   /* Allocate output buffer */
404   rowbytes = png_get_rowbytes (pngdec->png, pngdec->info);
405   buffer_size = pngdec->height * GST_ROUND_UP_4 (rowbytes);
406   ret = gst_pad_alloc_buffer (pngdec->srcpad, GST_BUFFER_OFFSET_NONE,
407       buffer_size, GST_PAD_CAPS (pngdec->srcpad), &buffer);
408   if (ret != GST_FLOW_OK) {
409     goto pause;
410   }
411
412   rows = (png_bytep *) g_malloc (sizeof (png_bytep) * height);
413
414   inp = GST_BUFFER_DATA (buffer);
415
416   for (i = 0; i < height; i++) {
417     rows[i] = inp;
418     inp += GST_ROUND_UP_4 (rowbytes);
419   }
420
421   /* Read the actual picture */
422   png_read_image (pngdec->png, rows);
423   free (rows);
424
425   /* Push the raw RGB frame */
426   ret = gst_pad_push (pngdec->srcpad, buffer);
427   if (ret != GST_FLOW_OK) {
428     goto pause;
429   }
430
431   /* And we are done */
432   gst_pad_pause_task (pngdec->sinkpad);
433   gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
434   return;
435
436 pause:
437   GST_LOG_OBJECT (pngdec, "pausing task, reason %s", gst_flow_get_name (ret));
438   gst_pad_pause_task (pngdec->sinkpad);
439   if (GST_FLOW_IS_FATAL (ret)) {
440     gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
441     GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
442         (_("Internal data stream error.")),
443         ("stream stopped, reason %s", gst_flow_get_name (ret)));
444   }
445 }
446
447 static GstFlowReturn
448 gst_pngdec_chain (GstPad * pad, GstBuffer * buffer)
449 {
450   GstPngDec *pngdec;
451   GstFlowReturn ret = GST_FLOW_OK;
452
453   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
454
455   if (!pngdec->setup) {
456     GST_LOG ("we are not configured yet");
457     ret = GST_FLOW_WRONG_STATE;
458     goto beach;
459   }
460
461   /* Something is going wrong in our callbacks */
462   if (pngdec->ret != GST_FLOW_OK) {
463     ret = pngdec->ret;
464     goto beach;
465   }
466
467   /* Let libpng come back here on error */
468   if (setjmp (png_jmpbuf (pngdec->png))) {
469     ret = GST_FLOW_ERROR;
470     goto beach;
471   }
472
473   /* Progressive loading of the PNG image */
474   png_process_data (pngdec->png, pngdec->info, GST_BUFFER_DATA (buffer),
475       GST_BUFFER_SIZE (buffer));
476
477   /* And release the buffer */
478   gst_buffer_unref (buffer);
479
480 beach:
481   return ret;
482 }
483
484 static gboolean
485 gst_pngdec_sink_event (GstPad * pad, GstEvent * event)
486 {
487   GstPngDec *pngdec;
488
489   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
490
491   switch (GST_EVENT_TYPE (event)) {
492     case GST_EVENT_EOS:
493       gst_pngdec_libpng_clear (pngdec);
494     default:
495       return gst_pad_event_default (pad, event);
496   }
497 }
498
499
500 /* Clean up the libpng structures */
501 static gboolean
502 gst_pngdec_libpng_clear (GstPngDec * pngdec)
503 {
504   png_infopp info = NULL, endinfo = NULL;
505
506   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
507
508   GST_LOG ("cleaning up libpng structures");
509
510   if (pngdec->info) {
511     info = &pngdec->info;
512   }
513
514   if (pngdec->endinfo) {
515     endinfo = &pngdec->endinfo;
516   }
517
518   if (pngdec->png) {
519     png_destroy_read_struct (&(pngdec->png), info, endinfo);
520     pngdec->png = NULL;
521     pngdec->info = NULL;
522     pngdec->endinfo = NULL;
523   }
524
525   pngdec->bpp = pngdec->color_type = pngdec->height = pngdec->width = -1;
526   pngdec->offset = 0;
527   pngdec->rowbytes = 0;
528   pngdec->buffer_out = NULL;
529
530   pngdec->setup = FALSE;
531
532   return TRUE;
533 }
534
535 static gboolean
536 gst_pngdec_libpng_init (GstPngDec * pngdec)
537 {
538   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
539
540   if (pngdec->setup) {
541     goto beach;
542   }
543
544   /* initialize png struct stuff */
545   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
546       (png_voidp) NULL, user_error_fn, user_warning_fn);
547
548   if (pngdec->png == NULL) {
549     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
550         ("Failed to initialize png structure"));
551     goto beach;
552   }
553
554   pngdec->info = png_create_info_struct (pngdec->png);
555   if (pngdec->info == NULL) {
556     gst_pngdec_libpng_clear (pngdec);
557     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
558         ("Failed to initialize info structure"));
559     goto beach;
560   }
561
562   pngdec->endinfo = png_create_info_struct (pngdec->png);
563   if (pngdec->endinfo == NULL) {
564     gst_pngdec_libpng_clear (pngdec);
565     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
566         ("Failed to initialize endinfo structure"));
567     goto beach;
568   }
569
570   pngdec->setup = TRUE;
571
572 beach:
573   return pngdec->setup;
574 }
575
576 static GstStateChangeReturn
577 gst_pngdec_change_state (GstElement * element, GstStateChange transition)
578 {
579   GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
580   GstPngDec *pngdec = NULL;
581
582   pngdec = GST_PNGDEC (element);
583
584   switch (transition) {
585     case GST_STATE_CHANGE_READY_TO_PAUSED:
586       gst_pngdec_libpng_init (pngdec);
587       break;
588     case GST_STATE_CHANGE_PAUSED_TO_READY:
589       gst_pngdec_libpng_clear (pngdec);
590       break;
591     default:
592       break;
593   }
594
595   ret = parent_class->change_state (element, transition);
596
597   return ret;
598 }
599
600 /* this function gets called when we activate ourselves in push mode. */
601 static gboolean
602 gst_pngdec_sink_activate_push (GstPad * sinkpad, gboolean active)
603 {
604   GstPngDec *pngdec;
605
606   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (sinkpad));
607
608   pngdec->ret = GST_FLOW_OK;
609
610   if (active) {
611     /* Let libpng come back here on error */
612     if (setjmp (png_jmpbuf (pngdec->png))) {
613       GST_LOG ("failed setting up libpng jumb");
614       gst_pngdec_libpng_clear (pngdec);
615       return FALSE;
616     }
617
618     GST_LOG ("setting up progressive loading callbacks");
619     png_set_progressive_read_fn (pngdec->png, pngdec,
620         user_info_callback, user_endrow_callback, user_end_callback);
621   }
622
623   return TRUE;
624 }
625
626 /* this function gets called when we activate ourselves in pull mode.
627  * We can perform  random access to the resource and we start a task
628  * to start reading */
629 static gboolean
630 gst_pngdec_sink_activate_pull (GstPad * sinkpad, gboolean active)
631 {
632   GstPngDec *pngdec;
633
634   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (sinkpad));
635
636   if (active) {
637     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_pngdec_task,
638         sinkpad);
639   } else {
640     return gst_pad_stop_task (sinkpad);
641   }
642 }
643
644 /* this function is called when the pad is activated and should start
645  * processing data.
646  *
647  * We check if we can do random access to decide if we work push or
648  * pull based.
649  */
650 static gboolean
651 gst_pngdec_sink_activate (GstPad * sinkpad)
652 {
653   if (gst_pad_check_pull_range (sinkpad)) {
654     return gst_pad_activate_pull (sinkpad, TRUE);
655   } else {
656     return gst_pad_activate_push (sinkpad, TRUE);
657   }
658 }