| 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 "base/string_util.h" |
| 8 #include "media/base/decoder_buffer.h" | 9 #include "media/base/decoder_buffer.h" |
| 9 #include "media/base/decryptor_client.h" | 10 #include "media/base/decryptor_client.h" |
| 10 #include "media/base/test_data_util.h" | 11 #include "media/base/test_data_util.h" |
| 11 #include "media/crypto/aes_decryptor.h" | 12 #include "media/crypto/aes_decryptor.h" |
| 12 #include "media/filters/chunk_demuxer_client.h" | 13 #include "media/filters/chunk_demuxer_client.h" |
| 13 | 14 |
| 14 namespace media { | 15 namespace media { |
| 15 | 16 |
| 16 static const char kSourceId[] = "SourceId"; | 17 static const char kSourceId[] = "SourceId"; |
| 17 static const char kClearKeySystem[] = "org.w3.clearkey"; | 18 static const char kClearKeySystem[] = "org.w3.clearkey"; |
| 18 static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; | 19 static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; |
| 19 | 20 |
| 21 static const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\""; |
| 22 static const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\""; |
| 23 static const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\""; |
| 24 static const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\""; |
| 25 |
| 20 // 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". |
| 21 static const uint8 kSecretKey[] = { | 27 static const uint8 kSecretKey[] = { |
| 22 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, | 28 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, |
| 23 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c | 29 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c |
| 24 }; | 30 }; |
| 25 | 31 |
| 32 static const int kAppendWholeFile = -1; |
| 33 |
| 26 // Helper class that emulates calls made on the ChunkDemuxer by the | 34 // Helper class that emulates calls made on the ChunkDemuxer by the |
| 27 // Media Source API. | 35 // Media Source API. |
| 28 class MockMediaSource : public ChunkDemuxerClient { | 36 class MockMediaSource : public ChunkDemuxerClient { |
| 29 public: | 37 public: |
| 30 MockMediaSource(const std::string& filename, int initial_append_size, | 38 MockMediaSource(const std::string& filename, const std::string& mimetype, |
| 31 bool has_audio, bool has_video) | 39 int initial_append_size) |
| 32 : url_(GetTestDataURL(filename)), | 40 : url_(GetTestDataURL(filename)), |
| 33 current_position_(0), | 41 current_position_(0), |
| 34 initial_append_size_(initial_append_size), | 42 initial_append_size_(initial_append_size), |
| 35 has_audio_(has_audio), | 43 mimetype_(mimetype) { |
| 36 has_video_(has_video) { | |
| 37 file_data_ = ReadTestDataFile(filename); | 44 file_data_ = ReadTestDataFile(filename); |
| 38 | 45 |
| 46 if (initial_append_size_ == kAppendWholeFile) |
| 47 initial_append_size_ = file_data_->GetDataSize(); |
| 48 |
| 39 DCHECK_GT(initial_append_size_, 0); | 49 DCHECK_GT(initial_append_size_, 0); |
| 40 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); | 50 DCHECK_LE(initial_append_size_, file_data_->GetDataSize()); |
| 41 } | 51 } |
| 42 | 52 |
| 43 virtual ~MockMediaSource() {} | 53 virtual ~MockMediaSource() {} |
| 44 | 54 |
| 45 void set_decryptor_client(DecryptorClient* decryptor_client) { | 55 void set_decryptor_client(DecryptorClient* decryptor_client) { |
| 46 decryptor_client_ = decryptor_client; | 56 decryptor_client_ = decryptor_client; |
| 47 } | 57 } |
| 48 | 58 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 60 | 70 |
| 61 void AppendData(int size) { | 71 void AppendData(int size) { |
| 62 DCHECK(chunk_demuxer_.get()); | 72 DCHECK(chunk_demuxer_.get()); |
| 63 DCHECK_LT(current_position_, file_data_->GetDataSize()); | 73 DCHECK_LT(current_position_, file_data_->GetDataSize()); |
| 64 DCHECK_LE(current_position_ + size, file_data_->GetDataSize()); | 74 DCHECK_LE(current_position_ + size, file_data_->GetDataSize()); |
| 65 CHECK(chunk_demuxer_->AppendData( | 75 CHECK(chunk_demuxer_->AppendData( |
| 66 kSourceId, file_data_->GetData() + current_position_, size)); | 76 kSourceId, file_data_->GetData() + current_position_, size)); |
| 67 current_position_ += size; | 77 current_position_ += size; |
| 68 } | 78 } |
| 69 | 79 |
| 80 void AppendAtTime(const base::TimeDelta& timestampOffset, |
| 81 const uint8* pData, int size) { |
| 82 CHECK(chunk_demuxer_->SetTimestampOffset(kSourceId, timestampOffset)); |
| 83 CHECK(chunk_demuxer_->AppendData(kSourceId, pData, size)); |
| 84 CHECK(chunk_demuxer_->SetTimestampOffset(kSourceId, base::TimeDelta())); |
| 85 } |
| 86 |
| 70 void EndOfStream() { | 87 void EndOfStream() { |
| 71 chunk_demuxer_->EndOfStream(PIPELINE_OK); | 88 chunk_demuxer_->EndOfStream(PIPELINE_OK); |
| 72 } | 89 } |
| 73 | 90 |
| 74 void Abort() { | 91 void Abort() { |
| 75 if (!chunk_demuxer_.get()) | 92 if (!chunk_demuxer_.get()) |
| 76 return; | 93 return; |
| 77 chunk_demuxer_->Shutdown(); | 94 chunk_demuxer_->Shutdown(); |
| 78 } | 95 } |
| 79 | 96 |
| 80 // ChunkDemuxerClient methods. | 97 // ChunkDemuxerClient methods. |
| 81 virtual void DemuxerOpened(ChunkDemuxer* demuxer) { | 98 virtual void DemuxerOpened(ChunkDemuxer* demuxer) { |
| 82 chunk_demuxer_ = demuxer; | 99 chunk_demuxer_ = demuxer; |
| 83 | 100 |
| 101 size_t semicolon = mimetype_.find(";"); |
| 102 std::string type = mimetype_.substr(0, semicolon); |
| 103 size_t quote1 = mimetype_.find("\""); |
| 104 size_t quote2 = mimetype_.find("\"", quote1 + 1); |
| 105 std::string codecStr = mimetype_.substr(quote1 + 1, quote2 - quote1 - 1); |
| 84 std::vector<std::string> codecs; | 106 std::vector<std::string> codecs; |
| 85 if (has_audio_) | 107 Tokenize(codecStr, ",", &codecs); |
| 86 codecs.push_back("vorbis"); | |
| 87 | 108 |
| 88 if (has_video_) | 109 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk); |
| 89 codecs.push_back("vp8"); | |
| 90 | |
| 91 chunk_demuxer_->AddId(kSourceId, "video/webm", codecs); | |
| 92 AppendData(initial_append_size_); | 110 AppendData(initial_append_size_); |
| 93 } | 111 } |
| 94 | 112 |
| 95 virtual void DemuxerClosed() { | 113 virtual void DemuxerClosed() { |
| 96 chunk_demuxer_ = NULL; | 114 chunk_demuxer_ = NULL; |
| 97 } | 115 } |
| 98 | 116 |
| 99 virtual void DemuxerNeedKey(scoped_array<uint8> init_data, | 117 virtual void DemuxerNeedKey(scoped_array<uint8> init_data, |
| 100 int init_data_size) { | 118 int init_data_size) { |
| 101 DCHECK(init_data.get()); | 119 DCHECK(init_data.get()); |
| 102 DCHECK_GT(init_data_size, 0); | 120 DCHECK_GT(init_data_size, 0); |
| 103 DCHECK(decryptor_client_); | 121 DCHECK(decryptor_client_); |
| 104 decryptor_client_->NeedKey("", "", init_data.Pass(), init_data_size); | 122 decryptor_client_->NeedKey("", "", init_data.Pass(), init_data_size); |
| 105 } | 123 } |
| 106 | 124 |
| 107 private: | 125 private: |
| 108 std::string url_; | 126 std::string url_; |
| 109 scoped_refptr<DecoderBuffer> file_data_; | 127 scoped_refptr<DecoderBuffer> file_data_; |
| 110 int current_position_; | 128 int current_position_; |
| 111 int initial_append_size_; | 129 int initial_append_size_; |
| 112 bool has_audio_; | 130 std::string mimetype_; |
| 113 bool has_video_; | |
| 114 scoped_refptr<ChunkDemuxer> chunk_demuxer_; | 131 scoped_refptr<ChunkDemuxer> chunk_demuxer_; |
| 115 DecryptorClient* decryptor_client_; | 132 DecryptorClient* decryptor_client_; |
| 116 }; | 133 }; |
| 117 | 134 |
| 118 class FakeDecryptorClient : public DecryptorClient { | 135 class FakeDecryptorClient : public DecryptorClient { |
| 119 public: | 136 public: |
| 120 FakeDecryptorClient() : decryptor_(this) {} | 137 FakeDecryptorClient() : decryptor_(this) {} |
| 121 | 138 |
| 122 AesDecryptor* decryptor() { | 139 AesDecryptor* decryptor() { |
| 123 return &decryptor_; | 140 return &decryptor_; |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 | 229 |
| 213 source->set_decryptor_client(encrypted_media); | 230 source->set_decryptor_client(encrypted_media); |
| 214 | 231 |
| 215 message_loop_.Run(); | 232 message_loop_.Run(); |
| 216 } | 233 } |
| 217 | 234 |
| 218 // Verifies that seeking works properly for ChunkDemuxer when the | 235 // Verifies that seeking works properly for ChunkDemuxer when the |
| 219 // seek happens while there is a pending read on the ChunkDemuxer | 236 // seek happens while there is a pending read on the ChunkDemuxer |
| 220 // and no data is available. | 237 // and no data is available. |
| 221 bool TestSeekDuringRead(const std::string& filename, | 238 bool TestSeekDuringRead(const std::string& filename, |
| 239 const std::string& mimetype, |
| 222 int initial_append_size, | 240 int initial_append_size, |
| 223 base::TimeDelta start_seek_time, | 241 base::TimeDelta start_seek_time, |
| 224 base::TimeDelta seek_time, | 242 base::TimeDelta seek_time, |
| 225 int seek_file_position, | 243 int seek_file_position, |
| 226 int seek_append_size, | 244 int seek_append_size) { |
| 227 bool has_audio, | 245 MockMediaSource source(filename, mimetype, initial_append_size); |
| 228 bool has_video) { | |
| 229 MockMediaSource source(filename, initial_append_size, has_audio, has_video); | |
| 230 StartPipelineWithMediaSource(&source); | 246 StartPipelineWithMediaSource(&source); |
| 231 | 247 |
| 232 if (pipeline_status_ != PIPELINE_OK) | 248 if (pipeline_status_ != PIPELINE_OK) |
| 233 return false; | 249 return false; |
| 234 | 250 |
| 235 Play(); | 251 Play(); |
| 236 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) | 252 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) |
| 237 return false; | 253 return false; |
| 238 | 254 |
| 239 source.Seek(seek_file_position, seek_append_size); | 255 source.Seek(seek_file_position, seek_append_size); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 262 | 278 |
| 263 Play(); | 279 Play(); |
| 264 | 280 |
| 265 ASSERT_TRUE(WaitUntilOnEnded()); | 281 ASSERT_TRUE(WaitUntilOnEnded()); |
| 266 | 282 |
| 267 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); | 283 EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1"); |
| 268 EXPECT_EQ(GetAudioHash(), "5699a4415b620e45b9d0aae531c9df76"); | 284 EXPECT_EQ(GetAudioHash(), "5699a4415b620e45b9d0aae531c9df76"); |
| 269 } | 285 } |
| 270 | 286 |
| 271 TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource) { | 287 TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource) { |
| 272 MockMediaSource source("bear-320x240.webm", 219229, true, true); | 288 MockMediaSource source("bear-320x240.webm", kWebM, 219229); |
| 273 StartPipelineWithMediaSource(&source); | 289 StartPipelineWithMediaSource(&source); |
| 274 source.EndOfStream(); | 290 source.EndOfStream(); |
| 275 ASSERT_EQ(pipeline_status_, PIPELINE_OK); | 291 ASSERT_EQ(pipeline_status_, PIPELINE_OK); |
| 276 | 292 |
| 277 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().size(), 1u); | 293 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().size(), 1u); |
| 278 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds(), 0); | 294 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds(), 0); |
| 279 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds(), 2737); | 295 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds(), 2737); |
| 280 | 296 |
| 281 Play(); | 297 Play(); |
| 282 | 298 |
| 283 ASSERT_TRUE(WaitUntilOnEnded()); | 299 ASSERT_TRUE(WaitUntilOnEnded()); |
| 284 source.Abort(); | 300 source.Abort(); |
| 285 Stop(); | 301 Stop(); |
| 286 } | 302 } |
| 287 | 303 |
| 304 TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_WebM) { |
| 305 MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM, |
| 306 kAppendWholeFile); |
| 307 StartPipelineWithMediaSource(&source); |
| 308 |
| 309 scoped_refptr<DecoderBuffer> second_file = |
| 310 ReadTestDataFile("bear-640x360.webm"); |
| 311 |
| 312 source.AppendAtTime(base::TimeDelta::FromSeconds(2), |
| 313 second_file->GetData(), second_file->GetDataSize()); |
| 314 |
| 315 source.EndOfStream(); |
| 316 ASSERT_EQ(pipeline_status_, PIPELINE_OK); |
| 317 |
| 318 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().size(), 1u); |
| 319 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds(), 0); |
| 320 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds(), 4763); |
| 321 |
| 322 Play(); |
| 323 |
| 324 ASSERT_TRUE(WaitUntilOnEnded()); |
| 325 source.Abort(); |
| 326 Stop(); |
| 327 } |
| 328 |
| 329 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) |
| 330 TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_MP4) { |
| 331 MockMediaSource source("bear.640x360_dash.mp4", kMP4, kAppendWholeFile); |
| 332 StartPipelineWithMediaSource(&source); |
| 333 |
| 334 scoped_refptr<DecoderBuffer> second_file = |
| 335 ReadTestDataFile("bear.1280x720_dash.mp4"); |
| 336 |
| 337 source.AppendAtTime(base::TimeDelta::FromSeconds(2), |
| 338 second_file->GetData(), second_file->GetDataSize()); |
| 339 |
| 340 source.EndOfStream(); |
| 341 ASSERT_EQ(pipeline_status_, PIPELINE_OK); |
| 342 |
| 343 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().size(), 1u); |
| 344 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds(), 0); |
| 345 EXPECT_EQ(pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds(), 4736); |
| 346 |
| 347 Play(); |
| 348 |
| 349 ASSERT_TRUE(WaitUntilOnEnded()); |
| 350 source.Abort(); |
| 351 Stop(); |
| 352 } |
| 353 #endif |
| 354 |
| 288 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) { | 355 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) { |
| 289 ASSERT_TRUE(Start(GetTestDataURL("bear-320x240-16x9-aspect.webm"), | 356 ASSERT_TRUE(Start(GetTestDataURL("bear-320x240-16x9-aspect.webm"), |
| 290 PIPELINE_OK)); | 357 PIPELINE_OK)); |
| 291 Play(); | 358 Play(); |
| 292 ASSERT_TRUE(WaitUntilOnEnded()); | 359 ASSERT_TRUE(WaitUntilOnEnded()); |
| 293 } | 360 } |
| 294 | 361 |
| 295 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { | 362 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { |
| 296 MockMediaSource source("bear-320x240-encrypted.webm", 220788, true, true); | 363 MockMediaSource source("bear-320x240-encrypted.webm", kWebM, 220788); |
| 297 FakeDecryptorClient encrypted_media; | 364 FakeDecryptorClient encrypted_media; |
| 298 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | 365 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 299 | 366 |
| 300 source.EndOfStream(); | 367 source.EndOfStream(); |
| 301 ASSERT_EQ(PIPELINE_OK, pipeline_status_); | 368 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
| 302 | 369 |
| 303 Play(); | 370 Play(); |
| 304 | 371 |
| 305 ASSERT_TRUE(WaitUntilOnEnded()); | 372 ASSERT_TRUE(WaitUntilOnEnded()); |
| 306 source.Abort(); | 373 source.Abort(); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 ASSERT_TRUE(WaitUntilOnEnded()); | 413 ASSERT_TRUE(WaitUntilOnEnded()); |
| 347 | 414 |
| 348 // Make sure seeking after reaching the end works as expected. | 415 // Make sure seeking after reaching the end works as expected. |
| 349 ASSERT_TRUE(Seek(seek_time)); | 416 ASSERT_TRUE(Seek(seek_time)); |
| 350 EXPECT_GE(pipeline_->GetMediaTime(), seek_time); | 417 EXPECT_GE(pipeline_->GetMediaTime(), seek_time); |
| 351 ASSERT_TRUE(WaitUntilOnEnded()); | 418 ASSERT_TRUE(WaitUntilOnEnded()); |
| 352 } | 419 } |
| 353 | 420 |
| 354 // Verify audio decoder & renderer can handle aborted demuxer reads. | 421 // Verify audio decoder & renderer can handle aborted demuxer reads. |
| 355 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) { | 422 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) { |
| 356 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", 8192, | 423 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", kAudioOnlyWebM, |
| 424 8192, |
| 357 base::TimeDelta::FromMilliseconds(464), | 425 base::TimeDelta::FromMilliseconds(464), |
| 358 base::TimeDelta::FromMilliseconds(617), | 426 base::TimeDelta::FromMilliseconds(617), |
| 359 0x10CA, 19730, true, false)); | 427 0x10CA, 19730)); |
| 360 } | 428 } |
| 361 | 429 |
| 362 // Verify video decoder & renderer can handle aborted demuxer reads. | 430 // Verify video decoder & renderer can handle aborted demuxer reads. |
| 363 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { | 431 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { |
| 364 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, | 432 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM, |
| 433 32768, |
| 365 base::TimeDelta::FromMilliseconds(200), | 434 base::TimeDelta::FromMilliseconds(200), |
| 366 base::TimeDelta::FromMilliseconds(1668), | 435 base::TimeDelta::FromMilliseconds(1668), |
| 367 0x1C896, 65536, false, true)); | 436 0x1C896, 65536)); |
| 368 } | 437 } |
| 369 | 438 |
| 370 } // namespace media | 439 } // namespace media |
| OLD | NEW |