jpegenc: enlarge buffer if libjpeg tells us it's out of space. Fixes buffer

overflow on some high-quality, low-resolution jpeg encodes.
This commit is contained in:
Peter van Hardenberg 2010-01-21 17:22:38 -08:00 committed by Michael Smith
parent 5d3d3f28e1
commit 701c7d4b2a
2 changed files with 62 additions and 22 deletions

View file

@ -218,14 +218,49 @@ gst_jpegenc_init_destination (j_compress_ptr cinfo)
static boolean static boolean
gst_jpegenc_flush_destination (j_compress_ptr cinfo) gst_jpegenc_flush_destination (j_compress_ptr cinfo)
{ {
GST_DEBUG ("gst_jpegenc_chain: flush_destination: buffer too small !!!"); GstBuffer *overflow_buffer;
guint32 old_buffer_size;
GstJpegEnc *jpegenc = (GstJpegEnc *) (cinfo->client_data);
GST_DEBUG_OBJECT (jpegenc,
"gst_jpegenc_chain: flush_destination: buffer too small");
/* Our output buffer wasn't big enough.
* Make a new buffer that's twice the size, */
old_buffer_size = GST_BUFFER_SIZE (jpegenc->output_buffer);
gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad,
GST_BUFFER_OFFSET_NONE, old_buffer_size * 2,
GST_PAD_CAPS (jpegenc->srcpad), &overflow_buffer);
memcpy (GST_BUFFER_DATA (overflow_buffer),
GST_BUFFER_DATA (jpegenc->output_buffer), old_buffer_size);
/* drop it into place, */
gst_buffer_unref (jpegenc->output_buffer);
jpegenc->output_buffer = overflow_buffer;
/* and last, update libjpeg on where to work. */
jpegenc->jdest.next_output_byte =
GST_BUFFER_DATA (jpegenc->output_buffer) + old_buffer_size;
jpegenc->jdest.free_in_buffer =
GST_BUFFER_SIZE (jpegenc->output_buffer) - old_buffer_size;
return TRUE; return TRUE;
} }
static void static void
gst_jpegenc_term_destination (j_compress_ptr cinfo) gst_jpegenc_term_destination (j_compress_ptr cinfo)
{ {
GST_DEBUG ("gst_jpegenc_chain: term_source"); GstJpegEnc *jpegenc = (GstJpegEnc *) (cinfo->client_data);
GST_DEBUG_OBJECT (jpegenc, "gst_jpegenc_chain: term_source");
/* Trim the buffer size and push it. */
GST_BUFFER_SIZE (jpegenc->output_buffer) =
GST_ROUND_UP_4 (GST_BUFFER_SIZE (jpegenc->output_buffer) -
jpegenc->jdest.free_in_buffer);
g_signal_emit (G_OBJECT (jpegenc), gst_jpegenc_signals[FRAME_ENCODED], 0);
jpegenc->last_ret = gst_pad_push (jpegenc->srcpad, jpegenc->output_buffer);
jpegenc->output_buffer = NULL;
} }
static void static void
@ -264,6 +299,8 @@ gst_jpegenc_init (GstJpegEnc * jpegenc)
jpegenc->jdest.empty_output_buffer = gst_jpegenc_flush_destination; jpegenc->jdest.empty_output_buffer = gst_jpegenc_flush_destination;
jpegenc->jdest.term_destination = gst_jpegenc_term_destination; jpegenc->jdest.term_destination = gst_jpegenc_term_destination;
jpegenc->cinfo.dest = &jpegenc->jdest; jpegenc->cinfo.dest = &jpegenc->jdest;
jpegenc->cinfo.client_data = jpegenc;
/* init properties */ /* init properties */
jpegenc->quality = JPEG_DEFAULT_QUALITY; jpegenc->quality = JPEG_DEFAULT_QUALITY;
@ -422,6 +459,10 @@ gst_jpegenc_resync (GstJpegEnc * jpegenc)
} }
#endif #endif
/* guard against a potential error in gst_jpegenc_term_destination
which occurs iff bufsize % 4 < free_space_remaining */
jpegenc->bufsize = GST_ROUND_UP_4 (jpegenc->bufsize);
jpeg_suppress_tables (&jpegenc->cinfo, TRUE); jpeg_suppress_tables (&jpegenc->cinfo, TRUE);
GST_DEBUG_OBJECT (jpegenc, "resync done"); GST_DEBUG_OBJECT (jpegenc, "resync done");
@ -434,7 +475,6 @@ gst_jpegenc_chain (GstPad * pad, GstBuffer * buf)
GstJpegEnc *jpegenc; GstJpegEnc *jpegenc;
guchar *data; guchar *data;
gulong size; gulong size;
GstBuffer *outbuf;
guint height, width; guint height, width;
guchar *base[3], *end[3]; guchar *base[3], *end[3];
gint i, j, k; gint i, j, k;
@ -452,12 +492,13 @@ gst_jpegenc_chain (GstPad * pad, GstBuffer * buf)
ret = ret =
gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad, gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad,
GST_BUFFER_OFFSET_NONE, jpegenc->bufsize, GST_PAD_CAPS (jpegenc->srcpad), GST_BUFFER_OFFSET_NONE, jpegenc->bufsize, GST_PAD_CAPS (jpegenc->srcpad),
&outbuf); &jpegenc->output_buffer);
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
goto done; goto done;
gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS); gst_buffer_copy_metadata (jpegenc->output_buffer, buf,
GST_BUFFER_COPY_TIMESTAMPS);
width = jpegenc->width; width = jpegenc->width;
height = jpegenc->height; height = jpegenc->height;
@ -470,8 +511,8 @@ gst_jpegenc_chain (GstPad * pad, GstBuffer * buf)
end[1] = base[1] + (height / 2) * I420_U_ROWSTRIDE (width); end[1] = base[1] + (height / 2) * I420_U_ROWSTRIDE (width);
end[2] = base[2] + (height / 2) * I420_V_ROWSTRIDE (width); end[2] = base[2] + (height / 2) * I420_V_ROWSTRIDE (width);
jpegenc->jdest.next_output_byte = GST_BUFFER_DATA (outbuf); jpegenc->jdest.next_output_byte = GST_BUFFER_DATA (jpegenc->output_buffer);
jpegenc->jdest.free_in_buffer = GST_BUFFER_SIZE (outbuf); jpegenc->jdest.free_in_buffer = GST_BUFFER_SIZE (jpegenc->output_buffer);
/* prepare for raw input */ /* prepare for raw input */
#if JPEG_LIB_VERSION >= 70 #if JPEG_LIB_VERSION >= 70
@ -503,16 +544,11 @@ gst_jpegenc_chain (GstPad * pad, GstBuffer * buf)
jpeg_write_raw_data (&jpegenc->cinfo, jpegenc->line, 2 * DCTSIZE); jpeg_write_raw_data (&jpegenc->cinfo, jpegenc->line, 2 * DCTSIZE);
} }
/* This will ensure that gst_jpegenc_term_destination is called; we push
the final output buffer from there */
jpeg_finish_compress (&jpegenc->cinfo); jpeg_finish_compress (&jpegenc->cinfo);
GST_LOG_OBJECT (jpegenc, "compressing done"); GST_LOG_OBJECT (jpegenc, "compressing done");
GST_BUFFER_SIZE (outbuf) =
GST_ROUND_UP_4 (jpegenc->bufsize - jpegenc->jdest.free_in_buffer);
g_signal_emit (G_OBJECT (jpegenc), gst_jpegenc_signals[FRAME_ENCODED], 0);
ret = gst_pad_push (jpegenc->srcpad, outbuf);
done: done:
gst_buffer_unref (buf); gst_buffer_unref (buf);

