Commit 5e36bafd authored by Jeffrey Kardatzke's avatar Jeffrey Kardatzke Committed by Commit Bot

Add decrypt support to VP9 VAAPI accelerator

This adds support for handling CENC v3 subsample encrypted stream for
VP9. VP9 will not need full sample (CENC v1) support.

BUG=b:153111783,b:155509231
TEST=VP9 decrypt+decode works (has driver issues though)

Change-Id: Ibf34e3bf7aaaf4de744044ecae5d064e9a45d01f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2551735
Commit-Queue: Jeffrey Kardatzke <jkardatzke@google.com>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#829822}
parent 85559b2c
......@@ -18,6 +18,9 @@
#include "media/gpu/vp9_picture.h"
namespace media {
using DecodeStatus = VP9Decoder::VP9Accelerator::Status;
namespace {
void FillV4L2VP9LoopFilterParams(
......@@ -202,7 +205,7 @@ scoped_refptr<VP9Picture> V4L2LegacyVP9Accelerator::CreateVP9Picture() {
return new V4L2VP9Picture(std::move(dec_surface));
}
bool V4L2LegacyVP9Accelerator::SubmitDecode(
DecodeStatus V4L2LegacyVP9Accelerator::SubmitDecode(
scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& segm_params,
const Vp9LoopFilterParams& lf_params,
......@@ -294,7 +297,7 @@ bool V4L2LegacyVP9Accelerator::SubmitDecode(
for (size_t i = 0; i < base::size(frame_hdr->ref_frame_idx); ++i) {
uint8_t idx = frame_hdr->ref_frame_idx[i];
if (idx >= kVp9NumRefFrames)
return false;
return DecodeStatus::kFail;
struct v4l2_vp9_reference_frame* v4l2_ref_frame =
&v4l2_decode_param.active_ref_frames[i];
......@@ -351,7 +354,7 @@ bool V4L2LegacyVP9Accelerator::SubmitDecode(
dec_surface->PrepareSetCtrls(&ext_ctrls);
if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
return false;
return DecodeStatus::kFail;
}
dec_surface->SetReferenceSurfaces(ref_surfaces);
......@@ -359,11 +362,11 @@ bool V4L2LegacyVP9Accelerator::SubmitDecode(
if (!surface_handler_->SubmitSlice(dec_surface.get(), frame_hdr->data,
frame_hdr->frame_size))
return false;
return DecodeStatus::kFail;
DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
surface_handler_->DecodeSurface(dec_surface);
return true;
return DecodeStatus::kOk;
}
bool V4L2LegacyVP9Accelerator::OutputPicture(scoped_refptr<VP9Picture> pic) {
......
......@@ -28,11 +28,11 @@ class V4L2LegacyVP9Accelerator : public VP9Decoder::VP9Accelerator {
// VP9Decoder::VP9Accelerator implementation.
scoped_refptr<VP9Picture> CreateVP9Picture() override;
bool SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& segm_params,
const Vp9LoopFilterParams& lf_params,
const Vp9ReferenceFrameVector& reference_frames,
base::OnceClosure done_cb) override;
Status SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& segm_params,
const Vp9LoopFilterParams& lf_params,
const Vp9ReferenceFrameVector& reference_frames,
base::OnceClosure done_cb) override;
bool OutputPicture(scoped_refptr<VP9Picture> pic) override;
......
......@@ -162,6 +162,12 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config,
std::move(init_cb).Run(StatusCode::kDecoderMissingCdmForEncryptedContent);
return;
}
if (config.codec() != kCodecH264 && config.codec() != kCodecVP9) {
VLOGF(1)
<< "Vaapi decoder does not support this codec for encrypted content";
std::move(init_cb).Run(StatusCode::kEncryptedContentUnsupported);
return;
}
cdm_context_ = cdm_context;
cdm_event_cb_registration_ = cdm_context_->RegisterEventCB(
base::BindRepeating(&VaapiVideoDecoder::OnCdmContextEvent,
......@@ -696,8 +702,11 @@ Status VaapiVideoDecoder::CreateAcceleratedVideoDecoder() {
decoder_.reset(new VP8Decoder(std::move(accelerator)));
} else if (profile_ >= VP9PROFILE_MIN && profile_ <= VP9PROFILE_MAX) {
auto accelerator =
std::make_unique<VP9VaapiVideoDecoderDelegate>(this, vaapi_wrapper_);
auto accelerator = std::make_unique<VP9VaapiVideoDecoderDelegate>(
this, vaapi_wrapper_,
BindToCurrentLoop(base::BindRepeating(
&VaapiVideoDecoder::ProtectedSessionUpdate, weak_this_)),
cdm_context_);
decoder_delegate_ = accelerator.get();
decoder_.reset(
......
......@@ -16,13 +16,17 @@
namespace media {
using DecodeStatus = VP9Decoder::VP9Accelerator::Status;
VP9VaapiVideoDecoderDelegate::VP9VaapiVideoDecoderDelegate(
DecodeSurfaceHandler<VASurface>* const vaapi_dec,
scoped_refptr<VaapiWrapper> vaapi_wrapper)
scoped_refptr<VaapiWrapper> vaapi_wrapper,
ProtectedSessionUpdateCB on_protected_session_update_cb,
CdmContext* cdm_context)
: VaapiVideoDecoderDelegate(vaapi_dec,
std::move(vaapi_wrapper),
base::DoNothing(),
nullptr) {}
std::move(on_protected_session_update_cb),
cdm_context) {}
VP9VaapiVideoDecoderDelegate::~VP9VaapiVideoDecoderDelegate() {
DCHECK(!picture_params_);
......@@ -38,7 +42,7 @@ scoped_refptr<VP9Picture> VP9VaapiVideoDecoderDelegate::CreateVP9Picture() {
return new VaapiVP9Picture(std::move(va_surface));
}
bool VP9VaapiVideoDecoderDelegate::SubmitDecode(
DecodeStatus VP9VaapiVideoDecoderDelegate::SubmitDecode(
scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& seg,
const Vp9LoopFilterParams& lf,
......@@ -54,18 +58,19 @@ bool VP9VaapiVideoDecoderDelegate::SubmitDecode(
VADecPictureParameterBufferVP9 pic_param{};
VASliceParameterBufferVP9 slice_param{};
VAEncryptionParameters crypto_param{};
if (!picture_params_) {
picture_params_ = vaapi_wrapper_->CreateVABuffer(
VAPictureParameterBufferType, sizeof(pic_param));
if (!picture_params_)
return false;
return DecodeStatus::kFail;
}
if (!slice_params_) {
slice_params_ = vaapi_wrapper_->CreateVABuffer(VASliceParameterBufferType,
sizeof(slice_param));
if (!slice_params_)
return false;
return DecodeStatus::kFail;
}
// Always re-create |encoded_data| because reusing the buffer causes horrific
// artifacts in decoded buffers. TODO(b/169725321): This seems to be a driver
......@@ -73,7 +78,36 @@ bool VP9VaapiVideoDecoderDelegate::SubmitDecode(
auto encoded_data = vaapi_wrapper_->CreateVABuffer(VASliceDataBufferType,
frame_hdr->frame_size);
if (!encoded_data)
return false;
return DecodeStatus::kFail;
bool uses_crypto = false;
const DecryptConfig* decrypt_config = pic->decrypt_config();
std::vector<VAEncryptionSegmentInfo> encryption_segment_info;
if (decrypt_config) {
if (!SetDecryptConfig(decrypt_config->Clone()))
return DecodeStatus::kFail;
if (!decrypt_config->subsamples().empty() &&
decrypt_config->subsamples()[0].cypher_bytes) {
ProtectedSessionState state = SetupDecryptDecode(
false /* full_sample */, &crypto_param, &encryption_segment_info,
decrypt_config->subsamples());
if (state == ProtectedSessionState::kFailed) {
LOG(ERROR)
<< "SubmitDecode fails because we couldn't setup the protected "
"session";
return DecodeStatus::kFail;
} else if (state != ProtectedSessionState::kCreated) {
return DecodeStatus::kTryAgain;
}
uses_crypto = true;
if (!crypto_params_) {
crypto_params_ = vaapi_wrapper_->CreateVABuffer(
VAEncryptionParameterBufferType, sizeof(crypto_param));
if (!crypto_params_)
return DecodeStatus::kFail;
}
}
}
pic_param.frame_width = base::checked_cast<uint16_t>(frame_hdr->frame_width);
pic_param.frame_height =
......@@ -162,14 +196,22 @@ bool VP9VaapiVideoDecoderDelegate::SubmitDecode(
seg_param.chroma_ac_quant_scale = seg.uv_dequant[i][1];
}
return vaapi_wrapper_->MapAndCopyAndExecute(
pic->AsVaapiVP9Picture()->va_surface()->id(),
std::vector<std::pair<VABufferID, VaapiWrapper::VABufferDescriptor>> buffers =
{{picture_params_->id(),
{picture_params_->type(), picture_params_->size(), &pic_param}},
{slice_params_->id(),
{slice_params_->type(), slice_params_->size(), &slice_param}},
{encoded_data->id(),
{encoded_data->type(), frame_hdr->frame_size, frame_hdr->data}}});
{encoded_data->type(), frame_hdr->frame_size, frame_hdr->data}}};
if (uses_crypto) {
buffers.push_back(
{crypto_params_->id(),
{crypto_params_->type(), crypto_params_->size(), &crypto_param}});
}
return vaapi_wrapper_->MapAndCopyAndExecute(
pic->AsVaapiVP9Picture()->va_surface()->id(), buffers)
? DecodeStatus::kOk
: DecodeStatus::kFail;
}
bool VP9VaapiVideoDecoderDelegate::OutputPicture(
......
......@@ -19,17 +19,21 @@ class VP9Picture;
class VP9VaapiVideoDecoderDelegate : public VP9Decoder::VP9Accelerator,
public VaapiVideoDecoderDelegate {
public:
VP9VaapiVideoDecoderDelegate(DecodeSurfaceHandler<VASurface>* const vaapi_dec,
scoped_refptr<VaapiWrapper> vaapi_wrapper);
VP9VaapiVideoDecoderDelegate(
DecodeSurfaceHandler<VASurface>* const vaapi_dec,
scoped_refptr<VaapiWrapper> vaapi_wrapper,
ProtectedSessionUpdateCB on_protected_session_update_cb =
base::DoNothing(),
CdmContext* cdm_context = nullptr);
~VP9VaapiVideoDecoderDelegate() override;
// VP9Decoder::VP9Accelerator implementation.
scoped_refptr<VP9Picture> CreateVP9Picture() override;
bool SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& seg,
const Vp9LoopFilterParams& lf,
const Vp9ReferenceFrameVector& reference_frames,
base::OnceClosure done_cb) override;
Status SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& seg,
const Vp9LoopFilterParams& lf,
const Vp9ReferenceFrameVector& reference_frames,
base::OnceClosure done_cb) override;
bool OutputPicture(scoped_refptr<VP9Picture> pic) override;
bool IsFrameContextRequired() const override;
......@@ -42,6 +46,7 @@ class VP9VaapiVideoDecoderDelegate : public VP9Decoder::VP9Accelerator,
private:
std::unique_ptr<ScopedVABuffer> picture_params_;
std::unique_ptr<ScopedVABuffer> slice_params_;
std::unique_ptr<ScopedVABuffer> crypto_params_;
DISALLOW_COPY_AND_ASSIGN(VP9VaapiVideoDecoderDelegate);
};
......
......@@ -101,6 +101,7 @@ bool VP9Decoder::Flush() {
void VP9Decoder::Reset() {
curr_frame_hdr_ = nullptr;
pending_pic_.reset();
ref_frames_.Clear();
......@@ -112,7 +113,21 @@ void VP9Decoder::Reset() {
}
VP9Decoder::DecodeResult VP9Decoder::Decode() {
while (1) {
while (true) {
// If we have a pending picture to decode, try that first.
if (pending_pic_) {
VP9Accelerator::Status status = DecodeAndOutputPicture(pending_pic_);
if (status == VP9Accelerator::Status::kFail) {
pending_pic_.reset();
SetError();
return kDecodeError;
}
if (status == VP9Accelerator::Status::kTryAgain)
return kTryAgain;
pending_pic_.reset();
}
// Read a new frame header if one is not awaiting decoding already.
std::unique_ptr<DecryptConfig> decrypt_config;
if (!curr_frame_hdr_) {
......@@ -238,29 +253,35 @@ VP9Decoder::DecodeResult VP9Decoder::Decode() {
return kConfigChange;
}
scoped_refptr<VP9Picture> pic = accelerator_->CreateVP9Picture();
if (!pic) {
pending_pic_ = accelerator_->CreateVP9Picture();
if (!pending_pic_) {
return kRanOutOfSurfaces;
}
DVLOG(2) << "Render resolution: " << new_render_rect.ToString();
pic->set_visible_rect(new_render_rect);
pic->set_bitstream_id(stream_id_);
pending_pic_->set_visible_rect(new_render_rect);
pending_pic_->set_bitstream_id(stream_id_);
pic->set_decrypt_config(std::move(decrypt_config));
pending_pic_->set_decrypt_config(std::move(decrypt_config));
// For VP9, container color spaces override video stream color spaces.
if (container_color_space_.IsSpecified()) {
pic->set_colorspace(container_color_space_);
} else if (curr_frame_hdr_) {
pic->set_colorspace(curr_frame_hdr_->GetColorSpace());
}
pic->frame_hdr = std::move(curr_frame_hdr_);
if (container_color_space_.IsSpecified())
pending_pic_->set_colorspace(container_color_space_);
else if (curr_frame_hdr_)
pending_pic_->set_colorspace(curr_frame_hdr_->GetColorSpace());
pending_pic_->frame_hdr = std::move(curr_frame_hdr_);
if (!DecodeAndOutputPicture(std::move(pic))) {
VP9Accelerator::Status status = DecodeAndOutputPicture(pending_pic_);
if (status == VP9Accelerator::Status::kFail) {
pending_pic_.reset();
SetError();
return kDecodeError;
}
if (status == VP9Accelerator::Status::kTryAgain)
return kTryAgain;
pending_pic_.reset();
}
}
......@@ -279,7 +300,8 @@ void VP9Decoder::UpdateFrameContext(
std::move(context_refresh_cb).Run(frame_ctx);
}
bool VP9Decoder::DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic) {
VP9Decoder::VP9Accelerator::Status VP9Decoder::DecodeAndOutputPicture(
scoped_refptr<VP9Picture> pic) {
DCHECK(!pic_size_.IsEmpty());
DCHECK(pic->frame_hdr);
......@@ -293,20 +315,19 @@ bool VP9Decoder::DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic) {
}
const Vp9Parser::Context& context = parser_.context();
if (!accelerator_->SubmitDecode(pic, context.segmentation(),
context.loop_filter(), ref_frames_,
std::move(done_cb))) {
return false;
}
VP9Accelerator::Status status = accelerator_->SubmitDecode(
pic, context.segmentation(), context.loop_filter(), ref_frames_,
std::move(done_cb));
if (status != VP9Accelerator::Status::kOk)
return status;
if (pic->frame_hdr->show_frame) {
if (!accelerator_->OutputPicture(pic)) {
return false;
}
if (!accelerator_->OutputPicture(pic))
return VP9Accelerator::Status::kFail;
}
ref_frames_.Refresh(std::move(pic));
return true;
return status;
}
void VP9Decoder::SetError() {
......
......@@ -33,6 +33,24 @@ class MEDIA_GPU_EXPORT VP9Decoder : public AcceleratedVideoDecoder {
public:
class MEDIA_GPU_EXPORT VP9Accelerator {
public:
// Methods may return kTryAgain if they need additional data (provided
// independently) in order to proceed. Examples are things like not having
// an appropriate key to decode encrypted content. This is not considered an
// unrecoverable error, but rather a pause to allow an application to
// independently provide the required data. When VP9Decoder::Decode()
// is called again, it will attempt to resume processing of the stream
// by calling the same method again.
enum class Status {
// Operation completed successfully.
kOk,
// Operation failed.
kFail,
// Operation failed because some external data is missing. Retry the same
// operation later, once the data has been provided.
kTryAgain,
};
VP9Accelerator();
virtual ~VP9Accelerator();
......@@ -61,11 +79,11 @@ class MEDIA_GPU_EXPORT VP9Decoder : public AcceleratedVideoDecoder {
// |lf_params| does not need to remain valid after this method returns.
//
// Return true when successful, false otherwise.
virtual bool SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& segm_params,
const Vp9LoopFilterParams& lf_params,
const Vp9ReferenceFrameVector& reference_frames,
const base::OnceClosure done_cb) = 0;
virtual Status SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& segm_params,
const Vp9LoopFilterParams& lf_params,
const Vp9ReferenceFrameVector& reference_frames,
const base::OnceClosure done_cb) = 0;
// Schedule output (display) of |pic|.
//
......@@ -112,8 +130,9 @@ class MEDIA_GPU_EXPORT VP9Decoder : public AcceleratedVideoDecoder {
private:
// Decode and possibly output |pic| (if the picture is to be shown).
// Return true on success, false otherwise.
bool DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic);
// Return kOk on success, kTryAgain if this should be attempted again on the
// next Decode call, and kFail otherwise.
VP9Accelerator::Status DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic);
// Get frame context state after decoding |pic| from the accelerator, and call
// |context_refresh_cb| with the acquired state.
......@@ -155,6 +174,9 @@ class MEDIA_GPU_EXPORT VP9Decoder : public AcceleratedVideoDecoder {
// Profile of input bitstream.
VideoCodecProfile profile_;
// Pending picture for decode when accelerator returns kTryAgain.
scoped_refptr<VP9Picture> pending_pic_;
size_t size_change_failure_counter_ = 0;
const std::unique_ptr<VP9Accelerator> accelerator_;
......
......@@ -13,6 +13,8 @@
namespace media {
using DecodeStatus = VP9Decoder::VP9Accelerator::Status;
#define RETURN_ON_HR_FAILURE(expr_name, expr) \
do { \
HRESULT expr_value = (expr); \
......@@ -334,7 +336,7 @@ bool D3D11VP9Accelerator::SubmitDecoderBuffer(
#undef RELEASE_BUFFER
}
bool D3D11VP9Accelerator::SubmitDecode(
DecodeStatus D3D11VP9Accelerator::SubmitDecode(
scoped_refptr<VP9Picture> picture,
const Vp9SegmentationParams& segmentation_params,
const Vp9LoopFilterParams& loop_filter_params,
......@@ -343,7 +345,7 @@ bool D3D11VP9Accelerator::SubmitDecode(
D3D11VP9Picture* pic = static_cast<D3D11VP9Picture*>(picture.get());
if (!BeginFrame(*pic))
return false;
return DecodeStatus::kFail;
DXVA_PicParams_VP9 pic_params = {};
CopyFrameParams(*pic, &pic_params);
......@@ -355,13 +357,17 @@ bool D3D11VP9Accelerator::SubmitDecode(
CopyHeaderSizeAndID(&pic_params, *pic);
if (!SubmitDecoderBuffer(pic_params, *pic))
return false;
return DecodeStatus::kFail;
HRESULT hr = video_context_->DecoderEndFrame(video_decoder_.Get());
if (FAILED(hr)) {
RecordFailure("DecoderEndFrame", logging::SystemErrorCodeToString(hr));
return DecodeStatus::kFail;
}
RETURN_ON_HR_FAILURE(DecoderEndFrame,
video_context_->DecoderEndFrame(video_decoder_.Get()));
if (on_finished_cb)
std::move(on_finished_cb).Run();
return true;
return DecodeStatus::kOk;
}
bool D3D11VP9Accelerator::OutputPicture(scoped_refptr<VP9Picture> picture) {
......
......@@ -30,11 +30,11 @@ class D3D11VP9Accelerator : public VP9Decoder::VP9Accelerator {
scoped_refptr<VP9Picture> CreateVP9Picture() override;
bool SubmitDecode(scoped_refptr<VP9Picture> picture,
const Vp9SegmentationParams& segmentation_params,
const Vp9LoopFilterParams& loop_filter_params,
const Vp9ReferenceFrameVector& reference_frames,
base::OnceClosure on_finished_cb) override;
Status SubmitDecode(scoped_refptr<VP9Picture> picture,
const Vp9SegmentationParams& segmentation_params,
const Vp9LoopFilterParams& loop_filter_params,
const Vp9ReferenceFrameVector& reference_frames,
base::OnceClosure on_finished_cb) override;
bool OutputPicture(scoped_refptr<VP9Picture> picture) override;
......
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