/*
 * Copyright (c) 2020, Derek Buitenhuis
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

 #include <assert.h>
 #include <inttypes.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "obuparse.h"
 
 /************************************
  * Bitreader functions and structs. *
  ************************************/
 
 typedef struct _OBPBitReader {
     uint8_t *buf;
     size_t buf_size;
     size_t buf_pos;
     uint64_t bit_buffer;
     uint8_t bits_in_buf;
 } _OBPBitReader;
 
 static inline _OBPBitReader _obp_new_br(uint8_t *buf, size_t buf_size)
 {
     _OBPBitReader ret = { buf, buf_size, 0, 0, 0 };
     return ret;
 }
 
 static inline uint64_t _obp_br_unchecked(_OBPBitReader *br, uint8_t n)
 {
     assert(n <= 63);
 
     while (n > br->bits_in_buf) {
         br->bit_buffer <<= 8;
         br->bit_buffer  |= (uint64_t) br->buf[br->buf_pos];
         br->bits_in_buf += 8;
         br->buf_pos++;
 
         if (br->bits_in_buf > 56) {
             if (n <= br->bits_in_buf)
                 break;
 
             if (n <= 64)
                 return (_obp_br_unchecked(br, 32) << 32) | (_obp_br_unchecked(br, n - 32));
         }
     }
 
     br->bits_in_buf -= n;
     return (br->bit_buffer >> br->bits_in_buf) & ((((uint64_t)1) << n) - 1);
 }
 
 static inline void _obp_br_byte_alignment(_OBPBitReader *br)
 {
     br->bits_in_buf -= br->bits_in_buf % 8;
 }
 
 static inline size_t _obp_br_get_pos(_OBPBitReader *br)
 {
     return (br->buf_pos * 8) - ((size_t) br->bits_in_buf);
 }
 
 #if OBP_UNCHECKED_BITREADER
 #define _obp_br(x, br, n) do { \
     x = _obp_br_unchecked(br, n); \
 } while(0)
 #else
 #define _obp_br(x, br, n) do { \
     size_t bytes_needed = ((n - br->bits_in_buf) + (1<<3) - 1) >> 3; \
     if (bytes_needed > (br->buf_size - br->buf_pos)) { \
         snprintf(err->error, err->size, "Ran out of bytes in buffer."); \
         return -1; \
     } \
     x = _obp_br_unchecked(br, n); \
 } while(0)
 #endif
 
 /************************************
  * Functions from AV1 specification. *
  ************************************/
 
 static inline int _obp_leb128(uint8_t *buf, size_t size, uint64_t *value, ptrdiff_t *consumed, OBPError *err)
 {
     *value       = 0;
     *consumed    = 0;
 
     for (uint64_t i = 0; i < 8; i++) {
         uint8_t b;
 
         if (((size_t) (*consumed) + 1) > size) {
             snprintf(err->error, err->size, "Buffer too short to read leb128 value.");
             return -1;
         }
 
         b       = buf[*consumed];
         *value |= ((uint64_t)(b & 0x7F)) << (i * 7);
         (*consumed)++;
 
         if ((b & 0x80) != 0x80)
             break;
     }
 
     return 0;
 }
 
 static inline int _obp_uvlc(_OBPBitReader *br, uint32_t *value, OBPError *err)
 {
     uint32_t leading_zeroes = 0;
     while (leading_zeroes < 32) {
         int b;
         _obp_br(b, br, 1);
         if (b != 0)
             break;
         leading_zeroes++;
     }
     if (leading_zeroes == 32) {
         snprintf(err->error, err->size, "Invalid VLC.");
         return -1;
     }
     uint32_t val;
     _obp_br(val, br, leading_zeroes);
     *value = val + ((1 << leading_zeroes) - 1);
     return 0;
 }
 
 static inline int32_t _obp_get_relative_dist(int32_t a, int32_t b, OBPSequenceHeader *seq)
 {
     int32_t diff, m;
 
     if (!seq->enable_order_hint)
         return 0;
 
     diff = a - b;
     m    = 1 << (seq->OrderHintBits - 1);
     diff = (diff & (m - 1)) - (diff & m);
 
     return diff;
 }
 
 
 
 static inline uint32_t _obp_tile_log2(uint32_t blkSize, uint32_t target)
 {
     uint32_t k;
     for (k = 0; (blkSize << k) < target; k++) {
     }
     return k;
 }
 
 static inline uint32_t _obp_floor_log2(uint32_t a)
 {
     uint32_t s = 0;
     uint32_t x = a;
     while (x != 0) {
         x = x >> 1;
         s++;
     }
     return s - 1;
 }
 
 static inline uint64_t _obp_le(uint8_t *buf, uint8_t n)
 {
     uint64_t t = 0;
     size_t pos = 0;
     for (uint8_t i = 0; i < n; i++) {
         uint8_t byte = buf[pos];
         t += ((uint64_t)byte) << (i * 8);
         pos++;
     }
     return t;
 }
 
 static inline int _obp_ns(_OBPBitReader *br, uint32_t n, uint32_t *out, OBPError *err)
 {
     uint32_t w = _obp_floor_log2(n) + 1;
     uint32_t m = (((uint32_t)1) << w) - n;
     uint32_t v;
     uint32_t extra_bit;
 
     assert(w - 1 <= 32);
     _obp_br(v, br, ((uint8_t)(w - 1)));
     if (v < m) {
         *out = v;
         return 0;
     }
     _obp_br(extra_bit, br, 1);
     *out = (v << 1) - m + extra_bit;
     return 0;
 }
 
 static inline int _obp_su(_OBPBitReader *br, uint32_t n, int32_t *out, OBPError *err)
 {
     int32_t value;
     uint32_t signMask;
 
     _obp_br(value, br, n);
     signMask = ((uint32_t)1) << (n - 1);
     if (value & signMask) {
         value = value - 2 * signMask;
     }
     *out = value;
     return 0;
 }
 
 static inline int _obp_decode_subexp(_OBPBitReader *br, int32_t numSyms, uint32_t *out, OBPError *err)
 {
     int32_t i  = 0;
     int32_t mk = 0;
     int32_t k  = 3;
     while (1) {
         int32_t b2 = i ? k + i - 1 : k;
         int32_t a  = 1 << b2;
         if (numSyms <= mk + 3 * a) {
             uint32_t val;
             int ret = _obp_ns(br, numSyms - mk, &val, err);
             if (ret < 0) {
                 return -1;
             }
             *out = val;
             return 0;
         } else {
             int subexp_more_bits;
             _obp_br(subexp_more_bits, br, 1);
             if (subexp_more_bits) {
                 i++;
                 mk += a;
             } else {
                 uint32_t subexp_bits;
                 assert(b2 <= 255);
                 _obp_br(subexp_bits, br, ((uint8_t)b2));
                 *out = subexp_bits + mk;
                 return 0;
             }
         }
     }
 }
 
 static inline int32_t _obps_inverse_recenter(int32_t r, uint32_t v)
 {
     if (((uint64_t)v) > ((uint64_t)(2 * r))) {
         return (int32_t) v;
     } else if (v & 1) {
         return r - ((v + 1) >> 1);
     } else {
         return r + (v >> 1);
     }
 }
 
 static inline int _obp_decode_unsigned_subexp_with_ref(_OBPBitReader *br, int32_t mx, int32_t r, int16_t *out, OBPError *err) {
     uint32_t v;
     int ret = _obp_decode_subexp(br, mx, &v, err);
     if (ret < 0) {
         return -1;
     }
     if (r < 0) { /* avoid signed shift */
         if (-(-r << 1) <= mx) {
             *out = _obps_inverse_recenter(r, v);
             return 0;
         }
     }
     if ((r << 1) <= mx) {
         *out = _obps_inverse_recenter(r, v);
         return 0;
     } else {
         *out = mx - 1 - _obps_inverse_recenter(mx - 1 - r, v);
         return 0;
     }
 
     return 0;
 }
 
 static inline int _obp_decode_signed_subexp_with_ref(_OBPBitReader *br, int32_t low, int32_t high, int32_t r, int16_t *out, OBPError *err) {
     int16_t val;
     int ret = _obp_decode_unsigned_subexp_with_ref(br, high - low, r - low, &val, err);
     if (ret < 0) {
         return -1;
     }
     *out = val + low;
     return 0;
 }
 
 /*********************
  * Helper functions. *
  *********************/
 
 #define _OBP_MAX(x,y) ((x) > (y) ? x : y)
 #define _OBP_MIN(x,y) ((x) < (y) ? x : y)
 
 static inline int _obp_is_valid_obu(OBPOBUType type)
 {
     return type == OBP_OBU_SEQUENCE_HEADER ||
            type == OBP_OBU_TEMPORAL_DELIMITER ||
            type == OBP_OBU_FRAME_HEADER ||
            type == OBP_OBU_TILE_GROUP ||
            type == OBP_OBU_METADATA ||
            type == OBP_OBU_FRAME ||
            type == OBP_OBU_REDUNDANT_FRAME_HEADER ||
            type == OBP_OBU_TILE_LIST ||
            type == OBP_OBU_PADDING;
 }
 
 static inline int _obp_set_frame_refs(OBPFrameHeader *fh, OBPSequenceHeader *seq, OBPState *state, OBPError *err)
 {
     int usedFrame[8];
     uint32_t curFrameHint, lastOrderHint, goldOrderHint, latestOrderHint, earliestOrderHint;
     int32_t ref;
     uint8_t shiftedOrderHints[8];
     const int Ref_Frame_List[7 - 2] = { 2, 3, 5, 6, 7 }; /*LAST2_FRAME, LAST3_FRAME, BWDREF_FRAME, ALTREF2_FRAME, ALTREF_FRAME */
     int32_t ref_frame_idx[8];
 
     for (int i = 0; i < 7; i++) {
         ref_frame_idx[i] = -1;
     }
     ref_frame_idx[1 - 1] = fh->last_frame_idx;
     ref_frame_idx[4 - 1] = fh->gold_frame_idx;
     for (int i = 0; i < 8; i++) {
         usedFrame[i] = 0;
     }
     usedFrame[fh->last_frame_idx] = 1;
     usedFrame[fh->gold_frame_idx] = 2;
     curFrameHint                  = 1 << (seq->OrderHintBits - 1);
     for (int i = 0; i < 8; i++) {
         shiftedOrderHints[i] = curFrameHint + _obp_get_relative_dist(state->RefOrderHint[i], fh->order_hint, seq);
     }
     lastOrderHint = shiftedOrderHints[fh->last_frame_idx];
     goldOrderHint = shiftedOrderHints[fh->gold_frame_idx];
     if (lastOrderHint >= curFrameHint || goldOrderHint >= curFrameHint) {
         snprintf(err->error, err->size, "(lastOrderHint >= curFrameHint || goldOrderHint >= curFrameHint) not allowed.");
         return -1;
     }
     /* find_latest_backward() */
     ref             = -1;
     latestOrderHint = 0;
     for (int i = 0; i < 8; i++) {
         uint32_t hint = shiftedOrderHints[i];
         if (!usedFrame[i] && (hint >= curFrameHint) && (ref < 0 || hint >= latestOrderHint)) {
             ref             = i;
             latestOrderHint = hint;
         }
     }
     if (ref >= 0) {
         ref_frame_idx[7 - 1] = ref;
         usedFrame[ref]       = 1;
     }
     /* find_earliest_backward() */
     ref               = -1;
     earliestOrderHint = 0;
     for (int i = 0; i < 8; i++) {
         uint32_t hint = shiftedOrderHints[i];
         if (!usedFrame[i] && (hint >= curFrameHint) && (ref < 0 || hint < earliestOrderHint)) {
             ref               = i;
             earliestOrderHint = hint;
         }
     }
     if (ref >= 0) {
         ref_frame_idx[5 - 1] = ref;
         usedFrame[ref]       = 1;
     }
     /* find_earliest_backward() */
     ref               = -1;
     earliestOrderHint = 0;
     for (int i = 0; i < 8; i++) {
         uint32_t hint = shiftedOrderHints[i];
         if (!usedFrame[i] && (hint >= curFrameHint) && (ref < 0 || hint < earliestOrderHint)) {
             ref               = i;
             earliestOrderHint = hint;
         }
     }
     if (ref >= 0) {
         ref_frame_idx[6 - 1] = ref;
         usedFrame[ref]       = 1;
     }
     for (int i = 0; i < 7 - 2; i++) {
         uint8_t refFrame = Ref_Frame_List[i];
         if (ref_frame_idx[refFrame - 1] < 0) {
             int32_t subref              = -1;
             uint32_t subLatestOrderHint = 0;
             for (int i = 0; i < 8; i++) {
                 uint32_t hint = shiftedOrderHints[i];
                 if (!usedFrame[i] && (hint < curFrameHint) && (ref < 0 || hint >= subLatestOrderHint)) {
                     ref                = i;
                     subLatestOrderHint = 0;
                 }
             }
             ref = subref;
             if (ref >= 0) {
                 ref_frame_idx[refFrame - 1] = ref;
                 usedFrame[ref]              = 1;
             }
         }
     }
     ref = -1;
     for (int i = 0; i < 8; i++) {
         uint32_t hint = shiftedOrderHints[i];
         if (ref < 0 || hint < earliestOrderHint) {
             ref = i;
             earliestOrderHint = hint;
         }
     }
     for (int i = 0; i < 7; i++) {
         if (ref_frame_idx[i] < 0) {
             ref_frame_idx[i] = ref;
         }
     }
     for (int i = 0; i < 7; i++) {
         fh->ref_frame_idx[i] = ref_frame_idx[i];
     }
 
     return 0;
 }
 
 static inline int _obp_read_delta_q(_OBPBitReader *br, int32_t *out, OBPError *err)
 {
     int delta_coded;
     _obp_br(delta_coded, br, 1);
     if (delta_coded) {
         int32_t val;
         int ret = _obp_su(br, 7, &val, err);
         if (ret < 0)
             return ret;
         *out = val;
     } else {
         *out = 0;
     }
     return 0;
 }
 
 static inline uint8_t _obp_get_qindex(int ignoreDeltaQ, int segmentId, int16_t CurrentQIndex, OBPFrameHeader *fh, int FeatureEnabled[8][8], int16_t FeatureData[8][8])
 {
     if (fh->segmentation_params.segmentation_enabled && FeatureEnabled[segmentId][0]) {
         int16_t data   = FeatureData[segmentId][0];
         int32_t qindex = data + ((int32_t) fh->quantization_params.base_q_idx);
         if (ignoreDeltaQ == 0 && fh->delta_q_params.delta_q_present == 1) {
             qindex = CurrentQIndex + data;
         }
         return _OBP_MAX(0, _OBP_MIN(255, qindex));
     }
 
     if (ignoreDeltaQ == 0 && fh->delta_q_params.delta_q_present == 1) {
         return CurrentQIndex;
     }
 
     return fh->quantization_params.base_q_idx;
 }
 
 static inline int _obp_read_global_param(_OBPBitReader *br, OBPFrameHeader *fh, uint8_t type, int ref, int idx, OBPError *err)
 {
     uint8_t absBits  = 12;
     uint8_t precBits = 15;
     if (idx < 2) {
         if (type == 1) {
             absBits  = 9 - !fh->allow_high_precision_mv;
             precBits = 3 - !fh->allow_high_precision_mv;
         } else {
             absBits  = 12;
             precBits = 6;
         }
     }
     int32_t precDiff = 16 - precBits;
     int32_t round    = ((idx % 3) == 2) ? (1 << 16) : 0;
     int32_t sub      = ((idx % 3) == 2) ? (1 << precBits) : 0;
     int32_t mx       = (1 << absBits);
     int32_t r        = (fh->global_motion_params.prev_gm_params[ref][idx] >> precDiff) - sub;
     int16_t val;
     int ret = _obp_decode_signed_subexp_with_ref(br, -mx, mx + 1, r, &val, err);
     if (ret < 0) {
         return -1;
     }
     if (val < 0) { /* signed shifts are bad. */
         val                                          = -val;
         fh->global_motion_params.gm_params[ref][idx] = (-(val << precDiff) + round);
     } else {
         fh->global_motion_params.gm_params[ref][idx] = (val << precDiff) + round;
     }
 
     return 0;
 }
 
 
 /*****************************
  * API functions start here. *
  *****************************/
 
 int obp_get_next_obu(uint8_t *buf, size_t buf_size, OBPOBUType *obu_type, ptrdiff_t *offset,
                      size_t *size, int *temporal_id, int *spatial_id, OBPError *err)
 {
     ptrdiff_t pos = 0;
     int obu_extension_flag;
     int obu_has_size_field;
 
     if (buf_size < 1) {
         snprintf(err->error, err->size, "Buffer is too small to contain an OBU.");
         return -1;
     }
 
     *obu_type          = (buf[pos] & 0x78) >> 3;
     obu_extension_flag = (buf[pos] & 0x04) >> 2;
     obu_has_size_field = (buf[pos] & 0x02) >> 1;
     pos++;
 
     if (!_obp_is_valid_obu(*obu_type)) {
         snprintf(err->error, err->size, "OBU header contains invalid OBU type: %d", *obu_type);
         return -1;
     }
 
     if (obu_extension_flag) {
         if (buf_size < 1) {
             snprintf(err->error, err->size, "Buffer is too small to contain an OBU extension header.");
             return -1;
         }
         *temporal_id = (buf[pos] & 0xE0) >> 5;
         *spatial_id  = (buf[pos] & 0x18) >> 3;
         pos++;
     } else {
         *temporal_id = 0;
         *spatial_id  = 0;
     }
 
     if (obu_has_size_field) {
         char err_buf[1024];
         uint64_t value;
         ptrdiff_t consumed;
         OBPError error = { &err_buf[0], 1024 };
 
         int ret      = _obp_leb128(buf + pos, buf_size - (size_t) pos, &value, &consumed, &error);
         if (ret < 0) {
             snprintf(err->error, err->size, "Failed to read OBU size: %s", &error.error[0]);
             return -1;
         }
 
         assert(value < UINT32_MAX);
 
         *offset = (ptrdiff_t) pos + consumed;
         *size   = (size_t) value;
     } else {
         *offset = (ptrdiff_t) pos;
         *size   = buf_size - (size_t) pos;
     }
 
     if (*size > buf_size - (size_t) *offset) {
         snprintf(err->error, err->size, "Invalid OBU size: larger than remaining buffer.");
         return -1;
     }
 
     return 0;
 }
 
 int obp_parse_sequence_header(uint8_t *buf, size_t buf_size, OBPSequenceHeader *seq_header, OBPError *err)
 {
     _OBPBitReader b   = _obp_new_br(buf, buf_size);
     _OBPBitReader *br = &b;
 
     _obp_br(seq_header->seq_profile, br, 3);
     _obp_br(seq_header->still_picture, br, 1);
     _obp_br(seq_header->reduced_still_picture_header, br, 1);
     if (seq_header->reduced_still_picture_header) {
         seq_header->timing_info_present_flag                     = 0;
         seq_header->decoder_model_info_present_flag              = 0;
         seq_header->initial_display_delay_present_flag           = 0;
         seq_header->operating_points_cnt_minus_1                 = 0;
         seq_header->operating_point_idc[0]                       = 0;
         _obp_br(seq_header->seq_level_idx[0], br, 5);
         seq_header->seq_tier[0]                                  = 0;
         seq_header->decoder_model_present_for_this_op[0]         = 0;
         seq_header->initial_display_delay_present_for_this_op[0] = 0;
     } else {
         _obp_br(seq_header->timing_info_present_flag, br, 1);
         if (seq_header->timing_info_present_flag) {
             /* timing_info() */
             _obp_br(seq_header->timing_info.num_units_in_display_tick, br, 32);
             _obp_br(seq_header->timing_info.time_scale, br, 32);
             _obp_br(seq_header->timing_info.equal_picture_interval, br, 1);
             if (seq_header->timing_info.equal_picture_interval) {
                 int ret = _obp_uvlc(br, &seq_header->timing_info.num_ticks_per_picture_minus_1, err);
                 if (ret < 0)
                     return -1;
             }
             _obp_br(seq_header->decoder_model_info_present_flag, br, 1);
             if (seq_header->decoder_model_info_present_flag) {
                 /* decoder_model_info() */
                 _obp_br(seq_header->decoder_model_info.buffer_delay_length_minus_1, br, 5);
                 _obp_br(seq_header->decoder_model_info.num_units_in_decoding_tick, br, 32);
                 _obp_br(seq_header->decoder_model_info.buffer_removal_time_length_minus_1, br, 5);
                 _obp_br(seq_header->decoder_model_info.frame_presentation_time_length_minus_1, br, 5);
             }
         } else {
             seq_header->decoder_model_info_present_flag = 0;
         }
         _obp_br(seq_header->initial_display_delay_present_flag, br, 1);
         _obp_br(seq_header->operating_points_cnt_minus_1, br, 5);
         for (uint8_t i = 0; i <= seq_header->operating_points_cnt_minus_1; i++) {
             _obp_br(seq_header->operating_point_idc[i], br, 12);
             _obp_br(seq_header->seq_level_idx[i], br, 5);
             if (seq_header->seq_level_idx[i] > 7) {
                 _obp_br(seq_header->seq_tier[i], br, 1);
             } else {
                 seq_header->seq_tier[i] = 0;
             }
             if (seq_header->decoder_model_info_present_flag) {
                 _obp_br(seq_header->decoder_model_present_for_this_op[i], br, 1);
                 if (seq_header->decoder_model_present_for_this_op[i]) {
                     /* operating_parameters_info() */
                     uint8_t n = seq_header->decoder_model_info.buffer_delay_length_minus_1 + 1;
                     _obp_br(seq_header->operating_parameters_info[i].decoder_buffer_delay, br, n);
                     _obp_br(seq_header->operating_parameters_info[i].encoder_buffer_delay, br, n);
                     _obp_br(seq_header->operating_parameters_info[i].low_delay_mode_flag, br, 1);
                 }
             } else {
                 seq_header->decoder_model_present_for_this_op[i] = 0;
             }
             if (seq_header->initial_display_delay_present_flag) {
                 _obp_br(seq_header->initial_display_delay_present_for_this_op[i], br, 1);
                 if (seq_header->initial_display_delay_present_for_this_op[i]) {
                     _obp_br(seq_header->initial_display_delay_minus_1[i], br, 4);
                 }
             }
         }
     }
     _obp_br(seq_header->frame_width_bits_minus_1, br, 4);
     _obp_br(seq_header->frame_height_bits_minus_1, br, 4);
     _obp_br(seq_header->max_frame_width_minus_1, br, seq_header->frame_width_bits_minus_1 + 1);
     _obp_br(seq_header->max_frame_height_minus_1, br, seq_header->frame_height_bits_minus_1 + 1);
     if (seq_header->reduced_still_picture_header) {
         seq_header->frame_id_numbers_present_flag = 0;
     } else {
         _obp_br(seq_header->frame_id_numbers_present_flag, br, 1);
     }
     if (seq_header->frame_id_numbers_present_flag) {
         _obp_br(seq_header->delta_frame_id_length_minus_2, br, 4);
         _obp_br(seq_header->additional_frame_id_length_minus_1, br, 3);
     }
     _obp_br(seq_header->use_128x128_superblock, br, 1);
     _obp_br(seq_header->enable_filter_intra, br, 1);
     _obp_br(seq_header->enable_intra_edge_filter, br, 1);
     if (seq_header->reduced_still_picture_header) {
         seq_header->enable_interintra_compound     = 0;
         seq_header->enable_masked_compound         = 0;
         seq_header->enable_warped_motion           = 0;
         seq_header->enable_dual_filter             = 0;
         seq_header->enable_order_hint              = 0;
         seq_header->enable_jnt_comp                = 0;
         seq_header->enable_ref_frame_mvs           = 0;
         seq_header->seq_force_screen_content_tools = 2; /* SELECT_SCREEN_CONTENT_TOOLS */
         seq_header->seq_force_integer_mv           = 2; /* SELECT_INTEGER_MV */
         seq_header->OrderHintBits                  = 0;
     } else {
         _obp_br(seq_header->enable_interintra_compound, br, 1);
         _obp_br(seq_header->enable_masked_compound, br, 1);
         _obp_br(seq_header->enable_warped_motion, br, 1);
         _obp_br(seq_header->enable_dual_filter, br, 1);
         _obp_br(seq_header->enable_order_hint, br, 1);
         if (seq_header->enable_order_hint) {
             _obp_br(seq_header->enable_jnt_comp, br, 1);
             _obp_br(seq_header->enable_ref_frame_mvs, br, 1);
         } else {
             seq_header->enable_jnt_comp = 0;
             seq_header->enable_ref_frame_mvs = 0;
         }
         _obp_br(seq_header->seq_choose_screen_content_tools, br, 1);
         if (seq_header->seq_choose_screen_content_tools) {
             seq_header->seq_force_screen_content_tools = 2; /* SELECT_SCREEN_CONTENT_TOOLS */
         } else {
             _obp_br(seq_header->seq_force_screen_content_tools, br, 1);
         }
         if (seq_header->seq_force_screen_content_tools > 0) {
             _obp_br(seq_header->seq_choose_integer_mv, br, 1);
             if (seq_header->seq_choose_integer_mv) {
                 seq_header->seq_force_integer_mv = 2; /* SELECT_INTEGER_MV */
             } else {
                 _obp_br(seq_header->seq_force_integer_mv, br, 1);
             }
         } else {
             seq_header->seq_force_integer_mv = 2; /* SELECT_INTEGER_MV */
         }
         if (seq_header->enable_order_hint) {
             _obp_br(seq_header->order_hint_bits_minus_1, br, 3);
             seq_header->OrderHintBits = seq_header->order_hint_bits_minus_1 + 1;
         } else {
             seq_header->OrderHintBits = 0;
         }
     }
     _obp_br(seq_header->enable_superres, br, 1);
     _obp_br(seq_header->enable_cdef, br, 1);
     _obp_br(seq_header->enable_restoration, br, 1);
     /* color_config() */
     _obp_br(seq_header->color_config.high_bitdepth, br, 1);
     if (seq_header->seq_profile == 2 && seq_header->color_config.high_bitdepth) {
         _obp_br(seq_header->color_config.twelve_bit, br, 1);
         seq_header->color_config.BitDepth = seq_header->color_config.twelve_bit ? 12 : 10;
     } else {
         seq_header->color_config.BitDepth = seq_header->color_config.high_bitdepth ? 10 : 8;
     }
     if (seq_header->seq_profile == 1) {
         seq_header->color_config.mono_chrome = 0;
     } else {
         _obp_br(seq_header->color_config.mono_chrome, br, 1);
     }
     seq_header->color_config.NumPlanes = seq_header->color_config.mono_chrome ? 1 : 3;
     _obp_br(seq_header->color_config.color_description_present_flag, br, 1);
     if (seq_header->color_config.color_description_present_flag) {
         _obp_br(seq_header->color_config.color_primaries, br, 8);
         _obp_br(seq_header->color_config.transfer_characteristics, br, 8);
         _obp_br(seq_header->color_config.matrix_coefficients, br, 8);
     } else {
         seq_header->color_config.color_primaries          = OBP_CP_UNSPECIFIED;
         seq_header->color_config.transfer_characteristics = OBP_TC_UNSPECIFIED;
         seq_header->color_config.matrix_coefficients      = OBP_MC_UNSPECIFIED;
     }
     if (seq_header->color_config.mono_chrome) {
         _obp_br(seq_header->color_config.color_range, br, 1);
         seq_header->color_config.subsampling_x          = 1;
         seq_header->color_config.subsampling_y          = 1;
         seq_header->color_config.chroma_sample_position = OBP_CSP_UNKNOWN;
         seq_header->color_config.separate_uv_delta_q    = 0;
         goto color_done;
     } else if (seq_header->color_config.color_primaries == OBP_CP_BT_709 &&
                seq_header->color_config.transfer_characteristics == OBP_TC_SRGB &&
                seq_header->color_config.matrix_coefficients == OBP_MC_IDENTITY) {
         seq_header->color_config.color_range = 1;
         seq_header->color_config.subsampling_x = 0;
         seq_header->color_config.subsampling_y = 0;
     } else {
         _obp_br(seq_header->color_config.color_range, br, 1);
         if (seq_header->seq_profile == 0) {
             seq_header->color_config.subsampling_x = 1;
             seq_header->color_config.subsampling_y = 1;
         } else if (seq_header->seq_profile == 1) {
             seq_header->color_config.subsampling_x = 0;
             seq_header->color_config.subsampling_y = 0;
         } else {
             if (seq_header->color_config.BitDepth == 12) {
                 _obp_br(seq_header->color_config.subsampling_x, br, 1);
                 if (seq_header->color_config.subsampling_x) {
                     _obp_br(seq_header->color_config.subsampling_y, br, 1);
                 } else {
                     seq_header->color_config.subsampling_y = 0;
                 }
             } else {
                 seq_header->color_config.subsampling_x = 1;
                 seq_header->color_config.subsampling_y = 0;
             }
         }
         if (seq_header->color_config.subsampling_x && seq_header->color_config.subsampling_y) {
             _obp_br(seq_header->color_config.chroma_sample_position, br, 2);
         }
     }
     _obp_br(seq_header->color_config.separate_uv_delta_q, br, 1);
 
 color_done:
     _obp_br(seq_header->film_grain_params_present, br, 1);
 
     return 0;
 }
 
 int obp_parse_tile_list(uint8_t *buf, size_t buf_size, OBPTileList *tile_list, OBPError *err)
 {
     size_t pos = 0;
 
     if (buf_size < 4) {
         snprintf(err->error, err->size, "Tile list OBU must be at least 4 bytes.");
         return -1;
     }
 
     tile_list->output_frame_width_in_tiles_minus_1  = buf[0];
     tile_list->output_frame_height_in_tiles_minus_1 = buf[1];
     tile_list->tile_count_minus_1                   = (((uint16_t) buf[2]) << 8) | buf[3];
     pos += 4;
 
     for (uint16_t i = 0; i < tile_list->tile_count_minus_1; i++) {
         if (pos + 5 > buf_size) {
             snprintf(err->error, err->size, "Tile list OBU malformed: Not enough bytes for next tile_list_entry().");
             return -1;
         }
 
         tile_list->tile_list_entry[i].anchor_frame_idx       = buf[pos];
         tile_list->tile_list_entry[i].anchor_tile_row        = buf[pos + 1];
         tile_list->tile_list_entry[i].anchor_tile_col        = buf[pos + 2];
         tile_list->tile_list_entry[i].tile_data_size_minus_1 = (((uint16_t) buf[pos + 3]) << 8) | buf[pos + 4];
         pos += 5;
 
         size_t N = 8 * (((size_t) tile_list->tile_list_entry[i].tile_data_size_minus_1) + 1);
 
         if (pos + N > buf_size) {
             snprintf(err->error, err->size, "Tile list OBU malformed: Not enough bytes for next tile_list_entry()'s data.");
             return -1;
         }
 
         tile_list->tile_list_entry[i].coded_tile_data      = buf + pos;
         tile_list->tile_list_entry[i].coded_tile_data_size = N;
         pos += N;
     }
 
     return 0;
 }
 
 int obp_parse_tile_group(uint8_t *buf, size_t buf_size, OBPFrameHeader *frame_header, OBPTileGroup *tile_group,
                          int *SeenFrameHeader, OBPError *err)
 {
     _OBPBitReader b   = _obp_new_br(buf, buf_size);
     _OBPBitReader *br = &b;
 
     tile_group->NumTiles                        = frame_header->tile_info.TileCols * frame_header->tile_info.TileRows;
     size_t startBitPos                          = 0;
     tile_group->tile_start_and_end_present_flag = 0;
 
     if (tile_group->NumTiles > 1) {
         _obp_br(tile_group->tile_start_and_end_present_flag, br, 1);
     }
     if (tile_group->NumTiles == 1 || !tile_group->tile_start_and_end_present_flag) {
         tile_group->tg_start = 0;
         tile_group->tg_end   = tile_group->NumTiles - 1;
     } else {
         uint8_t tileBits = _obp_tile_log2(1, frame_header->tile_info.TileCols) + _obp_tile_log2(1, frame_header->tile_info.TileRows);
         _obp_br(tile_group->tg_start, br, tileBits);
         _obp_br(tile_group->tg_end, br, tileBits);
     }
     _obp_br_byte_alignment(br);
     size_t endBitPos   = _obp_br_get_pos(br);
     size_t headerBytes = (endBitPos - startBitPos) / 8;
     size_t sz          = buf_size - headerBytes;
     size_t pos         = headerBytes;
 
     for (uint16_t TileNum = tile_group->tg_start; TileNum <= tile_group->tg_end; TileNum++) {
         /* tileRow = TileNum / TileCols */
         /* tileCol = TileNum % TileCols */
         int lastTile     = (TileNum == tile_group->tg_end);
         if (lastTile) {
             tile_group->TileSize[TileNum] = sz;
         } else {
             uint16_t TileSizeBytes = frame_header->tile_info.tile_size_bytes_minus_1 + 1;
             uint64_t tile_size_minus_1;
             if (sz < TileSizeBytes) {
                 snprintf(err->error, err->size, "Not enough bytes left to read tile size for tile %"PRIu16".", TileNum);
                 return -1;
             }
             tile_size_minus_1             = _obp_le(buf + pos, TileSizeBytes);
             tile_group->TileSize[TileNum] = tile_size_minus_1 + 1;
             if (sz < tile_group->TileSize[TileNum]) {
                 snprintf(err->error, err->size, "Not enough bytes to contain TileSize for tile %"PRIu16".", TileNum);
                 return -1;
             }
             sz  -= tile_group->TileSize[TileNum] + TileSizeBytes;
             pos += tile_group->TileSize[TileNum] + TileSizeBytes;
         }
         /* MiRowStart = MiRowStarts[ tileRow ] */
         /* MiRowEnd = MiRowStarts[ tileRow + 1 ] */
         /* MiColStart = MiColStarts[ tileCol ] */
         /* MiColEnd = MiColStarts[ tileCol + 1 ] */
         /* CurrentQIndex = base_q_idx */
         /* init_symbol( tileSize ) */
         /* decode_tile( ) */
         /* exit_symbol( ) */
     }
     if (tile_group->tg_end == tile_group->NumTiles - 1) {
         /* if ( !disable_frame_end_update_cdf ) {
                frame_end_update_cdf( )
            }
          */
         /* decode_frame_wrapup() is handled in obp_parse_frame_header. */
         *SeenFrameHeader = 0;
     }
 
     return 0;
 }
 
 int obp_parse_metadata(uint8_t *buf, size_t buf_size, OBPMetadata *metadata, OBPError *err)
 {
     uint64_t val;
     ptrdiff_t consumed;
     char err_buf[1024];
     _OBPBitReader b;
     _OBPBitReader *br;
     OBPError error = { &err_buf[0], 1024 };
 
     int ret = _obp_leb128(buf, buf_size, &val, &consumed, &error);
     if (ret < 0) {
         snprintf(err->error, err->size, "Couldn't read metadata type: %s", error.error);
         return -1;
     }
     metadata->metadata_type = val;
 
     b  = _obp_new_br(buf + consumed, buf_size - consumed);
     br = &b;
 
     if (metadata->metadata_type == OBP_METADATA_TYPE_HDR_CLL) {
         _obp_br(metadata->metadata_hdr_cll.max_cll, br, 16);
         _obp_br(metadata->metadata_hdr_cll.max_fall, br, 16);
     } else if (metadata->metadata_type == OBP_METADATA_TYPE_HDR_MDCV) {
         for (int i = 0; i < 3; i++) {
             _obp_br(metadata->metadata_hdr_mdcv.primary_chromaticity_x[i], br, 16);
             _obp_br(metadata->metadata_hdr_mdcv.primary_chromaticity_y[i], br, 16);
         }
         _obp_br(metadata->metadata_hdr_mdcv.white_point_chromaticity_x, br, 16);
         _obp_br(metadata->metadata_hdr_mdcv.white_point_chromaticity_y, br, 16);
         _obp_br(metadata->metadata_hdr_mdcv.luminance_max, br, 32);
         _obp_br(metadata->metadata_hdr_mdcv.luminance_min, br, 32);
     } else if (metadata->metadata_type == OBP_METADATA_TYPE_SCALABILITY) {
         _obp_br(metadata->metadata_scalability.scalability_mode_idc, br, 8);
         if (metadata->metadata_scalability.scalability_mode_idc) {
             /* scalability_structure() */
             _obp_br(metadata->metadata_scalability.scalability_structure.spatial_layers_cnt_minus_1, br, 2);
             _obp_br(metadata->metadata_scalability.scalability_structure.spatial_layer_dimensions_present_flag, br, 1);
             _obp_br(metadata->metadata_scalability.scalability_structure.spatial_layer_description_present_flag, br, 1);
             _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_description_present_flag, br, 1);
             _obp_br(metadata->metadata_scalability.scalability_structure.scalability_structure_reserved_3bits, br, 3);
             if (metadata->metadata_scalability.scalability_structure.spatial_layer_dimensions_present_flag) {
                 for (uint8_t i = 0; i < metadata->metadata_scalability.scalability_structure.spatial_layers_cnt_minus_1; i++) {
                     _obp_br(metadata->metadata_scalability.scalability_structure.spatial_layer_max_width[i], br, 16);
                     _obp_br(metadata->metadata_scalability.scalability_structure.spatial_layer_max_height[i], br, 16);
                 }
             }
             if (metadata->metadata_scalability.scalability_structure.spatial_layer_description_present_flag) {
                 for (uint8_t i = 0; i < metadata->metadata_scalability.scalability_structure.spatial_layers_cnt_minus_1; i++) {
                     _obp_br(metadata->metadata_scalability.scalability_structure.spatial_layer_ref_id[i], br, 8);
                 }
             }
             if (metadata->metadata_scalability.scalability_structure.temporal_group_description_present_flag) {
                 _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_size, br, 8);
                 for (uint8_t i = 0; i < metadata->metadata_scalability.scalability_structure.temporal_group_size; i++) {
                     _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_temporal_id[i], br, 3);
                     _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_temporal_switching_up_point_flag[i], br, 1);
                     _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_spatial_switching_up_point_flag[i], br, 1);
                     _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_ref_cnt[i], br, 3);
                     for (uint8_t j = 0; j < metadata->metadata_scalability.scalability_structure.temporal_group_ref_cnt[i]; j++) {
                         _obp_br(metadata->metadata_scalability.scalability_structure.temporal_group_ref_pic_diff[i][j], br, 8);
                     }
                 }
             }
         }
     } else if (metadata->metadata_type == OBP_METADATA_TYPE_ITUT_T35) {
         size_t offset = 1;
         _obp_br(metadata->metadata_itut_t35.itu_t_t35_country_code, br, 8);
         if (metadata->metadata_itut_t35.itu_t_t35_country_code == 0xFF) {
             _obp_br(metadata->metadata_itut_t35.itu_t_t35_country_code_extension_byte, br, 8);
             offset++;
         }
         metadata->metadata_itut_t35.itu_t_t35_payload_bytes = buf + consumed + offset;
         int non_zero_bytes_seen = 0;
         /*
          * OBUs with byte payloads at the end have a dumb property where you need to
          * know the trailing bits *before* you parse the OBU, despite the way the spec
          * the syntax displayed and defined. SO as a result, you need to find the *second*
          * non-zero byte at the end of the OBU payload, rather than the last one, as
          * the note in the ITU T.35 part of the spec says.
          */
         for (size_t i = buf_size - consumed - offset - 1; i > 0; i--) {
             if (metadata->metadata_itut_t35.itu_t_t35_payload_bytes[i] != 0) {
                 non_zero_bytes_seen++;
                 if (non_zero_bytes_seen == 2) {
                     metadata->metadata_itut_t35.itu_t_t35_payload_bytes_size = i + 1;
                 }
             }
         }
     } else if (metadata->metadata_type == OBP_METADATA_TYPE_TIMECODE) {
         _obp_br(metadata->metadata_timecode.counting_type, br, 5);
         _obp_br(metadata->metadata_timecode.full_timestamp_flag, br, 1);
         _obp_br(metadata->metadata_timecode.discontinuity_flag, br, 1);
         _obp_br(metadata->metadata_timecode.cnt_dropped_flag, br, 1);
         _obp_br(metadata->metadata_timecode.n_frames, br, 9);
         if (metadata->metadata_timecode.full_timestamp_flag) {
             _obp_br(metadata->metadata_timecode.seconds_value, br, 6);
             _obp_br(metadata->metadata_timecode.minutes_value, br, 6);
             _obp_br(metadata->metadata_timecode.hours_value, br, 5);
         } else {
             _obp_br(metadata->metadata_timecode.seconds_flag, br, 1);
             if (metadata->metadata_timecode.seconds_flag) {
                 _obp_br(metadata->metadata_timecode.seconds_value, br, 6);
                 _obp_br(metadata->metadata_timecode.minutes_flag, br, 1);
                 if (metadata->metadata_timecode.minutes_flag) {
                     _obp_br(metadata->metadata_timecode.minutes_value, br, 6);
                     _obp_br(metadata->metadata_timecode.hours_flag, br, 1);
                     if (metadata->metadata_timecode.hours_flag) {
                         _obp_br(metadata->metadata_timecode.hours_value, br, 5);
                     }
                 }
             }
         }
         _obp_br(metadata->metadata_timecode.time_offset_length, br, 5);
         if (metadata->metadata_timecode.time_offset_length > 0) {
              _obp_br(metadata->metadata_timecode.time_offset_value, br, metadata->metadata_timecode.time_offset_length);
         }
     } else if (metadata->metadata_type >= 6 && metadata->metadata_type <= 31) {
         metadata->unregistered.buf      = buf + consumed;
         metadata->unregistered.buf_size = buf_size - consumed;
     } else {
         snprintf(err->error, err->size, "Invalid metadata type: %"PRIu32"\n", metadata->metadata_type);
         return -1;
     }
 
     return 0;
 }
 
 int obp_parse_frame(uint8_t *buf, size_t buf_size, OBPSequenceHeader *seq, OBPState *state,
                     int temporal_id, int spatial_id, OBPFrameHeader *fh, OBPTileGroup *tile_group,
                     int *SeenFrameHeader, OBPError *err)
 {
     size_t startBitPos = 0, endBitPos, headerBytes;
     int ret = obp_parse_frame_header(buf, buf_size, seq, state, temporal_id, spatial_id, fh, SeenFrameHeader, err);
     if (ret < 0) {
         return -1;
     }
     endBitPos   = state->frame_header_end_pos;
     headerBytes = (endBitPos - startBitPos) / 8;
     return obp_parse_tile_group(buf + headerBytes, buf_size - headerBytes, fh, tile_group, SeenFrameHeader, err);
 }
 
 int obp_parse_frame_header(uint8_t *buf, size_t buf_size, OBPSequenceHeader *seq, OBPState *state,
                            int temporal_id, int spatial_id, OBPFrameHeader *fh, int *SeenFrameHeader, OBPError *err)
 {
     _OBPBitReader b   = _obp_new_br(buf, buf_size);
     _OBPBitReader *br = &b;
 
     if (*SeenFrameHeader == 1) {
         if (!state->prev_filled) {
             snprintf(err->error, err->size, "SeenFrameHeader is one, but no previous header exists in state.");
             return -1;
         }
         *fh = state->prev;
         return 0;
     }
 
     *SeenFrameHeader = 1;
 
     /* uncompressed_header() */
     int idLen = 0; /* only set to 0 to shut up a compiler warning. */
     if (seq->frame_id_numbers_present_flag) {
         idLen = seq->additional_frame_id_length_minus_1 + seq->delta_frame_id_length_minus_2 + 3;
     }
     uint8_t allFrames = 255; /* (1 << 8) - 1 */
     int FrameIsIntra;
     if (seq->reduced_still_picture_header) {
         fh->show_existing_frame = 0;
         fh->frame_type          = OBP_KEY_FRAME;
         FrameIsIntra            = 1;
         fh->show_frame          = 1;
         fh->showable_frame      = 1;
     } else {
         _obp_br(fh->show_existing_frame, br, 1);
         if (fh->show_existing_frame) {
             _obp_br(fh->frame_to_show_map_idx, br, 3);
             if (seq->decoder_model_info_present_flag && !seq->timing_info.equal_picture_interval) {
                 /* temporal_point_info() */
                 uint8_t n = seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1;
                 _obp_br(fh->temporal_point_info.frame_presentation_time, br, n);
             }
             fh->refresh_frame_flags = 0;
             if (seq->frame_id_numbers_present_flag) {
                 assert(idLen <= 255);
                 _obp_br(fh->display_frame_id, br, (uint8_t) idLen);
             }
             fh->frame_type = state->RefFrameType[fh->frame_to_show_map_idx];
             if (fh->frame_type == OBP_KEY_FRAME) {
                 fh->refresh_frame_flags = allFrames;
             }
             if (seq->film_grain_params_present) {
                 /* load_grain_params() */
                 fh->film_grain_params = state->RefGrainParams[fh->frame_to_show_map_idx];
             }
             return 0;
         }
         _obp_br(fh->frame_type, br, 2);
         FrameIsIntra = (fh->frame_type == OBP_INTRA_ONLY_FRAME || fh->frame_type == OBP_KEY_FRAME);
         _obp_br(fh->show_frame, br, 1);
         if (fh->show_frame && seq->decoder_model_info_present_flag && !seq->timing_info.equal_picture_interval){
             /* temporal_point_info() */
             uint8_t n = seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1;
             _obp_br(fh->temporal_point_info.frame_presentation_time, br, n);
         }
         if (fh->show_frame) {
             fh->showable_frame = (fh->frame_type != OBP_KEY_FRAME);
         } else {
             _obp_br(fh->showable_frame, br, 1);
         }
         if (fh->frame_type == OBP_SWITCH_FRAME || (fh->frame_type == OBP_KEY_FRAME && fh->show_frame)) {
             fh->error_resilient_mode = 1;
         } else {
             _obp_br(fh->error_resilient_mode, br, 1);
         }
     }
     if (fh->frame_type == OBP_KEY_FRAME && fh->show_frame) {
         for (int i = 0; i < 8; i++) {
             state->RefValid[i]     = 0;
             state->RefOrderHint[i] = 0;
         }
         for (int i = 0; i < 7; i++) {
             state->OrderHint[1 + i] = 0;
         }
     }
     _obp_br(fh->disable_cdf_update, br, 1);
     if (seq->seq_force_screen_content_tools == 2) {
         _obp_br(fh->allow_screen_content_tools, br, 1);
     } else {
         fh->allow_screen_content_tools = seq->seq_force_screen_content_tools;
     }
     if (fh->allow_screen_content_tools) {
         if (seq->seq_force_integer_mv == 2) {
             _obp_br(fh->force_integer_mv, br, 1);
         } else {
             fh->force_integer_mv = seq->seq_force_integer_mv;
         }
     } else {
         fh->force_integer_mv = 0;
     }
     if (FrameIsIntra) {
          fh->force_integer_mv = 1;
     }
     if (seq->frame_id_numbers_present_flag) {
         /*PrevFrameID = current_frame_id */
         assert(idLen <= 255);
         _obp_br(fh->current_frame_id, br, idLen);
         /* mark_ref_frames(idLen) */
         uint8_t diffLen = seq->delta_frame_id_length_minus_2 + 2;
         for (int i = 0; i < 8; i++) {
             if (fh->current_frame_id > (((uint32_t)1) << diffLen)) {
                 if (state->RefFrameId[i] > fh->current_frame_id || state->RefFrameId[i] < (fh->current_frame_id - (1 << diffLen))) {
                     state->RefValid[i] = 0;
                 }
             } else {
                 if (state->RefFrameId[i] > fh->current_frame_id && state->RefFrameId[i] < ((1 << idLen) + fh->current_frame_id + (1 << diffLen))) {
                     state->RefValid[i] = 0;
                 }
             }
         }
     } else {
         fh->current_frame_id = 0;
     }
     if (fh->frame_type == OBP_SWITCH_FRAME) {
         fh->frame_size_override_flag = 1;
     } else if (seq->reduced_still_picture_header) {
         fh->frame_size_override_flag = 0;
     } else {
         _obp_br(fh->frame_size_override_flag, br, 1);
     }
     if (seq->OrderHintBits) { /* Added by me. */
         _obp_br(fh->order_hint, br, seq->OrderHintBits);
     } else {
         fh->order_hint = 0;
     }
     uint8_t OrderHint = fh->order_hint;
     if (FrameIsIntra || fh->error_resilient_mode) {
         fh->primary_ref_frame = 7;
     } else {
         _obp_br(fh->primary_ref_frame, br, 3);
     }
     if (seq->decoder_model_info_present_flag) {
         _obp_br(fh->buffer_removal_time_present_flag, br, 1);
         if (fh->buffer_removal_time_present_flag) {
             for (uint8_t opNum = 0; opNum <= seq->operating_points_cnt_minus_1; opNum++) {
                 if (seq->decoder_model_present_for_this_op[opNum]) {
                     uint8_t opPtIdc = seq->operating_point_idc[opNum];
                     int inTemporalLayer = (opPtIdc >> temporal_id) & 1;
                     int inSpatialLayer = (opPtIdc >> (spatial_id + 8)) & 1;
                     if (opPtIdc == 0 || (inTemporalLayer && inSpatialLayer)) {
                         uint8_t n = seq->decoder_model_info.buffer_removal_time_length_minus_1 + 1;
                         _obp_br(fh->buffer_removal_time[opNum], br, n);
                     }
                 }
             }
         }
     }
     fh->allow_high_precision_mv = 0;
     fh->use_ref_frame_mvs = 0;
     fh->allow_intrabc = 0;
     if (fh->frame_type == OBP_SWITCH_FRAME || (fh->frame_type == OBP_KEY_FRAME && fh->show_frame)) {
         fh->refresh_frame_flags = allFrames;
     } else {
         _obp_br(fh->refresh_frame_flags, br, 8);
     }
     if (!FrameIsIntra || fh->refresh_frame_flags != allFrames) {
         if (fh->error_resilient_mode && seq->enable_order_hint) {
             for (int i = 0; i < 8; i++) {
                 _obp_br(fh->ref_order_hint[i], br, seq->OrderHintBits);
                 if (fh->ref_order_hint[i] != state->RefOrderHint[i]) {
                     state->RefValid[i] = 0;
                 }
             }
         }
     }
     uint32_t FrameWidth = 0, FrameHeight = 0;
     uint32_t UpscaledWidth;
     uint32_t MiCols, MiRows;
     if (FrameIsIntra) {
         /* frame_size() */
         if (fh->frame_size_override_flag) {
             uint8_t n = seq->frame_width_bits_minus_1 + 1;
             _obp_br(fh->frame_width_minus_1, br, n);
             n = seq->frame_height_bits_minus_1 + 1;
             _obp_br(fh->frame_height_minus_1, br, n);
             FrameWidth  = fh->frame_width_minus_1 + 1;
             FrameHeight = fh->frame_height_minus_1 + 1;
         } else {
             FrameWidth  = seq->max_frame_width_minus_1 + 1;
             FrameHeight = seq->max_frame_height_minus_1 + 1;
         }
         /* superres_params() */
         uint32_t SuperresDenom;
         if (seq->enable_superres) {
             _obp_br(fh->superres_params.use_superres, br, 1);
         } else {
             fh->superres_params.use_superres = 0;
         }
         if (fh->superres_params.use_superres) {
             _obp_br(fh->superres_params.coded_denom, br, 3);
             SuperresDenom = fh->superres_params.coded_denom + 9;
         } else {
             SuperresDenom = 8;
         }
         UpscaledWidth = FrameWidth;
         FrameWidth = (UpscaledWidth * 8 + (SuperresDenom / 2)) / SuperresDenom;
         /* compute_image_size() */
         MiCols = 2 * ((FrameWidth + 7) >> 3);
         MiRows = 2 * ((FrameHeight + 7) >> 3);
         /* render_size() */
         _obp_br(fh->render_and_frame_size_different, br, 1);
         if (fh->render_and_frame_size_different == 1) {
             _obp_br(fh->render_width_minus_1, br, 16);
             _obp_br(fh->render_height_minus_1, br, 16);
             fh->RenderWidth  = fh->render_width_minus_1 + 1;
             fh->RenderHeight = fh->render_height_minus_1 + 1;
         } else {
             fh->RenderWidth  = UpscaledWidth;
             fh->RenderHeight = FrameHeight;
         }
         if (fh->allow_screen_content_tools && UpscaledWidth == FrameWidth) {
             _obp_br(fh->allow_intrabc, br, 1);
         }
     } else {
         if (!seq->enable_order_hint) {
             fh->frame_refs_short_signaling = 0;
         } else {
             _obp_br(fh->frame_refs_short_signaling, br, 1);
             if (fh->frame_refs_short_signaling) {
                 int ret;
                 char err_buf[1024];
                 OBPError error = { &err_buf[0], 1024 };
                 _obp_br(fh->last_frame_idx, br, 3);
                 _obp_br(fh->gold_frame_idx, br, 3);
                 ret = _obp_set_frame_refs(fh, seq, state, &error);
                 if (ret < 0) {
                     snprintf(err->error, err->size, "Failed to set frame refs: %s", error.error);
                     return -1;
                 }
             }
         }
         for (int i = 0; i < 7; i++) {
             if (!fh->frame_refs_short_signaling) {
                 _obp_br(fh->ref_frame_idx[i], br, 3);
             }
             if (seq->frame_id_numbers_present_flag) {
                 uint8_t n = seq->delta_frame_id_length_minus_2 + 2;
                 _obp_br(fh->delta_frame_id_minus_1[i], br, n);
                 uint8_t DeltaFrameId    = fh->delta_frame_id_minus_1[i] + 1;
                 uint8_t expectedFrameId = ((fh->current_frame_id + (1 << idLen) - DeltaFrameId) % (1 << idLen));
                 if (state->RefFrameId[fh->ref_frame_idx[i]] != expectedFrameId) {
                     snprintf(err->error, err->size, "state->RefFrameId[fh->ref_frame_idx[i]] != expectedFrameId (%"PRIu8" vs %"PRIu8")",
                              state->RefFrameId[fh->ref_frame_idx[i]], expectedFrameId);
                     return -1;
                 }
             }
         }
         if (fh->frame_size_override_flag && !fh->error_resilient_mode) {
             for (int i = 0; i < 7; i++) {
                 _obp_br(fh->found_ref, br, 1);
                 if (fh->found_ref == 1) {
                     UpscaledWidth    = state->RefUpscaledWidth[fh->ref_frame_idx[i]];
                     FrameWidth       = UpscaledWidth;
                     FrameHeight      = state->RefFrameHeight[fh->ref_frame_idx[i]];
                     fh->RenderWidth  = state->RefRenderWidth[fh->ref_frame_idx[i]];
                     fh->RenderHeight = state->RefRenderHeight[fh->ref_frame_idx[i]];
                     break;
                 }
             }
             if (fh->found_ref == 0) {
                 /* frame_size() */
                 if (fh->frame_size_override_flag) {
                     uint8_t n = seq->frame_width_bits_minus_1 + 1;
                     _obp_br(fh->frame_width_minus_1, br, n);
                     n = seq->frame_height_bits_minus_1 + 1;
                     _obp_br(fh->frame_height_minus_1, br, n);
                     FrameWidth  = fh->frame_width_minus_1 + 1;
                     FrameHeight = fh->frame_height_minus_1 + 1;
                 } else {
                     FrameWidth  = seq->max_frame_width_minus_1 + 1;
                     FrameHeight = seq->max_frame_height_minus_1 + 1;
                 }
                 /* superres_params() */
                 uint32_t SuperresDenom;
                 if (seq->enable_superres) {
                     _obp_br(fh->superres_params.use_superres, br, 1);
                 } else {
                     fh->superres_params.use_superres = 0;
                 }
                 if (fh->superres_params.use_superres) {
                     _obp_br(fh->superres_params.coded_denom, br, 3);
                     SuperresDenom = fh->superres_params.coded_denom + 9;
                 } else {
                     SuperresDenom = 8;
                 }
                 UpscaledWidth = FrameWidth;
                 FrameWidth = (UpscaledWidth * 8 + (SuperresDenom / 2)) / SuperresDenom;
                 /* compute_image_size() */
                 MiCols = 2 * ((FrameWidth + 7) >> 3);
                 MiRows = 2 * ((FrameHeight + 7) >> 3);
                 /* render_size() */
                 _obp_br(fh->render_and_frame_size_different, br, 1);
                 if (fh->render_and_frame_size_different == 1) {
                     _obp_br(fh->render_width_minus_1, br, 16);
                     _obp_br(fh->render_height_minus_1, br, 16);
                     fh->RenderWidth  = fh->render_width_minus_1 + 1;
                     fh->RenderHeight = fh->render_height_minus_1 + 1;
                 } else {
                     fh->RenderWidth  = UpscaledWidth;
                     fh->RenderHeight = FrameHeight;
                 }
             } else {
                 /* superres_params() */
                 uint32_t SuperresDenom;
                 if (seq->enable_superres) {
                     _obp_br(fh->superres_params.use_superres, br, 1);
                 } else {
                     fh->superres_params.use_superres = 0;
                 }
                 if (fh->superres_params.use_superres) {
                     _obp_br(fh->superres_params.coded_denom, br, 3);
                     SuperresDenom = fh->superres_params.coded_denom + 9;
                 } else {
                     SuperresDenom = 8;
                 }
                 UpscaledWidth = FrameWidth;
                 FrameWidth = (UpscaledWidth * 8 + (SuperresDenom / 2)) / SuperresDenom;
                 /* compute_image_size() */
                 MiCols = 2 * ((FrameWidth + 7) >> 3);
                 MiRows = 2 * ((FrameHeight + 7) >> 3);
             }
         } else {
             /* frame_size() */
             if (fh->frame_size_override_flag) {
                 uint8_t n = seq->frame_width_bits_minus_1 + 1;
                 _obp_br(fh->frame_width_minus_1, br, n);
                 n = seq->frame_height_bits_minus_1 + 1;
                 _obp_br(fh->frame_height_minus_1, br, n);
                 FrameWidth  = fh->frame_width_minus_1 + 1;
                 FrameHeight = fh->frame_height_minus_1 + 1;
             } else {
                 FrameWidth  = seq->max_frame_width_minus_1 + 1;
                 FrameHeight = seq->max_frame_height_minus_1 + 1;
             }
             /* superres_params() */
             uint32_t SuperresDenom;
             if (seq->enable_superres) {
                 _obp_br(fh->superres_params.use_superres, br, 1);
             } else {
                 fh->superres_params.use_superres = 0;
             }
             if (fh->superres_params.use_superres) {
                 _obp_br(fh->superres_params.coded_denom, br, 3);
                 SuperresDenom = fh->superres_params.coded_denom + 9;
             } else {
                 SuperresDenom = 8;
             }
             UpscaledWidth = FrameWidth;
             FrameWidth = (UpscaledWidth * 8 + (SuperresDenom / 2)) / SuperresDenom;
             /* compute_image_size() */
             MiCols = 2 * ((FrameWidth + 7) >> 3);
             MiRows = 2 * ((FrameHeight + 7) >> 3);
             /* render_size() */
             _obp_br(fh->render_and_frame_size_different, br, 1);
             if (fh->render_and_frame_size_different == 1) {
                 _obp_br(fh->render_width_minus_1, br, 16);
                 _obp_br(fh->render_height_minus_1, br, 16);
                 fh->RenderWidth  = fh->render_width_minus_1 + 1;
                 fh->RenderHeight = fh->render_height_minus_1 + 1;
             } else {
                 fh->RenderWidth  = UpscaledWidth;
                 fh->RenderHeight = FrameHeight;
             }
         }
         if (fh->force_integer_mv) {
             fh->allow_high_precision_mv = 0;
         } else {
             _obp_br(fh->allow_high_precision_mv, br, 1);
         }
         /* read_interpolation_filer() */
         _obp_br(fh->interpolation_filter.is_filter_switchable, br, 1);
         if (fh->interpolation_filter.is_filter_switchable) {
             fh->interpolation_filter.interpolation_filter = 4;
         } else {
             _obp_br(fh->interpolation_filter.interpolation_filter, br, 2);
         }
         _obp_br(fh->is_motion_mode_switchable, br, 1);
         if (fh->error_resilient_mode || !seq->enable_ref_frame_mvs) {
             fh->use_ref_frame_mvs = 0;
         } else {
             _obp_br(fh->use_ref_frame_mvs, br, 1);
         }
         for (int i = 0; i < 7; i++) {
             int refFrame = 1 + i;
             uint8_t hint = state->RefOrderHint[fh->ref_frame_idx[i]];
             state->OrderHint[refFrame] = hint;
             if (!seq->enable_order_hint) {
                 state->RefFrameSignBias[refFrame] = 0;
             } else {
                 state->RefFrameSignBias[refFrame] = _obp_get_relative_dist((int32_t) hint, (int32_t) OrderHint, seq);
             }
         }
     }
     if (seq->reduced_still_picture_header || fh->disable_cdf_update) {
         fh->disable_frame_end_update_cdf = 1;
     } else {
         _obp_br(fh->disable_frame_end_update_cdf, br, 1);
     }
     int FeatureEnabled[8][8];
     int16_t FeatureData[8][8];
     if (fh->primary_ref_frame == 7) {
         /* init_non_coeff_cdfs() not relevant to OBU parsing. */
         /* setup_past_independence() */
         for (int i = 1; i < 7; i++) {
             fh->global_motion_params.gm_type[i] = 0;
             for (int j = 0; j < 6; j++) {
                 fh->global_motion_params.gm_params[i][j] = (i % 3 == 2) ? (((uint32_t)1) << 16) : 0;
             }
         }
         fh->loop_filter_params.loop_filter_delta_enabled = 1;
         fh->loop_filter_params.loop_filter_ref_deltas[0] = 1;
         fh->loop_filter_params.loop_filter_ref_deltas[1] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[2] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[3] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[4] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[5] = -1;
         fh->loop_filter_params.loop_filter_ref_deltas[6] = -1;
         fh->loop_filter_params.loop_filter_ref_deltas[7] = -1;
         for (int i = 0; i < 2; i++) {
             fh->loop_filter_params.loop_filter_mode_deltas[i] = 0;
         }
     } else {
         /* load_cdfs() not relevant to OBU parsing. */
         /* load_previous */
         int prevFrame = fh->ref_frame_idx[fh->primary_ref_frame];
         for (int i = 0; i > 8; i++) {
             for (int j = 0; j < 6; j++) {
                 fh->global_motion_params.prev_gm_params[i][j] = state->SavedGmParams[prevFrame][i][j];
             }
         }
         /* load_loop_filter_params() */
         for (int i = 0; i < 8; i++) {
             fh->loop_filter_params.loop_filter_ref_deltas[i]  = state->SavedLoopFilterRefDeltas[prevFrame][i];
             fh->loop_filter_params.loop_filter_mode_deltas[i] = state->SavedLoopFilterModeDeltas[prevFrame][i];
         }
         /* load_segmentation_params() */
         for (int i = 0; i < 8; i++) {
             for (int j = 0; j < 8; j++) {
                 FeatureEnabled[i][j] = state->SavedFeatureEnabled[prevFrame][i][j];
                 FeatureData[i][j]    = state->SavedFeatureData[prevFrame][i][j];
             }
         }
     }
     /* Not relevant to OBU parsing:
            if (fh->use_ref_frame_mvs) {
                motion_field_estimation()
            }
      */
     /* tile_info() */
     uint32_t sbCols          = seq->use_128x128_superblock ? ((MiCols + 31) >> 5) : ((MiCols + 15) >> 4);
     uint32_t sbRows          = seq->use_128x128_superblock ? ((MiRows + 31) >> 5) : ((MiRows + 15) >> 4);
     uint32_t sbShift         = seq->use_128x128_superblock ? 5 : 4;
     uint32_t sbSize          = sbShift + 2;
     uint32_t maxTileWidthSb  = 4096 >> sbSize;
     uint32_t maxTileAreaSb   = (4096 * 2304) >> (2 * sbSize);
     uint32_t minLog2TileCols = _obp_tile_log2(maxTileWidthSb, sbCols);
     uint32_t maxLog2TileCols = _obp_tile_log2(1, _OBP_MIN(sbCols, 64));
     uint32_t maxLog2TileRows = _obp_tile_log2(1, _OBP_MIN(sbRows, 64));
     uint32_t minLog2Tiles    = _OBP_MAX(minLog2TileCols, _obp_tile_log2(maxTileAreaSb, sbRows * sbCols));
     uint32_t minLog2TileRows, TileColsLog2, TileRowsLog2;
     _obp_br(fh->tile_info.uniform_tile_spacing_flag, br, 1);
     if (fh->tile_info.uniform_tile_spacing_flag) {
         TileColsLog2 = minLog2TileCols;
         while (TileColsLog2 < maxLog2TileCols) {
             int increment_tile_cols_log2;
             _obp_br(increment_tile_cols_log2, br, 1);
             if (increment_tile_cols_log2 == 1) {
                 TileColsLog2++;
             } else {
                 break;
             }
         }
         uint32_t tileWidthSb = (sbCols + (1 << TileColsLog2) - 1) >> TileColsLog2;
         int i = 0;
         for (uint32_t startSb = 0; startSb < sbCols; startSb += tileWidthSb) {
             /* MiColStarts[i] = startSb << sbShift; */
             i += 1;
         }
         /*MiColStarts[i]           = MiCols; */
         fh->tile_info.TileCols   = i;
 
         minLog2TileRows = _OBP_MAX((int64_t)minLog2Tiles - (int64_t)TileColsLog2, 0);
         TileRowsLog2 = minLog2TileRows;
         while (TileRowsLog2 < maxLog2TileRows) {
             int increment_tile_rows_log2;
             _obp_br(increment_tile_rows_log2, br, 1);
             if (increment_tile_rows_log2 == 1) {
                 TileRowsLog2++;
             } else {
                 break;
             }
         }
         uint32_t tileHeightSb = (sbRows + (1 << TileRowsLog2) - 1) >> TileRowsLog2;
         i = 0;
         for (uint32_t startSb = 0; startSb < sbRows; startSb += tileHeightSb) {
             /*MiRowStarts[i] = startSb << sbShift;*/
             i += 1;
         }
         /*MiRowStarts[i]           = MiRows;*/
         fh->tile_info.TileRows   = i;
     } else {
         uint32_t widestTileSb = 0;
         uint32_t startSb      = 0;
         uint32_t i, maxTileHeightSb;
         for (i = 0; startSb < sbCols; i++) {
             int ret;
             char err_buf[1024];
             OBPError error = { &err_buf[0], 1024 };
             uint32_t maxWidth, sizeSb;
             uint32_t width_in_sbs_minus_1;
             /* MiColStarts[i] = startSb << sbShift; */
             maxWidth       = _OBP_MIN(sbCols - startSb, maxTileWidthSb);
             ret            = _obp_ns(br, maxWidth, &width_in_sbs_minus_1, &error);
             if (ret < 0) {
                 snprintf(err->error, err->size, "Couldn't read width_in_sbs_minus_1: %s", error.error);
                 return -1;
             }
             sizeSb        = width_in_sbs_minus_1 + 1;
             widestTileSb  = _OBP_MAX(sizeSb, widestTileSb);
             startSb      += sizeSb;
         }
         /*MiColStarts[i]         = MiCols;*/
         fh->tile_info.TileCols = i;
         TileColsLog2           = _obp_tile_log2(1, fh->tile_info.TileCols);
 
         if (minLog2Tiles > 0) {
             maxTileAreaSb = (sbRows * sbCols) >> (minLog2Tiles + 1);
         } else {
             maxTileAreaSb = sbRows * sbCols;
         }
         maxTileHeightSb = _OBP_MAX(maxTileAreaSb / widestTileSb, 1);
 
         startSb = 0;
         for (i = 0; startSb < sbRows; i++) {
             int ret;
             char err_buf[1024];
             OBPError error = { &err_buf[0], 1024 };
             uint32_t maxHeight, sizeSb;
             uint32_t height_in_sbs_minus_1;
             /*MiRowStarts[i] = startSb << sbShift;*/
             maxHeight      = _OBP_MIN(sbRows - startSb, maxTileHeightSb);
             ret            = _obp_ns(br, maxHeight, &height_in_sbs_minus_1, &error);
             if (ret < 0) {
                 snprintf(err->error, err->size, "Couldn't read height_in_sbs_minus_1: %s", error.error);
                 return -1;
             }
             sizeSb   = height_in_sbs_minus_1 + 1;
             startSb += sizeSb;
         }
         /*MiRowStarts[i]          = MiRows*/;
         fh->tile_info.TileRows = i;
         TileRowsLog2           = _obp_tile_log2(1, fh->tile_info.TileRows);
     }
     if (TileColsLog2 > 0 || TileRowsLog2 > 0) {
         _obp_br(fh->tile_info.context_update_tile_id, br, (TileColsLog2 + TileRowsLog2));
         _obp_br(fh->tile_info.tile_size_bytes_minus_1, br, 2);
         /*TileSizeBytes = fh->tile_info.tile_size_bytes_minus_1 + 1;*/
     } else {
         fh->tile_info.context_update_tile_id = 0;
     }
     /* quantization_params() */
     _obp_br(fh->quantization_params.base_q_idx, br, 8);
     int32_t DeltaQYDc, DeltaQUDc, DeltaQUAc, DeltaQVDc, DeltaQVAc;
     int ret = _obp_read_delta_q(br, &DeltaQYDc, err);
     if (ret < 0) {
         return -1;
     }
     if (seq->color_config.NumPlanes > 1) {
         int ret;
         if (seq->color_config.separate_uv_delta_q) {
             _obp_br(fh->quantization_params.diff_uv_delta, br, 1);
         } else {
             fh->quantization_params.diff_uv_delta = 0;
         }
         ret = _obp_read_delta_q(br, &DeltaQUDc, err);
         if (ret < 0) {
             return -1;
         }
         ret = _obp_read_delta_q(br, &DeltaQUAc, err);
         if (ret < 0) {
             return -1;
         }
         if (fh->quantization_params.diff_uv_delta) {
             ret = _obp_read_delta_q(br, &DeltaQVDc, err);
             if (ret < 0) {
                 return -1;
             }
             ret = _obp_read_delta_q(br, &DeltaQVAc, err);
             if (ret < 0) {
                 return -1;
             }
         } else {
             DeltaQVDc = DeltaQUDc;
             DeltaQVAc = DeltaQUAc;
         }
     } else {
         DeltaQUDc = 0;
         DeltaQUAc = 0;
         DeltaQVDc = 0;
         DeltaQVAc = 0;
     }
     _obp_br(fh->quantization_params.using_qmatrix, br, 1);
     if (fh->quantization_params.using_qmatrix) {
         _obp_br(fh->quantization_params.qm_y, br, 4);
         _obp_br(fh->quantization_params.qm_u, br, 4);
         if (!seq->color_config.separate_uv_delta_q) {
             fh->quantization_params.qm_v = fh->quantization_params.qm_u;
         } else {
             _obp_br(fh->quantization_params.qm_v, br, 4);
         }
     }
     /* segmentation_params() */
     const uint8_t Segmentation_Feature_Bits[8] = { 8, 6, 6, 6, 6, 3, 0, 0 };
     const uint8_t Segmentation_Feature_Max[8]  = { 255, 63, 63, 63, 63, 7, 0, 0 };
     const int Segmentation_Feature_Signed[8]   = { 1, 1, 1, 1, 1, 0, 0, 0 };
     _obp_br(fh->segmentation_params.segmentation_enabled, br, 1);
     if (fh->segmentation_params.segmentation_enabled == 1) {
         if (fh->primary_ref_frame == 7) {
             fh->segmentation_params.segmentation_update_map      = 1;
             fh->segmentation_params.segmentation_temporal_update = 0;
             fh->segmentation_params.segmentation_update_data     = 1;
         } else {
             _obp_br(fh->segmentation_params.segmentation_update_map, br, 1);
             if (fh->segmentation_params.segmentation_update_map) {
                 _obp_br(fh->segmentation_params.segmentation_temporal_update, br, 1);
             }
             _obp_br(fh->segmentation_params.segmentation_update_data, br, 1);
         }
         if (fh->segmentation_params.segmentation_update_data == 1) {
             for (int i = 0; i < 8; i++) {
                 for (int j = 0; j < 8; j++) {
                     int16_t clippedValue;
                     int16_t feature_value = 0;
                     int feature_enabled;
                     _obp_br(feature_enabled, br, 1);
                     FeatureEnabled[i][j] = feature_enabled;
                     clippedValue         = 0;
                     if (feature_enabled) {
                         uint8_t bitsToRead = Segmentation_Feature_Bits[j];
                         int16_t limit      = Segmentation_Feature_Max[j];
                         if (Segmentation_Feature_Signed[j] == 1) {
                             int32_t val;
                             ret = _obp_su(br, 1 + bitsToRead, &val, err);
                             if (ret < 0) {
                                 return -1;
                             }
                             feature_value = val;
                             clippedValue  = _OBP_MAX(-limit, _OBP_MIN(limit, feature_value));
                         } else {
                             _obp_br(feature_value, br, bitsToRead);
                             clippedValue = _OBP_MAX(0, _OBP_MIN(limit, feature_value));
                         }
                     }
                     FeatureData[i][j] = clippedValue;
                 }
             }
         }
     } else {
         for (int i = 0; i < 8; i++) {
             for (int j = 0; j > 8; j++) {
                 FeatureEnabled[i][j] = 0;
                 FeatureData[i][j]    = 0;
             }
         }
     }
     /*int SegIdPreSkip    = 0;
     int LastActiveSegId = 0;
     for (int i = 0; i < 8; i++) {
         for (int j = 0; j < 8; j++) {
             if (FeatureEnabled[i][j]) {
                 LastActiveSegId = i;
                 if (j >= 5) {
                     SegIdPreSkip = 1;
                 }
             }
         }
     }*/
     /* delta_q_params() */
     fh->delta_q_params.delta_q_res     = 0;
     fh->delta_q_params.delta_q_present = 0;
     if (fh->quantization_params.base_q_idx > 0) {
         _obp_br(fh->delta_q_params.delta_q_present, br, 1);
     }
     if (fh->delta_q_params.delta_q_present) {
         _obp_br(fh->delta_q_params.delta_q_res, br, 2);
     }
     /* delta_lf_params() */
     fh->delta_lf_params.delta_lf_present = 0;
     fh->delta_lf_params.delta_lf_res     = 0;
     fh->delta_lf_params.delta_lf_multi   = 0;
     if (fh->delta_q_params.delta_q_present) {
         if (!fh->allow_intrabc) {
             _obp_br(fh->delta_lf_params.delta_lf_present, br, 1);
         }
         if (fh->delta_lf_params.delta_lf_present) {
             _obp_br(fh->delta_lf_params.delta_lf_res, br, 2);
             _obp_br(fh->delta_lf_params.delta_lf_multi, br, 1);
         }
     }
     /* skipped because not relevant:
 
        if (fh->primary_ref_frame == 7) {
            init_coeff_cdfs();
        } else {
            lnit_coeff_cdfs();
        }
      */
     int CodedLossless = 1;
     int LosslessArray[8];
     for (int segmentId = 0; segmentId < 8; segmentId++ ) {
         uint8_t qindex           = _obp_get_qindex(1, segmentId, 0, fh, FeatureEnabled, FeatureData);
         LosslessArray[segmentId] = (qindex == 0 && DeltaQYDc == 0 && DeltaQUAc == 0 && DeltaQUDc == 0 && DeltaQVAc == 0 && DeltaQVDc == 0);
         if (!LosslessArray[segmentId]) {
             CodedLossless = 0;
         }
         /* SegQMLevel not relevant to OBU parsing.*/
     }
     int AllLossless = (CodedLossless && (FrameWidth == UpscaledWidth));
     /* loop_filter_params() */
     if (CodedLossless || fh->allow_intrabc) {
         fh->loop_filter_params.loop_filter_delta_enabled = 1;
         fh->loop_filter_params.loop_filter_ref_deltas[0] = 1;
         fh->loop_filter_params.loop_filter_ref_deltas[1] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[2] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[3] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[4] = 0;
         fh->loop_filter_params.loop_filter_ref_deltas[5] = -1;
         fh->loop_filter_params.loop_filter_ref_deltas[6] = -1;
         fh->loop_filter_params.loop_filter_ref_deltas[7] = -1;
         for (int i = 0; i < 2; i++) {
             fh->loop_filter_params.loop_filter_mode_deltas[i] = 0;
         }
         /* return */
     } else {
         _obp_br(fh->loop_filter_params.loop_filter_level[0], br, 6);
         _obp_br(fh->loop_filter_params.loop_filter_level[1], br, 6);
         if (seq->color_config.NumPlanes > 1) {
             if (fh->loop_filter_params.loop_filter_level[0] || fh->loop_filter_params.loop_filter_level[1]) {
                 _obp_br(fh->loop_filter_params.loop_filter_level[2], br, 6);
                 _obp_br(fh->loop_filter_params.loop_filter_level[3], br, 6);
             }
         }
         _obp_br(fh->loop_filter_params.loop_filter_sharpness, br, 3);
         _obp_br(fh->loop_filter_params.loop_filter_delta_enabled, br, 1);
         if (fh->loop_filter_params.loop_filter_delta_enabled == 1) {
             _obp_br(fh->loop_filter_params.loop_filter_delta_update, br, 1);
             if (fh->loop_filter_params.loop_filter_delta_update == 1) {
                 for (int i = 0; i < 8; i++) {
                     int update_ref_delta;
                     _obp_br(update_ref_delta, br, 1);
                     if (update_ref_delta) {
                         int32_t val;
                         ret = _obp_su(br, 7, &val, err);
                         if (ret < 0) {
                             return -1;
                         }
                         fh->loop_filter_params.loop_filter_ref_deltas[i] = val;
                     }
                 }
                 for (int i = 0; i < 2; i++) {
                     int update_mode_delta;
                     _obp_br(update_mode_delta, br, 1);
                     if (update_mode_delta) {
                         int32_t val;
                         ret = _obp_su(br, 7, &val, err);
                         if (ret < 0) {
                             return -1;
                         }
                         fh->loop_filter_params.loop_filter_mode_deltas[i] = val;
                     }
                 }
             }
         }
     }
     /* cdef_params() */
     if (CodedLossless || fh->allow_intrabc || !seq->enable_cdef) {
         fh->cdef_params.cdef_bits               = 0;
         fh->cdef_params.cdef_y_pri_strength[0]  = 0;
         fh->cdef_params.cdef_y_sec_strength[0]  = 0;
         fh->cdef_params.cdef_uv_pri_strength[0] = 0;
         fh->cdef_params.cdef_uv_sec_strength[0] = 0;
         /* CdefDamping not relevant to OBU parsing. */
         /* return */
     } else {
         _obp_br(fh->cdef_params.cdef_damping_minus_3, br, 2);
         /* CdefDamping not relevant to OBU parsing. */
         _obp_br(fh->cdef_params.cdef_bits, br, 2);
         for (int i = 0; i < (1 << fh->cdef_params.cdef_bits); i++) {
             _obp_br(fh->cdef_params.cdef_y_pri_strength[i], br, 4);
             _obp_br(fh->cdef_params.cdef_y_sec_strength[i], br, 2);
             if (fh->cdef_params.cdef_y_sec_strength[i] == 3) {
                 fh->cdef_params.cdef_y_sec_strength[i] += 1;
             }
             if (seq->color_config.NumPlanes > 1) {
                 _obp_br(fh->cdef_params.cdef_uv_pri_strength[i], br, 4);
                 _obp_br(fh->cdef_params.cdef_uv_sec_strength[i], br, 2);
                 if (fh->cdef_params.cdef_uv_sec_strength[i] == 3) {
                     fh->cdef_params.cdef_uv_sec_strength[i] += 1;
                 }
             }
         }
     }
     if (AllLossless || fh->allow_intrabc || !seq->enable_restoration) {
         fh->lr_params.lr_type[0] = 0;
         fh->lr_params.lr_type[1] = 0;
         fh->lr_params.lr_type[2] = 0;
     } else {
         int UsesLr       = 0;
         int usesChromaLr = 0;
         for (int i = 0; i < seq->color_config.NumPlanes; i++) {
             _obp_br(fh->lr_params.lr_type[i], br, 2);
             if (fh->lr_params.lr_type[i] != 0) {
                 UsesLr = 1;
                 if (i > 0) {
                     usesChromaLr = 1;
                 }
             }
         }
         if (UsesLr) {
             if (seq->use_128x128_superblock) {
                 _obp_br(fh->lr_params.lr_unit_shift, br, 1);
                 fh->lr_params.lr_unit_shift++;
             } else {
                 _obp_br(fh->lr_params.lr_unit_shift, br, 1);
                 if (fh->lr_params.lr_unit_shift) {
                     uint8_t lr_unit_extra_shift;
                     _obp_br(lr_unit_extra_shift, br, 1);
                     fh->lr_params.lr_unit_shift += lr_unit_extra_shift;
                 }
             }
             /* LoopRestorationSize not relevant to OBU parsing. */
             if (seq->color_config.subsampling_x && seq->color_config.subsampling_y && usesChromaLr) {
                 _obp_br(fh->lr_params.lr_uv_shift, br, 1);
             } else {
                 fh->lr_params.lr_uv_shift = 0;
             }
             /* LoopRestorationSize not relevant to OBU parsing. */
         }
     }
     /* read_tx_mode */
     if (CodedLossless == 1) {
         /* TxMode not relevant to OBU parsing. */
     } else {
         _obp_br(fh->tx_mode_select, br, 1);
         if (fh->tx_mode_select) {
             /* TxMode not relevant to OBU parsing. */
         } else {
             /* TxMode not relevant to OBU parsing. */
         }
     }
     /* frame_reference_mode() */
     if (FrameIsIntra) {
         fh->reference_select = 0;
     } else {
         _obp_br(fh->reference_select, br, 1);
     }
     /* skip_mode_params() */
     int skipModeAllowed;
     if (FrameIsIntra || !fh->reference_select || !seq->enable_order_hint) {
         skipModeAllowed = 0;
     } else {
         int forwardIdx       = -1;
         int backwardIdx      = -1;
         int32_t forwardHint  = 0; /* Never declare by spec! Bug? */
         int32_t backwardHint = 0; /* Never declare by spec! Bug? */
         for (int i = 0; i < 7; i++) {
             int32_t refHint = state->RefOrderHint[fh->ref_frame_idx[i]];
             if(_obp_get_relative_dist(refHint, OrderHint, seq) < 0) {
                 if (forwardIdx < 0 || _obp_get_relative_dist(refHint, forwardHint, seq) > 0) {
                     forwardIdx  = i;
                     forwardHint = refHint;
                 }
             } else if (_obp_get_relative_dist(refHint, OrderHint, seq) > 0) {
                 if (backwardIdx < 0 || _obp_get_relative_dist(refHint, backwardHint, seq) < 0) {
                     backwardIdx  = i;
                     backwardHint = refHint;
                 }
             }
         }
         if (forwardIdx < 0) {
             skipModeAllowed = 0;
         } else if (backwardIdx >= 0) {
             skipModeAllowed = 1;
             /* SkipModeFrame not relevant to OBU parsing. */
         } else {
             int     secondForwardIdx = -1;
             int32_t secondForwardHint = 0; /* Never declare by spec! Bug? */
             for (int i = 0; i < 7; i++) {
                 int32_t refHint = state->RefOrderHint[fh->ref_frame_idx[i]];
                 if (_obp_get_relative_dist(refHint, forwardHint, seq) < 0) {
                     if (secondForwardIdx < 0 || _obp_get_relative_dist(refHint, secondForwardHint, seq) > 0) {
                         secondForwardIdx  = i;
                         secondForwardHint = refHint;
                     }
                 }
             }
             if (secondForwardIdx < 0) {
                 skipModeAllowed = 0;
             } else {
                 skipModeAllowed = 1;
                 /* SkipModeFrame not relevant to OBU parsing. */
             }
         }
     }
     if (skipModeAllowed) {
         _obp_br(fh->skip_mode_present, br, 1);
     } else {
         fh->skip_mode_present = 0;
     }
     if (FrameIsIntra || fh->error_resilient_mode || !seq->enable_warped_motion) {
         fh->allow_warped_motion = 0;
     } else {
         _obp_br(fh->allow_warped_motion, br, 1);
     }
     _obp_br(fh->reduced_tx_set, br, 1);
     /* global_motion_params() */
     for (int ref = 1; ref < 7; ref++) {
         fh->global_motion_params.gm_type[ref] = 0;
         for(int i = 0; i < 6; i++) {
             fh->global_motion_params.gm_params[ref][i] = (i % 3 == 2) ? (((uint32_t)1) << 16) : 0;
         }
     }
     if (FrameIsIntra) {
         /* return */
     } else {
         for (int ref = 1; ref <= 7; ref++) {
             uint8_t type;
             int is_global;
             _obp_br(is_global, br, 1);
             if (is_global) {
                 int is_rot_zoom;
                 _obp_br(is_rot_zoom, br, 1);
                 if (is_rot_zoom) {
                     type = 2;
                 } else {
                     int is_translation;
                     _obp_br(is_translation, br, 1);
                     type = is_translation ? 1 : 3;
                 }
             } else {
                 type = 0;
             }
             fh->global_motion_params.gm_type[ref] = type;
 
             if (type >= 2) {
                 ret = _obp_read_global_param(br, fh, type, ref, 2, err);
                 if (ret < 0) {
                     return -1;
                 }
                 ret = _obp_read_global_param(br, fh, type, ref, 3, err);
                 if (ret < 0) {
                     return -1;
                 }
                 if (type == 3) {
                     ret = _obp_read_global_param(br, fh, type, ref, 4, err);
                     if (ret < 0) {
                         return -1;
                     }
                     ret = _obp_read_global_param(br, fh, type, ref, 5, err);
                     if (ret < 0) {
                         return -1;
                     }
                 } else {
                     fh->global_motion_params.gm_params[ref][4] = -fh->global_motion_params.gm_params[ref][3];
                     fh->global_motion_params.gm_params[ref][5] = fh->global_motion_params.gm_params[ref][2];
                 }
             }
             if (type >= 1) {
                 ret = _obp_read_global_param(br, fh, type, ref, 0, err);
                 if (ret < 0) {
                     return -1;
                 }
                 ret = _obp_read_global_param(br, fh, type, ref, 1, err);
                 if (ret < 0) {
                     return -1;
                 }
             }
         }
     }
     /* film_grain_params() */
     if (!seq->film_grain_params_present || (!fh->show_frame && !fh->showable_frame)) {
         /* reset_grain_params() */
         memset(&fh->film_grain_params, 0, sizeof(fh->film_grain_params));
         /* return */
     } else {
         _obp_br(fh->film_grain_params.apply_grain, br, 1);
         if (!fh->film_grain_params.apply_grain) {
             /* reset_grain_params() */
             memset(&fh->film_grain_params, 0, sizeof(fh->film_grain_params));
             /* return */
         } else {
             _obp_br(fh->film_grain_params.grain_seed, br, 16);
             if (fh->frame_type == OBP_INTER_FRAME) {
                 _obp_br(fh->film_grain_params.update_grain, br, 1);
             } else {
                 fh->film_grain_params.update_grain = 1;
             }
             if (!fh->film_grain_params.update_grain) {
                 _obp_br(fh->film_grain_params.film_grain_params_ref_idx, br, 3);
                 uint16_t tempGrainSeed = fh->film_grain_params.grain_seed;
                 /* load_grain_params() */
                 fh->film_grain_params            = state->RefGrainParams[fh->film_grain_params.film_grain_params_ref_idx];
                 fh->film_grain_params.grain_seed = tempGrainSeed;
                 /* return */
             } else {
                 uint8_t numPosLuma, numPosChroma;
                 _obp_br(fh->film_grain_params.num_y_points, br, 4);
                 for (uint8_t i = 0; i < fh->film_grain_params.num_y_points; i++) {
                     _obp_br(fh->film_grain_params.point_y_value[i], br, 8);
                     _obp_br(fh->film_grain_params.point_y_scaling[i], br, 8);
                 }
                 if (seq->color_config.mono_chrome) {
                     fh->film_grain_params.chroma_scaling_from_luma = 0;
                 } else {
                     _obp_br(fh->film_grain_params.chroma_scaling_from_luma, br, 1);
                 }
                 if (seq->color_config.mono_chrome || fh->film_grain_params.chroma_scaling_from_luma ||
                     (seq->color_config.subsampling_x == 1 && seq->color_config.subsampling_y == 1 &&
                      fh->film_grain_params.num_y_points == 0)) {
                      fh->film_grain_params.num_cb_points = 0;
                      fh->film_grain_params.num_cr_points = 0;
                 } else {
                     _obp_br(fh->film_grain_params.num_cb_points, br, 4);
                     for (uint8_t i = 0; i < fh->film_grain_params.num_cb_points; i++) {
                         _obp_br(fh->film_grain_params.point_cb_value[i], br, 8);
                         _obp_br(fh->film_grain_params.point_cb_scaling[i], br, 8);
                     }
                     _obp_br(fh->film_grain_params.num_cr_points, br, 4);
                     for (uint8_t i = 0; i < fh->film_grain_params.num_cr_points; i++) {
                         _obp_br(fh->film_grain_params.point_cr_value[i], br, 8);
                         _obp_br(fh->film_grain_params.point_cr_scaling[i], br, 8);
                     }
                 }
                 _obp_br(fh->film_grain_params.grain_scaling_minus_8, br, 2);
                 _obp_br(fh->film_grain_params.ar_coeff_lag, br, 2);
                 numPosLuma = 2 * fh->film_grain_params.ar_coeff_lag * (fh->film_grain_params.ar_coeff_lag + 1);
                 if (fh->film_grain_params.num_y_points) {
                     numPosChroma = numPosLuma + 1;
                     for (uint8_t i = 0; i < numPosLuma; i++) {
                         _obp_br(fh->film_grain_params.ar_coeffs_y_plus_128[i], br, 8);
                     }
                 } else {
                     numPosChroma = numPosLuma;
                 }
                 if (fh->film_grain_params.chroma_scaling_from_luma || fh->film_grain_params.num_cb_points) {
                     for (uint8_t i = 0; i < numPosChroma; i++) {
                         _obp_br(fh->film_grain_params.ar_coeffs_cb_plus_128[i], br, 8);
                     }
                 }
                 if (fh->film_grain_params.chroma_scaling_from_luma || fh->film_grain_params.num_cr_points) {
                     for (uint8_t i = 0; i < numPosChroma; i++) {
                         _obp_br(fh->film_grain_params.ar_coeffs_cr_plus_128[i], br, 8);
                     }
                 }
                 _obp_br(fh->film_grain_params.ar_coeff_shift_minus_6, br, 2);
                 _obp_br(fh->film_grain_params.grain_scale_shift, br, 2);
                 if (fh->film_grain_params.num_cb_points) {
                     _obp_br(fh->film_grain_params.cb_mult, br, 8);
                     _obp_br(fh->film_grain_params.cb_luma_mult, br, 8);
                     _obp_br(fh->film_grain_params.cb_offset, br, 9);
                 }
                 if (fh->film_grain_params.num_cr_points) {
                     _obp_br(fh->film_grain_params.cr_mult, br, 8);
                     _obp_br(fh->film_grain_params.cr_luma_mult, br, 8);
                     _obp_br(fh->film_grain_params.cr_offset, br, 9);
                 }
                 _obp_br(fh->film_grain_params.overlap_flag, br, 1);
                 _obp_br(fh->film_grain_params.clip_to_restricted_range, br, 1);
             }
         }
     }
 
     /* Stash refs for future frame use. */
     /* decode_frame_wrapup() */
     for (int i = 0; i < 8; i++) {
         if ((fh->refresh_frame_flags >> i) & 1) {
             state->RefOrderHint[i]     = fh->order_hint;
             state->RefFrameType[i]     = fh->frame_type;
             state->RefUpscaledWidth[i] = UpscaledWidth;
             state->RefFrameHeight[i]   = FrameHeight;
             state->RefRenderWidth[i]   = fh->RenderWidth;
             state->RefRenderHeight[i]  = fh->RenderHeight;
             state->RefFrameId[i]       = fh->current_frame_id;
             state->RefGrainParams[i]   = fh->film_grain_params;
             /* save_grain_params() */
             for (int j = 0; j < 8; j++) {
                 for (int k = 0; k < 6; k++) {
                     state->SavedGmParams[i][j][k] = fh->global_motion_params.gm_params[j][k];
                 }
             }
             /* save_segmentation_params() */
             for (int j = 0; j < 8; j++) {
                 for (int k = 0; k < 8; k++) {
                     state->SavedFeatureEnabled[i][j][k] = FeatureEnabled[j][k];
                     state->SavedFeatureData[i][j][k]    = FeatureData[j][k];
                 }
             }
             /* save_loop_filter_params() */
             for (int j = 0; j < 8; j++) {
                 state->SavedLoopFilterRefDeltas[i][j]  = fh->loop_filter_params.loop_filter_ref_deltas[j];
                 state->SavedLoopFilterModeDeltas[i][j] = fh->loop_filter_params.loop_filter_mode_deltas[j];
             }
         }
     }
 
     /* Handle show_existing_frame semantics. */
     /* decode_frame_wrapup() */
     if (fh->show_existing_frame && fh->frame_type == OBP_KEY_FRAME) {
         fh->order_hint = state->RefOrderHint[fh->frame_to_show_map_idx];
         for (int i = 0; i < 8; i++) {
             for (int j = 0; j < 6; j++) {
                 fh->global_motion_params.gm_params[i][j] = state->SavedGmParams[fh->frame_to_show_map_idx][i][j];
             }
         }
     }
     if (fh->show_existing_frame) {
         *SeenFrameHeader = 0;
         state->prev_filled = 0;
     } else {
         state->prev = *fh;
         state->prev_filled = 1;
     }
 
     /* Stash byte position for use in OBU_FRAME parsing. */
     _obp_br_byte_alignment(br);
     state->frame_header_end_pos = _obp_br_get_pos(br);
 
     return 0;
 }
 