jpeg2000parse: Require either colorspace or sampling field in sink caps

And always set the sampling field on the src caps, if necessary guessing a
correct value for it from the colorspace field.

Also, did some cleanup: removed sampling enum - redundant.

https://bugzilla.gnome.org/show_bug.cgi?id=766236
This commit is contained in:
Aaron Boxer 2016-06-08 11:32:28 -04:00 committed by Sebastian Dröge
parent 3107f5df76
commit 3dc3a915ea
3 changed files with 170 additions and 135 deletions

View file

@ -25,6 +25,50 @@
#include "gstjpeg2000parse.h" #include "gstjpeg2000parse.h"
#include <gst/base/base.h> #include <gst/base/base.h>
/* convenience methods */
static gboolean
gst_jpeg2000_parse_is_rgb (const gchar * sampling)
{
return (!g_strcmp0 (sampling, GST_RTP_J2K_RGB) ||
!g_strcmp0 (sampling, GST_RTP_J2K_RGBA) ||
!g_strcmp0 (sampling, GST_RTP_J2K_BGR) ||
!g_strcmp0 (sampling, GST_RTP_J2K_BGRA));
}
static gboolean
gst_jpeg2000_parse_is_yuv (const gchar * sampling)
{
return (!g_strcmp0 (sampling, GST_RTP_J2K_YBRA) ||
!g_strcmp0 (sampling, GST_RTP_J2K_YBR444) ||
!g_strcmp0 (sampling, GST_RTP_J2K_YBR422) ||
!g_strcmp0 (sampling, GST_RTP_J2K_YBR420) ||
!g_strcmp0 (sampling, GST_RTP_J2K_YBR410));
}
static gboolean
gst_jpeg2000_parse_is_mono (const gchar * sampling)
{
return !g_strcmp0 (sampling, GST_RTP_J2K_GRAYSCALE);
}
static void
gst_jpeg2000_parse_get_subsampling (const gchar * sampling, guint8 * dx,
guint8 * dy)
{
*dx = 1;
*dy = 1;
if (!g_strcmp0 (sampling, GST_RTP_J2K_YBR422)) {
*dx = 2;
} else if (!g_strcmp0 (sampling, GST_RTP_J2K_YBR420)) {
*dx = 2;
*dy = 2;
} else if (!g_strcmp0 (sampling, GST_RTP_J2K_YBR410)) {
*dx = 4;
*dy = 2;
}
}
/* SOC marker plus minimum size of SIZ marker */ /* SOC marker plus minimum size of SIZ marker */
#define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (4+36) #define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (4+36)
#define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51 #define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
@ -38,14 +82,17 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/x-jpc," GST_STATIC_CAPS ("image/x-jpc,"
" width = (int)[1, MAX], height = (int)[1, MAX]," " width = (int)[1, MAX], height = (int)[1, MAX],"
"colorspace = (string) { sRGB, sYUV, GRAY }," "parsed = (boolean) true") GST_RTP_J2K_SAMPLING_LIST ","
"colorspace = (string) { sRGB, sYUV, GRAY }, "
" parsed = (boolean) true")
); );
static GstStaticPadTemplate sinktemplate = static GstStaticPadTemplate sinktemplate =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/x-jpc," GST_STATIC_CAPS ("image/x-jpc,"
"colorspace = (string) { sRGB, sYUV, GRAY }") GST_RTP_J2K_SAMPLING_LIST ";"
"image/x-jpc, " "colorspace = (string) { sRGB, sYUV, GRAY }")
); );
#define parent_class gst_jpeg2000_parse_parent_class #define parent_class gst_jpeg2000_parse_parent_class
@ -79,33 +126,29 @@ gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_handle_frame); GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_handle_frame);
} }
static void
gst_jpeg2000_parse_init (GstJPEG2000Parse * jpeg2000parse)
{
GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
}
static gboolean static gboolean
gst_jpeg2000_parse_start (GstBaseParse * parse) gst_jpeg2000_parse_start (GstBaseParse * parse)
{ {
GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse); GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
guint i;
GST_DEBUG_OBJECT (jpeg2000parse, "start"); GST_DEBUG_OBJECT (jpeg2000parse, "start");
gst_base_parse_set_min_frame_size (parse, GST_JPEG2000_PARSE_MIN_FRAME_SIZE); gst_base_parse_set_min_frame_size (parse, GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
jpeg2000parse->width = 0; jpeg2000parse->width = 0;
jpeg2000parse->height = 0; jpeg2000parse->height = 0;
for (i = 0; i < GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS; ++i) { jpeg2000parse->sampling = NULL;
jpeg2000parse->dx[i] = 0; jpeg2000parse->colorspace = NULL;
jpeg2000parse->dy[i] = 0;
}
jpeg2000parse->sampling = GST_RTP_SAMPLING_NONE;
return TRUE; return TRUE;
} }
static void
gst_jpeg2000_parse_init (GstJPEG2000Parse * jpeg2000parse)
{
GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
}
static gboolean static gboolean
gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event) gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event)
{ {
@ -135,16 +178,16 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
GstStructure *current_caps_struct = NULL; GstStructure *current_caps_struct = NULL;
const gchar *colorspace = NULL; const gchar *colorspace = NULL;
guint x0, y0, x1, y1; guint x0, y0, x1, y1;
guint width, height; guint width = 0, height = 0;
gboolean dimensions_changed = FALSE;
guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS]; guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS]; /* sub-sampling factors */ guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
gboolean subsampling_changed = FALSE;
guint16 numcomps; guint16 numcomps;
guint16 compno; guint16 compno;
const char *sampling = NULL; const char *parsed_sampling = NULL;
GstRtpSampling samplingEnum = GST_RTP_SAMPLING_NONE; const char *sink_sampling = NULL;
const char *source_sampling = NULL;
guint magic_offset = 0; guint magic_offset = 0;
GstCaps *src_caps = NULL;
if (!gst_buffer_map (frame->buffer, &map, GST_MAP_READ)) { if (!gst_buffer_map (frame->buffer, &map, GST_MAP_READ)) {
GST_ERROR_OBJECT (jpeg2000parse, "Unable to map buffer"); GST_ERROR_OBJECT (jpeg2000parse, "Unable to map buffer");
@ -210,7 +253,7 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
if (!gst_byte_reader_get_uint16_be (&reader, &numcomps)) if (!gst_byte_reader_get_uint16_be (&reader, &numcomps))
goto beach; goto beach;
if (numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) { if (numcomps == 2 || numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) {
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
("Unsupported number of components %d", numcomps)); ("Unsupported number of components %d", numcomps));
ret = GST_FLOW_NOT_NEGOTIATED; ret = GST_FLOW_NOT_NEGOTIATED;
@ -231,13 +274,8 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
goto beach; goto beach;
} }
/* colorspace is a required field */
colorspace = gst_structure_get_string (current_caps_struct, "colorspace"); colorspace = gst_structure_get_string (current_caps_struct, "colorspace");
if (!colorspace) { sink_sampling = gst_structure_get_string (current_caps_struct, "sampling");
GST_ERROR_OBJECT (jpeg2000parse, "Missing color space");
ret = GST_FLOW_NOT_NEGOTIATED;
goto beach;
}
for (compno = 0; compno < numcomps; ++compno) { for (compno = 0; compno < numcomps; ++compno) {
@ -254,106 +292,119 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
GST_DEBUG_OBJECT (jpeg2000parse, GST_DEBUG_OBJECT (jpeg2000parse,
"Parsed sub-sampling %d,%d for component %d", dx[compno], dy[compno], "Parsed sub-sampling %d,%d for component %d", dx[compno], dy[compno],
compno); compno);
} }
/* now we can set the dimensions and sub-sampling */
if (width != jpeg2000parse->width || height != jpeg2000parse->height) {
dimensions_changed = TRUE;
}
jpeg2000parse->width = width;
jpeg2000parse->height = height;
for (compno = 0; compno < numcomps; ++compno) { /*** sanity check on sub-sampling *****/
if (dx[compno] != jpeg2000parse->dx[compno] if (dx[0] != 1 || dy[0] != 1) {
|| dy[compno] != jpeg2000parse->dy[compno]) { GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel");
subsampling_changed = TRUE;
} }
jpeg2000parse->dx[compno] = dx[compno];
jpeg2000parse->dx[compno] = dy[compno];
}
/* we do not set sampling field for sub-sampled RGB or monochrome */
for (compno = 0; compno < numcomps; ++compno) {
if (strcmp (colorspace, "sYUV") && (dx[compno] > 1 || dy[compno] > 1)) {
GST_WARNING_OBJECT (jpeg2000parse,
"Unable to set sampling field for sub-sampled RGB or monochrome color spaces");
goto set_caps;
}
}
/* sanity check on sub-sampling */
if (dx[1] != dx[2] || dy[1] != dy[2]) { if (dx[1] != dx[2] || dy[1] != dy[2]) {
GST_WARNING_OBJECT (jpeg2000parse, GST_WARNING_OBJECT (jpeg2000parse,
"Unable to set sampling field because chroma channel sub-sampling factors are not equal"); "Chroma channel sub-sampling factors are not equal");
goto set_caps;
} }
for (compno = 0; compno < numcomps; ++compno) {
if (colorspace && g_strcmp0 (colorspace, "sYUV") && (dx[compno] > 1
|| dy[compno] > 1)) {
GST_WARNING_OBJECT (jpeg2000parse,
"Sub-sampled RGB or monochrome color spaces");
}
if (sink_sampling) {
guint8 dx_caps, dy_caps;
gst_jpeg2000_parse_get_subsampling (sink_sampling, &dx_caps, &dy_caps);
if (dx_caps != dx[compno] || dy_caps != dy[compno]) {
const gchar *inferred_colorspace = NULL;
GST_WARNING_OBJECT (jpeg2000parse,
"Sink caps sub-sampling %d,%d for channel %d does not match stream sub-sampling %d,%d",
dx_caps, dy_caps, compno, dx[compno], dy[compno]);
/* try to guess correct color space */
if (gst_jpeg2000_parse_is_mono (sink_sampling))
inferred_colorspace = "GRAY";
else if (gst_jpeg2000_parse_is_rgb (sink_sampling))
inferred_colorspace = "sRGB";
else if (gst_jpeg2000_parse_is_yuv (sink_sampling))
inferred_colorspace = "sYUV";
else if (colorspace)
inferred_colorspace = colorspace;
if (inferred_colorspace) {
sink_sampling = NULL;
colorspace = inferred_colorspace;
break;
} else {
/* unrecognized sink_sampling and no colorspace */
GST_ERROR_OBJECT (jpeg2000parse,
"Unrecognized sink sampling field and no sink colorspace field");
ret = GST_FLOW_NOT_NEGOTIATED;
goto beach;
}
}
}
}
/*************************************/
if (!strcmp (colorspace, "sYUV")) { /* if colorspace is present, we can work out the parsed_sampling field */
/* reject sub-sampled YUVA image */ if (colorspace) {
if (!g_strcmp0 (colorspace, "sYUV")) {
if (numcomps == 4) { if (numcomps == 4) {
guint i; guint i;
parsed_sampling = GST_RTP_J2K_YBRA;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
if (dx[i] > 1 || dy[i] > 1) { if (dx[i] > 1 || dy[i] > 1) {
GST_WARNING_OBJECT (jpeg2000parse, GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled YUVA images");
"Unable to set sampling field for sub-sampled YUVA images");
goto set_caps;
} }
} }
sampling = GST_RTP_J2K_YBRA;
samplingEnum = GST_RTP_SAMPLING_YBRA;
} else if (numcomps == 3) { } else if (numcomps == 3) {
/* use sub-sampling from U chroma channel */ /* use sub-sampling from U chroma channel */
if (dx[1] == 1 && dy[1] == 1) { if (dx[1] == 1 && dy[1] == 1) {
sampling = GST_RTP_J2K_YBR444; parsed_sampling = GST_RTP_J2K_YBR444;
samplingEnum = GST_RTP_SAMPLING_YBR444;
} else if (dx[1] == 2 && dy[1] == 2) { } else if (dx[1] == 2 && dy[1] == 2) {
sampling = GST_RTP_J2K_YBR420; parsed_sampling = GST_RTP_J2K_YBR420;
samplingEnum = GST_RTP_SAMPLING_YBR420;
} else if (dx[1] == 4 && dy[1] == 2) { } else if (dx[1] == 4 && dy[1] == 2) {
sampling = GST_RTP_J2K_YBR410; parsed_sampling = GST_RTP_J2K_YBR410;
samplingEnum = GST_RTP_SAMPLING_YBR410;
} else if (dx[1] == 2 && dy[1] == 1) { } else if (dx[1] == 2 && dy[1] == 1) {
sampling = GST_RTP_J2K_YBR422; parsed_sampling = GST_RTP_J2K_YBR422;
samplingEnum = GST_RTP_SAMPLING_YBR422;
} else { } else {
GST_WARNING_OBJECT (jpeg2000parse, GST_WARNING_OBJECT (jpeg2000parse,
"Unable to set sampling field for sub-sampling factors %d,%d", "Unsupported sub-sampling factors %d,%d", dx[1], dy[1]);
dx[1], dy[1]); /* best effort */
goto set_caps; parsed_sampling = GST_RTP_J2K_YBR444;
} }
} }
} else if (!strcmp (colorspace, "GRAY")) { } else if (!g_strcmp0 (colorspace, "GRAY")) {
sampling = GST_RTP_J2K_GRAYSCALE; parsed_sampling = GST_RTP_J2K_GRAYSCALE;
samplingEnum = GST_RTP_SAMPLING_GRAYSCALE;
} else { } else {
if (numcomps == 4) { parsed_sampling = (numcomps == 4) ? GST_RTP_J2K_RGBA : GST_RTP_J2K_RGB;
sampling = GST_RTP_J2K_RGBA; }
samplingEnum = GST_RTP_SAMPLING_RGBA;
} else { } else {
sampling = GST_RTP_J2K_RGB; if (gst_jpeg2000_parse_is_mono (sink_sampling)) {
samplingEnum = GST_RTP_SAMPLING_RGB; colorspace = "GRAY";
} else if (gst_jpeg2000_parse_is_rgb (sink_sampling)) {
colorspace = "sRGB";
} else {
/* best effort */
colorspace = "sYUV";
} }
} }
set_caps: source_sampling = sink_sampling ? sink_sampling : parsed_sampling;
if (dimensions_changed || subsampling_changed) { /* now we can set the source caps, if something has changed */
GstCaps *src_caps = NULL; if (width != jpeg2000parse->width ||
gint fr_num, fr_denom; height != jpeg2000parse->height ||
g_strcmp0 (jpeg2000parse->sampling, source_sampling) ||
g_strcmp0 (jpeg2000parse->colorspace, colorspace)) {
gint fr_num = 0, fr_denom = 0;
jpeg2000parse->width = width;
jpeg2000parse->height = height;
jpeg2000parse->sampling = source_sampling;
jpeg2000parse->colorspace = colorspace;
src_caps = src_caps =
gst_caps_new_simple (gst_structure_get_name (current_caps_struct), gst_caps_new_simple (gst_structure_get_name (current_caps_struct),
"width", G_TYPE_INT, jpeg2000parse->width, "height", G_TYPE_INT, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height,
jpeg2000parse->height, "colorspace", G_TYPE_STRING, colorspace, NULL); "colorspace", G_TYPE_STRING, colorspace,
"sampling", G_TYPE_STRING, source_sampling, NULL);
if (sampling) {
gst_caps_set_simple (src_caps, "sampling", G_TYPE_STRING, sampling, NULL);
}
if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num, if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num,
&fr_denom)) { &fr_denom)) {
@ -363,7 +414,6 @@ set_caps:
GST_WARNING_OBJECT (jpeg2000parse, "No framerate set"); GST_WARNING_OBJECT (jpeg2000parse, "No framerate set");
} }
if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), src_caps)) { if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), src_caps)) {
GST_ERROR_OBJECT (jpeg2000parse, "Unable to set source caps"); GST_ERROR_OBJECT (jpeg2000parse, "Unable to set source caps");
ret = GST_FLOW_NOT_NEGOTIATED; ret = GST_FLOW_NOT_NEGOTIATED;
@ -372,7 +422,6 @@ set_caps:
} }
gst_caps_unref (src_caps); gst_caps_unref (src_caps);
jpeg2000parse->sampling = samplingEnum;
} }
/* look for EOC end of codestream marker */ /* look for EOC end of codestream marker */
@ -398,5 +447,4 @@ beach:
gst_caps_unref (current_caps); gst_caps_unref (current_caps);
gst_buffer_unmap (frame->buffer, &map); gst_buffer_unmap (frame->buffer, &map);
return ret; return ret;
} }

View file

@ -48,19 +48,22 @@ struct _GstJPEG2000Parse
{ {
GstBaseParse baseparse; GstBaseParse baseparse;
guint width; guint width;
guint height; guint height;
guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS]; const gchar *sampling;
guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS]; /* subsampling factors */ const gchar *colorspace;
GstRtpSampling sampling;
}; };
struct _GstJPEG2000ParseClass struct _GstJPEG2000ParseClass
{ {
GstBaseParseClass parent_class; GstBaseParseClass parent_class;
}; };
G_END_DECLS G_END_DECLS
#endif #endif

View file

@ -58,22 +58,6 @@ GRAYSCALE: basically, a single component image of just multilevels of grey.
#define GST_RTP_J2K_SAMPLING_LIST "sampling = (string) {\"RGB\", \"BGR\", \"RGBA\", \"BGRA\", \"YCbCrA\", \"YCbCr-4:4:4\", \"YCbCr-4:2:2\", \"YCbCr-4:2:0\", \"YCbCr-4:1:1\", \"GRAYSCALE\"}" #define GST_RTP_J2K_SAMPLING_LIST "sampling = (string) {\"RGB\", \"BGR\", \"RGBA\", \"BGRA\", \"YCbCrA\", \"YCbCr-4:4:4\", \"YCbCr-4:2:2\", \"YCbCr-4:2:0\", \"YCbCr-4:1:1\", \"GRAYSCALE\"}"
typedef enum
{
GST_RTP_SAMPLING_NONE,
GST_RTP_SAMPLING_RGB,
GST_RTP_SAMPLING_BGR,
GST_RTP_SAMPLING_RGBA,
GST_RTP_SAMPLING_BGRA,
GST_RTP_SAMPLING_YBRA,
GST_RTP_SAMPLING_YBR444,
GST_RTP_SAMPLING_YBR422,
GST_RTP_SAMPLING_YBR420,
GST_RTP_SAMPLING_YBR410,
GST_RTP_SAMPLING_GRAYSCALE
} GstRtpSampling;
/* /*
* GstJ2KMarker: * GstJ2KMarker: