2016-06-01 23:02:33 +00:00
|
|
|
/* GStreamer JPEG 2000 Parser
|
|
|
|
* Copyright (C) <2016> Grok Image Compession Inc.
|
|
|
|
* @author Aaron Boxer <boxerab@gmail.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gstjpeg2000parse.h"
|
|
|
|
#include <gst/base/base.h>
|
|
|
|
|
2016-06-08 15:32:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
2016-06-21 16:41:46 +00:00
|
|
|
gst_jpeg2000_parse_get_subsampling (GstJPEG2000Sampling sampling, guint8 * dx,
|
2016-06-08 15:32:28 +00:00
|
|
|
guint8 * dy)
|
|
|
|
{
|
|
|
|
*dx = 1;
|
|
|
|
*dy = 1;
|
2016-06-21 16:41:46 +00:00
|
|
|
if (sampling == GST_JPEG2000_SAMPLING_YBR422) {
|
2016-06-08 15:32:28 +00:00
|
|
|
*dx = 2;
|
2016-06-21 16:41:46 +00:00
|
|
|
} else if (sampling == GST_JPEG2000_SAMPLING_YBR420) {
|
2016-06-08 15:32:28 +00:00
|
|
|
*dx = 2;
|
|
|
|
*dy = 2;
|
2016-06-21 16:41:46 +00:00
|
|
|
} else if (sampling == GST_JPEG2000_SAMPLING_YBR410) {
|
2016-06-08 15:32:28 +00:00
|
|
|
*dx = 4;
|
|
|
|
*dy = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-30 11:47:50 +00:00
|
|
|
#define GST_JPEG2000_JP2_SIZE_OF_BOX_ID 4
|
|
|
|
#define GST_JPEG2000_JP2_SIZE_OF_BOX_LEN 4
|
|
|
|
#define GST_JPEG2000_MARKER_SIZE 4
|
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
|
|
|
|
/* J2C has 8 bytes preceding J2K magic: 4 for size of box, and 4 for fourcc */
|
2016-06-30 11:47:50 +00:00
|
|
|
#define GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES (GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID)
|
2016-06-12 17:53:18 +00:00
|
|
|
|
2016-06-01 23:02:33 +00:00
|
|
|
/* SOC marker plus minimum size of SIZ marker */
|
2016-06-30 11:47:50 +00:00
|
|
|
#define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (GST_JPEG2000_MARKER_SIZE + GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES + 36)
|
2016-06-12 17:53:18 +00:00
|
|
|
|
2016-06-01 23:02:33 +00:00
|
|
|
#define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY (jpeg2000_parse_debug);
|
|
|
|
#define GST_CAT_DEFAULT jpeg2000_parse_debug
|
|
|
|
|
|
|
|
static GstStaticPadTemplate srctemplate =
|
2016-06-12 17:53:18 +00:00
|
|
|
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
|
2016-06-01 23:02:33 +00:00
|
|
|
GST_PAD_ALWAYS,
|
|
|
|
GST_STATIC_CAPS ("image/x-jpc,"
|
2016-06-12 17:53:18 +00:00
|
|
|
" width = (int)[1, MAX], height = (int)[1, MAX],"
|
2016-06-21 16:41:46 +00:00
|
|
|
GST_JPEG2000_SAMPLING_LIST ","
|
|
|
|
GST_JPEG2000_COLORSPACE_LIST ","
|
2016-06-12 17:53:18 +00:00
|
|
|
" parsed = (boolean) true;"
|
|
|
|
"image/x-j2c,"
|
2016-06-01 23:02:33 +00:00
|
|
|
" width = (int)[1, MAX], height = (int)[1, MAX],"
|
2016-06-21 16:41:46 +00:00
|
|
|
GST_JPEG2000_SAMPLING_LIST ","
|
|
|
|
GST_JPEG2000_COLORSPACE_LIST "," " parsed = (boolean) true")
|
2016-06-01 23:02:33 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
static GstStaticPadTemplate sinktemplate =
|
2016-06-08 15:32:28 +00:00
|
|
|
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
|
2016-06-01 23:02:33 +00:00
|
|
|
GST_PAD_ALWAYS,
|
|
|
|
GST_STATIC_CAPS ("image/x-jpc,"
|
2016-06-21 16:41:46 +00:00
|
|
|
GST_JPEG2000_SAMPLING_LIST ";"
|
2016-06-12 17:53:18 +00:00
|
|
|
"image/x-jpc, "
|
2016-06-21 16:41:46 +00:00
|
|
|
GST_JPEG2000_COLORSPACE_LIST ";"
|
2016-06-12 17:53:18 +00:00
|
|
|
"image/x-j2c,"
|
2016-06-21 16:41:46 +00:00
|
|
|
GST_JPEG2000_SAMPLING_LIST ";"
|
|
|
|
"image/x-j2c, " GST_JPEG2000_COLORSPACE_LIST)
|
2016-06-01 23:02:33 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
#define parent_class gst_jpeg2000_parse_parent_class
|
|
|
|
G_DEFINE_TYPE (GstJPEG2000Parse, gst_jpeg2000_parse, GST_TYPE_BASE_PARSE);
|
|
|
|
|
|
|
|
static gboolean gst_jpeg2000_parse_start (GstBaseParse * parse);
|
|
|
|
static gboolean gst_jpeg2000_parse_event (GstBaseParse * parse,
|
|
|
|
GstEvent * event);
|
|
|
|
static GstFlowReturn gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
|
|
|
|
GstBaseParseFrame * frame, gint * skipsize);
|
2016-06-12 17:53:18 +00:00
|
|
|
static gboolean gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse,
|
|
|
|
GstCaps * caps);
|
2016-06-01 23:02:33 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
|
|
|
|
{
|
|
|
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_INIT (jpeg2000_parse_debug, "jpeg2000parse", 0,
|
|
|
|
"jpeg 2000 parser");
|
|
|
|
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class, "JPEG 2000 parser",
|
|
|
|
"Codec/Parser/Video/Image",
|
|
|
|
"Parses JPEG 2000 files", "Aaron Boxer <boxerab@gmail.com>");
|
|
|
|
|
|
|
|
/* Override BaseParse vfuncs */
|
2016-06-12 17:53:18 +00:00
|
|
|
parse_class->set_sink_caps =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_set_sink_caps);
|
2016-06-01 23:02:33 +00:00
|
|
|
parse_class->start = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_start);
|
|
|
|
parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_event);
|
|
|
|
parse_class->handle_frame =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_handle_frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_jpeg2000_parse_start (GstBaseParse * parse)
|
|
|
|
{
|
|
|
|
GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
|
|
|
|
GST_DEBUG_OBJECT (jpeg2000parse, "start");
|
|
|
|
gst_base_parse_set_min_frame_size (parse, GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
|
|
|
|
|
|
|
|
jpeg2000parse->width = 0;
|
|
|
|
jpeg2000parse->height = 0;
|
|
|
|
|
2016-06-21 16:41:46 +00:00
|
|
|
jpeg2000parse->sampling = GST_JPEG2000_SAMPLING_NONE;
|
|
|
|
jpeg2000parse->colorspace = GST_JPEG2000_COLORSPACE_NONE;
|
2016-06-12 17:53:18 +00:00
|
|
|
jpeg2000parse->codec_format = GST_JPEG2000_PARSE_NO_CODEC;
|
2016-06-01 23:02:33 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2016-06-08 15:32:28 +00:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-06-01 23:02:33 +00:00
|
|
|
static gboolean
|
|
|
|
gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event)
|
|
|
|
{
|
|
|
|
gboolean res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (parse, event);
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
|
|
case GST_EVENT_FLUSH_STOP:
|
|
|
|
gst_base_parse_set_min_frame_size (parse,
|
|
|
|
GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
|
|
|
|
GstBaseParseFrame * frame, gint * skipsize)
|
|
|
|
{
|
|
|
|
GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
|
|
|
|
GstMapInfo map;
|
|
|
|
GstByteReader reader;
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
guint eoc_offset = 0;
|
|
|
|
GstCaps *current_caps = NULL;
|
|
|
|
GstStructure *current_caps_struct = NULL;
|
2016-09-24 14:05:16 +00:00
|
|
|
const gchar *colorspace_string = NULL;
|
2016-06-21 16:41:46 +00:00
|
|
|
GstJPEG2000Colorspace colorspace = GST_JPEG2000_COLORSPACE_NONE;
|
2016-06-01 23:02:33 +00:00
|
|
|
guint x0, y0, x1, y1;
|
2016-06-08 15:32:28 +00:00
|
|
|
guint width = 0, height = 0;
|
2016-06-01 23:02:33 +00:00
|
|
|
guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
|
2016-06-08 15:32:28 +00:00
|
|
|
guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
|
2016-06-01 23:02:33 +00:00
|
|
|
guint16 numcomps;
|
|
|
|
guint16 compno;
|
2016-06-21 16:41:46 +00:00
|
|
|
GstJPEG2000Sampling parsed_sampling = GST_JPEG2000_SAMPLING_NONE;
|
2016-09-24 14:05:16 +00:00
|
|
|
const gchar *sink_sampling_string = NULL;
|
2016-06-21 16:41:46 +00:00
|
|
|
GstJPEG2000Sampling sink_sampling = GST_JPEG2000_SAMPLING_NONE;
|
|
|
|
GstJPEG2000Sampling source_sampling = GST_JPEG2000_SAMPLING_NONE;
|
2016-06-01 23:02:33 +00:00
|
|
|
guint magic_offset = 0;
|
2016-06-12 17:53:18 +00:00
|
|
|
guint j2c_box_id_offset = 0;
|
|
|
|
guint num_prefix_bytes = 0; /* number of bytes to skip before actual code stream */
|
2016-06-08 15:32:28 +00:00
|
|
|
GstCaps *src_caps = NULL;
|
2016-06-12 17:53:18 +00:00
|
|
|
guint frame_size = 0;
|
2016-06-30 11:47:50 +00:00
|
|
|
gboolean is_j2c = jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C;
|
2016-06-01 23:02:33 +00:00
|
|
|
|
|
|
|
if (!gst_buffer_map (frame->buffer, &map, GST_MAP_READ)) {
|
|
|
|
GST_ERROR_OBJECT (jpeg2000parse, "Unable to map buffer");
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_byte_reader_init (&reader, map.data, map.size);
|
2016-06-30 11:47:50 +00:00
|
|
|
num_prefix_bytes = GST_JPEG2000_MARKER_SIZE;
|
2016-06-12 17:53:18 +00:00
|
|
|
|
2016-06-30 11:47:50 +00:00
|
|
|
if (is_j2c) {
|
|
|
|
num_prefix_bytes +=
|
|
|
|
GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID;
|
2016-06-12 17:53:18 +00:00
|
|
|
/* check for "jp2c" */
|
|
|
|
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 (j2c_box_id_offset == -1) {
|
|
|
|
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
|
|
|
|
("Missing contiguous code stream box for j2c stream"));
|
|
|
|
ret = GST_FLOW_ERROR;
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
}
|
2016-06-01 23:02:33 +00:00
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
/* Look for magic. If found, skip to beginning of frame */
|
2016-06-01 23:02:33 +00:00
|
|
|
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) {
|
2016-06-12 17:53:18 +00:00
|
|
|
*skipsize = gst_byte_reader_get_size (&reader) - num_prefix_bytes;
|
2016-06-01 23:02:33 +00:00
|
|
|
goto beach;
|
2016-07-01 13:05:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* see if we need to skip any bytes at beginning of frame */
|
|
|
|
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 (is_j2c)
|
|
|
|
*skipsize -= GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES;
|
|
|
|
if (*skipsize > 0)
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_j2c) {
|
|
|
|
/* sanity check on box id offset */
|
|
|
|
if (j2c_box_id_offset + GST_JPEG2000_JP2_SIZE_OF_BOX_ID != magic_offset) {
|
|
|
|
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
|
|
|
|
("Corrupt contiguous code stream box for j2c stream"));
|
|
|
|
ret = GST_FLOW_ERROR;
|
|
|
|
goto beach;
|
2016-06-30 11:47:50 +00:00
|
|
|
}
|
|
|
|
|
2016-07-01 13:05:49 +00:00
|
|
|
/* check that we have enough bytes for the J2C box length */
|
|
|
|
if (j2c_box_id_offset < GST_JPEG2000_JP2_SIZE_OF_BOX_LEN) {
|
|
|
|
*skipsize = gst_byte_reader_get_size (&reader) - num_prefix_bytes;
|
|
|
|
goto beach;
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
2016-07-01 13:05:49 +00:00
|
|
|
|
|
|
|
if (!gst_byte_reader_skip (&reader,
|
|
|
|
j2c_box_id_offset - GST_JPEG2000_JP2_SIZE_OF_BOX_LEN))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
/* read the box length, and adjust num_prefix_bytes accordingly */
|
|
|
|
if (!gst_byte_reader_get_uint32_be (&reader, &frame_size))
|
|
|
|
goto beach;
|
|
|
|
num_prefix_bytes -= GST_JPEG2000_JP2_SIZE_OF_BOX_LEN;
|
|
|
|
|
|
|
|
/* bail out if not enough data for frame */
|
|
|
|
if ((gst_byte_reader_get_size (&reader) < frame_size))
|
|
|
|
goto beach;
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 2 to skip marker size, and another 2 to skip rsiz field */
|
2016-06-12 17:53:18 +00:00
|
|
|
if (!gst_byte_reader_skip (&reader, num_prefix_bytes + 2 + 2))
|
2016-06-01 23:02:33 +00:00
|
|
|
goto beach;
|
|
|
|
|
|
|
|
if (!gst_byte_reader_get_uint32_be (&reader, &x1))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
if (!gst_byte_reader_get_uint32_be (&reader, &y1))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
if (!gst_byte_reader_get_uint32_be (&reader, &x0))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
if (!gst_byte_reader_get_uint32_be (&reader, &y0))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
/* sanity check on image dimensions */
|
|
|
|
if (x1 < x0 || y1 < y0) {
|
|
|
|
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
|
|
|
|
("Nonsensical image dimensions %d,%d,%d,%d", x0, y0, x1, y1));
|
2016-06-12 17:53:18 +00:00
|
|
|
ret = GST_FLOW_ERROR;
|
2016-06-01 23:02:33 +00:00
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
width = x1 - x0;
|
|
|
|
height = y1 - y0;
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (jpeg2000parse, "Parsed image dimensions %d,%d", width,
|
|
|
|
height);
|
|
|
|
|
|
|
|
/* skip tile dimensions */
|
|
|
|
if (!gst_byte_reader_skip (&reader, 4 * 4))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
/* read number of components */
|
|
|
|
if (!gst_byte_reader_get_uint16_be (&reader, &numcomps))
|
|
|
|
goto beach;
|
|
|
|
|
2016-06-08 15:32:28 +00:00
|
|
|
if (numcomps == 2 || numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) {
|
2016-06-01 23:02:33 +00:00
|
|
|
GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
|
|
|
|
("Unsupported number of components %d", numcomps));
|
|
|
|
ret = GST_FLOW_NOT_NEGOTIATED;
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
|
|
|
|
if (!current_caps) {
|
|
|
|
GST_ERROR_OBJECT (jpeg2000parse, "Unable to get current caps");
|
|
|
|
ret = GST_FLOW_NOT_NEGOTIATED;
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_caps_struct = gst_caps_get_structure (current_caps, 0);
|
|
|
|
if (!current_caps_struct) {
|
2016-06-12 17:53:18 +00:00
|
|
|
GST_ERROR_OBJECT (jpeg2000parse,
|
|
|
|
"Unable to get structure of current caps struct");
|
2016-06-01 23:02:33 +00:00
|
|
|
ret = GST_FLOW_NOT_NEGOTIATED;
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
2016-09-24 14:05:16 +00:00
|
|
|
colorspace_string = gst_structure_get_string
|
|
|
|
(current_caps_struct, "colorspace");
|
|
|
|
if (colorspace_string)
|
|
|
|
colorspace = gst_jpeg2000_colorspace_from_string (colorspace_string);
|
|
|
|
sink_sampling_string = gst_structure_get_string
|
|
|
|
(current_caps_struct, "sampling");
|
|
|
|
if (sink_sampling_string)
|
|
|
|
sink_sampling = gst_jpeg2000_sampling_from_string (sink_sampling_string);
|
2016-06-01 23:02:33 +00:00
|
|
|
|
|
|
|
for (compno = 0; compno < numcomps; ++compno) {
|
|
|
|
|
|
|
|
/* skip Ssiz (precision and signed/unsigned bit ) */
|
|
|
|
if (!gst_byte_reader_skip (&reader, 1))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
if (!gst_byte_reader_get_uint8 (&reader, dx + compno))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
if (!gst_byte_reader_get_uint8 (&reader, dy + compno))
|
|
|
|
goto beach;
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (jpeg2000parse,
|
|
|
|
"Parsed sub-sampling %d,%d for component %d", dx[compno], dy[compno],
|
|
|
|
compno);
|
|
|
|
}
|
|
|
|
|
2016-06-08 15:32:28 +00:00
|
|
|
/*** sanity check on sub-sampling *****/
|
|
|
|
if (dx[0] != 1 || dy[0] != 1) {
|
|
|
|
GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel");
|
|
|
|
}
|
|
|
|
if (dx[1] != dx[2] || dy[1] != dy[2]) {
|
|
|
|
GST_WARNING_OBJECT (jpeg2000parse,
|
|
|
|
"Chroma channel sub-sampling factors are not equal");
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
for (compno = 0; compno < numcomps; ++compno) {
|
2016-06-21 16:41:46 +00:00
|
|
|
if (colorspace != GST_JPEG2000_COLORSPACE_NONE
|
|
|
|
&& (colorspace != GST_JPEG2000_COLORSPACE_YUV)
|
|
|
|
&& (dx[compno] > 1 || dy[compno] > 1)) {
|
2016-06-01 23:02:33 +00:00
|
|
|
GST_WARNING_OBJECT (jpeg2000parse,
|
2016-06-08 15:32:28 +00:00
|
|
|
"Sub-sampled RGB or monochrome color spaces");
|
|
|
|
}
|
2016-06-21 16:41:46 +00:00
|
|
|
if (sink_sampling != GST_JPEG2000_SAMPLING_NONE) {
|
2016-06-08 15:32:28 +00:00
|
|
|
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]) {
|
2016-06-21 16:41:46 +00:00
|
|
|
GstJPEG2000Colorspace inferred_colorspace =
|
|
|
|
GST_JPEG2000_COLORSPACE_NONE;
|
2016-06-08 15:32:28 +00:00
|
|
|
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 */
|
2016-06-21 16:41:46 +00:00
|
|
|
if (gst_jpeg2000_sampling_is_mono (sink_sampling))
|
|
|
|
inferred_colorspace = GST_JPEG2000_COLORSPACE_GRAY;
|
|
|
|
else if (gst_jpeg2000_sampling_is_rgb (sink_sampling))
|
|
|
|
inferred_colorspace = GST_JPEG2000_COLORSPACE_RGB;
|
|
|
|
else if (gst_jpeg2000_sampling_is_yuv (sink_sampling))
|
|
|
|
inferred_colorspace = GST_JPEG2000_COLORSPACE_YUV;
|
2016-06-08 15:32:28 +00:00
|
|
|
else if (colorspace)
|
|
|
|
inferred_colorspace = colorspace;
|
2016-06-21 16:41:46 +00:00
|
|
|
if (inferred_colorspace != GST_JPEG2000_COLORSPACE_NONE) {
|
|
|
|
sink_sampling = GST_JPEG2000_SAMPLING_NONE;
|
2016-06-08 15:32:28 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-08 15:32:28 +00:00
|
|
|
/*************************************/
|
|
|
|
|
|
|
|
/* if colorspace is present, we can work out the parsed_sampling field */
|
2016-06-21 16:41:46 +00:00
|
|
|
if (colorspace != GST_JPEG2000_COLORSPACE_NONE) {
|
|
|
|
if (colorspace == GST_JPEG2000_COLORSPACE_YUV) {
|
2016-06-08 15:32:28 +00:00
|
|
|
if (numcomps == 4) {
|
|
|
|
guint i;
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_YBRA4444_EXT;
|
2016-06-08 15:32:28 +00:00
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
if (dx[i] > 1 || dy[i] > 1) {
|
|
|
|
GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled YUVA images");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (numcomps == 3) {
|
|
|
|
/* use sub-sampling from U chroma channel */
|
|
|
|
if (dx[1] == 1 && dy[1] == 1) {
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
|
2016-06-08 15:32:28 +00:00
|
|
|
} else if (dx[1] == 2 && dy[1] == 2) {
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_YBR420;
|
2016-06-08 15:32:28 +00:00
|
|
|
} else if (dx[1] == 4 && dy[1] == 2) {
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_YBR410;
|
2016-06-08 15:32:28 +00:00
|
|
|
} else if (dx[1] == 2 && dy[1] == 1) {
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_YBR422;
|
2016-06-08 15:32:28 +00:00
|
|
|
} else {
|
2016-06-01 23:02:33 +00:00
|
|
|
GST_WARNING_OBJECT (jpeg2000parse,
|
2016-06-08 15:32:28 +00:00
|
|
|
"Unsupported sub-sampling factors %d,%d", dx[1], dy[1]);
|
|
|
|
/* best effort */
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-21 16:41:46 +00:00
|
|
|
} else if (colorspace == GST_JPEG2000_COLORSPACE_GRAY) {
|
|
|
|
parsed_sampling = GST_JPEG2000_SAMPLING_GRAYSCALE;
|
2016-06-08 15:32:28 +00:00
|
|
|
} else {
|
2016-06-21 16:41:46 +00:00
|
|
|
parsed_sampling =
|
|
|
|
(numcomps ==
|
2016-07-11 21:02:53 +00:00
|
|
|
4) ? GST_JPEG2000_SAMPLING_RGBA : GST_JPEG2000_SAMPLING_RGB;
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-06-21 16:41:46 +00:00
|
|
|
if (gst_jpeg2000_sampling_is_mono (sink_sampling)) {
|
|
|
|
colorspace = GST_JPEG2000_COLORSPACE_GRAY;
|
|
|
|
} else if (gst_jpeg2000_sampling_is_rgb (sink_sampling)) {
|
|
|
|
colorspace = GST_JPEG2000_COLORSPACE_RGB;
|
2016-06-01 23:02:33 +00:00
|
|
|
} else {
|
2016-06-08 15:32:28 +00:00
|
|
|
/* best effort */
|
2016-06-21 16:41:46 +00:00
|
|
|
colorspace = GST_JPEG2000_COLORSPACE_YUV;
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-21 16:41:46 +00:00
|
|
|
|
2016-06-08 15:32:28 +00:00
|
|
|
/* now we can set the source caps, if something has changed */
|
2016-06-21 16:41:46 +00:00
|
|
|
source_sampling =
|
|
|
|
sink_sampling !=
|
|
|
|
GST_JPEG2000_SAMPLING_NONE ? sink_sampling : parsed_sampling;
|
|
|
|
if (width != jpeg2000parse->width || height != jpeg2000parse->height
|
|
|
|
|| jpeg2000parse->sampling != source_sampling
|
|
|
|
|| jpeg2000parse->colorspace != colorspace) {
|
2016-06-08 15:32:28 +00:00
|
|
|
gint fr_num = 0, fr_denom = 0;
|
|
|
|
|
|
|
|
jpeg2000parse->width = width;
|
|
|
|
jpeg2000parse->height = height;
|
|
|
|
jpeg2000parse->sampling = source_sampling;
|
|
|
|
jpeg2000parse->colorspace = colorspace;
|
2016-06-01 23:02:33 +00:00
|
|
|
|
|
|
|
src_caps =
|
|
|
|
gst_caps_new_simple (gst_structure_get_name (current_caps_struct),
|
2016-06-08 15:32:28 +00:00
|
|
|
"width", G_TYPE_INT, width, "height", G_TYPE_INT, height,
|
2016-06-21 16:41:46 +00:00
|
|
|
"colorspace", G_TYPE_STRING,
|
|
|
|
gst_jpeg2000_colorspace_to_string (colorspace), "sampling",
|
|
|
|
G_TYPE_STRING, gst_jpeg2000_sampling_to_string (source_sampling), NULL);
|
2016-06-01 23:02:33 +00:00
|
|
|
|
|
|
|
if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num,
|
|
|
|
&fr_denom)) {
|
|
|
|
gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fr_num,
|
|
|
|
fr_denom, NULL);
|
|
|
|
} else {
|
|
|
|
GST_WARNING_OBJECT (jpeg2000parse, "No framerate set");
|
|
|
|
}
|
|
|
|
if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), src_caps)) {
|
|
|
|
GST_ERROR_OBJECT (jpeg2000parse, "Unable to set source caps");
|
|
|
|
ret = GST_FLOW_NOT_NEGOTIATED;
|
|
|
|
gst_caps_unref (src_caps);
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
gst_caps_unref (src_caps);
|
|
|
|
}
|
2016-06-12 17:53:18 +00:00
|
|
|
/*************************************************/
|
2016-06-01 23:02:33 +00:00
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
/* look for EOC to mark frame end */
|
2016-06-01 23:02:33 +00:00
|
|
|
/* look for EOC end of codestream marker */
|
|
|
|
eoc_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0x0000ffff,
|
|
|
|
0xFFD9, 0, gst_byte_reader_get_remaining (&reader));
|
|
|
|
|
|
|
|
if (eoc_offset != -1) {
|
|
|
|
/* add 4 for eoc marker and eoc marker size */
|
2016-06-12 17:53:18 +00:00
|
|
|
guint eoc_frame_size = gst_byte_reader_get_pos (&reader) + eoc_offset + 4;
|
2016-06-01 23:02:33 +00:00
|
|
|
GST_DEBUG_OBJECT (jpeg2000parse,
|
2016-06-12 17:53:18 +00:00
|
|
|
"Found EOC at offset = %d, frame size = %d", eoc_offset,
|
|
|
|
eoc_frame_size);
|
2016-06-01 23:02:33 +00:00
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
/* bail out if not enough data for frame */
|
|
|
|
if (gst_byte_reader_get_size (&reader) < eoc_frame_size)
|
2016-06-01 23:02:33 +00:00
|
|
|
goto beach;
|
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
if (frame_size && frame_size != eoc_frame_size) {
|
|
|
|
GST_WARNING_OBJECT (jpeg2000parse,
|
|
|
|
"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;
|
2016-06-01 23:02:33 +00:00
|
|
|
}
|
|
|
|
|
2016-06-12 17:53:18 +00:00
|
|
|
/* 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);
|
|
|
|
|
2016-06-01 23:02:33 +00:00
|
|
|
beach:
|
|
|
|
if (current_caps)
|
|
|
|
gst_caps_unref (current_caps);
|
|
|
|
gst_buffer_unmap (frame->buffer, &map);
|
|
|
|
return ret;
|
|
|
|
}
|