jpeg2000parse: support j2c format

https://bugzilla.gnome.org/show_bug.cgi?id=767512
This commit is contained in:
Aaron Boxer 2016-06-12 13:53:18 -04:00 committed by Sebastian Dröge
parent 05b24c128a
commit 81e3b998be
2 changed files with 147 additions and 36 deletions

View file

@ -69,18 +69,29 @@ gst_jpeg2000_parse_get_subsampling (const gchar * sampling, guint8 * dx,
} }
} }
/* SOC marker plus minimum size of SIZ marker */
#define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (4+36)
#define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
#define GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC 4 #define GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC 4
#define GST_JPEG2000_PARSE_SIZE_OF_J2C_BOX_SIZE 4
/* J2C has 8 bytes preceding J2K magic: 4 for size of box, and 4 for fourcc */
#define GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES 8
/* SOC marker plus minimum size of SIZ marker */
#define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC + GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES + 36)
#define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
GST_DEBUG_CATEGORY (jpeg2000_parse_debug); GST_DEBUG_CATEGORY (jpeg2000_parse_debug);
#define GST_CAT_DEFAULT jpeg2000_parse_debug #define GST_CAT_DEFAULT jpeg2000_parse_debug
static GstStaticPadTemplate srctemplate = static GstStaticPadTemplate srctemplate =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, 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],"
GST_RTP_J2K_SAMPLING_LIST ","
"colorspace = (string) { sRGB, sYUV, GRAY }, "
" parsed = (boolean) true;"
"image/x-j2c,"
" width = (int)[1, MAX], height = (int)[1, MAX]," " width = (int)[1, MAX], height = (int)[1, MAX],"
GST_RTP_J2K_SAMPLING_LIST "," GST_RTP_J2K_SAMPLING_LIST ","
"colorspace = (string) { sRGB, sYUV, GRAY }, " "colorspace = (string) { sRGB, sYUV, GRAY }, "
@ -92,7 +103,11 @@ static GstStaticPadTemplate sinktemplate =
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/x-jpc," GST_STATIC_CAPS ("image/x-jpc,"
GST_RTP_J2K_SAMPLING_LIST ";" GST_RTP_J2K_SAMPLING_LIST ";"
"image/x-jpc, " "colorspace = (string) { sRGB, sYUV, GRAY }") "image/x-jpc, "
"colorspace = (string) { sRGB, sYUV, GRAY };"
"image/x-j2c,"
GST_RTP_J2K_SAMPLING_LIST ";"
"image/x-j2c, " "colorspace = (string) { sRGB, sYUV, GRAY }")
); );
#define parent_class gst_jpeg2000_parse_parent_class #define parent_class gst_jpeg2000_parse_parent_class
@ -103,6 +118,8 @@ static gboolean gst_jpeg2000_parse_event (GstBaseParse * parse,
GstEvent * event); GstEvent * event);
static GstFlowReturn gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, static GstFlowReturn gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, gint * skipsize); GstBaseParseFrame * frame, gint * skipsize);
static gboolean gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse,
GstCaps * caps);
static void static void
gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass) gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
@ -120,6 +137,8 @@ gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
"Parses JPEG 2000 files", "Aaron Boxer <boxerab@gmail.com>"); "Parses JPEG 2000 files", "Aaron Boxer <boxerab@gmail.com>");
/* Override BaseParse vfuncs */ /* Override BaseParse vfuncs */
parse_class->set_sink_caps =
GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_set_sink_caps);
parse_class->start = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_start); parse_class->start = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_start);
parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_event); parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_event);
parse_class->handle_frame = parse_class->handle_frame =
@ -138,6 +157,7 @@ gst_jpeg2000_parse_start (GstBaseParse * parse)
jpeg2000parse->sampling = NULL; jpeg2000parse->sampling = NULL;
jpeg2000parse->colorspace = NULL; jpeg2000parse->colorspace = NULL;
jpeg2000parse->codec_format = GST_JPEG2000_PARSE_NO_CODEC;
return TRUE; return TRUE;
} }
@ -149,6 +169,23 @@ gst_jpeg2000_parse_init (GstJPEG2000Parse * jpeg2000parse)
GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse)); GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
} }
static gboolean
gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps)
{
GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
GstStructure *caps_struct = gst_caps_get_structure (caps, 0);
if (gst_structure_has_name (caps_struct, "image/jp2")) {
jpeg2000parse->codec_format = GST_JPEG2000_PARSE_JP2;
} else if (gst_structure_has_name (caps_struct, "image/x-j2c")) {
jpeg2000parse->codec_format = GST_JPEG2000_PARSE_J2C;
} else if (gst_structure_has_name (caps_struct, "image/x-jpc")) {
jpeg2000parse->codec_format = GST_JPEG2000_PARSE_JPC;
}
return TRUE;
}
static gboolean static gboolean
gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event) gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event)
{ {
@ -173,7 +210,6 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
GstByteReader reader; GstByteReader reader;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
guint eoc_offset = 0; guint eoc_offset = 0;
GstCaps *current_caps = NULL; GstCaps *current_caps = NULL;
GstStructure *current_caps_struct = NULL; GstStructure *current_caps_struct = NULL;
const gchar *colorspace = NULL; const gchar *colorspace = NULL;
@ -187,7 +223,10 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
const char *sink_sampling = NULL; const char *sink_sampling = NULL;
const char *source_sampling = NULL; const char *source_sampling = NULL;
guint magic_offset = 0; guint magic_offset = 0;
guint j2c_box_id_offset = 0;
guint num_prefix_bytes = 0; /* number of bytes to skip before actual code stream */
GstCaps *src_caps = NULL; GstCaps *src_caps = NULL;
guint frame_size = 0;
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");
@ -195,28 +234,81 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
} }
gst_byte_reader_init (&reader, map.data, map.size); gst_byte_reader_init (&reader, map.data, map.size);
num_prefix_bytes = GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC;
/* skip to beginning of frame */ if (jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C) {
magic_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, /* check for "jp2c" */
GST_JPEG2000_PARSE_J2K_MAGIC, 0, gst_byte_reader_get_remaining (&reader)); j2c_box_id_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
GST_MAKE_FOURCC ('j', 'p', '2', 'c'), 0,
gst_byte_reader_get_remaining (&reader));
if (magic_offset == -1) { if (j2c_box_id_offset == -1) {
*skipsize = GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
gst_byte_reader_get_size (&reader) - ("Missing contiguous code stream box for j2c stream"));
GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC; ret = GST_FLOW_ERROR;
goto beach;
} else {
GST_DEBUG_OBJECT (jpeg2000parse, "Found magic at offset = %d",
magic_offset);
if (magic_offset > 0) {
*skipsize = magic_offset;
goto beach; goto beach;
} }
} }
/* Look for magic. If found, skip to beginning of frame */
magic_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
GST_JPEG2000_PARSE_J2K_MAGIC, 0, gst_byte_reader_get_remaining (&reader));
if (magic_offset == -1) {
*skipsize = gst_byte_reader_get_size (&reader) - num_prefix_bytes;
goto beach;
} else {
if (jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C) {
/* check for corrupt contiguous code stream box */
if (j2c_box_id_offset + GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC !=
magic_offset) {
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
("Corrupt contiguous code stream box for j2c stream"));
ret = GST_FLOW_ERROR;
goto beach;
}
/* check for missing contiguous code stream box size */
if (j2c_box_id_offset < GST_JPEG2000_PARSE_SIZE_OF_J2C_BOX_SIZE) {
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
("Missing contiguous code stream box size for j2c stream"));
ret = GST_FLOW_ERROR;
goto beach;
}
/* check that we have enough bytes for the J2C box size */
if (j2c_box_id_offset < GST_JPEG2000_PARSE_SIZE_OF_J2C_BOX_SIZE) {
*skipsize = gst_byte_reader_get_size (&reader) - num_prefix_bytes;
goto beach;
}
if (!gst_byte_reader_skip (&reader,
j2c_box_id_offset - GST_JPEG2000_PARSE_SIZE_OF_J2C_BOX_SIZE))
goto beach;
/* read the box size, and adjust num_prefix_bytes accordingly */
if (!gst_byte_reader_get_uint32_be (&reader, &frame_size))
goto beach;
num_prefix_bytes += GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES -
GST_JPEG2000_PARSE_SIZE_OF_J2C_BOX_SIZE;
}
GST_DEBUG_OBJECT (jpeg2000parse, "Found magic at offset = %d",
magic_offset);
if (magic_offset > 0) {
*skipsize = magic_offset;
/* J2C has 8 bytes preceding J2K magic */
if (jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C)
*skipsize -= GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES;
if (*skipsize > 0)
goto beach;
}
/* bail out if not enough data for frame */
if (frame_size && (gst_byte_reader_get_size (&reader) < frame_size))
goto beach;
}
/* 2 to skip marker size, and another 2 to skip rsiz field */ /* 2 to skip marker size, and another 2 to skip rsiz field */
if (!gst_byte_reader_skip (&reader, if (!gst_byte_reader_skip (&reader, num_prefix_bytes + 2 + 2))
GST_JPEG2000_PARSE_SIZE_OF_J2K_MAGIC + 2 + 2))
goto beach; goto beach;
if (!gst_byte_reader_get_uint32_be (&reader, &x1)) if (!gst_byte_reader_get_uint32_be (&reader, &x1))
@ -235,7 +327,7 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
if (x1 < x0 || y1 < y0) { if (x1 < x0 || y1 < y0) {
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
("Nonsensical image dimensions %d,%d,%d,%d", x0, y0, x1, y1)); ("Nonsensical image dimensions %d,%d,%d,%d", x0, y0, x1, y1));
ret = GST_FLOW_NOT_NEGOTIATED; ret = GST_FLOW_ERROR;
goto beach; goto beach;
} }
@ -269,7 +361,8 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
current_caps_struct = gst_caps_get_structure (current_caps, 0); current_caps_struct = gst_caps_get_structure (current_caps, 0);
if (!current_caps_struct) { if (!current_caps_struct) {
GST_ERROR_OBJECT (jpeg2000parse, "Unable to get structure of current caps"); GST_ERROR_OBJECT (jpeg2000parse,
"Unable to get structure of current caps struct");
ret = GST_FLOW_NOT_NEGOTIATED; ret = GST_FLOW_NOT_NEGOTIATED;
goto beach; goto beach;
} }
@ -294,7 +387,6 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
compno); compno);
} }
/*** sanity check on sub-sampling *****/ /*** sanity check on sub-sampling *****/
if (dx[0] != 1 || dy[0] != 1) { if (dx[0] != 1 || dy[0] != 1) {
GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel"); GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel");
@ -385,10 +477,8 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
colorspace = "sYUV"; colorspace = "sYUV";
} }
} }
source_sampling = sink_sampling ? sink_sampling : parsed_sampling;
/* now we can set the source caps, if something has changed */ /* now we can set the source caps, if something has changed */
source_sampling = sink_sampling ? sink_sampling : parsed_sampling;
if (width != jpeg2000parse->width || if (width != jpeg2000parse->width ||
height != jpeg2000parse->height || height != jpeg2000parse->height ||
g_strcmp0 (jpeg2000parse->sampling, source_sampling) || g_strcmp0 (jpeg2000parse->sampling, source_sampling) ||
@ -413,35 +503,46 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
} else { } else {
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;
gst_caps_unref (src_caps); gst_caps_unref (src_caps);
goto beach; goto beach;
} }
gst_caps_unref (src_caps); gst_caps_unref (src_caps);
} }
/*************************************************/
/* look for EOC to mark frame end */
/* look for EOC end of codestream marker */ /* look for EOC end of codestream marker */
eoc_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0x0000ffff, eoc_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0x0000ffff,
0xFFD9, 0, gst_byte_reader_get_remaining (&reader)); 0xFFD9, 0, gst_byte_reader_get_remaining (&reader));
if (eoc_offset != -1) { if (eoc_offset != -1) {
/* add 4 for eoc marker and eoc marker size */ /* add 4 for eoc marker and eoc marker size */
guint frame_size = gst_byte_reader_get_pos (&reader) + eoc_offset + 4; guint eoc_frame_size = gst_byte_reader_get_pos (&reader) + eoc_offset + 4;
GST_DEBUG_OBJECT (jpeg2000parse, GST_DEBUG_OBJECT (jpeg2000parse,
"Found EOC at offset = %d, frame size = %d", eoc_offset, frame_size); "Found EOC at offset = %d, frame size = %d", eoc_offset,
eoc_frame_size);
if (frame_size < gst_byte_reader_get_size (&reader)) /* bail out if not enough data for frame */
if (gst_byte_reader_get_size (&reader) < eoc_frame_size)
goto beach; goto beach;
gst_caps_unref (current_caps); if (frame_size && frame_size != eoc_frame_size) {
gst_buffer_unmap (frame->buffer, &map); GST_WARNING_OBJECT (jpeg2000parse,
return gst_base_parse_finish_frame (parse, frame, frame_size); "Frame size %d from contiguous code size does not equal frame size %d signalled by eoc",
frame_size, eoc_frame_size);
}
frame_size = eoc_frame_size;
} }
/* clean up and finish frame */
if (current_caps)
gst_caps_unref (current_caps);
gst_buffer_unmap (frame->buffer, &map);
return gst_base_parse_finish_frame (parse, frame, frame_size);
beach: beach:
if (current_caps) if (current_caps)
gst_caps_unref (current_caps); gst_caps_unref (current_caps);

View file

@ -44,6 +44,16 @@ typedef struct _GstJPEG2000ParseClass GstJPEG2000ParseClass;
#define GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS 4 #define GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS 4
typedef enum
{
GST_JPEG2000_PARSE_NO_CODEC,
GST_JPEG2000_PARSE_JPC, /* jpeg 2000 code stream */
GST_JPEG2000_PARSE_J2C, /* jpeg 2000 contiguous code stream box plus code stream */
GST_JPEG2000_PARSE_JP2, /* jpeg 2000 part I file format */
} GstJPEG2000ParseFormats;
struct _GstJPEG2000Parse struct _GstJPEG2000Parse
{ {
GstBaseParse baseparse; GstBaseParse baseparse;
@ -54,7 +64,7 @@ struct _GstJPEG2000Parse
const gchar *sampling; const gchar *sampling;
const gchar *colorspace; const gchar *colorspace;
GstJPEG2000ParseFormats codec_format;
}; };
struct _GstJPEG2000ParseClass struct _GstJPEG2000ParseClass