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 30 matching lines...) Expand all Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |