/**************************************************************************
 *
 * Copyright 2020 Advanced Micro Devices, Inc.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#include "pipe/p_video_codec.h"
#include "util/u_memory.h"
#include "util/u_video.h"
#include "vl/vl_video_buffer.h"

#include "entrypoint.h"
#include "vid_dec.h"
#include "vid_dec_av1.h"

static unsigned av1_f(struct vl_vlc *vlc, unsigned n)
{
   unsigned valid = vl_vlc_valid_bits(vlc);

   if (n == 0)
      return 0;

   if (valid < 32)
      vl_vlc_fillbits(vlc);

   return vl_vlc_get_uimsbf(vlc, n);
}

static unsigned av1_uvlc(struct vl_vlc *vlc)
{
   unsigned value;
   unsigned leadingZeros = 0;

   while (1) {
      bool done = av1_f(vlc, 1);
      if (done)
         break;
      leadingZeros++;
   }

   if (leadingZeros >= 32)
      return 0xffffffff;

   value = av1_f(vlc, leadingZeros);

   return value + (1 << leadingZeros) - 1;
}

static int av1_le(struct vl_vlc *vlc, const unsigned n)
{
   unsigned byte, t = 0;
   unsigned i;

   for (i = 0; i < n; ++i) {
      byte = av1_f(vlc, 8);
      t += (byte << (i * 8));
   }

   return t;
}

static unsigned av1_uleb128(struct vl_vlc *vlc)
{
   unsigned value = 0;
   unsigned leb128Bytes = 0;
   unsigned i;

   for (i = 0; i < 8; ++i) {
      leb128Bytes = av1_f(vlc, 8);
      value |= ((leb128Bytes & 0x7f) << (i * 7));
      if (!(leb128Bytes & 0x80))
         break;
   }

   return value;
}

static int av1_su(struct vl_vlc *vlc, const unsigned n)
{
   unsigned value = av1_f(vlc, n);
   unsigned signMask = 1 << (n - 1);

   if (value && signMask)
      value = value - 2 * signMask;

   return value;
}

static unsigned FloorLog2(unsigned x)
{
   unsigned s = 0;
   unsigned x1 = x;

   while (x1 != 0) {
      x1 = x1 >> 1;
      s++;
   }

   return s - 1;
}

static unsigned av1_ns(struct vl_vlc *vlc, unsigned n)
{
   unsigned w = FloorLog2(n) + 1;
   unsigned m = (1 << w) - n;
   unsigned v = av1_f(vlc, w - 1);

   if (v < m)
      return v;

   bool extra_bit = av1_f(vlc, 1);

   return (v << 1) - m + extra_bit;
}

static void av1_byte_alignment(struct vl_vlc *vlc)
{
   vl_vlc_eatbits(vlc, vl_vlc_valid_bits(vlc) % 8);
}

static void sequence_header_obu(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   bool timing_info_present_flag;
   bool initial_display_delay_present_flag;
   uint8_t seq_level_idx;
   bool initial_display_delay_present_for_this_op;
   bool high_bitdepth;
   bool twelve_bit;
   bool color_description_present_flag;
   uint8_t color_primaries;
   uint8_t transfer_characteristics;
   uint8_t matrix_coefficients;
   int i;

   seq->seq_profile = av1_f(vlc, 3);
   assert(seq->seq_profile < 3);

   av1_f(vlc, 1); /* still_picture */
   seq->reduced_still_picture_header = av1_f(vlc, 1);
   if (seq->reduced_still_picture_header) {
      timing_info_present_flag = 0;
      seq->decoder_model_info_present_flag = 0;
      initial_display_delay_present_flag = 0;
      seq->operating_points_cnt_minus_1 = 0;
      seq->operating_point_idc[0] = 0;
      seq_level_idx = av1_f(vlc, 5);
      seq->decoder_model_present_for_this_op[0] = 0;
      initial_display_delay_present_for_this_op = 0;
   } else {
      uint8_t buffer_delay_length_minus_1 = 0;

      timing_info_present_flag = av1_f(vlc, 1);
      if (timing_info_present_flag) {
         av1_f(vlc, 32); /* num_units_in_display_tick */
         av1_f(vlc, 32); /* time_scale */
         seq->timing_info.equal_picture_interval = av1_f(vlc, 1);
         if (seq->timing_info.equal_picture_interval)
            av1_uvlc(vlc); /* num_ticks_per_picture_minus_1 */

         seq->decoder_model_info_present_flag = av1_f(vlc, 1);
         if (seq->decoder_model_info_present_flag) {
             /* decoder_model_info */
            buffer_delay_length_minus_1 = av1_f(vlc, 5);
            seq->decoder_model_info.num_units_in_decoding_tick = av1_f(vlc, 32);
            seq->decoder_model_info.buffer_removal_time_length_minus_1 = av1_f(vlc, 5);
            seq->decoder_model_info.frame_presentation_time_length_minus_1 = av1_f(vlc, 5);
         }
      } else {
         seq->decoder_model_info_present_flag = 0;
      }

      initial_display_delay_present_flag = av1_f(vlc, 1);
      seq->operating_points_cnt_minus_1 = av1_f(vlc, 5);
      for (i = 0; i < seq->operating_points_cnt_minus_1 + 1; ++i) {
         seq->operating_point_idc[i] = av1_f(vlc, 12);
         seq_level_idx = av1_f(vlc, 5);
         if (seq_level_idx > 7)
            av1_f(vlc, 1); /* seq_tier */

         if (seq->decoder_model_info_present_flag) {
            seq->decoder_model_present_for_this_op[i] = av1_f(vlc, 1);
            if (seq->decoder_model_present_for_this_op[i]) {
               uint8_t n = buffer_delay_length_minus_1 + 1;
               av1_f(vlc, n); /* decoder_buffer_delay */
               av1_f(vlc, n); /* encoder_buffer_delay */
               av1_f(vlc, 1); /* low_delay_mode_flag */
            }
         } else {
            seq->decoder_model_present_for_this_op[i] = 0;
         }

         if (initial_display_delay_present_flag) {
            initial_display_delay_present_for_this_op = av1_f(vlc, 1);
            if (initial_display_delay_present_for_this_op)
               av1_f(vlc, 4); /* initial_display_delay_minus_1 */
         }
      }
   }

   seq->frame_width_bits_minus_1 = av1_f(vlc, 4);
   seq->frame_height_bits_minus_1 = av1_f(vlc, 4);
   seq->max_frame_width_minus_1 = av1_f(vlc, seq->frame_width_bits_minus_1 + 1);
   seq->max_frame_height_minus_1 = av1_f(vlc, seq->frame_height_bits_minus_1 + 1);

   if (seq->reduced_still_picture_header)
      seq->frame_id_numbers_present_flag = 0;
   else
      seq->frame_id_numbers_present_flag = av1_f(vlc, 1);
   if (seq->frame_id_numbers_present_flag) {
      seq->delta_frame_id_length_minus_2 = av1_f(vlc, 4);
      seq->additional_frame_id_length_minus_1 = av1_f(vlc, 3);
   }

   seq->use_128x128_superblock = av1_f(vlc, 1);
   seq->enable_filter_intra = av1_f(vlc, 1);
   seq->enable_intra_edge_filter = av1_f(vlc, 1);
   if (seq->reduced_still_picture_header) {
      seq->enable_interintra_compound = 0;
      seq->enable_masked_compound = 0;
      seq->enable_warped_motion = 0;
      seq->enable_dual_filter = 0;
      seq->enable_order_hint = 0;
      seq->enable_jnt_comp = 0;
      seq->enable_ref_frame_mvs = 0;
      seq->seq_force_screen_content_tools = AV1_SELECT_SCREEN_CONTENT_TOOLS;
      seq->seq_force_integer_mv = AV1_SELECT_INTEGER_MV;
      seq->OrderHintBits = 0;
  } else {
      bool seq_choose_screen_content_tools;
      seq->enable_interintra_compound = av1_f(vlc, 1);
      seq->enable_masked_compound = av1_f(vlc, 1);
      seq->enable_warped_motion = av1_f(vlc, 1);
      seq->enable_dual_filter = av1_f(vlc, 1);
      seq->enable_order_hint = av1_f(vlc, 1);
      if (seq->enable_order_hint) {
         seq->enable_jnt_comp = av1_f(vlc, 1);
         seq->enable_ref_frame_mvs = av1_f(vlc, 1);
      } else {
         seq->enable_jnt_comp = 0;
         seq->enable_ref_frame_mvs = 0;
      }

      seq_choose_screen_content_tools = av1_f(vlc, 1);
      seq->seq_force_screen_content_tools =
         seq_choose_screen_content_tools ? AV1_SELECT_SCREEN_CONTENT_TOOLS : av1_f(vlc, 1);

      if (seq->seq_force_screen_content_tools > 0) {
         bool seq_choose_integer_mv = av1_f(vlc, 1);
         seq->seq_force_integer_mv =
            seq_choose_integer_mv ? AV1_SELECT_INTEGER_MV : av1_f(vlc, 1);
      } else {
         seq->seq_force_integer_mv = AV1_SELECT_INTEGER_MV;
      }

      if (seq->enable_order_hint) {
         seq->order_hint_bits_minus_1 = av1_f(vlc, 3);
         seq->OrderHintBits = seq->order_hint_bits_minus_1 + 1;
      } else {
         seq->OrderHintBits = 0;
      }
   }

   seq->enable_superres = av1_f(vlc, 1);
   seq->enable_cdef = av1_f(vlc, 1);
   seq->enable_restoration = av1_f(vlc, 1);

   high_bitdepth = av1_f(vlc, 1);
   if (seq->seq_profile == 2 && high_bitdepth) {
      twelve_bit = av1_f(vlc, 1);
      seq->color_config.BitDepth = twelve_bit ? 12 : 10;
   } else if (seq->seq_profile <= 2) {
      seq->color_config.BitDepth = high_bitdepth ? 10 : 8;
   }

   seq->color_config.mono_chrome = (seq->seq_profile == 1) ? 0 : av1_f(vlc, 1);
   seq->color_config.NumPlanes = seq->color_config.mono_chrome ? 1 : 3;

   color_description_present_flag = av1_f(vlc, 1);
   if (color_description_present_flag) {
      color_primaries = av1_f(vlc, 8);
      transfer_characteristics = av1_f(vlc, 8);
      matrix_coefficients = av1_f(vlc, 8);
   } else {
      color_primaries = AV1_CP_UNSPECIFIED;
      transfer_characteristics = AV1_TC_UNSPECIFIED;
      matrix_coefficients = AV1_MC_UNSPECIFIED;
   }

   if (seq->color_config.mono_chrome) {
      av1_f(vlc, 1); /* color_range */
      seq->color_config.subsampling_x = 1;
      seq->color_config.subsampling_y = 1;
      seq->color_config.separate_uv_delta_q = 0;
   } else if (color_primaries == AV1_CP_BT_709 &&
              transfer_characteristics == AV1_TC_SRGB &&
              matrix_coefficients == AV1_MC_IDENTITY) {
      seq->color_config.subsampling_x = 0;
      seq->color_config.subsampling_y = 0;
   } else {
      av1_f(vlc, 1); /* color_range */
      if (seq->seq_profile == 0) {
         seq->color_config.subsampling_x = 1;
         seq->color_config.subsampling_y = 1;
      } else if (seq->seq_profile == 1 ) {
         seq->color_config.subsampling_x = 0;
         seq->color_config.subsampling_y = 0;
      } else {
         if (seq->color_config.BitDepth == 12) {
            seq->color_config.subsampling_x = av1_f(vlc, 1);
            if (seq->color_config.subsampling_x)
               seq->color_config.subsampling_y = av1_f(vlc, 1);
            else
               seq->color_config.subsampling_y = 0;
         } else {
            seq->color_config.subsampling_x = 1;
            seq->color_config.subsampling_y = 0;
         }
      }
      if (seq->color_config.subsampling_x && seq->color_config.subsampling_y)
         av1_f(vlc, 2); /* chroma_sample_position */
   }
   if (!seq->color_config.mono_chrome)
      seq->color_config.separate_uv_delta_q = av1_f(vlc, 1);

   seq->film_grain_params_present = av1_f(vlc, 1);

   priv->picture.av1.picture_parameter.profile = seq->seq_profile;
   priv->picture.av1.picture_parameter.seq_info_fields.use_128x128_superblock =
      seq->use_128x128_superblock;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_filter_intra =
      seq->enable_filter_intra;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_intra_edge_filter =
      seq->enable_intra_edge_filter;
   priv->picture.av1.picture_parameter.order_hint_bits_minus_1 =
      seq->order_hint_bits_minus_1;
   priv->picture.av1.picture_parameter.max_width = seq->max_frame_width_minus_1 + 1;
   priv->picture.av1.picture_parameter.max_height = seq->max_frame_height_minus_1 + 1;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_interintra_compound =
      seq->enable_interintra_compound;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_masked_compound =
      seq->enable_masked_compound;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_dual_filter =
      seq->enable_dual_filter;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_order_hint =
      seq->enable_order_hint;
   priv->picture.av1.picture_parameter.seq_info_fields.enable_jnt_comp =
      seq->enable_jnt_comp;
   priv->picture.av1.picture_parameter.seq_info_fields.ref_frame_mvs =
      seq->enable_ref_frame_mvs;
   priv->picture.av1.picture_parameter.bit_depth_idx =
      (seq->color_config.BitDepth - 8) >> 1;
   priv->picture.av1.picture_parameter.seq_info_fields.mono_chrome =
      seq->color_config.mono_chrome;
}

static void superres_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   unsigned coded_denom;

   if (seq->enable_superres)
      hdr->use_superres = av1_f(vlc, 1);
   else
      hdr->use_superres = 0;

   if (hdr->use_superres) {
      coded_denom = av1_f(vlc, 3 /* SUPERRES_DENOM_BITS */);
      hdr->SuperresDenom = coded_denom + 9 /* SUPERRES_DENOM_MIN */;
   } else {
      hdr->SuperresDenom = 8 /* SUPERRES_NUM */;
   }

   hdr->UpscaledWidth = hdr->FrameWidth;
   hdr->FrameWidth = (hdr->UpscaledWidth * 8 + (hdr->SuperresDenom / 2)) /
      hdr->SuperresDenom;
}

static void compute_image_size(vid_dec_PrivateType *priv)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);

   hdr->MiCols = 2 * ((hdr->FrameWidth + 7) >> 3);
   hdr->MiRows = 2 * ((hdr->FrameHeight + 7) >> 3);
}

static void frame_size(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   unsigned frame_width_minus_1;
   unsigned frame_height_minus_1;

   if (hdr->frame_size_override_flag) {
      frame_width_minus_1 = av1_f(vlc, seq->frame_width_bits_minus_1 + 1);
      frame_height_minus_1 = av1_f(vlc, seq->frame_height_bits_minus_1 + 1);
      hdr->FrameWidth = frame_width_minus_1 + 1;
      hdr->FrameHeight = frame_height_minus_1 + 1;
   } else {
      hdr->FrameWidth = seq->max_frame_width_minus_1 + 1;
      hdr->FrameHeight = seq->max_frame_height_minus_1 + 1;
   }

   superres_params(priv, vlc);
   compute_image_size(priv);
}

static void render_size(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   bool render_and_frame_size_different;
   unsigned render_width_minus_1;
   unsigned render_height_minus_1;

   render_and_frame_size_different = av1_f(vlc, 1);
   if (render_and_frame_size_different) {
      render_width_minus_1 = av1_f(vlc, 16);
      render_height_minus_1 = av1_f(vlc, 16);
      hdr->RenderWidth = render_width_minus_1 + 1;
      hdr->RenderHeight = render_height_minus_1 + 1;
   } else {
      hdr->RenderWidth = hdr->UpscaledWidth;
      hdr->RenderHeight = hdr->FrameHeight;
   }
}

static int get_relative_dist(vid_dec_PrivateType *priv, int a, int b)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   int diff;
   unsigned 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 uint8_t find_latest_backward(vid_dec_PrivateType *priv)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   uint8_t ref = 0xff;
   unsigned latestOrderHint = 0;
   int i;

   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i) {
      unsigned hint = hdr->shiftedOrderHints[i];
      if (!hdr->usedFrame[i] &&
          hint >= hdr->curFrameHint &&
          (ref == 0xff || hint >= latestOrderHint)) {
         ref = i;
         latestOrderHint = hint;
      }
   }

   return ref;
}

static uint8_t find_earliest_backward(vid_dec_PrivateType *priv)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   uint8_t ref = 0xff;
   unsigned earliestOrderHint = 0;
   int i;

   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i) {
      unsigned hint = hdr->shiftedOrderHints[i];
      if (!hdr->usedFrame[i] &&
          hint >= hdr->curFrameHint &&
          (ref == 0xff || hint < earliestOrderHint)) {
         ref = i;
         earliestOrderHint = hint;
      }
   }

   return ref;
}

static uint8_t find_latest_forward(vid_dec_PrivateType *priv)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   uint8_t ref = 0xff;
   unsigned latestOrderHint = 0;
   int i;

   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i) {
      unsigned hint = hdr->shiftedOrderHints[i];
      if (!hdr->usedFrame[i] &&
          hint < hdr->curFrameHint &&
          (ref == 0xff || hint >= latestOrderHint)) {
         ref = i;
         latestOrderHint = hint;
      }
   }

   return ref;
}

static void set_frame_refs(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   uint8_t Ref_Frame_List[5] = { AV1_LAST2_FRAME , AV1_LAST3_FRAME, AV1_BWDREF_FRAME,
      AV1_ALTREF2_FRAME, AV1_ALTREF_FRAME };
   unsigned earliestOrderHint = 0;
   uint8_t ref;
   int i;

   for (i = 0; i < AV1_REFS_PER_FRAME; ++i)
      hdr->ref_frame_idx[i] = 0xff;

   hdr->ref_frame_idx[0] = hdr->last_frame_idx;
   hdr->ref_frame_idx[AV1_GOLDEN_FRAME - AV1_LAST_FRAME] = hdr->gold_frame_idx;

   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i)
      hdr->usedFrame[i] = 0;

   hdr->usedFrame[hdr->last_frame_idx] = 1;
   hdr->usedFrame[hdr->gold_frame_idx] = 1;

   hdr->curFrameHint = 1 << (seq->OrderHintBits - 1);

   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i)
      hdr->shiftedOrderHints[i] =
         hdr->curFrameHint +
         get_relative_dist(priv, hdr->RefOrderHint[i], hdr->OrderHint);

   ref = find_latest_backward(priv);
   if (ref != 0xff) {
      hdr->ref_frame_idx[AV1_ALTREF_FRAME - AV1_LAST_FRAME] = ref;
      hdr->usedFrame[ref] = 1;
   }

   ref = find_earliest_backward(priv);
   if (ref != 0xff) {
      hdr->ref_frame_idx[AV1_BWDREF_FRAME - AV1_LAST_FRAME] = ref;
      hdr->usedFrame[ref] = 1;
   }

   ref = find_earliest_backward(priv);
   if (ref != 0xff) {
      hdr->ref_frame_idx[AV1_ALTREF2_FRAME - AV1_LAST_FRAME] = ref;
      hdr->usedFrame[ref] = 1;
   }

   for (i = 0; i < AV1_REFS_PER_FRAME - 2; ++i) {
      uint8_t refFrame = Ref_Frame_List[i];
      if (hdr->ref_frame_idx[refFrame - AV1_LAST_FRAME] == 0xff) {
         ref = find_latest_forward(priv);
         if (ref != 0xff) {
            hdr->ref_frame_idx[refFrame - AV1_LAST_FRAME] = ref;
            hdr->usedFrame[ref] = 1;
         }
      }
   }

   ref = 0xff;
   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i) {
      unsigned hint = hdr->shiftedOrderHints[i];
      if (ref == 0xff || hint < earliestOrderHint) {
         ref = i;
         earliestOrderHint = hint;
      }
   }

   for (i = 0; i < AV1_REFS_PER_FRAME; ++i) {
     if (hdr->ref_frame_idx[i] == 0xff)
        hdr->ref_frame_idx[i] = ref;
   }
}

static void frame_size_with_refs(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   bool found_ref;
   int i;

   for (i = 0; i < AV1_REFS_PER_FRAME; ++i) {
      found_ref = av1_f(vlc, 1);
      if (found_ref) {
         hdr->UpscaledWidth =
            priv->codec_data.av1.RefFrames[hdr->ref_frame_idx[i]].RefUpscaledWidth;
         hdr->FrameWidth = hdr->UpscaledWidth;
         hdr->FrameHeight =
            priv->codec_data.av1.RefFrames[hdr->ref_frame_idx[i]].RefFrameHeight;
         hdr->RenderWidth =
            priv->codec_data.av1.RefFrames[hdr->ref_frame_idx[i]].RefRenderWidth;
         hdr->RenderHeight =
            priv->codec_data.av1.RefFrames[hdr->ref_frame_idx[i]].RefRenderHeight;
         break;
      }
   }

   if (!found_ref) {
      frame_size(priv, vlc);
      render_size(priv, vlc);
   } else {
      superres_params(priv, vlc);
      compute_image_size(priv);
   }
}

static unsigned tile_log2(unsigned blkSize, unsigned target)
{
   unsigned k = 0;

   for (k = 0; (blkSize << k) < target; k++);

   return k;
}

static void tile_info(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct tile_info *ti = &(priv->codec_data.av1.uncompressed_header.ti);
   unsigned sbCols;
   unsigned sbRows;
   int width_sb;
   int height_sb;
   unsigned sbSize;
   unsigned maxTileWidthSb;
   unsigned minLog2TileCols;
   unsigned maxLog2TileCols;
   unsigned maxLog2TileRows;
   unsigned minLog2Tiles;
   bool uniform_tile_spacing_flag;
   unsigned maxTileAreaSb;
   unsigned startSb, i;

   sbCols = (seq->use_128x128_superblock) ?
      ((hdr->MiCols + 31) >> 5) : ((hdr->MiCols + 15) >> 4);
   sbRows = (seq->use_128x128_superblock) ?
      ((hdr->MiRows + 31) >> 5) : ((hdr->MiRows + 15) >> 4);
   width_sb = sbCols;
   height_sb = sbRows;
   sbSize = (seq->use_128x128_superblock ? 5 : 4) + 2;
   maxTileWidthSb = AV1_MAX_TILE_WIDTH >> sbSize;
   maxTileAreaSb = AV1_MAX_TILE_AREA >> (2 * sbSize);
   minLog2TileCols = tile_log2(maxTileWidthSb, sbCols);
   maxLog2TileCols = tile_log2(1, MIN2(sbCols, AV1_MAX_TILE_COLS));
   maxLog2TileRows = tile_log2(1, MIN2(sbRows, AV1_MAX_TILE_ROWS));
   minLog2Tiles = MAX2(minLog2TileCols, tile_log2(maxTileAreaSb, sbRows * sbCols));

   uniform_tile_spacing_flag = av1_f(vlc, 1);
   if (uniform_tile_spacing_flag) {
      unsigned tileWidthSb, tileHeightSb;
      unsigned minLog2TileRows;

      ti->TileColsLog2 = minLog2TileCols;
      while (ti->TileColsLog2 < maxLog2TileCols) {
         bool increment_tile_cols_log2 = av1_f(vlc, 1);
         if (increment_tile_cols_log2)
            ti->TileColsLog2++;
         else
            break;
      }
      tileWidthSb = (sbCols + (1 << ti->TileColsLog2) - 1) >> ti->TileColsLog2;
      i = 0;
      for (startSb = 0; startSb < sbCols; startSb += tileWidthSb) {
         ti->tile_col_start_sb[i] = startSb;
         i++;
      }
      ti->tile_col_start_sb[i] = sbCols;
      ti->TileCols = i;

      minLog2TileRows = (minLog2Tiles > ti->TileColsLog2)?
         (minLog2Tiles - ti->TileColsLog2) : 0;
      ti->TileRowsLog2 = minLog2TileRows;
      while (ti->TileRowsLog2 < maxLog2TileRows) {
         bool increment_tile_rows_log2 = av1_f(vlc, 1);
         if (increment_tile_rows_log2)
            ti->TileRowsLog2++;
         else
            break;
      }
      tileHeightSb = (sbRows + (1 << ti->TileRowsLog2) - 1) >> ti->TileRowsLog2;
      i = 0;
      for (startSb = 0; startSb < sbRows; startSb += tileHeightSb) {
         ti->tile_row_start_sb[i] = startSb;
         i++;
      }
      ti->tile_row_start_sb[i] = sbRows;
      ti->TileRows = i;
   } else {
      unsigned widestTileSb = 0;
      unsigned maxTileHeightSb;

      startSb = 0;
      for (i = 0; startSb < sbCols; ++i) {
         uint8_t maxWidth;
         unsigned sizeSb;
         unsigned width_in_sbs_minus_1;

         ti->tile_col_start_sb[i] = startSb;
         maxWidth = MIN2(sbCols - startSb, maxTileWidthSb);
         width_in_sbs_minus_1 = av1_ns(vlc, maxWidth);
         sizeSb = width_in_sbs_minus_1 + 1;
         widestTileSb = MAX2(sizeSb, widestTileSb);
         startSb += sizeSb;
         width_sb -= sizeSb;
      }
      ti->TileCols = i;

      ti->tile_col_start_sb[i] = startSb + width_sb;
      ti->TileColsLog2 = tile_log2(1, ti->TileCols);

      if (minLog2Tiles > 0)
         maxTileAreaSb = (sbRows * sbCols) >> (minLog2Tiles + 1);
      else
         maxTileAreaSb = (sbRows * sbCols);
      maxTileHeightSb = MAX2(maxTileAreaSb / widestTileSb, 1);

      startSb = 0;
      for (i = 0; startSb < sbRows; ++i) {
         uint8_t maxHeight;
         unsigned height_in_sbs_minus_1;

         maxHeight = MIN2(sbRows - startSb, maxTileHeightSb);
         height_in_sbs_minus_1 = av1_ns(vlc, maxHeight);
         ti->tile_row_start_sb[i] = startSb;
         startSb += height_in_sbs_minus_1 + 1;
         height_sb -= height_in_sbs_minus_1 + 1;
      }
      ti->TileRows = i;
      ti->tile_row_start_sb[i] = startSb + height_sb;
      ti->TileRowsLog2 = tile_log2(1, ti->TileRows);
   }

   if (ti->TileColsLog2 > 0 || ti->TileRowsLog2 > 0) {
      ti->context_update_tile_id =
         av1_f(vlc, ti->TileRowsLog2 + ti->TileColsLog2);
      uint8_t tile_size_bytes_minus_1 = av1_f(vlc, 2);
      ti->TileSizeBytes = tile_size_bytes_minus_1 + 1;
   } else {
      ti->context_update_tile_id = 0;
   }
}

