Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/renderers/renderer_impl.h" | 5 #include "media/renderers/renderer_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 10 #include "base/command_line.h" | |
| 10 #include "base/compiler_specific.h" | 11 #include "base/compiler_specific.h" |
| 11 #include "base/location.h" | 12 #include "base/location.h" |
| 12 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" |
| 14 #include "base/strings/string_number_conversions.h" | |
| 13 #include "media/base/audio_renderer.h" | 15 #include "media/base/audio_renderer.h" |
| 14 #include "media/base/bind_to_current_loop.h" | 16 #include "media/base/bind_to_current_loop.h" |
| 15 #include "media/base/demuxer_stream_provider.h" | 17 #include "media/base/demuxer_stream_provider.h" |
| 18 #include "media/base/media_switches.h" | |
| 16 #include "media/base/time_source.h" | 19 #include "media/base/time_source.h" |
| 17 #include "media/base/video_renderer.h" | 20 #include "media/base/video_renderer.h" |
| 18 #include "media/base/wall_clock_time_source.h" | 21 #include "media/base/wall_clock_time_source.h" |
| 19 | 22 |
| 20 namespace media { | 23 namespace media { |
| 21 | 24 |
| 22 RendererImpl::RendererImpl( | 25 RendererImpl::RendererImpl( |
| 23 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 26 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 24 scoped_ptr<AudioRenderer> audio_renderer, | 27 scoped_ptr<AudioRenderer> audio_renderer, |
| 25 scoped_ptr<VideoRenderer> video_renderer) | 28 scoped_ptr<VideoRenderer> video_renderer) |
| 26 : state_(STATE_UNINITIALIZED), | 29 : state_(STATE_UNINITIALIZED), |
| 27 task_runner_(task_runner), | 30 task_runner_(task_runner), |
| 28 audio_renderer_(audio_renderer.Pass()), | 31 audio_renderer_(audio_renderer.Pass()), |
| 29 video_renderer_(video_renderer.Pass()), | 32 video_renderer_(video_renderer.Pass()), |
| 30 time_source_(NULL), | 33 time_source_(NULL), |
| 31 time_ticking_(false), | 34 time_ticking_(false), |
| 32 audio_buffering_state_(BUFFERING_HAVE_NOTHING), | 35 audio_buffering_state_(BUFFERING_HAVE_NOTHING), |
| 33 video_buffering_state_(BUFFERING_HAVE_NOTHING), | 36 video_buffering_state_(BUFFERING_HAVE_NOTHING), |
| 34 audio_ended_(false), | 37 audio_ended_(false), |
| 35 video_ended_(false), | 38 video_ended_(false), |
| 36 cdm_context_(nullptr), | 39 cdm_context_(nullptr), |
| 37 underflow_disabled_for_testing_(false), | 40 underflow_disabled_for_testing_(false), |
| 38 clockless_video_playback_enabled_for_testing_(false), | 41 clockless_video_playback_enabled_for_testing_(false), |
| 42 video_underflow_threshold_(base::TimeDelta::FromSeconds(3)), | |
|
xhwang
2015/03/26 21:24:43
You can still use a file scope const instead of a
DaleCurtis
2015/03/26 21:36:08
This allows overriding for testing much more easil
xhwang
2015/03/28 00:32:40
You can declare this in the header file so it's av
| |
| 39 weak_factory_(this) { | 43 weak_factory_(this) { |
| 40 weak_this_ = weak_factory_.GetWeakPtr(); | 44 weak_this_ = weak_factory_.GetWeakPtr(); |
| 41 DVLOG(1) << __FUNCTION__; | 45 DVLOG(1) << __FUNCTION__; |
| 46 | |
| 47 // TODO(dalecurtis): Remove once experiments for http://crbug.com/470940 are | |
| 48 // complete. | |
| 49 int threshold_ms = 0; | |
| 50 std::string threshold_ms_str( | |
| 51 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 52 switches::kVideoUnderflowThresholdMs)); | |
| 53 if (base::StringToInt(threshold_ms_str, &threshold_ms) && threshold_ms > 0) { | |
|
xhwang
2015/03/26 21:24:43
s/>/>=?
We should be able to set the threshold to
| |
| 54 video_underflow_threshold_ = | |
| 55 base::TimeDelta::FromMilliseconds(threshold_ms); | |
| 56 } | |
| 42 } | 57 } |
| 43 | 58 |
| 44 RendererImpl::~RendererImpl() { | 59 RendererImpl::~RendererImpl() { |
| 45 DVLOG(1) << __FUNCTION__; | 60 DVLOG(1) << __FUNCTION__; |
| 46 DCHECK(task_runner_->BelongsToCurrentThread()); | 61 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 47 | 62 |
| 48 // Tear down in opposite order of construction as |video_renderer_| can still | 63 // Tear down in opposite order of construction as |video_renderer_| can still |
| 49 // need |time_source_| (which can be |audio_renderer_|) to be alive. | 64 // need |time_source_| (which can be |audio_renderer_|) to be alive. |
| 50 video_renderer_.reset(); | 65 video_renderer_.reset(); |
| 51 audio_renderer_.reset(); | 66 audio_renderer_.reset(); |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 329 | 344 |
| 330 DCHECK(!init_cb_.is_null()); | 345 DCHECK(!init_cb_.is_null()); |
| 331 | 346 |
| 332 if (status != PIPELINE_OK) { | 347 if (status != PIPELINE_OK) { |
| 333 base::ResetAndReturn(&init_cb_).Run(status); | 348 base::ResetAndReturn(&init_cb_).Run(status); |
| 334 return; | 349 return; |
| 335 } | 350 } |
| 336 | 351 |
| 337 if (audio_renderer_) { | 352 if (audio_renderer_) { |
| 338 time_source_ = audio_renderer_->GetTimeSource(); | 353 time_source_ = audio_renderer_->GetTimeSource(); |
| 339 } else { | 354 } else if (!time_source_) { |
|
xhwang
2015/03/26 21:24:43
hmm, why we need this?
DaleCurtis
2015/03/26 21:36:08
Because I'm overriding the TimeSource for testing,
xhwang
2015/03/28 00:32:40
Thanks for the explanation. Maybe worth a comment.
| |
| 340 wall_clock_time_source_.reset(new WallClockTimeSource()); | 355 wall_clock_time_source_.reset(new WallClockTimeSource()); |
| 341 time_source_ = wall_clock_time_source_.get(); | 356 time_source_ = wall_clock_time_source_.get(); |
| 342 } | 357 } |
| 343 | 358 |
| 344 state_ = STATE_PLAYING; | 359 state_ = STATE_PLAYING; |
| 345 DCHECK(time_source_); | 360 DCHECK(time_source_); |
| 346 DCHECK(audio_renderer_ || video_renderer_); | 361 DCHECK(audio_renderer_ || video_renderer_); |
| 347 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | 362 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); |
| 348 } | 363 } |
| 349 | 364 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 412 base::ResetAndReturn(&flush_cb_).Run(); | 427 base::ResetAndReturn(&flush_cb_).Run(); |
| 413 } | 428 } |
| 414 | 429 |
| 415 void RendererImpl::OnUpdateStatistics(const PipelineStatistics& stats) { | 430 void RendererImpl::OnUpdateStatistics(const PipelineStatistics& stats) { |
| 416 DCHECK(task_runner_->BelongsToCurrentThread()); | 431 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 417 statistics_cb_.Run(stats); | 432 statistics_cb_.Run(stats); |
| 418 } | 433 } |
| 419 | 434 |
| 420 void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state, | 435 void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state, |
| 421 BufferingState new_buffering_state) { | 436 BufferingState new_buffering_state) { |
| 437 const bool is_audio = buffering_state == &audio_buffering_state_; | |
| 422 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " | 438 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " |
| 423 << new_buffering_state << ") " | 439 << new_buffering_state << ") " << (is_audio ? "audio" : "video"); |
| 424 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); | |
| 425 DCHECK(task_runner_->BelongsToCurrentThread()); | 440 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 441 | |
| 426 bool was_waiting_for_enough_data = WaitingForEnoughData(); | 442 bool was_waiting_for_enough_data = WaitingForEnoughData(); |
| 427 | 443 |
| 444 // When audio is present, defer underflow callbacks for some time to avoid | |
| 445 // unnecessary glitches in audio; see http://crbug.com/144683#c53. | |
| 446 if (audio_renderer_ && !is_audio && state_ == STATE_PLAYING) { | |
| 447 if (video_buffering_state_ == BUFFERING_HAVE_ENOUGH && | |
| 448 new_buffering_state == BUFFERING_HAVE_NOTHING && | |
| 449 deferred_underflow_cb_.IsCancelled()) { | |
| 450 deferred_underflow_cb_.Reset(base::Bind( | |
| 451 &RendererImpl::OnBufferingStateChanged, weak_factory_.GetWeakPtr(), | |
| 452 buffering_state, new_buffering_state)); | |
| 453 task_runner_->PostDelayedTask(FROM_HERE, | |
| 454 deferred_underflow_cb_.callback(), | |
| 455 video_underflow_threshold_); | |
| 456 return; | |
| 457 } | |
| 458 | |
| 459 deferred_underflow_cb_.Cancel(); | |
| 460 } else if (!deferred_underflow_cb_.IsCancelled() && is_audio && | |
| 461 new_buffering_state == BUFFERING_HAVE_NOTHING) { | |
| 462 // If audio underflows while we have a deferred video underflow in progress | |
| 463 // we want to mark video as underflowed immediately and cancel the deferral. | |
| 464 deferred_underflow_cb_.Cancel(); | |
| 465 video_buffering_state_ = BUFFERING_HAVE_NOTHING; | |
| 466 } | |
| 467 | |
| 428 *buffering_state = new_buffering_state; | 468 *buffering_state = new_buffering_state; |
| 429 | 469 |
| 430 // Disable underflow by ignoring updates that renderers have ran out of data. | 470 // Disable underflow by ignoring updates that renderers have ran out of data. |
| 431 if (state_ == STATE_PLAYING && underflow_disabled_for_testing_ && | 471 if (state_ == STATE_PLAYING && underflow_disabled_for_testing_ && |
| 432 time_ticking_) { | 472 time_ticking_) { |
| 433 DVLOG(1) << "Update ignored because underflow is disabled for testing."; | 473 DVLOG(1) << "Update ignored because underflow is disabled for testing."; |
| 434 return; | 474 return; |
| 435 } | 475 } |
| 436 | 476 |
| 437 // Renderer underflowed. | 477 // Renderer underflowed. |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 571 } | 611 } |
| 572 | 612 |
| 573 // After OnError() returns, the pipeline may destroy |this|. | 613 // After OnError() returns, the pipeline may destroy |this|. |
| 574 base::ResetAndReturn(&error_cb_).Run(error); | 614 base::ResetAndReturn(&error_cb_).Run(error); |
| 575 | 615 |
| 576 if (!flush_cb_.is_null()) | 616 if (!flush_cb_.is_null()) |
| 577 base::ResetAndReturn(&flush_cb_).Run(); | 617 base::ResetAndReturn(&flush_cb_).Run(); |
| 578 } | 618 } |
| 579 | 619 |
| 580 } // namespace media | 620 } // namespace media |
| OLD | NEW |