diff --git a/ext/libpng/gstpngdec.c b/ext/libpng/gstpngdec.c index 74d8edf68f..429b585c12 100644 --- a/ext/libpng/gstpngdec.c +++ b/ext/libpng/gstpngdec.c @@ -1,5 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2012 Collabora Ltd. + * Author : Edward Hervey * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,41 +30,33 @@ #include #include #include -#include -#include #include GST_DEBUG_CATEGORY_STATIC (pngdec_debug); #define GST_CAT_DEFAULT pngdec_debug static gboolean gst_pngdec_libpng_init (GstPngDec * pngdec); -static gboolean gst_pngdec_libpng_clear (GstPngDec * pngdec); - -static GstStateChangeReturn gst_pngdec_change_state (GstElement * element, - GstStateChange transition); - -static gboolean gst_pngdec_sink_activate_mode (GstPad * sinkpad, - GstObject * parent, GstPadMode mode, gboolean active); -static gboolean gst_pngdec_sink_activate (GstPad * sinkpad, GstObject * parent); +static gboolean gst_pngdec_reset (GstVideoDecoder * decoder, gboolean hard); static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec); -static void gst_pngdec_task (GstPad * pad); -static GstFlowReturn gst_pngdec_chain (GstPad * pad, GstObject * parent, - GstBuffer * buffer); -static gboolean gst_pngdec_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_pngdec_sink_setcaps (GstPngDec * pngdec, GstCaps * caps); +static gboolean gst_pngdec_start (GstVideoDecoder * decoder); +static gboolean gst_pngdec_stop (GstVideoDecoder * decoder); +static gboolean gst_pngdec_set_format (GstVideoDecoder * Decoder, + GstVideoCodecState * state); +static GstFlowReturn gst_pngdec_handle_frame (GstVideoDecoder * decoder, + GstVideoCodecFrame * frame); -static GstFlowReturn gst_pngdec_negotiate_pool (GstPngDec * dec, - GstCaps * caps, GstVideoInfo * info); +GST_BOILERPLATE (GstPngDec, gst_pngdec, GstVideoDecoder, + GST_TYPE_VIDEO_DECODER); static GstStaticPadTemplate gst_pngdec_src_pad_template = -GST_STATIC_PAD_TEMPLATE ("src", + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE - ("{ RGBA, RGB, ARGB64, GRAY8, GRAY16_BE }")) + GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_ARGB_64 ";" + GST_VIDEO_CAPS_GRAY8 ";" GST_VIDEO_CAPS_GRAY16 ("BIG_ENDIAN")) ); static GstStaticPadTemplate gst_pngdec_sink_pad_template = @@ -72,66 +66,43 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("image/png") ); -#define gst_pngdec_parent_class parent_class -G_DEFINE_TYPE (GstPngDec, gst_pngdec, GST_TYPE_ELEMENT); +static void +gst_pngdec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_static_pad_template (element_class, + &gst_pngdec_src_pad_template); + gst_element_class_add_static_pad_template (element_class, + &gst_pngdec_sink_pad_template); + gst_element_class_set_details_simple (element_class, "PNG image decoder", + "Codec/Decoder/Image", + "Decode a png video frame to a raw image", + "Wim Taymans "); +} static void gst_pngdec_class_init (GstPngDecClass * klass) { - GstElementClass *gstelement_class; + GstVideoDecoderClass *vdec_class = (GstVideoDecoderClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_pngdec_change_state); - - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_pngdec_src_pad_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_pngdec_sink_pad_template)); - gst_element_class_set_static_metadata (gstelement_class, "PNG image decoder", - "Codec/Decoder/Image", - "Decode a png video frame to a raw image", - "Wim Taymans "); + vdec_class->start = gst_pngdec_start; + vdec_class->reset = gst_pngdec_reset; + vdec_class->set_format = gst_pngdec_set_format; + vdec_class->handle_frame = gst_pngdec_handle_frame; GST_DEBUG_CATEGORY_INIT (pngdec_debug, "pngdec", 0, "PNG image decoder"); } static void -gst_pngdec_init (GstPngDec * pngdec) +gst_pngdec_init (GstPngDec * pngdec, GstPngDecClass * klass) { - pngdec->sinkpad = - gst_pad_new_from_static_template (&gst_pngdec_sink_pad_template, "sink"); - gst_pad_set_activate_function (pngdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_pngdec_sink_activate)); - gst_pad_set_activatemode_function (pngdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_pngdec_sink_activate_mode)); - gst_pad_set_chain_function (pngdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_pngdec_chain)); - gst_pad_set_event_function (pngdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_pngdec_sink_event)); - gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->sinkpad); - - pngdec->srcpad = - gst_pad_new_from_static_template (&gst_pngdec_src_pad_template, "src"); - gst_pad_use_fixed_caps (pngdec->srcpad); - gst_element_add_pad (GST_ELEMENT (pngdec), pngdec->srcpad); - pngdec->buffer_out = NULL; pngdec->png = NULL; pngdec->info = NULL; pngdec->endinfo = NULL; - pngdec->setup = FALSE; pngdec->color_type = -1; - pngdec->width = -1; - pngdec->height = -1; - pngdec->fps_n = 0; - pngdec->fps_d = 1; - - pngdec->in_timestamp = GST_CLOCK_TIME_NONE; - pngdec->in_duration = GST_CLOCK_TIME_NONE; - - gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED); pngdec->image_ready = FALSE; } @@ -153,41 +124,55 @@ user_info_callback (png_structp png_ptr, png_infop info) { GstPngDec *pngdec = NULL; GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *buffer = NULL; - - pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr)); + size_t buffer_size; + guint height; GST_LOG ("info ready"); + pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr)); /* Generate the caps and configure */ ret = gst_pngdec_caps_create_and_set (pngdec); if (ret != GST_FLOW_OK) { goto beach; } - if (gst_pad_check_reconfigure (pngdec->srcpad)) { - GstCaps *caps; - - caps = gst_pad_get_current_caps (pngdec->srcpad); - gst_pngdec_negotiate_pool (pngdec, caps, &pngdec->vinfo); - gst_caps_unref (caps); - } + height = GST_VIDEO_INFO_HEIGHT (&pngdec->output_state->info); /* Allocate output buffer */ - g_assert (pngdec->pool); - ret = gst_buffer_pool_acquire_buffer (pngdec->pool, &buffer, NULL); - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (pngdec, "failed to acquire buffer"); + pngdec->rowbytes = png_get_rowbytes (pngdec->png, pngdec->info); + GST_DEBUG ("png told us each row takes %d bytes", pngdec->rowbytes); + if (pngdec->rowbytes > (G_MAXUINT32 - 3) + || height > G_MAXUINT32 / pngdec->rowbytes) { ret = GST_FLOW_ERROR; goto beach; } + pngdec->rowbytes = GST_ROUND_UP_4 (pngdec->rowbytes); + buffer_size = height * pngdec->rowbytes; - pngdec->buffer_out = buffer; + GST_DEBUG ("Allocating a buffer of %d bytes", buffer_size); + + g_assert (pngdec->buffer_out == NULL); + pngdec->buffer_out = pngdec->current_frame->output_buffer = + gst_buffer_new_and_alloc (buffer_size); beach: pngdec->ret = ret; } +static gboolean +gst_pngdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state) +{ + GstPngDec *pngdec = (GstPngDec *) decoder; + + if (pngdec->input_state) + gst_video_codec_state_unref (pngdec->input_state); + pngdec->input_state = gst_video_codec_state_ref (state); + + /* We'll set format later on */ + + return TRUE; +} + static void user_endrow_callback (png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) @@ -201,52 +186,16 @@ user_endrow_callback (png_structp png_ptr, png_bytep new_row, /* If buffer_out doesn't exist, it means buffer_alloc failed, which * will already have set the return code */ if (GST_IS_BUFFER (pngdec->buffer_out)) { - GstVideoFrame frame; - GstBuffer *buffer = pngdec->buffer_out; - size_t offset; - gint width; - guint8 *data; + size_t offset = row_num * pngdec->rowbytes; - if (!gst_video_frame_map (&frame, &pngdec->vinfo, buffer, GST_MAP_WRITE)) { - pngdec->ret = GST_FLOW_ERROR; - return; - } - - data = GST_VIDEO_FRAME_COMP_DATA (&frame, 0); - offset = row_num * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0); GST_LOG ("got row %u, copying in buffer %p at offset %" G_GSIZE_FORMAT, (guint) row_num, pngdec->buffer_out, offset); - width = GST_ROUND_UP_4 (png_get_rowbytes (pngdec->png, pngdec->info)); - memcpy (data + offset, new_row, width); - gst_video_frame_unmap (&frame); + memcpy (GST_BUFFER_DATA (pngdec->buffer_out) + offset, new_row, + pngdec->rowbytes); pngdec->ret = GST_FLOW_OK; } } -static gboolean -buffer_clip (GstPngDec * dec, GstBuffer * buffer) -{ - gboolean res = TRUE; - guint64 cstart, cstop; - - if ((!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) || - (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer))) || - (dec->segment.format != GST_FORMAT_TIME)) - goto beach; - - cstart = GST_BUFFER_TIMESTAMP (buffer); - cstop = GST_BUFFER_DURATION (buffer); - - if ((res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, - cstart, cstart + cstop, &cstart, &cstop))) { - GST_BUFFER_TIMESTAMP (buffer) = cstart; - GST_BUFFER_DURATION (buffer) = cstop - cstart; - } - -beach: - return res; -} - static void user_end_callback (png_structp png_ptr, png_infop info) { @@ -259,149 +208,22 @@ user_end_callback (png_structp png_ptr, png_infop info) if (!pngdec->buffer_out) return; - if (GST_CLOCK_TIME_IS_VALID (pngdec->in_timestamp)) - GST_BUFFER_TIMESTAMP (pngdec->buffer_out) = pngdec->in_timestamp; - if (GST_CLOCK_TIME_IS_VALID (pngdec->in_duration)) - GST_BUFFER_DURATION (pngdec->buffer_out) = pngdec->in_duration; + pngdec->ret = + gst_video_decoder_finish_frame (GST_VIDEO_DECODER (pngdec), + pngdec->current_frame); - /* buffer clipping */ - if (buffer_clip (pngdec, pngdec->buffer_out)) { - /* Push our buffer and then EOS if needed */ - GST_LOG_OBJECT (pngdec, "pushing buffer with ts=%" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (pngdec->buffer_out))); - - pngdec->ret = gst_pad_push (pngdec->srcpad, pngdec->buffer_out); - } else { - GST_LOG_OBJECT (pngdec, "dropped decoded buffer"); - gst_buffer_unref (pngdec->buffer_out); - } pngdec->buffer_out = NULL; pngdec->image_ready = TRUE; } -static void -user_read_data (png_structp png_ptr, png_bytep data, png_size_t length) -{ - GstPngDec *pngdec; - GstBuffer *buffer = NULL; - GstFlowReturn ret = GST_FLOW_OK; - guint size; - - pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr)); - - GST_LOG ("reading %" G_GSIZE_FORMAT " bytes of data at offset %d", length, - pngdec->offset); - - ret = gst_pad_pull_range (pngdec->sinkpad, pngdec->offset, length, &buffer); - if (ret != GST_FLOW_OK) - goto pause; - - size = gst_buffer_get_size (buffer); - - if (size != length) - goto short_buffer; - - gst_buffer_extract (buffer, 0, data, size); - gst_buffer_unref (buffer); - - pngdec->offset += length; - - return; - - /* ERRORS */ -pause: - { - GST_INFO_OBJECT (pngdec, "pausing task, reason %s", - gst_flow_get_name (ret)); - gst_pad_pause_task (pngdec->sinkpad); - if (ret == GST_FLOW_EOS) { - gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ()); - } else if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED) { - GST_ELEMENT_ERROR (pngdec, STREAM, FAILED, - (_("Internal data stream error.")), - ("stream stopped, reason %s", gst_flow_get_name (ret))); - gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ()); - } - png_error (png_ptr, "Internal data stream error."); - return; - } -short_buffer: - { - gst_buffer_unref (buffer); - GST_ELEMENT_ERROR (pngdec, STREAM, FAILED, - (_("Internal data stream error.")), - ("Read %u, needed %" G_GSIZE_FORMAT "bytes", size, length)); - ret = GST_FLOW_ERROR; - goto pause; - } -} - -static GstFlowReturn -gst_pngdec_negotiate_pool (GstPngDec * dec, GstCaps * caps, GstVideoInfo * info) -{ - GstQuery *query; - GstBufferPool *pool; - guint size, min, max; - GstStructure *config; - - /* find a pool for the negotiated caps now */ - query = gst_query_new_allocation (caps, TRUE); - - if (!gst_pad_peer_query (dec->srcpad, query)) { - GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints"); - } - - if (gst_query_get_n_allocation_pools (query) > 0) { - /* we got configuration from our peer, parse them */ - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); - size = MAX (size, info->size); - } else { - pool = NULL; - size = info->size; - min = max = 0; - } - - if (pool == NULL) { - /* we did not get a pool, make one ourselves then */ - pool = gst_video_buffer_pool_new (); - } - - if (dec->pool) { - gst_buffer_pool_set_active (dec->pool, TRUE); - gst_object_unref (dec->pool); - } - dec->pool = pool; - - config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set_params (config, caps, size, min, max); - - if (gst_query_has_allocation_meta (query, GST_VIDEO_META_API_TYPE)) { - /* just set the option, if the pool can support it we will transparently use - * it through the video info API. We could also see if the pool support this - * option and only activate it then. */ - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_META); - } - gst_buffer_pool_set_config (pool, config); - - /* and activate */ - gst_buffer_pool_set_active (pool, TRUE); - - gst_query_unref (query); - - return GST_FLOW_OK; -} static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec) { GstFlowReturn ret = GST_FLOW_OK; - GstCaps *caps = NULL, *res = NULL; - GstPadTemplate *templ = NULL; gint bpc = 0, color_type; png_uint_32 width, height; - GstVideoFormat format; - GstVideoInfo vinfo = { 0, }; + GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR); @@ -455,204 +277,74 @@ gst_pngdec_caps_create_and_set (GstPngDec * pngdec) png_read_update_info (pngdec->png, pngdec->info); /* Get IHDR header again after transformation settings */ - png_get_IHDR (pngdec->png, pngdec->info, &width, &height, &bpc, &pngdec->color_type, NULL, NULL, NULL); - pngdec->width = width; - pngdec->height = height; - - GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", pngdec->width, - pngdec->height); + GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", width, height); switch (pngdec->color_type) { case PNG_COLOR_TYPE_RGB: GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits"); - format = GST_VIDEO_FORMAT_RGB; + if (bpc == 8) + format = GST_VIDEO_FORMAT_RGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: - GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits"); - if (bpc == 1) + GST_LOG_OBJECT (pngdec, + "we have an alpha channel, depth is 32 or 64 bits"); + if (bpc == 8) format = GST_VIDEO_FORMAT_RGBA; - else + else if (bpc == 16) format = GST_VIDEO_FORMAT_ARGB64; break; case PNG_COLOR_TYPE_GRAY: GST_LOG_OBJECT (pngdec, "We have an gray image, depth is 8 or 16 (be) bits"); - if (bpc == 1) + if (bpc == 8) format = GST_VIDEO_FORMAT_GRAY8; - else + else if (bpc == 16) format = GST_VIDEO_FORMAT_GRAY16_BE; break; default: - GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL), - ("pngdec does not support this color type")); - ret = GST_FLOW_NOT_SUPPORTED; - goto beach; + break; } - gst_video_info_set_format (&vinfo, format, pngdec->width, pngdec->height); - vinfo.fps_n = pngdec->fps_n; - vinfo.fps_d = pngdec->fps_d; - vinfo.par_n = 1; - vinfo.par_d = 1; - - if (memcmp (&vinfo, &pngdec->vinfo, sizeof (vinfo)) == 0) { - GST_DEBUG_OBJECT (pngdec, "video info unchanged, skip negotiation"); - ret = GST_FLOW_OK; + if (format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL), + ("pngdec does not support this color type")); + ret = GST_FLOW_NOT_SUPPORTED; goto beach; } - pngdec->vinfo = vinfo; + /* Check if output state changed */ + if (pngdec->output_state) { + GstVideoInfo *info = &pngdec->output_state->info; - caps = gst_video_info_to_caps (&pngdec->vinfo); - - templ = gst_static_pad_template_get (&gst_pngdec_src_pad_template); - - res = gst_caps_intersect (caps, gst_pad_template_get_caps (templ)); - - gst_caps_unref (caps); - gst_object_unref (templ); - - if (!gst_pad_set_caps (pngdec->srcpad, res)) - ret = GST_FLOW_NOT_NEGOTIATED; - - /* clear pending reconfigure */ - gst_pad_check_reconfigure (pngdec->srcpad); - - GST_DEBUG_OBJECT (pngdec, "our caps %" GST_PTR_FORMAT, res); - gst_pngdec_negotiate_pool (pngdec, res, &pngdec->vinfo); - - gst_caps_unref (res); - - /* Push a newsegment event */ - if (pngdec->need_newsegment) { - gst_segment_init (&pngdec->segment, GST_FORMAT_TIME); - gst_pad_push_event (pngdec->srcpad, - gst_event_new_segment (&pngdec->segment)); - pngdec->need_newsegment = FALSE; + if (width == GST_VIDEO_INFO_WIDTH (info) && + height == GST_VIDEO_INFO_HEIGHT (info) && + GST_VIDEO_INFO_FORMAT (info) == format) { + goto beach; + } + gst_video_codec_state_unref (pngdec->output_state); } + pngdec->output_state = + gst_video_decoder_set_output_state (GST_VIDEO_DECODER (pngdec), format, + width, height, pngdec->input_state); + GST_DEBUG ("Final %d %d", GST_VIDEO_INFO_WIDTH (&pngdec->output_state->info), + GST_VIDEO_INFO_HEIGHT (&pngdec->output_state->info)); + beach: return ret; } -static void -gst_pngdec_task (GstPad * pad) -{ - GstPngDec *pngdec; - GstBuffer *buffer = NULL; - gint i = 0; - png_bytep *rows, inp = NULL; - GstFlowReturn ret = GST_FLOW_OK; - GstVideoFrame frame; - - pngdec = GST_PNGDEC (GST_OBJECT_PARENT (pad)); - - GST_LOG_OBJECT (pngdec, "read frame"); - - /* Let libpng come back here on error */ - if (setjmp (png_jmpbuf (pngdec->png))) { - ret = GST_FLOW_ERROR; - goto pause; - } - - /* Set reading callback */ - png_set_read_fn (pngdec->png, pngdec, user_read_data); - - /* Read info */ - png_read_info (pngdec->png, pngdec->info); - - pngdec->fps_n = 0; - pngdec->fps_d = 1; - - /* Generate the caps and configure */ - ret = gst_pngdec_caps_create_and_set (pngdec); - if (ret != GST_FLOW_OK) { - goto pause; - } - - /* Allocate output buffer */ - g_assert (pngdec->pool); - ret = gst_buffer_pool_acquire_buffer (pngdec->pool, &buffer, NULL); - if (ret != GST_FLOW_OK) - goto pause; - - rows = (png_bytep *) g_malloc (sizeof (png_bytep) * pngdec->height); - - if (!gst_video_frame_map (&frame, &pngdec->vinfo, buffer, GST_MAP_WRITE)) - goto invalid_frame; - - inp = GST_VIDEO_FRAME_COMP_DATA (&frame, 0); - - for (i = 0; i < pngdec->height; i++) { - rows[i] = inp; - inp += GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0); - } - - /* Read the actual picture */ - png_read_image (pngdec->png, rows); - g_free (rows); - - gst_video_frame_unmap (&frame); - inp = NULL; - - /* Push the raw RGB frame */ - ret = gst_pad_push (pngdec->srcpad, buffer); - buffer = NULL; - if (ret != GST_FLOW_OK) - goto pause; - - /* And we are done */ - gst_pad_pause_task (pngdec->sinkpad); - gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ()); - return; - -pause: - { - if (inp) - gst_video_frame_unmap (&frame); - if (buffer) - gst_buffer_unref (buffer); - GST_INFO_OBJECT (pngdec, "pausing task, reason %s", - gst_flow_get_name (ret)); - gst_pad_pause_task (pngdec->sinkpad); - if (ret == GST_FLOW_EOS) { - gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ()); - } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) { - GST_ELEMENT_ERROR (pngdec, STREAM, FAILED, - (_("Internal data stream error.")), - ("stream stopped, reason %s", gst_flow_get_name (ret))); - gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ()); - } - } -invalid_frame: - { - GST_DEBUG_OBJECT (pngdec, "could not map video frame"); - ret = GST_FLOW_ERROR; - goto pause; - } -} - static GstFlowReturn -gst_pngdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +gst_pngdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { - GstPngDec *pngdec; + GstPngDec *pngdec = (GstPngDec *) decoder; GstFlowReturn ret = GST_FLOW_OK; - GstMapInfo map = GST_MAP_INFO_INIT; - pngdec = GST_PNGDEC (parent); - - if (G_UNLIKELY (!pngdec->setup)) - goto not_configured; - - /* Something is going wrong in our callbacks */ - ret = pngdec->ret; - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - GST_WARNING_OBJECT (pngdec, "we have a pending return code of %d", ret); - goto beach; - } + GST_LOG_OBJECT (pngdec, "Got buffer, size=%u", + GST_BUFFER_SIZE (frame->input_buffer)); /* Let libpng come back here on error */ if (setjmp (png_jmpbuf (pngdec->png))) { @@ -661,167 +353,39 @@ gst_pngdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) goto beach; } - pngdec->in_timestamp = GST_BUFFER_TIMESTAMP (buffer); - pngdec->in_duration = GST_BUFFER_DURATION (buffer); - - gst_buffer_map (buffer, &map, GST_MAP_READ); - - GST_LOG_OBJECT (pngdec, "Got buffer, size=%d", (gint) map.size); + pngdec->current_frame = frame; /* Progressive loading of the PNG image */ - png_process_data (pngdec->png, pngdec->info, map.data, map.size); + png_process_data (pngdec->png, pngdec->info, + GST_BUFFER_DATA (frame->input_buffer), + GST_BUFFER_SIZE (frame->input_buffer)); if (pngdec->image_ready) { - if (pngdec->framed) { + if (1) { /* Reset ourselves for the next frame */ - gst_pngdec_libpng_clear (pngdec); - gst_pngdec_libpng_init (pngdec); + gst_pngdec_reset (decoder, TRUE); GST_LOG_OBJECT (pngdec, "setting up callbacks for next frame"); png_set_progressive_read_fn (pngdec->png, pngdec, user_info_callback, user_endrow_callback, user_end_callback); } else { GST_LOG_OBJECT (pngdec, "sending EOS"); - pngdec->ret = gst_pad_push_event (pngdec->srcpad, gst_event_new_eos ()); + pngdec->ret = GST_FLOW_UNEXPECTED; } pngdec->image_ready = FALSE; } - /* grab new return code */ ret = pngdec->ret; - beach: - if (G_LIKELY (map.data)) - gst_buffer_unmap (buffer, &map); - - /* And release the buffer */ - gst_buffer_unref (buffer); return ret; - - /* ERRORS */ -not_configured: - { - GST_LOG_OBJECT (pngdec, "we are not configured yet"); - ret = GST_FLOW_FLUSHING; - goto beach; - } } -static gboolean -gst_pngdec_sink_setcaps (GstPngDec * pngdec, GstCaps * caps) -{ - GstStructure *s; - gint num, denom; - - s = gst_caps_get_structure (caps, 0); - if (gst_structure_get_fraction (s, "framerate", &num, &denom)) { - GST_DEBUG_OBJECT (pngdec, "framed input"); - pngdec->framed = TRUE; - pngdec->fps_n = num; - pngdec->fps_d = denom; - } else { - GST_DEBUG_OBJECT (pngdec, "single picture input"); - pngdec->framed = FALSE; - pngdec->fps_n = 0; - pngdec->fps_d = 1; - } - - return TRUE; -} - -static gboolean -gst_pngdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstPngDec *pngdec; - gboolean res; - - pngdec = GST_PNGDEC (parent); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEGMENT:{ - gst_event_copy_segment (event, &pngdec->segment); - - GST_LOG_OBJECT (pngdec, "SEGMENT %" GST_SEGMENT_FORMAT, &pngdec->segment); - - if (pngdec->segment.format == GST_FORMAT_TIME) { - pngdec->need_newsegment = FALSE; - res = gst_pad_push_event (pngdec->srcpad, event); - } else { - gst_event_unref (event); - res = TRUE; - } - break; - } - case GST_EVENT_FLUSH_STOP: - { - gst_pngdec_libpng_clear (pngdec); - gst_pngdec_libpng_init (pngdec); - png_set_progressive_read_fn (pngdec->png, pngdec, - user_info_callback, user_endrow_callback, user_end_callback); - pngdec->ret = GST_FLOW_OK; - gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED); - res = gst_pad_push_event (pngdec->srcpad, event); - break; - } - case GST_EVENT_EOS: - { - GST_LOG_OBJECT (pngdec, "EOS"); - gst_pngdec_libpng_clear (pngdec); - pngdec->ret = GST_FLOW_EOS; - res = gst_pad_push_event (pngdec->srcpad, event); - break; - } - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - res = gst_pngdec_sink_setcaps (pngdec, caps); - gst_event_unref (event); - break; - } - default: - res = gst_pad_push_event (pngdec->srcpad, event); - break; - } - - return res; -} - - /* Clean up the libpng structures */ static gboolean -gst_pngdec_libpng_clear (GstPngDec * pngdec) +gst_pngdec_reset (GstVideoDecoder * decoder, gboolean hard) { - png_infopp info = NULL, endinfo = NULL; - - g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE); - - GST_LOG ("cleaning up libpng structures"); - - if (pngdec->info) { - info = &pngdec->info; - } - - if (pngdec->endinfo) { - endinfo = &pngdec->endinfo; - } - - if (pngdec->png) { - png_destroy_read_struct (&(pngdec->png), info, endinfo); - pngdec->png = NULL; - pngdec->info = NULL; - pngdec->endinfo = NULL; - } - - pngdec->color_type = pngdec->height = pngdec->width = -1; - pngdec->offset = 0; - pngdec->buffer_out = NULL; - - pngdec->setup = FALSE; - - pngdec->in_timestamp = GST_CLOCK_TIME_NONE; - pngdec->in_duration = GST_CLOCK_TIME_NONE; + gst_pngdec_stop (decoder); + gst_pngdec_start (decoder); return TRUE; } @@ -831,9 +395,6 @@ gst_pngdec_libpng_init (GstPngDec * pngdec) { g_return_val_if_fail (GST_IS_PNGDEC (pngdec), FALSE); - if (pngdec->setup) - return TRUE; - GST_LOG ("init libpng structures"); /* initialize png struct stuff */ @@ -851,7 +412,8 @@ gst_pngdec_libpng_init (GstPngDec * pngdec) if (pngdec->endinfo == NULL) goto endinfo_failed; - pngdec->setup = TRUE; + png_set_progressive_read_fn (pngdec->png, pngdec, + user_info_callback, user_endrow_callback, user_end_callback); return TRUE; @@ -864,138 +426,54 @@ init_failed: } info_failed: { - gst_pngdec_libpng_clear (pngdec); GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL), ("Failed to initialize info structure")); return FALSE; } endinfo_failed: { - gst_pngdec_libpng_clear (pngdec); GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL), ("Failed to initialize endinfo structure")); return FALSE; } } -static GstStateChangeReturn -gst_pngdec_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstPngDec *pngdec; - - pngdec = GST_PNGDEC (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_pngdec_libpng_init (pngdec); - pngdec->need_newsegment = TRUE; - pngdec->framed = FALSE; - pngdec->ret = GST_FLOW_OK; - gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret != GST_STATE_CHANGE_SUCCESS) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_pngdec_libpng_clear (pngdec); - if (pngdec->pool) - gst_object_unref (pngdec->pool); - break; - default: - break; - } - - return ret; -} - -/* this function gets called when we activate ourselves in pull mode. - * We can perform random access to the resource and we start a task - * to start reading */ static gboolean -gst_pngdec_sink_activate_mode (GstPad * sinkpad, GstObject * parent, - GstPadMode mode, gboolean active) +gst_pngdec_start (GstVideoDecoder * decoder) { - GstPngDec *pngdec = GST_PNGDEC (parent); - gboolean res; + GstPngDec *pngdec = (GstPngDec *) decoder; - switch (mode) { - case GST_PAD_MODE_PULL: - if (active) { - res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_pngdec_task, - sinkpad); - } else { - res = gst_pad_stop_task (sinkpad); - } - break; - case GST_PAD_MODE_PUSH: - GST_DEBUG_OBJECT (pngdec, "activating push/chain function"); - if (active) { - pngdec->ret = GST_FLOW_OK; + gst_pngdec_libpng_init (pngdec); - /* Let libpng come back here on error */ - if (setjmp (png_jmpbuf (pngdec->png))) - goto setup_failed; - - GST_LOG_OBJECT (pngdec, "setting up progressive loading callbacks"); - png_set_progressive_read_fn (pngdec->png, pngdec, - user_info_callback, user_endrow_callback, user_end_callback); - } else { - GST_DEBUG_OBJECT (pngdec, "deactivating push/chain function"); - } - res = TRUE; - break; - default: - res = FALSE; - break; - } - return res; - -setup_failed: - { - GST_LOG_OBJECT (pngdec, "failed setting up libpng jmpbuf"); - gst_pngdec_libpng_clear (pngdec); - return FALSE; - } + return TRUE; } -/* this function is called when the pad is activated and should start - * processing data. - * - * We check if we can do random access to decide if we work push or - * pull based. - */ static gboolean -gst_pngdec_sink_activate (GstPad * sinkpad, GstObject * parent) +gst_pngdec_stop (GstVideoDecoder * decoder) { - GstQuery *query; - gboolean pull_mode; + GstPngDec *pngdec = (GstPngDec *) decoder; + png_infopp info = NULL, endinfo = NULL; - query = gst_query_new_scheduling (); + GST_LOG ("cleaning up libpng structures"); - if (!gst_pad_peer_query (sinkpad, query)) { - gst_query_unref (query); - goto activate_push; + if (pngdec->info) { + info = &pngdec->info; } - pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL); - gst_query_unref (query); - - if (!pull_mode) - goto activate_push; - - GST_DEBUG_OBJECT (sinkpad, "activating pull"); - return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); - -activate_push: - { - GST_DEBUG_OBJECT (sinkpad, "activating push"); - return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + if (pngdec->endinfo) { + endinfo = &pngdec->endinfo; } + + if (pngdec->png) { + png_destroy_read_struct (&(pngdec->png), info, endinfo); + pngdec->png = NULL; + pngdec->info = NULL; + pngdec->endinfo = NULL; + } + + pngdec->color_type = -1; + pngdec->rowbytes = 0; + pngdec->buffer_out = NULL; + + return TRUE; } diff --git a/ext/libpng/gstpngdec.h b/ext/libpng/gstpngdec.h index 391bcf9106..f5639d3294 100644 --- a/ext/libpng/gstpngdec.h +++ b/ext/libpng/gstpngdec.h @@ -1,5 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2012 Collabora Ltd. + * Author : Edward Hervey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,9 +24,7 @@ #define __GST_PNGDEC_H__ #include -#include -#include - +#include #include G_BEGIN_DECLS @@ -40,45 +40,30 @@ typedef struct _GstPngDecClass GstPngDecClass; struct _GstPngDec { - GstElement element; + GstVideoDecoder parent; - GstPad *sinkpad, *srcpad; + GstVideoCodecState *input_state; + GstVideoCodecState *output_state; + GstVideoCodecFrame *current_frame; - gboolean need_newsegment; + GstFlowReturn ret; /* Progressive */ GstBuffer *buffer_out; - GstFlowReturn ret; + png_uint_32 rowbytes; - /* Pull range */ - gint offset; - png_structp png; png_infop info; png_infop endinfo; - gboolean setup; - GstVideoInfo vinfo; - gint width; - gint height; gint color_type; - gint fps_n; - gint fps_d; - GstBufferPool *pool; - - /* Chain */ - gboolean framed; - GstClockTime in_timestamp; - GstClockTime in_duration; - - GstSegment segment; gboolean image_ready; }; struct _GstPngDecClass { - GstElementClass parent_class; + GstVideoDecoderClass parent_class; }; GType gst_pngdec_get_type(void); diff --git a/ext/libpng/gstpngenc.c b/ext/libpng/gstpngenc.c index 2c5e71bb84..13fee1ed84 100644 --- a/ext/libpng/gstpngenc.c +++ b/ext/libpng/gstpngenc.c @@ -4,6 +4,9 @@ * Filter: * Copyright (C) 2000 Donald A. Graft * + * Copyright (C) 2012 Collabora Ltd. + * Author : Edward Hervey + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -27,6 +30,7 @@ #include #include #include "gstpngenc.h" +#include #include GST_DEBUG_CATEGORY_STATIC (pngenc_debug); @@ -39,15 +43,13 @@ enum LAST_SIGNAL }; -#define DEFAULT_SNAPSHOT FALSE -/* #define DEFAULT_NEWMEDIA FALSE */ +#define DEFAULT_SNAPSHOT TRUE #define DEFAULT_COMPRESSION_LEVEL 6 enum { ARG_0, ARG_SNAPSHOT, -/* ARG_NEWMEDIA, */ ARG_COMPRESSION_LEVEL }; @@ -61,25 +63,25 @@ GST_STATIC_PAD_TEMPLATE ("src", ); static GstStaticPadTemplate pngenc_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBA, RGB, GRAY8, GRAY16_BE }")) + GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_GRAY8 ";" GST_VIDEO_CAPS_GRAY16 ("BIG_ENDIAN")) ); -/* static GstElementClass *parent_class = NULL; */ - -G_DEFINE_TYPE (GstPngEnc, gst_pngenc, GST_TYPE_ELEMENT); +GST_BOILERPLATE (GstPngEnc, gst_pngenc, GstVideoEncoder, + GST_TYPE_VIDEO_ENCODER); static void gst_pngenc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_pngenc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstFlowReturn gst_pngenc_chain (GstPad * pad, GstObject * parent, - GstBuffer * data); -static gboolean gst_pngenc_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); +static GstFlowReturn gst_pngenc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame); +static gboolean gst_pngenc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state); static void user_error_fn (png_structp png_ptr, png_const_charp error_msg) @@ -93,14 +95,31 @@ user_warning_fn (png_structp png_ptr, png_const_charp warning_msg) g_warning ("%s", warning_msg); } +static void +gst_pngenc_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_static_pad_template + (element_class, &pngenc_sink_template); + gst_element_class_add_static_pad_template + (element_class, &pngenc_src_template); + gst_element_class_set_details_simple (element_class, "PNG image encoder", + "Codec/Encoder/Image", + "Encode a video frame to a .png image", + "Jeremy SIMON "); +} + static void gst_pngenc_class_init (GstPngEncClass * klass) { GObjectClass *gobject_class; - GstElementClass *element_class; + GstVideoEncoderClass *venc_class; gobject_class = (GObjectClass *) klass; - element_class = (GstElementClass *) klass; + venc_class = (GstVideoEncoderClass *) klass; + + parent_class = g_type_class_peek_parent (klass); gobject_class->get_property = gst_pngenc_get_property; gobject_class->set_property = gst_pngenc_set_property; @@ -110,11 +129,6 @@ gst_pngenc_class_init (GstPngEncClass * klass) "Send EOS after encoding a frame, useful for snapshots", DEFAULT_SNAPSHOT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -/* g_object_class_install_property (gobject_class, ARG_NEWMEDIA, */ -/* g_param_spec_boolean ("newmedia", "newmedia", */ -/* "Send new media discontinuity after encoding each frame", */ -/* DEFAULT_NEWMEDIA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); */ - g_object_class_install_property (gobject_class, ARG_COMPRESSION_LEVEL, g_param_spec_uint ("compression-level", "compression-level", "PNG compression level", @@ -122,110 +136,71 @@ gst_pngenc_class_init (GstPngEncClass * klass) DEFAULT_COMPRESSION_LEVEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - gst_element_class_add_pad_template - (element_class, gst_static_pad_template_get (&pngenc_sink_template)); - gst_element_class_add_pad_template - (element_class, gst_static_pad_template_get (&pngenc_src_template)); - gst_element_class_set_static_metadata (element_class, "PNG image encoder", - "Codec/Encoder/Image", - "Encode a video frame to a .png image", - "Jeremy SIMON "); + venc_class->set_format = gst_pngenc_set_format; + venc_class->handle_frame = gst_pngenc_handle_frame; GST_DEBUG_CATEGORY_INIT (pngenc_debug, "pngenc", 0, "PNG image encoder"); } static gboolean -gst_pngenc_setcaps (GstPngEnc * pngenc, GstCaps * caps) +gst_pngenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state) { - int fps_n, fps_d; - GstCaps *pcaps; - gboolean ret; - GstVideoInfo info; + GstPngEnc *pngenc; + gboolean ret = TRUE; + GstVideoInfo *info; + GstVideoCodecState *output_state; - ret = gst_video_info_from_caps (&info, caps); + pngenc = GST_PNGENC (encoder); + info = &state->info; - if (G_UNLIKELY (!ret)) - goto done; - - pngenc->info = info; - - switch (GST_VIDEO_INFO_FORMAT (&info)) { + switch (GST_VIDEO_INFO_FORMAT (info)) { case GST_VIDEO_FORMAT_RGBA: pngenc->png_color_type = PNG_COLOR_TYPE_RGBA; - pngenc->depth = 8; break; case GST_VIDEO_FORMAT_RGB: pngenc->png_color_type = PNG_COLOR_TYPE_RGB; - pngenc->depth = 8; break; case GST_VIDEO_FORMAT_GRAY8: - pngenc->png_color_type = PNG_COLOR_TYPE_GRAY; - pngenc->depth = 8; - break; case GST_VIDEO_FORMAT_GRAY16_BE: pngenc->png_color_type = PNG_COLOR_TYPE_GRAY; - pngenc->depth = 16; break; default: ret = FALSE; goto done; } - pngenc->width = GST_VIDEO_INFO_WIDTH (&info); - pngenc->height = GST_VIDEO_INFO_HEIGHT (&info); - fps_n = GST_VIDEO_INFO_FPS_N (&info); - fps_d = GST_VIDEO_INFO_FPS_D (&info); - - if (G_UNLIKELY (pngenc->width < 16 || pngenc->width > 1000000 || - pngenc->height < 16 || pngenc->height > 1000000)) { - ret = FALSE; - goto done; + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_GRAY16_BE: + pngenc->depth = 16; + break; + default: /* GST_VIDEO_FORMAT_RGB and GST_VIDEO_FORMAT_GRAY8 */ + pngenc->depth = 8; + break; } - pcaps = gst_caps_new_simple ("image/png", - "width", G_TYPE_INT, pngenc->width, - "height", G_TYPE_INT, pngenc->height, - "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL); + if (pngenc->input_state) + gst_video_codec_state_unref (pngenc->input_state); + pngenc->input_state = gst_video_codec_state_ref (state); - ret = gst_pad_set_caps (pngenc->srcpad, pcaps); + output_state = + gst_video_encoder_set_output_state (encoder, + gst_caps_new_simple ("image/png", NULL), state); + gst_video_codec_state_unref (output_state); - gst_caps_unref (pcaps); - - /* Fall-through. */ done: - if (G_UNLIKELY (!ret)) { - pngenc->width = 0; - pngenc->height = 0; - } return ret; } static void -gst_pngenc_init (GstPngEnc * pngenc) +gst_pngenc_init (GstPngEnc * pngenc, GstPngEncClass * g_class) { - /* sinkpad */ - pngenc->sinkpad = gst_pad_new_from_static_template - (&pngenc_sink_template, "sink"); - gst_pad_set_chain_function (pngenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_pngenc_chain)); - gst_pad_set_event_function (pngenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_pngenc_sink_event)); - gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->sinkpad); - - /* srcpad */ - pngenc->srcpad = gst_pad_new_from_static_template - (&pngenc_src_template, "src"); - gst_pad_use_fixed_caps (pngenc->srcpad); - gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->srcpad); - /* init settings */ pngenc->png_struct_ptr = NULL; pngenc->png_info_ptr = NULL; pngenc->snapshot = DEFAULT_SNAPSHOT; -/* pngenc->newmedia = FALSE; */ pngenc->compression_level = DEFAULT_COMPRESSION_LEVEL; } @@ -238,13 +213,10 @@ static void user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length) { GstPngEnc *pngenc; - GstMapInfo map; pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr); - gst_buffer_map (pngenc->buffer_out, &map, GST_MAP_WRITE); - if (pngenc->written + length >= map.size) { - gst_buffer_unmap (pngenc->buffer_out, &map); + if (pngenc->written + length >= GST_BUFFER_SIZE (pngenc->buffer_out)) { GST_ERROR_OBJECT (pngenc, "output buffer bigger than the input buffer!?"); png_error (png_ptr, "output buffer bigger than the input buffer!?"); @@ -252,66 +224,37 @@ user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length) return; } - GST_DEBUG_OBJECT (pngenc, "writing %u bytes", (guint) length); - - memcpy (map.data + pngenc->written, data, length); - gst_buffer_unmap (pngenc->buffer_out, &map); + memcpy (GST_BUFFER_DATA (pngenc->buffer_out) + pngenc->written, data, length); pngenc->written += length; } static GstFlowReturn -gst_pngenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +gst_pngenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) { GstPngEnc *pngenc; gint row_index; png_byte **row_pointers; GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *encoded_buf = NULL; - GstVideoFrame frame; + GstVideoInfo *info; - pngenc = GST_PNGENC (parent); + pngenc = GST_PNGENC (encoder); + info = &pngenc->input_state->info; GST_DEBUG_OBJECT (pngenc, "BEGINNING"); - if (G_UNLIKELY (pngenc->width <= 0 || pngenc->height <= 0)) { - ret = GST_FLOW_NOT_NEGOTIATED; - goto exit; - } - - if (!gst_video_frame_map (&frame, &pngenc->info, buf, GST_MAP_READ)) { - GST_ELEMENT_ERROR (pngenc, STREAM, FORMAT, (NULL), - ("Failed to map video frame, caps problem?")); - ret = GST_FLOW_ERROR; - goto exit; - } - /* initialize png struct stuff */ pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, user_error_fn, user_warning_fn); - if (pngenc->png_struct_ptr == NULL) { - GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL), - ("Failed to initialize png structure")); - ret = GST_FLOW_ERROR; - goto done; - } + if (pngenc->png_struct_ptr == NULL) + goto struct_init_fail; pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr); - if (!pngenc->png_info_ptr) { - png_destroy_write_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL); - GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL), - ("Failed to initialize the png info structure")); - ret = GST_FLOW_ERROR; - goto done; - } + if (!pngenc->png_info_ptr) + goto png_info_fail; /* non-0 return is from a longjmp inside of libpng */ - if (setjmp (png_jmpbuf (pngenc->png_struct_ptr)) != 0) { - png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr); - GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL), - ("returning from longjmp")); - ret = GST_FLOW_ERROR; - goto done; - } + if (setjmp (png_jmpbuf (pngenc->png_struct_ptr)) != 0) + goto longjmp_fail; png_set_filter (pngenc->png_struct_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE); @@ -319,8 +262,8 @@ gst_pngenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) png_set_IHDR (pngenc->png_struct_ptr, pngenc->png_info_ptr, - pngenc->width, - pngenc->height, + GST_VIDEO_INFO_WIDTH (info), + GST_VIDEO_INFO_HEIGHT (info), pngenc->depth, pngenc->png_color_type, PNG_INTERLACE_NONE, @@ -329,16 +272,17 @@ gst_pngenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) png_set_write_fn (pngenc->png_struct_ptr, pngenc, (png_rw_ptr) user_write_data, user_flush_data); - row_pointers = g_new (png_byte *, pngenc->height); + row_pointers = g_new (png_byte *, GST_VIDEO_INFO_HEIGHT (info)); - for (row_index = 0; row_index < pngenc->height; row_index++) { - row_pointers[row_index] = GST_VIDEO_FRAME_COMP_DATA (&frame, 0) + - (row_index * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0)); + for (row_index = 0; row_index < GST_VIDEO_INFO_HEIGHT (info); row_index++) { + row_pointers[row_index] = GST_BUFFER_DATA (frame->input_buffer) + + (row_index * GST_VIDEO_INFO_COMP_STRIDE (info, 0)); } /* allocate the output buffer */ pngenc->buffer_out = - gst_buffer_new_and_alloc (pngenc->height * pngenc->width); + gst_buffer_new_and_alloc (GST_VIDEO_INFO_HEIGHT (info) * + GST_VIDEO_INFO_COMP_STRIDE (info, 0)); pngenc->written = 0; png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr); @@ -347,71 +291,52 @@ gst_pngenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) g_free (row_pointers); - GST_DEBUG_OBJECT (pngenc, "written %d", pngenc->written); - - encoded_buf = - gst_buffer_copy_region (pngenc->buffer_out, GST_BUFFER_COPY_MEMORY, - 0, pngenc->written); - png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr); png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL); - GST_BUFFER_TIMESTAMP (encoded_buf) = GST_BUFFER_TIMESTAMP (buf); - GST_BUFFER_DURATION (encoded_buf) = GST_BUFFER_DURATION (buf); + /* Set final size and store */ + GST_BUFFER_SIZE (pngenc->buffer_out) = pngenc->written; + frame->output_buffer = pngenc->buffer_out; - if ((ret = gst_pad_push (pngenc->srcpad, encoded_buf)) != GST_FLOW_OK) + pngenc->buffer_out = NULL; + + if ((ret = gst_video_encoder_finish_frame (encoder, frame)) != GST_FLOW_OK) goto done; - if (pngenc->snapshot) { - GstEvent *event; - - GST_DEBUG_OBJECT (pngenc, "snapshot mode, sending EOS"); - /* send EOS event, since a frame has been pushed out */ - event = gst_event_new_eos (); - - gst_pad_push_event (pngenc->srcpad, event); - ret = GST_FLOW_EOS; - } + if (pngenc->snapshot) + ret = GST_FLOW_UNEXPECTED; done: - gst_video_frame_unmap (&frame); -exit: - gst_buffer_unref (buf); GST_DEBUG_OBJECT (pngenc, "END, ret:%d", ret); - if (pngenc->buffer_out != NULL) { - gst_buffer_unref (pngenc->buffer_out); - pngenc->buffer_out = NULL; - } - return ret; -} -static gboolean -gst_pngenc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstPngEnc *enc; - gboolean res; - - enc = GST_PNGENC (parent); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - res = gst_pngenc_setcaps (enc, caps); - gst_event_unref (event); - break; - } - default: - res = gst_pad_push_event (enc->srcpad, event); - break; + /* ERRORS */ +struct_init_fail: + { + GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL), + ("Failed to initialize png structure")); + return GST_FLOW_ERROR; + } + +png_info_fail: + { + png_destroy_write_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL); + GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL), + ("Failed to initialize the png info structure")); + return GST_FLOW_ERROR; + } + +longjmp_fail: + { + png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr); + GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL), + ("returning from longjmp")); + return GST_FLOW_ERROR; } - return res; } + static void gst_pngenc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -424,9 +349,6 @@ gst_pngenc_get_property (GObject * object, case ARG_SNAPSHOT: g_value_set_boolean (value, pngenc->snapshot); break; -/* case ARG_NEWMEDIA: */ -/* g_value_set_boolean (value, pngenc->newmedia); */ -/* break; */ case ARG_COMPRESSION_LEVEL: g_value_set_uint (value, pngenc->compression_level); break; @@ -449,9 +371,6 @@ gst_pngenc_set_property (GObject * object, case ARG_SNAPSHOT: pngenc->snapshot = g_value_get_boolean (value); break; -/* case ARG_NEWMEDIA: */ -/* pngenc->newmedia = g_value_get_boolean (value); */ -/* break; */ case ARG_COMPRESSION_LEVEL: pngenc->compression_level = g_value_get_uint (value); break; diff --git a/ext/libpng/gstpngenc.h b/ext/libpng/gstpngenc.h index ba306b9d98..b8150d401f 100644 --- a/ext/libpng/gstpngenc.h +++ b/ext/libpng/gstpngenc.h @@ -1,5 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2012 Collabora Ltd. + * Author : Edward Hervey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,7 +24,7 @@ #define __GST_PNGENC_H__ #include -#include +#include #include #ifdef __cplusplus @@ -41,20 +43,17 @@ typedef struct _GstPngEncClass GstPngEncClass; struct _GstPngEnc { - GstElement element; + GstVideoEncoder parent; - GstPad *sinkpad, *srcpad; + GstVideoCodecState *input_state; GstBuffer *buffer_out; guint written; png_structp png_struct_ptr; png_infop png_info_ptr; - GstVideoInfo info; gint png_color_type; gint depth; - gint width; - gint height; guint compression_level; gboolean snapshot; @@ -63,7 +62,7 @@ struct _GstPngEnc struct _GstPngEncClass { - GstElementClass parent_class; + GstVideoEncoderClass parent_class; }; GType gst_pngenc_get_type(void);