Chromium Code Reviews| 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 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 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::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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |