// Copyright 2022 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/gpu/windows/d3d_h265_accelerator.h"

#include <algorithm>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.h"
#include "media/base/win/mf_helpers.h"
#include "media/gpu/windows/d3d11_picture_buffer.h"
#include "media/media_buildflags.h"
#include "third_party/angle/include/EGL/egl.h"
#include "third_party/angle/include/EGL/eglext.h"
#include "ui/gfx/color_space.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/scoped_binders.h"

namespace media {

namespace {

using H265DecoderStatus = H265Decoder::H265Accelerator::Status;

}  // namespace

class D3D11H265Picture : public H265Picture {
 public:
  D3D11H265Picture(D3D11PictureBuffer* picture)
      : picture(picture), picture_index_(picture->picture_index()) {
    picture->set_in_picture_use(true);
  }

  raw_ptr<D3D11PictureBuffer> picture;
  size_t picture_index_;

  D3D11H265Picture* AsD3D11H265Picture() override { return this; }

 protected:
  ~D3D11H265Picture() override;
};

D3D11H265Picture::~D3D11H265Picture() {
  picture->set_in_picture_use(false);
}

D3DH265Accelerator::D3DH265Accelerator(D3DVideoDecoderClient* client,
                                       MediaLog* media_log,
                                       bool use_dxva_device_for_hevc_rext)
    : media_log_(media_log->Clone()),
      client_(client),
      use_dxva_device_for_hevc_rext_(use_dxva_device_for_hevc_rext) {
  DCHECK(client_);
}

D3DH265Accelerator::~D3DH265Accelerator() {}

scoped_refptr<H265Picture> D3DH265Accelerator::CreateH265Picture() {
  D3D11PictureBuffer* picture = client_->GetPicture();
  if (!picture) {
    return nullptr;
  }
  return base::MakeRefCounted<D3D11H265Picture>(picture);
}

bool D3DH265Accelerator::IsChromaSamplingSupported(
    VideoChromaSampling chroma_sampling) {
  return chroma_sampling == VideoChromaSampling::k420 ||
         chroma_sampling == VideoChromaSampling::k422 ||
         chroma_sampling == VideoChromaSampling::k444;
}

H265DecoderStatus D3DH265Accelerator::SubmitFrameMetadata(
    const H265SPS* sps,
    const H265PPS* pps,
    const H265SliceHeader* slice_hdr,
    const H265Picture::Vector& ref_pic_list,
    const H265Picture::Vector& ref_pic_set_lt_curr,
    const H265Picture::Vector& ref_pic_set_st_curr_after,
    const H265Picture::Vector& ref_pic_set_st_curr_before,
    scoped_refptr<H265Picture> pic) {
  D3D11H265Picture* d3d11_pic = pic->AsD3D11H265Picture();
  if (!d3d11_pic) {
    return H265DecoderStatus::kFail;
  }

  if (!client_->GetWrapper()->WaitForFrameBegins(d3d11_pic->picture.get())) {
    return H265DecoderStatus::kFail;
  }

  use_scaling_lists_ = sps->scaling_list_enabled_flag;

  poc_index_into_ref_pic_list_.clear();
  for (size_t i = 0; i < media::kMaxRefPicListSize; i++) {
    UNSAFE_TODO(ref_frame_list_[i].bPicEntry) = 0xFF;
    UNSAFE_TODO(ref_frame_pocs_[i]) = 0;
  }

  // |ref_pic_list| contains the set of pictures as described
  // in HEVC spec section 8.3.2, from the lists RefPicSetLtCurr,
  // RefPicSetLtFoll, RefPicSetStCurrBefore, RefPicSetStCurrAfter
  // and RefPicSetStFoll. When submitting a slice, will use information
  // in ref_pic_list0 and ref_pic_list1 to fill POCs of corresponding
  // list in picture param.
  if (ref_pic_list.size() > kMaxRefPicListSize) {
    DLOG(ERROR) << "Invalid fef pic list size.";
    return H265DecoderStatus::kFail;
  }

  int i = 0;
  for (auto& it : ref_pic_list) {
    if (!it) {
      continue;
    }
    D3D11H265Picture* our_ref_pic = it->AsD3D11H265Picture();
    if (!our_ref_pic) {
      continue;
    }
    UNSAFE_TODO(ref_frame_list_[i].Index7Bits) = our_ref_pic->picture_index_;
    UNSAFE_TODO(ref_frame_list_[i].AssociatedFlag) =
        our_ref_pic->IsLongTermRef();
    UNSAFE_TODO(ref_frame_pocs_[i]) = our_ref_pic->pic_order_cnt_val_;
    poc_index_into_ref_pic_list_[our_ref_pic->pic_order_cnt_val_] = i;
    i++;
  }
  return H265DecoderStatus::kOk;
}

void D3DH265Accelerator::FillPicParamsWithConstants(
    DXVA_PicParams_HEVC_Rext* pic_param) {
  std::visit(
      [&](auto& pic) {
        // According to DXVA spec section 2.2, this optional 1-bit flag
        // has no meaning when used for CurrPic so always configure to 0.
        pic.params.CurrPic.AssociatedFlag = 0;

        // num_tile_columns_minus1 and num_tile_rows_minus1 will only
        // be set if tiles are enabled. Set to 0 by default.
        pic.params.num_tile_columns_minus1 = 0;
        pic.params.num_tile_rows_minus1 = 0;

        // Host decoder may set this to 1 if sps_max_num_reorder_pics is 0,
        // but there is no requirement that NoPicReorderingFlag must be
        // derived from it. So we always set it to 0 here.
        pic.params.NoPicReorderingFlag = 0;

        // Must be set to 0 in absence of indication whether B slices are used
        // or not, and it does not affect the decoding process.
        pic.params.NoBiPredFlag = 0;

        // Shall be set to 0 and accelerators shall ignore its value.
        pic.params.ReservedBits1 = 0;

        // Bit field added to enable DWORD alignment and should be set to 0.
        pic.params.ReservedBits2 = 0;

        // Should always be set to 0.
        pic.params.ReservedBits3 = 0;

        // Should be set to 0 and ignored by accelerators
        pic.params.ReservedBits4 = 0;

        // Should always be set to 0.
        pic.params.ReservedBits5 = 0;

        // Should always be set to 0.
        pic.params.ReservedBits6 = 0;

        // Should always be set to 0.
        pic.params.ReservedBits7 = 0;
      },
      *pic_param);
}

#define ARG_SEL(_1, _2, NAME, ...) NAME
#define SPS_TO_PP1(a) (pic_param.params).a = sps->a;
#define SPS_TO_PPEXT(a) pic_param.a = sps->a;
#define SPS_TO_PP2(a, b) (pic_param.params).a = sps->b;
#define SPS_TO_PP(...) ARG_SEL(__VA_ARGS__, SPS_TO_PP2, SPS_TO_PP1)(__VA_ARGS__)
void D3DH265Accelerator::PicParamsFromSPS(DXVA_PicParams_HEVC_Rext* pic_params,
                                          const H265SPS* sps) {
  std::visit(
      [&](auto& pic_param) {
        // Refer to formula 7-14 and 7-16 of HEVC spec.
        int min_cb_log2_size_y =
            sps->log2_min_luma_coding_block_size_minus3 + 3;
        (pic_param.params).PicWidthInMinCbsY =
            sps->pic_width_in_luma_samples >> min_cb_log2_size_y;
        (pic_param.params).PicHeightInMinCbsY =
            sps->pic_height_in_luma_samples >> min_cb_log2_size_y;
        // wFormatAndSequenceInfoFlags from SPS
        SPS_TO_PP(chroma_format_idc);
        SPS_TO_PP(separate_colour_plane_flag);
        SPS_TO_PP(bit_depth_luma_minus8);
        SPS_TO_PP(bit_depth_chroma_minus8);
        SPS_TO_PP(log2_max_pic_order_cnt_lsb_minus4);

        is_rext_ = sps->profile_tier_level.general_profile_idc == 4;
        // HEVC DXVA spec does not clearly state which slot
        // in sps->sps_max_dec_pic_buffering_minus1 should
        // be used here. However section A4.1 of HEVC spec
        // requires the slot of highest tid to be used for
        // indicating the maximum DPB size if level is not
        // 8.5.
        int highest_tid = sps->sps_max_sub_layers_minus1;
        (pic_param.params).sps_max_dec_pic_buffering_minus1 =
            sps->sps_max_dec_pic_buffering_minus1[highest_tid];

        SPS_TO_PP(log2_min_luma_coding_block_size_minus3);
        SPS_TO_PP(log2_diff_max_min_luma_coding_block_size);

        // DXVA spec names them differently with HEVC spec.
        SPS_TO_PP(log2_min_transform_block_size_minus2,
                  log2_min_luma_transform_block_size_minus2);
        SPS_TO_PP(log2_diff_max_min_transform_block_size,
                  log2_diff_max_min_luma_transform_block_size);

        SPS_TO_PP(max_transform_hierarchy_depth_inter);
        SPS_TO_PP(max_transform_hierarchy_depth_intra);
        SPS_TO_PP(num_short_term_ref_pic_sets);
        SPS_TO_PP(num_long_term_ref_pics_sps);

        // dwCodingParamToolFlags extracted from SPS
        SPS_TO_PP(scaling_list_enabled_flag);
        SPS_TO_PP(amp_enabled_flag);
        SPS_TO_PP(sample_adaptive_offset_enabled_flag);
        SPS_TO_PP(pcm_enabled_flag);

        if (sps->pcm_enabled_flag) {
          SPS_TO_PP(pcm_sample_bit_depth_luma_minus1);
          SPS_TO_PP(pcm_sample_bit_depth_chroma_minus1);
          SPS_TO_PP(log2_min_pcm_luma_coding_block_size_minus3);
          SPS_TO_PP(log2_diff_max_min_pcm_luma_coding_block_size);
          SPS_TO_PP(pcm_loop_filter_disabled_flag);
        }
        SPS_TO_PP(long_term_ref_pics_present_flag);
        SPS_TO_PP(sps_temporal_mvp_enabled_flag);
        SPS_TO_PP(strong_intra_smoothing_enabled_flag);

        if (sps->sps_range_extension_flag) {
          SPS_TO_PPEXT(transform_skip_rotation_enabled_flag);
          SPS_TO_PPEXT(transform_skip_context_enabled_flag);
          SPS_TO_PPEXT(implicit_rdpcm_enabled_flag);
          SPS_TO_PPEXT(explicit_rdpcm_enabled_flag);
          SPS_TO_PPEXT(extended_precision_processing_flag);
          SPS_TO_PPEXT(intra_smoothing_disabled_flag);
          SPS_TO_PPEXT(high_precision_offsets_enabled_flag);
          SPS_TO_PPEXT(persistent_rice_adaptation_enabled_flag);
          SPS_TO_PPEXT(cabac_bypass_alignment_enabled_flag);
        }
      },
      *pic_params);
}
#undef SPS_TO_PP
#undef SPS_TO_PPEXT
#undef SPS_TO_PP2
#undef SPS_TO_PP1

#define PPS_TO_PPEXT(a) pic_param.a = pps->a;
#define PPS_TO_PP1(a) (pic_param.params).a = pps->a;
#define PPS_TO_PP2(a, b) (pic_param.params).a = pps->b;
#define PPS_TO_PP(...) ARG_SEL(__VA_ARGS__, PPS_TO_PP2, PPS_TO_PP1)(__VA_ARGS__)
void D3DH265Accelerator::PicParamsFromPPS(DXVA_PicParams_HEVC_Rext* pic_params,
                                          const H265PPS* pps) {
  std::visit(
      [&](auto& pic_param) {
        PPS_TO_PP(num_ref_idx_l0_default_active_minus1);
        PPS_TO_PP(num_ref_idx_l1_default_active_minus1);
        PPS_TO_PP(init_qp_minus26);

        // dwCodingParamToolFlags from PPS
        PPS_TO_PP(dependent_slice_segments_enabled_flag);
        PPS_TO_PP(output_flag_present_flag);
        PPS_TO_PP(num_extra_slice_header_bits);
        PPS_TO_PP(sign_data_hiding_enabled_flag);
        PPS_TO_PP(cabac_init_present_flag);

        // dwCodingSettingPicturePropertyFlags from PPS
        PPS_TO_PP(constrained_intra_pred_flag);
        PPS_TO_PP(transform_skip_enabled_flag);
        PPS_TO_PP(cu_qp_delta_enabled_flag);
        PPS_TO_PP(pps_slice_chroma_qp_offsets_present_flag);
        PPS_TO_PP(weighted_pred_flag);
        PPS_TO_PP(weighted_bipred_flag);
        PPS_TO_PP(transquant_bypass_enabled_flag);
        PPS_TO_PP(tiles_enabled_flag);
        PPS_TO_PP(entropy_coding_sync_enabled_flag);
        PPS_TO_PP(uniform_spacing_flag);
        if (pps->tiles_enabled_flag) {
          PPS_TO_PP(loop_filter_across_tiles_enabled_flag);
        }
        PPS_TO_PP(pps_loop_filter_across_slices_enabled_flag);
        PPS_TO_PP(deblocking_filter_override_enabled_flag);
        PPS_TO_PP(pps_deblocking_filter_disabled_flag);
        PPS_TO_PP(lists_modification_present_flag);
        PPS_TO_PP(slice_segment_header_extension_present_flag);

        PPS_TO_PP(pps_cb_qp_offset);
        PPS_TO_PP(pps_cr_qp_offset);
        if (pps->tiles_enabled_flag) {
          PPS_TO_PP(num_tile_columns_minus1);
          PPS_TO_PP(num_tile_rows_minus1);
          if (!pps->uniform_spacing_flag) {
            for (int i = 0; i <= pps->num_tile_columns_minus1; i++) {
              PPS_TO_PP(column_width_minus1[i]);
            }
            for (int j = 0; j <= pps->num_tile_rows_minus1; j++) {
              PPS_TO_PP(row_height_minus1[j]);
            }
          }
        }
        PPS_TO_PP(diff_cu_qp_delta_depth);
        PPS_TO_PP(pps_beta_offset_div2);
        PPS_TO_PP(pps_tc_offset_div2);
        PPS_TO_PP(log2_parallel_merge_level_minus2);

        if (pps->pps_range_extension_flag) {
          PPS_TO_PPEXT(cross_component_prediction_enabled_flag);
          PPS_TO_PPEXT(chroma_qp_offset_list_enabled_flag);
          if (pps->chroma_qp_offset_list_enabled_flag) {
            PPS_TO_PPEXT(diff_cu_chroma_qp_offset_depth);
            PPS_TO_PPEXT(chroma_qp_offset_list_len_minus1);
            for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) {
              PPS_TO_PPEXT(cb_qp_offset_list[i]);
              PPS_TO_PPEXT(cr_qp_offset_list[i]);
            }
          }
          PPS_TO_PPEXT(log2_sao_offset_scale_luma);
          PPS_TO_PPEXT(log2_sao_offset_scale_chroma);
          if (pps->transform_skip_enabled_flag) {
            PPS_TO_PPEXT(log2_max_transform_skip_block_size_minus2);
          }
        }
      },
      *pic_params);
  return;
}
#undef PPS_TO_PPEXT
#undef PPS_TO_PP
#undef PPS_TO_PP2
#undef PPS_TO_PP1
#undef ARG_SEL

