// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/parsers/h264_parser.h"

#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/containers/span_reader.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
#include "base/types/to_address.h"
#include "media/base/media_switches.h"
#include "media/base/subsample_entry.h"
#include "media/parsers/bit_reader_macros.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/hdr_metadata.h"

namespace media {

bool H264SliceHeader::IsPSlice() const {
  return (slice_type % 5 == kPSlice);
}

bool H264SliceHeader::IsBSlice() const {
  return (slice_type % 5 == kBSlice);
}

bool H264SliceHeader::IsISlice() const {
  return (slice_type % 5 == kISlice);
}

bool H264SliceHeader::IsSPSlice() const {
  return (slice_type % 5 == kSPSlice);
}

bool H264SliceHeader::IsSISlice() const {
  return (slice_type % 5 == kSISlice);
}

H264NALU::H264NALU() = default;

// static
void H264SPS::GetLevelConfigFromProfileLevel(VideoCodecProfile profile,
                                             uint8_t level,
                                             int* level_idc,
                                             bool* constraint_set3_flag) {
  // Spec A.3.1.
  // Note: we always use h264_output_level = 9 to indicate Level 1b in
  //       VideoEncodeAccelerator::Config, in order to tell apart from Level 1.1
  //       which level IDC is also 11.
  // For Baseline and Main profile, if requested level is Level 1b, set
  // level_idc to 11 and constraint_set3_flag to true. Otherwise, set level_idc
  // to 9 for Level 1b, and ten times level number for others.
  if ((profile == H264PROFILE_BASELINE || profile == H264PROFILE_MAIN) &&
      level == kLevelIDC1B) {
    *level_idc = 11;
    *constraint_set3_flag = true;
  } else {
    *level_idc = level;
  }
}

H264SPS::H264SPS() = default;
H264SPS::H264SPS(const H264SPS&) = default;
H264SPS& H264SPS::operator=(const H264SPS&) = default;
H264SPS::H264SPS(H264SPS&&) noexcept = default;
H264SPS& H264SPS::operator=(H264SPS&&) noexcept = default;
bool H264SPS::operator==(const H264SPS&) const = default;

// Based on T-REC-H.264 7.4.2.1.1, "Sequence parameter set data semantics",
// available from http://www.itu.int/rec/T-REC-H.264.
std::optional<gfx::Size> H264SPS::GetCodedSize() const {
  // Interlaced frames are twice the height of each field.
  const int mb_unit = 16;
  int map_unit = frame_mbs_only_flag ? 16 : 32;

  // Verify that the values are not too large before multiplying them.
  // TODO(sandersd): These limits could be much smaller. The currently-largest
  // specified limit (excluding SVC, multiview, etc., which I didn't bother to
  // read) is 543 macroblocks (section A.3.1).
  int max_mb_minus1 = std::numeric_limits<int>::max() / mb_unit - 1;
  int max_map_units_minus1 = std::numeric_limits<int>::max() / map_unit - 1;
  if (pic_width_in_mbs_minus1 > max_mb_minus1 ||
      pic_height_in_map_units_minus1 > max_map_units_minus1) {
    DVLOG(1) << "Coded size is too large.";
    return std::nullopt;
  }

  return gfx::Size(mb_unit * (pic_width_in_mbs_minus1 + 1),
                   map_unit * (pic_height_in_map_units_minus1 + 1));
}

// Also based on section 7.4.2.1.1.
std::optional<gfx::Rect> H264SPS::GetVisibleRect() const {
  std::optional<gfx::Size> coded_size = GetCodedSize();
  if (!coded_size)
    return std::nullopt;

  if (!frame_cropping_flag)
    return gfx::Rect(coded_size.value());

  int crop_unit_x;
  int crop_unit_y;
  if (chroma_array_type == 0) {
    crop_unit_x = 1;
    crop_unit_y = frame_mbs_only_flag ? 1 : 2;
  } else {
    // Section 6.2.
    // |chroma_format_idc| may be:
    //   1 => 4:2:0
    //   2 => 4:2:2
    //   3 => 4:4:4
    // Everything else has |chroma_array_type| == 0.
    int sub_width_c = chroma_format_idc > 2 ? 1 : 2;
    int sub_height_c = chroma_format_idc > 1 ? 1 : 2;
    crop_unit_x = sub_width_c;
    crop_unit_y = sub_height_c * (frame_mbs_only_flag ? 1 : 2);
  }

  // Verify that the values are not too large before multiplying.
  if (coded_size->width() / crop_unit_x < frame_crop_left_offset ||
      coded_size->width() / crop_unit_x < frame_crop_right_offset ||
      coded_size->height() / crop_unit_y < frame_crop_top_offset ||
      coded_size->height() / crop_unit_y < frame_crop_bottom_offset) {
    DVLOG(1) << "Frame cropping exceeds coded size.";
    return std::nullopt;
  }
  int crop_left = crop_unit_x * frame_crop_left_offset;
  int crop_right = crop_unit_x * frame_crop_right_offset;
  int crop_top = crop_unit_y * frame_crop_top_offset;
  int crop_bottom = crop_unit_y * frame_crop_bottom_offset;

  // Verify that the values are sane. Note that some decoders also require that
  // crops are smaller than a macroblock and/or that crops must be adjacent to
  // at least one corner of the coded frame.
  if (coded_size->width() - crop_left <= crop_right ||
      coded_size->height() - crop_top <= crop_bottom) {
    DVLOG(1) << "Frame cropping excludes entire frame.";
    return std::nullopt;
  }

  return gfx::Rect(crop_left, crop_top,
                   coded_size->width() - crop_left - crop_right,
                   coded_size->height() - crop_top - crop_bottom);
}

// Based on T-REC-H.264 E.2.1, "VUI parameters semantics",
// available from http://www.itu.int/rec/T-REC-H.264.
VideoColorSpace H264SPS::GetColorSpace() const {
  if (colour_description_present_flag) {
    return VideoColorSpace(
        colour_primaries, transfer_characteristics, matrix_coefficients,
        video_full_range_flag ? gfx::ColorSpace::RangeID::FULL
                              : gfx::ColorSpace::RangeID::LIMITED);
  } else {
    return VideoColorSpace();
  }
}

VideoChromaSampling H264SPS::GetChromaSampling() const {
  // Spec section 6.2
  switch (chroma_format_idc) {
    case 0:
      return VideoChromaSampling::k400;
    case 1:
      return VideoChromaSampling::k420;
    case 2:
      return VideoChromaSampling::k422;
    case 3:
      return VideoChromaSampling::k444;
    default:
      DVLOG(1) << "Unknown chroma subsampling format.";
      return VideoChromaSampling::kUnknown;
  }
}

uint8_t H264SPS::GetIndicatedLevel() const {
  // Spec A.3.1 and A.3.2
  // For Baseline, Constrained Baseline and Main profile, the indicated level is
  // Level 1b if level_idc is equal to 11 and constraint_set3_flag is true.
  if ((profile_idc == H264SPS::kProfileIDCBaseline ||
       profile_idc == H264SPS::kProfileIDCConstrainedBaseline ||
       profile_idc == H264SPS::kProfileIDCMain) &&
      level_idc == 11 && constraint_set3_flag) {
    return kLevelIDC1B;  // Level 1b
  }

  // Otherwise, the level_idc is equal to 9 for Level 1b, and others are equal
  // to values of ten times the level numbers.
  return base::checked_cast<uint8_t>(level_idc);
}

bool H264SPS::CheckIndicatedLevelWithinTarget(uint8_t target_level) const {
  // See table A-1 in spec.
  // Level 1.0 < 1b < 1.1 < 1.2 .... (in numeric order).
  uint8_t level = GetIndicatedLevel();
  if (target_level == kLevelIDC1p0)
    return level == kLevelIDC1p0;
  if (target_level == kLevelIDC1B)
    return level == kLevelIDC1p0 || level == kLevelIDC1B;
  return level <= target_level;
}

H264PPS::H264PPS() = default;
H264PPS::H264PPS(const H264PPS&) = default;
H264PPS& H264PPS::operator=(const H264PPS&) = default;
H264PPS::H264PPS(H264PPS&&) noexcept = default;
H264PPS& H264PPS::operator=(H264PPS&&) noexcept = default;
bool H264PPS::operator==(const H264PPS&) const = default;

H264SliceHeader::H264SliceHeader() = default;
H264SliceHeader::H264SliceHeader(const H264SliceHeader&) = default;
H264SliceHeader& H264SliceHeader::operator=(const H264SliceHeader&) = default;
H264SliceHeader::H264SliceHeader(H264SliceHeader&&) = default;
H264SliceHeader& H264SliceHeader::operator=(H264SliceHeader&&) = default;



H264SEI::H264SEI() = default;

H264SEI::~H264SEI() = default;

// ISO 14496 part 10
// VUI parameters: Table E-1 "Meaning of sample aspect ratio indicator"
const auto kTableSarWidth = std::to_array<int>({
    0,
    1,
    12,
    10,
    16,
    40,
    24,
    20,
    32,
    80,
    18,
    15,
    64,
    160,
    4,
    3,
    2,
});
const auto kTableSarHeight = std::to_array<int>({
    0,
    1,
    11,
    11,
    11,
    33,
    11,
    11,
    11,
    33,
    11,
    11,
    33,
    99,
    3,
    2,
    1,
});
static_assert(std::size(kTableSarWidth) == std::size(kTableSarHeight),
              "sar tables must have the same size");

H264Parser::H264Parser()
    : validate_extended_bitstream_(
          base::FeatureList::IsEnabled(kExtendedVideoBitstreamValidation)) {
  Reset();
}

H264Parser::~H264Parser() = default;

void H264Parser::Reset() {
  stream_ = {};
  encrypted_ranges_.clear();
  previous_nalu_range_.clear();
}

void H264Parser::SetStream(const uint8_t* stream, off_t stream_size) {
  SetStream(
      UNSAFE_TODO(base::span(stream, base::checked_cast<size_t>(stream_size))));
}

void H264Parser::SetStream(base::span<const uint8_t> stream) {
  std::vector<SubsampleEntry> subsamples;
  SetEncryptedStream(stream, subsamples);
}

void H264Parser::SetEncryptedStream(
    base::span<const uint8_t> stream,
    const std::vector<SubsampleEntry>& subsamples) {
  DCHECK_GT(stream.size(), 0u);

  stream_ = stream;
  previous_nalu_range_.clear();

  encrypted_ranges_.clear();
  const uint8_t* start = stream.data();
  const uint8_t* stream_end = UNSAFE_TODO(stream_.data() + stream_.size());
  for (size_t i = 0; i < subsamples.size() && start < stream_end; ++i) {
    UNSAFE_TODO(start += subsamples[i].clear_bytes);

    const uint8_t* end =
        std::min(UNSAFE_TODO(start + subsamples[i].cypher_bytes), stream_end);
    encrypted_ranges_.Add(start, end);
    start = end;
  }
}

const H264PPS* H264Parser::GetPPS(int pps_id) const {
  auto it = active_PPSes_.find(pps_id);
  if (it == active_PPSes_.end()) {
    DVLOG(1) << "Requested a nonexistent PPS id " << pps_id;
    return nullptr;
  }

  return it->second.get();
}

const H264SPS* H264Parser::GetSPS(int sps_id) const {
  auto it = active_SPSes_.find(sps_id);
  if (it == active_SPSes_.end()) {
    DVLOG(1) << "Requested a nonexistent SPS id " << sps_id;
    return nullptr;
  }

  return it->second.get();
}

// static
bool H264Parser::FindStartCode(base::span<const uint8_t> data,
                               size_t* offset,
                               size_t* start_code_size) {
  for (size_t i = 2; i < data.size(); ++i) {
    // The start code is "\0\0\1", ones are more unusual than zeroes, so let's
    // search for it first.
    const uint8_t* start_code = reinterpret_cast<const uint8_t*>(UNSAFE_TODO(
        memchr(base::to_address(data.begin() + i), 1, data.size() - i)));
    if (!start_code) {
      break;
    }
    i = start_code - data.data();
    size_t offset_temp = i - 2;
    auto [first, remain] = data.split_at(offset_temp);

    if (remain[0] == 0 && remain[1] == 0) {
      // Found three-byte start code, set pointer at its beginning.
      *offset = offset_temp;
      *start_code_size = 3;

      // If there is a zero byte before this start code,
      // then it's actually a four-byte start code, so backtrack one byte.
      if (*offset > 0 && first.back() == 0x00) {
        --(*offset);
        ++(*start_code_size);
      }

      return true;
    }
  }

  *offset = data.size();
  *start_code_size = 0;
  return false;
}

bool H264Parser::LocateNALU(size_t* nalu_size, size_t* start_code_size) {
  // Find the start code of next NALU.
  size_t nalu_start_off = 0;
  size_t annexb_start_code_size = 0;
  if (!FindStartCodeInClearRanges(stream_, encrypted_ranges_, &nalu_start_off,
                                  &annexb_start_code_size)) {
    DVLOG(4) << "Could not find start code, end of stream?";
    return false;
  }

  // Move the stream to the beginning of the NALU (pointing at the start code).
  stream_ = stream_.subspan(nalu_start_off);
  if (stream_.size() <= annexb_start_code_size) {
    DVLOG(3) << "End of stream";
    return false;
  }

  // Find the start code of next NALU;
  // if successful, |nalu_size_without_start_code| is the number of bytes from
  // after previous start code to before this one;
  // if next start code is not found, it is still a valid NALU since there
  // are some bytes left after the first start code: all the remaining bytes
  // belong to the current NALU.
  size_t next_start_code_size = 0;
  size_t nalu_size_without_start_code = 0;
  if (!FindStartCodeInClearRanges(
          stream_.subspan(annexb_start_code_size), encrypted_ranges_,
          &nalu_size_without_start_code, &next_start_code_size)) {
    nalu_size_without_start_code = stream_.size() - annexb_start_code_size;
  }
  *nalu_size = nalu_size_without_start_code + annexb_start_code_size;
  *start_code_size = annexb_start_code_size;
  return true;
}

// static
bool H264Parser::FindStartCodeInClearRanges(
    base::span<const uint8_t> data,
    const Ranges<const uint8_t*>& encrypted_ranges,
    size_t* offset,
    size_t* start_code_size) {
  if (encrypted_ranges.size() == 0) {
    return FindStartCode(data, offset, start_code_size);
  }

  size_t offset_temp = 0;
  do {
    if (!FindStartCode(data.subspan(offset_temp), offset, start_code_size)) {
      return false;
    }

    // Construct a Ranges object that represents the region occupied
    // by the start code and the 1 byte needed to read the NAL unit type.
    auto start_code = data.subspan(offset_temp + *offset, *start_code_size);
    Ranges<const uint8_t*> start_code_range;
    start_code_range.Add(start_code.data(),
                         UNSAFE_TODO(base::to_address(start_code.end()) + 1));

    if (encrypted_ranges.IntersectionWith(start_code_range).size() > 0) {
      // The start code is inside an encrypted section so we need to scan
      // for another start code.
      *start_code_size = 0;
      offset_temp += std::min(*offset + 1, data.size());
    }
  } while (*start_code_size == 0);

  // Update |*offset| to include the data we skipped over.
  *offset += offset_temp;
  return true;
}

bool H264Parser::FindStartCodeInClearRanges(
    const uint8_t* data,
    off_t data_size,
    const Ranges<const uint8_t*>& ranges,
    off_t* start_code_offset,
    off_t* start_code_size) {
  size_t offset = 0;
  size_t size = 0;
  bool result = FindStartCodeInClearRanges(
      UNSAFE_TODO(base::span(data, base::checked_cast<size_t>(data_size))),
      ranges, &offset, &size);
  *start_code_offset = offset;
  *start_code_size = size;
  return result;
}

// static
VideoCodecProfile H264Parser::ProfileIDCToVideoCodecProfile(int profile_idc) {
  switch (profile_idc) {
    case H264SPS::kProfileIDCBaseline:
      return H264PROFILE_BASELINE;
    case H264SPS::kProfileIDCMain:
      return H264PROFILE_MAIN;
    case H264SPS::kProfileIDCHigh:
      return H264PROFILE_HIGH;
    case H264SPS::kProfileIDHigh10:
      return H264PROFILE_HIGH10PROFILE;
    case H264SPS::kProfileIDHigh422:
      return H264PROFILE_HIGH422PROFILE;
    case H264SPS::kProfileIDHigh444Predictive:
      return H264PROFILE_HIGH444PREDICTIVEPROFILE;
    case H264SPS::kProfileIDScalableBaseline:
      return H264PROFILE_SCALABLEBASELINE;
    case H264SPS::kProfileIDScalableHigh:
      return H264PROFILE_SCALABLEHIGH;
    case H264SPS::kProfileIDStereoHigh:
      return H264PROFILE_STEREOHIGH;
    case H264SPS::kProfileIDSMultiviewHigh:
      return H264PROFILE_MULTIVIEWHIGH;
  }
  DVLOG(1) << "unknown video profile: " << profile_idc;
  return VIDEO_CODEC_PROFILE_UNKNOWN;
}

// static
bool H264Parser::ParseNALUs(base::span<const uint8_t> stream,
                            std::vector<H264NALU>* nalus) {
  DCHECK(nalus);
  H264Parser parser;
  parser.SetStream(stream);

  while (true) {
    H264NALU nalu;
    const H264Parser::Result result = parser.AdvanceToNextNALU(&nalu);
    if (result == H264Parser::kOk) {
      nalus->push_back(nalu);
    } else if (result == media::H264Parser::kEOStream) {
      return true;
    } else {
      DLOG(ERROR) << "Unexpected H264 parser result";
      return false;
    }
  }
}

H264Parser::Result H264Parser::AdvanceToNextNALU(H264NALU* nalu) {
  size_t start_code_size;
  size_t nalu_size_with_start_code;
  if (!LocateNALU(&nalu_size_with_start_code, &start_code_size)) {
    DVLOG(4) << "Could not find next NALU, bytes left in stream: "
             << stream_.size();
    stream_ = {};
    return kEOStream;
  }

  nalu->data = stream_.subspan(start_code_size,
                               nalu_size_with_start_code - start_code_size);
  DVLOG(4) << "NALU found: size=" << nalu_size_with_start_code;

  // Initialize bit reader at the start of found NALU.
  if (!br_.Initialize(nalu->data)) {
    stream_ = {};
    return kEOStream;
  }

  // Move parser state to after this NALU, so next time AdvanceToNextNALU
  // is called, we will effectively be skipping it;
  // other parsing functions will use the position saved
  // in bit reader for parsing, so we don't have to remember it here.
  stream_ = stream_.subspan(nalu_size_with_start_code);

  // Read NALU header, skip the forbidden_zero_bit, but check for it.
  int data;
  READ_BITS_OR_RETURN(1, &data);
  TRUE_OR_RETURN(data == 0);

  READ_BITS_OR_RETURN(2, &nalu->nal_ref_idc);
  READ_BITS_OR_RETURN(5, &nalu->nal_unit_type);

  DVLOG(4) << "NALU type: " << static_cast<int>(nalu->nal_unit_type)
           << " at: " << reinterpret_cast<const void*>(nalu->data.data())
           << " size: " << nalu->data.size()
           << " ref: " << static_cast<int>(nalu->nal_ref_idc);

  previous_nalu_range_.clear();
  previous_nalu_range_.Add(nalu->data.data(),
                           base::to_address(nalu->data.end()));
  return kOk;
}

// Default scaling lists (per spec).
static const std::array<uint8_t, kH264ScalingList4x4Length> kDefault4x4Intra = {
    {6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42}};

static const std::array<uint8_t, kH264ScalingList4x4Length> kDefault4x4Inter = {
    {10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34}};

static const std::array<uint8_t, kH264ScalingList8x8Length> kDefault8x8Intra = {
    {
        6,  10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23,
        23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27,
        27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31,
        31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42,
    }};

static const std::array<uint8_t, kH264ScalingList8x8Length> kDefault8x8Inter = {
    {
        9,  13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21,
        21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24,
        24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27,
        27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35,
    }};

static inline void DefaultScalingList4x4(
    int i,
    std::array<std::array<uint8_t, kH264ScalingList4x4Length>, 6>&
        scaling_list4x4) {
  DCHECK_LT(i, 6);

  if (i < 3)
    scaling_list4x4[i] = kDefault4x4Intra;
  else if (i < 6)
    scaling_list4x4[i] = kDefault4x4Inter;
}

static inline void DefaultScalingList8x8(
    int i,
    std::array<std::array<uint8_t, kH264ScalingList8x8Length>, 6>&
        scaling_list8x8) {
  DCHECK_LT(i, 6);

  if (i % 2 == 0)
    scaling_list8x8[i] = kDefault8x8Intra;
  else
    scaling_list8x8[i] = kDefault8x8Inter;
}

static void FallbackScalingList4x4(
    int i,
    const std::array<uint8_t, kH264ScalingList4x4Length>&
        default_scaling_list_intra,
    const std::array<uint8_t, kH264ScalingList4x4Length>&
        default_scaling_list_inter,
    std::array<std::array<uint8_t, kH264ScalingList4x4Length>, 6>&
        scaling_list4x4) {
  switch (i) {
    case 0:
      scaling_list4x4[i] = default_scaling_list_intra;
      break;

    case 1:
      scaling_list4x4[i] = scaling_list4x4[0];
      break;

    case 2:
      scaling_list4x4[i] = scaling_list4x4[1];
      break;

    case 3:
      scaling_list4x4[i] = default_scaling_list_inter;
      break;

    case 4:
      scaling_list4x4[i] = scaling_list4x4[3];
      break;

    case 5:
      scaling_list4x4[i] = scaling_list4x4[4];
      break;

    default:
      NOTREACHED();
  }
}

static void FallbackScalingList8x8(
    int i,
    const std::array<uint8_t, kH264ScalingList8x8Length>&
        default_scaling_list_intra,
    const std::array<uint8_t, kH264ScalingList8x8Length>&
        default_scaling_list_inter,
    std::array<std::array<uint8_t, kH264ScalingList8x8Length>, 6>&
        scaling_list8x8) {
  switch (i) {
    case 0:
      scaling_list8x8[i] = default_scaling_list_intra;
      break;

    case 1:
      scaling_list8x8[i] = default_scaling_list_inter;
      break;

    case 2:
      scaling_list8x8[i] = scaling_list8x8[0];
      break;

    case 3:
      scaling_list8x8[i] = scaling_list8x8[1];
      break;

    case 4:
      scaling_list8x8[i] = scaling_list8x8[2];
      break;

    case 5:
      scaling_list8x8[i] = scaling_list8x8[3];
      break;

    default:
      NOTREACHED();
  }
}

H264Parser::Result H264Parser::ParseScalingList(
    base::span<uint8_t> scaling_list,
    bool* use_default) {
  // See chapter 7.3.2.1.1.1.
  int last_scale = 8;
  int next_scale = 8;
  int delta_scale;

  *use_default = false;

  for (size_t j = 0; j < scaling_list.size(); ++j) {
    if (next_scale != 0) {
      READ_SE_OR_RETURN(&delta_scale);
      IN_RANGE_OR_RETURN(delta_scale, -128, 127);
      next_scale = (last_scale + delta_scale + 256) & 0xff;

      if (j == 0 && next_scale == 0) {
        *use_default = true;
        return kOk;
      }
    }

    scaling_list[j] = (next_scale == 0) ? last_scale : next_scale;
    last_scale = scaling_list[j];
  }

  return kOk;
}

H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
  // See 7.4.2.1.1.
  bool seq_scaling_list_present_flag;
  bool use_default;
  Result res;

  // Parse scaling_list4x4.
  for (int i = 0; i < 6; ++i) {
    READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);

    if (seq_scaling_list_present_flag) {
      res = ParseScalingList(sps->scaling_list4x4[i], &use_default);
      if (res != kOk)
        return res;

      if (use_default)
        DefaultScalingList4x4(i, sps->scaling_list4x4);

    } else {
      FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter,
                             sps->scaling_list4x4);
    }
  }

  // Parse scaling_list8x8.
  for (int i = 0; i < ((sps->chroma_format_idc != 3) ? 2 : 6); ++i) {
    READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);

    if (seq_scaling_list_present_flag) {
      res = ParseScalingList(sps->scaling_list8x8[i], &use_default);
      if (res != kOk)
        return res;

      if (use_default)
        DefaultScalingList8x8(i, sps->scaling_list8x8);

    } else {
      FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter,
                             sps->scaling_list8x8);
    }
  }

  return kOk;
}

H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
                                                    H264PPS* pps) {
  // See 7.4.2.2.
  bool pic_scaling_list_present_flag;
  bool use_default;
  Result res;

  for (int i = 0; i < 6; ++i) {
    READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);

    if (pic_scaling_list_present_flag) {
      res = ParseScalingList(pps->scaling_list4x4[i], &use_default);
      if (res != kOk)
        return res;

      if (use_default)
        DefaultScalingList4x4(i, pps->scaling_list4x4);

    } else {
      if (!sps.seq_scaling_matrix_present_flag) {
        // Table 7-2 fallback rule A in spec.
        FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter,
                               pps->scaling_list4x4);
      } else {
        // Table 7-2 fallback rule B in spec.
        FallbackScalingList4x4(i, sps.scaling_list4x4[0],
                               sps.scaling_list4x4[3], pps->scaling_list4x4);
      }
    }
  }

  if (pps->transform_8x8_mode_flag) {
    for (int i = 0; i < ((sps.chroma_format_idc != 3) ? 2 : 6); ++i) {
      READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);

      if (pic_scaling_list_present_flag) {
        res = ParseScalingList(pps->scaling_list8x8[i], &use_default);
        if (res != kOk)
          return res;

        if (use_default)
          DefaultScalingList8x8(i, pps->scaling_list8x8);

      } else {
        if (!sps.seq_scaling_matrix_present_flag) {
          // Table 7-2 fallback rule A in spec.
          FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter,
                                 pps->scaling_list8x8);
        } else {
          // Table 7-2 fallback rule B in spec.
          FallbackScalingList8x8(i, sps.scaling_list8x8[0],
                                 sps.scaling_list8x8[1], pps->scaling_list8x8);
        }
      }
    }
  }
  return kOk;
}

