Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chromecast / media / cma / base / buffering_controller.cc
blob8137d9ce3e22bb60b8fb0a576d0ef5791dea7dfa
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
3 // found in the LICENSE file.
5 #include "chromecast/media/cma/base/buffering_controller.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/single_thread_task_runner.h"
10 #include "chromecast/base/metrics/cast_metrics_helper.h"
11 #include "chromecast/media/cma/base/buffering_state.h"
12 #include "chromecast/media/cma/base/cma_logging.h"
13 #include "media/base/timestamp_constants.h"
15 namespace chromecast {
16 namespace media {
18 BufferingController::BufferingController(
19 const scoped_refptr<BufferingConfig>& config,
20 const BufferingNotificationCB& buffering_notification_cb)
21 : config_(config),
22 buffering_notification_cb_(buffering_notification_cb),
23 is_buffering_(false),
24 begin_buffering_time_(base::Time()),
25 initial_buffering_(true),
26 weak_factory_(this) {
27 weak_this_ = weak_factory_.GetWeakPtr();
28 thread_checker_.DetachFromThread();
29 CMALOG(kLogControl) << __FUNCTION__
30 << " High threshold: "
31 << config_->high_level().InMilliseconds()
32 << "ms Low threshold: "
33 << config_->low_level().InMilliseconds() << "ms";
36 BufferingController::~BufferingController() {
37 // Some weak pointers might possibly be invalidated here.
38 DCHECK(thread_checker_.CalledOnValidThread());
41 void BufferingController::UpdateHighLevelThreshold(
42 base::TimeDelta high_level_threshold) {
43 // Can only decrease the high level threshold.
44 if (high_level_threshold > config_->high_level())
45 return;
46 CMALOG(kLogControl) << "High buffer threshold: "
47 << high_level_threshold.InMilliseconds() << "ms";
48 config_->set_high_level(high_level_threshold);
50 // Make sure the low level threshold is somewhat consistent.
51 // Currently, we set it to one third of the high level threshold:
52 // this value could be adjusted in the future.
53 base::TimeDelta low_level_threshold = high_level_threshold / 3;
54 if (low_level_threshold <= config_->low_level()) {
55 CMALOG(kLogControl) << "Low buffer threshold: "
56 << low_level_threshold.InMilliseconds() << "ms";
57 config_->set_low_level(low_level_threshold);
60 // Signal all the streams the config has changed.
61 for (StreamList::iterator it = stream_list_.begin();
62 it != stream_list_.end(); ++it) {
63 (*it)->OnConfigChanged();
66 // Once all the streams have been notified, the buffering state must be
67 // updated (no notification is received from the streams).
68 OnBufferingStateChanged(false, false);
71 scoped_refptr<BufferingState> BufferingController::AddStream(
72 const std::string& stream_id) {
73 DCHECK(thread_checker_.CalledOnValidThread());
75 // Add a new stream to the list of streams being monitored.
76 scoped_refptr<BufferingState> buffering_state(new BufferingState(
77 stream_id,
78 config_,
79 base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_,
80 false, false),
81 base::Bind(&BufferingController::UpdateHighLevelThreshold, weak_this_)));
82 stream_list_.push_back(buffering_state);
84 // Update the state and force a notification to the streams.
85 // TODO(damienv): Should this be a PostTask ?
86 OnBufferingStateChanged(true, false);
88 return buffering_state;
91 void BufferingController::SetMediaTime(base::TimeDelta time) {
92 for (StreamList::iterator it = stream_list_.begin();
93 it != stream_list_.end(); ++it) {
94 (*it)->SetMediaTime(time);
98 base::TimeDelta BufferingController::GetMaxRenderingTime() const {
99 base::TimeDelta max_rendering_time(::media::kNoTimestamp());
100 for (StreamList::const_iterator it = stream_list_.begin();
101 it != stream_list_.end(); ++it) {
102 base::TimeDelta max_stream_rendering_time =
103 (*it)->GetMaxRenderingTime();
104 if (max_stream_rendering_time == ::media::kNoTimestamp())
105 return ::media::kNoTimestamp();
106 if (max_rendering_time == ::media::kNoTimestamp() ||
107 max_stream_rendering_time < max_rendering_time) {
108 max_rendering_time = max_stream_rendering_time;
111 return max_rendering_time;
114 void BufferingController::Reset() {
115 DCHECK(thread_checker_.CalledOnValidThread());
117 is_buffering_ = false;
118 initial_buffering_ = true;
119 stream_list_.clear();
122 void BufferingController::OnBufferingStateChanged(
123 bool force_notification, bool buffering_timeout) {
124 DCHECK(thread_checker_.CalledOnValidThread());
126 // Log the state of each stream.
127 DumpState();
129 bool is_low_buffering = IsLowBufferLevel();
130 bool is_high_buffering = !is_low_buffering;
131 if (!buffering_timeout) {
132 // Hysteresis:
133 // - to leave buffering, not only should we leave the low buffer level state
134 // but we should go to the high buffer level state (medium is not enough).
135 is_high_buffering = IsHighBufferLevel();
138 bool is_buffering_prv = is_buffering_;
139 if (is_buffering_) {
140 if (is_high_buffering)
141 is_buffering_ = false;
142 } else {
143 if (is_low_buffering)
144 is_buffering_ = true;
147 // Start buffering.
148 if (is_buffering_ && !is_buffering_prv) {
149 begin_buffering_time_ = base::Time::Now();
152 // End buffering.
153 if (is_buffering_prv && !is_buffering_) {
154 // TODO(damienv): |buffering_user_time| could be a UMA histogram.
155 base::Time current_time = base::Time::Now();
156 base::TimeDelta buffering_user_time = current_time - begin_buffering_time_;
157 CMALOG(kLogControl)
158 << "Buffering took: "
159 << buffering_user_time.InMilliseconds() << "ms";
160 chromecast::metrics::CastMetricsHelper::BufferingType buffering_type =
161 initial_buffering_ ?
162 chromecast::metrics::CastMetricsHelper::kInitialBuffering :
163 chromecast::metrics::CastMetricsHelper::kBufferingAfterUnderrun;
164 chromecast::metrics::CastMetricsHelper::GetInstance()->LogTimeToBufferAv(
165 buffering_type, buffering_user_time);
167 // Only the first buffering report is considered "initial buffering".
168 initial_buffering_ = false;
171 if (is_buffering_prv != is_buffering_ || force_notification)
172 buffering_notification_cb_.Run(is_buffering_);
175 bool BufferingController::IsHighBufferLevel() {
176 if (stream_list_.empty())
177 return true;
179 bool is_high_buffering = true;
180 for (StreamList::iterator it = stream_list_.begin();
181 it != stream_list_.end(); ++it) {
182 BufferingState::State stream_state = (*it)->GetState();
183 is_high_buffering = is_high_buffering &&
184 ((stream_state == BufferingState::kHighLevel) ||
185 (stream_state == BufferingState::kEosReached));
187 return is_high_buffering;
190 bool BufferingController::IsLowBufferLevel() {
191 if (stream_list_.empty())
192 return false;
194 for (StreamList::iterator it = stream_list_.begin();
195 it != stream_list_.end(); ++it) {
196 BufferingState::State stream_state = (*it)->GetState();
197 if (stream_state == BufferingState::kLowLevel)
198 return true;
201 return false;
204 void BufferingController::DumpState() const {
205 for (StreamList::const_iterator it = stream_list_.begin();
206 it != stream_list_.end(); ++it) {
207 CMALOG(kLogControl) << (*it)->ToString();
211 } // namespace media
212 } // namespace chromecast