Chromium Code Reviews| 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_util.h" | 16 #include "base/string_util.h" |
| 17 #include "base/synchronization/condition_variable.h" | 17 #include "base/synchronization/condition_variable.h" |
| 18 #include "media/base/audio_decoder.h" | 18 #include "media/base/audio_decoder.h" |
| 19 #include "media/base/audio_renderer.h" | 19 #include "media/base/audio_renderer.h" |
| 20 #include "media/base/buffers.h" | |
| 20 #include "media/base/callback_util.h" | 21 #include "media/base/callback_util.h" |
| 21 #include "media/base/clock.h" | 22 #include "media/base/clock.h" |
| 22 #include "media/base/filter_collection.h" | 23 #include "media/base/filter_collection.h" |
| 23 #include "media/base/media_log.h" | 24 #include "media/base/media_log.h" |
| 24 #include "media/base/video_decoder.h" | 25 #include "media/base/video_decoder.h" |
| 25 #include "media/base/video_renderer.h" | 26 #include "media/base/video_renderer.h" |
| 26 | 27 |
| 27 using base::TimeDelta; | 28 using base::TimeDelta; |
| 28 | 29 |
| 29 namespace media { | 30 namespace media { |
| 30 | 31 |
| 31 PipelineStatusNotification::PipelineStatusNotification() | 32 PipelineStatusNotification::PipelineStatusNotification() |
|
acolwell GONE FROM CHROMIUM
2012/07/30 18:33:10
Shouldn't this be in its own file? It isn't even u
| |
| 32 : cv_(&lock_), status_(PIPELINE_OK), notified_(false) { | 33 : cv_(&lock_), status_(PIPELINE_OK), notified_(false) { |
| 33 } | 34 } |
| 34 | 35 |
| 35 PipelineStatusNotification::~PipelineStatusNotification() { | 36 PipelineStatusNotification::~PipelineStatusNotification() { |
| 36 DCHECK(notified_); | 37 DCHECK(notified_); |
| 37 } | 38 } |
| 38 | 39 |
| 39 PipelineStatusCB PipelineStatusNotification::Callback() { | 40 PipelineStatusCB PipelineStatusNotification::Callback() { |
| 40 return base::Bind(&PipelineStatusNotification::Notify, | 41 return base::Bind(&PipelineStatusNotification::Notify, |
| 41 base::Unretained(this)); | 42 base::Unretained(this)); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 62 } | 63 } |
| 63 | 64 |
| 64 struct Pipeline::PipelineInitState { | 65 struct Pipeline::PipelineInitState { |
| 65 scoped_refptr<AudioDecoder> audio_decoder; | 66 scoped_refptr<AudioDecoder> audio_decoder; |
| 66 scoped_refptr<VideoDecoder> video_decoder; | 67 scoped_refptr<VideoDecoder> video_decoder; |
| 67 }; | 68 }; |
| 68 | 69 |
| 69 Pipeline::Pipeline(MessageLoop* message_loop, MediaLog* media_log) | 70 Pipeline::Pipeline(MessageLoop* message_loop, MediaLog* media_log) |
| 70 : message_loop_(message_loop->message_loop_proxy()), | 71 : message_loop_(message_loop->message_loop_proxy()), |
| 71 media_log_(media_log), | 72 media_log_(media_log), |
| 73 running_(false), | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
I believe it is the case that:
running_ == state_
| |
| 74 did_loading_progress_(false), | |
| 75 total_bytes_(0), | |
| 76 volume_(1.0f), | |
| 77 playback_rate_(0.0f), | |
| 72 clock_(new Clock(&base::Time::Now)), | 78 clock_(new Clock(&base::Time::Now)), |
| 73 waiting_for_clock_update_(false), | 79 waiting_for_clock_update_(false), |
| 80 status_(PIPELINE_OK), | |
| 81 has_audio_(false), | |
| 82 has_video_(false), | |
| 74 state_(kCreated), | 83 state_(kCreated), |
| 84 seek_timestamp_(kNoTimestamp()), | |
| 85 audio_disabled_(false), | |
| 75 creation_time_(base::Time::Now()) { | 86 creation_time_(base::Time::Now()) { |
| 76 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 87 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| 77 ResetState(); | |
| 78 media_log_->AddEvent( | 88 media_log_->AddEvent( |
| 79 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); | 89 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); |
| 80 } | 90 } |
| 81 | 91 |
| 82 Pipeline::~Pipeline() { | 92 Pipeline::~Pipeline() { |
| 83 base::AutoLock auto_lock(lock_); | 93 base::AutoLock auto_lock(lock_); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
If you think you need a lock in a dtor, you're gon
| |
| 84 DCHECK(!running_) << "Stop() must complete before destroying object"; | 94 DCHECK(!running_) << "Stop() must complete before destroying object"; |
| 85 DCHECK(!stop_pending_); | 95 DCHECK(start_cb_.is_null()); |
| 86 DCHECK(!seek_pending_); | 96 DCHECK(seek_cb_.is_null()); |
| 97 DCHECK(stop_cb_.is_null()); | |
| 87 | 98 |
| 88 media_log_->AddEvent( | 99 media_log_->AddEvent( |
| 89 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); | 100 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); |
| 90 } | 101 } |
| 91 | 102 |
| 92 void Pipeline::Start(scoped_ptr<FilterCollection> collection, | 103 void Pipeline::Start(scoped_ptr<FilterCollection> collection, |
| 93 const PipelineStatusCB& ended_cb, | 104 const PipelineStatusCB& ended_cb, |
| 94 const PipelineStatusCB& error_cb, | 105 const PipelineStatusCB& error_cb, |
| 95 const PipelineStatusCB& start_cb) { | 106 const PipelineStatusCB& start_cb) { |
| 96 base::AutoLock auto_lock(lock_); | 107 base::AutoLock auto_lock(lock_); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
replace w/ assertion about running on render threa
| |
| 97 CHECK(!running_) << "Media pipeline is already running"; | 108 CHECK(!running_) << "Media pipeline is already running"; |
| 109 running_ = true; | |
| 98 | 110 |
| 99 running_ = true; | |
| 100 message_loop_->PostTask(FROM_HERE, base::Bind( | 111 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 101 &Pipeline::StartTask, this, base::Passed(&collection), | 112 &Pipeline::StartTask, this, base::Passed(&collection), |
| 102 ended_cb, error_cb, start_cb)); | 113 ended_cb, error_cb, start_cb)); |
| 103 } | 114 } |
| 104 | 115 |
| 105 void Pipeline::Stop(const base::Closure& stop_cb) { | 116 void Pipeline::Stop(const base::Closure& stop_cb) { |
| 106 base::AutoLock auto_lock(lock_); | 117 base::AutoLock auto_lock(lock_); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
ditto to asserting on render thread (lock is not o
acolwell GONE FROM CHROMIUM
2012/07/30 18:33:10
Most of the GetXXX methods need locks because they
| |
| 107 CHECK(running_) << "Media pipeline isn't running"; | |
|
scherkus (not reviewing)
2012/07/28 02:26:07
it's now always valid to call Stop() -- this avoid
| |
| 108 | 118 |
| 109 // Stop the pipeline, which will set |running_| to false on our behalf. | |
| 110 message_loop_->PostTask(FROM_HERE, base::Bind( | 119 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 111 &Pipeline::StopTask, this, stop_cb)); | 120 &Pipeline::StopTask, this, stop_cb)); |
| 112 } | 121 } |
| 113 | 122 |
| 114 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { | 123 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| 115 base::AutoLock auto_lock(lock_); | 124 base::AutoLock auto_lock(lock_); |
| 116 CHECK(running_) << "Media pipeline isn't running"; | 125 CHECK(running_) << "Media pipeline isn't running"; |
| 117 | 126 |
| 118 message_loop_->PostTask(FROM_HERE, base::Bind( | 127 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 119 &Pipeline::SeekTask, this, time, seek_cb)); | 128 &Pipeline::SeekTask, this, time, seek_cb)); |
| 120 } | 129 } |
| 121 | 130 |
| 122 bool Pipeline::IsRunning() const { | 131 bool Pipeline::IsRunning() const { |
| 123 base::AutoLock auto_lock(lock_); | 132 base::AutoLock auto_lock(lock_); |
| 124 return running_; | 133 return running_; |
| 125 } | 134 } |
| 126 | 135 |
| 127 bool Pipeline::IsInitialized() const { | |
| 128 // TODO(scherkus): perhaps replace this with a bool that is set/get under the | |
| 129 // lock, because this is breaching the contract that |state_| is only accessed | |
| 130 // on |message_loop_|. | |
| 131 base::AutoLock auto_lock(lock_); | |
| 132 switch (state_) { | |
| 133 case kPausing: | |
| 134 case kFlushing: | |
| 135 case kSeeking: | |
| 136 case kStarting: | |
| 137 case kStarted: | |
| 138 case kEnded: | |
| 139 return true; | |
| 140 default: | |
| 141 return false; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 bool Pipeline::HasAudio() const { | 136 bool Pipeline::HasAudio() const { |
| 146 base::AutoLock auto_lock(lock_); | 137 base::AutoLock auto_lock(lock_); |
| 147 return has_audio_; | 138 return has_audio_; |
| 148 } | 139 } |
| 149 | 140 |
| 150 bool Pipeline::HasVideo() const { | 141 bool Pipeline::HasVideo() const { |
| 151 base::AutoLock auto_lock(lock_); | 142 base::AutoLock auto_lock(lock_); |
| 152 return has_video_; | 143 return has_video_; |
| 153 } | 144 } |
| 154 | 145 |
| 155 float Pipeline::GetPlaybackRate() const { | 146 float Pipeline::GetPlaybackRate() const { |
| 156 base::AutoLock auto_lock(lock_); | 147 base::AutoLock auto_lock(lock_); |
| 157 return playback_rate_; | 148 return playback_rate_; |
| 158 } | 149 } |
| 159 | 150 |
| 160 void Pipeline::SetPlaybackRate(float playback_rate) { | 151 void Pipeline::SetPlaybackRate(float playback_rate) { |
| 161 if (playback_rate < 0.0f) | 152 if (playback_rate < 0.0f) |
| 162 return; | 153 return; |
| 163 | 154 |
| 164 base::AutoLock auto_lock(lock_); | 155 base::AutoLock auto_lock(lock_); |
| 165 playback_rate_ = playback_rate; | 156 playback_rate_ = playback_rate; |
| 166 if (running_ && !tearing_down_) { | 157 if (running_) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
couldn't this be an early-return at the top of the
acolwell GONE FROM CHROMIUM
2012/07/30 18:33:10
The running_ check is here because it needs to be
| |
| 167 message_loop_->PostTask(FROM_HERE, base::Bind( | 158 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 168 &Pipeline::PlaybackRateChangedTask, this, playback_rate)); | 159 &Pipeline::PlaybackRateChangedTask, this, playback_rate)); |
| 169 } | 160 } |
| 170 } | 161 } |
| 171 | 162 |
| 172 float Pipeline::GetVolume() const { | 163 float Pipeline::GetVolume() const { |
| 173 base::AutoLock auto_lock(lock_); | 164 base::AutoLock auto_lock(lock_); |
| 174 return volume_; | 165 return volume_; |
| 175 } | 166 } |
| 176 | 167 |
| 177 void Pipeline::SetVolume(float volume) { | 168 void Pipeline::SetVolume(float volume) { |
| 178 if (volume < 0.0f || volume > 1.0f) | 169 if (volume < 0.0f || volume > 1.0f) |
| 179 return; | 170 return; |
| 180 | 171 |
| 181 base::AutoLock auto_lock(lock_); | 172 base::AutoLock auto_lock(lock_); |
| 182 volume_ = volume; | 173 volume_ = volume; |
| 183 if (running_ && !tearing_down_) { | 174 if (running_) { |
| 184 message_loop_->PostTask(FROM_HERE, base::Bind( | 175 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 185 &Pipeline::VolumeChangedTask, this, volume)); | 176 &Pipeline::VolumeChangedTask, this, volume)); |
| 186 } | 177 } |
| 187 } | 178 } |
| 188 | 179 |
| 189 TimeDelta Pipeline::GetMediaTime() const { | 180 TimeDelta Pipeline::GetMediaTime() const { |
| 190 base::AutoLock auto_lock(lock_); | 181 base::AutoLock auto_lock(lock_); |
| 191 return clock_->Elapsed(); | 182 return clock_->Elapsed(); |
| 192 } | 183 } |
| 193 | 184 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 236 | 227 |
| 237 PipelineStatistics Pipeline::GetStatistics() const { | 228 PipelineStatistics Pipeline::GetStatistics() const { |
| 238 base::AutoLock auto_lock(lock_); | 229 base::AutoLock auto_lock(lock_); |
| 239 return statistics_; | 230 return statistics_; |
| 240 } | 231 } |
| 241 | 232 |
| 242 void Pipeline::SetClockForTesting(Clock* clock) { | 233 void Pipeline::SetClockForTesting(Clock* clock) { |
| 243 clock_.reset(clock); | 234 clock_.reset(clock); |
| 244 } | 235 } |
| 245 | 236 |
| 246 void Pipeline::ResetState() { | 237 void Pipeline::SetErrorForTesting(PipelineStatus status) { |
| 247 base::AutoLock auto_lock(lock_); | 238 SetError(status); |
| 248 const TimeDelta kZero; | |
| 249 running_ = false; | |
| 250 stop_pending_ = false; | |
| 251 seek_pending_ = false; | |
| 252 tearing_down_ = false; | |
| 253 error_caused_teardown_ = false; | |
| 254 playback_rate_change_pending_ = false; | |
| 255 buffered_byte_ranges_.clear(); | |
| 256 did_loading_progress_ = false; | |
| 257 total_bytes_ = 0; | |
| 258 natural_size_.SetSize(0, 0); | |
| 259 volume_ = 1.0f; | |
| 260 playback_rate_ = 0.0f; | |
| 261 pending_playback_rate_ = 0.0f; | |
| 262 status_ = PIPELINE_OK; | |
| 263 has_audio_ = false; | |
| 264 has_video_ = false; | |
| 265 waiting_for_clock_update_ = false; | |
| 266 audio_disabled_ = false; | |
| 267 clock_->Reset(); | |
| 268 } | 239 } |
| 269 | 240 |
| 270 void Pipeline::SetState(State next_state) { | 241 void Pipeline::SetState(State next_state) { |
| 271 if (state_ != kStarted && next_state == kStarted && | 242 if (state_ != kStarted && next_state == kStarted && |
| 272 !creation_time_.is_null()) { | 243 !creation_time_.is_null()) { |
| 273 UMA_HISTOGRAM_TIMES( | 244 UMA_HISTOGRAM_TIMES( |
| 274 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); | 245 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); |
| 275 creation_time_ = base::Time(); | 246 creation_time_ = base::Time(); |
| 276 } | 247 } |
| 277 state_ = next_state; | 248 state_ = next_state; |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
can you assert a correct thread or lock is held?
| |
| 278 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 249 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
| 279 } | 250 } |
| 280 | 251 |
| 281 bool Pipeline::IsPipelineOk() { | 252 bool Pipeline::IsPipelineOk() { |
| 282 base::AutoLock auto_lock(lock_); | 253 base::AutoLock auto_lock(lock_); |
| 283 return status_ == PIPELINE_OK; | 254 return status_ == PIPELINE_OK; |
| 284 } | 255 } |
| 285 | 256 |
| 286 bool Pipeline::IsPipelineStopped() { | |
| 287 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 288 return state_ == kStopped || state_ == kError; | |
| 289 } | |
| 290 | |
| 291 bool Pipeline::IsPipelineTearingDown() { | |
| 292 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 293 return tearing_down_; | |
| 294 } | |
| 295 | |
| 296 bool Pipeline::IsPipelineStopPending() { | |
| 297 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 298 return stop_pending_; | |
| 299 } | |
| 300 | |
| 301 bool Pipeline::IsPipelineSeeking() { | |
| 302 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 303 if (!seek_pending_) | |
| 304 return false; | |
| 305 DCHECK(kSeeking == state_ || kPausing == state_ || | |
| 306 kFlushing == state_ || kStarting == state_) | |
| 307 << "Current state : " << state_; | |
| 308 return true; | |
| 309 } | |
| 310 | |
| 311 void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) { | |
| 312 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 313 if (cb.is_null()) | |
| 314 return; | |
| 315 cb.Run(status); | |
| 316 // Prevent double-reporting of errors to clients. | |
| 317 if (status != PIPELINE_OK) | |
| 318 error_cb_.Reset(); | |
| 319 } | |
| 320 | |
| 321 void Pipeline::FinishInitialization() { | |
| 322 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 323 // Execute the seek callback, if present. Note that this might be the | |
| 324 // initial callback passed into Start(). | |
| 325 ReportStatus(seek_cb_, status_); | |
| 326 seek_cb_.Reset(); | |
| 327 } | |
| 328 | |
| 329 // static | |
| 330 bool Pipeline::TransientState(State state) { | |
| 331 return state == kPausing || | |
| 332 state == kFlushing || | |
| 333 state == kSeeking || | |
| 334 state == kStarting || | |
| 335 state == kStopping; | |
| 336 } | |
| 337 | |
| 338 // static | |
| 339 Pipeline::State Pipeline::FindNextState(State current) { | |
| 340 // TODO(scherkus): refactor InitializeTask() to make use of this function. | |
| 341 if (current == kPausing) { | |
| 342 return kFlushing; | |
| 343 } else if (current == kFlushing) { | |
| 344 // We will always honor Seek() before Stop(). This is based on the | |
| 345 // assumption that we never accept Seek() after Stop(). | |
| 346 DCHECK(IsPipelineSeeking() || | |
| 347 IsPipelineStopPending() || | |
| 348 IsPipelineTearingDown()); | |
| 349 return IsPipelineSeeking() ? kSeeking : kStopping; | |
| 350 } else if (current == kSeeking) { | |
| 351 return kStarting; | |
| 352 } else if (current == kStarting) { | |
| 353 return kStarted; | |
| 354 } else if (current == kStopping) { | |
| 355 return error_caused_teardown_ ? kError : kStopped; | |
| 356 } else { | |
| 357 return current; | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 void Pipeline::OnDemuxerError(PipelineStatus error) { | 257 void Pipeline::OnDemuxerError(PipelineStatus error) { |
| 362 SetError(error); | 258 SetError(error); |
| 363 } | 259 } |
| 364 | 260 |
| 365 void Pipeline::SetError(PipelineStatus error) { | 261 void Pipeline::SetError(PipelineStatus error) { |
| 366 DCHECK(IsRunning()); | 262 DCHECK(IsRunning()); |
| 367 DCHECK_NE(PIPELINE_OK, error); | 263 DCHECK_NE(PIPELINE_OK, error); |
| 368 VLOG(1) << "Media pipeline error: " << error; | 264 VLOG(1) << "Media pipeline error: " << error; |
| 369 | 265 |
| 370 message_loop_->PostTask(FROM_HERE, base::Bind( | 266 message_loop_->PostTask(FROM_HERE, base::Bind( |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 446 TimeDelta epsilon = clock_->Duration() / 100; | 342 TimeDelta epsilon = clock_->Duration() / 100; |
| 447 if (time_offset < epsilon) | 343 if (time_offset < epsilon) |
| 448 return TimeDelta(); | 344 return TimeDelta(); |
| 449 if (time_offset + epsilon > clock_->Duration()) | 345 if (time_offset + epsilon > clock_->Duration()) |
| 450 return clock_->Duration(); | 346 return clock_->Duration(); |
| 451 return time_offset; | 347 return time_offset; |
| 452 } | 348 } |
| 453 | 349 |
| 454 void Pipeline::DoPause(const base::Closure& done_cb) { | 350 void Pipeline::DoPause(const base::Closure& done_cb) { |
| 455 DCHECK(message_loop_->BelongsToCurrentThread()); | 351 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 352 DCHECK_EQ(state_, kPausing); | |
| 353 DCHECK(seek_timestamp_ != kNoTimestamp()); | |
| 354 | |
| 456 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 355 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| 457 | 356 |
| 458 if (audio_renderer_) | 357 if (audio_renderer_) |
| 459 closures->push(base::Bind(&AudioRenderer::Pause, audio_renderer_)); | 358 closures->push(base::Bind(&AudioRenderer::Pause, audio_renderer_)); |
| 460 | 359 |
| 461 if (video_renderer_) | 360 if (video_renderer_) |
| 462 closures->push(base::Bind(&VideoRenderer::Pause, video_renderer_)); | 361 closures->push(base::Bind(&VideoRenderer::Pause, video_renderer_)); |
| 463 | 362 |
| 464 RunInSeries(closures.Pass(), done_cb); | 363 RunInSeries(closures.Pass(), done_cb); |
| 465 } | 364 } |
| 466 | 365 |
| 467 void Pipeline::DoFlush(const base::Closure& done_cb) { | 366 void Pipeline::DoFlush(const base::Closure& done_cb) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it crazy to still have this around?
Is it not a
| |
| 468 DCHECK(message_loop_->BelongsToCurrentThread()); | 367 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 368 DCHECK_EQ(state_, kFlushing); | |
| 369 DCHECK(seek_timestamp_ != kNoTimestamp()); | |
| 370 | |
| 469 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 371 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| 470 | 372 |
| 471 if (audio_renderer_) | 373 if (audio_renderer_) |
| 472 closures->push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); | 374 closures->push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); |
| 473 | 375 |
| 474 if (video_renderer_) | 376 if (video_renderer_) |
| 475 closures->push(base::Bind(&VideoRenderer::Flush, video_renderer_)); | 377 closures->push(base::Bind(&VideoRenderer::Flush, video_renderer_)); |
| 476 | 378 |
| 477 RunInParallel(closures.Pass(), done_cb); | 379 RunInParallel(closures.Pass(), done_cb); |
| 478 } | 380 } |
| 479 | 381 |
| 480 void Pipeline::DoPlay(const base::Closure& done_cb) { | 382 void Pipeline::DoPlay(const base::Closure& done_cb) { |
| 481 DCHECK(message_loop_->BelongsToCurrentThread()); | 383 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 384 DCHECK_EQ(state_, kStarting); | |
| 385 DCHECK(seek_timestamp_ == kNoTimestamp()); | |
| 386 | |
| 387 // Update playback rate and volume in case it changed before we resume | |
| 388 // playback. | |
| 389 PlaybackRateChangedTask(GetPlaybackRate()); | |
|
scherkus (not reviewing)
2012/07/28 02:26:07
here's the new method for handling rate/volume cha
| |
| 390 VolumeChangedTask(GetVolume()); | |
| 391 | |
| 482 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 392 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| 483 | 393 |
| 484 if (audio_renderer_) | 394 if (audio_renderer_) |
| 485 closures->push(base::Bind(&AudioRenderer::Play, audio_renderer_)); | 395 closures->push(base::Bind(&AudioRenderer::Play, audio_renderer_)); |
| 486 | 396 |
| 487 if (video_renderer_) | 397 if (video_renderer_) |
| 488 closures->push(base::Bind(&VideoRenderer::Play, video_renderer_)); | 398 closures->push(base::Bind(&VideoRenderer::Play, video_renderer_)); |
| 489 | 399 |
| 490 RunInSeries(closures.Pass(), done_cb); | 400 RunInSeries(closures.Pass(), done_cb); |
| 491 } | 401 } |
| 492 | 402 |
| 493 void Pipeline::DoStop(const base::Closure& done_cb) { | 403 void Pipeline::DoStop(const base::Closure& done_cb) { |
| 494 DCHECK(message_loop_->BelongsToCurrentThread()); | 404 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 405 DCHECK_EQ(state_, kStopping); | |
| 406 | |
| 407 if (video_decoder_) { | |
| 408 video_decoder_->PrepareForShutdownHack(); | |
|
scherkus (not reviewing)
2012/07/28 02:26:07
this might actually go away now that we don't Paus
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Yes, it can!
| |
| 409 video_decoder_ = NULL; | |
| 410 } | |
| 411 | |
| 495 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 412 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| 496 | 413 |
| 497 if (demuxer_) | 414 if (demuxer_) |
| 498 closures->push(base::Bind(&Demuxer::Stop, demuxer_)); | 415 closures->push(base::Bind(&Demuxer::Stop, demuxer_)); |
| 499 | 416 |
| 500 if (audio_renderer_) | 417 if (audio_renderer_) |
| 501 closures->push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); | 418 closures->push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); |
| 502 | 419 |
| 503 if (video_renderer_) | 420 if (video_renderer_) |
| 504 closures->push(base::Bind(&VideoRenderer::Stop, video_renderer_)); | 421 closures->push(base::Bind(&VideoRenderer::Stop, video_renderer_)); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 531 } | 448 } |
| 532 | 449 |
| 533 void Pipeline::OnRendererEnded() { | 450 void Pipeline::OnRendererEnded() { |
| 534 DCHECK(IsRunning()); | 451 DCHECK(IsRunning()); |
| 535 message_loop_->PostTask(FROM_HERE, base::Bind( | 452 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 536 &Pipeline::OnRendererEndedTask, this)); | 453 &Pipeline::OnRendererEndedTask, this)); |
| 537 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); | 454 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); |
| 538 } | 455 } |
| 539 | 456 |
| 540 // Called from any thread. | 457 // Called from any thread. |
| 541 void Pipeline::OnFilterInitialize(PipelineStatus status) { | |
| 542 // Continue the initialize task by proceeding to the next stage. | |
| 543 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 544 &Pipeline::InitializeTask, this, status)); | |
| 545 } | |
| 546 | |
| 547 // Called from any thread. | |
| 548 void Pipeline::OnFilterStateTransition() { | |
| 549 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 550 &Pipeline::FilterStateTransitionTask, this)); | |
| 551 } | |
| 552 | |
| 553 // Called from any thread. | |
| 554 // This method makes the PipelineStatusCB behave like a Closure. It | |
| 555 // makes it look like a host()->SetError() call followed by a call to | |
| 556 // OnFilterStateTransition() when errors occur. | |
| 557 // | |
| 558 // TODO: Revisit this code when SetError() is removed from FilterHost and | |
| 559 // all the Closures are converted to PipelineStatusCB. | |
| 560 void Pipeline::OnFilterStateTransitionWithStatus(PipelineStatus status) { | |
| 561 if (status != PIPELINE_OK) | |
| 562 SetError(status); | |
| 563 OnFilterStateTransition(); | |
| 564 } | |
| 565 | |
| 566 void Pipeline::OnTeardownStateTransition() { | |
| 567 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 568 &Pipeline::TeardownStateTransitionTask, this)); | |
| 569 } | |
| 570 | |
| 571 // Called from any thread. | |
| 572 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { | 458 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { |
| 573 base::AutoLock auto_lock(lock_); | 459 base::AutoLock auto_lock(lock_); |
| 574 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | 460 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| 575 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | 461 statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
| 576 statistics_.video_frames_decoded += stats.video_frames_decoded; | 462 statistics_.video_frames_decoded += stats.video_frames_decoded; |
| 577 statistics_.video_frames_dropped += stats.video_frames_dropped; | 463 statistics_.video_frames_dropped += stats.video_frames_dropped; |
| 578 } | 464 } |
| 579 | 465 |
| 580 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, | 466 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, |
| 581 const PipelineStatusCB& ended_cb, | 467 const PipelineStatusCB& ended_cb, |
| 582 const PipelineStatusCB& error_cb, | 468 const PipelineStatusCB& error_cb, |
| 583 const PipelineStatusCB& start_cb) { | 469 const PipelineStatusCB& start_cb) { |
| 584 DCHECK(message_loop_->BelongsToCurrentThread()); | 470 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 585 DCHECK_EQ(kCreated, state_); | 471 DCHECK_EQ(kCreated, state_); |
| 586 filter_collection_ = filter_collection.Pass(); | 472 filter_collection_ = filter_collection.Pass(); |
| 587 ended_cb_ = ended_cb; | 473 ended_cb_ = ended_cb; |
| 588 error_cb_ = error_cb; | 474 error_cb_ = error_cb; |
| 589 seek_cb_ = start_cb; | 475 start_cb_ = start_cb; |
| 590 | 476 |
| 591 // Kick off initialization. | 477 // Kick off initialization. |
| 592 pipeline_init_state_.reset(new PipelineInitState()); | 478 pipeline_init_state_.reset(new PipelineInitState()); |
| 593 | |
| 594 SetState(kInitDemuxer); | 479 SetState(kInitDemuxer); |
| 595 InitializeDemuxer(); | 480 DoInitDemuxer(base::Bind(&Pipeline::DoStateTransition, this)); |
| 596 } | 481 } |
| 597 | 482 |
| 598 // Main initialization method called on the pipeline thread. This code attempts | 483 bool Pipeline::IsTransitioning() { |
| 599 // to use the specified filter factory to build a pipeline. | 484 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 600 // Initialization step performed in this method depends on current state of this | 485 switch (state_) { |
| 601 // object, indicated by |state_|. After each step of initialization, this | 486 case kCreated: |
| 602 // object transits to the next stage. It starts by creating a Demuxer, and then | 487 case kStarted: |
| 603 // connects the Demuxer's audio stream to an AudioDecoder which is then | 488 case kStopped: |
| 604 // connected to an AudioRenderer. If the media has video, then it connects a | 489 return false; |
| 605 // VideoDecoder to the Demuxer's video stream, and then connects the | 490 |
| 606 // VideoDecoder to a VideoRenderer. | 491 case kInitDemuxer: |
| 607 // | 492 case kInitAudioDecoder: |
| 608 // When all required filters have been created and have called their | 493 case kInitAudioRenderer: |
| 609 // FilterHost's InitializationComplete() method, the pipeline will update its | 494 case kInitVideoDecoder: |
| 610 // state to kStarted and |init_cb_|, will be executed. | 495 case kInitVideoRenderer: |
| 611 // | 496 case kPausing: |
| 612 // TODO(hclam): InitializeTask() is now starting the pipeline asynchronously. It | 497 case kFlushing: |
| 613 // works like a big state change table. If we no longer need to start filters | 498 case kSeeking: |
| 614 // in order, we need to get rid of all the state change. | 499 case kStarting: |
| 615 void Pipeline::InitializeTask(PipelineStatus last_stage_status) { | 500 case kStopping: |
| 616 DCHECK(message_loop_->BelongsToCurrentThread()); | 501 return true; |
| 617 | 502 |
| 618 if (last_stage_status != PIPELINE_OK) { | 503 // Catch any left out states. |
| 619 // Currently only VideoDecoders have a recoverable error code. | 504 } |
| 620 if (state_ == kInitVideoDecoder && | 505 NOTREACHED(); |
| 621 last_stage_status == DECODER_ERROR_NOT_SUPPORTED) { | 506 return false; |
| 622 state_ = kInitAudioRenderer; | 507 } |
| 623 } else { | 508 |
| 624 SetError(last_stage_status); | 509 void Pipeline::OnStateTransition(PipelineStatus status) { |
| 510 // Force-post a task on state transitions since to avoid reentrancy between | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
s/since/
| |
| 511 // states. | |
| 512 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 513 &Pipeline::DoStateTransition, this, status)); | |
| 514 } | |
| 515 | |
| 516 void Pipeline::DoStateTransition(PipelineStatus status) { | |
| 517 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 518 | |
| 519 // Preserve existing abnormal status, otherwise update based on the result of | |
| 520 // the previous operation. | |
| 521 status_ = (status_ != PIPELINE_OK ? status_ : status); | |
| 522 | |
| 523 PipelineStatusCB done_cb = base::Bind(&Pipeline::OnStateTransition, this); | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Can move south of the error-handling case.
| |
| 524 base::Closure done_cb_no_status = base::Bind( | |
|
scherkus (not reviewing)
2012/07/28 02:26:07
now that there's only one entry point for state tr
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
See above; I like the CBing, but I think you can s
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
d_c_no_status is a funny name for a thing that has
| |
| 525 &Pipeline::OnStateTransition, this, status_); | |
| 526 | |
| 527 // Check if we need to stop due to an error or due to |stop_cb_| being set. | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
I'm pretty sure you dropped the VideoDecoder fallb
| |
| 528 if (state_ != kStopping && state_ != kStopped && | |
| 529 (status_ != PIPELINE_OK || !stop_cb_.is_null())) { | |
| 530 SetState(kStopping); | |
| 531 DoStop(done_cb_no_status); | |
| 532 return; | |
| 533 } | |
| 534 | |
| 535 switch(state_) { | |
| 536 case kCreated: | |
| 537 NOTREACHED() << "kCreated"; | |
|
scherkus (not reviewing)
2012/07/28 02:26:07
kCreated, kStarted, kStopped are non transitioning
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
IMO best to keep as-is, and drop the default: case
| |
| 538 return; | |
| 539 | |
| 540 case kInitDemuxer: | |
| 541 { | |
| 542 base::AutoLock auto_lock(lock_); | |
| 543 // We do not want to start the clock running. We only want to set the | |
| 544 // base media time so our timestamp calculations will be correct. | |
| 545 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); | |
| 546 } | |
| 547 SetState(kInitAudioDecoder); | |
| 548 DoInitAudioDecoder(done_cb); | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
I'm wondering whether the initialization dance cou
| |
| 549 return; | |
| 550 | |
| 551 case kInitAudioDecoder: | |
| 552 SetState(kInitAudioRenderer); | |
| 553 DoInitAudioRenderer(done_cb); | |
| 554 return; | |
| 555 | |
| 556 case kInitAudioRenderer: | |
| 557 SetState(kInitVideoDecoder); | |
| 558 DoInitVideoDecoder(done_cb); | |
| 559 return; | |
| 560 | |
| 561 case kInitVideoDecoder: | |
| 562 SetState(kInitVideoRenderer); | |
| 563 DoInitVideoRenderer(done_cb); | |
| 564 return; | |
| 565 | |
| 566 case kInitVideoRenderer: { | |
| 567 bool success = true; | |
| 568 { | |
| 569 base::AutoLock l(lock_); | |
| 570 has_audio_ = !!audio_renderer_ && !audio_disabled_; | |
|
scherkus (not reviewing)
2012/07/28 02:31:47
hmm... this will actually cause us to not render t
| |
| 571 has_video_ = !!video_renderer_; | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it really worthwhile maintaining members for th
| |
| 572 | |
| 573 // We're still successful if we have audio but the sound card is busted. | |
| 574 success = !!audio_renderer_ || !!video_renderer_; | |
| 575 } | |
| 576 if (!success) { | |
| 577 DoStateTransition(PIPELINE_ERROR_COULD_NOT_RENDER); | |
| 578 return; | |
| 579 } | |
| 580 | |
| 581 // Clear initialization state now that we're done. | |
| 582 filter_collection_.reset(); | |
| 583 pipeline_init_state_.reset(); | |
| 584 | |
| 585 // Kick off initial preroll. | |
| 586 seek_timestamp_ = demuxer_->GetStartTime(); | |
| 587 SetState(kSeeking); | |
| 588 DoSeek(true, done_cb); | |
| 589 return; | |
| 625 } | 590 } |
| 626 } | 591 |
| 627 | 592 case kPausing: |
| 628 // If we have received the stop or error signal, return immediately. | 593 DCHECK(seek_timestamp_ != kNoTimestamp()); |
| 629 if (IsPipelineStopPending() || IsPipelineStopped() || !IsPipelineOk()) | 594 SetState(kFlushing); |
| 630 return; | 595 DoFlush(done_cb_no_status); |
| 631 | 596 return; |
| 632 DCHECK(state_ == kInitDemuxer || | 597 |
| 633 state_ == kInitAudioDecoder || | 598 case kFlushing: |
| 634 state_ == kInitAudioRenderer || | 599 DCHECK(seek_timestamp_ != kNoTimestamp()); |
| 635 state_ == kInitVideoDecoder || | 600 SetState(kSeeking); |
| 636 state_ == kInitVideoRenderer); | 601 DoSeek(false, done_cb); |
| 637 | 602 return; |
| 638 // Demuxer created, create audio decoder. | 603 |
| 639 if (state_ == kInitDemuxer) { | 604 case kSeeking: |
| 640 SetState(kInitAudioDecoder); | 605 DCHECK(seek_timestamp_ != kNoTimestamp()); |
| 641 // If this method returns false, then there's no audio stream. | 606 seek_timestamp_ = kNoTimestamp(); |
| 642 if (InitializeAudioDecoder(demuxer_)) | 607 |
| 643 return; | 608 SetState(kStarting); |
| 644 } | 609 DoPlay(done_cb_no_status); |
| 645 | 610 return; |
| 646 // Assuming audio decoder was created, create audio renderer. | 611 |
| 647 if (state_ == kInitAudioDecoder) { | 612 case kStarting: |
| 648 SetState(kInitAudioRenderer); | 613 { |
| 649 | 614 base::AutoLock l(lock_); |
| 650 // Returns false if there's no audio stream. | 615 // We use audio stream to update the clock. So if there is such a |
| 651 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder)) { | 616 // stream, we pause the clock until we receive a valid timestamp. |
| 652 base::AutoLock auto_lock(lock_); | 617 waiting_for_clock_update_ = true; |
| 653 has_audio_ = true; | 618 if (!has_audio_) { |
| 654 return; | 619 clock_->SetMaxTime(clock_->Duration()); |
| 655 } | 620 StartClockIfWaitingForTimeUpdate_Locked(); |
| 656 } | 621 } |
| 657 | 622 } |
| 658 // Assuming audio renderer was created, create video decoder. | 623 |
| 659 if (state_ == kInitAudioRenderer) { | 624 SetState(kStarted); |
| 660 // Then perform the stage of initialization, i.e. initialize video decoder. | 625 if (!start_cb_.is_null()) { |
| 661 SetState(kInitVideoDecoder); | 626 DCHECK_EQ(status_, PIPELINE_OK); |
| 662 if (InitializeVideoDecoder(demuxer_)) | 627 base::ResetAndReturn(&start_cb_).Run(status_); |
| 663 return; | 628 return; |
| 664 } | 629 } |
| 665 | 630 if (!seek_cb_.is_null()) { |
| 666 // Assuming video decoder was created, create video renderer. | 631 DCHECK_EQ(status_, PIPELINE_OK); |
| 667 if (state_ == kInitVideoDecoder) { | 632 base::ResetAndReturn(&seek_cb_).Run(status_); |
| 668 SetState(kInitVideoRenderer); | 633 } |
| 669 if (InitializeVideoRenderer(pipeline_init_state_->video_decoder)) { | 634 return; |
| 670 base::AutoLock auto_lock(lock_); | 635 |
| 671 has_video_ = true; | 636 case kStarted: |
| 672 return; | 637 NOTREACHED() << "kStarted"; |
| 673 } | 638 return; |
| 674 } | 639 |
| 675 | 640 case kStopping: |
| 676 if (state_ == kInitVideoRenderer) { | 641 // Release all references. |
| 677 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) { | 642 pipeline_init_state_.reset(); |
| 678 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); | 643 filter_collection_.reset(); |
| 679 return; | 644 audio_renderer_ = NULL; |
| 680 } | 645 video_renderer_ = NULL; |
| 681 | 646 demuxer_ = NULL; |
| 682 // Clear initialization state now that we're done. | 647 { |
| 683 filter_collection_.reset(); | 648 base::AutoLock l(lock_); |
| 684 pipeline_init_state_.reset(); | 649 running_ = false; |
| 685 | 650 } |
| 686 // Initialization was successful, we are now considered paused, so it's safe | 651 SetState(kStopped); |
| 687 // to set the initial playback rate and volume. | 652 |
| 688 PlaybackRateChangedTask(GetPlaybackRate()); | 653 // Execute any client-initiated pending callbacks if we've got some, |
| 689 VolumeChangedTask(GetVolume()); | 654 // otherwise use the permanent callback |error_cb_|. |
| 690 | 655 if (!start_cb_.is_null()) { |
|
scherkus (not reviewing)
2012/07/28 02:26:07
put your thinking caps on for this one + look at m
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Can you be more assertive about the possibility of
| |
| 691 // Fire a seek request to get the renderers to preroll. We can skip a seek | 656 base::ResetAndReturn(&start_cb_).Run(status_); |
| 692 // here as the demuxer should be at the start of the stream. | 657 error_cb_.Reset(); |
| 693 seek_pending_ = true; | 658 } |
| 694 SetState(kSeeking); | 659 if (!seek_cb_.is_null()) { |
| 695 seek_timestamp_ = demuxer_->GetStartTime(); | 660 base::ResetAndReturn(&seek_cb_).Run(status_); |
| 696 DoSeek(seek_timestamp_, true, | 661 error_cb_.Reset(); |
| 697 base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this)); | 662 } |
| 698 } | 663 if (!stop_cb_.is_null()) { |
| 699 } | 664 base::ResetAndReturn(&stop_cb_).Run(); |
| 700 | 665 error_cb_.Reset(); |
| 701 // This method is called as a result of the client calling Pipeline::Stop() or | 666 } |
| 702 // as the result of an error condition. | 667 if (!error_cb_.is_null()) { |
| 703 // We stop the filters in the reverse order. | 668 DCHECK_NE(status_, PIPELINE_OK); |
| 704 // | 669 base::ResetAndReturn(&error_cb_).Run(status_); |
| 705 // TODO(scherkus): beware! this can get posted multiple times since we post | 670 } |
| 706 // Stop() tasks even if we've already stopped. Perhaps this should no-op for | 671 return; |
| 707 // additional calls, however most of this logic will be changing. | 672 |
| 673 case kStopped: | |
| 674 NOTREACHED() << "kStopped"; | |
| 675 return; | |
| 676 | |
| 677 default: | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
drop
| |
| 678 NOTREACHED() << "State has no transition: " << state_; | |
| 679 } | |
| 680 NOTREACHED() << "You should return, not break!"; | |
| 681 } | |
| 682 | |
| 708 void Pipeline::StopTask(const base::Closure& stop_cb) { | 683 void Pipeline::StopTask(const base::Closure& stop_cb) { |
| 709 DCHECK(message_loop_->BelongsToCurrentThread()); | 684 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 710 DCHECK(!IsPipelineStopPending()); | 685 DCHECK(stop_cb_.is_null()); |
| 711 DCHECK_NE(state_, kStopped); | 686 |
| 712 | 687 if (state_ == kStopped) { |
| 713 if (video_decoder_) { | 688 stop_cb.Run(); |
| 714 video_decoder_->PrepareForShutdownHack(); | 689 return; |
| 715 video_decoder_ = NULL; | |
| 716 } | |
| 717 | |
| 718 if (IsPipelineTearingDown() && error_caused_teardown_) { | |
| 719 // If we are stopping due to SetError(), stop normally instead of | |
| 720 // going to error state and calling |error_cb_|. This converts | |
| 721 // the teardown in progress from an error teardown into one that acts | |
| 722 // like the error never occurred. | |
| 723 base::AutoLock auto_lock(lock_); | |
| 724 status_ = PIPELINE_OK; | |
| 725 error_caused_teardown_ = false; | |
| 726 } | 690 } |
| 727 | 691 |
| 728 stop_cb_ = stop_cb; | 692 stop_cb_ = stop_cb; |
| 729 | 693 |
| 730 stop_pending_ = true; | 694 if (IsTransitioning()) |
| 731 if (!IsPipelineSeeking() && !IsPipelineTearingDown()) { | 695 return; |
| 732 // We will tear down pipeline immediately when there is no seek operation | 696 |
| 733 // pending and no teardown in progress. This should include the case where | 697 DoStateTransition(PIPELINE_OK); |
|
scherkus (not reviewing)
2012/07/28 02:26:07
technically I think I can SetState(kStopping) here
| |
| 734 // we are partially initialized. | |
| 735 TearDownPipeline(); | |
| 736 } | |
| 737 } | 698 } |
| 738 | 699 |
| 739 void Pipeline::ErrorChangedTask(PipelineStatus error) { | 700 void Pipeline::ErrorChangedTask(PipelineStatus error) { |
| 740 DCHECK(message_loop_->BelongsToCurrentThread()); | 701 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 741 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | 702 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
| 742 | 703 |
| 743 // Suppress executing additional error logic. Note that if we are currently | 704 // Preserve the error code. |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
s/the/an existing/
| |
| 744 // performing a normal stop, then we return immediately and continue the | 705 if (status_ != PIPELINE_OK) |
| 745 // normal stop. | 706 return; |
| 746 if (IsPipelineStopped() || IsPipelineTearingDown()) { | 707 |
| 747 return; | |
| 748 } | |
| 749 | |
| 750 base::AutoLock auto_lock(lock_); | |
| 751 status_ = error; | 708 status_ = error; |
| 752 | 709 |
| 753 error_caused_teardown_ = true; | 710 if (IsTransitioning()) |
| 754 | 711 return; |
| 755 // Posting TearDownPipeline() to message loop so that we can make sure | 712 |
| 756 // it runs after any pending callbacks that are already queued. | 713 DoStateTransition(status_); |
| 757 // |tearing_down_| is set early here to make sure that pending callbacks | |
| 758 // don't modify the state before TeadDownPipeline() can run. | |
| 759 tearing_down_ = true; | |
| 760 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 761 &Pipeline::TearDownPipeline, this)); | |
| 762 } | 714 } |
| 763 | 715 |
| 764 void Pipeline::PlaybackRateChangedTask(float playback_rate) { | 716 void Pipeline::PlaybackRateChangedTask(float playback_rate) { |
| 765 DCHECK(message_loop_->BelongsToCurrentThread()); | 717 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 766 | 718 |
| 767 if (!running_ || tearing_down_) | 719 // Suppress rate change until we're playing. |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it right to drop this? Or does WMPI already co
| |
| 768 return; | 720 if (state_ != kStarting && state_ != kStarted) |
| 769 | 721 return; |
| 770 // Suppress rate change until after seeking. | |
| 771 if (IsPipelineSeeking()) { | |
| 772 pending_playback_rate_ = playback_rate; | |
| 773 playback_rate_change_pending_ = true; | |
| 774 return; | |
| 775 } | |
| 776 | 722 |
| 777 { | 723 { |
| 778 base::AutoLock auto_lock(lock_); | 724 base::AutoLock auto_lock(lock_); |
| 779 clock_->SetPlaybackRate(playback_rate); | 725 clock_->SetPlaybackRate(playback_rate); |
| 780 } | 726 } |
| 781 | 727 |
| 782 // These will get set after initialization completes in case playback rate is | |
| 783 // set prior to initialization. | |
| 784 if (demuxer_) | 728 if (demuxer_) |
| 785 demuxer_->SetPlaybackRate(playback_rate); | 729 demuxer_->SetPlaybackRate(playback_rate); |
| 786 if (audio_renderer_) | 730 if (audio_renderer_) |
| 787 audio_renderer_->SetPlaybackRate(playback_rate_); | 731 audio_renderer_->SetPlaybackRate(playback_rate_); |
| 788 if (video_renderer_) | 732 if (video_renderer_) |
| 789 video_renderer_->SetPlaybackRate(playback_rate_); | 733 video_renderer_->SetPlaybackRate(playback_rate_); |
| 790 } | 734 } |
| 791 | 735 |
| 792 void Pipeline::VolumeChangedTask(float volume) { | 736 void Pipeline::VolumeChangedTask(float volume) { |
| 793 DCHECK(message_loop_->BelongsToCurrentThread()); | 737 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 794 if (!running_ || tearing_down_) | 738 |
| 739 // Suppress volume change until we're playing. | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
ditto
| |
| 740 if (state_ != kStarting && state_ != kStarted) | |
| 795 return; | 741 return; |
| 796 | 742 |
| 797 if (audio_renderer_) | 743 if (audio_renderer_) |
| 798 audio_renderer_->SetVolume(volume); | 744 audio_renderer_->SetVolume(volume); |
| 799 } | 745 } |
| 800 | 746 |
| 801 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { | 747 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| 802 DCHECK(message_loop_->BelongsToCurrentThread()); | 748 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 803 DCHECK(!IsPipelineStopPending()); | |
| 804 | 749 |
| 805 // Suppress seeking if we're not fully started. | 750 // Suppress seeking if we're not fully started. |
| 806 if (state_ != kStarted && state_ != kEnded) { | 751 if (state_ != kStarted) { |
| 807 // TODO(scherkus): should we run the callback? I'm tempted to say the API | 752 // TODO(scherkus): should we run the callback? I'm tempted to say the API |
| 808 // will only execute the first Seek() request. | 753 // will only execute the first Seek() request. |
| 809 DVLOG(1) << "Media pipeline has not started, ignoring seek to " | 754 DVLOG(1) << "Media pipeline has not started, ignoring seek to " |
| 810 << time.InMicroseconds() << " (current state: " << state_ << ")"; | 755 << time.InMicroseconds() << " (current state: " << state_ << ")"; |
| 811 return; | 756 return; |
| 812 } | 757 } |
| 813 | 758 |
| 814 DCHECK(!seek_pending_); | |
| 815 seek_pending_ = true; | |
| 816 | |
| 817 // We'll need to pause every filter before seeking. The state transition | 759 // We'll need to pause every filter before seeking. The state transition |
| 818 // is as follows: | 760 // is as follows: |
| 819 // kStarted/kEnded | |
| 820 // kPausing (for each filter) | |
| 821 // kSeeking (for each filter) | |
| 822 // kStarting (for each filter) | |
| 823 // kStarted | 761 // kStarted |
| 824 SetState(kPausing); | 762 // kPausing |
| 763 // kSeeking | |
| 764 // kStarting | |
| 765 // kStarted | |
| 825 seek_timestamp_ = std::max(time, demuxer_->GetStartTime()); | 766 seek_timestamp_ = std::max(time, demuxer_->GetStartTime()); |
| 826 seek_cb_ = seek_cb; | 767 seek_cb_ = seek_cb; |
| 827 | 768 |
| 828 // Kick off seeking! | 769 // Kick off seeking! |
| 829 { | 770 { |
| 830 base::AutoLock auto_lock(lock_); | 771 base::AutoLock auto_lock(lock_); |
| 831 if (clock_->IsPlaying()) | 772 if (clock_->IsPlaying()) |
| 832 clock_->Pause(); | 773 clock_->Pause(); |
| 833 } | 774 } |
| 834 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 775 SetState(kPausing); |
| 776 DoPause(base::Bind(&Pipeline::OnStateTransition, this, PIPELINE_OK)); | |
| 835 } | 777 } |
| 836 | 778 |
| 837 void Pipeline::OnRendererEndedTask() { | 779 void Pipeline::OnRendererEndedTask() { |
| 838 DCHECK(message_loop_->BelongsToCurrentThread()); | 780 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 839 | 781 |
| 840 // We can only end if we were actually playing. | 782 // We can only end if we were actually playing. |
| 841 if (state_ != kStarted) { | 783 if (state_ != kStarted) { |
| 842 return; | 784 return; |
| 843 } | 785 } |
| 844 | 786 |
| 845 DCHECK(audio_renderer_ || video_renderer_); | 787 DCHECK(audio_renderer_ || video_renderer_); |
| 846 | 788 |
| 847 // Make sure every extant renderer has ended. | 789 // Make sure every extant renderer has ended. |
| 848 if (audio_renderer_ && !audio_disabled_) { | 790 if (audio_renderer_ && !audio_disabled_) { |
| 849 if (!audio_renderer_->HasEnded()) { | 791 if (!audio_renderer_->HasEnded()) { |
| 850 return; | 792 return; |
| 851 } | 793 } |
| 852 | 794 |
| 853 // Start clock since there is no more audio to | 795 // Start clock since there is no more audio to |
| 854 // trigger clock updates. | 796 // trigger clock updates. |
| 855 base::AutoLock auto_lock(lock_); | 797 base::AutoLock auto_lock(lock_); |
| 856 clock_->SetMaxTime(clock_->Duration()); | 798 clock_->SetMaxTime(clock_->Duration()); |
| 857 StartClockIfWaitingForTimeUpdate_Locked(); | 799 StartClockIfWaitingForTimeUpdate_Locked(); |
| 858 } | 800 } |
| 859 | 801 |
| 860 if (video_renderer_ && !video_renderer_->HasEnded()) { | 802 if (video_renderer_ && !video_renderer_->HasEnded()) { |
| 861 return; | 803 return; |
| 862 } | 804 } |
| 863 | 805 |
| 864 // Transition to ended, executing the callback if present. | |
| 865 SetState(kEnded); | |
| 866 { | 806 { |
| 867 base::AutoLock auto_lock(lock_); | 807 base::AutoLock auto_lock(lock_); |
| 868 clock_->EndOfStream(); | 808 clock_->EndOfStream(); |
| 869 } | 809 } |
| 870 | 810 |
| 871 ReportStatus(ended_cb_, status_); | 811 DCHECK_EQ(status_, PIPELINE_OK); |
| 812 ended_cb_.Run(status_); | |
| 872 } | 813 } |
| 873 | 814 |
| 874 void Pipeline::AudioDisabledTask() { | 815 void Pipeline::AudioDisabledTask() { |
| 875 DCHECK(message_loop_->BelongsToCurrentThread()); | 816 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 876 | 817 |
| 877 base::AutoLock auto_lock(lock_); | 818 base::AutoLock auto_lock(lock_); |
| 878 has_audio_ = false; | 819 has_audio_ = false; |
| 879 audio_disabled_ = true; | 820 audio_disabled_ = true; |
| 880 | 821 |
| 881 // Notify our demuxer that we're no longer rendering audio. | 822 // Notify our demuxer that we're no longer rendering audio. |
| 882 demuxer_->OnAudioRendererDisabled(); | 823 demuxer_->OnAudioRendererDisabled(); |
| 883 | 824 |
| 884 // Start clock since there is no more audio to | 825 // Start clock since there is no more audio to |
| 885 // trigger clock updates. | 826 // trigger clock updates. |
| 886 clock_->SetMaxTime(clock_->Duration()); | 827 clock_->SetMaxTime(clock_->Duration()); |
| 887 StartClockIfWaitingForTimeUpdate_Locked(); | 828 StartClockIfWaitingForTimeUpdate_Locked(); |
| 888 } | 829 } |
| 889 | 830 |
| 890 void Pipeline::FilterStateTransitionTask() { | 831 void Pipeline::DoInitDemuxer(const PipelineStatusCB& done_cb) { |
| 891 DCHECK(message_loop_->BelongsToCurrentThread()); | 832 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 833 DCHECK_EQ(state_, kInitDemuxer); | |
| 892 | 834 |
| 893 // No reason transitioning if we've errored or have stopped. | 835 demuxer_ = filter_collection_->GetDemuxer(); |
| 894 if (IsPipelineStopped()) { | 836 if (!demuxer_) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
is this something other than programming error? C
| |
| 837 done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 895 return; | 838 return; |
| 896 } | 839 } |
| 897 | 840 |
| 898 // If we are tearing down, don't allow any state changes. Teardown | 841 demuxer_->Initialize(this, done_cb); |
| 899 // state changes will come in via TeardownStateTransitionTask(). | 842 } |
| 900 if (IsPipelineTearingDown()) { | 843 |
| 844 void Pipeline::DoInitAudioDecoder(const PipelineStatusCB& done_cb) { | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it right that Pipeline still initializes the de
| |
| 845 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 846 DCHECK_EQ(state_, kInitAudioDecoder); | |
| 847 | |
| 848 scoped_refptr<DemuxerStream> stream = | |
| 849 demuxer_->GetStream(DemuxerStream::AUDIO); | |
| 850 | |
| 851 if (!stream) { | |
| 852 done_cb.Run(PIPELINE_OK); | |
| 901 return; | 853 return; |
| 902 } | 854 } |
| 903 | 855 |
| 904 if (!TransientState(state_)) { | 856 filter_collection_->SelectAudioDecoder(&pipeline_init_state_->audio_decoder); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
This SelectFoo() api is funny in that it doesn't j
| |
| 905 NOTREACHED() << "Invalid current state: " << state_; | 857 if (!pipeline_init_state_->audio_decoder) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
ditto (here and below). Promote to DCHECK?
| |
| 906 SetError(PIPELINE_ERROR_ABORT); | 858 done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| 907 return; | 859 return; |
| 908 } | 860 } |
| 909 | 861 |
| 910 // Decrement the number of remaining transitions, making sure to transition | 862 pipeline_init_state_->audio_decoder->Initialize( |
| 911 // to the next state if needed. | 863 stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this)); |
| 912 SetState(FindNextState(state_)); | |
| 913 if (state_ == kSeeking) { | |
| 914 base::AutoLock auto_lock(lock_); | |
| 915 clock_->SetTime(seek_timestamp_, seek_timestamp_); | |
| 916 } | |
| 917 | |
| 918 // Carry out the action for the current state. | |
| 919 if (TransientState(state_)) { | |
| 920 if (state_ == kPausing) { | |
| 921 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 922 } else if (state_ == kFlushing) { | |
| 923 DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 924 } else if (state_ == kSeeking) { | |
| 925 DoSeek(seek_timestamp_, false, | |
| 926 base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this)); | |
| 927 } else if (state_ == kStarting) { | |
| 928 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 929 } else if (state_ == kStopping) { | |
| 930 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); | |
| 931 } else { | |
| 932 NOTREACHED() << "Unexpected state: " << state_; | |
| 933 } | |
| 934 } else if (state_ == kStarted) { | |
| 935 FinishInitialization(); | |
| 936 | |
| 937 // Finally, complete the seek. | |
| 938 seek_pending_ = false; | |
| 939 | |
| 940 // If a playback rate change was requested during a seek, do it now that | |
| 941 // the seek has compelted. | |
| 942 if (playback_rate_change_pending_) { | |
| 943 playback_rate_change_pending_ = false; | |
| 944 PlaybackRateChangedTask(pending_playback_rate_); | |
| 945 } | |
| 946 | |
| 947 base::AutoLock auto_lock(lock_); | |
| 948 // We use audio stream to update the clock. So if there is such a stream, | |
| 949 // we pause the clock until we receive a valid timestamp. | |
| 950 waiting_for_clock_update_ = true; | |
| 951 if (!has_audio_) { | |
| 952 clock_->SetMaxTime(clock_->Duration()); | |
| 953 StartClockIfWaitingForTimeUpdate_Locked(); | |
| 954 } | |
| 955 | |
| 956 if (IsPipelineStopPending()) { | |
| 957 // We had a pending stop request need to be honored right now. | |
| 958 TearDownPipeline(); | |
| 959 } | |
| 960 } else { | |
| 961 NOTREACHED() << "Unexpected state: " << state_; | |
| 962 } | |
| 963 } | 864 } |
| 964 | 865 |
| 965 void Pipeline::TeardownStateTransitionTask() { | 866 void Pipeline::DoInitAudioRenderer(const PipelineStatusCB& done_cb) { |
| 966 DCHECK(IsPipelineTearingDown()); | 867 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 967 switch (state_) { | 868 DCHECK_EQ(state_, kInitAudioRenderer); |
| 968 case kStopping: | |
| 969 SetState(error_caused_teardown_ ? kError : kStopped); | |
| 970 FinishDestroyingFiltersTask(); | |
| 971 break; | |
| 972 case kPausing: | |
| 973 SetState(kFlushing); | |
| 974 DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 975 break; | |
| 976 case kFlushing: | |
| 977 SetState(kStopping); | |
| 978 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 979 break; | |
| 980 | 869 |
| 981 case kCreated: | 870 if (!pipeline_init_state_->audio_decoder) { |
| 982 case kError: | 871 done_cb.Run(PIPELINE_OK); |
| 983 case kInitDemuxer: | |
| 984 case kInitAudioDecoder: | |
| 985 case kInitAudioRenderer: | |
| 986 case kInitVideoDecoder: | |
| 987 case kInitVideoRenderer: | |
| 988 case kSeeking: | |
| 989 case kStarting: | |
| 990 case kStopped: | |
| 991 case kStarted: | |
| 992 case kEnded: | |
| 993 NOTREACHED() << "Unexpected state for teardown: " << state_; | |
| 994 break; | |
| 995 // default: intentionally left out to force new states to cause compiler | |
| 996 // errors. | |
| 997 }; | |
| 998 } | |
| 999 | |
| 1000 void Pipeline::FinishDestroyingFiltersTask() { | |
| 1001 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1002 DCHECK(IsPipelineStopped()); | |
| 1003 | |
| 1004 audio_renderer_ = NULL; | |
| 1005 video_renderer_ = NULL; | |
| 1006 demuxer_ = NULL; | |
| 1007 | |
| 1008 if (error_caused_teardown_ && !IsPipelineOk() && !error_cb_.is_null()) | |
| 1009 error_cb_.Run(status_); | |
| 1010 | |
| 1011 if (stop_pending_) { | |
| 1012 stop_pending_ = false; | |
| 1013 ResetState(); | |
| 1014 // Notify the client that stopping has finished. | |
| 1015 base::ResetAndReturn(&stop_cb_).Run(); | |
| 1016 } | |
| 1017 | |
| 1018 tearing_down_ = false; | |
| 1019 error_caused_teardown_ = false; | |
| 1020 } | |
| 1021 | |
| 1022 void Pipeline::InitializeDemuxer() { | |
| 1023 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1024 DCHECK(IsPipelineOk()); | |
| 1025 | |
| 1026 demuxer_ = filter_collection_->GetDemuxer(); | |
| 1027 if (!demuxer_) { | |
| 1028 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1029 return; | 872 return; |
| 1030 } | 873 } |
| 1031 | 874 |
| 1032 demuxer_->Initialize(this, base::Bind(&Pipeline::OnDemuxerInitialized, this)); | 875 filter_collection_->SelectAudioRenderer(&audio_renderer_); |
| 1033 } | 876 if (!audio_renderer_) { |
| 1034 | 877 done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| 1035 void Pipeline::OnDemuxerInitialized(PipelineStatus status) { | |
| 1036 if (!message_loop_->BelongsToCurrentThread()) { | |
| 1037 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 1038 &Pipeline::OnDemuxerInitialized, this, status)); | |
| 1039 return; | 878 return; |
| 1040 } | 879 } |
| 1041 | 880 |
| 1042 if (status != PIPELINE_OK) { | |
| 1043 SetError(status); | |
| 1044 return; | |
| 1045 } | |
| 1046 | |
| 1047 { | |
| 1048 base::AutoLock auto_lock(lock_); | |
| 1049 // We do not want to start the clock running. We only want to set the base | |
| 1050 // media time so our timestamp calculations will be correct. | |
| 1051 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); | |
| 1052 } | |
| 1053 | |
| 1054 OnFilterInitialize(PIPELINE_OK); | |
| 1055 } | |
| 1056 | |
| 1057 bool Pipeline::InitializeAudioDecoder( | |
| 1058 const scoped_refptr<Demuxer>& demuxer) { | |
| 1059 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1060 DCHECK(IsPipelineOk()); | |
| 1061 DCHECK(demuxer); | |
| 1062 | |
| 1063 scoped_refptr<DemuxerStream> stream = | |
| 1064 demuxer->GetStream(DemuxerStream::AUDIO); | |
| 1065 | |
| 1066 if (!stream) | |
| 1067 return false; | |
| 1068 | |
| 1069 filter_collection_->SelectAudioDecoder(&pipeline_init_state_->audio_decoder); | |
| 1070 | |
| 1071 if (!pipeline_init_state_->audio_decoder) { | |
| 1072 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1073 return false; | |
| 1074 } | |
| 1075 | |
| 1076 pipeline_init_state_->audio_decoder->Initialize( | |
| 1077 stream, | |
| 1078 base::Bind(&Pipeline::OnFilterInitialize, this), | |
| 1079 base::Bind(&Pipeline::OnUpdateStatistics, this)); | |
| 1080 return true; | |
| 1081 } | |
| 1082 | |
| 1083 bool Pipeline::InitializeVideoDecoder( | |
| 1084 const scoped_refptr<Demuxer>& demuxer) { | |
| 1085 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1086 DCHECK(IsPipelineOk()); | |
| 1087 DCHECK(demuxer); | |
| 1088 | |
| 1089 scoped_refptr<DemuxerStream> stream = | |
| 1090 demuxer->GetStream(DemuxerStream::VIDEO); | |
| 1091 | |
| 1092 if (!stream) | |
| 1093 return false; | |
| 1094 | |
| 1095 filter_collection_->SelectVideoDecoder(&pipeline_init_state_->video_decoder); | |
| 1096 | |
| 1097 if (!pipeline_init_state_->video_decoder) { | |
| 1098 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1099 return false; | |
| 1100 } | |
| 1101 | |
| 1102 pipeline_init_state_->video_decoder->Initialize( | |
| 1103 stream, | |
| 1104 base::Bind(&Pipeline::OnFilterInitialize, this), | |
| 1105 base::Bind(&Pipeline::OnUpdateStatistics, this)); | |
| 1106 | |
| 1107 video_decoder_ = pipeline_init_state_->video_decoder; | |
| 1108 return true; | |
| 1109 } | |
| 1110 | |
| 1111 bool Pipeline::InitializeAudioRenderer( | |
| 1112 const scoped_refptr<AudioDecoder>& decoder) { | |
| 1113 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1114 DCHECK(IsPipelineOk()); | |
| 1115 | |
| 1116 if (!decoder) | |
| 1117 return false; | |
| 1118 | |
| 1119 filter_collection_->SelectAudioRenderer(&audio_renderer_); | |
| 1120 if (!audio_renderer_) { | |
| 1121 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 1122 return false; | |
| 1123 } | |
| 1124 | |
| 1125 audio_renderer_->Initialize( | 881 audio_renderer_->Initialize( |
| 1126 decoder, | 882 pipeline_init_state_->audio_decoder, |
| 1127 base::Bind(&Pipeline::OnFilterInitialize, this), | 883 done_cb, |
| 1128 base::Bind(&Pipeline::OnAudioUnderflow, this), | 884 base::Bind(&Pipeline::OnAudioUnderflow, this), |
| 1129 base::Bind(&Pipeline::OnAudioTimeUpdate, this), | 885 base::Bind(&Pipeline::OnAudioTimeUpdate, this), |
| 1130 base::Bind(&Pipeline::OnRendererEnded, this), | 886 base::Bind(&Pipeline::OnRendererEnded, this), |
| 1131 base::Bind(&Pipeline::OnAudioDisabled, this), | 887 base::Bind(&Pipeline::OnAudioDisabled, this), |
| 1132 base::Bind(&Pipeline::SetError, this)); | 888 base::Bind(&Pipeline::SetError, this)); |
| 1133 return true; | |
| 1134 } | 889 } |
| 1135 | 890 |
| 1136 bool Pipeline::InitializeVideoRenderer( | 891 void Pipeline::DoInitVideoDecoder(const PipelineStatusCB& done_cb) { |
| 1137 const scoped_refptr<VideoDecoder>& decoder) { | |
| 1138 DCHECK(message_loop_->BelongsToCurrentThread()); | 892 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 1139 DCHECK(IsPipelineOk()); | 893 DCHECK_EQ(state_, kInitVideoDecoder); |
| 1140 | 894 |
| 1141 if (!decoder) | 895 scoped_refptr<DemuxerStream> stream = |
| 1142 return false; | 896 demuxer_->GetStream(DemuxerStream::VIDEO); |
| 897 | |
| 898 if (!stream) { | |
| 899 done_cb.Run(PIPELINE_OK); | |
| 900 return; | |
| 901 } | |
| 902 | |
| 903 filter_collection_->SelectVideoDecoder(&pipeline_init_state_->video_decoder); | |
| 904 if (!pipeline_init_state_->video_decoder) { | |
| 905 done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | |
| 906 return; | |
| 907 } | |
| 908 | |
| 909 pipeline_init_state_->video_decoder->Initialize( | |
| 910 stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this)); | |
| 911 } | |
| 912 | |
| 913 void Pipeline::DoInitVideoRenderer(const PipelineStatusCB& done_cb) { | |
| 914 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 915 DCHECK_EQ(state_, kInitVideoRenderer); | |
| 916 | |
| 917 if (!pipeline_init_state_->video_decoder) { | |
| 918 done_cb.Run(PIPELINE_OK); | |
| 919 return; | |
| 920 } | |
| 1143 | 921 |
| 1144 filter_collection_->SelectVideoRenderer(&video_renderer_); | 922 filter_collection_->SelectVideoRenderer(&video_renderer_); |
| 1145 if (!video_renderer_) { | 923 if (!video_renderer_) { |
| 1146 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | 924 done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| 1147 return false; | 925 return; |
| 1148 } | 926 } |
| 1149 | 927 |
| 928 // TODO(scherkus): This is for PrepareForShutdownHack(), see | |
| 929 // http://crbug.com/110228 for details. | |
| 930 video_decoder_ = pipeline_init_state_->video_decoder; | |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Drop this member.
| |
| 931 | |
| 1150 video_renderer_->Initialize( | 932 video_renderer_->Initialize( |
| 1151 decoder, | 933 video_decoder_, |
| 1152 base::Bind(&Pipeline::OnFilterInitialize, this), | 934 done_cb, |
| 1153 base::Bind(&Pipeline::OnUpdateStatistics, this), | 935 base::Bind(&Pipeline::OnUpdateStatistics, this), |
| 1154 base::Bind(&Pipeline::OnVideoTimeUpdate, this), | 936 base::Bind(&Pipeline::OnVideoTimeUpdate, this), |
| 1155 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), | 937 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), |
| 1156 base::Bind(&Pipeline::OnRendererEnded, this), | 938 base::Bind(&Pipeline::OnRendererEnded, this), |
| 1157 base::Bind(&Pipeline::SetError, this), | 939 base::Bind(&Pipeline::SetError, this), |
| 1158 base::Bind(&Pipeline::GetMediaTime, this), | 940 base::Bind(&Pipeline::GetMediaTime, this), |
| 1159 base::Bind(&Pipeline::GetMediaDuration, this)); | 941 base::Bind(&Pipeline::GetMediaDuration, this)); |
| 1160 return true; | |
| 1161 } | 942 } |
| 1162 | 943 |
| 1163 void Pipeline::TearDownPipeline() { | 944 void Pipeline::DoSeek(bool skip_demuxer_seek, |
| 1164 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 1165 DCHECK_NE(kStopped, state_); | |
| 1166 | |
| 1167 DCHECK(!tearing_down_ || // Teardown on Stop(). | |
| 1168 (tearing_down_ && error_caused_teardown_) || // Teardown on error. | |
| 1169 (tearing_down_ && stop_pending_)); // Stop during teardown by error. | |
| 1170 | |
| 1171 // Mark that we already start tearing down operation. | |
| 1172 tearing_down_ = true; | |
| 1173 | |
| 1174 switch (state_) { | |
| 1175 case kCreated: | |
| 1176 case kError: | |
| 1177 SetState(kStopped); | |
| 1178 // Need to put this in the message loop to make sure that it comes | |
| 1179 // after any pending callback tasks that are already queued. | |
| 1180 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 1181 &Pipeline::FinishDestroyingFiltersTask, this)); | |
| 1182 break; | |
| 1183 | |
| 1184 case kInitDemuxer: | |
| 1185 case kInitAudioDecoder: | |
| 1186 case kInitAudioRenderer: | |
| 1187 case kInitVideoDecoder: | |
| 1188 case kInitVideoRenderer: | |
| 1189 // Make it look like initialization was successful. | |
| 1190 filter_collection_.reset(); | |
| 1191 pipeline_init_state_.reset(); | |
| 1192 | |
| 1193 SetState(kStopping); | |
| 1194 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 1195 | |
| 1196 FinishInitialization(); | |
| 1197 break; | |
| 1198 | |
| 1199 case kPausing: | |
| 1200 case kSeeking: | |
| 1201 case kFlushing: | |
| 1202 case kStarting: | |
| 1203 SetState(kStopping); | |
| 1204 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 1205 | |
| 1206 if (seek_pending_) { | |
| 1207 seek_pending_ = false; | |
| 1208 FinishInitialization(); | |
| 1209 } | |
| 1210 | |
| 1211 break; | |
| 1212 | |
| 1213 case kStarted: | |
| 1214 case kEnded: | |
| 1215 SetState(kPausing); | |
| 1216 DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | |
| 1217 break; | |
| 1218 | |
| 1219 case kStopping: | |
| 1220 case kStopped: | |
| 1221 NOTREACHED() << "Unexpected state for teardown: " << state_; | |
| 1222 break; | |
| 1223 // default: intentionally left out to force new states to cause compiler | |
| 1224 // errors. | |
| 1225 }; | |
| 1226 } | |
| 1227 | |
| 1228 void Pipeline::DoSeek(base::TimeDelta seek_timestamp, | |
| 1229 bool skip_demuxer_seek, | |
| 1230 const PipelineStatusCB& done_cb) { | 945 const PipelineStatusCB& done_cb) { |
| 1231 DCHECK(message_loop_->BelongsToCurrentThread()); | 946 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 947 DCHECK(state_ == kSeeking); | |
| 948 DCHECK(seek_timestamp_ != kNoTimestamp()); | |
| 949 | |
| 950 { | |
| 951 base::AutoLock l(lock_); | |
| 952 clock_->SetTime(seek_timestamp_, seek_timestamp_); | |
| 953 } | |
| 954 | |
| 1232 scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs( | 955 scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs( |
| 1233 new std::queue<PipelineStatusCBFunc>()); | 956 new std::queue<PipelineStatusCBFunc>()); |
| 1234 | 957 |
| 1235 if (!skip_demuxer_seek) | 958 if (!skip_demuxer_seek) |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is this really important? What happens if we don'
| |
| 1236 status_cbs->push(base::Bind(&Demuxer::Seek, demuxer_, seek_timestamp)); | 959 status_cbs->push(base::Bind(&Demuxer::Seek, demuxer_, seek_timestamp_)); |
| 1237 | 960 |
| 1238 if (audio_renderer_) | 961 if (audio_renderer_) |
| 1239 status_cbs->push(base::Bind( | 962 status_cbs->push(base::Bind( |
| 1240 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); | 963 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp_)); |
| 1241 | 964 |
| 1242 if (video_renderer_) | 965 if (video_renderer_) |
| 1243 status_cbs->push(base::Bind( | 966 status_cbs->push(base::Bind( |
| 1244 &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); | 967 &VideoRenderer::Preroll, video_renderer_, seek_timestamp_)); |
| 1245 | 968 |
| 1246 RunInSeriesWithStatus(status_cbs.Pass(), base::Bind( | 969 RunInSeriesWithStatus(status_cbs.Pass(), done_cb); |
| 1247 &Pipeline::ReportStatus, this, done_cb)); | |
| 1248 } | 970 } |
| 1249 | 971 |
| 1250 void Pipeline::OnAudioUnderflow() { | 972 void Pipeline::OnAudioUnderflow() { |
| 1251 if (!message_loop_->BelongsToCurrentThread()) { | 973 if (!message_loop_->BelongsToCurrentThread()) { |
| 1252 message_loop_->PostTask(FROM_HERE, base::Bind( | 974 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 1253 &Pipeline::OnAudioUnderflow, this)); | 975 &Pipeline::OnAudioUnderflow, this)); |
| 1254 return; | 976 return; |
| 1255 } | 977 } |
| 1256 | 978 |
| 1257 if (state_ != kStarted) | 979 if (state_ != kStarted) |
| 1258 return; | 980 return; |
| 1259 | 981 |
| 1260 if (audio_renderer_) | 982 if (audio_renderer_) |
| 1261 audio_renderer_->ResumeAfterUnderflow(true); | 983 audio_renderer_->ResumeAfterUnderflow(true); |
| 1262 } | 984 } |
| 1263 | 985 |
| 1264 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 986 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
| 1265 lock_.AssertAcquired(); | 987 lock_.AssertAcquired(); |
| 1266 if (!waiting_for_clock_update_) | 988 if (!waiting_for_clock_update_) |
| 1267 return; | 989 return; |
| 1268 | 990 |
| 1269 waiting_for_clock_update_ = false; | 991 waiting_for_clock_update_ = false; |
| 1270 clock_->Play(); | 992 clock_->Play(); |
| 1271 } | 993 } |
| 1272 | 994 |
| 1273 } // namespace media | 995 } // namespace media |
| OLD | NEW |