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_impl.h" | 5 #include "media/base/pipeline_impl.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
112 void RemoveTextStreamTask(DemuxerStream* text_stream); | 112 void RemoveTextStreamTask(DemuxerStream* text_stream); |
113 | 113 |
114 // Common handlers for notifications from renderers and demuxer. | 114 // Common handlers for notifications from renderers and demuxer. |
115 void OnPipelineError(PipelineStatus error); | 115 void OnPipelineError(PipelineStatus error); |
116 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 116 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
117 CdmContext* cdm_context, | 117 CdmContext* cdm_context, |
118 bool success); | 118 bool success); |
119 void CheckPlaybackEnded(); | 119 void CheckPlaybackEnded(); |
120 | 120 |
121 // State transition tasks. | 121 // State transition tasks. |
122 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); | |
123 void DoStop(const base::Closure& done_cb); | |
124 void SetState(State next_state); | 122 void SetState(State next_state); |
125 State GetNextState() const; | 123 void CompleteSeek(base::TimeDelta seek_time, PipelineStatus status); |
126 void StateTransitionTask(PipelineStatus status); | 124 void CompleteSuspend(PipelineStatus status); |
127 void InitializeDemuxer(const PipelineStatusCB& done_cb); | 125 void InitializeDemuxer(const PipelineStatusCB& done_cb); |
128 void InitializeRenderer(const PipelineStatusCB& done_cb); | 126 void InitializeRenderer(const PipelineStatusCB& done_cb); |
129 void DestroyRenderer(); | 127 void DestroyRenderer(); |
130 void ReportMetadata(); | 128 void ReportMetadata(); |
131 | 129 |
132 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; | 130 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
133 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 131 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
134 const scoped_refptr<MediaLog> media_log_; | 132 const scoped_refptr<MediaLog> media_log_; |
135 | 133 |
136 base::WeakPtr<PipelineImpl> weak_pipeline_; | 134 base::WeakPtr<PipelineImpl> weak_pipeline_; |
(...skipping 11 matching lines...) Expand all Loading... | |
148 | 146 |
149 // Current state of the pipeline. | 147 // Current state of the pipeline. |
150 State state_; | 148 State state_; |
151 | 149 |
152 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that | 150 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that |
153 // the pipeline is operating correctly. Any other value indicates that the | 151 // the pipeline is operating correctly. Any other value indicates that the |
154 // pipeline is stopped or is stopping. Clients can call the Stop() method to | 152 // pipeline is stopped or is stopping. Clients can call the Stop() method to |
155 // reset the pipeline state, and restore this to PIPELINE_OK. | 153 // reset the pipeline state, and restore this to PIPELINE_OK. |
156 PipelineStatus status_; | 154 PipelineStatus status_; |
157 | 155 |
158 // The timestamp to start playback from after starting/seeking/resuming has | |
159 // completed. | |
160 base::TimeDelta start_timestamp_; | |
161 | |
162 // Whether we've received the audio/video/text ended events. | 156 // Whether we've received the audio/video/text ended events. |
163 bool renderer_ended_; | 157 bool renderer_ended_; |
164 bool text_renderer_ended_; | 158 bool text_renderer_ended_; |
165 | 159 |
166 // Series of tasks to Start(), Seek(), and Resume(). | 160 // Series of tasks to Start(), Seek(), and Resume(). |
167 std::unique_ptr<SerialRunner> pending_callbacks_; | 161 std::unique_ptr<SerialRunner> pending_callbacks_; |
168 | 162 |
169 base::WeakPtr<RendererWrapper> weak_this_; | 163 base::WeakPtr<RendererWrapper> weak_this_; |
170 base::WeakPtrFactory<RendererWrapper> weak_factory_; | 164 base::WeakPtrFactory<RendererWrapper> weak_factory_; |
171 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); | 165 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); |
(...skipping 16 matching lines...) Expand all Loading... | |
188 weak_factory_(this) { | 182 weak_factory_(this) { |
189 weak_this_ = weak_factory_.GetWeakPtr(); | 183 weak_this_ = weak_factory_.GetWeakPtr(); |
190 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 184 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
191 } | 185 } |
192 | 186 |
193 PipelineImpl::RendererWrapper::~RendererWrapper() { | 187 PipelineImpl::RendererWrapper::~RendererWrapper() { |
194 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 188 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
195 DCHECK(state_ == kCreated || state_ == kStopped); | 189 DCHECK(state_ == kCreated || state_ == kStopped); |
196 } | 190 } |
197 | 191 |
192 // Note that the usage of base::Unretained() with the renderers is considered | |
193 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
194 // | |
195 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
196 // running on the media thread would result in crashes. | |
197 | |
198 void PipelineImpl::RendererWrapper::Start( | 198 void PipelineImpl::RendererWrapper::Start( |
199 Demuxer* demuxer, | 199 Demuxer* demuxer, |
200 std::unique_ptr<Renderer> renderer, | 200 std::unique_ptr<Renderer> renderer, |
201 std::unique_ptr<TextRenderer> text_renderer, | 201 std::unique_ptr<TextRenderer> text_renderer, |
202 base::WeakPtr<PipelineImpl> weak_pipeline) { | 202 base::WeakPtr<PipelineImpl> weak_pipeline) { |
203 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 203 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
204 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " | 204 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
205 << state_; | 205 << state_; |
206 | 206 |
207 SetState(kStarting); | |
208 | |
207 DCHECK(!demuxer_); | 209 DCHECK(!demuxer_); |
208 DCHECK(!shared_state_.renderer); | 210 DCHECK(!shared_state_.renderer); |
209 DCHECK(!text_renderer_); | 211 DCHECK(!text_renderer_); |
210 DCHECK(!renderer_ended_); | 212 DCHECK(!renderer_ended_); |
211 DCHECK(!text_renderer_ended_); | 213 DCHECK(!text_renderer_ended_); |
212 DCHECK(!weak_pipeline_); | 214 DCHECK(!weak_pipeline_); |
213 demuxer_ = demuxer; | 215 demuxer_ = demuxer; |
214 { | 216 { |
215 base::AutoLock auto_lock(shared_state_lock_); | 217 base::AutoLock auto_lock(shared_state_lock_); |
216 shared_state_.renderer = std::move(renderer); | 218 shared_state_.renderer = std::move(renderer); |
217 } | 219 } |
218 text_renderer_ = std::move(text_renderer); | 220 text_renderer_ = std::move(text_renderer); |
219 if (text_renderer_) { | 221 if (text_renderer_) { |
220 text_renderer_->Initialize( | 222 text_renderer_->Initialize( |
221 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); | 223 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); |
222 } | 224 } |
223 weak_pipeline_ = weak_pipeline; | 225 weak_pipeline_ = weak_pipeline; |
224 | 226 |
225 StateTransitionTask(PIPELINE_OK); | 227 // Queue asynchronous actions required to start. |
228 DCHECK(!pending_callbacks_); | |
229 SerialRunner::Queue fns; | |
230 | |
231 // Initialize demuxer. | |
232 fns.Push(base::Bind(&RendererWrapper::InitializeDemuxer, weak_this_)); | |
233 | |
234 // Once the demuxer is initialized successfully, media metadata must be | |
235 // available - report the metadata to client. | |
236 fns.Push(base::Bind(&RendererWrapper::ReportMetadata, weak_this_)); | |
237 | |
238 // Initialize renderer. | |
239 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | |
240 | |
241 // Run tasks. | |
242 pending_callbacks_ = | |
243 SerialRunner::Run(fns, base::Bind(&RendererWrapper::CompleteSeek, | |
244 weak_this_, base::TimeDelta())); | |
226 } | 245 } |
227 | 246 |
228 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { | 247 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
229 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 248 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
230 DCHECK(state_ != kStopping && state_ != kStopped); | 249 DCHECK(state_ != kStopping && state_ != kStopped); |
231 | 250 |
232 SetState(kStopping); | 251 SetState(kStopping); |
233 | 252 |
234 if (shared_state_.statistics.video_frames_decoded > 0) { | 253 if (shared_state_.statistics.video_frames_decoded > 0) { |
235 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | 254 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
236 shared_state_.statistics.video_frames_dropped); | 255 shared_state_.statistics.video_frames_dropped); |
237 } | 256 } |
238 | 257 |
239 // If we stop during starting/seeking/suspending/resuming we don't want to | 258 // If we stop during starting/seeking/suspending/resuming we don't want to |
240 // leave outstanding callbacks around. The callbacks also do not get run if | 259 // leave outstanding callbacks around. The callbacks also do not get run if |
241 // the pipeline is stopped before it had a chance to complete outstanding | 260 // the pipeline is stopped before it had a chance to complete outstanding |
242 // tasks. | 261 // tasks. |
243 pending_callbacks_.reset(); | 262 pending_callbacks_.reset(); |
244 | 263 |
245 DoStop(stop_cb); | 264 DestroyRenderer(); |
alokp
2016/06/24 06:15:06
DoStop() moved here.
| |
265 text_renderer_.reset(); | |
266 | |
267 if (demuxer_) { | |
268 demuxer_->Stop(); | |
269 demuxer_ = NULL; | |
270 } | |
271 | |
272 SetState(kStopped); | |
273 | |
274 // Post the stop callback to enqueue it after the tasks that may have been | |
275 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
276 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
277 // enqueued after |stop_cb|. This may be problematic because Demuxer may | |
278 // get destroyed as soon as |stop_cb| is run. In practice this is not a | |
279 // problem, but ideally Demuxer should be destroyed on the media thread. | |
280 media_task_runner_->PostTask(FROM_HERE, stop_cb); | |
246 } | 281 } |
247 | 282 |
248 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { | 283 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
249 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 284 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
250 | 285 |
251 // Suppress seeking if we're not fully started. | 286 // Suppress seeking if we're not fully started. |
252 if (state_ != kPlaying) { | 287 if (state_ != kPlaying) { |
253 DCHECK(state_ == kStopping || state_ == kStopped) | 288 DCHECK(state_ == kStopping || state_ == kStopped) |
254 << "Receive seek in unexpected state: " << state_; | 289 << "Receive seek in unexpected state: " << state_; |
255 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 290 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
256 return; | 291 return; |
257 } | 292 } |
258 | 293 |
259 const base::TimeDelta seek_timestamp = | 294 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); |
260 std::max(time, demuxer_->GetStartTime()); | |
261 | 295 |
262 SetState(kSeeking); | 296 SetState(kSeeking); |
263 renderer_ended_ = false; | 297 renderer_ended_ = false; |
264 text_renderer_ended_ = false; | 298 text_renderer_ended_ = false; |
265 start_timestamp_ = seek_timestamp; | |
266 | 299 |
267 DoSeek(seek_timestamp, | 300 // Queue asynchronous actions required to start. |
alokp
2016/06/24 06:15:07
DoSeek() moved here.
| |
268 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 301 DCHECK(!pending_callbacks_); |
302 SerialRunner::Queue bound_fns; | |
303 | |
304 // Pause. | |
305 if (text_renderer_) { | |
306 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
307 base::Unretained(text_renderer_.get()))); | |
308 } | |
309 | |
310 // Flush. | |
311 DCHECK(shared_state_.renderer); | |
312 bound_fns.Push(base::Bind(&Renderer::Flush, | |
313 base::Unretained(shared_state_.renderer.get()))); | |
314 | |
315 if (text_renderer_) { | |
316 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
317 base::Unretained(text_renderer_.get()))); | |
318 } | |
319 | |
320 // Seek demuxer. | |
321 bound_fns.Push( | |
322 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
323 | |
324 // Run tasks. | |
325 pending_callbacks_ = SerialRunner::Run( | |
326 bound_fns, | |
327 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_timestamp)); | |
269 } | 328 } |
270 | 329 |
271 void PipelineImpl::RendererWrapper::Suspend() { | 330 void PipelineImpl::RendererWrapper::Suspend() { |
272 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 331 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
273 | 332 |
274 // Suppress suspending if we're not playing. | 333 // Suppress suspending if we're not playing. |
275 if (state_ != kPlaying) { | 334 if (state_ != kPlaying) { |
276 DCHECK(state_ == kStopping || state_ == kStopped) | 335 DCHECK(state_ == kStopping || state_ == kStopped) |
277 << "Receive suspend in unexpected state: " << state_; | 336 << "Receive suspend in unexpected state: " << state_; |
278 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 337 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
279 return; | 338 return; |
280 } | 339 } |
281 DCHECK(shared_state_.renderer); | 340 DCHECK(shared_state_.renderer); |
282 DCHECK(!pending_callbacks_.get()); | 341 DCHECK(!pending_callbacks_.get()); |
283 | 342 |
284 SetState(kSuspending); | 343 SetState(kSuspending); |
285 | 344 |
286 // Freeze playback and record the media time before flushing. (Flushing clears | 345 // Freeze playback and record the media time before flushing. (Flushing clears |
287 // the value.) | 346 // the value.) |
288 shared_state_.renderer->SetPlaybackRate(0.0); | 347 shared_state_.renderer->SetPlaybackRate(0.0); |
289 { | 348 { |
290 base::AutoLock auto_lock(shared_state_lock_); | 349 base::AutoLock auto_lock(shared_state_lock_); |
291 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); | 350 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); |
292 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp()); | 351 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp()); |
293 } | 352 } |
294 | 353 |
295 // Queue the asynchronous actions required to stop playback. (Matches setup in | 354 // Queue the asynchronous actions required to stop playback. |
296 // DoSeek().) | |
297 // TODO(sandersd): Share implementation with DoSeek(). | |
298 SerialRunner::Queue fns; | 355 SerialRunner::Queue fns; |
299 | 356 |
300 if (text_renderer_) { | 357 if (text_renderer_) { |
301 fns.Push(base::Bind(&TextRenderer::Pause, | 358 fns.Push(base::Bind(&TextRenderer::Pause, |
302 base::Unretained(text_renderer_.get()))); | 359 base::Unretained(text_renderer_.get()))); |
303 } | 360 } |
304 | 361 |
305 fns.Push(base::Bind(&Renderer::Flush, | 362 fns.Push(base::Bind(&Renderer::Flush, |
306 base::Unretained(shared_state_.renderer.get()))); | 363 base::Unretained(shared_state_.renderer.get()))); |
307 | 364 |
308 if (text_renderer_) { | 365 if (text_renderer_) { |
309 fns.Push(base::Bind(&TextRenderer::Flush, | 366 fns.Push(base::Bind(&TextRenderer::Flush, |
310 base::Unretained(text_renderer_.get()))); | 367 base::Unretained(text_renderer_.get()))); |
311 } | 368 } |
312 | 369 |
313 pending_callbacks_ = SerialRunner::Run( | 370 pending_callbacks_ = SerialRunner::Run( |
314 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 371 fns, base::Bind(&RendererWrapper::CompleteSuspend, weak_this_)); |
315 } | 372 } |
316 | 373 |
317 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, | 374 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
318 base::TimeDelta timestamp) { | 375 base::TimeDelta timestamp) { |
319 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 376 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
320 | 377 |
321 // Suppress resuming if we're not suspended. | 378 // Suppress resuming if we're not suspended. |
322 if (state_ != kSuspended) { | 379 if (state_ != kSuspended) { |
323 DCHECK(state_ == kStopping || state_ == kStopped) | 380 DCHECK(state_ == kStopping || state_ == kStopped) |
324 << "Receive resume in unexpected state: " << state_; | 381 << "Receive resume in unexpected state: " << state_; |
325 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 382 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
326 return; | 383 return; |
327 } | 384 } |
328 DCHECK(!shared_state_.renderer); | 385 DCHECK(!shared_state_.renderer); |
329 DCHECK(!pending_callbacks_.get()); | 386 DCHECK(!pending_callbacks_.get()); |
330 | 387 |
331 SetState(kResuming); | 388 SetState(kResuming); |
332 | 389 |
333 { | 390 { |
334 base::AutoLock auto_lock(shared_state_lock_); | 391 base::AutoLock auto_lock(shared_state_lock_); |
335 shared_state_.renderer = std::move(renderer); | 392 shared_state_.renderer = std::move(renderer); |
336 } | 393 } |
337 | 394 |
338 // Set up for a seek. (Matches setup in SeekTask().) | |
339 // TODO(sandersd): Share implementation with SeekTask(). | |
340 renderer_ended_ = false; | 395 renderer_ended_ = false; |
341 text_renderer_ended_ = false; | 396 text_renderer_ended_ = false; |
342 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); | 397 base::TimeDelta start_timestamp = |
398 std::max(timestamp, demuxer_->GetStartTime()); | |
343 | 399 |
344 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), | 400 // Queue the asynchronous actions required to start playback. |
345 // we need to initialize the renderer ourselves (we don't want to enter state | |
346 // kInitDemuxer, and even if we did the current code would seek to the start | |
347 // instead of |timestamp|). | |
348 SerialRunner::Queue fns; | 401 SerialRunner::Queue fns; |
349 | 402 |
350 fns.Push( | 403 fns.Push( |
351 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); | 404 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp)); |
352 | 405 |
353 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | 406 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
354 | 407 |
355 pending_callbacks_ = SerialRunner::Run( | 408 pending_callbacks_ = SerialRunner::Run( |
356 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 409 fns, |
410 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, start_timestamp)); | |
357 } | 411 } |
358 | 412 |
359 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { | 413 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
360 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 414 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
361 | 415 |
362 playback_rate_ = playback_rate; | 416 playback_rate_ = playback_rate; |
363 if (state_ == kPlaying) | 417 if (state_ == kPlaying) |
364 shared_state_.renderer->SetPlaybackRate(playback_rate_); | 418 shared_state_.renderer->SetPlaybackRate(playback_rate_); |
365 } | 419 } |
366 | 420 |
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
612 return; | 666 return; |
613 | 667 |
614 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 668 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
615 return; | 669 return; |
616 | 670 |
617 DCHECK_EQ(status_, PIPELINE_OK); | 671 DCHECK_EQ(status_, PIPELINE_OK); |
618 main_task_runner_->PostTask( | 672 main_task_runner_->PostTask( |
619 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); | 673 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
620 } | 674 } |
621 | 675 |
622 // Note that the usage of base::Unretained() with the renderers is considered | |
623 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
624 // | |
625 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
626 // running on the media thread would result in crashes. | |
627 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, | |
628 const PipelineStatusCB& done_cb) { | |
629 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
630 DCHECK(!pending_callbacks_.get()); | |
631 DCHECK_EQ(state_, kSeeking); | |
632 SerialRunner::Queue bound_fns; | |
633 | |
634 // Pause. | |
635 if (text_renderer_) { | |
636 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
637 base::Unretained(text_renderer_.get()))); | |
638 } | |
639 | |
640 // Flush. | |
641 DCHECK(shared_state_.renderer); | |
642 bound_fns.Push(base::Bind(&Renderer::Flush, | |
643 base::Unretained(shared_state_.renderer.get()))); | |
644 | |
645 if (text_renderer_) { | |
646 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
647 base::Unretained(text_renderer_.get()))); | |
648 } | |
649 | |
650 // Seek demuxer. | |
651 bound_fns.Push( | |
652 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
653 | |
654 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | |
655 } | |
656 | |
657 void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { | |
658 DVLOG(2) << __FUNCTION__; | |
659 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
660 DCHECK_EQ(state_, kStopping); | |
661 DCHECK(!pending_callbacks_.get()); | |
662 | |
663 DestroyRenderer(); | |
664 text_renderer_.reset(); | |
665 | |
666 if (demuxer_) { | |
667 demuxer_->Stop(); | |
668 demuxer_ = NULL; | |
669 } | |
670 | |
671 SetState(kStopped); | |
672 | |
673 // Post the stop callback to enqueue it after the tasks that may have been | |
674 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
675 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
676 // enqueued after |done_cb|. This may be problematic because Demuxer may | |
677 // get destroyed as soon as |done_cb| is run. In practice this is not a | |
678 // problem, but ideally Demuxer should be destroyed on the media thread. | |
679 media_task_runner_->PostTask(FROM_HERE, done_cb); | |
680 } | |
681 | |
682 void PipelineImpl::RendererWrapper::SetState(State next_state) { | 676 void PipelineImpl::RendererWrapper::SetState(State next_state) { |
683 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 677 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
684 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " | 678 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
685 << PipelineImpl::GetStateString(next_state); | 679 << PipelineImpl::GetStateString(next_state); |
686 | 680 |
687 state_ = next_state; | 681 state_ = next_state; |
688 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 682 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
689 } | 683 } |
690 | 684 |
691 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { | 685 void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time, |
686 PipelineStatus status) { | |
692 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 687 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
693 DCHECK_EQ(status_, PIPELINE_OK) | 688 DCHECK(state_ == kStarting || state_ == kSeeking || state_ == kResuming); |
694 << "State transitions don't happen when there's an error: " << status_; | |
695 | 689 |
696 switch (state_) { | 690 DCHECK(pending_callbacks_); |
697 case kCreated: | 691 pending_callbacks_.reset(); |
698 return kInitDemuxer; | |
699 | 692 |
700 case kInitDemuxer: | |
701 return kInitRenderer; | |
702 | |
703 case kInitRenderer: | |
704 case kSeeking: | |
705 return kPlaying; | |
706 | |
707 case kSuspending: | |
708 return kSuspended; | |
709 | |
710 case kSuspended: | |
711 return kResuming; | |
712 | |
713 case kResuming: | |
714 return kPlaying; | |
715 | |
716 case kPlaying: | |
717 case kStopping: | |
718 case kStopped: | |
719 break; | |
720 } | |
721 NOTREACHED() << "State has no transition: " << state_; | |
722 return state_; | |
723 } | |
724 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { | |
725 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
726 | |
727 // No-op any state transitions if we're stopping or already encountered error. | |
728 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK) | |
729 return; | |
730 | |
731 // Report error from the previous operation. | |
732 if (status != PIPELINE_OK) { | 693 if (status != PIPELINE_OK) { |
733 OnPipelineError(status); | 694 OnPipelineError(status); |
734 return; | 695 return; |
735 } | 696 } |
736 | 697 |
737 // Guard against accidentally clearing |pending_callbacks_| for states that | 698 shared_state_.renderer->StartPlayingFrom( |
alokp
2016/06/24 06:15:07
copied from PipelineImpl::RendererWrapper::StateTr
| |
738 // use it as well as states that should not be using it. | 699 std::max(seek_time, demuxer_->GetStartTime())); |
739 DCHECK_EQ(pending_callbacks_.get() != NULL, | 700 { |
740 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 701 base::AutoLock auto_lock(shared_state_lock_); |
702 shared_state_.suspend_timestamp = kNoTimestamp(); | |
703 } | |
741 | 704 |
705 if (text_renderer_) | |
706 text_renderer_->StartPlaying(); | |
707 | |
708 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
709 shared_state_.renderer->SetVolume(volume_); | |
710 | |
711 SetState(kPlaying); | |
712 main_task_runner_->PostTask( | |
713 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_)); | |
714 } | |
715 | |
716 void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status) { | |
717 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
718 DCHECK_EQ(kSuspending, state_); | |
719 | |
720 DCHECK(pending_callbacks_); | |
742 pending_callbacks_.reset(); | 721 pending_callbacks_.reset(); |
743 | 722 |
744 PipelineStatusCB done_cb = | 723 // In case we are suspending or suspended, the error may be recoverable, |
745 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_); | 724 // so don't propagate it now, instead let the subsequent seek during resume |
725 // propagate it if it's unrecoverable. | |
726 LOG_IF(WARNING, status != PIPELINE_OK) | |
727 << "Encountered pipeline error while suspending: " << status; | |
746 | 728 |
747 // Switch states, performing any entrance actions for the new state as well. | 729 DestroyRenderer(); |
alokp
2016/06/24 06:15:07
copied from PipelineImpl::RendererWrapper::StateTr
| |
748 SetState(GetNextState()); | 730 { |
749 switch (state_) { | 731 base::AutoLock auto_lock(shared_state_lock_); |
750 case kInitDemuxer: | 732 shared_state_.statistics.audio_memory_usage = 0; |
751 return InitializeDemuxer(done_cb); | 733 shared_state_.statistics.video_memory_usage = 0; |
734 } | |
752 | 735 |
753 case kInitRenderer: | 736 SetState(kSuspended); |
754 // When the state_ transfers to kInitRenderer, it means the demuxer has | 737 main_task_runner_->PostTask( |
755 // finished parsing the init info. It should call ReportMetadata in case | 738 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_)); |
756 // meeting 'decode' error when passing media segment but WebMediaPlayer's | |
757 // ready_state_ is still ReadyStateHaveNothing. In that case, it will | |
758 // treat it as NetworkStateFormatError not NetworkStateDecodeError. | |
759 ReportMetadata(); | |
760 start_timestamp_ = demuxer_->GetStartTime(); | |
761 | |
762 return InitializeRenderer(done_cb); | |
763 | |
764 case kPlaying: | |
765 DCHECK(start_timestamp_ >= base::TimeDelta()); | |
766 shared_state_.renderer->StartPlayingFrom(start_timestamp_); | |
767 { | |
768 base::AutoLock auto_lock(shared_state_lock_); | |
769 shared_state_.suspend_timestamp = kNoTimestamp(); | |
770 } | |
771 | |
772 if (text_renderer_) | |
773 text_renderer_->StartPlaying(); | |
774 | |
775 main_task_runner_->PostTask( | |
776 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, | |
777 start_timestamp_)); | |
778 | |
779 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
780 shared_state_.renderer->SetVolume(volume_); | |
781 return; | |
782 | |
783 case kSuspended: | |
784 DestroyRenderer(); | |
785 { | |
786 base::AutoLock auto_lock(shared_state_lock_); | |
787 shared_state_.statistics.audio_memory_usage = 0; | |
788 shared_state_.statistics.video_memory_usage = 0; | |
789 } | |
790 main_task_runner_->PostTask( | |
791 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, | |
792 shared_state_.suspend_timestamp)); | |
793 return; | |
794 | |
795 case kStopping: | |
796 case kStopped: | |
797 case kCreated: | |
798 case kSeeking: | |
799 case kSuspending: | |
800 case kResuming: | |
801 NOTREACHED() << "State has no transition: " << state_; | |
802 return; | |
803 } | |
804 } | 739 } |
805 | 740 |
806 void PipelineImpl::RendererWrapper::InitializeDemuxer( | 741 void PipelineImpl::RendererWrapper::InitializeDemuxer( |
807 const PipelineStatusCB& done_cb) { | 742 const PipelineStatusCB& done_cb) { |
808 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 743 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
809 | 744 |
810 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 745 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
811 } | 746 } |
812 | 747 |
813 void PipelineImpl::RendererWrapper::InitializeRenderer( | 748 void PipelineImpl::RendererWrapper::InitializeRenderer( |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1098 } | 1033 } |
1099 | 1034 |
1100 #define RETURN_STRING(state) \ | 1035 #define RETURN_STRING(state) \ |
1101 case state: \ | 1036 case state: \ |
1102 return #state; | 1037 return #state; |
1103 | 1038 |
1104 // static | 1039 // static |
1105 const char* PipelineImpl::GetStateString(State state) { | 1040 const char* PipelineImpl::GetStateString(State state) { |
1106 switch (state) { | 1041 switch (state) { |
1107 RETURN_STRING(kCreated); | 1042 RETURN_STRING(kCreated); |
1108 RETURN_STRING(kInitDemuxer); | 1043 RETURN_STRING(kStarting); |
1109 RETURN_STRING(kInitRenderer); | |
1110 RETURN_STRING(kSeeking); | 1044 RETURN_STRING(kSeeking); |
1111 RETURN_STRING(kPlaying); | 1045 RETURN_STRING(kPlaying); |
1112 RETURN_STRING(kStopping); | 1046 RETURN_STRING(kStopping); |
1113 RETURN_STRING(kStopped); | 1047 RETURN_STRING(kStopped); |
1114 RETURN_STRING(kSuspending); | 1048 RETURN_STRING(kSuspending); |
1115 RETURN_STRING(kSuspended); | 1049 RETURN_STRING(kSuspended); |
1116 RETURN_STRING(kResuming); | 1050 RETURN_STRING(kResuming); |
1117 } | 1051 } |
1118 NOTREACHED(); | 1052 NOTREACHED(); |
1119 return "INVALID"; | 1053 return "INVALID"; |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1211 | 1145 |
1212 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | 1146 void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
1213 DVLOG(2) << __FUNCTION__; | 1147 DVLOG(2) << __FUNCTION__; |
1214 DCHECK(thread_checker_.CalledOnValidThread()); | 1148 DCHECK(thread_checker_.CalledOnValidThread()); |
1215 DCHECK(IsRunning()); | 1149 DCHECK(IsRunning()); |
1216 | 1150 |
1217 DCHECK(client_); | 1151 DCHECK(client_); |
1218 client_->OnVideoOpacityChange(opaque); | 1152 client_->OnVideoOpacityChange(opaque); |
1219 } | 1153 } |
1220 | 1154 |
1221 void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { | 1155 void PipelineImpl::OnSeekDone() { |
1222 DVLOG(3) << __FUNCTION__ << "(" << start_time.InMicroseconds() << ")"; | 1156 DVLOG(3) << __FUNCTION__; |
1223 DCHECK(thread_checker_.CalledOnValidThread()); | 1157 DCHECK(thread_checker_.CalledOnValidThread()); |
1224 DCHECK(IsRunning()); | 1158 DCHECK(IsRunning()); |
1225 | 1159 |
1226 DCHECK(!seek_cb_.is_null()); | 1160 DCHECK(!seek_cb_.is_null()); |
1227 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 1161 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
1228 } | 1162 } |
1229 | 1163 |
1230 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { | 1164 void PipelineImpl::OnSuspendDone() { |
1231 DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; | 1165 DVLOG(3) << __FUNCTION__; |
1232 DCHECK(thread_checker_.CalledOnValidThread()); | 1166 DCHECK(thread_checker_.CalledOnValidThread()); |
1233 DCHECK(IsRunning()); | 1167 DCHECK(IsRunning()); |
1234 | 1168 |
1235 DCHECK(!suspend_cb_.is_null()); | 1169 DCHECK(!suspend_cb_.is_null()); |
1236 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | 1170 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
1237 } | 1171 } |
1238 | 1172 |
1239 } // namespace media | 1173 } // namespace media |
OLD | NEW |