H264Parser::Result H264Parser::ParseAndIgnoreHRDParameters(
    bool* hrd_parameters_present) {
  int data;
  READ_BOOL_OR_RETURN(&data);  // {nal,vcl}_hrd_parameters_present_flag
  if (!data)
    return kOk;

  *hrd_parameters_present = true;

  int cpb_cnt_minus1;
  READ_UE_OR_RETURN(&cpb_cnt_minus1);
  IN_RANGE_OR_RETURN(cpb_cnt_minus1, 0, 31);
  READ_BITS_OR_RETURN(8, &data);  // bit_rate_scale, cpb_size_scale
  for (int i = 0; i <= cpb_cnt_minus1; ++i) {
    READ_UE_OR_RETURN(&data);    // bit_rate_value_minus1[i]
    READ_UE_OR_RETURN(&data);    // cpb_size_value_minus1[i]
    READ_BOOL_OR_RETURN(&data);  // cbr_flag
  }
  READ_BITS_OR_RETURN(20, &data);  // cpb/dpb delays, etc.

  return kOk;
}

H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) {
  bool aspect_ratio_info_present_flag;
  READ_BOOL_OR_RETURN(&aspect_ratio_info_present_flag);
  if (aspect_ratio_info_present_flag) {
    int aspect_ratio_idc;
    READ_BITS_OR_RETURN(8, &aspect_ratio_idc);
    if (aspect_ratio_idc == H264SPS::kExtendedSar) {
      READ_BITS_OR_RETURN(16, &sps->sar_width);
      READ_BITS_OR_RETURN(16, &sps->sar_height);
    } else {
      const int max_aspect_ratio_idc = std::size(kTableSarWidth) - 1;
      IN_RANGE_OR_RETURN(aspect_ratio_idc, 0, max_aspect_ratio_idc);
      sps->sar_width = kTableSarWidth[aspect_ratio_idc];
      sps->sar_height = kTableSarHeight[aspect_ratio_idc];
    }
  }

  int data;
  // Read and ignore overscan and video signal type info.
  READ_BOOL_OR_RETURN(&data);  // overscan_info_present_flag
  if (data)
    READ_BOOL_OR_RETURN(&data);  // overscan_appropriate_flag

  READ_BOOL_OR_RETURN(&sps->video_signal_type_present_flag);
  if (sps->video_signal_type_present_flag) {
    READ_BITS_OR_RETURN(3, &sps->video_format);
    READ_BOOL_OR_RETURN(&sps->video_full_range_flag);
    READ_BOOL_OR_RETURN(&sps->colour_description_present_flag);
    if (sps->colour_description_present_flag) {
      // color description syntax elements
      READ_BITS_OR_RETURN(8, &sps->colour_primaries);
      READ_BITS_OR_RETURN(8, &sps->transfer_characteristics);
      READ_BITS_OR_RETURN(8, &sps->matrix_coefficients);
    }
  }

  READ_BOOL_OR_RETURN(&data);  // chroma_loc_info_present_flag
  if (data) {
    READ_UE_OR_RETURN(&data);  // chroma_sample_loc_type_top_field
    IN_RANGE_IF_OR_RETURN(data, 0, 5, validate_extended_bitstream_);
    READ_UE_OR_RETURN(&data);  // chroma_sample_loc_type_bottom_field
    IN_RANGE_IF_OR_RETURN(data, 0, 5, validate_extended_bitstream_);
  }

  // Read and ignore timing info.
  READ_BOOL_OR_RETURN(&data);  // timing_info_present_flag
  if (data) {
    READ_BITS_OR_RETURN(16, &data);  // num_units_in_tick
    READ_BITS_OR_RETURN(16, &data);  // num_units_in_tick
    READ_BITS_OR_RETURN(16, &data);  // time_scale
    READ_BITS_OR_RETURN(16, &data);  // time_scale
    READ_BOOL_OR_RETURN(&data);      // fixed_frame_rate_flag
  }

  // Read and ignore NAL HRD parameters, if present.
  bool hrd_parameters_present = false;
  Result res = ParseAndIgnoreHRDParameters(&hrd_parameters_present);
  if (res != kOk)
    return res;

  // Read and ignore VCL HRD parameters, if present.
  res = ParseAndIgnoreHRDParameters(&hrd_parameters_present);
  if (res != kOk)
    return res;

  if (hrd_parameters_present)    // One of NAL or VCL params present is enough.
    READ_BOOL_OR_RETURN(&data);  // low_delay_hrd_flag

  READ_BOOL_OR_RETURN(&data);  // pic_struct_present_flag
  READ_BOOL_OR_RETURN(&sps->bitstream_restriction_flag);
  if (sps->bitstream_restriction_flag) {
    READ_BOOL_OR_RETURN(&data);  // motion_vectors_over_pic_boundaries_flag
    READ_UE_OR_RETURN(&data);    // max_bytes_per_pic_denom
    IN_RANGE_IF_OR_RETURN(data, 0, 16, validate_extended_bitstream_);
    READ_UE_OR_RETURN(&data);    // max_bits_per_mb_denom
    IN_RANGE_IF_OR_RETURN(data, 0, 16, validate_extended_bitstream_);
    READ_UE_OR_RETURN(&data);    // log2_max_mv_length_horizontal
    IN_RANGE_IF_OR_RETURN(data, 0, 16, validate_extended_bitstream_);
    READ_UE_OR_RETURN(&data);    // log2_max_mv_length_vertical
    IN_RANGE_IF_OR_RETURN(data, 0, 16, validate_extended_bitstream_);
    READ_UE_OR_RETURN(&sps->max_num_reorder_frames);
    READ_UE_OR_RETURN(&sps->max_dec_frame_buffering);
    TRUE_OR_RETURN(sps->max_dec_frame_buffering >= sps->max_num_ref_frames);
    IN_RANGE_OR_RETURN(sps->max_num_reorder_frames, 0,
                       sps->max_dec_frame_buffering);
  }

  return kOk;
}