static int read_delta_q(struct vl_vlc *vlc)
{
   bool delta_coded = av1_f(vlc, 1);
   int delta_q = 0;

   if (delta_coded)
      delta_q = av1_su(vlc, 7);

   return delta_q;
}

static void quantization_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct quantization_params* qp = &(priv->codec_data.av1.uncompressed_header.qp);
   bool using_qmatrix;

   qp->base_q_idx = av1_f(vlc, 8);
   qp->DeltaQYDc = read_delta_q(vlc);
   if (seq->color_config.NumPlanes > 1) {
      bool diff_uv_delta =
         (seq->color_config.separate_uv_delta_q) ? av1_f(vlc, 1) : 0;

      qp->DeltaQUDc = read_delta_q(vlc);
      qp->DeltaQUAc = read_delta_q(vlc);
      if (diff_uv_delta) {
         qp->DeltaQVDc = read_delta_q(vlc);
         qp->DeltaQVAc = read_delta_q(vlc);
      } else {
         qp->DeltaQVDc = qp->DeltaQUDc;
         qp->DeltaQVAc = qp->DeltaQUAc;
      }
   } else {
      qp->DeltaQVDc = 0;
      qp->DeltaQVAc = 0;
      qp->DeltaQUDc = 0;
      qp->DeltaQUAc = 0;
   }

   using_qmatrix = av1_f(vlc, 1);
   if (using_qmatrix) {
      qp->qm_y = av1_f(vlc, 4);
      qp->qm_u = av1_f(vlc, 4);
      if (!seq->color_config.separate_uv_delta_q)
         qp->qm_v = qp->qm_u;
      else
         qp->qm_v = av1_f(vlc, 4);
   } else {
      qp->qm_y = 0xf;
      qp->qm_u = 0xf;
      qp->qm_v = 0xf;
   }
}

static void segmentation_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct segmentation_params* sp = &(priv->codec_data.av1.uncompressed_header.sp);
   int i, j;

   sp->segmentation_enabled = av1_f(vlc, 1);
   if (sp->segmentation_enabled) {
      bool segmentation_update_data;

      if (hdr->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
         sp->segmentation_update_map = 1;
         sp->segmentation_temporal_update = 0;
         segmentation_update_data = 1;
      } else {
         sp->segmentation_update_map = av1_f(vlc, 1);
         if (sp->segmentation_update_map)
            sp->segmentation_temporal_update = av1_f(vlc, 1);
         else
            sp->segmentation_temporal_update = 0;
         segmentation_update_data = av1_f(vlc, 1);
      }

      if (segmentation_update_data) {
         uint8_t Segmentation_Feature_Bits[AV1_SEG_LVL_MAX] = { 8, 6, 6, 6, 6, 3, 0, 0 };
         bool Segmentation_Feature_Signed[AV1_SEG_LVL_MAX] = { 1, 1, 1, 1, 1, 0, 0, 0 };
         unsigned Segmentation_Feature_Max[AV1_SEG_LVL_MAX] = { 255, 63, 63, 63, 63, 7, 0, 0 };

         memset(sp->FeatureData, 0, sizeof(sp->FeatureData));
         memset(sp->FeatureMask, 0, sizeof(sp->FeatureMask));
         for (i = 0; i < AV1_MAX_SEGMENTS; ++i) {
            for (j = 0; j < AV1_SEG_LVL_MAX; ++j) {
               int feature_value = 0;
               bool feature_enabled = av1_f(vlc, 1);

               sp->FeatureEnabled[i][j] = feature_enabled;
               int clippedValue = 0;
               if (feature_enabled) {
                  uint8_t bitsToRead = Segmentation_Feature_Bits[j];
                  int limit = Segmentation_Feature_Max[j];
                  if (Segmentation_Feature_Signed[j]) {
                     feature_value = av1_su(vlc, 1 + bitsToRead);
                     clippedValue = CLAMP(feature_value, -limit, limit);
                     sp->FeatureMask[i] |= 1 << j;
                  } else {
                     feature_value = av1_f(vlc, bitsToRead);
                     clippedValue = CLAMP(feature_value, 0, limit);
                     sp->FeatureMask[i] |= 1 << j;
                  }
               }
               sp->FeatureData[i][j] = clippedValue;
            }
         }
      } else {
         int r = hdr->ref_frame_idx[hdr->primary_ref_frame];
         memcpy(sp, &(priv->codec_data.av1.refs[r].sp), sizeof(*sp));
      }
   } else {
      memset(sp, 0, sizeof(*sp));
   }
}

static void delta_q_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct quantization_params* qp = &(priv->codec_data.av1.uncompressed_header.qp);
   struct delta_q_params * dqp = &(priv->codec_data.av1.uncompressed_header.dqp);

   dqp->delta_q_present = 0;
   dqp->delta_q_res = 0;
   if (qp->base_q_idx > 0)
      dqp->delta_q_present = av1_f(vlc, 1);
   if (dqp->delta_q_present)
      dqp->delta_q_res = av1_f(vlc, 2);
}

static void delta_lf_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct delta_q_params * dqp = &(priv->codec_data.av1.uncompressed_header.dqp);
   struct delta_lf_params* dlfp = &(priv->codec_data.av1.uncompressed_header.dlfp);

   dlfp->delta_lf_present = 0;
   dlfp->delta_lf_res = 0;
   dlfp->delta_lf_multi = 0;
   if (dqp->delta_q_present) {
      if (!hdr->allow_intrabc)
         dlfp->delta_lf_present = av1_f(vlc, 1);

      if (dlfp->delta_lf_present) {
         dlfp->delta_lf_res = av1_f(vlc, 2);
         dlfp->delta_lf_multi = av1_f(vlc, 1);
      }
   }
}

static unsigned get_qindex(vid_dec_PrivateType * priv, bool ignoreDeltaQ, unsigned segmentId)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct segmentation_params* sp = &(priv->codec_data.av1.uncompressed_header.sp);
   struct quantization_params* qp = &(priv->codec_data.av1.uncompressed_header.qp);
   unsigned qindex = 0;

   if (sp->segmentation_enabled && sp->FeatureEnabled[segmentId][AV1_SEG_LVL_ALT_Q]) {
      unsigned data = sp->FeatureData[segmentId][AV1_SEG_LVL_ALT_Q];
      qindex = qp->base_q_idx + data;
      if (!ignoreDeltaQ && hdr->dqp.delta_q_present)
         qindex = data;

      return CLAMP(qindex, 0, 255);
   }

   if (!ignoreDeltaQ && hdr->dqp.delta_q_present)
      return 0;

   return qp->base_q_idx;
}

static void loop_filter_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct loop_filter_params *lfp = &(priv->codec_data.av1.uncompressed_header.lfp);
   int i;

   if (hdr->CodedLossless || hdr->allow_intrabc) {
      lfp->loop_filter_level[0] = 0;
      lfp->loop_filter_level[1] = 0;
      lfp->loop_filter_ref_deltas[AV1_INTRA_FRAME] = 1;
      lfp->loop_filter_ref_deltas[AV1_LAST_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_LAST2_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_LAST3_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_BWDREF_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_GOLDEN_FRAME] = -1;
      lfp->loop_filter_ref_deltas[AV1_ALTREF2_FRAME] = -1;
      lfp->loop_filter_ref_deltas[AV1_ALTREF_FRAME] = -1;
      lfp->loop_filter_mode_deltas[0] = 0;
      lfp->loop_filter_mode_deltas[1] = 0;
      return;
   }

   if (hdr->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
      lfp->loop_filter_ref_deltas[AV1_INTRA_FRAME] = 1;
      lfp->loop_filter_ref_deltas[AV1_LAST_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_LAST2_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_LAST3_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_BWDREF_FRAME] = 0;
      lfp->loop_filter_ref_deltas[AV1_GOLDEN_FRAME] = -1;
      lfp->loop_filter_ref_deltas[AV1_ALTREF2_FRAME] = -1;
      lfp->loop_filter_ref_deltas[AV1_ALTREF_FRAME] = -1;
      lfp->loop_filter_mode_deltas[0] = 0;
      lfp->loop_filter_mode_deltas[1] = 0;
   } else {
      int r = hdr->ref_frame_idx[hdr->primary_ref_frame];
      memcpy(lfp->loop_filter_ref_deltas,
            priv->codec_data.av1.refs[r].lfp.loop_filter_ref_deltas, 8);
      memcpy(lfp->loop_filter_mode_deltas,
            priv->codec_data.av1.refs[r].lfp.loop_filter_mode_deltas, 2);
   }

   lfp->loop_filter_level[0] = av1_f(vlc, 6);
   lfp->loop_filter_level[1] = av1_f(vlc, 6);
   if (seq->color_config.NumPlanes > 1) {
      if (lfp->loop_filter_level[0] || lfp->loop_filter_level[1]) {
         lfp->loop_filter_level[2] = av1_f(vlc, 6);
         lfp->loop_filter_level[3] = av1_f(vlc, 6);
      }
   }

   lfp->loop_filter_sharpness = av1_f(vlc, 3);
   lfp->loop_filter_delta_enabled = av1_f(vlc, 1);
   if (lfp->loop_filter_delta_enabled) {
      lfp->loop_filter_delta_update = av1_f(vlc, 1);
      if (lfp->loop_filter_delta_update) {
         for (i = 0; i < AV1_NUM_REF_FRAMES; ++i) {
            int8_t update_ref_delta = av1_f(vlc, 1);
            if (update_ref_delta)
               lfp->loop_filter_ref_deltas[i] = av1_su(vlc, 7);
         }

         for (i = 0; i < 2; ++i) {
            int8_t update_mode_delta = av1_f(vlc, 1);
            if (update_mode_delta)
               lfp->loop_filter_mode_deltas[i] = av1_su(vlc, 7);
         }
      }
   }
}

static void cdef_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct cdef_params *cdefp = &(priv->codec_data.av1.uncompressed_header.cdefp);;
   int i;

   if (hdr->CodedLossless || hdr->allow_intrabc || !seq->enable_cdef) {
      cdefp->cdef_bits = 0;
      cdefp->cdef_y_strengths[0] = 0;
      cdefp->cdef_uv_strengths[0] = 0;
      return;
   }

   cdefp->cdef_damping_minus_3 = av1_f(vlc, 2);
   cdefp->cdef_bits = av1_f(vlc, 2);
   for (i = 0; i < (1 << cdefp->cdef_bits); ++i) {
      cdefp->cdef_y_strengths[i] = av1_f(vlc, 6);
      if (seq->color_config.NumPlanes > 1)
         cdefp->cdef_uv_strengths[i] = av1_f(vlc, 6);
   }
}

