03e40dd772eefed8a6cefd5e63810a3b67977587
[platform/upstream/gstreamer.git] / ext / libpng / gstpngdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2012 Collabora Ltd.
4  *      Author : Edward Hervey <edward@collabora.com>
5  *
6  * This library is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * Library General Public License for more details.
10  *
11  * You should have received a copy of the GNU Library General Public
12  * License along with this library; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 02111-1307, USA.
15  *
16  */
17 /**
18  * SECTION:element-pngdec
19  *
20  * Decodes png images. If there is no framerate set on sink caps, it sends EOS
21  * after the first picture.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "gstpngdec.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <gst/video/video.h>
33 #include <gst/video/gstvideometa.h>
34 #include <gst/video/gstvideopool.h>
35 #include <gst/gst-i18n-plugin.h>
36
37 GST_DEBUG_CATEGORY_STATIC (pngdec_debug);
38 #define GST_CAT_DEFAULT pngdec_debug
39
40 static gboolean gst_pngdec_libpng_init (GstPngDec * pngdec);
41 static gboolean gst_pngdec_reset (GstVideoDecoder * decoder, gboolean hard);
42
43 static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec);
44
45 static gboolean gst_pngdec_start (GstVideoDecoder * decoder);
46 static gboolean gst_pngdec_stop (GstVideoDecoder * decoder);
47 static gboolean gst_pngdec_set_format (GstVideoDecoder * Decoder,
48     GstVideoCodecState * state);
49 static GstFlowReturn gst_pngdec_handle_frame (GstVideoDecoder * decoder,
50     GstVideoCodecFrame * frame);
51 static gboolean gst_pngdec_decide_allocation (GstVideoDecoder * decoder,
52     GstQuery * query);
53
54 #define parent_class gst_pngdec_parent_class
55 G_DEFINE_TYPE (GstPngDec, gst_pngdec, GST_TYPE_VIDEO_DECODER);
56
57 static GstStaticPadTemplate gst_pngdec_src_pad_template =
58 GST_STATIC_PAD_TEMPLATE ("src",
59     GST_PAD_SRC,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
62         ("{ RGBA, RGB, ARGB64, GRAY8, GRAY16_BE }"))
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 static void
73 gst_pngdec_class_init (GstPngDecClass * klass)
74 {
75   GstElementClass *element_class = (GstElementClass *) klass;
76   GstVideoDecoderClass *vdec_class = (GstVideoDecoderClass *) klass;
77
78   gst_element_class_add_pad_template (element_class,
79       gst_static_pad_template_get (&gst_pngdec_src_pad_template));
80   gst_element_class_add_pad_template (element_class,
81       gst_static_pad_template_get (&gst_pngdec_sink_pad_template));
82   gst_element_class_set_metadata (element_class, "PNG image decoder",
83       "Codec/Decoder/Image",
84       "Decode a png video frame to a raw image",
85       "Wim Taymans <wim@fluendo.com>");
86
87   vdec_class->start = gst_pngdec_start;
88   vdec_class->reset = gst_pngdec_reset;
89   vdec_class->set_format = gst_pngdec_set_format;
90   vdec_class->handle_frame = gst_pngdec_handle_frame;
91   vdec_class->decide_allocation = gst_pngdec_decide_allocation;
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->png = NULL;
100   pngdec->info = NULL;
101   pngdec->endinfo = NULL;
102
103   pngdec->color_type = -1;
104
105   pngdec->image_ready = FALSE;
106 }
107
108 static void
109 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
110 {
111   GST_ERROR ("%s", error_msg);
112 }
113
114 static void
115 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
116 {
117   GST_WARNING ("%s", warning_msg);
118 }
119
120 static void
121 user_info_callback (png_structp png_ptr, png_infop info)
122 {
123   GstPngDec *pngdec = NULL;
124   GstFlowReturn ret = GST_FLOW_OK;
125
126   GST_LOG ("info ready");
127
128   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
129   /* Generate the caps and configure */
130   ret = gst_pngdec_caps_create_and_set (pngdec);
131   if (ret != GST_FLOW_OK) {
132     goto beach;
133   }
134
135   /* Allocate output buffer */
136   ret =
137       gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (pngdec),
138       pngdec->current_frame);
139   if (G_UNLIKELY (ret != GST_FLOW_OK))
140     GST_DEBUG_OBJECT (pngdec, "failed to acquire buffer");
141
142 beach:
143   pngdec->ret = ret;
144 }
145
146 static gboolean
147 gst_pngdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
148 {
149   GstPngDec *pngdec = (GstPngDec *) decoder;
150
151   if (pngdec->input_state)
152     gst_video_codec_state_unref (pngdec->input_state);
153   pngdec->input_state = gst_video_codec_state_ref (state);
154
155   /* We'll set format later on */
156
157   return TRUE;
158 }
159
160 static void
161 user_endrow_callback (png_structp png_ptr, png_bytep new_row,
162     png_uint_32 row_num, int pass)
163 {
164   GstPngDec *pngdec = NULL;
165
166   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
167
168   /* FIXME: implement interlaced pictures */
169
170   /* If buffer_out doesn't exist, it means buffer_alloc failed, which 
171    * will already have set the return code */
172   if (GST_IS_BUFFER (pngdec->current_frame->output_buffer)) {
173     GstVideoFrame frame;
174     GstBuffer *buffer = pngdec->current_frame->output_buffer;
175     size_t offset;
176     gint width;
177     guint8 *data;
178
179     if (!gst_video_frame_map (&frame, &pngdec->output_state->info, buffer,
180             GST_MAP_WRITE)) {
181       pngdec->ret = GST_FLOW_ERROR;
182       return;
183     }
184
185     data = GST_VIDEO_FRAME_COMP_DATA (&frame, 0);
186     offset = row_num * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0);
187     GST_LOG ("got row %u, copying in buffer %p at offset %" G_GSIZE_FORMAT,
188         (guint) row_num, pngdec->current_frame->output_buffer, offset);
189     width = GST_ROUND_UP_4 (png_get_rowbytes (pngdec->png, pngdec->info));
190     memcpy (data + offset, new_row, width);
191     gst_video_frame_unmap (&frame);
192     pngdec->ret = GST_FLOW_OK;
193   }
194 }
195
196 static void
197 user_end_callback (png_structp png_ptr, png_infop info)
198 {
199   GstPngDec *pngdec = NULL;
200
201   pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));
202
203   GST_LOG_OBJECT (pngdec, "and we are done reading this image");
204
205   if (!pngdec->current_frame->output_buffer)
206     return;
207
208   gst_buffer_unmap (pngdec->current_frame->input_buffer,
209       &pngdec->current_frame_map);
210
211   pngdec->ret =
212       gst_video_decoder_finish_frame (GST_VIDEO_DECODER (pngdec),
213       pngdec->current_frame);
214
215   pngdec->image_ready = TRUE;
216 }
217
218
219 static GstFlowReturn
220 gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
221 {
222   GstFlowReturn ret = GST_FLOW_OK;
223   gint bpc = 0, color_type;
224   png_uint_32 width, height;
225   GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
226
227   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR);
228
229   /* Get bits per channel */
230   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
231
232   /* Get Color type */
233   color_type = png_get_color_type (pngdec->png, pngdec->info);
234
235   /* Add alpha channel if 16-bit depth, but not for GRAY images */
236   if ((bpc > 8) && (color_type != PNG_COLOR_TYPE_GRAY)) {
237     png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE);
238     png_set_swap (pngdec->png);
239   }
240 #if 0
241   /* We used to have this HACK to reverse the outgoing bytes, but the problem
242    * that originally required the hack seems to have been in videoconvert's
243    * RGBA descriptions. It doesn't seem needed now that's fixed, but might
244    * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */
245   if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
246     png_set_bgr (pngdec->png);
247 #endif
248
249   /* Gray scale with alpha channel converted to RGB */
250   if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
251     GST_LOG_OBJECT (pngdec,
252         "converting grayscale png with alpha channel to RGB");
253     png_set_gray_to_rgb (pngdec->png);
254   }
255
256   /* Gray scale converted to upscaled to 8 bits */
257   if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
258       (color_type == PNG_COLOR_TYPE_GRAY)) {
259     if (bpc < 8) {              /* Convert to 8 bits */
260       GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits");
261 #if PNG_LIBPNG_VER < 10400
262       png_set_gray_1_2_4_to_8 (pngdec->png);
263 #else
264       png_set_expand_gray_1_2_4_to_8 (pngdec->png);
265 #endif
266     }
267   }
268
269   /* Palette converted to RGB */
270   if (color_type == PNG_COLOR_TYPE_PALETTE) {
271     GST_LOG_OBJECT (pngdec, "converting palette png to RGB");
272     png_set_palette_to_rgb (pngdec->png);
273   }
274
275   /* Update the info structure */
276   png_read_update_info (pngdec->png, pngdec->info);
277
278   /* Get IHDR header again after transformation settings */
279   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
280       &bpc, &pngdec->color_type, NULL, NULL, NULL);
281
282   GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", (gint) width,
283       (gint) height);
284
285   switch (pngdec->color_type) {
286     case PNG_COLOR_TYPE_RGB:
287       GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits");
288       if (bpc == 8)
289         format = GST_VIDEO_FORMAT_RGB;
290       break;
291     case PNG_COLOR_TYPE_RGB_ALPHA:
292       GST_LOG_OBJECT (pngdec,
293           "we have an alpha channel, depth is 32 or 64 bits");
294       if (bpc == 8)
295         format = GST_VIDEO_FORMAT_RGBA;
296       else if (bpc == 16)
297         format = GST_VIDEO_FORMAT_ARGB64;
298       break;
299     case PNG_COLOR_TYPE_GRAY:
300       GST_LOG_OBJECT (pngdec,
301           "We have an gray image, depth is 8 or 16 (be) bits");
302       if (bpc == 8)
303         format = GST_VIDEO_FORMAT_GRAY8;
304       else if (bpc == 16)
305         format = GST_VIDEO_FORMAT_GRAY16_BE;
306       break;
307     default:
308       break;
309   }
310
311   if (format == GST_VIDEO_FORMAT_UNKNOWN) {
312     GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL),
313         ("pngdec does not support this color type"));
314     ret = GST_FLOW_NOT_SUPPORTED;
315     goto beach;
316   }
317
318   /* Check if output state changed */
319   if (pngdec->output_state) {
320     GstVideoInfo *info = &pngdec->output_state->info;
321
322     if (width == GST_VIDEO_INFO_WIDTH (info) &&
323         height == GST_VIDEO_INFO_HEIGHT (info) &&
324         GST_VIDEO_INFO_FORMAT (info) == format) {
325       goto beach;
326     }
327     gst_video_codec_state_unref (pngdec->output_state);
328   }
329
330   pngdec->output_state =
331       gst_video_decoder_set_output_state (GST_VIDEO_DECODER (pngdec), format,
332       width, height, pngdec->input_state);
333   gst_video_decoder_negotiate (GST_VIDEO_DECODER (pngdec));
334   GST_DEBUG ("Final %d %d", GST_VIDEO_INFO_WIDTH (&pngdec->output_state->info),
335       GST_VIDEO_INFO_HEIGHT (&pngdec->output_state->info));
336
337 beach:
338   return ret;
339 }
340
341 static GstFlowReturn
342 gst_pngdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
343 {
344   GstPngDec *pngdec = (GstPngDec *) decoder;
345   GstFlowReturn ret = GST_FLOW_OK;
346
347   GST_LOG_OBJECT (pngdec, "Got buffer, size=%u",
348       (guint) gst_buffer_get_size (frame->input_buffer));
349
350   /* Let libpng come back here on error */
351   if (setjmp (png_jmpbuf (pngdec->png))) {
352     GST_WARNING_OBJECT (pngdec, "error during decoding");
353     ret = GST_FLOW_ERROR;
354     goto beach;
355   }
356
357   pngdec->current_frame = frame;
358
359   /* Progressive loading of the PNG image */
360   if (!gst_buffer_map (frame->input_buffer, &pngdec->current_frame_map,
361           GST_MAP_READ)) {
362     GST_WARNING_OBJECT (pngdec, "Failed to map input buffer");
363     ret = GST_FLOW_ERROR;
364     goto beach;
365   }
366
367   png_process_data (pngdec->png, pngdec->info,
368       pngdec->current_frame_map.data, pngdec->current_frame_map.size);
369
370   if (pngdec->image_ready) {
371     if (1) {
372       /* Reset ourselves for the next frame */
373       gst_pngdec_reset (decoder, TRUE);
374       GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame");
375       png_set_progressive_read_fn (pngdec->png, pngdec,
376           user_info_callback, user_endrow_callback, user_end_callback);
377     } else {
378       GST_LOG_OBJECT (pngdec, "sending EOS");
379       pngdec->ret = GST_FLOW_EOS;
380     }
381     pngdec->image_ready = FALSE;
382   } else {
383     /* An error happened and we have to unmap */
384     gst_buffer_unmap (pngdec->current_frame->input_buffer,
385         &pngdec->current_frame_map);
386   }
387
388   ret = pngdec->ret;
389 beach:
390
391   return ret;
392 }
393
394 static gboolean
395 gst_pngdec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
396 {
397   GstBufferPool *pool = NULL;
398   GstStructure *config;
399
400   if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (bdec, query))
401     return FALSE;
402
403   if (gst_query_get_n_allocation_pools (query) > 0)
404     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
405
406   if (pool == NULL)
407     return FALSE;
408
409   config = gst_buffer_pool_get_config (pool);
410   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
411     gst_buffer_pool_config_add_option (config,
412         GST_BUFFER_POOL_OPTION_VIDEO_META);
413   }
414   gst_buffer_pool_set_config (pool, config);
415   gst_object_unref (pool);
416
417   return TRUE;
418 }
419
420
421 /* Clean up the libpng structures */
422 static gboolean
423 gst_pngdec_reset (GstVideoDecoder * decoder, gboolean hard)
424 {
425   gst_pngdec_stop (decoder);
426   gst_pngdec_start (decoder);
427
428   return TRUE;
429 }
430
431 static gboolean
432 gst_pngdec_libpng_init (GstPngDec * pngdec)
433 {
434   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
435
436   GST_LOG ("init libpng structures");
437
438   /* initialize png struct stuff */
439   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
440       (png_voidp) NULL, user_error_fn, user_warning_fn);
441
442   if (pngdec->png == NULL)
443     goto init_failed;
444
445   pngdec->info = png_create_info_struct (pngdec->png);
446   if (pngdec->info == NULL)
447     goto info_failed;
448
449   pngdec->endinfo = png_create_info_struct (pngdec->png);
450   if (pngdec->endinfo == NULL)
451     goto endinfo_failed;
452
453   png_set_progressive_read_fn (pngdec->png, pngdec,
454       user_info_callback, user_endrow_callback, user_end_callback);
455
456   return TRUE;
457
458   /* ERRORS */
459 init_failed:
460   {
461     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
462         ("Failed to initialize png structure"));
463     return FALSE;
464   }
465 info_failed:
466   {
467     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
468         ("Failed to initialize info structure"));
469     return FALSE;
470   }
471 endinfo_failed:
472   {
473     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
474         ("Failed to initialize endinfo structure"));
475     return FALSE;
476   }
477 }
478
479 static gboolean
480 gst_pngdec_start (GstVideoDecoder * decoder)
481 {
482   GstPngDec *pngdec = (GstPngDec *) decoder;
483
484   gst_pngdec_libpng_init (pngdec);
485
486   return TRUE;
487 }
488
489 static gboolean
490 gst_pngdec_stop (GstVideoDecoder * decoder)
491 {
492   GstPngDec *pngdec = (GstPngDec *) decoder;
493   png_infopp info = NULL, endinfo = NULL;
494
495   GST_LOG ("cleaning up libpng structures");
496
497   if (pngdec->info) {
498     info = &pngdec->info;
499   }
500
501   if (pngdec->endinfo) {
502     endinfo = &pngdec->endinfo;
503   }
504
505   if (pngdec->png) {
506     png_destroy_read_struct (&(pngdec->png), info, endinfo);
507     pngdec->png = NULL;
508     pngdec->info = NULL;
509     pngdec->endinfo = NULL;
510   }
511
512   pngdec->color_type = -1;
513
514   if (pngdec->input_state) {
515     gst_video_codec_state_unref (pngdec->input_state);
516     pngdec->input_state = NULL;
517   }
518   if (pngdec->output_state) {
519     gst_video_codec_state_unref (pngdec->output_state);
520     pngdec->output_state = NULL;
521   }
522
523   return TRUE;
524 }