Commit message: cbs_av1: Fix reading of overlong uvlc codes

The specification allows 2^32-1 to be encoded as any number of zeroes
greater than 31, followed by a one.  This previously failed because the
trace code would overflow the array containing the string representation
of the bits if there were more than 63 zeroes.  Fix that by splitting the
trace output into batches, and at the same time move it out of the default
path.

(While this seems likely to be a specification error, libaom does support
it so we probably should as well.)

From a test case by keval shah <skeval65@gmail.com>.

Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
CVE: CVE-2019-1000016
CWE: CWE-20
Vulnerability description: FFMPEG version 4.1 contains a CWE-129: Improper Validation of Array Index vulnerability in libavcodec/cbs_av1.c that can result in Denial of service. This attack appears to be exploitable via specially crafted AV1 file has to be provided as input. This vulnerability appears to have been fixed in after commit b97a4b658814b2de8b9f2a3bce491c002d34de31.

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_leb128(CodedBitstreamContext *ctx, PutBitContext *pbc,
                                const char *name, uint64_t value)
{
    int position, err, len, i;
    uint8_t byte;

    len = (av_log2(value) + 7) / 7;

    if (ctx->trace_enable)
        position = put_bits_count(pbc);

    for (i = 0; i < len; i++) {
        int subscript[2] = { 1, i };

        byte = value >> (7 * i) & 0x7f;
        if (i < len - 1)
            byte |= 0x80;

        err = ff_cbs_write_unsigned(ctx, pbc, 8, "leb128_byte[i]", subscript,
                                    byte, 0x00, 0xff);
        if (err < 0)
            return err;
    }

    if (ctx->trace_enable)
        ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value);

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc,
                               uint32_t range_max, const char *name,
                               const int *subscripts, uint32_t *write_to)
{
    uint32_t value;
    int position, err;
    uint32_t max_len, len, range_offset, range_bits;

    if (ctx->trace_enable)
        position = get_bits_count(gbc);

    av_assert0(range_max > 0);
    max_len = av_log2(range_max - 1) - 3;

    err = cbs_av1_read_increment(ctx, gbc, 0, max_len,
                                 "subexp_more_bits", &len);
    if (err < 0)
        return err;

    if (len) {
        range_bits   = 2 + len;
        range_offset = 1 << range_bits;
    } else {
        range_bits   = 3;
        range_offset = 0;
    }

    if (len < max_len) {
        err = ff_cbs_read_unsigned(ctx, gbc, range_bits,
                                   "subexp_bits", NULL, &value,
                                   0, MAX_UINT_BITS(range_bits));
        if (err < 0)
            return err;

    } else {
        err = cbs_av1_read_ns(ctx, gbc, range_max - range_offset,
                              "subexp_final_bits", NULL, &value);
        if (err < 0)
            return err;
    }
    value += range_offset;

    if (ctx->trace_enable)
        ff_cbs_trace_syntax_element(ctx, position,
                                    name, subscripts, "", value);

    *write_to = value;
    return err;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_assemble_fragment(CodedBitstreamContext *ctx,
                                     CodedBitstreamFragment *frag)
{
    size_t size, pos;
    int i;

    size = 0;
    for (i = 0; i < frag->nb_units; i++)
        size += frag->units[i].data_size;

    frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
    if (!frag->data_ref)
        return AVERROR(ENOMEM);
    frag->data = frag->data_ref->data;
    memset(frag->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    pos = 0;
    for (i = 0; i < frag->nb_units; i++) {
        memcpy(frag->data + pos, frag->units[i].data,
               frag->units[i].data_size);
        pos += frag->units[i].data_size;
    }
    av_assert0(pos == size);
    frag->data_size = size;

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_obu(CodedBitstreamContext *ctx,
                             CodedBitstreamUnit *unit,
                             PutBitContext *pbc)
{
    CodedBitstreamAV1Context *priv = ctx->priv_data;
    AV1RawOBU *obu = unit->content;
    PutBitContext pbc_tmp;
    AV1RawTileData *td;
    size_t header_size;
    int err, start_pos, end_pos, data_pos;

    // OBUs in the normal bitstream format must contain a size field
    // in every OBU (in annex B it is optional, but we don't support
    // writing that).
    obu->header.obu_has_size_field = 1;

    err = cbs_av1_write_obu_header(ctx, pbc, &obu->header);
    if (err < 0)
        return err;

    if (obu->header.obu_has_size_field) {
        pbc_tmp = *pbc;
        // Add space for the size field to fill later.
        put_bits32(pbc, 0);
        put_bits32(pbc, 0);
    }

    td = NULL;
    start_pos = put_bits_count(pbc);

    switch (obu->header.obu_type) {
    case AV1_OBU_SEQUENCE_HEADER:
        {
            err = cbs_av1_write_sequence_header_obu(ctx, pbc,
                                                    &obu->obu.sequence_header);
            if (err < 0)
                return err;

            av_buffer_unref(&priv->sequence_header_ref);
            priv->sequence_header = NULL;

            priv->sequence_header_ref = av_buffer_ref(unit->content_ref);
            if (!priv->sequence_header_ref)
                return AVERROR(ENOMEM);
            priv->sequence_header = &obu->obu.sequence_header;
        }
        break;
    case AV1_OBU_TEMPORAL_DELIMITER:
        {
            err = cbs_av1_write_temporal_delimiter_obu(ctx, pbc);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_FRAME_HEADER:
    case AV1_OBU_REDUNDANT_FRAME_HEADER:
        {
            err = cbs_av1_write_frame_header_obu(ctx, pbc,
                                                 &obu->obu.frame_header,
                                                 obu->header.obu_type ==
                                                 AV1_OBU_REDUNDANT_FRAME_HEADER,
                                                 NULL);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_TILE_GROUP:
        {
            err = cbs_av1_write_tile_group_obu(ctx, pbc,
                                               &obu->obu.tile_group);
            if (err < 0)
                return err;

            td = &obu->obu.tile_group.tile_data;
        }
        break;
    case AV1_OBU_FRAME:
        {
            err = cbs_av1_write_frame_obu(ctx, pbc, &obu->obu.frame, NULL);
            if (err < 0)
                return err;

            td = &obu->obu.frame.tile_group.tile_data;
        }
        break;
    case AV1_OBU_TILE_LIST:
        {
            err = cbs_av1_write_tile_list_obu(ctx, pbc, &obu->obu.tile_list);
            if (err < 0)
                return err;

            td = &obu->obu.tile_list.tile_data;
        }
        break;
    case AV1_OBU_METADATA:
        {
            err = cbs_av1_write_metadata_obu(ctx, pbc, &obu->obu.metadata);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_PADDING:
    default:
        return AVERROR(ENOSYS);
    }

    end_pos = put_bits_count(pbc);
    header_size = (end_pos - start_pos + 7) / 8;
    if (td) {
        obu->obu_size = header_size + td->data_size;
    } else if (header_size > 0) {
        // Add trailing bits and recalculate.
        err = cbs_av1_write_trailing_bits(ctx, pbc, 8 - end_pos % 8);
        if (err < 0)
            return err;
        end_pos = put_bits_count(pbc);
        obu->obu_size = header_size = (end_pos - start_pos + 7) / 8;
    } else {
        // Empty OBU.
        obu->obu_size = 0;
    }

    end_pos = put_bits_count(pbc);
    // Must now be byte-aligned.
    av_assert0(end_pos % 8 == 0);
    flush_put_bits(pbc);
    start_pos /= 8;
    end_pos   /= 8;

    *pbc = pbc_tmp;
    err = cbs_av1_write_leb128(ctx, pbc, "obu_size", obu->obu_size);
    if (err < 0)
        return err;

    data_pos = put_bits_count(pbc) / 8;
    flush_put_bits(pbc);
    av_assert0(data_pos <= start_pos);

    if (8 * obu->obu_size > put_bits_left(pbc))
        return AVERROR(ENOSPC);

    if (obu->obu_size > 0) {
        memmove(priv->write_buffer + data_pos,
                priv->write_buffer + start_pos, header_size);
        skip_put_bytes(pbc, header_size);

        if (td) {
            memcpy(priv->write_buffer + data_pos + header_size,
                   td->data, td->data_size);
            skip_put_bytes(pbc, td->data_size);
        }
    }

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static void cbs_av1_free_tile_data(AV1RawTileData *td)
{
    av_buffer_unref(&td->data_ref);
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc,
                              const char *name, uint32_t value,
                              uint32_t range_min, uint32_t range_max)
{
    uint32_t v;
    int position, zeroes;

    if (value < range_min || value > range_max) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: "
               "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n",
               name, value, range_min, range_max);
        return AVERROR_INVALIDDATA;
    }

    if (ctx->trace_enable)
        position = put_bits_count(pbc);

    if (value == 0) {
        zeroes = 0;
        put_bits(pbc, 1, 1);
    } else {
        zeroes = av_log2(value + 1);
        v = value - (1 << zeroes) + 1;
        put_bits(pbc, zeroes + 1, 1);
        put_bits(pbc, zeroes, v);
    }

    if (ctx->trace_enable) {
        char bits[65];
        int i, j;
        i = 0;
        for (j = 0; j < zeroes; j++)
            bits[i++] = '0';
        bits[i++] = '1';
        for (j = 0; j < zeroes; j++)
            bits[i++] = (v >> (zeroes - j - 1) & 1) ? '1' : '0';
        bits[i++] = 0;
        ff_cbs_trace_syntax_element(ctx, position, name, NULL,
                                    bits, value);
    }

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static void cbs_av1_free_obu(void *unit, uint8_t *content)
{
    AV1RawOBU *obu = (AV1RawOBU*)content;

    switch (obu->header.obu_type) {
    case AV1_OBU_TILE_GROUP:
        cbs_av1_free_tile_data(&obu->obu.tile_group.tile_data);
        break;
    case AV1_OBU_FRAME:
        cbs_av1_free_tile_data(&obu->obu.frame.tile_group.tile_data);
        break;
    case AV1_OBU_TILE_LIST:
        cbs_av1_free_tile_data(&obu->obu.tile_list.tile_data);
        break;
    case AV1_OBU_METADATA:
        cbs_av1_free_metadata(&obu->obu.metadata);
        break;
    }

    av_freep(&obu);
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc,
                                uint32_t range_max, const char *name,
                                const int *subscripts, uint32_t value)
{
    int position, err;
    uint32_t max_len, len, range_offset, range_bits;

    if (value > range_max) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: "
               "%"PRIu32", but must be in [0,%"PRIu32"].\n",
               name, value, range_max);
        return AVERROR_INVALIDDATA;
    }

    if (ctx->trace_enable)
        position = put_bits_count(pbc);

    av_assert0(range_max > 0);
    max_len = av_log2(range_max - 1) - 3;

    if (value < 8) {
        range_bits   = 3;
        range_offset = 0;
        len = 0;
    } else {
        range_bits = av_log2(value);
        len = range_bits - 2;
        if (len > max_len) {
            // The top bin is combined with the one below it.
            av_assert0(len == max_len + 1);
            --range_bits;
            len = max_len;
        }
        range_offset = 1 << range_bits;
    }

    err = cbs_av1_write_increment(ctx, pbc, 0, max_len,
                                  "subexp_more_bits", len);
    if (err < 0)
        return err;

    if (len < max_len) {
        err = ff_cbs_write_unsigned(ctx, pbc, range_bits,
                                    "subexp_bits", NULL,
                                    value - range_offset,
                                    0, MAX_UINT_BITS(range_bits));
        if (err < 0)
            return err;

    } else {
        err = cbs_av1_write_ns(ctx, pbc, range_max - range_offset,
                               "subexp_final_bits", NULL,
                               value - range_offset);
        if (err < 0)
            return err;
    }

    if (ctx->trace_enable)
        ff_cbs_trace_syntax_element(ctx, position,
                                    name, subscripts, "", value);

    return err;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_su(CodedBitstreamContext *ctx, GetBitContext *gbc,
                           int width, const char *name,
                           const int *subscripts, int32_t *write_to)
{
    int position;
    int32_t value;

    if (ctx->trace_enable)
        position = get_bits_count(gbc);

    if (get_bits_left(gbc) < width) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid signed value at "
               "%s: bitstream ended.\n", name);
        return AVERROR_INVALIDDATA;
    }

    value = get_sbits(gbc, width);

    if (ctx->trace_enable) {
        char bits[33];
        int i;
        for (i = 0; i < width; i++)
            bits[i] = value & (1 << (width - i - 1)) ? '1' : '0';
        bits[i] = 0;

        ff_cbs_trace_syntax_element(ctx, position,
                                    name, subscripts, bits, value);
    }

    *write_to = value;
    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_unit(CodedBitstreamContext *ctx,
                              CodedBitstreamUnit *unit)
{
    CodedBitstreamAV1Context *priv = ctx->priv_data;
    PutBitContext pbc;
    int err;

    if (!priv->write_buffer) {
        // Initial write buffer size is 1MB.
        priv->write_buffer_size = 1024 * 1024;

    reallocate_and_try_again:
        err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
        if (err < 0) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a "
                   "sufficiently large write buffer (last attempt "
                   "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size);
            return err;
        }
    }

    init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size);

    err = cbs_av1_write_obu(ctx, unit, &pbc);
    if (err == AVERROR(ENOSPC)) {
        // Overflow.
        priv->write_buffer_size *= 2;
        goto reallocate_and_try_again;
    }
    if (err < 0)
        return err;

    // Overflow but we didn't notice.
    av_assert0(put_bits_count(&pbc) <= 8 * priv->write_buffer_size);

    // OBU data must be byte-aligned.
    av_assert0(put_bits_count(&pbc) % 8 == 0);

    unit->data_size = put_bits_count(&pbc) / 8;
    flush_put_bits(&pbc);

    err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size);
    if (err < 0)
        return err;

    memcpy(unit->data, priv->write_buffer, unit->data_size);

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_unit(CodedBitstreamContext *ctx,
                             CodedBitstreamUnit *unit)
{
    CodedBitstreamAV1Context *priv = ctx->priv_data;
    AV1RawOBU *obu;
    GetBitContext gbc;
    int err, start_pos, end_pos;

    err = ff_cbs_alloc_unit_content(ctx, unit, sizeof(*obu),
                                    &cbs_av1_free_obu);
    if (err < 0)
        return err;
    obu = unit->content;

    err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
    if (err < 0)
        return err;

    err = cbs_av1_read_obu_header(ctx, &gbc, &obu->header);
    if (err < 0)
        return err;
    av_assert0(obu->header.obu_type == unit->type);

    if (obu->header.obu_has_size_field) {
        uint64_t obu_size;
        err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size);
        if (err < 0)
            return err;
        obu->obu_size = obu_size;
    } else {
        if (unit->data_size < 1 + obu->header.obu_extension_flag) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU length: "
                   "unit too short (%"SIZE_SPECIFIER").\n", unit->data_size);
            return AVERROR_INVALIDDATA;
        }
        obu->obu_size = unit->data_size - 1 - obu->header.obu_extension_flag;
    }

    start_pos = get_bits_count(&gbc);

    if (obu->header.obu_extension_flag) {
        priv->temporal_id = obu->header.temporal_id;
        priv->spatial_id  = obu->header.temporal_id;

        if (obu->header.obu_type != AV1_OBU_SEQUENCE_HEADER &&
            obu->header.obu_type != AV1_OBU_TEMPORAL_DELIMITER &&
            priv->operating_point_idc) {
            int in_temporal_layer =
                (priv->operating_point_idc >>  priv->temporal_id    ) & 1;
            int in_spatial_layer  =
                (priv->operating_point_idc >> (priv->spatial_id + 8)) & 1;
            if (!in_temporal_layer || !in_spatial_layer) {
                // Decoding will drop this OBU at this operating point.
            }
        }
    } else {
        priv->temporal_id = 0;
        priv->spatial_id  = 0;
    }

    switch (obu->header.obu_type) {
    case AV1_OBU_SEQUENCE_HEADER:
        {
            err = cbs_av1_read_sequence_header_obu(ctx, &gbc,
                                                   &obu->obu.sequence_header);
            if (err < 0)
                return err;

            av_buffer_unref(&priv->sequence_header_ref);
            priv->sequence_header = NULL;

            priv->sequence_header_ref = av_buffer_ref(unit->content_ref);
            if (!priv->sequence_header_ref)
                return AVERROR(ENOMEM);
            priv->sequence_header = &obu->obu.sequence_header;
        }
        break;
    case AV1_OBU_TEMPORAL_DELIMITER:
        {
            err = cbs_av1_read_temporal_delimiter_obu(ctx, &gbc);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_FRAME_HEADER:
    case AV1_OBU_REDUNDANT_FRAME_HEADER:
        {
            err = cbs_av1_read_frame_header_obu(ctx, &gbc,
                                                &obu->obu.frame_header,
                                                obu->header.obu_type ==
                                                AV1_OBU_REDUNDANT_FRAME_HEADER,
                                                unit->data_ref);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_TILE_GROUP:
        {
            err = cbs_av1_read_tile_group_obu(ctx, &gbc,
                                              &obu->obu.tile_group);
            if (err < 0)
                return err;

            err = cbs_av1_ref_tile_data(ctx, unit, &gbc,
                                        &obu->obu.tile_group.tile_data);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_FRAME:
        {
            err = cbs_av1_read_frame_obu(ctx, &gbc, &obu->obu.frame,
                                         unit->data_ref);
            if (err < 0)
                return err;

            err = cbs_av1_ref_tile_data(ctx, unit, &gbc,
                                        &obu->obu.frame.tile_group.tile_data);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_TILE_LIST:
        {
            err = cbs_av1_read_tile_list_obu(ctx, &gbc,
                                             &obu->obu.tile_list);
            if (err < 0)
                return err;

            err = cbs_av1_ref_tile_data(ctx, unit, &gbc,
                                        &obu->obu.tile_list.tile_data);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_METADATA:
        {
            err = cbs_av1_read_metadata_obu(ctx, &gbc, &obu->obu.metadata);
            if (err < 0)
                return err;
        }
        break;
    case AV1_OBU_PADDING:
    default:
        return AVERROR(ENOSYS);
    }

    end_pos = get_bits_count(&gbc);
    av_assert0(end_pos <= unit->data_size * 8);

    if (obu->obu_size > 0 &&
        obu->header.obu_type != AV1_OBU_TILE_GROUP &&
        obu->header.obu_type != AV1_OBU_FRAME) {
        err = cbs_av1_read_trailing_bits(ctx, &gbc,
                                         obu->obu_size * 8 + start_pos - end_pos);
        if (err < 0)
            return err;
    }

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc,
                            uint32_t n, const char *name,
                            const int *subscripts, uint32_t value)
{
    uint32_t w, m, v, extra_bit;
    int position;

    if (value > n) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: "
               "%"PRIu32", but must be in [0,%"PRIu32"].\n",
               name, value, n);
        return AVERROR_INVALIDDATA;
    }

    if (ctx->trace_enable)
        position = put_bits_count(pbc);

    w = av_log2(n) + 1;
    m = (1 << w) - n;

    if (put_bits_left(pbc) < w)
        return AVERROR(ENOSPC);

    if (value < m) {
        v = value;
        put_bits(pbc, w - 1, v);
    } else {
        v = m + ((value - m) >> 1);
        extra_bit = (value - m) & 1;
        put_bits(pbc, w - 1, v);
        put_bits(pbc, 1, extra_bit);
    }

    if (ctx->trace_enable) {
        char bits[33];
        int i;
        for (i = 0; i < w - 1; i++)
            bits[i] = (v >> i & 1) ? '1' : '0';
        if (value >= m)
            bits[i++] = extra_bit ? '1' : '0';
        bits[i] = 0;

        ff_cbs_trace_syntax_element(ctx, position,
                                    name, subscripts, bits, value);
    }

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static void cbs_av1_free_metadata(AV1RawMetadata *md)
{
    switch (md->metadata_type) {
    case AV1_METADATA_TYPE_ITUT_T35:
        av_buffer_unref(&md->metadata.itut_t35.payload_ref);
        break;
    }
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
                                  CodedBitstreamFragment *frag,
                                  int header)
{
    GetBitContext gbc;
    uint8_t *data;
    size_t size;
    uint64_t obu_length;
    int pos, err, trace;

    // Don't include this parsing in trace output.
    trace = ctx->trace_enable;
    ctx->trace_enable = 0;

    data = frag->data;
    size = frag->data_size;

    if (INT_MAX / 8 < size) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid fragment: "
               "too large (%"SIZE_SPECIFIER" bytes).\n", size);
        err = AVERROR_INVALIDDATA;
        goto fail;
    }

    while (size > 0) {
        AV1RawOBUHeader header;
        uint64_t obu_size;

        init_get_bits(&gbc, data, 8 * size);

        err = cbs_av1_read_obu_header(ctx, &gbc, &header);
        if (err < 0)
            goto fail;

        if (get_bits_left(&gbc) < 8) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU: fragment "
                   "too short (%"SIZE_SPECIFIER" bytes).\n", size);
            err = AVERROR_INVALIDDATA;
            goto fail;
        }

        if (header.obu_has_size_field) {
            err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size);
            if (err < 0)
                goto fail;
        } else
            obu_size = size - 1 - header.obu_extension_flag;

        pos = get_bits_count(&gbc);
        av_assert0(pos % 8 == 0 && pos / 8 <= size);

        obu_length = pos / 8 + obu_size;

        if (size < obu_length) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU length: "
                   "%"PRIu64", but only %"SIZE_SPECIFIER" bytes remaining in fragment.\n",
                   obu_length, size);
            err = AVERROR_INVALIDDATA;
            goto fail;
        }

        err = ff_cbs_insert_unit_data(ctx, frag, -1, header.obu_type,
                                      data, obu_length, frag->data_ref);
        if (err < 0)
            goto fail;

        data += obu_length;
        size -= obu_length;
    }

    err = 0;
