static gboolean 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 */ pngdec->png = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, user_error_fn, user_warning_fn); if (pngdec->png == NULL) goto init_failed; pngdec->info = png_create_info_struct (pngdec->png); if (pngdec->info == NULL) goto info_failed; pngdec->endinfo = png_create_info_struct (pngdec->png); if (pngdec->endinfo == NULL) goto endinfo_failed; pngdec->setup = TRUE; return TRUE; /* ERRORS */ init_failed: { GST_ELEMENT_ERROR (pngdec, LIBRARY, INIT, (NULL), ("Failed to initialize png structure")); return FALSE; } 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; } }
/* Clean up the libpng structures */ static gboolean gst_pngdec_libpng_clear (GstPngDec * pngdec) { 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->bpp = pngdec->color_type = pngdec->height = pngdec->width = -1; pngdec->offset = 0; pngdec->rowbytes = 0; pngdec->buffer_out = NULL; pngdec->setup = FALSE; pngdec->in_timestamp = GST_CLOCK_TIME_NONE; pngdec->in_duration = GST_CLOCK_TIME_NONE; return TRUE; }
static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec) { GstFlowReturn ret = GST_FLOW_OK; gint bpc = 0, color_type; png_uint_32 width, height; GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR); /* Get bits per channel */ bpc = png_get_bit_depth (pngdec->png, pngdec->info); /* Get Color type */ color_type = png_get_color_type (pngdec->png, pngdec->info); /* Add alpha channel if 16-bit depth, but not for GRAY images */ if ((bpc > 8) && (color_type != PNG_COLOR_TYPE_GRAY)) { png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE); png_set_swap (pngdec->png); } #if 0 /* We used to have this HACK to reverse the outgoing bytes, but the problem * that originally required the hack seems to have been in videoconvert's * RGBA descriptions. It doesn't seem needed now that's fixed, but might * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_bgr (pngdec->png); #endif /* Gray scale with alpha channel converted to RGB */ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { GST_LOG_OBJECT (pngdec, "converting grayscale png with alpha channel to RGB"); png_set_gray_to_rgb (pngdec->png); } /* Gray scale converted to upscaled to 8 bits */ if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (color_type == PNG_COLOR_TYPE_GRAY)) { if (bpc < 8) { /* Convert to 8 bits */ GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits"); #if PNG_LIBPNG_VER < 10400 png_set_gray_1_2_4_to_8 (pngdec->png); #else png_set_expand_gray_1_2_4_to_8 (pngdec->png); #endif } } /* Palette converted to RGB */ if (color_type == PNG_COLOR_TYPE_PALETTE) { GST_LOG_OBJECT (pngdec, "converting palette png to RGB"); png_set_palette_to_rgb (pngdec->png); } png_set_interlace_handling (pngdec->png); /* Update the info structure */ 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); GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", (gint) width, (gint) height); switch (pngdec->color_type) { case PNG_COLOR_TYPE_RGB: GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits"); 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 or 64 bits"); if (bpc == 8) format = GST_VIDEO_FORMAT_RGBA; 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 == 8) format = GST_VIDEO_FORMAT_GRAY8; else if (bpc == 16) format = GST_VIDEO_FORMAT_GRAY16_BE; break; default: break; } 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; } /* Check if output state changed */ if (pngdec->output_state) { GstVideoInfo *info = &pngdec->output_state->info; 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_video_decoder_negotiate (GST_VIDEO_DECODER (pngdec)); 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 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; g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR); /* Get bits per channel */ bpc = png_get_bit_depth (pngdec->png, pngdec->info); /* We don't handle 16 bits per color, strip down to 8 */ if (bpc == 16) { GST_LOG_OBJECT (pngdec, "this is a 16 bits per channel PNG image, strip down to 8 bits"); png_set_strip_16 (pngdec->png); } /* Get Color type */ color_type = png_get_color_type (pngdec->png, pngdec->info); #if 0 /* We used to have this HACK to reverse the outgoing bytes, but the problem * that originally required the hack seems to have been in ffmpegcolorspace's * RGBA descriptions. It doesn't seem needed now that's fixed, but might * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_bgr (pngdec->png); #endif /* Gray scale converted to RGB and upscaled to 8 bits */ if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (color_type == PNG_COLOR_TYPE_GRAY)) { GST_LOG_OBJECT (pngdec, "converting grayscale png to RGB"); png_set_gray_to_rgb (pngdec->png); if (bpc < 8) { /* Convert to 8 bits */ GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits"); png_set_gray_1_2_4_to_8 (pngdec->png); } } /* Palette converted to RGB */ if (color_type == PNG_COLOR_TYPE_PALETTE) { GST_LOG_OBJECT (pngdec, "converting palette png to RGB"); png_set_palette_to_rgb (pngdec->png); } /* Update the info structure */ 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); switch (pngdec->color_type) { case PNG_COLOR_TYPE_RGB: GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits"); pngdec->bpp = 24; break; case PNG_COLOR_TYPE_RGB_ALPHA: GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits"); pngdec->bpp = 32; break; default: GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL), ("pngdec does not support this color type")); ret = GST_FLOW_NOT_SUPPORTED; goto beach; } caps = gst_caps_new_simple ("video/x-raw-rgb", "width", G_TYPE_INT, pngdec->width, "height", G_TYPE_INT, pngdec->height, "bpp", G_TYPE_INT, pngdec->bpp, "framerate", GST_TYPE_FRACTION, pngdec->fps_n, pngdec->fps_d, NULL); 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; GST_DEBUG_OBJECT (pngdec, "our caps %" GST_PTR_FORMAT, res); gst_caps_unref (res); /* Push a newsegment event */ if (pngdec->need_newsegment) { gst_pad_push_event (pngdec->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); pngdec->need_newsegment = FALSE; } beach: return ret; }
static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec) { GstFlowReturn ret = GST_FLOW_OK; gint bpc = 0, color_type; png_uint_32 width, height; GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR); /* Get bits per channel */ bpc = png_get_bit_depth (pngdec->png, pngdec->info); /* Get Color type */ color_type = png_get_color_type (pngdec->png, pngdec->info); /* Add alpha channel if 16-bit depth, but not for GRAY images */ if ((bpc > 8) && (color_type != PNG_COLOR_TYPE_GRAY)) { png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE); png_set_swap (pngdec->png); } #if 0 /* We used to have this HACK to reverse the outgoing bytes, but the problem * that originally required the hack seems to have been in videoconvert's * RGBA descriptions. It doesn't seem needed now that's fixed, but might * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_bgr (pngdec->png); #endif /* Gray scale with alpha channel converted to RGB */ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { GST_LOG_OBJECT (pngdec, "converting grayscale png with alpha channel to RGB"); png_set_gray_to_rgb (pngdec->png); } /* Gray scale converted to upscaled to 8 bits */ if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (color_type == PNG_COLOR_TYPE_GRAY)) { if (bpc < 8) { /* Convert to 8 bits */ GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits"); #if PNG_LIBPNG_VER < 10400 png_set_gray_1_2_4_to_8 (pngdec->png); #else png_set_expand_gray_1_2_4_to_8 (pngdec->png); #endif } } /* Palette converted to RGB */ if (color_type == PNG_COLOR_TYPE_PALETTE) { GST_LOG_OBJECT (pngdec, "converting palette png to RGB"); png_set_palette_to_rgb (pngdec->png); } png_set_interlace_handling (pngdec->png); /* Update the info structure */ 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); GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", (gint) width, (gint) height); switch (pngdec->color_type) { case PNG_COLOR_TYPE_RGB: GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits"); 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 or 64 bits"); if (bpc == 8) format = GST_VIDEO_FORMAT_RGBA; 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 == 8) format = GST_VIDEO_FORMAT_GRAY8; else if (bpc == 16) format = GST_VIDEO_FORMAT_GRAY16_BE; break; default: break; } 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; } /* Check if output state changed */ if (pngdec->output_state) { GstVideoInfo *info = &pngdec->output_state->info; 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); } #ifdef HAVE_LIBPNG_1_5 if ((pngdec->color_type & PNG_COLOR_MASK_COLOR) && !(pngdec->color_type & PNG_COLOR_MASK_PALETTE) && png_get_valid (pngdec->png, pngdec->info, PNG_INFO_iCCP)) { png_charp icc_name; png_bytep icc_profile; int icc_compression_type; png_uint_32 icc_proflen = 0; png_uint_32 ret = png_get_iCCP (pngdec->png, pngdec->info, &icc_name, &icc_compression_type, &icc_profile, &icc_proflen); if ((ret & PNG_INFO_iCCP)) { gpointer gst_icc_prof = g_memdup (icc_profile, icc_proflen); GstBuffer *tagbuffer = NULL; GstSample *tagsample = NULL; GstTagList *taglist = NULL; GstStructure *info = NULL; GstCaps *caps; GST_DEBUG_OBJECT (pngdec, "extracted ICC profile '%s' length=%i", icc_name, (guint32) icc_proflen); tagbuffer = gst_buffer_new_wrapped (gst_icc_prof, icc_proflen); caps = gst_caps_new_empty_simple ("application/vnd.iccprofile"); info = gst_structure_new_empty ("application/vnd.iccprofile"); if (icc_name) gst_structure_set (info, "icc-name", G_TYPE_STRING, icc_name, NULL); tagsample = gst_sample_new (tagbuffer, caps, NULL, info); gst_buffer_unref (tagbuffer); gst_caps_unref (caps); taglist = gst_tag_list_new_empty (); gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_ATTACHMENT, tagsample, NULL); gst_sample_unref (tagsample); gst_video_decoder_merge_tags (GST_VIDEO_DECODER (pngdec), taglist, GST_TAG_MERGE_APPEND); gst_tag_list_unref (taglist); } } #endif pngdec->output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (pngdec), format, width, height, pngdec->input_state); gst_video_decoder_negotiate (GST_VIDEO_DECODER (pngdec)); GST_DEBUG ("Final %d %d", GST_VIDEO_INFO_WIDTH (&pngdec->output_state->info), GST_VIDEO_INFO_HEIGHT (&pngdec->output_state->info)); beach: return ret; }