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"
8 #include "base/location.h"
9 #include "base/message_loop/message_loop_proxy.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 {
18 BufferingController::BufferingController(
19 const scoped_refptr<BufferingConfig>& config,
20 const BufferingNotificationCB& buffering_notification_cb)
22 buffering_notification_cb_(buffering_notification_cb),
24 begin_buffering_time_(base::Time()),
25 initial_buffering_(true),
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())
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 DCHECK(thread_checker_.CalledOnValidThread());
69 // Add a new stream to the list of streams being monitored.
70 scoped_refptr<BufferingState> buffering_state(new BufferingState(
72 base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_,
74 base::Bind(&BufferingController::UpdateHighLevelThreshold, weak_this_)));
75 stream_list_.push_back(buffering_state);
77 // Update the state and force a notification to the streams.
78 // TODO(damienv): Should this be a PostTask ?
79 OnBufferingStateChanged(true, false);
81 return buffering_state;
84 void BufferingController::SetMediaTime(base::TimeDelta time) {
85 for (StreamList::iterator it = stream_list_.begin();
86 it != stream_list_.end(); ++it) {
87 (*it)->SetMediaTime(time);
91 base::TimeDelta BufferingController::GetMaxRenderingTime() const {
92 base::TimeDelta max_rendering_time(::media::kNoTimestamp());
93 for (StreamList::const_iterator it = stream_list_.begin();
94 it != stream_list_.end(); ++it) {
95 base::TimeDelta max_stream_rendering_time =
96 (*it)->GetMaxRenderingTime();
97 if (max_stream_rendering_time == ::media::kNoTimestamp())
98 return ::media::kNoTimestamp();
99 if (max_rendering_time == ::media::kNoTimestamp() ||
100 max_stream_rendering_time < max_rendering_time) {
101 max_rendering_time = max_stream_rendering_time;
104 return max_rendering_time;
107 void BufferingController::Reset() {
108 DCHECK(thread_checker_.CalledOnValidThread());
110 is_buffering_ = false;
111 initial_buffering_ = true;
112 stream_list_.clear();
115 void BufferingController::OnBufferingStateChanged(
116 bool force_notification, bool buffering_timeout) {
117 DCHECK(thread_checker_.CalledOnValidThread());
119 // Log the state of each stream.
122 bool is_low_buffering = IsLowBufferLevel();
123 bool is_high_buffering = !is_low_buffering;
124 if (!buffering_timeout) {
126 // - to leave buffering, not only should we leave the low buffer level state
127 // but we should go to the high buffer level state (medium is not enough).
128 is_high_buffering = IsHighBufferLevel();
131 bool is_buffering_prv = is_buffering_;
133 if (is_high_buffering)
134 is_buffering_ = false;
136 if (is_low_buffering)
137 is_buffering_ = true;
141 if (is_buffering_ && !is_buffering_prv) {
142 begin_buffering_time_ = base::Time::Now();
146 if (is_buffering_prv && !is_buffering_) {
147 // TODO(damienv): |buffering_user_time| could be a UMA histogram.
148 base::Time current_time = base::Time::Now();
149 base::TimeDelta buffering_user_time = current_time - begin_buffering_time_;
151 << "Buffering took: "
152 << buffering_user_time.InMilliseconds() << "ms";
153 chromecast::metrics::CastMetricsHelper::BufferingType buffering_type =
155 chromecast::metrics::CastMetricsHelper::kInitialBuffering :
156 chromecast::metrics::CastMetricsHelper::kBufferingAfterUnderrun;
157 chromecast::metrics::CastMetricsHelper::GetInstance()->LogTimeToBufferAv(
158 buffering_type, buffering_user_time);
160 // Only the first buffering report is considered "initial buffering".
161 initial_buffering_ = false;
164 if (is_buffering_prv != is_buffering_ || force_notification)
165 buffering_notification_cb_.Run(is_buffering_);
168 bool BufferingController::IsHighBufferLevel() {
169 if (stream_list_.empty())
172 bool is_high_buffering = true;
173 for (StreamList::iterator it = stream_list_.begin();
174 it != stream_list_.end(); ++it) {
175 BufferingState::State stream_state = (*it)->GetState();
176 is_high_buffering = is_high_buffering &&
177 ((stream_state == BufferingState::kHighLevel) ||
178 (stream_state == BufferingState::kEosReached));
180 return is_high_buffering;
183 bool BufferingController::IsLowBufferLevel() {
184 if (stream_list_.empty())
187 for (StreamList::iterator it = stream_list_.begin();
188 it != stream_list_.end(); ++it) {
189 BufferingState::State stream_state = (*it)->GetState();
190 if (stream_state == BufferingState::kLowLevel)
197 void BufferingController::DumpState() const {
198 CMALOG(kLogControl) << __FUNCTION__;
199 for (StreamList::const_iterator it = stream_list_.begin();
200 it != stream_list_.end(); ++it) {
201 CMALOG(kLogControl) << (*it)->ToString();
206 } // namespace chromecast