blob: b84bd5552f29380f440202599a670f8f2096a43a [file] [log] [blame]
// Copyright (c) 2012 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/filters/decrypting_video_decoder.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_context.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
#include "media/base/video_frame.h"
namespace media {
const char DecryptingVideoDecoder::kDecoderName[] = "DecryptingVideoDecoder";
DecryptingVideoDecoder::DecryptingVideoDecoder(
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
MediaLog* media_log)
: task_runner_(task_runner), media_log_(media_log) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
VideoDecoderType DecryptingVideoDecoder::GetDecoderType() const {
return VideoDecoderType::kDecrypting;
}
void DecryptingVideoDecoder::Initialize(const VideoDecoderConfig& config,
bool /* low_delay */,
CdmContext* cdm_context,
InitCB init_cb,
const OutputCB& output_cb,
const WaitingCB& waiting_cb) {
DVLOG(2) << __func__ << ": " << config.AsHumanReadableString();
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(state_ == kUninitialized || state_ == kIdle ||
state_ == kDecodeFinished)
<< state_;
DCHECK(!decode_cb_);
DCHECK(!reset_cb_);
DCHECK(config.IsValidConfig());
init_cb_ = BindToCurrentLoop(std::move(init_cb));
if (!cdm_context) {
// Once we have a CDM context, one should always be present.
DCHECK(!support_clear_content_);
std::move(init_cb_).Run(StatusCode::kDecoderMissingCdmForEncryptedContent);
return;
}
if (!config.is_encrypted() && !support_clear_content_) {
std::move(init_cb_).Run(StatusCode::kClearContentUnsupported);
return;
}
// Once initialized with encryption support, the value is sticky, so we'll use
// the decryptor for clear content as well.
support_clear_content_ = true;
output_cb_ = BindToCurrentLoop(output_cb);
config_ = config;
DCHECK(waiting_cb);
waiting_cb_ = waiting_cb;
if (state_ == kUninitialized) {
if (!cdm_context->GetDecryptor()) {
DVLOG(1) << __func__ << ": no decryptor";
std::move(init_cb_).Run(StatusCode::kDecoderFailedInitialization);
return;
}
decryptor_ = cdm_context->GetDecryptor();
event_cb_registration_ = cdm_context->RegisterEventCB(
base::BindRepeating(&DecryptingVideoDecoder::OnCdmContextEvent,
weak_factory_.GetWeakPtr()));
} else {
// Reinitialization (i.e. upon a config change). The new config can be
// encrypted or clear.
decryptor_->DeinitializeDecoder(Decryptor::kVideo);
}
state_ = kPendingDecoderInit;
decryptor_->InitializeVideoDecoder(
config_, BindToCurrentLoop(
base::BindOnce(&DecryptingVideoDecoder::FinishInitialization,
weak_factory_.GetWeakPtr())));
}
bool DecryptingVideoDecoder::SupportsDecryption() const {
return true;
}
void DecryptingVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
DecodeCB decode_cb) {
DVLOG(3) << "Decode()";
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(state_ == kIdle || state_ == kDecodeFinished || state_ == kError)
<< state_;
DCHECK(decode_cb);
CHECK(!decode_cb_) << "Overlapping decodes are not supported.";
decode_cb_ = BindToCurrentLoop(std::move(decode_cb));
if (state_ == kError) {
std::move(decode_cb_).Run(DecodeStatus::DECODE_ERROR);
return;
}
// Return empty frames if decoding has finished.
if (state_ == kDecodeFinished) {
std::move(decode_cb_).Run(DecodeStatus::OK);
return;
}
pending_buffer_to_decode_ = std::move(buffer);
state_ = kPendingDecode;
DecodePendingBuffer();
}
void DecryptingVideoDecoder::Reset(base::OnceClosure closure) {
DVLOG(2) << "Reset() - state: " << state_;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(state_ == kIdle || state_ == kPendingDecode ||
state_ == kWaitingForKey || state_ == kDecodeFinished ||
state_ == kError)
<< state_;
DCHECK(!init_cb_); // No Reset() during pending initialization.
DCHECK(!reset_cb_);
reset_cb_ = BindToCurrentLoop(std::move(closure));
decryptor_->ResetDecoder(Decryptor::kVideo);
// Reset() cannot complete if the decode callback is still pending.
// Defer the resetting process in this case. The |reset_cb_| will be fired
// after the decode callback is fired - see DecryptAndDecodeBuffer() and
// DeliverFrame().
if (state_ == kPendingDecode) {
DCHECK(decode_cb_);
return;
}
if (state_ == kWaitingForKey) {
CompleteWaitingForDecryptionKey();
DCHECK(decode_cb_);
pending_buffer_to_decode_.reset();
std::move(decode_cb_).Run(DecodeStatus::ABORTED);
}
DCHECK(!decode_cb_);
DoReset();
}
DecryptingVideoDecoder::~DecryptingVideoDecoder() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ == kUninitialized)
return;
if (state_ == kWaitingForKey)
CompleteWaitingForDecryptionKey();
if (state_ == kPendingDecode)
CompletePendingDecode(Decryptor::kError);
if (decryptor_) {
decryptor_->DeinitializeDecoder(Decryptor::kVideo);
decryptor_ = nullptr;
}
pending_buffer_to_decode_.reset();
if (init_cb_)
std::move(init_cb_).Run(StatusCode::kDecoderInitializeNeverCompleted);
if (decode_cb_)
std::move(decode_cb_).Run(DecodeStatus::ABORTED);
if (reset_cb_)
std::move(reset_cb_).Run();
}
void DecryptingVideoDecoder::FinishInitialization(bool success) {
DVLOG(2) << "FinishInitialization()";
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, kPendingDecoderInit) << state_;
DCHECK(init_cb_);
DCHECK(!reset_cb_); // No Reset() before initialization finished.
DCHECK(!decode_cb_); // No Decode() before initialization finished.
if (!success) {
DVLOG(1) << __func__ << ": failed to init video decoder on decryptor";
std::move(init_cb_).Run(StatusCode::kDecoderInitializeNeverCompleted);
decryptor_ = nullptr;
event_cb_registration_.reset();
state_ = kError;
return;
}
// Success!
state_ = kIdle;
std::move(init_cb_).Run(OkStatus());
}
void DecryptingVideoDecoder::DecodePendingBuffer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, kPendingDecode) << state_;
// Note: Traces require a unique ID per decode, if we ever support multiple
// in flight decodes, the trace begin+end macros need the same unique id.
DCHECK_EQ(GetMaxDecodeRequests(), 1);
TRACE_EVENT_ASYNC_BEGIN1(
"media", "DecryptingVideoDecoder::DecodePendingBuffer", this,
"timestamp_us",
pending_buffer_to_decode_->end_of_stream()
? 0
: pending_buffer_to_decode_->timestamp().InMicroseconds());
decryptor_->DecryptAndDecodeVideo(
pending_buffer_to_decode_,
BindToCurrentLoop(base::BindRepeating(
&DecryptingVideoDecoder::DeliverFrame, weak_factory_.GetWeakPtr())));
}
void DecryptingVideoDecoder::DeliverFrame(Decryptor::Status status,
scoped_refptr<VideoFrame> frame) {
DVLOG(3) << "DeliverFrame() - status: " << status;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, kPendingDecode) << state_;
DCHECK(decode_cb_);
DCHECK(pending_buffer_to_decode_.get());
CompletePendingDecode(status);
bool need_to_try_again_if_nokey_is_returned = key_added_while_decode_pending_;
key_added_while_decode_pending_ = false;
scoped_refptr<DecoderBuffer> scoped_pending_buffer_to_decode =
std::move(pending_buffer_to_decode_);
if (reset_cb_) {
std::move(decode_cb_).Run(DecodeStatus::ABORTED);
DoReset();
return;
}
DCHECK_EQ(status == Decryptor::kSuccess, frame.get() != nullptr);
if (status == Decryptor::kError) {
DVLOG(2) << "DeliverFrame() - kError";
MEDIA_LOG(ERROR, media_log_) << GetDecoderType() << ": decode error";
state_ = kError;
std::move(decode_cb_).Run(DecodeStatus::DECODE_ERROR);
return;
}
if (status == Decryptor::kNoKey) {
std::string key_id =
scoped_pending_buffer_to_decode->decrypt_config()->key_id();
std::string log_message =
"no key for key ID " + base::HexEncode(key_id.data(), key_id.size()) +
"; will resume decoding after new usable key is available";
DVLOG(1) << __func__ << ": " << log_message;
MEDIA_LOG(INFO, media_log_) << GetDecoderType() << ": " << log_message;
// Set |pending_buffer_to_decode_| back as we need to try decoding the
// pending buffer again when new key is added to the decryptor.
pending_buffer_to_decode_ = std::move(scoped_pending_buffer_to_decode);
if (need_to_try_again_if_nokey_is_returned) {
// The |state_| is still kPendingDecode.
MEDIA_LOG(INFO, media_log_)
<< GetDecoderType() << ": key was added, resuming decode";
DecodePendingBuffer();
return;
}
TRACE_EVENT_ASYNC_BEGIN0(
"media", "DecryptingVideoDecoder::WaitingForDecryptionKey", this);
state_ = kWaitingForKey;
waiting_cb_.Run(WaitingReason::kNoDecryptionKey);
return;
}
if (status == Decryptor::kNeedMoreData) {
DVLOG(2) << "DeliverFrame() - kNeedMoreData";
state_ = scoped_pending_buffer_to_decode->end_of_stream() ? kDecodeFinished
: kIdle;
std::move(decode_cb_).Run(DecodeStatus::OK);
return;
}
DCHECK_EQ(status, Decryptor::kSuccess);
CHECK(frame);
// Frame returned with kSuccess should not be an end-of-stream frame.
DCHECK(!frame->metadata().end_of_stream);
// If color space is not set, use the color space in the |config_|.
if (!frame->ColorSpace().IsValid()) {
DVLOG(3) << "Setting color space using information in the config.";
if (config_.color_space_info().IsSpecified())
frame->set_color_space(config_.color_space_info().ToGfxColorSpace());
}
output_cb_.Run(std::move(frame));
if (scoped_pending_buffer_to_decode->end_of_stream()) {
// Set |pending_buffer_to_decode_| back as we need to keep flushing the
// decryptor.
pending_buffer_to_decode_ = std::move(scoped_pending_buffer_to_decode);
DecodePendingBuffer();
return;
}
state_ = kIdle;
std::move(decode_cb_).Run(DecodeStatus::OK);
}
void DecryptingVideoDecoder::OnCdmContextEvent(CdmContext::Event event) {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (event != CdmContext::Event::kHasAdditionalUsableKey)
return;
if (state_ == kPendingDecode) {
key_added_while_decode_pending_ = true;
return;
}
if (state_ == kWaitingForKey) {
CompleteWaitingForDecryptionKey();
MEDIA_LOG(INFO, media_log_)
<< GetDecoderType() << ": key added, resuming decode";
state_ = kPendingDecode;
DecodePendingBuffer();
}
}
void DecryptingVideoDecoder::DoReset() {
DCHECK(!init_cb_);
DCHECK(!decode_cb_);
state_ = kIdle;
std::move(reset_cb_).Run();
}
void DecryptingVideoDecoder::CompletePendingDecode(Decryptor::Status status) {
DCHECK_EQ(state_, kPendingDecode);
TRACE_EVENT_ASYNC_END1("media", "DecryptingVideoDecoder::DecodePendingBuffer",
this, "status", Decryptor::GetStatusName(status));
}
void DecryptingVideoDecoder::CompleteWaitingForDecryptionKey() {
DCHECK_EQ(state_, kWaitingForKey);
TRACE_EVENT_ASYNC_END0(
"media", "DecryptingVideoDecoder::WaitingForDecryptionKey", this);
}
} // namespace media