• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer JPEG 2000 Parser
2  * Copyright (C) <2016-2017> Grok Image Compression Inc.
3  *  @author Aaron Boxer <boxerab@gmail.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #include "gstvideoparserselements.h"
26 #include "gstjpeg2000parse.h"
27 #include <gst/base/base.h>
28 
29 /* Not used at the moment
30 static gboolean gst_jpeg2000_parse_is_cinema(guint16 rsiz)   {
31 	return ((rsiz >= GST_JPEG2000_PARSE_PROFILE_CINEMA_2K) && (rsiz <= GST_JPEG2000_PARSE_PROFILE_CINEMA_S4K));
32 }
33 static gboolean gst_jpeg2000_parse_is_storage(guint16 rsiz)   {
34 	return (rsiz == GST_JPEG2000_PARSE_PROFILE_CINEMA_LTS);
35 }
36 */
37 static gboolean
gst_jpeg2000_parse_is_broadcast(guint16 rsiz)38 gst_jpeg2000_parse_is_broadcast (guint16 rsiz)
39 {
40   return ((rsiz >= GST_JPEG2000_PARSE_PROFILE_BC_SINGLE) &&
41       (rsiz <= ((GST_JPEG2000_PARSE_PROFILE_BC_MULTI_R) | (0x000b)))
42       && ((rsiz & (~GST_JPEG2000_PARSE_PROFILE_BC_MASK)) == 0));
43 }
44 
45 static gboolean
gst_jpeg2000_parse_is_imf(guint16 rsiz)46 gst_jpeg2000_parse_is_imf (guint16 rsiz)
47 {
48   return ((rsiz >= GST_JPEG2000_PARSE_PROFILE_IMF_2K)
49       && (rsiz <= ((GST_JPEG2000_PARSE_PROFILE_IMF_8K_R) | (0x009b))));
50 }
51 
52 static gboolean
gst_jpeg2000_parse_is_part_2(guint16 rsiz)53 gst_jpeg2000_parse_is_part_2 (guint16 rsiz)
54 {
55   return (rsiz & GST_JPEG2000_PARSE_PROFILE_PART2);
56 }
57 
58 
59 
60 static void
gst_jpeg2000_parse_get_subsampling(guint16 compno,GstJPEG2000Sampling sampling,guint8 * dx,guint8 * dy)61 gst_jpeg2000_parse_get_subsampling (guint16 compno,
62     GstJPEG2000Sampling sampling, guint8 * dx, guint8 * dy)
63 {
64   *dx = 1;
65   *dy = 1;
66   if (compno == 1 || compno == 2) {
67     if (sampling == GST_JPEG2000_SAMPLING_YBR422) {
68       *dx = 2;
69     } else if (sampling == GST_JPEG2000_SAMPLING_YBR420) {
70       *dx = 2;
71       *dy = 2;
72     } else if (sampling == GST_JPEG2000_SAMPLING_YBR411) {
73       *dx = 4;
74       *dy = 1;
75     } else if (sampling == GST_JPEG2000_SAMPLING_YBR410) {
76       *dx = 4;
77       *dy = 4;
78     }
79   }
80 }
81 
82 #define GST_JPEG2000_JP2_SIZE_OF_BOX_ID  	4
83 #define GST_JPEG2000_JP2_SIZE_OF_BOX_LEN	4
84 #define GST_JPEG2000_MARKER_SIZE  	4
85 
86 
87 /* J2C has 8 bytes preceding J2K magic: 4 for size of box, and 4 for fourcc */
88 #define GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES (GST_JPEG2000_JP2_SIZE_OF_BOX_LEN +  GST_JPEG2000_JP2_SIZE_OF_BOX_ID)
89 
90 /* SOC marker plus minimum size of SIZ marker */
91 #define GST_JPEG2000_PARSE_MIN_FRAME_SIZE (GST_JPEG2000_MARKER_SIZE + GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES + 36)
92 
93 #define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
94 #define GST_JPEG2000_PARSE_J2C_BOX_ID 0x6a703263        /* "jp2c" */
95 
96 GST_DEBUG_CATEGORY (jpeg2000_parse_debug);
97 #define GST_CAT_DEFAULT jpeg2000_parse_debug
98 
99 static GstStaticPadTemplate srctemplate =
100     GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
101     GST_PAD_ALWAYS,
102     GST_STATIC_CAPS ("image/x-jpc,"
103         " width = (int)[1, MAX], height = (int)[1, MAX],"
104         GST_JPEG2000_SAMPLING_LIST ","
105         GST_JPEG2000_COLORSPACE_LIST ","
106         " profile = (int)[0, 49151],"
107         " parsed = (boolean) true;"
108         "image/x-j2c,"
109         " width = (int)[1, MAX], height = (int)[1, MAX],"
110         GST_JPEG2000_SAMPLING_LIST ","
111         GST_JPEG2000_COLORSPACE_LIST ","
112         " profile = (int)[0, 49151],"
113         " parsed = (boolean) true ; "
114         "image/x-jpc-striped,"
115         " width = (int)[1, MAX], height = (int)[1, MAX],"
116         GST_JPEG2000_SAMPLING_LIST ","
117         GST_JPEG2000_COLORSPACE_LIST ","
118         " profile = (int)[0, 49151],"
119         " num-stripes = [ 2, MAX ], parsed = (boolean) true;")
120     );
121 
122 static GstStaticPadTemplate sinktemplate =
123     GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
124     GST_PAD_ALWAYS,
125     GST_STATIC_CAPS ("image/jp2; image/x-jpc; image/x-j2c; "
126         "image/x-jpc-striped"));
127 
128 #define parent_class gst_jpeg2000_parse_parent_class
129 G_DEFINE_TYPE (GstJPEG2000Parse, gst_jpeg2000_parse, GST_TYPE_BASE_PARSE);
130 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (jpeg2000parse, "jpeg2000parse",
131     GST_RANK_PRIMARY, GST_TYPE_JPEG2000_PARSE,
132     videoparsers_element_init (plugin));
133 
134 static gboolean gst_jpeg2000_parse_start (GstBaseParse * parse);
135 static gboolean gst_jpeg2000_parse_event (GstBaseParse * parse,
136     GstEvent * event);
137 static void gst_jpeg2000_parse_reset (GstBaseParse * parse,
138     gboolean hard_reset);
139 static GstFlowReturn gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
140     GstBaseParseFrame * frame, gint * skipsize);
141 static GstFlowReturn gst_jpeg2000_parse_pre_push_frame (GstBaseParse * parse,
142     GstBaseParseFrame * frame);
143 static gboolean gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse,
144     GstCaps * caps);
145 static GstJPEG2000ParseFormats
146 format_from_media_type (const GstStructure * structure);
147 
148 static void
gst_jpeg2000_parse_class_init(GstJPEG2000ParseClass * klass)149 gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
150 {
151   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
152   GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
153 
154   GST_DEBUG_CATEGORY_INIT (jpeg2000_parse_debug, "jpeg2000parse", 0,
155       "jpeg 2000 parser");
156 
157   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
158   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
159   gst_element_class_set_static_metadata (gstelement_class, "JPEG 2000 parser",
160       "Codec/Parser/Video/Image",
161       "Parses JPEG 2000 files", "Aaron Boxer <boxerab@gmail.com>");
162 
163   /* Override BaseParse vfuncs */
164   parse_class->set_sink_caps =
165       GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_set_sink_caps);
166   parse_class->start = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_start);
167   parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_event);
168   parse_class->handle_frame =
169       GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_handle_frame);
170   parse_class->pre_push_frame =
171       GST_DEBUG_FUNCPTR (gst_jpeg2000_parse_pre_push_frame);
172 }
173 
174 static void
gst_jpeg2000_parse_reset(GstBaseParse * parse,gboolean hard_reset)175 gst_jpeg2000_parse_reset (GstBaseParse * parse, gboolean hard_reset)
176 {
177   GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
178 
179   jpeg2000parse->parsed_j2c_box = FALSE;
180   jpeg2000parse->frame_size = 0;
181   if (hard_reset) {
182     jpeg2000parse->width = 0;
183     jpeg2000parse->height = 0;
184     jpeg2000parse->sampling = GST_JPEG2000_SAMPLING_NONE;
185     jpeg2000parse->colorspace = GST_JPEG2000_COLORSPACE_NONE;
186     jpeg2000parse->src_codec_format = GST_JPEG2000_PARSE_NO_CODEC;
187     jpeg2000parse->sink_codec_format = GST_JPEG2000_PARSE_NO_CODEC;
188   }
189 }
190 
191 static gboolean
gst_jpeg2000_parse_start(GstBaseParse * parse)192 gst_jpeg2000_parse_start (GstBaseParse * parse)
193 {
194   GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
195   GST_DEBUG_OBJECT (jpeg2000parse, "start");
196   gst_base_parse_set_min_frame_size (parse, GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
197   gst_jpeg2000_parse_reset (parse, TRUE);
198 
199   return TRUE;
200 }
201 
202 
203 static void
gst_jpeg2000_parse_init(GstJPEG2000Parse * jpeg2000parse)204 gst_jpeg2000_parse_init (GstJPEG2000Parse * jpeg2000parse)
205 {
206   GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
207   GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
208 }
209 
210 static gboolean
gst_jpeg2000_parse_set_sink_caps(GstBaseParse * parse,GstCaps * caps)211 gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps)
212 {
213   GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
214   GstStructure *caps_struct = gst_caps_get_structure (caps, 0);
215 
216   gst_jpeg2000_parse_reset (parse, TRUE);
217   jpeg2000parse->sink_codec_format = format_from_media_type (caps_struct);
218 
219   return TRUE;
220 }
221 
222 static gboolean
gst_jpeg2000_parse_event(GstBaseParse * parse,GstEvent * event)223 gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event)
224 {
225   gboolean res;
226   switch (GST_EVENT_TYPE (event)) {
227     case GST_EVENT_FLUSH_STOP:
228       gst_base_parse_set_min_frame_size (parse,
229           GST_JPEG2000_PARSE_MIN_FRAME_SIZE);
230       res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (parse, event);
231       break;
232     default:
233       res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (parse, event);
234       break;
235   }
236   return res;
237 }
238 
239 static GstJPEG2000ParseFormats
format_from_media_type(const GstStructure * structure)240 format_from_media_type (const GstStructure * structure)
241 {
242   const char *media_type = gst_structure_get_name (structure);
243   if (!strcmp (media_type, "image/x-j2c"))
244     return GST_JPEG2000_PARSE_J2C;
245   if (!strcmp (media_type, "image/x-jpc"))
246     return GST_JPEG2000_PARSE_JPC;
247   if (!strcmp (media_type, "image/jp2"))
248     return GST_JPEG2000_PARSE_JP2;
249   return GST_JPEG2000_PARSE_NO_CODEC;
250 }
251 
252 /* check downstream caps to configure media type */
253 static gboolean
gst_jpeg2000_parse_negotiate(GstJPEG2000Parse * parse,GstCaps * in_caps)254 gst_jpeg2000_parse_negotiate (GstJPEG2000Parse * parse, GstCaps * in_caps)
255 {
256   GstCaps *caps;
257   guint codec_format = GST_JPEG2000_PARSE_NO_CODEC;
258 
259   if (in_caps != NULL && !gst_caps_is_fixed (in_caps))
260     return FALSE;
261 
262   caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (parse));
263   GST_DEBUG_OBJECT (parse, "allowed caps: %" GST_PTR_FORMAT, caps);
264 
265   /* concentrate on leading structure, since decodebin parser
266    * capsfilter always includes parser template caps */
267   if (caps) {
268     caps = gst_caps_truncate (caps);
269     GST_DEBUG_OBJECT (parse, "negotiating with caps: %" GST_PTR_FORMAT, caps);
270   }
271 
272   if (in_caps && caps) {
273     if (gst_caps_can_intersect (in_caps, caps)) {
274       GST_DEBUG_OBJECT (parse, "downstream accepts upstream caps");
275       codec_format =
276           format_from_media_type (gst_caps_get_structure (in_caps, 0));
277       gst_caps_unref (caps);
278       caps = NULL;
279     }
280   }
281 
282   if (caps && !gst_caps_is_empty (caps)) {
283     /* fixate to avoid ambiguity with lists when parsing */
284     caps = gst_caps_fixate (caps);
285     codec_format = format_from_media_type (gst_caps_get_structure (caps, 0));
286   }
287   if (caps)
288     gst_caps_unref (caps);
289 
290   GST_DEBUG_OBJECT (parse, "selected codec format %d", codec_format);
291   parse->src_codec_format = codec_format;
292 
293   return codec_format != GST_JPEG2000_PARSE_NO_CODEC;
294 }
295 
296 static const char *
media_type_from_codec_format(GstJPEG2000ParseFormats f)297 media_type_from_codec_format (GstJPEG2000ParseFormats f)
298 {
299   switch (f) {
300     case GST_JPEG2000_PARSE_J2C:
301       return "image/x-j2c";
302     case GST_JPEG2000_PARSE_JP2:
303       return "image/jp2";
304     case GST_JPEG2000_PARSE_JPC:
305       return "image/x-jpc";
306     default:
307       g_assert_not_reached ();
308       return "invalid/x-invalid";
309   }
310 }
311 
312 static GstFlowReturn
gst_jpeg2000_parse_handle_frame(GstBaseParse * parse,GstBaseParseFrame * frame,gint * skipsize)313 gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
314     GstBaseParseFrame * frame, gint * skipsize)
315 {
316   GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
317   GstMapInfo map;
318   GstByteReader reader;
319   GstFlowReturn ret = GST_FLOW_OK;
320   guint eoc_offset = 0;
321   GstCaps *current_caps = NULL;
322   GstStructure *current_caps_struct = NULL;
323   GstJPEG2000Colorspace colorspace = GST_JPEG2000_COLORSPACE_NONE;
324   guint x0 = 0, y0 = 0, x1 = 0, y1 = 0;
325   guint width = 0, height = 0;
326   guint i;
327   guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
328   guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS];
329   guint16 numcomps = 0;
330   guint16 capabilities = 0;
331   guint16 profile = 0;
332   gboolean validate_main_level = FALSE;
333   guint8 main_level = 0;
334   guint8 sub_level = 0;
335   guint16 compno = 0;
336   GstJPEG2000Sampling parsed_sampling = GST_JPEG2000_SAMPLING_NONE;
337   const gchar *sink_sampling_string = NULL;
338   GstJPEG2000Sampling sink_sampling = GST_JPEG2000_SAMPLING_NONE;
339   GstJPEG2000Sampling source_sampling = GST_JPEG2000_SAMPLING_NONE;
340   guint num_prefix_bytes = 0;   /* number of bytes to skip before actual code stream */
341   GstCaps *src_caps = NULL;
342   guint eoc_frame_size = 0;
343   gint num_stripes = 1;
344   gint stripe_height = 0;
345 
346   for (i = 0; i < GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS; ++i) {
347     dx[i] = 1;
348     dy[i] = 1;
349   }
350 
351   if (!gst_buffer_map (frame->buffer, &map, GST_MAP_READ)) {
352     GST_ERROR_OBJECT (jpeg2000parse, "Unable to map buffer");
353     return GST_FLOW_ERROR;
354   }
355   gst_byte_reader_init (&reader, map.data, map.size);
356   current_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
357 
358   /* Parse J2C box */
359   if (!jpeg2000parse->parsed_j2c_box) {
360     gboolean has_j2c_box = FALSE;
361     gboolean is_j2c_src;
362     guint j2c_box_id_offset = -1;
363     guint magic_offset = -1;
364 
365     /* Look for magic. If not found, get more data */
366     magic_offset = gst_byte_reader_masked_scan_uint32_peek (&reader, 0xffffffff,
367         GST_JPEG2000_PARSE_J2K_MAGIC, 0,
368         gst_byte_reader_get_remaining (&reader), NULL);
369     if (magic_offset == -1)
370       goto beach;
371     GST_DEBUG_OBJECT (jpeg2000parse, "Found magic at offset = %d",
372         magic_offset);
373 
374     if (magic_offset > 0) {
375       j2c_box_id_offset =
376           gst_byte_reader_masked_scan_uint32_peek (&reader, 0xffffffff,
377           GST_JPEG2000_PARSE_J2C_BOX_ID, 0, magic_offset, NULL);
378       has_j2c_box = j2c_box_id_offset != -1;
379       /* sanity check on box id offset */
380       if (has_j2c_box) {
381         if (j2c_box_id_offset + GST_JPEG2000_JP2_SIZE_OF_BOX_ID != magic_offset
382             || j2c_box_id_offset < GST_JPEG2000_JP2_SIZE_OF_BOX_LEN) {
383           GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
384               ("Corrupt contiguous code stream box for j2c stream"));
385           ret = GST_FLOW_ERROR;
386           goto beach;
387         }
388         /* read the box length */
389         if (!gst_byte_reader_skip (&reader,
390                 j2c_box_id_offset - GST_JPEG2000_JP2_SIZE_OF_BOX_LEN))
391           goto beach;
392         if (!gst_byte_reader_get_uint32_be (&reader,
393                 &jpeg2000parse->frame_size))
394           goto beach;
395       }
396     }
397     jpeg2000parse->parsed_j2c_box = TRUE;
398 
399     /* determine downstream j2k format */
400     if (jpeg2000parse->src_codec_format == GST_JPEG2000_PARSE_NO_CODEC) {
401       if (!gst_jpeg2000_parse_negotiate (jpeg2000parse, current_caps)) {
402         ret = GST_FLOW_NOT_NEGOTIATED;
403         goto beach;
404       }
405     }
406 
407     /* treat JP2 as J2C */
408     if (jpeg2000parse->src_codec_format == GST_JPEG2000_PARSE_JP2)
409       jpeg2000parse->src_codec_format = GST_JPEG2000_PARSE_J2C;
410     is_j2c_src = jpeg2000parse->src_codec_format == GST_JPEG2000_PARSE_J2C;
411     /* we can't convert JPC to any other format */
412     if (!has_j2c_box && is_j2c_src) {
413       ret = GST_FLOW_NOT_NEGOTIATED;
414       goto beach;
415     }
416     /* sanity check on sink caps */
417     if (jpeg2000parse->sink_codec_format > GST_JPEG2000_PARSE_J2C
418         && !has_j2c_box) {
419       GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
420           ("Expected J2C box but found none."));
421       ret = GST_FLOW_ERROR;
422       goto beach;
423     }
424     /* adjust frame size for JPC src caps */
425     if (jpeg2000parse->frame_size &&
426         jpeg2000parse->src_codec_format == GST_JPEG2000_PARSE_JPC) {
427       jpeg2000parse->frame_size -=
428           GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID;
429     }
430     /* see if we need to skip any bytes at beginning of frame */
431     *skipsize = magic_offset;
432     if (is_j2c_src)
433       *skipsize -= GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES;
434     if (*skipsize > 0)
435       goto beach;
436     /* reset reader to beginning of buffer */
437     gst_byte_reader_set_pos (&reader, 0);
438   }
439 
440   /* we keep prefix bytes but skip them in order
441    * to process the rest of the frame */
442   /* magic prefix */
443   num_prefix_bytes = GST_JPEG2000_MARKER_SIZE;
444   /* J2C box prefix */
445   if (jpeg2000parse->src_codec_format == GST_JPEG2000_PARSE_J2C) {
446     num_prefix_bytes +=
447         GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID;
448   }
449   /* bail out if not enough data for code stream */
450   if (jpeg2000parse->frame_size &&
451       (gst_byte_reader_get_size (&reader) < jpeg2000parse->frame_size))
452     goto beach;
453 
454   /* skip prefix and 2 bytes for marker size */
455   if (!gst_byte_reader_skip (&reader, num_prefix_bytes + 2))
456     goto beach;
457 
458   if (!gst_byte_reader_get_uint16_be (&reader, &capabilities))
459     goto beach;
460 
461   profile = capabilities & GST_JPEG2000_PARSE_PROFILE_MASK;
462   if (!gst_jpeg2000_parse_is_part_2 (capabilities)) {
463     if ((profile > GST_JPEG2000_PARSE_PROFILE_CINEMA_LTS)
464         && !gst_jpeg2000_parse_is_broadcast (profile)
465         && !gst_jpeg2000_parse_is_imf (profile)) {
466       GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
467           ("Unrecognized JPEG 2000 profile %d", profile));
468       ret = GST_FLOW_ERROR;
469       goto beach;
470     }
471     if (gst_jpeg2000_parse_is_broadcast (profile)) {
472       main_level = capabilities & 0xF;
473       validate_main_level = TRUE;
474     } else if (gst_jpeg2000_parse_is_imf (profile)) {
475       main_level = capabilities & 0xF;
476       validate_main_level = TRUE;
477       sub_level = (capabilities >> 4) & 0xF;
478       if (sub_level > 9) {
479         GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
480             ("Sub level %d is invalid", sub_level));
481         ret = GST_FLOW_ERROR;
482         goto beach;
483       }
484     }
485     if (validate_main_level && main_level > 11) {
486       GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
487           ("Main level %d is invalid", main_level));
488       ret = GST_FLOW_ERROR;
489       goto beach;
490 
491     }
492   }
493 
494 
495   if (!gst_byte_reader_get_uint32_be (&reader, &x1))
496     goto beach;
497 
498   if (!gst_byte_reader_get_uint32_be (&reader, &y1))
499     goto beach;
500 
501   if (!gst_byte_reader_get_uint32_be (&reader, &x0))
502     goto beach;
503 
504   if (!gst_byte_reader_get_uint32_be (&reader, &y0))
505     goto beach;
506 
507   /* sanity check on image dimensions */
508   if (x1 < x0 || y1 < y0) {
509     GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
510         ("Nonsensical image dimensions %d,%d,%d,%d", x0, y0, x1, y1));
511     ret = GST_FLOW_ERROR;
512     goto beach;
513   }
514 
515   width = x1 - x0;
516   height = y1 - y0;
517 
518   GST_DEBUG_OBJECT (jpeg2000parse, "Parsed image dimensions %d,%d", width,
519       height);
520 
521   /* skip tile dimensions */
522   if (!gst_byte_reader_skip (&reader, 4 * 4))
523     goto beach;
524 
525   /* read number of components */
526   if (!gst_byte_reader_get_uint16_be (&reader, &numcomps))
527     goto beach;
528 
529   if (numcomps == 0 || numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) {
530     GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL,
531         ("Unsupported number of components %d", numcomps));
532     ret = GST_FLOW_NOT_NEGOTIATED;
533     goto beach;
534   }
535   if (current_caps) {
536     const gchar *colorspace_string = NULL;
537     current_caps_struct = gst_caps_get_structure (current_caps, 0);
538     if (!current_caps_struct) {
539       GST_ERROR_OBJECT (jpeg2000parse,
540           "Unable to get structure of current caps struct");
541       ret = GST_FLOW_NOT_NEGOTIATED;
542       goto beach;
543     }
544 
545     colorspace_string = gst_structure_get_string
546         (current_caps_struct, "colorspace");
547     if (colorspace_string)
548       colorspace = gst_jpeg2000_colorspace_from_string (colorspace_string);
549     sink_sampling_string = gst_structure_get_string
550         (current_caps_struct, "sampling");
551     if (sink_sampling_string)
552       sink_sampling = gst_jpeg2000_sampling_from_string (sink_sampling_string);
553 
554   }
555 
556   if (colorspace == GST_JPEG2000_COLORSPACE_NONE) {
557     /* guess color space based on number of components       */
558     if (numcomps == 0 || numcomps > 4) {
559       GST_ERROR_OBJECT (jpeg2000parse,
560           "Unable to guess color space from number of components %d", numcomps);
561       ret = GST_FLOW_NOT_NEGOTIATED;
562       goto beach;
563     }
564     colorspace =
565         (numcomps >=
566         3) ? GST_JPEG2000_COLORSPACE_RGB : GST_JPEG2000_COLORSPACE_GRAY;
567     if (numcomps == 4) {
568       GST_WARNING_OBJECT (jpeg2000parse, "No caps available: assuming RGBA");
569     } else if (numcomps == 3) {
570       GST_WARNING_OBJECT (jpeg2000parse, "No caps available: assuming RGB");
571     } else if (numcomps == 2) {
572       GST_WARNING_OBJECT (jpeg2000parse,
573           "No caps available: assuming grayscale with alpha");
574     }
575 
576   }
577 
578   for (compno = 0; compno < numcomps; ++compno) {
579 
580     /* skip Ssiz (precision and signed/unsigned bit )  */
581     if (!gst_byte_reader_skip (&reader, 1))
582       goto beach;
583 
584     if (!gst_byte_reader_get_uint8 (&reader, dx + compno))
585       goto beach;
586 
587     if (!gst_byte_reader_get_uint8 (&reader, dy + compno))
588       goto beach;
589 
590     GST_DEBUG_OBJECT (jpeg2000parse,
591         "Parsed sub-sampling %d,%d for component %d", dx[compno], dy[compno],
592         compno);
593   }
594 
595   /*** sanity check on sub-sampling *****/
596   if (dx[0] != 1 || dy[0] != 1) {
597     GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel");
598   }
599   if (dx[1] != dx[2] || dy[1] != dy[2]) {
600     GST_WARNING_OBJECT (jpeg2000parse,
601         "Chroma channel sub-sampling factors are not equal");
602   }
603   for (compno = 0; compno < numcomps; ++compno) {
604     if (colorspace != GST_JPEG2000_COLORSPACE_NONE
605         && (colorspace != GST_JPEG2000_COLORSPACE_YUV)
606         && (dx[compno] > 1 || dy[compno] > 1)) {
607       GST_WARNING_OBJECT (jpeg2000parse,
608           "Sub-sampled RGB or monochrome color spaces");
609     }
610     if (sink_sampling != GST_JPEG2000_SAMPLING_NONE) {
611       guint8 dx_caps, dy_caps;
612       gst_jpeg2000_parse_get_subsampling (compno, sink_sampling, &dx_caps,
613           &dy_caps);
614       if (dx_caps != dx[compno] || dy_caps != dy[compno]) {
615         GstJPEG2000Colorspace inferred_colorspace =
616             GST_JPEG2000_COLORSPACE_NONE;
617         GST_WARNING_OBJECT (jpeg2000parse,
618             "Sink caps sub-sampling %d,%d for channel %d does not match stream sub-sampling %d,%d",
619             dx_caps, dy_caps, compno, dx[compno], dy[compno]);
620         /* try to guess correct color space */
621         if (gst_jpeg2000_sampling_is_mono (sink_sampling))
622           inferred_colorspace = GST_JPEG2000_COLORSPACE_GRAY;
623         else if (gst_jpeg2000_sampling_is_rgb (sink_sampling))
624           inferred_colorspace = GST_JPEG2000_COLORSPACE_RGB;
625         else if (gst_jpeg2000_sampling_is_yuv (sink_sampling))
626           inferred_colorspace = GST_JPEG2000_COLORSPACE_YUV;
627         else if (colorspace)
628           inferred_colorspace = colorspace;
629         if (inferred_colorspace != GST_JPEG2000_COLORSPACE_NONE) {
630           sink_sampling = GST_JPEG2000_SAMPLING_NONE;
631           colorspace = inferred_colorspace;
632           break;
633         } else {
634           /* unrecognized sink_sampling and no colorspace */
635           GST_ERROR_OBJECT (jpeg2000parse,
636               "Unrecognized sink sampling field and no sink colorspace field");
637           ret = GST_FLOW_NOT_NEGOTIATED;
638           goto beach;
639         }
640       }
641     }
642   }
643   /*************************************/
644 
645   /* if colorspace is present, we can work out the parsed_sampling field */
646   if (colorspace != GST_JPEG2000_COLORSPACE_NONE) {
647     if (colorspace == GST_JPEG2000_COLORSPACE_YUV) {
648       if (numcomps == 4) {
649         guint i;
650         parsed_sampling = GST_JPEG2000_SAMPLING_YBRA4444_EXT;
651         for (i = 0; i < 4; ++i) {
652           if (dx[i] > 1 || dy[i] > 1) {
653             GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled YUVA images");
654           }
655         }
656       } else if (numcomps == 3) {
657         /* use sub-sampling from U chroma channel */
658         if (dx[1] == 1 && dy[1] == 1) {
659           parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
660         } else if (dx[1] == 2 && dy[1] == 2) {
661           parsed_sampling = GST_JPEG2000_SAMPLING_YBR420;
662         } else if (dx[1] == 4 && dy[1] == 1) {
663           parsed_sampling = GST_JPEG2000_SAMPLING_YBR411;
664         } else if (dx[1] == 4 && dy[1] == 4) {
665           parsed_sampling = GST_JPEG2000_SAMPLING_YBR410;
666         } else if (dx[1] == 2 && dy[1] == 1) {
667           parsed_sampling = GST_JPEG2000_SAMPLING_YBR422;
668         } else {
669           GST_WARNING_OBJECT (jpeg2000parse,
670               "Unsupported sub-sampling factors %d,%d", dx[1], dy[1]);
671           /* best effort */
672           parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
673         }
674       }
675     } else if (colorspace == GST_JPEG2000_COLORSPACE_GRAY) {
676       parsed_sampling = GST_JPEG2000_SAMPLING_GRAYSCALE;
677     } else {
678       parsed_sampling =
679           (numcomps ==
680           4) ? GST_JPEG2000_SAMPLING_RGBA : GST_JPEG2000_SAMPLING_RGB;
681     }
682   } else {
683     if (gst_jpeg2000_sampling_is_mono (sink_sampling)) {
684       colorspace = GST_JPEG2000_COLORSPACE_GRAY;
685     } else if (gst_jpeg2000_sampling_is_rgb (sink_sampling)) {
686       colorspace = GST_JPEG2000_COLORSPACE_RGB;
687     } else {
688       /* best effort */
689       colorspace = GST_JPEG2000_COLORSPACE_YUV;
690     }
691   }
692 
693   /* use caps height if in sub-frame mode, as encoded frame height will be
694    * strictly less than full frame height */
695   if (current_caps_struct &&
696       gst_structure_has_name (current_caps_struct, "image/x-jpc-striped")) {
697     gint h;
698 
699     if (!gst_structure_get_int (current_caps_struct, "num-stripes",
700             &num_stripes) || num_stripes < 2) {
701       GST_ELEMENT_ERROR (parse, STREAM, FORMAT, (NULL),
702           ("Striped JPEG 2000 is missing the stripe count"));
703       ret = GST_FLOW_ERROR;
704       goto beach;
705     }
706 
707     if (!gst_structure_get_int (current_caps_struct, "stripe-height",
708             &stripe_height)) {
709       stripe_height = height;
710     } else if (stripe_height != height &&
711         !GST_BUFFER_FLAG_IS_SET (frame->buffer, GST_BUFFER_FLAG_MARKER)) {
712       GST_WARNING_OBJECT (parse,
713           "Only the last stripe is expected to be different"
714           " from the stripe height (%d != %u)", height, stripe_height);
715     }
716 
717     gst_structure_get_int (current_caps_struct, "height", &h);
718     height = h;
719   }
720 
721   /* now we can set the source caps, if something has changed */
722   source_sampling =
723       sink_sampling !=
724       GST_JPEG2000_SAMPLING_NONE ? sink_sampling : parsed_sampling;
725   if (width != jpeg2000parse->width || height != jpeg2000parse->height
726       || jpeg2000parse->sampling != source_sampling
727       || jpeg2000parse->colorspace != colorspace) {
728     gint fr_num = 0, fr_denom = 0;
729 
730     src_caps =
731         gst_caps_new_simple (num_stripes > 1 ? "image/x-jpc-striped" :
732         media_type_from_codec_format (jpeg2000parse->src_codec_format),
733         "width", G_TYPE_INT, width,
734         "height", G_TYPE_INT, height,
735         "colorspace", G_TYPE_STRING,
736         gst_jpeg2000_colorspace_to_string (colorspace), "sampling",
737         G_TYPE_STRING, gst_jpeg2000_sampling_to_string (source_sampling),
738         "profile", G_TYPE_INT, profile, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
739 
740     if (num_stripes > 1)
741       gst_caps_set_simple (src_caps, "num-stripes", G_TYPE_INT, num_stripes,
742           "stripe_height", G_TYPE_INT, stripe_height, NULL);
743 
744     if (gst_jpeg2000_parse_is_broadcast (capabilities)
745         || gst_jpeg2000_parse_is_imf (capabilities)) {
746       gst_caps_set_simple (src_caps, "main-level", G_TYPE_INT, main_level,
747           NULL);
748       if (gst_jpeg2000_parse_is_imf (capabilities)) {
749         gst_caps_set_simple (src_caps, "sub-level", G_TYPE_INT, sub_level,
750             NULL);
751       }
752     }
753 
754     if (current_caps_struct) {
755       const gchar *caps_string;
756 
757       caps_string = gst_structure_get_string
758           (current_caps_struct, "colorimetry");
759       if (caps_string) {
760         gst_caps_set_simple (src_caps, "colorimetry", G_TYPE_STRING,
761             caps_string, NULL);
762       }
763       caps_string = gst_structure_get_string
764           (current_caps_struct, "interlace-mode");
765       if (caps_string) {
766         gst_caps_set_simple (src_caps, "interlace-mode", G_TYPE_STRING,
767             caps_string, NULL);
768       }
769       caps_string = gst_structure_get_string
770           (current_caps_struct, "field-order");
771       if (caps_string) {
772         gst_caps_set_simple (src_caps, "field-order", G_TYPE_STRING,
773             caps_string, NULL);
774       }
775       caps_string = gst_structure_get_string
776           (current_caps_struct, "multiview-mode");
777       if (caps_string) {
778         gst_caps_set_simple (src_caps, "multiview-mode", G_TYPE_STRING,
779             caps_string, NULL);
780       }
781       caps_string = gst_structure_get_string
782           (current_caps_struct, "chroma-site");
783       if (caps_string) {
784         gst_caps_set_simple (src_caps, "chroma-site", G_TYPE_STRING,
785             caps_string, NULL);
786       }
787       if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num,
788               &fr_denom)) {
789         gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fr_num,
790             fr_denom, NULL);
791       } else {
792         GST_WARNING_OBJECT (jpeg2000parse, "No framerate set");
793       }
794     }
795 
796     if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), src_caps)) {
797       GST_ERROR_OBJECT (jpeg2000parse, "Unable to set source caps");
798       ret = GST_FLOW_NOT_NEGOTIATED;
799       gst_caps_unref (src_caps);
800       goto beach;
801     }
802     gst_caps_unref (src_caps);
803     jpeg2000parse->width = width;
804     jpeg2000parse->height = height;
805     jpeg2000parse->sampling = source_sampling;
806     jpeg2000parse->colorspace = colorspace;
807   }
808   /*************************************************/
809 
810   /* look for EOC to mark frame end */
811   /* look for EOC end of codestream marker  */
812   eoc_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0x0000ffff,
813       0xFFD9, 0, gst_byte_reader_get_remaining (&reader));
814   if (eoc_offset == -1)
815     goto beach;
816 
817   /* add 4 for eoc marker and eoc marker size */
818   eoc_frame_size = gst_byte_reader_get_pos (&reader) + eoc_offset + 4;
819   GST_DEBUG_OBJECT (jpeg2000parse,
820       "Found EOC at offset = %d, frame size = %d", eoc_offset, eoc_frame_size);
821 
822   /* bail out if not enough data for frame */
823   if (gst_byte_reader_get_size (&reader) < eoc_frame_size)
824     goto beach;
825 
826   if (jpeg2000parse->frame_size && jpeg2000parse->frame_size != eoc_frame_size) {
827     GST_WARNING_OBJECT (jpeg2000parse,
828         "Frame size %d from contiguous code size does not equal frame size %d signaled by eoc",
829         jpeg2000parse->frame_size, eoc_frame_size);
830   }
831   jpeg2000parse->frame_size = eoc_frame_size;
832 
833 
834   /* clean up and finish frame */
835   if (current_caps)
836     gst_caps_unref (current_caps);
837   gst_buffer_unmap (frame->buffer, &map);
838   ret = gst_base_parse_finish_frame (parse, frame, jpeg2000parse->frame_size);
839   if (ret != GST_FLOW_OK)
840     gst_jpeg2000_parse_reset (parse, TRUE);
841   return ret;
842 
843 beach:
844   if (current_caps)
845     gst_caps_unref (current_caps);
846   gst_buffer_unmap (frame->buffer, &map);
847   if (ret != GST_FLOW_OK)
848     gst_jpeg2000_parse_reset (parse, TRUE);
849   return ret;
850 }
851 
852 static GstFlowReturn
gst_jpeg2000_parse_pre_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)853 gst_jpeg2000_parse_pre_push_frame (GstBaseParse * parse,
854     GstBaseParseFrame * frame)
855 {
856   gst_jpeg2000_parse_reset (parse, FALSE);
857   return GST_FLOW_OK;
858 
859 }
860