static void FillDefaultSeqScalingLists(H264SPS* sps) {
  for (auto& arr : sps->scaling_list4x4) {
    arr.fill(16);
  }
  for (auto& arr : sps->scaling_list8x8) {
    arr.fill(16);
  }
}

H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
  // See 7.4.2.1.
  int data;
  Result res;

  *sps_id = -1;

  std::unique_ptr<H264SPS> sps(new H264SPS());

  READ_BITS_OR_RETURN(8, &sps->profile_idc);
  READ_BOOL_OR_RETURN(&sps->constraint_set0_flag);
  READ_BOOL_OR_RETURN(&sps->constraint_set1_flag);
  READ_BOOL_OR_RETURN(&sps->constraint_set2_flag);
  READ_BOOL_OR_RETURN(&sps->constraint_set3_flag);
  READ_BOOL_OR_RETURN(&sps->constraint_set4_flag);
  READ_BOOL_OR_RETURN(&sps->constraint_set5_flag);
  READ_BITS_OR_RETURN(2, &data);  // reserved_zero_2bits
  READ_BITS_OR_RETURN(8, &sps->level_idc);
  READ_UE_OR_RETURN(&sps->seq_parameter_set_id);
  TRUE_OR_RETURN(sps->seq_parameter_set_id < 32);

  if (sps->profile_idc == 100 || sps->profile_idc == 110 ||
      sps->profile_idc == 122 || sps->profile_idc == 244 ||
      sps->profile_idc == 44 || sps->profile_idc == 83 ||
      sps->profile_idc == 86 || sps->profile_idc == 118 ||
      sps->profile_idc == 128) {
    READ_UE_OR_RETURN(&sps->chroma_format_idc);
    TRUE_OR_RETURN(sps->chroma_format_idc < 4);

    if (sps->chroma_format_idc == 3)
      READ_BOOL_OR_RETURN(&sps->separate_colour_plane_flag);

    READ_UE_OR_RETURN(&sps->bit_depth_luma_minus8);
    TRUE_OR_RETURN(sps->bit_depth_luma_minus8 < 7);

    READ_UE_OR_RETURN(&sps->bit_depth_chroma_minus8);
    TRUE_OR_RETURN(sps->bit_depth_chroma_minus8 < 7);

    READ_BOOL_OR_RETURN(&sps->qpprime_y_zero_transform_bypass_flag);
    READ_BOOL_OR_RETURN(&sps->seq_scaling_matrix_present_flag);

    if (sps->seq_scaling_matrix_present_flag) {
      DVLOG(4) << "Scaling matrix present";
      res = ParseSPSScalingLists(sps.get());
      if (res != kOk)
        return res;
    } else {
      FillDefaultSeqScalingLists(sps.get());
    }
  } else {
    sps->chroma_format_idc = 1;
    FillDefaultSeqScalingLists(sps.get());
  }

  if (sps->separate_colour_plane_flag)
    sps->chroma_array_type = 0;
  else
    sps->chroma_array_type = sps->chroma_format_idc;

  READ_UE_OR_RETURN(&sps->log2_max_frame_num_minus4);
  TRUE_OR_RETURN(sps->log2_max_frame_num_minus4 < 13);

  READ_UE_OR_RETURN(&sps->pic_order_cnt_type);
  TRUE_OR_RETURN(sps->pic_order_cnt_type < 3);

  if (sps->pic_order_cnt_type == 0) {
    READ_UE_OR_RETURN(&sps->log2_max_pic_order_cnt_lsb_minus4);
    TRUE_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 < 13);
    sps->expected_delta_per_pic_order_cnt_cycle = 0;
  } else if (sps->pic_order_cnt_type == 1) {
    READ_BOOL_OR_RETURN(&sps->delta_pic_order_always_zero_flag);
    READ_SE_OR_RETURN(&sps->offset_for_non_ref_pic);
    READ_SE_OR_RETURN(&sps->offset_for_top_to_bottom_field);
    READ_UE_OR_RETURN(&sps->num_ref_frames_in_pic_order_cnt_cycle);
    TRUE_OR_RETURN(sps->num_ref_frames_in_pic_order_cnt_cycle < 255);

    base::CheckedNumeric<int> offset_acc = 0;
    for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; ++i) {
      READ_SE_OR_RETURN(&sps->offset_for_ref_frame[i]);
      offset_acc += sps->offset_for_ref_frame[i];
    }
    if (!offset_acc.IsValid())
      return kInvalidStream;
    sps->expected_delta_per_pic_order_cnt_cycle = offset_acc.ValueOrDefault(0);
  }

  READ_UE_OR_RETURN(&sps->max_num_ref_frames);
  IN_RANGE_IF_OR_RETURN(sps->max_num_ref_frames, 0, 16,
                        validate_extended_bitstream_);
  READ_BOOL_OR_RETURN(&sps->gaps_in_frame_num_value_allowed_flag);

  READ_UE_OR_RETURN(&sps->pic_width_in_mbs_minus1);
  // H.264 Level 6.2 restricts max frame width to 8704 samples (544
  // macroblocks).
  IN_RANGE_OR_RETURN(sps->pic_width_in_mbs_minus1, 0, 543);
  READ_UE_OR_RETURN(&sps->pic_height_in_map_units_minus1);
  // H.264 Level 6.2 restricts max frame height to 8704 samples (544
  // macroblocks).
  IN_RANGE_OR_RETURN(sps->pic_height_in_map_units_minus1, 0, 543);

  READ_BOOL_OR_RETURN(&sps->frame_mbs_only_flag);
  if (!sps->frame_mbs_only_flag)
    READ_BOOL_OR_RETURN(&sps->mb_adaptive_frame_field_flag);

  READ_BOOL_OR_RETURN(&sps->direct_8x8_inference_flag);

  READ_BOOL_OR_RETURN(&sps->frame_cropping_flag);
  if (sps->frame_cropping_flag) {
    READ_UE_OR_RETURN(&sps->frame_crop_left_offset);
    READ_UE_OR_RETURN(&sps->frame_crop_right_offset);
    READ_UE_OR_RETURN(&sps->frame_crop_top_offset);
    READ_UE_OR_RETURN(&sps->frame_crop_bottom_offset);
  }

  READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag);
  if (sps->vui_parameters_present_flag) {
    DVLOG(4) << "VUI parameters present";
    res = ParseVUIParameters(sps.get());
    if (res != kOk)
      return res;
  }

  // If an SPS with the same id already exists, replace it.
  *sps_id = sps->seq_parameter_set_id;

  if (validate_extended_bitstream_) {
    auto it = active_SPSes_.find(*sps_id);
    if (it == active_SPSes_.end() || *(it->second) != *sps) {
      // Invalidate dependent PPSes since their validations against the old SPS
      // are no longer guaranteed to hold under the new SPS.
      std::erase_if(active_PPSes_, [id = *sps_id](const auto& pair) {
        return pair.second->seq_parameter_set_id == id;
      });
    }
  }

  active_SPSes_[*sps_id] = std::move(sps);

  return kOk;
}

