| // 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 |