From 8e9eb7781628725caaeaa24247676cbe3c098d65 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Fri, 6 Apr 2012 12:13:24 +0200 Subject: [PATCH] jpeg: Port jpegdec/jpegenc to base video classes Conflicts: ext/jpeg/gstjpegdec.c ext/jpeg/gstjpegdec.h ext/jpeg/gstjpegenc.c ext/jpeg/gstjpegenc.h Reverted to 0.10 versions for now, next port again. --- ext/jpeg/gstjpegdec.c | 1014 ++++++++++++++----------------------------------- ext/jpeg/gstjpegdec.h | 48 +-- ext/jpeg/gstjpegenc.c | 393 ++++++------------- ext/jpeg/gstjpegenc.h | 39 +- 4 files changed, 446 insertions(+), 1048 deletions(-) diff --git a/ext/jpeg/gstjpegdec.c b/ext/jpeg/gstjpegdec.c index 9a98221..a7e3b33 100644 --- a/ext/jpeg/gstjpegdec.c +++ b/ext/jpeg/gstjpegdec.c @@ -1,6 +1,8 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2009> Tim-Philipp Müller + * 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 @@ -66,10 +68,12 @@ static GstStaticPadTemplate gst_jpeg_dec_src_pad_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE - ("{ I420, RGB, BGR, RGBx, xRGB, BGRx, xBGR, GRAY8 }")) + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420") "; " + GST_VIDEO_CAPS_RGB "; " GST_VIDEO_CAPS_BGR "; " + GST_VIDEO_CAPS_RGBx "; " GST_VIDEO_CAPS_xRGB "; " + GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_xBGR "; " + GST_VIDEO_CAPS_GRAY8) ); - /* *INDENT-ON* */ /* FIXME: sof-marker is for IJG libjpeg 8, should be different for 6.2 */ @@ -88,30 +92,35 @@ GST_DEBUG_CATEGORY_STATIC (jpeg_dec_debug); #define GST_CAT_DEFAULT jpeg_dec_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE); +/* These macros are adapted from videotestsrc.c + * and/or gst-plugins/gst/games/gstvideoimage.c */ +#define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width)) +#define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2) +#define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2) + +#define I420_Y_OFFSET(w,h) (0) +#define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h))) +#define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2)) + +#define I420_SIZE(w,h) (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2)) + static void gst_jpeg_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_jpeg_dec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstFlowReturn gst_jpeg_dec_chain (GstPad * pad, GstObject * parent, - GstBuffer * buffer); -static GstCaps *gst_jpeg_dec_getcaps (GstPad * pad, GstCaps * filter); -static gboolean gst_jpeg_dec_sink_query (GstPad * pad, GstObject * parent, - GstQuery * query); -static gboolean gst_jpeg_dec_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_jpeg_dec_src_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static GstStateChangeReturn gst_jpeg_dec_change_state (GstElement * element, - GstStateChange transition); -static void gst_jpeg_dec_update_qos (GstJpegDec * dec, gdouble proportion, - GstClockTimeDiff diff, GstClockTime ts); -static void gst_jpeg_dec_reset_qos (GstJpegDec * dec); -static void gst_jpeg_dec_read_qos (GstJpegDec * dec, gdouble * proportion, - GstClockTime * time); - -#define gst_jpeg_dec_parent_class parent_class -G_DEFINE_TYPE (GstJpegDec, gst_jpeg_dec, GST_TYPE_ELEMENT); +static gboolean gst_jpeg_dec_set_format (GstVideoDecoder * dec, + GstVideoCodecState * state); +static gboolean gst_jpeg_dec_start (GstVideoDecoder * bdec); +static gboolean gst_jpeg_dec_stop (GstVideoDecoder * bdec); +static gboolean gst_jpeg_dec_reset (GstVideoDecoder * bdec, gboolean hard); +static GstFlowReturn gst_jpeg_dec_parse (GstVideoDecoder * bdec, + GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos); +static GstFlowReturn gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, + GstVideoCodecFrame * frame); + +GST_BOILERPLATE (GstJpegDec, gst_jpeg_dec, GstVideoDecoder, + GST_TYPE_VIDEO_DECODER); static void gst_jpeg_dec_finalize (GObject * object) @@ -120,19 +129,31 @@ gst_jpeg_dec_finalize (GObject * object) jpeg_destroy_decompress (&dec->cinfo); - g_object_unref (dec->adapter); - G_OBJECT_CLASS (parent_class)->finalize (object); } static void +gst_jpeg_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_static_pad_template (element_class, + &gst_jpeg_dec_src_pad_template); + gst_element_class_add_static_pad_template (element_class, + &gst_jpeg_dec_sink_pad_template); + gst_element_class_set_details_simple (element_class, "JPEG image decoder", + "Codec/Decoder/Image", + "Decode images from JPEG format", "Wim Taymans "); +} + +static void gst_jpeg_dec_class_init (GstJpegDecClass * klass) { - GstElementClass *gstelement_class; GObjectClass *gobject_class; + GstVideoDecoderClass *vdec_class; - gstelement_class = (GstElementClass *) klass; gobject_class = (GObjectClass *) klass; + vdec_class = (GstVideoDecoderClass *) klass; parent_class = g_type_class_peek_parent (klass); @@ -161,16 +182,12 @@ gst_jpeg_dec_class_init (GstJpegDecClass * klass) -1, G_MAXINT, JPEG_DEFAULT_MAX_ERRORS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_jpeg_dec_src_pad_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_jpeg_dec_sink_pad_template)); - gst_element_class_set_static_metadata (gstelement_class, "JPEG image decoder", - "Codec/Decoder/Image", - "Decode images from JPEG format", "Wim Taymans "); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_jpeg_dec_change_state); + vdec_class->start = gst_jpeg_dec_start; + vdec_class->stop = gst_jpeg_dec_stop; + vdec_class->reset = gst_jpeg_dec_reset; + vdec_class->parse = gst_jpeg_dec_parse; + vdec_class->set_format = gst_jpeg_dec_set_format; + vdec_class->handle_frame = gst_jpeg_dec_handle_frame; GST_DEBUG_CATEGORY_INIT (jpeg_dec_debug, "jpegdec", 0, "JPEG decoder"); GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE"); @@ -228,7 +245,7 @@ gst_jpeg_dec_post_error_or_warning (GstJpegDec * dec) ret = GST_FLOW_OK; } else if (max_errors == 0) { /* FIXME: do something more clever in "automatic mode" */ - if (dec->packetized) { + if (gst_video_decoder_get_packetized (GST_VIDEO_DECODER (dec))) { ret = (dec->error_count < 3) ? GST_FLOW_OK : GST_FLOW_ERROR; } else { ret = GST_FLOW_ERROR; @@ -255,29 +272,15 @@ static boolean gst_jpeg_dec_fill_input_buffer (j_decompress_ptr cinfo) { GstJpegDec *dec; - guint av; dec = CINFO_GET_JPEGDEC (cinfo); g_return_val_if_fail (dec != NULL, FALSE); + g_return_val_if_fail (dec->current_frame != NULL, FALSE); - av = gst_adapter_available_fast (dec->adapter); - GST_DEBUG_OBJECT (dec, "fill_input_buffer: fast av=%u, remaining=%u", av, - dec->rem_img_len); - - if (av == 0) { - GST_DEBUG_OBJECT (dec, "Out of data"); - return FALSE; - } - - if (dec->rem_img_len < av) - av = dec->rem_img_len; - dec->rem_img_len -= av; - - g_free (dec->cur_buf); - dec->cur_buf = gst_adapter_take (dec->adapter, av); - - cinfo->src->next_input_byte = dec->cur_buf; - cinfo->src->bytes_in_buffer = av; + cinfo->src->next_input_byte = + GST_BUFFER_DATA (dec->current_frame->input_buffer); + cinfo->src->bytes_in_buffer = + GST_BUFFER_SIZE (dec->current_frame->input_buffer); return TRUE; } @@ -299,7 +302,9 @@ gst_jpeg_dec_skip_input_data (j_decompress_ptr cinfo, glong num_bytes) if (num_bytes > 0 && cinfo->src->bytes_in_buffer >= num_bytes) { cinfo->src->next_input_byte += (size_t) num_bytes; cinfo->src->bytes_in_buffer -= (size_t) num_bytes; - } else if (num_bytes > 0) { + } +#if 0 + else if (num_bytes > 0) { gint available; num_bytes -= cinfo->src->bytes_in_buffer; @@ -316,6 +321,7 @@ gst_jpeg_dec_skip_input_data (j_decompress_ptr cinfo, glong num_bytes) gst_adapter_flush (dec->adapter, num_bytes); dec->rem_img_len -= num_bytes; } +#endif } static boolean @@ -355,28 +361,24 @@ METHODDEF (void) } static void -gst_jpeg_dec_init (GstJpegDec * dec) +gst_jpeg_dec_init (GstJpegDec * dec, GstJpegDecClass * class) { GST_DEBUG ("initializing"); +#if 0 /* create the sink and src pads */ dec->sinkpad = gst_pad_new_from_static_template (&gst_jpeg_dec_sink_pad_template, "sink"); gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - gst_pad_set_chain_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (gst_jpeg_dec_chain)); - gst_pad_set_event_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (gst_jpeg_dec_sink_event)); - gst_pad_set_query_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (gst_jpeg_dec_sink_query)); + gst_pad_set_setcaps_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (gst_jpeg_dec_setcaps)); dec->srcpad = gst_pad_new_from_static_template (&gst_jpeg_dec_src_pad_template, "src"); - gst_pad_set_event_function (dec->srcpad, - GST_DEBUG_FUNCPTR (gst_jpeg_dec_src_event)); gst_pad_use_fixed_caps (dec->srcpad); gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); +#endif /* setup jpeglib */ memset (&dec->cinfo, 0, sizeof (dec->cinfo)); @@ -399,10 +401,9 @@ gst_jpeg_dec_init (GstJpegDec * dec) /* init properties */ dec->idct_method = JPEG_DEFAULT_IDCT_METHOD; dec->max_errors = JPEG_DEFAULT_MAX_ERRORS; - - dec->adapter = gst_adapter_new (); } +#if 0 static gboolean gst_jpeg_dec_ensure_header (GstJpegDec * dec) { @@ -429,6 +430,7 @@ gst_jpeg_dec_ensure_header (GstJpegDec * dec) return TRUE; } +#endif static inline gboolean gst_jpeg_dec_parse_tag_has_entropy_segment (guint8 tag) @@ -438,42 +440,53 @@ gst_jpeg_dec_parse_tag_has_entropy_segment (guint8 tag) return FALSE; } -/* returns image length in bytes if parsed successfully, - * otherwise 0 if more data needed, - * if < 0 the absolute value needs to be flushed */ -static gint -gst_jpeg_dec_parse_image_data (GstJpegDec * dec) +static GstFlowReturn +gst_jpeg_dec_parse (GstVideoDecoder * bdec, GstVideoCodecFrame * frame, + GstAdapter * adapter, gboolean at_eos) { guint size; + gint ret, toadd = 0; gboolean resync; - GstAdapter *adapter = dec->adapter; - gint offset, noffset; + gint offset = 0, noffset; + GstJpegDec *dec = (GstJpegDec *) bdec; + + /* FIXME : The overhead of using scan_uint32 is massive */ size = gst_adapter_available (adapter); + GST_DEBUG ("Parsing jpeg image data (%u bytes)", size); - /* we expect at least 4 bytes, first of which start marker */ - if (gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0xffd80000, 0, 4)) - return 0; + if (at_eos) { + GST_DEBUG ("Flushing all data out"); + toadd = size; + goto have_full_frame; + } - GST_DEBUG ("Parsing jpeg image data (%u bytes)", size); + if (size < 8) + goto need_more_data; - GST_DEBUG ("Parse state: offset=%d, resync=%d, entropy len=%d", - dec->parse_offset, dec->parse_resync, dec->parse_entropy_len); + /* we expect at least 4 bytes, first of which start marker */ + ret = + gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0xffd80000, 0, + size - 4); + GST_DEBUG ("ret:%d", ret); + if (ret < 0) + goto need_more_data; - /* offset is 2 less than actual offset; - * - adapter needs at least 4 bytes for scanning, - * - start and end marker ensure at least that much - */ - /* resume from state offset */ - offset = dec->parse_offset; + if (ret) { + gst_adapter_flush (adapter, ret); + size -= ret; + } while (1) { guint frame_len; guint32 value; + GST_DEBUG ("offset:%d, size:%d", offset, size); + noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, offset, size - offset, &value); + /* lost sync if 0xff marker not where expected */ if ((resync = (noffset != offset))) { GST_DEBUG ("Lost sync at 0x%08x, resyncing", offset + 2); @@ -501,13 +514,17 @@ gst_jpeg_dec_parse_image_data (GstJpegDec * dec) /* clear parse state */ dec->parse_resync = FALSE; dec->parse_offset = 0; - return (offset + 4); - } else if (value == 0xd8) { + toadd = offset + 4; + goto have_full_frame; + } + if (value == 0xd8) { /* Skip this frame if we found another SOI marker */ GST_DEBUG ("0x%08x: SOI marker before EOI, skipping", offset + 2); dec->parse_resync = FALSE; dec->parse_offset = 0; - return -(offset + 2); + /* FIXME : Need to skip data */ + toadd -= offset + 2; + goto have_full_frame; } @@ -532,9 +549,14 @@ gst_jpeg_dec_parse_image_data (GstJpegDec * dec) if (gst_jpeg_dec_parse_tag_has_entropy_segment (value)) { guint eseglen = dec->parse_entropy_len; - GST_DEBUG ("0x%08x: finding entropy segment length", offset + 2); + GST_DEBUG ("0x%08x: finding entropy segment length (eseglen:%d)", + offset + 2, eseglen); + if (size < offset + 2 + frame_len + eseglen) + goto need_more_data; noffset = offset + 2 + frame_len + dec->parse_entropy_len; while (1) { + GST_DEBUG ("noffset:%d, size:%d, size - noffset:%d", + noffset, size, size - noffset); noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, noffset, size - noffset, &value); if (noffset < 0) { @@ -568,18 +590,23 @@ gst_jpeg_dec_parse_image_data (GstJpegDec * dec) GST_DEBUG ("found sync at 0x%x", offset + 2); } + /* Add current data to output buffer */ + toadd += frame_len + 2; offset += frame_len + 2; } - /* EXITS */ need_more_data: - { - dec->parse_offset = offset; - dec->parse_resync = resync; - return 0; - } + if (toadd) + gst_video_decoder_add_to_frame (bdec, toadd); + return GST_VIDEO_DECODER_FLOW_NEED_DATA; + +have_full_frame: + if (toadd) + gst_video_decoder_add_to_frame (bdec, toadd); + return gst_video_decoder_have_frame (bdec); } + /* shamelessly ripped from jpegutils.c in mjpegtools */ static void add_huff_table (j_decompress_ptr dinfo, @@ -703,75 +730,26 @@ guarantee_huff_tables (j_decompress_ptr dinfo) } static gboolean -gst_jpeg_dec_setcaps (GstJpegDec * dec, GstCaps * caps) +gst_jpeg_dec_set_format (GstVideoDecoder * dec, GstVideoCodecState * state) { - GstStructure *s; - const GValue *framerate; - - s = gst_caps_get_structure (caps, 0); - - if ((framerate = gst_structure_get_value (s, "framerate")) != NULL) { - dec->in_fps_n = gst_value_get_fraction_numerator (framerate); - dec->in_fps_d = gst_value_get_fraction_denominator (framerate); - dec->packetized = TRUE; - GST_DEBUG ("got framerate of %d/%d fps => packetized mode", - dec->in_fps_n, dec->in_fps_d); - } - - /* do not extract width/height here. we do that in the chain - * function on a per-frame basis (including the line[] array - * setup) */ + GstJpegDec *jpeg = GST_JPEG_DEC (dec); + GstVideoInfo *info = &state->info; + + /* FIXME : previously jpegdec would handled input as packetized + * if the framerate was present. Here we consider it packetized if + * the fps is != 1/1 */ + if (GST_VIDEO_INFO_FPS_N (info) != 1 && GST_VIDEO_INFO_FPS_D (info) != 1) + gst_video_decoder_set_packetized (dec, TRUE); + else + gst_video_decoder_set_packetized (dec, FALSE); - /* But we can take the framerate values and set them on the src pad */ + if (jpeg->input_state) + gst_video_codec_state_unref (jpeg->input_state); + jpeg->input_state = gst_video_codec_state_ref (state); return TRUE; } -static GstCaps * -gst_jpeg_dec_getcaps (GstPad * pad, GstCaps * filter) -{ - GstJpegDec *dec; - GstCaps *caps; - GstPad *peer; - GstCaps *templ_caps; - - dec = GST_JPEG_DEC (GST_OBJECT_PARENT (pad)); - - if (gst_pad_has_current_caps (pad)) - return gst_pad_get_current_caps (pad); - - peer = gst_pad_get_peer (dec->srcpad); - - templ_caps = gst_pad_get_pad_template_caps (pad); - - if (peer) { - GstCaps *peer_caps; - GstStructure *s; - guint i, n; - - peer_caps = gst_pad_query_caps (peer, filter); - - /* Translate peercaps to image/jpeg */ - peer_caps = gst_caps_make_writable (peer_caps); - n = gst_caps_get_size (peer_caps); - for (i = 0; i < n; i++) { - s = gst_caps_get_structure (peer_caps, i); - - gst_structure_set_name (s, "image/jpeg"); - } - - caps = gst_caps_intersect_full (peer_caps, templ_caps, - GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (peer_caps); - gst_caps_unref (templ_caps); - gst_object_unref (peer); - } else { - caps = templ_caps; - } - - return caps; -} - /* yuk */ static void @@ -810,7 +788,7 @@ gst_jpeg_dec_ensure_buffers (GstJpegDec * dec, guint maxrowbytes) { gint i; - if (G_LIKELY (dec->idr_width_allocated >= maxrowbytes)) + if (G_LIKELY (dec->idr_width_allocated == maxrowbytes)) return TRUE; /* FIXME: maybe just alloc one or three blocks altogether? */ @@ -831,28 +809,19 @@ gst_jpeg_dec_ensure_buffers (GstJpegDec * dec, guint maxrowbytes) } static void -gst_jpeg_dec_decode_grayscale (GstJpegDec * dec, GstVideoFrame * frame) +gst_jpeg_dec_decode_grayscale (GstJpegDec * dec, guchar * base[1], + guint width, guint height, guint pstride, guint rstride) { guchar *rows[16]; guchar **scanarray[1] = { rows }; gint i, j, k; gint lines; - guint8 *base[1]; - gint width, height; - gint pstride, rstride; GST_DEBUG_OBJECT (dec, "indirect decoding of grayscale"); - width = GST_VIDEO_FRAME_WIDTH (frame); - height = GST_VIDEO_FRAME_HEIGHT (frame); - if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width)))) return; - base[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0); - pstride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0); - rstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); - memcpy (rows, dec->idr_y, 16 * sizeof (gpointer)); i = 0; @@ -876,30 +845,19 @@ gst_jpeg_dec_decode_grayscale (GstJpegDec * dec, GstVideoFrame * frame) } static void -gst_jpeg_dec_decode_rgb (GstJpegDec * dec, GstVideoFrame * frame) +gst_jpeg_dec_decode_rgb (GstJpegDec * dec, guchar * base[3], + guint width, guint height, guint pstride, guint rstride) { guchar *r_rows[16], *g_rows[16], *b_rows[16]; guchar **scanarray[3] = { r_rows, g_rows, b_rows }; gint i, j, k; gint lines; - guint8 *base[3]; - guint pstride, rstride; - gint width, height; GST_DEBUG_OBJECT (dec, "indirect decoding of RGB"); - width = GST_VIDEO_FRAME_WIDTH (frame); - height = GST_VIDEO_FRAME_HEIGHT (frame); - if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width)))) return; - for (i = 0; i < 3; i++) - base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i); - - pstride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0); - rstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); - memcpy (r_rows, dec->idr_y, 16 * sizeof (gpointer)); memcpy (g_rows, dec->idr_u, 16 * sizeof (gpointer)); memcpy (b_rows, dec->idr_v, 16 * sizeof (gpointer)); @@ -929,35 +887,20 @@ gst_jpeg_dec_decode_rgb (GstJpegDec * dec, GstVideoFrame * frame) } static void -gst_jpeg_dec_decode_indirect (GstJpegDec * dec, GstVideoFrame * frame, - gint r_v, gint r_h, gint comp) +gst_jpeg_dec_decode_indirect (GstJpegDec * dec, guchar * base[3], + guchar * last[3], guint width, guint height, gint r_v, gint r_h, gint comp) { guchar *y_rows[16], *u_rows[16], *v_rows[16]; guchar **scanarray[3] = { y_rows, u_rows, v_rows }; gint i, j, k; gint lines; - guchar *base[3], *last[3]; - gint stride[3]; - gint width, height; GST_DEBUG_OBJECT (dec, "unadvantageous width or r_h, taking slow route involving memcpy"); - width = GST_VIDEO_FRAME_WIDTH (frame); - height = GST_VIDEO_FRAME_HEIGHT (frame); - if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width)))) return; - for (i = 0; i < 3; i++) { - base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i); - stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (frame, i); - /* make sure we don't make jpeglib write beyond our buffer, - * which might happen if (height % (r_v*DCTSIZE)) != 0 */ - last[i] = base[i] + (GST_VIDEO_FRAME_COMP_STRIDE (frame, i) * - (GST_VIDEO_FRAME_COMP_HEIGHT (frame, i) - 1)); - } - memcpy (y_rows, dec->idr_y, 16 * sizeof (gpointer)); memcpy (u_rows, dec->idr_u, 16 * sizeof (gpointer)); memcpy (v_rows, dec->idr_v, 16 * sizeof (gpointer)); @@ -976,30 +919,30 @@ gst_jpeg_dec_decode_indirect (GstJpegDec * dec, GstVideoFrame * frame, if (G_LIKELY (lines > 0)) { for (j = 0, k = 0; j < (r_v * DCTSIZE); j += r_v, k++) { if (G_LIKELY (base[0] <= last[0])) { - memcpy (base[0], y_rows[j], stride[0]); - base[0] += stride[0]; + memcpy (base[0], y_rows[j], I420_Y_ROWSTRIDE (width)); + base[0] += I420_Y_ROWSTRIDE (width); } if (r_v == 2) { if (G_LIKELY (base[0] <= last[0])) { - memcpy (base[0], y_rows[j + 1], stride[0]); - base[0] += stride[0]; + memcpy (base[0], y_rows[j + 1], I420_Y_ROWSTRIDE (width)); + base[0] += I420_Y_ROWSTRIDE (width); } } if (G_LIKELY (base[1] <= last[1] && base[2] <= last[2])) { if (r_h == 2) { - memcpy (base[1], u_rows[k], stride[1]); - memcpy (base[2], v_rows[k], stride[2]); + memcpy (base[1], u_rows[k], I420_U_ROWSTRIDE (width)); + memcpy (base[2], v_rows[k], I420_V_ROWSTRIDE (width)); } else if (r_h == 1) { - hresamplecpy1 (base[1], u_rows[k], stride[1]); - hresamplecpy1 (base[2], v_rows[k], stride[2]); + hresamplecpy1 (base[1], u_rows[k], I420_U_ROWSTRIDE (width)); + hresamplecpy1 (base[2], v_rows[k], I420_V_ROWSTRIDE (width)); } else { /* FIXME: implement (at least we avoid crashing by doing nothing) */ } } if (r_v == 2 || (k & 1) != 0) { - base[1] += stride[1]; - base[2] += stride[2]; + base[1] += I420_U_ROWSTRIDE (width); + base[2] += I420_V_ROWSTRIDE (width); } } } else { @@ -1008,8 +951,27 @@ gst_jpeg_dec_decode_indirect (GstJpegDec * dec, GstVideoFrame * frame, } } +#ifndef GST_DISABLE_GST_DEBUG +static inline void +dump_lines (guchar * base[3], guchar ** line[3], int v_samp0, int width) +{ + int j; + + for (j = 0; j < (v_samp0 * DCTSIZE); ++j) { + GST_LOG ("[%02d] %5d %5d %5d", j, + (line[0][j] >= base[0]) ? + (int) (line[0][j] - base[0]) / I420_Y_ROWSTRIDE (width) : -1, + (line[1][j] >= base[1]) ? + (int) (line[1][j] - base[1]) / I420_U_ROWSTRIDE (width) : -1, + (line[2][j] >= base[2]) ? + (int) (line[2][j] - base[2]) / I420_V_ROWSTRIDE (width) : -1); + } +} +#endif + static GstFlowReturn -gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame) +gst_jpeg_dec_decode_direct (GstJpegDec * dec, guchar * base[3], + guchar * last[3], guint width, guint height) { guchar **line[3]; /* the jpeg line buffer */ guchar *y[4 * DCTSIZE] = { NULL, }; /* alloc enough for the lines */ @@ -1017,9 +979,6 @@ gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame) guchar *v[4 * DCTSIZE] = { NULL, }; gint i, j; gint lines, v_samp[3]; - guchar *base[3], *last[3]; - gint stride[3]; - guint height; line[0] = y; line[1] = u; @@ -1032,44 +991,35 @@ gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame) if (G_UNLIKELY (v_samp[0] > 2 || v_samp[1] > 2 || v_samp[2] > 2)) goto format_not_supported; - height = GST_VIDEO_FRAME_HEIGHT (frame); - - for (i = 0; i < 3; i++) { - base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i); - stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (frame, i); - /* make sure we don't make jpeglib write beyond our buffer, - * which might happen if (height % (r_v*DCTSIZE)) != 0 */ - last[i] = base[i] + (GST_VIDEO_FRAME_COMP_STRIDE (frame, i) * - (GST_VIDEO_FRAME_COMP_HEIGHT (frame, i) - 1)); - } - /* let jpeglib decode directly into our final buffer */ GST_DEBUG_OBJECT (dec, "decoding directly into output buffer"); for (i = 0; i < height; i += v_samp[0] * DCTSIZE) { for (j = 0; j < (v_samp[0] * DCTSIZE); ++j) { /* Y */ - line[0][j] = base[0] + (i + j) * stride[0]; + line[0][j] = base[0] + (i + j) * I420_Y_ROWSTRIDE (width); if (G_UNLIKELY (line[0][j] > last[0])) line[0][j] = last[0]; /* U */ if (v_samp[1] == v_samp[0]) { - line[1][j] = base[1] + ((i + j) / 2) * stride[1]; + line[1][j] = base[1] + ((i + j) / 2) * I420_U_ROWSTRIDE (width); } else if (j < (v_samp[1] * DCTSIZE)) { - line[1][j] = base[1] + ((i / 2) + j) * stride[1]; + line[1][j] = base[1] + ((i / 2) + j) * I420_U_ROWSTRIDE (width); } if (G_UNLIKELY (line[1][j] > last[1])) line[1][j] = last[1]; /* V */ if (v_samp[2] == v_samp[0]) { - line[2][j] = base[2] + ((i + j) / 2) * stride[2]; + line[2][j] = base[2] + ((i + j) / 2) * I420_V_ROWSTRIDE (width); } else if (j < (v_samp[2] * DCTSIZE)) { - line[2][j] = base[2] + ((i / 2) + j) * stride[2]; + line[2][j] = base[2] + ((i / 2) + j) * I420_V_ROWSTRIDE (width); } if (G_UNLIKELY (line[2][j] > last[2])) line[2][j] = last[2]; } + /* dump_lines (base, line, v_samp[0], width); */ + lines = jpeg_read_raw_data (&dec->cinfo, line, v_samp[0] * DCTSIZE); if (G_UNLIKELY (!lines)) { GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0"); @@ -1087,278 +1037,79 @@ format_not_supported: } static void -gst_jpeg_dec_update_qos (GstJpegDec * dec, gdouble proportion, - GstClockTimeDiff diff, GstClockTime ts) -{ - GST_OBJECT_LOCK (dec); - dec->proportion = proportion; - if (G_LIKELY (ts != GST_CLOCK_TIME_NONE)) { - if (G_UNLIKELY (diff > dec->qos_duration)) - dec->earliest_time = ts + 2 * diff + dec->qos_duration; - else - dec->earliest_time = ts + diff; - } else { - dec->earliest_time = GST_CLOCK_TIME_NONE; - } - GST_OBJECT_UNLOCK (dec); -} - -static void -gst_jpeg_dec_reset_qos (GstJpegDec * dec) -{ - gst_jpeg_dec_update_qos (dec, 0.5, 0, GST_CLOCK_TIME_NONE); -} - -static void -gst_jpeg_dec_read_qos (GstJpegDec * dec, gdouble * proportion, - GstClockTime * time) -{ - GST_OBJECT_LOCK (dec); - *proportion = dec->proportion; - *time = dec->earliest_time; - GST_OBJECT_UNLOCK (dec); -} - -/* Perform qos calculations before decoding the next frame. Returns TRUE if the - * frame should be decoded, FALSE if the frame can be dropped entirely */ -static gboolean -gst_jpeg_dec_do_qos (GstJpegDec * dec, GstClockTime timestamp) -{ - GstClockTime qostime, earliest_time; - gdouble proportion; - - /* no timestamp, can't do QoS => decode frame */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) { - GST_LOG_OBJECT (dec, "invalid timestamp, can't do QoS, decode frame"); - return TRUE; - } - - /* get latest QoS observation values */ - gst_jpeg_dec_read_qos (dec, &proportion, &earliest_time); - - /* skip qos if we have no observation (yet) => decode frame */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) { - GST_LOG_OBJECT (dec, "no observation yet, decode frame"); - return TRUE; - } - - /* qos is done on running time */ - qostime = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME, - timestamp); - - /* see how our next timestamp relates to the latest qos timestamp */ - GST_LOG_OBJECT (dec, "qostime %" GST_TIME_FORMAT ", earliest %" - GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time)); - - if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) { - GST_DEBUG_OBJECT (dec, "we are late, drop frame"); - return FALSE; - } - - GST_LOG_OBJECT (dec, "decode frame"); - return TRUE; -} - -static gboolean -gst_jpeg_dec_buffer_pool (GstJpegDec * dec, GstCaps * caps) -{ - GstQuery *query; - GstBufferPool *pool; - guint size, min, max; - GstStructure *config; - static GstAllocationParams params = { 0, 0, 0, 15, }; - - GST_DEBUG_OBJECT (dec, "setting up bufferpool"); - - /* 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, "peer query failed, using defaults"); - } - - 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, dec->info.size); - } else { - pool = NULL; - size = dec->info.size; - min = max = 0; - } - gst_query_unref (query); - - if (pool == NULL) { - /* we did not get a pool, make one ourselves then */ - pool = gst_buffer_pool_new (); - } - - config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set_params (config, caps, size, min, max); - gst_buffer_pool_config_set_allocator (config, NULL, ¶ms); - /* and store */ - gst_buffer_pool_set_config (pool, config); - - if (dec->pool) { - gst_buffer_pool_set_active (dec->pool, FALSE); - gst_object_unref (dec->pool); - } - dec->pool = pool; - - /* and activate */ - gst_buffer_pool_set_active (pool, TRUE); - - return TRUE; -} - -static gboolean gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc) { - GstCaps *caps; + GstVideoCodecState *outstate; + GstVideoInfo *info; GstVideoFormat format; - GstVideoInfo info; - if (G_UNLIKELY (width == dec->info.width && height == dec->info.height && - dec->in_fps_n == dec->info.fps_n && dec->in_fps_d == dec->info.fps_d - && clrspc == dec->clrspc)) - return TRUE; + switch (clrspc) { + case JCS_RGB: + format = GST_VIDEO_FORMAT_RGB; + break; + case JCS_GRAYSCALE: + format = GST_VIDEO_FORMAT_GRAY8; + break; + default: + format = GST_VIDEO_FORMAT_I420; + break; + } - gst_video_info_init (&info); + /* Compare to currently configured output state */ + outstate = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (dec)); + if (outstate) { + info = &outstate->info; - /* framerate == 0/1 is a still frame */ - if (dec->in_fps_d == 0) { - info.fps_n = 0; - info.fps_d = 1; - } else { - info.fps_n = dec->in_fps_n; - info.fps_d = dec->in_fps_d; + if (width == GST_VIDEO_INFO_WIDTH (info) && + height == GST_VIDEO_INFO_HEIGHT (info) && + format == GST_VIDEO_INFO_FORMAT (info)) { + gst_video_codec_state_unref (outstate); + return; + } + gst_video_codec_state_unref (outstate); } - /* calculate or assume an average frame duration for QoS purposes */ - GST_OBJECT_LOCK (dec); - if (info.fps_n != 0) { - dec->qos_duration = - gst_util_uint64_scale (GST_SECOND, info.fps_d, info.fps_n); - dec->duration = dec->qos_duration; - } else { - /* if not set just use 25fps */ - dec->qos_duration = gst_util_uint64_scale (GST_SECOND, 1, 25); - dec->duration = GST_CLOCK_TIME_NONE; - } - GST_OBJECT_UNLOCK (dec); + outstate = + gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec), format, + width, height, dec->input_state); + + gst_video_codec_state_unref (outstate); if (dec->cinfo.jpeg_color_space == JCS_RGB) { - gint i; - GstCaps *allowed_caps; - GstVideoInfo tmpinfo; - - GST_DEBUG_OBJECT (dec, "selecting RGB format"); - /* retrieve allowed caps, and find the first one that reasonably maps - * to the parameters of the colourspace */ - caps = gst_pad_get_allowed_caps (dec->srcpad); - if (!caps) { - GST_DEBUG_OBJECT (dec, "... but no peer, using template caps"); - /* need to copy because get_allowed_caps returns a ref, - * and get_pad_template_caps doesn't */ - caps = gst_pad_get_pad_template_caps (dec->srcpad); - } - /* avoid lists of formats, etc */ - allowed_caps = gst_caps_normalize (caps); - caps = NULL; - GST_LOG_OBJECT (dec, "allowed source caps %" GST_PTR_FORMAT, allowed_caps); - - for (i = 0; i < gst_caps_get_size (allowed_caps); i++) { - if (caps) - gst_caps_unref (caps); - caps = gst_caps_copy_nth (allowed_caps, i); - /* sigh, ds and _parse_caps need fixed caps for parsing, fixate */ - caps = gst_caps_fixate (caps); - GST_LOG_OBJECT (dec, "checking caps %" GST_PTR_FORMAT, caps); - - if (!gst_video_info_from_caps (&tmpinfo, caps)) - continue; - /* we'll settle for the first (preferred) downstream rgb format */ - if (GST_VIDEO_INFO_IS_RGB (&tmpinfo)) - break; - /* default fall-back */ - format = GST_VIDEO_FORMAT_RGB; - } - if (caps) - gst_caps_unref (caps); - gst_caps_unref (allowed_caps); + /* some format info */ + dec->offset[0] = + gst_video_format_get_component_offset (format, 0, width, height); + dec->offset[1] = + gst_video_format_get_component_offset (format, 1, width, height); + dec->offset[2] = + gst_video_format_get_component_offset (format, 2, width, height); + /* equal for all components */ + dec->stride = gst_video_format_get_row_stride (format, 0, width); + dec->inc = gst_video_format_get_pixel_stride (format, 0); } else if (dec->cinfo.jpeg_color_space == JCS_GRAYSCALE) { /* TODO is anything else then 8bit supported in jpeg? */ - format = GST_VIDEO_FORMAT_GRAY8; - } else { - /* go for plain and simple I420 */ - /* TODO other YUV cases ? */ - format = GST_VIDEO_FORMAT_I420; + dec->offset[0] = + gst_video_format_get_component_offset (format, 0, width, height); + dec->stride = gst_video_format_get_row_stride (format, 0, width); + dec->inc = gst_video_format_get_pixel_stride (format, 0); } - gst_video_info_set_format (&info, format, width, height); - caps = gst_video_info_to_caps (&info); - - GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps); GST_DEBUG_OBJECT (dec, "max_v_samp_factor=%d", dec->cinfo.max_v_samp_factor); GST_DEBUG_OBJECT (dec, "max_h_samp_factor=%d", dec->cinfo.max_h_samp_factor); - - gst_pad_set_caps (dec->srcpad, caps); - - dec->info = info; - dec->clrspc = clrspc; - - gst_jpeg_dec_buffer_pool (dec, caps); - gst_caps_unref (caps); - - return TRUE; } static GstFlowReturn -gst_jpeg_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame) { GstFlowReturn ret = GST_FLOW_OK; - GstJpegDec *dec; - GstBuffer *outbuf = NULL; - gint img_len; + GstJpegDec *dec = (GstJpegDec *) bdec; + guchar *outdata; + guchar *base[3], *last[3]; gint width, height; gint r_h, r_v; guint code, hdr_ok; - GstClockTime timestamp, duration; - GstVideoFrame frame; - - dec = GST_JPEG_DEC (parent); - - timestamp = GST_BUFFER_TIMESTAMP (buf); - duration = GST_BUFFER_DURATION (buf); - - if (GST_CLOCK_TIME_IS_VALID (timestamp)) - dec->next_ts = timestamp; - - if (GST_BUFFER_IS_DISCONT (buf)) { - GST_DEBUG_OBJECT (dec, "buffer has DISCONT flag set"); - dec->discont = TRUE; - if (!dec->packetized && gst_adapter_available (dec->adapter)) { - GST_WARNING_OBJECT (dec, "DISCONT buffer in non-packetized mode, bad"); - gst_adapter_clear (dec->adapter); - } - } - - gst_adapter_push (dec->adapter, buf); - buf = NULL; - - /* If we are non-packetized and know the total incoming size in bytes, - * just wait until we have enough before doing any processing. */ - - if (!dec->packetized && (dec->segment.format == GST_FORMAT_BYTES) && - (dec->segment.stop != -1) && - (gst_adapter_available (dec->adapter) < dec->segment.stop)) { - /* We assume that non-packetized input in bytes is *one* single jpeg image */ - GST_DEBUG ("Non-packetized mode. Got %" G_GSIZE_FORMAT " bytes, " - "need %" G_GINT64_FORMAT, gst_adapter_available (dec->adapter), - dec->segment.stop); - goto need_more_data; - } +#if 0 again: if (!gst_jpeg_dec_ensure_header (dec)) goto need_more_data; @@ -1381,25 +1132,9 @@ again: goto again; } } - - dec->rem_img_len = img_len; - - GST_LOG_OBJECT (dec, "image size = %u", img_len); - - /* QoS: if we're too late anyway, skip decoding */ - if (dec->packetized && !gst_jpeg_dec_do_qos (dec, timestamp)) - goto skip_decoding; - -#ifndef GST_DISABLE_GST_DEBUG - { - guchar data[4]; - - gst_adapter_copy (dec->adapter, data, 0, 4); - GST_LOG_OBJECT (dec, "reading header %02x %02x %02x %02x", data[0], data[1], - data[2], data[3]); - } #endif + dec->current_frame = frame; gst_jpeg_dec_fill_input_buffer (&dec->cinfo); if (setjmp (dec->jerr.setjmp_buffer)) { @@ -1497,39 +1232,42 @@ again: gst_jpeg_dec_negotiate (dec, width, height, dec->cinfo.jpeg_color_space); - ret = gst_buffer_pool_acquire_buffer (dec->pool, &outbuf, NULL); + ret = gst_video_decoder_alloc_output_frame (bdec, frame); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; - if (!gst_video_frame_map (&frame, &dec->info, outbuf, GST_MAP_READWRITE)) - goto invalid_frame; + outdata = GST_BUFFER_DATA (frame->output_buffer); GST_LOG_OBJECT (dec, "width %d, height %d", width, height); - GST_BUFFER_TIMESTAMP (outbuf) = dec->next_ts; - - if (dec->packetized && GST_CLOCK_TIME_IS_VALID (dec->next_ts)) { - if (GST_CLOCK_TIME_IS_VALID (duration)) { - /* use duration from incoming buffer for outgoing buffer */ - dec->next_ts += duration; - } else if (GST_CLOCK_TIME_IS_VALID (dec->duration)) { - duration = dec->duration; - dec->next_ts += dec->duration; - } else { - duration = GST_CLOCK_TIME_NONE; - dec->next_ts = GST_CLOCK_TIME_NONE; - } - } else { - duration = GST_CLOCK_TIME_NONE; - dec->next_ts = GST_CLOCK_TIME_NONE; - } - GST_BUFFER_DURATION (outbuf) = duration; if (dec->cinfo.jpeg_color_space == JCS_RGB) { - gst_jpeg_dec_decode_rgb (dec, &frame); + base[0] = outdata + dec->offset[0]; + base[1] = outdata + dec->offset[1]; + base[2] = outdata + dec->offset[2]; + gst_jpeg_dec_decode_rgb (dec, base, width, height, dec->inc, dec->stride); } else if (dec->cinfo.jpeg_color_space == JCS_GRAYSCALE) { - gst_jpeg_dec_decode_grayscale (dec, &frame); + base[0] = outdata + dec->offset[0]; + gst_jpeg_dec_decode_grayscale (dec, base, width, height, dec->inc, + dec->stride); } else { + /* mind the swap, jpeglib outputs blue chroma first + * ensonic: I see no swap? + */ + base[0] = outdata + I420_Y_OFFSET (width, height); + base[1] = outdata + I420_U_OFFSET (width, height); + base[2] = outdata + I420_V_OFFSET (width, height); + + /* make sure we don't make jpeglib write beyond our buffer, + * which might happen if (height % (r_v*DCTSIZE)) != 0 */ + last[0] = base[0] + (I420_Y_ROWSTRIDE (width) * (height - 1)); + last[1] = + base[1] + (I420_U_ROWSTRIDE (width) * ((GST_ROUND_UP_2 (height) / 2) - + 1)); + last[2] = + base[2] + (I420_V_ROWSTRIDE (width) * ((GST_ROUND_UP_2 (height) / 2) - + 1)); + GST_LOG_OBJECT (dec, "decompressing (reqired scanline buffer height = %u)", dec->cinfo.rec_outbuf_height); @@ -1544,10 +1282,11 @@ again: || dec->cinfo.comp_info[2].h_samp_factor != 1)) { GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, dec, "indirect decoding using extra buffer copy"); - gst_jpeg_dec_decode_indirect (dec, &frame, r_v, r_h, + gst_jpeg_dec_decode_indirect (dec, base, last, width, height, r_v, r_h, dec->cinfo.num_components); } else { - ret = gst_jpeg_dec_decode_direct (dec, &frame); + ret = gst_jpeg_dec_decode_direct (dec, base, last, width, height); + if (G_UNLIKELY (ret != GST_FLOW_OK)) goto decode_direct_failed; } @@ -1556,48 +1295,12 @@ again: GST_LOG_OBJECT (dec, "decompressing finished"); jpeg_finish_decompress (&dec->cinfo); - gst_video_frame_unmap (&frame); - - /* Clipping */ - if (dec->segment.format == GST_FORMAT_TIME) { - guint64 start, stop, clip_start, clip_stop; - - GST_LOG_OBJECT (dec, "Attempting clipping"); - - start = GST_BUFFER_TIMESTAMP (outbuf); - if (GST_BUFFER_DURATION (outbuf) == GST_CLOCK_TIME_NONE) - stop = start; - else - stop = start + GST_BUFFER_DURATION (outbuf); - - if (gst_segment_clip (&dec->segment, GST_FORMAT_TIME, - start, stop, &clip_start, &clip_stop)) { - GST_LOG_OBJECT (dec, "Clipping start to %" GST_TIME_FORMAT, - GST_TIME_ARGS (clip_start)); - GST_BUFFER_TIMESTAMP (outbuf) = clip_start; - if (GST_BUFFER_DURATION (outbuf) != GST_CLOCK_TIME_NONE) { - GST_LOG_OBJECT (dec, "Clipping duration to %" GST_TIME_FORMAT, - GST_TIME_ARGS (clip_stop - clip_start)); - GST_BUFFER_DURATION (outbuf) = clip_stop - clip_start; - } - } else - goto drop_buffer; - } - /* reset error count on successful decode */ dec->error_count = 0; - ++dec->good_count; - - GST_LOG_OBJECT (dec, "pushing buffer (ts=%" GST_TIME_FORMAT ", dur=%" - GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); - - ret = gst_pad_push (dec->srcpad, outbuf); + ret = gst_video_decoder_finish_frame (bdec, frame); -skip_decoding: done: - gst_adapter_flush (dec->adapter, dec->rem_img_len); exit: @@ -1612,10 +1315,6 @@ exit: need_more_data: { GST_LOG_OBJECT (dec, "we need more data"); - if (outbuf) { - gst_buffer_unref (outbuf); - outbuf = NULL; - } ret = GST_FLOW_OK; goto exit; } @@ -1636,10 +1335,8 @@ decode_error: gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, "Decode error #%u: %s", code, err_msg); - if (outbuf) { - gst_buffer_unref (outbuf); - outbuf = NULL; - } + gst_video_decoder_drop_frame (bdec, frame); + ret = GST_FLOW_ERROR; goto done; } @@ -1647,7 +1344,6 @@ decode_direct_failed: { /* already posted an error message */ jpeg_abort_decompress (&dec->cinfo); - gst_buffer_replace (&outbuf, NULL); goto done; } alloc_failed: @@ -1659,27 +1355,13 @@ alloc_failed: GST_DEBUG_OBJECT (dec, "failed to alloc buffer, reason %s", reason); /* Reset for next time */ jpeg_abort_decompress (&dec->cinfo); - if (ret != GST_FLOW_EOS && ret != GST_FLOW_FLUSHING && + if (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_WRONG_STATE && ret != GST_FLOW_NOT_LINKED) { gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, "Buffer allocation failed, reason: %s", reason); } goto exit; } -invalid_frame: - { - jpeg_abort_decompress (&dec->cinfo); - gst_buffer_unref (outbuf); - ret = GST_FLOW_OK; - goto exit; - } -drop_buffer: - { - GST_WARNING_OBJECT (dec, "Outgoing buffer is outside configured segment"); - gst_buffer_unref (outbuf); - ret = GST_FLOW_OK; - goto exit; - } components_not_supported: { gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, @@ -1705,102 +1387,18 @@ invalid_yuvrgbgrayscale: } static gboolean -gst_jpeg_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event) +gst_jpeg_dec_reset (GstVideoDecoder * bdec, gboolean hard) { - GstJpegDec *dec; - gboolean res; - - dec = GST_JPEG_DEC (parent); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_QOS:{ - GstQOSType type; - GstClockTimeDiff diff; - GstClockTime timestamp; - gdouble proportion; - - gst_event_parse_qos (event, &type, &proportion, &diff, ×tamp); - gst_jpeg_dec_update_qos (dec, proportion, diff, timestamp); - break; - } - default: - break; - } - - res = gst_pad_push_event (dec->sinkpad, event); - - return res; -} - -static gboolean -gst_jpeg_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - gboolean ret = TRUE, forward = TRUE; - GstJpegDec *dec = GST_JPEG_DEC (parent); - - GST_DEBUG_OBJECT (dec, "event : %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - GST_DEBUG_OBJECT (dec, "Aborting decompress"); - jpeg_abort_decompress (&dec->cinfo); - gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED); - gst_adapter_clear (dec->adapter); - g_free (dec->cur_buf); - dec->cur_buf = NULL; - dec->parse_offset = 0; - dec->parse_entropy_len = 0; - dec->parse_resync = FALSE; - gst_jpeg_dec_reset_qos (dec); - break; - case GST_EVENT_SEGMENT: - gst_event_copy_segment (event, &dec->segment); - GST_DEBUG_OBJECT (dec, "Got NEWSEGMENT %" GST_SEGMENT_FORMAT, - &dec->segment); - break; - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - ret = gst_jpeg_dec_setcaps (dec, caps); - forward = FALSE; - break; - } - default: - break; - } - - if (forward) - ret = gst_pad_push_event (dec->srcpad, event); - else - gst_event_unref (event); + GstJpegDec *dec = (GstJpegDec *) bdec; - return ret; -} + jpeg_abort_decompress (&dec->cinfo); + g_free (dec->cur_buf); + dec->cur_buf = NULL; + dec->parse_offset = 0; + dec->parse_entropy_len = 0; + dec->parse_resync = FALSE; -static gboolean -gst_jpeg_dec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - gboolean res = FALSE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CAPS: - { - GstCaps *filter, *caps; - - gst_query_parse_caps (query, &filter); - caps = gst_jpeg_dec_getcaps (pad, filter); - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - res = TRUE; - break; - } - default: - res = gst_pad_query_default (pad, parent, query); - break; - } - return res; + return TRUE; } static void @@ -1847,54 +1445,28 @@ gst_jpeg_dec_get_property (GObject * object, guint prop_id, GValue * value, } } -static GstStateChangeReturn -gst_jpeg_dec_change_state (GstElement * element, GstStateChange transition) +static gboolean +gst_jpeg_dec_start (GstVideoDecoder * bdec) { - GstStateChangeReturn ret; - GstJpegDec *dec; + GstJpegDec *dec = (GstJpegDec *) bdec; - dec = GST_JPEG_DEC (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - dec->error_count = 0; - dec->good_count = 0; - dec->in_fps_n = 0; - dec->in_fps_d = 1; - gst_video_info_init (&dec->info); - dec->clrspc = -1; - dec->packetized = FALSE; - dec->next_ts = 0; - dec->discont = TRUE; - dec->parse_offset = 0; - dec->parse_entropy_len = 0; - dec->parse_resync = FALSE; - dec->cur_buf = NULL; - gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED); - gst_jpeg_dec_reset_qos (dec); - default: - break; - } + dec->error_count = 0; + dec->parse_offset = 0; + dec->parse_entropy_len = 0; + dec->parse_resync = FALSE; + dec->cur_buf = NULL; - 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_adapter_clear (dec->adapter); - g_free (dec->cur_buf); - dec->cur_buf = NULL; - gst_jpeg_dec_free_buffers (dec); - if (dec->pool) { - gst_buffer_pool_set_active (dec->pool, FALSE); - gst_object_unref (dec->pool); - } - dec->pool = NULL; - break; - default: - break; - } + return TRUE; +} - return ret; +static gboolean +gst_jpeg_dec_stop (GstVideoDecoder * bdec) +{ + GstJpegDec *dec = (GstJpegDec *) bdec; + + g_free (dec->cur_buf); + dec->cur_buf = NULL; + gst_jpeg_dec_free_buffers (dec); + + return TRUE; } diff --git a/ext/jpeg/gstjpegdec.h b/ext/jpeg/gstjpegdec.h index 4946ee5..80beac9 100644 --- a/ext/jpeg/gstjpegdec.h +++ b/ext/jpeg/gstjpegdec.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 @@ -25,6 +27,7 @@ #include #include #include +#include #include /* this is a hack hack hack to get around jpeglib header bugs... */ @@ -64,44 +67,20 @@ struct GstJpegDecSourceMgr { * doesn't handle the N buffers in, 1 buffer out case, * but only the 1-in 1-out case */ struct _GstJpegDec { - GstElement element; - - /* pads */ - GstPad *sinkpad; - GstPad *srcpad; - - GstAdapter *adapter; + GstVideoDecoder decoder; guint8 *cur_buf; - /* TRUE if each input buffer contains a whole jpeg image */ - gboolean packetized; - - /* the (expected) timestamp of the next frame */ - guint64 next_ts; - - GstSegment segment; - - /* TRUE if the next output buffer should have the DISCONT flag set */ - gboolean discont; + /* negotiated state */ + GstVideoCodecState *input_state; + GstVideoCodecFrame *current_frame; - /* QoS stuff *//* with LOCK */ - gdouble proportion; - GstClockTime earliest_time; - GstClockTime qos_duration; - - /* input state */ - gint in_fps_n; - gint in_fps_d; - - /* negotiated output state */ - GstBufferPool *pool; - GstVideoInfo info; - GstClockTime duration; - - gint clrspc; + gint offset[3]; + gint stride; + gint inc; /* parse state */ + gboolean saw_header; gint parse_offset; gint parse_entropy_len; gint parse_resync; @@ -118,9 +97,6 @@ struct _GstJpegDec { /* number of errors since start or last successfully decoded image */ guint error_count; - /* number of successfully decoded images since start */ - guint good_count; - struct jpeg_decompress_struct cinfo; struct GstJpegDecErrorMgr jerr; struct GstJpegDecSourceMgr jsrc; @@ -133,7 +109,7 @@ struct _GstJpegDec { }; struct _GstJpegDecClass { - GstElementClass parent_class; + GstVideoDecoderClass decoder_class; }; GType gst_jpeg_dec_get_type(void); diff --git a/ext/jpeg/gstjpegenc.c b/ext/jpeg/gstjpegenc.c index 0e9ef27..d59d90b 100644 --- a/ext/jpeg/gstjpegenc.c +++ b/ext/jpeg/gstjpegenc.c @@ -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 @@ -38,6 +40,7 @@ #include "gstjpegenc.h" #include "gstjpeg.h" #include +#include /* experimental */ /* setting smoothig seems to have no effect in libjepeg @@ -69,24 +72,25 @@ enum static void gst_jpegenc_reset (GstJpegEnc * enc); static void gst_jpegenc_finalize (GObject * object); -static GstFlowReturn gst_jpegenc_chain (GstPad * pad, GstObject * parent, - GstBuffer * buf); -static gboolean gst_jpegenc_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static GstCaps *gst_jpegenc_getcaps (GstPad * pad, GstCaps * filter); -static gboolean gst_jpegenc_sink_query (GstPad * pad, GstObject * parent, - GstQuery * query); - static void gst_jpegenc_resync (GstJpegEnc * jpegenc); static void gst_jpegenc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_jpegenc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_jpegenc_change_state (GstElement * element, - GstStateChange transition); + +static gboolean gst_jpegenc_start (GstVideoEncoder * benc); +static gboolean gst_jpegenc_stop (GstVideoEncoder * benc); +static gboolean gst_jpegenc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state); +static GstFlowReturn gst_jpegenc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame); +static gboolean gst_jpegenc_propose_allocation (GstVideoEncoder * encoder, + GstQuery * query); + +/* static guint gst_jpegenc_signals[LAST_SIGNAL] = { 0 }; */ #define gst_jpegenc_parent_class parent_class -G_DEFINE_TYPE (GstJpegEnc, gst_jpegenc, GST_TYPE_ELEMENT); +G_DEFINE_TYPE (GstJpegEnc, gst_jpegenc, GST_TYPE_VIDEO_ENCODER); /* *INDENT-OFF* */ static GstStaticPadTemplate gst_jpegenc_sink_pad_template = @@ -108,15 +112,18 @@ GST_STATIC_PAD_TEMPLATE ("src", "height = (int) [ 16, 65535 ], " "framerate = (fraction) [ 0/1, MAX ]") ); - static void gst_jpegenc_class_init (GstJpegEncClass * klass) { GObjectClass *gobject_class; - GstElementClass *gstelement_class; + GstElementClass *element_class; + GstVideoEncoderClass *venc_class; gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; + element_class = (GstElementClass *) klass; + venc_class = (GstVideoEncoderClass *) klass; + + parent_class = g_type_class_peek_parent (klass); gobject_class->finalize = gst_jpegenc_finalize; gobject_class->set_property = gst_jpegenc_set_property; @@ -141,21 +148,31 @@ gst_jpegenc_class_init (GstJpegEncClass * klass) JPEG_DEFAULT_IDCT_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - gstelement_class->change_state = gst_jpegenc_change_state; - - gst_element_class_add_pad_template (gstelement_class, + gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_jpegenc_sink_pad_template)); - gst_element_class_add_pad_template (gstelement_class, + gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_jpegenc_src_pad_template)); - gst_element_class_set_static_metadata (gstelement_class, "JPEG image encoder", + gst_element_class_set_details_simple (element_class, "JPEG image encoder", "Codec/Encoder/Image", "Encode images in JPEG format", "Wim Taymans "); + venc_class->start = gst_jpegenc_start; + venc_class->stop = gst_jpegenc_stop; + venc_class->set_format = gst_jpegenc_set_format; + venc_class->handle_frame = gst_jpegenc_handle_frame; + venc_class->propose_allocation = gst_jpegenc_propose_allocation; + GST_DEBUG_CATEGORY_INIT (jpegenc_debug, "jpegenc", 0, "JPEG encoding element"); } static void +gst_jpegenc_init_destination (j_compress_ptr cinfo) +{ + GST_DEBUG ("gst_jpegenc_chain: init_destination"); +} + +static void ensure_memory (GstJpegEnc * jpegenc) { GstMemory *new_memory; @@ -193,12 +210,6 @@ ensure_memory (GstJpegEnc * jpegenc) jpegenc->jdest.free_in_buffer = new_size - old_size; } -static void -gst_jpegenc_init_destination (j_compress_ptr cinfo) -{ - GST_DEBUG ("gst_jpegenc_chain: init_destination"); -} - static boolean gst_jpegenc_flush_destination (j_compress_ptr cinfo) { @@ -224,30 +235,17 @@ gst_jpegenc_term_destination (j_compress_ptr cinfo) jpegenc->output_map.size - jpegenc->jdest.free_in_buffer); jpegenc->output_map.data = NULL; jpegenc->output_map.size = 0; + + gst_video_frame_unmap (&jpegenc->current_vframe); + + gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (jpegenc), + jpegenc->current_frame); + jpegenc->current_frame = NULL; } static void gst_jpegenc_init (GstJpegEnc * jpegenc) { - /* create the sink and src pads */ - jpegenc->sinkpad = - gst_pad_new_from_static_template (&gst_jpegenc_sink_pad_template, "sink"); - gst_pad_set_chain_function (jpegenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_jpegenc_chain)); - gst_pad_set_query_function (jpegenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_jpegenc_sink_query)); - gst_pad_set_event_function (jpegenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_jpegenc_sink_event)); - gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->sinkpad); - - jpegenc->srcpad = - gst_pad_new_from_static_template (&gst_jpegenc_src_pad_template, "src"); - gst_pad_use_fixed_caps (jpegenc->srcpad); - gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->srcpad); - - /* reset the initial video state */ - gst_video_info_init (&jpegenc->info); - /* setup jpeglib */ memset (&jpegenc->cinfo, 0, sizeof (jpegenc->cinfo)); memset (&jpegenc->jerr, 0, sizeof (jpegenc->jerr)); @@ -285,8 +283,6 @@ gst_jpegenc_reset (GstJpegEnc * enc) enc->row[i][j] = NULL; } } - - gst_video_info_init (&enc->info); } static void @@ -296,220 +292,92 @@ gst_jpegenc_finalize (GObject * object) jpeg_destroy_compress (&filter->cinfo); - G_OBJECT_CLASS (parent_class)->finalize (object); -} + if (filter->input_state) + gst_video_codec_state_unref (filter->input_state); -static GstCaps * -gst_jpegenc_getcaps (GstPad * pad, GstCaps * filter) -{ - GstJpegEnc *jpegenc = GST_JPEGENC (gst_pad_get_parent (pad)); - GstCaps *caps, *othercaps; - GstCaps *otherfilter; - GstCaps *templ; - gint i, j; - GstStructure *structure = NULL; - - /* we want to proxy properties like width, height and framerate from the - other end of the element */ - if (filter) { - otherfilter = gst_caps_new_empty (); - for (i = 0; i < gst_caps_get_size (filter); i++) { - GstStructure *s = gst_structure_copy (gst_caps_get_structure (filter, i)); - - gst_structure_set_name (s, "image/jpeg"); - - gst_caps_append_structure (otherfilter, s); - } - } else { - otherfilter = NULL; - } - othercaps = gst_pad_peer_query_caps (jpegenc->srcpad, otherfilter); - if (otherfilter) - gst_caps_unref (otherfilter); - - templ = gst_pad_get_pad_template_caps (pad); - if (othercaps == NULL || - gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) { - caps = templ; - goto done; - } - - caps = gst_caps_new_empty (); - - for (i = 0; i < gst_caps_get_size (templ); i++) { - /* pick fields from peer caps */ - for (j = 0; j < gst_caps_get_size (othercaps); j++) { - GstStructure *s = gst_caps_get_structure (othercaps, j); - const GValue *val; - - structure = gst_structure_copy (gst_caps_get_structure (templ, i)); - if ((val = gst_structure_get_value (s, "width"))) - gst_structure_set_value (structure, "width", val); - if ((val = gst_structure_get_value (s, "height"))) - gst_structure_set_value (structure, "height", val); - if ((val = gst_structure_get_value (s, "framerate"))) - gst_structure_set_value (structure, "framerate", val); - - caps = gst_caps_merge_structure (caps, structure); - } - } - - gst_caps_unref (templ); - -done: - - gst_caps_replace (&othercaps, NULL); - gst_object_unref (jpegenc); - - return caps; -} - -static gboolean -gst_jpegenc_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - gboolean res; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CAPS: - { - GstCaps *filter, *caps; - - gst_query_parse_caps (query, &filter); - caps = gst_jpegenc_getcaps (pad, filter); - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - res = TRUE; - break; - } - default: - res = gst_pad_query_default (pad, parent, query); - break; - } - return res; + G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean -gst_jpegenc_setcaps (GstJpegEnc * enc, GstCaps * caps) +gst_jpegenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state) { - GstVideoInfo info; + GstJpegEnc *enc = GST_JPEGENC (encoder); gint i; - GstCaps *othercaps; - gboolean ret; - const GstVideoFormatInfo *vinfo; - - /* get info from caps */ - if (!gst_video_info_from_caps (&info, caps)) - goto refuse_caps; + GstVideoInfo *info = &state->info; + GstVideoCodecState *output; - /* store input description */ - enc->info = info; - - vinfo = info.finfo; + if (enc->input_state) + gst_video_codec_state_unref (enc->input_state); + enc->input_state = gst_video_codec_state_ref (state); /* prepare a cached image description */ - enc->channels = 3 + (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (vinfo) ? 1 : 0); + enc->channels = GST_VIDEO_INFO_N_COMPONENTS (info); + /* ... but any alpha is disregarded in encoding */ - if (GST_VIDEO_FORMAT_INFO_IS_GRAY (vinfo)) + if (GST_VIDEO_INFO_IS_GRAY (info)) enc->channels = 1; - else - enc->channels = 3; enc->h_max_samp = 0; enc->v_max_samp = 0; for (i = 0; i < enc->channels; ++i) { - enc->cwidth[i] = GST_VIDEO_INFO_COMP_WIDTH (&info, i); - enc->cheight[i] = GST_VIDEO_INFO_COMP_HEIGHT (&info, i); - enc->inc[i] = GST_VIDEO_INFO_COMP_PSTRIDE (&info, i); - - enc->h_samp[i] = GST_ROUND_UP_4 (info.width) / enc->cwidth[i]; + enc->cwidth[i] = GST_VIDEO_INFO_COMP_WIDTH (info, i); + enc->cheight[i] = GST_VIDEO_INFO_COMP_HEIGHT (info, i); + enc->inc[i] = GST_VIDEO_INFO_COMP_PSTRIDE (info, i); + enc->h_samp[i] = + GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (info)) / enc->cwidth[i]; enc->h_max_samp = MAX (enc->h_max_samp, enc->h_samp[i]); - enc->v_samp[i] = GST_ROUND_UP_4 (info.height) / enc->cheight[i]; + enc->v_samp[i] = + GST_ROUND_UP_4 (GST_VIDEO_INFO_HEIGHT (info)) / enc->cheight[i]; enc->v_max_samp = MAX (enc->v_max_samp, enc->v_samp[i]); } /* samp should only be 1, 2 or 4 */ g_assert (enc->h_max_samp <= 4); g_assert (enc->v_max_samp <= 4); + /* now invert */ /* maximum is invariant, as one of the components should have samp 1 */ for (i = 0; i < enc->channels; ++i) { + GST_DEBUG ("%d %d", enc->h_samp[i], enc->h_max_samp); enc->h_samp[i] = enc->h_max_samp / enc->h_samp[i]; enc->v_samp[i] = enc->v_max_samp / enc->v_samp[i]; } enc->planar = (enc->inc[0] == 1 && enc->inc[1] == 1 && enc->inc[2] == 1); - othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (enc->srcpad)); - gst_caps_set_simple (othercaps, - "width", G_TYPE_INT, info.width, "height", G_TYPE_INT, info.height, NULL); - if (info.fps_d > 0) - gst_caps_set_simple (othercaps, - "framerate", GST_TYPE_FRACTION, info.fps_n, info.fps_d, NULL); - if (info.par_d > 0) - gst_caps_set_simple (othercaps, - "pixel-aspect-ratio", GST_TYPE_FRACTION, info.par_n, info.par_d, NULL); + output = + gst_video_encoder_set_output_state (encoder, + gst_caps_new_empty_simple ("image/jpeg"), state); + gst_video_codec_state_unref (output); - ret = gst_pad_set_caps (enc->srcpad, othercaps); - gst_caps_unref (othercaps); + gst_jpegenc_resync (enc); - if (ret) - gst_jpegenc_resync (enc); - - return ret; - - /* ERRORS */ -refuse_caps: - { - GST_WARNING_OBJECT (enc, "refused caps %" GST_PTR_FORMAT, caps); - return FALSE; - } -} - -static gboolean -gst_jpegenc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - gboolean res; - GstJpegEnc *enc = GST_JPEGENC (parent); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - res = gst_jpegenc_setcaps (enc, caps); - gst_event_unref (event); - break; - } - default: - res = gst_pad_event_default (pad, parent, event); - break; - } - - return res; + return TRUE; } static void gst_jpegenc_resync (GstJpegEnc * jpegenc) { + GstVideoInfo *info; gint width, height; gint i, j; - const GstVideoFormatInfo *finfo; GST_DEBUG_OBJECT (jpegenc, "resync"); - finfo = jpegenc->info.finfo; + if (!jpegenc->input_state) + return; - jpegenc->cinfo.image_width = width = GST_VIDEO_INFO_WIDTH (&jpegenc->info); - jpegenc->cinfo.image_height = height = GST_VIDEO_INFO_HEIGHT (&jpegenc->info); + info = &jpegenc->input_state->info; + + jpegenc->cinfo.image_width = width = GST_VIDEO_INFO_WIDTH (info); + jpegenc->cinfo.image_height = height = GST_VIDEO_INFO_HEIGHT (info); jpegenc->cinfo.input_components = jpegenc->channels; GST_DEBUG_OBJECT (jpegenc, "width %d, height %d", width, height); - GST_DEBUG_OBJECT (jpegenc, "format %d", - GST_VIDEO_INFO_FORMAT (&jpegenc->info)); + GST_DEBUG_OBJECT (jpegenc, "format %d", GST_VIDEO_INFO_FORMAT (info)); - if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) { + if (GST_VIDEO_INFO_IS_RGB (info)) { GST_DEBUG_OBJECT (jpegenc, "RGB"); jpegenc->cinfo.in_color_space = JCS_RGB; - } else if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) { + } else if (GST_VIDEO_INFO_IS_GRAY (info)) { GST_DEBUG_OBJECT (jpegenc, "gray"); jpegenc->cinfo.in_color_space = JCS_GRAYSCALE; } else { @@ -518,7 +386,7 @@ gst_jpegenc_resync (GstJpegEnc * jpegenc) } /* input buffer size as max output */ - jpegenc->bufsize = GST_VIDEO_INFO_SIZE (&jpegenc->info); + jpegenc->bufsize = GST_VIDEO_INFO_SIZE (info); jpeg_set_defaults (&jpegenc->cinfo); jpegenc->cinfo.raw_data_in = TRUE; /* duh, libjpeg maps RGB to YUV ... and don't expect some conversion */ @@ -554,36 +422,33 @@ gst_jpegenc_resync (GstJpegEnc * jpegenc) } static GstFlowReturn -gst_jpegenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +gst_jpegenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) { - GstFlowReturn ret; GstJpegEnc *jpegenc; guint height; guchar *base[3], *end[3]; guint stride[3]; gint i, j, k; - GstBuffer *outbuf; - GstVideoFrame frame; static GstAllocationParams params = { 0, 0, 0, 3, }; - jpegenc = GST_JPEGENC (parent); + jpegenc = GST_JPEGENC (encoder); - if (G_UNLIKELY (GST_VIDEO_INFO_FORMAT (&jpegenc->info) == - GST_VIDEO_FORMAT_UNKNOWN)) - goto not_negotiated; + GST_LOG_OBJECT (jpegenc, "got new frame"); - if (!gst_video_frame_map (&frame, &jpegenc->info, buf, GST_MAP_READ)) + if (!gst_video_frame_map (&jpegenc->current_vframe, + &jpegenc->input_state->info, frame->input_buffer, GST_MAP_READ)) goto invalid_frame; - height = GST_VIDEO_FRAME_HEIGHT (&frame); + jpegenc->current_frame = frame; - GST_LOG_OBJECT (jpegenc, "got buffer of %" G_GSIZE_FORMAT " bytes", - gst_buffer_get_size (buf)); + height = GST_VIDEO_INFO_HEIGHT (&jpegenc->input_state->info); for (i = 0; i < jpegenc->channels; i++) { - base[i] = GST_VIDEO_FRAME_COMP_DATA (&frame, i); - stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i); - end[i] = base[i] + GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i) * stride[i]; + base[i] = GST_VIDEO_FRAME_COMP_DATA (&jpegenc->current_vframe, i); + stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (&jpegenc->current_vframe, i); + end[i] = + base[i] + GST_VIDEO_FRAME_COMP_HEIGHT (&jpegenc->current_vframe, + i) * stride[i]; } jpegenc->output_mem = gst_allocator_alloc (NULL, jpegenc->bufsize, ¶ms); @@ -643,33 +508,25 @@ gst_jpegenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) jpeg_finish_compress (&jpegenc->cinfo); GST_LOG_OBJECT (jpegenc, "compressing done"); - outbuf = gst_buffer_new (); - gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1); - gst_buffer_append_memory (outbuf, jpegenc->output_mem); - jpegenc->output_mem = NULL; - - ret = gst_pad_push (jpegenc->srcpad, outbuf); - - gst_video_frame_unmap (&frame); - gst_buffer_unref (buf); + return GST_FLOW_OK; - return ret; - - /* ERRORS */ -not_negotiated: - { - GST_WARNING_OBJECT (jpegenc, "no input format set (no caps on buffer)"); - gst_buffer_unref (buf); - return GST_FLOW_NOT_NEGOTIATED; - } invalid_frame: { GST_WARNING_OBJECT (jpegenc, "invalid frame received"); - gst_buffer_unref (buf); + gst_video_encoder_finish_frame (encoder, frame); return GST_FLOW_OK; } } +static gboolean +gst_jpegenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query) +{ + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE); + + return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, + query); +} + static void gst_jpegenc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -726,34 +583,24 @@ gst_jpegenc_get_property (GObject * object, guint prop_id, GValue * value, GST_OBJECT_UNLOCK (jpegenc); } -static GstStateChangeReturn -gst_jpegenc_change_state (GstElement * element, GstStateChange transition) +static gboolean +gst_jpegenc_start (GstVideoEncoder * benc) { - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstJpegEnc *filter = GST_JPEGENC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - GST_DEBUG_OBJECT (element, "setting line buffers"); - filter->line[0] = NULL; - filter->line[1] = NULL; - filter->line[2] = NULL; - break; - default: - break; - } + GstJpegEnc *enc = (GstJpegEnc *) benc; - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; + enc->line[0] = NULL; + enc->line[1] = NULL; + enc->line[2] = NULL; - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_jpegenc_reset (filter); - break; - default: - break; - } + return TRUE; +} + +static gboolean +gst_jpegenc_stop (GstVideoEncoder * benc) +{ + GstJpegEnc *enc = (GstJpegEnc *) benc; + + gst_jpegenc_reset (enc); - return ret; + return TRUE; } diff --git a/ext/jpeg/gstjpegenc.h b/ext/jpeg/gstjpegenc.h index f684ff8..95de0b0 100644 --- a/ext/jpeg/gstjpegenc.h +++ b/ext/jpeg/gstjpegenc.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 @@ -24,6 +26,7 @@ #include #include +#include /* this is a hack hack hack to get around jpeglib header bugs... */ #ifdef HAVE_STDLIB_H # undef HAVE_STDLIB_H @@ -46,23 +49,24 @@ G_BEGIN_DECLS typedef struct _GstJpegEnc GstJpegEnc; typedef struct _GstJpegEncClass GstJpegEncClass; +#define GST_JPEG_ENC_MAX_COMPONENT 4 + struct _GstJpegEnc { - GstElement element; + GstVideoEncoder encoder; - /* pads */ - GstPad *sinkpad, *srcpad; + GstVideoCodecState *input_state; + GstVideoCodecFrame *current_frame; - /* stream/image properties */ - GstVideoInfo info; - gint channels; + guint channels; - /* standard video_format indexed */ - gint inc[GST_VIDEO_MAX_COMPONENTS]; - gint cwidth[GST_VIDEO_MAX_COMPONENTS]; - gint cheight[GST_VIDEO_MAX_COMPONENTS]; - gint h_samp[GST_VIDEO_MAX_COMPONENTS]; - gint v_samp[GST_VIDEO_MAX_COMPONENTS]; + gint stride[GST_JPEG_ENC_MAX_COMPONENT]; + gint offset[GST_JPEG_ENC_MAX_COMPONENT]; + gint inc[GST_JPEG_ENC_MAX_COMPONENT]; + gint cwidth[GST_JPEG_ENC_MAX_COMPONENT]; + gint cheight[GST_JPEG_ENC_MAX_COMPONENT]; + gint h_samp[GST_JPEG_ENC_MAX_COMPONENT]; + gint v_samp[GST_JPEG_ENC_MAX_COMPONENT]; gint h_max_samp; gint v_max_samp; gboolean planar; @@ -82,16 +86,15 @@ struct _GstJpegEnc gint smoothing; gint idct_method; - /* cached return state for any problems that may occur in callbacks */ - GstFlowReturn last_ret; - - GstMemory *output_mem; - GstMapInfo output_map; + GstBuffer *output_buffer; }; struct _GstJpegEncClass { - GstElementClass parent_class; + GstVideoEncoderClass parent_class; + + /* signals */ + void (*frame_encoded) (GstElement * element); }; GType gst_jpegenc_get_type (void); -- 2.7.4