Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chromecast / media / cma / base / buffering_controller.cc
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/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"
14
15 namespace chromecast {
16 namespace media {
17
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();
29 }
30
31 BufferingController::~BufferingController() {
32   // Some weak pointers might possibly be invalidated here.
33   DCHECK(thread_checker_.CalledOnValidThread());
34 }
35
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);
44
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);
53   }
54
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();
59   }
60
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);
64 }
65
66 scoped_refptr<BufferingState> BufferingController::AddStream() {
67   DCHECK(thread_checker_.CalledOnValidThread());
68
69   // Add a new stream to the list of streams being monitored.
70   scoped_refptr<BufferingState> buffering_state(new BufferingState(
71       config_,
72       base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_,
73                  false, false),
74       base::Bind(&BufferingController::UpdateHighLevelThreshold, weak_this_)));
75   stream_list_.push_back(buffering_state);
76
77   // Update the state and force a notification to the streams.
78   // TODO(damienv): Should this be a PostTask ?
79   OnBufferingStateChanged(true, false);
80
81   return buffering_state;
82 }
83
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);
88   }
89 }
90
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;
102     }
103   }
104   return max_rendering_time;
105 }
106
107 void BufferingController::Reset() {
108   DCHECK(thread_checker_.CalledOnValidThread());
109
110   is_buffering_ = false;
111   initial_buffering_ = true;
112   stream_list_.clear();
113 }
114
115 void BufferingController::OnBufferingStateChanged(
116     bool force_notification, bool buffering_timeout) {
117   DCHECK(thread_checker_.CalledOnValidThread());
118
119   // Log the state of each stream.
120   DumpState();
121
122   bool is_low_buffering = IsLowBufferLevel();
123   bool is_high_buffering = !is_low_buffering;
124   if (!buffering_timeout) {
125     // Hysteresis:
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();
129   }
130
131   bool is_buffering_prv = is_buffering_;
132   if (is_buffering_) {
133     if (is_high_buffering)
134       is_buffering_ = false;
135   } else {
136     if (is_low_buffering)
137       is_buffering_ = true;
138   }
139
140   // Start buffering.
141   if (is_buffering_ && !is_buffering_prv) {
142     begin_buffering_time_ = base::Time::Now();
143   }
144
145   // End buffering.
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_;
150     CMALOG(kLogControl)
151         << "Buffering took: "
152         << buffering_user_time.InMilliseconds() << "ms";
153     chromecast::metrics::CastMetricsHelper::BufferingType buffering_type =
154         initial_buffering_ ?
155             chromecast::metrics::CastMetricsHelper::kInitialBuffering :
156             chromecast::metrics::CastMetricsHelper::kBufferingAfterUnderrun;
157     chromecast::metrics::CastMetricsHelper::GetInstance()->LogTimeToBufferAv(
158         buffering_type, buffering_user_time);
159
160     // Only the first buffering report is considered "initial buffering".
161     initial_buffering_ = false;
162   }
163
164   if (is_buffering_prv != is_buffering_ || force_notification)
165     buffering_notification_cb_.Run(is_buffering_);
166 }
167
168 bool BufferingController::IsHighBufferLevel() {
169   if (stream_list_.empty())
170     return true;
171
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));
179   }
180   return is_high_buffering;
181 }
182
183 bool BufferingController::IsLowBufferLevel() {
184   if (stream_list_.empty())
185     return false;
186
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)
191       return true;
192   }
193
194   return false;
195 }
196
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();
202   }
203 }
204
205 }  // namespace media
206 }  // namespace chromecast