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 |