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 <vector> | 5 #include <vector> |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/message_loop/message_loop.h" | 8 #include "base/message_loop/message_loop.h" |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "base/test/simple_test_tick_clock.h" | 10 #include "base/test/simple_test_tick_clock.h" |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_); | 98 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_); |
99 filter_collection_->SetAudioRenderer(audio_renderer.Pass()); | 99 filter_collection_->SetAudioRenderer(audio_renderer.Pass()); |
100 | 100 |
101 text_renderer_ = new TextRenderer( | 101 text_renderer_ = new TextRenderer( |
102 message_loop_.message_loop_proxy(), | 102 message_loop_.message_loop_proxy(), |
103 base::Bind(&PipelineTest::OnAddTextTrack, | 103 base::Bind(&PipelineTest::OnAddTextTrack, |
104 base::Unretained(this))); | 104 base::Unretained(this))); |
105 scoped_ptr<TextRenderer> text_renderer(text_renderer_); | 105 scoped_ptr<TextRenderer> text_renderer(text_renderer_); |
106 filter_collection_->SetTextRenderer(text_renderer.Pass()); | 106 filter_collection_->SetTextRenderer(text_renderer.Pass()); |
107 | 107 |
108 // InitializeDemuxer() adds overriding expectations for expected non-NULL | 108 // SetDemuxerExpectations() adds overriding expectations for expected |
109 // streams. | 109 // non-NULL streams. |
110 DemuxerStream* null_pointer = NULL; | 110 DemuxerStream* null_pointer = NULL; |
111 EXPECT_CALL(*demuxer_, GetStream(_)) | 111 EXPECT_CALL(*demuxer_, GetStream(_)) |
112 .WillRepeatedly(Return(null_pointer)); | 112 .WillRepeatedly(Return(null_pointer)); |
113 | 113 |
114 EXPECT_CALL(*demuxer_, GetTimelineOffset()) | 114 EXPECT_CALL(*demuxer_, GetTimelineOffset()) |
115 .WillRepeatedly(Return(base::Time())); | 115 .WillRepeatedly(Return(base::Time())); |
116 | 116 |
117 EXPECT_CALL(*demuxer_, GetLiveness()) | 117 EXPECT_CALL(*demuxer_, GetLiveness()) |
118 .WillRepeatedly(Return(Demuxer::LIVENESS_UNKNOWN)); | 118 .WillRepeatedly(Return(Demuxer::LIVENESS_UNKNOWN)); |
119 } | 119 } |
(...skipping 14 matching lines...) Expand all Loading... |
134 // Expect a stop callback if we were started. | 134 // Expect a stop callback if we were started. |
135 EXPECT_CALL(callbacks_, OnStop()); | 135 EXPECT_CALL(callbacks_, OnStop()); |
136 pipeline_->Stop(base::Bind(&CallbackHelper::OnStop, | 136 pipeline_->Stop(base::Bind(&CallbackHelper::OnStop, |
137 base::Unretained(&callbacks_))); | 137 base::Unretained(&callbacks_))); |
138 message_loop_.RunUntilIdle(); | 138 message_loop_.RunUntilIdle(); |
139 } | 139 } |
140 | 140 |
141 protected: | 141 protected: |
142 // Sets up expectations to allow the demuxer to initialize. | 142 // Sets up expectations to allow the demuxer to initialize. |
143 typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector; | 143 typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector; |
144 void InitializeDemuxer(MockDemuxerStreamVector* streams, | 144 void SetDemuxerExpectations(MockDemuxerStreamVector* streams, |
145 const base::TimeDelta& duration) { | 145 const base::TimeDelta& duration) { |
146 EXPECT_CALL(callbacks_, OnDurationChange()); | 146 EXPECT_CALL(callbacks_, OnDurationChange()); |
147 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | 147 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) |
148 .WillOnce(DoAll(SetDemuxerProperties(duration), | 148 .WillOnce(DoAll(SetDemuxerProperties(duration), |
149 RunCallback<1>(PIPELINE_OK))); | 149 RunCallback<1>(PIPELINE_OK))); |
150 | 150 |
151 // Configure the demuxer to return the streams. | 151 // Configure the demuxer to return the streams. |
152 for (size_t i = 0; i < streams->size(); ++i) { | 152 for (size_t i = 0; i < streams->size(); ++i) { |
153 DemuxerStream* stream = (*streams)[i]; | 153 DemuxerStream* stream = (*streams)[i]; |
154 EXPECT_CALL(*demuxer_, GetStream(stream->type())) | 154 EXPECT_CALL(*demuxer_, GetStream(stream->type())) |
155 .WillRepeatedly(Return(stream)); | 155 .WillRepeatedly(Return(stream)); |
156 } | 156 } |
157 } | 157 } |
158 | 158 |
159 void InitializeDemuxer(MockDemuxerStreamVector* streams) { | 159 void SetDemuxerExpectations(MockDemuxerStreamVector* streams) { |
160 // Initialize with a default non-zero duration. | 160 // Initialize with a default non-zero duration. |
161 InitializeDemuxer(streams, base::TimeDelta::FromSeconds(10)); | 161 SetDemuxerExpectations(streams, base::TimeDelta::FromSeconds(10)); |
162 } | 162 } |
163 | 163 |
164 scoped_ptr<StrictMock<MockDemuxerStream> > CreateStream( | 164 scoped_ptr<StrictMock<MockDemuxerStream> > CreateStream( |
165 DemuxerStream::Type type) { | 165 DemuxerStream::Type type) { |
166 scoped_ptr<StrictMock<MockDemuxerStream> > stream( | 166 scoped_ptr<StrictMock<MockDemuxerStream> > stream( |
167 new StrictMock<MockDemuxerStream>(type)); | 167 new StrictMock<MockDemuxerStream>(type)); |
168 return stream.Pass(); | 168 return stream.Pass(); |
169 } | 169 } |
170 | 170 |
171 // Sets up expectations to allow the video renderer to initialize. | 171 // Sets up expectations to allow the video renderer to initialize. |
172 void InitializeVideoRenderer(DemuxerStream* stream) { | 172 void SetVideoRendererExpectations(DemuxerStream* stream) { |
173 EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _, _)) | 173 EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _, _)) |
174 .WillOnce(DoAll(SaveArg<5>(&video_buffering_state_cb_), | 174 .WillOnce(DoAll(SaveArg<5>(&video_buffering_state_cb_), |
| 175 SaveArg<6>(&video_ended_cb_), |
175 RunCallback<2>(PIPELINE_OK))); | 176 RunCallback<2>(PIPELINE_OK))); |
176 } | 177 } |
177 | 178 |
178 // Sets up expectations to allow the audio renderer to initialize. | 179 // Sets up expectations to allow the audio renderer to initialize. |
179 void InitializeAudioRenderer(DemuxerStream* stream) { | 180 void SetAudioRendererExpectations(DemuxerStream* stream) { |
180 EXPECT_CALL(*audio_renderer_, Initialize(stream, _, _, _, _, _, _)) | 181 EXPECT_CALL(*audio_renderer_, Initialize(stream, _, _, _, _, _, _)) |
181 .WillOnce(DoAll(SaveArg<3>(&audio_time_cb_), | 182 .WillOnce(DoAll(SaveArg<3>(&audio_time_cb_), |
182 SaveArg<4>(&audio_buffering_state_cb_), | 183 SaveArg<4>(&audio_buffering_state_cb_), |
| 184 SaveArg<5>(&audio_ended_cb_), |
183 RunCallback<1>(PIPELINE_OK))); | 185 RunCallback<1>(PIPELINE_OK))); |
184 } | 186 } |
185 | 187 |
186 void AddTextStream() { | 188 void AddTextStream() { |
187 EXPECT_CALL(*this, OnAddTextTrack(_,_)) | 189 EXPECT_CALL(*this, OnAddTextTrack(_,_)) |
188 .WillOnce(Invoke(this, &PipelineTest::DoOnAddTextTrack)); | 190 .WillOnce(Invoke(this, &PipelineTest::DoOnAddTextTrack)); |
189 static_cast<DemuxerHost*>(pipeline_.get())->AddTextStream(text_stream(), | 191 static_cast<DemuxerHost*>(pipeline_.get())->AddTextStream(text_stream(), |
190 TextTrackConfig(kTextSubtitles, "", "", "")); | 192 TextTrackConfig(kTextSubtitles, "", "", "")); |
191 } | 193 } |
192 | 194 |
193 // Sets up expectations on the callback and initializes the pipeline. Called | 195 // Sets up expectations on the callback and initializes the pipeline. Called |
194 // after tests have set expectations any filters they wish to use. | 196 // after tests have set expectations any filters they wish to use. |
195 void InitializePipeline(PipelineStatus start_status) { | 197 void StartPipeline(PipelineStatus start_status) { |
196 EXPECT_CALL(callbacks_, OnStart(start_status)); | 198 EXPECT_CALL(callbacks_, OnStart(start_status)); |
197 | 199 |
198 if (start_status == PIPELINE_OK) { | 200 if (start_status == PIPELINE_OK) { |
199 EXPECT_CALL(callbacks_, OnMetadata(_)).WillOnce(SaveArg<0>(&metadata_)); | 201 EXPECT_CALL(callbacks_, OnMetadata(_)).WillOnce(SaveArg<0>(&metadata_)); |
200 | 202 |
201 if (audio_stream_) { | 203 if (audio_stream_) { |
202 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f)); | 204 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f)); |
203 EXPECT_CALL(*audio_renderer_, SetVolume(1.0f)); | 205 EXPECT_CALL(*audio_renderer_, SetVolume(1.0f)); |
204 EXPECT_CALL(*audio_renderer_, StartPlayingFrom(base::TimeDelta())) | 206 EXPECT_CALL(*audio_renderer_, StartPlayingFrom(base::TimeDelta())) |
205 .WillOnce(SetBufferingState(&audio_buffering_state_cb_, | 207 .WillOnce(SetBufferingState(&audio_buffering_state_cb_, |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 MockVideoRenderer* video_renderer_; | 337 MockVideoRenderer* video_renderer_; |
336 MockAudioRenderer* audio_renderer_; | 338 MockAudioRenderer* audio_renderer_; |
337 StrictMock<CallbackHelper> text_renderer_callbacks_; | 339 StrictMock<CallbackHelper> text_renderer_callbacks_; |
338 TextRenderer* text_renderer_; | 340 TextRenderer* text_renderer_; |
339 scoped_ptr<StrictMock<MockDemuxerStream> > audio_stream_; | 341 scoped_ptr<StrictMock<MockDemuxerStream> > audio_stream_; |
340 scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_; | 342 scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_; |
341 scoped_ptr<FakeTextTrackStream> text_stream_; | 343 scoped_ptr<FakeTextTrackStream> text_stream_; |
342 AudioRenderer::TimeCB audio_time_cb_; | 344 AudioRenderer::TimeCB audio_time_cb_; |
343 BufferingStateCB audio_buffering_state_cb_; | 345 BufferingStateCB audio_buffering_state_cb_; |
344 BufferingStateCB video_buffering_state_cb_; | 346 BufferingStateCB video_buffering_state_cb_; |
| 347 base::Closure audio_ended_cb_; |
| 348 base::Closure video_ended_cb_; |
345 VideoDecoderConfig video_decoder_config_; | 349 VideoDecoderConfig video_decoder_config_; |
346 PipelineMetadata metadata_; | 350 PipelineMetadata metadata_; |
347 | 351 |
348 private: | 352 private: |
349 DISALLOW_COPY_AND_ASSIGN(PipelineTest); | 353 DISALLOW_COPY_AND_ASSIGN(PipelineTest); |
350 }; | 354 }; |
351 | 355 |
352 // Test that playback controls methods no-op when the pipeline hasn't been | 356 // Test that playback controls methods no-op when the pipeline hasn't been |
353 // started. | 357 // started. |
354 TEST_F(PipelineTest, NotStarted) { | 358 TEST_F(PipelineTest, NotStarted) { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 Mock::VerifyAndClear(&callbacks_); | 405 Mock::VerifyAndClear(&callbacks_); |
402 EXPECT_CALL(callbacks_, OnStart(PIPELINE_OK)); | 406 EXPECT_CALL(callbacks_, OnStart(PIPELINE_OK)); |
403 } | 407 } |
404 | 408 |
405 TEST_F(PipelineTest, URLNotFound) { | 409 TEST_F(PipelineTest, URLNotFound) { |
406 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | 410 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) |
407 .WillOnce(RunCallback<1>(PIPELINE_ERROR_URL_NOT_FOUND)); | 411 .WillOnce(RunCallback<1>(PIPELINE_ERROR_URL_NOT_FOUND)); |
408 EXPECT_CALL(*demuxer_, Stop(_)) | 412 EXPECT_CALL(*demuxer_, Stop(_)) |
409 .WillOnce(RunClosure<0>()); | 413 .WillOnce(RunClosure<0>()); |
410 | 414 |
411 InitializePipeline(PIPELINE_ERROR_URL_NOT_FOUND); | 415 StartPipeline(PIPELINE_ERROR_URL_NOT_FOUND); |
412 } | 416 } |
413 | 417 |
414 TEST_F(PipelineTest, NoStreams) { | 418 TEST_F(PipelineTest, NoStreams) { |
415 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | 419 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) |
416 .WillOnce(RunCallback<1>(PIPELINE_OK)); | 420 .WillOnce(RunCallback<1>(PIPELINE_OK)); |
417 EXPECT_CALL(*demuxer_, Stop(_)) | 421 EXPECT_CALL(*demuxer_, Stop(_)) |
418 .WillOnce(RunClosure<0>()); | 422 .WillOnce(RunClosure<0>()); |
419 | 423 |
420 InitializePipeline(PIPELINE_ERROR_COULD_NOT_RENDER); | 424 StartPipeline(PIPELINE_ERROR_COULD_NOT_RENDER); |
421 } | 425 } |
422 | 426 |
423 TEST_F(PipelineTest, AudioStream) { | 427 TEST_F(PipelineTest, AudioStream) { |
424 CreateAudioStream(); | 428 CreateAudioStream(); |
425 MockDemuxerStreamVector streams; | 429 MockDemuxerStreamVector streams; |
426 streams.push_back(audio_stream()); | 430 streams.push_back(audio_stream()); |
427 | 431 |
428 InitializeDemuxer(&streams); | 432 SetDemuxerExpectations(&streams); |
429 InitializeAudioRenderer(audio_stream()); | 433 SetAudioRendererExpectations(audio_stream()); |
430 | 434 |
431 InitializePipeline(PIPELINE_OK); | 435 StartPipeline(PIPELINE_OK); |
432 EXPECT_TRUE(metadata_.has_audio); | 436 EXPECT_TRUE(metadata_.has_audio); |
433 EXPECT_FALSE(metadata_.has_video); | 437 EXPECT_FALSE(metadata_.has_video); |
434 } | 438 } |
435 | 439 |
436 TEST_F(PipelineTest, VideoStream) { | 440 TEST_F(PipelineTest, VideoStream) { |
437 CreateVideoStream(); | 441 CreateVideoStream(); |
438 MockDemuxerStreamVector streams; | 442 MockDemuxerStreamVector streams; |
439 streams.push_back(video_stream()); | 443 streams.push_back(video_stream()); |
440 | 444 |
441 InitializeDemuxer(&streams); | 445 SetDemuxerExpectations(&streams); |
442 InitializeVideoRenderer(video_stream()); | 446 SetVideoRendererExpectations(video_stream()); |
443 | 447 |
444 InitializePipeline(PIPELINE_OK); | 448 StartPipeline(PIPELINE_OK); |
445 EXPECT_FALSE(metadata_.has_audio); | 449 EXPECT_FALSE(metadata_.has_audio); |
446 EXPECT_TRUE(metadata_.has_video); | 450 EXPECT_TRUE(metadata_.has_video); |
447 } | 451 } |
448 | 452 |
449 TEST_F(PipelineTest, AudioVideoStream) { | 453 TEST_F(PipelineTest, AudioVideoStream) { |
450 CreateAudioStream(); | 454 CreateAudioStream(); |
451 CreateVideoStream(); | 455 CreateVideoStream(); |
452 MockDemuxerStreamVector streams; | 456 MockDemuxerStreamVector streams; |
453 streams.push_back(audio_stream()); | 457 streams.push_back(audio_stream()); |
454 streams.push_back(video_stream()); | 458 streams.push_back(video_stream()); |
455 | 459 |
456 InitializeDemuxer(&streams); | 460 SetDemuxerExpectations(&streams); |
457 InitializeAudioRenderer(audio_stream()); | 461 SetAudioRendererExpectations(audio_stream()); |
458 InitializeVideoRenderer(video_stream()); | 462 SetVideoRendererExpectations(video_stream()); |
459 | 463 |
460 InitializePipeline(PIPELINE_OK); | 464 StartPipeline(PIPELINE_OK); |
461 EXPECT_TRUE(metadata_.has_audio); | 465 EXPECT_TRUE(metadata_.has_audio); |
462 EXPECT_TRUE(metadata_.has_video); | 466 EXPECT_TRUE(metadata_.has_video); |
463 } | 467 } |
464 | 468 |
465 TEST_F(PipelineTest, VideoTextStream) { | 469 TEST_F(PipelineTest, VideoTextStream) { |
466 CreateVideoStream(); | 470 CreateVideoStream(); |
467 CreateTextStream(); | 471 CreateTextStream(); |
468 MockDemuxerStreamVector streams; | 472 MockDemuxerStreamVector streams; |
469 streams.push_back(video_stream()); | 473 streams.push_back(video_stream()); |
470 | 474 |
471 InitializeDemuxer(&streams); | 475 SetDemuxerExpectations(&streams); |
472 InitializeVideoRenderer(video_stream()); | 476 SetVideoRendererExpectations(video_stream()); |
473 | 477 |
474 InitializePipeline(PIPELINE_OK); | 478 StartPipeline(PIPELINE_OK); |
475 EXPECT_FALSE(metadata_.has_audio); | 479 EXPECT_FALSE(metadata_.has_audio); |
476 EXPECT_TRUE(metadata_.has_video); | 480 EXPECT_TRUE(metadata_.has_video); |
477 | 481 |
478 AddTextStream(); | 482 AddTextStream(); |
479 message_loop_.RunUntilIdle(); | 483 message_loop_.RunUntilIdle(); |
480 } | 484 } |
481 | 485 |
482 TEST_F(PipelineTest, VideoAudioTextStream) { | 486 TEST_F(PipelineTest, VideoAudioTextStream) { |
483 CreateVideoStream(); | 487 CreateVideoStream(); |
484 CreateAudioStream(); | 488 CreateAudioStream(); |
485 CreateTextStream(); | 489 CreateTextStream(); |
486 MockDemuxerStreamVector streams; | 490 MockDemuxerStreamVector streams; |
487 streams.push_back(video_stream()); | 491 streams.push_back(video_stream()); |
488 streams.push_back(audio_stream()); | 492 streams.push_back(audio_stream()); |
489 | 493 |
490 InitializeDemuxer(&streams); | 494 SetDemuxerExpectations(&streams); |
491 InitializeVideoRenderer(video_stream()); | 495 SetVideoRendererExpectations(video_stream()); |
492 InitializeAudioRenderer(audio_stream()); | 496 SetAudioRendererExpectations(audio_stream()); |
493 | 497 |
494 InitializePipeline(PIPELINE_OK); | 498 StartPipeline(PIPELINE_OK); |
495 EXPECT_TRUE(metadata_.has_audio); | 499 EXPECT_TRUE(metadata_.has_audio); |
496 EXPECT_TRUE(metadata_.has_video); | 500 EXPECT_TRUE(metadata_.has_video); |
497 | 501 |
498 AddTextStream(); | 502 AddTextStream(); |
499 message_loop_.RunUntilIdle(); | 503 message_loop_.RunUntilIdle(); |
500 } | 504 } |
501 | 505 |
502 TEST_F(PipelineTest, Seek) { | 506 TEST_F(PipelineTest, Seek) { |
503 CreateAudioStream(); | 507 CreateAudioStream(); |
504 CreateVideoStream(); | 508 CreateVideoStream(); |
505 CreateTextStream(); | 509 CreateTextStream(); |
506 MockDemuxerStreamVector streams; | 510 MockDemuxerStreamVector streams; |
507 streams.push_back(audio_stream()); | 511 streams.push_back(audio_stream()); |
508 streams.push_back(video_stream()); | 512 streams.push_back(video_stream()); |
509 | 513 |
510 InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000)); | 514 SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000)); |
511 InitializeAudioRenderer(audio_stream()); | 515 SetAudioRendererExpectations(audio_stream()); |
512 InitializeVideoRenderer(video_stream()); | 516 SetVideoRendererExpectations(video_stream()); |
513 | 517 |
514 // Initialize then seek! | 518 // Initialize then seek! |
515 InitializePipeline(PIPELINE_OK); | 519 StartPipeline(PIPELINE_OK); |
516 | 520 |
517 message_loop_.RunUntilIdle(); | 521 message_loop_.RunUntilIdle(); |
518 | 522 |
519 // Every filter should receive a call to Seek(). | 523 // Every filter should receive a call to Seek(). |
520 base::TimeDelta expected = base::TimeDelta::FromSeconds(2000); | 524 base::TimeDelta expected = base::TimeDelta::FromSeconds(2000); |
521 ExpectSeek(expected, false); | 525 ExpectSeek(expected, false); |
522 DoSeek(expected); | 526 DoSeek(expected); |
523 } | 527 } |
524 | 528 |
525 TEST_F(PipelineTest, SetVolume) { | 529 TEST_F(PipelineTest, SetVolume) { |
526 CreateAudioStream(); | 530 CreateAudioStream(); |
527 MockDemuxerStreamVector streams; | 531 MockDemuxerStreamVector streams; |
528 streams.push_back(audio_stream()); | 532 streams.push_back(audio_stream()); |
529 | 533 |
530 InitializeDemuxer(&streams); | 534 SetDemuxerExpectations(&streams); |
531 InitializeAudioRenderer(audio_stream()); | 535 SetAudioRendererExpectations(audio_stream()); |
532 | 536 |
533 // The audio renderer should receive a call to SetVolume(). | 537 // The audio renderer should receive a call to SetVolume(). |
534 float expected = 0.5f; | 538 float expected = 0.5f; |
535 EXPECT_CALL(*audio_renderer_, SetVolume(expected)); | 539 EXPECT_CALL(*audio_renderer_, SetVolume(expected)); |
536 | 540 |
537 // Initialize then set volume! | 541 // Initialize then set volume! |
538 InitializePipeline(PIPELINE_OK); | 542 StartPipeline(PIPELINE_OK); |
539 pipeline_->SetVolume(expected); | 543 pipeline_->SetVolume(expected); |
540 } | 544 } |
541 | 545 |
542 TEST_F(PipelineTest, Properties) { | 546 TEST_F(PipelineTest, Properties) { |
543 CreateVideoStream(); | 547 CreateVideoStream(); |
544 MockDemuxerStreamVector streams; | 548 MockDemuxerStreamVector streams; |
545 streams.push_back(video_stream()); | 549 streams.push_back(video_stream()); |
546 | 550 |
547 const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100); | 551 const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100); |
548 InitializeDemuxer(&streams, kDuration); | 552 SetDemuxerExpectations(&streams, kDuration); |
549 InitializeVideoRenderer(video_stream()); | 553 SetVideoRendererExpectations(video_stream()); |
550 | 554 |
551 InitializePipeline(PIPELINE_OK); | 555 StartPipeline(PIPELINE_OK); |
552 EXPECT_EQ(kDuration.ToInternalValue(), | 556 EXPECT_EQ(kDuration.ToInternalValue(), |
553 pipeline_->GetMediaDuration().ToInternalValue()); | 557 pipeline_->GetMediaDuration().ToInternalValue()); |
554 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | 558 EXPECT_FALSE(pipeline_->DidLoadingProgress()); |
555 } | 559 } |
556 | 560 |
557 TEST_F(PipelineTest, GetBufferedTimeRanges) { | 561 TEST_F(PipelineTest, GetBufferedTimeRanges) { |
558 CreateVideoStream(); | 562 CreateVideoStream(); |
559 MockDemuxerStreamVector streams; | 563 MockDemuxerStreamVector streams; |
560 streams.push_back(video_stream()); | 564 streams.push_back(video_stream()); |
561 | 565 |
562 const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100); | 566 const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100); |
563 InitializeDemuxer(&streams, kDuration); | 567 SetDemuxerExpectations(&streams, kDuration); |
564 InitializeVideoRenderer(video_stream()); | 568 SetVideoRendererExpectations(video_stream()); |
565 | 569 |
566 InitializePipeline(PIPELINE_OK); | 570 StartPipeline(PIPELINE_OK); |
567 | 571 |
568 EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size()); | 572 EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size()); |
569 | 573 |
570 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | 574 EXPECT_FALSE(pipeline_->DidLoadingProgress()); |
571 pipeline_->AddBufferedTimeRange(base::TimeDelta(), kDuration / 8); | 575 pipeline_->AddBufferedTimeRange(base::TimeDelta(), kDuration / 8); |
572 EXPECT_TRUE(pipeline_->DidLoadingProgress()); | 576 EXPECT_TRUE(pipeline_->DidLoadingProgress()); |
573 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | 577 EXPECT_FALSE(pipeline_->DidLoadingProgress()); |
574 EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); | 578 EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); |
575 EXPECT_EQ(base::TimeDelta(), pipeline_->GetBufferedTimeRanges().start(0)); | 579 EXPECT_EQ(base::TimeDelta(), pipeline_->GetBufferedTimeRanges().start(0)); |
576 EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0)); | 580 EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0)); |
577 | 581 |
578 base::TimeDelta kSeekTime = kDuration / 2; | 582 base::TimeDelta kSeekTime = kDuration / 2; |
579 ExpectSeek(kSeekTime, false); | 583 ExpectSeek(kSeekTime, false); |
580 DoSeek(kSeekTime); | 584 DoSeek(kSeekTime); |
581 | 585 |
582 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | 586 EXPECT_FALSE(pipeline_->DidLoadingProgress()); |
583 } | 587 } |
584 | 588 |
585 TEST_F(PipelineTest, EndedCallback) { | 589 TEST_F(PipelineTest, EndedCallback) { |
586 CreateAudioStream(); | 590 CreateAudioStream(); |
587 CreateVideoStream(); | 591 CreateVideoStream(); |
588 CreateTextStream(); | 592 CreateTextStream(); |
589 MockDemuxerStreamVector streams; | 593 MockDemuxerStreamVector streams; |
590 streams.push_back(audio_stream()); | 594 streams.push_back(audio_stream()); |
591 streams.push_back(video_stream()); | 595 streams.push_back(video_stream()); |
592 | 596 |
593 InitializeDemuxer(&streams); | 597 SetDemuxerExpectations(&streams); |
594 InitializeAudioRenderer(audio_stream()); | 598 SetAudioRendererExpectations(audio_stream()); |
595 InitializeVideoRenderer(video_stream()); | 599 SetVideoRendererExpectations(video_stream()); |
596 InitializePipeline(PIPELINE_OK); | 600 StartPipeline(PIPELINE_OK); |
597 | 601 |
598 AddTextStream(); | 602 AddTextStream(); |
599 | 603 |
600 // The ended callback shouldn't run until all renderers have ended. | 604 // The ended callback shouldn't run until all renderers have ended. |
601 pipeline_->OnAudioRendererEnded(); | 605 audio_ended_cb_.Run(); |
602 message_loop_.RunUntilIdle(); | 606 message_loop_.RunUntilIdle(); |
603 | 607 |
604 pipeline_->OnVideoRendererEnded(); | 608 video_ended_cb_.Run(); |
605 message_loop_.RunUntilIdle(); | 609 message_loop_.RunUntilIdle(); |
606 | 610 |
607 EXPECT_CALL(*audio_renderer_, StopRendering()); | 611 EXPECT_CALL(*audio_renderer_, StopRendering()); |
608 EXPECT_CALL(callbacks_, OnEnded()); | 612 EXPECT_CALL(callbacks_, OnEnded()); |
609 text_stream()->SendEosNotification(); | 613 text_stream()->SendEosNotification(); |
610 message_loop_.RunUntilIdle(); | 614 message_loop_.RunUntilIdle(); |
611 } | 615 } |
612 | 616 |
613 TEST_F(PipelineTest, AudioStreamShorterThanVideo) { | 617 TEST_F(PipelineTest, AudioStreamShorterThanVideo) { |
614 base::TimeDelta duration = base::TimeDelta::FromSeconds(10); | 618 base::TimeDelta duration = base::TimeDelta::FromSeconds(10); |
615 | 619 |
616 CreateAudioStream(); | 620 CreateAudioStream(); |
617 CreateVideoStream(); | 621 CreateVideoStream(); |
618 MockDemuxerStreamVector streams; | 622 MockDemuxerStreamVector streams; |
619 streams.push_back(audio_stream()); | 623 streams.push_back(audio_stream()); |
620 streams.push_back(video_stream()); | 624 streams.push_back(video_stream()); |
621 | 625 |
622 // Replace what's used for interpolating to simulate wall clock time. | 626 // Replace what's used for interpolating to simulate wall clock time. |
623 pipeline_->SetTimeDeltaInterpolatorForTesting( | 627 pipeline_->SetTimeDeltaInterpolatorForTesting( |
624 new TimeDeltaInterpolator(&test_tick_clock_)); | 628 new TimeDeltaInterpolator(&test_tick_clock_)); |
625 | 629 |
626 InitializeDemuxer(&streams, duration); | 630 SetDemuxerExpectations(&streams, duration); |
627 InitializeAudioRenderer(audio_stream()); | 631 SetAudioRendererExpectations(audio_stream()); |
628 InitializeVideoRenderer(video_stream()); | 632 SetVideoRendererExpectations(video_stream()); |
629 InitializePipeline(PIPELINE_OK); | 633 StartPipeline(PIPELINE_OK); |
630 | 634 |
631 EXPECT_EQ(0, pipeline_->GetMediaTime().ToInternalValue()); | 635 EXPECT_EQ(0, pipeline_->GetMediaTime().ToInternalValue()); |
632 | 636 |
633 float playback_rate = 1.0f; | 637 float playback_rate = 1.0f; |
634 EXPECT_CALL(*video_renderer_, SetPlaybackRate(playback_rate)); | 638 EXPECT_CALL(*video_renderer_, SetPlaybackRate(playback_rate)); |
635 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate)); | 639 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate)); |
636 pipeline_->SetPlaybackRate(playback_rate); | 640 pipeline_->SetPlaybackRate(playback_rate); |
637 message_loop_.RunUntilIdle(); | 641 message_loop_.RunUntilIdle(); |
638 | 642 |
639 InSequence s; | 643 InSequence s; |
640 | 644 |
641 // Verify that the clock doesn't advance since it hasn't been started by | 645 // Verify that the clock doesn't advance since it hasn't been started by |
642 // a time update from the audio stream. | 646 // a time update from the audio stream. |
643 int64 start_time = pipeline_->GetMediaTime().ToInternalValue(); | 647 int64 start_time = pipeline_->GetMediaTime().ToInternalValue(); |
644 test_tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100)); | 648 test_tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100)); |
645 EXPECT_EQ(pipeline_->GetMediaTime().ToInternalValue(), start_time); | 649 EXPECT_EQ(pipeline_->GetMediaTime().ToInternalValue(), start_time); |
646 | 650 |
647 // Signal end of audio stream. | 651 // Signal end of audio stream. |
648 pipeline_->OnAudioRendererEnded(); | 652 audio_ended_cb_.Run(); |
649 message_loop_.RunUntilIdle(); | 653 message_loop_.RunUntilIdle(); |
650 | 654 |
651 // Verify that the clock advances. | 655 // Verify that the clock advances. |
652 start_time = pipeline_->GetMediaTime().ToInternalValue(); | 656 start_time = pipeline_->GetMediaTime().ToInternalValue(); |
653 test_tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100)); | 657 test_tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100)); |
654 EXPECT_GT(pipeline_->GetMediaTime().ToInternalValue(), start_time); | 658 EXPECT_GT(pipeline_->GetMediaTime().ToInternalValue(), start_time); |
655 | 659 |
656 // Signal end of video stream and make sure OnEnded() callback occurs. | 660 // Signal end of video stream and make sure OnEnded() callback occurs. |
657 EXPECT_CALL(*audio_renderer_, StopRendering()); | 661 EXPECT_CALL(*audio_renderer_, StopRendering()); |
658 EXPECT_CALL(callbacks_, OnEnded()); | 662 EXPECT_CALL(callbacks_, OnEnded()); |
659 pipeline_->OnVideoRendererEnded(); | 663 video_ended_cb_.Run(); |
660 } | 664 } |
661 | 665 |
662 TEST_F(PipelineTest, ErrorDuringSeek) { | 666 TEST_F(PipelineTest, ErrorDuringSeek) { |
663 CreateAudioStream(); | 667 CreateAudioStream(); |
664 MockDemuxerStreamVector streams; | 668 MockDemuxerStreamVector streams; |
665 streams.push_back(audio_stream()); | 669 streams.push_back(audio_stream()); |
666 | 670 |
667 InitializeDemuxer(&streams); | 671 SetDemuxerExpectations(&streams); |
668 InitializeAudioRenderer(audio_stream()); | 672 SetAudioRendererExpectations(audio_stream()); |
669 InitializePipeline(PIPELINE_OK); | 673 StartPipeline(PIPELINE_OK); |
670 | 674 |
671 float playback_rate = 1.0f; | 675 float playback_rate = 1.0f; |
672 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate)); | 676 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate)); |
673 pipeline_->SetPlaybackRate(playback_rate); | 677 pipeline_->SetPlaybackRate(playback_rate); |
674 message_loop_.RunUntilIdle(); | 678 message_loop_.RunUntilIdle(); |
675 | 679 |
676 base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); | 680 base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); |
677 | 681 |
678 // Preroll() isn't called as the demuxer errors out first. | 682 // Preroll() isn't called as the demuxer errors out first. |
679 EXPECT_CALL(*audio_renderer_, StopRendering()); | 683 EXPECT_CALL(*audio_renderer_, StopRendering()); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
712 | 716 |
713 // No additional tasks should be queued as a result of these calls. | 717 // No additional tasks should be queued as a result of these calls. |
714 EXPECT_TRUE(message_loop->IsIdleForTesting()); | 718 EXPECT_TRUE(message_loop->IsIdleForTesting()); |
715 } | 719 } |
716 | 720 |
717 TEST_F(PipelineTest, NoMessageDuringTearDownFromError) { | 721 TEST_F(PipelineTest, NoMessageDuringTearDownFromError) { |
718 CreateAudioStream(); | 722 CreateAudioStream(); |
719 MockDemuxerStreamVector streams; | 723 MockDemuxerStreamVector streams; |
720 streams.push_back(audio_stream()); | 724 streams.push_back(audio_stream()); |
721 | 725 |
722 InitializeDemuxer(&streams); | 726 SetDemuxerExpectations(&streams); |
723 InitializeAudioRenderer(audio_stream()); | 727 SetAudioRendererExpectations(audio_stream()); |
724 InitializePipeline(PIPELINE_OK); | 728 StartPipeline(PIPELINE_OK); |
725 | 729 |
726 // Trigger additional requests on the pipeline during tear down from error. | 730 // Trigger additional requests on the pipeline during tear down from error. |
727 base::Callback<void(PipelineStatus)> cb = base::Bind( | 731 base::Callback<void(PipelineStatus)> cb = base::Bind( |
728 &TestNoCallsAfterError, pipeline_.get(), &message_loop_); | 732 &TestNoCallsAfterError, pipeline_.get(), &message_loop_); |
729 ON_CALL(callbacks_, OnError(_)) | 733 ON_CALL(callbacks_, OnError(_)) |
730 .WillByDefault(Invoke(&cb, &base::Callback<void(PipelineStatus)>::Run)); | 734 .WillByDefault(Invoke(&cb, &base::Callback<void(PipelineStatus)>::Run)); |
731 | 735 |
732 base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); | 736 base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); |
733 | 737 |
734 // Seek() isn't called as the demuxer errors out first. | 738 // Seek() isn't called as the demuxer errors out first. |
(...skipping 21 matching lines...) Expand all Loading... |
756 int max_time_in_ms) { | 760 int max_time_in_ms) { |
757 time_cb.Run(base::TimeDelta::FromMilliseconds(time_in_ms), | 761 time_cb.Run(base::TimeDelta::FromMilliseconds(time_in_ms), |
758 base::TimeDelta::FromMilliseconds(max_time_in_ms)); | 762 base::TimeDelta::FromMilliseconds(max_time_in_ms)); |
759 } | 763 } |
760 | 764 |
761 TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) { | 765 TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) { |
762 CreateAudioStream(); | 766 CreateAudioStream(); |
763 MockDemuxerStreamVector streams; | 767 MockDemuxerStreamVector streams; |
764 streams.push_back(audio_stream()); | 768 streams.push_back(audio_stream()); |
765 | 769 |
766 InitializeDemuxer(&streams); | 770 SetDemuxerExpectations(&streams); |
767 InitializeAudioRenderer(audio_stream()); | 771 SetAudioRendererExpectations(audio_stream()); |
768 InitializePipeline(PIPELINE_OK); | 772 StartPipeline(PIPELINE_OK); |
769 | 773 |
770 float playback_rate = 1.0f; | 774 float playback_rate = 1.0f; |
771 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate)); | 775 EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate)); |
772 pipeline_->SetPlaybackRate(playback_rate); | 776 pipeline_->SetPlaybackRate(playback_rate); |
773 message_loop_.RunUntilIdle(); | 777 message_loop_.RunUntilIdle(); |
774 | 778 |
775 // Provide an initial time update so that the pipeline transitions out of the | 779 // Provide an initial time update so that the pipeline transitions out of the |
776 // "waiting for time update" state. | 780 // "waiting for time update" state. |
777 audio_time_cb_.Run(base::TimeDelta::FromMilliseconds(100), | 781 audio_time_cb_.Run(base::TimeDelta::FromMilliseconds(100), |
778 base::TimeDelta::FromMilliseconds(500)); | 782 base::TimeDelta::FromMilliseconds(500)); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
814 } | 818 } |
815 | 819 |
816 static void DeletePipeline(scoped_ptr<Pipeline> pipeline) { | 820 static void DeletePipeline(scoped_ptr<Pipeline> pipeline) { |
817 // |pipeline| will go out of scope. | 821 // |pipeline| will go out of scope. |
818 } | 822 } |
819 | 823 |
820 TEST_F(PipelineTest, DeleteAfterStop) { | 824 TEST_F(PipelineTest, DeleteAfterStop) { |
821 CreateAudioStream(); | 825 CreateAudioStream(); |
822 MockDemuxerStreamVector streams; | 826 MockDemuxerStreamVector streams; |
823 streams.push_back(audio_stream()); | 827 streams.push_back(audio_stream()); |
824 InitializeDemuxer(&streams); | 828 SetDemuxerExpectations(&streams); |
825 InitializeAudioRenderer(audio_stream()); | 829 SetAudioRendererExpectations(audio_stream()); |
826 InitializePipeline(PIPELINE_OK); | 830 StartPipeline(PIPELINE_OK); |
827 | 831 |
828 ExpectStop(); | 832 ExpectStop(); |
829 | 833 |
830 Pipeline* pipeline = pipeline_.get(); | 834 Pipeline* pipeline = pipeline_.get(); |
831 pipeline->Stop(base::Bind(&DeletePipeline, base::Passed(&pipeline_))); | 835 pipeline->Stop(base::Bind(&DeletePipeline, base::Passed(&pipeline_))); |
832 message_loop_.RunUntilIdle(); | 836 message_loop_.RunUntilIdle(); |
833 } | 837 } |
834 | 838 |
835 TEST_F(PipelineTest, Underflow) { | 839 TEST_F(PipelineTest, Underflow) { |
836 CreateAudioStream(); | 840 CreateAudioStream(); |
837 CreateVideoStream(); | 841 CreateVideoStream(); |
838 MockDemuxerStreamVector streams; | 842 MockDemuxerStreamVector streams; |
839 streams.push_back(audio_stream()); | 843 streams.push_back(audio_stream()); |
840 streams.push_back(video_stream()); | 844 streams.push_back(video_stream()); |
841 | 845 |
842 InitializeDemuxer(&streams); | 846 SetDemuxerExpectations(&streams); |
843 InitializeAudioRenderer(audio_stream()); | 847 SetAudioRendererExpectations(audio_stream()); |
844 InitializeVideoRenderer(video_stream()); | 848 SetVideoRendererExpectations(video_stream()); |
845 InitializePipeline(PIPELINE_OK); | 849 StartPipeline(PIPELINE_OK); |
846 | 850 |
847 // Simulate underflow. | 851 // Simulate underflow. |
848 EXPECT_CALL(*audio_renderer_, StopRendering()); | 852 EXPECT_CALL(*audio_renderer_, StopRendering()); |
849 audio_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); | 853 audio_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); |
850 | 854 |
851 // Seek while underflowed. We shouldn't call StopRendering() again. | 855 // Seek while underflowed. We shouldn't call StopRendering() again. |
852 base::TimeDelta expected = base::TimeDelta::FromSeconds(5); | 856 base::TimeDelta expected = base::TimeDelta::FromSeconds(5); |
853 ExpectSeek(expected, true); | 857 ExpectSeek(expected, true); |
854 DoSeek(expected); | 858 DoSeek(expected); |
855 } | 859 } |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
937 | 941 |
938 EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>()); | 942 EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>()); |
939 return status; | 943 return status; |
940 } | 944 } |
941 | 945 |
942 CreateAudioStream(); | 946 CreateAudioStream(); |
943 CreateVideoStream(); | 947 CreateVideoStream(); |
944 MockDemuxerStreamVector streams; | 948 MockDemuxerStreamVector streams; |
945 streams.push_back(audio_stream()); | 949 streams.push_back(audio_stream()); |
946 streams.push_back(video_stream()); | 950 streams.push_back(video_stream()); |
947 InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000)); | 951 SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000)); |
948 | 952 |
949 if (state == kInitAudioRenderer) { | 953 if (state == kInitAudioRenderer) { |
950 if (stop_or_error == kStop) { | 954 if (stop_or_error == kStop) { |
951 EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _)) | 955 EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _)) |
952 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), | 956 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), |
953 RunCallback<1>(PIPELINE_OK))); | 957 RunCallback<1>(PIPELINE_OK))); |
954 EXPECT_CALL(callbacks_, OnStop()); | 958 EXPECT_CALL(callbacks_, OnStop()); |
955 } else { | 959 } else { |
956 status = PIPELINE_ERROR_INITIALIZATION_FAILED; | 960 status = PIPELINE_ERROR_INITIALIZATION_FAILED; |
957 EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _)) | 961 EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _)) |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1130 INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer); | 1134 INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer); |
1131 INSTANTIATE_TEARDOWN_TEST(Error, InitAudioRenderer); | 1135 INSTANTIATE_TEARDOWN_TEST(Error, InitAudioRenderer); |
1132 INSTANTIATE_TEARDOWN_TEST(Error, InitVideoRenderer); | 1136 INSTANTIATE_TEARDOWN_TEST(Error, InitVideoRenderer); |
1133 INSTANTIATE_TEARDOWN_TEST(Error, Flushing); | 1137 INSTANTIATE_TEARDOWN_TEST(Error, Flushing); |
1134 INSTANTIATE_TEARDOWN_TEST(Error, Seeking); | 1138 INSTANTIATE_TEARDOWN_TEST(Error, Seeking); |
1135 INSTANTIATE_TEARDOWN_TEST(Error, Playing); | 1139 INSTANTIATE_TEARDOWN_TEST(Error, Playing); |
1136 | 1140 |
1137 INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing); | 1141 INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing); |
1138 | 1142 |
1139 } // namespace media | 1143 } // namespace media |
OLD | NEW |