/*
 *  Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#include "common_video/h265/h265_bitstream_parser.h"

#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <optional>
#include <span>
#include <vector>

#include "common_video/h265/h265_common.h"
#include "common_video/h265/h265_pps_parser.h"
#include "common_video/h265/h265_sps_parser.h"
#include "common_video/h265/h265_vps_parser.h"
#include "rtc_base/bitstream_reader.h"
#include "rtc_base/logging.h"

#define IN_RANGE_OR_RETURN(val, min, max)                                     \
  do {                                                                        \
    if (!slice_reader.Ok() || (val) < (min) || (val) > (max)) {               \
      RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " #val \
                             " to be"                                         \
                          << " in range [" << (min) << ":" << (max) << "]"    \
                          << " found " << (val) << " instead";                \
      return kInvalidStream;                                                  \
    }                                                                         \
  } while (0)

#define IN_RANGE_OR_RETURN_NULL(val, min, max)                                \
  do {                                                                        \
    if (!slice_reader.Ok() || (val) < (min) || (val) > (max)) {               \
      RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " #val \
                             " to be"                                         \
                          << " in range [" << (min) << ":" << (max) << "]"    \
                          << " found " << (val) << " instead";                \
      return std::nullopt;                                                    \
    }                                                                         \
  } while (0)

#define IN_RANGE_OR_RETURN_VOID(val, min, max)                                \
  do {                                                                        \
    if (!slice_reader.Ok() || (val) < (min) || (val) > (max)) {               \
      RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " #val \
                             " to be"                                         \
                          << " in range [" << (min) << ":" << (max) << "]"    \
                          << " found " << (val) << " instead";                \
      return;                                                                 \
    }                                                                         \
  } while (0)

#define TRUE_OR_RETURN(a)                                                \
  do {                                                                   \
    if (!slice_reader.Ok() || !(a)) {                                    \
      RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " \
                          << #a;                                         \
      return kInvalidStream;                                             \
    }                                                                    \
  } while (0)

namespace webrtc {

namespace {

constexpr int kMaxAbsQpDeltaValue = 51;
constexpr int kMinQpValue = 0;
constexpr int kMaxQpValue = 51;
constexpr int kMaxRefIdxActive = 15;

}  // namespace

H265BitstreamParser::H265BitstreamParser() = default;
H265BitstreamParser::~H265BitstreamParser() = default;

// General note: this is based off the 08/2021 version of the H.265 standard,
// section 7.3.6.1. You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
H265BitstreamParser::Result H265BitstreamParser::ParseNonParameterSetNalu(
    std::span<const uint8_t> source,
    uint8_t nalu_type) {
  last_slice_qp_delta_ = std::nullopt;
  last_slice_pps_id_ = std::nullopt;
  const std::vector<uint8_t> slice_rbsp = H265::ParseRbsp(source);
  if (slice_rbsp.size() < H265::kNaluHeaderSize)
    return kInvalidStream;

  BitstreamReader slice_reader(slice_rbsp);
  slice_reader.ConsumeBits(H265::kNaluHeaderSize * 8);

  // first_slice_segment_in_pic_flag: u(1)
  bool first_slice_segment_in_pic_flag = slice_reader.Read<bool>();
  bool irap_pic = (H265::NaluType::kBlaWLp <= nalu_type &&
                   nalu_type <= H265::NaluType::kRsvIrapVcl23);
  if (irap_pic) {
    // no_output_of_prior_pics_flag: u(1)
    slice_reader.ConsumeBits(1);
  }
  // slice_pic_parameter_set_id: ue(v)
  uint32_t pps_id = slice_reader.ReadExponentialGolomb();
  IN_RANGE_OR_RETURN(pps_id, 0, 63);
  const H265PpsParser::PpsState* pps = GetPPS(pps_id);
  TRUE_OR_RETURN(pps);
  const H265SpsParser::SpsState* sps = GetSPS(pps->sps_id);
  TRUE_OR_RETURN(sps);
  bool dependent_slice_segment_flag = 0;
  if (!first_slice_segment_in_pic_flag) {
    if (pps->dependent_slice_segments_enabled_flag) {
      // dependent_slice_segment_flag: u(1)
      dependent_slice_segment_flag = slice_reader.Read<bool>();
    }

    // slice_segment_address: u(v)
    int32_t log2_ctb_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3 +
                              sps->log2_diff_max_min_luma_coding_block_size;
    uint32_t ctb_size_y = 1 << log2_ctb_size_y;
    uint32_t pic_width_in_ctbs_y = sps->pic_width_in_luma_samples / ctb_size_y;
    if (sps->pic_width_in_luma_samples % ctb_size_y)
      pic_width_in_ctbs_y++;

    uint32_t pic_height_in_ctbs_y =
        sps->pic_height_in_luma_samples / ctb_size_y;
    if (sps->pic_height_in_luma_samples % ctb_size_y)
      pic_height_in_ctbs_y++;

    uint32_t slice_segment_address_bits =
        H265::Log2Ceiling(pic_height_in_ctbs_y * pic_width_in_ctbs_y);
    TRUE_OR_RETURN(slice_segment_address_bits !=
                   std::numeric_limits<uint32_t>::max());
    slice_reader.ConsumeBits(slice_segment_address_bits);
  }

  if (dependent_slice_segment_flag == 0) {
    for (uint32_t i = 0; i < pps->num_extra_slice_header_bits; i++) {
      // slice_reserved_flag: u(1)
      slice_reader.ConsumeBits(1);
    }
    // slice_type: ue(v)
    uint32_t slice_type = slice_reader.ReadExponentialGolomb();
    IN_RANGE_OR_RETURN(slice_type, 0, 2);
    if (pps->output_flag_present_flag) {
      // pic_output_flag: u(1)
      slice_reader.ConsumeBits(1);
    }
    if (sps->separate_colour_plane_flag) {
      // colour_plane_id: u(2)
      slice_reader.ConsumeBits(2);
    }
    uint32_t num_long_term_sps = 0;
    uint32_t num_long_term_pics = 0;
    std::vector<bool> used_by_curr_pic_lt_flag;
    bool short_term_ref_pic_set_sps_flag = false;
    uint32_t short_term_ref_pic_set_idx = 0;
    H265SpsParser::ShortTermRefPicSet short_term_ref_pic_set;
    bool slice_temporal_mvp_enabled_flag = 0;
    if (nalu_type != H265::NaluType::kIdrWRadl &&
        nalu_type != H265::NaluType::kIdrNLp) {
      // slice_pic_order_cnt_lsb: u(v)
      uint32_t slice_pic_order_cnt_lsb_bits =
          sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
      slice_reader.ConsumeBits(slice_pic_order_cnt_lsb_bits);
      // short_term_ref_pic_set_sps_flag: u(1)
      short_term_ref_pic_set_sps_flag = slice_reader.Read<bool>();
      if (!short_term_ref_pic_set_sps_flag) {
        std::optional<H265SpsParser::ShortTermRefPicSet> ref_pic_set =
            H265SpsParser::ParseShortTermRefPicSet(
                sps->num_short_term_ref_pic_sets,
                sps->num_short_term_ref_pic_sets, sps->short_term_ref_pic_set,
                sps->sps_max_dec_pic_buffering_minus1
                    [sps->sps_max_sub_layers_minus1],
                slice_reader);
        TRUE_OR_RETURN(ref_pic_set);
        short_term_ref_pic_set = *ref_pic_set;

      } else if (sps->num_short_term_ref_pic_sets > 1) {
        // short_term_ref_pic_set_idx: u(v)
        uint32_t short_term_ref_pic_set_idx_bits =
            H265::Log2Ceiling(sps->num_short_term_ref_pic_sets);
        if ((1 << short_term_ref_pic_set_idx_bits) <
            sps->num_short_term_ref_pic_sets) {
          short_term_ref_pic_set_idx_bits++;
        }
        if (short_term_ref_pic_set_idx_bits > 0) {
          short_term_ref_pic_set_idx =
              slice_reader.ReadBits(short_term_ref_pic_set_idx_bits);
          IN_RANGE_OR_RETURN(short_term_ref_pic_set_idx, 0,
                             sps->num_short_term_ref_pic_sets - 1);
        }
      }
      if (sps->long_term_ref_pics_present_flag) {
        if (sps->num_long_term_ref_pics_sps > 0) {
          // num_long_term_sps: ue(v)
          num_long_term_sps = slice_reader.ReadExponentialGolomb();
          IN_RANGE_OR_RETURN(num_long_term_sps, 0,
                             sps->num_long_term_ref_pics_sps);
        }
        // num_long_term_pics: ue(v)
        num_long_term_pics = slice_reader.ReadExponentialGolomb();
        IN_RANGE_OR_RETURN(num_long_term_pics, 0,
                           kMaxLongTermRefPicSets - num_long_term_sps);
        used_by_curr_pic_lt_flag.resize(num_long_term_sps + num_long_term_pics,
                                        0);
        for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) {
          if (i < num_long_term_sps) {
            uint32_t lt_idx_sps = 0;
            if (sps->num_long_term_ref_pics_sps > 1) {
              // lt_idx_sps: u(v)
              uint32_t lt_idx_sps_bits =
                  H265::Log2Ceiling(sps->num_long_term_ref_pics_sps);
              lt_idx_sps = slice_reader.ReadBits(lt_idx_sps_bits);
              IN_RANGE_OR_RETURN(lt_idx_sps, 0,
                                 sps->num_long_term_ref_pics_sps - 1);
            }
            used_by_curr_pic_lt_flag[i] =
                sps->used_by_curr_pic_lt_sps_flag[lt_idx_sps];
          } else {
            // poc_lsb_lt: u(v)
            uint32_t poc_lsb_lt_bits =
                sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
            slice_reader.ConsumeBits(poc_lsb_lt_bits);
            // used_by_curr_pic_lt_flag: u(1)
            used_by_curr_pic_lt_flag[i] = slice_reader.Read<bool>();
          }
          // delta_poc_msb_present_flag: u(1)
          bool delta_poc_msb_present_flag = slice_reader.Read<bool>();
          if (delta_poc_msb_present_flag) {
            // delta_poc_msb_cycle_lt: ue(v)
            int delta_poc_msb_cycle_lt = slice_reader.ReadExponentialGolomb();
            IN_RANGE_OR_RETURN(
                delta_poc_msb_cycle_lt, 0,
                std::pow(2, 32 - sps->log2_max_pic_order_cnt_lsb_minus4 - 4));
          }
        }
      }
      if (sps->sps_temporal_mvp_enabled_flag) {
        // slice_temporal_mvp_enabled_flag: u(1)
        slice_temporal_mvp_enabled_flag = slice_reader.Read<bool>();
      }
    }

    if (sps->sample_adaptive_offset_enabled_flag) {
      // slice_sao_luma_flag: u(1)
      slice_reader.ConsumeBits(1);
      uint32_t chroma_array_type =
          sps->separate_colour_plane_flag == 0 ? sps->chroma_format_idc : 0;
      if (chroma_array_type != 0) {
        // slice_sao_chroma_flag: u(1)
        slice_reader.ConsumeBits(1);
      }
    }

    if (slice_type == H265::SliceType::kP ||
        slice_type == H265::SliceType::kB) {
      // num_ref_idx_active_override_flag: u(1)
      bool num_ref_idx_active_override_flag = slice_reader.Read<bool>();
      uint32_t num_ref_idx_l0_active_minus1 =
          pps->num_ref_idx_l0_default_active_minus1;
      uint32_t num_ref_idx_l1_active_minus1 =
          pps->num_ref_idx_l1_default_active_minus1;
      if (num_ref_idx_active_override_flag) {
        // num_ref_idx_l0_active_minus1: ue(v)
        num_ref_idx_l0_active_minus1 = slice_reader.ReadExponentialGolomb();
        IN_RANGE_OR_RETURN(num_ref_idx_l0_active_minus1, 0,
                           kMaxRefIdxActive - 1);
        if (slice_type == H265::SliceType::kB) {
          // num_ref_idx_l1_active_minus1: ue(v)
          num_ref_idx_l1_active_minus1 = slice_reader.ReadExponentialGolomb();
          IN_RANGE_OR_RETURN(num_ref_idx_l1_active_minus1, 0,
                             kMaxRefIdxActive - 1);
        }
      }

      uint32_t num_pic_total_curr = 0;
      uint32_t curr_rps_idx = 0;
      if (short_term_ref_pic_set_sps_flag) {
        curr_rps_idx = short_term_ref_pic_set_idx;
      } else {
        curr_rps_idx = sps->num_short_term_ref_pic_sets;
      }

      const H265SpsParser::ShortTermRefPicSet* ref_pic_set;
      if (curr_rps_idx < sps->short_term_ref_pic_set.size()) {
        ref_pic_set = &(sps->short_term_ref_pic_set[curr_rps_idx]);
      } else {
        ref_pic_set = &short_term_ref_pic_set;
      }

      // Equation 7-57
      IN_RANGE_OR_RETURN(ref_pic_set->num_negative_pics, 0,
                         kMaxShortTermRefPicSets);
      IN_RANGE_OR_RETURN(ref_pic_set->num_positive_pics, 0,
                         kMaxShortTermRefPicSets);
      for (uint32_t i = 0; i < ref_pic_set->num_negative_pics; i++) {
        if (ref_pic_set->used_by_curr_pic_s0[i]) {
          num_pic_total_curr++;
        }
      }
      for (uint32_t i = 0; i < ref_pic_set->num_positive_pics; i++) {
        if (ref_pic_set->used_by_curr_pic_s1[i]) {
          num_pic_total_curr++;
        }
      }
      for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) {
        if (used_by_curr_pic_lt_flag[i]) {
          num_pic_total_curr++;
        }
      }

      if (pps->lists_modification_present_flag && num_pic_total_curr > 1) {
        // ref_pic_lists_modification()
        uint32_t list_entry_bits = H265::Log2Ceiling(num_pic_total_curr);
        if ((1 << list_entry_bits) < num_pic_total_curr) {
          list_entry_bits++;
        }
        // ref_pic_list_modification_flag_l0: u(1)
        bool ref_pic_list_modification_flag_l0 = slice_reader.Read<bool>();
        if (ref_pic_list_modification_flag_l0) {
          for (uint32_t i = 0; i < num_ref_idx_l0_active_minus1; i++) {
            // list_entry_l0: u(v)
            slice_reader.ConsumeBits(list_entry_bits);
          }
        }
        if (slice_type == H265::SliceType::kB) {
          // ref_pic_list_modification_flag_l1: u(1)
          bool ref_pic_list_modification_flag_l1 = slice_reader.Read<bool>();
          if (ref_pic_list_modification_flag_l1) {
            for (uint32_t i = 0; i < num_ref_idx_l1_active_minus1; i++) {
              // list_entry_l1: u(v)
              slice_reader.ConsumeBits(list_entry_bits);
            }
          }
        }
      }
      if (slice_type == H265::SliceType::kB) {
        // mvd_l1_zero_flag: u(1)
        slice_reader.ConsumeBits(1);
      }
      if (pps->cabac_init_present_flag) {
        // cabac_init_flag: u(1)
        slice_reader.ConsumeBits(1);
      }
      if (slice_temporal_mvp_enabled_flag) {
        bool collocated_from_l0_flag = false;
        if (slice_type == H265::SliceType::kB) {
          // collocated_from_l0_flag: u(1)
          collocated_from_l0_flag = slice_reader.Read<bool>();
        }
        if ((collocated_from_l0_flag && num_ref_idx_l0_active_minus1 > 0) ||
            (!collocated_from_l0_flag && num_ref_idx_l1_active_minus1 > 0)) {
          // collocated_ref_idx: ue(v)
          uint32_t collocated_ref_idx = slice_reader.ReadExponentialGolomb();
          if ((slice_type == H265::SliceType::kP ||
               slice_type == H265::SliceType::kB) &&
              collocated_from_l0_flag) {
            IN_RANGE_OR_RETURN(collocated_ref_idx, 0,
                               num_ref_idx_l0_active_minus1);
          }
          if (slice_type == H265::SliceType::kB && !collocated_from_l0_flag) {
            IN_RANGE_OR_RETURN(collocated_ref_idx, 0,
                               num_ref_idx_l1_active_minus1);
          }
        }
      }

      // pred_weight_table()
      if ((pps->weighted_pred_flag && slice_type == H265::SliceType::kP) ||
          (pps->weighted_bipred_flag && slice_type == H265::SliceType::kB)) {
        uint32_t luma_log2_weight_denom = slice_reader.ReadExponentialGolomb();
        IN_RANGE_OR_RETURN(luma_log2_weight_denom, 0, 7);
        uint32_t chroma_array_type =
            sps->separate_colour_plane_flag == 0 ? sps->chroma_format_idc : 0;
        int32_t chroma_log2_weight_denom = luma_log2_weight_denom;
        // wp_offset_half_range_c and wp_offset_half_range_y depends on
        // sps.high_precision_offsets_enable_flag. Since range extension is not
        // supported, so for now below two are fixed to 128 instead of 1 <<
        // (sps.bit_depth_luma|chroma_minus8 + 7).
        int32_t wp_offset_half_range_c = (1 << 7);
        int32_t wp_offset_half_range_y = (1 << 7);
        if (chroma_array_type != 0) {
          // delta_chroma_log2_weight_denom: se(v)
          int32_t delta_chroma_log2_weight_denom =
              slice_reader.ReadSignedExponentialGolomb();
          IN_RANGE_OR_RETURN(delta_chroma_log2_weight_denom, -7, 7);
          chroma_log2_weight_denom += delta_chroma_log2_weight_denom;
        }
        IN_RANGE_OR_RETURN(chroma_log2_weight_denom, 0, 7);

        bool luma_weight_flag_l0[kMaxRefIdxActive] = {};
        bool chroma_weight_flag_l0[kMaxRefIdxActive] = {};
        int32_t delta_chroma_weight_l0[kMaxRefIdxActive][2] = {};
        int32_t luma_offset_l0[kMaxRefIdxActive] = {};
        int32_t delta_chroma_offset_l0[kMaxRefIdxActive][2] = {};
        for (uint32_t i = 0; i <= num_ref_idx_l0_active_minus1; i++) {
          // luma_weight_l0_flag: u(1). By syntax this should conditionally
          // check if the POC or layer ID of the reference picture is different,
          // but we don't support encoding referencing different layers in the
          // same AU. Skip the check for now.
          luma_weight_flag_l0[i] = slice_reader.Read<bool>();
        }
        if (chroma_array_type != 0) {
          for (uint32_t i = 0; i <= num_ref_idx_l0_active_minus1; i++) {
            // chroma_weight_l0_flag: u(1)
            chroma_weight_flag_l0[i] = slice_reader.Read<bool>();
          }
        }
        for (uint32_t i = 0; i <= num_ref_idx_l0_active_minus1; i++) {
          if (luma_weight_flag_l0[i]) {
            int32_t delta_luma_weight_l0[kMaxRefIdxActive] = {};
            delta_luma_weight_l0[i] =
                slice_reader.ReadSignedExponentialGolomb();
            IN_RANGE_OR_RETURN(delta_luma_weight_l0[i], -128, 127);
            luma_offset_l0[i] = slice_reader.ReadSignedExponentialGolomb();
            IN_RANGE_OR_RETURN(luma_offset_l0[i], -wp_offset_half_range_y,
                               wp_offset_half_range_y - 1);
          }
          if (chroma_weight_flag_l0[i]) {
            for (uint32_t j = 0; j < 2; j++) {
              delta_chroma_weight_l0[i][j] =
                  slice_reader.ReadSignedExponentialGolomb();
              IN_RANGE_OR_RETURN(delta_chroma_weight_l0[i][j], -128, 127);
              delta_chroma_offset_l0[i][j] =
                  slice_reader.ReadSignedExponentialGolomb();
              IN_RANGE_OR_RETURN(delta_chroma_offset_l0[i][j],
                                 -4 * wp_offset_half_range_c,
                                 4 * wp_offset_half_range_c - 1);
            }
          }
        }
        if (slice_type == H265::SliceType::kB) {
          bool luma_weight_flag_l1[kMaxRefIdxActive] = {};
          bool chroma_weight_flag_l1[kMaxRefIdxActive] = {};
          int32_t delta_chroma_weight_l1[kMaxRefIdxActive][2] = {};
          int32_t luma_offset_l1[kMaxRefIdxActive] = {};
          int32_t delta_chroma_offset_l1[kMaxRefIdxActive][2] = {};
          for (uint32_t i = 0; i < num_ref_idx_l1_active_minus1; i++) {
            luma_weight_flag_l1[i] = slice_reader.Read<bool>();
          }
          if (chroma_array_type != 0) {
            for (uint32_t i = 0; i <= num_ref_idx_l1_active_minus1; i++) {
              chroma_weight_flag_l1[i] = slice_reader.Read<bool>();
            }
          }
          for (uint32_t i = 0; i <= num_ref_idx_l1_active_minus1; i++) {
            if (luma_weight_flag_l1[i]) {
              int32_t delta_luma_weight_l1[kMaxRefIdxActive] = {};
              delta_luma_weight_l1[i] =
                  slice_reader.ReadSignedExponentialGolomb();
              IN_RANGE_OR_RETURN(delta_luma_weight_l1[i], -128, 127);
              luma_offset_l1[i] = slice_reader.ReadSignedExponentialGolomb();
              IN_RANGE_OR_RETURN(luma_offset_l1[i], -wp_offset_half_range_y,
                                 wp_offset_half_range_y - 1);
            }
            if (chroma_weight_flag_l1[i]) {
              for (uint32_t j = 0; j < 2; j++) {
                delta_chroma_weight_l1[i][j] =
                    slice_reader.ReadSignedExponentialGolomb();
                IN_RANGE_OR_RETURN(delta_chroma_weight_l1[i][j], -128, 127);
                delta_chroma_offset_l1[i][j] =
                    slice_reader.ReadSignedExponentialGolomb();
                IN_RANGE_OR_RETURN(delta_chroma_offset_l1[i][j],
                                   -4 * wp_offset_half_range_c,
                                   4 * wp_offset_half_range_c - 1);
              }
            }
          }
        }
      }

      // five_minus_max_num_merge_cand: ue(v)
      uint32_t five_minus_max_num_merge_cand =
          slice_reader.ReadExponentialGolomb();
      IN_RANGE_OR_RETURN(5 - five_minus_max_num_merge_cand, 1, 5);
    }
  }

  // slice_qp_delta: se(v)
  int32_t last_slice_qp_delta = slice_reader.ReadSignedExponentialGolomb();
  if (!slice_reader.Ok() || (abs(last_slice_qp_delta) > kMaxAbsQpDeltaValue)) {
    // Something has gone wrong, and the parsed value is invalid.
    RTC_LOG(LS_ERROR) << "Parsed QP value out of range.";
    return kInvalidStream;
  }
  // 7-54 in H265 spec.
  IN_RANGE_OR_RETURN(26 + pps->init_qp_minus26 + last_slice_qp_delta,
                     -pps->qp_bd_offset_y, 51);

  last_slice_qp_delta_ = last_slice_qp_delta;
  last_slice_pps_id_ = pps_id;
  if (!slice_reader.Ok()) {
    return kInvalidStream;
  }

  return kOk;
}

const H265PpsParser::PpsState* H265BitstreamParser::GetPPS(uint32_t id) const {
  auto it = pps_.find(id);
  if (it == pps_.end()) {
    RTC_LOG(LS_WARNING) << "Requested a nonexistent PPS id " << id;
    return nullptr;
  }

  return &it->second;
}

const H265SpsParser::SpsState* H265BitstreamParser::GetSPS(uint32_t id) const {
  auto it = sps_.find(id);
  if (it == sps_.end()) {
    RTC_LOG(LS_WARNING) << "Requested a nonexistent SPS id " << id;
    return nullptr;
  }

  return &it->second;
}

void H265BitstreamParser::ParseSlice(std::span<const uint8_t> slice) {
  if (slice.empty()) {
    RTC_LOG(LS_WARNING) << "Empty slice in H265 bitstream.";
    return;
  }
  H265::NaluType nalu_type = H265::ParseNaluType(slice[0]);
  switch (nalu_type) {
    case H265::NaluType::kVps: {
      std::optional<H265VpsParser::VpsState> vps_state;
      if (slice.size() >= H265::kNaluHeaderSize) {
        vps_state =
            H265VpsParser::ParseVps(slice.subspan(H265::kNaluHeaderSize));
      }

      if (!vps_state) {
        RTC_LOG(LS_WARNING) << "Unable to parse VPS from H265 bitstream.";
      } else {
        vps_[vps_state->id] = *vps_state;
      }
      break;
    }
    case H265::NaluType::kSps: {
      std::optional<H265SpsParser::SpsState> sps_state;
      if (slice.size() >= H265::kNaluHeaderSize) {
        sps_state =
            H265SpsParser::ParseSps(slice.subspan(H265::kNaluHeaderSize));
      }
      if (!sps_state) {
        RTC_LOG(LS_WARNING) << "Unable to parse SPS from H265 bitstream.";
      } else {
        sps_[sps_state->sps_id] = *sps_state;
      }
      break;
    }
    case H265::NaluType::kPps: {
      std::optional<H265PpsParser::PpsState> pps_state;
      if (slice.size() >= H265::kNaluHeaderSize) {
        std::vector<uint8_t> unpacked_buffer =
            H265::ParseRbsp(slice.subspan(H265::kNaluHeaderSize));
        BitstreamReader slice_reader(unpacked_buffer);
        // pic_parameter_set_id: ue(v)
        uint32_t pps_id = slice_reader.ReadExponentialGolomb();
        IN_RANGE_OR_RETURN_VOID(pps_id, 0, 63);
        // seq_parameter_set_id: ue(v)
        uint32_t sps_id = slice_reader.ReadExponentialGolomb();
        IN_RANGE_OR_RETURN_VOID(sps_id, 0, 15);
        const H265SpsParser::SpsState* sps = GetSPS(sps_id);
        pps_state =
            H265PpsParser::ParsePps(slice.subspan(H265::kNaluHeaderSize), sps);
      }
      if (!pps_state) {
        RTC_LOG(LS_WARNING) << "Unable to parse PPS from H265 bitstream.";
      } else {
        pps_[pps_state->pps_id] = *pps_state;
      }
      break;
    }
    case H265::NaluType::kAud:
    case H265::NaluType::kPrefixSei:
    case H265::NaluType::kSuffixSei:
    case H265::NaluType::kAp:
    case H265::NaluType::kFu:
      break;
    default:
      Result res = ParseNonParameterSetNalu(slice, nalu_type);
      if (res != kOk) {
        RTC_LOG(LS_INFO) << "Failed to parse bitstream. Error: " << res;
      }
      break;
  }
}

std::optional<uint32_t>
H265BitstreamParser::ParsePpsIdFromSliceSegmentLayerRbsp(
    std::span<const uint8_t> data,
    uint8_t nalu_type) {
  std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data);
  BitstreamReader slice_reader(unpacked_buffer);

  // first_slice_segment_in_pic_flag: u(1)
  slice_reader.ConsumeBits(1);
  if (!slice_reader.Ok()) {
    return std::nullopt;
  }

  if (nalu_type >= H265::NaluType::kBlaWLp &&
      nalu_type <= H265::NaluType::kRsvIrapVcl23) {
    // no_output_of_prior_pics_flag: u(1)
    slice_reader.ConsumeBits(1);
  }

  // slice_pic_parameter_set_id: ue(v)
  uint32_t slice_pic_parameter_set_id = slice_reader.ReadExponentialGolomb();
  IN_RANGE_OR_RETURN_NULL(slice_pic_parameter_set_id, 0, 63);
  if (!slice_reader.Ok()) {
    return std::nullopt;
  }

  return slice_pic_parameter_set_id;
}

std::optional<bool> H265BitstreamParser::IsFirstSliceSegmentInPic(
    std::span<const uint8_t> data) {
  std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data);
  BitstreamReader slice_reader(unpacked_buffer);

  // first_slice_segment_in_pic_flag: u(1)
  bool first_slice_segment_in_pic_flag = slice_reader.Read<bool>();
  if (!slice_reader.Ok()) {
    return std::nullopt;
  }

  return first_slice_segment_in_pic_flag;
}

void H265BitstreamParser::ParseBitstream(std::span<const uint8_t> bitstream) {
  std::vector<H265::NaluIndex> nalu_indices = H265::FindNaluIndices(bitstream);
  for (const H265::NaluIndex& index : nalu_indices)
    ParseSlice(
        bitstream.subspan(index.payload_start_offset, index.payload_size));
}

std::optional<int> H265BitstreamParser::GetLastSliceQp() const {
  if (!last_slice_qp_delta_ || !last_slice_pps_id_) {
    return std::nullopt;
  }
  const H265PpsParser::PpsState* pps = GetPPS(last_slice_pps_id_.value());
  if (!pps)
    return std::nullopt;
  const int parsed_qp = 26 + pps->init_qp_minus26 + *last_slice_qp_delta_;
  if (parsed_qp < kMinQpValue || parsed_qp > kMaxQpValue) {
    RTC_LOG(LS_ERROR) << "Parsed invalid QP from bitstream.";
    return std::nullopt;
  }
  return parsed_qp;
}

std::optional<uint32_t> H265BitstreamParser::GetLastSlicePpsId() const {
  if (!last_slice_pps_id_) {
    RTC_LOG(LS_ERROR) << "Failed to parse PPS id from bitstream.";
    return std::nullopt;
  }

  return last_slice_pps_id_;
}

}  // namespace webrtc