static void lr_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct loop_restoration_params *lrp = &(priv->codec_data.av1.uncompressed_header.lrp);
   uint8_t Remap_Lr_Type[4] =
      { AV1_RESTORE_NONE, AV1_RESTORE_SWITCHABLE, AV1_RESTORE_WIENER, AV1_RESTORE_SGRPROJ };
   bool UsesLr = false;
   bool UsesChromaLr = false;
   uint8_t lr_unit_shift, lr_uv_shift;
   int i;

   if (hdr->AllLossless || hdr->allow_intrabc || !seq->enable_restoration) {
      lrp->FrameRestorationType[0] = AV1_RESTORE_NONE;
      lrp->FrameRestorationType[1] = AV1_RESTORE_NONE;
      lrp->FrameRestorationType[2] = AV1_RESTORE_NONE;
      return;
   }

   for (i = 0; i < seq->color_config.NumPlanes; ++i) {
      uint8_t lr_type = av1_f(vlc, 2);
      lrp->FrameRestorationType[i] = Remap_Lr_Type[lr_type];
      if (lrp->FrameRestorationType[i] != AV1_RESTORE_NONE) {
         UsesLr = true;
         if (i > 0)
            UsesChromaLr = true;
      }
   }

   if (UsesLr) {
      if (seq->use_128x128_superblock) {
         lr_unit_shift = av1_f(vlc, 1) + 1;
      } else {
         lr_unit_shift = av1_f(vlc, 1);
         if (lr_unit_shift) {
            uint8_t lr_unit_extra_shift = av1_f(vlc, 1);
            lr_unit_shift += lr_unit_extra_shift;
         }
      }

      lrp->LoopRestorationSize[0] = AV1_RESTORATION_TILESIZE >> (2 - lr_unit_shift);
      lr_uv_shift =
         (seq->color_config.subsampling_x && seq->color_config.subsampling_y && UsesChromaLr) ?
         av1_f(vlc, 1) : 0;

      lrp->LoopRestorationSize[1] = lrp->LoopRestorationSize[0] >> lr_uv_shift;
      lrp->LoopRestorationSize[2] = lrp->LoopRestorationSize[0] >> lr_uv_shift;
   } else {
      lrp->LoopRestorationSize[0] = lrp->LoopRestorationSize[1] =
         lrp->LoopRestorationSize[2] = (1 << 8);
   }
}

static void tx_mode(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct tx_mode_params *tm = &(priv->codec_data.av1.uncompressed_header.tm);

   if (hdr->CodedLossless) {
      tm->TxMode = AV1_ONLY_4X4;
   } else {
      bool tx_mode_select = av1_f(vlc, 1);
      tm->TxMode = (tx_mode_select) ? AV1_TX_MODE_SELECT : AV1_TX_MODE_LARGEST;
   }
}

static void frame_reference_mode(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);

   if (hdr->FrameIsIntra)
      hdr->reference_select = SINGLE_REFERENCE;
   else
      hdr->reference_select = av1_f(vlc, 1) ? REFERENCE_MODE_SELECT : SINGLE_REFERENCE;
}

static void skip_mode_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct skip_mode_params *smp = &(priv->codec_data.av1.uncompressed_header.smp);;
   bool skipModeAllowed;
   int i;

   if (hdr->FrameIsIntra || hdr->reference_select == SINGLE_REFERENCE ||
         !seq->enable_order_hint) {
      skipModeAllowed = 0;
   } else {
      int ref_frame_offset[2] = { -1, INT_MAX };
      int ref_idx[2] = { -1, -1 };

      skipModeAllowed = 0;
      for (i = 0; i < AV1_REFS_PER_FRAME; ++i) {
         unsigned ref_offset = priv->codec_data.av1.refs[hdr->ref_frame_idx[i]].OrderHint;
         if (get_relative_dist(priv, ref_offset, hdr->OrderHint) < 0) {
            if (ref_frame_offset[0] == -1 ||
               get_relative_dist(priv, ref_offset, ref_frame_offset[0]) > 0) {
               ref_frame_offset[0] = ref_offset;
               ref_idx[0] = i;
            }
         } else if (get_relative_dist(priv, ref_offset, hdr->OrderHint) > 0) {
            if (ref_frame_offset[1] == INT_MAX ||
               get_relative_dist(priv, ref_offset, ref_frame_offset[1]) < 0) {
               ref_frame_offset[1] = ref_offset;
               ref_idx[1] = i;
            }
         }
      }

      if (ref_idx[0] != -1 && ref_idx[1] != -1) {
         skipModeAllowed = 1;
      } else if (ref_idx[0] != -1 && ref_idx[1] == -1) {
         ref_frame_offset[1] = -1;
         for (i = 0; i < AV1_ALTREF_FRAME - AV1_LAST_FRAME + 1; ++i) {
            unsigned ref_offset = priv->codec_data.av1.refs[hdr->ref_frame_idx[i]].OrderHint;
            if ((ref_frame_offset[0] != -1 &&
                get_relative_dist(priv, ref_offset, ref_frame_offset[0]) < 0) &&
                (ref_frame_offset[1] == -1 ||
                get_relative_dist(priv, ref_offset, ref_frame_offset[1]) > 0)) {
               ref_frame_offset[1] = ref_offset;
               ref_idx[1] = i;
            }
         }
         if (ref_frame_offset[1] != -1)
            skipModeAllowed = 1;
      }
   }

   smp->skip_mode_present = skipModeAllowed ? av1_f(vlc, 1) : 0;
}

static unsigned inverse_recenter(unsigned r, unsigned v)
{
   if (v > (2 * r))
      return v;
   else if (v & 1)
      return (r - ((v + 1) >> 1));
   else
      return (r + (v >> 1));
}

static unsigned decode_subexp(struct vl_vlc *vlc, unsigned numSyms)
{
   unsigned i = 0;
   unsigned mk = 0;
   unsigned k = 3;

   while (1) {
      unsigned b2 = (i) ? (k + i - 1) : k;
      unsigned a = 1 << b2;
      if (numSyms <= (mk + 3 * a)) {
         unsigned subexp_final_bits = av1_ns(vlc, (numSyms - mk));
         return (subexp_final_bits + mk);
      } else {
         bool subexp_more_bits = av1_f(vlc, 1);
         if (subexp_more_bits) {
            i++;
            mk += a;
         } else {
            unsigned subexp_bits = av1_f(vlc, b2);
            return (subexp_bits + mk);
         }
      }
   }
}

static unsigned decode_unsigned_subexp_with_ref(struct vl_vlc *vlc,
      unsigned mx, unsigned r)
{
   unsigned smart;
   unsigned v = decode_subexp(vlc, mx);

   if ((r << 1) <= mx) {
      smart = inverse_recenter(r, v);
      return smart;
   } else {
      smart = inverse_recenter(mx - 1 - r, v);
      return (mx - 1 - smart);
   }
}

static int decode_signed_subexp_with_ref(struct vl_vlc *vlc, int low, int high, int r)
{
   int x = decode_unsigned_subexp_with_ref(vlc, high - low, r - low);

   return (x + low);
}

static void read_global_param(struct global_motion_params* global_params,
                              struct global_motion_params* ref_params,
                              vid_dec_PrivateType *priv, struct vl_vlc *vlc,
                              uint8_t type, uint8_t ref, uint8_t idx)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   uint8_t absBits = 12; /* GM_ABS_ALPHA_BITS */
   uint8_t precBits = 15; /* GM_ALPHA_PREC_BITS */
   int precDiff, round, sub, mx, r = 0;

   if (idx < 2) {
      if (type == AV1_TRANSLATION) {
         absBits = 9 /* GM_ABS_TRANS_ONLY_BITS */ - !hdr->allow_high_precision_mv;
         precBits = 3 /* GM_TRANS_ONLY_PREC_BITS */ - !hdr->allow_high_precision_mv;
      } else {
         absBits = 12; /* GM_ABS_TRANS_BITS */
         precBits = 6; /* GM_TRANS_PREC_BITS */;
      }
   }

   precDiff = AV1_WARPEDMODEL_PREC_BITS - precBits;
   round = ((idx % 3) == 2) ? (1 << AV1_WARPEDMODEL_PREC_BITS) : 0;
   sub = ((idx % 3) == 2) ? (1 << precBits) : 0;
   mx = (int)(1 << absBits);
   if (ref_params)
      r = (ref_params->gm_params[ref][idx] >> precDiff) - sub;

   global_params->gm_params[ref][idx] =
      (decode_signed_subexp_with_ref(vlc, -mx, mx + 1, r) << precDiff) + round;
}

static void global_motion_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct global_motion_params *gmp = &(priv->codec_data.av1.uncompressed_header.gmp);
   struct global_motion_params *ref_gmp = NULL;
   unsigned ref, i;

   if (hdr->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
      for (ref = 0; ref < AV1_NUM_REF_FRAMES; ++ref) {
         gmp->GmType[ref] = AV1_IDENTITY;
         for (i = 0; i < 6; ++i)
            gmp->gm_params[ref][i] = (((i % 3) == 2) ? (1 << AV1_WARPEDMODEL_PREC_BITS) : 0);
      }
   } else {
      const int r = hdr->ref_frame_idx[hdr->primary_ref_frame];
      ref_gmp = &(priv->codec_data.av1.refs[r].gmp);
   }

   for (ref = AV1_LAST_FRAME; ref <= AV1_ALTREF_FRAME; ++ref) {
      gmp->GmType[ref] = AV1_IDENTITY;
      for (i = 0; i < 6; ++i)
         gmp->gm_params[ref][i] = (((i % 3) == 2) ? (1 << AV1_WARPEDMODEL_PREC_BITS) : 0);
   }

   if (hdr->FrameIsIntra)
      return;

   for (ref = AV1_LAST_FRAME; ref <= AV1_ALTREF_FRAME; ++ref) {
      uint8_t type = AV1_IDENTITY;
      bool is_global;

      gmp->GmType[ref] = AV1_IDENTITY;
      for (i = 0; i < 6; ++i)
         gmp->gm_params[ref][i] = (((i % 3) == 2) ? (1 << AV1_WARPEDMODEL_PREC_BITS) : 0);

      is_global = av1_f(vlc, 1);
      if (is_global) {
         bool is_rot_zoom = av1_f(vlc, 1);
         if (is_rot_zoom) {
            type = AV1_ROTZOOM;
         } else {
            bool is_translation = av1_f(vlc, 1);
            type = is_translation ? AV1_TRANSLATION : AV1_AFFINE;
         }
      }

      gmp->GmType[ref] = type;

      if (type >= AV1_ROTZOOM) {
         read_global_param(gmp, ref_gmp, priv, vlc, type, ref, 2);
         read_global_param(gmp, ref_gmp, priv, vlc, type, ref, 3);
         if (type == AV1_AFFINE) {
            read_global_param(gmp, ref_gmp, priv, vlc, type, ref, 4);
            read_global_param(gmp, ref_gmp, priv, vlc, type, ref, 5);
         } else {
            gmp->gm_params[ref][4] = -gmp->gm_params[ref][3];
            gmp->gm_params[ref][5] = gmp->gm_params[ref][2];
         }
      }

      if (type >= AV1_TRANSLATION) {
         read_global_param(gmp, ref_gmp, priv, vlc, type, ref, 0);
         read_global_param(gmp, ref_gmp, priv, vlc, type, ref, 1);
      }
   }
}

