From 1cd1162c7a2c0c9dea4c211e067ebb7b05993d08 Mon Sep 17 00:00:00 2001 From: "pwestin@webrtc.org" Date: Thu, 19 Apr 2012 12:13:52 +0000 Subject: [PATCH] Break out of send side bandwidth estimation and controll. Review URL: https://webrtc-codereview.appspot.com/490010 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2064 4adac7df-926f-26a2-2b94-8c16560cd09d --- src/modules/bitrate_controller/OWNERS | 5 + .../bitrate_controller.gypi | 41 +++ .../bitrate_controller_impl.cc | 211 +++++++++++++++ .../bitrate_controller_impl.h | 88 ++++++ .../include/bitrate_controller.h | 67 +++++ .../send_side_bandwidth_estimation.cc | 223 +++++++++++++++ .../send_side_bandwidth_estimation.h | 73 +++++ .../test/test_bitrate_controller.cc | 255 ++++++++++++++++++ .../test/test_bitrate_controller.gypi | 37 +++ src/modules/modules.gyp | 2 + .../rtp_rtcp/interface/rtp_rtcp_defines.h | 80 +++--- 11 files changed, 1051 insertions(+), 31 deletions(-) create mode 100644 src/modules/bitrate_controller/OWNERS create mode 100644 src/modules/bitrate_controller/bitrate_controller.gypi create mode 100644 src/modules/bitrate_controller/bitrate_controller_impl.cc create mode 100644 src/modules/bitrate_controller/bitrate_controller_impl.h create mode 100644 src/modules/bitrate_controller/include/bitrate_controller.h create mode 100644 src/modules/bitrate_controller/send_side_bandwidth_estimation.cc create mode 100644 src/modules/bitrate_controller/send_side_bandwidth_estimation.h create mode 100644 src/modules/bitrate_controller/test/test_bitrate_controller.cc create mode 100644 src/modules/bitrate_controller/test/test_bitrate_controller.gypi diff --git a/src/modules/bitrate_controller/OWNERS b/src/modules/bitrate_controller/OWNERS new file mode 100644 index 000000000..6c7028550 --- /dev/null +++ b/src/modules/bitrate_controller/OWNERS @@ -0,0 +1,5 @@ +pwestin@webrtc.org +stefan@webrtc.org +henrik.lundin@webrtc.org +mflodman@webrtc.org +asapersson@webrtc.org diff --git a/src/modules/bitrate_controller/bitrate_controller.gypi b/src/modules/bitrate_controller/bitrate_controller.gypi new file mode 100644 index 000000000..4514eb45d --- /dev/null +++ b/src/modules/bitrate_controller/bitrate_controller.gypi @@ -0,0 +1,41 @@ +# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'targets': [ + { + 'target_name': 'bitrate_controller', + 'type': '<(library)', + 'dependencies': [ + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'include_dirs': [ + '../interface', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../interface', + '<(webrtc_root)/modules/rtp_rtcp/interface', + ], + }, + 'sources': [ + 'send_side_bandwidth_estimation.h', + 'send_side_bandwidth_estimation.cc', + 'bitrate_controller.h', + 'bitrate_controller_impl.h', + 'bitrate_controller_impl.cc', + ], # source + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/src/modules/bitrate_controller/bitrate_controller_impl.cc b/src/modules/bitrate_controller/bitrate_controller_impl.cc new file mode 100644 index 000000000..f219dd6ec --- /dev/null +++ b/src/modules/bitrate_controller/bitrate_controller_impl.cc @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "modules/bitrate_controller/bitrate_controller_impl.h" + +#include + +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" + +namespace webrtc { + +class RtcpBandwidthObserverImpl : public RtcpBandwidthObserver { + public: + explicit RtcpBandwidthObserverImpl(BitrateControllerImpl* owner) + : owner_(owner) { + } + virtual ~RtcpBandwidthObserverImpl() { + } + // Received RTCP REMB or TMMBR. + virtual void OnReceivedEstimatedBitrate(const uint32_t bitrate) { + owner_->OnReceivedEstimatedBitrate(bitrate); + } + // Received RTCP receiver block. + virtual void OnReceivedRtcpReceiverReport( + const uint32_t ssrc, + const uint8_t fraction_loss, + const uint32_t rtt, + const uint32_t last_received_extended_high_seq_num, + const uint32_t now_ms) { + uint32_t number_of_packets = 0; + std::map::iterator it = + ssrc_to_last_received_extended_high_seq_num_.find(ssrc); + + if (it != ssrc_to_last_received_extended_high_seq_num_.end()) { + number_of_packets = last_received_extended_high_seq_num - it->second; + } + // Update last received for this SSRC. + ssrc_to_last_received_extended_high_seq_num_[ssrc] = + last_received_extended_high_seq_num; + + owner_->OnReceivedRtcpReceiverReport(fraction_loss, rtt, number_of_packets, + now_ms); + } + private: + std::map ssrc_to_last_received_extended_high_seq_num_; + BitrateControllerImpl* owner_; +}; + +BitrateController* BitrateController::CreateBitrateController() { + return new BitrateControllerImpl(); +} + +BitrateControllerImpl::BitrateControllerImpl() + : critsect_(CriticalSectionWrapper::CreateCriticalSection()) { +} + +BitrateControllerImpl::~BitrateControllerImpl() { + delete critsect_; +} + +RtcpBandwidthObserver* BitrateControllerImpl::CreateRtcpBandwidthObserver() { + return new RtcpBandwidthObserverImpl(this); +} + +void BitrateControllerImpl::SetBitrateObserver( + BitrateObserver* observer, + const uint32_t start_bitrate, + const uint32_t min_bitrate, + const uint32_t max_bitrate) { + CriticalSectionScoped cs(critsect_); + + std::map::iterator it = + bitrate_observers_.find(observer); + + if (it != bitrate_observers_.end()) { + // Update current configuration. + it->second->start_bitrate_ = start_bitrate; + it->second->min_bitrate_ = min_bitrate; + it->second->max_bitrate_ = max_bitrate; + } else { + // Add new settings. + bitrate_observers_[observer] = new BitrateConfiguration(start_bitrate, + min_bitrate, + max_bitrate); + } + uint32_t sum_start_bitrate = 0; + uint32_t sum_min_bitrate = 0; + uint32_t sum_max_bitrate = 0; + + // Summarize all configurations. + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); ++it) { + sum_start_bitrate += it->second->start_bitrate_; + sum_min_bitrate += it->second->min_bitrate_; + sum_max_bitrate += it->second->max_bitrate_; + } + // Only change start bitrate if we have exactly one observer. By definition + // you can only have one start bitrate, once we have our first estimate we + // will adapt from there. + if (bitrate_observers_.size() == 1) { + bandwidth_estimation_.SetSendBitrate(sum_start_bitrate); + } + bandwidth_estimation_.SetMinMaxBitrate(sum_min_bitrate, + sum_max_bitrate); +} + +void BitrateControllerImpl::RemoveBitrateObserver(BitrateObserver* observer) { + CriticalSectionScoped cs(critsect_); + std::map::iterator it = + bitrate_observers_.find(observer); + if (it != bitrate_observers_.end()) { + delete it->second; + bitrate_observers_.erase(it); + } +} + +void BitrateControllerImpl::OnReceivedEstimatedBitrate(const uint32_t bitrate) { + uint32_t new_bitrate = 0; + uint8_t fraction_lost = 0; + uint16_t rtt = 0; + CriticalSectionScoped cs(critsect_); + if (bandwidth_estimation_.UpdateBandwidthEstimate(bitrate, + &new_bitrate, + &fraction_lost, + &rtt)) { + OnNetworkChanged(new_bitrate, fraction_lost, rtt); + } +} + +void BitrateControllerImpl::OnReceivedRtcpReceiverReport( + const uint8_t fraction_loss, + const uint32_t rtt, + const int number_of_packets, + const uint32_t now_ms) { + uint32_t new_bitrate = 0; + uint8_t loss = fraction_loss; + CriticalSectionScoped cs(critsect_); + if (bandwidth_estimation_.UpdatePacketLoss(number_of_packets, rtt, now_ms, + &loss, &new_bitrate)) { + OnNetworkChanged(new_bitrate, loss, rtt); + } +} + +// We have the lock here. +void BitrateControllerImpl::OnNetworkChanged(const uint32_t bitrate, + const uint8_t fraction_loss, + const uint32_t rtt) { + // Sanity check. + uint32_t number_of_observers = bitrate_observers_.size(); + if (number_of_observers == 0) { + return; + } + uint32_t sum_min_bitrates = 0; + std::map::iterator it; + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); ++it) { + sum_min_bitrates += it->second->min_bitrate_; + } + if (bitrate <= sum_min_bitrates) { + // Min bitrate to all observers. + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); + ++it) { + it->first->OnNetworkChanged(it->second->min_bitrate_, fraction_loss, + rtt); + } + // Set sum of min to current send bitrate. + bandwidth_estimation_.SetSendBitrate(sum_min_bitrates); + return; + } + uint32_t bitrate_per_observer = (bitrate - sum_min_bitrates) / + number_of_observers; + + // Use map to sort list based on max bitrate. + ObserverSortingMap list_max_bitrates; + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); ++it) { + list_max_bitrates.insert(std::pair( + it->second->max_bitrate_, + new ObserverConfiguration(it->first, it->second->min_bitrate_))); + } + ObserverSortingMap::iterator max_it = list_max_bitrates.begin(); + while (max_it != list_max_bitrates.end()) { + number_of_observers--; + uint32_t observer_allowance = max_it->second->min_bitrate_ + + bitrate_per_observer; + if (max_it->first < observer_allowance) { + // We have more than enough for this observer. + // Carry the remainder forward. + uint32_t remainder = observer_allowance - max_it->first; + if (number_of_observers != 0) { + bitrate_per_observer += remainder / number_of_observers; + } + max_it->second->observer_->OnNetworkChanged(max_it->first, fraction_loss, + rtt); + } else { + max_it->second->observer_->OnNetworkChanged(observer_allowance, + fraction_loss, rtt); + } + delete max_it->second; + list_max_bitrates.erase(max_it); + // Prepare next iteration. + max_it = list_max_bitrates.begin(); + } +} +} // namespace webrtc + diff --git a/src/modules/bitrate_controller/bitrate_controller_impl.h b/src/modules/bitrate_controller/bitrate_controller_impl.h new file mode 100644 index 000000000..d2a59e886 --- /dev/null +++ b/src/modules/bitrate_controller/bitrate_controller_impl.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * Usage: this class will register multiple RtcpBitrateObserver's one at each + * RTCP module. It will aggregate the results and run one bandwidth estimation + * and push the result to the encoder via VideoEncoderCallback. + */ + +#ifndef WEBRTC_MODULES_BITRATE_CONTROLLER_BITRATE_CONTROLLER_IMPL_H_ +#define WEBRTC_MODULES_BITRATE_CONTROLLER_BITRATE_CONTROLLER_IMPL_H_ + +#include "modules/bitrate_controller/include/bitrate_controller.h" + +#include + +#include "system_wrappers/interface/critical_section_wrapper.h" +#include "modules/bitrate_controller/send_side_bandwidth_estimation.h" + +namespace webrtc { + +class RtcpBandwidthObserverImpl; + +class BitrateControllerImpl : public BitrateController { + public: + friend class RtcpBandwidthObserverImpl; + + explicit BitrateControllerImpl(); + virtual ~BitrateControllerImpl(); + + virtual RtcpBandwidthObserver* CreateRtcpBandwidthObserver(); + + virtual void SetBitrateObserver(BitrateObserver* observer, + const uint32_t start_bitrate, + const uint32_t min_bitrate, + const uint32_t max_bitrate); + + virtual void RemoveBitrateObserver(BitrateObserver* observer); + + protected: + struct BitrateConfiguration { + BitrateConfiguration(uint32_t start_bitrate, + uint32_t min_bitrate, + uint32_t max_bitrate) + : start_bitrate_(start_bitrate), + min_bitrate_(min_bitrate), + max_bitrate_(max_bitrate) { + } + uint32_t start_bitrate_; + uint32_t min_bitrate_; + uint32_t max_bitrate_; + }; + struct ObserverConfiguration { + ObserverConfiguration(BitrateObserver* observer, + uint32_t bitrate) + : observer_(observer), + min_bitrate_(bitrate) { + } + BitrateObserver* observer_; + uint32_t min_bitrate_; + }; + + // Called by BitrateObserver's direct from the RTCP module. + void OnReceivedEstimatedBitrate(const uint32_t bitrate); + + void OnReceivedRtcpReceiverReport(const uint8_t fraction_loss, + const uint32_t rtt, + const int number_of_packets, + const uint32_t now_ms); + + private: + typedef std::multimap ObserverSortingMap; + + void OnNetworkChanged(const uint32_t bitrate, + const uint8_t fraction_loss, // 0 - 255. + const uint32_t rtt); + + CriticalSectionWrapper* critsect_; + SendSideBandwidthEstimation bandwidth_estimation_; + std::map bitrate_observers_; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_BITRATE_CONTROLLER_BITRATE_CONTROLLER_IMPL_H_ diff --git a/src/modules/bitrate_controller/include/bitrate_controller.h b/src/modules/bitrate_controller/include/bitrate_controller.h new file mode 100644 index 000000000..c3e1dfa66 --- /dev/null +++ b/src/modules/bitrate_controller/include/bitrate_controller.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * Usage: this class will register multiple RtcpBitrateObserver's one at each + * RTCP module. It will aggregate the results and run one bandwidth estimation + * and push the result to the encoders via BitrateObserver(s). + */ + +#ifndef WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_ +#define WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_ + +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" + +namespace webrtc { + +class BitrateObserver { + /* + * Observer class for the encoders, each encoder should implement this class + * to get the target bitrate. It also get the fraction loss and rtt to + * optimize its settings for this type of network. + */ + public: + virtual void OnNetworkChanged(const uint32_t targer_bitrate, + const uint8_t fraction_loss, // 0 - 255. + const uint32_t rtt) = 0; + + protected: + virtual ~BitrateObserver() {} +}; + +// TODO(pwestin) move code from vie_remb in here + +class BitrateController { +/* + * This class collects feedback from all streams sent to a peer (via + * RTCPBandwidthObservers). It does one aggregated send side bandwidth + * estimation and divide the available bitrate between all its registered + * BitrateObservers. + */ + public: + static BitrateController* CreateBitrateController(); + virtual ~BitrateController() {} + + virtual RtcpBandwidthObserver* CreateRtcpBandwidthObserver() = 0; + + /* + * Set the start and max send bitrate used by the bandwidth management. + * + * observer, updates bitrates if already in use. + * min_bitrate_kbit = 0 equals no min bitrate. + * max_bitrate_kit = 0 equals no max bitrate. + */ + virtual void SetBitrateObserver(BitrateObserver* observer, + const uint32_t start_bitrate, + const uint32_t min_bitrate, + const uint32_t max_bitrate) = 0; + + virtual void RemoveBitrateObserver(BitrateObserver* observer) = 0; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_ diff --git a/src/modules/bitrate_controller/send_side_bandwidth_estimation.cc b/src/modules/bitrate_controller/send_side_bandwidth_estimation.cc new file mode 100644 index 000000000..12c5f42fe --- /dev/null +++ b/src/modules/bitrate_controller/send_side_bandwidth_estimation.cc @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/bitrate_controller/send_side_bandwidth_estimation.h" + +#include // sqrt() + +#include "system_wrappers/interface/trace.h" + +namespace webrtc { + +SendSideBandwidthEstimation::SendSideBandwidthEstimation() + : critsect_(CriticalSectionWrapper::CreateCriticalSection()), + accumulate_lost_packets_Q8_(0), + accumulate_expected_packets_(0), + bitrate_(0), + min_bitrate_configured_(0), + max_bitrate_configured_(0), + last_fraction_loss_(0), + last_round_trip_time_(0), + bwe_incoming_(0), + time_last_increase_(0), + time_last_decrease_(0) { +} + +SendSideBandwidthEstimation::~SendSideBandwidthEstimation() { + delete critsect_; +} + +void SendSideBandwidthEstimation::SetSendBitrate(const uint32_t bitrate) { + CriticalSectionScoped cs(critsect_); + bitrate_ = bitrate; +} + +void SendSideBandwidthEstimation::SetMinMaxBitrate(const uint32_t min_bitrate, + const uint32_t max_bitrate) { + CriticalSectionScoped cs(critsect_); + min_bitrate_configured_ = min_bitrate; + if (max_bitrate == 0) { + // no max configured use 1Gbit/s + max_bitrate_configured_ = 1000000000; + } else { + max_bitrate_configured_ = max_bitrate; + } +} + +bool SendSideBandwidthEstimation::UpdateBandwidthEstimate( + const uint32_t bandwidth, + uint32_t* new_bitrate, + uint8_t* fraction_lost, + uint16_t* rtt) { + *new_bitrate = 0; + CriticalSectionScoped cs(critsect_); + + bwe_incoming_ = bandwidth; + + if (bitrate_ == 0) { + // SendSideBandwidthEstimation off + return false; + } + if (bwe_incoming_ > 0 && bitrate_ > bwe_incoming_) { + bitrate_ = bwe_incoming_; + *new_bitrate = bitrate_; + *fraction_lost = last_fraction_loss_; + *rtt = last_round_trip_time_; + return true; + } + return false; +} + +bool SendSideBandwidthEstimation::UpdatePacketLoss( + const int number_of_packets, + const uint32_t rtt, + const uint32_t now_ms, + uint8_t* loss, + uint32_t* new_bitrate) { + CriticalSectionScoped cs(critsect_); + + if (bitrate_ == 0) { + // SendSideBandwidthEstimation off + return false; + } + // Update RTT. + last_round_trip_time_ = rtt; + + // Check sequence number diff and weight loss report + if (number_of_packets > 0) { + // Calculate number of lost packets. + const int num_lost_packets_Q8 = *loss * number_of_packets; + // Accumulate reports. + accumulate_lost_packets_Q8_ += num_lost_packets_Q8; + accumulate_expected_packets_ += number_of_packets; + + // Report loss if the total report is based on sufficiently many packets. + if (accumulate_expected_packets_ >= kLimitNumPackets) { + *loss = accumulate_lost_packets_Q8_ / accumulate_expected_packets_; + + // Reset accumulators + accumulate_lost_packets_Q8_ = 0; + accumulate_expected_packets_ = 0; + } else { + // Report zero loss until we have enough data to estimate + // the loss rate. + return false; + } + } + // Keep for next time. + last_fraction_loss_ = *loss; + + uint32_t bitrate = ShapeSimple(*loss, rtt, now_ms); + if (bitrate == 0) { + // No change. + return false; + } + bitrate_ = bitrate; + *new_bitrate = bitrate; + return true; +} + +bool SendSideBandwidthEstimation::AvailableBandwidth( + uint32_t* bandwidth) const { + CriticalSectionScoped cs(critsect_); + if (bitrate_ == 0) { + return false; + } + *bandwidth = bitrate_; + return true; +} + +/* + * Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply. + * The formula in RFC 3448, Section 3.1, is used. + */ +uint32_t SendSideBandwidthEstimation::CalcTFRCbps(uint16_t rtt, uint8_t loss) { + if (rtt == 0 || loss == 0) { + // input variables out of range + return 0; + } + double R = static_cast(rtt) / 1000; // RTT in seconds + int b = 1; // number of packets acknowledged by a single TCP acknowledgement; + // recommended = 1 + double t_RTO = 4.0 * R; // TCP retransmission timeout value in seconds + // recommended = 4*R + double p = static_cast(loss) / 255; // packet loss rate in [0, 1) + double s = static_cast(kAvgPacketSizeBytes); + + // calculate send rate in bytes/second + double X = s / (R * sqrt(2 * b * p / 3) + + (t_RTO * (3 * sqrt(3 * b * p / 8) * p * (1 + 32 * p * p)))); + + return (static_cast(X * 8)); // bits/second +} + +uint32_t SendSideBandwidthEstimation::ShapeSimple(uint8_t loss, uint32_t rtt, + uint32_t now_ms) { + uint32_t new_bitrate = 0; + bool reducing = false; + + // Limit the rate increases to once a kBWEIncreaseIntervalMs. + if (loss <= 5) { + if ((now_ms - time_last_increase_) < kBWEIncreaseIntervalMs) { + return bitrate_; + } + time_last_increase_ = now_ms; + } + // Limit the rate decreases to once a kBWEDecreaseIntervalMs + rtt. + if (loss > 26) { + if ((now_ms - time_last_decrease_) < kBWEDecreaseIntervalMs + rtt) { + return bitrate_; + } + time_last_decrease_ = now_ms; + } + + if (loss > 5 && loss <= 26) { + // 2% - 10% + new_bitrate = bitrate_; + } else if (loss > 26) { + // 26/256 ~= 10% + // reduce rate: newRate = rate * (1 - 0.5*lossRate) + // packetLoss = 256*lossRate + new_bitrate = static_cast((bitrate_ * + static_cast(512 - loss)) / 512.0); + reducing = true; + } else { + // increase rate by 8% + new_bitrate = static_cast(bitrate_ * 1.08 + 0.5); + + // add 1 kbps extra, just to make sure that we do not get stuck + // (gives a little extra increase at low rates, negligible at higher rates) + new_bitrate += 1000; + } + if (reducing) { + // Calculate what rate TFRC would apply in this situation + // scale loss to Q0 (back to [0, 255]) + uint32_t tfrc_bitrate = CalcTFRCbps(rtt, loss); + if (tfrc_bitrate > new_bitrate) { + // do not reduce further if rate is below TFRC rate + new_bitrate = tfrc_bitrate; + } + } + if (bwe_incoming_ > 0 && new_bitrate > bwe_incoming_) { + new_bitrate = bwe_incoming_; + } + if (new_bitrate > max_bitrate_configured_) { + new_bitrate = max_bitrate_configured_; + } + if (new_bitrate < min_bitrate_configured_) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "The configured min bitrate (%u kbps) is greater than the " + "estimated available bandwidth (%u kbps).\n", + min_bitrate_configured_ / 1000, new_bitrate / 1000); + new_bitrate = min_bitrate_configured_; + } + return new_bitrate; +} +} // namespace webrtc diff --git a/src/modules/bitrate_controller/send_side_bandwidth_estimation.h b/src/modules/bitrate_controller/send_side_bandwidth_estimation.h new file mode 100644 index 000000000..aa49016ef --- /dev/null +++ b/src/modules/bitrate_controller/send_side_bandwidth_estimation.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * FEC and NACK added bitrate is handled outside class + */ + +#ifndef WEBRTC_MODULES_BITRATE_CONTROLLER_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ +#define WEBRTC_MODULES_BITRATE_CONTROLLER_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ + +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "system_wrappers/interface/critical_section_wrapper.h" + +namespace webrtc { +class SendSideBandwidthEstimation { + public: + SendSideBandwidthEstimation(); + virtual ~SendSideBandwidthEstimation(); + + // Call when we receive a RTCP message with TMMBR or REMB + // Return true if new_bitrate is valid. + bool UpdateBandwidthEstimate(const uint32_t bandwidth, + uint32_t* new_bitrate, + uint8_t* fraction_lost, + uint16_t* rtt); + + // Call when we receive a RTCP message with a ReceiveBlock + // Return true if new_bitrate is valid. + bool UpdatePacketLoss(const int number_of_packets, + const uint32_t rtt, + const uint32_t now_ms, + uint8_t* loss, + uint32_t* new_bitrate); + + // Return false if no bandwidth estimate is available + bool AvailableBandwidth(uint32_t* bandwidth) const; + void SetSendBitrate(const uint32_t bitrate); + void SetMinMaxBitrate(const uint32_t min_bitrate, const uint32_t max_bitrate); + + private: + uint32_t ShapeSimple(uint8_t loss, uint32_t rtt, uint32_t now_ms); + + uint32_t CalcTFRCbps(uint16_t rtt, uint8_t loss); + + enum { kBWEIncreaseIntervalMs = 1000 }; + enum { kBWEDecreaseIntervalMs = 300 }; + enum { kLimitNumPackets = 20 }; + enum { kAvgPacketSizeBytes = 1000 }; + + CriticalSectionWrapper* critsect_; + + // incoming filters + int accumulate_lost_packets_Q8_; + int accumulate_expected_packets_; + + uint32_t bitrate_; + uint32_t min_bitrate_configured_; + uint32_t max_bitrate_configured_; + + uint8_t last_fraction_loss_; + uint16_t last_round_trip_time_; + + uint32_t bwe_incoming_; + uint32_t time_last_increase_; + uint32_t time_last_decrease_; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_BITRATE_CONTROLLER_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ diff --git a/src/modules/bitrate_controller/test/test_bitrate_controller.cc b/src/modules/bitrate_controller/test/test_bitrate_controller.cc new file mode 100644 index 000000000..0e77f4437 --- /dev/null +++ b/src/modules/bitrate_controller/test/test_bitrate_controller.cc @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include +#include + +#include "modules/bitrate_controller/include/bitrate_controller.h" +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" + +using webrtc::RtcpBandwidthObserver; +using webrtc::BitrateObserver; +using webrtc::BitrateController; + +class TestBitrateObserver: public BitrateObserver { + public: + TestBitrateObserver() + : last_bitrate(0), + last_fraction_loss(0), + last_rtt(0) { + } + + virtual void OnNetworkChanged(const uint32_t bitrate, + const uint8_t fraction_loss, + const uint32_t rtt) { + last_bitrate = bitrate; + last_fraction_loss = fraction_loss; + last_rtt = rtt; + } + uint32_t last_bitrate; + uint8_t last_fraction_loss; + uint32_t last_rtt; +}; + +class BitrateControllerTest : public ::testing::Test { + protected: + BitrateControllerTest() { + } + ~BitrateControllerTest() {} + + virtual void SetUp() { + controller_ = BitrateController::CreateBitrateController(); + bandwidth_observer_ = controller_->CreateRtcpBandwidthObserver(); + } + + virtual void TearDown() { + delete bandwidth_observer_; + delete controller_; + } + BitrateController* controller_; + RtcpBandwidthObserver* bandwidth_observer_; +}; + +TEST_F(BitrateControllerTest, Basic) { + TestBitrateObserver bitrate_observer; + controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 300000); + controller_->RemoveBitrateObserver(&bitrate_observer); +} + +TEST_F(BitrateControllerTest, OneBitrateObserverOneRtcpObserver) { + TestBitrateObserver bitrate_observer; + controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 300000); + + // Receive a high remb, test bitrate inc. + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); + + // Test start bitrate. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 1, 1); + EXPECT_EQ(bitrate_observer.last_bitrate, 200000u); + EXPECT_EQ(bitrate_observer.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer.last_rtt, 50u); + + // Test bitrate increase 8% per second. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 21, 1001); + EXPECT_EQ(bitrate_observer.last_bitrate, 217000u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 41, 2001); + EXPECT_EQ(bitrate_observer.last_bitrate, 235360u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 61, 3001); + EXPECT_EQ(bitrate_observer.last_bitrate, 255189u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 81, 4001); + EXPECT_EQ(bitrate_observer.last_bitrate, 276604u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 101, 5001); + EXPECT_EQ(bitrate_observer.last_bitrate, 299732u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 121, 6001); + EXPECT_EQ(bitrate_observer.last_bitrate, 300000u); // Max cap. + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 141, 7001); + EXPECT_EQ(bitrate_observer.last_bitrate, 300000u); // Max cap. + + // Test that a low REMB trigger immediately. + bandwidth_observer_->OnReceivedEstimatedBitrate(250000); + EXPECT_EQ(bitrate_observer.last_bitrate, 250000u); + EXPECT_EQ(bitrate_observer.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer.last_rtt, 50u); + + bandwidth_observer_->OnReceivedEstimatedBitrate(1000); + EXPECT_EQ(bitrate_observer.last_bitrate, 100000u); // Min cap. + controller_->RemoveBitrateObserver(&bitrate_observer); +} + +TEST_F(BitrateControllerTest, OneBitrateObserverTwoRtcpObservers) { + TestBitrateObserver bitrate_observer; + controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 300000); + + RtcpBandwidthObserver* second_bandwidth_observer = + controller_->CreateRtcpBandwidthObserver(); + + // Receive a high remb, test bitrate inc. + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); + + // Test start bitrate. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 1, 1); + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 100, 1, 1); + EXPECT_EQ(bitrate_observer.last_bitrate, 200000u); + EXPECT_EQ(bitrate_observer.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer.last_rtt, 100u); + + // Test bitrate increase 8% per second. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 21, 1001); + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 100, 21, 1001); + EXPECT_EQ(bitrate_observer.last_bitrate, 217000u); + + // Extra report should not change estimate. + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 100, 31, 1501); + EXPECT_EQ(bitrate_observer.last_bitrate, 217000u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 41, 2001); + EXPECT_EQ(bitrate_observer.last_bitrate, 235360u); + // Second report should not change estimate. + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 100, 41, 2001); + EXPECT_EQ(bitrate_observer.last_bitrate, 235360u); + + // Reports from only one bandwidth observer is ok. + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 50, 61, 3001); + EXPECT_EQ(bitrate_observer.last_bitrate, 255189u); + + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 50, 81, 4001); + EXPECT_EQ(bitrate_observer.last_bitrate, 276604u); + + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 50, 101, 5001); + EXPECT_EQ(bitrate_observer.last_bitrate, 299732u); + + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 50, 121, 6001); + EXPECT_EQ(bitrate_observer.last_bitrate, 300000u); // Max cap. + + second_bandwidth_observer->OnReceivedRtcpReceiverReport(1, 0, 50, 141, 7001); + EXPECT_EQ(bitrate_observer.last_bitrate, 300000u); // Max cap. + + // Test that a low REMB trigger immediately. + // We don't care which bandwidth observer that delivers the REMB. + second_bandwidth_observer->OnReceivedEstimatedBitrate(250000); + EXPECT_EQ(bitrate_observer.last_bitrate, 250000u); + EXPECT_EQ(bitrate_observer.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer.last_rtt, 50u); + + bandwidth_observer_->OnReceivedEstimatedBitrate(1000); + EXPECT_EQ(bitrate_observer.last_bitrate, 100000u); // Min cap. + controller_->RemoveBitrateObserver(&bitrate_observer); + delete second_bandwidth_observer; +} + +TEST_F(BitrateControllerTest, TwoBitrateObserversOneRtcpObserver) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + controller_->SetBitrateObserver(&bitrate_observer_1, 200000, 100000, 300000); + controller_->SetBitrateObserver(&bitrate_observer_2, 200000, 200000, 300000); + + // Receive a high remb, test bitrate inc. + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); + + // Test too low start bitrate, hence lower than sum of min. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 1, 1); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 100000u); + EXPECT_EQ(bitrate_observer_1.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer_1.last_rtt, 50u); + + EXPECT_EQ(bitrate_observer_2.last_bitrate, 200000u); + EXPECT_EQ(bitrate_observer_2.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer_2.last_rtt, 50u); + + // Test bitrate increase 8% per second, distributed equaly. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 21, 1001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 112500u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 212500u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 41, 2001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 126000u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 226000u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 61, 3001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 140580u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 240580u); + + // Check that the bitrate sum honor our REMB. + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 81, 4001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 150000u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 250000u); + + // Remove REMB cap, higher than sum of max. + bandwidth_observer_->OnReceivedEstimatedBitrate(700000); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 101, 5001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 166500u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 266500u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 121, 6001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 184320u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 284320u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 141, 7001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 207130u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 300000u); // Max cap. + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 161, 8001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 248700u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 300000u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 181, 9001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 293596u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 300000u); + + bandwidth_observer_->OnReceivedRtcpReceiverReport(1, 0, 50, 201, 10001); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 300000u); // Max cap. + EXPECT_EQ(bitrate_observer_2.last_bitrate, 300000u); + + // Test that a low REMB trigger immediately. + bandwidth_observer_->OnReceivedEstimatedBitrate(350000); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 125000u); + EXPECT_EQ(bitrate_observer_1.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer_1.last_rtt, 50u); + EXPECT_EQ(bitrate_observer_2.last_bitrate, 225000u); + EXPECT_EQ(bitrate_observer_2.last_fraction_loss, 0); + EXPECT_EQ(bitrate_observer_2.last_rtt, 50u); + + bandwidth_observer_->OnReceivedEstimatedBitrate(1000); + EXPECT_EQ(bitrate_observer_1.last_bitrate, 100000u); // Min cap. + EXPECT_EQ(bitrate_observer_2.last_bitrate, 200000u); // Min cap. + controller_->RemoveBitrateObserver(&bitrate_observer_1); + controller_->RemoveBitrateObserver(&bitrate_observer_2); +} + + diff --git a/src/modules/bitrate_controller/test/test_bitrate_controller.gypi b/src/modules/bitrate_controller/test/test_bitrate_controller.gypi new file mode 100644 index 000000000..c118e34f9 --- /dev/null +++ b/src/modules/bitrate_controller/test/test_bitrate_controller.gypi @@ -0,0 +1,37 @@ +# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'targets': [ + { + 'target_name': 'test_bitrate_controller', + 'type': 'executable', + 'dependencies': [ + 'bitrate_controller', + '<(webrtc_root)/../test/test.gyp:test_support_main', + '<(webrtc_root)/../testing/gtest.gyp:gtest', + ], + + 'include_dirs': [ + '../include', + '../', + '../../../system_wrappers/interface', + ], + + 'sources': [ + 'test_bitrate_controller.cc', + ], + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/src/modules/modules.gyp b/src/modules/modules.gyp index 83b5cc62a..01b673e1b 100644 --- a/src/modules/modules.gyp +++ b/src/modules/modules.gyp @@ -26,6 +26,7 @@ 'audio_processing/agc/agc.gypi', 'audio_processing/ns/ns.gypi', 'audio_processing/utility/util.gypi', + 'bitrate_controller/bitrate_controller.gypi', 'media_file/source/media_file.gypi', 'udp_transport/source/udp_transport.gypi', 'utility/source/utility.gypi', @@ -46,6 +47,7 @@ 'audio_coding/codecs/iSAC/isac_test.gypi', 'audio_coding/codecs/iSAC/isacfix_test.gypi', 'audio_processing/apm_tests.gypi', + 'bitrate_controller/test/test_bitrate_controller.gypi', 'rtp_rtcp/source/rtp_rtcp_tests.gypi', 'rtp_rtcp/test/test_bwe/test_bwe.gypi', 'rtp_rtcp/test/testFec/test_fec.gypi', diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h index b6a8a547c..7ba3a8f72 100644 --- a/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h +++ b/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h @@ -213,23 +213,43 @@ protected: virtual ~RtpFeedback() {} }; -class RtpAudioFeedback -{ -public: - virtual void OnReceivedTelephoneEvent(const WebRtc_Word32 id, - const WebRtc_UWord8 event, - const bool endOfEvent) = 0; +class RtpAudioFeedback { + public: + virtual void OnReceivedTelephoneEvent(const WebRtc_Word32 id, + const WebRtc_UWord8 event, + const bool endOfEvent) = 0; - virtual void OnPlayTelephoneEvent(const WebRtc_Word32 id, - const WebRtc_UWord8 event, - const WebRtc_UWord16 lengthMs, - const WebRtc_UWord8 volume) = 0; - -protected: - virtual ~RtpAudioFeedback() {} + virtual void OnPlayTelephoneEvent(const WebRtc_Word32 id, + const WebRtc_UWord8 event, + const WebRtc_UWord16 lengthMs, + const WebRtc_UWord8 volume) = 0; + protected: + virtual ~RtpAudioFeedback() {} }; +class RtcpIntraFrameObserver { + public: + virtual void OnReceivedIntraFrameRequest(const uint32_t ssrc) = 0; + protected: + virtual ~RtcpIntraFrameObserver() {} +}; +class RtcpBandwidthObserver { + public: + // REMB or TMMBR + virtual void OnReceivedEstimatedBitrate(const uint32_t bitrate) = 0; + + virtual void OnReceivedRtcpReceiverReport( + const uint32_t ssrc, + const uint8_t fraction_loss, + const uint32_t rtt, + const uint32_t last_received_extended_high_seqNum, + const uint32_t now_ms) = 0; + + virtual ~RtcpBandwidthObserver() {} +}; + +// TODO(pwestin) To be depricated... class RtpVideoFeedback { public: @@ -250,33 +270,31 @@ protected: // A clock interface that allows reading of absolute and relative // timestamps in an RTP/RTCP module. class RtpRtcpClock { -public: - virtual ~RtpRtcpClock() {} + public: + virtual ~RtpRtcpClock() {} - // Return a timestamp in milliseconds relative to some arbitrary - // source; the source is fixed for this clock. - virtual WebRtc_UWord32 GetTimeInMS() = 0; + // Return a timestamp in milliseconds relative to some arbitrary + // source; the source is fixed for this clock. + virtual WebRtc_UWord32 GetTimeInMS() = 0; - // Retrieve an NTP absolute timestamp. - virtual void CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) = 0; + // Retrieve an NTP absolute timestamp. + virtual void CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) = 0; }; // RtpReceiveBitrateUpdate is used to signal changes in bitrate estimates for // the incoming stream. -class RtpRemoteBitrateObserver -{ +class RtpRemoteBitrateObserver { public: - // Called when a receive channel has a new bitrate estimate for the incoming - // stream. - virtual void OnReceiveBitrateChanged(unsigned int ssrc, - unsigned int bitrate) = 0; + // Called when a receive channel has a new bitrate estimate for the incoming + // stream. + virtual void OnReceiveBitrateChanged(uint32_t ssrc, + uint32_t bitrate) = 0; - // Called when a REMB packet has been received. - virtual void OnReceivedRemb(unsigned int bitrate) = 0; + // TODO(pwestin)To be depricated... + // Called when a REMB packet has been received. + virtual void OnReceivedRemb(uint32_t bitrate) = 0; - virtual ~RtpRemoteBitrateObserver() {} + virtual ~RtpRemoteBitrateObserver() {} }; - } // namespace webrtc - #endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_DEFINES_H_