H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
  // See 7.4.2.2.
  const H264SPS* sps;
  Result res;

  *pps_id = -1;

  std::unique_ptr<H264PPS> pps(new H264PPS());

  READ_UE_OR_RETURN(&pps->pic_parameter_set_id);
  IN_RANGE_OR_RETURN(pps->pic_parameter_set_id, 0, 255);
  READ_UE_OR_RETURN(&pps->seq_parameter_set_id);
  TRUE_OR_RETURN(pps->seq_parameter_set_id < 32);

  if (active_SPSes_.find(pps->seq_parameter_set_id) == active_SPSes_.end()) {
    DVLOG(1) << "Invalid stream, no SPS id: " << pps->seq_parameter_set_id;
    return kInvalidStream;
  }

  sps = GetSPS(pps->seq_parameter_set_id);
  TRUE_OR_RETURN(sps);

  READ_BOOL_OR_RETURN(&pps->entropy_coding_mode_flag);
  READ_BOOL_OR_RETURN(&pps->bottom_field_pic_order_in_frame_present_flag);

  READ_UE_OR_RETURN(&pps->num_slice_groups_minus1);
  if (pps->num_slice_groups_minus1 > 1) {
    DVLOG(1) << "Slice groups not supported";
    return kUnsupportedStream;
  }

  READ_UE_OR_RETURN(&pps->num_ref_idx_l0_default_active_minus1);
  TRUE_OR_RETURN(pps->num_ref_idx_l0_default_active_minus1 < 32);

  READ_UE_OR_RETURN(&pps->num_ref_idx_l1_default_active_minus1);
  TRUE_OR_RETURN(pps->num_ref_idx_l1_default_active_minus1 < 32);

  READ_BOOL_OR_RETURN(&pps->weighted_pred_flag);
  READ_BITS_OR_RETURN(2, &pps->weighted_bipred_idc);
  TRUE_OR_RETURN(pps->weighted_bipred_idc < 3);

  READ_SE_OR_RETURN(&pps->pic_init_qp_minus26);
  IN_RANGE_OR_RETURN(pps->pic_init_qp_minus26, -26, 25);

  READ_SE_OR_RETURN(&pps->pic_init_qs_minus26);
  IN_RANGE_OR_RETURN(pps->pic_init_qs_minus26, -26, 25);

  READ_SE_OR_RETURN(&pps->chroma_qp_index_offset);
  IN_RANGE_OR_RETURN(pps->chroma_qp_index_offset, -12, 12);
  pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset;

  READ_BOOL_OR_RETURN(&pps->deblocking_filter_control_present_flag);
  READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag);
  READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag);

  bool pps_remainder_unencrypted = true;
  if (encrypted_ranges_.size()) {
    Ranges<const uint8_t*> pps_range;
    // Only check that the next byte is unencrypted, not the rest of the NALU.
    const uint8_t* next_byte =
        UNSAFE_TODO(previous_nalu_range_.end(0) - br_.NumBitsLeft() / 8);
    pps_range.Add(next_byte, UNSAFE_TODO(next_byte + 1));
    pps_remainder_unencrypted =
        (encrypted_ranges_.IntersectionWith(pps_range).size() == 0);
  }
  if (pps_remainder_unencrypted && br_.HasMoreRBSPData()) {
    if (sps->profile_idc == H264SPS::kProfileIDCBaseline ||
        sps->profile_idc == H264SPS::kProfileIDCConstrainedBaseline ||
        sps->profile_idc == H264SPS::kProfileIDCMain) {
      DVLOG(1) << "Invalid stream, ignored unexpected RBSB data in PPS frame";
    } else {
      READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag);
      READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag);

      if (pps->pic_scaling_matrix_present_flag) {
        DVLOG(4) << "Picture scaling matrix present";
        res = ParsePPSScalingLists(*sps, pps.get());
        if (res != kOk)
          return res;
      }

      READ_SE_OR_RETURN(&pps->second_chroma_qp_index_offset);
      IN_RANGE_OR_RETURN(pps->second_chroma_qp_index_offset, -12, 12);
    }
  }

  // If a PPS with the same id already exists, replace it.
  *pps_id = pps->pic_parameter_set_id;
  active_PPSes_[*pps_id] = std::move(pps);

  return kOk;
}

