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

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

Issue 6179006: Fix flaky behavior in pipeline teardown. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Apply changes suggested in code review Created 9 years, 11 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') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/callback.h" 8 #include "base/callback.h"
9 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/stl_util-inl.h" 10 #include "base/stl_util-inl.h"
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after
293 return current_bytes_; 293 return current_bytes_;
294 } 294 }
295 295
296 void PipelineImpl::ResetState() { 296 void PipelineImpl::ResetState() {
297 AutoLock auto_lock(lock_); 297 AutoLock auto_lock(lock_);
298 const base::TimeDelta kZero; 298 const base::TimeDelta kZero;
299 running_ = false; 299 running_ = false;
300 stop_pending_ = false; 300 stop_pending_ = false;
301 seek_pending_ = false; 301 seek_pending_ = false;
302 tearing_down_ = false; 302 tearing_down_ = false;
303 error_caused_teardown_ = false;
303 duration_ = kZero; 304 duration_ = kZero;
304 buffered_time_ = kZero; 305 buffered_time_ = kZero;
305 buffered_bytes_ = 0; 306 buffered_bytes_ = 0;
306 streaming_ = false; 307 streaming_ = false;
307 loaded_ = false; 308 loaded_ = false;
308 total_bytes_ = 0; 309 total_bytes_ = 0;
309 video_width_ = 0; 310 video_width_ = 0;
310 video_height_ = 0; 311 video_height_ = 0;
311 volume_ = 1.0f; 312 volume_ = 1.0f;
312 playback_rate_ = 0.0f; 313 playback_rate_ = 0.0f;
313 error_ = PIPELINE_OK; 314 error_ = PIPELINE_OK;
314 waiting_for_clock_update_ = false; 315 waiting_for_clock_update_ = false;
315 audio_disabled_ = false; 316 audio_disabled_ = false;
316 clock_->SetTime(kZero); 317 clock_->SetTime(kZero);
317 rendered_mime_types_.clear(); 318 rendered_mime_types_.clear();
318 } 319 }
319 320
320 void PipelineImpl::set_state(State next_state) { 321 void PipelineImpl::set_state(State next_state) {
321 state_ = next_state; 322 state_ = next_state;
322 } 323 }
323 324
324 bool PipelineImpl::IsPipelineOk() { 325 bool PipelineImpl::IsPipelineOk() {
325 return PIPELINE_OK == GetError(); 326 return PIPELINE_OK == GetError();
326 } 327 }
327 328
328 bool PipelineImpl::IsPipelineInitializing() {
329 DCHECK_EQ(MessageLoop::current(), message_loop_);
330 return state_ == kInitDataSource ||
331 state_ == kInitDemuxer ||
332 state_ == kInitAudioDecoder ||
333 state_ == kInitAudioRenderer ||
334 state_ == kInitVideoDecoder ||
335 state_ == kInitVideoRenderer;
336 }
337
338 bool PipelineImpl::IsPipelineStopped() { 329 bool PipelineImpl::IsPipelineStopped() {
339 DCHECK_EQ(MessageLoop::current(), message_loop_); 330 DCHECK_EQ(MessageLoop::current(), message_loop_);
340 return state_ == kStopped || state_ == kError; 331 return state_ == kStopped || state_ == kError;
341 } 332 }
342 333
343 bool PipelineImpl::IsPipelineTearingDown() { 334 bool PipelineImpl::IsPipelineTearingDown() {
344 DCHECK_EQ(MessageLoop::current(), message_loop_); 335 DCHECK_EQ(MessageLoop::current(), message_loop_);
345 return tearing_down_; 336 return tearing_down_;
346 } 337 }
347 338
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 } else if (current == kFlushing) { 377 } else if (current == kFlushing) {
387 // We will always honor Seek() before Stop(). This is based on the 378 // We will always honor Seek() before Stop(). This is based on the
388 // assumption that we never accept Seek() after Stop(). 379 // assumption that we never accept Seek() after Stop().
389 DCHECK(IsPipelineSeeking() || IsPipelineStopPending()); 380 DCHECK(IsPipelineSeeking() || IsPipelineStopPending());
390 return IsPipelineSeeking() ? kSeeking : kStopping; 381 return IsPipelineSeeking() ? kSeeking : kStopping;
391 } else if (current == kSeeking) { 382 } else if (current == kSeeking) {
392 return kStarting; 383 return kStarting;
393 } else if (current == kStarting) { 384 } else if (current == kStarting) {
394 return kStarted; 385 return kStarted;
395 } else if (current == kStopping) { 386 } else if (current == kStopping) {
396 return kStopped; 387 return error_caused_teardown_ ? kError : kStopped;
397 } else { 388 } else {
398 return current; 389 return current;
399 } 390 }
400 } 391 }
401 392
402 void PipelineImpl::SetError(PipelineError error) { 393 void PipelineImpl::SetError(PipelineError error) {
403 DCHECK(IsRunning()); 394 DCHECK(IsRunning());
404 DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!"; 395 DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!";
405 VLOG(1) << "Media pipeline error: " << error; 396 VLOG(1) << "Media pipeline error: " << error;
406 397
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
565 void PipelineImpl::InitializeTask() { 556 void PipelineImpl::InitializeTask() {
566 DCHECK_EQ(MessageLoop::current(), message_loop_); 557 DCHECK_EQ(MessageLoop::current(), message_loop_);
567 558
568 // If we have received the stop or error signal, return immediately. 559 // If we have received the stop or error signal, return immediately.
569 if (IsPipelineStopPending() || 560 if (IsPipelineStopPending() ||
570 IsPipelineStopped() || 561 IsPipelineStopped() ||
571 PIPELINE_OK != GetError()) { 562 PIPELINE_OK != GetError()) {
572 return; 563 return;
573 } 564 }
574 565
575 DCHECK(state_ == kCreated || IsPipelineInitializing()); 566 DCHECK(state_ == kCreated ||
567 state_ == kInitDataSource ||
568 state_ == kInitDemuxer ||
569 state_ == kInitAudioDecoder ||
570 state_ == kInitAudioRenderer ||
571 state_ == kInitVideoDecoder ||
572 state_ == kInitVideoRenderer);
576 573
577 // Just created, create data source. 574 // Just created, create data source.
578 if (state_ == kCreated) { 575 if (state_ == kCreated) {
579 set_state(kInitDataSource); 576 set_state(kInitDataSource);
580 pipeline_init_state_.reset(new PipelineInitState()); 577 pipeline_init_state_.reset(new PipelineInitState());
581 pipeline_init_state_->composite_ = new CompositeFilter(message_loop_); 578 pipeline_init_state_->composite_ = new CompositeFilter(message_loop_);
582 pipeline_init_state_->composite_->set_host(this); 579 pipeline_init_state_->composite_->set_host(this);
583 580
584 InitializeDataSource(); 581 InitializeDataSource();
585 return; 582 return;
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
663 660
664 // This method is called as a result of the client calling Pipeline::Stop() or 661 // This method is called as a result of the client calling Pipeline::Stop() or
665 // as the result of an error condition. 662 // as the result of an error condition.
666 // We stop the filters in the reverse order. 663 // We stop the filters in the reverse order.
667 // 664 //
668 // TODO(scherkus): beware! this can get posted multiple times since we post 665 // TODO(scherkus): beware! this can get posted multiple times since we post
669 // Stop() tasks even if we've already stopped. Perhaps this should no-op for 666 // Stop() tasks even if we've already stopped. Perhaps this should no-op for
670 // additional calls, however most of this logic will be changing. 667 // additional calls, however most of this logic will be changing.
671 void PipelineImpl::StopTask(PipelineCallback* stop_callback) { 668 void PipelineImpl::StopTask(PipelineCallback* stop_callback) {
672 DCHECK_EQ(MessageLoop::current(), message_loop_); 669 DCHECK_EQ(MessageLoop::current(), message_loop_);
673 PipelineError error = GetError(); 670 DCHECK(!IsPipelineStopPending());
671 DCHECK_NE(state_, kStopped);
674 672
675 if (state_ == kStopped || (IsPipelineStopPending() && error == PIPELINE_OK)) { 673 if (state_ == kStopped) {
676 // If we are already stopped or stopping normally, return immediately. 674 // Already stopped so just run callback.
675 stop_callback->Run();
677 delete stop_callback; 676 delete stop_callback;
678 return; 677 return;
679 } else if (state_ == kError || 678 }
680 (IsPipelineStopPending() && error != PIPELINE_OK)) { 679
680 if (IsPipelineTearingDown() && error_caused_teardown_) {
681 // If we are stopping due to SetError(), stop normally instead of 681 // If we are stopping due to SetError(), stop normally instead of
682 // going to error state. 682 // going to error state and calling |error_callback_|. This converts
683 // the teardown in progress from an error teardown into one that acts
684 // like the error never occurred.
683 AutoLock auto_lock(lock_); 685 AutoLock auto_lock(lock_);
684 error_ = PIPELINE_OK; 686 error_ = PIPELINE_OK;
687 error_caused_teardown_ = false;
685 } 688 }
686 689
687 stop_callback_.reset(stop_callback); 690 stop_callback_.reset(stop_callback);
688 691
689 stop_pending_ = true; 692 stop_pending_ = true;
690 if (!IsPipelineSeeking()) { 693 if (!IsPipelineSeeking() && !IsPipelineTearingDown()) {
691 // We will tear down pipeline immediately when there is no seek operation 694 // We will tear down pipeline immediately when there is no seek operation
692 // pending. This should include the case where we are partially initialized. 695 // pending and no teardown in progress. This should include the case where
693 // Ideally this case should use SetError() rather than Stop() to tear down. 696 // we are partially initialized.
694 DCHECK(!IsPipelineTearingDown());
695 TearDownPipeline(); 697 TearDownPipeline();
696 } 698 }
697 } 699 }
698 700
699 void PipelineImpl::ErrorChangedTask(PipelineError error) { 701 void PipelineImpl::ErrorChangedTask(PipelineError error) {
700 DCHECK_EQ(MessageLoop::current(), message_loop_); 702 DCHECK_EQ(MessageLoop::current(), message_loop_);
701 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; 703 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
702 704
703 // Suppress executing additional error logic. Note that if we are currently 705 // Suppress executing additional error logic. Note that if we are currently
704 // performing a normal stop, then we return immediately and continue the 706 // performing a normal stop, then we return immediately and continue the
705 // normal stop. 707 // normal stop.
706 if (IsPipelineStopped() || IsPipelineTearingDown()) { 708 if (IsPipelineStopped() || IsPipelineTearingDown()) {
707 return; 709 return;
708 } 710 }
709 711
710 AutoLock auto_lock(lock_); 712 AutoLock auto_lock(lock_);
711 error_ = error; 713 error_ = error;
712 714
715 error_caused_teardown_ = true;
713 TearDownPipeline(); 716 TearDownPipeline();
714 } 717 }
715 718
716 void PipelineImpl::PlaybackRateChangedTask(float playback_rate) { 719 void PipelineImpl::PlaybackRateChangedTask(float playback_rate) {
717 DCHECK_EQ(MessageLoop::current(), message_loop_); 720 DCHECK_EQ(MessageLoop::current(), message_loop_);
718 { 721 {
719 AutoLock auto_lock(lock_); 722 AutoLock auto_lock(lock_);
720 clock_->SetPlaybackRate(playback_rate); 723 clock_->SetPlaybackRate(playback_rate);
721 } 724 }
722 725
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
908 void PipelineImpl::FinishDestroyingFiltersTask() { 911 void PipelineImpl::FinishDestroyingFiltersTask() {
909 DCHECK_EQ(MessageLoop::current(), message_loop_); 912 DCHECK_EQ(MessageLoop::current(), message_loop_);
910 DCHECK(IsPipelineStopped()); 913 DCHECK(IsPipelineStopped());
911 914
912 // Clear renderer references. 915 // Clear renderer references.
913 audio_renderer_ = NULL; 916 audio_renderer_ = NULL;
914 video_renderer_ = NULL; 917 video_renderer_ = NULL;
915 918
916 pipeline_filter_ = NULL; 919 pipeline_filter_ = NULL;
917 920
918 stop_pending_ = false; 921 if (error_caused_teardown_ && GetError() != PIPELINE_OK &&
919 tearing_down_ = false; 922 error_callback_.get()) {
923 error_callback_->Run();
924 }
920 925
921 if (PIPELINE_OK == GetError()) { 926 if (stop_pending_) {
922 // Destroying filters due to Stop().
923 ResetState(); 927 ResetState();
924 928
925 // Notify the client that stopping has finished. 929 // Notify the client that stopping has finished.
926 if (stop_callback_.get()) { 930 if (stop_callback_.get()) {
927 stop_callback_->Run(); 931 stop_callback_->Run();
928 stop_callback_.reset(); 932 stop_callback_.reset();
929 } 933 }
930 } else {
931 // Destroying filters due to SetError().
932 set_state(kError);
933 // If our owner has requested to be notified of an error.
934 if (error_callback_.get()) {
935 error_callback_->Run();
936 }
937 } 934 }
935
936 stop_pending_ = false;
937 tearing_down_ = false;
938 error_caused_teardown_ = false;
938 } 939 }
939 940
940 bool PipelineImpl::PrepareFilter(scoped_refptr<Filter> filter) { 941 bool PipelineImpl::PrepareFilter(scoped_refptr<Filter> filter) {
941 bool ret = pipeline_init_state_->composite_->AddFilter(filter.get()); 942 bool ret = pipeline_init_state_->composite_->AddFilter(filter.get());
942 943
943 if (!ret) { 944 if (!ret) {
944 SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 945 SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
945 } 946 }
946 return ret; 947 return ret;
947 } 948 }
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
1112 return NULL; 1113 return NULL;
1113 } 1114 }
1114 1115
1115 void PipelineImpl::TearDownPipeline() { 1116 void PipelineImpl::TearDownPipeline() {
1116 DCHECK_EQ(MessageLoop::current(), message_loop_); 1117 DCHECK_EQ(MessageLoop::current(), message_loop_);
1117 DCHECK_NE(kStopped, state_); 1118 DCHECK_NE(kStopped, state_);
1118 1119
1119 // Mark that we already start tearing down operation. 1120 // Mark that we already start tearing down operation.
1120 tearing_down_ = true; 1121 tearing_down_ = true;
1121 1122
1122 if (IsPipelineInitializing()) { 1123 switch(state_) {
1123 // Make it look like initialization was successful. 1124 case kCreated:
1124 pipeline_filter_ = pipeline_init_state_->composite_; 1125 case kError:
1125 pipeline_init_state_.reset(); 1126 set_state(kStopped);
1127 message_loop_->PostTask(FROM_HERE,
1128 NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask));
1129 break;
1126 1130
1127 set_state(kStopping); 1131 case kInitDataSource:
1128 pipeline_filter_->Stop(NewCallback( 1132 case kInitDemuxer:
1129 this, &PipelineImpl::OnFilterStateTransition)); 1133 case kInitAudioDecoder:
1134 case kInitAudioRenderer:
1135 case kInitVideoDecoder:
1136 case kInitVideoRenderer:
1137 // Make it look like initialization was successful.
1138 pipeline_filter_ = pipeline_init_state_->composite_;
1139 pipeline_init_state_.reset();
1130 1140
1131 FinishInitialization(); 1141 set_state(kStopping);
1132 } else if (pipeline_filter_.get()) { 1142 pipeline_filter_->Stop(
1133 set_state(kPausing); 1143 NewCallback(this, &PipelineImpl::OnFilterStateTransition));
1134 pipeline_filter_->Pause(NewCallback( 1144
1135 this, &PipelineImpl::OnFilterStateTransition)); 1145 FinishInitialization();
1136 } else { 1146 break;
1137 set_state(kStopped); 1147
1138 message_loop_->PostTask(FROM_HERE, 1148 case kPausing:
1139 NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask)); 1149 case kSeeking:
1140 } 1150 case kFlushing:
1151 case kStarting:
1152 set_state(kStopping);
1153 pipeline_filter_->Stop(
1154 NewCallback(this, &PipelineImpl::OnFilterStateTransition));
1155 break;
1156
1157 case kStarted:
1158 case kEnded:
1159 set_state(kPausing);
1160 pipeline_filter_->Pause(
1161 NewCallback(this, &PipelineImpl::OnFilterStateTransition));
1162 break;
1163
1164 case kStopping:
1165 case kStopped:
1166 NOTREACHED() << "Unexpected state for teardown: " << state_;
1167 break;
1168 // default: intentionally left out to force new states to cause compiler
1169 // errors.
1170 };
1141 } 1171 }
1142 1172
1143 } // namespace media 1173 } // namespace media
OLDNEW
« no previous file with comments | « media/base/pipeline_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698