gst_plugins_good/ext/libpng/gstpngdec.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     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 static const GstElementDetails gst_pngdec_details =
       
    34 GST_ELEMENT_DETAILS ("PNG image decoder",
       
    35     "Codec/Decoder/Image",
       
    36     "Decode a png video frame to a raw image",
       
    37     "Wim Taymans <wim@fluendo.com>");
       
    38 
       
    39 GST_DEBUG_CATEGORY_STATIC (pngdec_debug);
       
    40 #define GST_CAT_DEFAULT pngdec_debug
       
    41 
       
    42 static void gst_pngdec_base_init (gpointer g_class);
       
    43 static void gst_pngdec_class_init (GstPngDecClass * klass);
       
    44 static void gst_pngdec_init (GstPngDec * pngdec);
       
    45 
       
    46 static gboolean gst_pngdec_libpng_init (GstPngDec * pngdec);
       
    47 static gboolean gst_pngdec_libpng_clear (GstPngDec * pngdec);
       
    48 
       
    49 static GstStateChangeReturn gst_pngdec_change_state (GstElement * element,
       
    50     GstStateChange transition);
       
    51 
       
    52 static gboolean gst_pngdec_sink_activate_push (GstPad * sinkpad,
       
    53     gboolean active);
       
    54 static gboolean gst_pngdec_sink_activate_pull (GstPad * sinkpad,
       
    55     gboolean active);
       
    56 static gboolean gst_pngdec_sink_activate (GstPad * sinkpad);
       
    57 
       
    58 static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec);
       
    59 
       
    60 static void gst_pngdec_task (GstPad * pad);
       
    61 static GstFlowReturn gst_pngdec_chain (GstPad * pad, GstBuffer * buffer);
       
    62 static gboolean gst_pngdec_sink_event (GstPad * pad, GstEvent * event);
       
    63 static gboolean gst_pngdec_sink_setcaps (GstPad * pad, GstCaps * caps);
       
    64 
       
    65 static GstElementClass *parent_class = NULL;
       
    66 
       
    67 GType
       
    68 gst_pngdec_get_type (void)
       
    69 {
       
    70   static GType pngdec_type = 0;
       
    71 
       
    72   if (!pngdec_type) {
       
    73     static const GTypeInfo pngdec_info = {
       
    74       sizeof (GstPngDecClass),
       
    75       gst_pngdec_base_init,
       
    76       NULL,
       
    77       (GClassInitFunc) gst_pngdec_class_init,
       
    78       NULL,
       
    79       NULL,
       
    80       sizeof (GstPngDec),
       
    81       0,
       
    82       (GInstanceInitFunc) gst_pngdec_init,
       
    83     };
       
    84 
       
    85     pngdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstPngDec",
       
    86         &pngdec_info, 0);
       
    87   }
       
    88   return pngdec_type;
       
    89 }
       
    90 
       
    91 static GstStaticPadTemplate gst_pngdec_src_pad_template =
       
    92     GST_STATIC_PAD_TEMPLATE ("src",
       
    93     GST_PAD_SRC,
       
    94     GST_PAD_ALWAYS,
       
    95     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB)
       
    96     );
       
    97 
       
    98 static GstStaticPadTemplate gst_pngdec_sink_pad_template =
       
    99 GST_STATIC_PAD_TEMPLATE ("sink",
       
   100     GST_PAD_SINK,
       
   101     GST_PAD_ALWAYS,
       
   102     GST_STATIC_CAPS ("image/png")
       
   103     );
       
   104 
       
   105 static void
       
   106 gst_pngdec_base_init (gpointer g_class)
       
   107 {
       
   108   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   109 
       
   110   gst_element_class_add_pad_template (element_class,
       
   111       gst_static_pad_template_get (&gst_pngdec_src_pad_template));
       
   112   gst_element_class_add_pad_template (element_class,
       
   113       gst_static_pad_template_get (&gst_pngdec_sink_pad_template));
       
   114   gst_element_class_set_details (element_class, &gst_pngdec_details);
       
   115 }
       
   116 
       
   117 static void
       
   118 gst_pngdec_class_init (GstPngDecClass * klass)
       
   119 {
       
   120   GstElementClass *gstelement_class;
       
   121 
       
   122   gstelement_class = (GstElementClass *) klass;
       
   123 
       
   124   parent_class = g_type_class_peek_parent (klass);
       
   125 
       
   126   gstelement_class->change_state = gst_pngdec_change_state;
       
   127 
       
   128   GST_DEBUG_CATEGORY_INIT (pngdec_debug, "pngdec", 0, "PNG image decoder");
       
   129 }
       
   130 
       
   131 static void
       
   132 gst_pngdec_init (GstPngDec * pngdec)
       
   133 {
       
   134   pngdec->sinkpad =
       
   135       gst_pad_new_from_static_template (&gst_pngdec_sink_pad_template, "sink");
       
   136   gst_pad_set_activate_function (pngdec->sinkpad, gst_pngdec_sink_activate);
       
   137   gst_pad_set_activatepush_function (pngdec->sinkpad,
       
   138       gst_pngdec_sink_activate_push);
       
   139   gst_pad_set_activatepull_function (pngdec->sinkpad,
       
   140       gst_pngdec_sink_activate_pull);
       
   141   gst_pad_set_chain_function (pngdec->sinkpad, gst_pngdec_chain);
       
   142   gst_pad_set_event_function (pngdec->sinkpad, gst_pngdec_sink_event);
       
   143   gst_pad_set_setcaps_function (pngdec->sinkpad, gst_pngdec_sink_setcaps);
       
   144   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->sinkpad);
       
   145 
       
   146   pngdec->srcpad =
       
   147       gst_pad_new_from_static_template (&gst_pngdec_src_pad_template, "src");
       
   148   gst_pad_use_fixed_caps (pngdec->srcpad);
       
   149   gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->srcpad);
       
   150 
       
   151   pngdec->buffer_out = NULL;
       
   152   pngdec->png = NULL;
       
   153   pngdec->info = NULL;
       
   154   pngdec->endinfo = NULL;
       
   155   pngdec->setup = FALSE;
       
   156 
       
   157   pngdec->color_type = -1;
       
   158   pngdec->width = -1;
       
   159   pngdec->height = -1;
       
   160   pngdec->bpp = -1;
       
   161   pngdec->fps_n = 0;
       
   162   pngdec->fps_d = 1;
       
   163 
       
   164   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
       
   165   pngdec->in_duration = GST_CLOCK_TIME_NONE;
       
   166 
       
   167   gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
       
   168 
       
   169   pngdec->image_ready = FALSE;
       
   170 }
       
   171 
       
   172 static void
       
   173 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
       
   174 {
       
   175   GST_ERROR ("%s", error_msg);
       
   176 }
       
   177 
       
   178 static void
       
   179 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
       
   180 {
       
   181   GST_WARNING ("%s", warning_msg);
       
   182 }
       
   183 
       
   184 static void
       
   185 user_info_callback (png_structp png_ptr, png_infop info)
       
   186 {
       
   187   GstPngDec *pngdec = NULL;
       
   188   GstFlowReturn ret = GST_FLOW_OK;
       
   189   size_t buffer_size;
       
   190   GstBuffer *buffer = NULL;
       
   191 
       
   192   pngdec = GST_PNGDEC (png_ptr->io_ptr);
       
   193 
       
   194   GST_LOG ("info ready");
       
   195 
       
   196   /* Generate the caps and configure */
       
   197   ret = gst_pngdec_caps_create_and_set (pngdec);
       
   198   if (ret != GST_FLOW_OK) {
       
   199     goto beach;
       
   200   }
       
   201 
       
   202   /* Allocate output buffer */
       
   203   pngdec->rowbytes = png_get_rowbytes (pngdec->png, pngdec->info);
       
   204   buffer_size = pngdec->height * GST_ROUND_UP_4 (pngdec->rowbytes);
       
   205   ret =
       
   206       gst_pad_alloc_buffer_and_set_caps (pngdec->srcpad, GST_BUFFER_OFFSET_NONE,
       
   207       buffer_size, GST_PAD_CAPS (pngdec->srcpad), &buffer);
       
   208   if (ret != GST_FLOW_OK) {
       
   209     goto beach;
       
   210   }
       
   211 
       
   212   pngdec->buffer_out = buffer;
       
   213 
       
   214 beach:
       
   215   pngdec->ret = ret;
       
   216 }
       
   217 
       
   218 static void
       
   219 user_endrow_callback (png_structp png_ptr, png_bytep new_row,
       
   220     png_uint_32 row_num, int pass)
       
   221 {
       
   222   GstPngDec *pngdec = NULL;
       
   223 
       
   224   pngdec = GST_PNGDEC (png_ptr->io_ptr);
       
   225 
       
   226   /* FIXME: implement interlaced pictures */
       
   227 
       
   228   /* If buffer_out doesn't exist, it means buffer_alloc failed, which 
       
   229    * will already have set the return code */
       
   230   if (GST_IS_BUFFER (pngdec->buffer_out)) {
       
   231     size_t offset = row_num * GST_ROUND_UP_4 (pngdec->rowbytes);
       
   232 
       
   233     GST_LOG ("got row %u, copying in buffer %p at offset %" G_GSIZE_FORMAT,
       
   234         (guint) row_num, pngdec->buffer_out, offset);
       
   235     memcpy (GST_BUFFER_DATA (pngdec->buffer_out) + offset, new_row,
       
   236         pngdec->rowbytes);
       
   237     pngdec->ret = GST_FLOW_OK;
       
   238   }
       
   239 }
       
   240 
       
   241 static gboolean
       
   242 buffer_clip (GstPngDec * dec, GstBuffer * buffer)
       
   243 {
       
   244   gboolean res = TRUE;
       
   245   gint64 cstart, cstop;
       
   246 
       
   247 
       
   248   if ((!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) ||
       
   249       (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer))) ||
       
   250       (dec->segment.format != GST_FORMAT_TIME))
       
   251     goto beach;
       
   252 
       
   253   cstart = GST_BUFFER_TIMESTAMP (buffer);
       
   254   cstop = GST_BUFFER_DURATION (buffer);
       
   255 
       
   256   if ((res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME,
       
   257               cstart, cstart + cstop, &cstart, &cstop))) {
       
   258     GST_BUFFER_TIMESTAMP (buffer) = cstart;
       
   259     GST_BUFFER_DURATION (buffer) = cstop - cstart;
       
   260   }
       
   261 
       
   262 beach:
       
   263   return res;
       
   264 }
       
   265 
       
   266 static void
       
   267 user_end_callback (png_structp png_ptr, png_infop info)
       
   268 {
       
   269   GstPngDec *pngdec = NULL;
       
   270 
       
   271   pngdec = GST_PNGDEC (png_ptr->io_ptr);
       
   272 
       
   273   GST_LOG_OBJECT (pngdec, "and we are done reading this image");
       
   274 
       
   275   if (!pngdec->buffer_out)
       
   276     return;
       
   277 
       
   278   if (GST_CLOCK_TIME_IS_VALID (pngdec->in_timestamp))
       
   279     GST_BUFFER_TIMESTAMP (pngdec->buffer_out) = pngdec->in_timestamp;
       
   280   if (GST_CLOCK_TIME_IS_VALID (pngdec->in_duration))
       
   281     GST_BUFFER_DURATION (pngdec->buffer_out) = pngdec->in_duration;
       
   282 
       
   283   /* buffer clipping */
       
   284   if (buffer_clip (pngdec, pngdec->buffer_out)) {
       
   285     /* Push our buffer and then EOS if needed */
       
   286     GST_LOG_OBJECT (pngdec, "pushing buffer with ts=%" GST_TIME_FORMAT,
       
   287         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (pngdec->buffer_out)));
       
   288 
       
   289     pngdec->ret = gst_pad_push (pngdec->srcpad, pngdec->buffer_out);
       
   290   } else {
       
   291     GST_LOG_OBJECT (pngdec, "dropped decoded buffer");
       
   292     gst_buffer_unref (pngdec->buffer_out);
       
   293   }
       
   294   pngdec->buffer_out = NULL;
       
   295   pngdec->image_ready = TRUE;
       
   296 }
       
   297 
       
   298 static void
       
   299 user_read_data (png_structp png_ptr, png_bytep data, png_size_t length)
       
   300 {
       
   301   GstPngDec *pngdec;
       
   302   GstBuffer *buffer;
       
   303   GstFlowReturn ret = GST_FLOW_OK;
       
   304   guint size;
       
   305 
       
   306   pngdec = GST_PNGDEC (png_ptr->io_ptr);
       
   307 
       
   308   GST_LOG ("reading %" G_GSIZE_FORMAT " bytes of data at offset %d", length,
       
   309       pngdec->offset);
       
   310 
       
   311   ret = gst_pad_pull_range (pngdec->sinkpad, pngdec->offset, length, &buffer);
       
   312   if (ret != GST_FLOW_OK)
       
   313     goto pause;
       
   314 
       
   315   size = GST_BUFFER_SIZE (buffer);
       
   316 
       
   317   if (size != length)
       
   318     goto short_buffer;
       
   319 
       
   320   memcpy (data, GST_BUFFER_DATA (buffer), size);
       
   321 
       
   322   gst_buffer_unref (buffer);
       
   323 
       
   324   pngdec->offset += length;
       
   325 
       
   326   return;
       
   327 
       
   328   /* ERRORS */
       
   329 pause:
       
   330   {
       
   331     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
       
   332         gst_flow_get_name (ret));
       
   333     gst_pad_pause_task (pngdec->sinkpad);
       
   334     if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
       
   335       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
       
   336           (_("Internal data stream error.")),
       
   337           ("stream stopped, reason %s", gst_flow_get_name (ret)));
       
   338       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
       
   339     }
       
   340     return;
       
   341   }
       
   342 short_buffer:
       
   343   {
       
   344     gst_buffer_unref (buffer);
       
   345     GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
       
   346         (_("Internal data stream error.")),
       
   347         ("Read %u, needed %" G_GSIZE_FORMAT "bytes", size, length));
       
   348     ret = GST_FLOW_ERROR;
       
   349     goto pause;
       
   350   }
       
   351 }
       
   352 
       
   353 static GstFlowReturn
       
   354 gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
       
   355 {
       
   356   GstFlowReturn ret = GST_FLOW_OK;
       
   357   GstCaps *caps = NULL, *res = NULL;
       
   358   GstPadTemplate *templ = NULL;
       
   359   gint bpc = 0, color_type;
       
   360   png_uint_32 width, height;
       
   361 
       
   362   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR);
       
   363 
       
   364   /* Get bits per channel */
       
   365   bpc = png_get_bit_depth (pngdec->png, pngdec->info);
       
   366 
       
   367   /* We don't handle 16 bits per color, strip down to 8 */
       
   368   if (bpc == 16) {
       
   369     GST_LOG_OBJECT (pngdec,
       
   370         "this is a 16 bits per channel PNG image, strip down to 8 bits");
       
   371     png_set_strip_16 (pngdec->png);
       
   372   }
       
   373 
       
   374   /* Get Color type */
       
   375   color_type = png_get_color_type (pngdec->png, pngdec->info);
       
   376 
       
   377 #if 0
       
   378   /* We used to have this HACK to reverse the outgoing bytes, but the problem
       
   379    * that originally required the hack seems to have been in ffmpegcolorspace's
       
   380    * RGBA descriptions. It doesn't seem needed now that's fixed, but might
       
   381    * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */
       
   382   if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
       
   383     png_set_bgr (pngdec->png);
       
   384 #endif
       
   385 
       
   386   /* Gray scale converted to RGB and upscaled to 8 bits */
       
   387   if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
       
   388       (color_type == PNG_COLOR_TYPE_GRAY)) {
       
   389     GST_LOG_OBJECT (pngdec, "converting grayscale png to RGB");
       
   390     png_set_gray_to_rgb (pngdec->png);
       
   391     if (bpc < 8) {              /* Convert to 8 bits */
       
   392       GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits");
       
   393       png_set_gray_1_2_4_to_8 (pngdec->png);
       
   394     }
       
   395   }
       
   396 
       
   397   /* Palette converted to RGB */
       
   398   if (color_type == PNG_COLOR_TYPE_PALETTE) {
       
   399     GST_LOG_OBJECT (pngdec, "converting palette png to RGB");
       
   400     png_set_palette_to_rgb (pngdec->png);
       
   401   }
       
   402 
       
   403   /* Update the info structure */
       
   404   png_read_update_info (pngdec->png, pngdec->info);
       
   405 
       
   406   /* Get IHDR header again after transformation settings */
       
   407 
       
   408   png_get_IHDR (pngdec->png, pngdec->info, &width, &height,
       
   409       &bpc, &pngdec->color_type, NULL, NULL, NULL);
       
   410 
       
   411   pngdec->width = width;
       
   412   pngdec->height = height;
       
   413 
       
   414   GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", pngdec->width,
       
   415       pngdec->height);
       
   416 
       
   417   switch (pngdec->color_type) {
       
   418     case PNG_COLOR_TYPE_RGB:
       
   419       GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits");
       
   420       pngdec->bpp = 24;
       
   421       break;
       
   422     case PNG_COLOR_TYPE_RGB_ALPHA:
       
   423       GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits");
       
   424       pngdec->bpp = 32;
       
   425       break;
       
   426     default:
       
   427       GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL),
       
   428           ("pngdec does not support this color type"));
       
   429       ret = GST_FLOW_NOT_SUPPORTED;
       
   430       goto beach;
       
   431   }
       
   432 
       
   433   caps = gst_caps_new_simple ("video/x-raw-rgb",
       
   434       "width", G_TYPE_INT, pngdec->width,
       
   435       "height", G_TYPE_INT, pngdec->height,
       
   436       "bpp", G_TYPE_INT, pngdec->bpp,
       
   437       "framerate", GST_TYPE_FRACTION, pngdec->fps_n, pngdec->fps_d, NULL);
       
   438 
       
   439   templ = gst_static_pad_template_get (&gst_pngdec_src_pad_template);
       
   440 
       
   441   res = gst_caps_intersect (caps, gst_pad_template_get_caps (templ));
       
   442 
       
   443   gst_caps_unref (caps);
       
   444   gst_object_unref (templ);
       
   445 
       
   446   if (!gst_pad_set_caps (pngdec->srcpad, res))
       
   447     ret = GST_FLOW_NOT_NEGOTIATED;
       
   448 
       
   449   GST_DEBUG_OBJECT (pngdec, "our caps %" GST_PTR_FORMAT, res);
       
   450 
       
   451   gst_caps_unref (res);
       
   452 
       
   453   /* Push a newsegment event */
       
   454   if (pngdec->need_newsegment) {
       
   455     gst_pad_push_event (pngdec->srcpad,
       
   456         gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
       
   457     pngdec->need_newsegment = FALSE;
       
   458   }
       
   459 
       
   460 beach:
       
   461   return ret;
       
   462 }
       
   463 
       
   464 static void
       
   465 gst_pngdec_task (GstPad * pad)
       
   466 {
       
   467   GstPngDec *pngdec;
       
   468   GstBuffer *buffer = NULL;
       
   469   size_t buffer_size = 0;
       
   470   gint i = 0;
       
   471   png_bytep *rows, inp;
       
   472   png_uint_32 rowbytes;
       
   473   GstFlowReturn ret = GST_FLOW_OK;
       
   474 
       
   475   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad));
       
   476 
       
   477   GST_LOG_OBJECT (pngdec, "read frame");
       
   478 
       
   479   /* Let libpng come back here on error */
       
   480   if (setjmp (png_jmpbuf (pngdec->png))) {
       
   481     ret = GST_FLOW_ERROR;
       
   482     goto pause;
       
   483   }
       
   484 
       
   485   /* Set reading callback */
       
   486   png_set_read_fn (pngdec->png, pngdec, user_read_data);
       
   487 
       
   488   /* Read info */
       
   489   png_read_info (pngdec->png, pngdec->info);
       
   490 
       
   491   /* Generate the caps and configure */
       
   492   ret = gst_pngdec_caps_create_and_set (pngdec);
       
   493   if (ret != GST_FLOW_OK) {
       
   494     goto pause;
       
   495   }
       
   496 
       
   497   /* Allocate output buffer */
       
   498   rowbytes = png_get_rowbytes (pngdec->png, pngdec->info);
       
   499   buffer_size = pngdec->height * GST_ROUND_UP_4 (rowbytes);
       
   500   ret =
       
   501       gst_pad_alloc_buffer_and_set_caps (pngdec->srcpad, GST_BUFFER_OFFSET_NONE,
       
   502       buffer_size, GST_PAD_CAPS (pngdec->srcpad), &buffer);
       
   503   if (ret != GST_FLOW_OK)
       
   504     goto pause;
       
   505 
       
   506   rows = (png_bytep *) g_malloc (sizeof (png_bytep) * pngdec->height);
       
   507 
       
   508   inp = GST_BUFFER_DATA (buffer);
       
   509 
       
   510   for (i = 0; i < pngdec->height; i++) {
       
   511     rows[i] = inp;
       
   512     inp += GST_ROUND_UP_4 (rowbytes);
       
   513   }
       
   514 
       
   515   /* Read the actual picture */
       
   516   png_read_image (pngdec->png, rows);
       
   517   free (rows);
       
   518 
       
   519   /* Push the raw RGB frame */
       
   520   ret = gst_pad_push (pngdec->srcpad, buffer);
       
   521   if (ret != GST_FLOW_OK)
       
   522     goto pause;
       
   523 
       
   524   /* And we are done */
       
   525   gst_pad_pause_task (pngdec->sinkpad);
       
   526   gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
       
   527   return;
       
   528 
       
   529 pause:
       
   530   {
       
   531     GST_INFO_OBJECT (pngdec, "pausing task, reason %s",
       
   532         gst_flow_get_name (ret));
       
   533     gst_pad_pause_task (pngdec->sinkpad);
       
   534     if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
       
   535       GST_ELEMENT_ERROR (pngdec, STREAM, FAILED,
       
   536           (_("Internal data stream error.")),
       
   537           ("stream stopped, reason %s", gst_flow_get_name (ret)));
       
   538       gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
       
   539     }
       
   540   }
       
   541 }
       
   542 
       
   543 static GstFlowReturn
       
   544 gst_pngdec_chain (GstPad * pad, GstBuffer * buffer)
       
   545 {
       
   546   GstPngDec *pngdec;
       
   547   GstFlowReturn ret = GST_FLOW_OK;
       
   548 
       
   549   pngdec = GST_PNGDEC (gst_pad_get_parent (pad));
       
   550 
       
   551   GST_LOG_OBJECT (pngdec, "Got buffer, size=%u", GST_BUFFER_SIZE (buffer));
       
   552 
       
   553   if (G_UNLIKELY (!pngdec->setup))
       
   554     goto not_configured;
       
   555 
       
   556   /* Something is going wrong in our callbacks */
       
   557   ret = pngdec->ret;
       
   558   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
       
   559     GST_WARNING_OBJECT (pngdec, "we have a pending return code of %d", ret);
       
   560     goto beach;
       
   561   }
       
   562 
       
   563   /* Let libpng come back here on error */
       
   564   if (setjmp (png_jmpbuf (pngdec->png))) {
       
   565     GST_WARNING_OBJECT (pngdec, "error during decoding");
       
   566     ret = GST_FLOW_ERROR;
       
   567     goto beach;
       
   568   }
       
   569 
       
   570   pngdec->in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
       
   571   pngdec->in_duration = GST_BUFFER_DURATION (buffer);
       
   572 
       
   573   /* Progressive loading of the PNG image */
       
   574   png_process_data (pngdec->png, pngdec->info, GST_BUFFER_DATA (buffer),
       
   575       GST_BUFFER_SIZE (buffer));
       
   576 
       
   577   if (pngdec->image_ready) {
       
   578     if (pngdec->framed) {
       
   579       /* Reset ourselves for the next frame */
       
   580       gst_pngdec_libpng_clear (pngdec);
       
   581       gst_pngdec_libpng_init (pngdec);
       
   582       GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame");
       
   583       png_set_progressive_read_fn (pngdec->png, pngdec,
       
   584           user_info_callback, user_endrow_callback, user_end_callback);
       
   585     } else {
       
   586       GST_LOG_OBJECT (pngdec, "sending EOS");
       
   587       pngdec->ret = gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ());
       
   588     }
       
   589     pngdec->image_ready = FALSE;
       
   590   }
       
   591 
       
   592   /* grab new return code */
       
   593   ret = pngdec->ret;
       
   594 
       
   595   /* And release the buffer */
       
   596   gst_buffer_unref (buffer);
       
   597 
       
   598 beach:
       
   599   gst_object_unref (pngdec);
       
   600 
       
   601   return ret;
       
   602 
       
   603   /* ERRORS */
       
   604 not_configured:
       
   605   {
       
   606     GST_LOG_OBJECT (pngdec, "we are not configured yet");
       
   607     ret = GST_FLOW_WRONG_STATE;
       
   608     goto beach;
       
   609   }
       
   610 }
       
   611 
       
   612 static gboolean
       
   613 gst_pngdec_sink_setcaps (GstPad * pad, GstCaps * caps)
       
   614 {
       
   615   GstStructure *s;
       
   616   GstPngDec *pngdec;
       
   617   gint num, denom;
       
   618 
       
   619   pngdec = GST_PNGDEC (gst_pad_get_parent (pad));
       
   620 
       
   621   s = gst_caps_get_structure (caps, 0);
       
   622   if (gst_structure_get_fraction (s, "framerate", &num, &denom)) {
       
   623     GST_DEBUG_OBJECT (pngdec, "framed input");
       
   624     pngdec->framed = TRUE;
       
   625     pngdec->fps_n = num;
       
   626     pngdec->fps_d = denom;
       
   627   } else {
       
   628     GST_DEBUG_OBJECT (pngdec, "single picture input");
       
   629     pngdec->framed = FALSE;
       
   630     pngdec->fps_n = 0;
       
   631     pngdec->fps_d = 1;
       
   632   }
       
   633 
       
   634   gst_object_unref (pngdec);
       
   635   return TRUE;
       
   636 }
       
   637 
       
   638 static gboolean
       
   639 gst_pngdec_sink_event (GstPad * pad, GstEvent * event)
       
   640 {
       
   641   GstPngDec *pngdec;
       
   642   gboolean res;
       
   643 
       
   644   pngdec = GST_PNGDEC (gst_pad_get_parent (pad));
       
   645 
       
   646   switch (GST_EVENT_TYPE (event)) {
       
   647     case GST_EVENT_NEWSEGMENT:{
       
   648       gdouble rate, arate;
       
   649       gboolean update;
       
   650       gint64 start, stop, position;
       
   651       GstFormat fmt;
       
   652 
       
   653       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
       
   654           &start, &stop, &position);
       
   655 
       
   656       gst_segment_set_newsegment_full (&pngdec->segment, update, rate, arate,
       
   657           fmt, start, stop, position);
       
   658 
       
   659       GST_LOG_OBJECT (pngdec, "NEWSEGMENT (%s)", gst_format_get_name (fmt));
       
   660 
       
   661       if (fmt == GST_FORMAT_TIME) {
       
   662         pngdec->need_newsegment = FALSE;
       
   663         res = gst_pad_push_event (pngdec->srcpad, event);
       
   664       } else {
       
   665         gst_event_unref (event);
       
   666         res = TRUE;
       
   667       }
       
   668       break;
       
   669     }
       
   670     case GST_EVENT_FLUSH_STOP:
       
   671     {
       
   672       gst_pngdec_libpng_clear (pngdec);
       
   673       gst_pngdec_libpng_init (pngdec);
       
   674       png_set_progressive_read_fn (pngdec->png, pngdec,
       
   675           user_info_callback, user_endrow_callback, user_end_callback);
       
   676       pngdec->ret = GST_FLOW_OK;
       
   677       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
       
   678       res = gst_pad_push_event (pngdec->srcpad, event);
       
   679       break;
       
   680     }
       
   681     case GST_EVENT_EOS:
       
   682     {
       
   683       GST_LOG_OBJECT (pngdec, "EOS");
       
   684       gst_pngdec_libpng_clear (pngdec);
       
   685       pngdec->ret = GST_FLOW_UNEXPECTED;
       
   686       res = gst_pad_push_event (pngdec->srcpad, event);
       
   687       break;
       
   688     }
       
   689     default:
       
   690       res = gst_pad_push_event (pngdec->srcpad, event);
       
   691       break;
       
   692   }
       
   693 
       
   694   gst_object_unref (pngdec);
       
   695   return res;
       
   696 }
       
   697 
       
   698 
       
   699 /* Clean up the libpng structures */
       
   700 static gboolean
       
   701 gst_pngdec_libpng_clear (GstPngDec * pngdec)
       
   702 {
       
   703   png_infopp info = NULL, endinfo = NULL;
       
   704 
       
   705   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
       
   706 
       
   707   GST_LOG ("cleaning up libpng structures");
       
   708 
       
   709   if (pngdec->info) {
       
   710     info = &pngdec->info;
       
   711   }
       
   712 
       
   713   if (pngdec->endinfo) {
       
   714     endinfo = &pngdec->endinfo;
       
   715   }
       
   716 
       
   717   if (pngdec->png) {
       
   718     png_destroy_read_struct (&(pngdec->png), info, endinfo);
       
   719     pngdec->png = NULL;
       
   720     pngdec->info = NULL;
       
   721     pngdec->endinfo = NULL;
       
   722   }
       
   723 
       
   724   pngdec->bpp = pngdec->color_type = pngdec->height = pngdec->width = -1;
       
   725   pngdec->offset = 0;
       
   726   pngdec->rowbytes = 0;
       
   727   pngdec->buffer_out = NULL;
       
   728 
       
   729   pngdec->setup = FALSE;
       
   730 
       
   731   pngdec->in_timestamp = GST_CLOCK_TIME_NONE;
       
   732   pngdec->in_duration = GST_CLOCK_TIME_NONE;
       
   733 
       
   734   return TRUE;
       
   735 }
       
   736 
       
   737 static gboolean
       
   738 gst_pngdec_libpng_init (GstPngDec * pngdec)
       
   739 {
       
   740   g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE);
       
   741 
       
   742   if (pngdec->setup)
       
   743     return TRUE;
       
   744 
       
   745   GST_LOG ("init libpng structures");
       
   746 
       
   747   /* initialize png struct stuff */
       
   748   pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
       
   749       (png_voidp) NULL, user_error_fn, user_warning_fn);
       
   750 
       
   751   if (pngdec->png == NULL)
       
   752     goto init_failed;
       
   753 
       
   754   pngdec->info = png_create_info_struct (pngdec->png);
       
   755   if (pngdec->info == NULL)
       
   756     goto info_failed;
       
   757 
       
   758   pngdec->endinfo = png_create_info_struct (pngdec->png);
       
   759   if (pngdec->endinfo == NULL)
       
   760     goto endinfo_failed;
       
   761 
       
   762   pngdec->setup = TRUE;
       
   763 
       
   764   return TRUE;
       
   765 
       
   766   /* ERRORS */
       
   767 init_failed:
       
   768   {
       
   769     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
       
   770         ("Failed to initialize png structure"));
       
   771     return FALSE;
       
   772   }
       
   773 info_failed:
       
   774   {
       
   775     gst_pngdec_libpng_clear (pngdec);
       
   776     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
       
   777         ("Failed to initialize info structure"));
       
   778     return FALSE;
       
   779   }
       
   780 endinfo_failed:
       
   781   {
       
   782     gst_pngdec_libpng_clear (pngdec);
       
   783     GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL),
       
   784         ("Failed to initialize endinfo structure"));
       
   785     return FALSE;
       
   786   }
       
   787 }
       
   788 
       
   789 static GstStateChangeReturn
       
   790 gst_pngdec_change_state (GstElement * element, GstStateChange transition)
       
   791 {
       
   792   GstStateChangeReturn ret;
       
   793   GstPngDec *pngdec;
       
   794 
       
   795   pngdec = GST_PNGDEC (element);
       
   796 
       
   797   switch (transition) {
       
   798     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
   799       gst_pngdec_libpng_init (pngdec);
       
   800       pngdec->need_newsegment = TRUE;
       
   801       pngdec->framed = FALSE;
       
   802       pngdec->ret = GST_FLOW_OK;
       
   803       gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED);
       
   804       break;
       
   805     default:
       
   806       break;
       
   807   }
       
   808 
       
   809   ret = parent_class->change_state (element, transition);
       
   810   if (ret != GST_STATE_CHANGE_SUCCESS)
       
   811     return ret;
       
   812 
       
   813   switch (transition) {
       
   814     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
   815       gst_pngdec_libpng_clear (pngdec);
       
   816       break;
       
   817     default:
       
   818       break;
       
   819   }
       
   820 
       
   821   return ret;
       
   822 }
       
   823 
       
   824 /* this function gets called when we activate ourselves in push mode. */
       
   825 static gboolean
       
   826 gst_pngdec_sink_activate_push (GstPad * sinkpad, gboolean active)
       
   827 {
       
   828   GstPngDec *pngdec;
       
   829 
       
   830   pngdec = GST_PNGDEC (GST_OBJECT_PARENT (sinkpad));
       
   831 
       
   832   pngdec->ret = GST_FLOW_OK;
       
   833 
       
   834   if (active) {
       
   835     /* Let libpng come back here on error */
       
   836     if (setjmp (png_jmpbuf (pngdec->png)))
       
   837       goto setup_failed;
       
   838 
       
   839     GST_LOG ("setting up progressive loading callbacks");
       
   840     png_set_progressive_read_fn (pngdec->png, pngdec,
       
   841         user_info_callback, user_endrow_callback, user_end_callback);
       
   842   }
       
   843   return TRUE;
       
   844 
       
   845 setup_failed:
       
   846   {
       
   847     GST_LOG ("failed setting up libpng jmpbuf");
       
   848     gst_pngdec_libpng_clear (pngdec);
       
   849     return FALSE;
       
   850   }
       
   851 }
       
   852 
       
   853 /* this function gets called when we activate ourselves in pull mode.
       
   854  * We can perform  random access to the resource and we start a task
       
   855  * to start reading */
       
   856 static gboolean
       
   857 gst_pngdec_sink_activate_pull (GstPad * sinkpad, gboolean active)
       
   858 {
       
   859   if (active) {
       
   860     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_pngdec_task,
       
   861         sinkpad);
       
   862   } else {
       
   863     return gst_pad_stop_task (sinkpad);
       
   864   }
       
   865 }
       
   866 
       
   867 /* this function is called when the pad is activated and should start
       
   868  * processing data.
       
   869  *
       
   870  * We check if we can do random access to decide if we work push or
       
   871  * pull based.
       
   872  */
       
   873 static gboolean
       
   874 gst_pngdec_sink_activate (GstPad * sinkpad)
       
   875 {
       
   876   if (gst_pad_check_pull_range (sinkpad)) {
       
   877     return gst_pad_activate_pull (sinkpad, TRUE);
       
   878   } else {
       
   879     return gst_pad_activate_push (sinkpad, TRUE);
       
   880   }
       
   881 }