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 3 // found in the LICENSE file. 4 5 #include "media/base/pipeline.h" 6 7 #include <algorithm> 8 9 #include "base/bind.h" 10 #include "base/callback.h" 11 #include "base/callback_helpers.h" 12 #include "base/compiler_specific.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/metrics/histogram.h" 15 #include "base/stl_util.h" 16 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_util.h" 18 #include "base/synchronization/condition_variable.h" 19 #include "media/base/audio_decoder.h" 20 #include "media/base/audio_renderer.h" 21 #include "media/base/clock.h" 22 #include "media/base/filter_collection.h" 23 #include "media/base/media_log.h" 24 #include "media/base/text_renderer.h" 25 #include "media/base/text_track_config.h" 26 #include "media/base/video_decoder.h" 27 #include "media/base/video_decoder_config.h" 28 #include "media/base/video_renderer.h" 29 30 using base::TimeDelta; 31 32 namespace media { 33 34 Pipeline::Pipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, 35 MediaLog* media_log) 36 : message_loop_(message_loop), 37 media_log_(media_log), 38 running_(false), 39 did_loading_progress_(false), 40 total_bytes_(0), 41 natural_size_(0, 0), 42 volume_(1.0f), 43 playback_rate_(0.0f), 44 clock_(new Clock(&default_tick_clock_)), 45 waiting_for_clock_update_(false), 46 status_(PIPELINE_OK), 47 has_audio_(false), 48 has_video_(false), 49 state_(kCreated), 50 audio_ended_(false), 51 video_ended_(false), 52 text_ended_(false), 53 audio_disabled_(false), 54 demuxer_(NULL), 55 creation_time_(default_tick_clock_.NowTicks()) { 56 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); 57 media_log_->AddEvent( 58 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); 59 } 60 61 Pipeline::~Pipeline() { 62 DCHECK(thread_checker_.CalledOnValidThread()) 63 << "Pipeline must be destroyed on same thread that created it"; 64 DCHECK(!running_) << "Stop() must complete before destroying object"; 65 DCHECK(stop_cb_.is_null()); 66 DCHECK(seek_cb_.is_null()); 67 68 media_log_->AddEvent( 69 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); 70 } 71 72 void Pipeline::Start(scoped_ptr<FilterCollection> collection, 73 const base::Closure& ended_cb, 74 const PipelineStatusCB& error_cb, 75 const PipelineStatusCB& seek_cb, 76 const BufferingStateCB& buffering_state_cb, 77 const base::Closure& duration_change_cb) { 78 base::AutoLock auto_lock(lock_); 79 CHECK(!running_) << "Media pipeline is already running"; 80 DCHECK(!buffering_state_cb.is_null()); 81 82 running_ = true; 83 message_loop_->PostTask(FROM_HERE, base::Bind( 84 &Pipeline::StartTask, base::Unretained(this), base::Passed(&collection), 85 ended_cb, error_cb, seek_cb, buffering_state_cb, duration_change_cb)); 86 } 87 88 void Pipeline::Stop(const base::Closure& stop_cb) { 89 base::AutoLock auto_lock(lock_); 90 message_loop_->PostTask(FROM_HERE, base::Bind( 91 &Pipeline::StopTask, base::Unretained(this), stop_cb)); 92 } 93 94 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { 95 base::AutoLock auto_lock(lock_); 96 if (!running_) { 97 NOTREACHED() << "Media pipeline isn't running"; 98 return; 99 } 100 101 message_loop_->PostTask(FROM_HERE, base::Bind( 102 &Pipeline::SeekTask, base::Unretained(this), time, seek_cb)); 103 } 104 105 bool Pipeline::IsRunning() const { 106 base::AutoLock auto_lock(lock_); 107 return running_; 108 } 109 110 bool Pipeline::HasAudio() const { 111 base::AutoLock auto_lock(lock_); 112 return has_audio_; 113 } 114 115 bool Pipeline::HasVideo() const { 116 base::AutoLock auto_lock(lock_); 117 return has_video_; 118 } 119 120 float Pipeline::GetPlaybackRate() const { 121 base::AutoLock auto_lock(lock_); 122 return playback_rate_; 123 } 124 125 void Pipeline::SetPlaybackRate(float playback_rate) { 126 if (playback_rate < 0.0f) 127 return; 128 129 base::AutoLock auto_lock(lock_); 130 playback_rate_ = playback_rate; 131 if (running_) { 132 message_loop_->PostTask(FROM_HERE, base::Bind( 133 &Pipeline::PlaybackRateChangedTask, base::Unretained(this), 134 playback_rate)); 135 } 136 } 137 138 float Pipeline::GetVolume() const { 139 base::AutoLock auto_lock(lock_); 140 return volume_; 141 } 142 143 void Pipeline::SetVolume(float volume) { 144 if (volume < 0.0f || volume > 1.0f) 145 return; 146 147 base::AutoLock auto_lock(lock_); 148 volume_ = volume; 149 if (running_) { 150 message_loop_->PostTask(FROM_HERE, base::Bind( 151 &Pipeline::VolumeChangedTask, base::Unretained(this), volume)); 152 } 153 } 154 155 TimeDelta Pipeline::GetMediaTime() const { 156 base::AutoLock auto_lock(lock_); 157 return clock_->Elapsed(); 158 } 159 160 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() { 161 base::AutoLock auto_lock(lock_); 162 Ranges<TimeDelta> time_ranges; 163 for (size_t i = 0; i < buffered_time_ranges_.size(); ++i) { 164 time_ranges.Add(buffered_time_ranges_.start(i), 165 buffered_time_ranges_.end(i)); 166 } 167 if (clock_->Duration() == TimeDelta() || total_bytes_ == 0) 168 return time_ranges; 169 for (size_t i = 0; i < buffered_byte_ranges_.size(); ++i) { 170 TimeDelta start = TimeForByteOffset_Locked(buffered_byte_ranges_.start(i)); 171 TimeDelta end = TimeForByteOffset_Locked(buffered_byte_ranges_.end(i)); 172 // Cap approximated buffered time at the length of the video. 173 end = std::min(end, clock_->Duration()); 174 time_ranges.Add(start, end); 175 } 176 177 return time_ranges; 178 } 179 180 TimeDelta Pipeline::GetMediaDuration() const { 181 base::AutoLock auto_lock(lock_); 182 return clock_->Duration(); 183 } 184 185 int64 Pipeline::GetTotalBytes() const { 186 base::AutoLock auto_lock(lock_); 187 return total_bytes_; 188 } 189 190 void Pipeline::GetNaturalVideoSize(gfx::Size* out_size) const { 191 CHECK(out_size); 192 base::AutoLock auto_lock(lock_); 193 *out_size = natural_size_; 194 } 195 196 bool Pipeline::DidLoadingProgress() const { 197 base::AutoLock auto_lock(lock_); 198 bool ret = did_loading_progress_; 199 did_loading_progress_ = false; 200 return ret; 201 } 202 203 PipelineStatistics Pipeline::GetStatistics() const { 204 base::AutoLock auto_lock(lock_); 205 return statistics_; 206 } 207 208 void Pipeline::SetClockForTesting(Clock* clock) { 209 clock_.reset(clock); 210 } 211 212 void Pipeline::SetErrorForTesting(PipelineStatus status) { 213 SetError(status); 214 } 215 216 void Pipeline::SetState(State next_state) { 217 if (state_ != kStarted && next_state == kStarted && 218 !creation_time_.is_null()) { 219 UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted", 220 default_tick_clock_.NowTicks() - creation_time_); 221 creation_time_ = base::TimeTicks(); 222 } 223 224 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state); 225 226 state_ = next_state; 227 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); 228 } 229 230 #define RETURN_STRING(state) case state: return #state; 231 232 const char* Pipeline::GetStateString(State state) { 233 switch (state) { 234 RETURN_STRING(kCreated); 235 RETURN_STRING(kInitDemuxer); 236 RETURN_STRING(kInitAudioRenderer); 237 RETURN_STRING(kInitVideoRenderer); 238 RETURN_STRING(kInitPrerolling); 239 RETURN_STRING(kSeeking); 240 RETURN_STRING(kStarting); 241 RETURN_STRING(kStarted); 242 RETURN_STRING(kStopping); 243 RETURN_STRING(kStopped); 244 } 245 NOTREACHED(); 246 return "INVALID"; 247 } 248 249 #undef RETURN_STRING 250 251 Pipeline::State Pipeline::GetNextState() const { 252 DCHECK(message_loop_->BelongsToCurrentThread()); 253 DCHECK(stop_cb_.is_null()) 254 << "State transitions don't happen when stopping"; 255 DCHECK_EQ(status_, PIPELINE_OK) 256 << "State transitions don't happen when there's an error: " << status_; 257 258 switch (state_) { 259 case kCreated: 260 return kInitDemuxer; 261 262 case kInitDemuxer: 263 if (demuxer_->GetStream(DemuxerStream::AUDIO)) 264 return kInitAudioRenderer; 265 if (demuxer_->GetStream(DemuxerStream::VIDEO)) 266 return kInitVideoRenderer; 267 return kInitPrerolling; 268 269 case kInitAudioRenderer: 270 if (demuxer_->GetStream(DemuxerStream::VIDEO)) 271 return kInitVideoRenderer; 272 return kInitPrerolling; 273 274 case kInitVideoRenderer: 275 return kInitPrerolling; 276 277 case kInitPrerolling: 278 return kStarting; 279 280 case kSeeking: 281 return kStarting; 282 283 case kStarting: 284 return kStarted; 285 286 case kStarted: 287 case kStopping: 288 case kStopped: 289 break; 290 } 291 NOTREACHED() << "State has no transition: " << state_; 292 return state_; 293 } 294 295 void Pipeline::OnDemuxerError(PipelineStatus error) { 296 SetError(error); 297 } 298 299 void Pipeline::AddTextStream(DemuxerStream* text_stream, 300 const TextTrackConfig& config) { 301 message_loop_->PostTask(FROM_HERE, base::Bind( 302 &Pipeline::AddTextStreamTask, base::Unretained(this), 303 text_stream, config)); 304 } 305 306 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) { 307 message_loop_->PostTask(FROM_HERE, base::Bind( 308 &Pipeline::RemoveTextStreamTask, base::Unretained(this), 309 text_stream)); 310 } 311 312 void Pipeline::SetError(PipelineStatus error) { 313 DCHECK(IsRunning()); 314 DCHECK_NE(PIPELINE_OK, error); 315 VLOG(1) << "Media pipeline error: " << error; 316 317 message_loop_->PostTask(FROM_HERE, base::Bind( 318 &Pipeline::ErrorChangedTask, base::Unretained(this), error)); 319 320 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error)); 321 } 322 323 void Pipeline::OnAudioDisabled() { 324 DCHECK(IsRunning()); 325 message_loop_->PostTask(FROM_HERE, base::Bind( 326 &Pipeline::AudioDisabledTask, base::Unretained(this))); 327 media_log_->AddEvent( 328 media_log_->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED)); 329 } 330 331 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) { 332 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds()); 333 DCHECK(IsRunning()); 334 base::AutoLock auto_lock(lock_); 335 336 if (!has_audio_) 337 return; 338 if (waiting_for_clock_update_ && time < clock_->Elapsed()) 339 return; 340 341 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see 342 // http://crbug.com/137973 343 if (state_ == kSeeking) 344 return; 345 346 clock_->SetTime(time, max_time); 347 StartClockIfWaitingForTimeUpdate_Locked(); 348 } 349 350 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) { 351 DCHECK(IsRunning()); 352 base::AutoLock auto_lock(lock_); 353 354 if (has_audio_) 355 return; 356 357 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see 358 // http://crbug.com/137973 359 if (state_ == kSeeking) 360 return; 361 362 DCHECK(!waiting_for_clock_update_); 363 clock_->SetMaxTime(max_time); 364 } 365 366 void Pipeline::SetDuration(TimeDelta duration) { 367 DCHECK(IsRunning()); 368 media_log_->AddEvent( 369 media_log_->CreateTimeEvent( 370 MediaLogEvent::DURATION_SET, "duration", duration)); 371 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); 372 373 base::AutoLock auto_lock(lock_); 374 clock_->SetDuration(duration); 375 if (!duration_change_cb_.is_null()) 376 duration_change_cb_.Run(); 377 } 378 379 void Pipeline::SetTotalBytes(int64 total_bytes) { 380 DCHECK(IsRunning()); 381 media_log_->AddEvent( 382 media_log_->CreateStringEvent( 383 MediaLogEvent::TOTAL_BYTES_SET, "total_bytes", 384 base::Int64ToString(total_bytes))); 385 int64 total_mbytes = total_bytes >> 20; 386 if (total_mbytes > kint32max) 387 total_mbytes = kint32max; 388 UMA_HISTOGRAM_CUSTOM_COUNTS( 389 "Media.TotalMBytes", static_cast<int32>(total_mbytes), 1, kint32max, 50); 390 391 base::AutoLock auto_lock(lock_); 392 total_bytes_ = total_bytes; 393 } 394 395 TimeDelta Pipeline::TimeForByteOffset_Locked(int64 byte_offset) const { 396 lock_.AssertAcquired(); 397 // Use floating point to avoid potential overflow when using 64 bit integers. 398 double time_offset_in_ms = clock_->Duration().InMilliseconds() * 399 (static_cast<double>(byte_offset) / total_bytes_); 400 TimeDelta time_offset(TimeDelta::FromMilliseconds( 401 static_cast<int64>(time_offset_in_ms))); 402 // Since the byte->time calculation is approximate, fudge the beginning & 403 // ending areas to look better. 404 TimeDelta epsilon = clock_->Duration() / 100; 405 if (time_offset < epsilon) 406 return TimeDelta(); 407 if (time_offset + epsilon > clock_->Duration()) 408 return clock_->Duration(); 409 return time_offset; 410 } 411 412 void Pipeline::OnStateTransition(PipelineStatus status) { 413 // Force post to process state transitions after current execution frame. 414 message_loop_->PostTask(FROM_HERE, base::Bind( 415 &Pipeline::StateTransitionTask, base::Unretained(this), status)); 416 } 417 418 void Pipeline::StateTransitionTask(PipelineStatus status) { 419 DCHECK(message_loop_->BelongsToCurrentThread()); 420 421 // No-op any state transitions if we're stopping. 422 if (state_ == kStopping || state_ == kStopped) 423 return; 424 425 // Preserve existing abnormal status, otherwise update based on the result of 426 // the previous operation. 427 status_ = (status_ != PIPELINE_OK ? status_ : status); 428 429 if (status_ != PIPELINE_OK) { 430 ErrorChangedTask(status_); 431 return; 432 } 433 434 // Guard against accidentally clearing |pending_callbacks_| for states that 435 // use it as well as states that should not be using it. 436 // 437 // TODO(scherkus): Make every state transition use |pending_callbacks_|. 438 DCHECK_EQ(pending_callbacks_.get() != NULL, 439 (state_ == kInitPrerolling || state_ == kStarting || 440 state_ == kSeeking)); 441 pending_callbacks_.reset(); 442 443 PipelineStatusCB done_cb = base::Bind( 444 &Pipeline::OnStateTransition, base::Unretained(this)); 445 446 // Switch states, performing any entrance actions for the new state as well. 447 SetState(GetNextState()); 448 switch (state_) { 449 case kInitDemuxer: 450 return InitializeDemuxer(done_cb); 451 452 case kInitAudioRenderer: 453 return InitializeAudioRenderer(done_cb); 454 455 case kInitVideoRenderer: 456 return InitializeVideoRenderer(done_cb); 457 458 case kInitPrerolling: 459 filter_collection_.reset(); 460 { 461 base::AutoLock l(lock_); 462 // We do not want to start the clock running. We only want to set the 463 // base media time so our timestamp calculations will be correct. 464 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); 465 466 // TODO(scherkus): |has_audio_| should be true no matter what -- 467 // otherwise people with muted/disabled sound cards will make our 468 // default controls look as if every video doesn't contain an audio 469 // track. 470 has_audio_ = audio_renderer_ != NULL && !audio_disabled_; 471 has_video_ = video_renderer_ != NULL; 472 } 473 if (!audio_renderer_ && !video_renderer_) { 474 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER); 475 return; 476 } 477 478 buffering_state_cb_.Run(kHaveMetadata); 479 480 return DoInitialPreroll(done_cb); 481 482 case kStarting: 483 return DoPlay(done_cb); 484 485 case kStarted: 486 { 487 base::AutoLock l(lock_); 488 // We use audio stream to update the clock. So if there is such a 489 // stream, we pause the clock until we receive a valid timestamp. 490 waiting_for_clock_update_ = true; 491 if (!has_audio_) { 492 clock_->SetMaxTime(clock_->Duration()); 493 StartClockIfWaitingForTimeUpdate_Locked(); 494 } 495 } 496 497 DCHECK(!seek_cb_.is_null()); 498 DCHECK_EQ(status_, PIPELINE_OK); 499 500 // Fire canplaythrough immediately after playback begins because of 501 // crbug.com/106480. 502 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed. 503 buffering_state_cb_.Run(kPrerollCompleted); 504 return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); 505 506 case kStopping: 507 case kStopped: 508 case kCreated: 509 case kSeeking: 510 NOTREACHED() << "State has no transition: " << state_; 511 return; 512 } 513 } 514 515 // Note that the usage of base::Unretained() with the audio/video renderers 516 // in the following DoXXX() functions is considered safe as they are owned by 517 // |pending_callbacks_| and share the same lifetime. 518 // 519 // That being said, deleting the renderers while keeping |pending_callbacks_| 520 // running on the media thread would result in crashes. 521 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { 522 DCHECK(message_loop_->BelongsToCurrentThread()); 523 DCHECK(!pending_callbacks_.get()); 524 SerialRunner::Queue bound_fns; 525 526 base::TimeDelta seek_timestamp = demuxer_->GetStartTime(); 527 528 // Preroll renderers. 529 if (audio_renderer_) { 530 bound_fns.Push(base::Bind( 531 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), 532 seek_timestamp)); 533 } 534 535 if (video_renderer_) { 536 bound_fns.Push(base::Bind( 537 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), 538 seek_timestamp)); 539 } 540 541 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 542 } 543 544 void Pipeline::DoSeek( 545 base::TimeDelta seek_timestamp, 546 const PipelineStatusCB& done_cb) { 547 DCHECK(message_loop_->BelongsToCurrentThread()); 548 DCHECK(!pending_callbacks_.get()); 549 SerialRunner::Queue bound_fns; 550 551 // Pause. 552 if (audio_renderer_) { 553 bound_fns.Push(base::Bind( 554 &AudioRenderer::Pause, base::Unretained(audio_renderer_.get()))); 555 } 556 if (video_renderer_) { 557 bound_fns.Push(base::Bind( 558 &VideoRenderer::Pause, base::Unretained(video_renderer_.get()))); 559 } 560 if (text_renderer_) { 561 bound_fns.Push(base::Bind( 562 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); 563 } 564 565 // Flush. 566 if (audio_renderer_) { 567 bound_fns.Push(base::Bind( 568 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); 569 } 570 if (video_renderer_) { 571 bound_fns.Push(base::Bind( 572 &VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); 573 } 574 if (text_renderer_) { 575 bound_fns.Push(base::Bind( 576 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); 577 } 578 579 // Seek demuxer. 580 bound_fns.Push(base::Bind( 581 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); 582 583 // Preroll renderers. 584 if (audio_renderer_) { 585 bound_fns.Push(base::Bind( 586 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), 587 seek_timestamp)); 588 } 589 590 if (video_renderer_) { 591 bound_fns.Push(base::Bind( 592 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), 593 seek_timestamp)); 594 } 595 596 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 597 } 598 599 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { 600 DCHECK(message_loop_->BelongsToCurrentThread()); 601 DCHECK(!pending_callbacks_.get()); 602 SerialRunner::Queue bound_fns; 603 604 PlaybackRateChangedTask(GetPlaybackRate()); 605 VolumeChangedTask(GetVolume()); 606 607 if (audio_renderer_) { 608 bound_fns.Push(base::Bind( 609 &AudioRenderer::Play, base::Unretained(audio_renderer_.get()))); 610 } 611 612 if (video_renderer_) { 613 bound_fns.Push(base::Bind( 614 &VideoRenderer::Play, base::Unretained(video_renderer_.get()))); 615 } 616 617 if (text_renderer_) { 618 bound_fns.Push(base::Bind( 619 &TextRenderer::Play, base::Unretained(text_renderer_.get()))); 620 } 621 622 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 623 } 624 625 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { 626 DCHECK(message_loop_->BelongsToCurrentThread()); 627 DCHECK(!pending_callbacks_.get()); 628 SerialRunner::Queue bound_fns; 629 630 if (demuxer_) { 631 bound_fns.Push(base::Bind( 632 &Demuxer::Stop, base::Unretained(demuxer_))); 633 } 634 635 if (audio_renderer_) { 636 bound_fns.Push(base::Bind( 637 &AudioRenderer::Stop, base::Unretained(audio_renderer_.get()))); 638 } 639 640 if (video_renderer_) { 641 bound_fns.Push(base::Bind( 642 &VideoRenderer::Stop, base::Unretained(video_renderer_.get()))); 643 } 644 645 if (text_renderer_) { 646 bound_fns.Push(base::Bind( 647 &TextRenderer::Stop, base::Unretained(text_renderer_.get()))); 648 } 649 650 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 651 } 652 653 void Pipeline::OnStopCompleted(PipelineStatus status) { 654 DCHECK(message_loop_->BelongsToCurrentThread()); 655 DCHECK_EQ(state_, kStopping); 656 { 657 base::AutoLock l(lock_); 658 running_ = false; 659 } 660 661 SetState(kStopped); 662 pending_callbacks_.reset(); 663 filter_collection_.reset(); 664 audio_renderer_.reset(); 665 video_renderer_.reset(); 666 text_renderer_.reset(); 667 demuxer_ = NULL; 668 669 // If we stop during initialization/seeking we want to run |seek_cb_| 670 // followed by |stop_cb_| so we don't leave outstanding callbacks around. 671 if (!seek_cb_.is_null()) { 672 base::ResetAndReturn(&seek_cb_).Run(status_); 673 error_cb_.Reset(); 674 } 675 if (!stop_cb_.is_null()) { 676 error_cb_.Reset(); 677 base::ResetAndReturn(&stop_cb_).Run(); 678 679 // NOTE: pipeline may be deleted at this point in time as a result of 680 // executing |stop_cb_|. 681 return; 682 } 683 if (!error_cb_.is_null()) { 684 DCHECK_NE(status_, PIPELINE_OK); 685 base::ResetAndReturn(&error_cb_).Run(status_); 686 } 687 } 688 689 void Pipeline::AddBufferedByteRange(int64 start, int64 end) { 690 DCHECK(IsRunning()); 691 base::AutoLock auto_lock(lock_); 692 buffered_byte_ranges_.Add(start, end); 693 did_loading_progress_ = true; 694 } 695 696 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, 697 base::TimeDelta end) { 698 DCHECK(IsRunning()); 699 base::AutoLock auto_lock(lock_); 700 buffered_time_ranges_.Add(start, end); 701 did_loading_progress_ = true; 702 } 703 704 void Pipeline::OnNaturalVideoSizeChanged(const gfx::Size& size) { 705 DCHECK(IsRunning()); 706 media_log_->AddEvent(media_log_->CreateVideoSizeSetEvent( 707 size.width(), size.height())); 708 709 base::AutoLock auto_lock(lock_); 710 natural_size_ = size; 711 } 712 713 void Pipeline::OnAudioRendererEnded() { 714 // Force post to process ended messages after current execution frame. 715 message_loop_->PostTask(FROM_HERE, base::Bind( 716 &Pipeline::DoAudioRendererEnded, base::Unretained(this))); 717 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED)); 718 } 719 720 void Pipeline::OnVideoRendererEnded() { 721 // Force post to process ended messages after current execution frame. 722 message_loop_->PostTask(FROM_HERE, base::Bind( 723 &Pipeline::DoVideoRendererEnded, base::Unretained(this))); 724 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED)); 725 } 726 727 void Pipeline::OnTextRendererEnded() { 728 // Force post to process ended messages after current execution frame. 729 message_loop_->PostTask(FROM_HERE, base::Bind( 730 &Pipeline::DoTextRendererEnded, base::Unretained(this))); 731 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); 732 } 733 734 // Called from any thread. 735 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { 736 base::AutoLock auto_lock(lock_); 737 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; 738 statistics_.video_bytes_decoded += stats.video_bytes_decoded; 739 statistics_.video_frames_decoded += stats.video_frames_decoded; 740 statistics_.video_frames_dropped += stats.video_frames_dropped; 741 } 742 743 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, 744 const base::Closure& ended_cb, 745 const PipelineStatusCB& error_cb, 746 const PipelineStatusCB& seek_cb, 747 const BufferingStateCB& buffering_state_cb, 748 const base::Closure& duration_change_cb) { 749 DCHECK(message_loop_->BelongsToCurrentThread()); 750 CHECK_EQ(kCreated, state_) 751 << "Media pipeline cannot be started more than once"; 752 753 filter_collection_ = filter_collection.Pass(); 754 ended_cb_ = ended_cb; 755 error_cb_ = error_cb; 756 seek_cb_ = seek_cb; 757 buffering_state_cb_ = buffering_state_cb; 758 duration_change_cb_ = duration_change_cb; 759 760 text_renderer_ = filter_collection_->GetTextRenderer(); 761 762 if (text_renderer_) { 763 text_renderer_->Initialize( 764 base::Bind(&Pipeline::OnTextRendererEnded, base::Unretained(this))); 765 } 766 767 StateTransitionTask(PIPELINE_OK); 768 } 769 770 void Pipeline::StopTask(const base::Closure& stop_cb) { 771 DCHECK(message_loop_->BelongsToCurrentThread()); 772 DCHECK(stop_cb_.is_null()); 773 774 if (state_ == kStopped) { 775 stop_cb.Run(); 776 return; 777 } 778 779 stop_cb_ = stop_cb; 780 781 // We may already be stopping due to a runtime error. 782 if (state_ == kStopping) 783 return; 784 785 SetState(kStopping); 786 pending_callbacks_.reset(); 787 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this))); 788 } 789 790 void Pipeline::ErrorChangedTask(PipelineStatus error) { 791 DCHECK(message_loop_->BelongsToCurrentThread()); 792 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; 793 794 if (state_ == kStopping || state_ == kStopped) 795 return; 796 797 SetState(kStopping); 798 pending_callbacks_.reset(); 799 status_ = error; 800 801 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this))); 802 } 803 804 void Pipeline::PlaybackRateChangedTask(float playback_rate) { 805 DCHECK(message_loop_->BelongsToCurrentThread()); 806 807 // Playback rate changes are only carried out while playing. 808 if (state_ != kStarting && state_ != kStarted) 809 return; 810 811 { 812 base::AutoLock auto_lock(lock_); 813 clock_->SetPlaybackRate(playback_rate); 814 } 815 816 if (audio_renderer_) 817 audio_renderer_->SetPlaybackRate(playback_rate_); 818 if (video_renderer_) 819 video_renderer_->SetPlaybackRate(playback_rate_); 820 } 821 822 void Pipeline::VolumeChangedTask(float volume) { 823 DCHECK(message_loop_->BelongsToCurrentThread()); 824 825 // Volume changes are only carried out while playing. 826 if (state_ != kStarting && state_ != kStarted) 827 return; 828 829 if (audio_renderer_) 830 audio_renderer_->SetVolume(volume); 831 } 832 833 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { 834 DCHECK(message_loop_->BelongsToCurrentThread()); 835 DCHECK(stop_cb_.is_null()); 836 837 // Suppress seeking if we're not fully started. 838 if (state_ != kStarted) { 839 DCHECK(state_ == kStopping || state_ == kStopped) 840 << "Receive extra seek in unexpected state: " << state_; 841 842 // TODO(scherkus): should we run the callback? I'm tempted to say the API 843 // will only execute the first Seek() request. 844 DVLOG(1) << "Media pipeline has not started, ignoring seek to " 845 << time.InMicroseconds() << " (current state: " << state_ << ")"; 846 return; 847 } 848 849 DCHECK(seek_cb_.is_null()); 850 851 SetState(kSeeking); 852 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); 853 seek_cb_ = seek_cb; 854 audio_ended_ = false; 855 video_ended_ = false; 856 text_ended_ = false; 857 858 // Kick off seeking! 859 { 860 base::AutoLock auto_lock(lock_); 861 if (clock_->IsPlaying()) 862 clock_->Pause(); 863 clock_->SetTime(seek_timestamp, seek_timestamp); 864 } 865 DoSeek(seek_timestamp, base::Bind( 866 &Pipeline::OnStateTransition, base::Unretained(this))); 867 } 868 869 void Pipeline::DoAudioRendererEnded() { 870 DCHECK(message_loop_->BelongsToCurrentThread()); 871 872 if (state_ != kStarted) 873 return; 874 875 DCHECK(!audio_ended_); 876 audio_ended_ = true; 877 878 // Start clock since there is no more audio to trigger clock updates. 879 if (!audio_disabled_) { 880 base::AutoLock auto_lock(lock_); 881 clock_->SetMaxTime(clock_->Duration()); 882 StartClockIfWaitingForTimeUpdate_Locked(); 883 } 884 885 RunEndedCallbackIfNeeded(); 886 } 887 888 void Pipeline::DoVideoRendererEnded() { 889 DCHECK(message_loop_->BelongsToCurrentThread()); 890 891 if (state_ != kStarted) 892 return; 893 894 DCHECK(!video_ended_); 895 video_ended_ = true; 896 897 RunEndedCallbackIfNeeded(); 898 } 899 900 void Pipeline::DoTextRendererEnded() { 901 DCHECK(message_loop_->BelongsToCurrentThread()); 902 903 if (state_ != kStarted) 904 return; 905 906 DCHECK(!text_ended_); 907 text_ended_ = true; 908 909 RunEndedCallbackIfNeeded(); 910 } 911 912 void Pipeline::RunEndedCallbackIfNeeded() { 913 DCHECK(message_loop_->BelongsToCurrentThread()); 914 915 if (audio_renderer_ && !audio_ended_ && !audio_disabled_) 916 return; 917 918 if (video_renderer_ && !video_ended_) 919 return; 920 921 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_) 922 return; 923 924 { 925 base::AutoLock auto_lock(lock_); 926 clock_->EndOfStream(); 927 } 928 929 DCHECK_EQ(status_, PIPELINE_OK); 930 ended_cb_.Run(); 931 } 932 933 void Pipeline::AudioDisabledTask() { 934 DCHECK(message_loop_->BelongsToCurrentThread()); 935 936 base::AutoLock auto_lock(lock_); 937 has_audio_ = false; 938 audio_disabled_ = true; 939 940 // Notify our demuxer that we're no longer rendering audio. 941 demuxer_->OnAudioRendererDisabled(); 942 943 // Start clock since there is no more audio to trigger clock updates. 944 clock_->SetMaxTime(clock_->Duration()); 945 StartClockIfWaitingForTimeUpdate_Locked(); 946 } 947 948 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream, 949 const TextTrackConfig& config) { 950 DCHECK(message_loop_->BelongsToCurrentThread()); 951 // TODO(matthewjheaney): fix up text_ended_ when text stream 952 // is added (http://crbug.com/321446). 953 text_renderer_->AddTextStream(text_stream, config); 954 } 955 956 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) { 957 DCHECK(message_loop_->BelongsToCurrentThread()); 958 text_renderer_->RemoveTextStream(text_stream); 959 } 960 961 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) { 962 DCHECK(message_loop_->BelongsToCurrentThread()); 963 964 demuxer_ = filter_collection_->GetDemuxer(); 965 demuxer_->Initialize(this, done_cb, text_renderer_); 966 } 967 968 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { 969 DCHECK(message_loop_->BelongsToCurrentThread()); 970 971 audio_renderer_ = filter_collection_->GetAudioRenderer(); 972 audio_renderer_->Initialize( 973 demuxer_->GetStream(DemuxerStream::AUDIO), 974 done_cb, 975 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), 976 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)), 977 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), 978 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), 979 base::Bind(&Pipeline::OnAudioDisabled, base::Unretained(this)), 980 base::Bind(&Pipeline::SetError, base::Unretained(this))); 981 } 982 983 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { 984 DCHECK(message_loop_->BelongsToCurrentThread()); 985 986 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); 987 988 { 989 // Get an initial natural size so we have something when we signal 990 // the kHaveMetadata buffering state. 991 base::AutoLock l(lock_); 992 natural_size_ = stream->video_decoder_config().natural_size(); 993 } 994 995 video_renderer_ = filter_collection_->GetVideoRenderer(); 996 video_renderer_->Initialize( 997 stream, 998 done_cb, 999 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), 1000 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)), 1001 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, base::Unretained(this)), 1002 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)), 1003 base::Bind(&Pipeline::SetError, base::Unretained(this)), 1004 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), 1005 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); 1006 } 1007 1008 void Pipeline::OnAudioUnderflow() { 1009 if (!message_loop_->BelongsToCurrentThread()) { 1010 message_loop_->PostTask(FROM_HERE, base::Bind( 1011 &Pipeline::OnAudioUnderflow, base::Unretained(this))); 1012 return; 1013 } 1014 1015 if (state_ != kStarted) 1016 return; 1017 1018 if (audio_renderer_) 1019 audio_renderer_->ResumeAfterUnderflow(); 1020 } 1021 1022 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { 1023 lock_.AssertAcquired(); 1024 if (!waiting_for_clock_update_) 1025 return; 1026 1027 waiting_for_clock_update_ = false; 1028 clock_->Play(); 1029 } 1030 1031 } // namespace media 1032