blob: 49aaab34bd14785e1958384918cffb1bb8ed7288 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/d3d11_h264_accelerator.h"
#include <d3d11.h>
#include <dxva.h>
#include <windows.h>
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "base/win/scoped_comptr.h"
#include "media/gpu/h264_decoder.h"
#include "media/gpu/h264_dpb.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 {
#define RETURN_ON_FAILURE(result, log, ret) \
do { \
if (!(result)) { \
DLOG(ERROR) << log; \
return ret; \
} \
} while (0)
D3D11PictureBuffer::D3D11PictureBuffer(PictureBuffer picture_buffer,
size_t level)
: picture_buffer_(picture_buffer), level_(level) {}
D3D11PictureBuffer::~D3D11PictureBuffer() {}
bool D3D11PictureBuffer::Init(
base::win::ScopedComPtr<ID3D11VideoDevice> video_device,
base::win::ScopedComPtr<ID3D11Texture2D> texture,
const GUID& decoder_guid) {
texture_ = texture;
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = {};
view_desc.DecodeProfile = decoder_guid;
view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
view_desc.Texture2D.ArraySlice = (UINT)level_;
HRESULT hr = video_device->CreateVideoDecoderOutputView(
texture.get(), &view_desc, output_view_.Receive());
CHECK(SUCCEEDED(hr));
EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
const EGLint stream_attributes[] = {
EGL_CONSUMER_LATENCY_USEC_KHR,
0,
EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR,
0,
EGL_NONE,
};
stream_ = eglCreateStreamKHR(egl_display, stream_attributes);
RETURN_ON_FAILURE(!!stream_, "Could not create stream", false);
gl::ScopedActiveTexture texture0(GL_TEXTURE0);
gl::ScopedTextureBinder texture0_binder(
GL_TEXTURE_EXTERNAL_OES, picture_buffer_.service_texture_ids()[0]);
gl::ScopedActiveTexture texture1(GL_TEXTURE1);
gl::ScopedTextureBinder texture1_binder(
GL_TEXTURE_EXTERNAL_OES, picture_buffer_.service_texture_ids()[1]);
EGLAttrib consumer_attributes[] = {
EGL_COLOR_BUFFER_TYPE,
EGL_YUV_BUFFER_EXT,
EGL_YUV_NUMBER_OF_PLANES_EXT,
2,
EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
0,
EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
1,
EGL_NONE,
};
EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
egl_display, stream_, consumer_attributes);
RETURN_ON_FAILURE(result, "Could not set stream consumer", false);
EGLAttrib producer_attributes[] = {
EGL_NONE,
};
result = eglCreateStreamProducerD3DTextureNV12ANGLE(egl_display, stream_,
producer_attributes);
EGLAttrib frame_attributes[] = {
EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, level_, EGL_NONE,
};
result = eglStreamPostD3DTextureNV12ANGLE(egl_display, stream_,
static_cast<void*>(texture.get()),
frame_attributes);
RETURN_ON_FAILURE(result, "Could not post texture", false);
result = eglStreamConsumerAcquireKHR(egl_display, stream_);
RETURN_ON_FAILURE(result, "Could not post acquire stream", false);
return true;
}
class D3D11H264Picture : public H264Picture {
public:
D3D11H264Picture(D3D11PictureBuffer* picture, size_t input_buffer_id)
: picture(picture),
level_(picture->level()),
input_buffer_id_(input_buffer_id) {}
D3D11PictureBuffer* picture;
size_t level_;
size_t input_buffer_id_;
protected:
~D3D11H264Picture() override;
};
D3D11H264Accelerator::D3D11H264Accelerator(
D3D11VideoDecoderClient* client,
base::win::ScopedComPtr<ID3D11VideoDecoder> video_decoder,
base::win::ScopedComPtr<ID3D11VideoDevice> video_device,
base::win::ScopedComPtr<ID3D11VideoContext> video_context)
: client_(client),
video_decoder_(video_decoder),
video_device_(video_device),
video_context_(video_context) {}
D3D11H264Accelerator::~D3D11H264Accelerator() {}
scoped_refptr<H264Picture> D3D11H264Accelerator::CreateH264Picture() {
D3D11PictureBuffer* picture = client_->GetPicture();
if (!picture) {
return nullptr;
}
picture->set_in_picture_use(true);
return make_scoped_refptr(
new D3D11H264Picture(picture, client_->input_buffer_id()));
}
bool 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,
const scoped_refptr<H264Picture>& pic) {
scoped_refptr<D3D11H264Picture> our_pic(
static_cast<D3D11H264Picture*>(pic.get()));
HRESULT hr;
hr = video_context_->DecoderBeginFrame(
video_decoder_.get(), our_pic->picture->output_view_.get(), 0, nullptr);
CHECK(SUCCEEDED(hr));
sps_ = *sps;
for (size_t i = 0; i < 16; 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;
int i = 0;
for (auto it = dpb.begin(); it != dpb.end(); it++) {
scoped_refptr<D3D11H264Picture> our_ref_pic(
static_cast<D3D11H264Picture*>(it->get()));
if (!our_ref_pic->ref) {
i++;
continue;
}
ref_frame_list_[i].Index7Bits = our_ref_pic->level_;
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->pic_num;
int ref = 3;
used_for_reference_flags_ |= ref << (2 * i);
non_existing_frame_flags_ |= (our_ref_pic->nonexisting) << i;
i++;
}
slice_info_.clear();
RetrieveBitstreamBuffer();
return true;
}
void D3D11H264Accelerator::RetrieveBitstreamBuffer() {
current_offset_ = 0;
void* buffer;
UINT buffer_size;
HRESULT hr = video_context_->GetDecoderBuffer(
video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_BITSTREAM, &buffer_size,
&buffer);
bitstream_buffer_bytes_ = (uint8_t*)buffer;
bitstream_buffer_size_ = buffer_size;
CHECK(SUCCEEDED(hr));
}
bool D3D11H264Accelerator::SubmitSlice(const H264PPS* pps,
const H264SliceHeader* slice_hdr,
const H264Picture::Vector& ref_pic_list0,
const H264Picture::Vector& ref_pic_list1,
const scoped_refptr<H264Picture>& pic,
const uint8_t* data,
size_t size) {
scoped_refptr<D3D11H264Picture> our_pic(
static_cast<D3D11H264Picture*>(pic.get()));
DXVA_PicParams_H264 pic_param = {};
#define FROM_SPS_TO_PP(a) pic_param.a = sps_.a
#define FROM_SPS_TO_PP2(a, b) pic_param.a = sps_.b
#define FROM_PPS_TO_PP(a) pic_param.a = pps->a
#define FROM_PPS_TO_PP2(a, b) pic_param.a = pps->b
#define FROM_SLICE_TO_PP(a) pic_param.a = slice_hdr->a
#define FROM_SLICE_TO_PP2(a, b) pic_param.a = slice_hdr->b
FROM_SPS_TO_PP2(wFrameWidthInMbsMinus1, pic_width_in_mbs_minus1);
FROM_SPS_TO_PP2(wFrameHeightInMbsMinus1, pic_height_in_map_units_minus1);
pic_param.CurrPic.Index7Bits = our_pic->level_;
// UNUSED: pic_param.CurrPic.AssociatedFlag = slide_hdr->field_pic_flag
FROM_SPS_TO_PP2(num_ref_frames, max_num_ref_frames);
FROM_SLICE_TO_PP(field_pic_flag);
pic_param.MbaffFrameFlag =
sps_.mb_adaptive_frame_field_flag && pic_param.field_pic_flag;
FROM_SPS_TO_PP2(residual_colour_transform_flag, separate_colour_plane_flag);
FROM_SLICE_TO_PP(sp_for_switch_flag);
FROM_SPS_TO_PP(chroma_format_idc);
pic_param.RefPicFlag = pic->ref;
FROM_PPS_TO_PP(constrained_intra_pred_flag);
FROM_PPS_TO_PP(weighted_pred_flag);
FROM_PPS_TO_PP(weighted_bipred_idc);
pic_param.MbsConsecutiveFlag = 1;
FROM_SPS_TO_PP(frame_mbs_only_flag);
FROM_PPS_TO_PP(transform_8x8_mode_flag);
// UNUSED: Minlumabipredsize
// UNUSED: pic_param.IntraPicFlag = slice_hdr->IsISlice();
FROM_SPS_TO_PP(bit_depth_luma_minus8);
FROM_SPS_TO_PP(bit_depth_chroma_minus8);
memcpy(pic_param.RefFrameList, ref_frame_list_,
sizeof pic_param.RefFrameList);
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;
}
memcpy(pic_param.FieldOrderCntList, field_order_cnt_list_,
sizeof pic_param.FieldOrderCntList);
FROM_PPS_TO_PP(pic_init_qs_minus26);
FROM_PPS_TO_PP(chroma_qp_index_offset);
FROM_PPS_TO_PP(second_chroma_qp_index_offset);
pic_param.ContinuationFlag = 1;
FROM_PPS_TO_PP(pic_init_qp_minus26);
FROM_PPS_TO_PP2(num_ref_idx_l0_active_minus1,
num_ref_idx_l0_default_active_minus1);
FROM_PPS_TO_PP2(num_ref_idx_l1_active_minus1,
num_ref_idx_l1_default_active_minus1);
// UNUSED: Reserved8BitsA
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_;
pic_param.frame_num = pic->frame_num;
FROM_SPS_TO_PP(log2_max_frame_num_minus4);
FROM_SPS_TO_PP(pic_order_cnt_type);
FROM_SPS_TO_PP(log2_max_pic_order_cnt_lsb_minus4);
FROM_SPS_TO_PP(delta_pic_order_always_zero_flag);
FROM_SPS_TO_PP(direct_8x8_inference_flag);
FROM_PPS_TO_PP(entropy_coding_mode_flag);
FROM_PPS_TO_PP2(pic_order_present_flag,
bottom_field_pic_order_in_frame_present_flag);
FROM_PPS_TO_PP(num_slice_groups_minus1);
CHECK_EQ(0u, pic_param.num_slice_groups_minus1);
// UNUSED: slice_group_map_type
FROM_PPS_TO_PP(deblocking_filter_control_present_flag);
FROM_PPS_TO_PP(redundant_pic_cnt_present_flag);
// UNUSED: Reserved8BitsB
// UNUSED: slice_group_change_rate
//
//
//
pic_param.StatusReportFeedbackNumber = 1;
UINT buffer_size;
void* buffer;
HRESULT hr = video_context_->GetDecoderBuffer(
video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS,
&buffer_size, &buffer);
CHECK(SUCCEEDED(hr));
memcpy(buffer, &pic_param, sizeof(pic_param));
hr = video_context_->ReleaseDecoderBuffer(
video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS);
CHECK(SUCCEEDED(hr));
DXVA_Qmatrix_H264 iq_matrix_buf = {};
if (pps->pic_scaling_matrix_present_flag) {
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 16; ++j)
iq_matrix_buf.bScalingLists4x4[i][j] = pps->scaling_list4x4[i][j];
}
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 64; ++j)
iq_matrix_buf.bScalingLists8x8[i][j] = pps->scaling_list8x8[i][j];
}
} else {
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 16; ++j)
iq_matrix_buf.bScalingLists4x4[i][j] = sps_.scaling_list4x4[i][j];
}
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 64; ++j)
iq_matrix_buf.bScalingLists8x8[i][j] = sps_.scaling_list8x8[i][j];
}
}
hr = video_context_->GetDecoderBuffer(
video_decoder_.get(),
D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX, &buffer_size,
&buffer);
CHECK(SUCCEEDED(hr));
memcpy(buffer, &iq_matrix_buf, sizeof(iq_matrix_buf));
hr = video_context_->ReleaseDecoderBuffer(
video_decoder_.get(),
D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX);
// Ideally all slices in a frame are put in the same bitstream buffer.
// However the bitstream buffer may not fit all the data, so split on the
// necessary boundaries.
size_t out_bitstream_size = size + 3;
size_t remaining_bitstream = out_bitstream_size;
size_t start_location = 0;
while (remaining_bitstream > 0) {
if (bitstream_buffer_size_ < remaining_bitstream &&
slice_info_.size() > 0) {
SubmitSliceData();
RetrieveBitstreamBuffer();
}
size_t bytes_to_copy = remaining_bitstream;
bool contains_end = true;
if (bytes_to_copy > bitstream_buffer_size_) {
bytes_to_copy = bitstream_buffer_size_;
contains_end = false;
}
size_t real_bytes_to_copy = bytes_to_copy;
// TODO(jbauman): fix hack
uint8_t* out_start = bitstream_buffer_bytes_;
if (bytes_to_copy >= 3 && start_location == 0) {
*(out_start++) = 0;
*(out_start++) = 0;
*(out_start++) = 1;
real_bytes_to_copy -= 3;
}
memcpy(out_start, data + start_location, real_bytes_to_copy);
DXVA_Slice_H264_Short slice_info = {};
slice_info.BSNALunitDataLocation = (UINT)current_offset_;
slice_info.SliceBytesInBuffer = (UINT)bytes_to_copy;
if (contains_end && start_location == 0)
slice_info.wBadSliceChopping = 0;
else if (!contains_end && start_location == 0)
slice_info.wBadSliceChopping = 1;
else if (contains_end && start_location != 0)
slice_info.wBadSliceChopping = 2;
else
slice_info.wBadSliceChopping = 3;
slice_info_.push_back(slice_info);
bitstream_buffer_size_ -= bytes_to_copy;
current_offset_ += bytes_to_copy;
start_location += bytes_to_copy;
remaining_bitstream -= bytes_to_copy;
bitstream_buffer_bytes_ += bytes_to_copy;
}
return true;
}
void D3D11H264Accelerator::SubmitSliceData() {
CHECK(slice_info_.size() > 0);
UINT buffer_size;
void* buffer;
HRESULT hr = video_context_->GetDecoderBuffer(
video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL,
&buffer_size, &buffer);
CHECK(SUCCEEDED(hr));
CHECK_LE(sizeof(slice_info_[0]) * slice_info_.size(), buffer_size);
memcpy(buffer, &slice_info_[0], sizeof(slice_info_[0]) * slice_info_.size());
hr = video_context_->ReleaseDecoderBuffer(
video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL);
hr = video_context_->ReleaseDecoderBuffer(
video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_BITSTREAM);
D3D11_VIDEO_DECODER_BUFFER_DESC buffers[4] = {};
buffers[0].BufferType = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS;
buffers[0].DataOffset = 0;
buffers[0].DataSize = sizeof(DXVA_PicParams_H264);
buffers[1].BufferType =
D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX;
buffers[1].DataOffset = 0;
buffers[1].DataSize = sizeof(DXVA_Qmatrix_H264);
buffers[2].BufferType = D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL;
buffers[2].DataOffset = 0;
buffers[2].DataSize = (UINT)(sizeof(slice_info_[0]) * slice_info_.size());
buffers[3].BufferType = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM;
buffers[3].DataOffset = 0;
buffers[3].DataSize = (UINT)current_offset_;
hr = video_context_->SubmitDecoderBuffers(video_decoder_.get(), 4, buffers);
current_offset_ = 0;
slice_info_.clear();
}
bool D3D11H264Accelerator::SubmitDecode(const scoped_refptr<H264Picture>& pic) {
SubmitSliceData();
HRESULT hr = video_context_->DecoderEndFrame(video_decoder_.get());
CHECK(SUCCEEDED(hr));
return true;
}
bool D3D11H264Accelerator::OutputPicture(
const scoped_refptr<H264Picture>& pic) {
scoped_refptr<D3D11H264Picture> our_pic(
static_cast<D3D11H264Picture*>(pic.get()));
client_->OutputResult(our_pic->picture, our_pic->input_buffer_id_);
return true;
}
D3D11H264Picture::~D3D11H264Picture() {
picture->set_in_picture_use(false);
}
} // namespace media