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 |