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/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 bool has_additional_usable_key) = 0; | 127 bool has_additional_usable_key) = 0; |
128 | 128 |
129 // Errors are not expected unless overridden. | 129 // Errors are not expected unless overridden. |
130 virtual void OnSessionError(const std::string& web_session_id, | 130 virtual void OnSessionError(const std::string& web_session_id, |
131 const std::string& error_name, | 131 const std::string& error_name, |
132 uint32 system_code, | 132 uint32 system_code, |
133 const std::string& error_message) { | 133 const std::string& error_message) { |
134 FAIL() << "Unexpected Key Error"; | 134 FAIL() << "Unexpected Key Error"; |
135 } | 135 } |
136 | 136 |
137 virtual void NeedKey(const std::string& type, | 137 virtual void OnEncryptedMediaInitData(const std::string& init_data_type, |
138 const std::vector<uint8>& init_data, | 138 const std::vector<uint8>& init_data, |
139 AesDecryptor* decryptor) = 0; | 139 AesDecryptor* decryptor) = 0; |
140 }; | 140 }; |
141 | 141 |
142 FakeEncryptedMedia(AppBase* app) | 142 FakeEncryptedMedia(AppBase* app) |
143 : decryptor_(base::Bind(&FakeEncryptedMedia::OnSessionMessage, | 143 : decryptor_(base::Bind(&FakeEncryptedMedia::OnSessionMessage, |
144 base::Unretained(this)), | 144 base::Unretained(this)), |
145 base::Bind(&FakeEncryptedMedia::OnSessionClosed, | 145 base::Bind(&FakeEncryptedMedia::OnSessionClosed, |
146 base::Unretained(this)), | 146 base::Unretained(this)), |
147 base::Bind(&FakeEncryptedMedia::OnSessionKeysChange, | 147 base::Bind(&FakeEncryptedMedia::OnSessionKeysChange, |
148 base::Unretained(this))), | 148 base::Unretained(this))), |
149 cdm_context_(&decryptor_), | 149 cdm_context_(&decryptor_), |
(...skipping 18 matching lines...) Expand all Loading... |
168 } | 168 } |
169 | 169 |
170 void OnSessionError(const std::string& web_session_id, | 170 void OnSessionError(const std::string& web_session_id, |
171 const std::string& error_name, | 171 const std::string& error_name, |
172 uint32 system_code, | 172 uint32 system_code, |
173 const std::string& error_message) { | 173 const std::string& error_message) { |
174 app_->OnSessionError( | 174 app_->OnSessionError( |
175 web_session_id, error_name, system_code, error_message); | 175 web_session_id, error_name, system_code, error_message); |
176 } | 176 } |
177 | 177 |
178 void NeedKey(const std::string& type, | 178 void OnEncryptedMediaInitData(const std::string& init_data_type, |
179 const std::vector<uint8>& init_data) { | 179 const std::vector<uint8>& init_data) { |
180 app_->NeedKey(type, init_data, &decryptor_); | 180 app_->OnEncryptedMediaInitData(init_data_type, init_data, &decryptor_); |
181 } | 181 } |
182 | 182 |
183 private: | 183 private: |
184 class TestCdmContext : public CdmContext { | 184 class TestCdmContext : public CdmContext { |
185 public: | 185 public: |
186 TestCdmContext(Decryptor* decryptor) : decryptor_(decryptor) {} | 186 TestCdmContext(Decryptor* decryptor) : decryptor_(decryptor) {} |
187 | 187 |
188 Decryptor* GetDecryptor() final { return decryptor_; } | 188 Decryptor* GetDecryptor() final { return decryptor_; } |
189 | 189 |
190 #if defined(ENABLE_BROWSER_CDMS) | 190 #if defined(ENABLE_BROWSER_CDMS) |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 void OnSessionClosed(const std::string& web_session_id) override { | 257 void OnSessionClosed(const std::string& web_session_id) override { |
258 EXPECT_EQ(current_session_id_, web_session_id); | 258 EXPECT_EQ(current_session_id_, web_session_id); |
259 } | 259 } |
260 | 260 |
261 void OnSessionKeysChange(const std::string& web_session_id, | 261 void OnSessionKeysChange(const std::string& web_session_id, |
262 bool has_additional_usable_key) override { | 262 bool has_additional_usable_key) override { |
263 EXPECT_EQ(current_session_id_, web_session_id); | 263 EXPECT_EQ(current_session_id_, web_session_id); |
264 EXPECT_EQ(has_additional_usable_key, true); | 264 EXPECT_EQ(has_additional_usable_key, true); |
265 } | 265 } |
266 | 266 |
267 void NeedKey(const std::string& type, | 267 void OnEncryptedMediaInitData(const std::string& init_data_type, |
268 const std::vector<uint8>& init_data, | 268 const std::vector<uint8>& init_data, |
269 AesDecryptor* decryptor) override { | 269 AesDecryptor* decryptor) override { |
270 if (current_session_id_.empty()) { | 270 if (current_session_id_.empty()) { |
271 decryptor->CreateSession(type, | 271 decryptor->CreateSession(init_data_type, kInitData, arraysize(kInitData), |
272 kInitData, | |
273 arraysize(kInitData), | |
274 MediaKeys::TEMPORARY_SESSION, | 272 MediaKeys::TEMPORARY_SESSION, |
275 CreateSessionPromise(RESOLVED)); | 273 CreateSessionPromise(RESOLVED)); |
276 EXPECT_FALSE(current_session_id_.empty()); | 274 EXPECT_FALSE(current_session_id_.empty()); |
277 } | 275 } |
278 | 276 |
279 // Clear Key really needs the key ID in |init_data|. For WebM, they are the | 277 // Clear Key really needs the key ID in |init_data|. For WebM, they are the |
280 // same, but this is not the case for ISO CENC. Therefore, provide the | 278 // same, but this is not the case for ISO CENC. Therefore, provide the |
281 // correct key ID. | 279 // correct key ID. |
282 const uint8* key_id = init_data.empty() ? NULL : &init_data[0]; | 280 const uint8* key_id = init_data.empty() ? NULL : &init_data[0]; |
283 size_t key_id_length = init_data.size(); | 281 size_t key_id_length = init_data.size(); |
284 if (type == kCencInitDataType) { | 282 if (init_data_type == kCencInitDataType) { |
285 key_id = kKeyId; | 283 key_id = kKeyId; |
286 key_id_length = arraysize(kKeyId); | 284 key_id_length = arraysize(kKeyId); |
287 } | 285 } |
288 | 286 |
289 // Convert key into a JSON structure and then add it. | 287 // Convert key into a JSON structure and then add it. |
290 std::string jwk = GenerateJWKSet( | 288 std::string jwk = GenerateJWKSet( |
291 kSecretKey, arraysize(kSecretKey), key_id, key_id_length); | 289 kSecretKey, arraysize(kSecretKey), key_id, key_id_length); |
292 decryptor->UpdateSession(current_session_id_, | 290 decryptor->UpdateSession(current_session_id_, |
293 reinterpret_cast<const uint8*>(jwk.data()), | 291 reinterpret_cast<const uint8*>(jwk.data()), |
294 jwk.size(), | 292 jwk.size(), |
295 CreatePromise(RESOLVED)); | 293 CreatePromise(RESOLVED)); |
296 } | 294 } |
297 | 295 |
298 std::string current_session_id_; | 296 std::string current_session_id_; |
299 }; | 297 }; |
300 | 298 |
301 class RotatingKeyProvidingApp : public KeyProvidingApp { | 299 class RotatingKeyProvidingApp : public KeyProvidingApp { |
302 public: | 300 public: |
303 RotatingKeyProvidingApp() : num_distint_need_key_calls_(0) {} | 301 RotatingKeyProvidingApp() : num_distint_need_key_calls_(0) {} |
304 ~RotatingKeyProvidingApp() override { | 302 ~RotatingKeyProvidingApp() override { |
305 // Expect that NeedKey is fired multiple times with different |init_data|. | 303 // Expect that OnEncryptedMediaInitData is fired multiple times with |
| 304 // different |init_data|. |
306 EXPECT_GT(num_distint_need_key_calls_, 1u); | 305 EXPECT_GT(num_distint_need_key_calls_, 1u); |
307 } | 306 } |
308 | 307 |
309 void NeedKey(const std::string& type, | 308 void OnEncryptedMediaInitData(const std::string& init_data_type, |
310 const std::vector<uint8>& init_data, | 309 const std::vector<uint8>& init_data, |
311 AesDecryptor* decryptor) override { | 310 AesDecryptor* decryptor) override { |
312 // Skip the request if the |init_data| has been seen. | 311 // Skip the request if the |init_data| has been seen. |
313 if (init_data == prev_init_data_) | 312 if (init_data == prev_init_data_) |
314 return; | 313 return; |
315 prev_init_data_ = init_data; | 314 prev_init_data_ = init_data; |
316 ++num_distint_need_key_calls_; | 315 ++num_distint_need_key_calls_; |
317 | 316 |
318 decryptor->CreateSession(type, | 317 decryptor->CreateSession(init_data_type, vector_as_array(&init_data), |
319 vector_as_array(&init_data), | 318 init_data.size(), MediaKeys::TEMPORARY_SESSION, |
320 init_data.size(), | |
321 MediaKeys::TEMPORARY_SESSION, | |
322 CreateSessionPromise(RESOLVED)); | 319 CreateSessionPromise(RESOLVED)); |
323 | 320 |
324 std::vector<uint8> key_id; | 321 std::vector<uint8> key_id; |
325 std::vector<uint8> key; | 322 std::vector<uint8> key; |
326 EXPECT_TRUE(GetKeyAndKeyId(init_data, &key, &key_id)); | 323 EXPECT_TRUE(GetKeyAndKeyId(init_data, &key, &key_id)); |
327 | 324 |
328 // Convert key into a JSON structure and then add it. | 325 // Convert key into a JSON structure and then add it. |
329 std::string jwk = GenerateJWKSet(vector_as_array(&key), | 326 std::string jwk = GenerateJWKSet(vector_as_array(&key), |
330 key.size(), | 327 key.size(), |
331 vector_as_array(&key_id), | 328 vector_as_array(&key_id), |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 EXPECT_FALSE(web_session_id.empty()); | 382 EXPECT_FALSE(web_session_id.empty()); |
386 FAIL() << "Unexpected Closed"; | 383 FAIL() << "Unexpected Closed"; |
387 } | 384 } |
388 | 385 |
389 void OnSessionKeysChange(const std::string& web_session_id, | 386 void OnSessionKeysChange(const std::string& web_session_id, |
390 bool has_additional_usable_key) override { | 387 bool has_additional_usable_key) override { |
391 EXPECT_FALSE(web_session_id.empty()); | 388 EXPECT_FALSE(web_session_id.empty()); |
392 EXPECT_EQ(has_additional_usable_key, true); | 389 EXPECT_EQ(has_additional_usable_key, true); |
393 } | 390 } |
394 | 391 |
395 void NeedKey(const std::string& type, | 392 void OnEncryptedMediaInitData(const std::string& init_data_type, |
396 const std::vector<uint8>& init_data, | 393 const std::vector<uint8>& init_data, |
397 AesDecryptor* decryptor) override {} | 394 AesDecryptor* decryptor) override {} |
398 }; | 395 }; |
399 | 396 |
400 // Helper class that emulates calls made on the ChunkDemuxer by the | 397 // Helper class that emulates calls made on the ChunkDemuxer by the |
401 // Media Source API. | 398 // Media Source API. |
402 class MockMediaSource { | 399 class MockMediaSource { |
403 public: | 400 public: |
404 MockMediaSource(const std::string& filename, | 401 MockMediaSource(const std::string& filename, |
405 const std::string& mimetype, | 402 const std::string& mimetype, |
406 int initial_append_size) | 403 int initial_append_size) |
407 : current_position_(0), | 404 : current_position_(0), |
408 initial_append_size_(initial_append_size), | 405 initial_append_size_(initial_append_size), |
409 mimetype_(mimetype), | 406 mimetype_(mimetype), |
410 chunk_demuxer_(new ChunkDemuxer( | 407 chunk_demuxer_(new ChunkDemuxer( |
411 base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)), | 408 base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)), |
412 base::Bind(&MockMediaSource::DemuxerNeedKey, | 409 base::Bind(&MockMediaSource::OnEncryptedMediaInitData, |
413 base::Unretained(this)), | 410 base::Unretained(this)), |
414 LogCB(), | 411 LogCB(), |
415 scoped_refptr<MediaLog>(new MediaLog()), | 412 scoped_refptr<MediaLog>(new MediaLog()), |
416 true)), | 413 true)), |
417 owned_chunk_demuxer_(chunk_demuxer_) { | 414 owned_chunk_demuxer_(chunk_demuxer_) { |
418 file_data_ = ReadTestDataFile(filename); | 415 file_data_ = ReadTestDataFile(filename); |
419 | 416 |
420 if (initial_append_size_ == kAppendWholeFile) | 417 if (initial_append_size_ == kAppendWholeFile) |
421 initial_append_size_ = file_data_->data_size(); | 418 initial_append_size_ = file_data_->data_size(); |
422 | 419 |
423 DCHECK_GT(initial_append_size_, 0); | 420 DCHECK_GT(initial_append_size_, 0); |
424 DCHECK_LE(initial_append_size_, file_data_->data_size()); | 421 DCHECK_LE(initial_append_size_, file_data_->data_size()); |
425 } | 422 } |
426 | 423 |
427 virtual ~MockMediaSource() {} | 424 virtual ~MockMediaSource() {} |
428 | 425 |
429 scoped_ptr<Demuxer> GetDemuxer() { return owned_chunk_demuxer_.Pass(); } | 426 scoped_ptr<Demuxer> GetDemuxer() { return owned_chunk_demuxer_.Pass(); } |
430 | 427 |
431 void set_need_key_cb(const Demuxer::NeedKeyCB& need_key_cb) { | 428 void set_encrypted_media_init_data_cb( |
432 need_key_cb_ = need_key_cb; | 429 const Demuxer::EncryptedMediaInitDataCB& encrypted_media_init_data_cb) { |
| 430 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; |
433 } | 431 } |
434 | 432 |
435 void Seek(base::TimeDelta seek_time, int new_position, int seek_append_size) { | 433 void Seek(base::TimeDelta seek_time, int new_position, int seek_append_size) { |
436 chunk_demuxer_->StartWaitingForSeek(seek_time); | 434 chunk_demuxer_->StartWaitingForSeek(seek_time); |
437 | 435 |
438 chunk_demuxer_->Abort( | 436 chunk_demuxer_->Abort( |
439 kSourceId, | 437 kSourceId, |
440 base::TimeDelta(), kInfiniteDuration(), &last_timestamp_offset_); | 438 base::TimeDelta(), kInfiniteDuration(), &last_timestamp_offset_); |
441 | 439 |
442 DCHECK_GE(new_position, 0); | 440 DCHECK_GE(new_position, 0); |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
528 mimetype_.substr(codecs_param_start, | 526 mimetype_.substr(codecs_param_start, |
529 codecs_param_end - codecs_param_start); | 527 codecs_param_end - codecs_param_start); |
530 Tokenize(codecs_param, ",", &codecs); | 528 Tokenize(codecs_param, ",", &codecs); |
531 } | 529 } |
532 | 530 |
533 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk); | 531 CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk); |
534 | 532 |
535 AppendData(initial_append_size_); | 533 AppendData(initial_append_size_); |
536 } | 534 } |
537 | 535 |
538 void DemuxerNeedKey(const std::string& type, | 536 void OnEncryptedMediaInitData(const std::string& init_data_type, |
539 const std::vector<uint8>& init_data) { | 537 const std::vector<uint8>& init_data) { |
540 DCHECK(!init_data.empty()); | 538 DCHECK(!init_data.empty()); |
541 CHECK(!need_key_cb_.is_null()); | 539 CHECK(!encrypted_media_init_data_cb_.is_null()); |
542 need_key_cb_.Run(type, init_data); | 540 encrypted_media_init_data_cb_.Run(init_data_type, init_data); |
543 } | 541 } |
544 | 542 |
545 base::TimeDelta last_timestamp_offset() const { | 543 base::TimeDelta last_timestamp_offset() const { |
546 return last_timestamp_offset_; | 544 return last_timestamp_offset_; |
547 } | 545 } |
548 | 546 |
549 MOCK_METHOD0(InitSegmentReceived, void(void)); | 547 MOCK_METHOD0(InitSegmentReceived, void(void)); |
550 | 548 |
551 private: | 549 private: |
552 scoped_refptr<DecoderBuffer> file_data_; | 550 scoped_refptr<DecoderBuffer> file_data_; |
553 int current_position_; | 551 int current_position_; |
554 int initial_append_size_; | 552 int initial_append_size_; |
555 std::string mimetype_; | 553 std::string mimetype_; |
556 ChunkDemuxer* chunk_demuxer_; | 554 ChunkDemuxer* chunk_demuxer_; |
557 scoped_ptr<Demuxer> owned_chunk_demuxer_; | 555 scoped_ptr<Demuxer> owned_chunk_demuxer_; |
558 Demuxer::NeedKeyCB need_key_cb_; | 556 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_; |
559 base::TimeDelta last_timestamp_offset_; | 557 base::TimeDelta last_timestamp_offset_; |
560 }; | 558 }; |
561 | 559 |
562 class PipelineIntegrationTest | 560 class PipelineIntegrationTest |
563 : public testing::Test, | 561 : public testing::Test, |
564 public PipelineIntegrationTestBase { | 562 public PipelineIntegrationTestBase { |
565 public: | 563 public: |
566 void StartPipelineWithMediaSource(MockMediaSource* source) { | 564 void StartPipelineWithMediaSource(MockMediaSource* source) { |
567 EXPECT_CALL(*source, InitSegmentReceived()).Times(AtLeast(1)); | 565 EXPECT_CALL(*source, InitSegmentReceived()).Times(AtLeast(1)); |
568 EXPECT_CALL(*this, OnMetadata(_)) | 566 EXPECT_CALL(*this, OnMetadata(_)) |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
619 base::Unretained(this)), | 617 base::Unretained(this)), |
620 base::Bind(&PipelineIntegrationTest::OnMetadata, | 618 base::Bind(&PipelineIntegrationTest::OnMetadata, |
621 base::Unretained(this)), | 619 base::Unretained(this)), |
622 base::Bind(&PipelineIntegrationTest::OnBufferingStateChanged, | 620 base::Bind(&PipelineIntegrationTest::OnBufferingStateChanged, |
623 base::Unretained(this)), | 621 base::Unretained(this)), |
624 base::Bind(&PipelineIntegrationTest::OnVideoFramePaint, | 622 base::Bind(&PipelineIntegrationTest::OnVideoFramePaint, |
625 base::Unretained(this)), | 623 base::Unretained(this)), |
626 base::Closure(), base::Bind(&PipelineIntegrationTest::OnAddTextTrack, | 624 base::Closure(), base::Bind(&PipelineIntegrationTest::OnAddTextTrack, |
627 base::Unretained(this))); | 625 base::Unretained(this))); |
628 | 626 |
629 source->set_need_key_cb(base::Bind(&FakeEncryptedMedia::NeedKey, | 627 source->set_encrypted_media_init_data_cb( |
630 base::Unretained(encrypted_media))); | 628 base::Bind(&FakeEncryptedMedia::OnEncryptedMediaInitData, |
| 629 base::Unretained(encrypted_media))); |
631 | 630 |
632 message_loop_.Run(); | 631 message_loop_.Run(); |
633 EXPECT_EQ(PIPELINE_OK, pipeline_status_); | 632 EXPECT_EQ(PIPELINE_OK, pipeline_status_); |
634 } | 633 } |
635 | 634 |
636 // Verifies that seeking works properly for ChunkDemuxer when the | 635 // Verifies that seeking works properly for ChunkDemuxer when the |
637 // seek happens while there is a pending read on the ChunkDemuxer | 636 // seek happens while there is a pending read on the ChunkDemuxer |
638 // and no data is available. | 637 // and no data is available. |
639 bool TestSeekDuringRead(const std::string& filename, | 638 bool TestSeekDuringRead(const std::string& filename, |
640 const std::string& mimetype, | 639 const std::string& mimetype, |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
712 TEST_F(PipelineIntegrationTest, F32PlaybackHashed) { | 711 TEST_F(PipelineIntegrationTest, F32PlaybackHashed) { |
713 ASSERT_EQ(PIPELINE_OK, Start("sfx_f32le.wav", kHashed)); | 712 ASSERT_EQ(PIPELINE_OK, Start("sfx_f32le.wav", kHashed)); |
714 Play(); | 713 Play(); |
715 ASSERT_TRUE(WaitUntilOnEnded()); | 714 ASSERT_TRUE(WaitUntilOnEnded()); |
716 EXPECT_EQ(std::string(kNullVideoHash), GetVideoHash()); | 715 EXPECT_EQ(std::string(kNullVideoHash), GetVideoHash()); |
717 EXPECT_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash()); | 716 EXPECT_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash()); |
718 } | 717 } |
719 | 718 |
720 TEST_F(PipelineIntegrationTest, BasicPlaybackEncrypted) { | 719 TEST_F(PipelineIntegrationTest, BasicPlaybackEncrypted) { |
721 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); | 720 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); |
722 set_need_key_cb(base::Bind(&FakeEncryptedMedia::NeedKey, | 721 set_encrypted_media_init_data_cb( |
723 base::Unretained(&encrypted_media))); | 722 base::Bind(&FakeEncryptedMedia::OnEncryptedMediaInitData, |
| 723 base::Unretained(&encrypted_media))); |
724 | 724 |
725 ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-av_enc-av.webm", | 725 ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-av_enc-av.webm", |
726 encrypted_media.GetCdmContext())); | 726 encrypted_media.GetCdmContext())); |
727 | 727 |
728 Play(); | 728 Play(); |
729 | 729 |
730 ASSERT_TRUE(WaitUntilOnEnded()); | 730 ASSERT_TRUE(WaitUntilOnEnded()); |
731 Stop(); | 731 Stop(); |
732 } | 732 } |
733 | 733 |
(...skipping 877 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1611 | 1611 |
1612 TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) { | 1612 TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) { |
1613 ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm")); | 1613 ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm")); |
1614 Play(); | 1614 Play(); |
1615 ASSERT_TRUE(WaitUntilOnEnded()); | 1615 ASSERT_TRUE(WaitUntilOnEnded()); |
1616 ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000), | 1616 ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000), |
1617 demuxer_->GetStartTime()); | 1617 demuxer_->GetStartTime()); |
1618 } | 1618 } |
1619 | 1619 |
1620 } // namespace media | 1620 } // namespace media |
OLD | NEW |