Testing and improving NADA algorithm.
A modified operation mode was added, holding: --- Stricter conditions for AcceleratedRampUp. --- Smoother GradualRateUpdate adjustments. --- New AcceleratedRampDown update mode. This mode reduces significantly the delay for bitrates around its minimum bound. Several NADA unittests and a few simulations were added. Fixed LinkedSet bug. Fixed IsNewerSequenceNumber/IsNewerTimestamp bug. BUG=4550 R=stefan@webrtc.org, tommi@webrtc.org Review URL: https://webrtc-codereview.appspot.com/54399004 Cr-Commit-Position: refs/heads/master@{#9340}
This commit is contained in:
@@ -531,9 +531,14 @@ void TestIsObsoleteTimestamp(uint32_t limit_timestamp) {
|
||||
// 1 sample ahead is not old.
|
||||
EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp(
|
||||
limit_timestamp + 1, limit_timestamp, kZeroHorizon));
|
||||
// 2^31 samples ahead is not old.
|
||||
// If |t1-t2|=2^31 and t1>t2, t2 is older than t1 but not the opposite.
|
||||
uint32_t other_timestamp = limit_timestamp + (1 << 31);
|
||||
uint32_t lowest_timestamp = std::min(limit_timestamp, other_timestamp);
|
||||
uint32_t highest_timestamp = std::max(limit_timestamp, other_timestamp);
|
||||
EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp(
|
||||
lowest_timestamp, highest_timestamp, kZeroHorizon));
|
||||
EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp(
|
||||
limit_timestamp + (1 << 31), limit_timestamp, kZeroHorizon));
|
||||
highest_timestamp, lowest_timestamp, kZeroHorizon));
|
||||
|
||||
// Fixed horizon at 10 samples.
|
||||
static const uint32_t kHorizon = 10;
|
||||
|
||||
@@ -509,6 +509,18 @@ inline AudioFrame& AudioFrame::Append(const AudioFrame& rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline int16_t ClampToInt16(int32_t input) {
|
||||
if (input < -0x00008000) {
|
||||
return -0x8000;
|
||||
} else if (input > 0x00007FFF) {
|
||||
return 0x7FFF;
|
||||
} else {
|
||||
return static_cast<int16_t>(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) {
|
||||
// Sanity check
|
||||
assert((num_channels_ > 0) && (num_channels_ < 3));
|
||||
@@ -541,15 +553,9 @@ inline AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) {
|
||||
} else {
|
||||
// IMPROVEMENT this can be done very fast in assembly
|
||||
for (int i = 0; i < samples_per_channel_ * num_channels_; i++) {
|
||||
int32_t wrapGuard =
|
||||
int32_t wrap_guard =
|
||||
static_cast<int32_t>(data_[i]) + static_cast<int32_t>(rhs.data_[i]);
|
||||
if (wrapGuard < -32768) {
|
||||
data_[i] = -32768;
|
||||
} else if (wrapGuard > 32767) {
|
||||
data_[i] = 32767;
|
||||
} else {
|
||||
data_[i] = (int16_t)wrapGuard;
|
||||
}
|
||||
data_[i] = ClampToInt16(wrap_guard);
|
||||
}
|
||||
}
|
||||
energy_ = 0xffffffff;
|
||||
@@ -572,15 +578,9 @@ inline AudioFrame& AudioFrame::operator-=(const AudioFrame& rhs) {
|
||||
speech_type_ = kUndefined;
|
||||
|
||||
for (int i = 0; i < samples_per_channel_ * num_channels_; i++) {
|
||||
int32_t wrapGuard =
|
||||
int32_t wrap_guard =
|
||||
static_cast<int32_t>(data_[i]) - static_cast<int32_t>(rhs.data_[i]);
|
||||
if (wrapGuard < -32768) {
|
||||
data_[i] = -32768;
|
||||
} else if (wrapGuard > 32767) {
|
||||
data_[i] = 32767;
|
||||
} else {
|
||||
data_[i] = (int16_t)wrapGuard;
|
||||
}
|
||||
data_[i] = ClampToInt16(wrap_guard);
|
||||
}
|
||||
energy_ = 0xffffffff;
|
||||
return *this;
|
||||
@@ -588,11 +588,24 @@ inline AudioFrame& AudioFrame::operator-=(const AudioFrame& rhs) {
|
||||
|
||||
inline bool IsNewerSequenceNumber(uint16_t sequence_number,
|
||||
uint16_t prev_sequence_number) {
|
||||
// Distinguish between elements that are exactly 0x8000 apart.
|
||||
// If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false
|
||||
// rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false.
|
||||
if (static_cast<uint16_t>(sequence_number - prev_sequence_number) == 0x8000) {
|
||||
return sequence_number > prev_sequence_number;
|
||||
}
|
||||
return sequence_number != prev_sequence_number &&
|
||||
static_cast<uint16_t>(sequence_number - prev_sequence_number) < 0x8000;
|
||||
}
|
||||
|
||||
inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
|
||||
// Distinguish between elements that are exactly 0x80000000 apart.
|
||||
// If t1>t2 and |t1-t2| = 0x80000000: IsNewer(t1,t2)=true,
|
||||
// IsNewer(t2,t1)=false
|
||||
// rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false.
|
||||
if (static_cast<uint32_t>(timestamp - prev_timestamp) == 0x80000000) {
|
||||
return timestamp > prev_timestamp;
|
||||
}
|
||||
return timestamp != prev_timestamp &&
|
||||
static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ TEST(IsNewerSequenceNumber, BackwardWrap) {
|
||||
EXPECT_FALSE(IsNewerSequenceNumber(0xFF00, 0x00FF));
|
||||
}
|
||||
|
||||
TEST(IsNewerSequenceNumber, HalfWayApart) {
|
||||
EXPECT_TRUE(IsNewerSequenceNumber(0x8000, 0x0000));
|
||||
EXPECT_FALSE(IsNewerSequenceNumber(0x0000, 0x8000));
|
||||
}
|
||||
|
||||
TEST(IsNewerTimestamp, Equal) {
|
||||
EXPECT_FALSE(IsNewerTimestamp(0x00000001, 0x000000001));
|
||||
}
|
||||
@@ -62,6 +67,11 @@ TEST(IsNewerTimestamp, BackwardWrap) {
|
||||
EXPECT_FALSE(IsNewerTimestamp(0xFFFF0000, 0x0000FFFF));
|
||||
}
|
||||
|
||||
TEST(IsNewerTimestamp, HalfWayApart) {
|
||||
EXPECT_TRUE(IsNewerTimestamp(0x80000000, 0x00000000));
|
||||
EXPECT_FALSE(IsNewerTimestamp(0x00000000, 0x80000000));
|
||||
}
|
||||
|
||||
TEST(LatestSequenceNumber, NoWrap) {
|
||||
EXPECT_EQ(0xFFFFu, LatestSequenceNumber(0xFFFF, 0xFFFE));
|
||||
EXPECT_EQ(0x0001u, LatestSequenceNumber(0x0001, 0x0000));
|
||||
@@ -101,4 +111,15 @@ TEST(LatestTimestamp, Wrap) {
|
||||
EXPECT_EQ(0x0000FFFFu, LatestTimestamp(0xFFFFFFFF, 0x0000FFFF));
|
||||
EXPECT_EQ(0x0000FFFFu, LatestTimestamp(0xFFFF0000, 0x0000FFFF));
|
||||
}
|
||||
|
||||
TEST(ClampToInt16, TestCases) {
|
||||
EXPECT_EQ(0x0000, ClampToInt16(0x00000000));
|
||||
EXPECT_EQ(0x0001, ClampToInt16(0x00000001));
|
||||
EXPECT_EQ(0x7FFF, ClampToInt16(0x00007FFF));
|
||||
EXPECT_EQ(0x7FFF, ClampToInt16(0x7FFFFFFF));
|
||||
EXPECT_EQ(-0x0001, ClampToInt16(-0x00000001));
|
||||
EXPECT_EQ(-0x8000, ClampToInt16(-0x8000));
|
||||
EXPECT_EQ(-0x8000, ClampToInt16(-0x7FFFFFFF));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@@ -183,6 +183,44 @@ TEST_P(BweSimulation, GoogleWifiTrace3Mbps) {
|
||||
RunFor(300 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweSimulation, LinearIncreasingCapacity) {
|
||||
PeriodicKeyFrameSource source(0, 30, 300, 0, 0, 1000000);
|
||||
PacedVideoSender sender(&uplink_, &source, GetParam());
|
||||
ChokeFilter filter(&uplink_, 0);
|
||||
RateCounterFilter counter(&uplink_, 0, "receiver_input");
|
||||
PacketReceiver receiver(&uplink_, 0, GetParam(), true, true);
|
||||
filter.SetMaxDelay(500);
|
||||
const int kStartingCapacityKbps = 150;
|
||||
const int kEndingCapacityKbps = 1500;
|
||||
const int kStepKbps = 5;
|
||||
const int kStepTimeMs = 1000;
|
||||
|
||||
for (int i = kStartingCapacityKbps; i <= kEndingCapacityKbps;
|
||||
i += kStepKbps) {
|
||||
filter.SetCapacity(i);
|
||||
RunFor(kStepTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweSimulation, LinearDecreasingCapacity) {
|
||||
PeriodicKeyFrameSource source(0, 30, 300, 0, 0, 1000000);
|
||||
PacedVideoSender sender(&uplink_, &source, GetParam());
|
||||
ChokeFilter filter(&uplink_, 0);
|
||||
RateCounterFilter counter(&uplink_, 0, "receiver_input");
|
||||
PacketReceiver receiver(&uplink_, 0, GetParam(), true, true);
|
||||
filter.SetMaxDelay(500);
|
||||
const int kStartingCapacityKbps = 1500;
|
||||
const int kEndingCapacityKbps = 150;
|
||||
const int kStepKbps = -5;
|
||||
const int kStepTimeMs = 1000;
|
||||
|
||||
for (int i = kStartingCapacityKbps; i >= kEndingCapacityKbps;
|
||||
i += kStepKbps) {
|
||||
filter.SetCapacity(i);
|
||||
RunFor(kStepTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweSimulation, PacerGoogleWifiTrace3Mbps) {
|
||||
PeriodicKeyFrameSource source(0, 30, 300, 0, 0, 1000);
|
||||
PacedVideoSender sender(&uplink_, &source, GetParam());
|
||||
@@ -234,11 +272,6 @@ TEST_P(BweSimulation, PacedSelfFairnessTest) {
|
||||
RunFairnessTest(GetParam(), 4, 0, 1000, 3000, 50);
|
||||
}
|
||||
|
||||
TEST_P(BweSimulation, PacedTcpFairnessTest) {
|
||||
srand(Clock::GetRealTimeClock()->TimeInMicroseconds());
|
||||
RunFairnessTest(GetParam(), 4, 0, 1000, 3000, 500);
|
||||
}
|
||||
|
||||
TEST_P(BweSimulation, PacedSelfFairness1000msTest) {
|
||||
srand(Clock::GetRealTimeClock()->TimeInMicroseconds());
|
||||
RunFairnessTest(GetParam(), 4, 0, 1000, 3000, 1000);
|
||||
@@ -258,6 +291,7 @@ TEST_P(BweSimulation, TcpFairness1000msTest) {
|
||||
srand(Clock::GetRealTimeClock()->TimeInMicroseconds());
|
||||
RunFairnessTest(GetParam(), 1, 1, 1000, 2000, 1000);
|
||||
}
|
||||
|
||||
#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
|
||||
} // namespace bwe
|
||||
} // namespace testing
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/nada.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
|
||||
|
||||
namespace webrtc {
|
||||
@@ -27,6 +28,10 @@ namespace testing {
|
||||
namespace bwe {
|
||||
|
||||
const int NadaBweReceiver::kMedian;
|
||||
const int64_t NadaBweReceiver::kPacketLossTimeWindowMs;
|
||||
const int64_t NadaBweReceiver::kReceivingRateTimeWindowMs;
|
||||
const int NadaBweSender::kMinRefRateKbps;
|
||||
const int NadaBweSender::kMaxRefRateKbps;
|
||||
|
||||
NadaBweReceiver::NadaBweReceiver(int flow_id)
|
||||
: BweReceiver(flow_id),
|
||||
@@ -46,7 +51,7 @@ NadaBweReceiver::~NadaBweReceiver() {
|
||||
|
||||
void NadaBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
||||
const MediaPacket& media_packet) {
|
||||
const float kAlpha = 0.9f; // Used for exponential smoothing.
|
||||
const float kAlpha = 0.1f; // Used for exponential smoothing.
|
||||
const int64_t kDelayLowThresholdMs = 50; // Referred as d_th.
|
||||
const int64_t kDelayMaxThresholdMs = 400; // Referred as d_max.
|
||||
|
||||
@@ -70,8 +75,11 @@ void NadaBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
||||
est_queuing_delay_signal_ms_ = exp_smoothed_delay_ms_;
|
||||
} else if (exp_smoothed_delay_ms_ < kDelayMaxThresholdMs) {
|
||||
est_queuing_delay_signal_ms_ = static_cast<int64_t>(
|
||||
pow(0.001 * (kDelayMaxThresholdMs - exp_smoothed_delay_ms_), 4.0) /
|
||||
pow(0.001 * (kDelayMaxThresholdMs - kDelayLowThresholdMs), 4.0));
|
||||
pow((static_cast<double>(kDelayMaxThresholdMs -
|
||||
exp_smoothed_delay_ms_)) /
|
||||
(kDelayMaxThresholdMs - kDelayLowThresholdMs),
|
||||
4.0) *
|
||||
kDelayLowThresholdMs);
|
||||
} else {
|
||||
est_queuing_delay_signal_ms_ = 0;
|
||||
}
|
||||
@@ -88,8 +96,10 @@ FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int64_t loss_signal_ms = static_cast<int64_t>(
|
||||
RecentPacketLossRatio() * kPacketLossPenaltyMs + 0.5f);
|
||||
float loss_fraction = RecentPacketLossRatio();
|
||||
|
||||
int64_t loss_signal_ms =
|
||||
static_cast<int64_t>(loss_fraction * kPacketLossPenaltyMs + 0.5f);
|
||||
int64_t congestion_signal_ms = est_queuing_delay_signal_ms_ + loss_signal_ms;
|
||||
|
||||
float derivative = 0.0f;
|
||||
@@ -102,7 +112,7 @@ FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
|
||||
|
||||
PacketIdentifierNode* latest = *(received_packets_->begin());
|
||||
int64_t corrected_send_time_ms =
|
||||
latest->send_time_ms_ + now_ms - latest->arrival_time_ms_;
|
||||
latest->send_time_ms + now_ms - latest->arrival_time_ms;
|
||||
|
||||
// Sends a tuple containing latest values of <d_hat_n, d_tilde_n, x_n, x'_n,
|
||||
// R_r> and additional information.
|
||||
@@ -138,7 +148,6 @@ float NadaBweReceiver::GlobalPacketLossRatio() {
|
||||
// E.g.: for a timewindow covering 5 packets of the following arrival sequence
|
||||
// {10 7 9 5 6} 8 3 2 4 1, the output will be 1/6 (#8 is considered as missing).
|
||||
float NadaBweReceiver::RecentPacketLossRatio() {
|
||||
const int64_t kRecentTimeWindowMs = 500;
|
||||
|
||||
if (received_packets_->empty()) {
|
||||
return 0.0f;
|
||||
@@ -148,16 +157,16 @@ float NadaBweReceiver::RecentPacketLossRatio() {
|
||||
PacketNodeIt node_it = received_packets_->begin(); // Latest.
|
||||
|
||||
// Lowest timestamp limit, oldest one that should be checked.
|
||||
int64_t time_limit_ms = (*node_it)->arrival_time_ms_ - kRecentTimeWindowMs;
|
||||
int64_t time_limit_ms = (*node_it)->arrival_time_ms - kPacketLossTimeWindowMs;
|
||||
// Oldest and newest values found within the given time window.
|
||||
uint16_t oldest_seq_nb = (*node_it)->sequence_number_;
|
||||
uint16_t oldest_seq_nb = (*node_it)->sequence_number;
|
||||
uint16_t newest_seq_nb = oldest_seq_nb;
|
||||
|
||||
while (node_it != received_packets_->end()) {
|
||||
if ((*node_it)->arrival_time_ms_ < time_limit_ms) {
|
||||
if ((*node_it)->arrival_time_ms < time_limit_ms) {
|
||||
break;
|
||||
}
|
||||
uint16_t seq_nb = (*node_it)->sequence_number_;
|
||||
uint16_t seq_nb = (*node_it)->sequence_number;
|
||||
if (IsNewerSequenceNumber(seq_nb, newest_seq_nb)) {
|
||||
newest_seq_nb = seq_nb;
|
||||
}
|
||||
@@ -167,7 +176,6 @@ float NadaBweReceiver::RecentPacketLossRatio() {
|
||||
++node_it;
|
||||
++number_packets_received;
|
||||
}
|
||||
|
||||
// Interval width between oldest and newest sequence number.
|
||||
// There was an overflow if newest_seq_nb < oldest_seq_nb.
|
||||
int gap = static_cast<uint16_t>(newest_seq_nb - oldest_seq_nb + 1);
|
||||
@@ -175,26 +183,58 @@ float NadaBweReceiver::RecentPacketLossRatio() {
|
||||
return static_cast<float>(gap - number_packets_received) / gap;
|
||||
}
|
||||
|
||||
// For a given time window, compute the receiving speed rate in kbps.
|
||||
// As described below, three cases are considered depending on the number of
|
||||
// packets received.
|
||||
size_t NadaBweReceiver::RecentReceivingRate() {
|
||||
const int64_t kRecentTimeWindowMs = 500;
|
||||
// If the receiver didn't receive any packet, return 0.
|
||||
if (received_packets_->empty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
size_t totalSize = 0;
|
||||
int64_t time_limit_ms = clock_.TimeInMilliseconds() - kRecentTimeWindowMs;
|
||||
size_t total_size = 0;
|
||||
int number_packets = 0;
|
||||
|
||||
PacketNodeIt node_it = received_packets_->begin();
|
||||
|
||||
int64_t last_time_ms = (*node_it)->arrival_time_ms;
|
||||
int64_t start_time_ms = last_time_ms;
|
||||
PacketNodeIt end = received_packets_->end();
|
||||
|
||||
while (node_it != end && (*node_it)->arrival_time_ms_ > time_limit_ms) {
|
||||
totalSize += (*node_it)->payload_size_;
|
||||
// Stops after including the first packet out of the timeWindow.
|
||||
// Ameliorates results when there are wide gaps between packets.
|
||||
// E.g. Large packets : p1(0ms), p2(3000ms).
|
||||
while (node_it != end) {
|
||||
total_size += (*node_it)->payload_size;
|
||||
last_time_ms = (*node_it)->arrival_time_ms;
|
||||
++number_packets;
|
||||
if ((*node_it)->arrival_time_ms <
|
||||
start_time_ms - kReceivingRateTimeWindowMs) {
|
||||
break;
|
||||
}
|
||||
++node_it;
|
||||
}
|
||||
|
||||
return static_cast<size_t>((1000 * totalSize) / kRecentTimeWindowMs);
|
||||
int64_t corrected_time_ms;
|
||||
// If the receiver received a single packet, return its size*8/timeWindow.
|
||||
if (number_packets == 1) {
|
||||
corrected_time_ms = kReceivingRateTimeWindowMs;
|
||||
} else {
|
||||
// If the receiver received multiple packets, use as time interval the gap
|
||||
// between first and last packet falling in the timeWindow corrected by the
|
||||
// factor number_packets/(number_packets-1).
|
||||
// E.g: Let timeWindow = 500ms, payload_size = 500bytes, number_packets=2,
|
||||
// packets received at t1(0ms) and t2(499 or 501ms). This prevent the
|
||||
// function from returning ~2*8, sending instead a more likely ~1*8 kbps.
|
||||
corrected_time_ms = (number_packets * (start_time_ms - last_time_ms)) /
|
||||
(number_packets - 1);
|
||||
}
|
||||
|
||||
// Converting from bytes/ms to kbits/s.
|
||||
return static_cast<size_t>(8 * total_size / corrected_time_ms);
|
||||
}
|
||||
|
||||
int64_t NadaBweReceiver::MedianFilter(int64_t* last_delays_ms, int size) {
|
||||
// Typically size = 5.
|
||||
// Typically, size = 5.
|
||||
std::vector<int64_t> array_copy(last_delays_ms, last_delays_ms + size);
|
||||
std::nth_element(array_copy.begin(), array_copy.begin() + size / 2,
|
||||
array_copy.end());
|
||||
@@ -211,12 +251,19 @@ int64_t NadaBweReceiver::ExponentialSmoothingFilter(int64_t new_value,
|
||||
(1.0f - alpha) * last_smoothed_value + 0.5f);
|
||||
}
|
||||
|
||||
// Implementation according to Cisco's proposal by default.
|
||||
NadaBweSender::NadaBweSender(int kbps, BitrateObserver* observer, Clock* clock)
|
||||
: clock_(clock), observer_(observer), bitrate_kbps_(kbps) {
|
||||
: clock_(clock),
|
||||
observer_(observer),
|
||||
bitrate_kbps_(kbps),
|
||||
original_operating_mode_(true) {
|
||||
}
|
||||
|
||||
NadaBweSender::NadaBweSender(BitrateObserver* observer, Clock* clock)
|
||||
: clock_(clock), observer_(observer), bitrate_kbps_(kMinRefRateKbps) {
|
||||
: clock_(clock),
|
||||
observer_(observer),
|
||||
bitrate_kbps_(kMinRefRateKbps),
|
||||
original_operating_mode_(true) {
|
||||
}
|
||||
|
||||
NadaBweSender::~NadaBweSender() {
|
||||
@@ -228,11 +275,13 @@ int NadaBweSender::GetFeedbackIntervalMs() const {
|
||||
|
||||
void NadaBweSender::GiveFeedback(const FeedbackPacket& feedback) {
|
||||
const NadaFeedback& fb = static_cast<const NadaFeedback&>(feedback);
|
||||
|
||||
// Following parameters might be optimized.
|
||||
const int64_t kQueuingDelayUpperBoundMs = 10;
|
||||
const float kDerivativeUpperBound = 10.0f * min_feedback_delay_ms_;
|
||||
|
||||
const int kMaxRefRateKbps = 1500; // Referred as R_max.
|
||||
const float kDerivativeUpperBound = 10.0f / min_feedback_delay_ms_;
|
||||
// In the modified version, a higher kMinUpperBound allows a higher d_hat
|
||||
// upper bound for calling AcceleratedRampUp.
|
||||
const float kProportionalityDelayBits = 20.0f;
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
float delta_s = now_ms - last_feedback_ms_;
|
||||
@@ -242,18 +291,46 @@ void NadaBweSender::GiveFeedback(const FeedbackPacket& feedback) {
|
||||
std::min(min_feedback_delay_ms_, static_cast<int64_t>(delta_s));
|
||||
|
||||
// Update RTT_0.
|
||||
int64_t rtt = now_ms - fb.latest_send_time_ms();
|
||||
min_round_trip_time_ms_ = std::min(min_round_trip_time_ms_, rtt);
|
||||
int64_t rtt_ms = now_ms - fb.latest_send_time_ms();
|
||||
min_round_trip_time_ms_ = std::min(min_round_trip_time_ms_, rtt_ms);
|
||||
|
||||
// Independent limits for those variables.
|
||||
// Independent limits for AcceleratedRampUp conditions variables:
|
||||
// x_n, d_tilde and x'_n in the original implementation, plus
|
||||
// d_hat and receiving_rate in the modified one.
|
||||
// There should be no packet losses/marking, hence x_n == d_tilde.
|
||||
if (fb.congestion_signal() == fb.est_queuing_delay_signal_ms() &&
|
||||
fb.est_queuing_delay_signal_ms() < kQueuingDelayUpperBoundMs &&
|
||||
fb.derivative() < kDerivativeUpperBound) {
|
||||
AcceleratedRampUp(fb, kMaxRefRateKbps);
|
||||
if (original_operating_mode_) {
|
||||
// Original if conditions and rate update.
|
||||
if (fb.congestion_signal() == fb.est_queuing_delay_signal_ms() &&
|
||||
fb.est_queuing_delay_signal_ms() < kQueuingDelayUpperBoundMs &&
|
||||
fb.derivative() < kDerivativeUpperBound) {
|
||||
AcceleratedRampUp(fb);
|
||||
} else {
|
||||
GradualRateUpdate(fb, delta_s, 1.0);
|
||||
}
|
||||
} else {
|
||||
GradualRateUpdate(fb, kMaxRefRateKbps, delta_s);
|
||||
// Modified if conditions and rate update; new ramp down mode.
|
||||
if (fb.congestion_signal() == fb.est_queuing_delay_signal_ms() &&
|
||||
fb.est_queuing_delay_signal_ms() < kQueuingDelayUpperBoundMs &&
|
||||
fb.exp_smoothed_delay_ms() <
|
||||
kMinRefRateKbps / kProportionalityDelayBits &&
|
||||
fb.derivative() < kDerivativeUpperBound &&
|
||||
fb.receiving_rate() > kMinRefRateKbps) {
|
||||
AcceleratedRampUp(fb);
|
||||
} else if (fb.congestion_signal() > kMaxCongestionSignalMs ||
|
||||
fb.exp_smoothed_delay_ms() > kMaxCongestionSignalMs) {
|
||||
AcceleratedRampDown(fb);
|
||||
} else {
|
||||
double bitrate_reference =
|
||||
(2.0 * bitrate_kbps_) / (kMaxRefRateKbps + kMinRefRateKbps);
|
||||
double smoothing_factor = pow(bitrate_reference, 0.75);
|
||||
GradualRateUpdate(fb, delta_s, smoothing_factor);
|
||||
}
|
||||
}
|
||||
|
||||
bitrate_kbps_ = std::min(bitrate_kbps_, kMaxRefRateKbps);
|
||||
bitrate_kbps_ = std::max(bitrate_kbps_, kMinRefRateKbps);
|
||||
|
||||
observer_->OnNetworkChanged(1000 * bitrate_kbps_, 0, rtt_ms);
|
||||
}
|
||||
|
||||
int64_t NadaBweSender::TimeUntilNextProcess() {
|
||||
@@ -264,46 +341,46 @@ int NadaBweSender::Process() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NadaBweSender::AcceleratedRampUp(const NadaFeedback& fb,
|
||||
const int kMaxRefRateKbps) {
|
||||
void NadaBweSender::AcceleratedRampUp(const NadaFeedback& fb) {
|
||||
const int kMaxRampUpQueuingDelayMs = 50; // Referred as T_th.
|
||||
const float kGamma0 = 0.5f; // Referred as gamma_0.
|
||||
|
||||
float gamma =
|
||||
std::min(kGamma0, static_cast<float>(kMaxRampUpQueuingDelayMs) /
|
||||
(min_round_trip_time_ms_ + min_feedback_delay_ms_));
|
||||
bitrate_kbps_ = static_cast<int>((1.0f + gamma) * fb.receiving_rate() + 0.5f);
|
||||
|
||||
bitrate_kbps_ = std::min(bitrate_kbps_, kMaxRefRateKbps);
|
||||
bitrate_kbps_ = std::max(bitrate_kbps_, kMinRefRateKbps);
|
||||
bitrate_kbps_ = static_cast<int>((1.0f + gamma) * fb.receiving_rate() + 0.5f);
|
||||
}
|
||||
|
||||
void NadaBweSender::AcceleratedRampDown(const NadaFeedback& fb) {
|
||||
const float kGamma0 = 0.9f;
|
||||
float gamma = 3.0f * kMaxCongestionSignalMs /
|
||||
(fb.congestion_signal() + fb.exp_smoothed_delay_ms());
|
||||
gamma = std::min(gamma, kGamma0);
|
||||
bitrate_kbps_ = gamma * fb.receiving_rate() + 0.5f;
|
||||
}
|
||||
|
||||
void NadaBweSender::GradualRateUpdate(const NadaFeedback& fb,
|
||||
const int kMaxRefRateKbps,
|
||||
const float delta_s) {
|
||||
float delta_s,
|
||||
double smoothing_factor) {
|
||||
const float kTauOMs = 500.0f; // Referred as tau_o.
|
||||
const float kEta = 2.0f; // Referred as eta.
|
||||
const float kKappa = 1.0f; // Referred as kappa.
|
||||
const float kReferenceDelayMs = 10.0f; // Referred as x_ref.
|
||||
const float kPriorityWeight = 1.0f; // Referred as w.
|
||||
|
||||
float kPriorityWeight = static_cast<float>(fb.exp_smoothed_delay_ms()) /
|
||||
kReferenceDelayMs; // Referred as w.
|
||||
float x_hat = fb.congestion_signal() + kEta * kTauOMs * fb.derivative();
|
||||
|
||||
float kTheta =
|
||||
kPriorityWeight * (kMaxRefRateKbps - kMinRefRateKbps) * kReferenceDelayMs;
|
||||
float x_hat = fb.congestion_signal() + kEta * kTauOMs * fb.derivative();
|
||||
|
||||
bitrate_kbps_ =
|
||||
bitrate_kbps_ +
|
||||
int original_increase =
|
||||
static_cast<int>((kKappa * delta_s *
|
||||
(kTheta - (bitrate_kbps_ - kMinRefRateKbps) * x_hat)) /
|
||||
(kTauOMs * kTauOMs) +
|
||||
0.5f);
|
||||
|
||||
bitrate_kbps_ = std::min(bitrate_kbps_, kMaxRefRateKbps);
|
||||
bitrate_kbps_ = std::max(bitrate_kbps_, kMinRefRateKbps);
|
||||
|
||||
observer_->OnNetworkChanged(1000 * bitrate_kbps_, 0, 0);
|
||||
bitrate_kbps_ = bitrate_kbps_ + smoothing_factor * original_increase;
|
||||
}
|
||||
|
||||
void LinkedSet::Insert(uint16_t sequence_number,
|
||||
@@ -312,9 +389,13 @@ void LinkedSet::Insert(uint16_t sequence_number,
|
||||
size_t payload_size) {
|
||||
std::map<uint16_t, PacketNodeIt>::iterator it = map_.find(sequence_number);
|
||||
if (it != map_.end()) {
|
||||
if (it->second != list_.begin()) {
|
||||
list_.erase(it->second);
|
||||
list_.push_front(*(it->second));
|
||||
PacketNodeIt node_it = it->second;
|
||||
PacketIdentifierNode* node = *node_it;
|
||||
node->arrival_time_ms = arrival_time_ms;
|
||||
if (node_it != list_.begin()) {
|
||||
list_.erase(node_it);
|
||||
list_.push_front(node);
|
||||
map_[sequence_number] = list_.begin();
|
||||
}
|
||||
} else {
|
||||
if (size() == capacity_) {
|
||||
@@ -325,12 +406,12 @@ void LinkedSet::Insert(uint16_t sequence_number,
|
||||
}
|
||||
}
|
||||
void LinkedSet::RemoveTail() {
|
||||
map_.erase(list_.back()->sequence_number_);
|
||||
map_.erase(list_.back()->sequence_number);
|
||||
list_.pop_back();
|
||||
}
|
||||
void LinkedSet::UpdateHead(PacketIdentifierNode* new_head) {
|
||||
list_.push_front(new_head);
|
||||
map_[new_head->sequence_number_] = list_.begin();
|
||||
map_[new_head->sequence_number] = list_.begin();
|
||||
}
|
||||
|
||||
} // namespace bwe
|
||||
|
||||
@@ -38,15 +38,15 @@ struct PacketIdentifierNode {
|
||||
int64_t send_time_ms,
|
||||
int64_t arrival_time_ms,
|
||||
size_t payload_size)
|
||||
: sequence_number_(sequence_number),
|
||||
send_time_ms_(send_time_ms),
|
||||
arrival_time_ms_(arrival_time_ms),
|
||||
payload_size_(payload_size) {}
|
||||
: sequence_number(sequence_number),
|
||||
send_time_ms(send_time_ms),
|
||||
arrival_time_ms(arrival_time_ms),
|
||||
payload_size(payload_size) {}
|
||||
|
||||
uint16_t sequence_number_;
|
||||
int64_t send_time_ms_;
|
||||
int64_t arrival_time_ms_;
|
||||
size_t payload_size_;
|
||||
uint16_t sequence_number;
|
||||
int64_t send_time_ms;
|
||||
int64_t arrival_time_ms;
|
||||
size_t payload_size;
|
||||
};
|
||||
|
||||
typedef std::list<PacketIdentifierNode*>::iterator PacketNodeIt;
|
||||
@@ -106,6 +106,14 @@ class NadaBweReceiver : public BweReceiver {
|
||||
int64_t last_smoothed_value,
|
||||
float alpha);
|
||||
|
||||
// With the assumption that packet loss is lower than 97%, the max gap
|
||||
// between elements in the set is lower than 0x8000, hence we have a
|
||||
// total order in the set. For (x,y,z) subset of the LinkedSet,
|
||||
// (x<=y and y<=z) ==> x<=z so the set can be sorted.
|
||||
static const int kSetCapacity = 1000;
|
||||
static const int64_t kPacketLossTimeWindowMs = 500;
|
||||
static const int64_t kReceivingRateTimeWindowMs = 500;
|
||||
|
||||
private:
|
||||
SimulatedClock clock_;
|
||||
int64_t last_feedback_ms_;
|
||||
@@ -116,8 +124,8 @@ class NadaBweReceiver : public BweReceiver {
|
||||
int last_delays_index_;
|
||||
int64_t exp_smoothed_delay_ms_; // Referred as d_hat_n.
|
||||
int64_t est_queuing_delay_signal_ms_; // Referred as d_tilde_n.
|
||||
|
||||
// Deals with packets sent more than once.
|
||||
static const int kSetCapacity = 10000; // Lower than 0xFFFF / 2.
|
||||
LinkedSet* received_packets_ = new LinkedSet(kSetCapacity);
|
||||
static const int kMedian = 5; // Used for k-points Median Filter.
|
||||
int64_t last_delays_ms_[kMedian]; // Used for Median Filter.
|
||||
@@ -135,22 +143,36 @@ class NadaBweSender : public BweSender {
|
||||
void OnPacketsSent(const Packets& packets) override {}
|
||||
int64_t TimeUntilNextProcess() override;
|
||||
int Process() override;
|
||||
void AcceleratedRampUp(const NadaFeedback& fb, const int kMaxRefRateKbps);
|
||||
void AcceleratedRampUp(const NadaFeedback& fb);
|
||||
void AcceleratedRampDown(const NadaFeedback& fb);
|
||||
void GradualRateUpdate(const NadaFeedback& fb,
|
||||
const int kMaxRefRateKbps,
|
||||
const float delta_s);
|
||||
float delta_s,
|
||||
double smoothing_factor);
|
||||
|
||||
int bitrate_kbps() const { return bitrate_kbps_; }
|
||||
void set_bitrate_kbps(int bitrate_kbps) { bitrate_kbps_ = bitrate_kbps; }
|
||||
bool original_operating_mode() const { return original_operating_mode_; }
|
||||
void set_original_operating_mode(bool original_operating_mode) {
|
||||
original_operating_mode_ = original_operating_mode;
|
||||
}
|
||||
int64_t NowMs() const { return clock_->TimeInMilliseconds(); }
|
||||
|
||||
static const int kMinRefRateKbps = 150; // Referred as R_min.
|
||||
static const int kMaxRefRateKbps = 1500; // Referred as R_max.
|
||||
|
||||
private:
|
||||
Clock* const clock_;
|
||||
BitrateObserver* const observer_;
|
||||
// Used as an upper bound for calling AcceleratedRampDown.
|
||||
const float kMaxCongestionSignalMs = 40.0f + kMinRefRateKbps / 15;
|
||||
// Referred as R_min, default initialization for bitrate R_n.
|
||||
const int kMinRefRateKbps = 150;
|
||||
int bitrate_kbps_; // Referred as "Reference Rate" = R_n.
|
||||
int64_t last_feedback_ms_ = 0;
|
||||
// Referred as delta_0, initialized as an upper bound.
|
||||
int64_t min_feedback_delay_ms_ = 5000;
|
||||
int64_t min_feedback_delay_ms_ = 200;
|
||||
// Referred as RTT_0, initialized as an upper bound.
|
||||
int64_t min_round_trip_time_ms_ = 500;
|
||||
int64_t min_round_trip_time_ms_ = 100;
|
||||
bool original_operating_mode_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(NadaBweSender);
|
||||
};
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/nada.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include "webrtc/base/common.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/packet.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/nada.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/packet_sender.h"
|
||||
@@ -23,67 +25,585 @@ namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
|
||||
class MedianFilterTest : public ::testing::Test {
|
||||
class FilterTest : public ::testing::Test {
|
||||
public:
|
||||
void FilterFromConstantArray() {
|
||||
for (int i = 0; i < kSize; v[i++] = 200) {
|
||||
}
|
||||
for (int i = 0; i < kSize; ++i) {
|
||||
void MedianFilterConstantArray() {
|
||||
std::fill_n(raw_signal_, kNumElements, kSignalValue);
|
||||
for (int i = 0; i < kNumElements; ++i) {
|
||||
int size = std::min(5, i + 1);
|
||||
m_filtered[i] = NadaBweReceiver::MedianFilter(v + i + 1 - size, size);
|
||||
median_filtered_[i] =
|
||||
NadaBweReceiver::MedianFilter(&raw_signal_[i + 1 - size], size);
|
||||
}
|
||||
}
|
||||
|
||||
void FilterFromIntermittentNoiseArray() {
|
||||
void MedianFilterIntermittentNoise() {
|
||||
const int kValue = 500;
|
||||
const int kNoise = 100;
|
||||
|
||||
for (int i = 0; i < kSize; i++) {
|
||||
v[i] = kValue + kNoise * (i % 10 == 9 ? 1 : 0);
|
||||
for (int i = 0; i < kNumElements; ++i) {
|
||||
raw_signal_[i] = kValue + kNoise * (i % 10 == 9 ? 1 : 0);
|
||||
}
|
||||
for (int i = 0; i < kSize; ++i) {
|
||||
for (int i = 0; i < kNumElements; ++i) {
|
||||
int size = std::min(5, i + 1);
|
||||
m_filtered[i] = NadaBweReceiver::MedianFilter(v + i + 1 - size, size);
|
||||
EXPECT_EQ(m_filtered[i], kValue);
|
||||
median_filtered_[i] =
|
||||
NadaBweReceiver::MedianFilter(&raw_signal_[i + 1 - size], size);
|
||||
EXPECT_EQ(median_filtered_[i], kValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
static const int kSize = 1000;
|
||||
int64_t v[kSize];
|
||||
int64_t m_filtered[kSize];
|
||||
};
|
||||
|
||||
class ExponentialSmoothingFilterTest : public ::testing::Test {
|
||||
public:
|
||||
void FilterFromConstantArray() {
|
||||
for (int i = 0; i < kSize; v[i++] = 200) {
|
||||
}
|
||||
void ExponentialSmoothingFilter(const int64_t raw_signal_[],
|
||||
int num_elements,
|
||||
int64_t exp_smoothed[]) {
|
||||
exp_smoothed[0] =
|
||||
NadaBweReceiver::ExponentialSmoothingFilter(v[0], -1, kAlpha);
|
||||
|
||||
for (int i = 1; i < kSize; ++i) {
|
||||
NadaBweReceiver::ExponentialSmoothingFilter(raw_signal_[0], -1, kAlpha);
|
||||
for (int i = 1; i < num_elements; ++i) {
|
||||
exp_smoothed[i] = NadaBweReceiver::ExponentialSmoothingFilter(
|
||||
v[i], exp_smoothed[i - 1], kAlpha);
|
||||
raw_signal_[i], exp_smoothed[i - 1], kAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
void ExponentialSmoothingConstantArray(int64_t exp_smoothed[]) {
|
||||
std::fill_n(raw_signal_, kNumElements, kSignalValue);
|
||||
ExponentialSmoothingFilter(raw_signal_, kNumElements, exp_smoothed);
|
||||
}
|
||||
|
||||
protected:
|
||||
static const int kSize = 1000;
|
||||
const float kAlpha = 0.8f;
|
||||
int64_t v[kSize];
|
||||
int64_t exp_smoothed[kSize];
|
||||
static const int kNumElements = 1000;
|
||||
static const int64_t kSignalValue;
|
||||
static const float kAlpha;
|
||||
int64_t raw_signal_[kNumElements];
|
||||
int64_t median_filtered_[kNumElements];
|
||||
};
|
||||
|
||||
TEST_F(MedianFilterTest, ConstantArray) {
|
||||
FilterFromConstantArray();
|
||||
for (int i = 0; i < kSize; ++i) {
|
||||
EXPECT_TRUE(m_filtered[i] == v[i]);
|
||||
const int64_t FilterTest::kSignalValue = 200;
|
||||
const float FilterTest::kAlpha = 0.1f;
|
||||
|
||||
class TestBitrateObserver : public BitrateObserver {
|
||||
public:
|
||||
TestBitrateObserver()
|
||||
: last_bitrate_(0), last_fraction_loss_(0), last_rtt_(0) {}
|
||||
|
||||
virtual void OnNetworkChanged(uint32_t bitrate,
|
||||
uint8_t fraction_loss,
|
||||
int64_t rtt) {
|
||||
last_bitrate_ = bitrate;
|
||||
last_fraction_loss_ = fraction_loss;
|
||||
last_rtt_ = rtt;
|
||||
}
|
||||
uint32_t last_bitrate_;
|
||||
uint8_t last_fraction_loss_;
|
||||
int64_t last_rtt_;
|
||||
};
|
||||
|
||||
class NadaSenderSideTest : public ::testing::Test {
|
||||
public:
|
||||
NadaSenderSideTest()
|
||||
: observer_(),
|
||||
simulated_clock_(0),
|
||||
nada_sender_(&observer_, &simulated_clock_) {}
|
||||
~NadaSenderSideTest() {}
|
||||
|
||||
private:
|
||||
TestBitrateObserver observer_;
|
||||
SimulatedClock simulated_clock_;
|
||||
|
||||
protected:
|
||||
NadaBweSender nada_sender_;
|
||||
};
|
||||
|
||||
class NadaReceiverSideTest : public ::testing::Test {
|
||||
protected:
|
||||
NadaReceiverSideTest() : nada_receiver_(kFlowId) {}
|
||||
~NadaReceiverSideTest() {}
|
||||
|
||||
const int kFlowId = 0;
|
||||
NadaBweReceiver nada_receiver_;
|
||||
};
|
||||
|
||||
class NadaFbGenerator {
|
||||
public:
|
||||
NadaFbGenerator();
|
||||
|
||||
static NadaFeedback NotCongestedFb(size_t receiving_rate,
|
||||
int64_t ref_signal_ms,
|
||||
int64_t send_time_ms) {
|
||||
int64_t exp_smoothed_delay_ms = ref_signal_ms;
|
||||
int64_t est_queuing_delay_signal_ms = ref_signal_ms;
|
||||
int64_t congestion_signal_ms = ref_signal_ms;
|
||||
float derivative = 0.0f;
|
||||
return NadaFeedback(kFlowId, kNowMs, exp_smoothed_delay_ms,
|
||||
est_queuing_delay_signal_ms, congestion_signal_ms,
|
||||
derivative, receiving_rate, send_time_ms);
|
||||
}
|
||||
|
||||
static NadaFeedback CongestedFb(size_t receiving_rate, int64_t send_time_ms) {
|
||||
int64_t exp_smoothed_delay_ms = 1000;
|
||||
int64_t est_queuing_delay_signal_ms = 800;
|
||||
int64_t congestion_signal_ms = 1000;
|
||||
float derivative = 1.0f;
|
||||
return NadaFeedback(kFlowId, kNowMs, exp_smoothed_delay_ms,
|
||||
est_queuing_delay_signal_ms, congestion_signal_ms,
|
||||
derivative, receiving_rate, send_time_ms);
|
||||
}
|
||||
|
||||
static NadaFeedback ExtremelyCongestedFb(size_t receiving_rate,
|
||||
int64_t send_time_ms) {
|
||||
int64_t exp_smoothed_delay_ms = 100000;
|
||||
int64_t est_queuing_delay_signal_ms = 0;
|
||||
int64_t congestion_signal_ms = 100000;
|
||||
float derivative = 10000.0f;
|
||||
return NadaFeedback(kFlowId, kNowMs, exp_smoothed_delay_ms,
|
||||
est_queuing_delay_signal_ms, congestion_signal_ms,
|
||||
derivative, receiving_rate, send_time_ms);
|
||||
}
|
||||
|
||||
private:
|
||||
// Arbitrary values, won't change these test results.
|
||||
static const int kFlowId = 2;
|
||||
static const int64_t kNowMs = 1000;
|
||||
};
|
||||
|
||||
// Verify if AcceleratedRampUp is called and that bitrate increases.
|
||||
TEST_F(NadaSenderSideTest, AcceleratedRampUp) {
|
||||
const int64_t kRefSignalMs = 3;
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
int original_bitrate = 2 * NadaBweSender::kMinRefRateKbps;
|
||||
size_t receiving_rate = static_cast<size_t>(original_bitrate);
|
||||
int64_t send_time_ms = nada_sender_.NowMs() - kOneWayDelayMs;
|
||||
|
||||
NadaFeedback not_congested_fb = NadaFbGenerator::NotCongestedFb(
|
||||
receiving_rate, kRefSignalMs, send_time_ms);
|
||||
|
||||
nada_sender_.set_original_operating_mode(true);
|
||||
nada_sender_.set_bitrate_kbps(original_bitrate);
|
||||
|
||||
// Trigger AcceleratedRampUp mode.
|
||||
nada_sender_.GiveFeedback(not_congested_fb);
|
||||
int bitrate_1_kbps = nada_sender_.bitrate_kbps();
|
||||
EXPECT_GT(bitrate_1_kbps, original_bitrate);
|
||||
// Updates the bitrate according to the receiving rate and other constant
|
||||
// parameters.
|
||||
nada_sender_.AcceleratedRampUp(not_congested_fb);
|
||||
EXPECT_EQ(nada_sender_.bitrate_kbps(), bitrate_1_kbps);
|
||||
|
||||
nada_sender_.set_original_operating_mode(false);
|
||||
nada_sender_.set_bitrate_kbps(original_bitrate);
|
||||
// Trigger AcceleratedRampUp mode.
|
||||
nada_sender_.GiveFeedback(not_congested_fb);
|
||||
bitrate_1_kbps = nada_sender_.bitrate_kbps();
|
||||
EXPECT_GT(bitrate_1_kbps, original_bitrate);
|
||||
nada_sender_.AcceleratedRampUp(not_congested_fb);
|
||||
EXPECT_EQ(nada_sender_.bitrate_kbps(), bitrate_1_kbps);
|
||||
}
|
||||
|
||||
// Verify if AcceleratedRampDown is called and if bitrate decreases.
|
||||
TEST_F(NadaSenderSideTest, AcceleratedRampDown) {
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
int original_bitrate = 3 * NadaBweSender::kMinRefRateKbps;
|
||||
size_t receiving_rate = static_cast<size_t>(original_bitrate);
|
||||
int64_t send_time_ms = nada_sender_.NowMs() - kOneWayDelayMs;
|
||||
|
||||
NadaFeedback congested_fb =
|
||||
NadaFbGenerator::CongestedFb(receiving_rate, send_time_ms);
|
||||
|
||||
nada_sender_.set_original_operating_mode(false);
|
||||
nada_sender_.set_bitrate_kbps(original_bitrate);
|
||||
nada_sender_.GiveFeedback(congested_fb); // Trigger AcceleratedRampDown mode.
|
||||
int bitrate_1_kbps = nada_sender_.bitrate_kbps();
|
||||
EXPECT_LE(bitrate_1_kbps, original_bitrate * 0.9f + 0.5f);
|
||||
EXPECT_LT(bitrate_1_kbps, original_bitrate);
|
||||
|
||||
// Updates the bitrate according to the receiving rate and other constant
|
||||
// parameters.
|
||||
nada_sender_.AcceleratedRampDown(congested_fb);
|
||||
int bitrate_2_kbps =
|
||||
std::max(nada_sender_.bitrate_kbps(), NadaBweSender::kMinRefRateKbps);
|
||||
EXPECT_EQ(bitrate_2_kbps, bitrate_1_kbps);
|
||||
}
|
||||
|
||||
TEST_F(NadaSenderSideTest, GradualRateUpdate) {
|
||||
const int64_t kDeltaSMs = 20;
|
||||
const int64_t kRefSignalMs = 20;
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
int original_bitrate = 2 * NadaBweSender::kMinRefRateKbps;
|
||||
size_t receiving_rate = static_cast<size_t>(original_bitrate);
|
||||
int64_t send_time_ms = nada_sender_.NowMs() - kOneWayDelayMs;
|
||||
|
||||
NadaFeedback congested_fb =
|
||||
NadaFbGenerator::CongestedFb(receiving_rate, send_time_ms);
|
||||
NadaFeedback not_congested_fb = NadaFbGenerator::NotCongestedFb(
|
||||
original_bitrate, kRefSignalMs, send_time_ms);
|
||||
|
||||
nada_sender_.set_bitrate_kbps(original_bitrate);
|
||||
double smoothing_factor = 0.0;
|
||||
nada_sender_.GradualRateUpdate(congested_fb, kDeltaSMs, smoothing_factor);
|
||||
EXPECT_EQ(nada_sender_.bitrate_kbps(), original_bitrate);
|
||||
|
||||
smoothing_factor = 1.0;
|
||||
nada_sender_.GradualRateUpdate(congested_fb, kDeltaSMs, smoothing_factor);
|
||||
EXPECT_LT(nada_sender_.bitrate_kbps(), original_bitrate);
|
||||
|
||||
nada_sender_.set_bitrate_kbps(original_bitrate);
|
||||
nada_sender_.GradualRateUpdate(not_congested_fb, kDeltaSMs, smoothing_factor);
|
||||
EXPECT_GT(nada_sender_.bitrate_kbps(), original_bitrate);
|
||||
}
|
||||
|
||||
// Sending bitrate should decrease and reach its Min bound.
|
||||
TEST_F(NadaSenderSideTest, VeryLowBandwith) {
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
const int kMin = NadaBweSender::kMinRefRateKbps;
|
||||
size_t receiving_rate = static_cast<size_t>(kMin);
|
||||
int64_t send_time_ms = nada_sender_.NowMs() - kOneWayDelayMs;
|
||||
|
||||
NadaFeedback extremely_congested_fb =
|
||||
NadaFbGenerator::ExtremelyCongestedFb(receiving_rate, send_time_ms);
|
||||
NadaFeedback congested_fb =
|
||||
NadaFbGenerator::CongestedFb(receiving_rate, send_time_ms);
|
||||
|
||||
nada_sender_.set_bitrate_kbps(5 * kMin);
|
||||
nada_sender_.set_original_operating_mode(true);
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Trigger GradualRateUpdate mode.
|
||||
nada_sender_.GiveFeedback(extremely_congested_fb);
|
||||
}
|
||||
// The original implementation doesn't allow the bitrate to stay at kMin,
|
||||
// even if the congestion signal is very high.
|
||||
EXPECT_GE(nada_sender_.bitrate_kbps(), kMin);
|
||||
|
||||
nada_sender_.set_original_operating_mode(false);
|
||||
nada_sender_.set_bitrate_kbps(5 * kMin);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
int previous_bitrate = nada_sender_.bitrate_kbps();
|
||||
// Trigger AcceleratedRampDown mode.
|
||||
nada_sender_.GiveFeedback(congested_fb);
|
||||
EXPECT_LE(nada_sender_.bitrate_kbps(), previous_bitrate);
|
||||
}
|
||||
EXPECT_EQ(nada_sender_.bitrate_kbps(), kMin);
|
||||
}
|
||||
|
||||
// Sending bitrate should increase and reach its Max bound.
|
||||
TEST_F(NadaSenderSideTest, VeryHighBandwith) {
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
const int kMax = NadaBweSender::kMaxRefRateKbps;
|
||||
const size_t kRecentReceivingRate = static_cast<size_t>(kMax);
|
||||
const int64_t kRefSignalMs = 5;
|
||||
int64_t send_time_ms = nada_sender_.NowMs() - kOneWayDelayMs;
|
||||
|
||||
NadaFeedback not_congested_fb = NadaFbGenerator::NotCongestedFb(
|
||||
kRecentReceivingRate, kRefSignalMs, send_time_ms);
|
||||
|
||||
nada_sender_.set_original_operating_mode(true);
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
int previous_bitrate = nada_sender_.bitrate_kbps();
|
||||
nada_sender_.GiveFeedback(not_congested_fb);
|
||||
EXPECT_GE(nada_sender_.bitrate_kbps(), previous_bitrate);
|
||||
}
|
||||
EXPECT_EQ(nada_sender_.bitrate_kbps(), kMax);
|
||||
|
||||
nada_sender_.set_original_operating_mode(false);
|
||||
nada_sender_.set_bitrate_kbps(NadaBweSender::kMinRefRateKbps);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
int previous_bitrate = nada_sender_.bitrate_kbps();
|
||||
nada_sender_.GiveFeedback(not_congested_fb);
|
||||
EXPECT_GE(nada_sender_.bitrate_kbps(), previous_bitrate);
|
||||
}
|
||||
EXPECT_EQ(nada_sender_.bitrate_kbps(), kMax);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, ReceivingRateNoPackets) {
|
||||
EXPECT_EQ(nada_receiver_.RecentReceivingRate(), static_cast<size_t>(0));
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, ReceivingRateSinglePacket) {
|
||||
const size_t kPayloadSizeBytes = 500 * 1000;
|
||||
const int64_t kSendTimeUs = 300 * 1000;
|
||||
const int64_t kArrivalTimeMs = kSendTimeUs / 1000 + 100;
|
||||
const uint16_t kSequenceNumber = 1;
|
||||
const int64_t kTimeWindowMs = NadaBweReceiver::kReceivingRateTimeWindowMs;
|
||||
|
||||
const MediaPacket media_packet(kFlowId, kSendTimeUs, kPayloadSizeBytes,
|
||||
kSequenceNumber);
|
||||
nada_receiver_.ReceivePacket(kArrivalTimeMs, media_packet);
|
||||
|
||||
const size_t kReceivingRateKbps = 8 * kPayloadSizeBytes / kTimeWindowMs;
|
||||
|
||||
EXPECT_EQ(nada_receiver_.RecentReceivingRate(), kReceivingRateKbps);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, ReceivingRateLargePackets) {
|
||||
const size_t kPayloadSizeBytes = 3000 * 1000;
|
||||
const int64_t kTimeGapMs = 3000; // Between each packet.
|
||||
const int64_t kOneWayDelayMs = 1000;
|
||||
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
int64_t send_time_us = i * kTimeGapMs * 1000;
|
||||
int64_t arrival_time_ms = send_time_us / 1000 + kOneWayDelayMs;
|
||||
uint16_t sequence_number = i;
|
||||
const MediaPacket media_packet(kFlowId, send_time_us, kPayloadSizeBytes,
|
||||
sequence_number);
|
||||
nada_receiver_.ReceivePacket(arrival_time_ms, media_packet);
|
||||
}
|
||||
|
||||
const size_t kReceivingRateKbps = 8 * kPayloadSizeBytes / kTimeGapMs;
|
||||
EXPECT_EQ(nada_receiver_.RecentReceivingRate(), kReceivingRateKbps);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, ReceivingRateSmallPackets) {
|
||||
const size_t kPayloadSizeBytes = 100 * 1000;
|
||||
const int64_t kTimeGapMs = 50; // Between each packet.
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
|
||||
for (int i = 1; i < 50; ++i) {
|
||||
int64_t send_time_us = i * kTimeGapMs * 1000;
|
||||
int64_t arrival_time_ms = send_time_us / 1000 + kOneWayDelayMs;
|
||||
uint16_t sequence_number = i;
|
||||
const MediaPacket media_packet(kFlowId, send_time_us, kPayloadSizeBytes,
|
||||
sequence_number);
|
||||
nada_receiver_.ReceivePacket(arrival_time_ms, media_packet);
|
||||
}
|
||||
|
||||
const size_t kReceivingRateKbps = 8 * kPayloadSizeBytes / kTimeGapMs;
|
||||
EXPECT_EQ(nada_receiver_.RecentReceivingRate(), kReceivingRateKbps);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, ReceivingRateIntermittentPackets) {
|
||||
const size_t kPayloadSizeBytes = 100 * 1000;
|
||||
const int64_t kTimeGapMs = 50; // Between each packet.
|
||||
const int64_t kFirstSendTimeMs = 0;
|
||||
const int64_t kOneWayDelayMs = 50;
|
||||
|
||||
// Gap between first and other packets
|
||||
const MediaPacket media_packet(kFlowId, kFirstSendTimeMs, kPayloadSizeBytes,
|
||||
1);
|
||||
nada_receiver_.ReceivePacket(kFirstSendTimeMs + kOneWayDelayMs, media_packet);
|
||||
|
||||
const int64_t kDelayAfterFirstPacketMs = 1000;
|
||||
const int kNumPackets = 5; // Small enough so that all packets are covered.
|
||||
EXPECT_LT((kNumPackets - 2) * kTimeGapMs,
|
||||
NadaBweReceiver::kReceivingRateTimeWindowMs);
|
||||
const int64_t kTimeWindowMs =
|
||||
kDelayAfterFirstPacketMs + (kNumPackets - 2) * kTimeGapMs;
|
||||
|
||||
for (int i = 2; i <= kNumPackets; ++i) {
|
||||
int64_t send_time_us =
|
||||
((i - 2) * kTimeGapMs + kFirstSendTimeMs + kDelayAfterFirstPacketMs) *
|
||||
1000;
|
||||
int64_t arrival_time_ms = send_time_us / 1000 + kOneWayDelayMs;
|
||||
uint16_t sequence_number = i;
|
||||
const MediaPacket media_packet(kFlowId, send_time_us, kPayloadSizeBytes,
|
||||
sequence_number);
|
||||
nada_receiver_.ReceivePacket(arrival_time_ms, media_packet);
|
||||
}
|
||||
|
||||
const size_t kTotalReceivedKb = 8 * kNumPackets * kPayloadSizeBytes;
|
||||
const int64_t kCorrectedTimeWindowMs =
|
||||
(kTimeWindowMs * kNumPackets) / (kNumPackets - 1);
|
||||
EXPECT_EQ(nada_receiver_.RecentReceivingRate(),
|
||||
kTotalReceivedKb / kCorrectedTimeWindowMs);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, ReceivingRateDuplicatedPackets) {
|
||||
const size_t kPayloadSizeBytes = 500 * 1000;
|
||||
const int64_t kSendTimeUs = 300 * 1000;
|
||||
const int64_t kArrivalTimeMs = kSendTimeUs / 1000 + 100;
|
||||
const uint16_t kSequenceNumber = 1;
|
||||
const int64_t kTimeWindowMs = NadaBweReceiver::kReceivingRateTimeWindowMs;
|
||||
|
||||
// Insert the same packet twice.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const MediaPacket media_packet(kFlowId, kSendTimeUs + 50 * i,
|
||||
kPayloadSizeBytes, kSequenceNumber);
|
||||
nada_receiver_.ReceivePacket(kArrivalTimeMs + 50 * i, media_packet);
|
||||
}
|
||||
// Should be counted only once.
|
||||
const size_t kReceivingRateKbps = 8 * kPayloadSizeBytes / kTimeWindowMs;
|
||||
|
||||
EXPECT_EQ(nada_receiver_.RecentReceivingRate(), kReceivingRateKbps);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, PacketLossNoPackets) {
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, PacketLossSinglePacket) {
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, 0);
|
||||
nada_receiver_.ReceivePacket(0, media_packet);
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, PacketLossContiguousPackets) {
|
||||
const int64_t kTimeWindowMs = NadaBweReceiver::kPacketLossTimeWindowMs;
|
||||
const int kSetCapacity = NadaBweReceiver::kSetCapacity;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||
// Sequence_number and flow_id are the only members that matter here.
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||
// Arrival time = 0, all packets will be considered.
|
||||
nada_receiver_.ReceivePacket(0, media_packet);
|
||||
}
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
|
||||
for (int i = 30; i > 20; i--) {
|
||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||
// Sequence_number and flow_id are the only members that matter here.
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||
// Only the packets sent in this for loop will be considered.
|
||||
nada_receiver_.ReceivePacket(2 * kTimeWindowMs, media_packet);
|
||||
}
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
|
||||
// Should handle uint16_t overflow.
|
||||
for (int i = 0xFFFF - 10; i < 0xFFFF + 10; ++i) {
|
||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||
// Only the packets sent in this for loop will be considered.
|
||||
nada_receiver_.ReceivePacket(4 * kTimeWindowMs, media_packet);
|
||||
}
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
|
||||
// Should handle set overflow.
|
||||
for (int i = 0; i < kSetCapacity * 1.5; ++i) {
|
||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||
// Only the packets sent in this for loop will be considered.
|
||||
nada_receiver_.ReceivePacket(6 * kTimeWindowMs, media_packet);
|
||||
}
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
}
|
||||
|
||||
// Should handle duplicates.
|
||||
TEST_F(NadaReceiverSideTest, PacketLossDuplicatedPackets) {
|
||||
const int64_t kTimeWindowMs = NadaBweReceiver::kPacketLossTimeWindowMs;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, 0);
|
||||
// Arrival time = 0, all packets will be considered.
|
||||
nada_receiver_.ReceivePacket(0, media_packet);
|
||||
}
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
|
||||
// Missing the element 5.
|
||||
const uint16_t kSequenceNumbers[] = {1, 2, 3, 4, 6, 7, 8};
|
||||
const int kNumPackets = ARRAY_SIZE(kSequenceNumbers);
|
||||
|
||||
// Insert each sequence number twice.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < kNumPackets; j++) {
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, kSequenceNumbers[j]);
|
||||
// Only the packets sent in this for loop will be considered.
|
||||
nada_receiver_.ReceivePacket(2 * kTimeWindowMs, media_packet);
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_NEAR(nada_receiver_.RecentPacketLossRatio(), 1.0f / (kNumPackets + 1),
|
||||
0.1f / (kNumPackets + 1));
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, PacketLossLakingPackets) {
|
||||
const int kSetCapacity = NadaBweReceiver::kSetCapacity;
|
||||
EXPECT_LT(kSetCapacity, 0xFFFF);
|
||||
|
||||
// Missing every other packet.
|
||||
for (int i = 0; i < kSetCapacity; ++i) {
|
||||
if ((i & 1) == 0) { // Only even sequence numbers.
|
||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||
// Arrival time = 0, all packets will be considered.
|
||||
nada_receiver_.ReceivePacket(0, media_packet);
|
||||
}
|
||||
}
|
||||
EXPECT_NEAR(nada_receiver_.RecentPacketLossRatio(), 0.5f, 0.01f);
|
||||
}
|
||||
|
||||
TEST_F(NadaReceiverSideTest, PacketLossLakingFewPackets) {
|
||||
const int kSetCapacity = NadaBweReceiver::kSetCapacity;
|
||||
EXPECT_LT(kSetCapacity, 0xFFFF);
|
||||
|
||||
const int kPeriod = 100;
|
||||
// Missing one for each kPeriod packets.
|
||||
for (int i = 0; i < kSetCapacity; ++i) {
|
||||
if ((i % kPeriod) != 0) {
|
||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||
// Arrival time = 0, all packets will be considered.
|
||||
nada_receiver_.ReceivePacket(0, media_packet);
|
||||
}
|
||||
}
|
||||
EXPECT_NEAR(nada_receiver_.RecentPacketLossRatio(), 1.0f / kPeriod,
|
||||
0.1f / kPeriod);
|
||||
}
|
||||
|
||||
// Packet's sequence numbers greatly apart, expect high loss.
|
||||
TEST_F(NadaReceiverSideTest, PacketLossWideGap) {
|
||||
const int64_t kTimeWindowMs = NadaBweReceiver::kPacketLossTimeWindowMs;
|
||||
|
||||
const MediaPacket media_packet1(0, 0, 0, 1);
|
||||
const MediaPacket media_packet2(0, 0, 0, 1000);
|
||||
// Only these two packets will be considered.
|
||||
nada_receiver_.ReceivePacket(0, media_packet1);
|
||||
nada_receiver_.ReceivePacket(0, media_packet2);
|
||||
EXPECT_NEAR(nada_receiver_.RecentPacketLossRatio(), 0.998f, 0.0001f);
|
||||
|
||||
const MediaPacket media_packet3(0, 0, 0, 0);
|
||||
const MediaPacket media_packet4(0, 0, 0, 0x8000);
|
||||
// Only these two packets will be considered.
|
||||
nada_receiver_.ReceivePacket(2 * kTimeWindowMs, media_packet3);
|
||||
nada_receiver_.ReceivePacket(2 * kTimeWindowMs, media_packet4);
|
||||
EXPECT_NEAR(nada_receiver_.RecentPacketLossRatio(), 0.99994f, 0.00001f);
|
||||
}
|
||||
|
||||
// Packets arriving unordered should not be counted as losted.
|
||||
TEST_F(NadaReceiverSideTest, PacketLossUnorderedPackets) {
|
||||
const int kNumPackets = NadaBweReceiver::kSetCapacity / 2;
|
||||
std::vector<uint16_t> sequence_numbers;
|
||||
|
||||
for (int i = 0; i < kNumPackets; ++i) {
|
||||
sequence_numbers.push_back(static_cast<uint16_t>(i + 1));
|
||||
}
|
||||
|
||||
random_shuffle(sequence_numbers.begin(), sequence_numbers.end());
|
||||
|
||||
for (int i = 0; i < kNumPackets; ++i) {
|
||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_numbers[i]);
|
||||
// Arrival time = 0, all packets will be considered.
|
||||
nada_receiver_.ReceivePacket(0, media_packet);
|
||||
}
|
||||
|
||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||
}
|
||||
|
||||
TEST_F(FilterTest, MedianConstantArray) {
|
||||
MedianFilterConstantArray();
|
||||
for (int i = 0; i < kNumElements; ++i) {
|
||||
EXPECT_EQ(median_filtered_[i], raw_signal_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MedianFilterTest, IntermittentNoise) {
|
||||
FilterFromIntermittentNoiseArray();
|
||||
TEST_F(FilterTest, MedianIntermittentNoise) {
|
||||
MedianFilterIntermittentNoise();
|
||||
}
|
||||
|
||||
TEST_F(FilterTest, ExponentialSmoothingConstantArray) {
|
||||
int64_t exp_smoothed[kNumElements];
|
||||
ExponentialSmoothingConstantArray(exp_smoothed);
|
||||
for (int i = 0; i < kNumElements; ++i) {
|
||||
EXPECT_EQ(exp_smoothed[i], kSignalValue);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FilterTest, ExponentialSmoothingInitialPertubation) {
|
||||
const int64_t kSignal[] = {90000, 0, 0, 0, 0, 0};
|
||||
const int kNumElements = ARRAY_SIZE(kSignal);
|
||||
int64_t exp_smoothed[kNumElements];
|
||||
ExponentialSmoothingFilter(kSignal, kNumElements, exp_smoothed);
|
||||
for (int i = 1; i < kNumElements; ++i) {
|
||||
EXPECT_EQ(
|
||||
exp_smoothed[i],
|
||||
static_cast<int64_t>(exp_smoothed[i - 1] * (1.0f - kAlpha) + 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bwe
|
||||
|
||||
Reference in New Issue
Block a user