png: fix video state leaks
[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_details_simple (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_alloc_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 ffmpegcolorspace'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_DEBUG ("Final %d %d", GST_VIDEO_INFO_WIDTH (&pngdec->output_state->info),
334       GST_VIDEO_INFO_HEIGHT (&pngdec->output_state->info));
335
336 beach:
337   return ret;
338 }
339
340 static GstFlowReturn
341 gst_pngdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
342 {
343   GstPngDec *pngdec = (GstPngDec *) decoder;
344   GstFlowReturn ret = GST_FLOW_OK;
345
346   GST_LOG_OBJECT (pngdec, "Got buffer, size=%u",
347       (guint) gst_buffer_get_size (frame->input_buffer));
348
349   /* Let libpng come back here on error */
350   if (setjmp (png_jmpbuf (pngdec->png))) {
351     GST_WARNING_OBJECT (pngdec, "error during decoding");
352     ret = GST_FLOW_ERROR;
353     goto beach;
354   }
355
356   pngdec->current_frame = frame;
357
358   /* Progressive loading of the PNG image */
359   if (!gst_buffer_map (frame->input_buffer, &pngdec->current_frame_map,
360           GST_MAP_READ)) {
361     GST_WARNING_OBJECT (pngdec, "Failed to map input buffer");
362     ret = GST_FLOW_ERROR;
363     goto beach;
364   }
365
366   png_process_data (pngdec->png, pngdec->info,
367       pngdec->current_frame_map.data, pngdec->current_frame_map.size);
368
369   if (pngdec->image_ready) {
370     if (1) {
371       /* Reset ourselves for the next frame */
372       gst_pngdec_reset (decoder, TRUE);
373       GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame");
374       png_set_progressive_read_fn (pngdec->png, pngdec,
375           user_info_callback, user_endrow_callback, user_end_callback);
376     } else {
377       GST_LOG_OBJECT (pngdec, "sending EOS");
378       pngdec->ret = GST_FLOW_EOS;
379     }
380     pngdec->image_ready = FALSE;
381   } else {
382     /* An error happened and we have to unmap */
383     gst_buffer_unmap (pngdec->current_frame->input_buffer,
384         &pngdec->current_frame_map);
385   }
386
387   ret = pngdec->ret;
388 beach:
389
390   return ret;
391 }
392
393 static gboolean
394 gst_pngdec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
395 {
396   GstBufferPool *pool;
397   GstStructure *config;
398
399   if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (bdec, query))
400     return FALSE;
401
402   g_assert (gst_query_get_n_allocation_pools (query) > 0);
403   gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
404   g_assert (pool != NULL);
405
406   config = gst_buffer_pool_get_config (pool);
407   if (gst_query_has_allocation_meta (query, GST_VIDEO_META_API_TYPE)) {
408     gst_buffer_pool_config_add_option (config,
409         GST_BUFFER_POOL_OPTION_VIDEO_META);
410   }
411   gst_buffer_pool_set_config (pool, config);
412   gst_object_unref (pool);
413
414   return TRUE;
415 }
416
417
418 /* Clean up the libpng structures */
419 static gboolean
420 gst_pngdec_reset (GstVideoDecoder * decoder, gboolean hard)
421 {
422   gst_pngdec_stop (decoder);
423   gst_pngdec_start (decoder);
424
425   return TRUE;
426 }
427
428 static gboolean
429 gst_pngdec_libpng_init (GstPngDec * pngdec)
430 {
431   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
432
433   GST_LOG ("init libpng structures");
434
435   /* initialize png struct stuff */
436   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
437       (png_voidp) NULL, user_error_fn, user_warning_fn);
438
439   if (pngdec->png == NULL)
440     goto init_failed;
441
442   pngdec->info = png_create_info_struct (pngdec->png);
443   if (pngdec->info == NULL)
444     goto info_failed;
445
446   pngdec->endinfo = png_create_info_struct (pngdec->png);
447   if (pngdec->endinfo == NULL)
448     goto endinfo_failed;
449
450   png_set_progressive_read_fn (pngdec->png, pngdec,
451       user_info_callback, user_endrow_callback, user_end_callback);
452
453   return TRUE;
454
455   /* ERRORS */
456 init_failed:
457   {
458     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
459         ("Failed to initialize png structure"));
460     return FALSE;
461   }
462 info_failed:
463   {
464     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
465         ("Failed to initialize info structure"));
466     return FALSE;
467   }
468 endinfo_failed:
469   {
470     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
471         ("Failed to initialize endinfo structure"));
472     return FALSE;
473   }
474 }
475
476 static gboolean
477 gst_pngdec_start (GstVideoDecoder * decoder)
478 {
479   GstPngDec *pngdec = (GstPngDec *) decoder;
480
481   gst_pngdec_libpng_init (pngdec);
482
483   return TRUE;
484 }
485
486 static gboolean
487 gst_pngdec_stop (GstVideoDecoder * decoder)
488 {
489   GstPngDec *pngdec = (GstPngDec *) decoder;
490   png_infopp info = NULL, endinfo = NULL;
491
492   GST_LOG ("cleaning up libpng structures");
493
494   if (pngdec->info) {
495     info = &pngdec->info;
496   }
497
498   if (pngdec->endinfo) {
499     endinfo = &pngdec->endinfo;
500   }
501
502   if (pngdec->png) {
503     png_destroy_read_struct (&(pngdec->png), info, endinfo);
504     pngdec->png = NULL;
505     pngdec->info = NULL;
506     pngdec->endinfo = NULL;
507   }
508
509   pngdec->color_type = -1;
510
511   if (pngdec->input_state) {
512     gst_video_codec_state_unref (pngdec->input_state);
513     pngdec->input_state = NULL;
514   }
515   if (pngdec->output_state) {
516     gst_video_codec_state_unref (pngdec->output_state);
517     pngdec->output_state = NULL;
518   }
519
520   return TRUE;
521 }