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/base/pipeline.h" | 5 #include "media/base/pipeline.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
407 metadata.natural_size = stream->video_decoder_config().natural_size(); | 407 metadata.natural_size = stream->video_decoder_config().natural_size(); |
408 metadata_cb_.Run(metadata); | 408 metadata_cb_.Run(metadata); |
409 } | 409 } |
410 | 410 |
411 return DoInitialPreroll(done_cb); | 411 return DoInitialPreroll(done_cb); |
412 | 412 |
413 case kPlaying: | 413 case kPlaying: |
414 PlaybackRateChangedTask(GetPlaybackRate()); | 414 PlaybackRateChangedTask(GetPlaybackRate()); |
415 VolumeChangedTask(GetVolume()); | 415 VolumeChangedTask(GetVolume()); |
416 | 416 |
| 417 // Handle renderers that immediately signal they have enough data. |
| 418 if (!WaitingForEnoughData()) |
| 419 StartPlayback(); |
| 420 |
417 // We enter this state from either kInitPrerolling or kSeeking. As of now | 421 // We enter this state from either kInitPrerolling or kSeeking. As of now |
418 // both those states call Preroll(), which means by time we enter this | 422 // both those states call Preroll(), which means by time we enter this |
419 // state we've already buffered enough data. Forcefully update the | 423 // state we've already buffered enough data. Forcefully update the |
420 // buffering state, which start the clock and renderers and transition | 424 // buffering state, which start the clock and renderers and transition |
421 // into kPlaying state. | 425 // into kPlaying state. |
422 // | 426 // |
423 // TODO(scherkus): Remove after renderers are taught to fire buffering | 427 // TODO(scherkus): Remove after renderers are taught to fire buffering |
424 // state callbacks http://crbug.com/144683 | 428 // state callbacks http://crbug.com/144683 |
425 DCHECK(WaitingForEnoughData()); | 429 if (video_renderer_) { |
426 if (audio_renderer_) | 430 DCHECK(WaitingForEnoughData()); |
427 BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH); | |
428 if (video_renderer_) | |
429 BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH); | 431 BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH); |
| 432 } |
430 return; | 433 return; |
431 | 434 |
432 case kStopping: | 435 case kStopping: |
433 case kStopped: | 436 case kStopped: |
434 case kCreated: | 437 case kCreated: |
435 case kSeeking: | 438 case kSeeking: |
436 NOTREACHED() << "State has no transition: " << state_; | 439 NOTREACHED() << "State has no transition: " << state_; |
437 return; | 440 return; |
438 } | 441 } |
439 } | 442 } |
440 | 443 |
441 // Note that the usage of base::Unretained() with the audio/video renderers | 444 // Note that the usage of base::Unretained() with the audio/video renderers |
442 // in the following DoXXX() functions is considered safe as they are owned by | 445 // in the following DoXXX() functions is considered safe as they are owned by |
443 // |pending_callbacks_| and share the same lifetime. | 446 // |pending_callbacks_| and share the same lifetime. |
444 // | 447 // |
445 // That being said, deleting the renderers while keeping |pending_callbacks_| | 448 // That being said, deleting the renderers while keeping |pending_callbacks_| |
446 // running on the media thread would result in crashes. | 449 // running on the media thread would result in crashes. |
447 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { | 450 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { |
448 DCHECK(task_runner_->BelongsToCurrentThread()); | 451 DCHECK(task_runner_->BelongsToCurrentThread()); |
449 DCHECK(!pending_callbacks_.get()); | 452 DCHECK(!pending_callbacks_.get()); |
450 SerialRunner::Queue bound_fns; | 453 SerialRunner::Queue bound_fns; |
451 | 454 |
452 base::TimeDelta seek_timestamp = demuxer_->GetStartTime(); | 455 base::TimeDelta seek_timestamp = demuxer_->GetStartTime(); |
453 | 456 |
454 // Preroll renderers. | 457 // Preroll renderers. |
455 if (audio_renderer_) { | 458 if (audio_renderer_) { |
456 bound_fns.Push(base::Bind( | 459 bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom, |
457 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), | 460 base::Unretained(audio_renderer_.get()), |
458 seek_timestamp)); | 461 seek_timestamp)); |
459 } | 462 } |
460 | 463 |
461 if (video_renderer_) { | 464 if (video_renderer_) { |
462 bound_fns.Push(base::Bind( | 465 bound_fns.Push(base::Bind( |
463 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), | 466 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), |
464 seek_timestamp)); | 467 seek_timestamp)); |
465 | 468 |
466 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering | 469 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering |
467 // state callbacks http://crbug.com/144683 | 470 // state callbacks http://crbug.com/144683 |
468 bound_fns.Push(base::Bind(&VideoRenderer::Play, | 471 bound_fns.Push(base::Bind(&VideoRenderer::Play, |
469 base::Unretained(video_renderer_.get()))); | 472 base::Unretained(video_renderer_.get()))); |
470 } | 473 } |
471 | 474 |
472 if (text_renderer_) { | 475 if (text_renderer_) { |
473 bound_fns.Push(base::Bind( | 476 bound_fns.Push(base::Bind( |
474 &TextRenderer::Play, base::Unretained(text_renderer_.get()))); | 477 &TextRenderer::Play, base::Unretained(text_renderer_.get()))); |
475 } | 478 } |
476 | 479 |
477 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 480 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
478 } | 481 } |
479 | 482 |
| 483 #if DCHECK_IS_ON |
| 484 static void VerifyBufferingStates(BufferingState* audio_buffering_state, |
| 485 BufferingState* video_buffering_state) { |
| 486 DCHECK_EQ(*audio_buffering_state, BUFFERING_HAVE_NOTHING); |
| 487 DCHECK_EQ(*video_buffering_state, BUFFERING_HAVE_NOTHING); |
| 488 } |
| 489 #endif |
| 490 |
480 void Pipeline::DoSeek( | 491 void Pipeline::DoSeek( |
481 base::TimeDelta seek_timestamp, | 492 base::TimeDelta seek_timestamp, |
482 const PipelineStatusCB& done_cb) { | 493 const PipelineStatusCB& done_cb) { |
483 DCHECK(task_runner_->BelongsToCurrentThread()); | 494 DCHECK(task_runner_->BelongsToCurrentThread()); |
484 DCHECK(!pending_callbacks_.get()); | 495 DCHECK(!pending_callbacks_.get()); |
485 SerialRunner::Queue bound_fns; | 496 SerialRunner::Queue bound_fns; |
486 | 497 |
487 // Pause. | 498 // Pause. |
488 if (text_renderer_) { | 499 if (text_renderer_) { |
489 bound_fns.Push(base::Bind( | 500 bound_fns.Push(base::Bind( |
490 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); | 501 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); |
491 } | 502 } |
492 | 503 |
493 // Flush. | 504 // Flush. |
494 if (audio_renderer_) { | 505 if (audio_renderer_) { |
495 bound_fns.Push(base::Bind( | 506 bound_fns.Push(base::Bind( |
496 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); | 507 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); |
| 508 } |
497 | 509 |
498 // TODO(scherkus): Remove after AudioRenderer is taught to fire buffering | |
499 // state callbacks http://crbug.com/144683 | |
500 bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged, | |
501 base::Unretained(this), | |
502 &audio_buffering_state_, | |
503 BUFFERING_HAVE_NOTHING)); | |
504 } | |
505 if (video_renderer_) { | 510 if (video_renderer_) { |
506 bound_fns.Push(base::Bind( | 511 bound_fns.Push(base::Bind( |
507 &VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); | 512 &VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); |
508 | 513 |
509 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering | 514 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering |
510 // state callbacks http://crbug.com/144683 | 515 // state callbacks http://crbug.com/144683 |
511 bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged, | 516 bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged, |
512 base::Unretained(this), | 517 base::Unretained(this), |
513 &video_buffering_state_, | 518 &video_buffering_state_, |
514 BUFFERING_HAVE_NOTHING)); | 519 BUFFERING_HAVE_NOTHING)); |
515 } | 520 } |
| 521 |
| 522 #if DCHECK_IS_ON |
| 523 // Verify renderers reset their buffering states. |
| 524 bound_fns.Push(base::Bind(&VerifyBufferingStates, |
| 525 &audio_buffering_state_, |
| 526 &video_buffering_state_)); |
| 527 #endif |
| 528 |
516 if (text_renderer_) { | 529 if (text_renderer_) { |
517 bound_fns.Push(base::Bind( | 530 bound_fns.Push(base::Bind( |
518 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); | 531 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); |
519 } | 532 } |
520 | 533 |
521 // Seek demuxer. | 534 // Seek demuxer. |
522 bound_fns.Push(base::Bind( | 535 bound_fns.Push(base::Bind( |
523 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | 536 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
524 | 537 |
525 // Preroll renderers. | 538 // Preroll renderers. |
526 if (audio_renderer_) { | 539 if (audio_renderer_) { |
527 bound_fns.Push(base::Bind( | 540 bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom, |
528 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), | 541 base::Unretained(audio_renderer_.get()), |
529 seek_timestamp)); | 542 seek_timestamp)); |
530 } | 543 } |
531 | 544 |
532 if (video_renderer_) { | 545 if (video_renderer_) { |
533 bound_fns.Push(base::Bind( | 546 bound_fns.Push(base::Bind( |
534 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), | 547 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), |
535 seek_timestamp)); | 548 seek_timestamp)); |
536 | 549 |
537 // TODO(scherkus): Remove after renderers are taught to fire buffering | 550 // TODO(scherkus): Remove after renderers are taught to fire buffering |
538 // state callbacks http://crbug.com/144683 | 551 // state callbacks http://crbug.com/144683 |
539 bound_fns.Push(base::Bind(&VideoRenderer::Play, | 552 bound_fns.Push(base::Bind(&VideoRenderer::Play, |
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
849 } | 862 } |
850 | 863 |
851 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { | 864 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { |
852 DCHECK(task_runner_->BelongsToCurrentThread()); | 865 DCHECK(task_runner_->BelongsToCurrentThread()); |
853 | 866 |
854 audio_renderer_ = filter_collection_->GetAudioRenderer(); | 867 audio_renderer_ = filter_collection_->GetAudioRenderer(); |
855 audio_renderer_->Initialize( | 868 audio_renderer_->Initialize( |
856 demuxer_->GetStream(DemuxerStream::AUDIO), | 869 demuxer_->GetStream(DemuxerStream::AUDIO), |
857 done_cb, | 870 done_cb, |
858 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), | 871 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), |
859 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)), | |
860 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), | 872 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), |
| 873 base::Bind(&Pipeline::BufferingStateChanged, base::Unretained(this), |
| 874 &audio_buffering_state_), |
861 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), | 875 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), |
862 base::Bind(&Pipeline::SetError, base::Unretained(this))); | 876 base::Bind(&Pipeline::SetError, base::Unretained(this))); |
863 } | 877 } |
864 | 878 |
865 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { | 879 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { |
866 DCHECK(task_runner_->BelongsToCurrentThread()); | 880 DCHECK(task_runner_->BelongsToCurrentThread()); |
867 | 881 |
868 video_renderer_ = filter_collection_->GetVideoRenderer(); | 882 video_renderer_ = filter_collection_->GetVideoRenderer(); |
869 video_renderer_->Initialize( | 883 video_renderer_->Initialize( |
870 demuxer_->GetStream(DemuxerStream::VIDEO), | 884 demuxer_->GetStream(DemuxerStream::VIDEO), |
871 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE, | 885 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE, |
872 done_cb, | 886 done_cb, |
873 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), | 887 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), |
874 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)), | 888 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)), |
875 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)), | 889 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)), |
876 base::Bind(&Pipeline::SetError, base::Unretained(this)), | 890 base::Bind(&Pipeline::SetError, base::Unretained(this)), |
877 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), | 891 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), |
878 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); | 892 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); |
879 } | 893 } |
880 | 894 |
881 void Pipeline::OnAudioUnderflow() { | |
882 if (!task_runner_->BelongsToCurrentThread()) { | |
883 task_runner_->PostTask(FROM_HERE, base::Bind( | |
884 &Pipeline::OnAudioUnderflow, base::Unretained(this))); | |
885 return; | |
886 } | |
887 | |
888 if (state_ != kPlaying) | |
889 return; | |
890 | |
891 if (audio_renderer_) | |
892 audio_renderer_->ResumeAfterUnderflow(); | |
893 } | |
894 | |
895 void Pipeline::BufferingStateChanged(BufferingState* buffering_state, | 895 void Pipeline::BufferingStateChanged(BufferingState* buffering_state, |
896 BufferingState new_buffering_state) { | 896 BufferingState new_buffering_state) { |
897 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " | 897 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " |
898 << " " << new_buffering_state << ") " | 898 << " " << new_buffering_state << ") " |
899 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); | 899 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); |
900 DCHECK(task_runner_->BelongsToCurrentThread()); | 900 DCHECK(task_runner_->BelongsToCurrentThread()); |
901 bool was_waiting_for_enough_data = WaitingForEnoughData(); | 901 bool was_waiting_for_enough_data = WaitingForEnoughData(); |
902 *buffering_state = new_buffering_state; | 902 *buffering_state = new_buffering_state; |
903 | 903 |
904 // Renderer underflowed. | 904 // Renderer underflowed. |
905 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { | 905 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { |
906 StartWaitingForEnoughData(); | 906 PausePlayback(); |
907 return; | 907 return; |
908 } | 908 } |
909 | 909 |
910 // Renderer prerolled. | 910 // Renderer prerolled. |
911 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { | 911 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { |
912 StartPlayback(); | 912 StartPlayback(); |
913 return; | 913 return; |
914 } | 914 } |
915 } | 915 } |
916 | 916 |
917 bool Pipeline::WaitingForEnoughData() const { | 917 bool Pipeline::WaitingForEnoughData() const { |
918 DCHECK(task_runner_->BelongsToCurrentThread()); | 918 DCHECK(task_runner_->BelongsToCurrentThread()); |
919 if (state_ != kPlaying) | 919 if (state_ != kPlaying) |
920 return false; | 920 return false; |
921 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) | 921 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) |
922 return true; | 922 return true; |
923 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) | 923 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) |
924 return true; | 924 return true; |
925 return false; | 925 return false; |
926 } | 926 } |
927 | 927 |
928 void Pipeline::StartWaitingForEnoughData() { | 928 void Pipeline::PausePlayback() { |
929 DVLOG(1) << __FUNCTION__; | 929 DVLOG(1) << __FUNCTION__; |
930 DCHECK_EQ(state_, kPlaying); | 930 DCHECK_EQ(state_, kPlaying); |
931 DCHECK(WaitingForEnoughData()); | 931 DCHECK(WaitingForEnoughData()); |
| 932 DCHECK(task_runner_->BelongsToCurrentThread()); |
932 | 933 |
933 base::AutoLock auto_lock(lock_); | 934 base::AutoLock auto_lock(lock_); |
934 PauseClockAndStopRendering_Locked(); | 935 PauseClockAndStopRendering_Locked(); |
935 } | 936 } |
936 | 937 |
937 void Pipeline::StartPlayback() { | 938 void Pipeline::StartPlayback() { |
938 DVLOG(1) << __FUNCTION__; | 939 DVLOG(1) << __FUNCTION__; |
939 DCHECK_EQ(state_, kPlaying); | 940 DCHECK_EQ(state_, kPlaying); |
940 DCHECK_EQ(clock_state_, CLOCK_PAUSED); | 941 DCHECK_EQ(clock_state_, CLOCK_PAUSED); |
941 DCHECK(!WaitingForEnoughData()); | 942 DCHECK(!WaitingForEnoughData()); |
| 943 DCHECK(task_runner_->BelongsToCurrentThread()); |
942 | 944 |
943 if (audio_renderer_) { | 945 if (audio_renderer_) { |
944 // We use audio stream to update the clock. So if there is such a | 946 // We use audio stream to update the clock. So if there is such a |
945 // stream, we pause the clock until we receive a valid timestamp. | 947 // stream, we pause the clock until we receive a valid timestamp. |
946 base::AutoLock auto_lock(lock_); | 948 base::AutoLock auto_lock(lock_); |
947 clock_state_ = CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE; | 949 clock_state_ = CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE; |
948 audio_renderer_->StartRendering(); | 950 audio_renderer_->StartRendering(); |
949 } else { | 951 } else { |
950 base::AutoLock auto_lock(lock_); | 952 base::AutoLock auto_lock(lock_); |
951 clock_state_ = CLOCK_PLAYING; | 953 clock_state_ = CLOCK_PLAYING; |
(...skipping 29 matching lines...) Expand all Loading... |
981 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 983 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
982 lock_.AssertAcquired(); | 984 lock_.AssertAcquired(); |
983 if (clock_state_ != CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE) | 985 if (clock_state_ != CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE) |
984 return; | 986 return; |
985 | 987 |
986 clock_state_ = CLOCK_PLAYING; | 988 clock_state_ = CLOCK_PLAYING; |
987 clock_->Play(); | 989 clock_->Play(); |
988 } | 990 } |
989 | 991 |
990 } // namespace media | 992 } // namespace media |
OLD | NEW |