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

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: 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 bool PipelineImpl::IsPipelineInitializing() {
scherkus (not reviewing) 2011/01/13 00:47:38 I wonder if we still need this method given the on
acolwell GONE FROM CHROMIUM 2011/01/13 01:39:16 Moved method body to DCHECK()and removed method.
329 DCHECK_EQ(MessageLoop::current(), message_loop_); 330 DCHECK_EQ(MessageLoop::current(), message_loop_);
330 return state_ == kInitDataSource || 331 return state_ == kInitDataSource ||
331 state_ == kInitDemuxer || 332 state_ == kInitDemuxer ||
332 state_ == kInitAudioDecoder || 333 state_ == kInitAudioDecoder ||
333 state_ == kInitAudioRenderer || 334 state_ == kInitAudioRenderer ||
334 state_ == kInitVideoDecoder || 335 state_ == kInitVideoDecoder ||
335 state_ == kInitVideoRenderer; 336 state_ == kInitVideoRenderer;
336 } 337 }
337 338
338 bool PipelineImpl::IsPipelineStopped() { 339 bool PipelineImpl::IsPipelineStopped() {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 } else if (current == kFlushing) { 387 } else if (current == kFlushing) {
387 // We will always honor Seek() before Stop(). This is based on the 388 // We will always honor Seek() before Stop(). This is based on the
388 // assumption that we never accept Seek() after Stop(). 389 // assumption that we never accept Seek() after Stop().
389 DCHECK(IsPipelineSeeking() || IsPipelineStopPending()); 390 DCHECK(IsPipelineSeeking() || IsPipelineStopPending());
390 return IsPipelineSeeking() ? kSeeking : kStopping; 391 return IsPipelineSeeking() ? kSeeking : kStopping;
391 } else if (current == kSeeking) { 392 } else if (current == kSeeking) {
392 return kStarting; 393 return kStarting;
393 } else if (current == kStarting) { 394 } else if (current == kStarting) {
394 return kStarted; 395 return kStarted;
395 } else if (current == kStopping) { 396 } else if (current == kStopping) {
396 return kStopped; 397 return error_caused_teardown_ ? kError : kStopped;
397 } else { 398 } else {
398 return current; 399 return current;
399 } 400 }
400 } 401 }
401 402
402 void PipelineImpl::SetError(PipelineError error) { 403 void PipelineImpl::SetError(PipelineError error) {
403 DCHECK(IsRunning()); 404 DCHECK(IsRunning());
404 DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!"; 405 DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!";
405 VLOG(1) << "Media pipeline error: " << error; 406 VLOG(1) << "Media pipeline error: " << error;
406 407
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after
663 664
664 // This method is called as a result of the client calling Pipeline::Stop() or 665 // This method is called as a result of the client calling Pipeline::Stop() or
665 // as the result of an error condition. 666 // as the result of an error condition.
666 // We stop the filters in the reverse order. 667 // We stop the filters in the reverse order.
667 // 668 //
668 // TODO(scherkus): beware! this can get posted multiple times since we post 669 // 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 670 // 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. 671 // additional calls, however most of this logic will be changing.
671 void PipelineImpl::StopTask(PipelineCallback* stop_callback) { 672 void PipelineImpl::StopTask(PipelineCallback* stop_callback) {
672 DCHECK_EQ(MessageLoop::current(), message_loop_); 673 DCHECK_EQ(MessageLoop::current(), message_loop_);
673 PipelineError error = GetError(); 674 DCHECK(!IsPipelineStopPending());
675 DCHECK_NE(state_, kStopped);
674 676
675 if (state_ == kStopped || (IsPipelineStopPending() && error == PIPELINE_OK)) { 677 if (state_ == kStopped) {
676 // If we are already stopped or stopping normally, return immediately. 678 // Already stopped so just run callback.
679 stop_callback->Run();
677 delete stop_callback; 680 delete stop_callback;
678 return; 681 return;
679 } else if (state_ == kError || 682 }
680 (IsPipelineStopPending() && error != PIPELINE_OK)) { 683
684 if (IsPipelineTearingDown() && error_caused_teardown_) {
scherkus (not reviewing) 2011/01/13 00:47:38 to confirm: this happens in the case someone calls
acolwell GONE FROM CHROMIUM 2011/01/13 01:39:16 This happens in the case where an error occurs tha
681 // If we are stopping due to SetError(), stop normally instead of 685 // If we are stopping due to SetError(), stop normally instead of
682 // going to error state. 686 // going to error state and calling |error_callback_|. This converts
687 // the teardown in progress from an error teardown into one that acts
688 // like the error never occurred.
683 AutoLock auto_lock(lock_); 689 AutoLock auto_lock(lock_);
684 error_ = PIPELINE_OK; 690 error_ = PIPELINE_OK;
691 error_caused_teardown_ = false;
685 } 692 }
686 693
687 stop_callback_.reset(stop_callback); 694 stop_callback_.reset(stop_callback);
688 695
689 stop_pending_ = true; 696 stop_pending_ = true;
690 if (!IsPipelineSeeking()) { 697 if (!IsPipelineSeeking() && !IsPipelineTearingDown()) {
691 // We will tear down pipeline immediately when there is no seek operation 698 // We will tear down pipeline immediately when there is no seek operation
692 // pending. This should include the case where we are partially initialized. 699 // 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. 700 // we are partially initialized.
694 DCHECK(!IsPipelineTearingDown());
695 TearDownPipeline(); 701 TearDownPipeline();
696 } 702 }
697 } 703 }
698 704
699 void PipelineImpl::ErrorChangedTask(PipelineError error) { 705 void PipelineImpl::ErrorChangedTask(PipelineError error) {
700 DCHECK_EQ(MessageLoop::current(), message_loop_); 706 DCHECK_EQ(MessageLoop::current(), message_loop_);
701 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; 707 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
702 708
703 // Suppress executing additional error logic. Note that if we are currently 709 // Suppress executing additional error logic. Note that if we are currently
704 // performing a normal stop, then we return immediately and continue the 710 // performing a normal stop, then we return immediately and continue the
705 // normal stop. 711 // normal stop.
706 if (IsPipelineStopped() || IsPipelineTearingDown()) { 712 if (IsPipelineStopped() || IsPipelineTearingDown()) {
707 return; 713 return;
708 } 714 }
709 715
710 AutoLock auto_lock(lock_); 716 AutoLock auto_lock(lock_);
711 error_ = error; 717 error_ = error;
712 718
719 error_caused_teardown_ = true;
713 TearDownPipeline(); 720 TearDownPipeline();
714 } 721 }
715 722
716 void PipelineImpl::PlaybackRateChangedTask(float playback_rate) { 723 void PipelineImpl::PlaybackRateChangedTask(float playback_rate) {
717 DCHECK_EQ(MessageLoop::current(), message_loop_); 724 DCHECK_EQ(MessageLoop::current(), message_loop_);
718 { 725 {
719 AutoLock auto_lock(lock_); 726 AutoLock auto_lock(lock_);
720 clock_->SetPlaybackRate(playback_rate); 727 clock_->SetPlaybackRate(playback_rate);
721 } 728 }
722 729
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
908 void PipelineImpl::FinishDestroyingFiltersTask() { 915 void PipelineImpl::FinishDestroyingFiltersTask() {
909 DCHECK_EQ(MessageLoop::current(), message_loop_); 916 DCHECK_EQ(MessageLoop::current(), message_loop_);
910 DCHECK(IsPipelineStopped()); 917 DCHECK(IsPipelineStopped());
911 918
912 // Clear renderer references. 919 // Clear renderer references.
913 audio_renderer_ = NULL; 920 audio_renderer_ = NULL;
914 video_renderer_ = NULL; 921 video_renderer_ = NULL;
915 922
916 pipeline_filter_ = NULL; 923 pipeline_filter_ = NULL;
917 924
918 stop_pending_ = false; 925 if (error_caused_teardown_ && GetError() != PIPELINE_OK &&
scherkus (not reviewing) 2011/01/13 00:47:38 nit: get rid of extra space between error_caused_t
acolwell GONE FROM CHROMIUM 2011/01/13 01:39:16 Done.
919 tearing_down_ = false; 926 error_callback_.get()) {
927 error_callback_->Run();
928 }
920 929
921 if (PIPELINE_OK == GetError()) { 930 if (stop_pending_) {
922 // Destroying filters due to Stop().
923 ResetState(); 931 ResetState();
924 932
925 // Notify the client that stopping has finished. 933 // Notify the client that stopping has finished.
926 if (stop_callback_.get()) { 934 if (stop_callback_.get()) {
927 stop_callback_->Run(); 935 stop_callback_->Run();
928 stop_callback_.reset(); 936 stop_callback_.reset();
929 } 937 }
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 } 938 }
939
940 stop_pending_ = false;
941 tearing_down_ = false;
942 error_caused_teardown_ = false;
938 } 943 }
939 944
940 bool PipelineImpl::PrepareFilter(scoped_refptr<Filter> filter) { 945 bool PipelineImpl::PrepareFilter(scoped_refptr<Filter> filter) {
941 bool ret = pipeline_init_state_->composite_->AddFilter(filter.get()); 946 bool ret = pipeline_init_state_->composite_->AddFilter(filter.get());
942 947
943 if (!ret) { 948 if (!ret) {
944 SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 949 SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
945 } 950 }
946 return ret; 951 return ret;
947 } 952 }
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
1112 return NULL; 1117 return NULL;
1113 } 1118 }
1114 1119
1115 void PipelineImpl::TearDownPipeline() { 1120 void PipelineImpl::TearDownPipeline() {
1116 DCHECK_EQ(MessageLoop::current(), message_loop_); 1121 DCHECK_EQ(MessageLoop::current(), message_loop_);
1117 DCHECK_NE(kStopped, state_); 1122 DCHECK_NE(kStopped, state_);
1118 1123
1119 // Mark that we already start tearing down operation. 1124 // Mark that we already start tearing down operation.
1120 tearing_down_ = true; 1125 tearing_down_ = true;
1121 1126
1122 if (IsPipelineInitializing()) { 1127 switch(state_) {
1123 // Make it look like initialization was successful. 1128 case kCreated:
1124 pipeline_filter_ = pipeline_init_state_->composite_; 1129 case kError:
1125 pipeline_init_state_.reset(); 1130 set_state(kStopped);
1131 message_loop_->PostTask(FROM_HERE,
1132 NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask));
1133 break;
1126 1134
1127 set_state(kStopping); 1135 case kInitDataSource:
1128 pipeline_filter_->Stop(NewCallback( 1136 case kInitDemuxer:
1129 this, &PipelineImpl::OnFilterStateTransition)); 1137 case kInitAudioDecoder:
1138 case kInitAudioRenderer:
1139 case kInitVideoDecoder:
1140 case kInitVideoRenderer:
1141 // Make it look like initialization was successful.
1142 pipeline_filter_ = pipeline_init_state_->composite_;
1143 pipeline_init_state_.reset();
1130 1144
1131 FinishInitialization(); 1145 set_state(kStopping);
1132 } else if (pipeline_filter_.get()) { 1146 pipeline_filter_->Stop(
1133 set_state(kPausing); 1147 NewCallback(this, &PipelineImpl::OnFilterStateTransition));
1134 pipeline_filter_->Pause(NewCallback( 1148
1135 this, &PipelineImpl::OnFilterStateTransition)); 1149 FinishInitialization();
1136 } else { 1150 break;
1137 set_state(kStopped); 1151
1138 message_loop_->PostTask(FROM_HERE, 1152 case kPausing:
1139 NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask)); 1153 case kSeeking:
1140 } 1154 case kFlushing:
1155 case kStarting:
1156 set_state(kStopping);
1157 pipeline_filter_->Stop(NewCallback(
scherkus (not reviewing) 2011/01/13 00:47:38 drop NewCallback to next line
acolwell GONE FROM CHROMIUM 2011/01/13 01:39:16 Done.
1158 this, &PipelineImpl::OnFilterStateTransition));
1159 break;
1160
1161 case kStarted:
1162 case kEnded:
1163 set_state(kPausing);
1164 pipeline_filter_->Pause(NewCallback(
scherkus (not reviewing) 2011/01/13 00:47:38 drop NewCallback to next line
acolwell GONE FROM CHROMIUM 2011/01/13 01:39:16 Done.
1165 this, &PipelineImpl::OnFilterStateTransition));
1166 break;
1167
1168 case kStopping:
1169 case kStopped:
1170 NOTREACHED() << "Unexpected state for teardown: " << state_;
1171 break;
1172 // default: intentionally left out to force new states to cause compiler
1173 // errors.
1174 };
1141 } 1175 }
1142 1176
1143 } // namespace media 1177 } // 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