blob: d1d21d9e5c8abd508c82cf059e0e59822ff57714 [file] [log] [blame]
// Copyright 2018 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/renderers/decrypting_renderer.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "media/base/cdm_context.h"
#include "media/base/demuxer_stream.h"
#include "media/base/media_log.h"
#include "media/base/media_resource.h"
#include "media/base/renderer_client.h"
#include "media/filters/decrypting_demuxer_stream.h"
#include "media/filters/decrypting_media_resource.h"
namespace media {
DecryptingRenderer::DecryptingRenderer(
std::unique_ptr<Renderer> renderer,
MediaLog* media_log,
const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner)
: renderer_(std::move(renderer)),
media_log_(media_log),
media_task_runner_(media_task_runner),
client_(nullptr),
media_resource_(nullptr),
decrypting_media_resource_(nullptr) {
DCHECK(renderer_);
}
DecryptingRenderer::~DecryptingRenderer() {}
// The behavior of Initialize():
//
// Streams CdmContext Action
// ---------------------------------------------------------------------
// Clear nullptr InitializeRenderer()
// Clear AesDecryptor CreateAndInitializeDecryptingMediaResource()
// Clear Other InitializeRenderer()
// Encrypted nullptr Wait
// Encrypted AesDecryptor CreateAndInitializeDecryptingMediaResource()
// Encrypted Other InitializeRenderer()
void DecryptingRenderer::Initialize(MediaResource* media_resource,
RendererClient* client,
PipelineStatusCallback init_cb) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DCHECK(media_resource);
DCHECK(client);
// Using |this| with a MediaResource::Type::URL will result in a crash.
DCHECK_EQ(media_resource->GetType(), MediaResource::Type::STREAM);
media_resource_ = media_resource;
client_ = client;
init_cb_ = std::move(init_cb);
bool has_encrypted_stream = HasEncryptedStream();
// If we do not have a valid |cdm_context_| and there are encrypted streams we
// need to wait.
if (!cdm_context_ && has_encrypted_stream) {
waiting_for_cdm_ = true;
return;
}
if (cdm_context_ && cdm_context_->GetDecryptor() &&
cdm_context_->GetDecryptor()->CanAlwaysDecrypt()) {
CreateAndInitializeDecryptingMediaResource();
return;
}
InitializeRenderer(true);
}
void DecryptingRenderer::SetCdm(CdmContext* cdm_context,
CdmAttachedCB cdm_attached_cb) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
if (cdm_context_) {
DVLOG(1) << "Switching CDM not supported.";
std::move(cdm_attached_cb).Run(false);
return;
}
cdm_context_ = cdm_context;
// If we are using an AesDecryptor all decryption will be handled by the
// DecryptingMediaResource instead of the renderer implementation.
if (cdm_context_->GetDecryptor() &&
cdm_context_->GetDecryptor()->CanAlwaysDecrypt()) {
// If Initialize() was invoked prior to this function then
// |waiting_for_cdm_| will be true (if we reached this branch). In this
// scenario we want to initialize the DecryptingMediaResource here.
if (waiting_for_cdm_)
CreateAndInitializeDecryptingMediaResource();
std::move(cdm_attached_cb).Run(true);
return;
}
renderer_->SetCdm(cdm_context_, std::move(cdm_attached_cb));
// We only want to initialize the renderer if we were waiting for the
// CdmContext, otherwise it will already have been initialized.
if (waiting_for_cdm_)
InitializeRenderer(true);
}
void DecryptingRenderer::SetLatencyHint(
absl::optional<base::TimeDelta> latency_hint) {
renderer_->SetLatencyHint(latency_hint);
}
void DecryptingRenderer::SetPreservesPitch(bool preserves_pitch) {
renderer_->SetPreservesPitch(preserves_pitch);
}
void DecryptingRenderer::SetAutoplayInitiated(bool autoplay_initiated) {
renderer_->SetAutoplayInitiated(autoplay_initiated);
}
void DecryptingRenderer::Flush(base::OnceClosure flush_cb) {
renderer_->Flush(std::move(flush_cb));
}
void DecryptingRenderer::StartPlayingFrom(base::TimeDelta time) {
renderer_->StartPlayingFrom(time);
}
void DecryptingRenderer::SetPlaybackRate(double playback_rate) {
renderer_->SetPlaybackRate(playback_rate);
}
void DecryptingRenderer::SetVolume(float volume) {
renderer_->SetVolume(volume);
}
base::TimeDelta DecryptingRenderer::GetMediaTime() {
return renderer_->GetMediaTime();
}
void DecryptingRenderer::OnSelectedVideoTracksChanged(
const std::vector<DemuxerStream*>& enabled_tracks,
base::OnceClosure change_completed_cb) {
renderer_->OnSelectedVideoTracksChanged(enabled_tracks,
std::move(change_completed_cb));
}
void DecryptingRenderer::OnEnabledAudioTracksChanged(
const std::vector<DemuxerStream*>& enabled_tracks,
base::OnceClosure change_completed_cb) {
renderer_->OnEnabledAudioTracksChanged(enabled_tracks,
std::move(change_completed_cb));
}
void DecryptingRenderer::CreateAndInitializeDecryptingMediaResource() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DCHECK(init_cb_);
decrypting_media_resource_ = std::make_unique<DecryptingMediaResource>(
media_resource_, cdm_context_, media_log_, media_task_runner_);
decrypting_media_resource_->Initialize(
base::BindOnce(&DecryptingRenderer::InitializeRenderer,
weak_factory_.GetWeakPtr()),
base::BindRepeating(&DecryptingRenderer::OnWaiting,
weak_factory_.GetWeakPtr()));
}
void DecryptingRenderer::InitializeRenderer(bool success) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
if (!success) {
std::move(init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
return;
}
// |decrypting_media_resource_| when |cdm_context_| is null and there are no
// encrypted streams.
MediaResource* const maybe_decrypting_media_resource =
decrypting_media_resource_ ? decrypting_media_resource_.get()
: media_resource_;
renderer_->Initialize(maybe_decrypting_media_resource, client_,
std::move(init_cb_));
}
bool DecryptingRenderer::HasEncryptedStream() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
for (auto* stream : media_resource_->GetAllStreams()) {
if ((stream->type() == DemuxerStream::AUDIO &&
stream->audio_decoder_config().is_encrypted()) ||
(stream->type() == DemuxerStream::VIDEO &&
stream->video_decoder_config().is_encrypted())) {
return true;
}
}
return false;
}
bool DecryptingRenderer::HasDecryptingMediaResourceForTesting() const {
return decrypting_media_resource_ != nullptr;
}
void DecryptingRenderer::OnWaiting(WaitingReason reason) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
client_->OnWaiting(reason);
}
} // namespace media