[MediaRouter] Update MR-2-Extension's PostMessage to return boolean.
[chromium-blink-merge.git] / chromecast / media / cma / base / buffering_controller.cc
blob3cd47bc959395e7883423769eb9cddceca009689
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/buffers.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();
31 BufferingController::~BufferingController() {
32 // Some weak pointers might possibly be invalidated here.
33 DCHECK(thread_checker_.CalledOnValidThread());
36 void BufferingController::UpdateHighLevelThreshold(
37 base::TimeDelta high_level_threshold) {
38 // Can only decrease the high level threshold.
39 if (high_level_threshold > config_->high_level())
40 return;
41 CMALOG(kLogControl) << "High buffer threshold: "
42 << high_level_threshold.InMilliseconds();
43 config_->set_high_level(high_level_threshold);
45 // Make sure the low level threshold is somewhat consistent.
46 // Currently, we set it to one third of the high level threshold:
47 // this value could be adjusted in the future.
48 base::TimeDelta low_level_threshold = high_level_threshold / 3;
49 if (low_level_threshold <= config_->low_level()) {
50 CMALOG(kLogControl) << "Low buffer threshold: "
51 << low_level_threshold.InMilliseconds();
52 config_->set_low_level(low_level_threshold);
55 // Signal all the streams the config has changed.
56 for (StreamList::iterator it = stream_list_.begin();
57 it != stream_list_.end(); ++it) {
58 (*it)->OnConfigChanged();
61 // Once all the streams have been notified, the buffering state must be
62 // updated (no notification is received from the streams).
63 OnBufferingStateChanged(false, false);
66 scoped_refptr<BufferingState> BufferingController::AddStream(
67 const std::string& stream_id) {
68 DCHECK(thread_checker_.CalledOnValidThread());
70 // Add a new stream to the list of streams being monitored.
71 scoped_refptr<BufferingState> buffering_state(new BufferingState(
72 stream_id,
73 config_,
74 base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_,
75 false, false),
76 base::Bind(&BufferingController::UpdateHighLevelThreshold, weak_this_)));
77 stream_list_.push_back(buffering_state);
79 // Update the state and force a notification to the streams.
80 // TODO(damienv): Should this be a PostTask ?
81 OnBufferingStateChanged(true, false);
83 return buffering_state;
86 void BufferingController::SetMediaTime(base::TimeDelta time) {
87 for (StreamList::iterator it = stream_list_.begin();
88 it != stream_list_.end(); ++it) {
89 (*it)->SetMediaTime(time);
93 base::TimeDelta BufferingController::GetMaxRenderingTime() const {
94 base::TimeDelta max_rendering_time(::media::kNoTimestamp());
95 for (StreamList::const_iterator it = stream_list_.begin();
96 it != stream_list_.end(); ++it) {
97 base::TimeDelta max_stream_rendering_time =
98 (*it)->GetMaxRenderingTime();
99 if (max_stream_rendering_time == ::media::kNoTimestamp())
100 return ::media::kNoTimestamp();
101 if (max_rendering_time == ::media::kNoTimestamp() ||
102 max_stream_rendering_time < max_rendering_time) {
103 max_rendering_time = max_stream_rendering_time;
106 return max_rendering_time;
109 void BufferingController::Reset() {
110 DCHECK(thread_checker_.CalledOnValidThread());
112 is_buffering_ = false;
113 initial_buffering_ = true;
114 stream_list_.clear();
117 void BufferingController::OnBufferingStateChanged(
118 bool force_notification, bool buffering_timeout) {
119 DCHECK(thread_checker_.CalledOnValidThread());
121 // Log the state of each stream.
122 DumpState();
124 bool is_low_buffering = IsLowBufferLevel();
125 bool is_high_buffering = !is_low_buffering;
126 if (!buffering_timeout) {
127 // Hysteresis:
128 // - to leave buffering, not only should we leave the low buffer level state
129 // but we should go to the high buffer level state (medium is not enough).
130 is_high_buffering = IsHighBufferLevel();
133 bool is_buffering_prv = is_buffering_;
134 if (is_buffering_) {
135 if (is_high_buffering)
136 is_buffering_ = false;
137 } else {
138 if (is_low_buffering)
139 is_buffering_ = true;
142 // Start buffering.
143 if (is_buffering_ && !is_buffering_prv) {
144 begin_buffering_time_ = base::Time::Now();
147 // End buffering.
148 if (is_buffering_prv && !is_buffering_) {
149 // TODO(damienv): |buffering_user_time| could be a UMA histogram.
150 base::Time current_time = base::Time::Now();
151 base::TimeDelta buffering_user_time = current_time - begin_buffering_time_;
152 CMALOG(kLogControl)
153 << "Buffering took: "
154 << buffering_user_time.InMilliseconds() << "ms";
155 chromecast::metrics::CastMetricsHelper::BufferingType buffering_type =
156 initial_buffering_ ?
157 chromecast::metrics::CastMetricsHelper::kInitialBuffering :
158 chromecast::metrics::CastMetricsHelper::kBufferingAfterUnderrun;
159 chromecast::metrics::CastMetricsHelper::GetInstance()->LogTimeToBufferAv(
160 buffering_type, buffering_user_time);
162 // Only the first buffering report is considered "initial buffering".
163 initial_buffering_ = false;
166 if (is_buffering_prv != is_buffering_ || force_notification)
167 buffering_notification_cb_.Run(is_buffering_);
170 bool BufferingController::IsHighBufferLevel() {
171 if (stream_list_.empty())
172 return true;
174 bool is_high_buffering = true;
175 for (StreamList::iterator it = stream_list_.begin();
176 it != stream_list_.end(); ++it) {
177 BufferingState::State stream_state = (*it)->GetState();
178 is_high_buffering = is_high_buffering &&
179 ((stream_state == BufferingState::kHighLevel) ||
180 (stream_state == BufferingState::kEosReached));
182 return is_high_buffering;
185 bool BufferingController::IsLowBufferLevel() {
186 if (stream_list_.empty())
187 return false;
189 for (StreamList::iterator it = stream_list_.begin();
190 it != stream_list_.end(); ++it) {
191 BufferingState::State stream_state = (*it)->GetState();
192 if (stream_state == BufferingState::kLowLevel)
193 return true;
196 return false;
199 void BufferingController::DumpState() const {
200 for (StreamList::const_iterator it = stream_list_.begin();
201 it != stream_list_.end(); ++it) {
202 CMALOG(kLogControl) << (*it)->ToString();
206 } // namespace media
207 } // namespace chromecast