blob: 7a9c0e76b13d6fb9b9cc3b162277304e3a8a80cb [file] [log] [blame]
// Copyright 2016 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/d3d11_h264_accelerator.h"
#include <type_traits>
#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/h264_decoder.h"
#include "media/gpu/h264_dpb.h"
#include "media/gpu/windows/d3d11_picture_buffer.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 H264DecoderStatus = H264Decoder::H264Accelerator::Status;
} // namespace
class D3D11H264Picture : public H264Picture {
public:
D3D11H264Picture(D3D11PictureBuffer* picture)
: picture(picture), picture_index_(picture->picture_index()) {
picture->set_in_picture_use(true);
}
raw_ptr<D3D11PictureBuffer> picture;
size_t picture_index_;
D3D11H264Picture* AsD3D11H264Picture() override { return this; }
protected:
~D3D11H264Picture() override;
};
D3D11H264Picture::~D3D11H264Picture() {
picture->set_in_picture_use(false);
}
D3D11H264Accelerator::D3D11H264Accelerator(D3D11VideoDecoderClient* client,
MediaLog* media_log)
: D3DAccelerator(client, media_log) {}
D3D11H264Accelerator::~D3D11H264Accelerator() {}
scoped_refptr<H264Picture> D3D11H264Accelerator::CreateH264Picture() {
D3D11PictureBuffer* picture = client_->GetPicture();
if (!picture) {
return nullptr;
}
return base::MakeRefCounted<D3D11H264Picture>(picture);
}
H264DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata(
const H264SPS* sps,
const H264PPS* pps,
const H264DPB& dpb,
const H264Picture::Vector& ref_pic_listp0,
const H264Picture::Vector& ref_pic_listb0,
const H264Picture::Vector& ref_pic_listb1,
scoped_refptr<H264Picture> pic) {
D3D11H264Picture* d3d11_pic = pic->AsD3D11H264Picture();
if (!d3d11_pic) {
return H264DecoderStatus::kFail;
}
if (!video_decoder_wrapper_->WaitForFrameBegins(d3d11_pic->picture.get())) {
return H264DecoderStatus::kFail;
}
sps_ = *sps;
for (size_t i = 0; i < media::kRefFrameMaxCount; i++) {
ref_frame_list_[i].bPicEntry = 0xFF;
field_order_cnt_list_[i][0] = 0;
field_order_cnt_list_[i][1] = 0;
frame_num_list_[i] = 0;
}
used_for_reference_flags_ = 0;
non_existing_frame_flags_ = 0;
// TODO(liberato): this is similar to H264Accelerator. can they share code?
int i = 0;
for (auto it = dpb.begin(); it != dpb.end(); i++, it++) {
// The DPB is supposed to have a maximum of 16 pictures in it, but there's
// nothing actually stopping it from having more. If we run into this case,
// something is clearly wrong, and we should just fail decoding rather than
// try to sort out which pictures really shouldn't be included.
if (i >= media::kRefFrameMaxCount)
return H264DecoderStatus::kFail;
D3D11H264Picture* our_ref_pic = it->get()->AsD3D11H264Picture();
// How does a non-d3d11 picture get here you might ask? The decoder
// inserts blank H264Picture objects that we can't use as part of filling
// gaps in frame numbers. If we see one, it's not a reference picture
// anyway, so skip it.
if (!our_ref_pic || !our_ref_pic->ref)
continue;
ref_frame_list_[i].Index7Bits = our_ref_pic->picture_index_;
ref_frame_list_[i].AssociatedFlag = our_ref_pic->long_term;
field_order_cnt_list_[i][0] = our_ref_pic->top_field_order_cnt;
field_order_cnt_list_[i][1] = our_ref_pic->bottom_field_order_cnt;
frame_num_list_[i] = ref_frame_list_[i].AssociatedFlag
? our_ref_pic->long_term_pic_num
: our_ref_pic->frame_num;
unsigned ref = 3;
used_for_reference_flags_ |= ref << (2 * i);
non_existing_frame_flags_ |= (our_ref_pic->nonexisting) << i;
}
return H264DecoderStatus::kOk;
}
void D3D11H264Accelerator::FillPicParamsWithConstants(
DXVA_PicParams_H264* pic) {
// From "DirectX Video Acceleration Specification for H.264/AVC Decoding":
// "The value shall be 1 unless the restricted-mode profile in use
// explicitly supports the value 0."
pic->MbsConsecutiveFlag = 1;
// The latest DXVA decoding guide says to set this to 3 if the software
// decoder (this class) is following the guide.
pic->Reserved16Bits = 3;
// |ContinuationFlag| indicates that we've filled in the remaining fields.
pic->ContinuationFlag = 1;
// Must be zero unless bit 13 of ConfigDecoderSpecific is set.
pic->Reserved8BitsA = 0;
// Unused, should always be zero.
pic->Reserved8BitsB = 0;
// Should always be 1.
pic->StatusReportFeedbackNumber = 1;
// UNUSED: slice_group_map_type (undocumented)
// UNUSED: slice_group_change_rate (undocumented)
}
#define ARG_SEL(_1, _2, NAME, ...) NAME
#define SPS_TO_PP1(a) pic_param->a = sps->a;
#define SPS_TO_PP2(a, b) pic_param->a = sps->b;
#define SPS_TO_PP(...) ARG_SEL(__VA_ARGS__, SPS_TO_PP2, SPS_TO_PP1)(__VA_ARGS__)
void D3D11H264Accelerator::PicParamsFromSPS(DXVA_PicParams_H264* pic_param,
const H264SPS* sps,
bool field_pic) {
// The H.264 specification now calls this |max_num_ref_frames|, while
// DXVA_PicParams_H264 continues to use the old name, |num_ref_frames|.
// See DirectX Video Acceleration for H.264/MPEG-4 AVC Decoding (4.2).
SPS_TO_PP(num_ref_frames, max_num_ref_frames);
SPS_TO_PP(wFrameWidthInMbsMinus1, pic_width_in_mbs_minus1);
SPS_TO_PP(wFrameHeightInMbsMinus1, pic_height_in_map_units_minus1);
SPS_TO_PP(residual_colour_transform_flag, separate_colour_plane_flag);
SPS_TO_PP(chroma_format_idc);
SPS_TO_PP(frame_mbs_only_flag);
SPS_TO_PP(bit_depth_luma_minus8);
SPS_TO_PP(bit_depth_chroma_minus8);
SPS_TO_PP(log2_max_frame_num_minus4);
SPS_TO_PP(pic_order_cnt_type);
SPS_TO_PP(log2_max_pic_order_cnt_lsb_minus4);
SPS_TO_PP(delta_pic_order_always_zero_flag);
SPS_TO_PP(direct_8x8_inference_flag);
pic_param->MbaffFrameFlag = sps->mb_adaptive_frame_field_flag && field_pic;
pic_param->field_pic_flag = field_pic;
pic_param->MinLumaBipredSize8x8Flag = sps->level_idc >= 31;
}
#undef SPS_TO_PP
#undef SPS_TO_PP2
#undef SPS_TO_PP1
#define PPS_TO_PP1(a) pic_param->a = pps->a;
#define PPS_TO_PP2(a, b) pic_param->a = pps->b;
#define PPS_TO_PP(...) ARG_SEL(__VA_ARGS__, PPS_TO_PP2, PPS_TO_PP1)(__VA_ARGS__)
bool D3D11H264Accelerator::PicParamsFromPPS(DXVA_PicParams_H264* pic_param,
const H264PPS* pps) {
PPS_TO_PP(constrained_intra_pred_flag);
PPS_TO_PP(weighted_pred_flag);
PPS_TO_PP(weighted_bipred_idc);
PPS_TO_PP(transform_8x8_mode_flag);
PPS_TO_PP(pic_init_qs_minus26);
PPS_TO_PP(chroma_qp_index_offset);
PPS_TO_PP(second_chroma_qp_index_offset);
PPS_TO_PP(pic_init_qp_minus26);
PPS_TO_PP(num_ref_idx_l0_active_minus1, num_ref_idx_l0_default_active_minus1);
PPS_TO_PP(num_ref_idx_l1_active_minus1, num_ref_idx_l1_default_active_minus1);
PPS_TO_PP(entropy_coding_mode_flag);
PPS_TO_PP(pic_order_present_flag,
bottom_field_pic_order_in_frame_present_flag);
PPS_TO_PP(deblocking_filter_control_present_flag);
PPS_TO_PP(redundant_pic_cnt_present_flag);
PPS_TO_PP(num_slice_groups_minus1);
if (pic_param->num_slice_groups_minus1) {
// TODO(liberato): UMA?
// TODO(liberato): media log?
LOG(ERROR) << "num_slice_groups_minus1 == "
<< pic_param->num_slice_groups_minus1;
return false;
}
return true;
}
#undef PPS_TO_PP
#undef PPS_TO_PP2
#undef PPS_TO_PP1
#undef ARG_SEL
void D3D11H264Accelerator::PicParamsFromSliceHeader(
DXVA_PicParams_H264* pic_param,
const H264SliceHeader* slice_hdr) {
pic_param->sp_for_switch_flag = slice_hdr->sp_for_switch_flag;
pic_param->field_pic_flag = slice_hdr->field_pic_flag;
pic_param->CurrPic.AssociatedFlag = slice_hdr->bottom_field_flag;
pic_param->IntraPicFlag = slice_hdr->IsISlice();
}
void D3D11H264Accelerator::PicParamsFromPic(DXVA_PicParams_H264* pic_param,
D3D11H264Picture* pic) {
pic_param->CurrPic.Index7Bits = pic->picture_index_;
pic_param->RefPicFlag = pic->ref;
pic_param->frame_num = pic->frame_num;
if (pic_param->field_pic_flag && pic_param->CurrPic.AssociatedFlag) {
pic_param->CurrFieldOrderCnt[1] = pic->bottom_field_order_cnt;
pic_param->CurrFieldOrderCnt[0] = 0;
} else if (pic_param->field_pic_flag && !pic_param->CurrPic.AssociatedFlag) {
pic_param->CurrFieldOrderCnt[0] = pic->top_field_order_cnt;
pic_param->CurrFieldOrderCnt[1] = 0;
} else {
pic_param->CurrFieldOrderCnt[0] = pic->top_field_order_cnt;
pic_param->CurrFieldOrderCnt[1] = pic->bottom_field_order_cnt;
}
}
H264DecoderStatus D3D11H264Accelerator::SubmitSlice(
const H264PPS* pps,
const H264SliceHeader* slice_hdr,
const H264Picture::Vector& ref_pic_list0,
const H264Picture::Vector& ref_pic_list1,
scoped_refptr<H264Picture> pic,
const uint8_t* data,
size_t size,
const std::vector<SubsampleEntry>& subsamples) {
if (!video_decoder_wrapper_->HasPendingBuffer(
D3DVideoDecoderWrapper::BufferType::kPictureParameters)) {
DXVA_PicParams_H264 pic_param = {};
FillPicParamsWithConstants(&pic_param);
PicParamsFromSPS(&pic_param, &sps_, slice_hdr->field_pic_flag);
if (!PicParamsFromPPS(&pic_param, pps)) {
return H264DecoderStatus::kFail;
}
PicParamsFromSliceHeader(&pic_param, slice_hdr);
D3D11H264Picture* d3d11_pic = pic->AsD3D11H264Picture();
if (!d3d11_pic) {
return H264DecoderStatus::kFail;
}
PicParamsFromPic(&pic_param, d3d11_pic);
memcpy(pic_param.RefFrameList, ref_frame_list_,
sizeof pic_param.RefFrameList);
memcpy(pic_param.FieldOrderCntList, field_order_cnt_list_,
sizeof pic_param.FieldOrderCntList);
memcpy(pic_param.FrameNumList, frame_num_list_,
sizeof pic_param.FrameNumList);
pic_param.UsedForReferenceFlags = used_for_reference_flags_;
pic_param.NonExistingFrameFlags = non_existing_frame_flags_;
auto params_buffer =
video_decoder_wrapper_->GetPictureParametersBuffer(sizeof(pic_param));
if (params_buffer.size() < sizeof(pic_param)) {
RecordFailure("Insufficient picture parameter buffer size",
D3D11StatusCode::kGetPicParamBufferFailed);
return H264DecoderStatus::kFail;
}
memcpy(params_buffer.data(), &pic_param, sizeof(pic_param));
if (!params_buffer.Commit()) {
return H264DecoderStatus::kFail;
}
}
if (!video_decoder_wrapper_->HasPendingBuffer(
D3DVideoDecoderWrapper::BufferType::kInverseQuantizationMatrix)) {
DXVA_Qmatrix_H264 iq_matrix = {};
const auto& scaling_list4x4_source = pps->pic_scaling_matrix_present_flag
? pps->scaling_list4x4
: sps_.scaling_list4x4;
static_assert(std::is_same<
std::remove_reference_t<decltype(iq_matrix.bScalingLists4x4)>,
std::remove_const_t<std::remove_reference_t<
decltype(scaling_list4x4_source)>>>::value);
memcpy(iq_matrix.bScalingLists4x4, scaling_list4x4_source,
sizeof(iq_matrix.bScalingLists4x4));
const auto& scaling_list8x8_source = pps->pic_scaling_matrix_present_flag
? pps->scaling_list8x8
: sps_.scaling_list8x8;
static_assert(
std::is_same<
std::remove_reference_t<decltype(iq_matrix.bScalingLists8x8[0])>,
std::remove_const_t<std::remove_reference_t<
decltype(scaling_list8x8_source[0])>>>::value);
static_assert(
std::extent<decltype(iq_matrix.bScalingLists8x8)>() <=
std::extent<
std::remove_reference_t<decltype(scaling_list8x8_source)>>());
memcpy(iq_matrix.bScalingLists8x8, scaling_list8x8_source,
sizeof(iq_matrix.bScalingLists8x8));
auto iq_matrix_buffer =
video_decoder_wrapper_->GetInverseQuantizationMatrixBuffer(
sizeof(iq_matrix));
if (iq_matrix_buffer.size() < sizeof(iq_matrix)) {
RecordFailure("Insufficient quant buffer size",
D3D11StatusCode::kGetQuantBufferFailed);
return H264DecoderStatus::kFail;
}
memcpy(iq_matrix_buffer.data(), &iq_matrix, sizeof(iq_matrix));
if (!iq_matrix_buffer.Commit()) {
return H264DecoderStatus::kFail;
}
}
// GetBitstreamBuffer() will create the buffer with the given size and return
// the buffer if it does not exist. Calling it here is to make sure it creates
// one with a large enough size |current_frame_size_| for D3D12 video decoder.
// D3D12 video decoder don't accept chopped bitstream buffer, so we need to
// reserve the buffer with the size large enough to contain the whole frame
// before the following call jumps into the base class who don't know this
// size.
CHECK_GT(current_frame_size_, 0u);
video_decoder_wrapper_->GetBitstreamBuffer(current_frame_size_);
constexpr uint8_t kStartCode[] = {0, 0, 1};
bool ok =
video_decoder_wrapper_
->AppendBitstreamAndSliceDataWithStartCode<DXVA_Slice_H264_Short>(
{data, size}, kStartCode);
return ok ? H264DecoderStatus::kOk : H264DecoderStatus::kFail;
}
H264DecoderStatus D3D11H264Accelerator::SubmitDecode(
scoped_refptr<H264Picture> pic) {
return video_decoder_wrapper_->SubmitSlice() &&
video_decoder_wrapper_->SubmitDecode()
? H264DecoderStatus::kOk
: H264DecoderStatus::kFail;
}
void D3D11H264Accelerator::Reset() {
current_frame_size_ = 0;
if (video_decoder_wrapper_) {
video_decoder_wrapper_->Reset();
}
}
bool D3D11H264Accelerator::OutputPicture(scoped_refptr<H264Picture> pic) {
D3D11H264Picture* our_pic = pic->AsD3D11H264Picture();
return our_pic && client_->OutputResult(our_pic, our_pic->picture);
}
H264Decoder::H264Accelerator::Status D3D11H264Accelerator::SetStream(
base::span<const uint8_t> stream,
const DecryptConfig* decrypt_config) {
current_frame_size_ = stream.size();
return H264Accelerator::SetStream(stream, decrypt_config);
}
} // namespace media