// 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 "base/single_thread_task_runner.h" #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" #include "base/test/task_environment.h" #include "media/base/demuxer_stream.h" #include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/filters/decrypting_media_resource.h" #include "testing/gmock/include/gmock/gmock.h" using ::base::test::RunCallback; using ::base::test::RunOnceCallback; using ::testing::_; using ::testing::AnyNumber; using ::testing::Invoke; using ::testing::Return; using ::testing::ReturnPointee; using ::testing::StrictMock; namespace media { class CdmContext; class DemuxerStream; class MediaLog; class DecryptingRendererTest : public testing::Test { public: DecryptingRendererTest() { auto renderer = std::make_unique>(); renderer_ = renderer.get(); decrypting_renderer_ = std::make_unique( std::move(renderer), &null_media_log_, task_environment_.GetMainThreadTaskRunner()); EXPECT_CALL(cdm_context_, GetDecryptor()) .WillRepeatedly(Return(&decryptor_)); EXPECT_CALL(decryptor_, CanAlwaysDecrypt()) .WillRepeatedly(ReturnPointee(&use_aes_decryptor_)); EXPECT_CALL(decryptor_, CancelDecrypt(_)).Times(AnyNumber()); EXPECT_CALL(decryptor_, RegisterNewKeyCB(_, _)).Times(AnyNumber()); EXPECT_CALL(media_resource_, GetAllStreams()) .WillRepeatedly(Invoke(this, &DecryptingRendererTest::GetAllStreams)); EXPECT_CALL(media_resource_, GetType()) .WillRepeatedly(Return(MediaResource::STREAM)); } ~DecryptingRendererTest() override { // Ensure that the DecryptingRenderer is destructed before other objects // that it internally references but does not own. decrypting_renderer_.reset(); } void AddStream(DemuxerStream::Type type, bool encrypted) { streams_.push_back(CreateMockDemuxerStream(type, encrypted)); } void UseAesDecryptor(bool use_aes_decryptor) { use_aes_decryptor_ = use_aes_decryptor; } std::vector GetAllStreams() { std::vector streams; for (auto& stream : streams_) { streams.push_back(stream.get()); } return streams; } protected: // Invoking InitializeRenderer(false) will cause the initialization of the // DecryptingRenderer to halt and an error will be propagated to the media // pipeline. void InitializeDecryptingRendererWithFalse() { decrypting_renderer_->InitializeRenderer(false); } bool use_aes_decryptor_ = false; base::test::TaskEnvironment task_environment_; base::MockCallback set_cdm_cb_; base::MockOnceCallback renderer_init_cb_; NullMediaLog null_media_log_; StrictMock cdm_context_; StrictMock decryptor_; StrictMock media_resource_; StrictMock renderer_client_; StrictMock* renderer_; std::unique_ptr decrypting_renderer_; std::vector>> streams_; }; TEST_F(DecryptingRendererTest, ClearStreams_NoCdm) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ false); AddStream(DemuxerStream::VIDEO, /* encrypted = */ false); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, ClearStreams_AesDecryptor) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ false); AddStream(DemuxerStream::VIDEO, /* encrypted = */ false); UseAesDecryptor(true); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_TRUE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, ClearStreams_OtherCdm) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ false); AddStream(DemuxerStream::VIDEO, /* encrypted = */ false); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(*renderer_, OnSetCdm(_, _)).WillOnce(RunOnceCallback<1>(true)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, EncryptedStreams_NoCdm) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ true); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, EncryptedStreams_AesDecryptor) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ true); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); UseAesDecryptor(true); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_TRUE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, EncryptedStreams_OtherCdm) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ true); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(*renderer_, OnSetCdm(_, _)).WillOnce(RunOnceCallback<1>(true)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, EncryptedStreams_AesDecryptor_CdmSetBeforeInit) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ true); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); UseAesDecryptor(true); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_TRUE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, EncryptedStreams_OtherCdm_CdmSetBeforeInit) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ true); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(*renderer_, OnSetCdm(_, _)).WillOnce(RunOnceCallback<1>(true)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, EncryptedAndClearStream_OtherCdm) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ false); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); EXPECT_CALL(*renderer_, OnInitialize(_, _, _)) .WillOnce(RunOnceCallback<2>(PIPELINE_OK)); EXPECT_CALL(*renderer_, OnSetCdm(_, _)).WillOnce(RunOnceCallback<1>(true)); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK)); EXPECT_CALL(set_cdm_cb_, Run(true)); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get()); task_environment_.RunUntilIdle(); EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting()); } TEST_F(DecryptingRendererTest, DecryptingMediaResourceInitFails) { AddStream(DemuxerStream::AUDIO, /* encrypted = */ false); AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); UseAesDecryptor(true); EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_ERROR_INITIALIZATION_FAILED)); decrypting_renderer_->Initialize(&media_resource_, &renderer_client_, renderer_init_cb_.Get()); task_environment_.RunUntilIdle(); // Cause a PIPELINE_ERROR_INITIALIZATION_FAILED error to be passed as a // parameter to the initialization callback. InitializeDecryptingRendererWithFalse(); } } // namespace media