H264Parser::Result H264Parser::ParseSPSExt(int* sps_id) {
  // See 7.4.2.1.
  int local_sps_id = -1;

  *sps_id = -1;

  READ_UE_OR_RETURN(&local_sps_id);
  TRUE_OR_RETURN(local_sps_id < 32);

  *sps_id = local_sps_id;
  return kOk;
}

H264Parser::Result H264Parser::ParseRefPicListModification(
    int num_ref_idx_active_minus1,
    H264ModificationOfPicNum* ref_list_mods) {
  H264ModificationOfPicNum* pic_num_mod;

  if (num_ref_idx_active_minus1 >= 32)
    return kInvalidStream;

  for (int i = 0; i < 32; ++i) {
    pic_num_mod = &UNSAFE_TODO(ref_list_mods[i]);
    READ_UE_OR_RETURN(&pic_num_mod->modification_of_pic_nums_idc);
    TRUE_OR_RETURN(pic_num_mod->modification_of_pic_nums_idc < 4);

    switch (pic_num_mod->modification_of_pic_nums_idc) {
      case 0:
      case 1:
        READ_UE_OR_RETURN(&pic_num_mod->abs_diff_pic_num_minus1);
        // H.264 Section 7.4.3.1: abs_diff_pic_num_minus1 shall be in
        // [0, MaxPicNum - 1], MaxPicNum <= 65536.
        IN_RANGE_IF_OR_RETURN(pic_num_mod->abs_diff_pic_num_minus1, 0, 65535,
                              validate_extended_bitstream_);
        break;

      case 2:
        READ_UE_OR_RETURN(&pic_num_mod->long_term_pic_num);
        // H.264 Section 7.4.3.1: long_term_pic_num in [0, MaxLongTermFrameIdx],
        // MaxLongTermFrameIdx <= max_num_ref_frames - 1 <= 15.
        IN_RANGE_IF_OR_RETURN(pic_num_mod->long_term_pic_num, 0, 15,
                              validate_extended_bitstream_);
        break;

      case 3:
        // Per spec, list cannot be empty.
        if (i == 0)
          return kInvalidStream;
        return kOk;

      default:
        return kInvalidStream;
    }
  }

  // If we got here, we didn't get loop end marker prematurely,
  // so make sure it is there for our client.
  int modification_of_pic_nums_idc;
  READ_UE_OR_RETURN(&modification_of_pic_nums_idc);
  TRUE_OR_RETURN(modification_of_pic_nums_idc == 3);

  return kOk;
}

H264Parser::Result H264Parser::ParseRefPicListModifications(
    H264SliceHeader* shdr) {
  Result res;

  if (!shdr->IsISlice() && !shdr->IsSISlice()) {
    READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l0);
    if (shdr->ref_pic_list_modification_flag_l0) {
      res = ParseRefPicListModification(shdr->num_ref_idx_l0_active_minus1,
                                        shdr->ref_list_l0_modifications.data());
      if (res != kOk)
        return res;
    }
  }

  if (shdr->IsBSlice()) {
    READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l1);
    if (shdr->ref_pic_list_modification_flag_l1) {
      res = ParseRefPicListModification(shdr->num_ref_idx_l1_active_minus1,
                                        shdr->ref_list_l1_modifications.data());
      if (res != kOk)
        return res;
    }
  }

  return kOk;
}

H264Parser::Result H264Parser::ParseWeightingFactors(
    int num_ref_idx_active_minus1,
    int chroma_array_type,
    int luma_log2_weight_denom,
    int chroma_log2_weight_denom,
    H264WeightingFactors* w_facts) {
  int def_luma_weight = 1 << luma_log2_weight_denom;
  int def_chroma_weight = 1 << chroma_log2_weight_denom;

  for (int i = 0; i < num_ref_idx_active_minus1 + 1; ++i) {
    READ_BOOL_OR_RETURN(&w_facts->luma_weight_flag);
    if (w_facts->luma_weight_flag) {
      READ_SE_OR_RETURN(&w_facts->luma_weight[i]);
      IN_RANGE_OR_RETURN(w_facts->luma_weight[i], -128, 127);

      READ_SE_OR_RETURN(&w_facts->luma_offset[i]);
      IN_RANGE_OR_RETURN(w_facts->luma_offset[i], -128, 127);
    } else {
      w_facts->luma_weight[i] = def_luma_weight;
      w_facts->luma_offset[i] = 0;
    }

    if (chroma_array_type != 0) {
      READ_BOOL_OR_RETURN(&w_facts->chroma_weight_flag);
      if (w_facts->chroma_weight_flag) {
        for (int j = 0; j < 2; ++j) {
          READ_SE_OR_RETURN(&w_facts->chroma_weight[i][j]);
          IN_RANGE_OR_RETURN(w_facts->chroma_weight[i][j], -128, 127);

          READ_SE_OR_RETURN(&w_facts->chroma_offset[i][j]);
          IN_RANGE_OR_RETURN(w_facts->chroma_offset[i][j], -128, 127);
        }
      } else {
        for (int j = 0; j < 2; ++j) {
          w_facts->chroma_weight[i][j] = def_chroma_weight;
          w_facts->chroma_offset[i][j] = 0;
        }
      }
    }
  }

  return kOk;
}

H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
                                                    H264SliceHeader* shdr) {
  READ_UE_OR_RETURN(&shdr->luma_log2_weight_denom);
  TRUE_OR_RETURN(shdr->luma_log2_weight_denom < 8);

  if (sps.chroma_array_type != 0)
    READ_UE_OR_RETURN(&shdr->chroma_log2_weight_denom);
  TRUE_OR_RETURN(shdr->chroma_log2_weight_denom < 8);

  Result res = ParseWeightingFactors(
      shdr->num_ref_idx_l0_active_minus1, sps.chroma_array_type,
      shdr->luma_log2_weight_denom, shdr->chroma_log2_weight_denom,
      &shdr->pred_weight_table_l0);
  if (res != kOk)
    return res;

  if (shdr->IsBSlice()) {
    res = ParseWeightingFactors(
        shdr->num_ref_idx_l1_active_minus1, sps.chroma_array_type,
        shdr->luma_log2_weight_denom, shdr->chroma_log2_weight_denom,
        &shdr->pred_weight_table_l1);
    if (res != kOk)
      return res;
  }

  return kOk;
}

