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 <stddef.h> | 5 #include <stddef.h> |
| 6 #include <stdint.h> | 6 #include <stdint.h> |
| 7 | 7 |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/memory/ref_counted.h" | 15 #include "base/memory/ref_counted.h" |
| 16 #include "base/run_loop.h" | 16 #include "base/run_loop.h" |
| 17 #include "base/strings/string_split.h" | 17 #include "base/strings/string_split.h" |
| 18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
| 20 #include "build/build_config.h" | 20 #include "build/build_config.h" |
| 21 #include "media/base/cdm_callback_promise.h" | 21 #include "media/base/cdm_callback_promise.h" |
| 22 #include "media/base/cdm_context.h" | |
| 23 #include "media/base/cdm_key_information.h" | 22 #include "media/base/cdm_key_information.h" |
| 24 #include "media/base/content_decryption_module.h" | |
| 25 #include "media/base/decoder_buffer.h" | 23 #include "media/base/decoder_buffer.h" |
| 26 #include "media/base/media.h" | 24 #include "media/base/media.h" |
| 27 #include "media/base/media_switches.h" | 25 #include "media/base/media_switches.h" |
| 28 #include "media/base/media_tracks.h" | 26 #include "media/base/media_tracks.h" |
| 29 #include "media/base/test_data_util.h" | 27 #include "media/base/test_data_util.h" |
| 30 #include "media/base/timestamp_constants.h" | 28 #include "media/base/timestamp_constants.h" |
| 31 #include "media/cdm/aes_decryptor.h" | 29 #include "media/cdm/aes_decryptor.h" |
| 32 #include "media/cdm/json_web_key.h" | 30 #include "media/cdm/json_web_key.h" |
| 33 #include "media/filters/chunk_demuxer.h" | |
| 34 #include "media/media_features.h" | 31 #include "media/media_features.h" |
| 35 #include "media/renderers/renderer_impl.h" | 32 #include "media/renderers/renderer_impl.h" |
| 36 #include "media/test/pipeline_integration_test_base.h" | 33 #include "media/test/pipeline_integration_test_base.h" |
| 37 #include "testing/gmock/include/gmock/gmock.h" | 34 #include "testing/gmock/include/gmock/gmock.h" |
| 38 #include "url/gurl.h" | 35 #include "url/gurl.h" |
| 39 | 36 |
| 40 #if defined(MOJO_RENDERER) | 37 #if defined(MOJO_RENDERER) |
| 41 #include "media/mojo/clients/mojo_renderer.h" | 38 #include "media/mojo/clients/mojo_renderer.h" |
| 42 #include "media/mojo/interfaces/interface_factory.mojom.h" | 39 #include "media/mojo/interfaces/interface_factory.mojom.h" |
| 43 #include "media/mojo/interfaces/renderer.mojom.h" | 40 #include "media/mojo/interfaces/renderer.mojom.h" |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 87 #endif | 84 #endif |
| 88 | 85 |
| 89 using testing::_; | 86 using testing::_; |
| 90 using testing::AnyNumber; | 87 using testing::AnyNumber; |
| 91 using testing::AtLeast; | 88 using testing::AtLeast; |
| 92 using testing::AtMost; | 89 using testing::AtMost; |
| 93 using testing::SaveArg; | 90 using testing::SaveArg; |
| 94 | 91 |
| 95 namespace media { | 92 namespace media { |
| 96 | 93 |
| 97 const char kSourceId[] = "SourceId"; | 94 namespace { |
|
DaleCurtis
2017/04/20 18:24:51
We don't generally use anonymous namespace in the
xjz
2017/04/20 21:26:10
Done.
| |
| 98 | 95 |
| 99 const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\""; | 96 const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\""; |
| 100 const char kWebMVP9[] = "video/webm; codecs=\"vp9\""; | 97 const char kWebMVP9[] = "video/webm; codecs=\"vp9\""; |
| 101 const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\""; | 98 const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\""; |
| 102 const char kOpusAudioOnlyWebM[] = "video/webm; codecs=\"opus\""; | 99 const char kOpusAudioOnlyWebM[] = "video/webm; codecs=\"opus\""; |
| 103 const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\""; | 100 const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\""; |
| 104 #if BUILDFLAG(USE_PROPRIETARY_CODECS) | 101 #if BUILDFLAG(USE_PROPRIETARY_CODECS) |
| 105 const char kADTS[] = "audio/aac"; | 102 const char kADTS[] = "audio/aac"; |
| 106 const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\""; | 103 const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\""; |
| 107 const char kMP4VideoAVC3[] = "video/mp4; codecs=\"avc3.64001f\""; | 104 const char kMP4VideoAVC3[] = "video/mp4; codecs=\"avc3.64001f\""; |
| 108 const char kMP4VideoVP9[] = "video/mp4; codecs=\"vp09.00.10.08.01.05.01\""; | 105 const char kMP4VideoVP9[] = "video/mp4; codecs=\"vp09.00.10.08.01.05.01\""; |
| 109 const char kMP4VideoHEVC1[] = "video/mp4; codecs=\"hvc1.1.6.L93.B0\""; | 106 const char kMP4VideoHEVC1[] = "video/mp4; codecs=\"hvc1.1.6.L93.B0\""; |
| 110 const char kMP4VideoHEVC2[] = "video/mp4; codecs=\"hev1.1.6.L93.B0\""; | 107 const char kMP4VideoHEVC2[] = "video/mp4; codecs=\"hev1.1.6.L93.B0\""; |
| 111 const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\""; | 108 const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\""; |
| 112 const char kMP4Audio[] = "audio/mp4; codecs=\"mp4a.40.2\""; | 109 const char kMP4Audio[] = "audio/mp4; codecs=\"mp4a.40.2\""; |
| 113 const char kMP3[] = "audio/mpeg"; | 110 const char kMP3[] = "audio/mpeg"; |
| 114 const char kMP2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\""; | 111 const char kMP2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\""; |
| 115 #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) | 112 #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) |
| 116 | 113 |
| 117 const size_t kAppendWholeFile = std::numeric_limits<size_t>::max(); | |
| 118 | |
| 119 // Constants for the Media Source config change tests. | 114 // Constants for the Media Source config change tests. |
| 120 const int kAppendTimeSec = 1; | 115 const int kAppendTimeSec = 1; |
| 121 const int kAppendTimeMs = kAppendTimeSec * 1000; | 116 const int kAppendTimeMs = kAppendTimeSec * 1000; |
| 122 const int k320WebMFileDurationMs = 2736; | 117 const int k320WebMFileDurationMs = 2736; |
| 123 const int k640WebMFileDurationMs = 2749; | 118 const int k640WebMFileDurationMs = 2749; |
| 124 const int kOpusEndTrimmingWebMFileDurationMs = 2741; | 119 const int kOpusEndTrimmingWebMFileDurationMs = 2741; |
| 125 const int kVP9WebMFileDurationMs = 2736; | 120 const int kVP9WebMFileDurationMs = 2736; |
| 126 const int kVP8AWebMFileDurationMs = 2734; | 121 const int kVP8AWebMFileDurationMs = 2734; |
| 127 | 122 |
| 128 #if !defined(MOJO_RENDERER) | 123 #if !defined(MOJO_RENDERER) |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 180 exploded_time.second = 56; | 175 exploded_time.second = 56; |
| 181 exploded_time.millisecond = 789; | 176 exploded_time.millisecond = 789; |
| 182 base::Time timeline_offset; | 177 base::Time timeline_offset; |
| 183 EXPECT_TRUE(base::Time::FromUTCExploded(exploded_time, &timeline_offset)); | 178 EXPECT_TRUE(base::Time::FromUTCExploded(exploded_time, &timeline_offset)); |
| 184 | 179 |
| 185 timeline_offset += base::TimeDelta::FromMicroseconds(123); | 180 timeline_offset += base::TimeDelta::FromMicroseconds(123); |
| 186 | 181 |
| 187 return timeline_offset; | 182 return timeline_offset; |
| 188 } | 183 } |
| 189 | 184 |
| 190 // Note: Tests using this class only exercise the DecryptingDemuxerStream path. | |
| 191 // They do not exercise the Decrypting{Audio|Video}Decoder path. | |
| 192 class FakeEncryptedMedia { | |
| 193 public: | |
| 194 // Defines the behavior of the "app" that responds to EME events. | |
| 195 class AppBase { | |
| 196 public: | |
| 197 virtual ~AppBase() {} | |
| 198 | |
| 199 virtual void OnSessionMessage( | |
| 200 const std::string& session_id, | |
| 201 ContentDecryptionModule::MessageType message_type, | |
| 202 const std::vector<uint8_t>& message, | |
| 203 AesDecryptor* decryptor) = 0; | |
| 204 | |
| 205 virtual void OnSessionClosed(const std::string& session_id) = 0; | |
| 206 | |
| 207 virtual void OnSessionKeysChange(const std::string& session_id, | |
| 208 bool has_additional_usable_key, | |
| 209 CdmKeysInfo keys_info) = 0; | |
| 210 | |
| 211 virtual void OnEncryptedMediaInitData(EmeInitDataType init_data_type, | |
| 212 const std::vector<uint8_t>& init_data, | |
| 213 AesDecryptor* decryptor) = 0; | |
| 214 }; | |
| 215 | |
| 216 FakeEncryptedMedia(AppBase* app) | |
| 217 : decryptor_(new AesDecryptor( | |
| 218 GURL::EmptyGURL(), | |
| 219 base::Bind(&FakeEncryptedMedia::OnSessionMessage, | |
| 220 base::Unretained(this)), | |
| 221 base::Bind(&FakeEncryptedMedia::OnSessionClosed, | |
| 222 base::Unretained(this)), | |
| 223 base::Bind(&FakeEncryptedMedia::OnSessionKeysChange, | |
| 224 base::Unretained(this)))), | |
| 225 cdm_context_(decryptor_.get()), | |
| 226 app_(app) {} | |
| 227 | |
| 228 CdmContext* GetCdmContext() { return &cdm_context_; } | |
| 229 | |
| 230 // Callbacks for firing session events. Delegate to |app_|. | |
| 231 void OnSessionMessage(const std::string& session_id, | |
| 232 ContentDecryptionModule::MessageType message_type, | |
| 233 const std::vector<uint8_t>& message) { | |
| 234 app_->OnSessionMessage(session_id, message_type, message, decryptor_.get()); | |
| 235 } | |
| 236 | |
| 237 void OnSessionClosed(const std::string& session_id) { | |
| 238 app_->OnSessionClosed(session_id); | |
| 239 } | |
| 240 | |
| 241 void OnSessionKeysChange(const std::string& session_id, | |
| 242 bool has_additional_usable_key, | |
| 243 CdmKeysInfo keys_info) { | |
| 244 app_->OnSessionKeysChange(session_id, has_additional_usable_key, | |
| 245 std::move(keys_info)); | |
| 246 } | |
| 247 | |
| 248 void OnEncryptedMediaInitData(EmeInitDataType init_data_type, | |
| 249 const std::vector<uint8_t>& init_data) { | |
| 250 app_->OnEncryptedMediaInitData(init_data_type, init_data, decryptor_.get()); | |
| 251 } | |
| 252 | |
| 253 private: | |
| 254 class TestCdmContext : public CdmContext { | |
| 255 public: | |
| 256 TestCdmContext(Decryptor* decryptor) : decryptor_(decryptor) {} | |
| 257 | |
| 258 Decryptor* GetDecryptor() final { return decryptor_; } | |
| 259 int GetCdmId() const final { return kInvalidCdmId; } | |
| 260 | |
| 261 private: | |
| 262 Decryptor* decryptor_; | |
| 263 }; | |
| 264 | |
| 265 scoped_refptr<AesDecryptor> decryptor_; | |
| 266 TestCdmContext cdm_context_; | |
| 267 std::unique_ptr<AppBase> app_; | |
| 268 }; | |
| 269 | |
| 270 enum PromiseResult { RESOLVED, REJECTED }; | 185 enum PromiseResult { RESOLVED, REJECTED }; |
| 271 | 186 |
| 272 // Provides the test key in response to the encrypted event. | 187 // Provides the test key in response to the encrypted event. |
| 273 class KeyProvidingApp : public FakeEncryptedMedia::AppBase { | 188 class KeyProvidingApp : public FakeEncryptedMedia::AppBase { |
| 274 public: | 189 public: |
| 275 KeyProvidingApp() {} | 190 KeyProvidingApp() {} |
| 276 | 191 |
| 277 void OnResolveWithSession(PromiseResult expected, | 192 void OnResolveWithSession(PromiseResult expected, |
| 278 const std::string& session_id) { | 193 const std::string& session_id) { |
| 279 EXPECT_EQ(expected, RESOLVED); | 194 EXPECT_EQ(expected, RESOLVED); |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 CdmKeysInfo keys_info) override { | 350 CdmKeysInfo keys_info) override { |
| 436 EXPECT_FALSE(session_id.empty()); | 351 EXPECT_FALSE(session_id.empty()); |
| 437 EXPECT_EQ(has_additional_usable_key, true); | 352 EXPECT_EQ(has_additional_usable_key, true); |
| 438 } | 353 } |
| 439 | 354 |
| 440 void OnEncryptedMediaInitData(EmeInitDataType init_data_type, | 355 void OnEncryptedMediaInitData(EmeInitDataType init_data_type, |
| 441 const std::vector<uint8_t>& init_data, | 356 const std::vector<uint8_t>& init_data, |
| 442 AesDecryptor* decryptor) override {} | 357 AesDecryptor* decryptor) override {} |
| 443 }; | 358 }; |
| 444 | 359 |
| 445 // Helper class that emulates calls made on the ChunkDemuxer by the | |
| 446 // Media Source API. | |
| 447 class MockMediaSource { | |
| 448 public: | |
| 449 MockMediaSource(const std::string& filename, | |
| 450 const std::string& mimetype, | |
| 451 size_t initial_append_size) | |
| 452 : current_position_(0), | |
| 453 initial_append_size_(initial_append_size), | |
| 454 mimetype_(mimetype), | |
| 455 chunk_demuxer_(new ChunkDemuxer( | |
| 456 base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)), | |
| 457 base::Bind(&MockMediaSource::OnEncryptedMediaInitData, | |
| 458 base::Unretained(this)), | |
| 459 &media_log_)), | |
| 460 owned_chunk_demuxer_(chunk_demuxer_) { | |
| 461 file_data_ = ReadTestDataFile(filename); | |
| 462 | |
| 463 if (initial_append_size_ == kAppendWholeFile) | |
| 464 initial_append_size_ = file_data_->data_size(); | |
| 465 | |
| 466 DCHECK_GT(initial_append_size_, 0u); | |
| 467 DCHECK_LE(initial_append_size_, file_data_->data_size()); | |
| 468 } | |
| 469 | |
| 470 virtual ~MockMediaSource() {} | |
| 471 | |
| 472 std::unique_ptr<Demuxer> GetDemuxer() { | |
| 473 return std::move(owned_chunk_demuxer_); | |
| 474 } | |
| 475 | |
| 476 void set_encrypted_media_init_data_cb( | |
| 477 const Demuxer::EncryptedMediaInitDataCB& encrypted_media_init_data_cb) { | |
| 478 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; | |
| 479 } | |
| 480 | |
| 481 void set_demuxer_failure_cb(const PipelineStatusCB& demuxer_failure_cb) { | |
| 482 demuxer_failure_cb_ = demuxer_failure_cb; | |
| 483 } | |
| 484 | |
| 485 void Seek(base::TimeDelta seek_time, | |
| 486 size_t new_position, | |
| 487 size_t seek_append_size) { | |
| 488 chunk_demuxer_->StartWaitingForSeek(seek_time); | |
| 489 | |
| 490 chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(), | |
| 491 kInfiniteDuration, | |
| 492 &last_timestamp_offset_); | |
| 493 | |
| 494 DCHECK_LT(new_position, file_data_->data_size()); | |
| 495 current_position_ = new_position; | |
| 496 | |
| 497 AppendData(seek_append_size); | |
| 498 } | |
| 499 | |
| 500 void Seek(base::TimeDelta seek_time) { | |
| 501 chunk_demuxer_->StartWaitingForSeek(seek_time); | |
| 502 } | |
| 503 | |
| 504 void AppendData(size_t size) { | |
| 505 DCHECK(chunk_demuxer_); | |
| 506 DCHECK_LT(current_position_, file_data_->data_size()); | |
| 507 DCHECK_LE(current_position_ + size, file_data_->data_size()); | |
| 508 | |
| 509 ASSERT_TRUE(chunk_demuxer_->AppendData( | |
| 510 kSourceId, file_data_->data() + current_position_, size, | |
| 511 base::TimeDelta(), kInfiniteDuration, &last_timestamp_offset_)); | |
| 512 current_position_ += size; | |
| 513 } | |
| 514 | |
| 515 bool AppendAtTime(base::TimeDelta timestamp_offset, | |
| 516 const uint8_t* pData, | |
| 517 int size) { | |
| 518 CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId)); | |
| 519 bool success = | |
| 520 chunk_demuxer_->AppendData(kSourceId, pData, size, base::TimeDelta(), | |
| 521 kInfiniteDuration, ×tamp_offset); | |
| 522 last_timestamp_offset_ = timestamp_offset; | |
| 523 return success; | |
| 524 } | |
| 525 | |
| 526 void AppendAtTimeWithWindow(base::TimeDelta timestamp_offset, | |
| 527 base::TimeDelta append_window_start, | |
| 528 base::TimeDelta append_window_end, | |
| 529 const uint8_t* pData, | |
| 530 int size) { | |
| 531 CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId)); | |
| 532 ASSERT_TRUE( | |
| 533 chunk_demuxer_->AppendData(kSourceId, pData, size, append_window_start, | |
| 534 append_window_end, ×tamp_offset)); | |
| 535 last_timestamp_offset_ = timestamp_offset; | |
| 536 } | |
| 537 | |
| 538 void SetMemoryLimits(size_t limit_bytes) { | |
| 539 chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::AUDIO, limit_bytes); | |
| 540 chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::VIDEO, limit_bytes); | |
| 541 } | |
| 542 | |
| 543 void EvictCodedFrames(base::TimeDelta currentMediaTime, size_t newDataSize) { | |
| 544 chunk_demuxer_->EvictCodedFrames(kSourceId, currentMediaTime, newDataSize); | |
| 545 } | |
| 546 | |
| 547 void RemoveRange(base::TimeDelta start, base::TimeDelta end) { | |
| 548 chunk_demuxer_->Remove(kSourceId, start, end); | |
| 549 } | |
| 550 | |
| 551 void EndOfStream() { chunk_demuxer_->MarkEndOfStream(PIPELINE_OK); } | |
| 552 | |
| 553 void Shutdown() { | |
| 554 if (!chunk_demuxer_) | |
| 555 return; | |
| 556 chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(), | |
| 557 kInfiniteDuration, | |
| 558 &last_timestamp_offset_); | |
| 559 chunk_demuxer_->Shutdown(); | |
| 560 chunk_demuxer_ = NULL; | |
| 561 } | |
| 562 | |
| 563 void DemuxerOpened() { | |
| 564 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 565 FROM_HERE, base::Bind(&MockMediaSource::DemuxerOpenedTask, | |
| 566 base::Unretained(this))); | |
| 567 } | |
| 568 | |
| 569 void DemuxerOpenedTask() { | |
| 570 ChunkDemuxer::Status status = AddId(); | |
| 571 if (status != ChunkDemuxer::kOk) { | |
| 572 CHECK(!demuxer_failure_cb_.is_null()); | |
| 573 demuxer_failure_cb_.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | |
| 574 return; | |
| 575 } | |
| 576 chunk_demuxer_->SetTracksWatcher( | |
| 577 kSourceId, base::Bind(&MockMediaSource::InitSegmentReceived, | |
| 578 base::Unretained(this))); | |
| 579 | |
| 580 AppendData(initial_append_size_); | |
| 581 } | |
| 582 | |
| 583 ChunkDemuxer::Status AddId() { | |
| 584 // This code assumes that |mimetype_| is one of the following forms. | |
| 585 // 1. audio/mpeg | |
| 586 // 2. video/webm;codec="vorbis,vp8". | |
| 587 size_t semicolon = mimetype_.find(";"); | |
| 588 std::string type = mimetype_; | |
| 589 std::string codecs_param = ""; | |
| 590 if (semicolon != std::string::npos) { | |
| 591 type = mimetype_.substr(0, semicolon); | |
| 592 size_t codecs_param_start = mimetype_.find("codecs=\"", semicolon); | |
| 593 | |
| 594 CHECK_NE(codecs_param_start, std::string::npos); | |
| 595 | |
| 596 codecs_param_start += 8; // Skip over the codecs=". | |
| 597 | |
| 598 size_t codecs_param_end = mimetype_.find("\"", codecs_param_start); | |
| 599 | |
| 600 CHECK_NE(codecs_param_end, std::string::npos); | |
| 601 | |
| 602 codecs_param = mimetype_.substr(codecs_param_start, | |
| 603 codecs_param_end - codecs_param_start); | |
| 604 } | |
| 605 | |
| 606 return chunk_demuxer_->AddId(kSourceId, type, codecs_param); | |
| 607 } | |
| 608 | |
| 609 void OnEncryptedMediaInitData(EmeInitDataType init_data_type, | |
| 610 const std::vector<uint8_t>& init_data) { | |
| 611 DCHECK(!init_data.empty()); | |
| 612 CHECK(!encrypted_media_init_data_cb_.is_null()); | |
| 613 encrypted_media_init_data_cb_.Run(init_data_type, init_data); | |
| 614 } | |
| 615 | |
| 616 base::TimeDelta last_timestamp_offset() const { | |
| 617 return last_timestamp_offset_; | |
| 618 } | |
| 619 | |
| 620 void InitSegmentReceived(std::unique_ptr<MediaTracks> tracks) { | |
| 621 CHECK(tracks.get()); | |
| 622 EXPECT_GT(tracks->tracks().size(), 0u); | |
| 623 CHECK(chunk_demuxer_); | |
| 624 // Verify that track ids are unique. | |
| 625 std::set<MediaTrack::Id> track_ids; | |
| 626 for (const auto& track : tracks->tracks()) { | |
| 627 EXPECT_EQ(track_ids.end(), track_ids.find(track->id())); | |
| 628 track_ids.insert(track->id()); | |
| 629 } | |
| 630 InitSegmentReceivedMock(tracks); | |
| 631 } | |
| 632 | |
| 633 MOCK_METHOD1(InitSegmentReceivedMock, void(std::unique_ptr<MediaTracks>&)); | |
| 634 | |
| 635 private: | |
| 636 MediaLog media_log_; | |
| 637 scoped_refptr<DecoderBuffer> file_data_; | |
| 638 size_t current_position_; | |
| 639 size_t initial_append_size_; | |
| 640 std::string mimetype_; | |
| 641 ChunkDemuxer* chunk_demuxer_; | |
| 642 std::unique_ptr<Demuxer> owned_chunk_demuxer_; | |
| 643 PipelineStatusCB demuxer_failure_cb_; | |
| 644 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_; | |
| 645 base::TimeDelta last_timestamp_offset_; | |
| 646 }; | |
| 647 | |
| 648 // A rough simulation of GpuVideoDecoder that fails every Decode() request. This | 360 // A rough simulation of GpuVideoDecoder that fails every Decode() request. This |
| 649 // is used to test post-Initialize() fallback paths. | 361 // is used to test post-Initialize() fallback paths. |
| 650 class FailingVideoDecoder : public VideoDecoder { | 362 class FailingVideoDecoder : public VideoDecoder { |
| 651 public: | 363 public: |
| 652 std::string GetDisplayName() const override { return "FailingVideoDecoder"; } | 364 std::string GetDisplayName() const override { return "FailingVideoDecoder"; } |
| 653 void Initialize(const VideoDecoderConfig& config, | 365 void Initialize(const VideoDecoderConfig& config, |
| 654 bool low_delay, | 366 bool low_delay, |
| 655 CdmContext* cdm_context, | 367 CdmContext* cdm_context, |
| 656 const InitCB& init_cb, | 368 const InitCB& init_cb, |
| 657 const OutputCB& output_cb) override { | 369 const OutputCB& output_cb) override { |
| 658 init_cb.Run(true); | 370 init_cb.Run(true); |
| 659 } | 371 } |
| 660 void Decode(const scoped_refptr<DecoderBuffer>& buffer, | 372 void Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 661 const DecodeCB& decode_cb) override { | 373 const DecodeCB& decode_cb) override { |
| 662 base::ThreadTaskRunnerHandle::Get()->PostTask( | 374 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 663 FROM_HERE, base::Bind(decode_cb, DecodeStatus::DECODE_ERROR)); | 375 FROM_HERE, base::Bind(decode_cb, DecodeStatus::DECODE_ERROR)); |
| 664 } | 376 } |
| 665 void Reset(const base::Closure& closure) override { closure.Run(); } | 377 void Reset(const base::Closure& closure) override { closure.Run(); } |
| 666 bool NeedsBitstreamConversion() const override { return true; } | 378 bool NeedsBitstreamConversion() const override { return true; } |
| 667 }; | 379 }; |
| 668 | 380 |
| 381 } // namespace | |
| 382 | |
| 669 // TODO(xhwang): These tests have been disabled for some time as apptests and no | 383 // TODO(xhwang): These tests have been disabled for some time as apptests and no |
| 670 // longer pass. They need to be reconstituted as shell tests. | 384 // longer pass. They need to be reconstituted as shell tests. |
| 671 // Currently there are compile issues which must be resolved, | 385 // Currently there are compile issues which must be resolved, |
| 672 // preferably by eliminating multiple inheritance here which is | 386 // preferably by eliminating multiple inheritance here which is |
| 673 // banned by Google C++ style. | 387 // banned by Google C++ style. |
| 674 #if defined(MOJO_RENDERER) && defined(ENABLE_MOJO_PIPELINE_INTEGRATION_TEST) | 388 #if defined(MOJO_RENDERER) && defined(ENABLE_MOJO_PIPELINE_INTEGRATION_TEST) |
| 675 class PipelineIntegrationTestHost : public service_manager::test::ServiceTest, | 389 class PipelineIntegrationTest : public service_manager::test::ServiceTest, |
| 676 public PipelineIntegrationTestBase { | 390 public PipelineIntegrationTestBase { |
| 677 public: | 391 public: |
| 678 PipelineIntegrationTestHost() | 392 PipelineIntegrationTest() |
| 679 : service_manager::test::ServiceTest( | 393 : service_manager::test::ServiceTest( |
| 680 "media_pipeline_integration_shelltests") {} | 394 "media_pipeline_integration_shelltests") {} |
| 681 | 395 |
| 682 void SetUp() override { | 396 void SetUp() override { |
| 683 ServiceTest::SetUp(); | 397 ServiceTest::SetUp(); |
| 684 InitializeMediaLibrary(); | 398 InitializeMediaLibrary(); |
| 685 } | 399 } |
| 686 | 400 |
| 687 protected: | 401 protected: |
| 688 std::unique_ptr<Renderer> CreateRenderer( | 402 std::unique_ptr<Renderer> CreateRenderer( |
| 689 CreateVideoDecodersCB prepend_video_decoders_cb, | 403 CreateVideoDecodersCB prepend_video_decoders_cb, |
| 690 CreateAudioDecodersCB prepend_audio_decoders_cb) override { | 404 CreateAudioDecodersCB prepend_audio_decoders_cb) override { |
| 691 connector()->BindInterface("media", &media_interface_factory_); | 405 connector()->BindInterface("media", &media_interface_factory_); |
| 692 | 406 |
| 693 mojom::RendererPtr mojo_renderer; | 407 mojom::RendererPtr mojo_renderer; |
| 694 media_interface_factory_->CreateRenderer(std::string(), | 408 media_interface_factory_->CreateRenderer(std::string(), |
| 695 mojo::MakeRequest(&mojo_renderer)); | 409 mojo::MakeRequest(&mojo_renderer)); |
| 696 | 410 |
| 697 return base::MakeUnique<MojoRenderer>(message_loop_.task_runner(), | 411 return base::MakeUnique<MojoRenderer>(message_loop_.task_runner(), |
| 698 std::move(mojo_renderer)); | 412 std::move(mojo_renderer)); |
| 699 } | 413 } |
| 700 | 414 |
| 701 private: | 415 private: |
| 702 mojom::InterfaceFactoryPtr media_interface_factory_; | 416 mojom::InterfaceFactoryPtr media_interface_factory_; |
| 703 }; | 417 }; |
| 704 #else | 418 #else |
| 705 class PipelineIntegrationTestHost : public testing::Test, | 419 class PipelineIntegrationTest : public testing::Test, |
| 706 public PipelineIntegrationTestBase {}; | 420 public PipelineIntegrationTestBase {}; |
| 707 #endif // defined(MOJO_RENDERER) | 421 #endif // defined(MOJO_RENDERER) |
| 708 | 422 |
| 709 class PipelineIntegrationTest : public PipelineIntegrationTestHost { | |
| 710 public: | |
| 711 PipelineStatus StartPipelineWithMediaSource(MockMediaSource* source) { | |
| 712 return StartPipelineWithMediaSource(source, kNormal, nullptr); | |
| 713 } | |
| 714 | |
| 715 PipelineStatus StartPipelineWithEncryptedMedia( | |
| 716 MockMediaSource* source, | |
| 717 FakeEncryptedMedia* encrypted_media) { | |
| 718 return StartPipelineWithMediaSource(source, kNormal, encrypted_media); | |
| 719 } | |
| 720 | |
| 721 PipelineStatus StartPipelineWithMediaSource( | |
| 722 MockMediaSource* source, | |
| 723 uint8_t test_type, | |
| 724 FakeEncryptedMedia* encrypted_media) { | |
| 725 hashing_enabled_ = test_type & kHashed; | |
| 726 clockless_playback_ = test_type & kClockless; | |
| 727 | |
| 728 if (!(test_type & kExpectDemuxerFailure)) | |
| 729 EXPECT_CALL(*source, InitSegmentReceivedMock(_)).Times(AtLeast(1)); | |
| 730 | |
| 731 EXPECT_CALL(*this, OnMetadata(_)) | |
| 732 .Times(AtMost(1)) | |
| 733 .WillRepeatedly(SaveArg<0>(&metadata_)); | |
| 734 EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)) | |
| 735 .Times(AnyNumber()); | |
| 736 EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)) | |
| 737 .Times(AnyNumber()); | |
| 738 EXPECT_CALL(*this, OnDurationChange()).Times(AnyNumber()); | |
| 739 EXPECT_CALL(*this, OnVideoNaturalSizeChange(_)).Times(AtMost(1)); | |
| 740 EXPECT_CALL(*this, OnVideoOpacityChange(_)).Times(AtMost(1)); | |
| 741 | |
| 742 source->set_demuxer_failure_cb(base::Bind( | |
| 743 &PipelineIntegrationTest::OnStatusCallback, base::Unretained(this))); | |
| 744 demuxer_ = source->GetDemuxer(); | |
| 745 | |
| 746 if (encrypted_media) { | |
| 747 EXPECT_CALL(*this, DecryptorAttached(true)); | |
| 748 | |
| 749 // Encrypted content used but keys provided in advance, so this is | |
| 750 // never called. | |
| 751 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); | |
| 752 pipeline_->SetCdm(encrypted_media->GetCdmContext(), | |
| 753 base::Bind(&PipelineIntegrationTest::DecryptorAttached, | |
| 754 base::Unretained(this))); | |
| 755 } else { | |
| 756 // Encrypted content not used, so this is never called. | |
| 757 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); | |
| 758 } | |
| 759 | |
| 760 pipeline_->Start(demuxer_.get(), CreateRenderer(), this, | |
| 761 base::Bind(&PipelineIntegrationTest::OnStatusCallback, | |
| 762 base::Unretained(this))); | |
| 763 | |
| 764 if (encrypted_media) { | |
| 765 source->set_encrypted_media_init_data_cb( | |
| 766 base::Bind(&FakeEncryptedMedia::OnEncryptedMediaInitData, | |
| 767 base::Unretained(encrypted_media))); | |
| 768 } | |
| 769 base::RunLoop().Run(); | |
| 770 return pipeline_status_; | |
| 771 } | |
| 772 | |
| 773 // Verifies that seeking works properly for ChunkDemuxer when the | |
| 774 // seek happens while there is a pending read on the ChunkDemuxer | |
| 775 // and no data is available. | |
| 776 bool TestSeekDuringRead(const std::string& filename, | |
| 777 const std::string& mimetype, | |
| 778 int initial_append_size, | |
| 779 base::TimeDelta start_seek_time, | |
| 780 base::TimeDelta seek_time, | |
| 781 int seek_file_position, | |
| 782 int seek_append_size) { | |
| 783 MockMediaSource source(filename, mimetype, initial_append_size); | |
| 784 | |
| 785 if (StartPipelineWithMediaSource(&source) != PIPELINE_OK) | |
| 786 return false; | |
| 787 | |
| 788 Play(); | |
| 789 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) | |
| 790 return false; | |
| 791 | |
| 792 source.Seek(seek_time, seek_file_position, seek_append_size); | |
| 793 if (!Seek(seek_time)) | |
| 794 return false; | |
| 795 | |
| 796 source.EndOfStream(); | |
| 797 | |
| 798 source.Shutdown(); | |
| 799 Stop(); | |
| 800 return true; | |
| 801 } | |
| 802 }; | |
| 803 | |
| 804 struct PlaybackTestData { | 423 struct PlaybackTestData { |
| 805 const std::string filename; | 424 const std::string filename; |
| 806 const uint32_t start_time_ms; | 425 const uint32_t start_time_ms; |
| 807 const uint32_t duration_ms; | 426 const uint32_t duration_ms; |
| 808 }; | 427 }; |
| 809 | 428 |
| 810 struct MSEPlaybackTestData { | 429 struct MSEPlaybackTestData { |
| 811 const std::string filename; | 430 const std::string filename; |
| 812 const std::string mimetype; | 431 const std::string mimetype; |
| 813 const size_t append_bytes; | 432 const size_t append_bytes; |
| (...skipping 1858 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2672 | 2291 |
| 2673 TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) { | 2292 TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) { |
| 2674 ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm")); | 2293 ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm")); |
| 2675 Play(); | 2294 Play(); |
| 2676 ASSERT_TRUE(WaitUntilOnEnded()); | 2295 ASSERT_TRUE(WaitUntilOnEnded()); |
| 2677 ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000), | 2296 ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000), |
| 2678 demuxer_->GetStartTime()); | 2297 demuxer_->GetStartTime()); |
| 2679 } | 2298 } |
| 2680 | 2299 |
| 2681 } // namespace media | 2300 } // namespace media |
| OLD | NEW |