Commit 9cd21087 authored by Rintaro Kuroiwa's avatar Rintaro Kuroiwa Committed by Commit Bot

Support encrypted streams for D3D11H264Accelerator

- Handle subsamples.
- Use ID3D11VideoContext1 for SubmitDecoderBuffers1.
- Pass a decryption context to DecoderBeginFrame().
- Change GetAddressOf in D3D11VideoDecoderImpl::Initialize to
  ReleaseAndGetAddressOf so that it doesn't leak when initialized
  multiple times.

Bug: 799310
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I802f3afff630811f3f2a497557ad7af566ff3f4a
Reviewed-on: https://chromium-review.googlesource.com/1123694
Commit-Queue: Rintaro Kuroiwa <rkuroiwa@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#572392}
parent bcce1f33
......@@ -8,6 +8,7 @@
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "media/base/cdm_proxy_context.h"
#include "media/gpu/h264_decoder.h"
#include "media/gpu/h264_dpb.h"
#include "media/gpu/windows/d3d11_picture_buffer.h"
......@@ -25,6 +26,22 @@ namespace media {
using Status = H264Decoder::H264Accelerator::Status;
namespace {
// Converts SubsampleEntry to D3D11_VIDEO_DECODER_SUB_SAMPLE_MAPPING_BLOCK.
void AppendSubsamples(
const std::vector<SubsampleEntry>& from,
std::vector<D3D11_VIDEO_DECODER_SUB_SAMPLE_MAPPING_BLOCK>* to) {
for (const auto& from_entry : from) {
D3D11_VIDEO_DECODER_SUB_SAMPLE_MAPPING_BLOCK subsample = {};
subsample.ClearSize = from_entry.clear_bytes;
subsample.EncryptedSize = from_entry.cypher_bytes;
to->push_back(subsample);
}
}
} // namespace
class D3D11H264Picture : public H264Picture {
public:
D3D11H264Picture(D3D11PictureBuffer* picture)
......@@ -45,10 +62,12 @@ D3D11H264Picture::~D3D11H264Picture() {
D3D11H264Accelerator::D3D11H264Accelerator(
D3D11VideoDecoderClient* client,
CdmProxyContext* cdm_proxy_context,
Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder,
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context)
Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context)
: client_(client),
cdm_proxy_context_(cdm_proxy_context),
video_decoder_(video_decoder),
video_device_(video_device),
video_context_(video_context) {}
......@@ -71,21 +90,50 @@ Status D3D11H264Accelerator::SubmitFrameMetadata(
const H264Picture::Vector& ref_pic_listb0,
const H264Picture::Vector& ref_pic_listb1,
const scoped_refptr<H264Picture>& pic) {
const bool is_encrypted = pic->decrypt_config();
if (is_encrypted && !cdm_proxy_context_) {
DVLOG(1) << "The input is encrypted but there is no proxy context.";
return Status::kFail;
}
std::unique_ptr<D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION> content_key;
// This decrypt context has to be outside the if block because pKeyInfo in
// D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION is a pointer (to a GUID).
base::Optional<CdmProxyContext::D3D11DecryptContext> decrypt_context;
if (is_encrypted) {
decrypt_context = cdm_proxy_context_->GetD3D11DecryptContext(
pic->decrypt_config()->key_id());
if (!decrypt_context) {
DVLOG(1) << "Cannot find decrypt context for the frame.";
return Status::kNoKey;
}
content_key =
std::make_unique<D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION>();
content_key->pCryptoSession = decrypt_context->crypto_session;
content_key->pBlob = const_cast<void*>(decrypt_context->key_blob);
content_key->BlobSize = decrypt_context->key_blob_size;
content_key->pKeyInfoId = &decrypt_context->key_info_guid;
frame_iv_.assign(pic->decrypt_config()->iv().begin(),
pic->decrypt_config()->iv().end());
}
scoped_refptr<D3D11H264Picture> our_pic(
static_cast<D3D11H264Picture*>(pic.get()));
HRESULT hr;
for (;;) {
hr = video_context_->DecoderBeginFrame(
video_decoder_.Get(), our_pic->picture->output_view().Get(), 0,
nullptr);
video_decoder_.Get(), our_pic->picture->output_view().Get(),
content_key ? sizeof(*content_key) : 0, content_key.get());
if (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING) {
// Hardware is busy. We should make the call again.
// TODO(liberato): For now, just busy wait.
;
} else if (!SUCCEEDED(hr)) {
LOG(ERROR) << "DecoderBeginFrame failed";
LOG(ERROR) << "DecoderBeginFrame failed: "
<< logging::SystemErrorCodeToString(hr);
return Status::kFail;
} else {
break;
......@@ -312,6 +360,31 @@ Status D3D11H264Accelerator::SubmitSlice(
size_t remaining_bitstream = out_bitstream_size;
size_t start_location = 0;
const bool is_encrypted = pic->decrypt_config();
if (is_encrypted) {
// For now, the entire frame has to fit into the bitstream buffer. This way
// the subsample ClearSize adjustment below should work.
if (bitstream_buffer_size_ < remaining_bitstream) {
LOG(ERROR) << "Input slice NALU (" << remaining_bitstream
<< ") too big to fit in the bistream buffer ("
<< bitstream_buffer_size_ << ").";
return Status::kFail;
}
AppendSubsamples(subsamples, &subsamples_);
if (!subsamples.empty()) {
// 3 added to clear bytes because a start code is prepended to the slice
// NALU.
// TODO(rkuroiwa): This should be done right after the start code is
// written to the buffer, but currently the start code is written in the
// loop (which is not the right place, there's only one slice NALU passed
// into this function) and it's not easy to identify where the subsample
// starts in the buffer.
subsamples_[subsamples_.size() - subsamples.size()].ClearSize += 3;
}
}
while (remaining_bitstream > 0) {
if (bitstream_buffer_size_ < remaining_bitstream &&
slice_info_.size() > 0) {
......@@ -397,7 +470,7 @@ bool D3D11H264Accelerator::SubmitSliceData() {
return false;
}
D3D11_VIDEO_DECODER_BUFFER_DESC buffers[4] = {};
D3D11_VIDEO_DECODER_BUFFER_DESC1 buffers[4] = {};
buffers[0].BufferType = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS;
buffers[0].DataOffset = 0;
buffers[0].DataSize = sizeof(DXVA_PicParams_H264);
......@@ -407,18 +480,32 @@ bool D3D11H264Accelerator::SubmitSliceData() {
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[2].DataSize = 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_;
buffers[3].DataSize = current_offset_;
if (!frame_iv_.empty()) {
buffers[3].pIV = frame_iv_.data();
buffers[3].IVSize = frame_iv_.size();
// Subsmaples matter iff there is IV, for decryption.
if (!subsamples_.empty()) {
buffers[3].pSubSampleMappingBlock = subsamples_.data();
buffers[3].SubSampleMappingCount = subsamples_.size();
}
}
hr = video_context_->SubmitDecoderBuffers(video_decoder_.Get(), 4, buffers);
hr = video_context_->SubmitDecoderBuffers1(video_decoder_.Get(),
base::size(buffers), buffers);
current_offset_ = 0;
slice_info_.clear();
bitstream_buffer_bytes_ = nullptr;
bitstream_buffer_size_ = 0;
frame_iv_.clear();
subsamples_.clear();
if (!SUCCEEDED(hr)) {
LOG(ERROR) << "SubmitDecoderBuffers failed";
LOG(ERROR) << "SubmitDecoderBuffers failed: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
......
......@@ -5,7 +5,7 @@
#ifndef MEDIA_GPU_WINDOWS_D3D11_H264_ACCELERATOR_H_
#define MEDIA_GPU_WINDOWS_D3D11_H264_ACCELERATOR_H_
#include <d3d11.h>
#include <d3d11_1.h>
#include <d3d9.h>
#include <dxva.h>
#include <wrl/client.h>
......@@ -23,6 +23,7 @@
#include "ui/gl/gl_image.h"
namespace media {
class CdmProxyContext;
class D3D11H264Accelerator;
class D3D11PictureBuffer;
......@@ -35,11 +36,13 @@ class D3D11VideoDecoderClient {
class D3D11H264Accelerator : public H264Decoder::H264Accelerator {
public:
// |cdm_proxy_context| may be null for clear content.
D3D11H264Accelerator(
D3D11VideoDecoderClient* client,
CdmProxyContext* cdm_proxy_context,
Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder,
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context);
Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context);
~D3D11H264Accelerator() override;
// H264Decoder::H264Accelerator implementation.
......@@ -68,10 +71,11 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator {
bool RetrieveBitstreamBuffer();
D3D11VideoDecoderClient* client_;
CdmProxyContext* const cdm_proxy_context_;
Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder_;
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device_;
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context_;
Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context_;
// This information set at the beginning of a frame and saved for processing
// all the slices.
......@@ -88,6 +92,12 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator {
size_t bitstream_buffer_size_ = 0;
uint8_t* bitstream_buffer_bytes_ = nullptr;
// This contains the subsamples (clear and encrypted) of the slice data
// in D3D11_VIDEO_DECODER_BUFFER_BITSTREAM buffer.
std::vector<D3D11_VIDEO_DECODER_SUB_SAMPLE_MAPPING_BLOCK> subsamples_;
// IV for the current frame.
std::vector<uint8_t> frame_iv_;
DISALLOW_COPY_AND_ASSIGN(D3D11H264Accelerator);
};
......
......@@ -73,19 +73,19 @@ void D3D11VideoDecoderImpl::Initialize(
// could use our own device, and run on the mojo thread, but texture sharing
// seems to be difficult.
device_ = gl::QueryD3D11DeviceObjectFromANGLE();
device_->GetImmediateContext(device_context_.GetAddressOf());
device_->GetImmediateContext(device_context_.ReleaseAndGetAddressOf());
HRESULT hr;
// TODO(liberato): Handle cleanup better. Also consider being less chatty in
// the logs, since this will fall back.
hr = device_context_.CopyTo(video_context_.GetAddressOf());
hr = device_context_.CopyTo(video_context_.ReleaseAndGetAddressOf());
if (!SUCCEEDED(hr)) {
NotifyError("Failed to get device context");
return;
}
hr = device_.CopyTo(video_device_.GetAddressOf());
hr = device_.CopyTo(video_device_.ReleaseAndGetAddressOf());
if (!SUCCEEDED(hr)) {
NotifyError("Failed to get video device");
return;
......@@ -162,16 +162,22 @@ void D3D11VideoDecoderImpl::Initialize(
memcpy(&decoder_guid_, &decoder_guid, sizeof decoder_guid_);
Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder;
hr = video_device_->CreateVideoDecoder(&desc, &dec_config,
video_decoder.GetAddressOf());
hr = video_device_->CreateVideoDecoder(
&desc, &dec_config, video_decoder.ReleaseAndGetAddressOf());
if (!video_decoder.Get()) {
NotifyError("Failed to create a video decoder");
return;
}
CdmProxyContext* proxy_context = nullptr;
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
if (cdm_context)
proxy_context = cdm_context->GetCdmProxyContext();
#endif
accelerated_video_decoder_ = std::make_unique<H264Decoder>(
std::make_unique<D3D11H264Accelerator>(this, video_decoder, video_device_,
video_context_),
std::make_unique<D3D11H264Accelerator>(this, proxy_context, video_decoder,
video_device_, video_context_),
config.color_space_info());
// |cdm_context| could be null for clear playback.
......@@ -323,7 +329,7 @@ void D3D11VideoDecoderImpl::CreatePictureBuffers() {
Microsoft::WRL::ComPtr<ID3D11Texture2D> out_texture;
HRESULT hr = device_->CreateTexture2D(&texture_desc, nullptr,
out_texture.GetAddressOf());
out_texture.ReleaseAndGetAddressOf());
if (!SUCCEEDED(hr)) {
NotifyError("Failed to create a Texture2D for PictureBuffers");
return;
......
......@@ -5,7 +5,7 @@
#ifndef MEDIA_GPU_D3D11_VIDEO_DECODER_IMPL_H_
#define MEDIA_GPU_D3D11_VIDEO_DECODER_IMPL_H_
#include <d3d11.h>
#include <d3d11_1.h>
#include <wrl/client.h>
#include <list>
......@@ -95,7 +95,7 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl : public VideoDecoder,
Microsoft::WRL::ComPtr<ID3D11Device> device_;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> device_context_;
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device_;
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context_;
Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context_;
std::unique_ptr<AcceleratedVideoDecoder> accelerated_video_decoder_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment