Chromium Code Reviews| 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 MockEncryptedMedia : public MockDecryptorClient { | |
|
ddorwin
2012/06/14 21:00:13
FakeEncryptedMediaApp or MockDecryptorClientImpl w
xhwang
2012/06/15 01:41:04
Done.
| |
| 119 public: | |
| 120 MockEncryptedMedia() : decryptor_(this) {} | |
| 121 | |
| 122 AesDecryptor* decryptor() { | |
| 123 return &decryptor_; | |
| 124 } | |
| 125 | |
| 126 // DecryptorClient implementation. | |
| 127 virtual void KeyMessage(const std::string& key_system, | |
| 128 const std::string& session_id, | |
| 129 scoped_array<uint8> message, | |
| 130 int message_length, | |
| 131 const std::string& default_url) { | |
| 132 EXPECT_TRUE(key_system == kClearKeySystem); | |
| 133 EXPECT_TRUE(!session_id.empty()); | |
|
ddorwin
2012/06/14 21:00:13
EXPECT_FALSE(session_id.empty());
xhwang
2012/06/15 01:41:04
Done.
| |
| 134 EXPECT_TRUE(message.get()); | |
| 135 EXPECT_GT(message_length, 0); | |
| 136 key_system_string_ = key_system; | |
| 137 session_id_string_ = session_id; | |
| 138 } | |
| 139 | |
| 140 virtual void NeedKey(const std::string& key_system, | |
| 141 const std::string& session_id, | |
| 142 scoped_array<uint8> init_data, | |
| 143 int init_data_length) { | |
| 144 key_system_string_ = key_system; | |
| 145 session_id_string_ = session_id; | |
| 146 if (key_system_string_.empty()) { | |
| 147 DCHECK(session_id_string_.empty()); | |
|
ddorwin
2012/06/14 21:00:13
Something like:
// First event - initialize a sess
xhwang
2012/06/15 01:41:04
Done.
| |
| 148 decryptor_.GenerateKeyRequest(kClearKeySystem, | |
| 149 kInitData, arraysize(kInitData)); | |
| 150 } | |
| 151 | |
| 152 EXPECT_FALSE(key_system_string_.empty()); | |
| 153 EXPECT_FALSE(session_id_string_.empty()); | |
| 154 // In test file bear-320x240-encrypted.webm, the decryption key is equal to | |
| 155 // |init_data|. | |
| 156 decryptor_.AddKey(key_system_string_, init_data.get(), init_data_length, | |
| 157 init_data.get(), init_data_length, session_id_string_); | |
| 158 } | |
| 159 | |
| 160 private: | |
| 161 AesDecryptor decryptor_; | |
| 162 std::string key_system_string_; | |
|
ddorwin
2012/06/14 21:00:13
Both "key_system*" are strings. Maybe "current_key
xhwang
2012/06/15 01:41:04
Done.
| |
| 163 std::string session_id_string_; | |
| 117 }; | 164 }; |
| 118 | 165 |
| 119 class PipelineIntegrationTest | 166 class PipelineIntegrationTest |
| 120 : public testing::Test, | 167 : public testing::Test, |
| 121 public PipelineIntegrationTestBase { | 168 public PipelineIntegrationTestBase { |
| 122 public: | 169 public: |
| 123 void StartPipelineWithMediaSource(MockMediaSource& source) { | 170 void StartPipelineWithMediaSource(MockMediaSource* source) { |
| 124 pipeline_->Start( | 171 pipeline_->Start( |
| 125 CreateFilterCollection(&source), | 172 CreateFilterCollection(source), |
| 126 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | 173 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), |
| 127 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | 174 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), |
| 128 QuitOnStatusCB(PIPELINE_OK)); | 175 QuitOnStatusCB(PIPELINE_OK)); |
| 129 | 176 |
| 130 ASSERT_TRUE(decoder_.get()); | 177 ASSERT_TRUE(decoder_.get()); |
| 131 source.set_decryptor(decryptor_.get()); | |
| 132 | 178 |
| 133 message_loop_.Run(); | 179 message_loop_.Run(); |
| 134 } | 180 } |
| 181 | |
| 182 void StartPipelineWithEncryptedMedia(MockMediaSource* source, | |
| 183 MockEncryptedMedia* encrypted_media) { | |
| 184 pipeline_->Start( | |
| 185 CreateFilterCollection(source), | |
| 186 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | |
| 187 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | |
| 188 QuitOnStatusCB(PIPELINE_OK)); | |
| 189 | |
| 190 ASSERT_TRUE(decoder_.get()); | |
| 191 decoder_->set_decryptor(encrypted_media->decryptor()); | |
| 192 source->set_decryptor_client(encrypted_media); | |
| 193 | |
| 194 EXPECT_CALL(*encrypted_media, KeyAdded(kClearKeySystem, _)); | |
|
ddorwin
2012/06/14 21:00:13
NotNull() doesn't work?
xhwang
2012/06/15 01:41:04
Done.
| |
| 195 | |
| 196 message_loop_.Run(); | |
| 197 } | |
| 135 | 198 |
| 136 // Verifies that seeking works properly for ChunkDemuxer when the | 199 // Verifies that seeking works properly for ChunkDemuxer when the |
| 137 // seek happens while there is a pending read on the ChunkDemuxer | 200 // seek happens while there is a pending read on the ChunkDemuxer |
| 138 // and no data is available. | 201 // and no data is available. |
| 139 bool TestSeekDuringRead(const std::string& filename, | 202 bool TestSeekDuringRead(const std::string& filename, |
| 140 int initial_append_size, | 203 int initial_append_size, |
| 141 base::TimeDelta start_seek_time, | 204 base::TimeDelta start_seek_time, |
| 142 base::TimeDelta seek_time, | 205 base::TimeDelta seek_time, |
| 143 int seek_file_position, | 206 int seek_file_position, |
| 144 int seek_append_size, | 207 int seek_append_size, |
| 145 bool has_audio, | 208 bool has_audio, |
| 146 bool has_video) { | 209 bool has_video) { |
| 147 MockMediaSource source(filename, initial_append_size, has_audio, has_video); | 210 MockMediaSource source(filename, initial_append_size, has_audio, has_video); |
| 148 StartPipelineWithMediaSource(source); | 211 StartPipelineWithMediaSource(&source); |
| 149 | 212 |
| 150 if (pipeline_status_ != PIPELINE_OK) | 213 if (pipeline_status_ != PIPELINE_OK) |
| 151 return false; | 214 return false; |
| 152 | 215 |
| 153 Play(); | 216 Play(); |
| 154 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) | 217 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) |
| 155 return false; | 218 return false; |
| 156 | 219 |
| 157 source.Seek(seek_file_position, seek_append_size); | 220 source.Seek(seek_file_position, seek_append_size); |
| 158 if (!Seek(seek_time)) | 221 if (!Seek(seek_time)) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 181 Play(); | 244 Play(); |
| 182 | 245 |
| 183 ASSERT_TRUE(WaitUntilOnEnded()); | 246 ASSERT_TRUE(WaitUntilOnEnded()); |
| 184 | 247 |
| 185 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); | 248 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); |
| 186 EXPECT_EQ(GetAudioHash(), "6138555be3389e6aba4c8e6f70195d50"); | 249 EXPECT_EQ(GetAudioHash(), "6138555be3389e6aba4c8e6f70195d50"); |
| 187 } | 250 } |
| 188 | 251 |
| 189 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { | 252 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { |
| 190 MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true); | 253 MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true); |
| 191 StartPipelineWithMediaSource(source); | 254 MockEncryptedMedia encrypted_media; |
| 255 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | |
| 192 | 256 |
| 193 source.EndOfStream(); | 257 source.EndOfStream(); |
| 194 ASSERT_EQ(PIPELINE_OK, pipeline_status_); | 258 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
| 195 | 259 |
| 196 Play(); | 260 Play(); |
| 197 | 261 |
| 198 ASSERT_TRUE(WaitUntilOnEnded()); | 262 ASSERT_TRUE(WaitUntilOnEnded()); |
| 199 source.Abort(); | 263 source.Abort(); |
| 200 Stop(); | 264 Stop(); |
| 201 } | 265 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 254 | 318 |
| 255 // Verify video decoder & renderer can handle aborted demuxer reads. | 319 // Verify video decoder & renderer can handle aborted demuxer reads. |
| 256 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { | 320 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { |
| 257 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, | 321 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, |
| 258 base::TimeDelta::FromMilliseconds(200), | 322 base::TimeDelta::FromMilliseconds(200), |
| 259 base::TimeDelta::FromMilliseconds(1668), | 323 base::TimeDelta::FromMilliseconds(1668), |
| 260 0x1C896, 65536, false, true)); | 324 0x1C896, 65536, false, true)); |
| 261 } | 325 } |
| 262 | 326 |
| 263 } // namespace media | 327 } // namespace media |
| OLD | NEW |