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