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