void D3DH265Accelerator::PicParamsFromSliceHeader(
    DXVA_PicParams_HEVC_Rext* pic_params,
    const H265SPS* sps,
    const H265SliceHeader* slice_hdr) {
  std::visit(
      [&](auto& pic_param) {
        // IDR_W_RADL and IDR_N_LP NALUs do not contain st_rps in slice header.
        // Otherwise if short_term_ref_pic_set_sps_flag is 1, host decoder
        // shall set ucNumDeltaPocsOfRefRpsIdx to 0.
        if (slice_hdr->short_term_ref_pic_set_sps_flag) {
          pic_param.params.ucNumDeltaPocsOfRefRpsIdx = 0;
          pic_param.params.wNumBitsForShortTermRPSInSlice = 0;
        } else {
          pic_param.params.ucNumDeltaPocsOfRefRpsIdx =
              slice_hdr->st_ref_pic_set.rps_idx_num_delta_pocs;
          pic_param.params.wNumBitsForShortTermRPSInSlice =
              slice_hdr->st_rps_bits;
        }
        pic_param.params.IrapPicFlag = slice_hdr->irap_pic;
        auto nal_unit_type = slice_hdr->nal_unit_type;
        pic_param.params.IdrPicFlag = (nal_unit_type == H265NALU::IDR_W_RADL ||
                                       nal_unit_type == H265NALU::IDR_N_LP);
        pic_param.params.IntraPicFlag = slice_hdr->irap_pic;
      },
      *pic_params);
}

