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