Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(255)

Side by Side Diff: media/base/pipeline.cc

Issue 276973004: Introduce audio/video BufferingState to Pipeline. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/base/pipeline.h ('k') | media/base/pipeline_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 30 matching lines...) Expand all
41 did_loading_progress_(false), 41 did_loading_progress_(false),
42 volume_(1.0f), 42 volume_(1.0f),
43 playback_rate_(0.0f), 43 playback_rate_(0.0f),
44 clock_(new Clock(&default_tick_clock_)), 44 clock_(new Clock(&default_tick_clock_)),
45 waiting_for_clock_update_(false), 45 waiting_for_clock_update_(false),
46 status_(PIPELINE_OK), 46 status_(PIPELINE_OK),
47 state_(kCreated), 47 state_(kCreated),
48 audio_ended_(false), 48 audio_ended_(false),
49 video_ended_(false), 49 video_ended_(false),
50 text_ended_(false), 50 text_ended_(false),
51 audio_buffering_state_(BUFFERING_HAVE_NOTHING),
52 video_buffering_state_(BUFFERING_HAVE_NOTHING),
51 demuxer_(NULL), 53 demuxer_(NULL),
52 creation_time_(default_tick_clock_.NowTicks()) { 54 creation_time_(default_tick_clock_.NowTicks()) {
53 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); 55 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
54 media_log_->AddEvent( 56 media_log_->AddEvent(
55 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); 57 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
56 } 58 }
57 59
58 Pipeline::~Pipeline() { 60 Pipeline::~Pipeline() {
59 DCHECK(thread_checker_.CalledOnValidThread()) 61 DCHECK(thread_checker_.CalledOnValidThread())
60 << "Pipeline must be destroyed on same thread that created it"; 62 << "Pipeline must be destroyed on same thread that created it";
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 183
182 void Pipeline::SetClockForTesting(Clock* clock) { 184 void Pipeline::SetClockForTesting(Clock* clock) {
183 clock_.reset(clock); 185 clock_.reset(clock);
184 } 186 }
185 187
186 void Pipeline::SetErrorForTesting(PipelineStatus status) { 188 void Pipeline::SetErrorForTesting(PipelineStatus status) {
187 SetError(status); 189 SetError(status);
188 } 190 }
189 191
190 void Pipeline::SetState(State next_state) { 192 void Pipeline::SetState(State next_state) {
191 if (state_ != kStarted && next_state == kStarted && 193 if (state_ != kPlaying && next_state == kPlaying &&
192 !creation_time_.is_null()) { 194 !creation_time_.is_null()) {
193 UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted", 195 UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
194 default_tick_clock_.NowTicks() - creation_time_); 196 default_tick_clock_.NowTicks() - creation_time_);
195 creation_time_ = base::TimeTicks(); 197 creation_time_ = base::TimeTicks();
196 } 198 }
197 199
198 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state); 200 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state);
199 201
200 state_ = next_state; 202 state_ = next_state;
201 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); 203 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
202 } 204 }
203 205
204 #define RETURN_STRING(state) case state: return #state; 206 #define RETURN_STRING(state) case state: return #state;
205 207
206 const char* Pipeline::GetStateString(State state) { 208 const char* Pipeline::GetStateString(State state) {
207 switch (state) { 209 switch (state) {
208 RETURN_STRING(kCreated); 210 RETURN_STRING(kCreated);
209 RETURN_STRING(kInitDemuxer); 211 RETURN_STRING(kInitDemuxer);
210 RETURN_STRING(kInitAudioRenderer); 212 RETURN_STRING(kInitAudioRenderer);
211 RETURN_STRING(kInitVideoRenderer); 213 RETURN_STRING(kInitVideoRenderer);
212 RETURN_STRING(kInitPrerolling); 214 RETURN_STRING(kInitPrerolling);
213 RETURN_STRING(kSeeking); 215 RETURN_STRING(kSeeking);
214 RETURN_STRING(kStarting); 216 RETURN_STRING(kPlaying);
215 RETURN_STRING(kStarted);
216 RETURN_STRING(kStopping); 217 RETURN_STRING(kStopping);
217 RETURN_STRING(kStopped); 218 RETURN_STRING(kStopped);
218 } 219 }
219 NOTREACHED(); 220 NOTREACHED();
220 return "INVALID"; 221 return "INVALID";
221 } 222 }
222 223
223 #undef RETURN_STRING 224 #undef RETURN_STRING
224 225
225 Pipeline::State Pipeline::GetNextState() const { 226 Pipeline::State Pipeline::GetNextState() const {
(...skipping 16 matching lines...) Expand all
242 243
243 case kInitAudioRenderer: 244 case kInitAudioRenderer:
244 if (demuxer_->GetStream(DemuxerStream::VIDEO)) 245 if (demuxer_->GetStream(DemuxerStream::VIDEO))
245 return kInitVideoRenderer; 246 return kInitVideoRenderer;
246 return kInitPrerolling; 247 return kInitPrerolling;
247 248
248 case kInitVideoRenderer: 249 case kInitVideoRenderer:
249 return kInitPrerolling; 250 return kInitPrerolling;
250 251
251 case kInitPrerolling: 252 case kInitPrerolling:
252 return kStarting; 253 return kPlaying;
253 254
254 case kSeeking: 255 case kSeeking:
255 return kStarting; 256 return kPlaying;
256 257
257 case kStarting: 258 case kPlaying:
258 return kStarted;
259
260 case kStarted:
261 case kStopping: 259 case kStopping:
262 case kStopped: 260 case kStopped:
263 break; 261 break;
264 } 262 }
265 NOTREACHED() << "State has no transition: " << state_; 263 NOTREACHED() << "State has no transition: " << state_;
266 return state_; 264 return state_;
267 } 265 }
268 266
269 void Pipeline::OnDemuxerError(PipelineStatus error) { 267 void Pipeline::OnDemuxerError(PipelineStatus error) {
270 SetError(error); 268 SetError(error);
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 // the previous operation. 355 // the previous operation.
358 status_ = (status_ != PIPELINE_OK ? status_ : status); 356 status_ = (status_ != PIPELINE_OK ? status_ : status);
359 357
360 if (status_ != PIPELINE_OK) { 358 if (status_ != PIPELINE_OK) {
361 ErrorChangedTask(status_); 359 ErrorChangedTask(status_);
362 return; 360 return;
363 } 361 }
364 362
365 // Guard against accidentally clearing |pending_callbacks_| for states that 363 // Guard against accidentally clearing |pending_callbacks_| for states that
366 // use it as well as states that should not be using it. 364 // use it as well as states that should not be using it.
367 //
368 // TODO(scherkus): Make every state transition use |pending_callbacks_|.
369 DCHECK_EQ(pending_callbacks_.get() != NULL, 365 DCHECK_EQ(pending_callbacks_.get() != NULL,
370 (state_ == kInitPrerolling || state_ == kStarting || 366 (state_ == kInitPrerolling || state_ == kSeeking));
371 state_ == kSeeking)); 367
372 pending_callbacks_.reset(); 368 pending_callbacks_.reset();
373 369
374 PipelineStatusCB done_cb = base::Bind( 370 PipelineStatusCB done_cb = base::Bind(
375 &Pipeline::OnStateTransition, base::Unretained(this)); 371 &Pipeline::OnStateTransition, base::Unretained(this));
376 372
377 // Switch states, performing any entrance actions for the new state as well. 373 // Switch states, performing any entrance actions for the new state as well.
378 SetState(GetNextState()); 374 SetState(GetNextState());
379 switch (state_) { 375 switch (state_) {
380 case kInitDemuxer: 376 case kInitDemuxer:
381 return InitializeDemuxer(done_cb); 377 return InitializeDemuxer(done_cb);
(...skipping 23 matching lines...) Expand all
405 metadata.has_video = video_renderer_; 401 metadata.has_video = video_renderer_;
406 metadata.timeline_offset = demuxer_->GetTimelineOffset(); 402 metadata.timeline_offset = demuxer_->GetTimelineOffset();
407 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); 403 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
408 if (stream) 404 if (stream)
409 metadata.natural_size = stream->video_decoder_config().natural_size(); 405 metadata.natural_size = stream->video_decoder_config().natural_size();
410 metadata_cb_.Run(metadata); 406 metadata_cb_.Run(metadata);
411 } 407 }
412 408
413 return DoInitialPreroll(done_cb); 409 return DoInitialPreroll(done_cb);
414 410
415 case kStarting: 411 case kPlaying:
416 return DoPlay(done_cb); 412 PlaybackRateChangedTask(GetPlaybackRate());
413 VolumeChangedTask(GetVolume());
417 414
418 case kStarted: 415 // We enter this state from either kInitPrerolling or kSeeking. As of now
419 { 416 // both those states call Preroll(), which means by time we enter this
420 base::AutoLock l(lock_); 417 // state we've already buffered enough data. Forcefully update the
421 // We use audio stream to update the clock. So if there is such a 418 // buffering state, which start the clock and renderers and transition
422 // stream, we pause the clock until we receive a valid timestamp. 419 // into kPlaying state.
423 waiting_for_clock_update_ = true; 420 //
424 if (!audio_renderer_) { 421 // TODO(scherkus): Remove after renderers are taught to fire buffering
425 clock_->SetMaxTime(clock_->Duration()); 422 // state callbacks http://crbug.com/144683
426 StartClockIfWaitingForTimeUpdate_Locked(); 423 DCHECK(WaitingForEnoughData());
427 } 424 if (audio_renderer_)
428 } 425 AudioBufferingStateChanged(BUFFERING_HAVE_ENOUGH);
429 426 if (video_renderer_)
430 DCHECK(!seek_cb_.is_null()); 427 VideoBufferingStateChanged(BUFFERING_HAVE_ENOUGH);
431 DCHECK_EQ(status_, PIPELINE_OK); 428 return;
432
433 // Fire canplaythrough immediately after playback begins because of
434 // crbug.com/106480.
435 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
436 preroll_completed_cb_.Run();
437 return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
438 429
439 case kStopping: 430 case kStopping:
440 case kStopped: 431 case kStopped:
441 case kCreated: 432 case kCreated:
442 case kSeeking: 433 case kSeeking:
443 NOTREACHED() << "State has no transition: " << state_; 434 NOTREACHED() << "State has no transition: " << state_;
444 return; 435 return;
445 } 436 }
446 } 437 }
447 438
(...skipping 14 matching lines...) Expand all
462 if (audio_renderer_) { 453 if (audio_renderer_) {
463 bound_fns.Push(base::Bind( 454 bound_fns.Push(base::Bind(
464 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), 455 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
465 seek_timestamp)); 456 seek_timestamp));
466 } 457 }
467 458
468 if (video_renderer_) { 459 if (video_renderer_) {
469 bound_fns.Push(base::Bind( 460 bound_fns.Push(base::Bind(
470 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), 461 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
471 seek_timestamp)); 462 seek_timestamp));
463
464 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
465 // state callbacks http://crbug.com/144683
466 bound_fns.Push(base::Bind(&VideoRenderer::Play,
467 base::Unretained(video_renderer_.get())));
468 }
469
470 if (text_renderer_) {
471 bound_fns.Push(base::Bind(
472 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
472 } 473 }
473 474
474 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 475 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
475 } 476 }
476 477
477 void Pipeline::DoSeek( 478 void Pipeline::DoSeek(
478 base::TimeDelta seek_timestamp, 479 base::TimeDelta seek_timestamp,
479 const PipelineStatusCB& done_cb) { 480 const PipelineStatusCB& done_cb) {
480 DCHECK(task_runner_->BelongsToCurrentThread()); 481 DCHECK(task_runner_->BelongsToCurrentThread());
481 DCHECK(!pending_callbacks_.get()); 482 DCHECK(!pending_callbacks_.get());
482 SerialRunner::Queue bound_fns; 483 SerialRunner::Queue bound_fns;
483 484
484 // Pause. 485 // Pause.
485 if (audio_renderer_) { 486 if (audio_renderer_) {
486 bound_fns.Push(base::Bind( 487 bound_fns.Push(base::Bind(
487 &AudioRenderer::Pause, base::Unretained(audio_renderer_.get()))); 488 &AudioRenderer::Pause, base::Unretained(audio_renderer_.get())));
488 } 489 }
489 if (text_renderer_) { 490 if (text_renderer_) {
490 bound_fns.Push(base::Bind( 491 bound_fns.Push(base::Bind(
491 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); 492 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
492 } 493 }
493 494
494 // Flush. 495 // Flush.
495 if (audio_renderer_) { 496 if (audio_renderer_) {
496 bound_fns.Push(base::Bind( 497 bound_fns.Push(base::Bind(
497 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); 498 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
499
500 // TODO(scherkus): Remove after AudioRenderer is taught to fire buffering
501 // state callbacks http://crbug.com/144683
502 bound_fns.Push(base::Bind(&Pipeline::AudioBufferingStateChanged,
503 base::Unretained(this),
504 BUFFERING_HAVE_NOTHING));
498 } 505 }
499 if (video_renderer_) { 506 if (video_renderer_) {
500 bound_fns.Push(base::Bind( 507 bound_fns.Push(base::Bind(
501 &VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); 508 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
509
510 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
511 // state callbacks http://crbug.com/144683
512 bound_fns.Push(base::Bind(&Pipeline::VideoBufferingStateChanged,
513 base::Unretained(this),
514 BUFFERING_HAVE_NOTHING));
502 } 515 }
503 if (text_renderer_) { 516 if (text_renderer_) {
504 bound_fns.Push(base::Bind( 517 bound_fns.Push(base::Bind(
505 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); 518 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
506 } 519 }
507 520
508 // Seek demuxer. 521 // Seek demuxer.
509 bound_fns.Push(base::Bind( 522 bound_fns.Push(base::Bind(
510 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); 523 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
511 524
512 // Preroll renderers. 525 // Preroll renderers.
513 if (audio_renderer_) { 526 if (audio_renderer_) {
514 bound_fns.Push(base::Bind( 527 bound_fns.Push(base::Bind(
515 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), 528 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
516 seek_timestamp)); 529 seek_timestamp));
517 } 530 }
518 531
519 if (video_renderer_) { 532 if (video_renderer_) {
520 bound_fns.Push(base::Bind( 533 bound_fns.Push(base::Bind(
521 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), 534 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
522 seek_timestamp)); 535 seek_timestamp));
523 }
524 536
525 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 537 // TODO(scherkus): Remove after renderers are taught to fire buffering
526 } 538 // state callbacks http://crbug.com/144683
527 539 bound_fns.Push(base::Bind(&VideoRenderer::Play,
528 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { 540 base::Unretained(video_renderer_.get())));
529 DCHECK(task_runner_->BelongsToCurrentThread());
530 DCHECK(!pending_callbacks_.get());
531 SerialRunner::Queue bound_fns;
532
533 PlaybackRateChangedTask(GetPlaybackRate());
534 VolumeChangedTask(GetVolume());
535
536 if (audio_renderer_) {
537 bound_fns.Push(base::Bind(
538 &AudioRenderer::Play, base::Unretained(audio_renderer_.get())));
539 }
540
541 if (video_renderer_) {
542 bound_fns.Push(base::Bind(
543 &VideoRenderer::Play, base::Unretained(video_renderer_.get())));
544 } 541 }
545 542
546 if (text_renderer_) { 543 if (text_renderer_) {
547 bound_fns.Push(base::Bind( 544 bound_fns.Push(base::Bind(
548 &TextRenderer::Play, base::Unretained(text_renderer_.get()))); 545 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
549 } 546 }
550 547
551 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 548 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
552 } 549 }
553 550
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
699 pending_callbacks_.reset(); 696 pending_callbacks_.reset();
700 status_ = error; 697 status_ = error;
701 698
702 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this))); 699 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
703 } 700 }
704 701
705 void Pipeline::PlaybackRateChangedTask(float playback_rate) { 702 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
706 DCHECK(task_runner_->BelongsToCurrentThread()); 703 DCHECK(task_runner_->BelongsToCurrentThread());
707 704
708 // Playback rate changes are only carried out while playing. 705 // Playback rate changes are only carried out while playing.
709 if (state_ != kStarting && state_ != kStarted) 706 if (state_ != kPlaying)
710 return; 707 return;
711 708
712 { 709 {
713 base::AutoLock auto_lock(lock_); 710 base::AutoLock auto_lock(lock_);
714 clock_->SetPlaybackRate(playback_rate); 711 clock_->SetPlaybackRate(playback_rate);
715 } 712 }
716 713
717 if (audio_renderer_) 714 if (audio_renderer_)
718 audio_renderer_->SetPlaybackRate(playback_rate_); 715 audio_renderer_->SetPlaybackRate(playback_rate_);
719 if (video_renderer_) 716 if (video_renderer_)
720 video_renderer_->SetPlaybackRate(playback_rate_); 717 video_renderer_->SetPlaybackRate(playback_rate_);
721 } 718 }
722 719
723 void Pipeline::VolumeChangedTask(float volume) { 720 void Pipeline::VolumeChangedTask(float volume) {
724 DCHECK(task_runner_->BelongsToCurrentThread()); 721 DCHECK(task_runner_->BelongsToCurrentThread());
725 722
726 // Volume changes are only carried out while playing. 723 // Volume changes are only carried out while playing.
727 if (state_ != kStarting && state_ != kStarted) 724 if (state_ != kPlaying)
728 return; 725 return;
729 726
730 if (audio_renderer_) 727 if (audio_renderer_)
731 audio_renderer_->SetVolume(volume); 728 audio_renderer_->SetVolume(volume);
732 } 729 }
733 730
734 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { 731 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
735 DCHECK(task_runner_->BelongsToCurrentThread()); 732 DCHECK(task_runner_->BelongsToCurrentThread());
736 DCHECK(stop_cb_.is_null()); 733 DCHECK(stop_cb_.is_null());
737 734
738 // Suppress seeking if we're not fully started. 735 // Suppress seeking if we're not fully started.
739 if (state_ != kStarted) { 736 if (state_ != kPlaying) {
740 DCHECK(state_ == kStopping || state_ == kStopped) 737 DCHECK(state_ == kStopping || state_ == kStopped)
741 << "Receive extra seek in unexpected state: " << state_; 738 << "Receive extra seek in unexpected state: " << state_;
742 739
743 // TODO(scherkus): should we run the callback? I'm tempted to say the API 740 // TODO(scherkus): should we run the callback? I'm tempted to say the API
744 // will only execute the first Seek() request. 741 // will only execute the first Seek() request.
745 DVLOG(1) << "Media pipeline has not started, ignoring seek to " 742 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
746 << time.InMicroseconds() << " (current state: " << state_ << ")"; 743 << time.InMicroseconds() << " (current state: " << state_ << ")";
747 return; 744 return;
748 } 745 }
749 746
(...skipping 13 matching lines...) Expand all
763 clock_->Pause(); 760 clock_->Pause();
764 clock_->SetTime(seek_timestamp, seek_timestamp); 761 clock_->SetTime(seek_timestamp, seek_timestamp);
765 } 762 }
766 DoSeek(seek_timestamp, base::Bind( 763 DoSeek(seek_timestamp, base::Bind(
767 &Pipeline::OnStateTransition, base::Unretained(this))); 764 &Pipeline::OnStateTransition, base::Unretained(this)));
768 } 765 }
769 766
770 void Pipeline::DoAudioRendererEnded() { 767 void Pipeline::DoAudioRendererEnded() {
771 DCHECK(task_runner_->BelongsToCurrentThread()); 768 DCHECK(task_runner_->BelongsToCurrentThread());
772 769
773 if (state_ != kStarted) 770 if (state_ != kPlaying)
774 return; 771 return;
775 772
776 DCHECK(!audio_ended_); 773 DCHECK(!audio_ended_);
777 audio_ended_ = true; 774 audio_ended_ = true;
778 775
779 // Start clock since there is no more audio to trigger clock updates. 776 // Start clock since there is no more audio to trigger clock updates.
780 { 777 {
781 base::AutoLock auto_lock(lock_); 778 base::AutoLock auto_lock(lock_);
782 clock_->SetMaxTime(clock_->Duration()); 779 clock_->SetMaxTime(clock_->Duration());
783 StartClockIfWaitingForTimeUpdate_Locked(); 780 StartClockIfWaitingForTimeUpdate_Locked();
784 } 781 }
785 782
786 RunEndedCallbackIfNeeded(); 783 RunEndedCallbackIfNeeded();
787 } 784 }
788 785
789 void Pipeline::DoVideoRendererEnded() { 786 void Pipeline::DoVideoRendererEnded() {
790 DCHECK(task_runner_->BelongsToCurrentThread()); 787 DCHECK(task_runner_->BelongsToCurrentThread());
791 788
792 if (state_ != kStarted) 789 if (state_ != kPlaying)
793 return; 790 return;
794 791
795 DCHECK(!video_ended_); 792 DCHECK(!video_ended_);
796 video_ended_ = true; 793 video_ended_ = true;
797 794
798 RunEndedCallbackIfNeeded(); 795 RunEndedCallbackIfNeeded();
799 } 796 }
800 797
801 void Pipeline::DoTextRendererEnded() { 798 void Pipeline::DoTextRendererEnded() {
802 DCHECK(task_runner_->BelongsToCurrentThread()); 799 DCHECK(task_runner_->BelongsToCurrentThread());
803 800
804 if (state_ != kStarted) 801 if (state_ != kPlaying)
805 return; 802 return;
806 803
807 DCHECK(!text_ended_); 804 DCHECK(!text_ended_);
808 text_ended_ = true; 805 text_ended_ = true;
809 806
810 RunEndedCallbackIfNeeded(); 807 RunEndedCallbackIfNeeded();
811 } 808 }
812 809
813 void Pipeline::RunEndedCallbackIfNeeded() { 810 void Pipeline::RunEndedCallbackIfNeeded() {
814 DCHECK(task_runner_->BelongsToCurrentThread()); 811 DCHECK(task_runner_->BelongsToCurrentThread());
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
881 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); 878 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
882 } 879 }
883 880
884 void Pipeline::OnAudioUnderflow() { 881 void Pipeline::OnAudioUnderflow() {
885 if (!task_runner_->BelongsToCurrentThread()) { 882 if (!task_runner_->BelongsToCurrentThread()) {
886 task_runner_->PostTask(FROM_HERE, base::Bind( 883 task_runner_->PostTask(FROM_HERE, base::Bind(
887 &Pipeline::OnAudioUnderflow, base::Unretained(this))); 884 &Pipeline::OnAudioUnderflow, base::Unretained(this)));
888 return; 885 return;
889 } 886 }
890 887
891 if (state_ != kStarted) 888 if (state_ != kPlaying)
892 return; 889 return;
893 890
894 if (audio_renderer_) 891 if (audio_renderer_)
895 audio_renderer_->ResumeAfterUnderflow(); 892 audio_renderer_->ResumeAfterUnderflow();
896 } 893 }
897 894
895 void Pipeline::AudioBufferingStateChanged(BufferingState buffering_state) {
acolwell GONE FROM CHROMIUM 2014/05/12 13:46:54 nit: You could avoid duplicate code below if you b
scherkus (not reviewing) 2014/05/12 15:22:03 Done.
896 DCHECK(task_runner_->BelongsToCurrentThread());
897 bool was_waiting_for_enough_data = WaitingForEnoughData();
898 audio_buffering_state_ = buffering_state;
899
900 // Audio renderer underflowed.
901 if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
902 StartWaitingForEnoughData();
903 return;
904 }
905
906 // Audio renderer prerolled.
907 if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
908 StartPlayback();
909 return;
910 }
911 }
912
913 void Pipeline::VideoBufferingStateChanged(BufferingState buffering_state) {
914 DCHECK(task_runner_->BelongsToCurrentThread());
915 bool was_waiting_for_enough_data = WaitingForEnoughData();
916 video_buffering_state_ = buffering_state;
917
918 // Video renderer underflowed.
919 if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
920 StartWaitingForEnoughData();
921 return;
922 }
923
924 // Video renderer prerolled.
925 if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
926 StartPlayback();
927 return;
928 }
929 }
930
931 bool Pipeline::WaitingForEnoughData() const {
932 DCHECK(task_runner_->BelongsToCurrentThread());
933 if (state_ != kPlaying)
934 return false;
935 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
936 return true;
937 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
938 return true;
939 return false;
940 }
941
942 void Pipeline::StartWaitingForEnoughData() {
943 DCHECK_EQ(state_, kPlaying);
944 DCHECK(WaitingForEnoughData());
945
946 if (audio_renderer_)
947 audio_renderer_->Pause();
948
949 base::AutoLock auto_lock(lock_);
950 clock_->Pause();
951 }
952
953 void Pipeline::StartPlayback() {
954 DCHECK_EQ(state_, kPlaying);
955 DCHECK(!WaitingForEnoughData());
956
957 if (audio_renderer_) {
958 audio_renderer_->Play();
959
960 base::AutoLock auto_lock(lock_);
961 // We use video stream to update the clock. So if there is such a
acolwell GONE FROM CHROMIUM 2014/05/12 13:46:54 nit: s/video/audio?
scherkus (not reviewing) 2014/05/12 15:22:03 Done.
962 // stream, we pause the clock until we receive a valid timestamp.
963 waiting_for_clock_update_ = true;
964 } else {
965 base::AutoLock auto_lock(lock_);
966 clock_->SetMaxTime(clock_->Duration());
967 clock_->Play();
968 }
969
970 preroll_completed_cb_.Run();
971 if (!seek_cb_.is_null())
972 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
973 }
974
898 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { 975 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
899 lock_.AssertAcquired(); 976 lock_.AssertAcquired();
900 if (!waiting_for_clock_update_) 977 if (!waiting_for_clock_update_)
901 return; 978 return;
902 979
903 waiting_for_clock_update_ = false; 980 waiting_for_clock_update_ = false;
904 clock_->Play(); 981 clock_->Play();
905 } 982 }
906 983
907 } // namespace media 984 } // namespace media
OLDNEW
« no previous file with comments | « media/base/pipeline.h ('k') | media/base/pipeline_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698