static void film_grain_params(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   struct film_grain_params *fgp = &(priv->codec_data.av1.uncompressed_header.fgp);

   bool update_grain;
   uint8_t numPosLuma;
   uint8_t numPosChroma;
   unsigned i;

   if (!seq->film_grain_params_present ||
         (!hdr->show_frame && !hdr->showable_frame)) {
      memset(fgp, 0, sizeof(*fgp));
      return;
   }

   fgp->apply_grain = av1_f(vlc, 1);
   if (!fgp->apply_grain) {
      memset(fgp, 0, sizeof(*fgp));
      return;
   }

   fgp->grain_seed = av1_f(vlc, 16);
   update_grain =
      (hdr->frame_type == AV1_INTER_FRAME) ? av1_f(vlc, 1) : 1;

   if (!update_grain) {
      uint8_t film_grain_params_ref_idx = av1_f(vlc, 3);
      uint16_t tempGrainSeed = fgp->grain_seed;
      memcpy(fgp, &(priv->codec_data.av1.refs[film_grain_params_ref_idx].fgp),
            sizeof(*fgp));
      fgp->grain_seed = tempGrainSeed;
      return;
   }

   fgp->num_y_points = av1_f(vlc, 4);
   for (i = 0; i < fgp->num_y_points; ++i) {
      fgp->point_y_value[i] = av1_f(vlc, 8);
      fgp->point_y_scaling[i] = av1_f(vlc, 8);
   }

   fgp->chroma_scaling_from_luma =
      (seq->color_config.mono_chrome) ? 0 : av1_f(vlc, 1);
   if (seq->color_config.mono_chrome || fgp->chroma_scaling_from_luma ||
       (seq->color_config.subsampling_x && seq->color_config.subsampling_y &&
       (fgp->num_y_points == 0))) {
      fgp->num_cb_points = 0;
      fgp->num_cr_points = 0;
   } else {
      fgp->num_cb_points = av1_f(vlc, 4);
      for (i = 0; i < fgp->num_cb_points; ++i) {
         fgp->point_cb_value[i] = av1_f(vlc, 8);
         fgp->point_cb_scaling[i] = av1_f(vlc, 8);
      }
      fgp->num_cr_points = av1_f(vlc, 4);
      for (i = 0; i < fgp->num_cr_points; ++i) {
         fgp->point_cr_value[i] = av1_f(vlc, 8);
         fgp->point_cr_scaling[i] = av1_f(vlc, 8);
      }
   }

   fgp->grain_scaling_minus_8 = av1_f(vlc, 2);
   fgp->ar_coeff_lag = av1_f(vlc, 2);
   numPosLuma = 2 * fgp->ar_coeff_lag * (fgp->ar_coeff_lag + 1);
   if (fgp->num_y_points) {
      numPosChroma = numPosLuma + 1;
      for (i = 0; i < numPosLuma; ++i) {
         uint8_t ar_coeffs_y_plus_128 = av1_f(vlc, 8);
         fgp->ar_coeffs_y[i] = ar_coeffs_y_plus_128 - 128;
      }
   } else {
      numPosChroma = numPosLuma;
   }

   if (fgp->chroma_scaling_from_luma || fgp->num_cb_points) {
      for (i = 0; i < numPosChroma; ++i) {
         uint8_t ar_coeffs_cb_plus_128 = av1_f(vlc, 8);
         fgp->ar_coeffs_cb[i] = ar_coeffs_cb_plus_128 - 128;
      }
   }

   if (fgp->chroma_scaling_from_luma || fgp->num_cr_points) {
      for (i = 0; i < numPosChroma; ++i) {
         uint8_t ar_coeffs_cr_plus_128 = av1_f(vlc, 8);
         fgp->ar_coeffs_cr[i] = ar_coeffs_cr_plus_128 - 128;
      }
   }

   fgp->ar_coeff_shift_minus_6 = av1_f(vlc, 2);
   fgp->grain_scale_shift = av1_f(vlc, 2);
   if (fgp->num_cb_points) {
      fgp->cb_mult = av1_f(vlc, 8);
      fgp->cb_luma_mult = av1_f(vlc, 8);
      fgp->cb_offset = av1_f(vlc, 9);
   }

   if (fgp->num_cr_points) {
      fgp->cr_mult = av1_f(vlc, 8);
      fgp->cr_luma_mult = av1_f(vlc, 8);
      fgp->cr_offset = av1_f(vlc, 9);
   }

   fgp->overlap_flag = av1_f(vlc, 1);
   fgp->clip_to_restricted_range = av1_f(vlc, 1);
}

