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/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
{
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();
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())
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(
79 base::Bind(&BufferingController::OnBufferingStateChanged
, weak_this_
,
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.
129 bool is_low_buffering
= IsLowBufferLevel();
130 bool is_high_buffering
= !is_low_buffering
;
131 if (!buffering_timeout
) {
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_
;
140 if (is_high_buffering
)
141 is_buffering_
= false;
143 if (is_low_buffering
)
144 is_buffering_
= true;
148 if (is_buffering_
&& !is_buffering_prv
) {
149 begin_buffering_time_
= base::Time::Now();
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_
;
158 << "Buffering took: "
159 << buffering_user_time
.InMilliseconds() << "ms";
160 chromecast::metrics::CastMetricsHelper::BufferingType buffering_type
=
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())
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())
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
)
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();
212 } // namespace chromecast