| OLD | NEW |
| 1 // Copyright (c) 2008-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008-2009 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 // TODO(scherkus): clean up PipelineImpl... too many crazy function names, | 5 // TODO(scherkus): clean up PipelineImpl... too many crazy function names, |
| 6 // potential deadlocks, etc... | 6 // potential deadlocks, etc... |
| 7 | 7 |
| 8 #include "base/compiler_specific.h" | 8 #include "base/compiler_specific.h" |
| 9 #include "base/condition_variable.h" | 9 #include "base/condition_variable.h" |
| 10 #include "base/stl_util-inl.h" | 10 #include "base/stl_util-inl.h" |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 CHECK(*count >= 0); | 66 CHECK(*count >= 0); |
| 67 if (*count == 0) { | 67 if (*count == 0) { |
| 68 cond_var->Signal(); | 68 cond_var->Signal(); |
| 69 } | 69 } |
| 70 } | 70 } |
| 71 | 71 |
| 72 } // namespace | 72 } // namespace |
| 73 | 73 |
| 74 PipelineImpl::PipelineImpl(MessageLoop* message_loop) | 74 PipelineImpl::PipelineImpl(MessageLoop* message_loop) |
| 75 : message_loop_(message_loop), | 75 : message_loop_(message_loop), |
| 76 state_(kCreated) { | 76 state_(kCreated), |
| 77 remaining_transitions_(0) { |
| 77 ResetState(); | 78 ResetState(); |
| 78 } | 79 } |
| 79 | 80 |
| 80 PipelineImpl::~PipelineImpl() { | 81 PipelineImpl::~PipelineImpl() { |
| 81 AutoLock auto_lock(lock_); | 82 AutoLock auto_lock(lock_); |
| 82 DCHECK(!running_) << "Stop() must complete before destroying object"; | 83 DCHECK(!running_) << "Stop() must complete before destroying object"; |
| 83 } | 84 } |
| 84 | 85 |
| 85 // Creates the PipelineInternal and calls it's start method. | 86 // Creates the PipelineInternal and calls it's start method. |
| 86 bool PipelineImpl::Start(FilterFactory* factory, | 87 bool PipelineImpl::Start(FilterFactory* factory, |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 bool PipelineImpl::IsRunning() const { | 136 bool PipelineImpl::IsRunning() const { |
| 136 AutoLock auto_lock(lock_); | 137 AutoLock auto_lock(lock_); |
| 137 return running_; | 138 return running_; |
| 138 } | 139 } |
| 139 | 140 |
| 140 bool PipelineImpl::IsInitialized() const { | 141 bool PipelineImpl::IsInitialized() const { |
| 141 // TODO(scherkus): perhaps replace this with a bool that is set/get under the | 142 // TODO(scherkus): perhaps replace this with a bool that is set/get under the |
| 142 // lock, because this is breaching the contract that |state_| is only accessed | 143 // lock, because this is breaching the contract that |state_| is only accessed |
| 143 // on |message_loop_|. | 144 // on |message_loop_|. |
| 144 AutoLock auto_lock(lock_); | 145 AutoLock auto_lock(lock_); |
| 145 return state_ == kStarted; | 146 switch (state_) { |
| 147 case kPausing: |
| 148 case kSeeking: |
| 149 case kStarting: |
| 150 case kStarted: |
| 151 return true; |
| 152 default: |
| 153 return false; |
| 154 } |
| 146 } | 155 } |
| 147 | 156 |
| 148 bool PipelineImpl::IsRendered(const std::string& major_mime_type) const { | 157 bool PipelineImpl::IsRendered(const std::string& major_mime_type) const { |
| 149 AutoLock auto_lock(lock_); | 158 AutoLock auto_lock(lock_); |
| 150 bool is_rendered = (rendered_mime_types_.find(major_mime_type) != | 159 bool is_rendered = (rendered_mime_types_.find(major_mime_type) != |
| 151 rendered_mime_types_.end()); | 160 rendered_mime_types_.end()); |
| 152 return is_rendered; | 161 return is_rendered; |
| 153 } | 162 } |
| 154 | 163 |
| 155 float PipelineImpl::GetPlaybackRate() const { | 164 float PipelineImpl::GetPlaybackRate() const { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 bool PipelineImpl::IsPipelineInitializing() { | 260 bool PipelineImpl::IsPipelineInitializing() { |
| 252 DCHECK_EQ(MessageLoop::current(), message_loop_); | 261 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 253 return state_ == kInitDataSource || | 262 return state_ == kInitDataSource || |
| 254 state_ == kInitDemuxer || | 263 state_ == kInitDemuxer || |
| 255 state_ == kInitAudioDecoder || | 264 state_ == kInitAudioDecoder || |
| 256 state_ == kInitAudioRenderer || | 265 state_ == kInitAudioRenderer || |
| 257 state_ == kInitVideoDecoder || | 266 state_ == kInitVideoDecoder || |
| 258 state_ == kInitVideoRenderer; | 267 state_ == kInitVideoRenderer; |
| 259 } | 268 } |
| 260 | 269 |
| 270 // static |
| 271 bool PipelineImpl::StateTransitionsToStarted(State state) { |
| 272 return state == kPausing || state == kSeeking || state == kStarting; |
| 273 } |
| 274 |
| 275 // static |
| 276 PipelineImpl::State PipelineImpl::FindNextState(State current) { |
| 277 // TODO(scherkus): refactor InitializeTask() to make use of this function. |
| 278 if (current == kPausing) |
| 279 return kSeeking; |
| 280 if (current == kSeeking) |
| 281 return kStarting; |
| 282 if (current == kStarting) |
| 283 return kStarted; |
| 284 return current; |
| 285 } |
| 286 |
| 261 void PipelineImpl::SetError(PipelineError error) { | 287 void PipelineImpl::SetError(PipelineError error) { |
| 262 DCHECK(IsRunning()); | 288 DCHECK(IsRunning()); |
| 263 DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!"; | 289 DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!"; |
| 264 LOG(INFO) << "Media pipeline error: " << error; | 290 LOG(INFO) << "Media pipeline error: " << error; |
| 265 | 291 |
| 266 AutoLock auto_lock(lock_); | 292 AutoLock auto_lock(lock_); |
| 267 error_ = error; | 293 error_ = error; |
| 268 message_loop_->PostTask(FROM_HERE, | 294 message_loop_->PostTask(FROM_HERE, |
| 269 NewRunnableMethod(this, &PipelineImpl::ErrorChangedTask, error)); | 295 NewRunnableMethod(this, &PipelineImpl::ErrorChangedTask, error)); |
| 270 } | 296 } |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 } | 350 } |
| 325 | 351 |
| 326 // Called from any thread. | 352 // Called from any thread. |
| 327 void PipelineImpl::OnFilterInitialize() { | 353 void PipelineImpl::OnFilterInitialize() { |
| 328 // Continue the initialize task by proceeding to the next stage. | 354 // Continue the initialize task by proceeding to the next stage. |
| 329 message_loop_->PostTask(FROM_HERE, | 355 message_loop_->PostTask(FROM_HERE, |
| 330 NewRunnableMethod(this, &PipelineImpl::InitializeTask)); | 356 NewRunnableMethod(this, &PipelineImpl::InitializeTask)); |
| 331 } | 357 } |
| 332 | 358 |
| 333 // Called from any thread. | 359 // Called from any thread. |
| 334 void PipelineImpl::OnFilterSeek() { | 360 void PipelineImpl::OnFilterStateTransition() { |
| 335 // TODO(scherkus): have PipelineInternal wait to receive replies from every | 361 // Continue walking down the filters. |
| 336 // filter before calling the client's |seek_callback_|. | 362 message_loop_->PostTask(FROM_HERE, |
| 363 NewRunnableMethod(this, &PipelineImpl::FilterStateTransitionTask)); |
| 337 } | 364 } |
| 338 | 365 |
| 339 void PipelineImpl::StartTask(FilterFactory* filter_factory, | 366 void PipelineImpl::StartTask(FilterFactory* filter_factory, |
| 340 const std::string& url, | 367 const std::string& url, |
| 341 PipelineCallback* start_callback) { | 368 PipelineCallback* start_callback) { |
| 342 DCHECK_EQ(MessageLoop::current(), message_loop_); | 369 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 343 DCHECK_EQ(kCreated, state_); | 370 DCHECK_EQ(kCreated, state_); |
| 344 filter_factory_ = filter_factory; | 371 filter_factory_ = filter_factory; |
| 345 url_ = url; | 372 url_ = url; |
| 346 start_callback_.reset(start_callback); | 373 seek_callback_.reset(start_callback); |
| 347 | 374 |
| 348 // Kick off initialization. | 375 // Kick off initialization. |
| 349 InitializeTask(); | 376 InitializeTask(); |
| 350 } | 377 } |
| 351 | 378 |
| 352 // Main initialization method called on the pipeline thread. This code attempts | 379 // Main initialization method called on the pipeline thread. This code attempts |
| 353 // to use the specified filter factory to build a pipeline. | 380 // to use the specified filter factory to build a pipeline. |
| 354 // Initialization step performed in this method depends on current state of this | 381 // Initialization step performed in this method depends on current state of this |
| 355 // object, indicated by |state_|. After each step of initialization, this | 382 // object, indicated by |state_|. After each step of initialization, this |
| 356 // object transits to the next stage. It starts by creating a DataSource, | 383 // object transits to the next stage. It starts by creating a DataSource, |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 return; | 450 return; |
| 424 } | 451 } |
| 425 } | 452 } |
| 426 | 453 |
| 427 if (state_ == kInitVideoRenderer) { | 454 if (state_ == kInitVideoRenderer) { |
| 428 if (!IsPipelineOk() || !HasRenderedMimeTypes()) { | 455 if (!IsPipelineOk() || !HasRenderedMimeTypes()) { |
| 429 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); | 456 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); |
| 430 return; | 457 return; |
| 431 } | 458 } |
| 432 | 459 |
| 433 // Initialization was successful, set the volume and playback rate. | 460 // We've successfully created and initialized every filter, so we no longer |
| 461 // need the filter factory. |
| 462 filter_factory_ = NULL; |
| 463 |
| 464 // Initialization was successful, we are now considered paused, so it's safe |
| 465 // to set the initial playback rate and volume. |
| 434 PlaybackRateChangedTask(GetPlaybackRate()); | 466 PlaybackRateChangedTask(GetPlaybackRate()); |
| 435 VolumeChangedTask(GetVolume()); | 467 VolumeChangedTask(GetVolume()); |
| 436 | 468 |
| 437 state_ = kStarted; | 469 // Fire the initial seek request to get the filters to preroll. |
| 438 filter_factory_ = NULL; | 470 state_ = kSeeking; |
| 439 if (start_callback_.get()) { | 471 remaining_transitions_ = filters_.size(); |
| 440 start_callback_->Run(); | 472 seek_timestamp_ = base::TimeDelta(); |
| 441 start_callback_.reset(); | 473 filters_.front()->Seek(seek_timestamp_, |
| 442 } | 474 NewCallback(this, &PipelineImpl::OnFilterStateTransition)); |
| 443 } | 475 } |
| 444 } | 476 } |
| 445 | 477 |
| 446 // This method is called as a result of the client calling Pipeline::Stop() or | 478 // This method is called as a result of the client calling Pipeline::Stop() or |
| 447 // as the result of an error condition. If there is no error, then set the | 479 // as the result of an error condition. If there is no error, then set the |
| 448 // pipeline's |error_| member to PIPELINE_STOPPING. We stop the filters in the | 480 // pipeline's |error_| member to PIPELINE_STOPPING. We stop the filters in the |
| 449 // reverse order. | 481 // reverse order. |
| 450 // | 482 // |
| 451 // TODO(scherkus): beware! this can get posted multiple times since we post | 483 // TODO(scherkus): beware! this can get posted multiple times since we post |
| 452 // Stop() tasks even if we've already stopped. Perhaps this should no-op for | 484 // Stop() tasks even if we've already stopped. Perhaps this should no-op for |
| (...skipping 30 matching lines...) Expand all Loading... |
| 483 // Suppress executing additional error logic. | 515 // Suppress executing additional error logic. |
| 484 // TODO(hclam): Remove the condition for kStopped. It is there only because | 516 // TODO(hclam): Remove the condition for kStopped. It is there only because |
| 485 // FFmpegDemuxer submits a read error while reading after it is called to | 517 // FFmpegDemuxer submits a read error while reading after it is called to |
| 486 // stop. After FFmpegDemuxer is cleaned up we should remove this condition | 518 // stop. After FFmpegDemuxer is cleaned up we should remove this condition |
| 487 // and add an extra assert. | 519 // and add an extra assert. |
| 488 if (state_ == kError || state_ == kStopped) { | 520 if (state_ == kError || state_ == kStopped) { |
| 489 return; | 521 return; |
| 490 } | 522 } |
| 491 | 523 |
| 492 // Notify the client that starting did not complete, if necessary. | 524 // Notify the client that starting did not complete, if necessary. |
| 493 if (IsPipelineInitializing() && start_callback_.get()) { | 525 if (IsPipelineInitializing() && seek_callback_.get()) { |
| 494 start_callback_->Run(); | 526 seek_callback_->Run(); |
| 495 } | 527 } |
| 496 start_callback_.reset(); | 528 seek_callback_.reset(); |
| 497 filter_factory_ = NULL; | 529 filter_factory_ = NULL; |
| 498 | 530 |
| 499 // We no longer need to examine our previous state, set it to stopped. | 531 // We no longer need to examine our previous state, set it to stopped. |
| 500 state_ = kError; | 532 state_ = kError; |
| 501 | 533 |
| 502 // Destroy every filter and reset the pipeline as well. | 534 // Destroy every filter and reset the pipeline as well. |
| 503 DestroyFilters(); | 535 DestroyFilters(); |
| 504 } | 536 } |
| 505 | 537 |
| 506 void PipelineImpl::PlaybackRateChangedTask(float playback_rate) { | 538 void PipelineImpl::PlaybackRateChangedTask(float playback_rate) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 518 scoped_refptr<AudioRenderer> audio_renderer; | 550 scoped_refptr<AudioRenderer> audio_renderer; |
| 519 GetFilter(&audio_renderer); | 551 GetFilter(&audio_renderer); |
| 520 if (audio_renderer) { | 552 if (audio_renderer) { |
| 521 audio_renderer->SetVolume(volume); | 553 audio_renderer->SetVolume(volume); |
| 522 } | 554 } |
| 523 } | 555 } |
| 524 | 556 |
| 525 void PipelineImpl::SeekTask(base::TimeDelta time, | 557 void PipelineImpl::SeekTask(base::TimeDelta time, |
| 526 PipelineCallback* seek_callback) { | 558 PipelineCallback* seek_callback) { |
| 527 DCHECK_EQ(MessageLoop::current(), message_loop_); | 559 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 528 seek_callback_.reset(seek_callback); | |
| 529 | 560 |
| 530 // Supress seeking if we haven't fully started. | 561 // Suppress seeking if we're not fully started. |
| 531 if (state_ != kStarted) { | 562 if (state_ != kStarted) { |
| 563 // TODO(scherkus): should we run the callback? I'm tempted to say the API |
| 564 // will only execute the first Seek() request. |
| 565 LOG(INFO) << "Media pipeline is not in started state, ignoring seek to " |
| 566 << time.InMicroseconds(); |
| 567 delete seek_callback; |
| 532 return; | 568 return; |
| 533 } | 569 } |
| 534 | 570 |
| 535 for (FilterVector::iterator iter = filters_.begin(); | 571 // We'll need to pause every filter before seeking. The state transition |
| 536 iter != filters_.end(); | 572 // is as follows: |
| 537 ++iter) { | 573 // kStarted |
| 538 (*iter)->Seek(time, NewCallback(this, &PipelineImpl::OnFilterSeek)); | 574 // kPausing (for each filter) |
| 575 // kSeeking (for each filter) |
| 576 // kStarting (for each filter) |
| 577 // kStarted |
| 578 state_ = kPausing; |
| 579 seek_timestamp_ = time; |
| 580 seek_callback_.reset(seek_callback); |
| 581 remaining_transitions_ = filters_.size(); |
| 582 |
| 583 // Kick off seeking! |
| 584 filters_.front()->Pause( |
| 585 NewCallback(this, &PipelineImpl::OnFilterStateTransition)); |
| 586 } |
| 587 |
| 588 void PipelineImpl::FilterStateTransitionTask() { |
| 589 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 590 |
| 591 if (!StateTransitionsToStarted(state_)) { |
| 592 NOTREACHED() << "Invalid current state: " << state_; |
| 593 SetError(PIPELINE_ERROR_ABORT); |
| 594 return; |
| 539 } | 595 } |
| 540 | 596 |
| 541 // TODO(hclam): we should set the time when the above seek operations were all | 597 // Decrement the number of remaining transitions, making sure to transition |
| 542 // successful and first frame/packet at the desired time is decoded. I'm | 598 // to the next state if needed. |
| 543 // setting the time here because once we do the callback the user can ask for | 599 CHECK(remaining_transitions_ <= filters_.size()); |
| 544 // current time immediately, which is the old time. In order to get rid this | 600 CHECK(remaining_transitions_ > 0u); |
| 545 // little glitch, we either assume the seek was successful and time is updated | 601 if (--remaining_transitions_ == 0) { |
| 546 // immediately here or we set time and do callback when we have new | 602 state_ = FindNextState(state_); |
| 547 // frames/packets. | 603 if (StateTransitionsToStarted(state_)) { |
| 548 SetTime(time); | 604 remaining_transitions_ = filters_.size(); |
| 549 if (seek_callback_.get()) { | 605 } |
| 550 seek_callback_->Run(); | 606 } |
| 551 seek_callback_.reset(); | 607 |
| 608 // Carry out the action for the current state. |
| 609 if (StateTransitionsToStarted(state_)) { |
| 610 MediaFilter* filter = filters_[filters_.size() - remaining_transitions_]; |
| 611 if (state_ == kPausing) { |
| 612 filter->Pause(NewCallback(this, &PipelineImpl::OnFilterStateTransition)); |
| 613 } else if (state_ == kSeeking) { |
| 614 filter->Seek(seek_timestamp_, |
| 615 NewCallback(this, &PipelineImpl::OnFilterStateTransition)); |
| 616 } else if (state_ == kStarting) { |
| 617 filter->Play(NewCallback(this, &PipelineImpl::OnFilterStateTransition)); |
| 618 } else { |
| 619 NOTREACHED(); |
| 620 } |
| 621 } else if (state_ == kStarted) { |
| 622 // We've completed the seek, update the time. |
| 623 SetTime(seek_timestamp_); |
| 624 |
| 625 // Execute the seek callback, if present. Note that this might be the |
| 626 // initial callback passed into Start(). |
| 627 if (seek_callback_.get()) { |
| 628 seek_callback_->Run(); |
| 629 seek_callback_.reset(); |
| 630 } |
| 631 |
| 632 // Finally, reset our seeking timestamp back to zero. |
| 633 seek_timestamp_ = base::TimeDelta(); |
| 634 } else { |
| 635 NOTREACHED(); |
| 552 } | 636 } |
| 553 } | 637 } |
| 554 | 638 |
| 555 template <class Filter, class Source> | 639 template <class Filter, class Source> |
| 556 void PipelineImpl::CreateFilter(FilterFactory* filter_factory, | 640 void PipelineImpl::CreateFilter(FilterFactory* filter_factory, |
| 557 Source source, | 641 Source source, |
| 558 const MediaFormat& media_format) { | 642 const MediaFormat& media_format) { |
| 559 DCHECK_EQ(MessageLoop::current(), message_loop_); | 643 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 560 DCHECK(IsPipelineOk()); | 644 DCHECK(IsPipelineOk()); |
| 561 | 645 |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 708 | 792 |
| 709 // Reset the pipeline, which will decrement a reference to this object. | 793 // Reset the pipeline, which will decrement a reference to this object. |
| 710 // We will get destroyed as soon as the remaining tasks finish executing. | 794 // We will get destroyed as soon as the remaining tasks finish executing. |
| 711 // To be safe, we'll set our pipeline reference to NULL. | 795 // To be safe, we'll set our pipeline reference to NULL. |
| 712 filters_.clear(); | 796 filters_.clear(); |
| 713 filter_types_.clear(); | 797 filter_types_.clear(); |
| 714 STLDeleteElements(&filter_threads_); | 798 STLDeleteElements(&filter_threads_); |
| 715 } | 799 } |
| 716 | 800 |
| 717 } // namespace media | 801 } // namespace media |
| OLD | NEW |