1 /* GStreamer JPEG 2000 Parser
2 * Copyright (C) <2016> Grok Image Compession Inc.
3 * @author Aaron Boxer <boxerab@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstjpeg2000parse.h"
26 #include <gst/base/base.h>
31 gst_jpeg2000_parse_get_subsampling (GstJPEG2000Sampling sampling, guint8 * dx,
36 if (sampling == GST_JPEG2000_SAMPLING_YBR422) {
38 } else if (sampling == GST_JPEG2000_SAMPLING_YBR420) {
41 } else if (sampling == GST_JPEG2000_SAMPLING_YBR410) {
47 #define GST_JPEG2000_JP2_SIZE_OF_BOX_ID 4
48 #define GST_JPEG2000_JP2_SIZE_OF_BOX_LEN 4
49 #define GST_JPEG2000_MARKER_SIZE 4
52 /* J2C has 8 bytes preceding J2K magic: 4 for size of box, and 4 for fourcc */
53 #define GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES (GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID)
55 /* SOC marker plus minimum size of SIZ marker */
56 #define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (GST_JPEG2000_MARKER_SIZE + GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES + 36)
58 #define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
60 GST_DEBUG_CATEGORY (jpeg2000_parse_debug);
61 #define GST_CAT_DEFAULT jpeg2000_parse_debug
63 static GstStaticPadTemplate srctemplate =
64 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
66 GST_STATIC_CAPS ("image/x-jpc,"
67 " width = (int)[1, MAX], height = (int)[1, MAX],"
68 GST_JPEG2000_SAMPLING_LIST ","
69 GST_JPEG2000_COLORSPACE_LIST ","
70 " parsed = (boolean) true;"
72 " width = (int)[1, MAX], height = (int)[1, MAX],"
73 GST_JPEG2000_SAMPLING_LIST ","
74 GST_JPEG2000_COLORSPACE_LIST "," " parsed = (boolean) true")
77 static GstStaticPadTemplate sinktemplate =
78 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
80 GST_STATIC_CAPS ("image/x-jpc,"
81 GST_JPEG2000_SAMPLING_LIST ";"
83 GST_JPEG2000_COLORSPACE_LIST ";"
85 GST_JPEG2000_SAMPLING_LIST ";"
86 "image/x-j2c, " GST_JPEG2000_COLORSPACE_LIST)
89 #define parent_class gst_jpeg2000_parse_parent_class
90 G_DEFINE_TYPE (GstJPEG2000Parse, gst_jpeg2000_parse, GST_TYPE_BASE_PARSE);
92 static gboolean gst_jpeg2000_parse_start (GstBaseParse * parse);
93 static gboolean gst_jpeg2000_parse_event (GstBaseParse * parse,
95 static GstFlowReturn gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
96 GstBaseParseFrame * frame, gint * skipsize);
97 static gboolean gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse,
101 gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
103 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
104 GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
106 GST_DEBUG_CATEGORY_INIT (jpeg2000_parse_debug, "jpeg2000parse", 0,
109 gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
110 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
111 gst_element_class_set_static_metadata (gstelement_class, "JPEG 2000 parser",
112 "Codec/Parser/Video/Image",
113 "Parses JPEG 2000 files", "Aaron Boxer <boxerab@gmail.com>");
115 /* Override BaseParse vfuncs */
116 parse_class->set_sink_caps =
117 GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_set_sink_caps);
118 parse_class->start = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_start);
119 parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_event);
120 parse_class->handle_frame =
121 GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_handle_frame);
125 gst_jpeg2000_parse_start (GstBaseParse * parse)
127 GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
128 GST_DEBUG_OBJECT (jpeg2000parse, "start");
129 gst_base_parse_set_min_frame_size (parse, GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
131 jpeg2000parse->width = 0;
132 jpeg2000parse->height = 0;
134 jpeg2000parse->sampling = GST_JPEG2000_SAMPLING_NONE;
135 jpeg2000parse->colorspace = GST_JPEG2000_COLORSPACE_NONE;
136 jpeg2000parse->codec_format = GST_JPEG2000_PARSE_NO_CODEC;
142 gst_jpeg2000_parse_init (GstJPEG2000Parse * jpeg2000parse)
144 GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
145 GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
149 gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps)
151 GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
152 GstStructure *caps_struct = gst_caps_get_structure (caps, 0);
154 if (gst_structure_has_name (caps_struct, "image/jp2")) {
155 jpeg2000parse->codec_format = GST_JPEG2000_PARSE_JP2;
156 } else if (gst_structure_has_name (caps_struct, "image/x-j2c")) {
157 jpeg2000parse->codec_format = GST_JPEG2000_PARSE_J2C;
158 } else if (gst_structure_has_name (caps_struct, "image/x-jpc")) {
159 jpeg2000parse->codec_format = GST_JPEG2000_PARSE_JPC;
166 gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event)
168 gboolean res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (parse, event);
169 switch (GST_EVENT_TYPE (event)) {
170 case GST_EVENT_FLUSH_STOP:
171 gst_base_parse_set_min_frame_size (parse,
172 GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
181 gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
182 GstBaseParseFrame * frame, gint * skipsize)
184 GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
186 GstByteReader reader;
187 GstFlowReturn ret = GST_FLOW_OK;
188 guint eoc_offset = 0;
189 GstCaps *current_caps = NULL;
190 GstStructure *current_caps_struct = NULL;
191 GstJPEG2000Colorspace colorspace = GST_JPEG2000_COLORSPACE_NONE;
192 guint x0, y0, x1, y1;
193 guint width = 0, height = 0;
194 guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
195 guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
198 GstJPEG2000Sampling parsed_sampling = GST_JPEG2000_SAMPLING_NONE;
199 GstJPEG2000Sampling sink_sampling = GST_JPEG2000_SAMPLING_NONE;
200 GstJPEG2000Sampling source_sampling = GST_JPEG2000_SAMPLING_NONE;
201 guint magic_offset = 0;
202 guint j2c_box_id_offset = 0;
203 guint num_prefix_bytes = 0; /* number of bytes to skip before actual code stream */
204 GstCaps *src_caps = NULL;
205 guint frame_size = 0;
206 gboolean is_j2c = jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C;
208 if (!gst_buffer_map (frame->buffer, &map, GST_MAP_READ)) {
209 GST_ERROR_OBJECT (jpeg2000parse, "Unable to map buffer");
210 return GST_FLOW_ERROR;
213 gst_byte_reader_init (&reader, map.data, map.size);
214 num_prefix_bytes = GST_JPEG2000_MARKER_SIZE;
218 GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID;
219 /* check for "jp2c" */
220 j2c_box_id_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
221 GST_MAKE_FOURCC ('j', 'p', '2', 'c'), 0,
222 gst_byte_reader_get_remaining (&reader));
224 if (j2c_box_id_offset == -1) {
225 GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
226 ("Missing contiguous code stream box for j2c stream"));
227 ret = GST_FLOW_ERROR;
232 /* Look for magic. If found, skip to beginning of frame */
233 magic_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
234 GST_JPEG2000_PARSE_J2K_MAGIC, 0, gst_byte_reader_get_remaining (&reader));
235 if (magic_offset == -1) {
236 *skipsize = gst_byte_reader_get_size (&reader) - num_prefix_bytes;
240 /* see if we need to skip any bytes at beginning of frame */
241 GST_DEBUG_OBJECT (jpeg2000parse, "Found magic at offset = %d",
243 if (magic_offset > 0) {
244 *skipsize = magic_offset;
245 /* J2C has 8 bytes preceding J2K magic */
247 *skipsize -= GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES;
254 /* sanity check on box id offset */
255 if (j2c_box_id_offset + GST_JPEG2000_JP2_SIZE_OF_BOX_ID != magic_offset) {
256 GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
257 ("Corrupt contiguous code stream box for j2c stream"));
258 ret = GST_FLOW_ERROR;
262 /* check that we have enough bytes for the J2C box length */
263 if (j2c_box_id_offset < GST_JPEG2000_JP2_SIZE_OF_BOX_LEN) {
264 *skipsize = gst_byte_reader_get_size (&reader) - num_prefix_bytes;
268 if (!gst_byte_reader_skip (&reader,
269 j2c_box_id_offset - GST_JPEG2000_JP2_SIZE_OF_BOX_LEN))
272 /* read the box length, and adjust num_prefix_bytes accordingly */
273 if (!gst_byte_reader_get_uint32_be (&reader, &frame_size))
275 num_prefix_bytes -= GST_JPEG2000_JP2_SIZE_OF_BOX_LEN;
277 /* bail out if not enough data for frame */
278 if ((gst_byte_reader_get_size (&reader) < frame_size))
283 /* 2 to skip marker size, and another 2 to skip rsiz field */
284 if (!gst_byte_reader_skip (&reader, num_prefix_bytes + 2 + 2))
287 if (!gst_byte_reader_get_uint32_be (&reader, &x1))
290 if (!gst_byte_reader_get_uint32_be (&reader, &y1))
293 if (!gst_byte_reader_get_uint32_be (&reader, &x0))
296 if (!gst_byte_reader_get_uint32_be (&reader, &y0))
299 /* sanity check on image dimensions */
300 if (x1 < x0 || y1 < y0) {
301 GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
302 ("Nonsensical image dimensions %d,%d,%d,%d", x0, y0, x1, y1));
303 ret = GST_FLOW_ERROR;
310 GST_DEBUG_OBJECT (jpeg2000parse, "Parsed image dimensions %d,%d", width,
313 /* skip tile dimensions */
314 if (!gst_byte_reader_skip (&reader, 4 * 4))
317 /* read number of components */
318 if (!gst_byte_reader_get_uint16_be (&reader, &numcomps))
321 if (numcomps == 2 || numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) {
322 GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
323 ("Unsupported number of components %d", numcomps));
324 ret = GST_FLOW_NOT_NEGOTIATED;
328 current_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
330 GST_ERROR_OBJECT (jpeg2000parse, "Unable to get current caps");
331 ret = GST_FLOW_NOT_NEGOTIATED;
335 current_caps_struct = gst_caps_get_structure (current_caps, 0);
336 if (!current_caps_struct) {
337 GST_ERROR_OBJECT (jpeg2000parse,
338 "Unable to get structure of current caps struct");
339 ret = GST_FLOW_NOT_NEGOTIATED;
344 gst_jpeg2000_colorspace_from_string (gst_structure_get_string
345 (current_caps_struct, "colorspace"));
347 gst_jpeg2000_sampling_from_string (gst_structure_get_string
348 (current_caps_struct, "sampling"));
350 for (compno = 0; compno < numcomps; ++compno) {
352 /* skip Ssiz (precision and signed/unsigned bit ) */
353 if (!gst_byte_reader_skip (&reader, 1))
356 if (!gst_byte_reader_get_uint8 (&reader, dx + compno))
359 if (!gst_byte_reader_get_uint8 (&reader, dy + compno))
362 GST_DEBUG_OBJECT (jpeg2000parse,
363 "Parsed sub-sampling %d,%d for component %d", dx[compno], dy[compno],
367 /*** sanity check on sub-sampling *****/
368 if (dx[0] != 1 || dy[0] != 1) {
369 GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel");
371 if (dx[1] != dx[2] || dy[1] != dy[2]) {
372 GST_WARNING_OBJECT (jpeg2000parse,
373 "Chroma channel sub-sampling factors are not equal");
375 for (compno = 0; compno < numcomps; ++compno) {
376 if (colorspace != GST_JPEG2000_COLORSPACE_NONE
377 && (colorspace != GST_JPEG2000_COLORSPACE_YUV)
378 && (dx[compno] > 1 || dy[compno] > 1)) {
379 GST_WARNING_OBJECT (jpeg2000parse,
380 "Sub-sampled RGB or monochrome color spaces");
382 if (sink_sampling != GST_JPEG2000_SAMPLING_NONE) {
383 guint8 dx_caps, dy_caps;
384 gst_jpeg2000_parse_get_subsampling (sink_sampling, &dx_caps, &dy_caps);
385 if (dx_caps != dx[compno] || dy_caps != dy[compno]) {
386 GstJPEG2000Colorspace inferred_colorspace =
387 GST_JPEG2000_COLORSPACE_NONE;
388 GST_WARNING_OBJECT (jpeg2000parse,
389 "Sink caps sub-sampling %d,%d for channel %d does not match stream sub-sampling %d,%d",
390 dx_caps, dy_caps, compno, dx[compno], dy[compno]);
391 /* try to guess correct color space */
392 if (gst_jpeg2000_sampling_is_mono (sink_sampling))
393 inferred_colorspace = GST_JPEG2000_COLORSPACE_GRAY;
394 else if (gst_jpeg2000_sampling_is_rgb (sink_sampling))
395 inferred_colorspace = GST_JPEG2000_COLORSPACE_RGB;
396 else if (gst_jpeg2000_sampling_is_yuv (sink_sampling))
397 inferred_colorspace = GST_JPEG2000_COLORSPACE_YUV;
399 inferred_colorspace = colorspace;
400 if (inferred_colorspace != GST_JPEG2000_COLORSPACE_NONE) {
401 sink_sampling = GST_JPEG2000_SAMPLING_NONE;
402 colorspace = inferred_colorspace;
405 /* unrecognized sink_sampling and no colorspace */
406 GST_ERROR_OBJECT (jpeg2000parse,
407 "Unrecognized sink sampling field and no sink colorspace field");
408 ret = GST_FLOW_NOT_NEGOTIATED;
414 /*************************************/
416 /* if colorspace is present, we can work out the parsed_sampling field */
417 if (colorspace != GST_JPEG2000_COLORSPACE_NONE) {
418 if (colorspace == GST_JPEG2000_COLORSPACE_YUV) {
421 parsed_sampling = GST_JPEG2000_SAMPLING_YBRA4444_EXT;
422 for (i = 0; i < 4; ++i) {
423 if (dx[i] > 1 || dy[i] > 1) {
424 GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled YUVA images");
427 } else if (numcomps == 3) {
428 /* use sub-sampling from U chroma channel */
429 if (dx[1] == 1 && dy[1] == 1) {
430 parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
431 } else if (dx[1] == 2 && dy[1] == 2) {
432 parsed_sampling = GST_JPEG2000_SAMPLING_YBR420;
433 } else if (dx[1] == 4 && dy[1] == 2) {
434 parsed_sampling = GST_JPEG2000_SAMPLING_YBR410;
435 } else if (dx[1] == 2 && dy[1] == 1) {
436 parsed_sampling = GST_JPEG2000_SAMPLING_YBR422;
438 GST_WARNING_OBJECT (jpeg2000parse,
439 "Unsupported sub-sampling factors %d,%d", dx[1], dy[1]);
441 parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
444 } else if (colorspace == GST_JPEG2000_COLORSPACE_GRAY) {
445 parsed_sampling = GST_JPEG2000_SAMPLING_GRAYSCALE;
449 4) ? GST_JPEG2000_SAMPLING_RGBA : GST_JPEG2000_COLORSPACE_RGB;
452 if (gst_jpeg2000_sampling_is_mono (sink_sampling)) {
453 colorspace = GST_JPEG2000_COLORSPACE_GRAY;
454 } else if (gst_jpeg2000_sampling_is_rgb (sink_sampling)) {
455 colorspace = GST_JPEG2000_COLORSPACE_RGB;
458 colorspace = GST_JPEG2000_COLORSPACE_YUV;
462 /* now we can set the source caps, if something has changed */
465 GST_JPEG2000_SAMPLING_NONE ? sink_sampling : parsed_sampling;
466 if (width != jpeg2000parse->width || height != jpeg2000parse->height
467 || jpeg2000parse->sampling != source_sampling
468 || jpeg2000parse->colorspace != colorspace) {
469 gint fr_num = 0, fr_denom = 0;
471 jpeg2000parse->width = width;
472 jpeg2000parse->height = height;
473 jpeg2000parse->sampling = source_sampling;
474 jpeg2000parse->colorspace = colorspace;
477 gst_caps_new_simple (gst_structure_get_name (current_caps_struct),
478 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height,
479 "colorspace", G_TYPE_STRING,
480 gst_jpeg2000_colorspace_to_string (colorspace), "sampling",
481 G_TYPE_STRING, gst_jpeg2000_sampling_to_string (source_sampling), NULL);
483 if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num,
485 gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fr_num,
488 GST_WARNING_OBJECT (jpeg2000parse, "No framerate set");
490 if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), src_caps)) {
491 GST_ERROR_OBJECT (jpeg2000parse, "Unable to set source caps");
492 ret = GST_FLOW_NOT_NEGOTIATED;
493 gst_caps_unref (src_caps);
496 gst_caps_unref (src_caps);
498 /*************************************************/
500 /* look for EOC to mark frame end */
501 /* look for EOC end of codestream marker */
502 eoc_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0x0000ffff,
503 0xFFD9, 0, gst_byte_reader_get_remaining (&reader));
505 if (eoc_offset != -1) {
506 /* add 4 for eoc marker and eoc marker size */
507 guint eoc_frame_size = gst_byte_reader_get_pos (&reader) + eoc_offset + 4;
508 GST_DEBUG_OBJECT (jpeg2000parse,
509 "Found EOC at offset = %d, frame size = %d", eoc_offset,
512 /* bail out if not enough data for frame */
513 if (gst_byte_reader_get_size (&reader) < eoc_frame_size)
516 if (frame_size && frame_size != eoc_frame_size) {
517 GST_WARNING_OBJECT (jpeg2000parse,
518 "Frame size %d from contiguous code size does not equal frame size %d signalled by eoc",
519 frame_size, eoc_frame_size);
521 frame_size = eoc_frame_size;
524 /* clean up and finish frame */
526 gst_caps_unref (current_caps);
527 gst_buffer_unmap (frame->buffer, &map);
528 return gst_base_parse_finish_frame (parse, frame, frame_size);
532 gst_caps_unref (current_caps);
533 gst_buffer_unmap (frame->buffer, &map);