| 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" |
| 33 #include "media/test/fake_encrypted_media.h" |
| 34 #include "media/test/mock_media_source.h" |
| 36 #include "media/test/pipeline_integration_test_base.h" | 35 #include "media/test/pipeline_integration_test_base.h" |
| 37 #include "testing/gmock/include/gmock/gmock.h" | 36 #include "testing/gmock/include/gmock/gmock.h" |
| 38 #include "url/gurl.h" | 37 #include "url/gurl.h" |
| 39 | 38 |
| 40 #if defined(MOJO_RENDERER) | 39 #if defined(MOJO_RENDERER) |
| 41 #include "media/mojo/clients/mojo_renderer.h" | 40 #include "media/mojo/clients/mojo_renderer.h" |
| 42 #include "media/mojo/interfaces/interface_factory.mojom.h" | 41 #include "media/mojo/interfaces/interface_factory.mojom.h" |
| 43 #include "media/mojo/interfaces/renderer.mojom.h" | 42 #include "media/mojo/interfaces/renderer.mojom.h" |
| 44 #include "services/service_manager/public/cpp/connect.h" | 43 #include "services/service_manager/public/cpp/connect.h" |
| 45 #include "services/service_manager/public/cpp/service_test.h" | 44 #include "services/service_manager/public/cpp/service_test.h" |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 #endif | 86 #endif |
| 88 | 87 |
| 89 using testing::_; | 88 using testing::_; |
| 90 using testing::AnyNumber; | 89 using testing::AnyNumber; |
| 91 using testing::AtLeast; | 90 using testing::AtLeast; |
| 92 using testing::AtMost; | 91 using testing::AtMost; |
| 93 using testing::SaveArg; | 92 using testing::SaveArg; |
| 94 | 93 |
| 95 namespace media { | 94 namespace media { |
| 96 | 95 |
| 97 const char kSourceId[] = "SourceId"; | |
| 98 | |
| 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 bool EvictCodedFrames(base::TimeDelta currentMediaTime, size_t newDataSize) { | |
| 544 return chunk_demuxer_->EvictCodedFrames(kSourceId, currentMediaTime, | |
| 545 newDataSize); | |
| 546 } | |
| 547 | |
| 548 void RemoveRange(base::TimeDelta start, base::TimeDelta end) { | |
| 549 chunk_demuxer_->Remove(kSourceId, start, end); | |
| 550 } | |
| 551 | |
| 552 void EndOfStream() { chunk_demuxer_->MarkEndOfStream(PIPELINE_OK); } | |
| 553 | |
| 554 void Shutdown() { | |
| 555 if (!chunk_demuxer_) | |
| 556 return; | |
| 557 chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(), | |
| 558 kInfiniteDuration, | |
| 559 &last_timestamp_offset_); | |
| 560 chunk_demuxer_->Shutdown(); | |
| 561 chunk_demuxer_ = NULL; | |
| 562 } | |
| 563 | |
| 564 void DemuxerOpened() { | |
| 565 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 566 FROM_HERE, base::Bind(&MockMediaSource::DemuxerOpenedTask, | |
| 567 base::Unretained(this))); | |
| 568 } | |
| 569 | |
| 570 void DemuxerOpenedTask() { | |
| 571 ChunkDemuxer::Status status = AddId(); | |
| 572 if (status != ChunkDemuxer::kOk) { | |
| 573 CHECK(!demuxer_failure_cb_.is_null()); | |
| 574 demuxer_failure_cb_.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | |
| 575 return; | |
| 576 } | |
| 577 chunk_demuxer_->SetTracksWatcher( | |
| 578 kSourceId, base::Bind(&MockMediaSource::InitSegmentReceived, | |
| 579 base::Unretained(this))); | |
| 580 | |
| 581 AppendData(initial_append_size_); | |
| 582 } | |
| 583 | |
| 584 ChunkDemuxer::Status AddId() { | |
| 585 // This code assumes that |mimetype_| is one of the following forms. | |
| 586 // 1. audio/mpeg | |
| 587 // 2. video/webm;codec="vorbis,vp8". | |
| 588 size_t semicolon = mimetype_.find(";"); | |
| 589 std::string type = mimetype_; | |
| 590 std::string codecs_param = ""; | |
| 591 if (semicolon != std::string::npos) { | |
| 592 type = mimetype_.substr(0, semicolon); | |
| 593 size_t codecs_param_start = mimetype_.find("codecs=\"", semicolon); | |
| 594 | |
| 595 CHECK_NE(codecs_param_start, std::string::npos); | |
| 596 | |
| 597 codecs_param_start += 8; // Skip over the codecs=". | |
| 598 | |
| 599 size_t codecs_param_end = mimetype_.find("\"", codecs_param_start); | |
| 600 | |
| 601 CHECK_NE(codecs_param_end, std::string::npos); | |
| 602 | |
| 603 codecs_param = mimetype_.substr(codecs_param_start, | |
| 604 codecs_param_end - codecs_param_start); | |
| 605 } | |
| 606 | |
| 607 return chunk_demuxer_->AddId(kSourceId, type, codecs_param); | |
| 608 } | |
| 609 | |
| 610 void OnEncryptedMediaInitData(EmeInitDataType init_data_type, | |
| 611 const std::vector<uint8_t>& init_data) { | |
| 612 DCHECK(!init_data.empty()); | |
| 613 CHECK(!encrypted_media_init_data_cb_.is_null()); | |
| 614 encrypted_media_init_data_cb_.Run(init_data_type, init_data); | |
| 615 } | |
| 616 | |
| 617 base::TimeDelta last_timestamp_offset() const { | |
| 618 return last_timestamp_offset_; | |
| 619 } | |
| 620 | |
| 621 void InitSegmentReceived(std::unique_ptr<MediaTracks> tracks) { | |
| 622 CHECK(tracks.get()); | |
| 623 EXPECT_GT(tracks->tracks().size(), 0u); | |
| 624 CHECK(chunk_demuxer_); | |
| 625 // Verify that track ids are unique. | |
| 626 std::set<MediaTrack::Id> track_ids; | |
| 627 for (const auto& track : tracks->tracks()) { | |
| 628 EXPECT_EQ(track_ids.end(), track_ids.find(track->id())); | |
| 629 track_ids.insert(track->id()); | |
| 630 } | |
| 631 InitSegmentReceivedMock(tracks); | |
| 632 } | |
| 633 | |
| 634 MOCK_METHOD1(InitSegmentReceivedMock, void(std::unique_ptr<MediaTracks>&)); | |
| 635 | |
| 636 private: | |
| 637 MediaLog media_log_; | |
| 638 scoped_refptr<DecoderBuffer> file_data_; | |
| 639 size_t current_position_; | |
| 640 size_t initial_append_size_; | |
| 641 std::string mimetype_; | |
| 642 ChunkDemuxer* chunk_demuxer_; | |
| 643 std::unique_ptr<Demuxer> owned_chunk_demuxer_; | |
| 644 PipelineStatusCB demuxer_failure_cb_; | |
| 645 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_; | |
| 646 base::TimeDelta last_timestamp_offset_; | |
| 647 }; | |
| 648 | |
| 649 // A rough simulation of GpuVideoDecoder that fails every Decode() request. This | 360 // A rough simulation of GpuVideoDecoder that fails every Decode() request. This |
| 650 // is used to test post-Initialize() fallback paths. | 361 // is used to test post-Initialize() fallback paths. |
| 651 class FailingVideoDecoder : public VideoDecoder { | 362 class FailingVideoDecoder : public VideoDecoder { |
| 652 public: | 363 public: |
| 653 std::string GetDisplayName() const override { return "FailingVideoDecoder"; } | 364 std::string GetDisplayName() const override { return "FailingVideoDecoder"; } |
| 654 void Initialize(const VideoDecoderConfig& config, | 365 void Initialize(const VideoDecoderConfig& config, |
| 655 bool low_delay, | 366 bool low_delay, |
| 656 CdmContext* cdm_context, | 367 CdmContext* cdm_context, |
| 657 const InitCB& init_cb, | 368 const InitCB& init_cb, |
| 658 const OutputCB& output_cb) override { | 369 const OutputCB& output_cb) override { |
| 659 init_cb.Run(true); | 370 init_cb.Run(true); |
| 660 } | 371 } |
| 661 void Decode(const scoped_refptr<DecoderBuffer>& buffer, | 372 void Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 662 const DecodeCB& decode_cb) override { | 373 const DecodeCB& decode_cb) override { |
| 663 base::ThreadTaskRunnerHandle::Get()->PostTask( | 374 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 664 FROM_HERE, base::Bind(decode_cb, DecodeStatus::DECODE_ERROR)); | 375 FROM_HERE, base::Bind(decode_cb, DecodeStatus::DECODE_ERROR)); |
| 665 } | 376 } |
| 666 void Reset(const base::Closure& closure) override { closure.Run(); } | 377 void Reset(const base::Closure& closure) override { closure.Run(); } |
| 667 bool NeedsBitstreamConversion() const override { return true; } | 378 bool NeedsBitstreamConversion() const override { return true; } |
| 668 }; | 379 }; |
| 669 | 380 |
| 670 // TODO(xhwang): These tests have been disabled for some time as apptests and no | 381 // TODO(xhwang): These tests have been disabled for some time as apptests and no |
| 671 // longer pass. They need to be reconstituted as shell tests. | 382 // longer pass. They need to be reconstituted as shell tests. |
| 672 // Currently there are compile issues which must be resolved, | 383 // Currently there are compile issues which must be resolved, |
| 673 // preferably by eliminating multiple inheritance here which is | 384 // preferably by eliminating multiple inheritance here which is |
| 674 // banned by Google C++ style. | 385 // banned by Google C++ style. |
| 675 #if defined(MOJO_RENDERER) && defined(ENABLE_MOJO_PIPELINE_INTEGRATION_TEST) | 386 #if defined(MOJO_RENDERER) && defined(ENABLE_MOJO_PIPELINE_INTEGRATION_TEST) |
| 676 class PipelineIntegrationTestHost : public service_manager::test::ServiceTest, | 387 class PipelineIntegrationTest : public service_manager::test::ServiceTest, |
| 677 public PipelineIntegrationTestBase { | 388 public PipelineIntegrationTestBase { |
| 678 public: | 389 public: |
| 679 PipelineIntegrationTestHost() | 390 PipelineIntegrationTest() |
| 680 : service_manager::test::ServiceTest( | 391 : service_manager::test::ServiceTest( |
| 681 "media_pipeline_integration_shelltests") {} | 392 "media_pipeline_integration_shelltests") {} |
| 682 | 393 |
| 683 void SetUp() override { | 394 void SetUp() override { |
| 684 ServiceTest::SetUp(); | 395 ServiceTest::SetUp(); |
| 685 InitializeMediaLibrary(); | 396 InitializeMediaLibrary(); |
| 686 } | 397 } |
| 687 | 398 |
| 688 protected: | 399 protected: |
| 689 std::unique_ptr<Renderer> CreateRenderer( | 400 std::unique_ptr<Renderer> CreateRenderer( |
| 690 CreateVideoDecodersCB prepend_video_decoders_cb, | 401 CreateVideoDecodersCB prepend_video_decoders_cb, |
| 691 CreateAudioDecodersCB prepend_audio_decoders_cb) override { | 402 CreateAudioDecodersCB prepend_audio_decoders_cb) override { |
| 692 connector()->BindInterface("media", &media_interface_factory_); | 403 connector()->BindInterface("media", &media_interface_factory_); |
| 693 | 404 |
| 694 mojom::RendererPtr mojo_renderer; | 405 mojom::RendererPtr mojo_renderer; |
| 695 media_interface_factory_->CreateRenderer(std::string(), | 406 media_interface_factory_->CreateRenderer(std::string(), |
| 696 mojo::MakeRequest(&mojo_renderer)); | 407 mojo::MakeRequest(&mojo_renderer)); |
| 697 | 408 |
| 698 return base::MakeUnique<MojoRenderer>(message_loop_.task_runner(), | 409 return base::MakeUnique<MojoRenderer>(message_loop_.task_runner(), |
| 699 std::move(mojo_renderer)); | 410 std::move(mojo_renderer)); |
| 700 } | 411 } |
| 701 | 412 |
| 702 private: | 413 private: |
| 703 mojom::InterfaceFactoryPtr media_interface_factory_; | 414 mojom::InterfaceFactoryPtr media_interface_factory_; |
| 704 }; | 415 }; |
| 705 #else | 416 #else |
| 706 class PipelineIntegrationTestHost : public testing::Test, | 417 class PipelineIntegrationTest : public testing::Test, |
| 707 public PipelineIntegrationTestBase {}; | 418 public PipelineIntegrationTestBase { |
| 708 #endif // defined(MOJO_RENDERER) | |
| 709 | |
| 710 class PipelineIntegrationTest : public PipelineIntegrationTestHost { | |
| 711 public: | 419 public: |
| 712 PipelineStatus StartPipelineWithMediaSource(MockMediaSource* source) { | |
| 713 return StartPipelineWithMediaSource(source, kNormal, nullptr); | |
| 714 } | |
| 715 | |
| 716 PipelineStatus StartPipelineWithEncryptedMedia( | |
| 717 MockMediaSource* source, | |
| 718 FakeEncryptedMedia* encrypted_media) { | |
| 719 return StartPipelineWithMediaSource(source, kNormal, encrypted_media); | |
| 720 } | |
| 721 | |
| 722 PipelineStatus StartPipelineWithMediaSource( | |
| 723 MockMediaSource* source, | |
| 724 uint8_t test_type, | |
| 725 FakeEncryptedMedia* encrypted_media) { | |
| 726 hashing_enabled_ = test_type & kHashed; | |
| 727 clockless_playback_ = test_type & kClockless; | |
| 728 | |
| 729 if (!(test_type & kExpectDemuxerFailure)) | |
| 730 EXPECT_CALL(*source, InitSegmentReceivedMock(_)).Times(AtLeast(1)); | |
| 731 | |
| 732 EXPECT_CALL(*this, OnMetadata(_)) | |
| 733 .Times(AtMost(1)) | |
| 734 .WillRepeatedly(SaveArg<0>(&metadata_)); | |
| 735 EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)) | |
| 736 .Times(AnyNumber()); | |
| 737 EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)) | |
| 738 .Times(AnyNumber()); | |
| 739 EXPECT_CALL(*this, OnDurationChange()).Times(AnyNumber()); | |
| 740 EXPECT_CALL(*this, OnVideoNaturalSizeChange(_)).Times(AtMost(1)); | |
| 741 EXPECT_CALL(*this, OnVideoOpacityChange(_)).Times(AtMost(1)); | |
| 742 | |
| 743 source->set_demuxer_failure_cb(base::Bind( | |
| 744 &PipelineIntegrationTest::OnStatusCallback, base::Unretained(this))); | |
| 745 demuxer_ = source->GetDemuxer(); | |
| 746 | |
| 747 if (encrypted_media) { | |
| 748 EXPECT_CALL(*this, DecryptorAttached(true)); | |
| 749 | |
| 750 // Encrypted content used but keys provided in advance, so this is | |
| 751 // never called. | |
| 752 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); | |
| 753 pipeline_->SetCdm(encrypted_media->GetCdmContext(), | |
| 754 base::Bind(&PipelineIntegrationTest::DecryptorAttached, | |
| 755 base::Unretained(this))); | |
| 756 } else { | |
| 757 // Encrypted content not used, so this is never called. | |
| 758 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); | |
| 759 } | |
| 760 | |
| 761 pipeline_->Start(demuxer_.get(), CreateRenderer(), this, | |
| 762 base::Bind(&PipelineIntegrationTest::OnStatusCallback, | |
| 763 base::Unretained(this))); | |
| 764 | |
| 765 if (encrypted_media) { | |
| 766 source->set_encrypted_media_init_data_cb( | |
| 767 base::Bind(&FakeEncryptedMedia::OnEncryptedMediaInitData, | |
| 768 base::Unretained(encrypted_media))); | |
| 769 } | |
| 770 base::RunLoop().Run(); | |
| 771 return pipeline_status_; | |
| 772 } | |
| 773 | |
| 774 // Verifies that seeking works properly for ChunkDemuxer when the | 420 // Verifies that seeking works properly for ChunkDemuxer when the |
| 775 // seek happens while there is a pending read on the ChunkDemuxer | 421 // seek happens while there is a pending read on the ChunkDemuxer |
| 776 // and no data is available. | 422 // and no data is available. |
| 777 bool TestSeekDuringRead(const std::string& filename, | 423 bool TestSeekDuringRead(const std::string& filename, |
| 778 const std::string& mimetype, | 424 const std::string& mimetype, |
| 779 int initial_append_size, | 425 int initial_append_size, |
| 780 base::TimeDelta start_seek_time, | 426 base::TimeDelta start_seek_time, |
| 781 base::TimeDelta seek_time, | 427 base::TimeDelta seek_time, |
| 782 int seek_file_position, | 428 int seek_file_position, |
| 783 int seek_append_size) { | 429 int seek_append_size) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 794 if (!Seek(seek_time)) | 440 if (!Seek(seek_time)) |
| 795 return false; | 441 return false; |
| 796 | 442 |
| 797 source.EndOfStream(); | 443 source.EndOfStream(); |
| 798 | 444 |
| 799 source.Shutdown(); | 445 source.Shutdown(); |
| 800 Stop(); | 446 Stop(); |
| 801 return true; | 447 return true; |
| 802 } | 448 } |
| 803 }; | 449 }; |
| 450 #endif // defined(MOJO_RENDERER) |
| 804 | 451 |
| 805 struct PlaybackTestData { | 452 struct PlaybackTestData { |
| 806 const std::string filename; | 453 const std::string filename; |
| 807 const uint32_t start_time_ms; | 454 const uint32_t start_time_ms; |
| 808 const uint32_t duration_ms; | 455 const uint32_t duration_ms; |
| 809 }; | 456 }; |
| 810 | 457 |
| 811 struct MSEPlaybackTestData { | 458 struct MSEPlaybackTestData { |
| 812 const std::string filename; | 459 const std::string filename; |
| 813 const std::string mimetype; | 460 const std::string mimetype; |
| (...skipping 1889 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2703 | 2350 |
| 2704 TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) { | 2351 TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) { |
| 2705 ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm")); | 2352 ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm")); |
| 2706 Play(); | 2353 Play(); |
| 2707 ASSERT_TRUE(WaitUntilOnEnded()); | 2354 ASSERT_TRUE(WaitUntilOnEnded()); |
| 2708 ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000), | 2355 ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000), |
| 2709 demuxer_->GetStartTime()); | 2356 demuxer_->GetStartTime()); |
| 2710 } | 2357 } |
| 2711 | 2358 |
| 2712 } // namespace media | 2359 } // namespace media |
| OLD | NEW |