| 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" |
| 11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
| 12 #include "base/compiler_specific.h" | 12 #include "base/compiler_specific.h" |
| 13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 14 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
| 15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
| 16 #include "base/string_number_conversions.h" | 16 #include "base/string_number_conversions.h" |
| 17 #include "base/string_util.h" | 17 #include "base/string_util.h" |
| 18 #include "base/synchronization/condition_variable.h" | 18 #include "base/synchronization/condition_variable.h" |
| 19 #include "media/base/audio_decoder.h" | 19 #include "media/base/audio_decoder.h" |
| 20 #include "media/base/audio_renderer.h" | 20 #include "media/base/audio_renderer.h" |
| 21 #include "media/base/buffers.h" | |
| 22 #include "media/base/clock.h" | 21 #include "media/base/clock.h" |
| 23 #include "media/base/filter_collection.h" | 22 #include "media/base/filter_collection.h" |
| 24 #include "media/base/media_log.h" | 23 #include "media/base/media_log.h" |
| 25 #include "media/base/video_decoder.h" | 24 #include "media/base/video_decoder.h" |
| 26 #include "media/base/video_decoder_config.h" | 25 #include "media/base/video_decoder_config.h" |
| 27 #include "media/base/video_renderer.h" | 26 #include "media/base/video_renderer.h" |
| 28 | 27 |
| 29 using base::TimeDelta; | 28 using base::TimeDelta; |
| 30 | 29 |
| 31 namespace media { | 30 namespace media { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 56 while (!notified_) | 55 while (!notified_) |
| 57 cv_.Wait(); | 56 cv_.Wait(); |
| 58 } | 57 } |
| 59 | 58 |
| 60 media::PipelineStatus PipelineStatusNotification::status() { | 59 media::PipelineStatus PipelineStatusNotification::status() { |
| 61 base::AutoLock auto_lock(lock_); | 60 base::AutoLock auto_lock(lock_); |
| 62 DCHECK(notified_); | 61 DCHECK(notified_); |
| 63 return status_; | 62 return status_; |
| 64 } | 63 } |
| 65 | 64 |
| 66 struct Pipeline::PipelineInitState { | |
| 67 scoped_refptr<AudioDecoder> audio_decoder; | |
| 68 }; | |
| 69 | |
| 70 Pipeline::Pipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, | 65 Pipeline::Pipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 71 MediaLog* media_log) | 66 MediaLog* media_log) |
| 72 : message_loop_(message_loop), | 67 : message_loop_(message_loop), |
| 73 media_log_(media_log), | 68 media_log_(media_log), |
| 74 running_(false), | 69 running_(false), |
| 75 seek_pending_(false), | |
| 76 tearing_down_(false), | |
| 77 playback_rate_change_pending_(false), | |
| 78 did_loading_progress_(false), | 70 did_loading_progress_(false), |
| 79 total_bytes_(0), | 71 total_bytes_(0), |
| 80 natural_size_(0, 0), | 72 natural_size_(0, 0), |
| 81 volume_(1.0f), | 73 volume_(1.0f), |
| 82 playback_rate_(0.0f), | 74 playback_rate_(0.0f), |
| 83 pending_playback_rate_(0.0f), | |
| 84 clock_(new Clock(&base::Time::Now)), | 75 clock_(new Clock(&base::Time::Now)), |
| 85 waiting_for_clock_update_(false), | 76 waiting_for_clock_update_(false), |
| 86 status_(PIPELINE_OK), | 77 status_(PIPELINE_OK), |
| 87 has_audio_(false), | 78 has_audio_(false), |
| 88 has_video_(false), | 79 has_video_(false), |
| 89 state_(kCreated), | 80 state_(kCreated), |
| 90 seek_timestamp_(kNoTimestamp()), | |
| 91 audio_ended_(false), | 81 audio_ended_(false), |
| 92 video_ended_(false), | 82 video_ended_(false), |
| 93 audio_disabled_(false), | 83 audio_disabled_(false), |
| 94 creation_time_(base::Time::Now()) { | 84 creation_time_(base::Time::Now()) { |
| 95 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 85 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| 96 media_log_->AddEvent( | 86 media_log_->AddEvent( |
| 97 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); | 87 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); |
| 98 } | 88 } |
| 99 | 89 |
| 100 Pipeline::~Pipeline() { | 90 Pipeline::~Pipeline() { |
| 101 base::AutoLock auto_lock(lock_); | 91 DCHECK(thread_checker_.CalledOnValidThread()) |
| 92 << "Pipeline must be destroyed on same thread that created it"; |
| 102 DCHECK(!running_) << "Stop() must complete before destroying object"; | 93 DCHECK(!running_) << "Stop() must complete before destroying object"; |
| 103 DCHECK(stop_cb_.is_null()); | 94 DCHECK(stop_cb_.is_null()); |
| 104 DCHECK(!seek_pending_); | 95 DCHECK(seek_cb_.is_null()); |
| 105 | 96 |
| 106 media_log_->AddEvent( | 97 media_log_->AddEvent( |
| 107 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); | 98 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); |
| 108 } | 99 } |
| 109 | 100 |
| 110 void Pipeline::Start(scoped_ptr<FilterCollection> collection, | 101 void Pipeline::Start(scoped_ptr<FilterCollection> collection, |
| 111 const PipelineStatusCB& ended_cb, | 102 const PipelineStatusCB& ended_cb, |
| 112 const PipelineStatusCB& error_cb, | 103 const PipelineStatusCB& error_cb, |
| 113 const PipelineStatusCB& seek_cb, | 104 const PipelineStatusCB& seek_cb, |
| 114 const BufferingStateCB& buffering_state_cb) { | 105 const BufferingStateCB& buffering_state_cb) { |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 SetError(status); | 240 SetError(status); |
| 250 } | 241 } |
| 251 | 242 |
| 252 void Pipeline::SetState(State next_state) { | 243 void Pipeline::SetState(State next_state) { |
| 253 if (state_ != kStarted && next_state == kStarted && | 244 if (state_ != kStarted && next_state == kStarted && |
| 254 !creation_time_.is_null()) { | 245 !creation_time_.is_null()) { |
| 255 UMA_HISTOGRAM_TIMES( | 246 UMA_HISTOGRAM_TIMES( |
| 256 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); | 247 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); |
| 257 creation_time_ = base::Time(); | 248 creation_time_ = base::Time(); |
| 258 } | 249 } |
| 250 |
| 251 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state); |
| 252 |
| 259 state_ = next_state; | 253 state_ = next_state; |
| 260 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 254 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
| 261 } | 255 } |
| 262 | 256 |
| 263 bool Pipeline::IsPipelineOk() { | 257 #define RETURN_STRING(state) case state: return #state; |
| 264 base::AutoLock auto_lock(lock_); | 258 |
| 265 return status_ == PIPELINE_OK; | 259 const char* Pipeline::GetStateString(State state) { |
| 260 switch (state) { |
| 261 RETURN_STRING(kCreated); |
| 262 RETURN_STRING(kInitDemuxer); |
| 263 RETURN_STRING(kInitAudioDecoder); |
| 264 RETURN_STRING(kInitAudioRenderer); |
| 265 RETURN_STRING(kInitVideoRenderer); |
| 266 RETURN_STRING(kInitPrerolling); |
| 267 RETURN_STRING(kSeeking); |
| 268 RETURN_STRING(kStarting); |
| 269 RETURN_STRING(kStarted); |
| 270 RETURN_STRING(kStopping); |
| 271 RETURN_STRING(kStopped); |
| 272 } |
| 273 NOTREACHED(); |
| 274 return "INVALID"; |
| 266 } | 275 } |
| 267 | 276 |
| 268 bool Pipeline::IsPipelineSeeking() { | 277 #undef RETURN_STRING |
| 278 |
| 279 Pipeline::State Pipeline::GetNextState() const { |
| 269 DCHECK(message_loop_->BelongsToCurrentThread()); | 280 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 270 if (!seek_pending_) | 281 DCHECK(stop_cb_.is_null()) |
| 271 return false; | 282 << "State transitions don't happen when stopping"; |
| 272 DCHECK(kSeeking == state_ || kPausing == state_ || | 283 DCHECK_EQ(status_, PIPELINE_OK) |
| 273 kFlushing == state_ || kStarting == state_) | 284 << "State transitions don't happen when there's an error: " << status_; |
| 274 << "Current state : " << state_; | |
| 275 return true; | |
| 276 } | |
| 277 | 285 |
| 278 void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) { | 286 switch (state_) { |
| 279 DCHECK(message_loop_->BelongsToCurrentThread()); | 287 case kCreated: |
| 280 if (cb.is_null()) | 288 return kInitDemuxer; |
| 281 return; | |
| 282 cb.Run(status); | |
| 283 // Prevent double-reporting of errors to clients. | |
| 284 if (status != PIPELINE_OK) | |
| 285 error_cb_.Reset(); | |
| 286 } | |
| 287 | 289 |
| 288 void Pipeline::FinishSeek() { | 290 case kInitDemuxer: |
| 289 DCHECK(message_loop_->BelongsToCurrentThread()); | 291 if (demuxer_->GetStream(DemuxerStream::AUDIO)) |
| 290 seek_timestamp_ = kNoTimestamp(); | 292 return kInitAudioDecoder; |
| 291 seek_pending_ = false; | 293 if (demuxer_->GetStream(DemuxerStream::VIDEO)) |
| 294 return kInitVideoRenderer; |
| 295 return kInitPrerolling; |
| 292 | 296 |
| 293 // Execute the seek callback, if present. Note that this might be the | 297 case kInitAudioDecoder: |
| 294 // initial callback passed into Start(). | 298 return kInitAudioRenderer; |
| 295 ReportStatus(seek_cb_, status_); | |
| 296 seek_cb_.Reset(); | |
| 297 } | |
| 298 | 299 |
| 299 // static | 300 case kInitAudioRenderer: |
| 300 bool Pipeline::TransientState(State state) { | 301 if (demuxer_->GetStream(DemuxerStream::VIDEO)) |
| 301 return state == kPausing || | 302 return kInitVideoRenderer; |
| 302 state == kFlushing || | 303 return kInitPrerolling; |
| 303 state == kSeeking || | |
| 304 state == kStarting || | |
| 305 state == kStopping; | |
| 306 } | |
| 307 | 304 |
| 308 // static | 305 case kInitVideoRenderer: |
| 309 Pipeline::State Pipeline::FindNextState(State current) { | 306 return kInitPrerolling; |
| 310 // TODO(scherkus): refactor InitializeTask() to make use of this function. | 307 |
| 311 if (current == kPausing) { | 308 case kInitPrerolling: |
| 312 return kFlushing; | 309 return kStarting; |
| 313 } else if (current == kFlushing) { | 310 |
| 314 // We will always honor Seek() before Stop(). This is based on the | 311 case kSeeking: |
| 315 // assumption that we never accept Seek() after Stop(). | 312 return kStarting; |
| 316 DCHECK(IsPipelineSeeking() || | 313 |
| 317 !stop_cb_.is_null() || | 314 case kStarting: |
| 318 tearing_down_); | 315 return kStarted; |
| 319 return IsPipelineSeeking() ? kSeeking : kStopping; | 316 |
| 320 } else if (current == kSeeking) { | 317 case kStarted: |
| 321 return kStarting; | 318 case kStopping: |
| 322 } else if (current == kStarting) { | 319 case kStopped: |
| 323 return kStarted; | 320 break; |
| 324 } else if (current == kStopping) { | |
| 325 return kStopped; | |
| 326 } else { | |
| 327 return current; | |
| 328 } | 321 } |
| 322 NOTREACHED() << "State has no transition: " << state_; |
| 323 return state_; |
| 329 } | 324 } |
| 330 | 325 |
| 331 void Pipeline::OnDemuxerError(PipelineStatus error) { | 326 void Pipeline::OnDemuxerError(PipelineStatus error) { |
| 332 SetError(error); | 327 SetError(error); |
| 333 } | 328 } |
| 334 | 329 |
| 335 void Pipeline::SetError(PipelineStatus error) { | 330 void Pipeline::SetError(PipelineStatus error) { |
| 336 DCHECK(IsRunning()); | 331 DCHECK(IsRunning()); |
| 337 DCHECK_NE(PIPELINE_OK, error); | 332 DCHECK_NE(PIPELINE_OK, error); |
| 338 VLOG(1) << "Media pipeline error: " << error; | 333 VLOG(1) << "Media pipeline error: " << error; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 415 // Since the byte->time calculation is approximate, fudge the beginning & | 410 // Since the byte->time calculation is approximate, fudge the beginning & |
| 416 // ending areas to look better. | 411 // ending areas to look better. |
| 417 TimeDelta epsilon = clock_->Duration() / 100; | 412 TimeDelta epsilon = clock_->Duration() / 100; |
| 418 if (time_offset < epsilon) | 413 if (time_offset < epsilon) |
| 419 return TimeDelta(); | 414 return TimeDelta(); |
| 420 if (time_offset + epsilon > clock_->Duration()) | 415 if (time_offset + epsilon > clock_->Duration()) |
| 421 return clock_->Duration(); | 416 return clock_->Duration(); |
| 422 return time_offset; | 417 return time_offset; |
| 423 } | 418 } |
| 424 | 419 |
| 425 void Pipeline::DoPause(const PipelineStatusCB& done_cb) { | 420 void Pipeline::OnStateTransition(PipelineStatus status) { |
| 421 // Force post to process state transitions after current execution frame. |
| 422 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 423 &Pipeline::StateTransitionTask, this, status)); |
| 424 } |
| 425 |
| 426 void Pipeline::StateTransitionTask(PipelineStatus status) { |
| 427 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 428 |
| 429 // No-op any state transitions if we're stopping. |
| 430 if (state_ == kStopping || state_ == kStopped) |
| 431 return; |
| 432 |
| 433 // Preserve existing abnormal status, otherwise update based on the result of |
| 434 // the previous operation. |
| 435 status_ = (status_ != PIPELINE_OK ? status_ : status); |
| 436 |
| 437 if (status_ != PIPELINE_OK) { |
| 438 ErrorChangedTask(status_); |
| 439 return; |
| 440 } |
| 441 |
| 442 // Guard against accidentally clearing |pending_callbacks_| for states that |
| 443 // use it as well as states that should not be using it. |
| 444 // |
| 445 // TODO(scherkus): Make every state transition use |pending_callbacks_|. |
| 446 DCHECK_EQ(pending_callbacks_.get() != NULL, |
| 447 (state_ == kInitPrerolling || state_ == kStarting || |
| 448 state_ == kSeeking)); |
| 449 pending_callbacks_.reset(); |
| 450 |
| 451 PipelineStatusCB done_cb = base::Bind(&Pipeline::OnStateTransition, this); |
| 452 |
| 453 // Switch states, performing any entrance actions for the new state as well. |
| 454 SetState(GetNextState()); |
| 455 switch (state_) { |
| 456 case kInitDemuxer: |
| 457 return InitializeDemuxer(done_cb); |
| 458 |
| 459 case kInitAudioDecoder: |
| 460 return InitializeAudioDecoder(done_cb); |
| 461 |
| 462 case kInitAudioRenderer: |
| 463 return InitializeAudioRenderer(done_cb); |
| 464 |
| 465 case kInitVideoRenderer: |
| 466 return InitializeVideoRenderer(done_cb); |
| 467 |
| 468 case kInitPrerolling: |
| 469 filter_collection_.reset(); |
| 470 { |
| 471 base::AutoLock l(lock_); |
| 472 // We do not want to start the clock running. We only want to set the |
| 473 // base media time so our timestamp calculations will be correct. |
| 474 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); |
| 475 |
| 476 // TODO(scherkus): |has_audio_| should be true no matter what -- |
| 477 // otherwise people with muted/disabled sound cards will make our |
| 478 // default controls look as if every video doesn't contain an audio |
| 479 // track. |
| 480 has_audio_ = audio_renderer_ != NULL && !audio_disabled_; |
| 481 has_video_ = video_renderer_ != NULL; |
| 482 } |
| 483 if (!audio_renderer_ && !video_renderer_) { |
| 484 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER); |
| 485 return; |
| 486 } |
| 487 |
| 488 buffering_state_cb_.Run(kHaveMetadata); |
| 489 |
| 490 return DoInitialPreroll(done_cb); |
| 491 |
| 492 case kStarting: |
| 493 return DoPlay(done_cb); |
| 494 |
| 495 case kStarted: |
| 496 { |
| 497 base::AutoLock l(lock_); |
| 498 // We use audio stream to update the clock. So if there is such a |
| 499 // stream, we pause the clock until we receive a valid timestamp. |
| 500 waiting_for_clock_update_ = true; |
| 501 if (!has_audio_) { |
| 502 clock_->SetMaxTime(clock_->Duration()); |
| 503 StartClockIfWaitingForTimeUpdate_Locked(); |
| 504 } |
| 505 } |
| 506 |
| 507 DCHECK(!seek_cb_.is_null()); |
| 508 DCHECK_EQ(status_, PIPELINE_OK); |
| 509 |
| 510 // Fire canplaythrough immediately after playback begins because of |
| 511 // crbug.com/106480. |
| 512 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed. |
| 513 buffering_state_cb_.Run(kPrerollCompleted); |
| 514 return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
| 515 |
| 516 case kStopping: |
| 517 case kStopped: |
| 518 case kCreated: |
| 519 case kSeeking: |
| 520 NOTREACHED() << "State has no transition: " << state_; |
| 521 return; |
| 522 } |
| 523 } |
| 524 |
| 525 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { |
| 426 DCHECK(message_loop_->BelongsToCurrentThread()); | 526 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 427 DCHECK(!pending_callbacks_.get()); | 527 DCHECK(!pending_callbacks_.get()); |
| 428 SerialRunner::Queue bound_fns; | 528 SerialRunner::Queue bound_fns; |
| 429 | 529 |
| 430 if (audio_renderer_) | 530 base::TimeDelta seek_timestamp = demuxer_->GetStartTime(); |
| 431 bound_fns.Push(base::Bind(&AudioRenderer::Pause, audio_renderer_)); | |
| 432 | 531 |
| 433 if (video_renderer_) | 532 // Preroll renderers. |
| 434 bound_fns.Push(base::Bind(&VideoRenderer::Pause, video_renderer_)); | 533 if (audio_renderer_) { |
| 534 bound_fns.Push(base::Bind( |
| 535 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); |
| 536 } |
| 537 |
| 538 if (video_renderer_) { |
| 539 bound_fns.Push(base::Bind( |
| 540 &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); |
| 541 } |
| 435 | 542 |
| 436 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 543 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
| 437 } | 544 } |
| 438 | 545 |
| 439 void Pipeline::DoFlush(const PipelineStatusCB& done_cb) { | 546 void Pipeline::DoSeek( |
| 547 base::TimeDelta seek_timestamp, |
| 548 const PipelineStatusCB& done_cb) { |
| 440 DCHECK(message_loop_->BelongsToCurrentThread()); | 549 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 441 DCHECK(!pending_callbacks_.get()); | 550 DCHECK(!pending_callbacks_.get()); |
| 442 SerialRunner::Queue bound_fns; | 551 SerialRunner::Queue bound_fns; |
| 443 | 552 |
| 553 // Pause. |
| 554 if (audio_renderer_) |
| 555 bound_fns.Push(base::Bind(&AudioRenderer::Pause, audio_renderer_)); |
| 556 if (video_renderer_) |
| 557 bound_fns.Push(base::Bind(&VideoRenderer::Pause, video_renderer_)); |
| 558 |
| 559 // Flush. |
| 444 if (audio_renderer_) | 560 if (audio_renderer_) |
| 445 bound_fns.Push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); | 561 bound_fns.Push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); |
| 446 | |
| 447 if (video_renderer_) | 562 if (video_renderer_) |
| 448 bound_fns.Push(base::Bind(&VideoRenderer::Flush, video_renderer_)); | 563 bound_fns.Push(base::Bind(&VideoRenderer::Flush, video_renderer_)); |
| 449 | 564 |
| 565 // Seek demuxer. |
| 566 bound_fns.Push(base::Bind( |
| 567 &Demuxer::Seek, demuxer_, seek_timestamp)); |
| 568 |
| 569 // Preroll renderers. |
| 570 if (audio_renderer_) { |
| 571 bound_fns.Push(base::Bind( |
| 572 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); |
| 573 } |
| 574 |
| 575 if (video_renderer_) { |
| 576 bound_fns.Push(base::Bind( |
| 577 &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); |
| 578 } |
| 579 |
| 450 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 580 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
| 451 } | 581 } |
| 452 | 582 |
| 453 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { | 583 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { |
| 454 DCHECK(message_loop_->BelongsToCurrentThread()); | 584 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 455 DCHECK(!pending_callbacks_.get()); | 585 DCHECK(!pending_callbacks_.get()); |
| 456 SerialRunner::Queue bound_fns; | 586 SerialRunner::Queue bound_fns; |
| 457 | 587 |
| 588 PlaybackRateChangedTask(GetPlaybackRate()); |
| 589 VolumeChangedTask(GetVolume()); |
| 590 |
| 458 if (audio_renderer_) | 591 if (audio_renderer_) |
| 459 bound_fns.Push(base::Bind(&AudioRenderer::Play, audio_renderer_)); | 592 bound_fns.Push(base::Bind(&AudioRenderer::Play, audio_renderer_)); |
| 460 | 593 |
| 461 if (video_renderer_) | 594 if (video_renderer_) |
| 462 bound_fns.Push(base::Bind(&VideoRenderer::Play, video_renderer_)); | 595 bound_fns.Push(base::Bind(&VideoRenderer::Play, video_renderer_)); |
| 463 | 596 |
| 464 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 597 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
| 465 } | 598 } |
| 466 | 599 |
| 467 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { | 600 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { |
| 468 DCHECK(message_loop_->BelongsToCurrentThread()); | 601 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 469 DCHECK(!pending_callbacks_.get()); | 602 DCHECK(!pending_callbacks_.get()); |
| 470 SerialRunner::Queue bound_fns; | 603 SerialRunner::Queue bound_fns; |
| 471 | 604 |
| 472 if (demuxer_) | 605 if (demuxer_) |
| 473 bound_fns.Push(base::Bind(&Demuxer::Stop, demuxer_)); | 606 bound_fns.Push(base::Bind(&Demuxer::Stop, demuxer_)); |
| 474 | 607 |
| 475 if (audio_renderer_) | 608 if (audio_renderer_) |
| 476 bound_fns.Push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); | 609 bound_fns.Push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); |
| 477 | 610 |
| 478 if (video_renderer_) | 611 if (video_renderer_) |
| 479 bound_fns.Push(base::Bind(&VideoRenderer::Stop, video_renderer_)); | 612 bound_fns.Push(base::Bind(&VideoRenderer::Stop, video_renderer_)); |
| 480 | 613 |
| 481 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 614 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
| 482 } | 615 } |
| 483 | 616 |
| 617 void Pipeline::OnStopCompleted(PipelineStatus status) { |
| 618 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 619 DCHECK_EQ(state_, kStopping); |
| 620 { |
| 621 base::AutoLock l(lock_); |
| 622 running_ = false; |
| 623 } |
| 624 |
| 625 SetState(kStopped); |
| 626 pending_callbacks_.reset(); |
| 627 filter_collection_.reset(); |
| 628 audio_decoder_ = NULL; |
| 629 audio_renderer_ = NULL; |
| 630 video_renderer_ = NULL; |
| 631 demuxer_ = NULL; |
| 632 |
| 633 // If we stop during initialization/seeking we want to run |seek_cb_| |
| 634 // followed by |stop_cb_| so we don't leave outstanding callbacks around. |
| 635 if (!seek_cb_.is_null()) { |
| 636 base::ResetAndReturn(&seek_cb_).Run(status_); |
| 637 error_cb_.Reset(); |
| 638 } |
| 639 if (!stop_cb_.is_null()) { |
| 640 base::ResetAndReturn(&stop_cb_).Run(); |
| 641 error_cb_.Reset(); |
| 642 } |
| 643 if (!error_cb_.is_null()) { |
| 644 DCHECK_NE(status_, PIPELINE_OK); |
| 645 base::ResetAndReturn(&error_cb_).Run(status_); |
| 646 } |
| 647 } |
| 648 |
| 484 void Pipeline::AddBufferedByteRange(int64 start, int64 end) { | 649 void Pipeline::AddBufferedByteRange(int64 start, int64 end) { |
| 485 DCHECK(IsRunning()); | 650 DCHECK(IsRunning()); |
| 486 base::AutoLock auto_lock(lock_); | 651 base::AutoLock auto_lock(lock_); |
| 487 buffered_byte_ranges_.Add(start, end); | 652 buffered_byte_ranges_.Add(start, end); |
| 488 did_loading_progress_ = true; | 653 did_loading_progress_ = true; |
| 489 } | 654 } |
| 490 | 655 |
| 491 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, | 656 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, |
| 492 base::TimeDelta end) { | 657 base::TimeDelta end) { |
| 493 DCHECK(IsRunning()); | 658 DCHECK(IsRunning()); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 513 } | 678 } |
| 514 | 679 |
| 515 void Pipeline::OnVideoRendererEnded() { | 680 void Pipeline::OnVideoRendererEnded() { |
| 516 // Force post to process ended messages after current execution frame. | 681 // Force post to process ended messages after current execution frame. |
| 517 message_loop_->PostTask(FROM_HERE, base::Bind( | 682 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 518 &Pipeline::DoVideoRendererEnded, this)); | 683 &Pipeline::DoVideoRendererEnded, this)); |
| 519 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED)); | 684 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED)); |
| 520 } | 685 } |
| 521 | 686 |
| 522 // Called from any thread. | 687 // Called from any thread. |
| 523 void Pipeline::OnFilterInitialize(PipelineStatus status) { | |
| 524 // Continue the initialize task by proceeding to the next stage. | |
| 525 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 526 &Pipeline::InitializeTask, this, status)); | |
| 527 } | |
| 528 | |
| 529 // Called from any thread. | |
| 530 // This method makes the PipelineStatusCB behave like a Closure. It | |
| 531 // makes it look like a host()->SetError() call followed by a call to | |
| 532 // OnFilterStateTransition() when errors occur. | |
| 533 // | |
| 534 // TODO(scherkus): Revisit this code when SetError() is removed from FilterHost | |
| 535 // and all the Closures are converted to PipelineStatusCB. | |
| 536 void Pipeline::OnFilterStateTransition(PipelineStatus status) { | |
| 537 if (status != PIPELINE_OK) | |
| 538 SetError(status); | |
| 539 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 540 &Pipeline::FilterStateTransitionTask, this)); | |
| 541 } | |
| 542 | |
| 543 void Pipeline::OnTeardownStateTransition(PipelineStatus status) { | |
| 544 // Ignore any errors during teardown. | |
| 545 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 546 &Pipeline::TeardownStateTransitionTask, this)); | |
| 547 } | |
| 548 | |
| 549 // Called from any thread. | |
| 550 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { | 688 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { |
| 551 base::AutoLock auto_lock(lock_); | 689 base::AutoLock auto_lock(lock_); |
| 552 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | 690 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| 553 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | 691 statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
| 554 statistics_.video_frames_decoded += stats.video_frames_decoded; | 692 statistics_.video_frames_decoded += stats.video_frames_decoded; |
| 555 statistics_.video_frames_dropped += stats.video_frames_dropped; | 693 statistics_.video_frames_dropped += stats.video_frames_dropped; |
| 556 } | 694 } |
| 557 | 695 |
| 558 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, | 696 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, |
| 559 const PipelineStatusCB& ended_cb, | 697 const PipelineStatusCB& ended_cb, |
| 560 const PipelineStatusCB& error_cb, | 698 const PipelineStatusCB& error_cb, |
| 561 const PipelineStatusCB& seek_cb, | 699 const PipelineStatusCB& seek_cb, |
| 562 const BufferingStateCB& buffering_state_cb) { | 700 const BufferingStateCB& buffering_state_cb) { |
| 563 DCHECK(message_loop_->BelongsToCurrentThread()); | 701 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 564 CHECK_EQ(kCreated, state_) | 702 CHECK_EQ(kCreated, state_) |
| 565 << "Media pipeline cannot be started more than once"; | 703 << "Media pipeline cannot be started more than once"; |
| 566 | 704 |
| 567 filter_collection_ = filter_collection.Pass(); | 705 filter_collection_ = filter_collection.Pass(); |
| 568 ended_cb_ = ended_cb; | 706 ended_cb_ = ended_cb; |
| 569 error_cb_ = error_cb; | 707 error_cb_ = error_cb; |
| 570 seek_cb_ = seek_cb; | 708 seek_cb_ = seek_cb; |
| 571 buffering_state_cb_ = buffering_state_cb; | 709 buffering_state_cb_ = buffering_state_cb; |
| 572 | 710 |
| 573 // Kick off initialization. | 711 StateTransitionTask(PIPELINE_OK); |
| 574 pipeline_init_state_.reset(new PipelineInitState()); | |
| 575 | |
| 576 SetState(kInitDemuxer); | |
| 577 InitializeDemuxer(); | |
| 578 } | 712 } |
| 579 | 713 |
| 580 // Main initialization method called on the pipeline thread. This code attempts | |
| 581 // to use the specified filter factory to build a pipeline. | |
| 582 // Initialization step performed in this method depends on current state of this | |
| 583 // object, indicated by |state_|. After each step of initialization, this | |
| 584 // object transits to the next stage. It starts by creating a Demuxer, and then | |
| 585 // connects the Demuxer's audio stream to an AudioDecoder which is then | |
| 586 // connected to an AudioRenderer. If the media has video, then it connects a | |
| 587 // VideoDecoder to the Demuxer's video stream, and then connects the | |
| 588 // VideoDecoder to a VideoRenderer. | |
| 589 // | |
| 590 // When all required filters have been created and have called their | |
| 591 // FilterHost's InitializationComplete() method, the pipeline will update its | |
| 592 // state to kStarted and |init_cb_|, will be executed. | |
| 593 // | |
| 594 // TODO(hclam): InitializeTask() is now starting the pipeline asynchronously. It | |
| 595 // works like a big state change table. If we no longer need to start filters | |
| 596 // in order, we need to get rid of all the state change. | |
| 597 void Pipeline::InitializeTask(PipelineStatus last_stage_status) { | |
| 598 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 599 | |
| 600 if (last_stage_status != PIPELINE_OK) { | |
| 601 SetError(last_stage_status); | |
| 602 return; | |
| 603 } | |
| 604 | |
| 605 // If we have received the stop or error signal, return immediately. | |
| 606 if (!stop_cb_.is_null() || state_ == kStopped || !IsPipelineOk()) | |
| 607 return; | |
| 608 | |
| 609 DCHECK(state_ == kInitDemuxer || | |
| 610 state_ == kInitAudioDecoder || | |
| 611 state_ == kInitAudioRenderer || | |
| 612 state_ == kInitVideoRenderer); | |
| 613 | |
| 614 // Demuxer created, create audio decoder. | |
| 615 if (state_ == kInitDemuxer) { | |
| 616 SetState(kInitAudioDecoder); | |
| 617 // If this method returns false, then there's no audio stream. | |
| 618 if (InitializeAudioDecoder(demuxer_)) | |
| 619 return; | |
| 620 } | |
| 621 | |
| 622 // Assuming audio decoder was created, create audio renderer. | |
| 623 if (state_ == kInitAudioDecoder) { | |
| 624 SetState(kInitAudioRenderer); | |
| 625 | |
| 626 // Returns false if there's no audio stream. | |
| 627 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder)) { | |
| 628 base::AutoLock auto_lock(lock_); | |
| 629 has_audio_ = true; | |
| 630 return; | |
| 631 } | |
| 632 } | |
| 633 | |
| 634 // Assuming audio renderer was created, create video renderer. | |
| 635 if (state_ == kInitAudioRenderer) { | |
| 636 SetState(kInitVideoRenderer); | |
| 637 scoped_refptr<DemuxerStream> video_stream = | |
| 638 demuxer_->GetStream(DemuxerStream::VIDEO); | |
| 639 if (InitializeVideoRenderer(video_stream)) { | |
| 640 base::AutoLock auto_lock(lock_); | |
| 641 has_video_ = true; | |
| 642 | |
| 643 // Get an initial natural size so we have something when we signal | |
| 644 // the kHaveMetadata buffering state. | |
| 645 natural_size_ = video_stream->video_decoder_config().natural_size(); | |
| 646 return; | |
| 647 } | |
| 648 } | |
| 649 | |
| 650 if (state_ == kInitVideoRenderer) { | |
| 651 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) { | |
| 652 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); | |
| 653 return; | |
| 654 } | |
| 655 | |
| 656 // Clear initialization state now that we're done. | |
| 657 filter_collection_.reset(); | |
| 658 pipeline_init_state_.reset(); | |
| 659 | |
| 660 // Initialization was successful, we are now considered paused, so it's safe | |
| 661 // to set the initial playback rate and volume. | |
| 662 PlaybackRateChangedTask(GetPlaybackRate()); | |
| 663 VolumeChangedTask(GetVolume()); | |
| 664 | |
| 665 buffering_state_cb_.Run(kHaveMetadata); | |
| 666 | |
| 667 // Fire a seek request to get the renderers to preroll. We can skip a seek | |
| 668 // here as the demuxer should be at the start of the stream. | |
| 669 seek_pending_ = true; | |
| 670 SetState(kSeeking); | |
| 671 seek_timestamp_ = demuxer_->GetStartTime(); | |
| 672 DoSeek(seek_timestamp_, true, | |
| 673 base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 674 } | |
| 675 } | |
| 676 | |
| 677 // This method is called as a result of the client calling Pipeline::Stop() or | |
| 678 // as the result of an error condition. | |
| 679 // We stop the filters in the reverse order. | |
| 680 // | |
| 681 // TODO(scherkus): beware! this can get posted multiple times since we post | |
| 682 // Stop() tasks even if we've already stopped. Perhaps this should no-op for | |
| 683 // additional calls, however most of this logic will be changing. | |
| 684 void Pipeline::StopTask(const base::Closure& stop_cb) { | 714 void Pipeline::StopTask(const base::Closure& stop_cb) { |
| 685 DCHECK(message_loop_->BelongsToCurrentThread()); | 715 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 686 DCHECK(stop_cb_.is_null()); | 716 DCHECK(stop_cb_.is_null()); |
| 687 | 717 |
| 688 if (state_ == kStopped) { | 718 if (state_ == kStopped) { |
| 689 stop_cb.Run(); | 719 stop_cb.Run(); |
| 690 return; | 720 return; |
| 691 } | 721 } |
| 692 | 722 |
| 723 // TODO(scherkus): Remove after pipeline state machine refactoring has some |
| 724 // time to bake http://crbug.com/110228 |
| 693 if (video_renderer_) | 725 if (video_renderer_) |
| 694 video_renderer_->PrepareForShutdownHack(); | 726 video_renderer_->PrepareForShutdownHack(); |
| 695 | 727 |
| 696 if (tearing_down_ && status_ != PIPELINE_OK) { | 728 SetState(kStopping); |
| 697 // If we are stopping due to SetError(), stop normally instead of | 729 pending_callbacks_.reset(); |
| 698 // going to error state and calling |error_cb_|. This converts | |
| 699 // the teardown in progress from an error teardown into one that acts | |
| 700 // like the error never occurred. | |
| 701 base::AutoLock auto_lock(lock_); | |
| 702 status_ = PIPELINE_OK; | |
| 703 } | |
| 704 | |
| 705 stop_cb_ = stop_cb; | 730 stop_cb_ = stop_cb; |
| 706 | 731 |
| 707 if (!IsPipelineSeeking() && !tearing_down_) { | 732 DoStop(base::Bind(&Pipeline::OnStopCompleted, this)); |
| 708 // We will tear down pipeline immediately when there is no seek operation | |
| 709 // pending and no teardown in progress. This should include the case where | |
| 710 // we are partially initialized. | |
| 711 TearDownPipeline(); | |
| 712 } | |
| 713 } | 733 } |
| 714 | 734 |
| 715 void Pipeline::ErrorChangedTask(PipelineStatus error) { | 735 void Pipeline::ErrorChangedTask(PipelineStatus error) { |
| 716 DCHECK(message_loop_->BelongsToCurrentThread()); | 736 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 717 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | 737 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
| 718 | 738 |
| 719 // Suppress executing additional error logic. Note that if we are currently | 739 if (state_ == kStopping || state_ == kStopped) |
| 720 // performing a normal stop, then we return immediately and continue the | |
| 721 // normal stop. | |
| 722 if (state_ == kStopped || tearing_down_) { | |
| 723 return; | 740 return; |
| 724 } | |
| 725 | 741 |
| 726 base::AutoLock auto_lock(lock_); | 742 SetState(kStopping); |
| 743 pending_callbacks_.reset(); |
| 727 status_ = error; | 744 status_ = error; |
| 728 | 745 |
| 729 // Posting TearDownPipeline() to message loop so that we can make sure | 746 DoStop(base::Bind(&Pipeline::OnStopCompleted, this)); |
| 730 // it runs after any pending callbacks that are already queued. | |
| 731 // |tearing_down_| is set early here to make sure that pending callbacks | |
| 732 // don't modify the state before TearDownPipeline() can run. | |
| 733 tearing_down_ = true; | |
| 734 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 735 &Pipeline::TearDownPipeline, this)); | |
| 736 } | 747 } |
| 737 | 748 |
| 738 void Pipeline::PlaybackRateChangedTask(float playback_rate) { | 749 void Pipeline::PlaybackRateChangedTask(float playback_rate) { |
| 739 DCHECK(message_loop_->BelongsToCurrentThread()); | 750 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 740 | 751 |
| 741 if (state_ == kStopped || tearing_down_) | 752 // Playback rate changes are only carried out while playing. |
| 753 if (state_ != kStarting && state_ != kStarted) |
| 742 return; | 754 return; |
| 743 | 755 |
| 744 // Suppress rate change until after seeking. | |
| 745 if (IsPipelineSeeking()) { | |
| 746 pending_playback_rate_ = playback_rate; | |
| 747 playback_rate_change_pending_ = true; | |
| 748 return; | |
| 749 } | |
| 750 | |
| 751 { | 756 { |
| 752 base::AutoLock auto_lock(lock_); | 757 base::AutoLock auto_lock(lock_); |
| 753 clock_->SetPlaybackRate(playback_rate); | 758 clock_->SetPlaybackRate(playback_rate); |
| 754 } | 759 } |
| 755 | 760 |
| 756 // These will get set after initialization completes in case playback rate is | |
| 757 // set prior to initialization. | |
| 758 if (demuxer_) | 761 if (demuxer_) |
| 759 demuxer_->SetPlaybackRate(playback_rate); | 762 demuxer_->SetPlaybackRate(playback_rate); |
| 760 if (audio_renderer_) | 763 if (audio_renderer_) |
| 761 audio_renderer_->SetPlaybackRate(playback_rate_); | 764 audio_renderer_->SetPlaybackRate(playback_rate_); |
| 762 if (video_renderer_) | 765 if (video_renderer_) |
| 763 video_renderer_->SetPlaybackRate(playback_rate_); | 766 video_renderer_->SetPlaybackRate(playback_rate_); |
| 764 } | 767 } |
| 765 | 768 |
| 766 void Pipeline::VolumeChangedTask(float volume) { | 769 void Pipeline::VolumeChangedTask(float volume) { |
| 767 DCHECK(message_loop_->BelongsToCurrentThread()); | 770 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 768 | 771 |
| 769 if (state_ == kStopped || tearing_down_) | 772 // Volume changes are only carried out while playing. |
| 773 if (state_ != kStarting && state_ != kStarted) |
| 770 return; | 774 return; |
| 771 | 775 |
| 772 if (audio_renderer_) | 776 if (audio_renderer_) |
| 773 audio_renderer_->SetVolume(volume); | 777 audio_renderer_->SetVolume(volume); |
| 774 } | 778 } |
| 775 | 779 |
| 776 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { | 780 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| 777 DCHECK(message_loop_->BelongsToCurrentThread()); | 781 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 778 DCHECK(stop_cb_.is_null()); | 782 DCHECK(stop_cb_.is_null()); |
| 779 | 783 |
| 780 // Suppress seeking if we're not fully started. | 784 // Suppress seeking if we're not fully started. |
| 781 if (state_ != kStarted) { | 785 if (state_ != kStarted) { |
| 786 DCHECK(state_ == kStopping || state_ == kStopped) |
| 787 << "Receive extra seek in unexpected state: " << state_; |
| 788 |
| 782 // TODO(scherkus): should we run the callback? I'm tempted to say the API | 789 // TODO(scherkus): should we run the callback? I'm tempted to say the API |
| 783 // will only execute the first Seek() request. | 790 // will only execute the first Seek() request. |
| 784 DVLOG(1) << "Media pipeline has not started, ignoring seek to " | 791 DVLOG(1) << "Media pipeline has not started, ignoring seek to " |
| 785 << time.InMicroseconds() << " (current state: " << state_ << ")"; | 792 << time.InMicroseconds() << " (current state: " << state_ << ")"; |
| 786 return; | 793 return; |
| 787 } | 794 } |
| 788 | 795 |
| 789 DCHECK(!seek_pending_); | 796 DCHECK(seek_cb_.is_null()); |
| 790 seek_pending_ = true; | |
| 791 | 797 |
| 792 // We'll need to pause every filter before seeking. The state transition | 798 SetState(kSeeking); |
| 793 // is as follows: | 799 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); |
| 794 // kStarted | 800 seek_cb_ = seek_cb; |
| 795 // kPausing (for each filter) | |
| 796 // kSeeking (for each filter) | |
| 797 // kStarting (for each filter) | |
| 798 // kStarted | |
| 799 SetState(kPausing); | |
| 800 audio_ended_ = false; | 801 audio_ended_ = false; |
| 801 video_ended_ = false; | 802 video_ended_ = false; |
| 802 seek_timestamp_ = std::max(time, demuxer_->GetStartTime()); | |
| 803 seek_cb_ = seek_cb; | |
| 804 | 803 |
| 805 // Kick off seeking! | 804 // Kick off seeking! |
| 806 { | 805 { |
| 807 base::AutoLock auto_lock(lock_); | 806 base::AutoLock auto_lock(lock_); |
| 808 if (clock_->IsPlaying()) | 807 if (clock_->IsPlaying()) |
| 809 clock_->Pause(); | 808 clock_->Pause(); |
| 809 clock_->SetTime(seek_timestamp, seek_timestamp); |
| 810 } | 810 } |
| 811 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 811 DoSeek(seek_timestamp, base::Bind(&Pipeline::OnStateTransition, this)); |
| 812 } | 812 } |
| 813 | 813 |
| 814 void Pipeline::DoAudioRendererEnded() { | 814 void Pipeline::DoAudioRendererEnded() { |
| 815 DCHECK(message_loop_->BelongsToCurrentThread()); | 815 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 816 | 816 |
| 817 if (state_ != kStarted) | 817 if (state_ != kStarted) |
| 818 return; | 818 return; |
| 819 | 819 |
| 820 DCHECK(!audio_ended_); | 820 DCHECK(!audio_ended_); |
| 821 audio_ended_ = true; | 821 audio_ended_ = true; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 849 return; | 849 return; |
| 850 | 850 |
| 851 if (video_renderer_ && !video_ended_) | 851 if (video_renderer_ && !video_ended_) |
| 852 return; | 852 return; |
| 853 | 853 |
| 854 { | 854 { |
| 855 base::AutoLock auto_lock(lock_); | 855 base::AutoLock auto_lock(lock_); |
| 856 clock_->EndOfStream(); | 856 clock_->EndOfStream(); |
| 857 } | 857 } |
| 858 | 858 |
| 859 ReportStatus(ended_cb_, status_); | 859 // TODO(scherkus): Change |ended_cb_| into a Closure. |
| 860 DCHECK_EQ(status_, PIPELINE_OK); |
| 861 ended_cb_.Run(status_); |
| 860 } | 862 } |
| 861 | 863 |
| 862 void Pipeline::AudioDisabledTask() { | 864 void Pipeline::AudioDisabledTask() { |
| 863 DCHECK(message_loop_->BelongsToCurrentThread()); | 865 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 864 | 866 |
| 865 base::AutoLock auto_lock(lock_); | 867 base::AutoLock auto_lock(lock_); |
| 866 has_audio_ = false; | 868 has_audio_ = false; |
| 867 audio_disabled_ = true; | 869 audio_disabled_ = true; |
| 868 | 870 |
| 869 // Notify our demuxer that we're no longer rendering audio. | 871 // Notify our demuxer that we're no longer rendering audio. |
| 870 demuxer_->OnAudioRendererDisabled(); | 872 demuxer_->OnAudioRendererDisabled(); |
| 871 | 873 |
| 872 // Start clock since there is no more audio to | 874 // Start clock since there is no more audio to trigger clock updates. |
| 873 // trigger clock updates. | |
| 874 clock_->SetMaxTime(clock_->Duration()); | 875 clock_->SetMaxTime(clock_->Duration()); |
| 875 StartClockIfWaitingForTimeUpdate_Locked(); | 876 StartClockIfWaitingForTimeUpdate_Locked(); |
| 876 } | 877 } |
| 877 | 878 |
| 878 void Pipeline::FilterStateTransitionTask() { | 879 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) { |
| 879 DCHECK(message_loop_->BelongsToCurrentThread()); | 880 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 880 DCHECK(pending_callbacks_.get()) | |
| 881 << "Filter state transitions must be completed via pending_callbacks_"; | |
| 882 pending_callbacks_.reset(); | |
| 883 | 881 |
| 884 // State transitions while tearing down are handled via | 882 demuxer_ = filter_collection_->GetDemuxer(); |
| 885 // TeardownStateTransitionTask(). | 883 demuxer_->Initialize(this, done_cb); |
| 886 // | |
| 887 // TODO(scherkus): Merge all state machinery! | |
| 888 if (state_ == kStopped || tearing_down_) { | |
| 889 return; | |
| 890 } | |
| 891 | |
| 892 if (!TransientState(state_)) { | |
| 893 NOTREACHED() << "Invalid current state: " << state_; | |
| 894 SetError(PIPELINE_ERROR_ABORT); | |
| 895 return; | |
| 896 } | |
| 897 | |
| 898 // Decrement the number of remaining transitions, making sure to transition | |
| 899 // to the next state if needed. | |
| 900 SetState(FindNextState(state_)); | |
| 901 if (state_ == kSeeking) { | |
| 902 base::AutoLock auto_lock(lock_); | |
| 903 DCHECK(seek_timestamp_ != kNoTimestamp()); | |
| 904 clock_->SetTime(seek_timestamp_, seek_timestamp_); | |
| 905 } | |
| 906 | |
| 907 // Carry out the action for the current state. | |
| 908 if (TransientState(state_)) { | |
| 909 if (state_ == kPausing) { | |
| 910 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 911 } else if (state_ == kFlushing) { | |
| 912 DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 913 } else if (state_ == kSeeking) { | |
| 914 DoSeek(seek_timestamp_, false, | |
| 915 base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 916 } else if (state_ == kStarting) { | |
| 917 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 918 } else if (state_ == kStopping) { | |
| 919 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 920 } else { | |
| 921 NOTREACHED() << "Unexpected state: " << state_; | |
| 922 } | |
| 923 } else if (state_ == kStarted) { | |
| 924 | |
| 925 // Fire canplaythrough immediately after playback begins because of | |
| 926 // crbug.com/106480. | |
| 927 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed. | |
| 928 if (status_ == PIPELINE_OK) | |
| 929 buffering_state_cb_.Run(kPrerollCompleted); | |
| 930 | |
| 931 FinishSeek(); | |
| 932 | |
| 933 // If a playback rate change was requested during a seek, do it now that | |
| 934 // the seek has compelted. | |
| 935 if (playback_rate_change_pending_) { | |
| 936 playback_rate_change_pending_ = false; | |
| 937 PlaybackRateChangedTask(pending_playback_rate_); | |
| 938 } | |
| 939 | |
| 940 base::AutoLock auto_lock(lock_); | |
| 941 // We use audio stream to update the clock. So if there is such a stream, | |
| 942 // we pause the clock until we receive a valid timestamp. | |
| 943 waiting_for_clock_update_ = true; | |
| 944 if (!has_audio_) { | |
| 945 clock_->SetMaxTime(clock_->Duration()); | |
| 946 StartClockIfWaitingForTimeUpdate_Locked(); | |
| 947 } | |
| 948 | |
| 949 // Check if we have a pending stop request that needs to be honored. | |
| 950 if (!stop_cb_.is_null()) { | |
| 951 TearDownPipeline(); | |
| 952 } | |
| 953 } else { | |
| 954 NOTREACHED() << "Unexpected state: " << state_; | |
| 955 } | |
| 956 } | 884 } |
| 957 | 885 |
| 958 void Pipeline::TeardownStateTransitionTask() { | 886 void Pipeline::InitializeAudioDecoder(const PipelineStatusCB& done_cb) { |
| 959 DCHECK(tearing_down_); | 887 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 960 DCHECK(pending_callbacks_.get()) | |
| 961 << "Teardown state transitions must be completed via pending_callbacks_"; | |
| 962 pending_callbacks_.reset(); | |
| 963 | 888 |
| 964 switch (state_) { | 889 scoped_refptr<DemuxerStream> stream = |
| 965 case kStopping: | 890 demuxer_->GetStream(DemuxerStream::AUDIO); |
| 966 SetState(kStopped); | 891 DCHECK(stream); |
| 967 FinishDestroyingFiltersTask(); | |
| 968 break; | |
| 969 case kPausing: | |
| 970 SetState(kFlushing); | |
| 971 DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 972 break; | |
| 973 case kFlushing: | |
| 974 SetState(kStopping); | |
| 975 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 976 break; | |
| 977 | 892 |
| 978 case kCreated: | 893 filter_collection_->SelectAudioDecoder(&audio_decoder_); |
| 979 case kInitDemuxer: | 894 audio_decoder_->Initialize( |
| 980 case kInitAudioDecoder: | 895 stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this)); |
| 981 case kInitAudioRenderer: | |
| 982 case kInitVideoRenderer: | |
| 983 case kSeeking: | |
| 984 case kStarting: | |
| 985 case kStopped: | |
| 986 case kStarted: | |
| 987 NOTREACHED() << "Unexpected state for teardown: " << state_; | |
| 988 break; | |
| 989 // default: intentionally left out to force new states to cause compiler | |
| 990 // errors. | |
| 991 }; | |
| 992 } | 896 } |
| 993 | 897 |
| 994 void Pipeline::FinishDestroyingFiltersTask() { | 898 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { |
| 995 DCHECK(message_loop_->BelongsToCurrentThread()); | 899 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 996 DCHECK_EQ(state_, kStopped); | 900 DCHECK(audio_decoder_); |
| 997 | |
| 998 audio_renderer_ = NULL; | |
| 999 video_renderer_ = NULL; | |
| 1000 demuxer_ = NULL; | |
| 1001 tearing_down_ = false; | |
| 1002 { | |
| 1003 base::AutoLock l(lock_); | |
| 1004 running_ = false; | |
| 1005 } | |
| 1006 | |
| 1007 if (!IsPipelineOk() && !error_cb_.is_null()) | |
| 1008 error_cb_.Run(status_); | |
| 1009 | |
| 1010 if (!stop_cb_.is_null()) | |
| 1011 base::ResetAndReturn(&stop_cb_).Run(); | |
| 1012 } | |
| 1013 | |
| 1014 void Pipeline::InitializeDemuxer() { | |
| 1015 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1016 DCHECK(IsPipelineOk()); | |
| 1017 | |
| 1018 demuxer_ = filter_collection_->GetDemuxer(); | |
| 1019 if (!demuxer_) { | |
| 1020 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1021 return; | |
| 1022 } | |
| 1023 | |
| 1024 demuxer_->Initialize(this, base::Bind(&Pipeline::OnDemuxerInitialized, this)); | |
| 1025 } | |
| 1026 | |
| 1027 void Pipeline::OnDemuxerInitialized(PipelineStatus status) { | |
| 1028 if (!message_loop_->BelongsToCurrentThread()) { | |
| 1029 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 1030 &Pipeline::OnDemuxerInitialized, this, status)); | |
| 1031 return; | |
| 1032 } | |
| 1033 | |
| 1034 if (status != PIPELINE_OK) { | |
| 1035 SetError(status); | |
| 1036 return; | |
| 1037 } | |
| 1038 | |
| 1039 { | |
| 1040 base::AutoLock auto_lock(lock_); | |
| 1041 // We do not want to start the clock running. We only want to set the base | |
| 1042 // media time so our timestamp calculations will be correct. | |
| 1043 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); | |
| 1044 } | |
| 1045 | |
| 1046 OnFilterInitialize(PIPELINE_OK); | |
| 1047 } | |
| 1048 | |
| 1049 bool Pipeline::InitializeAudioDecoder( | |
| 1050 const scoped_refptr<Demuxer>& demuxer) { | |
| 1051 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1052 DCHECK(IsPipelineOk()); | |
| 1053 DCHECK(demuxer); | |
| 1054 | |
| 1055 scoped_refptr<DemuxerStream> stream = | |
| 1056 demuxer->GetStream(DemuxerStream::AUDIO); | |
| 1057 | |
| 1058 if (!stream) | |
| 1059 return false; | |
| 1060 | |
| 1061 filter_collection_->SelectAudioDecoder(&pipeline_init_state_->audio_decoder); | |
| 1062 | |
| 1063 if (!pipeline_init_state_->audio_decoder) { | |
| 1064 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1065 return false; | |
| 1066 } | |
| 1067 | |
| 1068 pipeline_init_state_->audio_decoder->Initialize( | |
| 1069 stream, | |
| 1070 base::Bind(&Pipeline::OnFilterInitialize, this), | |
| 1071 base::Bind(&Pipeline::OnUpdateStatistics, this)); | |
| 1072 return true; | |
| 1073 } | |
| 1074 | |
| 1075 bool Pipeline::InitializeAudioRenderer( | |
| 1076 const scoped_refptr<AudioDecoder>& decoder) { | |
| 1077 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1078 DCHECK(IsPipelineOk()); | |
| 1079 | |
| 1080 if (!decoder) | |
| 1081 return false; | |
| 1082 | 901 |
| 1083 filter_collection_->SelectAudioRenderer(&audio_renderer_); | 902 filter_collection_->SelectAudioRenderer(&audio_renderer_); |
| 1084 if (!audio_renderer_) { | |
| 1085 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1086 return false; | |
| 1087 } | |
| 1088 | |
| 1089 audio_renderer_->Initialize( | 903 audio_renderer_->Initialize( |
| 1090 decoder, | 904 audio_decoder_, |
| 1091 base::Bind(&Pipeline::OnFilterInitialize, this), | 905 done_cb, |
| 1092 base::Bind(&Pipeline::OnAudioUnderflow, this), | 906 base::Bind(&Pipeline::OnAudioUnderflow, this), |
| 1093 base::Bind(&Pipeline::OnAudioTimeUpdate, this), | 907 base::Bind(&Pipeline::OnAudioTimeUpdate, this), |
| 1094 base::Bind(&Pipeline::OnAudioRendererEnded, this), | 908 base::Bind(&Pipeline::OnAudioRendererEnded, this), |
| 1095 base::Bind(&Pipeline::OnAudioDisabled, this), | 909 base::Bind(&Pipeline::OnAudioDisabled, this), |
| 1096 base::Bind(&Pipeline::SetError, this)); | 910 base::Bind(&Pipeline::SetError, this)); |
| 1097 return true; | |
| 1098 } | 911 } |
| 1099 | 912 |
| 1100 bool Pipeline::InitializeVideoRenderer( | 913 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { |
| 1101 const scoped_refptr<DemuxerStream>& stream) { | |
| 1102 DCHECK(message_loop_->BelongsToCurrentThread()); | 914 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 1103 DCHECK(IsPipelineOk()); | |
| 1104 | 915 |
| 1105 if (!stream) | 916 scoped_refptr<DemuxerStream> stream = |
| 1106 return false; | 917 demuxer_->GetStream(DemuxerStream::VIDEO); |
| 918 DCHECK(stream); |
| 919 |
| 920 { |
| 921 // Get an initial natural size so we have something when we signal |
| 922 // the kHaveMetadata buffering state. |
| 923 base::AutoLock l(lock_); |
| 924 natural_size_ = stream->video_decoder_config().natural_size(); |
| 925 } |
| 1107 | 926 |
| 1108 filter_collection_->SelectVideoRenderer(&video_renderer_); | 927 filter_collection_->SelectVideoRenderer(&video_renderer_); |
| 1109 if (!video_renderer_) { | |
| 1110 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1111 return false; | |
| 1112 } | |
| 1113 | |
| 1114 video_renderer_->Initialize( | 928 video_renderer_->Initialize( |
| 1115 stream, | 929 stream, |
| 1116 *filter_collection_->GetVideoDecoders(), | 930 *filter_collection_->GetVideoDecoders(), |
| 1117 base::Bind(&Pipeline::OnFilterInitialize, this), | 931 done_cb, |
| 1118 base::Bind(&Pipeline::OnUpdateStatistics, this), | 932 base::Bind(&Pipeline::OnUpdateStatistics, this), |
| 1119 base::Bind(&Pipeline::OnVideoTimeUpdate, this), | 933 base::Bind(&Pipeline::OnVideoTimeUpdate, this), |
| 1120 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), | 934 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), |
| 1121 base::Bind(&Pipeline::OnVideoRendererEnded, this), | 935 base::Bind(&Pipeline::OnVideoRendererEnded, this), |
| 1122 base::Bind(&Pipeline::SetError, this), | 936 base::Bind(&Pipeline::SetError, this), |
| 1123 base::Bind(&Pipeline::GetMediaTime, this), | 937 base::Bind(&Pipeline::GetMediaTime, this), |
| 1124 base::Bind(&Pipeline::GetMediaDuration, this)); | 938 base::Bind(&Pipeline::GetMediaDuration, this)); |
| 1125 filter_collection_->GetVideoDecoders()->clear(); | 939 filter_collection_->GetVideoDecoders()->clear(); |
| 1126 return true; | |
| 1127 } | |
| 1128 | |
| 1129 void Pipeline::TearDownPipeline() { | |
| 1130 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1131 DCHECK_NE(kStopped, state_); | |
| 1132 | |
| 1133 // We're either... | |
| 1134 // 1) ...tearing down due to Stop() (it doesn't set tearing_down_) | |
| 1135 // 2) ...tearing down due to an error (it does set tearing_down_) | |
| 1136 // 3) ...tearing down due to an error and Stop() was called during that time | |
| 1137 DCHECK(!tearing_down_ || | |
| 1138 (tearing_down_ && status_ != PIPELINE_OK) || | |
| 1139 (tearing_down_ && !stop_cb_.is_null())); | |
| 1140 | |
| 1141 // Mark that we already start tearing down operation. | |
| 1142 tearing_down_ = true; | |
| 1143 | |
| 1144 // Cancel any pending operation so we can proceed with teardown. | |
| 1145 pending_callbacks_.reset(); | |
| 1146 | |
| 1147 switch (state_) { | |
| 1148 case kCreated: | |
| 1149 SetState(kStopped); | |
| 1150 // Need to put this in the message loop to make sure that it comes | |
| 1151 // after any pending callback tasks that are already queued. | |
| 1152 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 1153 &Pipeline::FinishDestroyingFiltersTask, this)); | |
| 1154 break; | |
| 1155 | |
| 1156 case kInitDemuxer: | |
| 1157 case kInitAudioDecoder: | |
| 1158 case kInitAudioRenderer: | |
| 1159 case kInitVideoRenderer: | |
| 1160 // Make it look like initialization was successful. | |
| 1161 filter_collection_.reset(); | |
| 1162 pipeline_init_state_.reset(); | |
| 1163 | |
| 1164 SetState(kStopping); | |
| 1165 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 1166 | |
| 1167 FinishSeek(); | |
| 1168 break; | |
| 1169 | |
| 1170 case kPausing: | |
| 1171 case kSeeking: | |
| 1172 case kFlushing: | |
| 1173 case kStarting: | |
| 1174 SetState(kStopping); | |
| 1175 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 1176 | |
| 1177 if (seek_pending_) | |
| 1178 FinishSeek(); | |
| 1179 | |
| 1180 break; | |
| 1181 | |
| 1182 case kStarted: | |
| 1183 SetState(kPausing); | |
| 1184 DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 1185 break; | |
| 1186 | |
| 1187 case kStopping: | |
| 1188 case kStopped: | |
| 1189 NOTREACHED() << "Unexpected state for teardown: " << state_; | |
| 1190 break; | |
| 1191 // default: intentionally left out to force new states to cause compiler | |
| 1192 // errors. | |
| 1193 }; | |
| 1194 } | |
| 1195 | |
| 1196 void Pipeline::DoSeek(base::TimeDelta seek_timestamp, | |
| 1197 bool skip_demuxer_seek, | |
| 1198 const PipelineStatusCB& done_cb) { | |
| 1199 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1200 DCHECK(!pending_callbacks_.get()); | |
| 1201 SerialRunner::Queue bound_fns; | |
| 1202 | |
| 1203 if (!skip_demuxer_seek) { | |
| 1204 bound_fns.Push(base::Bind( | |
| 1205 &Demuxer::Seek, demuxer_, seek_timestamp)); | |
| 1206 } | |
| 1207 | |
| 1208 if (audio_renderer_) { | |
| 1209 bound_fns.Push(base::Bind( | |
| 1210 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); | |
| 1211 } | |
| 1212 | |
| 1213 if (video_renderer_) { | |
| 1214 bound_fns.Push(base::Bind( | |
| 1215 &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); | |
| 1216 } | |
| 1217 | |
| 1218 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | |
| 1219 } | 940 } |
| 1220 | 941 |
| 1221 void Pipeline::OnAudioUnderflow() { | 942 void Pipeline::OnAudioUnderflow() { |
| 1222 if (!message_loop_->BelongsToCurrentThread()) { | 943 if (!message_loop_->BelongsToCurrentThread()) { |
| 1223 message_loop_->PostTask(FROM_HERE, base::Bind( | 944 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 1224 &Pipeline::OnAudioUnderflow, this)); | 945 &Pipeline::OnAudioUnderflow, this)); |
| 1225 return; | 946 return; |
| 1226 } | 947 } |
| 1227 | 948 |
| 1228 if (state_ != kStarted) | 949 if (state_ != kStarted) |
| 1229 return; | 950 return; |
| 1230 | 951 |
| 1231 if (audio_renderer_) | 952 if (audio_renderer_) |
| 1232 audio_renderer_->ResumeAfterUnderflow(true); | 953 audio_renderer_->ResumeAfterUnderflow(true); |
| 1233 } | 954 } |
| 1234 | 955 |
| 1235 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 956 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
| 1236 lock_.AssertAcquired(); | 957 lock_.AssertAcquired(); |
| 1237 if (!waiting_for_clock_update_) | 958 if (!waiting_for_clock_update_) |
| 1238 return; | 959 return; |
| 1239 | 960 |
| 1240 waiting_for_clock_update_ = false; | 961 waiting_for_clock_update_ = false; |
| 1241 clock_->Play(); | 962 clock_->Play(); |
| 1242 } | 963 } |
| 1243 | 964 |
| 1244 } // namespace media | 965 } // namespace media |
| OLD | NEW |