blob: 9b2e4214546252df1fad81a881c380780c2d51a9 [file] [log] [blame]
// Copyright 2019 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/fuchsia/cdm/fuchsia_stream_decryptor.h"
#include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/media/drm/cpp/fidl.h>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "media/base/encryption_pattern.h"
#include "media/base/subsample_entry.h"
namespace media {
namespace {
std::string GetEncryptionScheme(EncryptionScheme mode) {
switch (mode) {
case EncryptionScheme::kCenc:
return fuchsia::media::ENCRYPTION_SCHEME_CENC;
case EncryptionScheme::kCbcs:
return fuchsia::media::ENCRYPTION_SCHEME_CBCS;
default:
NOTREACHED() << "unknown encryption mode " << static_cast<int>(mode);
return "";
}
}
std::vector<fuchsia::media::SubsampleEntry> GetSubsamples(
const std::vector<SubsampleEntry>& subsamples) {
std::vector<fuchsia::media::SubsampleEntry> fuchsia_subsamples(
subsamples.size());
for (size_t i = 0; i < subsamples.size(); i++) {
fuchsia_subsamples[i].clear_bytes = subsamples[i].clear_bytes;
fuchsia_subsamples[i].encrypted_bytes = subsamples[i].cypher_bytes;
}
return fuchsia_subsamples;
}
fuchsia::media::EncryptionPattern GetEncryptionPattern(
EncryptionPattern pattern) {
fuchsia::media::EncryptionPattern fuchsia_pattern;
fuchsia_pattern.clear_blocks = pattern.skip_byte_block();
fuchsia_pattern.encrypted_blocks = pattern.crypt_byte_block();
return fuchsia_pattern;
}
fuchsia::media::FormatDetails GetClearFormatDetails() {
fuchsia::media::EncryptedFormat encrypted_format;
encrypted_format.set_scheme(fuchsia::media::ENCRYPTION_SCHEME_UNENCRYPTED)
.set_subsamples({})
.set_init_vector({});
fuchsia::media::FormatDetails format;
format.set_format_details_version_ordinal(0);
format.mutable_domain()->crypto().set_encrypted(std::move(encrypted_format));
return format;
}
fuchsia::media::FormatDetails GetEncryptedFormatDetails(
const DecryptConfig* config) {
DCHECK(config);
fuchsia::media::EncryptedFormat encrypted_format;
encrypted_format.set_scheme(GetEncryptionScheme(config->encryption_scheme()))
.set_key_id(std::vector<uint8_t>(config->key_id().begin(),
config->key_id().end()))
.set_init_vector(
std::vector<uint8_t>(config->iv().begin(), config->iv().end()))
.set_subsamples(GetSubsamples(config->subsamples()));
if (config->encryption_scheme() == EncryptionScheme::kCbcs) {
DCHECK(config->encryption_pattern().has_value());
encrypted_format.set_pattern(
GetEncryptionPattern(config->encryption_pattern().value()));
}
fuchsia::media::FormatDetails format;
format.set_format_details_version_ordinal(0);
format.mutable_domain()->crypto().set_encrypted(std::move(encrypted_format));
return format;
}
} // namespace
FuchsiaStreamDecryptor::FuchsiaStreamDecryptor(
fuchsia::media::StreamProcessorPtr processor)
: processor_(std::move(processor), this),
allocator_("CrFuchsiaStreamDecryptor") {}
FuchsiaStreamDecryptor::~FuchsiaStreamDecryptor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
base::RepeatingClosure FuchsiaStreamDecryptor::GetOnNewKeyClosure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return BindToCurrentLoop(base::BindRepeating(
&FuchsiaStreamDecryptor::OnNewKey, weak_factory_.GetWeakPtr()));
}
void FuchsiaStreamDecryptor::Initialize(Sink* sink,
size_t min_buffer_size,
size_t min_buffer_count) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sink_ = sink;
min_buffer_size_ = min_buffer_size;
min_buffer_count_ = min_buffer_count;
input_buffer_collection_ = allocator_.AllocateNewCollection();
input_buffer_collection_->CreateSharedToken(
base::BindOnce(&StreamProcessorHelper::SetInputBufferCollectionToken,
base::Unretained(&processor_)));
auto buffer_constraints = VmoBuffer::GetRecommendedConstraints(
kInputBufferCount, min_buffer_size_, /*writable=*/true);
input_buffer_collection_->Initialize(std::move(buffer_constraints),
"CrFuchsiaStreamDecryptor");
input_buffer_collection_->AcquireBuffers(base::BindOnce(
&FuchsiaStreamDecryptor::OnInputBuffersAcquired, base::Unretained(this)));
}
void FuchsiaStreamDecryptor::EnqueueBuffer(
scoped_refptr<DecoderBuffer> buffer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
input_writer_queue_.EnqueueBuffer(std::move(buffer));
}
void FuchsiaStreamDecryptor::Reset() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Close current stream and drop all the cached decoder buffers.
// Keep input and output buffers to avoid buffer re-allocation.
processor_.Reset();
input_writer_queue_.ResetQueue();
waiting_for_key_ = false;
}
void FuchsiaStreamDecryptor::OnStreamProcessorAllocateOutputBuffers(
const fuchsia::media::StreamBufferConstraints& stream_constraints) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
output_buffer_collection_ = allocator_.AllocateNewCollection();
output_buffer_collection_->CreateSharedToken(
base::BindOnce(&StreamProcessorHelper::CompleteOutputBuffersAllocation,
base::Unretained(&processor_)));
output_buffer_collection_->CreateSharedToken(
base::BindOnce(&Sink::OnSysmemBufferStreamBufferCollectionToken,
base::Unretained(sink_)));
fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
buffer_constraints.usage.none = fuchsia::sysmem::noneUsage;
buffer_constraints.min_buffer_count = min_buffer_count_;
buffer_constraints.has_buffer_memory_constraints = true;
buffer_constraints.buffer_memory_constraints.min_size_bytes =
min_buffer_size_;
buffer_constraints.buffer_memory_constraints.ram_domain_supported = true;
buffer_constraints.buffer_memory_constraints.cpu_domain_supported = true;
buffer_constraints.buffer_memory_constraints.inaccessible_domain_supported =
true;
output_buffer_collection_->Initialize(std::move(buffer_constraints),
"CrFuchsiaStreamDecryptorOutput");
}
void FuchsiaStreamDecryptor::OnStreamProcessorEndOfStream() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sink_->OnSysmemBufferStreamEndOfStream();
}
void FuchsiaStreamDecryptor::OnStreamProcessorOutputFormat(
fuchsia::media::StreamOutputFormat format) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void FuchsiaStreamDecryptor::OnStreamProcessorOutputPacket(
StreamProcessorHelper::IoPacket packet) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sink_->OnSysmemBufferStreamOutputPacket(std::move(packet));
}
void FuchsiaStreamDecryptor::OnStreamProcessorNoKey() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!waiting_for_key_);
// Reset stream position, but keep all pending buffers. They will be
// resubmitted later, when we have a new key.
input_writer_queue_.ResetPositionAndPause();
if (retry_on_no_key_event_) {
retry_on_no_key_event_ = false;
input_writer_queue_.Unpause();
return;
}
waiting_for_key_ = true;
sink_->OnSysmemBufferStreamNoKey();
}
void FuchsiaStreamDecryptor::OnStreamProcessorError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OnError();
}
void FuchsiaStreamDecryptor::OnError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Reset();
// No need to reset other fields since OnError() is called for non-recoverable
// errors.
sink_->OnSysmemBufferStreamError();
}
void FuchsiaStreamDecryptor::OnInputBuffersAcquired(
std::vector<VmoBuffer> buffers,
const fuchsia::sysmem::SingleBufferSettings&) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (buffers.empty()) {
OnError();
return;
}
input_writer_queue_.Start(
std::move(buffers),
base::BindRepeating(&FuchsiaStreamDecryptor::SendInputPacket,
base::Unretained(this)),
base::BindRepeating(&FuchsiaStreamDecryptor::ProcessEndOfStream,
base::Unretained(this)));
}
void FuchsiaStreamDecryptor::SendInputPacket(
const DecoderBuffer* buffer,
StreamProcessorHelper::IoPacket packet) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!packet.unit_end()) {
// The encrypted data size is too big. Decryptor should consider
// splitting the buffer and update the IV and subsample entries.
// TODO(crbug.com/1003651): Handle large encrypted buffer correctly. For
// now, just reject the decryption.
LOG(ERROR) << "DecoderBuffer doesn't fit in one packet.";
OnError();
return;
}
fuchsia::media::FormatDetails format =
(buffer->decrypt_config())
? GetEncryptedFormatDetails(buffer->decrypt_config())
: GetClearFormatDetails();
packet.set_format(std::move(format));
processor_.Process(std::move(packet));
}
void FuchsiaStreamDecryptor::ProcessEndOfStream() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
processor_.ProcessEos();
}
void FuchsiaStreamDecryptor::OnNewKey() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!waiting_for_key_) {
retry_on_no_key_event_ = true;
return;
}
DCHECK(!retry_on_no_key_event_);
waiting_for_key_ = false;
input_writer_queue_.Unpause();
}
} // namespace media