1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/functional/bind.h"
10 #include "base/functional/callback_helpers.h"
11 #include "base/test/gmock_callback_support.h"
12 #include "base/test/mock_callback.h"
13 #include "base/test/task_environment.h"
14 #include "media/base/decoder_buffer.h"
15 #include "media/base/decrypt_config.h"
16 #include "media/base/decryptor.h"
17 #include "media/base/demuxer_stream.h"
18 #include "media/base/media_util.h"
19 #include "media/base/mock_filters.h"
20 #include "media/base/pipeline_status.h"
21 #include "media/base/test_helpers.h"
22 #include "media/filters/decrypting_demuxer_stream.h"
23 #include "media/filters/decrypting_media_resource.h"
24 #include "testing/gmock/include/gmock/gmock.h"
26 using ::base::test::RunCallback;
27 using ::base::test::RunOnceCallback;
29 using ::testing::AnyNumber;
30 using ::testing::Invoke;
31 using ::testing::Return;
32 using ::testing::StrictMock;
36 static constexpr int kFakeBufferSize = 16;
37 static constexpr char kFakeKeyId[] = "Key ID";
38 static constexpr char kFakeIv[] = "0123456789abcdef";
40 // Use anonymous namespace here to prevent the actions to be defined multiple
41 // times across multiple test files. Sadly we can't use static for them.
44 ACTION_P(ReturnBuffer, buffer) {
46 buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, {buffer});
51 class DecryptingMediaResourceTest : public testing::Test {
53 DecryptingMediaResourceTest() {
54 encrypted_buffer_ = base::MakeRefCounted<DecoderBuffer>(kFakeBufferSize);
55 encrypted_buffer_->set_decrypt_config(
56 DecryptConfig::CreateCencConfig(kFakeKeyId, kFakeIv, {}));
58 EXPECT_CALL(cdm_context_, RegisterEventCB(_)).Times(AnyNumber());
59 EXPECT_CALL(cdm_context_, GetDecryptor())
60 .WillRepeatedly(Return(&decryptor_));
61 EXPECT_CALL(decryptor_, CanAlwaysDecrypt()).WillRepeatedly(Return(true));
62 EXPECT_CALL(decryptor_, CancelDecrypt(_)).Times(AnyNumber());
63 EXPECT_CALL(demuxer_, GetAllStreams())
65 Invoke(this, &DecryptingMediaResourceTest::GetAllStreams));
67 decrypting_media_resource_ = std::make_unique<DecryptingMediaResource>(
68 &demuxer_, &cdm_context_, &null_media_log_,
69 task_environment_.GetMainThreadTaskRunner());
72 ~DecryptingMediaResourceTest() override {
73 // Ensure that the DecryptingMediaResource is destructed before other
74 // objects that it internally references but does not own.
75 decrypting_media_resource_.reset();
78 bool HasEncryptedStream() {
79 for (auto* stream : decrypting_media_resource_->GetAllStreams()) {
80 if ((stream->type() == DemuxerStream::AUDIO &&
81 stream->audio_decoder_config().is_encrypted()) ||
82 (stream->type() == DemuxerStream::VIDEO &&
83 stream->video_decoder_config().is_encrypted()))
90 void AddStream(DemuxerStream::Type type, bool encrypted) {
91 streams_.push_back(CreateMockDemuxerStream(type, encrypted));
94 std::vector<DemuxerStream*> GetAllStreams() {
95 std::vector<DemuxerStream*> streams;
97 for (auto& stream : streams_) {
98 streams.push_back(stream.get());
104 MOCK_METHOD2(BufferReady,
105 void(DemuxerStream::Status, DemuxerStream::DecoderBufferVector));
108 base::test::TaskEnvironment task_environment_;
109 base::MockCallback<DecryptingMediaResource::InitCB>
110 decrypting_media_resource_init_cb_;
111 base::MockCallback<WaitingCB> waiting_cb_;
112 NullMediaLog null_media_log_;
113 StrictMock<MockDecryptor> decryptor_;
114 StrictMock<MockDemuxer> demuxer_;
115 StrictMock<MockCdmContext> cdm_context_;
116 std::unique_ptr<DecryptingMediaResource> decrypting_media_resource_;
117 std::vector<std::unique_ptr<StrictMock<MockDemuxerStream>>> streams_;
119 // Constant buffer to be returned by the input demuxer streams and
121 scoped_refptr<DecoderBuffer> encrypted_buffer_;
124 TEST_F(DecryptingMediaResourceTest, ClearStreams) {
125 AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
126 AddStream(DemuxerStream::VIDEO, /* encrypted = */ false);
128 EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
130 decrypting_media_resource_->Initialize(
131 decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get());
132 task_environment_.RunUntilIdle();
135 decrypting_media_resource_->DecryptingDemuxerStreamCountForTesting(), 2);
136 EXPECT_FALSE(HasEncryptedStream());
139 TEST_F(DecryptingMediaResourceTest, EncryptedStreams) {
140 AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
141 AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
143 EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
145 decrypting_media_resource_->Initialize(
146 decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get());
147 task_environment_.RunUntilIdle();
149 // When using an AesDecryptor we preemptively wrap our streams with a
150 // DecryptingDemuxerStream, regardless of encryption. With this in mind, we
151 // should have three DecryptingDemuxerStreams.
153 decrypting_media_resource_->DecryptingDemuxerStreamCountForTesting(), 2);
155 // All of the streams that we get from our DecryptingMediaResource, NOT the
156 // internal MediaResource implementation, should be clear.
157 EXPECT_FALSE(HasEncryptedStream());
160 TEST_F(DecryptingMediaResourceTest, MixedStreams) {
161 AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
162 AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
164 EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
166 decrypting_media_resource_->Initialize(
167 decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get());
168 task_environment_.RunUntilIdle();
171 decrypting_media_resource_->DecryptingDemuxerStreamCountForTesting(), 2);
172 EXPECT_FALSE(HasEncryptedStream());
175 TEST_F(DecryptingMediaResourceTest,
176 OneDecryptingDemuxerStreamFailsInitialization) {
177 AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
178 AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
180 // The first DecryptingDemuxerStream will fail to initialize, causing the
181 // callback to be run with a value of false. The second
182 // DecryptingDemuxerStream will succeed but never invoke the callback.
183 EXPECT_CALL(cdm_context_, GetDecryptor())
184 .WillOnce(Return(nullptr))
185 .WillRepeatedly(Return(&decryptor_));
186 EXPECT_CALL(decrypting_media_resource_init_cb_, Run(false));
188 decrypting_media_resource_->Initialize(
189 decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get());
190 task_environment_.RunUntilIdle();
193 TEST_F(DecryptingMediaResourceTest,
194 BothDecryptingDemuxerStreamsFailInitialization) {
195 AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
196 AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
198 // Both DecryptingDemuxerStreams will fail to initialize but the callback
199 // should still only be invoked a single time.
200 EXPECT_CALL(cdm_context_, GetDecryptor()).WillRepeatedly(Return(nullptr));
201 EXPECT_CALL(decrypting_media_resource_init_cb_, Run(false));
203 decrypting_media_resource_->Initialize(
204 decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get());
205 task_environment_.RunUntilIdle();
208 TEST_F(DecryptingMediaResourceTest, WaitingCallback) {
209 AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
211 EXPECT_CALL(*streams_.front(), OnRead(_))
212 .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
213 EXPECT_CALL(decryptor_, Decrypt(_, encrypted_buffer_, _))
214 .WillRepeatedly(RunOnceCallback<2>(Decryptor::kNoKey,
215 scoped_refptr<DecoderBuffer>()));
216 EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
217 EXPECT_CALL(waiting_cb_, Run(WaitingReason::kNoDecryptionKey));
219 decrypting_media_resource_->Initialize(
220 decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get());
221 decrypting_media_resource_->GetAllStreams().front()->Read(
222 1, base::BindOnce(&DecryptingMediaResourceTest::BufferReady,
223 base::Unretained(this)));
224 task_environment_.RunUntilIdle();