According to ISO/IEC 14996-12, size == 1 means a 64-bit extended-size field occurs *after* the 32-bit box type, not before. This fix should allow correct parsing of JXL files with extended-size boxes. Signed-off-by: Leo Izen <leo.izen@gmail.com>
		
			
				
	
	
		
			521 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			521 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * JPEG XL Header Parser
 | 
						|
 * Copyright (c) 2023 Leo Izen <leo.izen@gmail.com>
 | 
						|
 *
 | 
						|
 * This file is part of FFmpeg.
 | 
						|
 *
 | 
						|
 * FFmpeg is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * FFmpeg is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with FFmpeg; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
 | 
						|
#include "bytestream.h"
 | 
						|
#define UNCHECKED_BITSTREAM_READER 0
 | 
						|
#define BITSTREAM_READER_LE
 | 
						|
#include "get_bits.h"
 | 
						|
#include "jpegxl.h"
 | 
						|
#include "jpegxl_parse.h"
 | 
						|
 | 
						|
/* read a U32(c_i + u(u_i)) */
 | 
						|
static av_always_inline uint32_t jxl_u32(GetBitContext *gb,
 | 
						|
                        uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3,
 | 
						|
                        uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3)
 | 
						|
{
 | 
						|
    const uint32_t constants[4] = {c0, c1, c2, c3};
 | 
						|
    const uint32_t ubits    [4] = {u0, u1, u2, u3};
 | 
						|
    uint32_t ret, choice = get_bits(gb, 2);
 | 
						|
 | 
						|
    ret = constants[choice];
 | 
						|
    if (ubits[choice])
 | 
						|
        ret += get_bits_long(gb, ubits[choice]);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static av_always_inline uint32_t jxl_enum(GetBitContext *gb)
 | 
						|
{
 | 
						|
    return jxl_u32(gb, 0, 1, 2, 18, 0, 0, 4, 6);
 | 
						|
}
 | 
						|
 | 
						|
/* read a U64() */
 | 
						|
static uint64_t jxl_u64(GetBitContext *gb)
 | 
						|
{
 | 
						|
    uint64_t shift = 12, ret;
 | 
						|
 | 
						|
    switch (get_bits(gb, 2)) {
 | 
						|
    case 1:
 | 
						|
        ret = 1 + get_bits(gb, 4);
 | 
						|
        break;
 | 
						|
    case 2:
 | 
						|
        ret = 17 + get_bits(gb, 8);
 | 
						|
        break;
 | 
						|
    case 3:
 | 
						|
        ret = get_bits(gb, 12);
 | 
						|
        while (get_bits1(gb)) {
 | 
						|
            if (shift < 60) {
 | 
						|
                ret |= (uint64_t)get_bits(gb, 8) << shift;
 | 
						|
                shift += 8;
 | 
						|
            } else {
 | 
						|
                ret |= (uint64_t)get_bits(gb, 4) << shift;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        ret = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio)
 | 
						|
{
 | 
						|
    uint64_t height64 = height; /* avoid integer overflow */
 | 
						|
    switch (ratio) {
 | 
						|
    case 1:
 | 
						|
        return height;
 | 
						|
    case 2:
 | 
						|
        return (uint32_t)((height64 * 12) / 10);
 | 
						|
    case 3:
 | 
						|
        return (uint32_t)((height64 * 4) / 3);
 | 
						|
    case 4:
 | 
						|
        return (uint32_t)((height64 * 3) / 2);
 | 
						|
    case 5:
 | 
						|
        return (uint32_t)((height64 * 16) / 9);
 | 
						|
    case 6:
 | 
						|
        return (uint32_t)((height64 * 5) / 4);
 | 
						|
    case 7:
 | 
						|
        return (uint32_t)(height64 * 2);
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0; /* manual width */
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * validate a Jpeg XL Size Header
 | 
						|
 * @return >= 0 upon valid size, < 0 upon invalid size found
 | 
						|
 */
 | 
						|
static int jpegxl_read_size_header(GetBitContext *gb, FFJXLMetadata *meta, int validate)
 | 
						|
{
 | 
						|
    uint32_t width, height;
 | 
						|
 | 
						|
    if (get_bits1(gb)) {
 | 
						|
        /* small size header */
 | 
						|
        height = (get_bits(gb, 5) + 1) << 3;
 | 
						|
        width = jpegxl_width_from_ratio(height, get_bits(gb, 3));
 | 
						|
        if (!width)
 | 
						|
            width = (get_bits(gb, 5) + 1) << 3;
 | 
						|
    } else {
 | 
						|
        /* large size header */
 | 
						|
        height = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30);
 | 
						|
        width = jpegxl_width_from_ratio(height, get_bits(gb, 3));
 | 
						|
        if (!width)
 | 
						|
            width = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30);
 | 
						|
    }
 | 
						|
    if (validate && (width > (1 << 18) || height > (1 << 18)
 | 
						|
        || (width >> 4) * (height >> 4) > (1 << 20)))
 | 
						|
        return AVERROR_INVALIDDATA;
 | 
						|
 | 
						|
    if (meta) {
 | 
						|
        meta->width = meta->coded_width = width;
 | 
						|
        meta->height = meta->coded_height = height;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * validate a Jpeg XL Preview Header
 | 
						|
 * @return >= 0 upon valid size, < 0 upon invalid size found
 | 
						|
 */
 | 
						|
static int jpegxl_read_preview_header(GetBitContext *gb, int validate)
 | 
						|
{
 | 
						|
    uint32_t width, height;
 | 
						|
 | 
						|
    if (get_bits1(gb)) {
 | 
						|
        /* coded height and width divided by eight */
 | 
						|
        height = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3;
 | 
						|
        width = jpegxl_width_from_ratio(height, get_bits(gb, 3));
 | 
						|
        if (!width)
 | 
						|
            width = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3;
 | 
						|
    } else {
 | 
						|
        /* full height and width coded */
 | 
						|
        height = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12);
 | 
						|
        width = jpegxl_width_from_ratio(height, get_bits(gb, 3));
 | 
						|
        if (!width)
 | 
						|
            width = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12);
 | 
						|
    }
 | 
						|
    if (validate && (width > 4096 || height > 4096))
 | 
						|
        return AVERROR_INVALIDDATA;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * get a Jpeg XL BitDepth Header. These cannot be invalid.
 | 
						|
 */
 | 
						|
static void jpegxl_get_bit_depth(GetBitContext *gb, FFJXLMetadata *meta)
 | 
						|
{
 | 
						|
    int bit_depth;
 | 
						|
    if (get_bits1(gb)) {
 | 
						|
        /* float samples */
 | 
						|
        bit_depth = jxl_u32(gb, 32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */
 | 
						|
        skip_bits_long(gb, 4); /* exponent */
 | 
						|
    } else {
 | 
						|
        /* integer samples */
 | 
						|
        bit_depth = jxl_u32(gb, 8, 10, 12, 1, 0, 0, 0, 6);
 | 
						|
    }
 | 
						|
    if (meta)
 | 
						|
        meta->bit_depth = bit_depth;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * validate a Jpeg XL Extra Channel Info bundle
 | 
						|
 * @return >= 0 upon valid, < 0 upon invalid
 | 
						|
 */
 | 
						|
static int jpegxl_read_extra_channel_info(GetBitContext *gb, FFJXLMetadata *meta, int validate)
 | 
						|
{
 | 
						|
    int default_alpha = get_bits1(gb);
 | 
						|
    uint32_t type, name_len = 0;
 | 
						|
 | 
						|
    if (!default_alpha) {
 | 
						|
        type = jxl_enum(gb);
 | 
						|
        if (validate && type > 63)
 | 
						|
            return AVERROR_INVALIDDATA; /* enum types cannot be 64+ */
 | 
						|
        if (validate && validate < 10 && type == JPEGXL_CT_BLACK)
 | 
						|
            return AVERROR_INVALIDDATA;
 | 
						|
        jpegxl_get_bit_depth(gb, NULL);
 | 
						|
        jxl_u32(gb, 0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */
 | 
						|
        /* max of name_len is 1071 = 48 + 2^10 - 1 */
 | 
						|
        name_len = 8 * jxl_u32(gb, 0, 0, 16, 48, 0, 4, 5, 10);
 | 
						|
    } else {
 | 
						|
        type = JPEGXL_CT_ALPHA;
 | 
						|
    }
 | 
						|
 | 
						|
    if (get_bits_left(gb) < name_len)
 | 
						|
        return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
 | 
						|
    /* skip over the name */
 | 
						|
    skip_bits_long(gb, name_len);
 | 
						|
 | 
						|
    if (!default_alpha && type == JPEGXL_CT_ALPHA)
 | 
						|
        skip_bits1(gb);
 | 
						|
 | 
						|
    if (type == JPEGXL_CT_SPOT_COLOR)
 | 
						|
        skip_bits_long(gb, 16 * 4);
 | 
						|
 | 
						|
    if (type == JPEGXL_CT_CFA)
 | 
						|
        jxl_u32(gb, 1, 0, 3, 19, 0, 2, 4, 8);
 | 
						|
 | 
						|
    if (meta && type == JPEGXL_CT_ALPHA)
 | 
						|
        meta->have_alpha = 1;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int jpegxl_skip_extensions(GetBitContext *gb)
 | 
						|
{
 | 
						|
    uint64_t extensions = jxl_u64(gb), extensions_len = 0;
 | 
						|
 | 
						|
    if (get_bits_left(gb) <= 0)
 | 
						|
        return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
 | 
						|
    if (!extensions)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    for (int i = 0; i < 64; i++) {
 | 
						|
        if (extensions & (UINT64_C(1) << i))
 | 
						|
            extensions_len += jxl_u64(gb);
 | 
						|
        if (get_bits_left(gb) <= 0)
 | 
						|
            return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (extensions_len > INT_MAX || get_bits_left(gb) <= extensions_len)
 | 
						|
        return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
 | 
						|
    skip_bits_long(gb, extensions_len);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int ff_jpegxl_parse_codestream_header(const uint8_t *buf, int buflen, FFJXLMetadata *meta, int validate)
 | 
						|
{
 | 
						|
    GetBitContext gbi, *gb = &gbi;
 | 
						|
 | 
						|
    int all_default, extra_fields = 0;
 | 
						|
    int xyb_encoded = 1, have_icc_profile = 0;
 | 
						|
    int animation_offset = 0, have_timecodes = 0;
 | 
						|
 | 
						|
    FFJXLPrimaries primaries = JPEGXL_PR_SRGB;
 | 
						|
    FFJXLTransferCharacteristic trc = JPEGXL_TR_SRGB + (1U << 24);
 | 
						|
    FFJXLWhitePoint white_point = JPEGXL_WP_D65;
 | 
						|
    FFJXLColorSpace color_space = JPEGXL_CS_RGB;
 | 
						|
 | 
						|
    AVRational tb;
 | 
						|
    uint32_t num_extra_channels = 0;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = init_get_bits8(gb, buf, buflen);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
 | 
						|
    if (get_bits(gb, 16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE && validate)
 | 
						|
        return AVERROR_INVALIDDATA;
 | 
						|
 | 
						|
    ret = jpegxl_read_size_header(gb, meta, validate);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
 | 
						|
    all_default = get_bits1(gb);
 | 
						|
    if (!all_default)
 | 
						|
        extra_fields = get_bits1(gb);
 | 
						|
 | 
						|
    if (extra_fields) {
 | 
						|
        int orientation = get_bits(gb, 3);
 | 
						|
        if (orientation > 3 && meta)
 | 
						|
            FFSWAP(uint32_t, meta->width, meta->height);
 | 
						|
 | 
						|
        /*
 | 
						|
         * intrinstic size
 | 
						|
         * any size header here is valid, but as it
 | 
						|
         * is variable length we have to read it
 | 
						|
         */
 | 
						|
        if (get_bits1(gb))
 | 
						|
            jpegxl_read_size_header(gb, NULL, 0);
 | 
						|
 | 
						|
        /* preview header */
 | 
						|
        if (get_bits1(gb)) {
 | 
						|
            ret = jpegxl_read_preview_header(gb, 0);
 | 
						|
            if (ret < 0)
 | 
						|
                return ret;
 | 
						|
        }
 | 
						|
 | 
						|
        /* animation header */
 | 
						|
        if (get_bits1(gb)) {
 | 
						|
            animation_offset = get_bits_count(gb);
 | 
						|
            tb.den = jxl_u32(gb, 100, 1000, 1, 1, 0, 0, 10, 30);
 | 
						|
            tb.num = jxl_u32(gb, 1, 1001, 1, 1, 0, 0, 8, 10);
 | 
						|
            jxl_u32(gb, 0, 0, 0, 0, 0, 3, 16, 32);
 | 
						|
            have_timecodes = get_bits1(gb);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (animation_offset && meta) {
 | 
						|
        meta->animation_offset = animation_offset;
 | 
						|
        meta->timebase = tb;
 | 
						|
        meta->have_timecodes = have_timecodes;
 | 
						|
    }
 | 
						|
 | 
						|
    if (get_bits_left(gb) <= 0)
 | 
						|
        return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
 | 
						|
    if (!all_default) {
 | 
						|
        jpegxl_get_bit_depth(gb, meta);
 | 
						|
 | 
						|
        /* modular_16bit_buffers must equal 1 */
 | 
						|
        if (!get_bits1(gb) && validate && validate < 10)
 | 
						|
            return AVERROR_INVALIDDATA;
 | 
						|
 | 
						|
        num_extra_channels = jxl_u32(gb, 0, 1, 2, 1, 0, 0, 4, 12);
 | 
						|
        if (num_extra_channels > 4 && validate && validate < 10)
 | 
						|
            return AVERROR_INVALIDDATA;
 | 
						|
        for (uint32_t i = 0; i < num_extra_channels; i++) {
 | 
						|
            ret = jpegxl_read_extra_channel_info(gb, meta, validate);
 | 
						|
            if (ret < 0)
 | 
						|
                return ret;
 | 
						|
            if (get_bits_left(gb) <= 0)
 | 
						|
                return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
        }
 | 
						|
 | 
						|
        xyb_encoded = get_bits1(gb);
 | 
						|
 | 
						|
        /* color encoding bundle */
 | 
						|
        if (!get_bits1(gb)) {
 | 
						|
            have_icc_profile = get_bits1(gb);
 | 
						|
            color_space = jxl_enum(gb);
 | 
						|
            if (color_space > 63 && validate)
 | 
						|
                return AVERROR_INVALIDDATA;
 | 
						|
            if (!have_icc_profile) {
 | 
						|
                if (color_space != JPEGXL_CS_XYB) {
 | 
						|
                    white_point = jxl_enum(gb);
 | 
						|
                    if (white_point > 63 && validate)
 | 
						|
                        return AVERROR_INVALIDDATA;
 | 
						|
                    if (white_point == JPEGXL_WP_CUSTOM) {
 | 
						|
                        /* ux and uy values */
 | 
						|
                        jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21);
 | 
						|
                        jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21);
 | 
						|
                    }
 | 
						|
                    if (color_space != JPEGXL_CS_GRAY) {
 | 
						|
                        /* primaries */
 | 
						|
                        primaries = jxl_enum(gb);
 | 
						|
                        if (primaries > 63 && validate)
 | 
						|
                            return AVERROR_INVALIDDATA;
 | 
						|
                        if (primaries == JPEGXL_PR_CUSTOM) {
 | 
						|
                            /* ux/uy values for r,g,b */
 | 
						|
                            for (int i = 0; i < 6; i++) {
 | 
						|
                                jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21);
 | 
						|
                                if (get_bits_left(gb) <= 0)
 | 
						|
                                    return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                /* transfer characteristics */
 | 
						|
                if (get_bits1(gb)) {
 | 
						|
                    /* gamma */
 | 
						|
                    trc = get_bits(gb, 24);
 | 
						|
                } else {
 | 
						|
                    /* transfer function */
 | 
						|
                    trc = jxl_enum(gb);
 | 
						|
                    if (trc > 63 && validate)
 | 
						|
                        return AVERROR_INVALIDDATA;
 | 
						|
                    trc += (1U << 24);
 | 
						|
                }
 | 
						|
 | 
						|
                /* rendering intent */
 | 
						|
                if (jxl_enum(gb) > 63 && validate)
 | 
						|
                    return AVERROR_INVALIDDATA;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /* tone mapping bundle */
 | 
						|
        if (extra_fields && !get_bits1(gb))
 | 
						|
            skip_bits_long(gb, 16 + 16 + 1 + 16);
 | 
						|
 | 
						|
        ret = jpegxl_skip_extensions(gb);
 | 
						|
        if (ret < 0)
 | 
						|
            return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    if (meta) {
 | 
						|
        meta->xyb_encoded = xyb_encoded;
 | 
						|
        meta->have_icc_profile = have_icc_profile;
 | 
						|
        meta->csp = color_space;
 | 
						|
        meta->primaries = primaries;
 | 
						|
        meta->wp = white_point;
 | 
						|
        meta->trc = trc;
 | 
						|
        if (!meta->bit_depth)
 | 
						|
            meta->bit_depth = 8;
 | 
						|
        meta->num_extra_channels = num_extra_channels;
 | 
						|
    }
 | 
						|
 | 
						|
    /* default transform */
 | 
						|
    if (!get_bits1(gb)) {
 | 
						|
        /* opsin inverse matrix */
 | 
						|
        if (xyb_encoded && !get_bits1(gb))
 | 
						|
            skip_bits_long(gb, 16 * 16);
 | 
						|
        /* cw_mask and default weights */
 | 
						|
        if (get_bits1(gb))
 | 
						|
            skip_bits_long(gb, 16 * 15);
 | 
						|
        if (get_bits1(gb))
 | 
						|
            skip_bits_long(gb, 16 * 55);
 | 
						|
        if (get_bits1(gb))
 | 
						|
            skip_bits_long(gb, 16 * 210);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!have_icc_profile) {
 | 
						|
        int bits_remaining = 7 - ((get_bits_count(gb) - 1) & 0x7);
 | 
						|
        if (bits_remaining && get_bits(gb, bits_remaining))
 | 
						|
            return AVERROR_INVALIDDATA;
 | 
						|
    }
 | 
						|
 | 
						|
    if (get_bits_left(gb) < 0)
 | 
						|
        return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
 | 
						|
    return get_bits_count(gb);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * copies as much of the codestream into the buffer as possible
 | 
						|
 * pass a shorter buflen to request less
 | 
						|
 * returns the number of bytes consumed from input, may be greater than input_len
 | 
						|
 * if the input doesn't end on an ISOBMFF-box boundary
 | 
						|
 */
 | 
						|
int ff_jpegxl_collect_codestream_header(const uint8_t *input_buffer, int input_len,
 | 
						|
                                        uint8_t *buffer, int buflen, int *copied)
 | 
						|
{
 | 
						|
    GetByteContext gb;
 | 
						|
    int pos = 0, last_box = 0;
 | 
						|
    bytestream2_init(&gb, input_buffer, input_len);
 | 
						|
 | 
						|
    while (1) {
 | 
						|
        uint64_t size;
 | 
						|
        uint32_t tag;
 | 
						|
        int head_size = 8;
 | 
						|
 | 
						|
        if (bytestream2_get_bytes_left(&gb) < 8)
 | 
						|
            return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
 | 
						|
        size = bytestream2_get_be32(&gb);
 | 
						|
        tag = bytestream2_get_le32(&gb);
 | 
						|
 | 
						|
        if (size == 1) {
 | 
						|
            if (bytestream2_get_bytes_left(&gb) < 8)
 | 
						|
                return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
            size = bytestream2_get_be64(&gb);
 | 
						|
            head_size = 16;
 | 
						|
        }
 | 
						|
        /* invalid ISOBMFF size */
 | 
						|
        if (size && size <= head_size)
 | 
						|
            return AVERROR_INVALIDDATA;
 | 
						|
        if (size)
 | 
						|
            size -= head_size;
 | 
						|
 | 
						|
        if (tag == MKTAG('j','x','l','p')) {
 | 
						|
            uint32_t idx;
 | 
						|
            if (bytestream2_get_bytes_left(&gb) < 4)
 | 
						|
                return AVERROR_BUFFER_TOO_SMALL;
 | 
						|
            idx = bytestream2_get_be32(&gb);
 | 
						|
            if (idx >= UINT32_C(0x80000000))
 | 
						|
                last_box = 1;
 | 
						|
            if (size) {
 | 
						|
                if (size <= 4)
 | 
						|
                    return AVERROR_INVALIDDATA;
 | 
						|
                size -= 4;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (tag == MKTAG('j','x','l','c'))
 | 
						|
            last_box = 1;
 | 
						|
 | 
						|
        /*
 | 
						|
         * size = 0 means "until EOF". this is legal but uncommon
 | 
						|
         * here we just set it to the remaining size of the probe buffer
 | 
						|
         */
 | 
						|
        if (!size)
 | 
						|
            size = bytestream2_get_bytes_left(&gb);
 | 
						|
        else
 | 
						|
            pos += size + head_size;
 | 
						|
 | 
						|
        if (tag == MKTAG('j','x','l','c') || tag == MKTAG('j','x','l','p')) {
 | 
						|
            if (size > buflen - *copied)
 | 
						|
                size = buflen - *copied;
 | 
						|
            /*
 | 
						|
             * arbitrary chunking of the payload makes this memcpy hard to avoid
 | 
						|
             * in practice this will only be performed one or two times at most
 | 
						|
             */
 | 
						|
            *copied += bytestream2_get_buffer(&gb, buffer + *copied, size);
 | 
						|
        } else {
 | 
						|
            bytestream2_skip(&gb, size);
 | 
						|
        }
 | 
						|
        if (last_box || bytestream2_get_bytes_left(&gb) <= 0 || *copied >= buflen)
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    return pos;
 | 
						|
}
 |