gstjpeg2000parse: improved parsing of jpc magic and j2c box
[platform/upstream/gstreamer.git] / gst / videoparsers / gstjpeg2000parse.c
1 /* GStreamer JPEG 2000 Parser
2  * Copyright (C) <2016> Grok Image Compession 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
29
30 static void
31 gst_jpeg2000_parse_get_subsampling (GstJPEG2000Sampling sampling, guint8 * dx,
32     guint8 * dy)
33 {
34   *dx = 1;
35   *dy = 1;
36   if (sampling == GST_JPEG2000_SAMPLING_YBR422) {
37     *dx = 2;
38   } else if (sampling == GST_JPEG2000_SAMPLING_YBR420) {
39     *dx = 2;
40     *dy = 2;
41   } else if (sampling == GST_JPEG2000_SAMPLING_YBR410) {
42     *dx = 4;
43     *dy = 2;
44   }
45 }
46
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
50
51
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)
54
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)
57
58 #define GST_JPEG2000_PARSE_J2K_MAGIC 0xFF4FFF51
59
60 GST_DEBUG_CATEGORY (jpeg2000_parse_debug);
61 #define GST_CAT_DEFAULT jpeg2000_parse_debug
62
63 static GstStaticPadTemplate srctemplate =
64     GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
65     GST_PAD_ALWAYS,
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;"
71         "image/x-j2c,"
72         " width = (int)[1, MAX], height = (int)[1, MAX],"
73         GST_JPEG2000_SAMPLING_LIST ","
74         GST_JPEG2000_COLORSPACE_LIST "," " parsed = (boolean) true")
75     );
76
77 static GstStaticPadTemplate sinktemplate =
78     GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
79     GST_PAD_ALWAYS,
80     GST_STATIC_CAPS ("image/x-jpc,"
81         GST_JPEG2000_SAMPLING_LIST ";"
82         "image/x-jpc, "
83         GST_JPEG2000_COLORSPACE_LIST ";"
84         "image/x-j2c,"
85         GST_JPEG2000_SAMPLING_LIST ";"
86         "image/x-j2c, " GST_JPEG2000_COLORSPACE_LIST)
87     );
88
89 #define parent_class gst_jpeg2000_parse_parent_class
90 G_DEFINE_TYPE (GstJPEG2000Parse, gst_jpeg2000_parse, GST_TYPE_BASE_PARSE);
91
92 static gboolean gst_jpeg2000_parse_start (GstBaseParse * parse);
93 static gboolean gst_jpeg2000_parse_event (GstBaseParse * parse,
94     GstEvent * event);
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,
98     GstCaps * caps);
99
100 static void
101 gst_jpeg2000_parse_class_init (GstJPEG2000ParseClass * klass)
102 {
103   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
104   GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
105
106   GST_DEBUG_CATEGORY_INIT (jpeg2000_parse_debug, "jpeg2000parse", 0,
107       "jpeg 2000 parser");
108
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>");
114
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);
122 }
123
124 static gboolean
125 gst_jpeg2000_parse_start (GstBaseParse * parse)
126 {
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);
130
131   jpeg2000parse->width = 0;
132   jpeg2000parse->height = 0;
133
134   jpeg2000parse->sampling = GST_JPEG2000_SAMPLING_NONE;
135   jpeg2000parse->colorspace = GST_JPEG2000_COLORSPACE_NONE;
136   jpeg2000parse->codec_format = GST_JPEG2000_PARSE_NO_CODEC;
137   return TRUE;
138 }
139
140
141 static void
142 gst_jpeg2000_parse_init (GstJPEG2000Parse * jpeg2000parse)
143 {
144   GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
145   GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (jpeg2000parse));
146 }
147
148 static gboolean
149 gst_jpeg2000_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps)
150 {
151   GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
152   GstStructure *caps_struct = gst_caps_get_structure (caps, 0);
153
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;
160   }
161
162   return TRUE;
163 }
164
165 static gboolean
166 gst_jpeg2000_parse_event (GstBaseParse * parse, GstEvent * event)
167 {
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);
173       break;
174     default:
175       break;
176   }
177   return res;
178 }
179
180 static GstFlowReturn
181 gst_jpeg2000_parse_handle_frame (GstBaseParse * parse,
182     GstBaseParseFrame * frame, gint * skipsize)
183 {
184   GstJPEG2000Parse *jpeg2000parse = GST_JPEG2000_PARSE (parse);
185   GstMapInfo map;
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];
196   guint16 numcomps;
197   guint16 compno;
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;
207
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;
211   }
212
213   gst_byte_reader_init (&reader, map.data, map.size);
214   num_prefix_bytes = GST_JPEG2000_MARKER_SIZE;
215
216   if (is_j2c) {
217     num_prefix_bytes +=
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));
223
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;
228       goto beach;
229     }
230   }
231
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;
237     goto beach;
238   } else {
239
240     /* see if we need to skip any bytes at beginning of frame */
241     GST_DEBUG_OBJECT (jpeg2000parse, "Found magic at offset = %d",
242         magic_offset);
243     if (magic_offset > 0) {
244       *skipsize = magic_offset;
245       /* J2C has 8 bytes preceding J2K magic */
246       if (is_j2c)
247         *skipsize -= GST_JPEG2000_PARSE_SIZE_OF_J2C_PREFIX_BYTES;
248       if (*skipsize > 0)
249         goto beach;
250     }
251
252     if (is_j2c) {
253
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;
259         goto beach;
260       }
261
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;
265         goto beach;
266       }
267
268       if (!gst_byte_reader_skip (&reader,
269               j2c_box_id_offset - GST_JPEG2000_JP2_SIZE_OF_BOX_LEN))
270         goto beach;
271
272       /* read the box length, and adjust num_prefix_bytes accordingly  */
273       if (!gst_byte_reader_get_uint32_be (&reader, &frame_size))
274         goto beach;
275       num_prefix_bytes -= GST_JPEG2000_JP2_SIZE_OF_BOX_LEN;
276
277       /* bail out if not enough data for frame */
278       if ((gst_byte_reader_get_size (&reader) < frame_size))
279         goto beach;
280     }
281   }
282
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))
285     goto beach;
286
287   if (!gst_byte_reader_get_uint32_be (&reader, &x1))
288     goto beach;
289
290   if (!gst_byte_reader_get_uint32_be (&reader, &y1))
291     goto beach;
292
293   if (!gst_byte_reader_get_uint32_be (&reader, &x0))
294     goto beach;
295
296   if (!gst_byte_reader_get_uint32_be (&reader, &y0))
297     goto beach;
298
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;
304     goto beach;
305   }
306
307   width = x1 - x0;
308   height = y1 - y0;
309
310   GST_DEBUG_OBJECT (jpeg2000parse, "Parsed image dimensions %d,%d", width,
311       height);
312
313   /* skip tile dimensions */
314   if (!gst_byte_reader_skip (&reader, 4 * 4))
315     goto beach;
316
317   /* read number of components */
318   if (!gst_byte_reader_get_uint16_be (&reader, &numcomps))
319     goto beach;
320
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;
325     goto beach;
326   }
327
328   current_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
329   if (!current_caps) {
330     GST_ERROR_OBJECT (jpeg2000parse, "Unable to get current caps");
331     ret = GST_FLOW_NOT_NEGOTIATED;
332     goto beach;
333   }
334
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;
340     goto beach;
341   }
342
343   colorspace =
344       gst_jpeg2000_colorspace_from_string (gst_structure_get_string
345       (current_caps_struct, "colorspace"));
346   sink_sampling =
347       gst_jpeg2000_sampling_from_string (gst_structure_get_string
348       (current_caps_struct, "sampling"));
349
350   for (compno = 0; compno < numcomps; ++compno) {
351
352     /* skip Ssiz (precision and signed/unsigned bit )  */
353     if (!gst_byte_reader_skip (&reader, 1))
354       goto beach;
355
356     if (!gst_byte_reader_get_uint8 (&reader, dx + compno))
357       goto beach;
358
359     if (!gst_byte_reader_get_uint8 (&reader, dy + compno))
360       goto beach;
361
362     GST_DEBUG_OBJECT (jpeg2000parse,
363         "Parsed sub-sampling %d,%d for component %d", dx[compno], dy[compno],
364         compno);
365   }
366
367   /*** sanity check on sub-sampling *****/
368   if (dx[0] != 1 || dy[0] != 1) {
369     GST_WARNING_OBJECT (jpeg2000parse, "Sub-sampled luma channel");
370   }
371   if (dx[1] != dx[2] || dy[1] != dy[2]) {
372     GST_WARNING_OBJECT (jpeg2000parse,
373         "Chroma channel sub-sampling factors are not equal");
374   }
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");
381     }
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;
398         else if (colorspace)
399           inferred_colorspace = colorspace;
400         if (inferred_colorspace != GST_JPEG2000_COLORSPACE_NONE) {
401           sink_sampling = GST_JPEG2000_SAMPLING_NONE;
402           colorspace = inferred_colorspace;
403           break;
404         } else {
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;
409           goto beach;
410         }
411       }
412     }
413   }
414   /*************************************/
415
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) {
419       if (numcomps == 4) {
420         guint i;
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");
425           }
426         }
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;
437         } else {
438           GST_WARNING_OBJECT (jpeg2000parse,
439               "Unsupported sub-sampling factors %d,%d", dx[1], dy[1]);
440           /* best effort */
441           parsed_sampling = GST_JPEG2000_SAMPLING_YBR444;
442         }
443       }
444     } else if (colorspace == GST_JPEG2000_COLORSPACE_GRAY) {
445       parsed_sampling = GST_JPEG2000_SAMPLING_GRAYSCALE;
446     } else {
447       parsed_sampling =
448           (numcomps ==
449           4) ? GST_JPEG2000_SAMPLING_RGBA : GST_JPEG2000_COLORSPACE_RGB;
450     }
451   } else {
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;
456     } else {
457       /* best effort */
458       colorspace = GST_JPEG2000_COLORSPACE_YUV;
459     }
460   }
461
462   /* now we can set the source caps, if something has changed */
463   source_sampling =
464       sink_sampling !=
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;
470
471     jpeg2000parse->width = width;
472     jpeg2000parse->height = height;
473     jpeg2000parse->sampling = source_sampling;
474     jpeg2000parse->colorspace = colorspace;
475
476     src_caps =
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);
482
483     if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num,
484             &fr_denom)) {
485       gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fr_num,
486           fr_denom, NULL);
487     } else {
488       GST_WARNING_OBJECT (jpeg2000parse, "No framerate set");
489     }
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);
494       goto beach;
495     }
496     gst_caps_unref (src_caps);
497   }
498   /*************************************************/
499
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));
504
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,
510         eoc_frame_size);
511
512     /* bail out if not enough data for frame */
513     if (gst_byte_reader_get_size (&reader) < eoc_frame_size)
514       goto beach;
515
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);
520     }
521     frame_size = eoc_frame_size;
522   }
523
524   /* clean up and finish frame */
525   if (current_caps)
526     gst_caps_unref (current_caps);
527   gst_buffer_unmap (frame->buffer, &map);
528   return gst_base_parse_finish_frame (parse, frame, frame_size);
529
530 beach:
531   if (current_caps)
532     gst_caps_unref (current_caps);
533   gst_buffer_unmap (frame->buffer, &map);
534   return ret;
535 }