static void frame_header_obu(vid_dec_PrivateType *priv, struct vl_vlc *vlc)
{
   struct av1_sequence_header_obu *seq = &(priv->codec_data.av1.seq);
   struct av1_uncompressed_header_obu *hdr = &(priv->codec_data.av1.uncompressed_header);
   unsigned idLen = 0;
   unsigned allFrames;
   int i, j;

   memset(hdr, 0, sizeof(*hdr));

   if (seq->frame_id_numbers_present_flag)
      idLen = seq->additional_frame_id_length_minus_1 +
         seq->delta_frame_id_length_minus_2 + 3;

   allFrames = (1 << AV1_NUM_REF_FRAMES) - 1;
   if (seq->reduced_still_picture_header) {
      hdr->show_existing_frame = 0;
      hdr->frame_type = AV1_KEY_FRAME;
      hdr->FrameIsIntra = 1;
      hdr->show_frame = 1;
      hdr->showable_frame = 0;
   } else {
      hdr->show_existing_frame = av1_f(vlc, 1);
      if (hdr->show_existing_frame) {
         hdr->frame_to_show_map_idx = av1_f(vlc, 3);
         if (seq->decoder_model_info_present_flag &&
               !seq->timing_info.equal_picture_interval)
            av1_f(vlc, seq->decoder_model_info.
                  frame_presentation_time_length_minus_1 + 1);
         hdr->refresh_frame_flags  = 0;
         if (seq->frame_id_numbers_present_flag)
            av1_f(vlc, idLen); /* display_frame_id */

         hdr->frame_type =
            priv->codec_data.av1.RefFrames[priv->codec_data.av1.uncompressed_header.
               frame_to_show_map_idx].RefFrameType;

         return;
      }

      hdr->frame_type = av1_f(vlc, 2);
      hdr->FrameIsIntra = (hdr->frame_type == AV1_INTRA_ONLY_FRAME ||
            hdr->frame_type == AV1_KEY_FRAME);
      hdr->show_frame = av1_f(vlc, 1);

      if (hdr->show_frame && seq->decoder_model_info_present_flag &&
            !seq->timing_info.equal_picture_interval)
         av1_f(vlc, seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1);

      hdr->showable_frame =
         hdr->show_frame ? (hdr->frame_type != AV1_KEY_FRAME) : av1_f(vlc, 1);

      hdr->error_resilient_mode = (hdr->frame_type == AV1_SWITCH_FRAME ||
          (hdr->frame_type == AV1_KEY_FRAME && hdr->show_frame)) ? 1 : av1_f(vlc, 1);
   }

   if (hdr->frame_type == AV1_KEY_FRAME && hdr->show_frame) {
      for (i = 0; i < AV1_NUM_REF_FRAMES; ++i)
         hdr->RefOrderHint[i] = 0;
   }

   hdr->disable_cdf_update = av1_f(vlc, 1);

   hdr->allow_screen_content_tools =
      (seq->seq_force_screen_content_tools == AV1_SELECT_SCREEN_CONTENT_TOOLS) ?
      av1_f(vlc, 1) : seq->seq_force_screen_content_tools;

   if (hdr->allow_screen_content_tools) {
      if (seq->seq_force_integer_mv == AV1_SELECT_INTEGER_MV)
         hdr->force_integer_mv = av1_f(vlc, 1);
      else
         hdr->force_integer_mv = seq->seq_force_integer_mv;
   } else {
      hdr->force_integer_mv = 0;
   }

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

   hdr->current_frame_id =
      seq->frame_id_numbers_present_flag ? av1_f(vlc, idLen) : 0;

   if (hdr->frame_type == AV1_SWITCH_FRAME)
      hdr->frame_size_override_flag = 1;
   else if (seq->reduced_still_picture_header)
      hdr->frame_size_override_flag = 0;
   else
      hdr->frame_size_override_flag = av1_f(vlc, 1);

   hdr->OrderHint = av1_f(vlc, seq->OrderHintBits);

   if (hdr->FrameIsIntra || hdr->error_resilient_mode)
      hdr->primary_ref_frame = AV1_PRIMARY_REF_NONE;
   else
      hdr->primary_ref_frame = av1_f(vlc, 3);

   if (seq->decoder_model_info_present_flag) {
      bool buffer_removal_time_present_flag = av1_f(vlc, 1);
      if (buffer_removal_time_present_flag) {
         for (i = 0; i <= seq->operating_points_cnt_minus_1; ++i) {
            if (seq->decoder_model_present_for_this_op[i]) {
               unsigned opPtIdc;
               bool inTemporalLayer;
               bool inSpatialLayer;
               opPtIdc = seq->operating_point_idc[i];
               inTemporalLayer =
                  (opPtIdc >> priv->codec_data.av1.ext.temporal_id) & 1;
               inSpatialLayer =
                  (opPtIdc >> (priv->codec_data.av1.ext.spatial_id + 8)) & 1;
               if ((opPtIdc == 0) || (inTemporalLayer && inSpatialLayer))
                  av1_f(vlc, seq->decoder_model_info.
                        buffer_removal_time_length_minus_1 + 1);
            }
         }
      }
   }

   hdr->allow_high_precision_mv = 0;
   hdr->use_ref_frame_mvs = 0;
   hdr->allow_intrabc = 0;

   hdr->refresh_frame_flags = allFrames = (hdr->frame_type == AV1_SWITCH_FRAME ||
         (hdr->frame_type == AV1_KEY_FRAME && hdr->show_frame)) ?
      allFrames : av1_f(vlc, AV1_NUM_REF_FRAMES);

   if (!hdr->FrameIsIntra || hdr->refresh_frame_flags != allFrames) {
      if (hdr->error_resilient_mode && seq->enable_order_hint) {
         for (i = 0; i < AV1_NUM_REF_FRAMES; ++i)
            av1_f(vlc, seq->OrderHintBits);
      }
   }

   if (hdr->FrameIsIntra) {
      frame_size(priv, vlc);
      render_size(priv, vlc);
      if (hdr->allow_screen_content_tools && (hdr->UpscaledWidth == hdr->FrameWidth))
         hdr->allow_intrabc = av1_f(vlc, 1);
   } else {
      bool is_filter_switchable;
      bool frame_refs_short_signaling;

      if (!seq->enable_order_hint) {
         frame_refs_short_signaling = 0;
      } else {
         frame_refs_short_signaling = av1_f(vlc, 1);
         if (frame_refs_short_signaling) {
            hdr->last_frame_idx = av1_f(vlc, 3);
            hdr->gold_frame_idx = av1_f(vlc, 3);
            set_frame_refs(priv, vlc);
         }
      }

      for (i = 0; i < AV1_REFS_PER_FRAME; ++i) {
         if (!frame_refs_short_signaling)
            hdr->ref_frame_idx[i] = av1_f(vlc, 3);
         if (seq->frame_id_numbers_present_flag)
            av1_f(vlc, seq->delta_frame_id_length_minus_2 + 2);
      }

      if (hdr->frame_size_override_flag && !hdr->error_resilient_mode) {
         frame_size_with_refs(priv, vlc);
      } else {
         frame_size(priv, vlc);
         render_size(priv, vlc);
      }

      hdr->allow_high_precision_mv = hdr->force_integer_mv ? 0 : av1_f(vlc, 1);

      is_filter_switchable = av1_f(vlc, 1);
      hdr->interpolation_filter = is_filter_switchable ? 4 /* SWITCHABLE */ : av1_f(vlc, 2);

      hdr->is_motion_mode_switchable = av1_f(vlc, 1);
      hdr->use_ref_frame_mvs =
         (hdr->error_resilient_mode || !seq->enable_ref_frame_mvs) ? 0 : av1_f(vlc, 1);
   }

   hdr->disable_frame_end_update_cdf =
      (seq->reduced_still_picture_header || hdr->disable_cdf_update) ? 1 : av1_f(vlc, 1);

   tile_info(priv, vlc);
   quantization_params(priv, vlc);
   segmentation_params(priv, vlc);
   delta_q_params(priv, vlc);
   delta_lf_params(priv, vlc);

   hdr->CodedLossless = 1;
   for (i = 0; i < AV1_MAX_SEGMENTS; ++i) {
      unsigned qindex = get_qindex(priv, 1, i);
      bool LosslessArray =
         (qindex == 0) && (hdr->qp.DeltaQYDc == 0) &&
         (hdr->qp.DeltaQUAc == 0) && (hdr->qp.DeltaQUDc == 0) &&
         (hdr->qp.DeltaQVAc == 0) && (hdr->qp.DeltaQVDc == 0);

      if (!LosslessArray)
         hdr->CodedLossless = 0;
   }
   hdr->AllLossless = hdr->CodedLossless && (hdr->FrameWidth == hdr->UpscaledWidth);

   loop_filter_params(priv, vlc);
   cdef_params(priv, vlc);
   lr_params(priv, vlc);
   tx_mode(priv, vlc);
   frame_reference_mode(priv, vlc);
   skip_mode_params(priv, vlc);

   if (hdr->FrameIsIntra || hdr->error_resilient_mode || !seq->enable_warped_motion)
      hdr->allow_warped_motion = 0;
   else
      hdr->allow_warped_motion = av1_f(vlc, 1);
   hdr->reduced_tx_set = av1_f(vlc, 1);

   global_motion_params(priv, vlc);

   film_grain_params(priv, vlc);

   priv->picture.av1.picture_parameter.pic_info_fields.frame_type = hdr->frame_type;
   priv->picture.av1.picture_parameter.pic_info_fields.show_frame = hdr->show_frame;
   priv->picture.av1.picture_parameter.pic_info_fields.error_resilient_mode =
      hdr->error_resilient_mode;
   priv->picture.av1.picture_parameter.pic_info_fields.disable_cdf_update =
      hdr->disable_cdf_update;
   priv->picture.av1.picture_parameter.pic_info_fields.allow_screen_content_tools =
      hdr->allow_screen_content_tools;
   priv->picture.av1.picture_parameter.pic_info_fields.force_integer_mv =
      hdr->force_integer_mv;
   priv->picture.av1.picture_parameter.current_frame_id = hdr->current_frame_id;
   priv->picture.av1.picture_parameter.order_hint = hdr->OrderHint;
   priv->picture.av1.picture_parameter.primary_ref_frame = hdr->primary_ref_frame;
   priv->picture.av1.picture_parameter.frame_width = hdr->FrameWidth;
   priv->picture.av1.picture_parameter.frame_height = hdr->FrameHeight;
   priv->picture.av1.picture_parameter.pic_info_fields.use_superres =
      hdr->use_superres;
   priv->picture.av1.picture_parameter.superres_scale_denominator =
      hdr->SuperresDenom;

   for (i = 0; i < AV1_REFS_PER_FRAME; ++i)
      priv->picture.av1.picture_parameter.ref_frame_idx[i] = hdr->ref_frame_idx[i];

   priv->picture.av1.picture_parameter.pic_info_fields.allow_high_precision_mv =
      hdr->allow_high_precision_mv;
   priv->picture.av1.picture_parameter.pic_info_fields.allow_intrabc = hdr->allow_intrabc;
   priv->picture.av1.picture_parameter.pic_info_fields.use_ref_frame_mvs =
      hdr->use_ref_frame_mvs;
   priv->picture.av1.picture_parameter.interp_filter = hdr->interpolation_filter;
   priv->picture.av1.picture_parameter.pic_info_fields.is_motion_mode_switchable =
      hdr->is_motion_mode_switchable;
   priv->picture.av1.picture_parameter.refresh_frame_flags =
      hdr->refresh_frame_flags;
   priv->picture.av1.picture_parameter.pic_info_fields.disable_frame_end_update_cdf =
      hdr->disable_frame_end_update_cdf;

   /* Tile Info */
   priv->picture.av1.picture_parameter.tile_rows = hdr->ti.TileRows;
   priv->picture.av1.picture_parameter.tile_cols = hdr->ti.TileCols;
   priv->picture.av1.picture_parameter.context_update_tile_id =
      hdr->ti.context_update_tile_id;
   for (i = 0; i <AV1_MAX_TILE_ROWS; ++i)
      priv->picture.av1.picture_parameter.tile_row_start_sb[i] =
         hdr->ti.tile_row_start_sb[i];
   for (i = 0; i <AV1_MAX_TILE_COLS; ++i)
      priv->picture.av1.picture_parameter.tile_col_start_sb[i] =
         hdr->ti.tile_col_start_sb[i];

   /* Quantization Params */
   priv->picture.av1.picture_parameter.base_qindex =  hdr->qp.base_q_idx;
   priv->picture.av1.picture_parameter.y_dc_delta_q = hdr->qp.DeltaQYDc;
   priv->picture.av1.picture_parameter.u_dc_delta_q = hdr->qp.DeltaQUDc;
   priv->picture.av1.picture_parameter.u_ac_delta_q = hdr->qp.DeltaQUAc;
   priv->picture.av1.picture_parameter.v_dc_delta_q = hdr->qp.DeltaQVDc;
   priv->picture.av1.picture_parameter.v_ac_delta_q = hdr->qp.DeltaQVAc;
   priv->picture.av1.picture_parameter.qmatrix_fields.qm_y = hdr->qp.qm_y;
   priv->picture.av1.picture_parameter.qmatrix_fields.qm_u = hdr->qp.qm_u;
   priv->picture.av1.picture_parameter.qmatrix_fields.qm_v = hdr->qp.qm_v;

   /* Segmentation Params */
   priv->picture.av1.picture_parameter.seg_info.segment_info_fields.enabled =
      hdr->sp.segmentation_enabled;
   priv->picture.av1.picture_parameter.seg_info.segment_info_fields.update_map =
      hdr->sp.segmentation_update_map;
   priv->picture.av1.picture_parameter.seg_info.segment_info_fields.temporal_update =
      hdr->sp.segmentation_temporal_update;
   for (i = 0; i < AV1_MAX_SEGMENTS; ++i) {
      for (j = 0; j < AV1_SEG_LVL_MAX; ++j)
         priv->picture.av1.picture_parameter.seg_info.feature_data[i][j] =
            hdr->sp.FeatureData[i][j];
      priv->picture.av1.picture_parameter.seg_info.feature_mask[i] =
         hdr->sp.FeatureMask[i];
   }

   /* Delta Q Params */
   priv->picture.av1.picture_parameter.mode_control_fields.delta_q_present_flag =
      hdr->dqp.delta_q_present;
   priv->picture.av1.picture_parameter.mode_control_fields.log2_delta_q_res =
      hdr->dqp.delta_q_res;

   /* Delta LF Params */
   priv->picture.av1.picture_parameter.mode_control_fields.delta_lf_present_flag =
      hdr->dlfp.delta_lf_present;
   priv->picture.av1.picture_parameter.mode_control_fields.log2_delta_lf_res =
      hdr->dlfp.delta_lf_res;
   priv->picture.av1.picture_parameter.mode_control_fields.delta_lf_multi =
      hdr->dlfp.delta_lf_multi;

   /* Loop Filter Params */
   for (i = 0; i < 2; ++i)
      priv->picture.av1.picture_parameter.filter_level[i] = hdr->lfp.loop_filter_level[i];
   priv->picture.av1.picture_parameter.filter_level_u = hdr->lfp.loop_filter_level[2];
   priv->picture.av1.picture_parameter.filter_level_v = hdr->lfp.loop_filter_level[3];
   priv->picture.av1.picture_parameter.loop_filter_info_fields.sharpness_level =
      hdr->lfp.loop_filter_sharpness;
   priv->picture.av1.picture_parameter.loop_filter_info_fields.mode_ref_delta_enabled =
      hdr->lfp.loop_filter_delta_enabled;
   priv->picture.av1.picture_parameter.loop_filter_info_fields.mode_ref_delta_update =
      hdr->lfp.loop_filter_delta_update;
   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i)
      priv->picture.av1.picture_parameter.ref_deltas[i] =
         hdr->lfp.loop_filter_ref_deltas[i];
   for (i = 0; i < 2; ++i)
      priv->picture.av1.picture_parameter.mode_deltas[i] =
         hdr->lfp.loop_filter_mode_deltas[i];

   /* CDEF Params */
   priv->picture.av1.picture_parameter.cdef_damping_minus_3 =
      hdr->cdefp.cdef_damping_minus_3;
   priv->picture.av1.picture_parameter.cdef_bits = hdr->cdefp.cdef_bits;
   for (i = 0; i < AV1_MAX_CDEF_BITS_ARRAY; ++i) {
      priv->picture.av1.picture_parameter.cdef_y_strengths[i] =
         hdr->cdefp.cdef_y_strengths[i];
      priv->picture.av1.picture_parameter.cdef_uv_strengths[i] =
         hdr->cdefp.cdef_uv_strengths[i];
   }

   /* Loop Restoration Params */
   priv->picture.av1.picture_parameter.loop_restoration_fields.yframe_restoration_type =
      hdr->lrp.FrameRestorationType[0];
   priv->picture.av1.picture_parameter.loop_restoration_fields.cbframe_restoration_type =
      hdr->lrp.FrameRestorationType[1];
   priv->picture.av1.picture_parameter.loop_restoration_fields.crframe_restoration_type =
      hdr->lrp.FrameRestorationType[2];
   for (i = 0; i < 3; ++i)
      priv->picture.av1.picture_parameter.lr_unit_size[i] = hdr->lrp.LoopRestorationSize[i];

   priv->picture.av1.picture_parameter.mode_control_fields.tx_mode = hdr->tm.TxMode;
   priv->picture.av1.picture_parameter.mode_control_fields.reference_select =
      (hdr->reference_select == REFERENCE_MODE_SELECT) ? COMPOUND_REFERENCE : SINGLE_REFERENCE;
   priv->picture.av1.picture_parameter.mode_control_fields.skip_mode_present =
      hdr->smp.skip_mode_present;
   priv->picture.av1.picture_parameter.pic_info_fields.allow_warped_motion =
      hdr->allow_warped_motion;
   priv->picture.av1.picture_parameter.mode_control_fields.reduced_tx_set_used =
      hdr->reduced_tx_set;

   /* Global Motion Params */
   for (i = 0; i < 7; ++i) {
      priv->picture.av1.picture_parameter.wm[i].wmtype = hdr->gmp.GmType[i + 1];
      for (j = 0; j < 6; ++j)
         priv->picture.av1.picture_parameter.wm[i].wmmat[j] = hdr->gmp.gm_params[i + 1][j];
   }

   /* Film Grain Params */
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.apply_grain =
      hdr->fgp.apply_grain;
   priv->picture.av1.picture_parameter.film_grain_info.grain_seed =
      hdr->fgp.grain_seed;
   priv->picture.av1.picture_parameter.film_grain_info.num_y_points =
      hdr->fgp.num_y_points;
   for (i = 0; i < AV1_FG_MAX_NUM_Y_POINTS; ++i) {
      priv->picture.av1.picture_parameter.film_grain_info.point_y_value[i] =
         hdr->fgp.point_y_value[i];
      priv->picture.av1.picture_parameter.film_grain_info.point_y_scaling[i] =
         hdr->fgp.point_y_scaling[i];
   }
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      chroma_scaling_from_luma = hdr->fgp.chroma_scaling_from_luma;
   priv->picture.av1.picture_parameter.film_grain_info.num_cb_points =
      hdr->fgp.num_cb_points;
   priv->picture.av1.picture_parameter.film_grain_info.num_cr_points =
      hdr->fgp.num_cr_points;
   for (i = 0; i < AV1_FG_MAX_NUM_CBR_POINTS; ++i) {
      priv->picture.av1.picture_parameter.film_grain_info.point_cb_value[i] =
         hdr->fgp.point_cb_value[i];
      priv->picture.av1.picture_parameter.film_grain_info.point_cb_scaling[i] =
         hdr->fgp.point_cb_scaling[i];
      priv->picture.av1.picture_parameter.film_grain_info.point_cr_value[i] =
         hdr->fgp.point_cr_value[i];
      priv->picture.av1.picture_parameter.film_grain_info.point_cr_scaling[i] =
         hdr->fgp.point_cr_scaling[i];
   }
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      grain_scaling_minus_8 = hdr->fgp.grain_scaling_minus_8;
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      ar_coeff_lag = hdr->fgp.ar_coeff_lag;
   for (i = 0; i < AV1_FG_MAX_NUM_POS_LUMA; ++i)
      priv->picture.av1.picture_parameter.film_grain_info.ar_coeffs_y[i] =
         hdr->fgp.ar_coeffs_y[i];
   for (i = 0; i < AV1_FG_MAX_NUM_POS_CHROMA; ++i) {
      priv->picture.av1.picture_parameter.film_grain_info.ar_coeffs_cb[i] =
         hdr->fgp.ar_coeffs_cb[i];
      priv->picture.av1.picture_parameter.film_grain_info.ar_coeffs_cr[i] =
         hdr->fgp.ar_coeffs_cr[i];
   }
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      ar_coeff_shift_minus_6 = hdr->fgp.ar_coeff_shift_minus_6;
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      grain_scale_shift = hdr->fgp.grain_scale_shift;
   priv->picture.av1.picture_parameter.film_grain_info.cb_mult = hdr->fgp.cb_mult;
   priv->picture.av1.picture_parameter.film_grain_info.cb_luma_mult = hdr->fgp.cb_luma_mult;
   priv->picture.av1.picture_parameter.film_grain_info.cb_offset = hdr->fgp.cb_offset;
   priv->picture.av1.picture_parameter.film_grain_info.cr_mult = hdr->fgp.cr_mult;
   priv->picture.av1.picture_parameter.film_grain_info.cr_luma_mult = hdr->fgp.cr_luma_mult;
   priv->picture.av1.picture_parameter.film_grain_info.cr_offset = hdr->fgp.cr_offset;
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      overlap_flag = hdr->fgp.overlap_flag;
   priv->picture.av1.picture_parameter.film_grain_info.film_grain_info_fields.
      clip_to_restricted_range = hdr->fgp.clip_to_restricted_range;
}