void D3DH265Accelerator::PicParamsFromPic(DXVA_PicParams_HEVC_Rext* pic_params,
                                          D3D11H265Picture* pic) {
  std::visit(
      [&](auto& pic_param) {
        pic_param.params.CurrPicOrderCntVal = pic->pic_order_cnt_val_;
        pic_param.params.CurrPic.Index7Bits = pic->picture_index_;
      },
      *pic_params);
}

bool D3DH265Accelerator::PicParamsFromRefLists(
    DXVA_PicParams_HEVC_Rext* pic_params,
    const H265Picture::Vector& ref_pic_set_lt_curr,
    const H265Picture::Vector& ref_pic_set_st_curr_after,
    const H265Picture::Vector& ref_pic_set_st_curr_before) {
  constexpr int kDxvaInvalidRefPicIndex = 0xFF;

  bool ref_check_ok = true;
  std::visit(
      [&](auto& pic_param) {
        // Fills one of the fixed-size reference picture set arrays in
        // DXVA_PicParams_HEVC. The bound comes from the destination array
        // itself instead of a local constant, so this cannot write past the
        // end if the array in the Windows SDK is ever a different size than
        // this code assumes.
        auto fill_ref_pic_set = [&](auto& dest_array,
                                    const H265Picture::Vector& ref_pic_set,
                                    const char* rps_name) {
          auto dest = base::span(dest_array);
          std::ranges::fill(dest, kDxvaInvalidRefPicIndex);

          size_t idx = 0;
          for (const auto& pic : ref_pic_set) {
            if (!pic) {
              continue;
            }
            if (idx >= dest.size()) {
              DLOG(ERROR) << "Invalid " << rps_name << " size.";
              ref_check_ok = false;
              return;
            }
            int poc_index =
                poc_index_into_ref_pic_list_[pic->pic_order_cnt_val_];
            if (poc_index < 0) {
              DLOG(ERROR) << "Invalid index of POC for " << rps_name << ".";
              ref_check_ok = false;
              poc_index = kDxvaInvalidRefPicIndex;
            }
            dest[idx++] = poc_index;
          }
        };

        fill_ref_pic_set(pic_param.params.RefPicSetStCurrBefore,
                         ref_pic_set_st_curr_before, "RefPicSetStCurrBefore");
        fill_ref_pic_set(pic_param.params.RefPicSetStCurrAfter,
                         ref_pic_set_st_curr_after, "RefPicSetStCurrAfter");
        fill_ref_pic_set(pic_param.params.RefPicSetLtCurr, ref_pic_set_lt_curr,
                         "RefPicSetLtCurr");

        base::span(pic_param.params.PicOrderCntValList)
            .copy_prefix_from(
                base::span(ref_frame_pocs_).first(kMaxRefPicListSize - 1));
      },
      *pic_params);

  return ref_check_ok;
}

