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

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: fixes 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 BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH);
429 426 if (video_renderer_)
430 DCHECK(!seek_cb_.is_null()); 427 BufferingStateChanged(&video_buffering_state_, 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::BufferingStateChanged,
503 base::Unretained(this),
504 &audio_buffering_state_,
505 BUFFERING_HAVE_NOTHING));
498 } 506 }
499 if (video_renderer_) { 507 if (video_renderer_) {
500 bound_fns.Push(base::Bind( 508 bound_fns.Push(base::Bind(
501 &VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); 509 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
510
511 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
512 // state callbacks http://crbug.com/144683
513 bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
514 base::Unretained(this),
515 &video_buffering_state_,
516 BUFFERING_HAVE_NOTHING));
502 } 517 }
503 if (text_renderer_) { 518 if (text_renderer_) {
504 bound_fns.Push(base::Bind( 519 bound_fns.Push(base::Bind(
505 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); 520 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
506 } 521 }
507 522
508 // Seek demuxer. 523 // Seek demuxer.
509 bound_fns.Push(base::Bind( 524 bound_fns.Push(base::Bind(
510 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); 525 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
511 526
512 // Preroll renderers. 527 // Preroll renderers.
513 if (audio_renderer_) { 528 if (audio_renderer_) {
514 bound_fns.Push(base::Bind( 529 bound_fns.Push(base::Bind(
515 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), 530 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
516 seek_timestamp)); 531 seek_timestamp));
517 } 532 }
518 533
519 if (video_renderer_) { 534 if (video_renderer_) {
520 bound_fns.Push(base::Bind( 535 bound_fns.Push(base::Bind(
521 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), 536 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
522 seek_timestamp)); 537 seek_timestamp));
523 }
524 538
525 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 539 // TODO(scherkus): Remove after renderers are taught to fire buffering
526 } 540 // state callbacks http://crbug.com/144683
527 541 bound_fns.Push(base::Bind(&VideoRenderer::Play,
528 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { 542 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 } 543 }
545 544
546 if (text_renderer_) { 545 if (text_renderer_) {
547 bound_fns.Push(base::Bind( 546 bound_fns.Push(base::Bind(
548 &TextRenderer::Play, base::Unretained(text_renderer_.get()))); 547 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
549 } 548 }
550 549
551 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 550 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
552 } 551 }
553 552
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
699 pending_callbacks_.reset(); 698 pending_callbacks_.reset();
700 status_ = error; 699 status_ = error;
701 700
702 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this))); 701 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
703 } 702 }
704 703
705 void Pipeline::PlaybackRateChangedTask(float playback_rate) { 704 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
706 DCHECK(task_runner_->BelongsToCurrentThread()); 705 DCHECK(task_runner_->BelongsToCurrentThread());
707 706
708 // Playback rate changes are only carried out while playing. 707 // Playback rate changes are only carried out while playing.
709 if (state_ != kStarting && state_ != kStarted) 708 if (state_ != kPlaying)
710 return; 709 return;
711 710
712 { 711 {
713 base::AutoLock auto_lock(lock_); 712 base::AutoLock auto_lock(lock_);
714 clock_->SetPlaybackRate(playback_rate); 713 clock_->SetPlaybackRate(playback_rate);
715 } 714 }
716 715
717 if (audio_renderer_) 716 if (audio_renderer_)
718 audio_renderer_->SetPlaybackRate(playback_rate_); 717 audio_renderer_->SetPlaybackRate(playback_rate_);
719 if (video_renderer_) 718 if (video_renderer_)
720 video_renderer_->SetPlaybackRate(playback_rate_); 719 video_renderer_->SetPlaybackRate(playback_rate_);
721 } 720 }
722 721
723 void Pipeline::VolumeChangedTask(float volume) { 722 void Pipeline::VolumeChangedTask(float volume) {
724 DCHECK(task_runner_->BelongsToCurrentThread()); 723 DCHECK(task_runner_->BelongsToCurrentThread());
725 724
726 // Volume changes are only carried out while playing. 725 // Volume changes are only carried out while playing.
727 if (state_ != kStarting && state_ != kStarted) 726 if (state_ != kPlaying)
728 return; 727 return;
729 728
730 if (audio_renderer_) 729 if (audio_renderer_)
731 audio_renderer_->SetVolume(volume); 730 audio_renderer_->SetVolume(volume);
732 } 731 }
733 732
734 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { 733 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
735 DCHECK(task_runner_->BelongsToCurrentThread()); 734 DCHECK(task_runner_->BelongsToCurrentThread());
736 DCHECK(stop_cb_.is_null()); 735 DCHECK(stop_cb_.is_null());
737 736
738 // Suppress seeking if we're not fully started. 737 // Suppress seeking if we're not fully started.
739 if (state_ != kStarted) { 738 if (state_ != kPlaying) {
740 DCHECK(state_ == kStopping || state_ == kStopped) 739 DCHECK(state_ == kStopping || state_ == kStopped)
741 << "Receive extra seek in unexpected state: " << state_; 740 << "Receive extra seek in unexpected state: " << state_;
742 741
743 // TODO(scherkus): should we run the callback? I'm tempted to say the API 742 // TODO(scherkus): should we run the callback? I'm tempted to say the API
744 // will only execute the first Seek() request. 743 // will only execute the first Seek() request.
745 DVLOG(1) << "Media pipeline has not started, ignoring seek to " 744 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
746 << time.InMicroseconds() << " (current state: " << state_ << ")"; 745 << time.InMicroseconds() << " (current state: " << state_ << ")";
747 return; 746 return;
748 } 747 }
749 748
(...skipping 13 matching lines...) Expand all
763 clock_->Pause(); 762 clock_->Pause();
764 clock_->SetTime(seek_timestamp, seek_timestamp); 763 clock_->SetTime(seek_timestamp, seek_timestamp);
765 } 764 }
766 DoSeek(seek_timestamp, base::Bind( 765 DoSeek(seek_timestamp, base::Bind(
767 &Pipeline::OnStateTransition, base::Unretained(this))); 766 &Pipeline::OnStateTransition, base::Unretained(this)));
768 } 767 }
769 768
770 void Pipeline::DoAudioRendererEnded() { 769 void Pipeline::DoAudioRendererEnded() {
771 DCHECK(task_runner_->BelongsToCurrentThread()); 770 DCHECK(task_runner_->BelongsToCurrentThread());
772 771
773 if (state_ != kStarted) 772 if (state_ != kPlaying)
774 return; 773 return;
775 774
776 DCHECK(!audio_ended_); 775 DCHECK(!audio_ended_);
777 audio_ended_ = true; 776 audio_ended_ = true;
778 777
779 // Start clock since there is no more audio to trigger clock updates. 778 // Start clock since there is no more audio to trigger clock updates.
780 { 779 {
781 base::AutoLock auto_lock(lock_); 780 base::AutoLock auto_lock(lock_);
782 clock_->SetMaxTime(clock_->Duration()); 781 clock_->SetMaxTime(clock_->Duration());
783 StartClockIfWaitingForTimeUpdate_Locked(); 782 StartClockIfWaitingForTimeUpdate_Locked();
784 } 783 }
785 784
786 RunEndedCallbackIfNeeded(); 785 RunEndedCallbackIfNeeded();
787 } 786 }
788 787
789 void Pipeline::DoVideoRendererEnded() { 788 void Pipeline::DoVideoRendererEnded() {
790 DCHECK(task_runner_->BelongsToCurrentThread()); 789 DCHECK(task_runner_->BelongsToCurrentThread());
791 790
792 if (state_ != kStarted) 791 if (state_ != kPlaying)
793 return; 792 return;
794 793
795 DCHECK(!video_ended_); 794 DCHECK(!video_ended_);
796 video_ended_ = true; 795 video_ended_ = true;
797 796
798 RunEndedCallbackIfNeeded(); 797 RunEndedCallbackIfNeeded();
799 } 798 }
800 799
801 void Pipeline::DoTextRendererEnded() { 800 void Pipeline::DoTextRendererEnded() {
802 DCHECK(task_runner_->BelongsToCurrentThread()); 801 DCHECK(task_runner_->BelongsToCurrentThread());
803 802
804 if (state_ != kStarted) 803 if (state_ != kPlaying)
805 return; 804 return;
806 805
807 DCHECK(!text_ended_); 806 DCHECK(!text_ended_);
808 text_ended_ = true; 807 text_ended_ = true;
809 808
810 RunEndedCallbackIfNeeded(); 809 RunEndedCallbackIfNeeded();
811 } 810 }
812 811
813 void Pipeline::RunEndedCallbackIfNeeded() { 812 void Pipeline::RunEndedCallbackIfNeeded() {
814 DCHECK(task_runner_->BelongsToCurrentThread()); 813 DCHECK(task_runner_->BelongsToCurrentThread());
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
881 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); 880 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
882 } 881 }
883 882
884 void Pipeline::OnAudioUnderflow() { 883 void Pipeline::OnAudioUnderflow() {
885 if (!task_runner_->BelongsToCurrentThread()) { 884 if (!task_runner_->BelongsToCurrentThread()) {
886 task_runner_->PostTask(FROM_HERE, base::Bind( 885 task_runner_->PostTask(FROM_HERE, base::Bind(
887 &Pipeline::OnAudioUnderflow, base::Unretained(this))); 886 &Pipeline::OnAudioUnderflow, base::Unretained(this)));
888 return; 887 return;
889 } 888 }
890 889
891 if (state_ != kStarted) 890 if (state_ != kPlaying)
892 return; 891 return;
893 892
894 if (audio_renderer_) 893 if (audio_renderer_)
895 audio_renderer_->ResumeAfterUnderflow(); 894 audio_renderer_->ResumeAfterUnderflow();
896 } 895 }
897 896
897 void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
898 BufferingState new_buffering_state) {
899 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
900 << " " << new_buffering_state << ") "
901 << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
902 DCHECK(task_runner_->BelongsToCurrentThread());
903 bool was_waiting_for_enough_data = WaitingForEnoughData();
904 *buffering_state = new_buffering_state;
905
906 // Renderer underflowed.
907 if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
908 StartWaitingForEnoughData();
909 return;
910 }
911
912 // Renderer prerolled.
913 if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
914 StartPlayback();
915 return;
916 }
917 }
918
919 bool Pipeline::WaitingForEnoughData() const {
920 DCHECK(task_runner_->BelongsToCurrentThread());
921 if (state_ != kPlaying)
922 return false;
923 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
924 return true;
925 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
926 return true;
927 return false;
928 }
929
930 void Pipeline::StartWaitingForEnoughData() {
931 DCHECK_EQ(state_, kPlaying);
932 DCHECK(WaitingForEnoughData());
933
934 if (audio_renderer_)
935 audio_renderer_->Pause();
936
937 base::AutoLock auto_lock(lock_);
938 clock_->Pause();
939 }
940
941 void Pipeline::StartPlayback() {
942 DCHECK_EQ(state_, kPlaying);
943 DCHECK(!WaitingForEnoughData());
944
945 if (audio_renderer_) {
946 audio_renderer_->Play();
947
948 base::AutoLock auto_lock(lock_);
949 // We use audio stream to update the clock. So if there is such a
950 // stream, we pause the clock until we receive a valid timestamp.
951 waiting_for_clock_update_ = true;
952 } else {
953 base::AutoLock auto_lock(lock_);
954 clock_->SetMaxTime(clock_->Duration());
955 clock_->Play();
956 }
957
958 preroll_completed_cb_.Run();
959 if (!seek_cb_.is_null())
960 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
961 }
962
898 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { 963 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
899 lock_.AssertAcquired(); 964 lock_.AssertAcquired();
900 if (!waiting_for_clock_update_) 965 if (!waiting_for_clock_update_)
901 return; 966 return;
902 967
903 waiting_for_clock_update_ = false; 968 waiting_for_clock_update_ = false;
904 clock_->Play(); 969 clock_->Play();
905 } 970 }
906 971
907 } // namespace media 972 } // 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