Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  obu.c   Sprache: C

 
/*
 * Copyright © 2018-2021, VideoLAN and dav1d authors
 * Copyright © 2018, Two Orioles, LLC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include "config.h"

#include <errno.h>
#include <limits.h>
#include <stdio.h>

#include "dav1d/data.h"

#include "common/frame.h"
#include "common/intops.h"
#include "common/validate.h"

#include "src/decode.h"
#include "src/getbits.h"
#include "src/levels.h"
#include "src/log.h"
#include "src/obu.h"
#include "src/ref.h"
#include "src/thread_task.h"

static int check_trailing_bits(GetBits *const gb,
                               const int strict_std_compliance)
{
    const int trailing_one_bit = dav1d_get_bit(gb);

    if (gb->error)
        return DAV1D_ERR(EINVAL);

    if (!strict_std_compliance)
        return 0;

    if (!trailing_one_bit || gb->state)
        return DAV1D_ERR(EINVAL);

    ptrdiff_t size = gb->ptr_end - gb->ptr;
    while (size > 0 && gb->ptr[size - 1] == 0)
        size--;

    if (size)
        return DAV1D_ERR(EINVAL);

    return 0;
}

static NOINLINE int parse_seq_hdr(Dav1dSequenceHeader *const hdr,
                                  GetBits *const gb,
                                  const int strict_std_compliance)
{
#define DEBUG_SEQ_HDR 0

#if DEBUG_SEQ_HDR
    const unsigned init_bit_pos = dav1d_get_bits_pos(gb);
#endif

    memset(hdr, 0, sizeof(*hdr));
    hdr->profile = dav1d_get_bits(gb, 3);
    if (hdr->profile > 2) goto error;
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-profile: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

    hdr->still_picture = dav1d_get_bit(gb);
    hdr->reduced_still_picture_header = dav1d_get_bit(gb);
    if (hdr->reduced_still_picture_header && !hdr->still_picture) goto error;
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-stillpicture_flags: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

    if (hdr->reduced_still_picture_header) {
        hdr->num_operating_points = 1;
        hdr->operating_points[0].major_level = dav1d_get_bits(gb, 3);
        hdr->operating_points[0].minor_level = dav1d_get_bits(gb, 2);
        hdr->operating_points[0].initial_display_delay = 10;
    } else {
        hdr->timing_info_present = dav1d_get_bit(gb);
        if (hdr->timing_info_present) {
            hdr->num_units_in_tick = dav1d_get_bits(gb, 32);
            hdr->time_scale = dav1d_get_bits(gb, 32);
            if (strict_std_compliance && (!hdr->num_units_in_tick || !hdr->time_scale))
                goto error;
            hdr->equal_picture_interval = dav1d_get_bit(gb);
            if (hdr->equal_picture_interval) {
                const unsigned num_ticks_per_picture = dav1d_get_vlc(gb);
                if (num_ticks_per_picture == 0xFFFFFFFFU)
                    goto error;
                hdr->num_ticks_per_picture = num_ticks_per_picture + 1;
            }

            hdr->decoder_model_info_present = dav1d_get_bit(gb);
            if (hdr->decoder_model_info_present) {
                hdr->encoder_decoder_buffer_delay_length = dav1d_get_bits(gb, 5) + 1;
                hdr->num_units_in_decoding_tick = dav1d_get_bits(gb, 32);
                if (strict_std_compliance && !hdr->num_units_in_decoding_tick)
                    goto error;
                hdr->buffer_removal_delay_length = dav1d_get_bits(gb, 5) + 1;
                hdr->frame_presentation_delay_length = dav1d_get_bits(gb, 5) + 1;
            }
        }
#if DEBUG_SEQ_HDR
        printf("SEQHDR: post-timinginfo: off=%u\n",
               dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

        hdr->display_model_info_present = dav1d_get_bit(gb);
        hdr->num_operating_points = dav1d_get_bits(gb, 5) + 1;
        for (int i = 0; i < hdr->num_operating_points; i++) {
            struct Dav1dSequenceHeaderOperatingPoint *const op =
                &hdr->operating_points[i];
            op->idc = dav1d_get_bits(gb, 12);
            if (op->idc && (!(op->idc & 0xff) || !(op->idc & 0xf00)))
                goto error;
            op->major_level = 2 + dav1d_get_bits(gb, 3);
            op->minor_level = dav1d_get_bits(gb, 2);
            if (op->major_level > 3)
                op->tier = dav1d_get_bit(gb);
            if (hdr->decoder_model_info_present) {
                op->decoder_model_param_present = dav1d_get_bit(gb);
                if (op->decoder_model_param_present) {
                    struct Dav1dSequenceHeaderOperatingParameterInfo *const opi =
                        &hdr->operating_parameter_info[i];
                    opi->decoder_buffer_delay =
                        dav1d_get_bits(gb, hdr->encoder_decoder_buffer_delay_length);
                    opi->encoder_buffer_delay =
                        dav1d_get_bits(gb, hdr->encoder_decoder_buffer_delay_length);
                    opi->low_delay_mode = dav1d_get_bit(gb);
                }
            }
            if (hdr->display_model_info_present)
                op->display_model_param_present = dav1d_get_bit(gb);
            op->initial_display_delay =
                op->display_model_param_present ? dav1d_get_bits(gb, 4) + 1 : 10;
        }
#if DEBUG_SEQ_HDR
        printf("SEQHDR: post-operating-points: off=%u\n",
               dav1d_get_bits_pos(gb) - init_bit_pos);
#endif
    }

    hdr->width_n_bits = dav1d_get_bits(gb, 4) + 1;
    hdr->height_n_bits = dav1d_get_bits(gb, 4) + 1;
    hdr->max_width = dav1d_get_bits(gb, hdr->width_n_bits) + 1;
    hdr->max_height = dav1d_get_bits(gb, hdr->height_n_bits) + 1;
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-size: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif
    if (!hdr->reduced_still_picture_header) {
        hdr->frame_id_numbers_present = dav1d_get_bit(gb);
        if (hdr->frame_id_numbers_present) {
            hdr->delta_frame_id_n_bits = dav1d_get_bits(gb, 4) + 2;
            hdr->frame_id_n_bits = dav1d_get_bits(gb, 3) + hdr->delta_frame_id_n_bits + 1;
        }
    }
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-frame-id-numbers-present: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

    hdr->sb128 = dav1d_get_bit(gb);
    hdr->filter_intra = dav1d_get_bit(gb);
    hdr->intra_edge_filter = dav1d_get_bit(gb);
    if (hdr->reduced_still_picture_header) {
        hdr->screen_content_tools = DAV1D_ADAPTIVE;
        hdr->force_integer_mv = DAV1D_ADAPTIVE;
    } else {
        hdr->inter_intra = dav1d_get_bit(gb);
        hdr->masked_compound = dav1d_get_bit(gb);
        hdr->warped_motion = dav1d_get_bit(gb);
        hdr->dual_filter = dav1d_get_bit(gb);
        hdr->order_hint = dav1d_get_bit(gb);
        if (hdr->order_hint) {
            hdr->jnt_comp = dav1d_get_bit(gb);
            hdr->ref_frame_mvs = dav1d_get_bit(gb);
        }
        hdr->screen_content_tools = dav1d_get_bit(gb) ? DAV1D_ADAPTIVE : dav1d_get_bit(gb);
    #if DEBUG_SEQ_HDR
        printf("SEQHDR: post-screentools: off=%u\n",
               dav1d_get_bits_pos(gb) - init_bit_pos);
    #endif
        hdr->force_integer_mv = hdr->screen_content_tools ?
                                dav1d_get_bit(gb) ? DAV1D_ADAPTIVE : dav1d_get_bit(gb) : 2;
        if (hdr->order_hint)
            hdr->order_hint_n_bits = dav1d_get_bits(gb, 3) + 1;
    }
    hdr->super_res = dav1d_get_bit(gb);
    hdr->cdef = dav1d_get_bit(gb);
    hdr->restoration = dav1d_get_bit(gb);
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-featurebits: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

    hdr->hbd = dav1d_get_bit(gb);
    if (hdr->profile == 2 && hdr->hbd)
        hdr->hbd += dav1d_get_bit(gb);
    if (hdr->profile != 1)
        hdr->monochrome = dav1d_get_bit(gb);
    hdr->color_description_present = dav1d_get_bit(gb);
    if (hdr->color_description_present) {
        hdr->pri = dav1d_get_bits(gb, 8);
        hdr->trc = dav1d_get_bits(gb, 8);
        hdr->mtrx = dav1d_get_bits(gb, 8);
    } else {
        hdr->pri = DAV1D_COLOR_PRI_UNKNOWN;
        hdr->trc = DAV1D_TRC_UNKNOWN;
        hdr->mtrx = DAV1D_MC_UNKNOWN;
    }
    if (hdr->monochrome) {
        hdr->color_range = dav1d_get_bit(gb);
        hdr->layout = DAV1D_PIXEL_LAYOUT_I400;
        hdr->ss_hor = hdr->ss_ver = 1;
        hdr->chr = DAV1D_CHR_UNKNOWN;
    } else if (hdr->pri == DAV1D_COLOR_PRI_BT709 &&
               hdr->trc == DAV1D_TRC_SRGB &&
               hdr->mtrx == DAV1D_MC_IDENTITY)
    {
        hdr->layout = DAV1D_PIXEL_LAYOUT_I444;
        hdr->color_range = 1;
        if (hdr->profile != 1 && !(hdr->profile == 2 && hdr->hbd == 2))
            goto error;
    } else {
        hdr->color_range = dav1d_get_bit(gb);
        switch (hdr->profile) {
        case 0: hdr->layout = DAV1D_PIXEL_LAYOUT_I420;
                hdr->ss_hor = hdr->ss_ver = 1;
                break;
        case 1: hdr->layout = DAV1D_PIXEL_LAYOUT_I444;
                break;
        case 2:
            if (hdr->hbd == 2) {
                hdr->ss_hor = dav1d_get_bit(gb);
                if (hdr->ss_hor)
                    hdr->ss_ver = dav1d_get_bit(gb);
            } else
                hdr->ss_hor = 1;
            hdr->layout = hdr->ss_hor ?
                          hdr->ss_ver ? DAV1D_PIXEL_LAYOUT_I420 :
                                        DAV1D_PIXEL_LAYOUT_I422 :
                                        DAV1D_PIXEL_LAYOUT_I444;
            break;
        }
        hdr->chr = (hdr->ss_hor & hdr->ss_ver) ?
                   dav1d_get_bits(gb, 2) : DAV1D_CHR_UNKNOWN;
    }
    if (strict_std_compliance &&
        hdr->mtrx == DAV1D_MC_IDENTITY && hdr->layout != DAV1D_PIXEL_LAYOUT_I444)
    {
        goto error;
    }
    if (!hdr->monochrome)
        hdr->separate_uv_delta_q = dav1d_get_bit(gb);
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-colorinfo: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

    hdr->film_grain_present = dav1d_get_bit(gb);
#if DEBUG_SEQ_HDR
    printf("SEQHDR: post-filmgrain: off=%u\n",
           dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

    // We needn't bother flushing the OBU here: we'll check we didn't
    // overrun in the caller and will then discard gb, so there's no
    // point in setting its position properly.

    return check_trailing_bits(gb, strict_std_compliance);

error:
    return DAV1D_ERR(EINVAL);
}

int dav1d_parse_sequence_header(Dav1dSequenceHeader *const out,
                                const uint8_t *const ptr, const size_t sz)
{
    validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL));
    validate_input_or_ret(ptr != NULL, DAV1D_ERR(EINVAL));
    validate_input_or_ret(sz > 0 && sz <= SIZE_MAX / 2, DAV1D_ERR(EINVAL));

    GetBits gb;
    dav1d_init_get_bits(&gb, ptr, sz);
    int res = DAV1D_ERR(ENOENT);

    do {
        dav1d_get_bit(&gb); // obu_forbidden_bit
        const enum Dav1dObuType type = dav1d_get_bits(&gb, 4);
        const int has_extension = dav1d_get_bit(&gb);
        const int has_length_field = dav1d_get_bit(&gb);
        dav1d_get_bits(&gb, 1 + 8 * has_extension); // ignore

        const uint8_t *obu_end = gb.ptr_end;
        if (has_length_field) {
            const size_t len = dav1d_get_uleb128(&gb);
            if (len > (size_t)(obu_end - gb.ptr)) return DAV1D_ERR(EINVAL);
            obu_end = gb.ptr + len;
        }

        if (type == DAV1D_OBU_SEQ_HDR) {
            if ((res = parse_seq_hdr(out, &gb, 0)) < 0) return res;
            if (gb.ptr > obu_end) return DAV1D_ERR(EINVAL);
            dav1d_bytealign_get_bits(&gb);
        }

        if (gb.error) return DAV1D_ERR(EINVAL);
        assert(gb.state == 0 && gb.bits_left == 0);
        gb.ptr = obu_end;
    } while (gb.ptr < gb.ptr_end);

    return res;
}

static int read_frame_size(Dav1dContext *const c, GetBits *const gb,
                           const int use_ref)
{
    const Dav1dSequenceHeader *const seqhdr = c->seq_hdr;
    Dav1dFrameHeader *const hdr = c->frame_hdr;

    if (use_ref) {
        for (int i = 0; i < 7; i++) {
            if (dav1d_get_bit(gb)) {
                const Dav1dThreadPicture *const ref =
                    &c->refs[c->frame_hdr->refidx[i]].p;
                if (!ref->p.frame_hdr) return -1;
                hdr->width[1] = ref->p.frame_hdr->width[1];
                hdr->height = ref->p.frame_hdr->height;
                hdr->render_width = ref->p.frame_hdr->render_width;
                hdr->render_height = ref->p.frame_hdr->render_height;
                hdr->super_res.enabled = seqhdr->super_res && dav1d_get_bit(gb);
                if (hdr->super_res.enabled) {
                    const int d = hdr->super_res.width_scale_denominator =
                        9 + dav1d_get_bits(gb, 3);
                    hdr->width[0] = imax((hdr->width[1] * 8 + (d >> 1)) / d,
                                         imin(16, hdr->width[1]));
                } else {
                    hdr->super_res.width_scale_denominator = 8;
                    hdr->width[0] = hdr->width[1];
                }
                return 0;
            }
        }
    }

    if (hdr->frame_size_override) {
        hdr->width[1] = dav1d_get_bits(gb, seqhdr->width_n_bits) + 1;
        hdr->height = dav1d_get_bits(gb, seqhdr->height_n_bits) + 1;
    } else {
        hdr->width[1] = seqhdr->max_width;
        hdr->height = seqhdr->max_height;
    }
    hdr->super_res.enabled = seqhdr->super_res && dav1d_get_bit(gb);
    if (hdr->super_res.enabled) {
        const int d = hdr->super_res.width_scale_denominator = 9 + dav1d_get_bits(gb, 3);
        hdr->width[0] = imax((hdr->width[1] * 8 + (d >> 1)) / d, imin(16, hdr->width[1]));
    } else {
        hdr->super_res.width_scale_denominator = 8;
        hdr->width[0] = hdr->width[1];
    }
    hdr->have_render_size = dav1d_get_bit(gb);
    if (hdr->have_render_size) {
        hdr->render_width = dav1d_get_bits(gb, 16) + 1;
        hdr->render_height = dav1d_get_bits(gb, 16) + 1;
    } else {
        hdr->render_width = hdr->width[1];
        hdr->render_height = hdr->height;
    }
    return 0;
}

static inline int tile_log2(const int sz, const int tgt) {
    int k;
    for (k = 0; (sz << k) < tgt; k++) ;
    return k;
}

static const Dav1dLoopfilterModeRefDeltas default_mode_ref_deltas = {
    .mode_delta = { 0, 0 },
    .ref_delta = { 1, 0, 0, 0, -1, 0, -1, -1 },
};

static int parse_frame_hdr(Dav1dContext *const c, GetBits *const gb) {
#define DEBUG_FRAME_HDR 0

#if DEBUG_FRAME_HDR
    const uint8_t *const init_ptr = gb->ptr;
#endif
    const Dav1dSequenceHeader *const seqhdr = c->seq_hdr;
    Dav1dFrameHeader *const hdr = c->frame_hdr;

    hdr->show_existing_frame =
        !seqhdr->reduced_still_picture_header && dav1d_get_bit(gb);
#if DEBUG_FRAME_HDR
    printf("HDR: post-show_existing_frame: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    if (hdr->show_existing_frame) {
        hdr->existing_frame_idx = dav1d_get_bits(gb, 3);
        if (seqhdr->decoder_model_info_present && !seqhdr->equal_picture_interval)
            hdr->frame_presentation_delay = dav1d_get_bits(gb, seqhdr->frame_presentation_delay_length);
        if (seqhdr->frame_id_numbers_present) {
            hdr->frame_id = dav1d_get_bits(gb, seqhdr->frame_id_n_bits);
            Dav1dFrameHeader *const ref_frame_hdr = c->refs[hdr->existing_frame_idx].p.p.frame_hdr;
            if (!ref_frame_hdr || ref_frame_hdr->frame_id != hdr->frame_id) goto error;
        }
        return 0;
    }

    hdr->frame_type = seqhdr->reduced_still_picture_header ? DAV1D_FRAME_TYPE_KEY : dav1d_get_bits(gb, 2);
    hdr->show_frame = seqhdr->reduced_still_picture_header || dav1d_get_bit(gb);
    if (hdr->show_frame) {
        if (seqhdr->decoder_model_info_present && !seqhdr->equal_picture_interval)
            hdr->frame_presentation_delay = dav1d_get_bits(gb, seqhdr->frame_presentation_delay_length);
        hdr->showable_frame = hdr->frame_type != DAV1D_FRAME_TYPE_KEY;
    } else
        hdr->showable_frame = dav1d_get_bit(gb);
    hdr->error_resilient_mode =
        (hdr->frame_type == DAV1D_FRAME_TYPE_KEY && hdr->show_frame) ||
        hdr->frame_type == DAV1D_FRAME_TYPE_SWITCH ||
        seqhdr->reduced_still_picture_header || dav1d_get_bit(gb);
#if DEBUG_FRAME_HDR
    printf("HDR: post-frametype_bits: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->disable_cdf_update = dav1d_get_bit(gb);
    hdr->allow_screen_content_tools = seqhdr->screen_content_tools == DAV1D_ADAPTIVE ?
                                 dav1d_get_bit(gb) : seqhdr->screen_content_tools;
    if (hdr->allow_screen_content_tools)
        hdr->force_integer_mv = seqhdr->force_integer_mv == DAV1D_ADAPTIVE ?
                                dav1d_get_bit(gb) : seqhdr->force_integer_mv;
    else
        hdr->force_integer_mv = 0;

    if (IS_KEY_OR_INTRA(hdr))
        hdr->force_integer_mv = 1;

    if (seqhdr->frame_id_numbers_present)
        hdr->frame_id = dav1d_get_bits(gb, seqhdr->frame_id_n_bits);

    hdr->frame_size_override = seqhdr->reduced_still_picture_header ? 0 :
                               hdr->frame_type == DAV1D_FRAME_TYPE_SWITCH ? 1 : dav1d_get_bit(gb);
#if DEBUG_FRAME_HDR
    printf("HDR: post-frame_size_override_flag: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->frame_offset = seqhdr->order_hint ?
                        dav1d_get_bits(gb, seqhdr->order_hint_n_bits) : 0;
    hdr->primary_ref_frame = !hdr->error_resilient_mode && IS_INTER_OR_SWITCH(hdr) ?
                             dav1d_get_bits(gb, 3) : DAV1D_PRIMARY_REF_NONE;

    if (seqhdr->decoder_model_info_present) {
        hdr->buffer_removal_time_present = dav1d_get_bit(gb);
        if (hdr->buffer_removal_time_present) {
            for (int i = 0; i < c->seq_hdr->num_operating_points; i++) {
                const struct Dav1dSequenceHeaderOperatingPoint *const seqop = &seqhdr->operating_points[i];
                struct Dav1dFrameHeaderOperatingPoint *const op = &hdr->operating_points[i];
                if (seqop->decoder_model_param_present) {
                    int in_temporal_layer = (seqop->idc >> hdr->temporal_id) & 1;
                    int in_spatial_layer  = (seqop->idc >> (hdr->spatial_id + 8)) & 1;
                    if (!seqop->idc || (in_temporal_layer && in_spatial_layer))
                        op->buffer_removal_time = dav1d_get_bits(gb, seqhdr->buffer_removal_delay_length);
                }
            }
        }
    }

    if (IS_KEY_OR_INTRA(hdr)) {
        hdr->refresh_frame_flags = (hdr->frame_type == DAV1D_FRAME_TYPE_KEY &&
                                    hdr->show_frame) ? 0xff : dav1d_get_bits(gb, 8);
        if (hdr->refresh_frame_flags != 0xff && hdr->error_resilient_mode && seqhdr->order_hint)
            for (int i = 0; i < 8; i++)
                dav1d_get_bits(gb, seqhdr->order_hint_n_bits);
        if (c->strict_std_compliance &&
            hdr->frame_type == DAV1D_FRAME_TYPE_INTRA && hdr->refresh_frame_flags == 0xff)
        {
            goto error;
        }
        if (read_frame_size(c, gb, 0) < 0) goto error;
        hdr->allow_intrabc = hdr->allow_screen_content_tools &&
                             !hdr->super_res.enabled && dav1d_get_bit(gb);
        hdr->use_ref_frame_mvs = 0;
    } else {
        hdr->allow_intrabc = 0;
        hdr->refresh_frame_flags = hdr->frame_type == DAV1D_FRAME_TYPE_SWITCH ? 0xff :
                                   dav1d_get_bits(gb, 8);
        if (hdr->error_resilient_mode && seqhdr->order_hint)
            for (int i = 0; i < 8; i++)
                dav1d_get_bits(gb, seqhdr->order_hint_n_bits);
        hdr->frame_ref_short_signaling =
            seqhdr->order_hint && dav1d_get_bit(gb);
        if (hdr->frame_ref_short_signaling) { // FIXME: Nearly verbatim copy from section 7.8
            hdr->refidx[0] = dav1d_get_bits(gb, 3);
            hdr->refidx[1] = hdr->refidx[2] = -1;
            hdr->refidx[3] = dav1d_get_bits(gb, 3);
            hdr->refidx[4] = hdr->refidx[5] = hdr->refidx[6] = -1;

            int shifted_frame_offset[8];
            const int current_frame_offset = 1 << (seqhdr->order_hint_n_bits - 1);
            for (int i = 0; i < 8; i++) {
                if (!c->refs[i].p.p.frame_hdr) goto error;
                shifted_frame_offset[i] = current_frame_offset +
                    get_poc_diff(seqhdr->order_hint_n_bits,
                                 c->refs[i].p.p.frame_hdr->frame_offset,
                                 hdr->frame_offset);
            }

            int used_frame[8] = { 0 };
            used_frame[hdr->refidx[0]] = 1;
            used_frame[hdr->refidx[3]] = 1;

            int latest_frame_offset = -1;
            for (int i = 0; i < 8; i++) {
                const int hint = shifted_frame_offset[i];
                if (!used_frame[i] && hint >= current_frame_offset &&
                    hint >= latest_frame_offset)
                {
                    hdr->refidx[6] = i;
                    latest_frame_offset = hint;
                }
            }
            if (latest_frame_offset != -1)
                used_frame[hdr->refidx[6]] = 1;

            int earliest_frame_offset = INT_MAX;
            for (int i = 0; i < 8; i++) {
                const int hint = shifted_frame_offset[i];
                if (!used_frame[i] && hint >= current_frame_offset &&
                    hint < earliest_frame_offset)
                {
                    hdr->refidx[4] = i;
                    earliest_frame_offset = hint;
                }
            }
            if (earliest_frame_offset != INT_MAX)
                used_frame[hdr->refidx[4]] = 1;

            earliest_frame_offset = INT_MAX;
            for (int i = 0; i < 8; i++) {
                const int hint = shifted_frame_offset[i];
                if (!used_frame[i] && hint >= current_frame_offset &&
                    (hint < earliest_frame_offset))
                {
                    hdr->refidx[5] = i;
                    earliest_frame_offset = hint;
                }
            }
            if (earliest_frame_offset != INT_MAX)
                used_frame[hdr->refidx[5]] = 1;

            for (int i = 1; i < 7; i++) {
                if (hdr->refidx[i] < 0) {
                    latest_frame_offset = -1;
                    for (int j = 0; j < 8; j++) {
                        const int hint = shifted_frame_offset[j];
                        if (!used_frame[j] && hint < current_frame_offset &&
                            hint >= latest_frame_offset)
                        {
                            hdr->refidx[i] = j;
                            latest_frame_offset = hint;
                        }
                    }
                    if (latest_frame_offset != -1)
                        used_frame[hdr->refidx[i]] = 1;
                }
            }

            earliest_frame_offset = INT_MAX;
            int ref = -1;
            for (int i = 0; i < 8; i++) {
                const int hint = shifted_frame_offset[i];
                if (hint < earliest_frame_offset) {
                    ref = i;
                    earliest_frame_offset = hint;
                }
            }
            for (int i = 0; i < 7; i++) {
                if (hdr->refidx[i] < 0)
                    hdr->refidx[i] = ref;
            }
        }
        for (int i = 0; i < 7; i++) {
            if (!hdr->frame_ref_short_signaling)
                hdr->refidx[i] = dav1d_get_bits(gb, 3);
            if (seqhdr->frame_id_numbers_present) {
                const unsigned delta_ref_frame_id = dav1d_get_bits(gb, seqhdr->delta_frame_id_n_bits) + 1;
                const unsigned ref_frame_id = (hdr->frame_id + (1 << seqhdr->frame_id_n_bits) - delta_ref_frame_id) & ((1 << seqhdr->frame_id_n_bits) - 1);
                Dav1dFrameHeader *const ref_frame_hdr = c->refs[hdr->refidx[i]].p.p.frame_hdr;
                if (!ref_frame_hdr || ref_frame_hdr->frame_id != ref_frame_id) goto error;
            }
        }
        const int use_ref = !hdr->error_resilient_mode &&
                            hdr->frame_size_override;
        if (read_frame_size(c, gb, use_ref) < 0) goto error;
        hdr->hp = !hdr->force_integer_mv && dav1d_get_bit(gb);
        hdr->subpel_filter_mode = dav1d_get_bit(gb) ? DAV1D_FILTER_SWITCHABLE :
                                                          dav1d_get_bits(gb, 2);
        hdr->switchable_motion_mode = dav1d_get_bit(gb);
        hdr->use_ref_frame_mvs = !hdr->error_resilient_mode &&
            seqhdr->ref_frame_mvs && seqhdr->order_hint &&
            IS_INTER_OR_SWITCH(hdr) && dav1d_get_bit(gb);
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-frametype-specific-bits: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    hdr->refresh_context = !seqhdr->reduced_still_picture_header &&
                           !hdr->disable_cdf_update && !dav1d_get_bit(gb);
#if DEBUG_FRAME_HDR
    printf("HDR: post-refresh_context: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // tile data
    hdr->tiling.uniform = dav1d_get_bit(gb);
    const int sbsz_min1 = (64 << seqhdr->sb128) - 1;
    const int sbsz_log2 = 6 + seqhdr->sb128;
    const int sbw = (hdr->width[0] + sbsz_min1) >> sbsz_log2;
    const int sbh = (hdr->height + sbsz_min1) >> sbsz_log2;
    const int max_tile_width_sb = 4096 >> sbsz_log2;
    const int max_tile_area_sb = 4096 * 2304 >> (2 * sbsz_log2);
    hdr->tiling.min_log2_cols = tile_log2(max_tile_width_sb, sbw);
    hdr->tiling.max_log2_cols = tile_log2(1, imin(sbw, DAV1D_MAX_TILE_COLS));
    hdr->tiling.max_log2_rows = tile_log2(1, imin(sbh, DAV1D_MAX_TILE_ROWS));
    const int min_log2_tiles = imax(tile_log2(max_tile_area_sb, sbw * sbh),
                              hdr->tiling.min_log2_cols);
    if (hdr->tiling.uniform) {
        for (hdr->tiling.log2_cols = hdr->tiling.min_log2_cols;
             hdr->tiling.log2_cols < hdr->tiling.max_log2_cols && dav1d_get_bit(gb);
             hdr->tiling.log2_cols++) ;
        const int tile_w = 1 + ((sbw - 1) >> hdr->tiling.log2_cols);
        hdr->tiling.cols = 0;
        for (int sbx = 0; sbx < sbw; sbx += tile_w, hdr->tiling.cols++)
            hdr->tiling.col_start_sb[hdr->tiling.cols] = sbx;
        hdr->tiling.min_log2_rows =
            imax(min_log2_tiles - hdr->tiling.log2_cols, 0);

        for (hdr->tiling.log2_rows = hdr->tiling.min_log2_rows;
             hdr->tiling.log2_rows < hdr->tiling.max_log2_rows && dav1d_get_bit(gb);
             hdr->tiling.log2_rows++) ;
        const int tile_h = 1 + ((sbh - 1) >> hdr->tiling.log2_rows);
        hdr->tiling.rows = 0;
        for (int sby = 0; sby < sbh; sby += tile_h, hdr->tiling.rows++)
            hdr->tiling.row_start_sb[hdr->tiling.rows] = sby;
    } else {
        hdr->tiling.cols = 0;
        int widest_tile = 0, max_tile_area_sb = sbw * sbh;
        for (int sbx = 0; sbx < sbw && hdr->tiling.cols < DAV1D_MAX_TILE_COLS; hdr->tiling.cols++) {
            const int tile_width_sb = imin(sbw - sbx, max_tile_width_sb);
            const int tile_w = (tile_width_sb > 1) ?
                                   1 + dav1d_get_uniform(gb, tile_width_sb) :
                                   1;
            hdr->tiling.col_start_sb[hdr->tiling.cols] = sbx;
            sbx += tile_w;
            widest_tile = imax(widest_tile, tile_w);
        }
        hdr->tiling.log2_cols = tile_log2(1, hdr->tiling.cols);
        if (min_log2_tiles) max_tile_area_sb >>= min_log2_tiles + 1;
        const int max_tile_height_sb = imax(max_tile_area_sb / widest_tile, 1);

        hdr->tiling.rows = 0;
        for (int sby = 0; sby < sbh && hdr->tiling.rows < DAV1D_MAX_TILE_ROWS; hdr->tiling.rows++) {
            const int tile_height_sb = imin(sbh - sby, max_tile_height_sb);
            const int tile_h = (tile_height_sb > 1) ?
                                   1 + dav1d_get_uniform(gb, tile_height_sb) :
                                   1;
            hdr->tiling.row_start_sb[hdr->tiling.rows] = sby;
            sby += tile_h;
        }
        hdr->tiling.log2_rows = tile_log2(1, hdr->tiling.rows);
    }
    hdr->tiling.col_start_sb[hdr->tiling.cols] = sbw;
    hdr->tiling.row_start_sb[hdr->tiling.rows] = sbh;
    if (hdr->tiling.log2_cols || hdr->tiling.log2_rows) {
        hdr->tiling.update = dav1d_get_bits(gb, hdr->tiling.log2_cols +
                                                hdr->tiling.log2_rows);
        if (hdr->tiling.update >= hdr->tiling.cols * hdr->tiling.rows)
            goto error;
        hdr->tiling.n_bytes = dav1d_get_bits(gb, 2) + 1;
    } else {
        hdr->tiling.n_bytes = 0;
        hdr->tiling.update = 0;
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-tiling: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // quant data
    hdr->quant.yac = dav1d_get_bits(gb, 8);
    hdr->quant.ydc_delta = dav1d_get_bit(gb) ? dav1d_get_sbits(gb, 7) : 0;
    if (!seqhdr->monochrome) {
        // If the sequence header says that delta_q might be different
        // for U, V, we must check whether it actually is for this
        // frame.
        const int diff_uv_delta = seqhdr->separate_uv_delta_q ? dav1d_get_bit(gb) : 0;
        hdr->quant.udc_delta = dav1d_get_bit(gb) ? dav1d_get_sbits(gb, 7) : 0;
        hdr->quant.uac_delta = dav1d_get_bit(gb) ? dav1d_get_sbits(gb, 7) : 0;
        if (diff_uv_delta) {
            hdr->quant.vdc_delta = dav1d_get_bit(gb) ? dav1d_get_sbits(gb, 7) : 0;
            hdr->quant.vac_delta = dav1d_get_bit(gb) ? dav1d_get_sbits(gb, 7) : 0;
        } else {
            hdr->quant.vdc_delta = hdr->quant.udc_delta;
            hdr->quant.vac_delta = hdr->quant.uac_delta;
        }
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-quant: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->quant.qm = dav1d_get_bit(gb);
    if (hdr->quant.qm) {
        hdr->quant.qm_y = dav1d_get_bits(gb, 4);
        hdr->quant.qm_u = dav1d_get_bits(gb, 4);
        hdr->quant.qm_v =
            seqhdr->separate_uv_delta_q ? dav1d_get_bits(gb, 4) :
                                          hdr->quant.qm_u;
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-qm: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // segmentation data
    hdr->segmentation.enabled = dav1d_get_bit(gb);
    if (hdr->segmentation.enabled) {
        if (hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) {
            hdr->segmentation.update_map = 1;
            hdr->segmentation.temporal = 0;
            hdr->segmentation.update_data = 1;
        } else {
            hdr->segmentation.update_map = dav1d_get_bit(gb);
            hdr->segmentation.temporal =
                hdr->segmentation.update_map ? dav1d_get_bit(gb) : 0;
            hdr->segmentation.update_data = dav1d_get_bit(gb);
        }

        if (hdr->segmentation.update_data) {
            hdr->segmentation.seg_data.preskip = 0;
            hdr->segmentation.seg_data.last_active_segid = -1;
            for (int i = 0; i < DAV1D_MAX_SEGMENTS; i++) {
                Dav1dSegmentationData *const seg =
                    &hdr->segmentation.seg_data.d[i];
                if (dav1d_get_bit(gb)) {
                    seg->delta_q = dav1d_get_sbits(gb, 9);
                    hdr->segmentation.seg_data.last_active_segid = i;
                } else {
                    seg->delta_q = 0;
                }
                if (dav1d_get_bit(gb)) {
                    seg->delta_lf_y_v = dav1d_get_sbits(gb, 7);
                    hdr->segmentation.seg_data.last_active_segid = i;
                } else {
                    seg->delta_lf_y_v = 0;
                }
                if (dav1d_get_bit(gb)) {
                    seg->delta_lf_y_h = dav1d_get_sbits(gb, 7);
                    hdr->segmentation.seg_data.last_active_segid = i;
                } else {
                    seg->delta_lf_y_h = 0;
                }
                if (dav1d_get_bit(gb)) {
                    seg->delta_lf_u = dav1d_get_sbits(gb, 7);
                    hdr->segmentation.seg_data.last_active_segid = i;
                } else {
                    seg->delta_lf_u = 0;
                }
                if (dav1d_get_bit(gb)) {
                    seg->delta_lf_v = dav1d_get_sbits(gb, 7);
                    hdr->segmentation.seg_data.last_active_segid = i;
                } else {
                    seg->delta_lf_v = 0;
                }
                if (dav1d_get_bit(gb)) {
                    seg->ref = dav1d_get_bits(gb, 3);
                    hdr->segmentation.seg_data.last_active_segid = i;
                    hdr->segmentation.seg_data.preskip = 1;
                } else {
                    seg->ref = -1;
                }
                if ((seg->skip = dav1d_get_bit(gb))) {
                    hdr->segmentation.seg_data.last_active_segid = i;
                    hdr->segmentation.seg_data.preskip = 1;
                }
                if ((seg->globalmv = dav1d_get_bit(gb))) {
                    hdr->segmentation.seg_data.last_active_segid = i;
                    hdr->segmentation.seg_data.preskip = 1;
                }
            }
        } else {
            // segmentation.update_data was false so we should copy
            // segmentation data from the reference frame.
            assert(hdr->primary_ref_frame != DAV1D_PRIMARY_REF_NONE);
            const int pri_ref = hdr->refidx[hdr->primary_ref_frame];
            if (!c->refs[pri_ref].p.p.frame_hdr) goto error;
            hdr->segmentation.seg_data =
                c->refs[pri_ref].p.p.frame_hdr->segmentation.seg_data;
        }
    } else {
        memset(&hdr->segmentation.seg_data, 0, sizeof(Dav1dSegmentationDataSet));
        for (int i = 0; i < DAV1D_MAX_SEGMENTS; i++)
            hdr->segmentation.seg_data.d[i].ref = -1;
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-segmentation: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // delta q
    hdr->delta.q.present = hdr->quant.yac ? dav1d_get_bit(gb) : 0;
    hdr->delta.q.res_log2 = hdr->delta.q.present ? dav1d_get_bits(gb, 2) : 0;
    hdr->delta.lf.present = hdr->delta.q.present && !hdr->allow_intrabc &&
                            dav1d_get_bit(gb);
    hdr->delta.lf.res_log2 = hdr->delta.lf.present ? dav1d_get_bits(gb, 2) : 0;
    hdr->delta.lf.multi = hdr->delta.lf.present ? dav1d_get_bit(gb) : 0;
#if DEBUG_FRAME_HDR
    printf("HDR: post-delta_q_lf_flags: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // derive lossless flags
    const int delta_lossless = !hdr->quant.ydc_delta && !hdr->quant.udc_delta &&
        !hdr->quant.uac_delta && !hdr->quant.vdc_delta && !hdr->quant.vac_delta;
    hdr->all_lossless = 1;
    for (int i = 0; i < DAV1D_MAX_SEGMENTS; i++) {
        hdr->segmentation.qidx[i] = hdr->segmentation.enabled ?
            iclip_u8(hdr->quant.yac + hdr->segmentation.seg_data.d[i].delta_q) :
            hdr->quant.yac;
        hdr->segmentation.lossless[i] =
            !hdr->segmentation.qidx[i] && delta_lossless;
        hdr->all_lossless &= hdr->segmentation.lossless[i];
    }

    // loopfilter
    if (hdr->all_lossless || hdr->allow_intrabc) {
        hdr->loopfilter.level_y[0] = hdr->loopfilter.level_y[1] = 0;
        hdr->loopfilter.level_u = hdr->loopfilter.level_v = 0;
        hdr->loopfilter.sharpness = 0;
        hdr->loopfilter.mode_ref_delta_enabled = 1;
        hdr->loopfilter.mode_ref_delta_update = 1;
        hdr->loopfilter.mode_ref_deltas = default_mode_ref_deltas;
    } else {
        hdr->loopfilter.level_y[0] = dav1d_get_bits(gb, 6);
        hdr->loopfilter.level_y[1] = dav1d_get_bits(gb, 6);
        if (!seqhdr->monochrome &&
            (hdr->loopfilter.level_y[0] || hdr->loopfilter.level_y[1]))
        {
            hdr->loopfilter.level_u = dav1d_get_bits(gb, 6);
            hdr->loopfilter.level_v = dav1d_get_bits(gb, 6);
        }
        hdr->loopfilter.sharpness = dav1d_get_bits(gb, 3);

        if (hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) {
            hdr->loopfilter.mode_ref_deltas = default_mode_ref_deltas;
        } else {
            const int ref = hdr->refidx[hdr->primary_ref_frame];
            if (!c->refs[ref].p.p.frame_hdr) goto error;
            hdr->loopfilter.mode_ref_deltas =
                c->refs[ref].p.p.frame_hdr->loopfilter.mode_ref_deltas;
        }
        hdr->loopfilter.mode_ref_delta_enabled = dav1d_get_bit(gb);
        if (hdr->loopfilter.mode_ref_delta_enabled) {
            hdr->loopfilter.mode_ref_delta_update = dav1d_get_bit(gb);
            if (hdr->loopfilter.mode_ref_delta_update) {
                for (int i = 0; i < 8; i++)
                    if (dav1d_get_bit(gb))
                        hdr->loopfilter.mode_ref_deltas.ref_delta[i] =
                            dav1d_get_sbits(gb, 7);
                for (int i = 0; i < 2; i++)
                    if (dav1d_get_bit(gb))
                        hdr->loopfilter.mode_ref_deltas.mode_delta[i] =
                            dav1d_get_sbits(gb, 7);
            }
        }
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-lpf: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // cdef
    if (!hdr->all_lossless && seqhdr->cdef && !hdr->allow_intrabc) {
        hdr->cdef.damping = dav1d_get_bits(gb, 2) + 3;
        hdr->cdef.n_bits = dav1d_get_bits(gb, 2);
        for (int i = 0; i < (1 << hdr->cdef.n_bits); i++) {
            hdr->cdef.y_strength[i] = dav1d_get_bits(gb, 6);
            if (!seqhdr->monochrome)
                hdr->cdef.uv_strength[i] = dav1d_get_bits(gb, 6);
        }
    } else {
        hdr->cdef.n_bits = 0;
        hdr->cdef.y_strength[0] = 0;
        hdr->cdef.uv_strength[0] = 0;
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-cdef: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    // restoration
    if ((!hdr->all_lossless || hdr->super_res.enabled) &&
        seqhdr->restoration && !hdr->allow_intrabc)
    {
        hdr->restoration.type[0] = dav1d_get_bits(gb, 2);
        if (!seqhdr->monochrome) {
            hdr->restoration.type[1] = dav1d_get_bits(gb, 2);
            hdr->restoration.type[2] = dav1d_get_bits(gb, 2);
        } else {
            hdr->restoration.type[1] =
            hdr->restoration.type[2] = DAV1D_RESTORATION_NONE;
        }

        if (hdr->restoration.type[0] || hdr->restoration.type[1] ||
            hdr->restoration.type[2])
        {
            // Log2 of the restoration unit size.
            hdr->restoration.unit_size[0] = 6 + seqhdr->sb128;
            if (dav1d_get_bit(gb)) {
                hdr->restoration.unit_size[0]++;
                if (!seqhdr->sb128)
                    hdr->restoration.unit_size[0] += dav1d_get_bit(gb);
            }
            hdr->restoration.unit_size[1] = hdr->restoration.unit_size[0];
            if ((hdr->restoration.type[1] || hdr->restoration.type[2]) &&
                seqhdr->ss_hor == 1 && seqhdr->ss_ver == 1)
            {
                hdr->restoration.unit_size[1] -= dav1d_get_bit(gb);
            }
        } else {
            hdr->restoration.unit_size[0] = 8;
        }
    } else {
        hdr->restoration.type[0] = DAV1D_RESTORATION_NONE;
        hdr->restoration.type[1] = DAV1D_RESTORATION_NONE;
        hdr->restoration.type[2] = DAV1D_RESTORATION_NONE;
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-restoration: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    hdr->txfm_mode = hdr->all_lossless ? DAV1D_TX_4X4_ONLY :
                     dav1d_get_bit(gb) ? DAV1D_TX_SWITCHABLE : DAV1D_TX_LARGEST;
#if DEBUG_FRAME_HDR
    printf("HDR: post-txfmmode: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->switchable_comp_refs = IS_INTER_OR_SWITCH(hdr) ? dav1d_get_bit(gb) : 0;
#if DEBUG_FRAME_HDR
    printf("HDR: post-refmode: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->skip_mode_allowed = 0;
    if (hdr->switchable_comp_refs && IS_INTER_OR_SWITCH(hdr) && seqhdr->order_hint) {
        const unsigned poc = hdr->frame_offset;
        unsigned off_before = 0xFFFFFFFFU;
        int off_after = -1;
        int off_before_idx, off_after_idx;
        for (int i = 0; i < 7; i++) {
            if (!c->refs[hdr->refidx[i]].p.p.frame_hdr) goto error;
            const unsigned refpoc = c->refs[hdr->refidx[i]].p.p.frame_hdr->frame_offset;

            const int diff = get_poc_diff(seqhdr->order_hint_n_bits, refpoc, poc);
            if (diff > 0) {
                if (off_after == -1 || get_poc_diff(seqhdr->order_hint_n_bits,
                                                    off_after, refpoc) > 0)
                {
                    off_after = refpoc;
                    off_after_idx = i;
                }
            } else if (diff < 0 && (off_before == 0xFFFFFFFFU ||
                                    get_poc_diff(seqhdr->order_hint_n_bits,
                                                 refpoc, off_before) > 0))
            {
                off_before = refpoc;
                off_before_idx = i;
            }
        }

        if (off_before != 0xFFFFFFFFU && off_after != -1) {
            hdr->skip_mode_refs[0] = imin(off_before_idx, off_after_idx);
            hdr->skip_mode_refs[1] = imax(off_before_idx, off_after_idx);
            hdr->skip_mode_allowed = 1;
        } else if (off_before != 0xFFFFFFFFU) {
            unsigned off_before2 = 0xFFFFFFFFU;
            int off_before2_idx;
            for (int i = 0; i < 7; i++) {
                if (!c->refs[hdr->refidx[i]].p.p.frame_hdr) goto error;
                const unsigned refpoc = c->refs[hdr->refidx[i]].p.p.frame_hdr->frame_offset;
                if (get_poc_diff(seqhdr->order_hint_n_bits,
                                 refpoc, off_before) < 0) {
                    if (off_before2 == 0xFFFFFFFFU ||
                        get_poc_diff(seqhdr->order_hint_n_bits,
                                     refpoc, off_before2) > 0)
                    {
                        off_before2 = refpoc;
                        off_before2_idx = i;
                    }
                }
            }

            if (off_before2 != 0xFFFFFFFFU) {
                hdr->skip_mode_refs[0] = imin(off_before_idx, off_before2_idx);
                hdr->skip_mode_refs[1] = imax(off_before_idx, off_before2_idx);
                hdr->skip_mode_allowed = 1;
            }
        }
    }
    hdr->skip_mode_enabled = hdr->skip_mode_allowed ? dav1d_get_bit(gb) : 0;
#if DEBUG_FRAME_HDR
    printf("HDR: post-extskip: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->warp_motion = !hdr->error_resilient_mode && IS_INTER_OR_SWITCH(hdr) &&
        seqhdr->warped_motion && dav1d_get_bit(gb);
#if DEBUG_FRAME_HDR
    printf("HDR: post-warpmotionbit: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif
    hdr->reduced_txtp_set = dav1d_get_bit(gb);
#if DEBUG_FRAME_HDR
    printf("HDR: post-reducedtxtpset: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    for (int i = 0; i < 7; i++)
        hdr->gmv[i] = dav1d_default_wm_params;

    if (IS_INTER_OR_SWITCH(hdr)) {
        for (int i = 0; i < 7; i++) {
            hdr->gmv[i].type = !dav1d_get_bit(gb) ? DAV1D_WM_TYPE_IDENTITY :
                                dav1d_get_bit(gb) ? DAV1D_WM_TYPE_ROT_ZOOM :
                                dav1d_get_bit(gb) ? DAV1D_WM_TYPE_TRANSLATION :
                                                  DAV1D_WM_TYPE_AFFINE;

            if (hdr->gmv[i].type == DAV1D_WM_TYPE_IDENTITY) continue;

            const Dav1dWarpedMotionParams *ref_gmv;
            if (hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) {
                ref_gmv = &dav1d_default_wm_params;
            } else {
                const int pri_ref = hdr->refidx[hdr->primary_ref_frame];
                if (!c->refs[pri_ref].p.p.frame_hdr) goto error;
                ref_gmv = &c->refs[pri_ref].p.p.frame_hdr->gmv[i];
            }
            int32_t *const mat = hdr->gmv[i].matrix;
            const int32_t *const ref_mat = ref_gmv->matrix;
            int bits, shift;

            if (hdr->gmv[i].type >= DAV1D_WM_TYPE_ROT_ZOOM) {
                mat[2] = (1 << 16) + 2 *
                    dav1d_get_bits_subexp(gb, (ref_mat[2] - (1 << 16)) >> 1, 12);
                mat[3] = 2 * dav1d_get_bits_subexp(gb, ref_mat[3] >> 1, 12);

                bits = 12;
                shift = 10;
            } else {
                bits = 9 - !hdr->hp;
                shift = 13 + !hdr->hp;
            }

            if (hdr->gmv[i].type == DAV1D_WM_TYPE_AFFINE) {
                mat[4] = 2 * dav1d_get_bits_subexp(gb, ref_mat[4] >> 1, 12);
                mat[5] = (1 << 16) + 2 *
                    dav1d_get_bits_subexp(gb, (ref_mat[5] - (1 << 16)) >> 1, 12);
            } else {
                mat[4] = -mat[3];
                mat[5] = mat[2];
            }

            mat[0] = dav1d_get_bits_subexp(gb, ref_mat[0] >> shift, bits) * (1 << shift);
            mat[1] = dav1d_get_bits_subexp(gb, ref_mat[1] >> shift, bits) * (1 << shift);
        }
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-gmv: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    hdr->film_grain.present = seqhdr->film_grain_present &&
                              (hdr->show_frame || hdr->showable_frame) &&
                              dav1d_get_bit(gb);
    if (hdr->film_grain.present) {
        const unsigned seed = dav1d_get_bits(gb, 16);
        hdr->film_grain.update = hdr->frame_type != DAV1D_FRAME_TYPE_INTER || dav1d_get_bit(gb);
        if (!hdr->film_grain.update) {
            const int refidx = dav1d_get_bits(gb, 3);
            int i;
            for (i = 0; i < 7; i++)
                if (hdr->refidx[i] == refidx)
                    break;
            if (i == 7 || !c->refs[refidx].p.p.frame_hdr) goto error;
            hdr->film_grain.data = c->refs[refidx].p.p.frame_hdr->film_grain.data;
            hdr->film_grain.data.seed = seed;
        } else {
            Dav1dFilmGrainData *const fgd = &hdr->film_grain.data;
            fgd->seed = seed;

            fgd->num_y_points = dav1d_get_bits(gb, 4);
            if (fgd->num_y_points > 14) goto error;
            for (int i = 0; i < fgd->num_y_points; i++) {
                fgd->y_points[i][0] = dav1d_get_bits(gb, 8);
                if (i && fgd->y_points[i - 1][0] >= fgd->y_points[i][0])
                    goto error;
                fgd->y_points[i][1] = dav1d_get_bits(gb, 8);
            }

            fgd->chroma_scaling_from_luma =
                !seqhdr->monochrome && dav1d_get_bit(gb);
            if (seqhdr->monochrome || fgd->chroma_scaling_from_luma ||
                (seqhdr->ss_ver == 1 && seqhdr->ss_hor == 1 && !fgd->num_y_points))
            {
                fgd->num_uv_points[0] = fgd->num_uv_points[1] = 0;
            } else for (int pl = 0; pl < 2; pl++) {
                fgd->num_uv_points[pl] = dav1d_get_bits(gb, 4);
                if (fgd->num_uv_points[pl] > 10) goto error;
                for (int i = 0; i < fgd->num_uv_points[pl]; i++) {
                    fgd->uv_points[pl][i][0] = dav1d_get_bits(gb, 8);
                    if (i && fgd->uv_points[pl][i - 1][0] >= fgd->uv_points[pl][i][0])
                        goto error;
                    fgd->uv_points[pl][i][1] = dav1d_get_bits(gb, 8);
                }
            }

            if (seqhdr->ss_hor == 1 && seqhdr->ss_ver == 1 &&
                !!fgd->num_uv_points[0] != !!fgd->num_uv_points[1])
            {
                goto error;
            }

            fgd->scaling_shift = dav1d_get_bits(gb, 2) + 8;
            fgd->ar_coeff_lag = dav1d_get_bits(gb, 2);
            const int num_y_pos = 2 * fgd->ar_coeff_lag * (fgd->ar_coeff_lag + 1);
            if (fgd->num_y_points)
                for (int i = 0; i < num_y_pos; i++)
                    fgd->ar_coeffs_y[i] = dav1d_get_bits(gb, 8) - 128;
            for (int pl = 0; pl < 2; pl++)
                if (fgd->num_uv_points[pl] || fgd->chroma_scaling_from_luma) {
                    const int num_uv_pos = num_y_pos + !!fgd->num_y_points;
                    for (int i = 0; i < num_uv_pos; i++)
                        fgd->ar_coeffs_uv[pl][i] = dav1d_get_bits(gb, 8) - 128;
                    if (!fgd->num_y_points)
                        fgd->ar_coeffs_uv[pl][num_uv_pos] = 0;
                }
            fgd->ar_coeff_shift = dav1d_get_bits(gb, 2) + 6;
            fgd->grain_scale_shift = dav1d_get_bits(gb, 2);
            for (int pl = 0; pl < 2; pl++)
                if (fgd->num_uv_points[pl]) {
                    fgd->uv_mult[pl] = dav1d_get_bits(gb, 8) - 128;
                    fgd->uv_luma_mult[pl] = dav1d_get_bits(gb, 8) - 128;
                    fgd->uv_offset[pl] = dav1d_get_bits(gb, 9) - 256;
                }
            fgd->overlap_flag = dav1d_get_bit(gb);
            fgd->clip_to_restricted_range = dav1d_get_bit(gb);
        }
    } else {
        memset(&hdr->film_grain.data, 0, sizeof(hdr->film_grain.data));
    }
#if DEBUG_FRAME_HDR
    printf("HDR: post-filmgrain: off=%td\n",
           (gb->ptr - init_ptr) * 8 - gb->bits_left);
#endif

    return 0;

error:
    dav1d_log(c, "Error parsing frame header\n");
    return DAV1D_ERR(EINVAL);
}

static void parse_tile_hdr(Dav1dContext *const c, GetBits *const gb) {
    const int n_tiles = c->frame_hdr->tiling.cols * c->frame_hdr->tiling.rows;
    const int have_tile_pos = n_tiles > 1 ? dav1d_get_bit(gb) : 0;

    if (have_tile_pos) {
        const int n_bits = c->frame_hdr->tiling.log2_cols +
                           c->frame_hdr->tiling.log2_rows;
        c->tile[c->n_tile_data].start = dav1d_get_bits(gb, n_bits);
        c->tile[c->n_tile_data].end = dav1d_get_bits(gb, n_bits);
    } else {
        c->tile[c->n_tile_data].start = 0;
        c->tile[c->n_tile_data].end = n_tiles - 1;
    }
}

ptrdiff_t dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in) {
    GetBits gb;
    int res;

    dav1d_init_get_bits(&gb, in->data, in->sz);

    // obu header
    const int obu_forbidden_bit = dav1d_get_bit(&gb);
    if (c->strict_std_compliance && obu_forbidden_bit) goto error;
    const enum Dav1dObuType type = dav1d_get_bits(&gb, 4);
    const int has_extension = dav1d_get_bit(&gb);
    const int has_length_field = dav1d_get_bit(&gb);
    dav1d_get_bit(&gb); // reserved

    int temporal_id = 0, spatial_id = 0;
    if (has_extension) {
        temporal_id = dav1d_get_bits(&gb, 3);
        spatial_id = dav1d_get_bits(&gb, 2);
        dav1d_get_bits(&gb, 3); // reserved
    }

    if (has_length_field) {
        const size_t len = dav1d_get_uleb128(&gb);
        if (len > (size_t)(gb.ptr_end - gb.ptr)) goto error;
        gb.ptr_end = gb.ptr + len;
    }
    if (gb.error) goto error;

    // We must have read a whole number of bytes at this point (1 byte
    // for the header and whole bytes at a time when reading the
    // leb128 length field).
    assert(gb.bits_left == 0);

    // skip obu not belonging to the selected temporal/spatial layer
    if (type != DAV1D_OBU_SEQ_HDR && type != DAV1D_OBU_TD &&
        has_extension && c->operating_point_idc != 0)
    {
        const int in_temporal_layer = (c->operating_point_idc >> temporal_id) & 1;
        const int in_spatial_layer = (c->operating_point_idc >> (spatial_id + 8)) & 1;
        if (!in_temporal_layer || !in_spatial_layer)
            return gb.ptr_end - gb.ptr_start;
    }

    switch (type) {
    case DAV1D_OBU_SEQ_HDR: {
        Dav1dRef *ref = dav1d_ref_create_using_pool(c->seq_hdr_pool,
                                                    sizeof(Dav1dSequenceHeader));
        if (!ref) return DAV1D_ERR(ENOMEM);
        Dav1dSequenceHeader *seq_hdr = ref->data;
        if ((res = parse_seq_hdr(seq_hdr, &gb, c->strict_std_compliance)) < 0) {
            dav1d_log(c, "Error parsing sequence header\n");
            dav1d_ref_dec(&ref);
            goto error;
        }

        const int op_idx =
            c->operating_point < seq_hdr->num_operating_points ? c->operating_point : 0;
        c->operating_point_idc = seq_hdr->operating_points[op_idx].idc;
        const unsigned spatial_mask = c->operating_point_idc >> 8;
        c->max_spatial_id = spatial_mask ? ulog2(spatial_mask) : 0;

        // If we have read a sequence header which is different from
        // the old one, this is a new video sequence and can't use any
        // previous state. Free that state.

        if (!c->seq_hdr) {
            c->frame_hdr = NULL;
            c->frame_flags |= PICTURE_FLAG_NEW_SEQUENCE;
        // see 7.5, operating_parameter_info is allowed to change in
        // sequence headers of a single sequence
        } else if (memcmp(seq_hdr, c->seq_hdr, offsetof(Dav1dSequenceHeader, operating_parameter_info))) {
            c->frame_hdr = NULL;
            c->mastering_display = NULL;
            c->content_light = NULL;
            dav1d_ref_dec(&c->mastering_display_ref);
            dav1d_ref_dec(&c->content_light_ref);
            for (int i = 0; i < 8; i++) {
                if (c->refs[i].p.p.frame_hdr)
                    dav1d_thread_picture_unref(&c->refs[i].p);
                dav1d_ref_dec(&c->refs[i].segmap);
                dav1d_ref_dec(&c->refs[i].refmvs);
                dav1d_cdf_thread_unref(&c->cdf[i]);
            }
            c->frame_flags |= PICTURE_FLAG_NEW_SEQUENCE;
        // If operating_parameter_info changed, signal it
        } else if (memcmp(seq_hdr->operating_parameter_info, c->seq_hdr->operating_parameter_info,
                          sizeof(seq_hdr->operating_parameter_info)))
        {
            c->frame_flags |= PICTURE_FLAG_NEW_OP_PARAMS_INFO;
        }
        dav1d_ref_dec(&c->seq_hdr_ref);
        c->seq_hdr_ref = ref;
        c->seq_hdr = seq_hdr;
        break;
    }
    case DAV1D_OBU_REDUNDANT_FRAME_HDR:
        if (c->frame_hdr) break;
        // fall-through
    case DAV1D_OBU_FRAME:
    case DAV1D_OBU_FRAME_HDR:
        if (!c->seq_hdr) goto error;
        if (!c->frame_hdr_ref) {
            c->frame_hdr_ref = dav1d_ref_create_using_pool(c->frame_hdr_pool,
                                                           sizeof(Dav1dFrameHeader));
            if (!c->frame_hdr_ref) return DAV1D_ERR(ENOMEM);
        }
#ifndef NDEBUG
        // ensure that the reference is writable
        assert(dav1d_ref_is_writable(c->frame_hdr_ref));
#endif
        c->frame_hdr = c->frame_hdr_ref->data;
        memset(c->frame_hdr, 0, sizeof(*c->frame_hdr));
        c->frame_hdr->temporal_id = temporal_id;
        c->frame_hdr->spatial_id = spatial_id;
        if ((res = parse_frame_hdr(c, &gb)) < 0) {
            c->frame_hdr = NULL;
            goto error;
        }
        for (int n = 0; n < c->n_tile_data; n++)
            dav1d_data_unref_internal(&c->tile[n].data);
        c->n_tile_data = 0;
        c->n_tiles = 0;
        if (type != DAV1D_OBU_FRAME) {
            // This is actually a frame header OBU so read the
            // trailing bit and check for overrun.
            if (check_trailing_bits(&gb, c->strict_std_compliance) < 0) {
                c->frame_hdr = NULL;
                goto error;
            }
        }

        if (c->frame_size_limit && (int64_t)c->frame_hdr->width[1] *
            c->frame_hdr->height > c->frame_size_limit)
        {
            dav1d_log(c, "Frame size %dx%d exceeds limit %u\n", c->frame_hdr->width[1],
                      c->frame_hdr->height, c->frame_size_limit);
            c->frame_hdr = NULL;
            return DAV1D_ERR(ERANGE);
        }

        if (type != DAV1D_OBU_FRAME)
            break;
        // OBU_FRAMEs shouldn't be signaled with show_existing_frame
        if (c->frame_hdr->show_existing_frame) {
            c->frame_hdr = NULL;
            goto error;
        }

        // This is the frame header at the start of a frame OBU.
        // There's no trailing bit at the end to skip, but we do need
        // to align to the next byte.
        dav1d_bytealign_get_bits(&gb);
        // fall-through
    case DAV1D_OBU_TILE_GRP: {
        if (!c->frame_hdr) goto error;
        if (c->n_tile_data_alloc < c->n_tile_data + 1) {
            if ((c->n_tile_data + 1) > INT_MAX / (int)sizeof(*c->tile)) goto error;
            struct Dav1dTileGroup *tile = dav1d_realloc(ALLOC_TILE, c->tile,
                                                        (c->n_tile_data + 1) * sizeof(*c->tile));
            if (!tile) goto error;
            c->tile = tile;
            memset(c->tile + c->n_tile_data, 0, sizeof(*c->tile));
            c->n_tile_data_alloc = c->n_tile_data + 1;
        }
        parse_tile_hdr(c, &gb);
        // Align to the next byte boundary and check for overrun.
        dav1d_bytealign_get_bits(&gb);
        if (gb.error) goto error;

        dav1d_data_ref(&c->tile[c->n_tile_data].data, in);
        c->tile[c->n_tile_data].data.data = gb.ptr;
        c->tile[c->n_tile_data].data.sz = (size_t)(gb.ptr_end - gb.ptr);
        // ensure tile groups are in order and sane, see 6.10.1
        if (c->tile[c->n_tile_data].start > c->tile[c->n_tile_data].end ||
            c->tile[c->n_tile_data].start != c->n_tiles)
        {
            for (int i = 0; i <= c->n_tile_data; i++)
                dav1d_data_unref_internal(&c->tile[i].data);
            c->n_tile_data = 0;
            c->n_tiles = 0;
            goto error;
        }
        c->n_tiles += 1 + c->tile[c->n_tile_data].end -
                          c->tile[c->n_tile_data].start;
        c->n_tile_data++;
        break;
    }
    case DAV1D_OBU_METADATA: {
#define DEBUG_OBU_METADATA 0
#if DEBUG_OBU_METADATA
        const uint8_t *const init_ptr = gb.ptr;
#endif
        // obu metadta type field
        const enum ObuMetaType meta_type = dav1d_get_uleb128(&gb);
        if (gb.error) goto error;

        switch (meta_type) {
        case OBU_META_HDR_CLL: {
            Dav1dRef *ref = dav1d_ref_create(ALLOC_OBU_META,
                                             sizeof(Dav1dContentLightLevel));
            if (!ref) return DAV1D_ERR(ENOMEM);
            Dav1dContentLightLevel *const content_light = ref->data;

            content_light->max_content_light_level = dav1d_get_bits(&gb, 16);
#if DEBUG_OBU_METADATA
            printf("CLLOBU: max-content-light-level: %d [off=%td]\n",
                   content_light->max_content_light_level,
                   (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif
            content_light->max_frame_average_light_level = dav1d_get_bits(&gb, 16);
#if DEBUG_OBU_METADATA
            printf("CLLOBU: max-frame-average-light-level: %d [off=%td]\n",
                   content_light->max_frame_average_light_level,
                   (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif

            if (check_trailing_bits(&gb, c->strict_std_compliance) < 0) {
                dav1d_ref_dec(&ref);
                goto error;
            }

            dav1d_ref_dec(&c->content_light_ref);
            c->content_light = content_light;
            c->content_light_ref = ref;
            break;
        }
        case OBU_META_HDR_MDCV: {
            Dav1dRef *ref = dav1d_ref_create(ALLOC_OBU_META,
                                             sizeof(Dav1dMasteringDisplay));
            if (!ref) return DAV1D_ERR(ENOMEM);
            Dav1dMasteringDisplay *const mastering_display = ref->data;

            for (int i = 0; i < 3; i++) {
                mastering_display->primaries[i][0] = dav1d_get_bits(&gb, 16);
                mastering_display->primaries[i][1] = dav1d_get_bits(&gb, 16);
#if DEBUG_OBU_METADATA
                printf("MDCVOBU: primaries[%d]: (%d, %d) [off=%td]\n", i,
                       mastering_display->primaries[i][0],
                       mastering_display->primaries[i][1],
                       (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif
            }
            mastering_display->white_point[0] = dav1d_get_bits(&gb, 16);
#if DEBUG_OBU_METADATA
            printf("MDCVOBU: white-point-x: %d [off=%td]\n",
                   mastering_display->white_point[0],
                   (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif
            mastering_display->white_point[1] = dav1d_get_bits(&gb, 16);
#if DEBUG_OBU_METADATA
            printf("MDCVOBU: white-point-y: %d [off=%td]\n",
                   mastering_display->white_point[1],
                   (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif
            mastering_display->max_luminance = dav1d_get_bits(&gb, 32);
#if DEBUG_OBU_METADATA
            printf("MDCVOBU: max-luminance: %d [off=%td]\n",
                   mastering_display->max_luminance,
                   (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif
            mastering_display->min_luminance = dav1d_get_bits(&gb, 32);
#if DEBUG_OBU_METADATA
            printf("MDCVOBU: min-luminance: %d [off=%td]\n",
                   mastering_display->min_luminance,
                   (gb.ptr - init_ptr) * 8 - gb.bits_left);
#endif
            if (check_trailing_bits(&gb, c->strict_std_compliance) < 0) {
                dav1d_ref_dec(&ref);
                goto error;
            }

            dav1d_ref_dec(&c->mastering_display_ref);
            c->mastering_display = mastering_display;
            c->mastering_display_ref = ref;
            break;
        }
        case OBU_META_ITUT_T35: {
            ptrdiff_t payload_size = gb.ptr_end - gb.ptr;
            // Don't take into account all the trailing bits for payload_size
            while (payload_size > 0 && !gb.ptr[payload_size - 1])
                payload_size--; // trailing_zero_bit x 8
            payload_size--; // trailing_one_bit + trailing_zero_bit x 7

            int country_code_extension_byte = 0;
            const int country_code = dav1d_get_bits(&gb, 8);
            payload_size--;
            if (country_code == 0xFF) {
                country_code_extension_byte = dav1d_get_bits(&gb, 8);
                payload_size--;
            }

            if (payload_size <= 0 || gb.ptr[payload_size] != 0x80) {
                dav1d_log(c, "Malformed ITU-T T.35 metadata message format\n");
                break;
            }

            if ((c->n_itut_t35 + 1) > INT_MAX / (int)sizeof(*c->itut_t35)) goto error;
            struct Dav1dITUTT35 *itut_t35 = dav1d_realloc(ALLOC_OBU_META, c->itut_t35,
                                                          (c->n_itut_t35 + 1) * sizeof(*c->itut_t35));
            if (!itut_t35) goto error;
            c->itut_t35 = itut_t35;
            memset(c->itut_t35 + c->n_itut_t35, 0, sizeof(*c->itut_t35));

            struct itut_t35_ctx_context *itut_t35_ctx;
            if (!c->n_itut_t35) {
                assert(!c->itut_t35_ref);
                itut_t35_ctx = dav1d_malloc(ALLOC_OBU_META, sizeof(struct itut_t35_ctx_context));
                if (!itut_t35_ctx) goto error;
                c->itut_t35_ref = dav1d_ref_init(&itut_t35_ctx->ref, c->itut_t35,
                                                 dav1d_picture_free_itut_t35, itut_t35_ctx, 0);
            } else {
                assert(c->itut_t35_ref && atomic_load(&c->itut_t35_ref->ref_cnt) == 1);
                itut_t35_ctx = c->itut_t35_ref->user_data;
                c->itut_t35_ref->const_data = (uint8_t *)c->itut_t35;
            }
            itut_t35_ctx->itut_t35 = c->itut_t35;
            itut_t35_ctx->n_itut_t35 = c->n_itut_t35 + 1;

            Dav1dITUTT35 *const itut_t35_metadata = &c->itut_t35[c->n_itut_t35];
            itut_t35_metadata->payload = dav1d_malloc(ALLOC_OBU_META, payload_size);
            if (!itut_t35_metadata->payload) goto error;

            itut_t35_metadata->country_code = country_code;
            itut_t35_metadata->country_code_extension_byte = country_code_extension_byte;
            itut_t35_metadata->payload_size = payload_size;

            // We know that we've read a whole number of bytes and that the
            // payload is within the OBU boundaries, so just use memcpy()
            assert(gb.bits_left == 0);
            memcpy(itut_t35_metadata->payload, gb.ptr, payload_size);

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=72 G=85

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge