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