mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-13 11:34:10 +00:00
jpegdec: add support for RGB and grayscale color space
Also refactor src caps negotiation and setting.
This commit is contained in:
parent
b0ac4a4560
commit
78a2b22ed5
2 changed files with 240 additions and 90 deletions
|
@ -59,12 +59,17 @@ enum
|
|||
PROP_IDCT_METHOD
|
||||
};
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
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_YUV ("I420"))
|
||||
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)
|
||||
);
|
||||
/* *INDENT-ON* */
|
||||
|
||||
static GstStaticPadTemplate gst_jpeg_dec_sink_pad_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
|
@ -714,9 +719,51 @@ gst_jpeg_dec_ensure_buffers (GstJpegDec * dec, guint maxrowbytes)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
|
||||
GST_DEBUG_OBJECT (dec, "indirect decoding of RGB");
|
||||
|
||||
if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width))))
|
||||
return;
|
||||
|
||||
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));
|
||||
|
||||
i = 0;
|
||||
while (i < height) {
|
||||
lines = jpeg_read_raw_data (&dec->cinfo, scanarray, DCTSIZE);
|
||||
if (G_LIKELY (lines > 0)) {
|
||||
for (j = 0; (j < DCTSIZE) && (i < height); j++, i++) {
|
||||
gint p;
|
||||
|
||||
p = 0;
|
||||
for (k = 0; k < width; k++) {
|
||||
base[0][p] = r_rows[j][k];
|
||||
base[1][p] = g_rows[j][k];
|
||||
base[2][p] = b_rows[j][k];
|
||||
p += pstride;
|
||||
}
|
||||
base[0] += rstride;
|
||||
base[1] += rstride;
|
||||
base[2] += rstride;
|
||||
}
|
||||
} else {
|
||||
GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_jpeg_dec_decode_indirect (GstJpegDec * dec, guchar * base[3],
|
||||
guchar * last[3], guint width, guint height, gint r_v, gint r_h)
|
||||
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 };
|
||||
|
@ -733,6 +780,15 @@ gst_jpeg_dec_decode_indirect (GstJpegDec * dec, guchar * base[3],
|
|||
memcpy (u_rows, dec->idr_u, 16 * sizeof (gpointer));
|
||||
memcpy (v_rows, dec->idr_v, 16 * sizeof (gpointer));
|
||||
|
||||
/* fill chroma components for grayscale */
|
||||
if (comp == 1) {
|
||||
GST_DEBUG_OBJECT (dec, "grayscale, filling chroma");
|
||||
for (i = 0; i < 16; i++) {
|
||||
memset (u_rows[i], GST_ROUND_UP_32 (width), 0x80);
|
||||
memset (v_rows[i], GST_ROUND_UP_32 (width), 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < height; i += r_v * DCTSIZE) {
|
||||
lines = jpeg_read_raw_data (&dec->cinfo, scanarray, r_v * DCTSIZE);
|
||||
if (G_LIKELY (lines > 0)) {
|
||||
|
@ -927,6 +983,110 @@ gst_jpeg_dec_do_qos (GstJpegDec * dec, GstClockTime timestamp)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc)
|
||||
{
|
||||
GstCaps *caps;
|
||||
GstVideoFormat format;
|
||||
|
||||
if (G_UNLIKELY (width == dec->caps_width && height == dec->caps_height &&
|
||||
dec->framerate_numerator == dec->caps_framerate_numerator &&
|
||||
dec->framerate_denominator == dec->caps_framerate_denominator &&
|
||||
clrspc == dec->clrspc))
|
||||
return;
|
||||
|
||||
/* framerate == 0/1 is a still frame */
|
||||
if (dec->framerate_denominator == 0) {
|
||||
dec->framerate_numerator = 0;
|
||||
dec->framerate_denominator = 1;
|
||||
}
|
||||
|
||||
/* calculate or assume an average frame duration for QoS purposes */
|
||||
GST_OBJECT_LOCK (dec);
|
||||
if (dec->framerate_numerator != 0) {
|
||||
dec->qos_duration = gst_util_uint64_scale (GST_SECOND,
|
||||
dec->framerate_denominator, dec->framerate_numerator);
|
||||
} else {
|
||||
/* if not set just use 25fps */
|
||||
dec->qos_duration = gst_util_uint64_scale (GST_SECOND, 1, 25);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (dec);
|
||||
|
||||
if (dec->cinfo.jpeg_color_space == JCS_RGB) {
|
||||
gint i;
|
||||
GstCaps *allowed_caps;
|
||||
|
||||
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_caps_copy (gst_pad_get_pad_template_caps (dec->srcpad));
|
||||
}
|
||||
/* avoid lists of fourcc, etc */
|
||||
allowed_caps = gst_caps_normalize (caps);
|
||||
gst_caps_unref (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 */
|
||||
gst_pad_fixate_caps (dec->srcpad, caps);
|
||||
GST_LOG_OBJECT (dec, "checking caps %" GST_PTR_FORMAT, caps);
|
||||
if (!gst_video_format_parse_caps (caps, &format, NULL, NULL))
|
||||
continue;
|
||||
/* we'll settle for the first (preferred) downstream rgb format */
|
||||
if (gst_video_format_is_rgb (format))
|
||||
break;
|
||||
/* default fall-back */
|
||||
format = GST_VIDEO_FORMAT_RGB;
|
||||
}
|
||||
if (caps)
|
||||
gst_caps_unref (caps);
|
||||
gst_caps_unref (allowed_caps);
|
||||
caps = gst_video_format_new_caps (format, width, height,
|
||||
dec->framerate_numerator, dec->framerate_denominator, 1, 1);
|
||||
dec->outsize = gst_video_format_get_size (format, width, height);
|
||||
/* 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 {
|
||||
/* go for plain and simple I420 */
|
||||
/* TODO other YUV cases ? */
|
||||
caps = gst_caps_new_simple ("video/x-raw-yuv",
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
|
||||
"width", G_TYPE_INT, width, "height", G_TYPE_INT, height,
|
||||
"framerate", GST_TYPE_FRACTION, dec->framerate_numerator,
|
||||
dec->framerate_denominator, NULL);
|
||||
dec->outsize = I420_SIZE (width, height);
|
||||
}
|
||||
|
||||
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);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
dec->caps_width = width;
|
||||
dec->caps_height = height;
|
||||
dec->caps_framerate_numerator = dec->framerate_numerator;
|
||||
dec->caps_framerate_denominator = dec->framerate_denominator;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_jpeg_dec_chain (GstPad * pad, GstBuffer * buf)
|
||||
{
|
||||
|
@ -1035,7 +1195,9 @@ gst_jpeg_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
goto components_not_supported;
|
||||
|
||||
/* verify color space expectation to avoid going *boom* or bogus output */
|
||||
if (dec->cinfo.jpeg_color_space != JCS_YCbCr)
|
||||
if (dec->cinfo.jpeg_color_space != JCS_YCbCr &&
|
||||
dec->cinfo.jpeg_color_space != JCS_GRAYSCALE &&
|
||||
dec->cinfo.jpeg_color_space != JCS_RGB)
|
||||
goto unsupported_colorspace;
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
|
@ -1054,7 +1216,7 @@ gst_jpeg_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
/* prepare for raw output */
|
||||
dec->cinfo.do_fancy_upsampling = FALSE;
|
||||
dec->cinfo.do_block_smoothing = FALSE;
|
||||
dec->cinfo.out_color_space = JCS_YCbCr;
|
||||
dec->cinfo.out_color_space = dec->cinfo.jpeg_color_space;
|
||||
dec->cinfo.dct_method = dec->idct_method;
|
||||
dec->cinfo.raw_data_out = TRUE;
|
||||
|
||||
|
@ -1064,11 +1226,27 @@ gst_jpeg_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
GST_WARNING_OBJECT (dec, "failed to start decompression cycle");
|
||||
}
|
||||
|
||||
/* YUV sanity checks to get safe and reasonable I420 output */
|
||||
g_assert (dec->cinfo.num_components == 3);
|
||||
if (r_v > 2 || r_v < dec->cinfo.comp_info[0].v_samp_factor ||
|
||||
r_h < dec->cinfo.comp_info[0].h_samp_factor)
|
||||
goto invalid_yuv;
|
||||
/* sanity checks to get safe and reasonable output */
|
||||
switch (dec->cinfo.jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
break;
|
||||
case JCS_RGB:
|
||||
if (dec->cinfo.num_components != 3 || dec->cinfo.max_v_samp_factor > 1 ||
|
||||
dec->cinfo.max_h_samp_factor > 1)
|
||||
goto invalid_yuvrgb;
|
||||
break;
|
||||
case JCS_YCbCr:
|
||||
if (dec->cinfo.num_components != 3 ||
|
||||
r_v > 2 || r_v < dec->cinfo.comp_info[0].v_samp_factor ||
|
||||
r_v < dec->cinfo.comp_info[1].v_samp_factor ||
|
||||
r_h < dec->cinfo.comp_info[0].h_samp_factor ||
|
||||
r_h < dec->cinfo.comp_info[1].h_samp_factor)
|
||||
goto invalid_yuvrgb;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
width = dec->cinfo.output_width;
|
||||
height = dec->cinfo.output_height;
|
||||
|
@ -1077,50 +1255,7 @@ gst_jpeg_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
height < MIN_HEIGHT || height > MAX_HEIGHT))
|
||||
goto wrong_size;
|
||||
|
||||
if (G_UNLIKELY (width != dec->caps_width || height != dec->caps_height ||
|
||||
dec->framerate_numerator != dec->caps_framerate_numerator ||
|
||||
dec->framerate_denominator != dec->caps_framerate_denominator)) {
|
||||
GstCaps *caps;
|
||||
|
||||
/* framerate == 0/1 is a still frame */
|
||||
if (dec->framerate_denominator == 0) {
|
||||
dec->framerate_numerator = 0;
|
||||
dec->framerate_denominator = 1;
|
||||
}
|
||||
|
||||
/* calculate or assume an average frame duration for QoS purposes */
|
||||
GST_OBJECT_LOCK (dec);
|
||||
if (dec->framerate_numerator != 0) {
|
||||
dec->qos_duration = gst_util_uint64_scale (GST_SECOND,
|
||||
dec->framerate_denominator, dec->framerate_numerator);
|
||||
} else {
|
||||
/* if not set just use 25fps */
|
||||
dec->qos_duration = gst_util_uint64_scale (GST_SECOND, 1, 25);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (dec);
|
||||
|
||||
caps = gst_caps_new_simple ("video/x-raw-yuv",
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
|
||||
"width", G_TYPE_INT, width,
|
||||
"height", G_TYPE_INT, height,
|
||||
"framerate", GST_TYPE_FRACTION, dec->framerate_numerator,
|
||||
dec->framerate_denominator, NULL);
|
||||
|
||||
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);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
dec->caps_width = width;
|
||||
dec->caps_height = height;
|
||||
dec->caps_framerate_numerator = dec->framerate_numerator;
|
||||
dec->caps_framerate_denominator = dec->framerate_denominator;
|
||||
dec->outsize = I420_SIZE (width, height);
|
||||
}
|
||||
gst_jpeg_dec_negotiate (dec, width, height, dec->cinfo.jpeg_color_space);
|
||||
|
||||
ret = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE,
|
||||
dec->outsize, GST_PAD_CAPS (dec->srcpad), &outbuf);
|
||||
|
@ -1153,43 +1288,51 @@ gst_jpeg_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
}
|
||||
GST_BUFFER_DURATION (outbuf) = duration;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* For some widths jpeglib requires more horizontal padding than I420
|
||||
* provides. In those cases we need to decode into separate buffers and then
|
||||
* copy over the data into our final picture buffer, otherwise jpeglib might
|
||||
* write over the end of a line into the beginning of the next line,
|
||||
* resulting in blocky artifacts on the left side of the picture. */
|
||||
if (G_UNLIKELY (width % (dec->cinfo.max_h_samp_factor * DCTSIZE) != 0
|
||||
|| dec->cinfo.comp_info[0].h_samp_factor != 2
|
||||
|| dec->cinfo.comp_info[1].h_samp_factor != 1
|
||||
|| 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, base, last, width, height, r_v, r_h);
|
||||
if (dec->cinfo.jpeg_color_space == JCS_RGB) {
|
||||
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 {
|
||||
ret = gst_jpeg_dec_decode_direct (dec, base, last, width, height);
|
||||
/* 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);
|
||||
|
||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||
goto decode_direct_failed;
|
||||
/* 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);
|
||||
|
||||
/* For some widths jpeglib requires more horizontal padding than I420
|
||||
* provides. In those cases we need to decode into separate buffers and then
|
||||
* copy over the data into our final picture buffer, otherwise jpeglib might
|
||||
* write over the end of a line into the beginning of the next line,
|
||||
* resulting in blocky artifacts on the left side of the picture. */
|
||||
if (G_UNLIKELY (width % (dec->cinfo.max_h_samp_factor * DCTSIZE) != 0
|
||||
|| dec->cinfo.comp_info[0].h_samp_factor != 2
|
||||
|| dec->cinfo.comp_info[1].h_samp_factor != 1
|
||||
|| 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, base, last, width, height, r_v, r_h,
|
||||
dec->cinfo.num_components);
|
||||
} else {
|
||||
ret = gst_jpeg_dec_decode_direct (dec, base, last, width, height);
|
||||
|
||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||
goto decode_direct_failed;
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (dec, "decompressing finished");
|
||||
|
@ -1323,10 +1466,10 @@ unsupported_colorspace:
|
|||
ret = GST_FLOW_ERROR;
|
||||
goto done;
|
||||
}
|
||||
invalid_yuv:
|
||||
invalid_yuvrgb:
|
||||
{
|
||||
GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
|
||||
("Picture is corrupt or unhandled YUV layout"));
|
||||
("Picture is corrupt or unhandled YUV/RGB layout"));
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
@ -1456,6 +1599,7 @@ gst_jpeg_dec_change_state (GstElement * element, GstStateChange transition)
|
|||
dec->caps_framerate_numerator = dec->caps_framerate_denominator = 0;
|
||||
dec->caps_width = -1;
|
||||
dec->caps_height = -1;
|
||||
dec->clrspc = -1;
|
||||
dec->packetized = FALSE;
|
||||
dec->next_ts = 0;
|
||||
dec->discont = TRUE;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <setjmp.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
/* this is a hack hack hack to get around jpeglib header bugs... */
|
||||
#ifdef HAVE_STDLIB_H
|
||||
|
@ -96,6 +97,11 @@ struct _GstJpegDec {
|
|||
gint caps_width;
|
||||
gint caps_height;
|
||||
gint outsize;
|
||||
gint clrspc;
|
||||
|
||||
gint offset[3];
|
||||
gint stride;
|
||||
gint inc;
|
||||
|
||||
/* properties */
|
||||
gint idct_method;
|
||||
|
|
Loading…
Reference in a new issue