H265DecoderStatus D3DH265Accelerator::SubmitSlice(
    const H265SPS* sps,
    const H265PPS* pps,
    const H265SliceHeader* slice_hdr,
    const H265Picture::Vector& ref_pic_list0,
    const H265Picture::Vector& ref_pic_list1,
    const H265Picture::Vector& ref_pic_set_lt_curr,
    const H265Picture::Vector& ref_pic_set_st_curr_after,
    const H265Picture::Vector& ref_pic_set_st_curr_before,
    scoped_refptr<H265Picture> pic,
    const uint8_t* data,
    size_t size,
    const std::vector<SubsampleEntry>& subsamples) {
  if (!client_->GetWrapper()->HasPendingBuffer(
          D3DVideoDecoderWrapper::BufferType::kPictureParameters)) {
    DXVA_PicParams_HEVC_Rext pic_param;

    if (use_dxva_device_for_hevc_rext_) {
      pic_param.emplace<DXVA_PicParams_HEVC_RangeExt>(
          DXVA_PicParams_HEVC_RangeExt{});
    } else {
      pic_param.emplace<DXVA_PicParams_HEVC_Rext_Intel>(
          DXVA_PicParams_HEVC_Rext_Intel{});
    }

    D3D11H265Picture* d3d11_pic = pic->AsD3D11H265Picture();
    if (!d3d11_pic) {
      return H265DecoderStatus::kFail;
    }

    FillPicParamsWithConstants(&pic_param);
    PicParamsFromSPS(&pic_param, sps);
    PicParamsFromPPS(&pic_param, pps);
    PicParamsFromSliceHeader(&pic_param, sps, slice_hdr);
    PicParamsFromPic(&pic_param, d3d11_pic);

    std::visit(
        [&](auto& param) {
          base::span<DXVA_PicEntry_HEVC>(param.params.RefPicList)
              .copy_from(base::span<const DXVA_PicEntry_HEVC>(ref_frame_list_));
        },
        pic_param);

    if (!PicParamsFromRefLists(&pic_param, ref_pic_set_lt_curr,
                               ref_pic_set_st_curr_after,
                               ref_pic_set_st_curr_before)) {
      return H265DecoderStatus::kFail;
    }

    std::visit(
        [&](auto& param) {
          param.params.StatusReportFeedbackNumber =
              current_status_report_feedback_num_++;
        },
        pic_param);

    // Determine the size of the selected type.
    size_t pic_params_size =
        is_rext_ ? (use_dxva_device_for_hevc_rext_
                        ? sizeof(DXVA_PicParams_HEVC_RangeExt)
                        : sizeof(DXVA_PicParams_HEVC_Rext_Intel))
                 : sizeof(DXVA_PicParams_HEVC);
    auto params_buffer =
        client_->GetWrapper()->GetPictureParametersBuffer(pic_params_size);
    // For 420 content the driver may only allow main part picture parameters.
    if (is_rext_ && params_buffer.size() < pic_params_size) {
      pic_params_size = sizeof(DXVA_PicParams_HEVC);
    }
    if (params_buffer.size() < pic_params_size) {
      MEDIA_LOG(ERROR, media_log_)
          << "Insufficient picture parameter buffer size";
      return H265DecoderStatus::kFail;
    }

    std::visit(
        [&](const auto& param) {
          params_buffer.data().copy_prefix_from(
              base::byte_span_from_ref(param).first(pic_params_size));
        },
        pic_param);

    if (!params_buffer.Commit()) {
      return H265DecoderStatus::kFail;
    }
  }

  // Fill up the quantitization matrix data structure when
  // pps->scaling_list_enabled is true. See section 4.2
  // of DXVA spec for HEVC.
  if (use_scaling_lists_ &&
      !client_->GetWrapper()->HasPendingBuffer(
          D3DVideoDecoderWrapper::BufferType::kInverseQuantizationMatrix)) {
    DXVA_Qmatrix_HEVC iq_matrix = {};
    const H265ScalingListData* scaling_lists =
        pps->pps_scaling_list_data_present_flag ? &pps->scaling_list_data
                                                : &sps->scaling_list_data;

    static_assert(
        std::is_same<
            std::remove_reference_t<decltype(iq_matrix.ucScalingLists0[0][0])>,
            std::remove_const_t<std::remove_reference_t<
                decltype(scaling_lists->scaling_list_4x4[0][0])>>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists0)>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_4x4)>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists0[0])>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_4x4[0])>>::value);
    UNSAFE_TODO(memcpy(iq_matrix.ucScalingLists0,
                       scaling_lists->scaling_list_4x4.data(),
                       sizeof iq_matrix.ucScalingLists0));

    static_assert(
        std::is_same<
            std::remove_reference_t<decltype(iq_matrix.ucScalingLists1[0][0])>,
            std::remove_const_t<std::remove_reference_t<
                decltype(scaling_lists->scaling_list_8x8[0][0])>>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists1)>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_8x8)>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists1[0])>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_8x8[0])>>::value);
    UNSAFE_TODO(memcpy(iq_matrix.ucScalingLists1,
                       scaling_lists->scaling_list_8x8.data(),
                       sizeof iq_matrix.ucScalingLists1));

    static_assert(
        std::is_same<
            std::remove_reference_t<decltype(iq_matrix.ucScalingLists2[0][0])>,
            std::remove_const_t<std::remove_reference_t<
                decltype(scaling_lists->scaling_list_16x16[0][0])>>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists2)>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_16x16)>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists2[0])>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_16x16[0])>>::value);
    UNSAFE_TODO(memcpy(iq_matrix.ucScalingLists2,
                       scaling_lists->scaling_list_16x16.data(),
                       sizeof iq_matrix.ucScalingLists2));

    static_assert(
        std::is_same<
            std::remove_reference_t<decltype(iq_matrix.ucScalingLists3[0][0])>,
            std::remove_const_t<std::remove_reference_t<
                decltype(scaling_lists->scaling_list_32x32[0][0])>>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists3)>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_32x32)>>::value);
    static_assert(std::extent<decltype(iq_matrix.ucScalingLists3[0])>() <=
                  std::tuple_size<std::remove_reference_t<
                      decltype(scaling_lists->scaling_list_32x32[0])>>::value);
    UNSAFE_TODO(memcpy(iq_matrix.ucScalingLists3[0],
                       scaling_lists->scaling_list_32x32[0].data(),
                       sizeof(iq_matrix.ucScalingLists3[0])));
    UNSAFE_TODO(memcpy(iq_matrix.ucScalingLists3[1],
                       scaling_lists->scaling_list_32x32[3].data(),
                       sizeof(iq_matrix.ucScalingLists3[1])));

    static_assert(
        std::is_same<
            std::remove_reference_t<
                decltype(iq_matrix.ucScalingListDCCoefSizeID2[0])>,
            std::remove_const_t<std::remove_reference_t<
                decltype(scaling_lists->scaling_list_dc_coef_16x16[0])>>>::
            value);
    static_assert(
        std::extent<decltype(iq_matrix.ucScalingListDCCoefSizeID2)>() <=
        std::tuple_size<std::remove_reference_t<
            decltype(scaling_lists->scaling_list_dc_coef_16x16)>>::value);
    UNSAFE_TODO(memcpy(iq_matrix.ucScalingListDCCoefSizeID2,
                       scaling_lists->scaling_list_dc_coef_16x16.data(),
                       sizeof(iq_matrix.ucScalingListDCCoefSizeID2)));
    iq_matrix.ucScalingListDCCoefSizeID3[0] =
        scaling_lists->scaling_list_dc_coef_32x32[0];
    iq_matrix.ucScalingListDCCoefSizeID3[1] =
        scaling_lists->scaling_list_dc_coef_32x32[3];

    auto iq_matrix_buffer =
        client_->GetWrapper()->GetInverseQuantizationMatrixBuffer(
            sizeof(iq_matrix));
    if (iq_matrix_buffer.size() < sizeof(iq_matrix)) {
      MEDIA_LOG(ERROR, media_log_) << "Insufficient quant buffer size";
      return H265DecoderStatus::kFail;
    }

    iq_matrix_buffer.data().copy_prefix_from(
        base::byte_span_from_ref(iq_matrix));

    if (!iq_matrix_buffer.Commit()) {
      return H265DecoderStatus::kFail;
    }
  }

  CHECK_GT(current_frame_size_, 0u);
  client_->GetWrapper()->GetBitstreamBuffer(current_frame_size_);

  constexpr uint8_t kStartCode[] = {0, 0, 1};
  bool ok =
      client_->GetWrapper()
          ->AppendBitstreamAndSliceDataWithStartCode<DXVA_Slice_HEVC_Short>(
              UNSAFE_TODO(base::span<const uint8_t>(data, size)), kStartCode);

  return ok ? H265DecoderStatus::kOk : H265DecoderStatus::kFail;
}

H265DecoderStatus D3DH265Accelerator::SubmitDecode(
    scoped_refptr<H265Picture> pic) {
  return client_->GetWrapper()->SubmitSlice() &&
                 client_->GetWrapper()->SubmitDecode()
             ? H265DecoderStatus::kOk
             : H265DecoderStatus::kFail;
}

void D3DH265Accelerator::Reset() {
  current_frame_size_ = 0;
  if (client_->GetWrapper()) {
    client_->GetWrapper()->Reset();
  }
}

H265Decoder::H265Accelerator::Status D3DH265Accelerator::SetStream(
    base::span<const uint8_t> stream,
    const DecryptConfig* decrypt_config) {
  current_frame_size_ = stream.size();
  return H265Accelerator::SetStream(stream, decrypt_config);
}

bool D3DH265Accelerator::OutputPicture(scoped_refptr<H265Picture> pic) {
  D3D11H265Picture* our_pic = pic->AsD3D11H265Picture();
  return our_pic && client_->OutputResult(our_pic, our_pic->picture);
}

}  // namespace media
