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

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: add spec link 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 {
ddorwin 2012/12/21 04:19:06 FakeEncryptedMediaSink or some similar noun?
xhwang 2012/12/21 05:50:07 The current name is consistent with MockMediaSourc
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 // Callbacks for firing key events.
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();
ddorwin 2012/12/21 04:19:06 Do we not expect this? Should it be: FAIL() << "Un
xhwang 2012/12/21 05:50:07 Done.
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;
ddorwin 2012/12/21 04:19:06 Do we really need this member var given line 68?
xhwang 2012/12/21 05:50:07 This could be triggered by line 88, where the curr
ddorwin 2012/12/21 20:05:30 I guess we are using current_key_system_ as a bool
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;
ddorwin 2012/12/21 04:19:06 Is this ever not clear key?
xhwang 2012/12/21 05:50:07 The demuxer doesn't know about key system, so |key
81 current_session_id_ = session_id;
ddorwin 2012/12/21 04:19:06 Should we wipe out a session id if KeyMessage was
xhwang 2012/12/21 05:50:07 Similar to key_system, session_id may not be set i
ddorwin 2012/12/21 20:05:30 My point was that this line _might_ replace the se
xhwang 2012/12/22 00:46:34 Hmm, I'll think about this more and change it to a
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());
ddorwin 2012/12/21 04:19:06 Lots of DCHECKs here and below. Should these be EX
xhwang 2012/12/21 05:50:07 Replace this DCHECK with EXPECT_TRUE. If we need t
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
63 decryptor_client_ = decryptor_client; 132 void set_need_key_cb(const NeedKeyCB& need_key_cb) {
133 need_key_cb_ = need_key_cb;
64 } 134 }
65 135
66 void Seek(int new_position, int seek_append_size) { 136 void Seek(int new_position, int seek_append_size) {
67 chunk_demuxer_->StartWaitingForSeek(); 137 chunk_demuxer_->StartWaitingForSeek();
68 138
69 chunk_demuxer_->Abort(kSourceId); 139 chunk_demuxer_->Abort(kSourceId);
70 140
71 DCHECK_GE(new_position, 0); 141 DCHECK_GE(new_position, 0);
72 DCHECK_LT(new_position, file_data_->GetDataSize()); 142 DCHECK_LT(new_position, file_data_->GetDataSize());
73 current_position_ = new_position; 143 current_position_ = new_position;
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 Tokenize(codecStr, ",", &codecs); 188 Tokenize(codecStr, ",", &codecs);
119 189
120 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk); 190 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk);
121 AppendData(initial_append_size_); 191 AppendData(initial_append_size_);
122 } 192 }
123 193
124 void DemuxerNeedKey(const std::string& type, 194 void DemuxerNeedKey(const std::string& type,
125 scoped_array<uint8> init_data, int init_data_size) { 195 scoped_array<uint8> init_data, int init_data_size) {
126 DCHECK(init_data.get()); 196 DCHECK(init_data.get());
127 DCHECK_GT(init_data_size, 0); 197 DCHECK_GT(init_data_size, 0);
128 DCHECK(decryptor_client_); 198 need_key_cb_.Run("", "", type, init_data.Pass(), init_data_size);
129 decryptor_client_->NeedKey("", "", type, init_data.Pass(), init_data_size);
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 NeedKeyCB need_key_cb_;
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_need_key_cb(base::Bind(&FakeEncryptedMedia::NeedKey,
247 base::Unretained(encrypted_media)));
241 248
242 message_loop_.Run(); 249 message_loop_.Run();
243 } 250 }
244 251
245 // Verifies that seeking works properly for ChunkDemuxer when the 252 // Verifies that seeking works properly for ChunkDemuxer when the
246 // seek happens while there is a pending read on the ChunkDemuxer 253 // seek happens while there is a pending read on the ChunkDemuxer
247 // and no data is available. 254 // and no data is available.
248 bool TestSeekDuringRead(const std::string& filename, 255 bool TestSeekDuringRead(const std::string& filename,
249 const std::string& mimetype, 256 const std::string& mimetype,
250 int initial_append_size, 257 int initial_append_size,
(...skipping 16 matching lines...) Expand all
267 return false; 274 return false;
268 275
269 source.EndOfStream(); 276 source.EndOfStream();
270 277
271 source.Abort(); 278 source.Abort();
272 Stop(); 279 Stop();
273 return true; 280 return true;
274 } 281 }
275 }; 282 };
276 283
277
278 TEST_F(PipelineIntegrationTest, BasicPlayback) { 284 TEST_F(PipelineIntegrationTest, BasicPlayback) {
279 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"), PIPELINE_OK)); 285 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"), PIPELINE_OK));
280 286
281 Play(); 287 Play();
282 288
283 ASSERT_TRUE(WaitUntilOnEnded()); 289 ASSERT_TRUE(WaitUntilOnEnded());
284 } 290 }
285 291
286 TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) { 292 TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
287 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"), 293 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240.webm"),
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 371
366 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) { 372 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) {
367 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-16x9-aspect.webm"), 373 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-16x9-aspect.webm"),
368 PIPELINE_OK)); 374 PIPELINE_OK));
369 Play(); 375 Play();
370 ASSERT_TRUE(WaitUntilOnEnded()); 376 ASSERT_TRUE(WaitUntilOnEnded());
371 } 377 }
372 378
373 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { 379 TEST_F(PipelineIntegrationTest, EncryptedPlayback) {
374 MockMediaSource source("bear-320x240-encrypted.webm", kWebM, 219816); 380 MockMediaSource source("bear-320x240-encrypted.webm", kWebM, 219816);
375 FakeDecryptorClient encrypted_media; 381 FakeEncryptedMedia encrypted_media;
376 StartPipelineWithEncryptedMedia(&source, &encrypted_media); 382 StartPipelineWithEncryptedMedia(&source, &encrypted_media);
377 383
378 source.EndOfStream(); 384 source.EndOfStream();
379 ASSERT_EQ(PIPELINE_OK, pipeline_status_); 385 ASSERT_EQ(PIPELINE_OK, pipeline_status_);
380 386
381 Play(); 387 Play();
382 388
383 ASSERT_TRUE(WaitUntilOnEnded()); 389 ASSERT_TRUE(WaitUntilOnEnded());
384 source.Abort(); 390 source.Abort();
385 Stop(); 391 Stop();
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 // Verify video decoder & renderer can handle aborted demuxer reads. 447 // Verify video decoder & renderer can handle aborted demuxer reads.
442 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { 448 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
443 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM, 449 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM,
444 32768, 450 32768,
445 base::TimeDelta::FromMilliseconds(200), 451 base::TimeDelta::FromMilliseconds(200),
446 base::TimeDelta::FromMilliseconds(1668), 452 base::TimeDelta::FromMilliseconds(1668),
447 0x1C896, 65536)); 453 0x1C896, 65536));
448 } 454 }
449 455
450 } // namespace media 456 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698