OLD | NEW |
---|---|
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 "media/base/decoder_buffer.h" | 8 #include "media/base/decoder_buffer.h" |
9 #include "media/base/mock_filters.h" | |
9 #include "media/base/test_data_util.h" | 10 #include "media/base/test_data_util.h" |
11 #include "media/crypto/aes_decryptor.h" | |
12 #include "media/crypto/decryptor_client.h" | |
10 #include "media/filters/chunk_demuxer_client.h" | 13 #include "media/filters/chunk_demuxer_client.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 | |
16 using ::testing::_; | |
11 | 17 |
12 namespace media { | 18 namespace media { |
13 | 19 |
14 // Key ID of the video track in test file "bear-320x240-encrypted.webm". | 20 static const char kSourceId[] = "SourceId"; |
15 static const unsigned char kKeyId[] = | 21 static const char kClearKeySystem[] = "org.w3.clearkey"; |
16 "\x11\xa5\x18\x37\xc4\x73\x84\x03\xe5\xe6\x57\xed\x8e\x06\xd9\x7c"; | 22 static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; |
17 | |
18 static const char* kSourceId = "SourceId"; | |
19 | 23 |
20 // Helper class that emulates calls made on the ChunkDemuxer by the | 24 // Helper class that emulates calls made on the ChunkDemuxer by the |
21 // Media Source API. | 25 // Media Source API. |
22 class MockMediaSource : public ChunkDemuxerClient { | 26 class MockMediaSource : public ChunkDemuxerClient { |
23 public: | 27 public: |
24 MockMediaSource(const std::string& filename, int initial_append_size, | 28 MockMediaSource(const std::string& filename, int initial_append_size, |
25 bool has_audio, bool has_video) | 29 bool has_audio, bool has_video) |
26 : url_(GetTestDataURL(filename)), | 30 : url_(GetTestDataURL(filename)), |
27 current_position_(0), | 31 current_position_(0), |
28 initial_append_size_(initial_append_size), | 32 initial_append_size_(initial_append_size), |
29 has_audio_(has_audio), | 33 has_audio_(has_audio), |
30 has_video_(has_video) { | 34 has_video_(has_video) { |
31 file_data_ = ReadTestDataFile(filename); | 35 file_data_ = ReadTestDataFile(filename); |
32 | 36 |
33 DCHECK_GT(initial_append_size_, 0); | 37 DCHECK_GT(initial_append_size_, 0); |
34 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); | 38 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); |
35 } | 39 } |
36 | 40 |
37 virtual ~MockMediaSource() {} | 41 virtual ~MockMediaSource() {} |
38 | 42 |
39 void set_decryptor(AesDecryptor* decryptor) { | 43 void set_decryptor_client(DecryptorClient* decryptor_client) { |
40 decryptor_ = decryptor; | 44 decryptor_client_ = decryptor_client; |
41 } | |
42 AesDecryptor* decryptor() const { | |
43 return decryptor_; | |
44 } | 45 } |
45 | 46 |
46 const std::string& url() const { return url_; } | 47 const std::string& url() const { return url_; } |
47 | 48 |
48 void Seek(int new_position, int seek_append_size) { | 49 void Seek(int new_position, int seek_append_size) { |
49 chunk_demuxer_->StartWaitingForSeek(); | 50 chunk_demuxer_->StartWaitingForSeek(); |
50 | 51 |
51 chunk_demuxer_->Abort(kSourceId); | 52 chunk_demuxer_->Abort(kSourceId); |
52 | 53 |
53 DCHECK_GE(new_position, 0); | 54 DCHECK_GE(new_position, 0); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
88 codecs.push_back("vp8"); | 89 codecs.push_back("vp8"); |
89 | 90 |
90 chunk_demuxer_->AddId(kSourceId, "video/webm", codecs); | 91 chunk_demuxer_->AddId(kSourceId, "video/webm", codecs); |
91 AppendData(initial_append_size_); | 92 AppendData(initial_append_size_); |
92 } | 93 } |
93 | 94 |
94 virtual void DemuxerClosed() { | 95 virtual void DemuxerClosed() { |
95 chunk_demuxer_ = NULL; | 96 chunk_demuxer_ = NULL; |
96 } | 97 } |
97 | 98 |
98 virtual void KeyNeeded(scoped_array<uint8> init_data, int init_data_size) { | 99 virtual void DemuxerNeedKey(scoped_array<uint8> init_data, |
100 int init_data_size) { | |
99 DCHECK(init_data.get()); | 101 DCHECK(init_data.get()); |
100 DCHECK_EQ(init_data_size, 16); | 102 DCHECK_EQ(init_data_size, 16); |
101 DCHECK(decryptor()); | 103 DCHECK(decryptor_client_); |
102 // In test file bear-320x240-encrypted.webm, the decryption key is equal to | 104 decryptor_client_->NeedKey("", "", init_data.Pass(), init_data_size); |
103 // |init_data|. | |
104 decryptor()->AddKey(init_data.get(), init_data_size, | |
105 init_data.get(), init_data_size); | |
106 } | 105 } |
107 | 106 |
108 private: | 107 private: |
109 std::string url_; | 108 std::string url_; |
110 scoped_refptr<DecoderBuffer> file_data_; | 109 scoped_refptr<DecoderBuffer> file_data_; |
111 int current_position_; | 110 int current_position_; |
112 int initial_append_size_; | 111 int initial_append_size_; |
113 bool has_audio_; | 112 bool has_audio_; |
114 bool has_video_; | 113 bool has_video_; |
115 scoped_refptr<ChunkDemuxer> chunk_demuxer_; | 114 scoped_refptr<ChunkDemuxer> chunk_demuxer_; |
116 AesDecryptor* decryptor_; | 115 DecryptorClient* decryptor_client_; |
116 }; | |
117 | |
118 class MockDecryptorClientImpl : public DecryptorClient { | |
scherkus (not reviewing)
2012/06/15 04:29:21
Based on our IM chat... could other clients (i.e.,
xhwang
2012/06/15 16:39:18
This MockDecryptorClientImpl just simulates an app
| |
119 public: | |
120 MockDecryptorClientImpl() : decryptor_(this) {} | |
121 | |
122 AesDecryptor* decryptor() { | |
123 return &decryptor_; | |
124 } | |
125 | |
126 // DecryptorClient implementation. | |
127 virtual void KeyAdded(const std::string& key_system, | |
ddorwin
2012/06/15 04:29:25
Why did you override these instead of using GMock
xhwang
2012/06/15 16:39:18
In the IM-chat, scherkus@ points out that in pipel
ddorwin
2012/06/16 02:01:32
Ah, I missed the parent class change and the class
xhwang
2012/06/19 16:37:48
Done in http://codereview.chromium.org/10539150/,
| |
128 const std::string& session_id) { | |
129 EXPECT_EQ(key_system, kClearKeySystem); | |
ddorwin
2012/06/15 04:29:25
Expected values should be on LHS
xhwang
2012/06/15 16:39:18
Done.
| |
130 EXPECT_FALSE(session_id.empty()); | |
131 } | |
132 | |
133 virtual void KeyError(const std::string& key_system, | |
134 const std::string& session_id, | |
135 AesDecryptor::KeyError error_code, | |
136 int system_code) { | |
137 NOTIMPLEMENTED(); | |
ddorwin
2012/06/15 04:29:25
Isn't unexpected call enough from GMock enough?
Th
xhwang
2012/06/15 16:39:18
See comment above.
| |
138 } | |
139 | |
140 virtual void KeyMessage(const std::string& key_system, | |
141 const std::string& session_id, | |
142 scoped_array<uint8> message, | |
143 int message_length, | |
144 const std::string& default_url) { | |
145 EXPECT_TRUE(key_system == kClearKeySystem); | |
146 EXPECT_FALSE(session_id.empty()); | |
147 EXPECT_TRUE(message.get()); | |
148 EXPECT_GT(message_length, 0); | |
149 | |
150 current_key_system_ = key_system; | |
151 current_session_id_ = session_id; | |
152 } | |
153 | |
154 virtual void NeedKey(const std::string& key_system, | |
155 const std::string& session_id, | |
156 scoped_array<uint8> init_data, | |
157 int init_data_length) { | |
158 current_key_system_ = key_system; | |
159 current_session_id_ = session_id; | |
160 | |
161 // When NeedKey is called from the demuxer, the |key_system| will be empty. | |
162 // In this case, we need to call GenerateKeyRequest() to initialize a | |
163 // session (which will call KeyMessage). | |
164 if (current_key_system_.empty()) { | |
165 DCHECK(current_session_id_.empty()); | |
166 decryptor_.GenerateKeyRequest(kClearKeySystem, | |
167 kInitData, arraysize(kInitData)); | |
168 } | |
169 | |
170 EXPECT_FALSE(current_key_system_.empty()); | |
171 EXPECT_FALSE(current_session_id_.empty()); | |
172 // In test file bear-320x240-encrypted.webm, the decryption key is equal to | |
ddorwin
2012/06/16 02:01:32
If this Fake is specific to this one file. We migh
xhwang
2012/06/19 16:37:48
The reason I didn't put AddKey() in KeyMessage() i
| |
173 // |init_data|. | |
174 decryptor_.AddKey(current_key_system_, init_data.get(), init_data_length, | |
175 init_data.get(), init_data_length, current_session_id_); | |
176 } | |
177 | |
178 private: | |
179 AesDecryptor decryptor_; | |
180 std::string current_key_system_; | |
181 std::string current_session_id_; | |
117 }; | 182 }; |
118 | 183 |
119 class PipelineIntegrationTest | 184 class PipelineIntegrationTest |
120 : public testing::Test, | 185 : public testing::Test, |
121 public PipelineIntegrationTestBase { | 186 public PipelineIntegrationTestBase { |
122 public: | 187 public: |
123 void StartPipelineWithMediaSource(MockMediaSource& source) { | 188 void StartPipelineWithMediaSource(MockMediaSource* source) { |
124 pipeline_->Start( | 189 pipeline_->Start( |
125 CreateFilterCollection(&source), | 190 CreateFilterCollection(source), |
126 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | 191 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), |
127 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | 192 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), |
128 QuitOnStatusCB(PIPELINE_OK)); | 193 QuitOnStatusCB(PIPELINE_OK)); |
129 | 194 |
130 ASSERT_TRUE(decoder_.get()); | 195 ASSERT_TRUE(decoder_.get()); |
131 source.set_decryptor(decryptor_.get()); | |
132 | 196 |
133 message_loop_.Run(); | 197 message_loop_.Run(); |
134 } | 198 } |
199 | |
200 void StartPipelineWithEncryptedMedia( | |
201 MockMediaSource* source, | |
202 MockDecryptorClientImpl* encrypted_media) { | |
203 pipeline_->Start( | |
204 CreateFilterCollection(source), | |
205 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | |
206 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | |
207 QuitOnStatusCB(PIPELINE_OK)); | |
208 | |
209 ASSERT_TRUE(decoder_.get()); | |
210 decoder_->set_decryptor(encrypted_media->decryptor()); | |
211 source->set_decryptor_client(encrypted_media); | |
212 | |
213 message_loop_.Run(); | |
214 } | |
135 | 215 |
136 // Verifies that seeking works properly for ChunkDemuxer when the | 216 // Verifies that seeking works properly for ChunkDemuxer when the |
137 // seek happens while there is a pending read on the ChunkDemuxer | 217 // seek happens while there is a pending read on the ChunkDemuxer |
138 // and no data is available. | 218 // and no data is available. |
139 bool TestSeekDuringRead(const std::string& filename, | 219 bool TestSeekDuringRead(const std::string& filename, |
140 int initial_append_size, | 220 int initial_append_size, |
141 base::TimeDelta start_seek_time, | 221 base::TimeDelta start_seek_time, |
142 base::TimeDelta seek_time, | 222 base::TimeDelta seek_time, |
143 int seek_file_position, | 223 int seek_file_position, |
144 int seek_append_size, | 224 int seek_append_size, |
145 bool has_audio, | 225 bool has_audio, |
146 bool has_video) { | 226 bool has_video) { |
147 MockMediaSource source(filename, initial_append_size, has_audio, has_video); | 227 MockMediaSource source(filename, initial_append_size, has_audio, has_video); |
148 StartPipelineWithMediaSource(source); | 228 StartPipelineWithMediaSource(&source); |
149 | 229 |
150 if (pipeline_status_ != PIPELINE_OK) | 230 if (pipeline_status_ != PIPELINE_OK) |
151 return false; | 231 return false; |
152 | 232 |
153 Play(); | 233 Play(); |
154 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) | 234 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) |
155 return false; | 235 return false; |
156 | 236 |
157 source.Seek(seek_file_position, seek_append_size); | 237 source.Seek(seek_file_position, seek_append_size); |
158 if (!Seek(seek_time)) | 238 if (!Seek(seek_time)) |
(...skipping 22 matching lines...) Expand all Loading... | |
181 Play(); | 261 Play(); |
182 | 262 |
183 ASSERT_TRUE(WaitUntilOnEnded()); | 263 ASSERT_TRUE(WaitUntilOnEnded()); |
184 | 264 |
185 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); | 265 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); |
186 EXPECT_EQ(GetAudioHash(), "6138555be3389e6aba4c8e6f70195d50"); | 266 EXPECT_EQ(GetAudioHash(), "6138555be3389e6aba4c8e6f70195d50"); |
187 } | 267 } |
188 | 268 |
189 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { | 269 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { |
190 MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true); | 270 MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true); |
191 StartPipelineWithMediaSource(source); | 271 MockDecryptorClientImpl encrypted_media; |
272 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | |
192 | 273 |
193 source.EndOfStream(); | 274 source.EndOfStream(); |
194 ASSERT_EQ(PIPELINE_OK, pipeline_status_); | 275 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
195 | 276 |
196 Play(); | 277 Play(); |
197 | 278 |
198 ASSERT_TRUE(WaitUntilOnEnded()); | 279 ASSERT_TRUE(WaitUntilOnEnded()); |
199 source.Abort(); | 280 source.Abort(); |
200 Stop(); | 281 Stop(); |
201 } | 282 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
254 | 335 |
255 // Verify video decoder & renderer can handle aborted demuxer reads. | 336 // Verify video decoder & renderer can handle aborted demuxer reads. |
256 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { | 337 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { |
257 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, | 338 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, |
258 base::TimeDelta::FromMilliseconds(200), | 339 base::TimeDelta::FromMilliseconds(200), |
259 base::TimeDelta::FromMilliseconds(1668), | 340 base::TimeDelta::FromMilliseconds(1668), |
260 0x1C896, 65536, false, true)); | 341 0x1C896, 65536, false, true)); |
261 } | 342 } |
262 | 343 |
263 } // namespace media | 344 } // namespace media |
OLD | NEW |