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

Side by Side Diff: media/base/pipeline_impl.cc

Issue 2091893003: Make PipelineImpl state change tasks consistent. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: restores avtrack currTime logic Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/base/pipeline_impl.h ('k') | media/base/pipeline_impl_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « media/base/pipeline_impl.h ('k') | media/base/pipeline_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698