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 14namespace chromecast { 15namespace media { 16 17BufferingController::BufferingController( 18 const scoped_refptr<BufferingConfig>& config, 19 const BufferingNotificationCB& buffering_notification_cb) 20 : config_(config), 21 buffering_notification_cb_(buffering_notification_cb), 22 is_buffering_(false), 23 begin_buffering_time_(base::Time()), 24 weak_factory_(this) { 25 weak_this_ = weak_factory_.GetWeakPtr(); 26 thread_checker_.DetachFromThread(); 27} 28 29BufferingController::~BufferingController() { 30 // Some weak pointers might possibly be invalidated here. 31 DCHECK(thread_checker_.CalledOnValidThread()); 32} 33 34void 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 64scoped_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 82void 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 89base::TimeDelta BufferingController::GetMaxRenderingTime() const { 90 base::TimeDelta max_rendering_time(::media::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 == ::media::kNoTimestamp()) 96 return ::media::kNoTimestamp(); 97 if (max_rendering_time == ::media::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 105void BufferingController::Reset() { 106 DCHECK(thread_checker_.CalledOnValidThread()); 107 108 is_buffering_ = false; 109 stream_list_.clear(); 110} 111 112void 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 156bool 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 171bool 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 185void 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 media 194} // namespace chromecast 195