// 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/bind_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, MediaLog* media_log, const scoped_refptr 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( base::Optional latency_hint) { renderer_->SetLatencyHint(latency_hint); } void DecryptingRenderer::SetPreservesPitch(bool preserves_pitch) { renderer_->SetPreservesPitch(preserves_pitch); } 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& enabled_tracks, base::OnceClosure change_completed_cb) { renderer_->OnSelectedVideoTracksChanged(enabled_tracks, std::move(change_completed_cb)); } void DecryptingRenderer::OnEnabledAudioTracksChanged( const std::vector& 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( 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