static void parse_tile_hdr(vid_dec_PrivateType *priv, struct vl_vlc *vlc,
      unsigned start_bits_pos, unsigned total_obu_len)
{
   struct tile_info *ti = &(priv->codec_data.av1.uncompressed_header.ti);
   unsigned tg_start, tg_end;
   unsigned NumTiles, tileBits;
   bool tile_start_and_end_present_flag;
   unsigned size[AV1_MAX_NUM_TILES] = { 0 };
   unsigned offset[AV1_MAX_NUM_TILES] = { 0 };
   unsigned frame_header_size, left_size;
   unsigned i, j;

   NumTiles = ti->TileCols * ti->TileRows;
   tile_start_and_end_present_flag = 0;
   if (NumTiles > 1)
      tile_start_and_end_present_flag = av1_f(vlc, 1);

   if (NumTiles == 1 || !tile_start_and_end_present_flag) {
      tg_start = 0;
      tg_end = NumTiles - 1;
   } else {
      tileBits = ti->TileColsLog2 + ti->TileRowsLog2;
      tg_start = av1_f(vlc, tileBits);
      tg_end = av1_f(vlc, tileBits);
   }

   av1_byte_alignment(vlc);

   frame_header_size = (start_bits_pos - vl_vlc_bits_left(vlc)) / 8;
   left_size = total_obu_len - frame_header_size;
   for (i = tg_start; i <= tg_end; ++i) {
      if (i == tg_start) {
         offset[i] = priv->codec_data.av1.bs_obu_td_sz +
            priv->codec_data.av1.bs_obu_seq_sz + frame_header_size +
            ti->TileSizeBytes;
         if (tg_start == tg_end) {
            size[i] = left_size;
            for (j = 0; j < size[i]; ++j) {
               vl_vlc_fillbits(vlc);
               vl_vlc_eatbits(vlc, 8);
            }
            break;
         }
      } else {
         offset[i] = offset[i - 1] + ti->TileSizeBytes + size[i - 1];
         left_size -= ti->TileSizeBytes + size[i - 1];
      }

      if (i != tg_end) {
         size[i] = av1_le(vlc, ti->TileSizeBytes) + 1;
      } else {
         offset[i] = offset[i - 1] + size[i - 1];
         size[i] = left_size;
      }

      for (j = 0; j < size[i]; ++j) {
         vl_vlc_fillbits(vlc);
         vl_vlc_eatbits(vlc, 8);
      }
   }

   for (i = tg_start; i <= tg_end; ++i) {
      priv->picture.av1.slice_parameter.slice_data_offset[i] = offset[i];
      priv->picture.av1.slice_parameter.slice_data_size[i] = size[i];
   }
}

static struct dec_av1_task *dec_av1_NeedTask(vid_dec_PrivateType *priv)
{
   struct pipe_video_buffer templat = {};
   struct dec_av1_task *task;
   struct vl_screen *omx_screen;
   struct pipe_screen *pscreen;

   omx_screen = priv->screen;
   assert(omx_screen);

   pscreen = omx_screen->pscreen;
   assert(pscreen);

   if (!list_is_empty(&priv->codec_data.av1.free_tasks)) {
      task = LIST_ENTRY(struct dec_av1_task,
            priv->codec_data.av1.free_tasks.next, list);
      task->buf_ref_count = 1;
      list_del(&task->list);
      return task;
   }

   task = CALLOC_STRUCT(dec_av1_task);
   if (!task)
      return NULL;

   memset(&templat, 0, sizeof(templat));
   templat.width = priv->codec->width;
   templat.height = priv->codec->height;
   templat.buffer_format = pscreen->get_video_param(
         pscreen,
         PIPE_VIDEO_PROFILE_UNKNOWN,
         PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
         PIPE_VIDEO_CAP_PREFERED_FORMAT
   );
   templat.interlaced = false;

   task->buf = priv->pipe->create_video_buffer(priv->pipe, &templat);
   if (!task->buf) {
      FREE(task);
      return NULL;
   }
   task->buf_ref_count = 1;
   task->is_sef_task = false;

   return task;
}

static void dec_av1_ReleaseTask(vid_dec_PrivateType *priv,
      struct list_head *head)
{
   if (!head || !head->next)
      return;

   list_for_each_entry_safe(struct dec_av1_task, task, head, list) {
      task->buf->destroy(task->buf);
      FREE(task);
   }
}

static void dec_av1_MoveTask(struct list_head *from,
      struct list_head *to)
{
   to->prev->next = from->next;
   from->next->prev = to->prev;
   from->prev->next = to;
   to->prev = from->prev;
   list_inithead(from);
}

static void dec_av1_SortTask(vid_dec_PrivateType *priv)
{
   int i;

   list_for_each_entry_safe(struct dec_av1_task, t,
         &priv->codec_data.av1.finished_tasks, list) {
      bool found = false;
      for (i = 0; i < 8; ++i) {
         if (t->buf == priv->picture.av1.ref[i]) {
            found = true;
            break;
         }
      }
      if (!found && t->buf_ref_count == 0) {
         list_del(&t->list);
         list_addtail(&t->list, &priv->codec_data.av1.free_tasks);
      }
   }
}

static struct dec_av1_task *dec_av1_SearchTask(vid_dec_PrivateType *priv,
      struct list_head *tasks)
{
   unsigned idx =
      priv->codec_data.av1.uncompressed_header.frame_to_show_map_idx;

   list_for_each_entry_safe(struct dec_av1_task, t, tasks, list) {
      if (t->buf == priv->picture.av1.ref[idx])
         return t;
   }

   return NULL;
}

static bool dec_av1_GetStartedTask(vid_dec_PrivateType *priv,
      struct dec_av1_task *task, struct list_head *tasks)
{
   struct dec_av1_task *started_task;

   ++priv->codec_data.av1.que_num;
   list_addtail(&task->list, &priv->codec_data.av1.started_tasks);
   if (priv->codec_data.av1.que_num <= 16)
      return false;

   started_task = LIST_ENTRY(struct dec_av1_task,
      priv->codec_data.av1.started_tasks.next, list);
   list_del(&started_task->list);
   list_addtail(&started_task->list, tasks);
   --priv->codec_data.av1.que_num;

   return true;
}

static void dec_av1_ShowExistingframe(vid_dec_PrivateType *priv)
{
   struct input_buf_private *inp = priv->in_buffers[0]->pInputPortPrivate;
   struct dec_av1_task *task, *existing_task;
   bool fnd;

   task = CALLOC_STRUCT(dec_av1_task);
   if (!task)
      return;

   task->is_sef_task = true;

   mtx_lock(&priv->codec_data.av1.mutex);
   dec_av1_MoveTask(&inp->tasks, &priv->codec_data.av1.finished_tasks);
   dec_av1_SortTask(priv);
   existing_task = dec_av1_SearchTask(priv, &priv->codec_data.av1.started_tasks);
   if (existing_task) {
      ++existing_task->buf_ref_count;
      task->buf = existing_task->buf;
      task->buf_ref = &existing_task->buf;
      task->buf_ref_count = 0;
   } else {
      existing_task = dec_av1_SearchTask(priv, &priv->codec_data.av1.finished_tasks);
      if (existing_task) {
         struct vl_screen *omx_screen;
         struct pipe_screen *pscreen;
         struct pipe_video_buffer templat = {};
         struct pipe_video_buffer *buf;
         struct pipe_box box={};

         omx_screen = priv->screen;
         assert(omx_screen);

         pscreen = omx_screen->pscreen;
         assert(pscreen);

         memset(&templat, 0, sizeof(templat));
         templat.width = priv->codec->width;
         templat.height = priv->codec->height;
         templat.buffer_format = pscreen->get_video_param(
            pscreen,
            PIPE_VIDEO_PROFILE_UNKNOWN,
            PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
            PIPE_VIDEO_CAP_PREFERED_FORMAT
         );
         templat.interlaced = false;
         buf = priv->pipe->create_video_buffer(priv->pipe, &templat);
         if (!buf) {
            FREE(task);
            mtx_unlock(&priv->codec_data.av1.mutex);
            return;
         }

         box.width = priv->codec->width;
         box.height = priv->codec->height;
         box.depth = 1;
         priv->pipe->resource_copy_region(priv->pipe,
               ((struct vl_video_buffer *)buf)->resources[0],
               0, 0, 0, 0,
               ((struct vl_video_buffer *)(existing_task->buf))->resources[0],
               0, &box);
         box.width /= 2;
         box.height/= 2;
         priv->pipe->resource_copy_region(priv->pipe,
               ((struct vl_video_buffer *)buf)->resources[1],
               0, 0, 0, 0,
               ((struct vl_video_buffer *)(existing_task->buf))->resources[1],
               0, &box);
         priv->pipe->flush(priv->pipe, NULL, 0);
         existing_task->buf_ref_count = 0;
         task->buf = buf;
         task->buf_ref_count = 1;
      } else {
         FREE(task);
         mtx_unlock(&priv->codec_data.av1.mutex);
         return;
      }
   }
   dec_av1_SortTask(priv);

   fnd = dec_av1_GetStartedTask(priv, task, &inp->tasks);
   mtx_unlock(&priv->codec_data.av1.mutex);
   if (fnd)
      priv->frame_finished = 1;
}

static struct dec_av1_task *dec_av1_BeginFrame(vid_dec_PrivateType *priv)
{
   struct input_buf_private *inp = priv->in_buffers[0]->pInputPortPrivate;
   struct dec_av1_task *task;

   if (priv->frame_started)
      return NULL;

   if (!priv->codec) {
      struct vl_screen *omx_screen;
      struct pipe_screen *pscreen;
      struct pipe_video_codec templat = {};
      bool supported;

      omx_screen = priv->screen;
      assert(omx_screen);

      pscreen = omx_screen->pscreen;
      assert(pscreen);

      supported = pscreen->get_video_param(pscreen, priv->profile,
            PIPE_VIDEO_ENTRYPOINT_BITSTREAM, PIPE_VIDEO_CAP_SUPPORTED);
      assert(supported && "AV1 is not supported");

      templat.profile = priv->profile;
      templat.entrypoint = PIPE_VIDEO_ENTRYPOINT_BITSTREAM;
      templat.chroma_format = PIPE_VIDEO_CHROMA_FORMAT_420;
      templat.max_references = AV1_NUM_REF_FRAMES;
      templat.expect_chunked_decode = true;
      omx_base_video_PortType *port;
      port = (omx_base_video_PortType *)priv->ports[OMX_BASE_FILTER_INPUTPORT_INDEX];
      templat.width = port->sPortParam.format.video.nFrameWidth;
      templat.height = port->sPortParam.format.video.nFrameHeight;

      priv->codec = priv->pipe->create_video_codec(priv->pipe, &templat);
   }

   mtx_lock(&priv->codec_data.av1.mutex);
   dec_av1_MoveTask(&inp->tasks, &priv->codec_data.av1.finished_tasks);
   dec_av1_SortTask(priv);
   mtx_unlock(&priv->codec_data.av1.mutex);

   task = dec_av1_NeedTask(priv);
   if (!task)
      return NULL;

   priv->codec->begin_frame(priv->codec, task->buf, &priv->picture.base);
   priv->frame_started = true;

   return task;
}

