Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,5 @@
danilchap@webrtc.org
stefan@webrtc.org
terelius@webrtc.org
mflodman@webrtc.org
perkj@webrtc.org

View file

@ -0,0 +1,401 @@
/*
* Copyright (c) 2014 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/remote_bitrate_estimator/aimd_rate_control.h"
#include <inttypes.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include "absl/strings/match.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
namespace {
constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(200);
constexpr double kDefaultBackoffFactor = 0.85;
constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
double ReadBackoffFactor(const FieldTrialsView& key_value_config) {
std::string experiment_string =
key_value_config.Lookup(kBweBackOffFactorExperiment);
double backoff_factor;
int parsed_values =
sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor);
if (parsed_values == 1) {
if (backoff_factor >= 1.0) {
RTC_LOG(LS_WARNING) << "Back-off factor must be less than 1.";
} else if (backoff_factor <= 0.0) {
RTC_LOG(LS_WARNING) << "Back-off factor must be greater than 0.";
} else {
return backoff_factor;
}
}
RTC_LOG(LS_WARNING) << "Failed to parse parameters for AimdRateControl "
"experiment from field trial string. Using default.";
return kDefaultBackoffFactor;
}
} // namespace
AimdRateControl::AimdRateControl(const FieldTrialsView& key_value_config)
: AimdRateControl(key_value_config, /* send_side =*/false) {}
AimdRateControl::AimdRateControl(const FieldTrialsView& key_value_config,
bool send_side)
: min_configured_bitrate_(kCongestionControllerMinBitrate),
max_configured_bitrate_(DataRate::KilobitsPerSec(30000)),
current_bitrate_(max_configured_bitrate_),
latest_estimated_throughput_(current_bitrate_),
link_capacity_(),
rate_control_state_(RateControlState::kRcHold),
time_last_bitrate_change_(Timestamp::MinusInfinity()),
time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
time_first_throughput_estimate_(Timestamp::MinusInfinity()),
bitrate_is_initialized_(false),
beta_(key_value_config.IsEnabled(kBweBackOffFactorExperiment)
? ReadBackoffFactor(key_value_config)
: kDefaultBackoffFactor),
in_alr_(false),
rtt_(kDefaultRtt),
send_side_(send_side),
no_bitrate_increase_in_alr_(
key_value_config.IsEnabled("WebRTC-DontIncreaseDelayBasedBweInAlr")),
subtract_additional_backoff_term_(!key_value_config.IsDisabled(
"WebRTC-Bwe-SubtractAdditionalBackoffTerm")) {
ParseFieldTrial(
{&disable_estimate_bounded_increase_,
&use_current_estimate_as_min_upper_bound_},
key_value_config.Lookup("WebRTC-Bwe-EstimateBoundedIncrease"));
RTC_LOG(LS_INFO) << "Using aimd rate control with back off factor " << beta_;
}
AimdRateControl::~AimdRateControl() {}
void AimdRateControl::SetStartBitrate(DataRate start_bitrate) {
current_bitrate_ = start_bitrate;
latest_estimated_throughput_ = current_bitrate_;
bitrate_is_initialized_ = true;
}
void AimdRateControl::SetMinBitrate(DataRate min_bitrate) {
min_configured_bitrate_ = min_bitrate;
current_bitrate_ = std::max(min_bitrate, current_bitrate_);
}
bool AimdRateControl::ValidEstimate() const {
return bitrate_is_initialized_;
}
TimeDelta AimdRateControl::GetFeedbackInterval() const {
// Estimate how often we can send RTCP if we allocate up to 5% of bandwidth
// to feedback.
const DataSize kRtcpSize = DataSize::Bytes(80);
const DataRate rtcp_bitrate = current_bitrate_ * 0.05;
const TimeDelta interval = kRtcpSize / rtcp_bitrate;
const TimeDelta kMinFeedbackInterval = TimeDelta::Millis(200);
const TimeDelta kMaxFeedbackInterval = TimeDelta::Millis(1000);
return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval);
}
bool AimdRateControl::TimeToReduceFurther(Timestamp at_time,
DataRate estimated_throughput) const {
const TimeDelta bitrate_reduction_interval =
rtt_.Clamped(TimeDelta::Millis(10), TimeDelta::Millis(200));
if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) {
return true;
}
if (ValidEstimate()) {
// TODO(terelius/holmer): Investigate consequences of increasing
// the threshold to 0.95 * LatestEstimate().
const DataRate threshold = 0.5 * LatestEstimate();
return estimated_throughput < threshold;
}
return false;
}
bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const {
return ValidEstimate() &&
TimeToReduceFurther(at_time,
LatestEstimate() / 2 - DataRate::BitsPerSec(1));
}
DataRate AimdRateControl::LatestEstimate() const {
return current_bitrate_;
}
void AimdRateControl::SetRtt(TimeDelta rtt) {
rtt_ = rtt;
}
DataRate AimdRateControl::Update(const RateControlInput& input,
Timestamp at_time) {
// Set the initial bit rate value to what we're receiving the first half
// second.
// TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code.
if (!bitrate_is_initialized_) {
const TimeDelta kInitializationTime = TimeDelta::Seconds(5);
RTC_DCHECK_LE(kBitrateWindow, kInitializationTime);
if (time_first_throughput_estimate_.IsInfinite()) {
if (input.estimated_throughput)
time_first_throughput_estimate_ = at_time;
} else if (at_time - time_first_throughput_estimate_ >
kInitializationTime &&
input.estimated_throughput) {
current_bitrate_ = *input.estimated_throughput;
bitrate_is_initialized_ = true;
}
}
ChangeBitrate(input, at_time);
return current_bitrate_;
}
void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) {
in_alr_ = in_alr;
}
void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) {
bitrate_is_initialized_ = true;
DataRate prev_bitrate = current_bitrate_;
current_bitrate_ = ClampBitrate(bitrate);
time_last_bitrate_change_ = at_time;
if (current_bitrate_ < prev_bitrate) {
time_last_bitrate_decrease_ = at_time;
}
}
void AimdRateControl::SetNetworkStateEstimate(
const absl::optional<NetworkStateEstimate>& estimate) {
network_estimate_ = estimate;
}
double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
RTC_DCHECK(!current_bitrate_.IsZero());
const TimeDelta kFrameInterval = TimeDelta::Seconds(1) / 30;
DataSize frame_size = current_bitrate_ * kFrameInterval;
const DataSize kPacketSize = DataSize::Bytes(1200);
double packets_per_frame = std::ceil(frame_size / kPacketSize);
DataSize avg_packet_size = frame_size / packets_per_frame;
// Approximate the over-use estimator delay to 100 ms.
TimeDelta response_time = rtt_ + TimeDelta::Millis(100);
response_time = response_time * 2;
double increase_rate_bps_per_second =
(avg_packet_size / response_time).bps<double>();
double kMinIncreaseRateBpsPerSecond = 4000;
return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second);
}
TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const {
const TimeDelta kMinPeriod = TimeDelta::Seconds(2);
const TimeDelta kDefaultPeriod = TimeDelta::Seconds(3);
const TimeDelta kMaxPeriod = TimeDelta::Seconds(50);
double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond();
if (!last_decrease_)
return kDefaultPeriod;
double time_to_recover_decrease_seconds =
last_decrease_->bps() / increase_rate_bps_per_second;
TimeDelta period = TimeDelta::Seconds(time_to_recover_decrease_seconds);
return period.Clamped(kMinPeriod, kMaxPeriod);
}
void AimdRateControl::ChangeBitrate(const RateControlInput& input,
Timestamp at_time) {
absl::optional<DataRate> new_bitrate;
DataRate estimated_throughput =
input.estimated_throughput.value_or(latest_estimated_throughput_);
if (input.estimated_throughput)
latest_estimated_throughput_ = *input.estimated_throughput;
// An over-use should always trigger us to reduce the bitrate, even though
// we have not yet established our first estimate. By acting on the over-use,
// we will end up with a valid estimate.
if (!bitrate_is_initialized_ &&
input.bw_state != BandwidthUsage::kBwOverusing)
return;
ChangeState(input, at_time);
switch (rate_control_state_) {
case RateControlState::kRcHold:
break;
case RateControlState::kRcIncrease: {
if (estimated_throughput > link_capacity_.UpperBound())
link_capacity_.Reset();
// We limit the new bitrate based on the troughput to avoid unlimited
// bitrate increases. We allow a bit more lag at very low rates to not too
// easily get stuck if the encoder produces uneven outputs.
DataRate increase_limit =
1.5 * estimated_throughput + DataRate::KilobitsPerSec(10);
if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) {
// Do not increase the delay based estimate in alr since the estimator
// will not be able to get transport feedback necessary to detect if
// the new estimate is correct.
// If we have previously increased above the limit (for instance due to
// probing), we don't allow further changes.
increase_limit = current_bitrate_;
}
if (current_bitrate_ < increase_limit) {
DataRate increased_bitrate = DataRate::MinusInfinity();
if (link_capacity_.has_estimate()) {
// The link_capacity estimate is reset if the measured throughput
// is too far from the estimate. We can therefore assume that our
// target rate is reasonably close to link capacity and use additive
// increase.
DataRate additive_increase =
AdditiveRateIncrease(at_time, time_last_bitrate_change_);
increased_bitrate = current_bitrate_ + additive_increase;
} else {
// If we don't have an estimate of the link capacity, use faster ramp
// up to discover the capacity.
DataRate multiplicative_increase = MultiplicativeRateIncrease(
at_time, time_last_bitrate_change_, current_bitrate_);
increased_bitrate = current_bitrate_ + multiplicative_increase;
}
new_bitrate = std::min(increased_bitrate, increase_limit);
}
time_last_bitrate_change_ = at_time;
break;
}
case RateControlState::kRcDecrease: {
DataRate decreased_bitrate = DataRate::PlusInfinity();
// Set bit rate to something slightly lower than the measured throughput
// to get rid of any self-induced delay.
decreased_bitrate = estimated_throughput * beta_;
if (decreased_bitrate > DataRate::KilobitsPerSec(5) &&
subtract_additional_backoff_term_) {
decreased_bitrate -= DataRate::KilobitsPerSec(5);
}
if (decreased_bitrate > current_bitrate_) {
// TODO(terelius): The link_capacity estimate may be based on old
// throughput measurements. Relying on them may lead to unnecessary
// BWE drops.
if (link_capacity_.has_estimate()) {
decreased_bitrate = beta_ * link_capacity_.estimate();
}
}
// Avoid increasing the rate when over-using.
if (decreased_bitrate < current_bitrate_) {
new_bitrate = decreased_bitrate;
}
if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) {
if (!new_bitrate.has_value()) {
last_decrease_ = DataRate::Zero();
} else {
last_decrease_ = current_bitrate_ - *new_bitrate;
}
}
if (estimated_throughput < link_capacity_.LowerBound()) {
// The current throughput is far from the estimated link capacity. Clear
// the estimate to allow an immediate update in OnOveruseDetected.
link_capacity_.Reset();
}
bitrate_is_initialized_ = true;
link_capacity_.OnOveruseDetected(estimated_throughput);
// Stay on hold until the pipes are cleared.
rate_control_state_ = RateControlState::kRcHold;
time_last_bitrate_change_ = at_time;
time_last_bitrate_decrease_ = at_time;
break;
}
default:
RTC_DCHECK_NOTREACHED();
}
current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_));
}
DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const {
if (!disable_estimate_bounded_increase_ && network_estimate_ &&
network_estimate_->link_capacity_upper.IsFinite()) {
DataRate upper_bound =
use_current_estimate_as_min_upper_bound_
? std::max(network_estimate_->link_capacity_upper, current_bitrate_)
: network_estimate_->link_capacity_upper;
new_bitrate = std::min(upper_bound, new_bitrate);
}
if (network_estimate_ && network_estimate_->link_capacity_lower.IsFinite() &&
new_bitrate < current_bitrate_) {
new_bitrate = std::min(
current_bitrate_,
std::max(new_bitrate, network_estimate_->link_capacity_lower * beta_));
}
new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
return new_bitrate;
}
DataRate AimdRateControl::MultiplicativeRateIncrease(
Timestamp at_time,
Timestamp last_time,
DataRate current_bitrate) const {
double alpha = 1.08;
if (last_time.IsFinite()) {
auto time_since_last_update = at_time - last_time;
alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0));
}
DataRate multiplicative_increase =
std::max(current_bitrate * (alpha - 1.0), DataRate::BitsPerSec(1000));
return multiplicative_increase;
}
DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time,
Timestamp last_time) const {
double time_period_seconds = (at_time - last_time).seconds<double>();
double data_rate_increase_bps =
GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds;
return DataRate::BitsPerSec(data_rate_increase_bps);
}
void AimdRateControl::ChangeState(const RateControlInput& input,
Timestamp at_time) {
switch (input.bw_state) {
case BandwidthUsage::kBwNormal:
if (rate_control_state_ == RateControlState::kRcHold) {
time_last_bitrate_change_ = at_time;
rate_control_state_ = RateControlState::kRcIncrease;
}
break;
case BandwidthUsage::kBwOverusing:
if (rate_control_state_ != RateControlState::kRcDecrease) {
rate_control_state_ = RateControlState::kRcDecrease;
}
break;
case BandwidthUsage::kBwUnderusing:
rate_control_state_ = RateControlState::kRcHold;
break;
default:
RTC_DCHECK_NOTREACHED();
}
}
} // namespace webrtc

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2014 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/link_capacity_estimator.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
// A rate control implementation based on additive increases of
// bitrate when no over-use is detected and multiplicative decreases when
// over-uses are detected. When we think the available bandwidth has changes or
// is unknown, we will switch to a "slow-start mode" where we increase
// multiplicatively.
class AimdRateControl {
public:
explicit AimdRateControl(const FieldTrialsView& key_value_config);
AimdRateControl(const FieldTrialsView& key_value_config, bool send_side);
~AimdRateControl();
// Returns true if the target bitrate has been initialized. This happens
// either if it has been explicitly set via SetStartBitrate/SetEstimate, or if
// we have measured a throughput.
bool ValidEstimate() const;
void SetStartBitrate(DataRate start_bitrate);
void SetMinBitrate(DataRate min_bitrate);
TimeDelta GetFeedbackInterval() const;
// Returns true if the bitrate estimate hasn't been changed for more than
// an RTT, or if the estimated_throughput is less than half of the current
// estimate. Should be used to decide if we should reduce the rate further
// when over-using.
bool TimeToReduceFurther(Timestamp at_time,
DataRate estimated_throughput) const;
// As above. To be used if overusing before we have measured a throughput.
bool InitialTimeToReduceFurther(Timestamp at_time) const;
DataRate LatestEstimate() const;
void SetRtt(TimeDelta rtt);
DataRate Update(const RateControlInput& input, Timestamp at_time);
void SetInApplicationLimitedRegion(bool in_alr);
void SetEstimate(DataRate bitrate, Timestamp at_time);
void SetNetworkStateEstimate(
const absl::optional<NetworkStateEstimate>& estimate);
// Returns the increase rate when used bandwidth is near the link capacity.
double GetNearMaxIncreaseRateBpsPerSecond() const;
// Returns the expected time between overuse signals (assuming steady state).
TimeDelta GetExpectedBandwidthPeriod() const;
private:
enum class RateControlState { kRcHold, kRcIncrease, kRcDecrease };
friend class GoogCcStatePrinter;
// Update the target bitrate based on, among other things, the current rate
// control state, the current target bitrate and the estimated throughput.
// When in the "increase" state the bitrate will be increased either
// additively or multiplicatively depending on the rate control region. When
// in the "decrease" state the bitrate will be decreased to slightly below the
// current throughput. When in the "hold" state the bitrate will be kept
// constant to allow built up queues to drain.
void ChangeBitrate(const RateControlInput& input, Timestamp at_time);
DataRate ClampBitrate(DataRate new_bitrate) const;
DataRate MultiplicativeRateIncrease(Timestamp at_time,
Timestamp last_ms,
DataRate current_bitrate) const;
DataRate AdditiveRateIncrease(Timestamp at_time, Timestamp last_time) const;
void UpdateChangePeriod(Timestamp at_time);
void ChangeState(const RateControlInput& input, Timestamp at_time);
DataRate min_configured_bitrate_;
DataRate max_configured_bitrate_;
DataRate current_bitrate_;
DataRate latest_estimated_throughput_;
LinkCapacityEstimator link_capacity_;
absl::optional<NetworkStateEstimate> network_estimate_;
RateControlState rate_control_state_;
Timestamp time_last_bitrate_change_;
Timestamp time_last_bitrate_decrease_;
Timestamp time_first_throughput_estimate_;
bool bitrate_is_initialized_;
double beta_;
bool in_alr_;
TimeDelta rtt_;
const bool send_side_;
// Allow the delay based estimate to only increase as long as application
// limited region (alr) is not detected.
const bool no_bitrate_increase_in_alr_;
// If true, subtract an additional 5kbps when backing off.
const bool subtract_additional_backoff_term_;
// If "Disabled", estimated link capacity is not used as upper bound.
FieldTrialFlag disable_estimate_bounded_increase_{"Disabled"};
FieldTrialParameter<bool> use_current_estimate_as_min_upper_bound_{"c_upper",
true};
absl::optional<DataRate> last_decrease_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2016 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/remote_bitrate_estimator/include/bwe_defines.h"
namespace webrtc {
const char kBweTypeHistogram[] = "WebRTC.BWE.Types";
RateControlInput::RateControlInput(
BandwidthUsage bw_state,
const absl::optional<DataRate>& estimated_throughput)
: bw_state(bw_state), estimated_throughput(estimated_throughput) {}
RateControlInput::~RateControlInput() = default;
} // namespace webrtc

View file

@ -0,0 +1,47 @@
/*
* 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/network_state_predictor.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
namespace webrtc {
inline constexpr DataRate kCongestionControllerMinBitrate =
DataRate::BitsPerSec(5'000);
inline constexpr TimeDelta kBitrateWindow = TimeDelta::Seconds(1);
extern const char kBweTypeHistogram[];
enum BweNames {
kReceiverNoExtension = 0,
kReceiverTOffset = 1,
kReceiverAbsSendTime = 2,
kSendSideTransportSeqNum = 3,
kBweNamesMax = 4
};
struct RateControlInput {
RateControlInput(BandwidthUsage bw_state,
const absl::optional<DataRate>& estimated_throughput);
~RateControlInput();
BandwidthUsage bw_state;
absl::optional<DataRate> estimated_throughput;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_

View file

@ -0,0 +1,64 @@
/*
* 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.
*/
// This class estimates the incoming available bandwidth.
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
#include <cstdint>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
namespace webrtc {
class Clock;
// RemoteBitrateObserver is used to signal changes in bitrate estimates for
// the incoming streams.
class RemoteBitrateObserver {
public:
// Called when a receive channel group has a new bitrate estimate for the
// incoming streams.
virtual void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
uint32_t bitrate) = 0;
virtual ~RemoteBitrateObserver() {}
};
class RemoteBitrateEstimator : public CallStatsObserver {
public:
~RemoteBitrateEstimator() override {}
// Called for each incoming packet. Updates the incoming payload bitrate
// estimate and the over-use detector. If an over-use is detected the
// remote bitrate estimate will be updated.
virtual void IncomingPacket(const RtpPacketReceived& rtp_packet) = 0;
// Removes all data for `ssrc`.
virtual void RemoveStream(uint32_t ssrc) = 0;
// Returns latest estimate or DataRate::Zero() if estimation is unavailable.
virtual DataRate LatestEstimate() const = 0;
virtual TimeDelta Process() = 0;
protected:
static constexpr TimeDelta kProcessInterval = TimeDelta::Millis(500);
static constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2);
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2013 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/remote_bitrate_estimator/inter_arrival.h"
#include "modules/include/module_common_types_public.h"
#include "rtc_base/logging.h"
namespace webrtc {
static const int kBurstDeltaThresholdMs = 5;
static const int kMaxBurstDurationMs = 100;
InterArrival::InterArrival(uint32_t timestamp_group_length_ticks,
double timestamp_to_ms_coeff)
: kTimestampGroupLengthTicks(timestamp_group_length_ticks),
current_timestamp_group_(),
prev_timestamp_group_(),
timestamp_to_ms_coeff_(timestamp_to_ms_coeff),
num_consecutive_reordered_packets_(0) {}
bool InterArrival::ComputeDeltas(uint32_t timestamp,
int64_t arrival_time_ms,
int64_t system_time_ms,
size_t packet_size,
uint32_t* timestamp_delta,
int64_t* arrival_time_delta_ms,
int* packet_size_delta) {
RTC_DCHECK(timestamp_delta);
RTC_DCHECK(arrival_time_delta_ms);
RTC_DCHECK(packet_size_delta);
bool calculated_deltas = false;
if (current_timestamp_group_.IsFirstPacket()) {
// We don't have enough data to update the filter, so we store it until we
// have two frames of data to process.
current_timestamp_group_.timestamp = timestamp;
current_timestamp_group_.first_timestamp = timestamp;
current_timestamp_group_.first_arrival_ms = arrival_time_ms;
} else if (!PacketInOrder(timestamp)) {
return false;
} else if (NewTimestampGroup(arrival_time_ms, timestamp)) {
// First packet of a later frame, the previous frame sample is ready.
if (prev_timestamp_group_.complete_time_ms >= 0) {
*timestamp_delta =
current_timestamp_group_.timestamp - prev_timestamp_group_.timestamp;
*arrival_time_delta_ms = current_timestamp_group_.complete_time_ms -
prev_timestamp_group_.complete_time_ms;
// Check system time differences to see if we have an unproportional jump
// in arrival time. In that case reset the inter-arrival computations.
int64_t system_time_delta_ms =
current_timestamp_group_.last_system_time_ms -
prev_timestamp_group_.last_system_time_ms;
if (*arrival_time_delta_ms - system_time_delta_ms >=
kArrivalTimeOffsetThresholdMs) {
RTC_LOG(LS_WARNING)
<< "The arrival time clock offset has changed (diff = "
<< *arrival_time_delta_ms - system_time_delta_ms
<< " ms), resetting.";
Reset();
return false;
}
if (*arrival_time_delta_ms < 0) {
// The group of packets has been reordered since receiving its local
// arrival timestamp.
++num_consecutive_reordered_packets_;
if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) {
RTC_LOG(LS_WARNING)
<< "Packets are being reordered on the path from the "
"socket to the bandwidth estimator. Ignoring this "
"packet for bandwidth estimation, resetting.";
Reset();
}
return false;
} else {
num_consecutive_reordered_packets_ = 0;
}
RTC_DCHECK_GE(*arrival_time_delta_ms, 0);
*packet_size_delta = static_cast<int>(current_timestamp_group_.size) -
static_cast<int>(prev_timestamp_group_.size);
calculated_deltas = true;
}
prev_timestamp_group_ = current_timestamp_group_;
// The new timestamp is now the current frame.
current_timestamp_group_.first_timestamp = timestamp;
current_timestamp_group_.timestamp = timestamp;
current_timestamp_group_.first_arrival_ms = arrival_time_ms;
current_timestamp_group_.size = 0;
} else {
current_timestamp_group_.timestamp =
LatestTimestamp(current_timestamp_group_.timestamp, timestamp);
}
// Accumulate the frame size.
current_timestamp_group_.size += packet_size;
current_timestamp_group_.complete_time_ms = arrival_time_ms;
current_timestamp_group_.last_system_time_ms = system_time_ms;
return calculated_deltas;
}
bool InterArrival::PacketInOrder(uint32_t timestamp) {
if (current_timestamp_group_.IsFirstPacket()) {
return true;
} else {
// Assume that a diff which is bigger than half the timestamp interval
// (32 bits) must be due to reordering. This code is almost identical to
// that in IsNewerTimestamp() in module_common_types.h.
uint32_t timestamp_diff =
timestamp - current_timestamp_group_.first_timestamp;
return timestamp_diff < 0x80000000;
}
}
// Assumes that `timestamp` is not reordered compared to
// `current_timestamp_group_`.
bool InterArrival::NewTimestampGroup(int64_t arrival_time_ms,
uint32_t timestamp) const {
if (current_timestamp_group_.IsFirstPacket()) {
return false;
} else if (BelongsToBurst(arrival_time_ms, timestamp)) {
return false;
} else {
uint32_t timestamp_diff =
timestamp - current_timestamp_group_.first_timestamp;
return timestamp_diff > kTimestampGroupLengthTicks;
}
}
bool InterArrival::BelongsToBurst(int64_t arrival_time_ms,
uint32_t timestamp) const {
RTC_DCHECK_GE(current_timestamp_group_.complete_time_ms, 0);
int64_t arrival_time_delta_ms =
arrival_time_ms - current_timestamp_group_.complete_time_ms;
uint32_t timestamp_diff = timestamp - current_timestamp_group_.timestamp;
int64_t ts_delta_ms = timestamp_to_ms_coeff_ * timestamp_diff + 0.5;
if (ts_delta_ms == 0)
return true;
int propagation_delta_ms = arrival_time_delta_ms - ts_delta_ms;
if (propagation_delta_ms < 0 &&
arrival_time_delta_ms <= kBurstDeltaThresholdMs &&
arrival_time_ms - current_timestamp_group_.first_arrival_ms <
kMaxBurstDurationMs)
return true;
return false;
}
void InterArrival::Reset() {
num_consecutive_reordered_packets_ = 0;
current_timestamp_group_ = TimestampGroup();
prev_timestamp_group_ = TimestampGroup();
}
} // namespace webrtc

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2013 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
// Helper class to compute the inter-arrival time delta and the size delta
// between two timestamp groups. A timestamp is a 32 bit unsigned number with
// a client defined rate.
class InterArrival {
public:
// After this many packet groups received out of order InterArrival will
// reset, assuming that clocks have made a jump.
static constexpr int kReorderedResetThreshold = 3;
static constexpr int64_t kArrivalTimeOffsetThresholdMs = 3000;
// A timestamp group is defined as all packets with a timestamp which are at
// most timestamp_group_length_ticks older than the first timestamp in that
// group.
InterArrival(uint32_t timestamp_group_length_ticks,
double timestamp_to_ms_coeff);
InterArrival() = delete;
InterArrival(const InterArrival&) = delete;
InterArrival& operator=(const InterArrival&) = delete;
// This function returns true if a delta was computed, or false if the current
// group is still incomplete or if only one group has been completed.
// `timestamp` is the timestamp.
// `arrival_time_ms` is the local time at which the packet arrived.
// `packet_size` is the size of the packet.
// `timestamp_delta` (output) is the computed timestamp delta.
// `arrival_time_delta_ms` (output) is the computed arrival-time delta.
// `packet_size_delta` (output) is the computed size delta.
bool ComputeDeltas(uint32_t timestamp,
int64_t arrival_time_ms,
int64_t system_time_ms,
size_t packet_size,
uint32_t* timestamp_delta,
int64_t* arrival_time_delta_ms,
int* packet_size_delta);
private:
struct TimestampGroup {
TimestampGroup()
: size(0),
first_timestamp(0),
timestamp(0),
first_arrival_ms(-1),
complete_time_ms(-1) {}
bool IsFirstPacket() const { return complete_time_ms == -1; }
size_t size;
uint32_t first_timestamp;
uint32_t timestamp;
int64_t first_arrival_ms;
int64_t complete_time_ms;
int64_t last_system_time_ms;
};
// Returns true if the packet with timestamp `timestamp` arrived in order.
bool PacketInOrder(uint32_t timestamp);
// Returns true if the last packet was the end of the current batch and the
// packet with `timestamp` is the first of a new batch.
bool NewTimestampGroup(int64_t arrival_time_ms, uint32_t timestamp) const;
bool BelongsToBurst(int64_t arrival_time_ms, uint32_t timestamp) const;
void Reset();
const uint32_t kTimestampGroupLengthTicks;
TimestampGroup current_timestamp_group_;
TimestampGroup prev_timestamp_group_;
double timestamp_to_ms_coeff_;
int num_consecutive_reordered_packets_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_

View file

@ -0,0 +1,103 @@
/*
* 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/remote_bitrate_estimator/overuse_detector.h"
#include <math.h>
#include <stdio.h>
#include <algorithm>
#include <string>
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
namespace {
constexpr double kMaxAdaptOffsetMs = 15.0;
constexpr double kOverUsingTimeThreshold = 10;
constexpr int kMaxNumDeltas = 60;
constexpr double kUp = 0.0087;
constexpr double kDown = 0.039;
} // namespace
OveruseDetector::OveruseDetector() = default;
BandwidthUsage OveruseDetector::State() const {
return hypothesis_;
}
BandwidthUsage OveruseDetector::Detect(double offset,
double ts_delta,
int num_of_deltas,
int64_t now_ms) {
if (num_of_deltas < 2) {
return BandwidthUsage::kBwNormal;
}
const double T = std::min(num_of_deltas, kMaxNumDeltas) * offset;
BWE_TEST_LOGGING_PLOT(1, "T", now_ms, T);
BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
if (T > threshold_) {
if (time_over_using_ == -1) {
// Initialize the timer. Assume that we've been
// over-using half of the time since the previous
// sample.
time_over_using_ = ts_delta / 2;
} else {
// Increment timer
time_over_using_ += ts_delta;
}
overuse_counter_++;
if (time_over_using_ > kOverUsingTimeThreshold && overuse_counter_ > 1) {
if (offset >= prev_offset_) {
time_over_using_ = 0;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwOverusing;
}
}
} else if (T < -threshold_) {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwUnderusing;
} else {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwNormal;
}
prev_offset_ = offset;
UpdateThreshold(T, now_ms);
return hypothesis_;
}
void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) {
if (last_update_ms_ == -1)
last_update_ms_ = now_ms;
if (fabs(modified_offset) > threshold_ + kMaxAdaptOffsetMs) {
// Avoid adapting the threshold to big latency spikes, caused e.g.,
// by a sudden capacity drop.
last_update_ms_ = now_ms;
return;
}
const double k = fabs(modified_offset) < threshold_ ? kDown : kUp;
const int64_t kMaxTimeDeltaMs = 100;
int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
threshold_ += k * (fabs(modified_offset) - threshold_) * time_delta_ms;
threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
last_update_ms_ = now_ms;
}
} // namespace webrtc

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
#include <stdint.h>
#include "api/network_state_predictor.h"
namespace webrtc {
class OveruseDetector {
public:
OveruseDetector();
OveruseDetector(const OveruseDetector&) = delete;
OveruseDetector& operator=(const OveruseDetector&) = delete;
~OveruseDetector() = default;
// Update the detection state based on the estimated inter-arrival time delta
// offset. `timestamp_delta` is the delta between the last timestamp which the
// estimated offset is based on and the last timestamp on which the last
// offset was based on, representing the time between detector updates.
// `num_of_deltas` is the number of deltas the offset estimate is based on.
// Returns the state after the detection update.
BandwidthUsage Detect(double offset,
double timestamp_delta,
int num_of_deltas,
int64_t now_ms);
// Returns the current detector state.
BandwidthUsage State() const;
private:
void UpdateThreshold(double modified_offset, int64_t now_ms);
double threshold_ = 12.5;
int64_t last_update_ms_ = -1;
double prev_offset_ = 0.0;
double time_over_using_ = -1;
int overuse_counter_ = 0;
BandwidthUsage hypothesis_ = BandwidthUsage::kBwNormal;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2013 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/remote_bitrate_estimator/overuse_estimator.h"
#include <math.h>
#include <string.h>
#include <algorithm>
#include "api/network_state_predictor.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr int kMinFramePeriodHistoryLength = 60;
constexpr int kDeltaCounterMax = 1000;
} // namespace
OveruseEstimator::OveruseEstimator() = default;
void OveruseEstimator::Update(int64_t t_delta,
double ts_delta,
int size_delta,
BandwidthUsage current_hypothesis,
int64_t now_ms) {
const double min_frame_period = UpdateMinFramePeriod(ts_delta);
const double t_ts_delta = t_delta - ts_delta;
BWE_TEST_LOGGING_PLOT(1, "dm_ms", now_ms, t_ts_delta);
double fs_delta = size_delta;
++num_of_deltas_;
if (num_of_deltas_ > kDeltaCounterMax) {
num_of_deltas_ = kDeltaCounterMax;
}
// Update the Kalman filter.
E_[0][0] += process_noise_[0];
E_[1][1] += process_noise_[1];
if ((current_hypothesis == BandwidthUsage::kBwOverusing &&
offset_ < prev_offset_) ||
(current_hypothesis == BandwidthUsage::kBwUnderusing &&
offset_ > prev_offset_)) {
E_[1][1] += 10 * process_noise_[1];
}
const double h[2] = {fs_delta, 1.0};
const double Eh[2] = {E_[0][0] * h[0] + E_[0][1] * h[1],
E_[1][0] * h[0] + E_[1][1] * h[1]};
BWE_TEST_LOGGING_PLOT(1, "d_ms", now_ms, slope_ * h[0] - offset_);
const double residual = t_ts_delta - slope_ * h[0] - offset_;
const bool in_stable_state =
(current_hypothesis == BandwidthUsage::kBwNormal);
const double max_residual = 3.0 * sqrt(var_noise_);
// We try to filter out very late frames. For instance periodic key
// frames doesn't fit the Gaussian model well.
if (fabs(residual) < max_residual) {
UpdateNoiseEstimate(residual, min_frame_period, in_stable_state);
} else {
UpdateNoiseEstimate(residual < 0 ? -max_residual : max_residual,
min_frame_period, in_stable_state);
}
const double denom = var_noise_ + h[0] * Eh[0] + h[1] * Eh[1];
const double K[2] = {Eh[0] / denom, Eh[1] / denom};
const double IKh[2][2] = {{1.0 - K[0] * h[0], -K[0] * h[1]},
{-K[1] * h[0], 1.0 - K[1] * h[1]}};
const double e00 = E_[0][0];
const double e01 = E_[0][1];
// Update state.
E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
// The covariance matrix must be positive semi-definite.
bool positive_semi_definite =
E_[0][0] + E_[1][1] >= 0 &&
E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0;
RTC_DCHECK(positive_semi_definite);
if (!positive_semi_definite) {
RTC_LOG(LS_ERROR)
<< "The over-use estimator's covariance matrix is no longer "
"semi-definite.";
}
slope_ = slope_ + K[0] * residual;
prev_offset_ = offset_;
offset_ = offset_ + K[1] * residual;
BWE_TEST_LOGGING_PLOT(1, "kc", now_ms, K[0]);
BWE_TEST_LOGGING_PLOT(1, "km", now_ms, K[1]);
BWE_TEST_LOGGING_PLOT(1, "slope_1/bps", now_ms, slope_);
BWE_TEST_LOGGING_PLOT(1, "var_noise", now_ms, var_noise_);
}
double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) {
double min_frame_period = ts_delta;
if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) {
ts_delta_hist_.pop_front();
}
for (const double old_ts_delta : ts_delta_hist_) {
min_frame_period = std::min(old_ts_delta, min_frame_period);
}
ts_delta_hist_.push_back(ts_delta);
return min_frame_period;
}
void OveruseEstimator::UpdateNoiseEstimate(double residual,
double ts_delta,
bool stable_state) {
if (!stable_state) {
return;
}
// Faster filter during startup to faster adapt to the jitter level
// of the network. `alpha` is tuned for 30 frames per second, but is scaled
// according to `ts_delta`.
double alpha = 0.01;
if (num_of_deltas_ > 10 * 30) {
alpha = 0.002;
}
// Only update the noise estimate if we're not over-using. `beta` is a
// function of alpha and the time delta since the previous update.
const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0);
avg_noise_ = beta * avg_noise_ + (1 - beta) * residual;
var_noise_ = beta * var_noise_ +
(1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual);
if (var_noise_ < 1) {
var_noise_ = 1;
}
}
} // namespace webrtc

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
#include <stdint.h>
#include <deque>
#include "api/network_state_predictor.h"
namespace webrtc {
class OveruseEstimator {
public:
OveruseEstimator();
OveruseEstimator(const OveruseEstimator&) = delete;
OveruseEstimator& operator=(const OveruseEstimator&) = delete;
~OveruseEstimator() = default;
// Update the estimator with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
// `current_hypothesis` should be the hypothesis of the over-use detector at
// this time.
void Update(int64_t t_delta,
double ts_delta,
int size_delta,
BandwidthUsage current_hypothesis,
int64_t now_ms);
// Returns the estimated noise/jitter variance in ms^2.
double var_noise() const { return var_noise_; }
// Returns the estimated inter-arrival time delta offset in ms.
double offset() const { return offset_; }
// Returns the number of deltas which the current over-use estimator state is
// based on.
int num_of_deltas() const { return num_of_deltas_; }
private:
double UpdateMinFramePeriod(double ts_delta);
void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state);
int num_of_deltas_ = 0;
double slope_ = 8.0 / 512.0;
double offset_ = 0;
double prev_offset_ = 0;
double E_[2][2] = {{100.0, 0.0}, {0.0, 1e-1}};
double process_noise_[2] = {1e-13, 1e-3};
double avg_noise_ = 0.0;
double var_noise_ = 50.0;
std::deque<double> ts_delta_hist_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2021 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/remote_bitrate_estimator/packet_arrival_map.h"
#include <algorithm>
#include <cstdint>
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
namespace webrtc {
void PacketArrivalTimeMap::AddPacket(int64_t sequence_number,
Timestamp arrival_time) {
RTC_DCHECK_GE(arrival_time, Timestamp::Zero());
if (!has_seen_packet()) {
// First packet.
Reallocate(kMinCapacity);
begin_sequence_number_ = sequence_number;
end_sequence_number_ = sequence_number + 1;
arrival_times_[Index(sequence_number)] = arrival_time;
return;
}
if (sequence_number >= begin_sequence_number() &&
sequence_number < end_sequence_number()) {
// The packet is within the buffer - no need to expand it.
arrival_times_[Index(sequence_number)] = arrival_time;
return;
}
if (sequence_number < begin_sequence_number()) {
// The packet goes before the current buffer. Expand to add packet, but only
// if it fits within kMaxNumberOfPackets.
int64_t new_size = end_sequence_number() - sequence_number;
if (new_size > kMaxNumberOfPackets) {
// Don't expand the buffer further, as that would remove newly received
// packets.
return;
}
AdjustToSize(new_size);
arrival_times_[Index(sequence_number)] = arrival_time;
SetNotReceived(sequence_number + 1, begin_sequence_number_);
begin_sequence_number_ = sequence_number;
return;
}
// The packet goes after the buffer.
RTC_DCHECK_GE(sequence_number, end_sequence_number_);
int64_t new_end_sequence_number = sequence_number + 1;
if (new_end_sequence_number >= end_sequence_number_ + kMaxNumberOfPackets) {
// All old packets have to be removed.
begin_sequence_number_ = sequence_number;
end_sequence_number_ = new_end_sequence_number;
arrival_times_[Index(sequence_number)] = arrival_time;
return;
}
if (begin_sequence_number_ < new_end_sequence_number - kMaxNumberOfPackets) {
// Remove oldest entries
begin_sequence_number_ = new_end_sequence_number - kMaxNumberOfPackets;
RTC_DCHECK_GT(end_sequence_number_, begin_sequence_number_);
}
AdjustToSize(new_end_sequence_number - begin_sequence_number_);
// Packets can be received out-of-order. If this isn't the next expected
// packet, add enough placeholders to fill the gap.
SetNotReceived(end_sequence_number_, sequence_number);
end_sequence_number_ = new_end_sequence_number;
arrival_times_[Index(sequence_number)] = arrival_time;
}
void PacketArrivalTimeMap::SetNotReceived(
int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive) {
static constexpr Timestamp value = Timestamp::MinusInfinity();
int begin_index = Index(begin_sequence_number_inclusive);
int end_index = Index(end_sequence_number_exclusive);
if (begin_index <= end_index) {
// Entries to clear are in single block:
// [......{-----}....]
std::fill(arrival_times_.get() + begin_index,
arrival_times_.get() + end_index, value);
} else {
// Entries to clear span across arrival_times_ border:
// [--}..........{---]
std::fill(arrival_times_.get() + begin_index,
arrival_times_.get() + capacity(), value);
std::fill(arrival_times_.get(), arrival_times_.get() + end_index, value);
}
}
void PacketArrivalTimeMap::RemoveOldPackets(int64_t sequence_number,
Timestamp arrival_time_limit) {
int64_t check_to = std::min(sequence_number, end_sequence_number_);
while (begin_sequence_number_ < check_to &&
arrival_times_[Index(begin_sequence_number_)] <= arrival_time_limit) {
++begin_sequence_number_;
}
AdjustToSize(end_sequence_number_ - begin_sequence_number_);
}
void PacketArrivalTimeMap::EraseTo(int64_t sequence_number) {
if (sequence_number < begin_sequence_number_) {
return;
}
if (sequence_number >= end_sequence_number_) {
// Erase all.
begin_sequence_number_ = end_sequence_number_;
return;
}
// Remove some.
begin_sequence_number_ = sequence_number;
AdjustToSize(end_sequence_number_ - begin_sequence_number_);
}
void PacketArrivalTimeMap::AdjustToSize(int new_size) {
if (new_size > capacity()) {
int new_capacity = capacity();
while (new_capacity < new_size)
new_capacity *= 2;
Reallocate(new_capacity);
}
if (capacity() > std::max(kMinCapacity, 4 * new_size)) {
int new_capacity = capacity();
while (new_capacity > 2 * std::max(new_size, kMinCapacity)) {
new_capacity /= 2;
}
Reallocate(new_capacity);
}
RTC_DCHECK_LE(new_size, capacity());
}
void PacketArrivalTimeMap::Reallocate(int new_capacity) {
int new_capacity_minus_1 = new_capacity - 1;
// Check capacity is a power of 2.
RTC_DCHECK_EQ(new_capacity & new_capacity_minus_1, 0);
// Create uninitialized memory.
// All valid entries should be set by `AddPacket` before use.
void* raw = operator new[](new_capacity * sizeof(Timestamp));
Timestamp* new_buffer = static_cast<Timestamp*>(raw);
for (int64_t sequence_number = begin_sequence_number_;
sequence_number < end_sequence_number_; ++sequence_number) {
new_buffer[sequence_number & new_capacity_minus_1] =
arrival_times_[sequence_number & capacity_minus_1_];
}
arrival_times_.reset(new_buffer);
capacity_minus_1_ = new_capacity_minus_1;
}
} // namespace webrtc

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
namespace webrtc {
// PacketArrivalTimeMap is an optimized map of packet sequence number to arrival
// time, limited in size to never exceed `kMaxNumberOfPackets`. It will grow as
// needed, and remove old packets, and will expand to allow earlier packets to
// be added (out-of-order).
//
// Not yet received packets have the arrival time zero. The queue will not span
// larger than necessary and the last packet should always be received. The
// first packet in the queue doesn't have to be received in case of receiving
// packets out-of-order.
class PacketArrivalTimeMap {
public:
struct PacketArrivalTime {
Timestamp arrival_time;
int64_t sequence_number;
};
// Impossible to request feedback older than what can be represented by 15
// bits.
static constexpr int kMaxNumberOfPackets = (1 << 15);
PacketArrivalTimeMap() = default;
PacketArrivalTimeMap(const PacketArrivalTimeMap&) = delete;
PacketArrivalTimeMap& operator=(const PacketArrivalTimeMap&) = delete;
~PacketArrivalTimeMap() = default;
// Indicates if the packet with `sequence_number` has already been received.
bool has_received(int64_t sequence_number) const {
return sequence_number >= begin_sequence_number() &&
sequence_number < end_sequence_number() &&
arrival_times_[Index(sequence_number)] >= Timestamp::Zero();
}
// Returns the sequence number of the first entry in the map, i.e. the
// sequence number that a `begin()` iterator would represent.
int64_t begin_sequence_number() const { return begin_sequence_number_; }
// Returns the sequence number of the element just after the map, i.e. the
// sequence number that an `end()` iterator would represent.
int64_t end_sequence_number() const { return end_sequence_number_; }
// Returns an element by `sequence_number`, which must be valid, i.e.
// between [begin_sequence_number, end_sequence_number).
Timestamp get(int64_t sequence_number) {
RTC_DCHECK_GE(sequence_number, begin_sequence_number());
RTC_DCHECK_LT(sequence_number, end_sequence_number());
return arrival_times_[Index(sequence_number)];
}
// Returns timestamp and sequence number of the received packet with sequence
// number equal or larger than `sequence_number`. `sequence_number` must be in
// range [begin_sequence_number, end_sequence_number).
PacketArrivalTime FindNextAtOrAfter(int64_t sequence_number) const {
RTC_DCHECK_GE(sequence_number, begin_sequence_number());
RTC_DCHECK_LT(sequence_number, end_sequence_number());
while (true) {
Timestamp t = arrival_times_[Index(sequence_number)];
if (t >= Timestamp::Zero()) {
return {.arrival_time = t, .sequence_number = sequence_number};
}
++sequence_number;
}
}
// Clamps `sequence_number` between [begin_sequence_number,
// end_sequence_number].
int64_t clamp(int64_t sequence_number) const {
return std::clamp(sequence_number, begin_sequence_number(),
end_sequence_number());
}
// Erases all elements from the beginning of the map until `sequence_number`.
void EraseTo(int64_t sequence_number);
// Records the fact that a packet with `sequence_number` arrived at
// `arrival_time_ms`.
void AddPacket(int64_t sequence_number, Timestamp arrival_time);
// Removes packets from the beginning of the map as long as they are received
// before `sequence_number` and with an age older than `arrival_time_limit`
void RemoveOldPackets(int64_t sequence_number, Timestamp arrival_time_limit);
private:
static constexpr int kMinCapacity = 128;
// Returns index in the `arrival_times_` for value for `sequence_number`.
int Index(int64_t sequence_number) const {
// Note that sequence_number might be negative, thus taking '%' requires
// extra handling and can be slow. Because capacity is a power of two, it
// is much faster to use '&' operator.
return sequence_number & capacity_minus_1_;
}
void SetNotReceived(int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive);
// Adjust capacity to match new_size, may reduce capacity.
// On return guarantees capacity >= new_size.
void AdjustToSize(int new_size);
void Reallocate(int new_capacity);
int capacity() const { return capacity_minus_1_ + 1; }
bool has_seen_packet() const { return arrival_times_ != nullptr; }
// Circular buffer. Packet with sequence number `sequence_number`
// is stored in the slot `sequence_number % capacity_`
std::unique_ptr<Timestamp[]> arrival_times_ = nullptr;
// Allocated size of the `arrival_times_`
// capacity_ is a power of 2 in range [kMinCapacity, kMaxNumberOfPackets]
// `capacity - 1` is used much more often than `capacity`, thus that value is
// stored.
int capacity_minus_1_ = -1;
// The unwrapped sequence number for valid range of sequence numbers.
// arrival_times_ entries only valid for sequence numbers in range
// `begin_sequence_number_ <= sequence_number < end_sequence_number_`
int64_t begin_sequence_number_ = 0;
int64_t end_sequence_number_ = 0;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_

View file

@ -0,0 +1,371 @@
/*
* Copyright (c) 2013 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/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h"
#include <math.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
constexpr TimeDelta kMinClusterDelta = TimeDelta::Millis(1);
constexpr TimeDelta kInitialProbingInterval = TimeDelta::Seconds(2);
constexpr int kTimestampGroupLengthMs = 5;
constexpr int kAbsSendTimeInterArrivalUpshift = 8;
constexpr int kInterArrivalShift =
RTPHeaderExtension::kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift;
constexpr int kMinClusterSize = 4;
constexpr int kMaxProbePackets = 15;
constexpr int kExpectedNumberOfProbes = 3;
constexpr double kTimestampToMs =
1000.0 / static_cast<double>(1 << kInterArrivalShift);
template <typename K, typename V>
std::vector<K> Keys(const std::map<K, V>& map) {
std::vector<K> keys;
keys.reserve(map.size());
for (const auto& kv_pair : map) {
keys.push_back(kv_pair.first);
}
return keys;
}
} // namespace
RemoteBitrateEstimatorAbsSendTime::~RemoteBitrateEstimatorAbsSendTime() =
default;
bool RemoteBitrateEstimatorAbsSendTime::IsWithinClusterBounds(
TimeDelta send_delta,
const Cluster& cluster_aggregate) {
if (cluster_aggregate.count == 0)
return true;
TimeDelta cluster_mean =
cluster_aggregate.send_mean / cluster_aggregate.count;
return (send_delta - cluster_mean).Abs() < TimeDelta::Micros(2'500);
}
void RemoteBitrateEstimatorAbsSendTime::MaybeAddCluster(
const Cluster& cluster_aggregate,
std::list<Cluster>& clusters) {
if (cluster_aggregate.count < kMinClusterSize ||
cluster_aggregate.send_mean <= TimeDelta::Zero() ||
cluster_aggregate.recv_mean <= TimeDelta::Zero()) {
return;
}
Cluster cluster;
cluster.send_mean = cluster_aggregate.send_mean / cluster_aggregate.count;
cluster.recv_mean = cluster_aggregate.recv_mean / cluster_aggregate.count;
cluster.mean_size = cluster_aggregate.mean_size / cluster_aggregate.count;
cluster.count = cluster_aggregate.count;
cluster.num_above_min_delta = cluster_aggregate.num_above_min_delta;
clusters.push_back(cluster);
}
RemoteBitrateEstimatorAbsSendTime::RemoteBitrateEstimatorAbsSendTime(
RemoteBitrateObserver* observer,
Clock* clock)
: clock_(clock), observer_(observer), remote_rate_(field_trials_) {
RTC_DCHECK(clock_);
RTC_DCHECK(observer_);
RTC_LOG(LS_INFO) << "RemoteBitrateEstimatorAbsSendTime: Instantiating.";
}
std::list<RemoteBitrateEstimatorAbsSendTime::Cluster>
RemoteBitrateEstimatorAbsSendTime::ComputeClusters() const {
std::list<Cluster> clusters;
Cluster cluster_aggregate;
Timestamp prev_send_time = Timestamp::MinusInfinity();
Timestamp prev_recv_time = Timestamp::MinusInfinity();
for (const Probe& probe : probes_) {
if (prev_send_time.IsFinite()) {
TimeDelta send_delta = probe.send_time - prev_send_time;
TimeDelta recv_delta = probe.recv_time - prev_recv_time;
if (send_delta >= kMinClusterDelta && recv_delta >= kMinClusterDelta) {
++cluster_aggregate.num_above_min_delta;
}
if (!IsWithinClusterBounds(send_delta, cluster_aggregate)) {
MaybeAddCluster(cluster_aggregate, clusters);
cluster_aggregate = Cluster();
}
cluster_aggregate.send_mean += send_delta;
cluster_aggregate.recv_mean += recv_delta;
cluster_aggregate.mean_size += probe.payload_size;
++cluster_aggregate.count;
}
prev_send_time = probe.send_time;
prev_recv_time = probe.recv_time;
}
MaybeAddCluster(cluster_aggregate, clusters);
return clusters;
}
const RemoteBitrateEstimatorAbsSendTime::Cluster*
RemoteBitrateEstimatorAbsSendTime::FindBestProbe(
const std::list<Cluster>& clusters) const {
DataRate highest_probe_bitrate = DataRate::Zero();
const Cluster* best = nullptr;
for (const auto& cluster : clusters) {
if (cluster.send_mean == TimeDelta::Zero() ||
cluster.recv_mean == TimeDelta::Zero()) {
continue;
}
if (cluster.num_above_min_delta > cluster.count / 2 &&
(cluster.recv_mean - cluster.send_mean <= TimeDelta::Millis(2) &&
cluster.send_mean - cluster.recv_mean <= TimeDelta::Millis(5))) {
DataRate probe_bitrate =
std::min(cluster.SendBitrate(), cluster.RecvBitrate());
if (probe_bitrate > highest_probe_bitrate) {
highest_probe_bitrate = probe_bitrate;
best = &cluster;
}
} else {
RTC_LOG(LS_INFO) << "Probe failed, sent at "
<< cluster.SendBitrate().bps() << " bps, received at "
<< cluster.RecvBitrate().bps()
<< " bps. Mean send delta: " << cluster.send_mean.ms()
<< " ms, mean recv delta: " << cluster.recv_mean.ms()
<< " ms, num probes: " << cluster.count;
break;
}
}
return best;
}
RemoteBitrateEstimatorAbsSendTime::ProbeResult
RemoteBitrateEstimatorAbsSendTime::ProcessClusters(Timestamp now) {
std::list<Cluster> clusters = ComputeClusters();
if (clusters.empty()) {
// If we reach the max number of probe packets and still have no clusters,
// we will remove the oldest one.
if (probes_.size() >= kMaxProbePackets)
probes_.pop_front();
return ProbeResult::kNoUpdate;
}
if (const Cluster* best = FindBestProbe(clusters)) {
DataRate probe_bitrate = std::min(best->SendBitrate(), best->RecvBitrate());
// Make sure that a probe sent on a lower bitrate than our estimate can't
// reduce the estimate.
if (IsBitrateImproving(probe_bitrate)) {
RTC_LOG(LS_INFO) << "Probe successful, sent at "
<< best->SendBitrate().bps() << " bps, received at "
<< best->RecvBitrate().bps()
<< " bps. Mean send delta: " << best->send_mean.ms()
<< " ms, mean recv delta: " << best->recv_mean.ms()
<< " ms, num probes: " << best->count;
remote_rate_.SetEstimate(probe_bitrate, now);
return ProbeResult::kBitrateUpdated;
}
}
// Not probing and received non-probe packet, or finished with current set
// of probes.
if (clusters.size() >= kExpectedNumberOfProbes)
probes_.clear();
return ProbeResult::kNoUpdate;
}
bool RemoteBitrateEstimatorAbsSendTime::IsBitrateImproving(
DataRate probe_bitrate) const {
bool initial_probe =
!remote_rate_.ValidEstimate() && probe_bitrate > DataRate::Zero();
bool bitrate_above_estimate = remote_rate_.ValidEstimate() &&
probe_bitrate > remote_rate_.LatestEstimate();
return initial_probe || bitrate_above_estimate;
}
void RemoteBitrateEstimatorAbsSendTime::IncomingPacket(
const RtpPacketReceived& rtp_packet) {
uint32_t send_time_24bits;
if (!rtp_packet.GetExtension<AbsoluteSendTime>(&send_time_24bits)) {
RTC_LOG(LS_WARNING)
<< "RemoteBitrateEstimatorAbsSendTimeImpl: Incoming packet "
"is missing absolute send time extension!";
return;
}
Timestamp arrival_time = rtp_packet.arrival_time();
DataSize payload_size =
DataSize::Bytes(rtp_packet.payload_size() + rtp_packet.padding_size());
if (!uma_recorded_) {
RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram, BweNames::kReceiverAbsSendTime,
BweNames::kBweNamesMax);
uma_recorded_ = true;
}
// Shift up send time to use the full 32 bits that inter_arrival works with,
// so wrapping works properly.
uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift;
Timestamp send_time =
Timestamp::Millis(static_cast<int64_t>(timestamp) * kTimestampToMs);
Timestamp now = clock_->CurrentTime();
// TODO(holmer): SSRCs are only needed for REMB, should be broken out from
// here.
// Check if incoming bitrate estimate is valid, and if it needs to be reset.
absl::optional<DataRate> incoming_bitrate =
incoming_bitrate_.Rate(arrival_time);
if (incoming_bitrate) {
incoming_bitrate_initialized_ = true;
} else if (incoming_bitrate_initialized_) {
// Incoming bitrate had a previous valid value, but now not enough data
// point are left within the current window. Reset incoming bitrate
// estimator so that the window size will only contain new data points.
incoming_bitrate_.Reset();
incoming_bitrate_initialized_ = false;
}
incoming_bitrate_.Update(payload_size, arrival_time);
if (first_packet_time_.IsInfinite()) {
first_packet_time_ = now;
}
uint32_t ts_delta = 0;
int64_t t_delta = 0;
int size_delta = 0;
bool update_estimate = false;
DataRate target_bitrate = DataRate::Zero();
TimeoutStreams(now);
RTC_DCHECK(inter_arrival_);
RTC_DCHECK(estimator_);
ssrcs_.insert_or_assign(rtp_packet.Ssrc(), now);
// For now only try to detect probes while we don't have a valid estimate.
// We currently assume that only packets larger than 200 bytes are paced by
// the sender.
static constexpr DataSize kMinProbePacketSize = DataSize::Bytes(200);
if (payload_size > kMinProbePacketSize &&
(!remote_rate_.ValidEstimate() ||
now - first_packet_time_ < kInitialProbingInterval)) {
// TODO(holmer): Use a map instead to get correct order?
if (total_probes_received_ < kMaxProbePackets) {
TimeDelta send_delta = TimeDelta::Millis(-1);
TimeDelta recv_delta = TimeDelta::Millis(-1);
if (!probes_.empty()) {
send_delta = send_time - probes_.back().send_time;
recv_delta = arrival_time - probes_.back().recv_time;
}
RTC_LOG(LS_INFO) << "Probe packet received: send time=" << send_time.ms()
<< " ms, recv time=" << arrival_time.ms()
<< " ms, send delta=" << send_delta.ms()
<< " ms, recv delta=" << recv_delta.ms() << " ms.";
}
probes_.emplace_back(send_time, arrival_time, payload_size);
++total_probes_received_;
// Make sure that a probe which updated the bitrate immediately has an
// effect by calling the OnReceiveBitrateChanged callback.
if (ProcessClusters(now) == ProbeResult::kBitrateUpdated)
update_estimate = true;
}
if (inter_arrival_->ComputeDeltas(timestamp, arrival_time.ms(), now.ms(),
payload_size.bytes(), &ts_delta, &t_delta,
&size_delta)) {
double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift);
estimator_->Update(t_delta, ts_delta_ms, size_delta, detector_.State(),
arrival_time.ms());
detector_.Detect(estimator_->offset(), ts_delta_ms,
estimator_->num_of_deltas(), arrival_time.ms());
}
if (!update_estimate) {
// Check if it's time for a periodic update or if we should update because
// of an over-use.
if (last_update_.IsInfinite() ||
now.ms() - last_update_.ms() >
remote_rate_.GetFeedbackInterval().ms()) {
update_estimate = true;
} else if (detector_.State() == BandwidthUsage::kBwOverusing) {
absl::optional<DataRate> incoming_rate =
incoming_bitrate_.Rate(arrival_time);
if (incoming_rate.has_value() &&
remote_rate_.TimeToReduceFurther(now, *incoming_rate)) {
update_estimate = true;
}
}
}
if (update_estimate) {
// The first overuse should immediately trigger a new estimate.
// We also have to update the estimate immediately if we are overusing
// and the target bitrate is too high compared to what we are receiving.
const RateControlInput input(detector_.State(),
incoming_bitrate_.Rate(arrival_time));
target_bitrate = remote_rate_.Update(input, now);
update_estimate = remote_rate_.ValidEstimate();
}
if (update_estimate) {
last_update_ = now;
observer_->OnReceiveBitrateChanged(Keys(ssrcs_),
target_bitrate.bps<uint32_t>());
}
}
TimeDelta RemoteBitrateEstimatorAbsSendTime::Process() {
return TimeDelta::PlusInfinity();
}
void RemoteBitrateEstimatorAbsSendTime::TimeoutStreams(Timestamp now) {
for (auto it = ssrcs_.begin(); it != ssrcs_.end();) {
if (now - it->second > kStreamTimeOut) {
ssrcs_.erase(it++);
} else {
++it;
}
}
if (ssrcs_.empty()) {
// We can't update the estimate if we don't have any active streams.
inter_arrival_ = std::make_unique<InterArrival>(
(kTimestampGroupLengthMs << kInterArrivalShift) / 1000, kTimestampToMs);
estimator_ = std::make_unique<OveruseEstimator>();
// We deliberately don't reset the first_packet_time_ms_ here for now since
// we only probe for bandwidth in the beginning of a call right now.
}
}
void RemoteBitrateEstimatorAbsSendTime::OnRttUpdate(int64_t avg_rtt_ms,
int64_t /*max_rtt_ms*/) {
remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms));
}
void RemoteBitrateEstimatorAbsSendTime::RemoveStream(uint32_t ssrc) {
ssrcs_.erase(ssrc);
}
DataRate RemoteBitrateEstimatorAbsSendTime::LatestEstimate() const {
// Currently accessed only from the worker thread (see Call::GetStats()).
if (!remote_rate_.ValidEstimate() || ssrcs_.empty()) {
return DataRate::Zero();
}
return remote_rate_.LatestEstimate();
}
} // namespace webrtc

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_ABS_SEND_TIME_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_ABS_SEND_TIME_H_
#include <stddef.h>
#include <stdint.h>
#include <list>
#include <map>
#include <memory>
#include <vector>
#include "api/rtp_headers.h"
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "modules/remote_bitrate_estimator/overuse_estimator.h"
#include "rtc_base/bitrate_tracker.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
class RemoteBitrateEstimatorAbsSendTime : public RemoteBitrateEstimator {
public:
RemoteBitrateEstimatorAbsSendTime(RemoteBitrateObserver* observer,
Clock* clock);
RemoteBitrateEstimatorAbsSendTime() = delete;
RemoteBitrateEstimatorAbsSendTime(const RemoteBitrateEstimatorAbsSendTime&) =
delete;
RemoteBitrateEstimatorAbsSendTime& operator=(
const RemoteBitrateEstimatorAbsSendTime&) = delete;
~RemoteBitrateEstimatorAbsSendTime() override;
void IncomingPacket(const RtpPacketReceived& rtp_packet) override;
TimeDelta Process() override;
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
void RemoveStream(uint32_t ssrc) override;
DataRate LatestEstimate() const override;
private:
struct Probe {
Probe(Timestamp send_time, Timestamp recv_time, DataSize payload_size)
: send_time(send_time),
recv_time(recv_time),
payload_size(payload_size) {}
Timestamp send_time;
Timestamp recv_time;
DataSize payload_size;
};
struct Cluster {
DataRate SendBitrate() const { return mean_size / send_mean; }
DataRate RecvBitrate() const { return mean_size / recv_mean; }
TimeDelta send_mean = TimeDelta::Zero();
TimeDelta recv_mean = TimeDelta::Zero();
// TODO(holmer): Add some variance metric as well?
DataSize mean_size = DataSize::Zero();
int count = 0;
int num_above_min_delta = 0;
};
enum class ProbeResult { kBitrateUpdated, kNoUpdate };
static bool IsWithinClusterBounds(TimeDelta send_delta,
const Cluster& cluster_aggregate);
static void MaybeAddCluster(const Cluster& cluster_aggregate,
std::list<Cluster>& clusters);
std::list<Cluster> ComputeClusters() const;
const Cluster* FindBestProbe(const std::list<Cluster>& clusters) const;
// Returns true if a probe which changed the estimate was detected.
ProbeResult ProcessClusters(Timestamp now);
bool IsBitrateImproving(DataRate probe_bitrate) const;
void TimeoutStreams(Timestamp now);
Clock* const clock_;
const FieldTrialBasedConfig field_trials_;
RemoteBitrateObserver* const observer_;
std::unique_ptr<InterArrival> inter_arrival_;
std::unique_ptr<OveruseEstimator> estimator_;
OveruseDetector detector_;
BitrateTracker incoming_bitrate_{kBitrateWindow};
bool incoming_bitrate_initialized_ = false;
std::list<Probe> probes_;
size_t total_probes_received_ = 0;
Timestamp first_packet_time_ = Timestamp::MinusInfinity();
Timestamp last_update_ = Timestamp::MinusInfinity();
bool uma_recorded_ = false;
std::map<uint32_t, Timestamp> ssrcs_;
AimdRateControl remote_rate_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_ABS_SEND_TIME_H_

View file

@ -0,0 +1,189 @@
/*
* 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/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
#include <cstdint>
#include <utility>
#include "absl/types/optional.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "modules/remote_bitrate_estimator/overuse_estimator.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
constexpr int kTimestampGroupLengthMs = 5;
constexpr double kTimestampToMs = 1.0 / 90.0;
} // namespace
RemoteBitrateEstimatorSingleStream::Detector::Detector()
: last_packet_time(Timestamp::Zero()),
inter_arrival(90 * kTimestampGroupLengthMs, kTimestampToMs) {}
RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream(
RemoteBitrateObserver* observer,
Clock* clock)
: clock_(clock),
incoming_bitrate_(kBitrateWindow),
last_valid_incoming_bitrate_(DataRate::Zero()),
remote_rate_(field_trials_),
observer_(observer),
process_interval_(kProcessInterval),
uma_recorded_(false) {
RTC_LOG(LS_INFO) << "RemoteBitrateEstimatorSingleStream: Instantiating.";
}
RemoteBitrateEstimatorSingleStream::~RemoteBitrateEstimatorSingleStream() =
default;
void RemoteBitrateEstimatorSingleStream::IncomingPacket(
const RtpPacketReceived& rtp_packet) {
absl::optional<int32_t> transmission_time_offset =
rtp_packet.GetExtension<TransmissionOffset>();
if (!uma_recorded_) {
BweNames type = transmission_time_offset.has_value()
? BweNames::kReceiverTOffset
: BweNames::kReceiverNoExtension;
RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram, type, BweNames::kBweNamesMax);
uma_recorded_ = true;
}
uint32_t ssrc = rtp_packet.Ssrc();
uint32_t rtp_timestamp =
rtp_packet.Timestamp() + transmission_time_offset.value_or(0);
Timestamp now = clock_->CurrentTime();
Detector& estimator = overuse_detectors_[ssrc];
estimator.last_packet_time = now;
// Check if incoming bitrate estimate is valid, and if it needs to be reset.
absl::optional<DataRate> incoming_bitrate = incoming_bitrate_.Rate(now);
if (incoming_bitrate) {
last_valid_incoming_bitrate_ = *incoming_bitrate;
} else if (last_valid_incoming_bitrate_ > DataRate::Zero()) {
// Incoming bitrate had a previous valid value, but now not enough data
// point are left within the current window. Reset incoming bitrate
// estimator so that the window size will only contain new data points.
incoming_bitrate_.Reset();
last_valid_incoming_bitrate_ = DataRate::Zero();
}
size_t payload_size = rtp_packet.payload_size() + rtp_packet.padding_size();
incoming_bitrate_.Update(payload_size, now);
const BandwidthUsage prior_state = estimator.detector.State();
uint32_t timestamp_delta = 0;
int64_t time_delta = 0;
int size_delta = 0;
int64_t now_ms = now.ms();
if (estimator.inter_arrival.ComputeDeltas(
rtp_timestamp, rtp_packet.arrival_time().ms(), now_ms, payload_size,
&timestamp_delta, &time_delta, &size_delta)) {
double timestamp_delta_ms = timestamp_delta * kTimestampToMs;
estimator.estimator.Update(time_delta, timestamp_delta_ms, size_delta,
estimator.detector.State(), now_ms);
estimator.detector.Detect(estimator.estimator.offset(), timestamp_delta_ms,
estimator.estimator.num_of_deltas(), now_ms);
}
if (estimator.detector.State() == BandwidthUsage::kBwOverusing) {
absl::optional<DataRate> incoming_bitrate = incoming_bitrate_.Rate(now);
if (incoming_bitrate.has_value() &&
(prior_state != BandwidthUsage::kBwOverusing ||
remote_rate_.TimeToReduceFurther(now, *incoming_bitrate))) {
// The first overuse should immediately trigger a new estimate.
// We also have to update the estimate immediately if we are overusing
// and the target bitrate is too high compared to what we are receiving.
UpdateEstimate(now);
}
}
}
TimeDelta RemoteBitrateEstimatorSingleStream::Process() {
Timestamp now = clock_->CurrentTime();
Timestamp next_process_time = last_process_time_.has_value()
? *last_process_time_ + process_interval_
: now;
// TODO(bugs.webrtc.org/13756): Removing rounding to milliseconds after
// investigating why tests fails without that rounding.
if (now.ms() >= next_process_time.ms()) {
UpdateEstimate(now);
last_process_time_ = now;
return process_interval_;
}
return next_process_time - now;
}
void RemoteBitrateEstimatorSingleStream::UpdateEstimate(Timestamp now) {
BandwidthUsage bw_state = BandwidthUsage::kBwNormal;
auto it = overuse_detectors_.begin();
while (it != overuse_detectors_.end()) {
if (now - it->second.last_packet_time > kStreamTimeOut) {
// This over-use detector hasn't received packets for `kStreamTimeOut`
// and is considered stale.
overuse_detectors_.erase(it++);
} else {
// Make sure that we trigger an over-use if any of the over-use detectors
// is detecting over-use.
if (it->second.detector.State() > bw_state) {
bw_state = it->second.detector.State();
}
++it;
}
}
// We can't update the estimate if we don't have any active streams.
if (overuse_detectors_.empty()) {
return;
}
const RateControlInput input(bw_state, incoming_bitrate_.Rate(now));
uint32_t target_bitrate = remote_rate_.Update(input, now).bps<uint32_t>();
if (remote_rate_.ValidEstimate()) {
process_interval_ = remote_rate_.GetFeedbackInterval();
RTC_DCHECK_GT(process_interval_, TimeDelta::Zero());
if (observer_)
observer_->OnReceiveBitrateChanged(GetSsrcs(), target_bitrate);
}
}
void RemoteBitrateEstimatorSingleStream::OnRttUpdate(int64_t avg_rtt_ms,
int64_t max_rtt_ms) {
remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms));
}
void RemoteBitrateEstimatorSingleStream::RemoveStream(uint32_t ssrc) {
overuse_detectors_.erase(ssrc);
}
DataRate RemoteBitrateEstimatorSingleStream::LatestEstimate() const {
if (!remote_rate_.ValidEstimate() || overuse_detectors_.empty()) {
return DataRate::Zero();
}
return remote_rate_.LatestEstimate();
}
std::vector<uint32_t> RemoteBitrateEstimatorSingleStream::GetSsrcs() const {
std::vector<uint32_t> ssrcs;
ssrcs.reserve(overuse_detectors_.size());
for (const auto& [ssrc, unused] : overuse_detectors_) {
ssrcs.push_back(ssrc);
}
return ssrcs;
}
} // namespace webrtc

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <vector>
#include "absl/types/optional.h"
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "modules/remote_bitrate_estimator/overuse_estimator.h"
#include "rtc_base/bitrate_tracker.h"
namespace webrtc {
class Clock;
struct RTPHeader;
class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator {
public:
RemoteBitrateEstimatorSingleStream(RemoteBitrateObserver* observer,
Clock* clock);
RemoteBitrateEstimatorSingleStream() = delete;
RemoteBitrateEstimatorSingleStream(
const RemoteBitrateEstimatorSingleStream&) = delete;
RemoteBitrateEstimatorSingleStream& operator=(
const RemoteBitrateEstimatorSingleStream&) = delete;
~RemoteBitrateEstimatorSingleStream() override;
void IncomingPacket(const RtpPacketReceived& rtp_packet) override;
TimeDelta Process() override;
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
void RemoveStream(uint32_t ssrc) override;
DataRate LatestEstimate() const override;
private:
struct Detector {
Detector();
Timestamp last_packet_time;
InterArrival inter_arrival;
OveruseEstimator estimator;
OveruseDetector detector;
};
// Triggers a new estimate calculation.
void UpdateEstimate(Timestamp now);
std::vector<uint32_t> GetSsrcs() const;
Clock* const clock_;
const FieldTrialBasedConfig field_trials_;
std::map<uint32_t, Detector> overuse_detectors_;
BitrateTracker incoming_bitrate_;
DataRate last_valid_incoming_bitrate_;
AimdRateControl remote_rate_;
RemoteBitrateObserver* const observer_;
absl::optional<Timestamp> last_process_time_;
TimeDelta process_interval_;
bool uma_recorded_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_

View file

@ -0,0 +1,351 @@
/*
* Copyright (c) 2015 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/remote_bitrate_estimator/remote_estimator_proxy.h"
#include <algorithm>
#include <cstdint>
#include <limits>
#include <memory>
#include <utility>
#include "absl/types/optional.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
namespace {
constexpr TimeDelta kBackWindow = TimeDelta::Millis(500);
constexpr TimeDelta kMinInterval = TimeDelta::Millis(50);
constexpr TimeDelta kMaxInterval = TimeDelta::Millis(250);
constexpr TimeDelta kDefaultInterval = TimeDelta::Millis(100);
TimeDelta GetAbsoluteSendTimeDelta(uint32_t new_sendtime,
uint32_t previous_sendtime) {
static constexpr uint32_t kWrapAroundPeriod = 0x0100'0000;
RTC_DCHECK_LT(new_sendtime, kWrapAroundPeriod);
RTC_DCHECK_LT(previous_sendtime, kWrapAroundPeriod);
uint32_t delta = (new_sendtime - previous_sendtime) % kWrapAroundPeriod;
if (delta >= kWrapAroundPeriod / 2) {
// absolute send time wraps around, thus treat deltas larger than half of
// the wrap around period as negative.
delta = (previous_sendtime - new_sendtime) % kWrapAroundPeriod;
return TimeDelta::Micros(int64_t{delta} * -1'000'000 / (1 << 18));
}
return TimeDelta::Micros(int64_t{delta} * 1'000'000 / (1 << 18));
}
} // namespace
RemoteEstimatorProxy::RemoteEstimatorProxy(
TransportFeedbackSender feedback_sender,
NetworkStateEstimator* network_state_estimator)
: feedback_sender_(std::move(feedback_sender)),
last_process_time_(Timestamp::MinusInfinity()),
network_state_estimator_(network_state_estimator),
media_ssrc_(0),
feedback_packet_count_(0),
packet_overhead_(DataSize::Zero()),
send_interval_(kDefaultInterval),
send_periodic_feedback_(true),
previous_abs_send_time_(0),
abs_send_timestamp_(Timestamp::Zero()),
last_arrival_time_with_abs_send_time_(Timestamp::MinusInfinity()) {
RTC_LOG(LS_INFO)
<< "Maximum interval between transport feedback RTCP messages: "
<< kMaxInterval;
}
RemoteEstimatorProxy::~RemoteEstimatorProxy() {}
void RemoteEstimatorProxy::MaybeCullOldPackets(int64_t sequence_number,
Timestamp arrival_time) {
if (periodic_window_start_seq_ >=
packet_arrival_times_.end_sequence_number() &&
arrival_time - Timestamp::Zero() >= kBackWindow) {
// Start new feedback packet, cull old packets.
packet_arrival_times_.RemoveOldPackets(sequence_number,
arrival_time - kBackWindow);
}
}
void RemoteEstimatorProxy::IncomingPacket(const RtpPacketReceived& packet) {
if (packet.arrival_time().IsInfinite()) {
RTC_LOG(LS_WARNING) << "Arrival time not set.";
return;
}
uint16_t seqnum = 0;
absl::optional<FeedbackRequest> feedback_request;
if (!packet.GetExtension<TransportSequenceNumber>(&seqnum) &&
!packet.GetExtension<TransportSequenceNumberV2>(&seqnum,
&feedback_request)) {
// This function expected to be called only for packets that have
// TransportSequenceNumber rtp header extension, however malformed RTP
// packet may contain unparsable TransportSequenceNumber.
RTC_DCHECK(packet.HasExtension<TransportSequenceNumber>() ||
packet.HasExtension<TransportSequenceNumberV2>())
<< " Expected transport sequence number.";
return;
}
MutexLock lock(&lock_);
send_periodic_feedback_ = packet.HasExtension<TransportSequenceNumber>();
media_ssrc_ = packet.Ssrc();
int64_t seq = unwrapper_.Unwrap(seqnum);
if (send_periodic_feedback_) {
MaybeCullOldPackets(seq, packet.arrival_time());
if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) {
periodic_window_start_seq_ = seq;
}
}
// We are only interested in the first time a packet is received.
if (packet_arrival_times_.has_received(seq)) {
return;
}
packet_arrival_times_.AddPacket(seq, packet.arrival_time());
// Limit the range of sequence numbers to send feedback for.
if (periodic_window_start_seq_ <
packet_arrival_times_.begin_sequence_number()) {
periodic_window_start_seq_ = packet_arrival_times_.begin_sequence_number();
}
if (feedback_request.has_value()) {
// Send feedback packet immediately.
SendFeedbackOnRequest(seq, *feedback_request);
}
absl::optional<uint32_t> absolute_send_time_24bits =
packet.GetExtension<AbsoluteSendTime>();
if (network_state_estimator_ && absolute_send_time_24bits.has_value()) {
PacketResult packet_result;
packet_result.receive_time = packet.arrival_time();
if (packet.arrival_time() - last_arrival_time_with_abs_send_time_ <
TimeDelta::Seconds(10)) {
abs_send_timestamp_ += GetAbsoluteSendTimeDelta(
*absolute_send_time_24bits, previous_abs_send_time_);
} else {
abs_send_timestamp_ = packet.arrival_time();
}
last_arrival_time_with_abs_send_time_ = packet.arrival_time();
previous_abs_send_time_ = *absolute_send_time_24bits;
packet_result.sent_packet.send_time = abs_send_timestamp_;
packet_result.sent_packet.size =
DataSize::Bytes(packet.size()) + packet_overhead_;
packet_result.sent_packet.sequence_number = seq;
network_state_estimator_->OnReceivedPacket(packet_result);
}
}
TimeDelta RemoteEstimatorProxy::Process(Timestamp now) {
MutexLock lock(&lock_);
if (!send_periodic_feedback_) {
// If TransportSequenceNumberV2 has been received in one packet,
// PeriodicFeedback is disabled for the rest of the call.
return TimeDelta::PlusInfinity();
}
Timestamp next_process_time = last_process_time_ + send_interval_;
if (now >= next_process_time) {
last_process_time_ = now;
SendPeriodicFeedbacks();
return send_interval_;
}
return next_process_time - now;
}
void RemoteEstimatorProxy::OnBitrateChanged(int bitrate_bps) {
// TwccReportSize = Ipv4(20B) + UDP(8B) + SRTP(10B) +
// AverageTwccReport(30B)
// TwccReport size at 50ms interval is 24 byte.
// TwccReport size at 250ms interval is 36 byte.
// AverageTwccReport = (TwccReport(50ms) + TwccReport(250ms)) / 2
constexpr DataSize kTwccReportSize = DataSize::Bytes(20 + 8 + 10 + 30);
constexpr DataRate kMinTwccRate = kTwccReportSize / kMaxInterval;
// Let TWCC reports occupy 5% of total bandwidth.
DataRate twcc_bitrate = DataRate::BitsPerSec(0.05 * bitrate_bps);
// Check upper send_interval bound by checking bitrate to avoid overflow when
// dividing by small bitrate, in particular avoid dividing by zero bitrate.
TimeDelta send_interval =
twcc_bitrate <= kMinTwccRate
? kMaxInterval
: std::max(kTwccReportSize / twcc_bitrate, kMinInterval);
MutexLock lock(&lock_);
send_interval_ = send_interval;
}
void RemoteEstimatorProxy::SetTransportOverhead(DataSize overhead_per_packet) {
MutexLock lock(&lock_);
packet_overhead_ = overhead_per_packet;
}
void RemoteEstimatorProxy::SendPeriodicFeedbacks() {
// `periodic_window_start_seq_` is the first sequence number to include in
// the current feedback packet. Some older may still be in the map, in case
// a reordering happens and we need to retransmit them.
if (!periodic_window_start_seq_)
return;
std::unique_ptr<rtcp::RemoteEstimate> remote_estimate;
if (network_state_estimator_) {
absl::optional<NetworkStateEstimate> state_estimate =
network_state_estimator_->GetCurrentEstimate();
if (state_estimate) {
remote_estimate = std::make_unique<rtcp::RemoteEstimate>();
remote_estimate->SetEstimate(state_estimate.value());
}
}
int64_t packet_arrival_times_end_seq =
packet_arrival_times_.end_sequence_number();
while (periodic_window_start_seq_ < packet_arrival_times_end_seq) {
auto feedback_packet = MaybeBuildFeedbackPacket(
/*include_timestamps=*/true, *periodic_window_start_seq_,
packet_arrival_times_end_seq,
/*is_periodic_update=*/true);
if (feedback_packet == nullptr) {
break;
}
RTC_DCHECK(feedback_sender_ != nullptr);
std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets;
if (remote_estimate) {
packets.push_back(std::move(remote_estimate));
}
packets.push_back(std::move(feedback_packet));
feedback_sender_(std::move(packets));
// Note: Don't erase items from packet_arrival_times_ after sending, in
// case they need to be re-sent after a reordering. Removal will be
// handled by OnPacketArrival once packets are too old.
}
}
void RemoteEstimatorProxy::SendFeedbackOnRequest(
int64_t sequence_number,
const FeedbackRequest& feedback_request) {
if (feedback_request.sequence_count == 0) {
return;
}
int64_t first_sequence_number =
sequence_number - feedback_request.sequence_count + 1;
auto feedback_packet = MaybeBuildFeedbackPacket(
feedback_request.include_timestamps, first_sequence_number,
sequence_number + 1, /*is_periodic_update=*/false);
// Even though this is called when a packet has just been added,
// no feedback may be produced when that new packet is too old.
if (feedback_packet == nullptr) {
return;
}
// Clear up to the first packet that is included in this feedback packet.
packet_arrival_times_.EraseTo(first_sequence_number);
RTC_DCHECK(feedback_sender_ != nullptr);
std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets;
packets.push_back(std::move(feedback_packet));
feedback_sender_(std::move(packets));
}
std::unique_ptr<rtcp::TransportFeedback>
RemoteEstimatorProxy::MaybeBuildFeedbackPacket(
bool include_timestamps,
int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive,
bool is_periodic_update) {
RTC_DCHECK_LT(begin_sequence_number_inclusive, end_sequence_number_exclusive);
int64_t start_seq =
packet_arrival_times_.clamp(begin_sequence_number_inclusive);
int64_t end_seq = packet_arrival_times_.clamp(end_sequence_number_exclusive);
// Create the packet on demand, as it's not certain that there are packets
// in the range that have been received.
std::unique_ptr<rtcp::TransportFeedback> feedback_packet;
int64_t next_sequence_number = begin_sequence_number_inclusive;
for (int64_t seq = start_seq; seq < end_seq; ++seq) {
PacketArrivalTimeMap::PacketArrivalTime packet =
packet_arrival_times_.FindNextAtOrAfter(seq);
seq = packet.sequence_number;
if (seq >= end_seq) {
break;
}
if (feedback_packet == nullptr) {
feedback_packet =
std::make_unique<rtcp::TransportFeedback>(include_timestamps);
feedback_packet->SetMediaSsrc(media_ssrc_);
// It should be possible to add `seq` to this new `feedback_packet`,
// If difference between `seq` and `begin_sequence_number_inclusive`,
// is too large, discard reporting too old missing packets.
static constexpr int kMaxMissingSequenceNumbers = 0x7FFE;
int64_t base_sequence_number = std::max(begin_sequence_number_inclusive,
seq - kMaxMissingSequenceNumbers);
// Base sequence number is the expected first sequence number. This is
// known, but we might not have actually received it, so the base time
// shall be the time of the first received packet in the feedback.
feedback_packet->SetBase(static_cast<uint16_t>(base_sequence_number),
packet.arrival_time);
feedback_packet->SetFeedbackSequenceNumber(feedback_packet_count_++);
if (!feedback_packet->AddReceivedPacket(static_cast<uint16_t>(seq),
packet.arrival_time)) {
// Could not add a single received packet to the feedback.
RTC_DCHECK_NOTREACHED()
<< "Failed to create an RTCP transport feedback with base sequence "
"number "
<< base_sequence_number << " and 1st received " << seq;
periodic_window_start_seq_ = seq;
return nullptr;
}
} else {
if (!feedback_packet->AddReceivedPacket(static_cast<uint16_t>(seq),
packet.arrival_time)) {
// Could not add timestamp, feedback packet might be full. Return and
// try again with a fresh packet.
break;
}
}
next_sequence_number = seq + 1;
}
if (is_periodic_update) {
periodic_window_start_seq_ = next_sequence_number;
}
return feedback_packet;
}
} // namespace webrtc

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_
#include <deque>
#include <functional>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/rtp_headers.h"
#include "api/transport/network_control.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/packet_arrival_map.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
// Class used when send-side BWE is enabled: This proxy is instantiated on the
// receive side. It buffers a number of receive timestamps and then sends
// transport feedback messages back too the send side.
class RemoteEstimatorProxy {
public:
// Used for sending transport feedback messages when send side
// BWE is used.
using TransportFeedbackSender = std::function<void(
std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets)>;
RemoteEstimatorProxy(TransportFeedbackSender feedback_sender,
NetworkStateEstimator* network_state_estimator);
~RemoteEstimatorProxy();
void IncomingPacket(const RtpPacketReceived& packet);
// Sends periodic feedback if it is time to send it.
// Returns time until next call to Process should be made.
TimeDelta Process(Timestamp now);
void OnBitrateChanged(int bitrate);
void SetTransportOverhead(DataSize overhead_per_packet);
private:
void MaybeCullOldPackets(int64_t sequence_number, Timestamp arrival_time)
RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
void SendPeriodicFeedbacks() RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
void SendFeedbackOnRequest(int64_t sequence_number,
const FeedbackRequest& feedback_request)
RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
// Returns a Transport Feedback packet with information about as many packets
// that has been received between [`begin_sequence_number_incl`,
// `end_sequence_number_excl`) that can fit in it. If `is_periodic_update`,
// this represents sending a periodic feedback message, which will make it
// update the `periodic_window_start_seq_` variable with the first packet that
// was not included in the feedback packet, so that the next update can
// continue from that sequence number.
//
// If no incoming packets were added, nullptr is returned.
//
// `include_timestamps` decide if the returned TransportFeedback should
// include timestamps.
std::unique_ptr<rtcp::TransportFeedback> MaybeBuildFeedbackPacket(
bool include_timestamps,
int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive,
bool is_periodic_update) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
const TransportFeedbackSender feedback_sender_;
Timestamp last_process_time_;
Mutex lock_;
// `network_state_estimator_` may be null.
NetworkStateEstimator* const network_state_estimator_
RTC_PT_GUARDED_BY(&lock_);
uint32_t media_ssrc_ RTC_GUARDED_BY(&lock_);
uint8_t feedback_packet_count_ RTC_GUARDED_BY(&lock_);
SeqNumUnwrapper<uint16_t> unwrapper_ RTC_GUARDED_BY(&lock_);
DataSize packet_overhead_ RTC_GUARDED_BY(&lock_);
// The next sequence number that should be the start sequence number during
// periodic reporting. Will be absl::nullopt before the first seen packet.
absl::optional<int64_t> periodic_window_start_seq_ RTC_GUARDED_BY(&lock_);
// Packet arrival times, by sequence number.
PacketArrivalTimeMap packet_arrival_times_ RTC_GUARDED_BY(&lock_);
TimeDelta send_interval_ RTC_GUARDED_BY(&lock_);
bool send_periodic_feedback_ RTC_GUARDED_BY(&lock_);
// Unwraps absolute send times.
uint32_t previous_abs_send_time_ RTC_GUARDED_BY(&lock_);
Timestamp abs_send_timestamp_ RTC_GUARDED_BY(&lock_);
Timestamp last_arrival_time_with_abs_send_time_ RTC_GUARDED_BY(&lock_);
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2013 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/remote_bitrate_estimator/test/bwe_test_logging.h"
#if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <algorithm>
#include "rtc_base/checks.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/strings/string_builder.h"
namespace webrtc {
namespace testing {
namespace bwe {
static std::string ToString(uint32_t v) {
rtc::StringBuilder ss;
ss << v;
return ss.Release();
}
Logging::ThreadState::ThreadState() = default;
Logging::ThreadState::~ThreadState() = default;
Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) {
Logging::GetInstance()->PushState(ToString(name), timestamp_ms, enabled);
}
Logging::Context::Context(const std::string& name,
int64_t timestamp_ms,
bool enabled) {
Logging::GetInstance()->PushState(name, timestamp_ms, enabled);
}
Logging::Context::Context(const char* name,
int64_t timestamp_ms,
bool enabled) {
Logging::GetInstance()->PushState(name, timestamp_ms, enabled);
}
Logging::Context::~Context() {
Logging::GetInstance()->PopState();
}
Logging* Logging::GetInstance() {
static Logging* logging = new Logging();
return logging;
}
void Logging::SetGlobalContext(uint32_t name) {
MutexLock lock(&mutex_);
thread_map_[rtc::CurrentThreadId()].global_state.tag = ToString(name);
}
void Logging::SetGlobalContext(const std::string& name) {
MutexLock lock(&mutex_);
thread_map_[rtc::CurrentThreadId()].global_state.tag = name;
}
void Logging::SetGlobalContext(const char* name) {
MutexLock lock(&mutex_);
thread_map_[rtc::CurrentThreadId()].global_state.tag = name;
}
void Logging::SetGlobalEnable(bool enabled) {
MutexLock lock(&mutex_);
thread_map_[rtc::CurrentThreadId()].global_state.enabled = enabled;
}
void Logging::Log(const char format[], ...) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("%s\t", state.tag.c_str());
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
}
}
void Logging::Plot(int figure, const std::string& name, double value) {
Plot(figure, name, value, 0, "-");
}
void Logging::Plot(int figure,
const std::string& name,
double value,
uint32_t ssrc) {
Plot(figure, name, value, ssrc, "-");
}
void Logging::Plot(int figure,
const std::string& name,
double value,
const std::string& alg_name) {
Plot(figure, name, value, 0, alg_name);
}
void Logging::Plot(int figure,
const std::string& name,
double value,
uint32_t ssrc,
const std::string& alg_name) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("PLOT\t%d\t%s:%" PRIu32 "@%s\t%f\t%f\n", figure, name.c_str(), ssrc,
alg_name.c_str(), state.timestamp_ms * 0.001, value);
}
}
void Logging::PlotBar(int figure,
const std::string& name,
double value,
int flow_id) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("BAR\t%d\t%s_%d\t%f\n", figure, name.c_str(), flow_id, value);
}
}
void Logging::PlotBaselineBar(int figure,
const std::string& name,
double value,
int flow_id) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("BASELINE\t%d\t%s_%d\t%f\n", figure, name.c_str(), flow_id, value);
}
}
void Logging::PlotErrorBar(int figure,
const std::string& name,
double value,
double ylow,
double yhigh,
const std::string& error_title,
int flow_id) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("ERRORBAR\t%d\t%s_%d\t%f\t%f\t%f\t%s\n", figure, name.c_str(),
flow_id, value, ylow, yhigh, error_title.c_str());
}
}
void Logging::PlotLimitErrorBar(int figure,
const std::string& name,
double value,
double ylow,
double yhigh,
const std::string& error_title,
double ymax,
const std::string& limit_title,
int flow_id) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("LIMITERRORBAR\t%d\t%s_%d\t%f\t%f\t%f\t%s\t%f\t%s\n", figure,
name.c_str(), flow_id, value, ylow, yhigh, error_title.c_str(), ymax,
limit_title.c_str());
}
}
void Logging::PlotLabel(int figure,
const std::string& title,
const std::string& y_label,
int num_flows) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("LABEL\t%d\t%s\t%s\t%d\n", figure, title.c_str(), y_label.c_str(),
num_flows);
}
}
Logging::Logging() : thread_map_() {}
Logging::~Logging() = default;
Logging::State::State() : tag(""), timestamp_ms(0), enabled(true) {}
Logging::State::State(const std::string& tag,
int64_t timestamp_ms,
bool enabled)
: tag(tag), timestamp_ms(timestamp_ms), enabled(enabled) {}
void Logging::State::MergePrevious(const State& previous) {
if (tag.empty()) {
tag = previous.tag;
} else if (!previous.tag.empty()) {
tag = previous.tag + "_" + tag;
}
timestamp_ms = std::max(previous.timestamp_ms, timestamp_ms);
enabled = previous.enabled && enabled;
}
void Logging::PushState(const std::string& append_to_tag,
int64_t timestamp_ms,
bool enabled) {
MutexLock lock(&mutex_);
State new_state(append_to_tag, timestamp_ms, enabled);
ThreadState* thread_state = &thread_map_[rtc::CurrentThreadId()];
std::stack<State>* stack = &thread_state->stack;
if (stack->empty()) {
new_state.MergePrevious(thread_state->global_state);
} else {
new_state.MergePrevious(stack->top());
}
stack->push(new_state);
}
void Logging::PopState() {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
std::stack<State>* stack = &it->second.stack;
int64_t newest_timestamp_ms = stack->top().timestamp_ms;
stack->pop();
if (!stack->empty()) {
State* state = &stack->top();
// Update time so that next log/plot will use the latest time seen so far
// in this call tree.
state->timestamp_ms = std::max(state->timestamp_ms, newest_timestamp_ms);
}
}
} // namespace bwe
} // namespace testing
} // namespace webrtc
#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE

View file

@ -0,0 +1,360 @@
/*
* Copyright (c) 2013 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_LOGGING_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_LOGGING_H_
// To enable BWE logging, run this command from trunk/ :
// build/gyp_chromium --depth=. webrtc/modules/modules.gyp
// -Denable_bwe_test_logging=1
#ifndef BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
#define BWE_TEST_LOGGING_COMPILE_TIME_ENABLE 0
#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
// BWE logging allows you to insert dynamically named log/plot points in the
// call tree. E.g. the function:
// void f1() {
// BWE_TEST_LOGGING_TIME(clock_->TimeInMilliseconds());
// BWE_TEST_LOGGING_CONTEXT("stream");
// for (uint32_t i=0; i<4; ++i) {
// BWE_TEST_LOGGING_ENABLE(i & 1);
// BWE_TEST_LOGGING_CONTEXT(i);
// BWE_TEST_LOGGING_LOG1("weight", "%f tonnes", weights_[i]);
// for (float j=0.0f; j<1.0; j+=0.4f) {
// BWE_TEST_LOGGING_PLOT(0, "bps", -1, j);
// }
// }
// }
//
// Might produce the output:
// stream_00000001_weight 13.000000 tonnes
// PLOT stream_00000001_bps 1.000000 0.000000
// PLOT stream_00000001_bps 1.000000 0.400000
// PLOT stream_00000001_bps 1.000000 0.800000
// stream_00000003_weight 39.000000 tonnes
// PLOT stream_00000003_bps 1.000000 0.000000
// PLOT stream_00000003_bps 1.000000 0.400000
// PLOT stream_00000003_bps 1.000000 0.800000
//
// Log *contexts* are names concatenated with '_' between them, with the name
// of the logged/plotted string/value last. Plot *time* is inherited down the
// tree. A branch is enabled by default but can be *disabled* to reduce output.
// The difference between the RTC_LOG and PLOT macros is that PLOT prefixes the
// line so it can be easily filtered, plus it outputs the current time.
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
// Set a thread-global base logging context. This name will be prepended to all
// hierarchical contexts.
// `name` is a char*, std::string or uint32_t to name the context.
#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name)
// Thread-globally allow/disallow logging.
// `enable` is expected to be a bool.
#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled)
// Insert a (hierarchical) logging context.
// `name` is a char*, std::string or uint32_t to name the context.
#define BWE_TEST_LOGGING_CONTEXT(name)
// Allow/disallow logging down the call tree from this point. Logging must be
// enabled all the way to the root of the call tree to take place.
// `enable` is expected to be a bool.
#define BWE_TEST_LOGGING_ENABLE(enabled)
// Set current time (only affects PLOT output). Down the call tree, the latest
// time set always takes precedence.
// `time` is an int64_t time in ms, or -1 to inherit time from previous context.
#define BWE_TEST_LOGGING_TIME(time)
// Print to stdout, e.g.:
// Context1_Context2_Name printf-formated-string
// `name` is a char*, std::string or uint32_t to name the log line.
// `format` is a printf format string.
// |_1...| are arguments for printf.
#define BWE_TEST_LOGGING_LOG1(name, format, _1)
#define BWE_TEST_LOGGING_LOG2(name, format, _1, _2)
#define BWE_TEST_LOGGING_LOG3(name, format, _1, _2, _3)
#define BWE_TEST_LOGGING_LOG4(name, format, _1, _2, _3, _4)
#define BWE_TEST_LOGGING_LOG5(name, format, _1, _2, _3, _4, _5)
// Print to stdout in tab-separated format suitable for plotting, e.g.:
// PLOT figure Context1_Context2_Name time value
// `figure` is a figure id. Different figures are plotted in different windows.
// `name` is a char*, std::string or uint32_t to name the plotted value.
// `time` is an int64_t time in ms, or -1 to inherit time from previous context.
// `value` is a double precision float to be plotted.
// `ssrc` identifies the source of a stream
// `alg_name` is an optional argument, a string
#define BWE_TEST_LOGGING_PLOT(figure, name, time, value)
#define BWE_TEST_LOGGING_PLOT_WITH_NAME(figure, name, time, value, alg_name)
#define BWE_TEST_LOGGING_PLOT_WITH_SSRC(figure, name, time, value, ssrc)
#define BWE_TEST_LOGGING_PLOT_WITH_NAME_AND_SSRC(figure, name, time, value, \
ssrc, alg_name)
// Print to stdout in tab-separated format suitable for plotting, e.g.:
// BAR figure Context1_Context2_Name x_left width value
// `figure` is a figure id. Different figures are plotted in different windows.
// `name` is a char*, std::string or uint32_t to name the plotted value.
// `value` is a double precision float to be plotted.
// `ylow` and `yhigh` are double precision float for the error line.
// `title` is a string and refers to the error label.
// `ymax` is a double precision float for the limit horizontal line.
// `limit_title` is a string and refers to the limit label.
#define BWE_TEST_LOGGING_BAR(figure, name, value, flow_id)
#define BWE_TEST_LOGGING_ERRORBAR(figure, name, value, ylow, yhigh, \
error_title, flow_id)
#define BWE_TEST_LOGGING_LIMITERRORBAR( \
figure, name, value, ylow, yhigh, error_title, ymax, limit_title, flow_id)
#define BWE_TEST_LOGGING_BASELINEBAR(figure, name, value, flow_id)
// `num_flows` is an integer refering to the number of RMCAT flows in the
// scenario.
// Define `x_label` and `y_label` for plots.
#define BWE_TEST_LOGGING_LABEL(figure, x_label, y_label, num_flows)
#else // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
#include <map>
#include <memory>
#include <stack>
#include <string>
#include "rtc_base/synchronization/mutex.h"
#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \
do { \
webrtc::testing::bwe::Logging::GetInstance()->SetGlobalContext(name); \
} while (0)
#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) \
do { \
webrtc::testing::bwe::Logging::GetInstance()->SetGlobalEnable(enabled); \
} while (0)
#define __BWE_TEST_LOGGING_CONTEXT_NAME(ctx, line) ctx##line
#define __BWE_TEST_LOGGING_CONTEXT_DECLARE(ctx, line, name, time, enabled) \
webrtc::testing::bwe::Logging::Context __BWE_TEST_LOGGING_CONTEXT_NAME( \
ctx, line)(name, time, enabled)
#define BWE_TEST_LOGGING_CONTEXT(name) \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __LINE__, name, -1, true)
#define BWE_TEST_LOGGING_ENABLE(enabled) \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __LINE__, "", -1, \
static_cast<bool>(enabled))
#define BWE_TEST_LOGGING_TIME(time) \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __LINE__, "", \
static_cast<int64_t>(time), true)
#define BWE_TEST_LOGGING_LOG1(name, format, _1) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1); \
} while (0)
#define BWE_TEST_LOGGING_LOG2(name, format, _1, _2) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2); \
} while (0)
#define BWE_TEST_LOGGING_LOG3(name, format, _1, _2, _3) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2, _3); \
} while (0)
#define BWE_TEST_LOGGING_LOG4(name, format, _1, _2, _3, _4) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2, _3, _4); \
} while (0)
#define BWE_TEST_LOGGING_LOG5(name, format, _1, _2, _3, _4, _5) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2, _3, _4, \
_5); \
} while (0)
#define BWE_TEST_LOGGING_PLOT(figure, name, time, value) \
do { \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \
static_cast<int64_t>(time), true); \
webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value); \
} while (0)
#define BWE_TEST_LOGGING_PLOT_WITH_NAME(figure, name, time, value, alg_name) \
do { \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \
static_cast<int64_t>(time), true); \
webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value, \
alg_name); \
} while (0)
#define BWE_TEST_LOGGING_PLOT_WITH_SSRC(figure, name, time, value, ssrc) \
do { \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \
static_cast<int64_t>(time), true); \
webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value, \
ssrc); \
} while (0)
#define BWE_TEST_LOGGING_PLOT_WITH_NAME_AND_SSRC(figure, name, time, value, \
ssrc, alg_name) \
do { \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \
static_cast<int64_t>(time), true); \
webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value, \
ssrc, alg_name); \
} while (0)
#define BWE_TEST_LOGGING_BAR(figure, name, value, flow_id) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->PlotBar(figure, name, value, \
flow_id); \
} while (0)
#define BWE_TEST_LOGGING_BASELINEBAR(figure, name, value, flow_id) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->PlotBaselineBar( \
figure, name, value, flow_id); \
} while (0)
#define BWE_TEST_LOGGING_ERRORBAR(figure, name, value, ylow, yhigh, title, \
flow_id) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->PlotErrorBar( \
figure, name, value, ylow, yhigh, title, flow_id); \
} while (0)
#define BWE_TEST_LOGGING_LIMITERRORBAR( \
figure, name, value, ylow, yhigh, error_title, ymax, limit_title, flow_id) \
do { \
BWE_TEST_LOGGING_CONTEXT(name); \
webrtc::testing::bwe::Logging::GetInstance()->PlotLimitErrorBar( \
figure, name, value, ylow, yhigh, error_title, ymax, limit_title, \
flow_id); \
} while (0)
#define BWE_TEST_LOGGING_LABEL(figure, title, y_label, num_flows) \
do { \
BWE_TEST_LOGGING_CONTEXT(title); \
webrtc::testing::bwe::Logging::GetInstance()->PlotLabel( \
figure, title, y_label, num_flows); \
} while (0)
namespace webrtc {
namespace testing {
namespace bwe {
class Logging {
public:
class Context {
public:
Context(uint32_t name, int64_t timestamp_ms, bool enabled);
Context(const std::string& name, int64_t timestamp_ms, bool enabled);
Context(const char* name, int64_t timestamp_ms, bool enabled);
Context() = delete;
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
~Context();
};
static Logging* GetInstance();
void SetGlobalContext(uint32_t name);
void SetGlobalContext(const std::string& name);
void SetGlobalContext(const char* name);
void SetGlobalEnable(bool enabled);
#if defined(__GNUC__)
// Note: Implicit `this` argument counts as the first argument.
__attribute__((__format__(__printf__, 2, 3)))
#endif
void
Log(const char format[], ...);
void Plot(int figure, const std::string& name, double value);
void Plot(int figure,
const std::string& name,
double value,
const std::string& alg_name);
void Plot(int figure, const std::string& name, double value, uint32_t ssrc);
void Plot(int figure,
const std::string& name,
double value,
uint32_t ssrc,
const std::string& alg_name);
void PlotBar(int figure, const std::string& name, double value, int flow_id);
void PlotBaselineBar(int figure,
const std::string& name,
double value,
int flow_id);
void PlotErrorBar(int figure,
const std::string& name,
double value,
double ylow,
double yhigh,
const std::string& error_title,
int flow_id);
void PlotLimitErrorBar(int figure,
const std::string& name,
double value,
double ylow,
double yhigh,
const std::string& error_title,
double ymax,
const std::string& limit_title,
int flow_id);
void PlotLabel(int figure,
const std::string& title,
const std::string& y_label,
int num_flows);
private:
struct State {
State();
State(const std::string& new_tag, int64_t timestamp_ms, bool enabled);
void MergePrevious(const State& previous);
std::string tag;
int64_t timestamp_ms;
bool enabled;
};
struct ThreadState {
ThreadState();
~ThreadState();
State global_state;
std::stack<State> stack;
};
typedef std::map<uint32_t, ThreadState> ThreadMap;
Logging();
~Logging();
Logging(const Logging&) = delete;
Logging& operator=(const Logging&) = delete;
void PushState(const std::string& append_to_tag,
int64_t timestamp_ms,
bool enabled);
void PopState();
Mutex mutex_;
ThreadMap thread_map_;
};
} // namespace bwe
} // namespace testing
} // namespace webrtc
#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_LOGGING_H_

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2014 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/remote_bitrate_estimator/tools/bwe_rtp.h"
#include <stdio.h>
#include <set>
#include <sstream>
#include <string>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "test/rtp_file_reader.h"
ABSL_FLAG(std::string,
extension_type,
"abs",
"Extension type, either abs for absolute send time or tsoffset "
"for timestamp offset.");
std::string ExtensionType() {
return absl::GetFlag(FLAGS_extension_type);
}
ABSL_FLAG(int, extension_id, 3, "Extension id.");
int ExtensionId() {
return absl::GetFlag(FLAGS_extension_id);
}
ABSL_FLAG(std::string, input_file, "", "Input file.");
std::string InputFile() {
return absl::GetFlag(FLAGS_input_file);
}
ABSL_FLAG(std::string,
ssrc_filter,
"",
"Comma-separated list of SSRCs in hexadecimal which are to be "
"used as input to the BWE (only applicable to pcap files).");
std::set<uint32_t> SsrcFilter() {
std::string ssrc_filter_string = absl::GetFlag(FLAGS_ssrc_filter);
if (ssrc_filter_string.empty())
return std::set<uint32_t>();
std::stringstream ss;
std::string ssrc_filter = ssrc_filter_string;
std::set<uint32_t> ssrcs;
// Parse the ssrcs in hexadecimal format.
ss << std::hex << ssrc_filter;
uint32_t ssrc;
while (ss >> ssrc) {
ssrcs.insert(ssrc);
ss.ignore(1, ',');
}
return ssrcs;
}
bool ParseArgsAndSetupRtpReader(
int argc,
char** argv,
std::unique_ptr<webrtc::test::RtpFileReader>& rtp_reader,
webrtc::RtpHeaderExtensionMap& rtp_header_extensions) {
absl::ParseCommandLine(argc, argv);
std::string filename = InputFile();
std::set<uint32_t> ssrc_filter = SsrcFilter();
fprintf(stderr, "Filter on SSRC: ");
for (auto& s : ssrc_filter) {
fprintf(stderr, "0x%08x, ", s);
}
fprintf(stderr, "\n");
if (filename.substr(filename.find_last_of('.')) == ".pcap") {
fprintf(stderr, "Opening as pcap\n");
rtp_reader.reset(webrtc::test::RtpFileReader::Create(
webrtc::test::RtpFileReader::kPcap, filename.c_str(), SsrcFilter()));
} else {
fprintf(stderr, "Opening as rtp\n");
rtp_reader.reset(webrtc::test::RtpFileReader::Create(
webrtc::test::RtpFileReader::kRtpDump, filename.c_str()));
}
if (!rtp_reader) {
fprintf(stderr, "Cannot open input file %s\n", filename.c_str());
return false;
}
fprintf(stderr, "Input file: %s\n\n", filename.c_str());
webrtc::RTPExtensionType extension = webrtc::kRtpExtensionAbsoluteSendTime;
if (ExtensionType() == "tsoffset") {
extension = webrtc::kRtpExtensionTransmissionTimeOffset;
fprintf(stderr, "Extension: toffset\n");
} else if (ExtensionType() == "abs") {
fprintf(stderr, "Extension: abs\n");
} else {
fprintf(stderr, "Unknown extension type\n");
return false;
}
rtp_header_extensions.RegisterByType(ExtensionId(), extension);
return true;
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2014 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_
#include <memory>
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "test/rtp_file_reader.h"
bool ParseArgsAndSetupRtpReader(
int argc,
char** argv,
std::unique_ptr<webrtc::test::RtpFileReader>& rtp_reader,
webrtc::RtpHeaderExtensionMap& rtp_header_extensions);
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2014 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 <stdio.h>
#include <memory>
#include "modules/remote_bitrate_estimator/tools/bwe_rtp.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "rtc_base/strings/string_builder.h"
#include "test/rtp_file_reader.h"
int main(int argc, char* argv[]) {
std::unique_ptr<webrtc::test::RtpFileReader> reader;
webrtc::RtpHeaderExtensionMap rtp_header_extensions;
if (!ParseArgsAndSetupRtpReader(argc, argv, reader, rtp_header_extensions)) {
return -1;
}
bool arrival_time_only = (argc >= 5 && strncmp(argv[4], "-t", 2) == 0);
fprintf(stdout,
"seqnum timestamp ts_offset abs_sendtime recvtime "
"markerbit ssrc size original_size\n");
int packet_counter = 0;
int non_zero_abs_send_time = 0;
int non_zero_ts_offsets = 0;
webrtc::test::RtpPacket packet;
while (reader->NextPacket(&packet)) {
webrtc::RtpPacket header(&rtp_header_extensions);
header.Parse(packet.data, packet.length);
uint32_t abs_send_time = 0;
if (header.GetExtension<webrtc::AbsoluteSendTime>(&abs_send_time) &&
abs_send_time != 0)
++non_zero_abs_send_time;
int32_t toffset = 0;
if (header.GetExtension<webrtc::TransmissionOffset>(&toffset) &&
toffset != 0)
++non_zero_ts_offsets;
if (arrival_time_only) {
rtc::StringBuilder ss;
ss << static_cast<int64_t>(packet.time_ms) * 1000000;
fprintf(stdout, "%s\n", ss.str().c_str());
} else {
fprintf(stdout, "%u %u %d %u %u %d %u %zu %zu\n", header.SequenceNumber(),
header.Timestamp(), toffset, abs_send_time, packet.time_ms,
header.Marker(), header.Ssrc(), packet.length,
packet.original_length);
}
++packet_counter;
}
fprintf(stderr, "Parsed %d packets\n", packet_counter);
fprintf(stderr, "Packets with non-zero absolute send time: %d\n",
non_zero_abs_send_time);
fprintf(stderr, "Packets with non-zero timestamp offset: %d\n",
non_zero_ts_offsets);
return 0;
}