View file

@ -31,7 +31,6 @@
#include <jpeglib.h> #include <jpeglib.h>
G_BEGIN_DECLS G_BEGIN_DECLS
#define GST_TYPE_JPEGENC \ #define GST_TYPE_JPEGENC \
(gst_jpegenc_get_type()) (gst_jpegenc_get_type())
#define GST_JPEGENC(obj) \ #define GST_JPEGENC(obj) \
@ -42,15 +41,15 @@ G_BEGIN_DECLS
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JPEGENC)) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JPEGENC))
#define GST_IS_JPEGENC_CLASS(klass) \ #define GST_IS_JPEGENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JPEGENC)) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JPEGENC))
typedef struct _GstJpegEnc GstJpegEnc; typedef struct _GstJpegEnc GstJpegEnc;
typedef struct _GstJpegEncClass GstJpegEncClass; typedef struct _GstJpegEncClass GstJpegEncClass;
struct _GstJpegEnc { struct _GstJpegEnc
{
GstElement element; GstElement element;
/* pads */ /* pads */
GstPad *sinkpad,*srcpad; GstPad *sinkpad, *srcpad;
/* video state */ /* video state */
gint format; gint format;
@ -70,17 +69,22 @@ struct _GstJpegEnc {
gint quality; gint quality;
gint smoothing; gint smoothing;
gint idct_method; gint idct_method;
/* cached return state for any problems that may occur in callbacks */
GstFlowReturn last_ret;
GstBuffer *output_buffer;
}; };
struct _GstJpegEncClass { struct _GstJpegEncClass
{
GstElementClass parent_class; GstElementClass parent_class;
/* signals */ /* signals */
void (*frame_encoded) (GstElement *element); void (*frame_encoded) (GstElement * element);
}; };
GType gst_jpegenc_get_type(void); GType gst_jpegenc_get_type (void);
G_END_DECLS G_END_DECLS
#endif /* __GST_JPEGENC_H__ */ #endif /* __GST_JPEGENC_H__ */