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

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

Issue 10837206: Rewrite media::Pipeline state transition machinery and simplify shutdown. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src
Patch Set: nits Created 8 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « media/base/pipeline.h ('k') | media/base/pipeline_status.h » ('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.h" 5 #include "media/base/pipeline.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/callback_helpers.h" 11 #include "base/callback_helpers.h"
12 #include "base/compiler_specific.h" 12 #include "base/compiler_specific.h"
13 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/message_loop.h" 14 #include "base/message_loop.h"
15 #include "base/stl_util.h" 15 #include "base/stl_util.h"
16 #include "base/string_number_conversions.h" 16 #include "base/string_number_conversions.h"
17 #include "base/string_util.h" 17 #include "base/string_util.h"
18 #include "base/synchronization/condition_variable.h" 18 #include "base/synchronization/condition_variable.h"
19 #include "media/base/audio_decoder.h" 19 #include "media/base/audio_decoder.h"
20 #include "media/base/audio_renderer.h" 20 #include "media/base/audio_renderer.h"
21 #include "media/base/buffers.h"
22 #include "media/base/clock.h" 21 #include "media/base/clock.h"
23 #include "media/base/filter_collection.h" 22 #include "media/base/filter_collection.h"
24 #include "media/base/media_log.h" 23 #include "media/base/media_log.h"
25 #include "media/base/video_decoder.h" 24 #include "media/base/video_decoder.h"
26 #include "media/base/video_decoder_config.h" 25 #include "media/base/video_decoder_config.h"
27 #include "media/base/video_renderer.h" 26 #include "media/base/video_renderer.h"
28 27
29 using base::TimeDelta; 28 using base::TimeDelta;
30 29
31 namespace media { 30 namespace media {
(...skipping 24 matching lines...) Expand all
56 while (!notified_) 55 while (!notified_)
57 cv_.Wait(); 56 cv_.Wait();
58 } 57 }
59 58
60 media::PipelineStatus PipelineStatusNotification::status() { 59 media::PipelineStatus PipelineStatusNotification::status() {
61 base::AutoLock auto_lock(lock_); 60 base::AutoLock auto_lock(lock_);
62 DCHECK(notified_); 61 DCHECK(notified_);
63 return status_; 62 return status_;
64 } 63 }
65 64
66 struct Pipeline::PipelineInitState {
67 scoped_refptr<AudioDecoder> audio_decoder;
68 };
69
70 Pipeline::Pipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, 65 Pipeline::Pipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop,
71 MediaLog* media_log) 66 MediaLog* media_log)
72 : message_loop_(message_loop), 67 : message_loop_(message_loop),
73 media_log_(media_log), 68 media_log_(media_log),
74 running_(false), 69 running_(false),
75 seek_pending_(false),
76 tearing_down_(false),
77 playback_rate_change_pending_(false),
78 did_loading_progress_(false), 70 did_loading_progress_(false),
79 total_bytes_(0), 71 total_bytes_(0),
80 natural_size_(0, 0), 72 natural_size_(0, 0),
81 volume_(1.0f), 73 volume_(1.0f),
82 playback_rate_(0.0f), 74 playback_rate_(0.0f),
83 pending_playback_rate_(0.0f),
84 clock_(new Clock(&base::Time::Now)), 75 clock_(new Clock(&base::Time::Now)),
85 waiting_for_clock_update_(false), 76 waiting_for_clock_update_(false),
86 status_(PIPELINE_OK), 77 status_(PIPELINE_OK),
87 has_audio_(false), 78 has_audio_(false),
88 has_video_(false), 79 has_video_(false),
89 state_(kCreated), 80 state_(kCreated),
90 seek_timestamp_(kNoTimestamp()),
91 audio_ended_(false), 81 audio_ended_(false),
92 video_ended_(false), 82 video_ended_(false),
93 audio_disabled_(false), 83 audio_disabled_(false),
94 creation_time_(base::Time::Now()) { 84 creation_time_(base::Time::Now()) {
95 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); 85 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
96 media_log_->AddEvent( 86 media_log_->AddEvent(
97 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); 87 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
98 } 88 }
99 89
100 Pipeline::~Pipeline() { 90 Pipeline::~Pipeline() {
101 base::AutoLock auto_lock(lock_); 91 DCHECK(thread_checker_.CalledOnValidThread())
92 << "Pipeline must be destroyed on same thread that created it";
102 DCHECK(!running_) << "Stop() must complete before destroying object"; 93 DCHECK(!running_) << "Stop() must complete before destroying object";
103 DCHECK(stop_cb_.is_null()); 94 DCHECK(stop_cb_.is_null());
104 DCHECK(!seek_pending_); 95 DCHECK(seek_cb_.is_null());
105 96
106 media_log_->AddEvent( 97 media_log_->AddEvent(
107 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); 98 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
108 } 99 }
109 100
110 void Pipeline::Start(scoped_ptr<FilterCollection> collection, 101 void Pipeline::Start(scoped_ptr<FilterCollection> collection,
111 const PipelineStatusCB& ended_cb, 102 const PipelineStatusCB& ended_cb,
112 const PipelineStatusCB& error_cb, 103 const PipelineStatusCB& error_cb,
113 const PipelineStatusCB& seek_cb, 104 const PipelineStatusCB& seek_cb,
114 const BufferingStateCB& buffering_state_cb) { 105 const BufferingStateCB& buffering_state_cb) {
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 SetError(status); 240 SetError(status);
250 } 241 }
251 242
252 void Pipeline::SetState(State next_state) { 243 void Pipeline::SetState(State next_state) {
253 if (state_ != kStarted && next_state == kStarted && 244 if (state_ != kStarted && next_state == kStarted &&
254 !creation_time_.is_null()) { 245 !creation_time_.is_null()) {
255 UMA_HISTOGRAM_TIMES( 246 UMA_HISTOGRAM_TIMES(
256 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); 247 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_);
257 creation_time_ = base::Time(); 248 creation_time_ = base::Time();
258 } 249 }
250
251 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state);
252
259 state_ = next_state; 253 state_ = next_state;
260 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); 254 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
261 } 255 }
262 256
263 bool Pipeline::IsPipelineOk() { 257 #define RETURN_STRING(state) case state: return #state;
264 base::AutoLock auto_lock(lock_); 258
265 return status_ == PIPELINE_OK; 259 const char* Pipeline::GetStateString(State state) {
260 switch (state) {
261 RETURN_STRING(kCreated);
262 RETURN_STRING(kInitDemuxer);
263 RETURN_STRING(kInitAudioDecoder);
264 RETURN_STRING(kInitAudioRenderer);
265 RETURN_STRING(kInitVideoRenderer);
266 RETURN_STRING(kInitPrerolling);
267 RETURN_STRING(kSeeking);
268 RETURN_STRING(kStarting);
269 RETURN_STRING(kStarted);
270 RETURN_STRING(kStopping);
271 RETURN_STRING(kStopped);
272 }
273 NOTREACHED();
274 return "INVALID";
266 } 275 }
267 276
268 bool Pipeline::IsPipelineSeeking() { 277 #undef RETURN_STRING
278
279 Pipeline::State Pipeline::GetNextState() const {
269 DCHECK(message_loop_->BelongsToCurrentThread()); 280 DCHECK(message_loop_->BelongsToCurrentThread());
270 if (!seek_pending_) 281 DCHECK(stop_cb_.is_null())
271 return false; 282 << "State transitions don't happen when stopping";
272 DCHECK(kSeeking == state_ || kPausing == state_ || 283 DCHECK_EQ(status_, PIPELINE_OK)
273 kFlushing == state_ || kStarting == state_) 284 << "State transitions don't happen when there's an error: " << status_;
274 << "Current state : " << state_;
275 return true;
276 }
277 285
278 void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) { 286 switch (state_) {
279 DCHECK(message_loop_->BelongsToCurrentThread()); 287 case kCreated:
280 if (cb.is_null()) 288 return kInitDemuxer;
281 return;
282 cb.Run(status);
283 // Prevent double-reporting of errors to clients.
284 if (status != PIPELINE_OK)
285 error_cb_.Reset();
286 }
287 289
288 void Pipeline::FinishSeek() { 290 case kInitDemuxer:
289 DCHECK(message_loop_->BelongsToCurrentThread()); 291 if (demuxer_->GetStream(DemuxerStream::AUDIO))
290 seek_timestamp_ = kNoTimestamp(); 292 return kInitAudioDecoder;
291 seek_pending_ = false; 293 if (demuxer_->GetStream(DemuxerStream::VIDEO))
294 return kInitVideoRenderer;
295 return kInitPrerolling;
292 296
293 // Execute the seek callback, if present. Note that this might be the 297 case kInitAudioDecoder:
294 // initial callback passed into Start(). 298 return kInitAudioRenderer;
295 ReportStatus(seek_cb_, status_);
296 seek_cb_.Reset();
297 }
298 299
299 // static 300 case kInitAudioRenderer:
300 bool Pipeline::TransientState(State state) { 301 if (demuxer_->GetStream(DemuxerStream::VIDEO))
301 return state == kPausing || 302 return kInitVideoRenderer;
302 state == kFlushing || 303 return kInitPrerolling;
303 state == kSeeking ||
304 state == kStarting ||
305 state == kStopping;
306 }
307 304
308 // static 305 case kInitVideoRenderer:
309 Pipeline::State Pipeline::FindNextState(State current) { 306 return kInitPrerolling;
310 // TODO(scherkus): refactor InitializeTask() to make use of this function. 307
311 if (current == kPausing) { 308 case kInitPrerolling:
312 return kFlushing; 309 return kStarting;
313 } else if (current == kFlushing) { 310
314 // We will always honor Seek() before Stop(). This is based on the 311 case kSeeking:
315 // assumption that we never accept Seek() after Stop(). 312 return kStarting;
316 DCHECK(IsPipelineSeeking() || 313
317 !stop_cb_.is_null() || 314 case kStarting:
318 tearing_down_); 315 return kStarted;
319 return IsPipelineSeeking() ? kSeeking : kStopping; 316
320 } else if (current == kSeeking) { 317 case kStarted:
321 return kStarting; 318 case kStopping:
322 } else if (current == kStarting) { 319 case kStopped:
323 return kStarted; 320 break;
324 } else if (current == kStopping) {
325 return kStopped;
326 } else {
327 return current;
328 } 321 }
322 NOTREACHED() << "State has no transition: " << state_;
323 return state_;
329 } 324 }
330 325
331 void Pipeline::OnDemuxerError(PipelineStatus error) { 326 void Pipeline::OnDemuxerError(PipelineStatus error) {
332 SetError(error); 327 SetError(error);
333 } 328 }
334 329
335 void Pipeline::SetError(PipelineStatus error) { 330 void Pipeline::SetError(PipelineStatus error) {
336 DCHECK(IsRunning()); 331 DCHECK(IsRunning());
337 DCHECK_NE(PIPELINE_OK, error); 332 DCHECK_NE(PIPELINE_OK, error);
338 VLOG(1) << "Media pipeline error: " << error; 333 VLOG(1) << "Media pipeline error: " << error;
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 // Since the byte->time calculation is approximate, fudge the beginning & 410 // Since the byte->time calculation is approximate, fudge the beginning &
416 // ending areas to look better. 411 // ending areas to look better.
417 TimeDelta epsilon = clock_->Duration() / 100; 412 TimeDelta epsilon = clock_->Duration() / 100;
418 if (time_offset < epsilon) 413 if (time_offset < epsilon)
419 return TimeDelta(); 414 return TimeDelta();
420 if (time_offset + epsilon > clock_->Duration()) 415 if (time_offset + epsilon > clock_->Duration())
421 return clock_->Duration(); 416 return clock_->Duration();
422 return time_offset; 417 return time_offset;
423 } 418 }
424 419
425 void Pipeline::DoPause(const PipelineStatusCB& done_cb) { 420 void Pipeline::OnStateTransition(PipelineStatus status) {
421 // Force post to process state transitions after current execution frame.
422 message_loop_->PostTask(FROM_HERE, base::Bind(
423 &Pipeline::StateTransitionTask, this, status));
424 }
425
426 void Pipeline::StateTransitionTask(PipelineStatus status) {
427 DCHECK(message_loop_->BelongsToCurrentThread());
428
429 // No-op any state transitions if we're stopping.
430 if (state_ == kStopping || state_ == kStopped)
431 return;
432
433 // Preserve existing abnormal status, otherwise update based on the result of
434 // the previous operation.
435 status_ = (status_ != PIPELINE_OK ? status_ : status);
436
437 if (status_ != PIPELINE_OK) {
438 ErrorChangedTask(status_);
439 return;
440 }
441
442 // Guard against accidentally clearing |pending_callbacks_| for states that
443 // use it as well as states that should not be using it.
444 //
445 // TODO(scherkus): Make every state transition use |pending_callbacks_|.
446 DCHECK_EQ(pending_callbacks_.get() != NULL,
447 (state_ == kInitPrerolling || state_ == kStarting ||
448 state_ == kSeeking));
449 pending_callbacks_.reset();
450
451 PipelineStatusCB done_cb = base::Bind(&Pipeline::OnStateTransition, this);
452
453 // Switch states, performing any entrance actions for the new state as well.
454 SetState(GetNextState());
455 switch (state_) {
456 case kInitDemuxer:
457 return InitializeDemuxer(done_cb);
458
459 case kInitAudioDecoder:
460 return InitializeAudioDecoder(done_cb);
461
462 case kInitAudioRenderer:
463 return InitializeAudioRenderer(done_cb);
464
465 case kInitVideoRenderer:
466 return InitializeVideoRenderer(done_cb);
467
468 case kInitPrerolling:
469 filter_collection_.reset();
470 {
471 base::AutoLock l(lock_);
472 // We do not want to start the clock running. We only want to set the
473 // base media time so our timestamp calculations will be correct.
474 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
475
476 // TODO(scherkus): |has_audio_| should be true no matter what --
477 // otherwise people with muted/disabled sound cards will make our
478 // default controls look as if every video doesn't contain an audio
479 // track.
480 has_audio_ = audio_renderer_ != NULL && !audio_disabled_;
481 has_video_ = video_renderer_ != NULL;
482 }
483 if (!audio_renderer_ && !video_renderer_) {
484 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
485 return;
486 }
487
488 buffering_state_cb_.Run(kHaveMetadata);
489
490 return DoInitialPreroll(done_cb);
491
492 case kStarting:
493 return DoPlay(done_cb);
494
495 case kStarted:
496 {
497 base::AutoLock l(lock_);
498 // We use audio stream to update the clock. So if there is such a
499 // stream, we pause the clock until we receive a valid timestamp.
500 waiting_for_clock_update_ = true;
501 if (!has_audio_) {
502 clock_->SetMaxTime(clock_->Duration());
503 StartClockIfWaitingForTimeUpdate_Locked();
504 }
505 }
506
507 DCHECK(!seek_cb_.is_null());
508 DCHECK_EQ(status_, PIPELINE_OK);
509
510 // Fire canplaythrough immediately after playback begins because of
511 // crbug.com/106480.
512 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
513 buffering_state_cb_.Run(kPrerollCompleted);
514 return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
515
516 case kStopping:
517 case kStopped:
518 case kCreated:
519 case kSeeking:
520 NOTREACHED() << "State has no transition: " << state_;
521 return;
522 }
523 }
524
525 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
426 DCHECK(message_loop_->BelongsToCurrentThread()); 526 DCHECK(message_loop_->BelongsToCurrentThread());
427 DCHECK(!pending_callbacks_.get()); 527 DCHECK(!pending_callbacks_.get());
428 SerialRunner::Queue bound_fns; 528 SerialRunner::Queue bound_fns;
429 529
430 if (audio_renderer_) 530 base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
431 bound_fns.Push(base::Bind(&AudioRenderer::Pause, audio_renderer_));
432 531
433 if (video_renderer_) 532 // Preroll renderers.
434 bound_fns.Push(base::Bind(&VideoRenderer::Pause, video_renderer_)); 533 if (audio_renderer_) {
534 bound_fns.Push(base::Bind(
535 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp));
536 }
537
538 if (video_renderer_) {
539 bound_fns.Push(base::Bind(
540 &VideoRenderer::Preroll, video_renderer_, seek_timestamp));
541 }
435 542
436 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 543 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
437 } 544 }
438 545
439 void Pipeline::DoFlush(const PipelineStatusCB& done_cb) { 546 void Pipeline::DoSeek(
547 base::TimeDelta seek_timestamp,
548 const PipelineStatusCB& done_cb) {
440 DCHECK(message_loop_->BelongsToCurrentThread()); 549 DCHECK(message_loop_->BelongsToCurrentThread());
441 DCHECK(!pending_callbacks_.get()); 550 DCHECK(!pending_callbacks_.get());
442 SerialRunner::Queue bound_fns; 551 SerialRunner::Queue bound_fns;
443 552
553 // Pause.
554 if (audio_renderer_)
555 bound_fns.Push(base::Bind(&AudioRenderer::Pause, audio_renderer_));
556 if (video_renderer_)
557 bound_fns.Push(base::Bind(&VideoRenderer::Pause, video_renderer_));
558
559 // Flush.
444 if (audio_renderer_) 560 if (audio_renderer_)
445 bound_fns.Push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); 561 bound_fns.Push(base::Bind(&AudioRenderer::Flush, audio_renderer_));
446
447 if (video_renderer_) 562 if (video_renderer_)
448 bound_fns.Push(base::Bind(&VideoRenderer::Flush, video_renderer_)); 563 bound_fns.Push(base::Bind(&VideoRenderer::Flush, video_renderer_));
449 564
565 // Seek demuxer.
566 bound_fns.Push(base::Bind(
567 &Demuxer::Seek, demuxer_, seek_timestamp));
568
569 // Preroll renderers.
570 if (audio_renderer_) {
571 bound_fns.Push(base::Bind(
572 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp));
573 }
574
575 if (video_renderer_) {
576 bound_fns.Push(base::Bind(
577 &VideoRenderer::Preroll, video_renderer_, seek_timestamp));
578 }
579
450 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 580 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
451 } 581 }
452 582
453 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { 583 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) {
454 DCHECK(message_loop_->BelongsToCurrentThread()); 584 DCHECK(message_loop_->BelongsToCurrentThread());
455 DCHECK(!pending_callbacks_.get()); 585 DCHECK(!pending_callbacks_.get());
456 SerialRunner::Queue bound_fns; 586 SerialRunner::Queue bound_fns;
457 587
588 PlaybackRateChangedTask(GetPlaybackRate());
589 VolumeChangedTask(GetVolume());
590
458 if (audio_renderer_) 591 if (audio_renderer_)
459 bound_fns.Push(base::Bind(&AudioRenderer::Play, audio_renderer_)); 592 bound_fns.Push(base::Bind(&AudioRenderer::Play, audio_renderer_));
460 593
461 if (video_renderer_) 594 if (video_renderer_)
462 bound_fns.Push(base::Bind(&VideoRenderer::Play, video_renderer_)); 595 bound_fns.Push(base::Bind(&VideoRenderer::Play, video_renderer_));
463 596
464 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 597 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
465 } 598 }
466 599
467 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { 600 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
468 DCHECK(message_loop_->BelongsToCurrentThread()); 601 DCHECK(message_loop_->BelongsToCurrentThread());
469 DCHECK(!pending_callbacks_.get()); 602 DCHECK(!pending_callbacks_.get());
470 SerialRunner::Queue bound_fns; 603 SerialRunner::Queue bound_fns;
471 604
472 if (demuxer_) 605 if (demuxer_)
473 bound_fns.Push(base::Bind(&Demuxer::Stop, demuxer_)); 606 bound_fns.Push(base::Bind(&Demuxer::Stop, demuxer_));
474 607
475 if (audio_renderer_) 608 if (audio_renderer_)
476 bound_fns.Push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); 609 bound_fns.Push(base::Bind(&AudioRenderer::Stop, audio_renderer_));
477 610
478 if (video_renderer_) 611 if (video_renderer_)
479 bound_fns.Push(base::Bind(&VideoRenderer::Stop, video_renderer_)); 612 bound_fns.Push(base::Bind(&VideoRenderer::Stop, video_renderer_));
480 613
481 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 614 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
482 } 615 }
483 616
617 void Pipeline::OnStopCompleted(PipelineStatus status) {
618 DCHECK(message_loop_->BelongsToCurrentThread());
619 DCHECK_EQ(state_, kStopping);
620 {
621 base::AutoLock l(lock_);
622 running_ = false;
623 }
624
625 SetState(kStopped);
626 pending_callbacks_.reset();
627 filter_collection_.reset();
628 audio_decoder_ = NULL;
629 audio_renderer_ = NULL;
630 video_renderer_ = NULL;
631 demuxer_ = NULL;
632
633 // If we stop during initialization/seeking we want to run |seek_cb_|
634 // followed by |stop_cb_| so we don't leave outstanding callbacks around.
635 if (!seek_cb_.is_null()) {
636 base::ResetAndReturn(&seek_cb_).Run(status_);
637 error_cb_.Reset();
638 }
639 if (!stop_cb_.is_null()) {
640 base::ResetAndReturn(&stop_cb_).Run();
641 error_cb_.Reset();
642 }
643 if (!error_cb_.is_null()) {
644 DCHECK_NE(status_, PIPELINE_OK);
645 base::ResetAndReturn(&error_cb_).Run(status_);
646 }
647 }
648
484 void Pipeline::AddBufferedByteRange(int64 start, int64 end) { 649 void Pipeline::AddBufferedByteRange(int64 start, int64 end) {
485 DCHECK(IsRunning()); 650 DCHECK(IsRunning());
486 base::AutoLock auto_lock(lock_); 651 base::AutoLock auto_lock(lock_);
487 buffered_byte_ranges_.Add(start, end); 652 buffered_byte_ranges_.Add(start, end);
488 did_loading_progress_ = true; 653 did_loading_progress_ = true;
489 } 654 }
490 655
491 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, 656 void Pipeline::AddBufferedTimeRange(base::TimeDelta start,
492 base::TimeDelta end) { 657 base::TimeDelta end) {
493 DCHECK(IsRunning()); 658 DCHECK(IsRunning());
(...skipping 19 matching lines...) Expand all
513 } 678 }
514 679
515 void Pipeline::OnVideoRendererEnded() { 680 void Pipeline::OnVideoRendererEnded() {
516 // Force post to process ended messages after current execution frame. 681 // Force post to process ended messages after current execution frame.
517 message_loop_->PostTask(FROM_HERE, base::Bind( 682 message_loop_->PostTask(FROM_HERE, base::Bind(
518 &Pipeline::DoVideoRendererEnded, this)); 683 &Pipeline::DoVideoRendererEnded, this));
519 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED)); 684 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
520 } 685 }
521 686
522 // Called from any thread. 687 // Called from any thread.
523 void Pipeline::OnFilterInitialize(PipelineStatus status) {
524 // Continue the initialize task by proceeding to the next stage.
525 message_loop_->PostTask(FROM_HERE, base::Bind(
526 &Pipeline::InitializeTask, this, status));
527 }
528
529 // Called from any thread.
530 // This method makes the PipelineStatusCB behave like a Closure. It
531 // makes it look like a host()->SetError() call followed by a call to
532 // OnFilterStateTransition() when errors occur.
533 //
534 // TODO(scherkus): Revisit this code when SetError() is removed from FilterHost
535 // and all the Closures are converted to PipelineStatusCB.
536 void Pipeline::OnFilterStateTransition(PipelineStatus status) {
537 if (status != PIPELINE_OK)
538 SetError(status);
539 message_loop_->PostTask(FROM_HERE, base::Bind(
540 &Pipeline::FilterStateTransitionTask, this));
541 }
542
543 void Pipeline::OnTeardownStateTransition(PipelineStatus status) {
544 // Ignore any errors during teardown.
545 message_loop_->PostTask(FROM_HERE, base::Bind(
546 &Pipeline::TeardownStateTransitionTask, this));
547 }
548
549 // Called from any thread.
550 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { 688 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
551 base::AutoLock auto_lock(lock_); 689 base::AutoLock auto_lock(lock_);
552 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; 690 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
553 statistics_.video_bytes_decoded += stats.video_bytes_decoded; 691 statistics_.video_bytes_decoded += stats.video_bytes_decoded;
554 statistics_.video_frames_decoded += stats.video_frames_decoded; 692 statistics_.video_frames_decoded += stats.video_frames_decoded;
555 statistics_.video_frames_dropped += stats.video_frames_dropped; 693 statistics_.video_frames_dropped += stats.video_frames_dropped;
556 } 694 }
557 695
558 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, 696 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection,
559 const PipelineStatusCB& ended_cb, 697 const PipelineStatusCB& ended_cb,
560 const PipelineStatusCB& error_cb, 698 const PipelineStatusCB& error_cb,
561 const PipelineStatusCB& seek_cb, 699 const PipelineStatusCB& seek_cb,
562 const BufferingStateCB& buffering_state_cb) { 700 const BufferingStateCB& buffering_state_cb) {
563 DCHECK(message_loop_->BelongsToCurrentThread()); 701 DCHECK(message_loop_->BelongsToCurrentThread());
564 CHECK_EQ(kCreated, state_) 702 CHECK_EQ(kCreated, state_)
565 << "Media pipeline cannot be started more than once"; 703 << "Media pipeline cannot be started more than once";
566 704
567 filter_collection_ = filter_collection.Pass(); 705 filter_collection_ = filter_collection.Pass();
568 ended_cb_ = ended_cb; 706 ended_cb_ = ended_cb;
569 error_cb_ = error_cb; 707 error_cb_ = error_cb;
570 seek_cb_ = seek_cb; 708 seek_cb_ = seek_cb;
571 buffering_state_cb_ = buffering_state_cb; 709 buffering_state_cb_ = buffering_state_cb;
572 710
573 // Kick off initialization. 711 StateTransitionTask(PIPELINE_OK);
574 pipeline_init_state_.reset(new PipelineInitState());
575
576 SetState(kInitDemuxer);
577 InitializeDemuxer();
578 } 712 }
579 713
580 // Main initialization method called on the pipeline thread. This code attempts
581 // to use the specified filter factory to build a pipeline.
582 // Initialization step performed in this method depends on current state of this
583 // object, indicated by |state_|. After each step of initialization, this
584 // object transits to the next stage. It starts by creating a Demuxer, and then
585 // connects the Demuxer's audio stream to an AudioDecoder which is then
586 // connected to an AudioRenderer. If the media has video, then it connects a
587 // VideoDecoder to the Demuxer's video stream, and then connects the
588 // VideoDecoder to a VideoRenderer.
589 //
590 // When all required filters have been created and have called their
591 // FilterHost's InitializationComplete() method, the pipeline will update its
592 // state to kStarted and |init_cb_|, will be executed.
593 //
594 // TODO(hclam): InitializeTask() is now starting the pipeline asynchronously. It
595 // works like a big state change table. If we no longer need to start filters
596 // in order, we need to get rid of all the state change.
597 void Pipeline::InitializeTask(PipelineStatus last_stage_status) {
598 DCHECK(message_loop_->BelongsToCurrentThread());
599
600 if (last_stage_status != PIPELINE_OK) {
601 SetError(last_stage_status);
602 return;
603 }
604
605 // If we have received the stop or error signal, return immediately.
606 if (!stop_cb_.is_null() || state_ == kStopped || !IsPipelineOk())
607 return;
608
609 DCHECK(state_ == kInitDemuxer ||
610 state_ == kInitAudioDecoder ||
611 state_ == kInitAudioRenderer ||
612 state_ == kInitVideoRenderer);
613
614 // Demuxer created, create audio decoder.
615 if (state_ == kInitDemuxer) {
616 SetState(kInitAudioDecoder);
617 // If this method returns false, then there's no audio stream.
618 if (InitializeAudioDecoder(demuxer_))
619 return;
620 }
621
622 // Assuming audio decoder was created, create audio renderer.
623 if (state_ == kInitAudioDecoder) {
624 SetState(kInitAudioRenderer);
625
626 // Returns false if there's no audio stream.
627 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder)) {
628 base::AutoLock auto_lock(lock_);
629 has_audio_ = true;
630 return;
631 }
632 }
633
634 // Assuming audio renderer was created, create video renderer.
635 if (state_ == kInitAudioRenderer) {
636 SetState(kInitVideoRenderer);
637 scoped_refptr<DemuxerStream> video_stream =
638 demuxer_->GetStream(DemuxerStream::VIDEO);
639 if (InitializeVideoRenderer(video_stream)) {
640 base::AutoLock auto_lock(lock_);
641 has_video_ = true;
642
643 // Get an initial natural size so we have something when we signal
644 // the kHaveMetadata buffering state.
645 natural_size_ = video_stream->video_decoder_config().natural_size();
646 return;
647 }
648 }
649
650 if (state_ == kInitVideoRenderer) {
651 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) {
652 SetError(PIPELINE_ERROR_COULD_NOT_RENDER);
653 return;
654 }
655
656 // Clear initialization state now that we're done.
657 filter_collection_.reset();
658 pipeline_init_state_.reset();
659
660 // Initialization was successful, we are now considered paused, so it's safe
661 // to set the initial playback rate and volume.
662 PlaybackRateChangedTask(GetPlaybackRate());
663 VolumeChangedTask(GetVolume());
664
665 buffering_state_cb_.Run(kHaveMetadata);
666
667 // Fire a seek request to get the renderers to preroll. We can skip a seek
668 // here as the demuxer should be at the start of the stream.
669 seek_pending_ = true;
670 SetState(kSeeking);
671 seek_timestamp_ = demuxer_->GetStartTime();
672 DoSeek(seek_timestamp_, true,
673 base::Bind(&Pipeline::OnFilterStateTransition, this));
674 }
675 }
676
677 // This method is called as a result of the client calling Pipeline::Stop() or
678 // as the result of an error condition.
679 // We stop the filters in the reverse order.
680 //
681 // TODO(scherkus): beware! this can get posted multiple times since we post
682 // Stop() tasks even if we've already stopped. Perhaps this should no-op for
683 // additional calls, however most of this logic will be changing.
684 void Pipeline::StopTask(const base::Closure& stop_cb) { 714 void Pipeline::StopTask(const base::Closure& stop_cb) {
685 DCHECK(message_loop_->BelongsToCurrentThread()); 715 DCHECK(message_loop_->BelongsToCurrentThread());
686 DCHECK(stop_cb_.is_null()); 716 DCHECK(stop_cb_.is_null());
687 717
688 if (state_ == kStopped) { 718 if (state_ == kStopped) {
689 stop_cb.Run(); 719 stop_cb.Run();
690 return; 720 return;
691 } 721 }
692 722
723 // TODO(scherkus): Remove after pipeline state machine refactoring has some
724 // time to bake http://crbug.com/110228
693 if (video_renderer_) 725 if (video_renderer_)
694 video_renderer_->PrepareForShutdownHack(); 726 video_renderer_->PrepareForShutdownHack();
695 727
696 if (tearing_down_ && status_ != PIPELINE_OK) { 728 SetState(kStopping);
697 // If we are stopping due to SetError(), stop normally instead of 729 pending_callbacks_.reset();
698 // going to error state and calling |error_cb_|. This converts
699 // the teardown in progress from an error teardown into one that acts
700 // like the error never occurred.
701 base::AutoLock auto_lock(lock_);
702 status_ = PIPELINE_OK;
703 }
704
705 stop_cb_ = stop_cb; 730 stop_cb_ = stop_cb;
706 731
707 if (!IsPipelineSeeking() && !tearing_down_) { 732 DoStop(base::Bind(&Pipeline::OnStopCompleted, this));
708 // We will tear down pipeline immediately when there is no seek operation
709 // pending and no teardown in progress. This should include the case where
710 // we are partially initialized.
711 TearDownPipeline();
712 }
713 } 733 }
714 734
715 void Pipeline::ErrorChangedTask(PipelineStatus error) { 735 void Pipeline::ErrorChangedTask(PipelineStatus error) {
716 DCHECK(message_loop_->BelongsToCurrentThread()); 736 DCHECK(message_loop_->BelongsToCurrentThread());
717 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; 737 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
718 738
719 // Suppress executing additional error logic. Note that if we are currently 739 if (state_ == kStopping || state_ == kStopped)
720 // performing a normal stop, then we return immediately and continue the
721 // normal stop.
722 if (state_ == kStopped || tearing_down_) {
723 return; 740 return;
724 }
725 741
726 base::AutoLock auto_lock(lock_); 742 SetState(kStopping);
743 pending_callbacks_.reset();
727 status_ = error; 744 status_ = error;
728 745
729 // Posting TearDownPipeline() to message loop so that we can make sure 746 DoStop(base::Bind(&Pipeline::OnStopCompleted, this));
730 // it runs after any pending callbacks that are already queued.
731 // |tearing_down_| is set early here to make sure that pending callbacks
732 // don't modify the state before TearDownPipeline() can run.
733 tearing_down_ = true;
734 message_loop_->PostTask(FROM_HERE, base::Bind(
735 &Pipeline::TearDownPipeline, this));
736 } 747 }
737 748
738 void Pipeline::PlaybackRateChangedTask(float playback_rate) { 749 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
739 DCHECK(message_loop_->BelongsToCurrentThread()); 750 DCHECK(message_loop_->BelongsToCurrentThread());
740 751
741 if (state_ == kStopped || tearing_down_) 752 // Playback rate changes are only carried out while playing.
753 if (state_ != kStarting && state_ != kStarted)
742 return; 754 return;
743 755
744 // Suppress rate change until after seeking.
745 if (IsPipelineSeeking()) {
746 pending_playback_rate_ = playback_rate;
747 playback_rate_change_pending_ = true;
748 return;
749 }
750
751 { 756 {
752 base::AutoLock auto_lock(lock_); 757 base::AutoLock auto_lock(lock_);
753 clock_->SetPlaybackRate(playback_rate); 758 clock_->SetPlaybackRate(playback_rate);
754 } 759 }
755 760
756 // These will get set after initialization completes in case playback rate is
757 // set prior to initialization.
758 if (demuxer_) 761 if (demuxer_)
759 demuxer_->SetPlaybackRate(playback_rate); 762 demuxer_->SetPlaybackRate(playback_rate);
760 if (audio_renderer_) 763 if (audio_renderer_)
761 audio_renderer_->SetPlaybackRate(playback_rate_); 764 audio_renderer_->SetPlaybackRate(playback_rate_);
762 if (video_renderer_) 765 if (video_renderer_)
763 video_renderer_->SetPlaybackRate(playback_rate_); 766 video_renderer_->SetPlaybackRate(playback_rate_);
764 } 767 }
765 768
766 void Pipeline::VolumeChangedTask(float volume) { 769 void Pipeline::VolumeChangedTask(float volume) {
767 DCHECK(message_loop_->BelongsToCurrentThread()); 770 DCHECK(message_loop_->BelongsToCurrentThread());
768 771
769 if (state_ == kStopped || tearing_down_) 772 // Volume changes are only carried out while playing.
773 if (state_ != kStarting && state_ != kStarted)
770 return; 774 return;
771 775
772 if (audio_renderer_) 776 if (audio_renderer_)
773 audio_renderer_->SetVolume(volume); 777 audio_renderer_->SetVolume(volume);
774 } 778 }
775 779
776 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { 780 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
777 DCHECK(message_loop_->BelongsToCurrentThread()); 781 DCHECK(message_loop_->BelongsToCurrentThread());
778 DCHECK(stop_cb_.is_null()); 782 DCHECK(stop_cb_.is_null());
779 783
780 // Suppress seeking if we're not fully started. 784 // Suppress seeking if we're not fully started.
781 if (state_ != kStarted) { 785 if (state_ != kStarted) {
786 DCHECK(state_ == kStopping || state_ == kStopped)
787 << "Receive extra seek in unexpected state: " << state_;
788
782 // TODO(scherkus): should we run the callback? I'm tempted to say the API 789 // TODO(scherkus): should we run the callback? I'm tempted to say the API
783 // will only execute the first Seek() request. 790 // will only execute the first Seek() request.
784 DVLOG(1) << "Media pipeline has not started, ignoring seek to " 791 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
785 << time.InMicroseconds() << " (current state: " << state_ << ")"; 792 << time.InMicroseconds() << " (current state: " << state_ << ")";
786 return; 793 return;
787 } 794 }
788 795
789 DCHECK(!seek_pending_); 796 DCHECK(seek_cb_.is_null());
790 seek_pending_ = true;
791 797
792 // We'll need to pause every filter before seeking. The state transition 798 SetState(kSeeking);
793 // is as follows: 799 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
794 // kStarted 800 seek_cb_ = seek_cb;
795 // kPausing (for each filter)
796 // kSeeking (for each filter)
797 // kStarting (for each filter)
798 // kStarted
799 SetState(kPausing);
800 audio_ended_ = false; 801 audio_ended_ = false;
801 video_ended_ = false; 802 video_ended_ = false;
802 seek_timestamp_ = std::max(time, demuxer_->GetStartTime());
803 seek_cb_ = seek_cb;
804 803
805 // Kick off seeking! 804 // Kick off seeking!
806 { 805 {
807 base::AutoLock auto_lock(lock_); 806 base::AutoLock auto_lock(lock_);
808 if (clock_->IsPlaying()) 807 if (clock_->IsPlaying())
809 clock_->Pause(); 808 clock_->Pause();
809 clock_->SetTime(seek_timestamp, seek_timestamp);
810 } 810 }
811 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); 811 DoSeek(seek_timestamp, base::Bind(&Pipeline::OnStateTransition, this));
812 } 812 }
813 813
814 void Pipeline::DoAudioRendererEnded() { 814 void Pipeline::DoAudioRendererEnded() {
815 DCHECK(message_loop_->BelongsToCurrentThread()); 815 DCHECK(message_loop_->BelongsToCurrentThread());
816 816
817 if (state_ != kStarted) 817 if (state_ != kStarted)
818 return; 818 return;
819 819
820 DCHECK(!audio_ended_); 820 DCHECK(!audio_ended_);
821 audio_ended_ = true; 821 audio_ended_ = true;
(...skipping 27 matching lines...) Expand all
849 return; 849 return;
850 850
851 if (video_renderer_ && !video_ended_) 851 if (video_renderer_ && !video_ended_)
852 return; 852 return;
853 853
854 { 854 {
855 base::AutoLock auto_lock(lock_); 855 base::AutoLock auto_lock(lock_);
856 clock_->EndOfStream(); 856 clock_->EndOfStream();
857 } 857 }
858 858
859 ReportStatus(ended_cb_, status_); 859 // TODO(scherkus): Change |ended_cb_| into a Closure.
860 DCHECK_EQ(status_, PIPELINE_OK);
861 ended_cb_.Run(status_);
860 } 862 }
861 863
862 void Pipeline::AudioDisabledTask() { 864 void Pipeline::AudioDisabledTask() {
863 DCHECK(message_loop_->BelongsToCurrentThread()); 865 DCHECK(message_loop_->BelongsToCurrentThread());
864 866
865 base::AutoLock auto_lock(lock_); 867 base::AutoLock auto_lock(lock_);
866 has_audio_ = false; 868 has_audio_ = false;
867 audio_disabled_ = true; 869 audio_disabled_ = true;
868 870
869 // Notify our demuxer that we're no longer rendering audio. 871 // Notify our demuxer that we're no longer rendering audio.
870 demuxer_->OnAudioRendererDisabled(); 872 demuxer_->OnAudioRendererDisabled();
871 873
872 // Start clock since there is no more audio to 874 // Start clock since there is no more audio to trigger clock updates.
873 // trigger clock updates.
874 clock_->SetMaxTime(clock_->Duration()); 875 clock_->SetMaxTime(clock_->Duration());
875 StartClockIfWaitingForTimeUpdate_Locked(); 876 StartClockIfWaitingForTimeUpdate_Locked();
876 } 877 }
877 878
878 void Pipeline::FilterStateTransitionTask() { 879 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
879 DCHECK(message_loop_->BelongsToCurrentThread()); 880 DCHECK(message_loop_->BelongsToCurrentThread());
880 DCHECK(pending_callbacks_.get())
881 << "Filter state transitions must be completed via pending_callbacks_";
882 pending_callbacks_.reset();
883 881
884 // State transitions while tearing down are handled via 882 demuxer_ = filter_collection_->GetDemuxer();
885 // TeardownStateTransitionTask(). 883 demuxer_->Initialize(this, done_cb);
886 //
887 // TODO(scherkus): Merge all state machinery!
888 if (state_ == kStopped || tearing_down_) {
889 return;
890 }
891
892 if (!TransientState(state_)) {
893 NOTREACHED() << "Invalid current state: " << state_;
894 SetError(PIPELINE_ERROR_ABORT);
895 return;
896 }
897
898 // Decrement the number of remaining transitions, making sure to transition
899 // to the next state if needed.
900 SetState(FindNextState(state_));
901 if (state_ == kSeeking) {
902 base::AutoLock auto_lock(lock_);
903 DCHECK(seek_timestamp_ != kNoTimestamp());
904 clock_->SetTime(seek_timestamp_, seek_timestamp_);
905 }
906
907 // Carry out the action for the current state.
908 if (TransientState(state_)) {
909 if (state_ == kPausing) {
910 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this));
911 } else if (state_ == kFlushing) {
912 DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this));
913 } else if (state_ == kSeeking) {
914 DoSeek(seek_timestamp_, false,
915 base::Bind(&Pipeline::OnFilterStateTransition, this));
916 } else if (state_ == kStarting) {
917 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this));
918 } else if (state_ == kStopping) {
919 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this));
920 } else {
921 NOTREACHED() << "Unexpected state: " << state_;
922 }
923 } else if (state_ == kStarted) {
924
925 // Fire canplaythrough immediately after playback begins because of
926 // crbug.com/106480.
927 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
928 if (status_ == PIPELINE_OK)
929 buffering_state_cb_.Run(kPrerollCompleted);
930
931 FinishSeek();
932
933 // If a playback rate change was requested during a seek, do it now that
934 // the seek has compelted.
935 if (playback_rate_change_pending_) {
936 playback_rate_change_pending_ = false;
937 PlaybackRateChangedTask(pending_playback_rate_);
938 }
939
940 base::AutoLock auto_lock(lock_);
941 // We use audio stream to update the clock. So if there is such a stream,
942 // we pause the clock until we receive a valid timestamp.
943 waiting_for_clock_update_ = true;
944 if (!has_audio_) {
945 clock_->SetMaxTime(clock_->Duration());
946 StartClockIfWaitingForTimeUpdate_Locked();
947 }
948
949 // Check if we have a pending stop request that needs to be honored.
950 if (!stop_cb_.is_null()) {
951 TearDownPipeline();
952 }
953 } else {
954 NOTREACHED() << "Unexpected state: " << state_;
955 }
956 } 884 }
957 885
958 void Pipeline::TeardownStateTransitionTask() { 886 void Pipeline::InitializeAudioDecoder(const PipelineStatusCB& done_cb) {
959 DCHECK(tearing_down_); 887 DCHECK(message_loop_->BelongsToCurrentThread());
960 DCHECK(pending_callbacks_.get())
961 << "Teardown state transitions must be completed via pending_callbacks_";
962 pending_callbacks_.reset();
963 888
964 switch (state_) { 889 scoped_refptr<DemuxerStream> stream =
965 case kStopping: 890 demuxer_->GetStream(DemuxerStream::AUDIO);
966 SetState(kStopped); 891 DCHECK(stream);
967 FinishDestroyingFiltersTask();
968 break;
969 case kPausing:
970 SetState(kFlushing);
971 DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this));
972 break;
973 case kFlushing:
974 SetState(kStopping);
975 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this));
976 break;
977 892
978 case kCreated: 893 filter_collection_->SelectAudioDecoder(&audio_decoder_);
979 case kInitDemuxer: 894 audio_decoder_->Initialize(
980 case kInitAudioDecoder: 895 stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this));
981 case kInitAudioRenderer:
982 case kInitVideoRenderer:
983 case kSeeking:
984 case kStarting:
985 case kStopped:
986 case kStarted:
987 NOTREACHED() << "Unexpected state for teardown: " << state_;
988 break;
989 // default: intentionally left out to force new states to cause compiler
990 // errors.
991 };
992 } 896 }
993 897
994 void Pipeline::FinishDestroyingFiltersTask() { 898 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
995 DCHECK(message_loop_->BelongsToCurrentThread()); 899 DCHECK(message_loop_->BelongsToCurrentThread());
996 DCHECK_EQ(state_, kStopped); 900 DCHECK(audio_decoder_);
997
998 audio_renderer_ = NULL;
999 video_renderer_ = NULL;
1000 demuxer_ = NULL;
1001 tearing_down_ = false;
1002 {
1003 base::AutoLock l(lock_);
1004 running_ = false;
1005 }
1006
1007 if (!IsPipelineOk() && !error_cb_.is_null())
1008 error_cb_.Run(status_);
1009
1010 if (!stop_cb_.is_null())
1011 base::ResetAndReturn(&stop_cb_).Run();
1012 }
1013
1014 void Pipeline::InitializeDemuxer() {
1015 DCHECK(message_loop_->BelongsToCurrentThread());
1016 DCHECK(IsPipelineOk());
1017
1018 demuxer_ = filter_collection_->GetDemuxer();
1019 if (!demuxer_) {
1020 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
1021 return;
1022 }
1023
1024 demuxer_->Initialize(this, base::Bind(&Pipeline::OnDemuxerInitialized, this));
1025 }
1026
1027 void Pipeline::OnDemuxerInitialized(PipelineStatus status) {
1028 if (!message_loop_->BelongsToCurrentThread()) {
1029 message_loop_->PostTask(FROM_HERE, base::Bind(
1030 &Pipeline::OnDemuxerInitialized, this, status));
1031 return;
1032 }
1033
1034 if (status != PIPELINE_OK) {
1035 SetError(status);
1036 return;
1037 }
1038
1039 {
1040 base::AutoLock auto_lock(lock_);
1041 // We do not want to start the clock running. We only want to set the base
1042 // media time so our timestamp calculations will be correct.
1043 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
1044 }
1045
1046 OnFilterInitialize(PIPELINE_OK);
1047 }
1048
1049 bool Pipeline::InitializeAudioDecoder(
1050 const scoped_refptr<Demuxer>& demuxer) {
1051 DCHECK(message_loop_->BelongsToCurrentThread());
1052 DCHECK(IsPipelineOk());
1053 DCHECK(demuxer);
1054
1055 scoped_refptr<DemuxerStream> stream =
1056 demuxer->GetStream(DemuxerStream::AUDIO);
1057
1058 if (!stream)
1059 return false;
1060
1061 filter_collection_->SelectAudioDecoder(&pipeline_init_state_->audio_decoder);
1062
1063 if (!pipeline_init_state_->audio_decoder) {
1064 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
1065 return false;
1066 }
1067
1068 pipeline_init_state_->audio_decoder->Initialize(
1069 stream,
1070 base::Bind(&Pipeline::OnFilterInitialize, this),
1071 base::Bind(&Pipeline::OnUpdateStatistics, this));
1072 return true;
1073 }
1074
1075 bool Pipeline::InitializeAudioRenderer(
1076 const scoped_refptr<AudioDecoder>& decoder) {
1077 DCHECK(message_loop_->BelongsToCurrentThread());
1078 DCHECK(IsPipelineOk());
1079
1080 if (!decoder)
1081 return false;
1082 901
1083 filter_collection_->SelectAudioRenderer(&audio_renderer_); 902 filter_collection_->SelectAudioRenderer(&audio_renderer_);
1084 if (!audio_renderer_) {
1085 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
1086 return false;
1087 }
1088
1089 audio_renderer_->Initialize( 903 audio_renderer_->Initialize(
1090 decoder, 904 audio_decoder_,
1091 base::Bind(&Pipeline::OnFilterInitialize, this), 905 done_cb,
1092 base::Bind(&Pipeline::OnAudioUnderflow, this), 906 base::Bind(&Pipeline::OnAudioUnderflow, this),
1093 base::Bind(&Pipeline::OnAudioTimeUpdate, this), 907 base::Bind(&Pipeline::OnAudioTimeUpdate, this),
1094 base::Bind(&Pipeline::OnAudioRendererEnded, this), 908 base::Bind(&Pipeline::OnAudioRendererEnded, this),
1095 base::Bind(&Pipeline::OnAudioDisabled, this), 909 base::Bind(&Pipeline::OnAudioDisabled, this),
1096 base::Bind(&Pipeline::SetError, this)); 910 base::Bind(&Pipeline::SetError, this));
1097 return true;
1098 } 911 }
1099 912
1100 bool Pipeline::InitializeVideoRenderer( 913 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
1101 const scoped_refptr<DemuxerStream>& stream) {
1102 DCHECK(message_loop_->BelongsToCurrentThread()); 914 DCHECK(message_loop_->BelongsToCurrentThread());
1103 DCHECK(IsPipelineOk());
1104 915
1105 if (!stream) 916 scoped_refptr<DemuxerStream> stream =
1106 return false; 917 demuxer_->GetStream(DemuxerStream::VIDEO);
918 DCHECK(stream);
919
920 {
921 // Get an initial natural size so we have something when we signal
922 // the kHaveMetadata buffering state.
923 base::AutoLock l(lock_);
924 natural_size_ = stream->video_decoder_config().natural_size();
925 }
1107 926
1108 filter_collection_->SelectVideoRenderer(&video_renderer_); 927 filter_collection_->SelectVideoRenderer(&video_renderer_);
1109 if (!video_renderer_) {
1110 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
1111 return false;
1112 }
1113
1114 video_renderer_->Initialize( 928 video_renderer_->Initialize(
1115 stream, 929 stream,
1116 *filter_collection_->GetVideoDecoders(), 930 *filter_collection_->GetVideoDecoders(),
1117 base::Bind(&Pipeline::OnFilterInitialize, this), 931 done_cb,
1118 base::Bind(&Pipeline::OnUpdateStatistics, this), 932 base::Bind(&Pipeline::OnUpdateStatistics, this),
1119 base::Bind(&Pipeline::OnVideoTimeUpdate, this), 933 base::Bind(&Pipeline::OnVideoTimeUpdate, this),
1120 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), 934 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this),
1121 base::Bind(&Pipeline::OnVideoRendererEnded, this), 935 base::Bind(&Pipeline::OnVideoRendererEnded, this),
1122 base::Bind(&Pipeline::SetError, this), 936 base::Bind(&Pipeline::SetError, this),
1123 base::Bind(&Pipeline::GetMediaTime, this), 937 base::Bind(&Pipeline::GetMediaTime, this),
1124 base::Bind(&Pipeline::GetMediaDuration, this)); 938 base::Bind(&Pipeline::GetMediaDuration, this));
1125 filter_collection_->GetVideoDecoders()->clear(); 939 filter_collection_->GetVideoDecoders()->clear();
1126 return true;
1127 }
1128
1129 void Pipeline::TearDownPipeline() {
1130 DCHECK(message_loop_->BelongsToCurrentThread());
1131 DCHECK_NE(kStopped, state_);
1132
1133 // We're either...
1134 // 1) ...tearing down due to Stop() (it doesn't set tearing_down_)
1135 // 2) ...tearing down due to an error (it does set tearing_down_)
1136 // 3) ...tearing down due to an error and Stop() was called during that time
1137 DCHECK(!tearing_down_ ||
1138 (tearing_down_ && status_ != PIPELINE_OK) ||
1139 (tearing_down_ && !stop_cb_.is_null()));
1140
1141 // Mark that we already start tearing down operation.
1142 tearing_down_ = true;
1143
1144 // Cancel any pending operation so we can proceed with teardown.
1145 pending_callbacks_.reset();
1146
1147 switch (state_) {
1148 case kCreated:
1149 SetState(kStopped);
1150 // Need to put this in the message loop to make sure that it comes
1151 // after any pending callback tasks that are already queued.
1152 message_loop_->PostTask(FROM_HERE, base::Bind(
1153 &Pipeline::FinishDestroyingFiltersTask, this));
1154 break;
1155
1156 case kInitDemuxer:
1157 case kInitAudioDecoder:
1158 case kInitAudioRenderer:
1159 case kInitVideoRenderer:
1160 // Make it look like initialization was successful.
1161 filter_collection_.reset();
1162 pipeline_init_state_.reset();
1163
1164 SetState(kStopping);
1165 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this));
1166
1167 FinishSeek();
1168 break;
1169
1170 case kPausing:
1171 case kSeeking:
1172 case kFlushing:
1173 case kStarting:
1174 SetState(kStopping);
1175 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this));
1176
1177 if (seek_pending_)
1178 FinishSeek();
1179
1180 break;
1181
1182 case kStarted:
1183 SetState(kPausing);
1184 DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this));
1185 break;
1186
1187 case kStopping:
1188 case kStopped:
1189 NOTREACHED() << "Unexpected state for teardown: " << state_;
1190 break;
1191 // default: intentionally left out to force new states to cause compiler
1192 // errors.
1193 };
1194 }
1195
1196 void Pipeline::DoSeek(base::TimeDelta seek_timestamp,
1197 bool skip_demuxer_seek,
1198 const PipelineStatusCB& done_cb) {
1199 DCHECK(message_loop_->BelongsToCurrentThread());
1200 DCHECK(!pending_callbacks_.get());
1201 SerialRunner::Queue bound_fns;
1202
1203 if (!skip_demuxer_seek) {
1204 bound_fns.Push(base::Bind(
1205 &Demuxer::Seek, demuxer_, seek_timestamp));
1206 }
1207
1208 if (audio_renderer_) {
1209 bound_fns.Push(base::Bind(
1210 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp));
1211 }
1212
1213 if (video_renderer_) {
1214 bound_fns.Push(base::Bind(
1215 &VideoRenderer::Preroll, video_renderer_, seek_timestamp));
1216 }
1217
1218 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
1219 } 940 }
1220 941
1221 void Pipeline::OnAudioUnderflow() { 942 void Pipeline::OnAudioUnderflow() {
1222 if (!message_loop_->BelongsToCurrentThread()) { 943 if (!message_loop_->BelongsToCurrentThread()) {
1223 message_loop_->PostTask(FROM_HERE, base::Bind( 944 message_loop_->PostTask(FROM_HERE, base::Bind(
1224 &Pipeline::OnAudioUnderflow, this)); 945 &Pipeline::OnAudioUnderflow, this));
1225 return; 946 return;
1226 } 947 }
1227 948
1228 if (state_ != kStarted) 949 if (state_ != kStarted)
1229 return; 950 return;
1230 951
1231 if (audio_renderer_) 952 if (audio_renderer_)
1232 audio_renderer_->ResumeAfterUnderflow(true); 953 audio_renderer_->ResumeAfterUnderflow(true);
1233 } 954 }
1234 955
1235 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { 956 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
1236 lock_.AssertAcquired(); 957 lock_.AssertAcquired();
1237 if (!waiting_for_clock_update_) 958 if (!waiting_for_clock_update_)
1238 return; 959 return;
1239 960
1240 waiting_for_clock_update_ = false; 961 waiting_for_clock_update_ = false;
1241 clock_->Play(); 962 clock_->Play();
1242 } 963 }
1243 964
1244 } // namespace media 965 } // namespace media
OLDNEW
« no previous file with comments | « media/base/pipeline.h ('k') | media/base/pipeline_status.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698