fail:
    ctx->trace_enable = trace;
    return err;
}

Project: FFmpeg
File: cbs_av1.c

static void cbs_av1_close(CodedBitstreamContext *ctx)
{
    CodedBitstreamAV1Context *priv = ctx->priv_data;

    av_buffer_unref(&priv->sequence_header_ref);
    av_buffer_unref(&priv->frame_header_ref);

    av_freep(&priv->write_buffer);
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc,
                               const char *name, uint64_t *write_to)
{
    uint64_t value;
    int position, err, i;

    if (ctx->trace_enable)
        position = get_bits_count(gbc);

    value = 0;
    for (i = 0; i < 8; i++) {
        int subscript[2] = { 1, i };
        uint32_t byte;
        err = ff_cbs_read_unsigned(ctx, gbc, 8, "leb128_byte[i]", subscript,
                                   &byte, 0x00, 0xff);
        if (err < 0)
            return err;

        value |= (uint64_t)(byte & 0x7f) << (i * 7);
        if (!(byte & 0x80))
            break;
    }

    if (ctx->trace_enable)
        ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value);

    *write_to = value;
    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pbc,
                                   uint32_t range_min, uint32_t range_max,
                                   const char *name, uint32_t value)
{
    int len;

    av_assert0(range_min <= range_max && range_max - range_min < 32);
    if (value < range_min || value > range_max) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: "
               "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n",
               name, value, range_min, range_max);
        return AVERROR_INVALIDDATA;
    }

    if (value == range_max)
        len = range_max - range_min;
    else
        len = value - range_min + 1;
    if (put_bits_left(pbc) < len)
        return AVERROR(ENOSPC);

    if (ctx->trace_enable) {
        char bits[33];
        int i;
        for (i = 0; i < len; i++) {
            if (range_min + i == value)
                bits[i] = '0';
            else
                bits[i] = '1';
        }
        bits[i] = 0;
        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),
                                    name, NULL, bits, value);
    }

    if (len > 0)
        put_bits(pbc, len, (1 << len) - 1 - (value != range_max));

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc,
                             const char *name, uint32_t *write_to,
                             uint32_t range_min, uint32_t range_max)
{
    uint32_t zeroes, bits_value, value;
    int position;

    if (ctx->trace_enable)
        position = get_bits_count(gbc);

    zeroes = 0;
    while (1) {
        if (get_bits_left(gbc) < 1) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at "
                   "%s: bitstream ended.\n", name);
            return AVERROR_INVALIDDATA;
        }

        if (get_bits1(gbc))
            break;
        ++zeroes;
    }

    if (zeroes >= 32) {
        value = MAX_UINT_BITS(32);
    } else {
        if (get_bits_left(gbc) < zeroes) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at "
                   "%s: bitstream ended.\n", name);
            return AVERROR_INVALIDDATA;
        }

        bits_value = get_bits_long(gbc, zeroes);
        value = bits_value + (UINT32_C(1) << zeroes) - 1;
    }

    if (ctx->trace_enable) {
        char bits[65];
        int i, j, k;

        if (zeroes >= 32) {
            while (zeroes > 32) {
                k = FFMIN(zeroes - 32, 32);
                for (i = 0; i < k; i++)
                    bits[i] = '0';
                bits[i] = 0;
                ff_cbs_trace_syntax_element(ctx, position, name,
                                            NULL, bits, 0);
                zeroes -= k;
                position += k;
            }
        }

        for (i = 0; i < zeroes; i++)
            bits[i] = '0';
        bits[i++] = '1';

        if (zeroes < 32) {
            for (j = 0; j < zeroes; j++)
                bits[i++] = (bits_value >> (zeroes - j - 1) & 1) ? '1' : '0';
        }

        bits[i] = 0;
        ff_cbs_trace_syntax_element(ctx, position, name,
                                    NULL, bits, value);
    }

    if (value < range_min || value > range_max) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: "
               "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n",
               name, value, range_min, range_max);
        return AVERROR_INVALIDDATA;
    }

    *write_to = value;
    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc,
                                  uint32_t range_min, uint32_t range_max,
                                  const char *name, uint32_t *write_to)
{
    uint32_t value;
    int position, i;
    char bits[33];

    av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1);
    if (ctx->trace_enable)
        position = get_bits_count(gbc);

    for (i = 0, value = range_min; value < range_max;) {
        if (get_bits_left(gbc) < 1) {
            av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid increment value at "
                   "%s: bitstream ended.\n", name);
            return AVERROR_INVALIDDATA;
        }
        if (get_bits1(gbc)) {
            bits[i++] = '1';
            ++value;
        } else {
            bits[i++] = '0';
            break;
        }
    }

    if (ctx->trace_enable) {
        bits[i] = 0;
        ff_cbs_trace_syntax_element(ctx, position,
                                    name, NULL, bits, value);
    }

    *write_to = value;
    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_get_relative_dist(const AV1RawSequenceHeader *seq,
                                     unsigned int a, unsigned int b)
{
    unsigned int diff, m;
    if (!seq->enable_order_hint)
        return 0;
    diff = a - b;
    m = 1 << seq->order_hint_bits_minus_1;
    diff = (diff & (m - 1)) - (diff & m);
    return diff;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc,
                           uint32_t n, const char *name,
                           const int *subscripts, uint32_t *write_to)
{
    uint32_t w, m, v, extra_bit, value;
    int position;

    av_assert0(n > 0);

    if (ctx->trace_enable)
        position = get_bits_count(gbc);

    w = av_log2(n) + 1;
    m = (1 << w) - n;

    if (get_bits_left(gbc) < w) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid non-symmetric value at "
               "%s: bitstream ended.\n", name);
        return AVERROR_INVALIDDATA;
    }

    if (w - 1 > 0)
        v = get_bits(gbc, w - 1);
    else
        v = 0;

    if (v < m) {
        value = v;
    } else {
        extra_bit = get_bits1(gbc);
        value = (v << 1) - m + extra_bit;
    }

    if (ctx->trace_enable) {
        char bits[33];
        int i;
        for (i = 0; i < w - 1; i++)
            bits[i] = (v >> i & 1) ? '1' : '0';
        if (v >= m)
            bits[i++] = extra_bit ? '1' : '0';
        bits[i] = 0;

        ff_cbs_trace_syntax_element(ctx, position,
                                    name, subscripts, bits, value);
    }

    *write_to = value;
    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_write_su(CodedBitstreamContext *ctx, PutBitContext *pbc,
                            int width, const char *name,
                            const int *subscripts, int32_t value)
{
    if (put_bits_left(pbc) < width)
        return AVERROR(ENOSPC);

    if (ctx->trace_enable) {
        char bits[33];
        int i;
        for (i = 0; i < width; i++)
            bits[i] = value & (1 << (width - i - 1)) ? '1' : '0';
        bits[i] = 0;

        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),
                                    name, subscripts, bits, value);
    }

    put_sbits(pbc, width, value);

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_ref_tile_data(CodedBitstreamContext *ctx,
                                 CodedBitstreamUnit *unit,
                                 GetBitContext *gbc,
                                 AV1RawTileData *td)
{
    int pos;

    pos = get_bits_count(gbc);
    if (pos >= 8 * unit->data_size) {
        av_log(ctx->log_ctx, AV_LOG_ERROR, "Bitstream ended before "
               "any data in tile group (%d bits read).\n", pos);
        return AVERROR_INVALIDDATA;
    }
    // Must be byte-aligned at this point.
    av_assert0(pos % 8 == 0);

    td->data_ref = av_buffer_ref(unit->data_ref);
    if (!td->data_ref)
        return AVERROR(ENOMEM);

    td->data      = unit->data      + pos / 8;
    td->data_size = unit->data_size - pos / 8;

    return 0;
}

Project: FFmpeg
File: cbs_av1.c

static int cbs_av1_tile_log2(int blksize, int target)
{
    int k;
    for (k = 0; (blksize << k) < target; k++);
    return k;
}