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/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 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(
74 base::Bind(&BufferingController::OnBufferingStateChanged
, weak_this_
,
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.
124 bool is_low_buffering
= IsLowBufferLevel();
125 bool is_high_buffering
= !is_low_buffering
;
126 if (!buffering_timeout
) {
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_
;
135 if (is_high_buffering
)
136 is_buffering_
= false;
138 if (is_low_buffering
)
139 is_buffering_
= true;
143 if (is_buffering_
&& !is_buffering_prv
) {
144 begin_buffering_time_
= base::Time::Now();
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_
;
153 << "Buffering took: "
154 << buffering_user_time
.InMilliseconds() << "ms";
155 chromecast::metrics::CastMetricsHelper::BufferingType buffering_type
=
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())
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())
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
)
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();
207 } // namespace chromecast