H264Parser::Result H264Parser::ParseDecRefPicMarking(H264SliceHeader* shdr) {
  size_t bits_left_at_start = br_.NumBitsLeft();
  size_t epb_at_start = br_.NumEmulationPreventionBytesRead();

  if (shdr->idr_pic_flag) {
    READ_BOOL_OR_RETURN(&shdr->no_output_of_prior_pics_flag);
    READ_BOOL_OR_RETURN(&shdr->long_term_reference_flag);
  } else {
    READ_BOOL_OR_RETURN(&shdr->adaptive_ref_pic_marking_mode_flag);

    H264DecRefPicMarking* marking;
    if (shdr->adaptive_ref_pic_marking_mode_flag) {
      size_t i;
      for (i = 0; i < std::size(shdr->ref_pic_marking); ++i) {
        marking = &shdr->ref_pic_marking[i];

        READ_UE_OR_RETURN(&marking->memory_mgmnt_control_operation);
        if (marking->memory_mgmnt_control_operation == 0)
          break;

        if (marking->memory_mgmnt_control_operation == 1 ||
            marking->memory_mgmnt_control_operation == 3) {
          READ_UE_OR_RETURN(&marking->difference_of_pic_nums_minus1);
          // H.264 Section 7.4.3.3: difference_of_pic_nums_minus1 shall be in
          // [0, MaxPicNum - 1], MaxPicNum <= 65536.
          IN_RANGE_IF_OR_RETURN(marking->difference_of_pic_nums_minus1, 0,
                                65535, validate_extended_bitstream_);
        }

        if (marking->memory_mgmnt_control_operation == 2) {
          READ_UE_OR_RETURN(&marking->long_term_pic_num);
          // H.264 Section 7.4.3.3: long_term_pic_num in [0,
          // MaxLongTermFrameIdx], MaxLongTermFrameIdx <= max_num_ref_frames - 1
          // <= 15.
          IN_RANGE_IF_OR_RETURN(marking->long_term_pic_num, 0, 15,
                                validate_extended_bitstream_);
        }

        if (marking->memory_mgmnt_control_operation == 3 ||
            marking->memory_mgmnt_control_operation == 6) {
          READ_UE_OR_RETURN(&marking->long_term_frame_idx);
          // H.264 Section 7.4.3.3: long_term_frame_idx in [0,
          // MaxLongTermFrameIdx], MaxLongTermFrameIdx <= max_num_ref_frames - 1
          // <= 15.
          IN_RANGE_IF_OR_RETURN(marking->long_term_frame_idx, 0, 15,
                                validate_extended_bitstream_);
        }

        if (marking->memory_mgmnt_control_operation == 4) {
          READ_UE_OR_RETURN(&marking->max_long_term_frame_idx_plus1);
          // H.264 Section 7.4.3.3: max_long_term_frame_idx_plus1 in [0,
          // max_num_ref_frames], max_num_ref_frames <= 16.
          IN_RANGE_IF_OR_RETURN(marking->max_long_term_frame_idx_plus1, 0, 16,
                                validate_extended_bitstream_);
        }

        if (marking->memory_mgmnt_control_operation > 6)
          return kInvalidStream;
      }

      if (i == std::size(shdr->ref_pic_marking)) {
        DVLOG(1) << "Ran out of dec ref pic marking fields";
        return kUnsupportedStream;
      }
    }
  }

  shdr->dec_ref_pic_marking_bit_size =
      (bits_left_at_start - br_.NumBitsLeft()) -
      8 * (br_.NumEmulationPreventionBytesRead() - epb_at_start);
  return kOk;
}

H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu,
                                                H264SliceHeader* shdr) {
  // See 7.4.3.
  const H264SPS* sps;
  const H264PPS* pps;
  Result res;

  *shdr = H264SliceHeader();

  shdr->idr_pic_flag = (nalu.nal_unit_type == 5);
  shdr->nal_ref_idc = nalu.nal_ref_idc;
  shdr->nalu_data = nalu.data.data();
  shdr->nalu_size = nalu.data.size();

  READ_UE_OR_RETURN(&shdr->first_mb_in_slice);
  READ_UE_OR_RETURN(&shdr->slice_type);
  TRUE_OR_RETURN(shdr->slice_type < 10);

  READ_UE_OR_RETURN(&shdr->pic_parameter_set_id);

  pps = GetPPS(shdr->pic_parameter_set_id);
  TRUE_OR_RETURN(pps);

  sps = GetSPS(pps->seq_parameter_set_id);
  TRUE_OR_RETURN(sps);

  if (sps->separate_colour_plane_flag) {
    DVLOG(1) << "Interlaced streams not supported";
    return kUnsupportedStream;
  }

  READ_BITS_OR_RETURN(sps->log2_max_frame_num_minus4 + 4, &shdr->frame_num);
  if (!sps->frame_mbs_only_flag) {
    READ_BOOL_OR_RETURN(&shdr->field_pic_flag);
    if (shdr->field_pic_flag) {
      // Note that per-spec, the field_pic_flag should be used as a denominator
      // when calculating frame_height while checking pic_size_in_mbs below.
      // If interlaced streams ever become supported, additional arithmetic will
      // need to be added to the calculation of `frame_height_in_mbs`.
      DVLOG(1) << "Interlaced streams not supported";
      return kUnsupportedStream;
    }
  }

  // H.264 spec 7.4.3: first_mb_in_slice shall be in [0, PicSizeInMbs - 1].
  // Without this check the value flows unvalidated into
  // VASliceParameterBufferH264.first_mb_in_slice and is used by the VA-API
  // driver as a write offset into the decode surface.
  {
    base::CheckedNumeric<int> height = sps->pic_height_in_map_units_minus1;
    height += 1;
    height *= (2 - sps->frame_mbs_only_flag);
    base::CheckedNumeric<int> pic_size = sps->pic_width_in_mbs_minus1;
    pic_size += 1;
    pic_size *= height;
    TRUE_OR_RETURN(pic_size.IsValid());
    const int pic_size_in_mbs = pic_size.ValueOrDie();
    IN_RANGE_OR_RETURN(shdr->first_mb_in_slice, 0, pic_size_in_mbs - 1);
  }

  if (shdr->idr_pic_flag) {
    READ_UE_OR_RETURN(&shdr->idr_pic_id);
    IN_RANGE_OR_RETURN(shdr->idr_pic_id, 0, 65535);
  }

  size_t bits_left_at_pic_order_cnt_start = br_.NumBitsLeft();
  size_t epb_at_pic_order_cnt_start = br_.NumEmulationPreventionBytesRead();
  if (sps->pic_order_cnt_type == 0) {
    READ_BITS_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 + 4,
                        &shdr->pic_order_cnt_lsb);
    if (pps->bottom_field_pic_order_in_frame_present_flag &&
        !shdr->field_pic_flag)
      READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt_bottom);
  }

  if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) {
    READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt0);
    if (pps->bottom_field_pic_order_in_frame_present_flag &&
        !shdr->field_pic_flag)
      READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt1);
  }

  shdr->pic_order_cnt_bit_size =
      (bits_left_at_pic_order_cnt_start - br_.NumBitsLeft()) -
      8 * (br_.NumEmulationPreventionBytesRead() - epb_at_pic_order_cnt_start);

  if (pps->redundant_pic_cnt_present_flag) {
    READ_UE_OR_RETURN(&shdr->redundant_pic_cnt);
    TRUE_OR_RETURN(shdr->redundant_pic_cnt < 128);
  }

  if (shdr->IsBSlice())
    READ_BOOL_OR_RETURN(&shdr->direct_spatial_mv_pred_flag);

  if (shdr->IsPSlice() || shdr->IsSPSlice() || shdr->IsBSlice()) {
    READ_BOOL_OR_RETURN(&shdr->num_ref_idx_active_override_flag);
    if (shdr->num_ref_idx_active_override_flag) {
      READ_UE_OR_RETURN(&shdr->num_ref_idx_l0_active_minus1);
      if (shdr->IsBSlice())
        READ_UE_OR_RETURN(&shdr->num_ref_idx_l1_active_minus1);
    } else {
      shdr->num_ref_idx_l0_active_minus1 =
          pps->num_ref_idx_l0_default_active_minus1;
      if (shdr->IsBSlice()) {
        shdr->num_ref_idx_l1_active_minus1 =
            pps->num_ref_idx_l1_default_active_minus1;
      }
    }
  }
  if (shdr->field_pic_flag) {
    TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 32);
    TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 32);
  } else {
    TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 16);
    TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 16);
  }

  if (nalu.nal_unit_type == H264NALU::kCodedSliceExtension) {
    return kUnsupportedStream;
  } else {
    res = ParseRefPicListModifications(shdr);
    if (res != kOk)
      return res;
  }

  if ((pps->weighted_pred_flag && (shdr->IsPSlice() || shdr->IsSPSlice())) ||
      (pps->weighted_bipred_idc == 1 && shdr->IsBSlice())) {
    res = ParsePredWeightTable(*sps, shdr);
    if (res != kOk)
      return res;
  }

  if (nalu.nal_ref_idc != 0) {
    res = ParseDecRefPicMarking(shdr);
    if (res != kOk)
      return res;
  }

  if (pps->entropy_coding_mode_flag && !shdr->IsISlice() &&
      !shdr->IsSISlice()) {
    READ_UE_OR_RETURN(&shdr->cabac_init_idc);
    TRUE_OR_RETURN(shdr->cabac_init_idc < 3);
  }

  READ_SE_OR_RETURN(&shdr->slice_qp_delta);
  // SliceQPY = 26 + pic_init_qp_minus26 + slice_qp_delta
  // -QpBdOffset_Y <= SliceQPY <= 51
  int qp_bd_offset_y = 6 * sps->bit_depth_luma_minus8;
  int base_qp = 26 + pps->pic_init_qp_minus26;
  IN_RANGE_OR_RETURN(shdr->slice_qp_delta, -qp_bd_offset_y - base_qp,
                     51 - base_qp);

  if (shdr->IsSPSlice() || shdr->IsSISlice()) {
    if (shdr->IsSPSlice()) {
      READ_BOOL_OR_RETURN(&shdr->sp_for_switch_flag);
    }
    READ_SE_OR_RETURN(&shdr->slice_qs_delta);
    // SliceQSY = 26 + pic_init_qs_minus26 + slice_qs_delta
    // 0 <= SliceQSY <= 51
    int base_qs = 26 + pps->pic_init_qs_minus26;
    IN_RANGE_OR_RETURN(shdr->slice_qs_delta, -base_qs, 51 - base_qs);
  }

  if (pps->deblocking_filter_control_present_flag) {
    READ_UE_OR_RETURN(&shdr->disable_deblocking_filter_idc);
    TRUE_OR_RETURN(shdr->disable_deblocking_filter_idc < 3);

    if (shdr->disable_deblocking_filter_idc != 1) {
      READ_SE_OR_RETURN(&shdr->slice_alpha_c0_offset_div2);
      IN_RANGE_OR_RETURN(shdr->slice_alpha_c0_offset_div2, -6, 6);

      READ_SE_OR_RETURN(&shdr->slice_beta_offset_div2);
      IN_RANGE_OR_RETURN(shdr->slice_beta_offset_div2, -6, 6);
    }
  }

  if (pps->num_slice_groups_minus1 > 0) {
    DVLOG(1) << "Slice groups not supported";
    return kUnsupportedStream;
  }

  size_t epb = br_.NumEmulationPreventionBytesRead();
  shdr->header_bit_size = (shdr->nalu_size - epb) * 8 - br_.NumBitsLeft();

  return kOk;
}

