NADA's proposal from Cisco.

The implementation of this proposal is in progress.
More unittest will be added.
Sender side is being implemented.
Some constants need to be tuned.

BUG=4550
R=stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/43299004

Cr-Commit-Position: refs/heads/master@{#9146}
This commit is contained in:
Cesar Magalhaes 2015-05-06 22:29:01 +02:00
parent f353dd59b5
commit c81591d63f
13 changed files with 550 additions and 94 deletions

View File

@ -82,6 +82,7 @@
'test/packet.h', 'test/packet.h',
'test/estimators/nada.cc', 'test/estimators/nada.cc',
'test/estimators/nada.h', 'test/estimators/nada.h',
'test/estimators/nada_unittest.cc',
'test/estimators/remb.cc', 'test/estimators/remb.cc',
'test/estimators/remb.h', 'test/estimators/remb.h',
'test/estimators/send_side.cc', 'test/estimators/send_side.cc',

View File

@ -178,9 +178,10 @@ void MediaPacket::SetAbsSendTimeMs(int64_t abs_send_time_ms) {
RembFeedback::RembFeedback(int flow_id, RembFeedback::RembFeedback(int flow_id,
int64_t send_time_us, int64_t send_time_us,
int64_t last_send_time_ms,
uint32_t estimated_bps, uint32_t estimated_bps,
RTCPReportBlock report_block) RTCPReportBlock report_block)
: FeedbackPacket(flow_id, send_time_us), : FeedbackPacket(flow_id, send_time_us, last_send_time_ms),
estimated_bps_(estimated_bps), estimated_bps_(estimated_bps),
report_block_(report_block) { report_block_(report_block) {
} }
@ -188,8 +189,9 @@ RembFeedback::RembFeedback(int flow_id,
SendSideBweFeedback::SendSideBweFeedback( SendSideBweFeedback::SendSideBweFeedback(
int flow_id, int flow_id,
int64_t send_time_us, int64_t send_time_us,
int64_t last_send_time_ms,
const std::vector<PacketInfo>& packet_feedback_vector) const std::vector<PacketInfo>& packet_feedback_vector)
: FeedbackPacket(flow_id, send_time_us), : FeedbackPacket(flow_id, send_time_us, last_send_time_ms),
packet_feedback_vector_(packet_feedback_vector) { packet_feedback_vector_(packet_feedback_vector) {
} }

View File

@ -14,11 +14,10 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/constructormagic.h" #include "webrtc/base/constructormagic.h"
#include "webrtc/modules/remote_bitrate_estimator/test/packet.h"
#include "webrtc/modules/remote_bitrate_estimator/test/packet_sender.h" #include "webrtc/modules/remote_bitrate_estimator/test/packet_sender.h"
#include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/fileutils.h"
using std::vector;
namespace webrtc { namespace webrtc {
namespace testing { namespace testing {
namespace bwe { namespace bwe {
@ -947,7 +946,7 @@ TEST(BweTestFramework_VideoSenderTest, FeedbackIneffective) {
TestVideoSender(&sender, 9998, 1000, 500, 1025000); TestVideoSender(&sender, 9998, 1000, 500, 1025000);
// Make sure feedback has no effect on a regular video sender. // Make sure feedback has no effect on a regular video sender.
RembFeedback* feedback = new RembFeedback(0, 0, 512000, RTCPReportBlock()); RembFeedback* feedback = new RembFeedback(0, 0, 0, 512000, RTCPReportBlock());
Packets packets; Packets packets;
packets.push_back(feedback); packets.push_back(feedback);
sender.RunFor(0, &packets); sender.RunFor(0, &packets);
@ -962,7 +961,7 @@ TEST(BweTestFramework_AdaptiveVideoSenderTest, FeedbackChangesBitrate) {
TestVideoSender(&sender, 9998, 1000, 500, 1025000); TestVideoSender(&sender, 9998, 1000, 500, 1025000);
// Make sure we can reduce the bitrate. // Make sure we can reduce the bitrate.
RembFeedback* feedback = new RembFeedback(0, 0, 512000, RTCPReportBlock()); RembFeedback* feedback = new RembFeedback(0, 0, 0, 512000, RTCPReportBlock());
Packets packets; Packets packets;
packets.push_back(feedback); packets.push_back(feedback);
sender.RunFor(0, &packets); sender.RunFor(0, &packets);
@ -971,7 +970,7 @@ TEST(BweTestFramework_AdaptiveVideoSenderTest, FeedbackChangesBitrate) {
// Increase the bitrate to the initial bitrate and verify that the output is // Increase the bitrate to the initial bitrate and verify that the output is
// the same. // the same.
feedback = new RembFeedback(0, 0, 820000, RTCPReportBlock()); feedback = new RembFeedback(0, 0, 0, 820000, RTCPReportBlock());
packets.push_back(feedback); packets.push_back(feedback);
sender.RunFor(10000, &packets); sender.RunFor(10000, &packets);
EXPECT_EQ(820000u, source.bits_per_second()); EXPECT_EQ(820000u, source.bits_per_second());
@ -987,7 +986,7 @@ TEST(BweTestFramework_AdaptiveVideoSenderTest, Paced_FeedbackChangesBitrate) {
TestVideoSender(&sender, 9998, 1000, 500, 1025000); TestVideoSender(&sender, 9998, 1000, 500, 1025000);
// Make sure we can reduce the bitrate. // Make sure we can reduce the bitrate.
RembFeedback* feedback = new RembFeedback(0, 1, 512000, RTCPReportBlock()); RembFeedback* feedback = new RembFeedback(0, 1, 0, 512000, RTCPReportBlock());
Packets packets; Packets packets;
packets.push_back(feedback); packets.push_back(feedback);
sender.RunFor(10000, &packets); sender.RunFor(10000, &packets);
@ -996,7 +995,7 @@ TEST(BweTestFramework_AdaptiveVideoSenderTest, Paced_FeedbackChangesBitrate) {
// Increase the bitrate to the initial bitrate and verify that the output is // Increase the bitrate to the initial bitrate and verify that the output is
// the same. // the same.
feedback = new RembFeedback(0, 0, 820000, RTCPReportBlock()); feedback = new RembFeedback(0, 0, 0, 820000, RTCPReportBlock());
packets.push_back(feedback); packets.push_back(feedback);
sender.RunFor(10000, &packets); sender.RunFor(10000, &packets);
EXPECT_EQ(820000u, source.bits_per_second()); EXPECT_EQ(820000u, source.bits_per_second());

View File

@ -6,18 +6,28 @@
* tree. An additional intellectual property rights grant can be found * tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may * in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*
*/ */
// Implementation of Network-Assisted Dynamic Adaptation's (NADA's) proposal.
// Version according to Draft Document (mentioned in references)
// http://tools.ietf.org/html/draft-zhu-rmcat-nada-06
// From March 26, 2015.
#include <math.h>
#include <algorithm> #include <algorithm>
#include <vector>
#include <iostream>
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/nada.h" #include "webrtc/modules/remote_bitrate_estimator/test/estimators/nada.h"
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
namespace webrtc { namespace webrtc {
namespace testing { namespace testing {
namespace bwe { namespace bwe {
const int NadaBweReceiver::kMedian;
NadaBweReceiver::NadaBweReceiver(int flow_id) NadaBweReceiver::NadaBweReceiver(int flow_id)
: BweReceiver(flow_id), : BweReceiver(flow_id),
clock_(0), clock_(0),
@ -25,7 +35,10 @@ NadaBweReceiver::NadaBweReceiver(int flow_id)
recv_stats_(ReceiveStatistics::Create(&clock_)), recv_stats_(ReceiveStatistics::Create(&clock_)),
baseline_delay_ms_(0), baseline_delay_ms_(0),
delay_signal_ms_(0), delay_signal_ms_(0),
last_congestion_signal_ms_(0) { last_congestion_signal_ms_(0),
last_delays_index_(0),
exp_smoothed_delay_ms_(-1),
est_queuing_delay_signal_ms_(0) {
} }
NadaBweReceiver::~NadaBweReceiver() { NadaBweReceiver::~NadaBweReceiver() {
@ -33,34 +46,51 @@ NadaBweReceiver::~NadaBweReceiver() {
void NadaBweReceiver::ReceivePacket(int64_t arrival_time_ms, void NadaBweReceiver::ReceivePacket(int64_t arrival_time_ms,
const MediaPacket& media_packet) { const MediaPacket& media_packet) {
const float kAlpha = 0.9f; // Used for exponential smoothing.
const int64_t kDelayLowThresholdMs = 50; // Referred as d_th.
const int64_t kDelayMaxThresholdMs = 400; // Referred as d_max.
clock_.AdvanceTimeMilliseconds(arrival_time_ms - clock_.TimeInMilliseconds()); clock_.AdvanceTimeMilliseconds(arrival_time_ms - clock_.TimeInMilliseconds());
recv_stats_->IncomingPacket(media_packet.header(), recv_stats_->IncomingPacket(media_packet.header(),
media_packet.payload_size(), false); media_packet.payload_size(), false);
int64_t delay_ms = arrival_time_ms - media_packet.creation_time_us() / 1000; int64_t delay_ms = arrival_time_ms -
// TODO(holmer): The min should time out after 10 minutes. media_packet.creation_time_us() / 1000; // Refered as x_n.
if (delay_ms < baseline_delay_ms_) { // The min should be updated within the first 10 minutes.
baseline_delay_ms_ = delay_ms; if (clock_.TimeInMilliseconds() < 10 * 60 * 1000) {
baseline_delay_ms_ = std::min(baseline_delay_ms_, delay_ms);
} }
delay_signal_ms_ = delay_ms - baseline_delay_ms_; delay_signal_ms_ = delay_ms - baseline_delay_ms_; // Refered as d_n.
last_delays_ms_[(last_delays_index_++) % kMedian] = delay_signal_ms_;
int size = std::min(last_delays_index_, kMedian);
int64_t median_filtered_delay_ms_ = MedianFilter(last_delays_ms_, size);
exp_smoothed_delay_ms_ = ExponentialSmoothingFilter(
median_filtered_delay_ms_, exp_smoothed_delay_ms_, kAlpha);
if (exp_smoothed_delay_ms_ < kDelayLowThresholdMs) {
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));
} else {
est_queuing_delay_signal_ms_ = 0;
}
received_packets_->Insert(media_packet.sequence_number(),
media_packet.send_time_ms(), arrival_time_ms,
media_packet.payload_size());
} }
FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) { FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
if (now_ms - last_feedback_ms_ < 100) const int64_t kPacketLossPenaltyMs = 1000; // Referred as d_L.
return NULL;
StatisticianMap statisticians = recv_stats_->GetActiveStatisticians(); if (now_ms - last_feedback_ms_ < 100) {
int64_t loss_signal_ms = 0.0f; return NULL;
if (!statisticians.empty()) {
RtcpStatistics stats;
if (!statisticians.begin()->second->GetStatistics(&stats, true)) {
const float kLossSignalWeight = 1000.0f;
loss_signal_ms =
(kLossSignalWeight * static_cast<float>(stats.fraction_lost) + 127) /
255;
}
} }
int64_t congestion_signal_ms = delay_signal_ms_ + loss_signal_ms; int64_t loss_signal_ms = static_cast<int64_t>(
RecentPacketLossRatio() * kPacketLossPenaltyMs + 0.5f);
int64_t congestion_signal_ms = est_queuing_delay_signal_ms_ + loss_signal_ms;
float derivative = 0.0f; float derivative = 0.0f;
if (last_feedback_ms_ > 0) { if (last_feedback_ms_ > 0) {
@ -69,14 +99,124 @@ FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
} }
last_feedback_ms_ = now_ms; last_feedback_ms_ = now_ms;
last_congestion_signal_ms_ = congestion_signal_ms; last_congestion_signal_ms_ = congestion_signal_ms;
return new NadaFeedback(flow_id_, now_ms, congestion_signal_ms, derivative);
PacketIdentifierNode* latest = *(received_packets_->begin());
int64_t corrected_send_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.
return new NadaFeedback(flow_id_, now_ms, exp_smoothed_delay_ms_,
est_queuing_delay_signal_ms_, congestion_signal_ms,
derivative, RecentReceivingRate(),
corrected_send_time_ms);
}
float NadaBweReceiver::GlobalPacketLossRatio() {
if (received_packets_->empty()) {
return 0.0f;
}
// Possibly there are packets missing.
const uint16_t kMaxGap = 1.5 * kSetCapacity;
uint16_t min = received_packets_->find_min();
uint16_t max = received_packets_->find_max();
int gap;
if (max - min < kMaxGap) {
gap = max - min + 1;
} else { // There was an overflow.
max = received_packets_->upper_bound(kMaxGap);
min = received_packets_->lower_bound(0xFFFF - kMaxGap);
gap = max + (0xFFFF - min) + 2;
}
return static_cast<float>(received_packets_->size()) / gap;
}
// Go through a fixed time window of most recent packets received and
// counts packets missing to obtain the packet loss ratio. If an unordered
// packet falls out of the timewindow it will be counted as missing.
// 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;
}
int number_packets_received = 0;
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;
// Oldest and newest values found within the given time window.
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) {
break;
}
uint16_t seq_nb = (*node_it)->sequence_number_;
if (IsNewerSequenceNumber(seq_nb, newest_seq_nb)) {
newest_seq_nb = seq_nb;
}
if (IsNewerSequenceNumber(oldest_seq_nb, seq_nb)) {
oldest_seq_nb = seq_nb;
}
++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);
return static_cast<float>(gap - number_packets_received) / gap;
}
size_t NadaBweReceiver::RecentReceivingRate() {
const int64_t kRecentTimeWindowMs = 500;
if (received_packets_->empty()) {
return 0.0f;
}
size_t totalSize = 0;
int64_t time_limit_ms = clock_.TimeInMilliseconds() - kRecentTimeWindowMs;
PacketNodeIt node_it = received_packets_->begin();
PacketNodeIt end = received_packets_->end();
while (node_it != end && (*node_it)->arrival_time_ms_ > time_limit_ms) {
totalSize += (*node_it)->payload_size_;
++node_it;
}
return static_cast<size_t>((1000 * totalSize) / kRecentTimeWindowMs);
}
int64_t NadaBweReceiver::MedianFilter(int64_t* last_delays_ms, int size) {
// 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());
return array_copy.at(size / 2);
}
int64_t NadaBweReceiver::ExponentialSmoothingFilter(int64_t new_value,
int64_t last_smoothed_value,
float alpha) {
if (last_smoothed_value < 0) {
return new_value; // Handling initial case.
}
return static_cast<int64_t>(alpha * new_value +
(1.0f - alpha) * last_smoothed_value + 0.5f);
} }
NadaBweSender::NadaBweSender(int kbps, BitrateObserver* observer, Clock* clock) NadaBweSender::NadaBweSender(int kbps, BitrateObserver* observer, Clock* clock)
: clock_(clock), : clock_(clock), observer_(observer), bitrate_kbps_(kbps) {
observer_(observer), }
bitrate_kbps_(kbps),
last_feedback_ms_(0) { NadaBweSender::NadaBweSender(BitrateObserver* observer, Clock* clock)
: clock_(clock), observer_(observer), bitrate_kbps_(kMinRefRateKbps) {
} }
NadaBweSender::~NadaBweSender() { NadaBweSender::~NadaBweSender() {
@ -88,31 +228,32 @@ int NadaBweSender::GetFeedbackIntervalMs() const {
void NadaBweSender::GiveFeedback(const FeedbackPacket& feedback) { void NadaBweSender::GiveFeedback(const FeedbackPacket& feedback) {
const NadaFeedback& fb = static_cast<const NadaFeedback&>(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_;
// TODO(holmer): Implement special start-up behavior. const int kMaxRefRateKbps = 1500; // Referred as R_max.
const float kEta = 2.0f;
const float kTaoO = 500.0f;
float x_hat = fb.congestion_signal() + kEta * kTaoO * fb.derivative();
int64_t now_ms = clock_->TimeInMilliseconds(); int64_t now_ms = clock_->TimeInMilliseconds();
float delta_s = now_ms - last_feedback_ms_; float delta_s = now_ms - last_feedback_ms_;
last_feedback_ms_ = now_ms; last_feedback_ms_ = now_ms;
// Update delta_0.
min_feedback_delay_ms_ =
std::min(min_feedback_delay_ms_, static_cast<int64_t>(delta_s));
const float kPriorityWeight = 1.0f; // Update RTT_0.
const float kReferenceDelayS = 10.0f; int64_t rtt = now_ms - fb.latest_send_time_ms();
float kTheta = min_round_trip_time_ms_ = std::min(min_round_trip_time_ms_, rtt);
kPriorityWeight * (kMaxBitrateKbps - kMinBitrateKbps) * kReferenceDelayS;
const float kKappa = 1.0f; // Independent limits for those variables.
bitrate_kbps_ = bitrate_kbps_ + // There should be no packet losses/marking, hence x_n == d_tilde.
kKappa * delta_s / (kTaoO * kTaoO) * if (fb.congestion_signal() == fb.est_queuing_delay_signal_ms() &&
(kTheta - (bitrate_kbps_ - kMinBitrateKbps) * x_hat) + fb.est_queuing_delay_signal_ms() < kQueuingDelayUpperBoundMs &&
0.5f; fb.derivative() < kDerivativeUpperBound) {
bitrate_kbps_ = std::min(bitrate_kbps_, kMaxBitrateKbps); AcceleratedRampUp(fb, kMaxRefRateKbps);
bitrate_kbps_ = std::max(bitrate_kbps_, kMinBitrateKbps); } else {
GradualRateUpdate(fb, kMaxRefRateKbps, delta_s);
observer_->OnNetworkChanged(1000 * bitrate_kbps_, 0, 0); }
} }
int64_t NadaBweSender::TimeUntilNextProcess() { int64_t NadaBweSender::TimeUntilNextProcess() {
@ -123,6 +264,75 @@ int NadaBweSender::Process() {
return 0; return 0;
} }
void NadaBweSender::AcceleratedRampUp(const NadaFeedback& fb,
const int kMaxRefRateKbps) {
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);
}
void NadaBweSender::GradualRateUpdate(const NadaFeedback& fb,
const int kMaxRefRateKbps,
const float delta_s) {
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.
float kPriorityWeight = static_cast<float>(fb.exp_smoothed_delay_ms()) /
kReferenceDelayMs; // Referred as w.
float kTheta =
kPriorityWeight * (kMaxRefRateKbps - kMinRefRateKbps) * kReferenceDelayMs;
float x_hat = fb.congestion_signal() + kEta * kTauOMs * fb.derivative();
bitrate_kbps_ =
bitrate_kbps_ +
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);
}
void LinkedSet::Insert(uint16_t sequence_number,
int64_t send_time_ms,
int64_t arrival_time_ms,
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));
}
} else {
if (size() == capacity_) {
RemoveTail();
}
UpdateHead(new PacketIdentifierNode(sequence_number, send_time_ms,
arrival_time_ms, payload_size));
}
}
void LinkedSet::RemoveTail() {
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();
}
} // namespace bwe } // namespace bwe
} // namespace testing } // namespace testing
} // namespace webrtc } // namespace webrtc

View File

@ -6,12 +6,23 @@
* tree. An additional intellectual property rights grant can be found * tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may * in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ *
*/
// Implementation of Network-Assisted Dynamic Adaptation's (NADA's) proposal
// Version according to Draft Document (mentioned in references)
// http://tools.ietf.org/html/draft-zhu-rmcat-nada-06
// From March 26, 2015.
#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_NADA_H_ #ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_NADA_H_
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_NADA_H_ #define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_NADA_H_
#include <list>
#include <map>
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/modules/remote_bitrate_estimator/test/bwe.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe.h"
#include "webrtc/voice_engine/channel.h"
namespace webrtc { namespace webrtc {
@ -20,6 +31,65 @@ class ReceiveStatistics;
namespace testing { namespace testing {
namespace bwe { namespace bwe {
// Holds only essential information about packets to be saved for
// further use, e.g. for calculating packet loss and receiving rate.
struct PacketIdentifierNode {
PacketIdentifierNode(uint16_t sequence_number,
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) {}
uint16_t sequence_number_;
int64_t send_time_ms_;
int64_t arrival_time_ms_;
size_t payload_size_;
};
typedef std::list<PacketIdentifierNode*>::iterator PacketNodeIt;
// FIFO implementation for a limited capacity set.
// Used for keeping the latest arrived packets while avoiding duplicates.
// Allows efficient insertion, deletion and search.
class LinkedSet {
public:
explicit LinkedSet(size_t capacity) : capacity_(capacity) {}
// If the arriving packet (identified by its sequence number) is already
// in the LinkedSet, move its Node to the head of the list. Else, create
// a PacketIdentifierNode n_ and then UpdateHead(n_), calling RemoveTail()
// if the LinkedSet reached its maximum capacity.
void Insert(uint16_t sequence_number,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t payload_size);
PacketNodeIt begin() { return list_.begin(); }
PacketNodeIt end() { return list_.end(); }
bool empty() { return list_.empty(); }
size_t size() { return list_.size(); }
// Gets the latest arrived sequence number.
uint16_t find_max() { return map_.rbegin()->first; }
// Gets the first arrived sequence number still saved in the LinkedSet.
uint16_t find_min() { return map_.begin()->first; }
// Gets the lowest saved sequence number that is >= than the input key.
uint16_t lower_bound(uint16_t key) { return map_.lower_bound(key)->first; }
// Gets the highest saved sequence number that is <= than the input key.
uint16_t upper_bound(uint16_t key) { return map_.upper_bound(key)->first; }
private:
// Pop oldest element from the back of the list and remove it from the map.
void RemoveTail();
// Add new element to the front of the list and insert it in the map.
void UpdateHead(PacketIdentifierNode* new_head);
size_t capacity_;
std::map<uint16_t, PacketNodeIt> map_;
std::list<PacketIdentifierNode*> list_;
};
class NadaBweReceiver : public BweReceiver { class NadaBweReceiver : public BweReceiver {
public: public:
explicit NadaBweReceiver(int flow_id); explicit NadaBweReceiver(int flow_id);
@ -28,32 +98,59 @@ class NadaBweReceiver : public BweReceiver {
void ReceivePacket(int64_t arrival_time_ms, void ReceivePacket(int64_t arrival_time_ms,
const MediaPacket& media_packet) override; const MediaPacket& media_packet) override;
FeedbackPacket* GetFeedback(int64_t now_ms) override; FeedbackPacket* GetFeedback(int64_t now_ms) override;
float GlobalPacketLossRatio();
float RecentPacketLossRatio();
size_t RecentReceivingRate();
static int64_t MedianFilter(int64_t* v, int size);
static int64_t ExponentialSmoothingFilter(int64_t new_value,
int64_t last_smoothed_value,
float alpha);
private: private:
SimulatedClock clock_; SimulatedClock clock_;
int64_t last_feedback_ms_; int64_t last_feedback_ms_;
rtc::scoped_ptr<ReceiveStatistics> recv_stats_; rtc::scoped_ptr<ReceiveStatistics> recv_stats_;
int64_t baseline_delay_ms_; int64_t baseline_delay_ms_; // Referred as d_f.
int64_t delay_signal_ms_; int64_t delay_signal_ms_; // Referred as d_n.
int64_t last_congestion_signal_ms_; int64_t last_congestion_signal_ms_;
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.
}; };
class NadaBweSender : public BweSender { class NadaBweSender : public BweSender {
public: public:
NadaBweSender(int kbps, BitrateObserver* observer, Clock* clock); NadaBweSender(int kbps, BitrateObserver* observer, Clock* clock);
NadaBweSender(BitrateObserver* observer, Clock* clock);
virtual ~NadaBweSender(); virtual ~NadaBweSender();
int GetFeedbackIntervalMs() const override; int GetFeedbackIntervalMs() const override;
// Updates the min_feedback_delay_ms_ and the min_round_trip_time_ms_.
void GiveFeedback(const FeedbackPacket& feedback) override; void GiveFeedback(const FeedbackPacket& feedback) override;
void OnPacketsSent(const Packets& packets) override {} void OnPacketsSent(const Packets& packets) override {}
int64_t TimeUntilNextProcess() override; int64_t TimeUntilNextProcess() override;
int Process() override; int Process() override;
void AcceleratedRampUp(const NadaFeedback& fb, const int kMaxRefRateKbps);
void GradualRateUpdate(const NadaFeedback& fb,
const int kMaxRefRateKbps,
const float delta_s);
private: private:
Clock* const clock_; Clock* const clock_;
BitrateObserver* const observer_; BitrateObserver* const observer_;
int bitrate_kbps_; // Referred as R_min, default initialization for bitrate R_n.
int64_t last_feedback_ms_; 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;
// Referred as RTT_0, initialized as an upper bound.
int64_t min_round_trip_time_ms_ = 500;
DISALLOW_IMPLICIT_CONSTRUCTORS(NadaBweSender); DISALLOW_IMPLICIT_CONSTRUCTORS(NadaBweSender);
}; };

View File

@ -0,0 +1,91 @@
/*
* 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 <algorithm>
#include <numeric>
#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"
#include "webrtc/test/testsupport/fileutils.h"
namespace webrtc {
namespace testing {
namespace bwe {
class MedianFilterTest : public ::testing::Test {
public:
void FilterFromConstantArray() {
for (int i = 0; i < kSize; v[i++] = 200) {
}
for (int i = 0; i < kSize; ++i) {
int size = std::min(5, i + 1);
m_filtered[i] = NadaBweReceiver::MedianFilter(v + i + 1 - size, size);
}
}
void FilterFromIntermittentNoiseArray() {
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 < kSize; ++i) {
int size = std::min(5, i + 1);
m_filtered[i] = NadaBweReceiver::MedianFilter(v + i + 1 - size, size);
EXPECT_EQ(m_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) {
}
exp_smoothed[0] =
NadaBweReceiver::ExponentialSmoothingFilter(v[0], -1, kAlpha);
for (int i = 1; i < kSize; ++i) {
exp_smoothed[i] = NadaBweReceiver::ExponentialSmoothingFilter(
v[i], exp_smoothed[i - 1], kAlpha);
}
}
protected:
static const int kSize = 1000;
const float kAlpha = 0.8f;
int64_t v[kSize];
int64_t exp_smoothed[kSize];
};
TEST_F(MedianFilterTest, ConstantArray) {
FilterFromConstantArray();
for (int i = 0; i < kSize; ++i) {
EXPECT_TRUE(m_filtered[i] == v[i]);
}
}
TEST_F(MedianFilterTest, IntermittentNoise) {
FilterFromIntermittentNoiseArray();
}
} // namespace bwe
} // namespace testing
} // namespace webrtc

View File

@ -111,8 +111,10 @@ FeedbackPacket* RembReceiver::GetFeedback(int64_t now_ms) {
if (!statisticians.empty()) { if (!statisticians.empty()) {
report_block = BuildReportBlock(statisticians.begin()->second); report_block = BuildReportBlock(statisticians.begin()->second);
} }
feedback =
new RembFeedback(flow_id_, now_ms * 1000, estimated_bps, report_block); feedback = new RembFeedback(flow_id_, now_ms * 1000, last_feedback_ms_,
estimated_bps, report_block);
last_feedback_ms_ = now_ms;
double estimated_kbps = static_cast<double>(estimated_bps) / 1000.0; double estimated_kbps = static_cast<double>(estimated_bps) / 1000.0;
RTC_UNUSED(estimated_kbps); RTC_UNUSED(estimated_kbps);

View File

@ -70,6 +70,7 @@ class RembReceiver : public BweReceiver, public RemoteBitrateObserver {
SimulatedClock clock_; SimulatedClock clock_;
rtc::scoped_ptr<ReceiveStatistics> recv_stats_; rtc::scoped_ptr<ReceiveStatistics> recv_stats_;
int64_t latest_estimate_bps_; int64_t latest_estimate_bps_;
int64_t last_feedback_ms_;
rtc::scoped_ptr<RemoteBitrateEstimator> estimator_; rtc::scoped_ptr<RemoteBitrateEstimator> estimator_;
DISALLOW_IMPLICIT_CONSTRUCTORS(RembReceiver); DISALLOW_IMPLICIT_CONSTRUCTORS(RembReceiver);

View File

@ -126,9 +126,10 @@ void SendSideBweReceiver::ReceivePacket(int64_t arrival_time_ms,
FeedbackPacket* SendSideBweReceiver::GetFeedback(int64_t now_ms) { FeedbackPacket* SendSideBweReceiver::GetFeedback(int64_t now_ms) {
if (now_ms - last_feedback_ms_ < 100) if (now_ms - last_feedback_ms_ < 100)
return NULL; return NULL;
int64_t latest_send_time_ms = last_feedback_ms_;
last_feedback_ms_ = now_ms; last_feedback_ms_ = now_ms;
FeedbackPacket* fb = FeedbackPacket* fb = new SendSideBweFeedback(
new SendSideBweFeedback(flow_id_, now_ms * 1000, packet_feedback_vector_); flow_id_, now_ms * 1000, latest_send_time_ms, packet_feedback_vector_);
packet_feedback_vector_.clear(); packet_feedback_vector_.clear();
return fb; return fb;
} }

View File

@ -35,10 +35,9 @@ void TcpBweReceiver::ReceivePacket(int64_t arrival_time_ms,
} }
FeedbackPacket* TcpBweReceiver::GetFeedback(int64_t now_ms) { FeedbackPacket* TcpBweReceiver::GetFeedback(int64_t now_ms) {
// if (now_ms - last_feedback_ms_ < 100) FeedbackPacket* fb =
// return NULL; new TcpFeedback(flow_id_, now_ms * 1000, last_feedback_ms_, acks_);
last_feedback_ms_ = now_ms; last_feedback_ms_ = now_ms;
FeedbackPacket* fb = new TcpFeedback(flow_id_, now_ms * 1000, acks_);
acks_.clear(); acks_.clear();
return fb; return fb;
} }

View File

@ -39,12 +39,17 @@ class Packet {
virtual int64_t send_time_us() const { return send_time_us_; } virtual int64_t send_time_us() const { return send_time_us_; }
virtual size_t payload_size() const { return payload_size_; } virtual size_t payload_size() const { return payload_size_; }
virtual Packet::Type GetPacketType() const = 0; virtual Packet::Type GetPacketType() const = 0;
void set_sender_timestamp_us(int64_t sender_timestamp_us) {
sender_timestamp_us_ = sender_timestamp_us;
}
int64_t sender_timestamp_us() const { return sender_timestamp_us_; }
private: protected:
int flow_id_; int flow_id_;
int64_t creation_time_us_; // Time when the packet was created. int64_t creation_time_us_; // Time when the packet was created.
int64_t send_time_us_; // Time the packet left last processor touching it. int64_t send_time_us_; // Time the packet left last processor touching it.
size_t payload_size_; // Size of the (non-existent, simulated) payload. int64_t sender_timestamp_us_; // Time the packet left the Sender.
size_t payload_size_; // Size of the (non-existent, simulated) payload.
}; };
class MediaPacket : public Packet { class MediaPacket : public Packet {
@ -69,6 +74,8 @@ class MediaPacket : public Packet {
void SetAbsSendTimeMs(int64_t abs_send_time_ms); void SetAbsSendTimeMs(int64_t abs_send_time_ms);
const RTPHeader& header() const { return header_; } const RTPHeader& header() const { return header_; }
virtual Packet::Type GetPacketType() const { return kMedia; } virtual Packet::Type GetPacketType() const { return kMedia; }
uint16_t sequence_number() const { return header_.sequenceNumber; }
int64_t send_time_ms() const { return send_time_us_ / 1000; }
private: private:
static const int kAbsSendTimeFraction = 18; static const int kAbsSendTimeFraction = 18;
@ -81,17 +88,25 @@ class MediaPacket : public Packet {
class FeedbackPacket : public Packet { class FeedbackPacket : public Packet {
public: public:
FeedbackPacket(int flow_id, int64_t send_time_us) FeedbackPacket(int flow_id,
: Packet(flow_id, send_time_us, 0) {} int64_t this_send_time_us,
int64_t latest_send_time_ms)
: Packet(flow_id, this_send_time_us, 0),
latest_send_time_ms_(latest_send_time_ms) {}
virtual ~FeedbackPacket() {} virtual ~FeedbackPacket() {}
virtual Packet::Type GetPacketType() const { return kFeedback; } virtual Packet::Type GetPacketType() const { return kFeedback; }
int64_t latest_send_time_ms() const { return latest_send_time_ms_; }
private:
int64_t latest_send_time_ms_; // Time stamp for the latest sent packet.
}; };
class RembFeedback : public FeedbackPacket { class RembFeedback : public FeedbackPacket {
public: public:
RembFeedback(int flow_id, RembFeedback(int flow_id,
int64_t send_time_us, int64_t send_time_us,
int64_t latest_send_time_ms,
uint32_t estimated_bps, uint32_t estimated_bps,
RTCPReportBlock report_block); RTCPReportBlock report_block);
virtual ~RembFeedback() {} virtual ~RembFeedback() {}
@ -109,6 +124,7 @@ class SendSideBweFeedback : public FeedbackPacket {
typedef std::map<uint16_t, int64_t> ArrivalTimesMap; typedef std::map<uint16_t, int64_t> ArrivalTimesMap;
SendSideBweFeedback(int flow_id, SendSideBweFeedback(int flow_id,
int64_t send_time_us, int64_t send_time_us,
int64_t latest_send_time_ms,
const std::vector<PacketInfo>& packet_feedback_vector); const std::vector<PacketInfo>& packet_feedback_vector);
virtual ~SendSideBweFeedback() {} virtual ~SendSideBweFeedback() {}
@ -123,28 +139,45 @@ class SendSideBweFeedback : public FeedbackPacket {
class NadaFeedback : public FeedbackPacket { class NadaFeedback : public FeedbackPacket {
public: public:
NadaFeedback(int flow_id, NadaFeedback(int flow_id,
int64_t send_time_us, int64_t this_send_time_us,
int64_t exp_smoothed_delay_ms,
int64_t est_queuing_delay_signal_ms,
int64_t congestion_signal, int64_t congestion_signal,
float derivative) float derivative,
: FeedbackPacket(flow_id, send_time_us), float receiving_rate,
int64_t latest_send_time_ms)
: FeedbackPacket(flow_id, this_send_time_us, latest_send_time_ms),
exp_smoothed_delay_ms_(exp_smoothed_delay_ms),
est_queuing_delay_signal_ms_(est_queuing_delay_signal_ms),
congestion_signal_(congestion_signal), congestion_signal_(congestion_signal),
derivative_(derivative) {} derivative_(derivative),
receiving_rate_(receiving_rate) {}
virtual ~NadaFeedback() {} virtual ~NadaFeedback() {}
int64_t exp_smoothed_delay_ms() const { return exp_smoothed_delay_ms_; }
int64_t est_queuing_delay_signal_ms() const {
return est_queuing_delay_signal_ms_;
}
int64_t congestion_signal() const { return congestion_signal_; } int64_t congestion_signal() const { return congestion_signal_; }
float derivative() const { return derivative_; } float derivative() const { return derivative_; }
float receiving_rate() const { return receiving_rate_; }
private: private:
int64_t congestion_signal_; int64_t exp_smoothed_delay_ms_; // Referred as d_hat_n.
float derivative_; int64_t est_queuing_delay_signal_ms_; // Referred as d_tilde_n.
int64_t congestion_signal_; // Referred as x_n.
float derivative_; // Referred as x'_n.
float receiving_rate_; // Referred as R_r.
}; };
class TcpFeedback : public FeedbackPacket { class TcpFeedback : public FeedbackPacket {
public: public:
TcpFeedback(int flow_id, TcpFeedback(int flow_id,
int64_t send_time_us, int64_t send_time_us,
int64_t latest_send_time_ms,
const std::vector<uint16_t>& acked_packets) const std::vector<uint16_t>& acked_packets)
: FeedbackPacket(flow_id, send_time_us), acked_packets_(acked_packets) {} : FeedbackPacket(flow_id, send_time_us, latest_send_time_ms),
acked_packets_(acked_packets) {}
virtual ~TcpFeedback() {} virtual ~TcpFeedback() {}
const std::vector<uint16_t>& acked_packets() const { return acked_packets_; } const std::vector<uint16_t>& acked_packets() const { return acked_packets_; }
@ -156,6 +189,7 @@ class TcpFeedback : public FeedbackPacket {
typedef std::list<Packet*> Packets; typedef std::list<Packet*> Packets;
typedef std::list<Packet*>::iterator PacketsIt; typedef std::list<Packet*>::iterator PacketsIt;
typedef std::list<Packet*>::const_iterator PacketsConstIt; typedef std::list<Packet*>::const_iterator PacketsConstIt;
} // namespace bwe } // namespace bwe
} // namespace testing } // namespace testing
} // namespace webrtc } // namespace webrtc

View File

@ -40,14 +40,16 @@ std::list<FeedbackPacket*> GetFeedbackPackets(Packets* in_out,
return fb_packets; return fb_packets;
} }
void PacketSender::SetSenderTimestamps(Packets* in_out) {
for (auto it = in_out->begin(); it != in_out->end(); ++it) {
(*it)->set_sender_timestamp_us(clock_.TimeInMilliseconds() * 1000);
}
}
VideoSender::VideoSender(PacketProcessorListener* listener, VideoSender::VideoSender(PacketProcessorListener* listener,
VideoSource* source, VideoSource* source,
BandwidthEstimatorType estimator_type) BandwidthEstimatorType estimator_type)
: PacketSender(listener, source->flow_id()), : PacketSender(listener, source->flow_id()),
// For Packet::send_time_us() to be comparable with timestamps from
// clock_, the clock of the VideoSender and the Source must be aligned.
// We assume that both start at time 0.
clock_(0),
source_(source), source_(source),
bwe_(CreateBweSender(estimator_type, bwe_(CreateBweSender(estimator_type,
source_->bits_per_second() / 1000, source_->bits_per_second() / 1000,
@ -60,10 +62,10 @@ VideoSender::~VideoSender() {
} }
void VideoSender::RunFor(int64_t time_ms, Packets* in_out) { void VideoSender::RunFor(int64_t time_ms, Packets* in_out) {
int64_t now_ms = clock_.TimeInMilliseconds(); std::list<FeedbackPacket*> feedbacks = GetFeedbackPackets(
std::list<FeedbackPacket*> feedbacks = in_out, clock_.TimeInMilliseconds() + time_ms, source_->flow_id());
GetFeedbackPackets(in_out, now_ms + time_ms, source_->flow_id());
ProcessFeedbackAndGeneratePackets(time_ms, &feedbacks, in_out); ProcessFeedbackAndGeneratePackets(time_ms, &feedbacks, in_out);
SetSenderTimestamps(in_out);
} }
void VideoSender::ProcessFeedbackAndGeneratePackets( void VideoSender::ProcessFeedbackAndGeneratePackets(
@ -190,6 +192,7 @@ void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
} }
} while (clock_.TimeInMilliseconds() < end_time_ms); } while (clock_.TimeInMilliseconds() < end_time_ms);
QueuePackets(in_out, end_time_ms * 1000); QueuePackets(in_out, end_time_ms * 1000);
SetSenderTimestamps(in_out);
} }
int64_t PacedVideoSender::TimeUntilNextProcess( int64_t PacedVideoSender::TimeUntilNextProcess(
@ -267,14 +270,15 @@ void PacedVideoSender::OnNetworkChanged(uint32_t target_bitrate_bps,
} }
void TcpSender::RunFor(int64_t time_ms, Packets* in_out) { void TcpSender::RunFor(int64_t time_ms, Packets* in_out) {
if (now_ms_ + time_ms < offset_ms_) { if (clock_.TimeInMilliseconds() + time_ms < offset_ms_) {
now_ms_ += time_ms; clock_.AdvanceTimeMilliseconds(time_ms);
return; return;
} }
BWE_TEST_LOGGING_CONTEXT("Sender"); BWE_TEST_LOGGING_CONTEXT("Sender");
BWE_TEST_LOGGING_CONTEXT(*flow_ids().begin()); BWE_TEST_LOGGING_CONTEXT(*flow_ids().begin());
std::list<FeedbackPacket*> feedbacks =
GetFeedbackPackets(in_out, now_ms_ + time_ms, *flow_ids().begin()); std::list<FeedbackPacket*> feedbacks = GetFeedbackPackets(
in_out, clock_.TimeInMilliseconds() + time_ms, *flow_ids().begin());
// The number of packets which are sent in during time_ms depends on the // The number of packets which are sent in during time_ms depends on the
// number of packets in_flight_ and the max number of packets in flight // number of packets in_flight_ and the max number of packets in flight
// (cwnd_). Therefore SendPackets() isn't directly dependent on time_ms. // (cwnd_). Therefore SendPackets() isn't directly dependent on time_ms.
@ -283,8 +287,16 @@ void TcpSender::RunFor(int64_t time_ms, Packets* in_out) {
SendPackets(in_out); SendPackets(in_out);
} }
for (auto it = in_flight_.begin(); it != in_flight_.end();) {
if (it->time_ms < clock_.TimeInMilliseconds() - 1000)
in_flight_.erase(it++);
else
++it;
}
SendPackets(in_out); SendPackets(in_out);
now_ms_ += time_ms; clock_.AdvanceTimeMilliseconds(time_ms);
SetSenderTimestamps(in_out);
} }
void TcpSender::SendPackets(Packets* in_out) { void TcpSender::SendPackets(Packets* in_out) {
@ -298,6 +310,7 @@ void TcpSender::SendPackets(Packets* in_out) {
Packets generated = GeneratePackets(packets_to_send); Packets generated = GeneratePackets(packets_to_send);
for (Packet* packet : generated) for (Packet* packet : generated)
in_flight_.insert(InFlight(*static_cast<MediaPacket*>(packet))); in_flight_.insert(InFlight(*static_cast<MediaPacket*>(packet)));
in_out->merge(generated, DereferencingComparator<Packet>); in_out->merge(generated, DereferencingComparator<Packet>);
} }
} }
@ -312,7 +325,7 @@ void TcpSender::UpdateCongestionControl(const FeedbackPacket* fb) {
expected - static_cast<uint16_t>(tcp_fb->acked_packets().size()); expected - static_cast<uint16_t>(tcp_fb->acked_packets().size());
for (uint16_t ack_seq_num : tcp_fb->acked_packets()) for (uint16_t ack_seq_num : tcp_fb->acked_packets())
in_flight_.erase(InFlight(ack_seq_num, now_ms_)); in_flight_.erase(InFlight(ack_seq_num, clock_.TimeInMilliseconds()));
if (missing > 0) { if (missing > 0) {
HandleLoss(); HandleLoss();
@ -329,7 +342,7 @@ void TcpSender::UpdateCongestionControl(const FeedbackPacket* fb) {
int TcpSender::TriggerTimeouts() { int TcpSender::TriggerTimeouts() {
int timed_out = 0; int timed_out = 0;
for (auto it = in_flight_.begin(); it != in_flight_.end();) { for (auto it = in_flight_.begin(); it != in_flight_.end();) {
if (it->time_ms < now_ms_ - 1000) { if (it->time_ms < clock_.TimeInMilliseconds() - 1000) {
in_flight_.erase(it++); in_flight_.erase(it++);
++timed_out; ++timed_out;
} else { } else {
@ -347,7 +360,8 @@ void TcpSender::HandleLoss() {
Packets TcpSender::GeneratePackets(size_t num_packets) { Packets TcpSender::GeneratePackets(size_t num_packets) {
Packets generated; Packets generated;
for (size_t i = 0; i < num_packets; ++i) { for (size_t i = 0; i < num_packets; ++i) {
generated.push_back(new MediaPacket(*flow_ids().begin(), 1000 * now_ms_, generated.push_back(new MediaPacket(*flow_ids().begin(),
1000 * clock_.TimeInMilliseconds(),
1200, next_sequence_number_++)); 1200, next_sequence_number_++));
} }
return generated; return generated;

View File

@ -28,7 +28,11 @@ namespace bwe {
class PacketSender : public PacketProcessor { class PacketSender : public PacketProcessor {
public: public:
PacketSender(PacketProcessorListener* listener, int flow_id) PacketSender(PacketProcessorListener* listener, int flow_id)
: PacketProcessor(listener, flow_id, kSender) {} : PacketProcessor(listener, flow_id, kSender),
// For Packet::send_time_us() to be comparable with timestamps from
// clock_, the clock of the PacketSender and the Source must be aligned.
// We assume that both start at time 0.
clock_(0) {}
virtual ~PacketSender() {} virtual ~PacketSender() {}
// Call GiveFeedback() with the returned interval in milliseconds, provided // Call GiveFeedback() with the returned interval in milliseconds, provided
// there is a new estimate available. // there is a new estimate available.
@ -36,6 +40,10 @@ class PacketSender : public PacketProcessor {
// output of the estimators is sampled and therefore the baseline files may // output of the estimators is sampled and therefore the baseline files may
// have to be regenerated. // have to be regenerated.
virtual int GetFeedbackIntervalMs() const = 0; virtual int GetFeedbackIntervalMs() const = 0;
void SetSenderTimestamps(Packets* in_out);
protected:
SimulatedClock clock_;
}; };
class VideoSender : public PacketSender, public BitrateObserver { class VideoSender : public PacketSender, public BitrateObserver {
@ -60,7 +68,6 @@ class VideoSender : public PacketSender, public BitrateObserver {
std::list<FeedbackPacket*>* feedbacks, std::list<FeedbackPacket*>* feedbacks,
Packets* generated); Packets* generated);
SimulatedClock clock_;
VideoSource* source_; VideoSource* source_;
rtc::scoped_ptr<BweSender> bwe_; rtc::scoped_ptr<BweSender> bwe_;
int64_t start_of_run_ms_; int64_t start_of_run_ms_;
@ -107,7 +114,6 @@ class TcpSender : public PacketSender {
public: public:
TcpSender(PacketProcessorListener* listener, int flow_id, int64_t offset_ms) TcpSender(PacketProcessorListener* listener, int flow_id, int64_t offset_ms)
: PacketSender(listener, flow_id), : PacketSender(listener, flow_id),
now_ms_(0),
cwnd_(10), cwnd_(10),
ssthresh_(std::numeric_limits<int>::max()), ssthresh_(std::numeric_limits<int>::max()),
ack_received_(false), ack_received_(false),
@ -146,7 +152,6 @@ class TcpSender : public PacketSender {
void HandleLoss(); void HandleLoss();
Packets GeneratePackets(size_t num_packets); Packets GeneratePackets(size_t num_packets);
int64_t now_ms_;
float cwnd_; float cwnd_;
int ssthresh_; int ssthresh_;
std::set<InFlight> in_flight_; std::set<InFlight> in_flight_;