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" |
| 11 | 14 |
| 12 namespace media { | 15 namespace media { |
| 13 | 16 |
| 14 // Key ID of the video track in test file "bear-320x240-encrypted.webm". | |
| 15 static const unsigned char kKeyId[] = | |
| 16 "\x11\xa5\x18\x37\xc4\x73\x84\x03\xe5\xe6\x57\xed\x8e\x06\xd9\x7c"; | |
| 17 | |
| 18 static const char* kSourceId = "SourceId"; | 17 static const char* kSourceId = "SourceId"; |
| 19 | 18 |
| 20 // Helper class that emulates calls made on the ChunkDemuxer by the | 19 // Helper class that emulates calls made on the ChunkDemuxer by the |
| 21 // Media Source API. | 20 // Media Source API. |
| 22 class MockMediaSource : public ChunkDemuxerClient { | 21 class MockMediaSource : public ChunkDemuxerClient { |
| 23 public: | 22 public: |
| 24 MockMediaSource(const std::string& filename, int initial_append_size) | 23 MockMediaSource(const std::string& filename, int initial_append_size) |
| 25 : url_(GetTestDataURL(filename)), | 24 : url_(GetTestDataURL(filename)), |
| 26 current_position_(0), | 25 current_position_(0), |
| 27 initial_append_size_(initial_append_size) { | 26 initial_append_size_(initial_append_size) { |
| 28 file_data_ = ReadTestDataFile(filename); | 27 file_data_ = ReadTestDataFile(filename); |
| 29 | 28 |
| 30 DCHECK_GT(initial_append_size_, 0); | 29 DCHECK_GT(initial_append_size_, 0); |
| 31 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); | 30 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); |
| 32 } | 31 } |
| 33 | 32 |
| 34 virtual ~MockMediaSource() {} | 33 virtual ~MockMediaSource() {} |
| 35 | 34 |
| 36 void set_decryptor(AesDecryptor* decryptor) { | 35 void set_decryptor_client(DecryptorClient* decryptor_client) { |
| 37 decryptor_ = decryptor; | 36 decryptor_client_ = decryptor_client; |
| 38 } | |
| 39 AesDecryptor* decryptor() const { | |
| 40 return decryptor_; | |
| 41 } | 37 } |
| 42 | 38 |
| 43 const std::string& url() const { return url_; } | 39 const std::string& url() const { return url_; } |
| 44 | 40 |
| 45 void Seek(int new_position, int seek_append_size) { | 41 void Seek(int new_position, int seek_append_size) { |
| 46 chunk_demuxer_->StartWaitingForSeek(); | 42 chunk_demuxer_->StartWaitingForSeek(); |
| 47 | 43 |
| 48 chunk_demuxer_->Abort(kSourceId); | 44 chunk_demuxer_->Abort(kSourceId); |
| 49 | 45 |
| 50 DCHECK_GE(new_position, 0); | 46 DCHECK_GE(new_position, 0); |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 81 codecs[0] = "vp8"; | 77 codecs[0] = "vp8"; |
| 82 codecs[1] = "vorbis"; | 78 codecs[1] = "vorbis"; |
| 83 chunk_demuxer_->AddId(kSourceId, "video/webm", codecs); | 79 chunk_demuxer_->AddId(kSourceId, "video/webm", codecs); |
| 84 AppendData(initial_append_size_); | 80 AppendData(initial_append_size_); |
| 85 } | 81 } |
| 86 | 82 |
| 87 virtual void DemuxerClosed() { | 83 virtual void DemuxerClosed() { |
| 88 chunk_demuxer_ = NULL; | 84 chunk_demuxer_ = NULL; |
| 89 } | 85 } |
| 90 | 86 |
| 91 virtual void KeyNeeded(scoped_array<uint8> init_data, int init_data_size) { | 87 virtual void DemuxerKeyNeeded(scoped_array<uint8> init_data, |
| 88 int init_data_size) { | |
| 92 DCHECK(init_data.get()); | 89 DCHECK(init_data.get()); |
| 93 DCHECK_EQ(init_data_size, 16); | 90 DCHECK_EQ(init_data_size, 16); |
| 94 DCHECK(decryptor()); | 91 DCHECK(decryptor_client_); |
| 95 // In test file bear-320x240-encrypted.webm, the decryption key is equal to | 92 decryptor_client_->KeyNeeded("", "", init_data.Pass(), init_data_size); |
| 96 // |init_data|. | |
| 97 decryptor()->AddKey(init_data.get(), init_data_size, | |
| 98 init_data.get(), init_data_size); | |
| 99 } | 93 } |
| 100 | 94 |
| 101 private: | 95 private: |
| 102 std::string url_; | 96 std::string url_; |
| 103 scoped_refptr<DecoderBuffer> file_data_; | 97 scoped_refptr<DecoderBuffer> file_data_; |
| 104 int current_position_; | 98 int current_position_; |
| 105 int initial_append_size_; | 99 int initial_append_size_; |
| 106 scoped_refptr<ChunkDemuxer> chunk_demuxer_; | 100 scoped_refptr<ChunkDemuxer> chunk_demuxer_; |
| 101 DecryptorClient* decryptor_client_; | |
| 102 }; | |
| 103 | |
| 104 class MockEncryptedMedia : public MockDecryptorClient { | |
|
ddorwin
2012/06/11 21:02:40
The class name is not very descriptive.
xhwang
2012/06/12 19:01:15
Agreed, but I haven't found a good name for this.
| |
| 105 public: | |
| 106 MockEncryptedMedia() {} | |
| 107 virtual ~MockEncryptedMedia() {} | |
| 108 | |
| 109 void set_decryptor(AesDecryptor* decryptor) { | |
| 110 decryptor_ = decryptor; | |
| 111 } | |
| 112 | |
| 113 // DecryptorClient implementation. | |
| 114 virtual void KeyNeeded(const std::string& key_system, | |
| 115 const std::string& session_id, | |
| 116 scoped_array<uint8> init_data, | |
| 117 int init_data_length) { | |
| 118 // In test file bear-320x240-encrypted.webm, the decryption key is equal to | |
| 119 // |init_data|. | |
| 120 DCHECK(decryptor_); | |
| 121 decryptor_->AddKey(key_system, init_data.get(), init_data_length, | |
|
ddorwin
2012/06/11 21:02:40
key_system and session_id will be empty on keyneed
xhwang
2012/06/12 19:01:15
Done.
| |
| 122 init_data.get(), init_data_length, session_id); | |
| 123 } | |
| 124 | |
| 125 private: | |
| 107 AesDecryptor* decryptor_; | 126 AesDecryptor* decryptor_; |
| 108 }; | 127 }; |
| 109 | 128 |
| 110 class PipelineIntegrationTest | 129 class PipelineIntegrationTest |
| 111 : public testing::Test, | 130 : public testing::Test, |
| 112 public PipelineIntegrationTestBase { | 131 public PipelineIntegrationTestBase { |
| 113 public: | 132 public: |
| 114 void StartPipelineWithMediaSource(MockMediaSource& source) { | 133 void StartPipelineWithMediaSource(MockMediaSource* source) { |
| 115 pipeline_->Start( | 134 pipeline_->Start( |
| 116 CreateFilterCollection(&source), | 135 CreateFilterCollection(source), |
| 117 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | 136 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), |
| 118 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | 137 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), |
| 119 NetworkEventCB(), QuitOnStatusCB(PIPELINE_OK)); | 138 NetworkEventCB(), QuitOnStatusCB(PIPELINE_OK)); |
| 120 | 139 |
| 121 ASSERT_TRUE(decoder_.get()); | 140 ASSERT_TRUE(decoder_.get()); |
| 122 source.set_decryptor(decryptor_.get()); | |
| 123 | 141 |
| 124 message_loop_.Run(); | 142 message_loop_.Run(); |
| 125 } | 143 } |
| 144 | |
| 145 void StartPipelineWithEncryptedMedia(MockMediaSource* source, | |
| 146 MockEncryptedMedia* encrypted_media) { | |
| 147 pipeline_->Start( | |
| 148 CreateFilterCollection(source), | |
| 149 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | |
| 150 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | |
| 151 NetworkEventCB(), QuitOnStatusCB(PIPELINE_OK)); | |
| 152 | |
| 153 ASSERT_TRUE(decoder_.get()); | |
| 154 ASSERT_TRUE(decryptor_.get()); | |
| 155 encrypted_media->set_decryptor(decryptor_.get()); | |
| 156 decryptor_->Init(encrypted_media); | |
| 157 source->set_decryptor_client(encrypted_media); | |
| 158 | |
| 159 EXPECT_CALL(*encrypted_media, KeyAdded("", "")); | |
| 160 | |
| 161 message_loop_.Run(); | |
| 162 } | |
| 126 | 163 |
| 127 // Verifies that seeking works properly for ChunkDemuxer when the | 164 // Verifies that seeking works properly for ChunkDemuxer when the |
| 128 // seek happens while there is a pending read on the ChunkDemuxer | 165 // seek happens while there is a pending read on the ChunkDemuxer |
| 129 // and no data is available. | 166 // and no data is available. |
| 130 bool TestSeekDuringRead(const std::string& filename, | 167 bool TestSeekDuringRead(const std::string& filename, |
| 131 int initial_append_size, | 168 int initial_append_size, |
| 132 base::TimeDelta start_seek_time, | 169 base::TimeDelta start_seek_time, |
| 133 base::TimeDelta seek_time, | 170 base::TimeDelta seek_time, |
| 134 int seek_file_position, | 171 int seek_file_position, |
| 135 int seek_append_size) { | 172 int seek_append_size) { |
| 136 MockMediaSource source(filename, initial_append_size); | 173 MockMediaSource source(filename, initial_append_size); |
| 137 StartPipelineWithMediaSource(source); | 174 StartPipelineWithMediaSource(&source); |
| 138 | 175 |
| 139 if (pipeline_status_ != PIPELINE_OK) | 176 if (pipeline_status_ != PIPELINE_OK) |
| 140 return false; | 177 return false; |
| 141 | 178 |
| 142 Play(); | 179 Play(); |
| 143 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) | 180 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) |
| 144 return false; | 181 return false; |
| 145 | 182 |
| 146 source.Seek(seek_file_position, seek_append_size); | 183 source.Seek(seek_file_position, seek_append_size); |
| 147 if (!Seek(seek_time)) | 184 if (!Seek(seek_time)) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 170 Play(); | 207 Play(); |
| 171 | 208 |
| 172 ASSERT_TRUE(WaitUntilOnEnded()); | 209 ASSERT_TRUE(WaitUntilOnEnded()); |
| 173 | 210 |
| 174 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); | 211 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); |
| 175 EXPECT_EQ(GetAudioHash(), "6138555be3389e6aba4c8e6f70195d50"); | 212 EXPECT_EQ(GetAudioHash(), "6138555be3389e6aba4c8e6f70195d50"); |
| 176 } | 213 } |
| 177 | 214 |
| 178 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { | 215 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { |
| 179 MockMediaSource source("bear-320x240-encrypted.webm", 219726); | 216 MockMediaSource source("bear-320x240-encrypted.webm", 219726); |
| 180 StartPipelineWithMediaSource(source); | 217 MockEncryptedMedia encrypted_media; |
| 218 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | |
| 181 | 219 |
| 182 source.EndOfStream(); | 220 source.EndOfStream(); |
| 183 ASSERT_EQ(PIPELINE_OK, pipeline_status_); | 221 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
| 184 | 222 |
| 185 Play(); | 223 Play(); |
| 186 | 224 |
| 187 ASSERT_TRUE(WaitUntilOnEnded()); | 225 ASSERT_TRUE(WaitUntilOnEnded()); |
| 188 source.Abort(); | 226 source.Abort(); |
| 189 Stop(); | 227 Stop(); |
| 190 } | 228 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 | 281 |
| 244 // Verify video decoder & renderer can handle aborted demuxer reads. | 282 // Verify video decoder & renderer can handle aborted demuxer reads. |
| 245 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { | 283 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { |
| 246 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, | 284 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, |
| 247 base::TimeDelta::FromMilliseconds(200), | 285 base::TimeDelta::FromMilliseconds(200), |
| 248 base::TimeDelta::FromMilliseconds(1668), | 286 base::TimeDelta::FromMilliseconds(1668), |
| 249 0x1C896, 65536)); | 287 0x1C896, 65536)); |
| 250 } | 288 } |
| 251 | 289 |
| 252 } // namespace media | 290 } // namespace media |
| OLD | NEW |