H264Parser::Result H264Parser::ParseSEI(H264SEI* sei) {
  int byte;
  int num_parsed_sei_msg = 0;
  // According to spec 7.3.2.3, we should loop parsing SEI NALU
  // as long as `more_rbsp_data` condition is true, which means
  // if the NALU's RBSP data is large enough and `more_rbsp_data`
  // condition keeps true all the time, we are very likely have to
  // loop the parsing millions of times.
  //
  // The spec doesn't provide any pattern to let us validate the
  // the parsed SEI messages, so we have to set a limit here.
  constexpr int kMaxParsedSEIMessages = 64;
  do {
    base::CheckedNumeric<int> type_checked = 0;
    READ_BITS_OR_RETURN(8, &byte);
    while (byte == 0xff) {
      type_checked += 255;
      READ_BITS_OR_RETURN(8, &byte);
    }
    type_checked += byte;

    if (!type_checked.IsValid()) {
      DVLOG(1) << "SEI type overflow";
      return kInvalidStream;
    }
    int type = type_checked.ValueOrDie();

    base::CheckedNumeric<int> payload_size_checked = 0;
    READ_BITS_OR_RETURN(8, &byte);
    while (byte == 0xff) {
      payload_size_checked += 255;
      READ_BITS_OR_RETURN(8, &byte);
    }
    payload_size_checked += byte;

    if (!payload_size_checked.IsValid()) {
      DVLOG(1) << "SEI payload size overflow";
      return kInvalidStream;
    }

    int payload_size = payload_size_checked.ValueOrDie();
    base::CheckedNumeric<int> num_bits_remain_checked =
        payload_size_checked * 8;

    if (!num_bits_remain_checked.IsValid()) {
      DVLOG(1) << "SEI payload bits overflow";
      return kInvalidStream;
    }

    int num_bits_remain = num_bits_remain_checked.ValueOrDie();

    DVLOG(4) << "Found SEI message type: " << type
             << " payload size: " << payload_size;

    enum Type {
      kSEIUserDataRegisteredItuTT35 = 4,
      kSEIRecoveryPoint = 6,
      kSEIMasteringDisplayInfo = 137,
      kSEIContentLightLevelInfo = 144,
    };

    H264SEIMessage sei_msg;
    switch (type) {
      case kSEIUserDataRegisteredItuTT35: {
        auto& itu_t_t35 = sei_msg.emplace<H26xSEIUserDataRegisteredT35>();
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(8, &byte, &num_bits_remain);
        itu_t_t35.country_code = byte;
        if (itu_t_t35.country_code == 0xff) {
          READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(8, &byte, &num_bits_remain);
          itu_t_t35.country_code_extension_byte = byte;
        }
        RETURN_IF_NUM_BITS_REMAIN_NEGATIVE(num_bits_remain);
        size_t payload_bytes = num_bits_remain / 8;
        if (payload_bytes > 0) {
          itu_t_t35.payload = base::HeapArray<uint8_t>::Uninit(payload_bytes);
          for (size_t i = 0; i < payload_bytes; ++i) {
            READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(8, &byte, &num_bits_remain);
            itu_t_t35.payload[i] = byte;
          }
        }
        break;
      }
      case kSEIRecoveryPoint: {
        auto& recovery_point = sei_msg.emplace<H264SEIRecoveryPoint>();
        READ_UE_AND_MINUS_BITS_READ_OR_RETURN(
            &recovery_point.recovery_frame_cnt, &num_bits_remain);
        IN_RANGE_IF_OR_RETURN(recovery_point.recovery_frame_cnt, 0, 65535,
                              validate_extended_bitstream_);
        READ_BOOL_AND_MINUS_BITS_READ_OR_RETURN(
            &recovery_point.exact_match_flag, &num_bits_remain);
        READ_BOOL_AND_MINUS_BITS_READ_OR_RETURN(
            &recovery_point.broken_link_flag, &num_bits_remain);
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(
            2, &recovery_point.changing_slice_group_idc, &num_bits_remain);
        IN_RANGE_OR_RETURN(recovery_point.changing_slice_group_idc, 0, 2);
        break;
      }
      case kSEIContentLightLevelInfo: {
        auto& info = sei_msg.emplace<H26xSEIContentLightLevelInfo>();
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(
            16, &info.max_content_light_level, &num_bits_remain);
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(
            16, &info.max_picture_average_light_level, &num_bits_remain);
        break;
      }
      case kSEIMasteringDisplayInfo: {
        auto& info = sei_msg.emplace<H26xSEIMasteringDisplayInfo>();
        for (auto& primary : info.display_primaries) {
          for (auto& component : primary) {
            READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(16, &component,
                                                    &num_bits_remain);
          }
        }
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(16, &info.white_points[0],
                                                &num_bits_remain);
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(16, &info.white_points[1],
                                                &num_bits_remain);
        uint32_t luminace_high_31bits, luminance_low_1bit;
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(31, &luminace_high_31bits,
                                                &num_bits_remain);
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(1, &luminance_low_1bit,
                                                &num_bits_remain);
        info.max_luminance =
            (luminace_high_31bits << 1) + (luminance_low_1bit & 0x1);
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(31, &luminace_high_31bits,
                                                &num_bits_remain);
        READ_BITS_AND_MINUS_BITS_READ_OR_RETURN(1, &luminance_low_1bit,
                                                &num_bits_remain);
        info.min_luminance =
            (luminace_high_31bits << 1) + (luminance_low_1bit & 0x1);
        break;
      }
      default:
        DVLOG(4) << "Unsupported SEI message";
        break;
    }
    TRUE_OR_RETURN(num_bits_remain >= 0);
    // D.1.1 Not byted aligned in payload or unsupported SEI, skip bits.
    if (num_bits_remain > 0)
      SKIP_BITS_OR_RETURN(num_bits_remain);
    // Only add parsed SEI messages.
    if (num_bits_remain < payload_size * 8) {
      sei->msgs.push_back(std::move(sei_msg));
    }
    // In case the loop endless.
    if (++num_parsed_sei_msg > kMaxParsedSEIMessages)
      return kInvalidStream;
  } while (br_.HasMoreRBSPData());

  return kOk;
}

std::vector<SubsampleEntry> H264Parser::GetCurrentSubsamples() {
  DCHECK_EQ(previous_nalu_range_.size(), 1u)
      << "This should only be called after a "
         "successful call to AdvanceToNextNalu()";

  auto intersection = encrypted_ranges_.IntersectionWith(previous_nalu_range_);
  return EncryptedRangesToSubsampleEntry(
      previous_nalu_range_.start(0), previous_nalu_range_.end(0), intersection);
}

}  // namespace media
