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 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
121 void RemoveTextStreamTask(DemuxerStream* text_stream); | 121 void RemoveTextStreamTask(DemuxerStream* text_stream); |
122 | 122 |
123 // Common handlers for notifications from renderers and demuxer. | 123 // Common handlers for notifications from renderers and demuxer. |
124 void OnPipelineError(PipelineStatus error); | 124 void OnPipelineError(PipelineStatus error); |
125 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 125 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
126 CdmContext* cdm_context, | 126 CdmContext* cdm_context, |
127 bool success); | 127 bool success); |
128 void CheckPlaybackEnded(); | 128 void CheckPlaybackEnded(); |
129 | 129 |
130 // State transition tasks. | 130 // State transition tasks. |
131 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); | |
132 void DoStop(const base::Closure& done_cb); | |
133 void SetState(State next_state); | 131 void SetState(State next_state); |
134 State GetNextState() const; | 132 void CompleteSeek(base::TimeDelta seek_time, PipelineStatus status); |
135 void StateTransitionTask(PipelineStatus status); | 133 void CompleteSuspend(PipelineStatus status); |
136 void InitializeDemuxer(const PipelineStatusCB& done_cb); | 134 void InitializeDemuxer(const PipelineStatusCB& done_cb); |
137 void InitializeRenderer(const PipelineStatusCB& done_cb); | 135 void InitializeRenderer(const PipelineStatusCB& done_cb); |
138 void DestroyRenderer(); | 136 void DestroyRenderer(); |
139 void ReportMetadata(); | 137 void ReportMetadata(); |
140 | 138 |
141 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; | 139 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
142 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 140 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
143 const scoped_refptr<MediaLog> media_log_; | 141 const scoped_refptr<MediaLog> media_log_; |
144 | 142 |
145 base::WeakPtr<PipelineImpl> weak_pipeline_; | 143 base::WeakPtr<PipelineImpl> weak_pipeline_; |
(...skipping 11 matching lines...) Expand all Loading... | |
157 | 155 |
158 // Current state of the pipeline. | 156 // Current state of the pipeline. |
159 State state_; | 157 State state_; |
160 | 158 |
161 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that | 159 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that |
162 // the pipeline is operating correctly. Any other value indicates that the | 160 // the pipeline is operating correctly. Any other value indicates that the |
163 // pipeline is stopped or is stopping. Clients can call the Stop() method to | 161 // pipeline is stopped or is stopping. Clients can call the Stop() method to |
164 // reset the pipeline state, and restore this to PIPELINE_OK. | 162 // reset the pipeline state, and restore this to PIPELINE_OK. |
165 PipelineStatus status_; | 163 PipelineStatus status_; |
166 | 164 |
167 // The timestamp to start playback from after starting/seeking/resuming has | |
168 // completed. | |
169 base::TimeDelta start_timestamp_; | |
170 | |
171 // Whether we've received the audio/video/text ended events. | 165 // Whether we've received the audio/video/text ended events. |
172 bool renderer_ended_; | 166 bool renderer_ended_; |
173 bool text_renderer_ended_; | 167 bool text_renderer_ended_; |
174 | 168 |
175 // Series of tasks to Start(), Seek(), and Resume(). | 169 // Series of tasks to Start(), Seek(), and Resume(). |
176 std::unique_ptr<SerialRunner> pending_callbacks_; | 170 std::unique_ptr<SerialRunner> pending_callbacks_; |
177 | 171 |
178 base::WeakPtr<RendererWrapper> weak_this_; | 172 base::WeakPtr<RendererWrapper> weak_this_; |
179 base::WeakPtrFactory<RendererWrapper> weak_factory_; | 173 base::WeakPtrFactory<RendererWrapper> weak_factory_; |
180 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); | 174 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); |
(...skipping 16 matching lines...) Expand all Loading... | |
197 weak_factory_(this) { | 191 weak_factory_(this) { |
198 weak_this_ = weak_factory_.GetWeakPtr(); | 192 weak_this_ = weak_factory_.GetWeakPtr(); |
199 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 193 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
200 } | 194 } |
201 | 195 |
202 PipelineImpl::RendererWrapper::~RendererWrapper() { | 196 PipelineImpl::RendererWrapper::~RendererWrapper() { |
203 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 197 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
204 DCHECK(state_ == kCreated || state_ == kStopped); | 198 DCHECK(state_ == kCreated || state_ == kStopped); |
205 } | 199 } |
206 | 200 |
201 // Note that the usage of base::Unretained() with the renderers is considered | |
202 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
203 // | |
204 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
205 // running on the media thread would result in crashes. | |
206 | |
207 void PipelineImpl::RendererWrapper::Start( | 207 void PipelineImpl::RendererWrapper::Start( |
208 Demuxer* demuxer, | 208 Demuxer* demuxer, |
209 std::unique_ptr<Renderer> renderer, | 209 std::unique_ptr<Renderer> renderer, |
210 std::unique_ptr<TextRenderer> text_renderer, | 210 std::unique_ptr<TextRenderer> text_renderer, |
211 base::WeakPtr<PipelineImpl> weak_pipeline) { | 211 base::WeakPtr<PipelineImpl> weak_pipeline) { |
212 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 212 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
213 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " | 213 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
214 << state_; | 214 << state_; |
215 | 215 |
216 SetState(kStarting); | |
217 | |
216 DCHECK(!demuxer_); | 218 DCHECK(!demuxer_); |
217 DCHECK(!shared_state_.renderer); | 219 DCHECK(!shared_state_.renderer); |
218 DCHECK(!text_renderer_); | 220 DCHECK(!text_renderer_); |
219 DCHECK(!renderer_ended_); | 221 DCHECK(!renderer_ended_); |
220 DCHECK(!text_renderer_ended_); | 222 DCHECK(!text_renderer_ended_); |
221 DCHECK(!weak_pipeline_); | 223 DCHECK(!weak_pipeline_); |
222 demuxer_ = demuxer; | 224 demuxer_ = demuxer; |
223 { | 225 { |
224 base::AutoLock auto_lock(shared_state_lock_); | 226 base::AutoLock auto_lock(shared_state_lock_); |
225 shared_state_.renderer = std::move(renderer); | 227 shared_state_.renderer = std::move(renderer); |
226 } | 228 } |
227 text_renderer_ = std::move(text_renderer); | 229 text_renderer_ = std::move(text_renderer); |
228 if (text_renderer_) { | 230 if (text_renderer_) { |
229 text_renderer_->Initialize( | 231 text_renderer_->Initialize( |
230 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); | 232 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); |
231 } | 233 } |
232 weak_pipeline_ = weak_pipeline; | 234 weak_pipeline_ = weak_pipeline; |
233 | 235 |
234 StateTransitionTask(PIPELINE_OK); | 236 // Queue asynchronous actions required to start. |
237 DCHECK(!pending_callbacks_); | |
238 SerialRunner::Queue fns; | |
239 | |
240 // Initialize demuxer. | |
241 fns.Push(base::Bind(&RendererWrapper::InitializeDemuxer, weak_this_)); | |
242 | |
243 // Once the demuxer is initialized successfully, media metadata must be | |
244 // available - report the metadata to client. | |
245 fns.Push(base::Bind(&RendererWrapper::ReportMetadata, weak_this_)); | |
246 | |
247 // Initialize renderer. | |
248 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | |
249 | |
250 // Run tasks. | |
251 pending_callbacks_ = | |
252 SerialRunner::Run(fns, base::Bind(&RendererWrapper::CompleteSeek, | |
253 weak_this_, base::TimeDelta())); | |
235 } | 254 } |
236 | 255 |
237 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { | 256 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
238 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 257 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
239 DCHECK(state_ != kStopping && state_ != kStopped); | 258 DCHECK(state_ != kStopping && state_ != kStopped); |
240 | 259 |
241 SetState(kStopping); | 260 SetState(kStopping); |
242 | 261 |
243 if (shared_state_.statistics.video_frames_decoded > 0) { | 262 if (shared_state_.statistics.video_frames_decoded > 0) { |
244 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | 263 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
245 shared_state_.statistics.video_frames_dropped); | 264 shared_state_.statistics.video_frames_dropped); |
246 } | 265 } |
247 | 266 |
248 // If we stop during starting/seeking/suspending/resuming we don't want to | 267 // If we stop during starting/seeking/suspending/resuming we don't want to |
249 // leave outstanding callbacks around. The callbacks also do not get run if | 268 // leave outstanding callbacks around. The callbacks also do not get run if |
250 // the pipeline is stopped before it had a chance to complete outstanding | 269 // the pipeline is stopped before it had a chance to complete outstanding |
251 // tasks. | 270 // tasks. |
252 pending_callbacks_.reset(); | 271 pending_callbacks_.reset(); |
253 | 272 |
254 DoStop(stop_cb); | 273 DestroyRenderer(); |
274 text_renderer_.reset(); | |
275 | |
276 if (demuxer_) { | |
277 demuxer_->Stop(); | |
278 demuxer_ = NULL; | |
279 } | |
280 | |
281 SetState(kStopped); | |
282 | |
283 // Post the stop callback to enqueue it after the tasks that may have been | |
284 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
285 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
286 // enqueued after |stop_cb|. This may be problematic because Demuxer may | |
287 // get destroyed as soon as |stop_cb| is run. In practice this is not a | |
288 // problem, but ideally Demuxer should be destroyed on the media thread. | |
289 media_task_runner_->PostTask(FROM_HERE, stop_cb); | |
255 } | 290 } |
256 | 291 |
257 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { | 292 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
258 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 293 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
259 | 294 |
260 // Suppress seeking if we're not fully started. | 295 // Suppress seeking if we're not fully started. |
261 if (state_ != kPlaying) { | 296 if (state_ != kPlaying) { |
262 DCHECK(state_ == kStopping || state_ == kStopped) | 297 DCHECK(state_ == kStopping || state_ == kStopped) |
263 << "Receive seek in unexpected state: " << state_; | 298 << "Receive seek in unexpected state: " << state_; |
264 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 299 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
265 return; | 300 return; |
266 } | 301 } |
267 | 302 |
268 const base::TimeDelta seek_timestamp = | 303 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); |
269 std::max(time, demuxer_->GetStartTime()); | |
270 | 304 |
271 SetState(kSeeking); | 305 SetState(kSeeking); |
272 renderer_ended_ = false; | 306 renderer_ended_ = false; |
273 text_renderer_ended_ = false; | 307 text_renderer_ended_ = false; |
274 start_timestamp_ = seek_timestamp; | |
275 | 308 |
276 DoSeek(seek_timestamp, | 309 // Queue asynchronous actions required to start. |
277 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 310 DCHECK(!pending_callbacks_); |
311 SerialRunner::Queue bound_fns; | |
312 | |
313 // Pause. | |
314 if (text_renderer_) { | |
315 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
316 base::Unretained(text_renderer_.get()))); | |
317 } | |
318 | |
319 // Flush. | |
320 DCHECK(shared_state_.renderer); | |
321 bound_fns.Push(base::Bind(&Renderer::Flush, | |
322 base::Unretained(shared_state_.renderer.get()))); | |
323 | |
324 if (text_renderer_) { | |
325 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
326 base::Unretained(text_renderer_.get()))); | |
327 } | |
328 | |
329 // Seek demuxer. | |
330 bound_fns.Push( | |
331 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
332 | |
333 // Run tasks. | |
334 pending_callbacks_ = SerialRunner::Run( | |
335 bound_fns, | |
336 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_timestamp)); | |
278 } | 337 } |
279 | 338 |
280 void PipelineImpl::RendererWrapper::Suspend() { | 339 void PipelineImpl::RendererWrapper::Suspend() { |
281 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 340 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
282 | 341 |
283 // Suppress suspending if we're not playing. | 342 // Suppress suspending if we're not playing. |
284 if (state_ != kPlaying) { | 343 if (state_ != kPlaying) { |
285 DCHECK(state_ == kStopping || state_ == kStopped) | 344 DCHECK(state_ == kStopping || state_ == kStopped) |
286 << "Receive suspend in unexpected state: " << state_; | 345 << "Receive suspend in unexpected state: " << state_; |
287 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 346 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
288 return; | 347 return; |
289 } | 348 } |
290 DCHECK(shared_state_.renderer); | 349 DCHECK(shared_state_.renderer); |
291 DCHECK(!pending_callbacks_.get()); | 350 DCHECK(!pending_callbacks_.get()); |
292 | 351 |
293 SetState(kSuspending); | 352 SetState(kSuspending); |
294 | 353 |
295 // Freeze playback and record the media time before flushing. (Flushing clears | 354 // Freeze playback and record the media time before flushing. (Flushing clears |
296 // the value.) | 355 // the value.) |
297 shared_state_.renderer->SetPlaybackRate(0.0); | 356 shared_state_.renderer->SetPlaybackRate(0.0); |
298 { | 357 { |
299 base::AutoLock auto_lock(shared_state_lock_); | 358 base::AutoLock auto_lock(shared_state_lock_); |
300 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); | 359 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); |
301 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp); | 360 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp); |
302 } | 361 } |
303 | 362 |
304 // Queue the asynchronous actions required to stop playback. (Matches setup in | 363 // Queue the asynchronous actions required to stop playback. |
305 // DoSeek().) | |
306 // TODO(sandersd): Share implementation with DoSeek(). | |
307 SerialRunner::Queue fns; | 364 SerialRunner::Queue fns; |
308 | 365 |
309 if (text_renderer_) { | 366 if (text_renderer_) { |
310 fns.Push(base::Bind(&TextRenderer::Pause, | 367 fns.Push(base::Bind(&TextRenderer::Pause, |
311 base::Unretained(text_renderer_.get()))); | 368 base::Unretained(text_renderer_.get()))); |
312 } | 369 } |
313 | 370 |
314 fns.Push(base::Bind(&Renderer::Flush, | 371 fns.Push(base::Bind(&Renderer::Flush, |
315 base::Unretained(shared_state_.renderer.get()))); | 372 base::Unretained(shared_state_.renderer.get()))); |
316 | 373 |
317 if (text_renderer_) { | 374 if (text_renderer_) { |
318 fns.Push(base::Bind(&TextRenderer::Flush, | 375 fns.Push(base::Bind(&TextRenderer::Flush, |
319 base::Unretained(text_renderer_.get()))); | 376 base::Unretained(text_renderer_.get()))); |
320 } | 377 } |
321 | 378 |
322 pending_callbacks_ = SerialRunner::Run( | 379 pending_callbacks_ = SerialRunner::Run( |
323 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 380 fns, base::Bind(&RendererWrapper::CompleteSuspend, weak_this_)); |
324 } | 381 } |
325 | 382 |
326 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, | 383 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
327 base::TimeDelta timestamp) { | 384 base::TimeDelta timestamp) { |
328 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 385 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
329 | 386 |
330 // Suppress resuming if we're not suspended. | 387 // Suppress resuming if we're not suspended. |
331 if (state_ != kSuspended) { | 388 if (state_ != kSuspended) { |
332 DCHECK(state_ == kStopping || state_ == kStopped) | 389 DCHECK(state_ == kStopping || state_ == kStopped) |
333 << "Receive resume in unexpected state: " << state_; | 390 << "Receive resume in unexpected state: " << state_; |
334 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 391 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
335 return; | 392 return; |
336 } | 393 } |
337 DCHECK(!shared_state_.renderer); | 394 DCHECK(!shared_state_.renderer); |
338 DCHECK(!pending_callbacks_.get()); | 395 DCHECK(!pending_callbacks_.get()); |
339 | 396 |
340 SetState(kResuming); | 397 SetState(kResuming); |
341 | 398 |
342 { | 399 { |
343 base::AutoLock auto_lock(shared_state_lock_); | 400 base::AutoLock auto_lock(shared_state_lock_); |
344 shared_state_.renderer = std::move(renderer); | 401 shared_state_.renderer = std::move(renderer); |
345 } | 402 } |
346 | 403 |
347 // Set up for a seek. (Matches setup in SeekTask().) | |
348 // TODO(sandersd): Share implementation with SeekTask(). | |
349 renderer_ended_ = false; | 404 renderer_ended_ = false; |
350 text_renderer_ended_ = false; | 405 text_renderer_ended_ = false; |
351 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); | 406 base::TimeDelta start_timestamp = |
407 std::max(timestamp, demuxer_->GetStartTime()); | |
352 | 408 |
353 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), | 409 // Queue the asynchronous actions required to start playback. |
354 // we need to initialize the renderer ourselves (we don't want to enter state | |
355 // kInitDemuxer, and even if we did the current code would seek to the start | |
356 // instead of |timestamp|). | |
357 SerialRunner::Queue fns; | 410 SerialRunner::Queue fns; |
358 | 411 |
359 fns.Push( | 412 fns.Push( |
360 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); | 413 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp)); |
361 | 414 |
362 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | 415 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
363 | 416 |
364 pending_callbacks_ = SerialRunner::Run( | 417 pending_callbacks_ = SerialRunner::Run( |
365 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 418 fns, |
419 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, start_timestamp)); | |
366 } | 420 } |
367 | 421 |
368 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { | 422 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
369 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 423 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
370 | 424 |
371 playback_rate_ = playback_rate; | 425 playback_rate_ = playback_rate; |
372 if (state_ == kPlaying) | 426 if (state_ == kPlaying) |
373 shared_state_.renderer->SetPlaybackRate(playback_rate_); | 427 shared_state_.renderer->SetPlaybackRate(playback_rate_); |
374 } | 428 } |
375 | 429 |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
525 // a notification when pipeline is stopped/shut down, it's safe to ignore it. | 579 // a notification when pipeline is stopped/shut down, it's safe to ignore it. |
526 if (state_ == kStopping || state_ == kStopped) { | 580 if (state_ == kStopping || state_ == kStopped) { |
527 return; | 581 return; |
528 } | 582 } |
529 | 583 |
530 DCHECK(demuxer_); | 584 DCHECK(demuxer_); |
531 DCHECK(shared_state_.renderer); | 585 DCHECK(shared_state_.renderer); |
532 | 586 |
533 base::TimeDelta currTime = (state_ == kPlaying) | 587 base::TimeDelta currTime = (state_ == kPlaying) |
534 ? shared_state_.renderer->GetMediaTime() | 588 ? shared_state_.renderer->GetMediaTime() |
535 : start_timestamp_; | 589 : demuxer_->GetStartTime(); |
alokp
2016/07/29 18:30:52
I cannot call RendererWrapper::GetMediaTime here b
servolk
2016/07/29 18:37:36
Yes, I believe this should be fine.
Indeed OnEnabl
| |
536 demuxer_->OnEnabledAudioTracksChanged(enabledTrackIds, currTime); | 590 demuxer_->OnEnabledAudioTracksChanged(enabledTrackIds, currTime); |
537 } | 591 } |
538 | 592 |
539 void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged( | 593 void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged( |
540 const std::vector<MediaTrack::Id>& selectedTrackId) { | 594 const std::vector<MediaTrack::Id>& selectedTrackId) { |
541 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 595 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
542 | 596 |
543 // Track status notifications might be delivered asynchronously. If we receive | 597 // Track status notifications might be delivered asynchronously. If we receive |
544 // a notification when pipeline is stopped/shut down, it's safe to ignore it. | 598 // a notification when pipeline is stopped/shut down, it's safe to ignore it. |
545 if (state_ == kStopping || state_ == kStopped) { | 599 if (state_ == kStopping || state_ == kStopped) { |
546 return; | 600 return; |
547 } | 601 } |
548 | 602 |
549 DCHECK(demuxer_); | 603 DCHECK(demuxer_); |
550 DCHECK(shared_state_.renderer); | 604 DCHECK(shared_state_.renderer); |
551 | 605 |
552 base::TimeDelta currTime = (state_ == kPlaying) | 606 base::TimeDelta currTime = (state_ == kPlaying) |
553 ? shared_state_.renderer->GetMediaTime() | 607 ? shared_state_.renderer->GetMediaTime() |
554 : start_timestamp_; | 608 : demuxer_->GetStartTime(); |
555 demuxer_->OnSelectedVideoTrackChanged(selectedTrackId, currTime); | 609 demuxer_->OnSelectedVideoTrackChanged(selectedTrackId, currTime); |
556 } | 610 } |
557 | 611 |
558 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( | 612 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( |
559 const PipelineStatistics& stats) { | 613 const PipelineStatistics& stats) { |
560 DVLOG(3) << __func__; | 614 DVLOG(3) << __func__; |
561 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 615 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
562 | 616 |
563 base::AutoLock auto_lock(shared_state_lock_); | 617 base::AutoLock auto_lock(shared_state_lock_); |
564 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded; | 618 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded; |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
677 return; | 731 return; |
678 | 732 |
679 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 733 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
680 return; | 734 return; |
681 | 735 |
682 DCHECK_EQ(status_, PIPELINE_OK); | 736 DCHECK_EQ(status_, PIPELINE_OK); |
683 main_task_runner_->PostTask( | 737 main_task_runner_->PostTask( |
684 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); | 738 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
685 } | 739 } |
686 | 740 |
687 // Note that the usage of base::Unretained() with the renderers is considered | |
688 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
689 // | |
690 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
691 // running on the media thread would result in crashes. | |
692 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, | |
693 const PipelineStatusCB& done_cb) { | |
694 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
695 DCHECK(!pending_callbacks_.get()); | |
696 DCHECK_EQ(state_, kSeeking); | |
697 SerialRunner::Queue bound_fns; | |
698 | |
699 // Pause. | |
700 if (text_renderer_) { | |
701 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
702 base::Unretained(text_renderer_.get()))); | |
703 } | |
704 | |
705 // Flush. | |
706 DCHECK(shared_state_.renderer); | |
707 bound_fns.Push(base::Bind(&Renderer::Flush, | |
708 base::Unretained(shared_state_.renderer.get()))); | |
709 | |
710 if (text_renderer_) { | |
711 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
712 base::Unretained(text_renderer_.get()))); | |
713 } | |
714 | |
715 // Seek demuxer. | |
716 bound_fns.Push( | |
717 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
718 | |
719 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | |
720 } | |
721 | |
722 void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { | |
723 DVLOG(2) << __func__; | |
724 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
725 DCHECK_EQ(state_, kStopping); | |
726 DCHECK(!pending_callbacks_.get()); | |
727 | |
728 DestroyRenderer(); | |
729 text_renderer_.reset(); | |
730 | |
731 if (demuxer_) { | |
732 demuxer_->Stop(); | |
733 demuxer_ = NULL; | |
734 } | |
735 | |
736 SetState(kStopped); | |
737 | |
738 // Post the stop callback to enqueue it after the tasks that may have been | |
739 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
740 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
741 // enqueued after |done_cb|. This may be problematic because Demuxer may | |
742 // get destroyed as soon as |done_cb| is run. In practice this is not a | |
743 // problem, but ideally Demuxer should be destroyed on the media thread. | |
744 media_task_runner_->PostTask(FROM_HERE, done_cb); | |
745 } | |
746 | |
747 void PipelineImpl::RendererWrapper::SetState(State next_state) { | 741 void PipelineImpl::RendererWrapper::SetState(State next_state) { |
748 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 742 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
749 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " | 743 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
750 << PipelineImpl::GetStateString(next_state); | 744 << PipelineImpl::GetStateString(next_state); |
751 | 745 |
752 state_ = next_state; | 746 state_ = next_state; |
753 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 747 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
754 } | 748 } |
755 | 749 |
756 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { | 750 void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time, |
751 PipelineStatus status) { | |
757 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 752 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
758 DCHECK_EQ(status_, PIPELINE_OK) | 753 DCHECK(state_ == kStarting || state_ == kSeeking || state_ == kResuming); |
759 << "State transitions don't happen when there's an error: " << status_; | |
760 | 754 |
761 switch (state_) { | 755 DCHECK(pending_callbacks_); |
762 case kCreated: | 756 pending_callbacks_.reset(); |
763 return kInitDemuxer; | |
764 | 757 |
765 case kInitDemuxer: | |
766 return kInitRenderer; | |
767 | |
768 case kInitRenderer: | |
769 case kSeeking: | |
770 return kPlaying; | |
771 | |
772 case kSuspending: | |
773 return kSuspended; | |
774 | |
775 case kSuspended: | |
776 return kResuming; | |
777 | |
778 case kResuming: | |
779 return kPlaying; | |
780 | |
781 case kPlaying: | |
782 case kStopping: | |
783 case kStopped: | |
784 break; | |
785 } | |
786 NOTREACHED() << "State has no transition: " << state_; | |
787 return state_; | |
788 } | |
789 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { | |
790 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
791 | |
792 // No-op any state transitions if we're stopping or already encountered error. | |
793 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK) | |
794 return; | |
795 | |
796 // Report error from the previous operation. | |
797 if (status != PIPELINE_OK) { | 758 if (status != PIPELINE_OK) { |
798 OnPipelineError(status); | 759 OnPipelineError(status); |
799 return; | 760 return; |
800 } | 761 } |
801 | 762 |
802 // Guard against accidentally clearing |pending_callbacks_| for states that | 763 shared_state_.renderer->StartPlayingFrom( |
803 // use it as well as states that should not be using it. | 764 std::max(seek_time, demuxer_->GetStartTime())); |
804 DCHECK_EQ(pending_callbacks_.get() != NULL, | 765 { |
805 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 766 base::AutoLock auto_lock(shared_state_lock_); |
767 shared_state_.suspend_timestamp = kNoTimestamp; | |
768 } | |
806 | 769 |
770 if (text_renderer_) | |
771 text_renderer_->StartPlaying(); | |
772 | |
773 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
774 shared_state_.renderer->SetVolume(volume_); | |
775 | |
776 SetState(kPlaying); | |
777 main_task_runner_->PostTask( | |
778 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_)); | |
779 } | |
780 | |
781 void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status) { | |
782 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
783 DCHECK_EQ(kSuspending, state_); | |
784 | |
785 DCHECK(pending_callbacks_); | |
807 pending_callbacks_.reset(); | 786 pending_callbacks_.reset(); |
808 | 787 |
809 PipelineStatusCB done_cb = | 788 // In case we are suspending or suspended, the error may be recoverable, |
810 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_); | 789 // so don't propagate it now, instead let the subsequent seek during resume |
790 // propagate it if it's unrecoverable. | |
791 LOG_IF(WARNING, status != PIPELINE_OK) | |
792 << "Encountered pipeline error while suspending: " << status; | |
811 | 793 |
812 // Switch states, performing any entrance actions for the new state as well. | 794 DestroyRenderer(); |
813 SetState(GetNextState()); | 795 { |
814 switch (state_) { | 796 base::AutoLock auto_lock(shared_state_lock_); |
815 case kInitDemuxer: | 797 shared_state_.statistics.audio_memory_usage = 0; |
816 return InitializeDemuxer(done_cb); | 798 shared_state_.statistics.video_memory_usage = 0; |
799 } | |
817 | 800 |
818 case kInitRenderer: | 801 SetState(kSuspended); |
819 // When the state_ transfers to kInitRenderer, it means the demuxer has | 802 main_task_runner_->PostTask( |
820 // finished parsing the init info. It should call ReportMetadata in case | 803 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_)); |
821 // meeting 'decode' error when passing media segment but WebMediaPlayer's | |
822 // ready_state_ is still ReadyStateHaveNothing. In that case, it will | |
823 // treat it as NetworkStateFormatError not NetworkStateDecodeError. | |
824 ReportMetadata(); | |
825 start_timestamp_ = demuxer_->GetStartTime(); | |
826 | |
827 return InitializeRenderer(done_cb); | |
828 | |
829 case kPlaying: | |
830 DCHECK(start_timestamp_ >= base::TimeDelta()); | |
831 shared_state_.renderer->StartPlayingFrom(start_timestamp_); | |
832 { | |
833 base::AutoLock auto_lock(shared_state_lock_); | |
834 shared_state_.suspend_timestamp = kNoTimestamp; | |
835 } | |
836 | |
837 if (text_renderer_) | |
838 text_renderer_->StartPlaying(); | |
839 | |
840 main_task_runner_->PostTask( | |
841 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, | |
842 start_timestamp_)); | |
843 | |
844 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
845 shared_state_.renderer->SetVolume(volume_); | |
846 return; | |
847 | |
848 case kSuspended: | |
849 DestroyRenderer(); | |
850 { | |
851 base::AutoLock auto_lock(shared_state_lock_); | |
852 shared_state_.statistics.audio_memory_usage = 0; | |
853 shared_state_.statistics.video_memory_usage = 0; | |
854 } | |
855 main_task_runner_->PostTask( | |
856 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, | |
857 shared_state_.suspend_timestamp)); | |
858 return; | |
859 | |
860 case kStopping: | |
861 case kStopped: | |
862 case kCreated: | |
863 case kSeeking: | |
864 case kSuspending: | |
865 case kResuming: | |
866 NOTREACHED() << "State has no transition: " << state_; | |
867 return; | |
868 } | |
869 } | 804 } |
870 | 805 |
871 void PipelineImpl::RendererWrapper::InitializeDemuxer( | 806 void PipelineImpl::RendererWrapper::InitializeDemuxer( |
872 const PipelineStatusCB& done_cb) { | 807 const PipelineStatusCB& done_cb) { |
873 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 808 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
874 | 809 |
875 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 810 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
876 } | 811 } |
877 | 812 |
878 void PipelineImpl::RendererWrapper::InitializeRenderer( | 813 void PipelineImpl::RendererWrapper::InitializeRenderer( |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1163 } | 1098 } |
1164 | 1099 |
1165 #define RETURN_STRING(state) \ | 1100 #define RETURN_STRING(state) \ |
1166 case state: \ | 1101 case state: \ |
1167 return #state; | 1102 return #state; |
1168 | 1103 |
1169 // static | 1104 // static |
1170 const char* PipelineImpl::GetStateString(State state) { | 1105 const char* PipelineImpl::GetStateString(State state) { |
1171 switch (state) { | 1106 switch (state) { |
1172 RETURN_STRING(kCreated); | 1107 RETURN_STRING(kCreated); |
1173 RETURN_STRING(kInitDemuxer); | 1108 RETURN_STRING(kStarting); |
1174 RETURN_STRING(kInitRenderer); | |
1175 RETURN_STRING(kSeeking); | 1109 RETURN_STRING(kSeeking); |
1176 RETURN_STRING(kPlaying); | 1110 RETURN_STRING(kPlaying); |
1177 RETURN_STRING(kStopping); | 1111 RETURN_STRING(kStopping); |
1178 RETURN_STRING(kStopped); | 1112 RETURN_STRING(kStopped); |
1179 RETURN_STRING(kSuspending); | 1113 RETURN_STRING(kSuspending); |
1180 RETURN_STRING(kSuspended); | 1114 RETURN_STRING(kSuspended); |
1181 RETURN_STRING(kResuming); | 1115 RETURN_STRING(kResuming); |
1182 } | 1116 } |
1183 NOTREACHED(); | 1117 NOTREACHED(); |
1184 return "INVALID"; | 1118 return "INVALID"; |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1276 | 1210 |
1277 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | 1211 void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
1278 DVLOG(2) << __func__; | 1212 DVLOG(2) << __func__; |
1279 DCHECK(thread_checker_.CalledOnValidThread()); | 1213 DCHECK(thread_checker_.CalledOnValidThread()); |
1280 DCHECK(IsRunning()); | 1214 DCHECK(IsRunning()); |
1281 | 1215 |
1282 DCHECK(client_); | 1216 DCHECK(client_); |
1283 client_->OnVideoOpacityChange(opaque); | 1217 client_->OnVideoOpacityChange(opaque); |
1284 } | 1218 } |
1285 | 1219 |
1286 void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { | 1220 void PipelineImpl::OnSeekDone() { |
1287 DVLOG(3) << __func__ << "(" << start_time.InMicroseconds() << ")"; | 1221 DVLOG(3) << __func__; |
1288 DCHECK(thread_checker_.CalledOnValidThread()); | 1222 DCHECK(thread_checker_.CalledOnValidThread()); |
1289 DCHECK(IsRunning()); | 1223 DCHECK(IsRunning()); |
1290 | 1224 |
1291 DCHECK(!seek_cb_.is_null()); | 1225 DCHECK(!seek_cb_.is_null()); |
1292 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 1226 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
1293 } | 1227 } |
1294 | 1228 |
1295 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { | 1229 void PipelineImpl::OnSuspendDone() { |
1296 DVLOG(3) << __func__ << "(" << suspend_time.InMicroseconds() << ")"; | 1230 DVLOG(3) << __func__; |
1297 DCHECK(thread_checker_.CalledOnValidThread()); | 1231 DCHECK(thread_checker_.CalledOnValidThread()); |
1298 DCHECK(IsRunning()); | 1232 DCHECK(IsRunning()); |
1299 | 1233 |
1300 DCHECK(!suspend_cb_.is_null()); | 1234 DCHECK(!suspend_cb_.is_null()); |
1301 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | 1235 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
1302 } | 1236 } |
1303 | 1237 |
1304 } // namespace media | 1238 } // namespace media |
OLD | NEW |