static void dec_av1_EndFrame(vid_dec_PrivateType *priv, struct dec_av1_task *task)
{
   struct input_buf_private *inp = priv->in_buffers[0]->pInputPortPrivate;
   unsigned refresh_frame_flags;
   bool fnd;
   unsigned i;

   if (!priv->frame_started || ! task)
      return;

   priv->codec->end_frame(priv->codec, task->buf, &priv->picture.base);
   priv->frame_started = false;

   refresh_frame_flags = priv->codec_data.av1.uncompressed_header.refresh_frame_flags;
   for (i = 0; i < AV1_NUM_REF_FRAMES; ++i) {
      if (refresh_frame_flags & (1 << i)) {
         memcpy(&priv->codec_data.av1.refs[i], &priv->codec_data.av1.uncompressed_header,
            sizeof(struct av1_uncompressed_header_obu));
         priv->picture.av1.ref[i] = task->buf;
         priv->codec_data.av1.RefFrames[i].RefFrameType =
            priv->codec_data.av1.uncompressed_header.frame_type;
         priv->codec_data.av1.RefFrames[i].RefFrameId =
            priv->codec_data.av1.uncompressed_header.current_frame_id;
         priv->codec_data.av1.RefFrames[i].RefUpscaledWidth =
            priv->codec_data.av1.uncompressed_header.UpscaledWidth;
         priv->codec_data.av1.RefFrames[i].RefFrameWidth =
            priv->codec_data.av1.uncompressed_header.FrameWidth;
         priv->codec_data.av1.RefFrames[i].RefFrameHeight =
            priv->codec_data.av1.uncompressed_header.FrameHeight;
         priv->codec_data.av1.RefFrames[i].RefRenderWidth =
            priv->codec_data.av1.uncompressed_header.RenderWidth;
         priv->codec_data.av1.RefFrames[i].RefRenderHeight =
            priv->codec_data.av1.uncompressed_header.RenderHeight;
      }
   }
   if (!priv->picture.av1.picture_parameter.pic_info_fields.show_frame)
       task->no_show_frame = true;

   mtx_lock(&priv->codec_data.av1.mutex);
   fnd = dec_av1_GetStartedTask(priv, task, &priv->codec_data.av1.decode_tasks);
   if (!fnd) {
      mtx_unlock(&priv->codec_data.av1.mutex);
      return;
   }
   if (!priv->codec_data.av1.stacked_frame)
      dec_av1_MoveTask(&priv->codec_data.av1.decode_tasks, &inp->tasks);
   mtx_unlock(&priv->codec_data.av1.mutex);
   priv->frame_finished = 1;
}

static void dec_av1_Decode(vid_dec_PrivateType *priv, struct vl_vlc *vlc,
                           unsigned min_bits_left)
{
   unsigned start_bits_pos = vl_vlc_bits_left(vlc);
   unsigned start_bits = vl_vlc_valid_bits(vlc);
   unsigned start_bytes = start_bits / 8;
   const void *obu_data = vlc->data;
   uint8_t start_buf[8];
   unsigned num_buffers = 0;
   void * const * buffers[4];
   unsigned sizes[4];
   unsigned obu_size = 0;
   unsigned total_obu_len;
   enum av1_obu_type type;
   bool obu_extension_flag;
   bool obu_has_size_field;
   unsigned i;

   for (i = 0; i < start_bytes; ++i)
      start_buf[i] =
         vl_vlc_peekbits(vlc, start_bits) >> ((start_bytes - i - 1) * 8);

   /* obu header */
   av1_f(vlc, 1); /* obu_forbidden_bit */
   type = av1_f(vlc, 4);
   obu_extension_flag = av1_f(vlc, 1);
   obu_has_size_field = av1_f(vlc, 1);
   av1_f(vlc, 1); /* obu_reserved_1bit */
   if (obu_extension_flag) {
      priv->codec_data.av1.ext.temporal_id = av1_f(vlc, 3);
      priv->codec_data.av1.ext.spatial_id = av1_f(vlc, 2);
      av1_f(vlc, 3); /* extension_header_reserved_3bits */
   }

   obu_size = (obu_has_size_field) ? av1_uleb128(vlc) :
              (priv->sizes[0] - (unsigned)obu_extension_flag - 1);
   total_obu_len = (start_bits_pos - vl_vlc_bits_left(vlc)) / 8 + obu_size;

   switch (type) {
   case AV1_OBU_SEQUENCE_HEADER: {
      sequence_header_obu(priv, vlc);
      av1_byte_alignment(vlc);
      priv->codec_data.av1.bs_obu_seq_sz = total_obu_len;
      memcpy(priv->codec_data.av1.bs_obu_seq_buf, start_buf, start_bytes);
      memcpy(priv->codec_data.av1.bs_obu_seq_buf + start_bytes, obu_data,
             total_obu_len - start_bytes);
      break;
   }
   case AV1_OBU_TEMPORAL_DELIMITER:
      av1_byte_alignment(vlc);
      priv->codec_data.av1.bs_obu_td_sz = total_obu_len;
      memcpy(priv->codec_data.av1.bs_obu_td_buf, start_buf, total_obu_len);
      break;
   case AV1_OBU_FRAME_HEADER:
      frame_header_obu(priv, vlc);
      if (priv->codec_data.av1.uncompressed_header.show_existing_frame)
         dec_av1_ShowExistingframe(priv);
      av1_byte_alignment(vlc);
      break;
   case AV1_OBU_FRAME: {
      struct dec_av1_task *task;

      frame_header_obu(priv, vlc);
      av1_byte_alignment(vlc);

      parse_tile_hdr(priv, vlc, start_bits_pos, total_obu_len);
      av1_byte_alignment(vlc);

      task = dec_av1_BeginFrame(priv);
      if (!task)
         return;

      if (priv->codec_data.av1.bs_obu_td_sz) {
         buffers[num_buffers] = (void *)priv->codec_data.av1.bs_obu_td_buf;
         sizes[num_buffers++] = priv->codec_data.av1.bs_obu_td_sz;
         priv->codec_data.av1.bs_obu_td_sz = 0;
      }
      if (priv->codec_data.av1.bs_obu_seq_sz) {
         buffers[num_buffers] = (void *)priv->codec_data.av1.bs_obu_seq_buf;
         sizes[num_buffers++] = priv->codec_data.av1.bs_obu_seq_sz;
         priv->codec_data.av1.bs_obu_seq_sz = 0;
      }
      buffers[num_buffers] = (void *)start_buf;
      sizes[num_buffers++] = start_bytes;
      buffers[num_buffers] = (void *)obu_data;
      sizes[num_buffers++] = total_obu_len - start_bytes;

      priv->codec->decode_bitstream(priv->codec, priv->target,
         &priv->picture.base, num_buffers, (const void * const*)buffers, sizes);

      priv->codec_data.av1.stacked_frame =
            (vl_vlc_bits_left(vlc) > min_bits_left) ? true : false;

      dec_av1_EndFrame(priv, task);
      break;
   }
   default:
      av1_byte_alignment(vlc);
      break;
   }

   return;
}

OMX_ERRORTYPE vid_dec_av1_AllocateInBuffer(omx_base_PortType *port,
             OMX_INOUT OMX_BUFFERHEADERTYPE **buf, OMX_IN OMX_U32 idx,
             OMX_IN OMX_PTR private, OMX_IN OMX_U32 size)
{
   struct input_buf_private *inp;
   OMX_ERRORTYPE r;

   r = base_port_AllocateBuffer(port, buf, idx, private, size);
   if (r)
      return r;

   inp = (*buf)->pInputPortPrivate = CALLOC_STRUCT(input_buf_private);
   if (!inp) {
      base_port_FreeBuffer(port, idx, *buf);
      return OMX_ErrorInsufficientResources;
   }

   list_inithead(&inp->tasks);

   return OMX_ErrorNone;
}

OMX_ERRORTYPE vid_dec_av1_UseInBuffer(omx_base_PortType *port,
             OMX_BUFFERHEADERTYPE **buf, OMX_U32 idx,
             OMX_PTR private, OMX_U32 size, OMX_U8 *mem)
{
   struct input_buf_private *inp;
   OMX_ERRORTYPE r;

   r = base_port_UseBuffer(port, buf, idx, private, size, mem);
   if (r)
      return r;

   inp = (*buf)->pInputPortPrivate = CALLOC_STRUCT(input_buf_private);
   if (!inp) {
      base_port_FreeBuffer(port, idx, *buf);
      return OMX_ErrorInsufficientResources;
   }

   list_inithead(&inp->tasks);

   return OMX_ErrorNone;
}

void vid_dec_av1_FreeInputPortPrivate(vid_dec_PrivateType *priv,
                                      OMX_BUFFERHEADERTYPE *buf)
{
   struct input_buf_private *inp = buf->pInputPortPrivate;

   if (!inp || !inp->tasks.next)
      return;

   list_for_each_entry_safe(struct dec_av1_task, task, &inp->tasks, list) {
      task->buf->destroy(task->buf);
      FREE(task);
   }
}

void vid_dec_av1_ReleaseTasks(vid_dec_PrivateType *priv)
{
   dec_av1_ReleaseTask(priv, &priv->codec_data.av1.free_tasks);
   dec_av1_ReleaseTask(priv, &priv->codec_data.av1.started_tasks);
   dec_av1_ReleaseTask(priv, &priv->codec_data.av1.decode_tasks);
   dec_av1_ReleaseTask(priv, &priv->codec_data.av1.finished_tasks);
   mtx_destroy(&priv->codec_data.av1.mutex);
}

void vid_dec_av1_FrameDecoded(OMX_COMPONENTTYPE *comp,
                              OMX_BUFFERHEADERTYPE* input,
                              OMX_BUFFERHEADERTYPE* output)
{
   vid_dec_PrivateType *priv = comp->pComponentPrivate;
   bool eos = !!(input->nFlags & OMX_BUFFERFLAG_EOS);
   struct input_buf_private *inp = input->pInputPortPrivate;
   struct dec_av1_task *task;
   bool stacked = false;

   mtx_lock(&priv->codec_data.av1.mutex);
   if (list_length(&inp->tasks) > 1)
      stacked = true;

   if (list_is_empty(&inp->tasks)) {
      task = LIST_ENTRY(struct dec_av1_task,
            priv->codec_data.av1.started_tasks.next, list);
      list_del(&task->list);
      list_addtail(&task->list, &inp->tasks);
      --priv->codec_data.av1.que_num;
   }

   task = LIST_ENTRY(struct dec_av1_task, inp->tasks.next, list);

   if (!task->no_show_frame) {
      vid_dec_FillOutput(priv, task->buf, output);
      output->nFilledLen = output->nAllocLen;
      output->nTimeStamp = input->nTimeStamp;
   } else {
      task->no_show_frame = false;
      output->nFilledLen = 0;
   }

   if (task->is_sef_task) {
      if (task->buf_ref_count == 0) {
         struct dec_av1_task *t = container_of(task->buf_ref, struct dec_av1_task, buf);
         list_del(&task->list);
         t->buf_ref_count--;
         list_del(&t->list);
         list_addtail(&t->list, &priv->codec_data.av1.finished_tasks);
      } else if (task->buf_ref_count == 1) {
         list_del(&task->list);
         task->buf->destroy(task->buf);
         task->buf_ref_count--;
      }
      FREE(task);
   } else {
      if (task->buf_ref_count == 1) {
         list_del(&task->list);
         list_addtail(&task->list, &priv->codec_data.av1.finished_tasks);
         task->buf_ref_count--;
      } else if (task->buf_ref_count == 2) {
         list_del(&task->list);
         task->buf_ref_count--;
         list_addtail(&task->list, &priv->codec_data.av1.finished_tasks);
      }
   }

   if (eos && input->pInputPortPrivate) {
      if (!priv->codec_data.av1.que_num)
         input->nFilledLen = 0;
      else
         vid_dec_av1_FreeInputPortPrivate(priv, input);
   }
   else {
      if (!stacked)
         input->nFilledLen = 0;
   }
   mtx_unlock(&priv->codec_data.av1.mutex);
}

void vid_dec_av1_Init(vid_dec_PrivateType *priv)
{
   priv->picture.base.profile = PIPE_VIDEO_PROFILE_AV1_MAIN;
   priv->Decode = dec_av1_Decode;
   list_inithead(&priv->codec_data.av1.free_tasks);
   list_inithead(&priv->codec_data.av1.started_tasks);
   list_inithead(&priv->codec_data.av1.decode_tasks);
   list_inithead(&priv->codec_data.av1.finished_tasks);
   (void)mtx_init(&priv->codec_data.av1.mutex, mtx_plain);
}
