Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Side by Side Diff: media/filters/pipeline_integration_test.cc

Issue 11226019: Encrypted Media: Replace DecryptorClient with key event callbacks. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: tests updated; ready for review Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/pipeline_integration_test_base.h" 5 #include "media/filters/pipeline_integration_test_base.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/string_util.h" 8 #include "base/string_util.h"
9 #include "media/base/decoder_buffer.h" 9 #include "media/base/decoder_buffer.h"
10 #include "media/base/decryptor_client.h"
11 #include "media/base/test_data_util.h" 10 #include "media/base/test_data_util.h"
12 #include "media/crypto/aes_decryptor.h" 11 #include "media/crypto/aes_decryptor.h"
13 12
14 using testing::AtMost; 13 using testing::AtMost;
15 14
16 namespace media { 15 namespace media {
17 16
18 static const char kSourceId[] = "SourceId"; 17 static const char kSourceId[] = "SourceId";
19 static const char kClearKeySystem[] = "org.w3.clearkey"; 18 static const char kClearKeySystem[] = "org.w3.clearkey";
20 static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; 19 static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 };
21 20
22 static const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\""; 21 static const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\"";
23 static const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\""; 22 static const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\"";
24 static const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\""; 23 static const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\"";
25 static const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\""; 24 static const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\"";
26 25
27 // Key used to encrypt video track in test file "bear-320x240-encrypted.webm". 26 // Key used to encrypt video track in test file "bear-320x240-encrypted.webm".
28 static const uint8 kSecretKey[] = { 27 static const uint8 kSecretKey[] = {
29 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, 28 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
30 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c 29 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c
31 }; 30 };
32 31
33 static const int kAppendWholeFile = -1; 32 static const int kAppendWholeFile = -1;
34 33
34 class FakeEncryptedMedia {
xhwang 2012/12/20 00:06:01 moved from old code, renamed, and removed "virtual
35 public:
36 FakeEncryptedMedia()
37 : decryptor_(base::Bind(&FakeEncryptedMedia::KeyAdded,
38 base::Unretained(this)),
39 base::Bind(&FakeEncryptedMedia::KeyError,
40 base::Unretained(this)),
41 base::Bind(&FakeEncryptedMedia::KeyMessage,
42 base::Unretained(this)),
43 base::Bind(&FakeEncryptedMedia::NeedKey,
44 base::Unretained(this))) {
45 }
46
47 AesDecryptor* decryptor() {
48 return &decryptor_;
49 }
50
51 // DecryptorClient implementation.
scherkus (not reviewing) 2012/12/20 15:18:55 class doesn't exist anymore
xhwang 2012/12/20 18:06:31 Done.
52 void KeyAdded(const std::string& key_system, const std::string& session_id) {
53 EXPECT_EQ(kClearKeySystem, key_system);
54 EXPECT_FALSE(session_id.empty());
55 }
56
57 void KeyError(const std::string& key_system,
58 const std::string& session_id,
59 AesDecryptor::KeyError error_code,
60 int system_code) {
61 NOTIMPLEMENTED();
62 }
63
64 void KeyMessage(const std::string& key_system,
65 const std::string& session_id,
66 const std::string& message,
67 const std::string& default_url) {
68 EXPECT_EQ(kClearKeySystem, key_system);
69 EXPECT_FALSE(session_id.empty());
70 EXPECT_FALSE(message.empty());
71
72 current_key_system_ = key_system;
73 current_session_id_ = session_id;
74 }
75
76 void NeedKey(const std::string& key_system,
77 const std::string& session_id,
78 const std::string& type,
79 scoped_array<uint8> init_data, int init_data_length) {
80 current_key_system_ = key_system;
81 current_session_id_ = session_id;
82
83 // When NeedKey is called from the demuxer, the |key_system| will be empty.
84 // In this case, we need to call GenerateKeyRequest() to initialize a
85 // session (which will call KeyMessage).
86 if (current_key_system_.empty()) {
87 DCHECK(current_session_id_.empty());
88 EXPECT_TRUE(decryptor_.GenerateKeyRequest(
89 kClearKeySystem, type, kInitData, arraysize(kInitData)));
90 }
91
92 EXPECT_FALSE(current_key_system_.empty());
93 EXPECT_FALSE(current_session_id_.empty());
94 decryptor_.AddKey(current_key_system_, kSecretKey, arraysize(kSecretKey),
95 init_data.get(), init_data_length, current_session_id_);
96 }
97
98 private:
99 AesDecryptor decryptor_;
100 std::string current_key_system_;
101 std::string current_session_id_;
102 };
103
35 // Helper class that emulates calls made on the ChunkDemuxer by the 104 // Helper class that emulates calls made on the ChunkDemuxer by the
36 // Media Source API. 105 // Media Source API.
37 class MockMediaSource { 106 class MockMediaSource {
38 public: 107 public:
39 MockMediaSource(const std::string& filename, const std::string& mimetype, 108 MockMediaSource(const std::string& filename, const std::string& mimetype,
40 int initial_append_size) 109 int initial_append_size)
41 : file_path_(GetTestDataFilePath(filename)), 110 : file_path_(GetTestDataFilePath(filename)),
42 current_position_(0), 111 current_position_(0),
43 initial_append_size_(initial_append_size), 112 initial_append_size_(initial_append_size),
44 mimetype_(mimetype) { 113 mimetype_(mimetype) {
45 chunk_demuxer_ = new ChunkDemuxer( 114 chunk_demuxer_ = new ChunkDemuxer(
46 base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)), 115 base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)),
47 base::Bind(&MockMediaSource::DemuxerNeedKey, base::Unretained(this)), 116 base::Bind(&MockMediaSource::DemuxerNeedKey, base::Unretained(this)),
48 LogCB()); 117 LogCB());
49 118
50 file_data_ = ReadTestDataFile(filename); 119 file_data_ = ReadTestDataFile(filename);
51 120
52 if (initial_append_size_ == kAppendWholeFile) 121 if (initial_append_size_ == kAppendWholeFile)
53 initial_append_size_ = file_data_->GetDataSize(); 122 initial_append_size_ = file_data_->GetDataSize();
54 123
55 DCHECK_GT(initial_append_size_, 0); 124 DCHECK_GT(initial_append_size_, 0);
56 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); 125 DCHECK_LE(initial_append_size_, file_data_->GetDataSize());
57 } 126 }
58 127
59 virtual ~MockMediaSource() {} 128 virtual ~MockMediaSource() {}
60 129
61 const scoped_refptr<ChunkDemuxer>& demuxer() const { return chunk_demuxer_; } 130 const scoped_refptr<ChunkDemuxer>& demuxer() const { return chunk_demuxer_; }
62 void set_decryptor_client(DecryptorClient* decryptor_client) { 131 void set_encrypted_media(FakeEncryptedMedia* encrypted_media) {
63 decryptor_client_ = decryptor_client; 132 encrypted_media_ = encrypted_media;
64 } 133 }
65 134
66 void Seek(int new_position, int seek_append_size) { 135 void Seek(int new_position, int seek_append_size) {
67 chunk_demuxer_->StartWaitingForSeek(); 136 chunk_demuxer_->StartWaitingForSeek();
68 137
69 chunk_demuxer_->Abort(kSourceId); 138 chunk_demuxer_->Abort(kSourceId);
70 139
71 DCHECK_GE(new_position, 0); 140 DCHECK_GE(new_position, 0);
72 DCHECK_LT(new_position, file_data_->GetDataSize()); 141 DCHECK_LT(new_position, file_data_->GetDataSize());
73 current_position_ = new_position; 142 current_position_ = new_position;
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 Tokenize(codecStr, ",", &codecs); 187 Tokenize(codecStr, ",", &codecs);
119 188
120 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk); 189 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk);
121 AppendData(initial_append_size_); 190 AppendData(initial_append_size_);
122 } 191 }
123 192
124 void DemuxerNeedKey(const std::string& type, 193 void DemuxerNeedKey(const std::string& type,
125 scoped_array<uint8> init_data, int init_data_size) { 194 scoped_array<uint8> init_data, int init_data_size) {
126 DCHECK(init_data.get()); 195 DCHECK(init_data.get());
127 DCHECK_GT(init_data_size, 0); 196 DCHECK_GT(init_data_size, 0);
128 DCHECK(decryptor_client_); 197 DCHECK(encrypted_media_);
129 decryptor_client_->NeedKey("", "", type, init_data.Pass(), init_data_size); 198 encrypted_media_->NeedKey("", "", type, init_data.Pass(), init_data_size);
scherkus (not reviewing) 2012/12/20 15:18:55 nit: this pointer could get replaced with a NeedKe
xhwang 2012/12/20 18:06:31 Done.
130 } 199 }
131 200
132 private: 201 private:
133 FilePath file_path_; 202 FilePath file_path_;
134 scoped_refptr<DecoderBuffer> file_data_; 203 scoped_refptr<DecoderBuffer> file_data_;
135 int current_position_; 204 int current_position_;
136 int initial_append_size_; 205 int initial_append_size_;
137 std::string mimetype_; 206 std::string mimetype_;
138 scoped_refptr<ChunkDemuxer> chunk_demuxer_; 207 scoped_refptr<ChunkDemuxer> chunk_demuxer_;
139 DecryptorClient* decryptor_client_; 208 FakeEncryptedMedia* encrypted_media_;
140 };
141
142 class FakeDecryptorClient : public DecryptorClient {
143 public:
144 FakeDecryptorClient() : decryptor_(this) {}
145
146 AesDecryptor* decryptor() {
147 return &decryptor_;
148 }
149
150 // DecryptorClient implementation.
151 virtual void KeyAdded(const std::string& key_system,
152 const std::string& session_id) {
153 EXPECT_EQ(kClearKeySystem, key_system);
154 EXPECT_FALSE(session_id.empty());
155 }
156
157 virtual void KeyError(const std::string& key_system,
158 const std::string& session_id,
159 AesDecryptor::KeyError error_code,
160 int system_code) {
161 NOTIMPLEMENTED();
162 }
163
164 virtual void KeyMessage(const std::string& key_system,
165 const std::string& session_id,
166 const std::string& message,
167 const std::string& default_url) {
168 EXPECT_EQ(kClearKeySystem, key_system);
169 EXPECT_FALSE(session_id.empty());
170 EXPECT_FALSE(message.empty());
171
172 current_key_system_ = key_system;
173 current_session_id_ = session_id;
174 }
175
176 virtual void NeedKey(const std::string& key_system,
177 const std::string& session_id,
178 const std::string& type,
179 scoped_array<uint8> init_data,
180 int init_data_length) {
181 current_key_system_ = key_system;
182 current_session_id_ = session_id;
183
184 // When NeedKey is called from the demuxer, the |key_system| will be empty.
185 // In this case, we need to call GenerateKeyRequest() to initialize a
186 // session (which will call KeyMessage).
187 if (current_key_system_.empty()) {
188 DCHECK(current_session_id_.empty());
189 EXPECT_TRUE(decryptor_.GenerateKeyRequest(
190 kClearKeySystem, type, kInitData, arraysize(kInitData)));
191 }
192
193 EXPECT_FALSE(current_key_system_.empty());
194 EXPECT_FALSE(current_session_id_.empty());
195 decryptor_.AddKey(current_key_system_, kSecretKey, arraysize(kSecretKey),
196 init_data.get(), init_data_length, current_session_id_);
197 }
198
199 private:
200 AesDecryptor decryptor_;
201 std::string current_key_system_;
202 std::string current_session_id_;
203 }; 209 };
204 210
205 class PipelineIntegrationTest 211 class PipelineIntegrationTest
206 : public testing::Test, 212 : public testing::Test,
207 public PipelineIntegrationTestBase { 213 public PipelineIntegrationTestBase {
208 public: 214 public:
209 void StartPipelineWithMediaSource(MockMediaSource* source) { 215 void StartPipelineWithMediaSource(MockMediaSource* source) {
210 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 216 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
211 .Times(AtMost(1)); 217 .Times(AtMost(1));
212 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 218 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
213 .Times(AtMost(1)); 219 .Times(AtMost(1));
214 pipeline_->Start( 220 pipeline_->Start(
215 CreateFilterCollection(source->demuxer(), NULL), 221 CreateFilterCollection(source->demuxer(), NULL),
216 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), 222 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
217 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), 223 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
218 QuitOnStatusCB(PIPELINE_OK), 224 QuitOnStatusCB(PIPELINE_OK),
219 base::Bind(&PipelineIntegrationTest::OnBufferingState, 225 base::Bind(&PipelineIntegrationTest::OnBufferingState,
220 base::Unretained(this))); 226 base::Unretained(this)));
221 227
222 message_loop_.Run(); 228 message_loop_.Run();
223 } 229 }
224 230
225 void StartPipelineWithEncryptedMedia( 231 void StartPipelineWithEncryptedMedia(
226 MockMediaSource* source, 232 MockMediaSource* source,
227 FakeDecryptorClient* encrypted_media) { 233 FakeEncryptedMedia* encrypted_media) {
228 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 234 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
229 .Times(AtMost(1)); 235 .Times(AtMost(1));
230 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 236 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
231 .Times(AtMost(1)); 237 .Times(AtMost(1));
232 pipeline_->Start( 238 pipeline_->Start(
233 CreateFilterCollection(source->demuxer(), encrypted_media->decryptor()), 239 CreateFilterCollection(source->demuxer(), encrypted_media->decryptor()),
234 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), 240 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
235 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), 241 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
236 QuitOnStatusCB(PIPELINE_OK), 242 QuitOnStatusCB(PIPELINE_OK),
237 base::Bind(&PipelineIntegrationTest::OnBufferingState, 243 base::Bind(&PipelineIntegrationTest::OnBufferingState,
238 base::Unretained(this))); 244 base::Unretained(this)));
239 245
240 source->set_decryptor_client(encrypted_media); 246 source->set_encrypted_media(encrypted_media);
241 247
242 message_loop_.Run(); 248 message_loop_.Run();
243 } 249 }
244 250
245 // Verifies that seeking works properly for ChunkDemuxer when the 251 // Verifies that seeking works properly for ChunkDemuxer when the
246 // seek happens while there is a pending read on the ChunkDemuxer 252 // seek happens while there is a pending read on the ChunkDemuxer
247 // and no data is available. 253 // and no data is available.
248 bool TestSeekDuringRead(const std::string& filename, 254 bool TestSeekDuringRead(const std::string& filename,
249 const std::string& mimetype, 255 const std::string& mimetype,
250 int initial_append_size, 256 int initial_append_size,
(...skipping 16 matching lines...) Expand all
267 return false; 273 return false;
268 274
269 source.EndOfStream(); 275 source.EndOfStream();
270 276
271 source.Abort(); 277 source.Abort();
272 Stop(); 278 Stop();
273 return true; 279 return true;
274 } 280 }
275 }; 281 };
276 282
277
278 TEST_F(PipelineIntegrationTest, BasicPlayback) { 283 TEST_F(PipelineIntegrationTest, BasicPlayback) {
279 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"), PIPELINE_OK)); 284 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"), PIPELINE_OK));
280 285
281 Play(); 286 Play();
282 287
283 ASSERT_TRUE(WaitUntilOnEnded()); 288 ASSERT_TRUE(WaitUntilOnEnded());
284 } 289 }
285 290
286 TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) { 291 TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
287 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"), 292 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"),
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 370
366 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) { 371 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) {
367 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-16x9-aspect.webm"), 372 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-16x9-aspect.webm"),
368 PIPELINE_OK)); 373 PIPELINE_OK));
369 Play(); 374 Play();
370 ASSERT_TRUE(WaitUntilOnEnded()); 375 ASSERT_TRUE(WaitUntilOnEnded());
371 } 376 }
372 377
373 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { 378 TEST_F(PipelineIntegrationTest, EncryptedPlayback) {
374 MockMediaSource source("bear-320x240-encrypted.webm", kWebM, 219816); 379 MockMediaSource source("bear-320x240-encrypted.webm", kWebM, 219816);
375 FakeDecryptorClient encrypted_media; 380 FakeEncryptedMedia encrypted_media;
376 StartPipelineWithEncryptedMedia(&source, &encrypted_media); 381 StartPipelineWithEncryptedMedia(&source, &encrypted_media);
377 382
378 source.EndOfStream(); 383 source.EndOfStream();
379 ASSERT_EQ(PIPELINE_OK, pipeline_status_); 384 ASSERT_EQ(PIPELINE_OK, pipeline_status_);
380 385
381 Play(); 386 Play();
382 387
383 ASSERT_TRUE(WaitUntilOnEnded()); 388 ASSERT_TRUE(WaitUntilOnEnded());
384 source.Abort(); 389 source.Abort();
385 Stop(); 390 Stop();
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 // Verify video decoder & renderer can handle aborted demuxer reads. 446 // Verify video decoder & renderer can handle aborted demuxer reads.
442 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { 447 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
443 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM, 448 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM,
444 32768, 449 32768,
445 base::TimeDelta::FromMilliseconds(200), 450 base::TimeDelta::FromMilliseconds(200),
446 base::TimeDelta::FromMilliseconds(1668), 451 base::TimeDelta::FromMilliseconds(1668),
447 0x1C896, 65536)); 452 0x1C896, 65536));
448 } 453 }
449 454
450 } // namespace media 455 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698