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

Side by Side Diff: media/base/pipeline_impl.cc

Issue 159476: Merge 21611 - Implemented proper pausethenseek behaviour for the media pipeli... (Closed) Base URL: svn://chrome-svn/chrome/branches/195/src/
Patch Set: Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/base/pipeline_impl.h ('k') | media/base/pipeline_impl_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Modified: svn:mergeinfo
Merged /trunk/src/media/base/pipeline_impl.cc:r21611
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « media/base/pipeline_impl.h ('k') | media/base/pipeline_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698