OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "chromecast/media/cma/base/buffering_controller.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/location.h" | |
9 #include "base/message_loop/message_loop_proxy.h" | |
10 #include "chromecast/media/cma/base/buffering_state.h" | |
11 #include "chromecast/media/cma/base/cma_logging.h" | |
12 #include "media/base/buffers.h" | |
13 | |
14 namespace media { | |
15 namespace cma { | |
16 | |
17 BufferingController::BufferingController( | |
18 const scoped_refptr<BufferingConfig>& config, | |
19 const BufferingNotificationCb& buffering_notification_cb) | |
20 : weak_factory_(this), | |
lcwu1
2014/08/28 02:14:58
While it won't affect code correctness language-wi
damienv1
2014/08/29 15:09:05
I changed the order in the header file and forgot
| |
21 config_(config), | |
22 buffering_notification_cb_(buffering_notification_cb), | |
23 is_buffering_(false), | |
24 begin_buffering_time_(base::Time()) { | |
25 weak_this_ = weak_factory_.GetWeakPtr(); | |
26 thread_checker_.DetachFromThread(); | |
27 } | |
28 | |
29 BufferingController::~BufferingController() { | |
30 // Some weak pointers might possibly be invalidated here. | |
31 DCHECK(thread_checker_.CalledOnValidThread()); | |
32 } | |
33 | |
34 void BufferingController::UpdateHighLevelThreshold( | |
35 base::TimeDelta high_level_threshold) { | |
36 // Can only decrease the high level threshold. | |
37 if (high_level_threshold > config_->high_level()) | |
38 return; | |
39 CMALOG(kLogControl) << "High buffer threshold: " | |
40 << high_level_threshold.InMilliseconds(); | |
41 config_->set_high_level(high_level_threshold); | |
42 | |
43 // Make sure the low level threshold is somewhat consistent. | |
44 // Currently, we set it to one third of the high level threshold: | |
45 // this value could be adjusted in the future. | |
46 base::TimeDelta low_level_threshold = high_level_threshold / 3; | |
47 if (low_level_threshold <= config_->low_level()) { | |
48 CMALOG(kLogControl) << "Low buffer threshold: " | |
49 << low_level_threshold.InMilliseconds(); | |
50 config_->set_low_level(low_level_threshold); | |
51 } | |
52 | |
53 // Signal all the streams the config has changed. | |
54 for (StreamList::iterator it = stream_list_.begin(); | |
55 it != stream_list_.end(); ++it) { | |
56 (*it)->OnConfigChanged(); | |
57 } | |
58 | |
59 // Once all the streams have been notified, the buffering state must be | |
60 // updated (no notification is received from the streams). | |
61 OnBufferingStateChanged(false, false); | |
62 } | |
63 | |
64 scoped_refptr<BufferingState> BufferingController::AddStream() { | |
65 DCHECK(thread_checker_.CalledOnValidThread()); | |
66 | |
67 // Add a new stream to the list of streams being monitored. | |
68 scoped_refptr<BufferingState> buffering_state(new BufferingState( | |
69 config_, | |
70 base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_, | |
71 false, false), | |
72 base::Bind(&BufferingController::UpdateHighLevelThreshold, weak_this_))); | |
73 stream_list_.push_back(buffering_state); | |
74 | |
75 // Update the state and force a notification to the streams. | |
76 // TODO(damienv): Should this be a PostTask ? | |
77 OnBufferingStateChanged(true, false); | |
78 | |
79 return buffering_state; | |
80 } | |
81 | |
82 void BufferingController::SetMediaTime(base::TimeDelta time) { | |
83 for (StreamList::iterator it = stream_list_.begin(); | |
84 it != stream_list_.end(); ++it) { | |
85 (*it)->SetMediaTime(time); | |
86 } | |
87 } | |
88 | |
89 base::TimeDelta BufferingController::GetMaxRenderingTime() const { | |
90 base::TimeDelta max_rendering_time(kNoTimestamp()); | |
91 for (StreamList::const_iterator it = stream_list_.begin(); | |
92 it != stream_list_.end(); ++it) { | |
93 base::TimeDelta max_stream_rendering_time = | |
94 (*it)->GetMaxRenderingTime(); | |
95 if (max_stream_rendering_time == kNoTimestamp()) | |
96 return kNoTimestamp(); | |
97 if (max_rendering_time == kNoTimestamp() || | |
98 max_stream_rendering_time < max_rendering_time) { | |
99 max_rendering_time = max_stream_rendering_time; | |
100 } | |
101 } | |
102 return max_rendering_time; | |
103 } | |
104 | |
105 void BufferingController::Reset() { | |
106 DCHECK(thread_checker_.CalledOnValidThread()); | |
107 | |
108 is_buffering_ = false; | |
109 stream_list_.clear(); | |
110 } | |
111 | |
112 void BufferingController::OnBufferingStateChanged( | |
113 bool force_notification, bool buffering_timeout) { | |
114 DCHECK(thread_checker_.CalledOnValidThread()); | |
115 | |
116 // Log the state of each stream. | |
117 DumpState(); | |
118 | |
119 bool is_low_buffering = IsLowBufferLevel(); | |
120 bool is_high_buffering = !is_low_buffering; | |
121 if (!buffering_timeout) { | |
122 // Hysteresis: | |
123 // - to leave buffering, not only should we leave the low buffer level state | |
124 // but we should go to the high buffer level state (medium is not enough). | |
125 is_high_buffering = IsHighBufferLevel(); | |
126 } | |
127 | |
128 bool is_buffering_prv = is_buffering_; | |
129 if (is_buffering_) { | |
130 if (is_high_buffering) | |
131 is_buffering_ = false; | |
132 } else { | |
133 if (is_low_buffering) | |
134 is_buffering_ = true; | |
135 } | |
136 | |
137 // Start buffering. | |
138 if (is_buffering_ && !is_buffering_prv) { | |
139 begin_buffering_time_ = base::Time::Now(); | |
140 } | |
141 | |
142 // End buffering. | |
143 if (is_buffering_prv && !is_buffering_) { | |
144 // TODO(damienv): |buffering_user_time| could be a UMA histogram. | |
145 base::Time current_time = base::Time::Now(); | |
146 base::TimeDelta buffering_user_time = current_time - begin_buffering_time_; | |
147 CMALOG(kLogControl) | |
148 << "Buffering took: " | |
149 << buffering_user_time.InMilliseconds() << "ms"; | |
150 } | |
151 | |
152 if (is_buffering_prv != is_buffering_ || force_notification) | |
153 buffering_notification_cb_.Run(is_buffering_); | |
154 } | |
155 | |
156 bool BufferingController::IsHighBufferLevel() { | |
157 if (stream_list_.empty()) | |
158 return true; | |
159 | |
160 bool is_high_buffering = true; | |
161 for (StreamList::iterator it = stream_list_.begin(); | |
162 it != stream_list_.end(); ++it) { | |
163 BufferingState::State stream_state = (*it)->GetState(); | |
164 is_high_buffering = is_high_buffering && | |
165 ((stream_state == BufferingState::kHighLevel) || | |
166 (stream_state == BufferingState::kEosReached)); | |
167 } | |
168 return is_high_buffering; | |
169 } | |
170 | |
171 bool BufferingController::IsLowBufferLevel() { | |
172 if (stream_list_.empty()) | |
173 return false; | |
174 | |
175 for (StreamList::iterator it = stream_list_.begin(); | |
176 it != stream_list_.end(); ++it) { | |
177 BufferingState::State stream_state = (*it)->GetState(); | |
178 if (stream_state == BufferingState::kLowLevel) | |
179 return true; | |
180 } | |
181 | |
182 return false; | |
183 } | |
184 | |
185 void BufferingController::DumpState() const { | |
186 CMALOG(kLogControl) << __FUNCTION__; | |
187 for (StreamList::const_iterator it = stream_list_.begin(); | |
188 it != stream_list_.end(); ++it) { | |
189 CMALOG(kLogControl) << (*it)->ToString(); | |
190 } | |
191 } | |
192 | |
193 } // namespace cma | |
194 } // namespace media | |
OLD | NEW |