Enabling Packet-Loss plots for BweReceiver.
Packet-loss computation and plot were added to BweReceiver class. Objective function and plot were added to PacketReceiver class. BUG=4550 R=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/56459004 Cr-Commit-Position: refs/heads/master@{#9391}
This commit is contained in:
parent
c1b9d4e686
commit
77cabab51a
@ -22,6 +22,16 @@ namespace webrtc {
|
|||||||
namespace testing {
|
namespace testing {
|
||||||
namespace bwe {
|
namespace bwe {
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
const int kSetCapacity = 1000;
|
||||||
|
|
||||||
|
BweReceiver::BweReceiver(int flow_id)
|
||||||
|
: flow_id_(flow_id), received_packets_(kSetCapacity) {
|
||||||
|
}
|
||||||
|
|
||||||
class NullBweSender : public BweSender {
|
class NullBweSender : public BweSender {
|
||||||
public:
|
public:
|
||||||
NullBweSender() {}
|
NullBweSender() {}
|
||||||
@ -86,6 +96,98 @@ BweReceiver* CreateBweReceiver(BandwidthEstimatorType type,
|
|||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BweReceiver::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 BweReceiver::RecentPacketLossRatio() {
|
||||||
|
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 - kPacketLossTimeWindowMs;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
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_) {
|
||||||
|
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
|
||||||
|
@ -20,6 +20,66 @@ namespace webrtc {
|
|||||||
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(int 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; }
|
||||||
|
size_t capacity() { return capacity_; }
|
||||||
|
|
||||||
|
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_;
|
||||||
|
};
|
||||||
|
|
||||||
const int kMinBitrateKbps = 150;
|
const int kMinBitrateKbps = 150;
|
||||||
const int kMaxBitrateKbps = 3000;
|
const int kMaxBitrateKbps = 3000;
|
||||||
|
|
||||||
@ -38,15 +98,23 @@ class BweSender : public Module {
|
|||||||
|
|
||||||
class BweReceiver {
|
class BweReceiver {
|
||||||
public:
|
public:
|
||||||
explicit BweReceiver(int flow_id) : flow_id_(flow_id) {}
|
explicit BweReceiver(int flow_id);
|
||||||
virtual ~BweReceiver() {}
|
virtual ~BweReceiver() {}
|
||||||
|
|
||||||
virtual void ReceivePacket(int64_t arrival_time_ms,
|
virtual void ReceivePacket(int64_t arrival_time_ms,
|
||||||
const MediaPacket& media_packet) {}
|
const MediaPacket& media_packet) {}
|
||||||
virtual FeedbackPacket* GetFeedback(int64_t now_ms) { return NULL; }
|
virtual FeedbackPacket* GetFeedback(int64_t now_ms) { return NULL; }
|
||||||
|
|
||||||
|
float GlobalPacketLossRatio();
|
||||||
|
float RecentPacketLossRatio();
|
||||||
|
size_t GetSetCapacity() { return received_packets_.capacity(); }
|
||||||
|
|
||||||
|
static const int64_t kPacketLossTimeWindowMs = 500;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int flow_id_;
|
int flow_id_;
|
||||||
|
// Deals with packets sent more than once.
|
||||||
|
LinkedSet received_packets_;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BandwidthEstimatorType {
|
enum BandwidthEstimatorType {
|
||||||
|
@ -28,10 +28,9 @@ namespace testing {
|
|||||||
namespace bwe {
|
namespace bwe {
|
||||||
|
|
||||||
const int NadaBweReceiver::kMedian;
|
const int NadaBweReceiver::kMedian;
|
||||||
const int64_t NadaBweReceiver::kPacketLossTimeWindowMs;
|
|
||||||
const int64_t NadaBweReceiver::kReceivingRateTimeWindowMs;
|
|
||||||
const int NadaBweSender::kMinRefRateKbps;
|
const int NadaBweSender::kMinRefRateKbps;
|
||||||
const int NadaBweSender::kMaxRefRateKbps;
|
const int NadaBweSender::kMaxRefRateKbps;
|
||||||
|
const int64_t NadaBweReceiver::kReceivingRateTimeWindowMs;
|
||||||
|
|
||||||
NadaBweReceiver::NadaBweReceiver(int flow_id)
|
NadaBweReceiver::NadaBweReceiver(int flow_id)
|
||||||
: BweReceiver(flow_id),
|
: BweReceiver(flow_id),
|
||||||
@ -84,9 +83,9 @@ void NadaBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
|||||||
est_queuing_delay_signal_ms_ = 0;
|
est_queuing_delay_signal_ms_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
received_packets_->Insert(media_packet.sequence_number(),
|
received_packets_.Insert(media_packet.sequence_number(),
|
||||||
media_packet.send_time_ms(), arrival_time_ms,
|
media_packet.send_time_ms(), arrival_time_ms,
|
||||||
media_packet.payload_size());
|
media_packet.payload_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
|
FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
|
||||||
@ -110,7 +109,7 @@ 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;
|
||||||
|
|
||||||
PacketIdentifierNode* latest = *(received_packets_->begin());
|
PacketIdentifierNode* latest = *(received_packets_.begin());
|
||||||
int64_t corrected_send_time_ms =
|
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;
|
||||||
|
|
||||||
@ -122,83 +121,22 @@ FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
|
|||||||
corrected_send_time_ms);
|
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() {
|
|
||||||
|
|
||||||
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 - kPacketLossTimeWindowMs;
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For a given time window, compute the receiving speed rate in kbps.
|
// For a given time window, compute the receiving speed rate in kbps.
|
||||||
// As described below, three cases are considered depending on the number of
|
// As described below, three cases are considered depending on the number of
|
||||||
// packets received.
|
// packets received.
|
||||||
size_t NadaBweReceiver::RecentReceivingRate() {
|
size_t NadaBweReceiver::RecentReceivingRate() {
|
||||||
// If the receiver didn't receive any packet, return 0.
|
// If the receiver didn't receive any packet, return 0.
|
||||||
if (received_packets_->empty()) {
|
if (received_packets_.empty()) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
size_t total_size = 0;
|
size_t total_size = 0;
|
||||||
int number_packets = 0;
|
int number_packets = 0;
|
||||||
|
|
||||||
PacketNodeIt node_it = received_packets_->begin();
|
PacketNodeIt node_it = received_packets_.begin();
|
||||||
|
|
||||||
int64_t last_time_ms = (*node_it)->arrival_time_ms;
|
int64_t last_time_ms = (*node_it)->arrival_time_ms;
|
||||||
int64_t start_time_ms = last_time_ms;
|
int64_t start_time_ms = last_time_ms;
|
||||||
PacketNodeIt end = received_packets_->end();
|
PacketNodeIt end = received_packets_.end();
|
||||||
|
|
||||||
// Stops after including the first packet out of the timeWindow.
|
// Stops after including the first packet out of the timeWindow.
|
||||||
// Ameliorates results when there are wide gaps between packets.
|
// Ameliorates results when there are wide gaps between packets.
|
||||||
@ -218,13 +156,14 @@ size_t NadaBweReceiver::RecentReceivingRate() {
|
|||||||
// If the receiver received a single packet, return its size*8/timeWindow.
|
// If the receiver received a single packet, return its size*8/timeWindow.
|
||||||
if (number_packets == 1) {
|
if (number_packets == 1) {
|
||||||
corrected_time_ms = kReceivingRateTimeWindowMs;
|
corrected_time_ms = kReceivingRateTimeWindowMs;
|
||||||
} else {
|
}
|
||||||
// If the receiver received multiple packets, use as time interval the gap
|
// If the receiver received multiple packets, use as time interval the gap
|
||||||
// between first and last packet falling in the timeWindow corrected by the
|
// between first and last packet falling in the timeWindow corrected by the
|
||||||
// factor number_packets/(number_packets-1).
|
// factor number_packets/(number_packets-1).
|
||||||
// E.g: Let timeWindow = 500ms, payload_size = 500bytes, number_packets=2,
|
// E.g: Let timeWindow = 500ms, payload_size = 500 bytes, number_packets = 2,
|
||||||
// packets received at t1(0ms) and t2(499 or 501ms). This prevent the
|
// packets received at t1(0ms) and t2(499 or 501ms). This prevent the function
|
||||||
// function from returning ~2*8, sending instead a more likely ~1*8 kbps.
|
// from returning ~2*8, sending instead a more likely ~1*8 kbps.
|
||||||
|
else {
|
||||||
corrected_time_ms = (number_packets * (start_time_ms - last_time_ms)) /
|
corrected_time_ms = (number_packets * (start_time_ms - last_time_ms)) /
|
||||||
(number_packets - 1);
|
(number_packets - 1);
|
||||||
}
|
}
|
||||||
@ -383,37 +322,6 @@ void NadaBweSender::GradualRateUpdate(const NadaFeedback& fb,
|
|||||||
bitrate_kbps_ = bitrate_kbps_ + smoothing_factor * original_increase;
|
bitrate_kbps_ = bitrate_kbps_ + smoothing_factor * original_increase;
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
|
||||||
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_) {
|
|
||||||
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
|
||||||
|
@ -31,65 +31,6 @@ 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);
|
||||||
@ -98,20 +39,13 @@ 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();
|
size_t RecentReceivingRate();
|
||||||
static int64_t MedianFilter(int64_t* v, int size);
|
static int64_t MedianFilter(int64_t* v, int size);
|
||||||
static int64_t ExponentialSmoothingFilter(int64_t new_value,
|
static int64_t ExponentialSmoothingFilter(int64_t new_value,
|
||||||
int64_t last_smoothed_value,
|
int64_t last_smoothed_value,
|
||||||
float alpha);
|
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;
|
static const int64_t kReceivingRateTimeWindowMs = 500;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -125,8 +59,6 @@ class NadaBweReceiver : public BweReceiver {
|
|||||||
int64_t exp_smoothed_delay_ms_; // Referred as d_hat_n.
|
int64_t exp_smoothed_delay_ms_; // Referred as d_hat_n.
|
||||||
int64_t est_queuing_delay_signal_ms_; // Referred as d_tilde_n.
|
int64_t est_queuing_delay_signal_ms_; // Referred as d_tilde_n.
|
||||||
|
|
||||||
// Deals with packets sent more than once.
|
|
||||||
LinkedSet* received_packets_ = new LinkedSet(kSetCapacity);
|
|
||||||
static const int kMedian = 5; // Used for k-points Median Filter.
|
static const int kMedian = 5; // Used for k-points Median Filter.
|
||||||
int64_t last_delays_ms_[kMedian]; // Used for Median Filter.
|
int64_t last_delays_ms_[kMedian]; // Used for Median Filter.
|
||||||
};
|
};
|
||||||
|
@ -435,7 +435,7 @@ TEST_F(NadaReceiverSideTest, PacketLossSinglePacket) {
|
|||||||
|
|
||||||
TEST_F(NadaReceiverSideTest, PacketLossContiguousPackets) {
|
TEST_F(NadaReceiverSideTest, PacketLossContiguousPackets) {
|
||||||
const int64_t kTimeWindowMs = NadaBweReceiver::kPacketLossTimeWindowMs;
|
const int64_t kTimeWindowMs = NadaBweReceiver::kPacketLossTimeWindowMs;
|
||||||
const int kSetCapacity = NadaBweReceiver::kSetCapacity;
|
size_t set_capacity = nada_receiver_.GetSetCapacity();
|
||||||
|
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||||
@ -465,7 +465,7 @@ TEST_F(NadaReceiverSideTest, PacketLossContiguousPackets) {
|
|||||||
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
EXPECT_EQ(nada_receiver_.RecentPacketLossRatio(), 0.0f);
|
||||||
|
|
||||||
// Should handle set overflow.
|
// Should handle set overflow.
|
||||||
for (int i = 0; i < kSetCapacity * 1.5; ++i) {
|
for (int i = 0; i < set_capacity * 1.5; ++i) {
|
||||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||||
// Only the packets sent in this for loop will be considered.
|
// Only the packets sent in this for loop will be considered.
|
||||||
@ -503,11 +503,11 @@ TEST_F(NadaReceiverSideTest, PacketLossDuplicatedPackets) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(NadaReceiverSideTest, PacketLossLakingPackets) {
|
TEST_F(NadaReceiverSideTest, PacketLossLakingPackets) {
|
||||||
const int kSetCapacity = NadaBweReceiver::kSetCapacity;
|
size_t set_capacity = nada_receiver_.GetSetCapacity();
|
||||||
EXPECT_LT(kSetCapacity, 0xFFFF);
|
EXPECT_LT(set_capacity, static_cast<size_t>(0xFFFF));
|
||||||
|
|
||||||
// Missing every other packet.
|
// Missing every other packet.
|
||||||
for (int i = 0; i < kSetCapacity; ++i) {
|
for (size_t i = 0; i < set_capacity; ++i) {
|
||||||
if ((i & 1) == 0) { // Only even sequence numbers.
|
if ((i & 1) == 0) { // Only even sequence numbers.
|
||||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||||
@ -519,12 +519,12 @@ TEST_F(NadaReceiverSideTest, PacketLossLakingPackets) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(NadaReceiverSideTest, PacketLossLakingFewPackets) {
|
TEST_F(NadaReceiverSideTest, PacketLossLakingFewPackets) {
|
||||||
const int kSetCapacity = NadaBweReceiver::kSetCapacity;
|
size_t set_capacity = nada_receiver_.GetSetCapacity();
|
||||||
EXPECT_LT(kSetCapacity, 0xFFFF);
|
EXPECT_LT(set_capacity, static_cast<size_t>(0xFFFF));
|
||||||
|
|
||||||
const int kPeriod = 100;
|
const int kPeriod = 100;
|
||||||
// Missing one for each kPeriod packets.
|
// Missing one for each kPeriod packets.
|
||||||
for (int i = 0; i < kSetCapacity; ++i) {
|
for (size_t i = 0; i < set_capacity; ++i) {
|
||||||
if ((i % kPeriod) != 0) {
|
if ((i % kPeriod) != 0) {
|
||||||
uint16_t sequence_number = static_cast<uint16_t>(i);
|
uint16_t sequence_number = static_cast<uint16_t>(i);
|
||||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
const MediaPacket media_packet(kFlowId, 0, 0, sequence_number);
|
||||||
@ -557,16 +557,16 @@ TEST_F(NadaReceiverSideTest, PacketLossWideGap) {
|
|||||||
|
|
||||||
// Packets arriving unordered should not be counted as losted.
|
// Packets arriving unordered should not be counted as losted.
|
||||||
TEST_F(NadaReceiverSideTest, PacketLossUnorderedPackets) {
|
TEST_F(NadaReceiverSideTest, PacketLossUnorderedPackets) {
|
||||||
const int kNumPackets = NadaBweReceiver::kSetCapacity / 2;
|
int num_packets = nada_receiver_.GetSetCapacity() / 2;
|
||||||
std::vector<uint16_t> sequence_numbers;
|
std::vector<uint16_t> sequence_numbers;
|
||||||
|
|
||||||
for (int i = 0; i < kNumPackets; ++i) {
|
for (int i = 0; i < num_packets; ++i) {
|
||||||
sequence_numbers.push_back(static_cast<uint16_t>(i + 1));
|
sequence_numbers.push_back(static_cast<uint16_t>(i + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
random_shuffle(sequence_numbers.begin(), sequence_numbers.end());
|
random_shuffle(sequence_numbers.begin(), sequence_numbers.end());
|
||||||
|
|
||||||
for (int i = 0; i < kNumPackets; ++i) {
|
for (int i = 0; i < num_packets; ++i) {
|
||||||
const MediaPacket media_packet(kFlowId, 0, 0, sequence_numbers[i]);
|
const MediaPacket media_packet(kFlowId, 0, 0, sequence_numbers[i]);
|
||||||
// Arrival time = 0, all packets will be considered.
|
// Arrival time = 0, all packets will be considered.
|
||||||
nada_receiver_.ReceivePacket(0, media_packet);
|
nada_receiver_.ReceivePacket(0, media_packet);
|
||||||
|
@ -99,6 +99,10 @@ void RembReceiver::ReceivePacket(int64_t arrival_time_ms,
|
|||||||
media_packet.header());
|
media_packet.header());
|
||||||
clock_.AdvanceTimeMilliseconds(arrival_time_ms - clock_.TimeInMilliseconds());
|
clock_.AdvanceTimeMilliseconds(arrival_time_ms - clock_.TimeInMilliseconds());
|
||||||
ASSERT_TRUE(arrival_time_ms == clock_.TimeInMilliseconds());
|
ASSERT_TRUE(arrival_time_ms == clock_.TimeInMilliseconds());
|
||||||
|
|
||||||
|
received_packets_.Insert(media_packet.sequence_number(),
|
||||||
|
media_packet.send_time_ms(), arrival_time_ms,
|
||||||
|
media_packet.payload_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedbackPacket* RembReceiver::GetFeedback(int64_t now_ms) {
|
FeedbackPacket* RembReceiver::GetFeedback(int64_t now_ms) {
|
||||||
|
@ -127,6 +127,10 @@ void SendSideBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
|||||||
packet_feedback_vector_.push_back(PacketInfo(
|
packet_feedback_vector_.push_back(PacketInfo(
|
||||||
arrival_time_ms, media_packet.sender_timestamp_us() / 1000,
|
arrival_time_ms, media_packet.sender_timestamp_us() / 1000,
|
||||||
media_packet.header().sequenceNumber, media_packet.payload_size()));
|
media_packet.header().sequenceNumber, media_packet.payload_size()));
|
||||||
|
|
||||||
|
received_packets_.Insert(media_packet.sequence_number(),
|
||||||
|
media_packet.send_time_ms(), arrival_time_ms,
|
||||||
|
media_packet.payload_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedbackPacket* SendSideBweReceiver::GetFeedback(int64_t now_ms) {
|
FeedbackPacket* SendSideBweReceiver::GetFeedback(int64_t now_ms) {
|
||||||
|
@ -35,6 +35,10 @@ void TcpBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
|||||||
const MediaPacket& media_packet) {
|
const MediaPacket& media_packet) {
|
||||||
latest_owd_ms_ = arrival_time_ms - media_packet.sender_timestamp_us() / 1000;
|
latest_owd_ms_ = arrival_time_ms - media_packet.sender_timestamp_us() / 1000;
|
||||||
acks_.push_back(media_packet.header().sequenceNumber);
|
acks_.push_back(media_packet.header().sequenceNumber);
|
||||||
|
|
||||||
|
received_packets_.Insert(media_packet.sequence_number(),
|
||||||
|
media_packet.send_time_ms(), arrival_time_ms,
|
||||||
|
media_packet.payload_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedbackPacket* TcpBweReceiver::GetFeedback(int64_t now_ms) {
|
FeedbackPacket* TcpBweReceiver::GetFeedback(int64_t now_ms) {
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/remote_bitrate_estimator/test/packet_receiver.h"
|
#include "webrtc/modules/remote_bitrate_estimator/test/packet_receiver.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
@ -31,13 +32,32 @@ PacketReceiver::PacketReceiver(PacketProcessorListener* listener,
|
|||||||
bool plot_bwe)
|
bool plot_bwe)
|
||||||
: PacketProcessor(listener, flow_id, kReceiver),
|
: PacketProcessor(listener, flow_id, kReceiver),
|
||||||
delay_log_prefix_(),
|
delay_log_prefix_(),
|
||||||
|
metric_log_prefix_(),
|
||||||
|
packet_loss_log_prefix_(),
|
||||||
last_delay_plot_ms_(0),
|
last_delay_plot_ms_(0),
|
||||||
|
last_metric_plot_ms_(0),
|
||||||
|
last_packet_loss_plot_ms_(0),
|
||||||
plot_delay_(plot_delay),
|
plot_delay_(plot_delay),
|
||||||
bwe_receiver_(CreateBweReceiver(bwe_type, flow_id, plot_bwe)) {
|
// TODO(magalhaesc) Add separated plot_objective_function and
|
||||||
|
// plot_packet_loss parameters to the constructor.
|
||||||
|
plot_objective_function_(plot_delay),
|
||||||
|
plot_packet_loss_(plot_delay),
|
||||||
|
bwe_receiver_(CreateBweReceiver(bwe_type, flow_id, plot_bwe)),
|
||||||
|
total_delay_ms_(0),
|
||||||
|
total_throughput_(0),
|
||||||
|
number_packets_(0) {
|
||||||
// Setup the prefix ststd::rings used when logging.
|
// Setup the prefix ststd::rings used when logging.
|
||||||
std::stringstream ss;
|
std::stringstream ss1;
|
||||||
ss << "Delay_" << flow_id << "#2";
|
ss1 << "Delay_" << flow_id << "#2";
|
||||||
delay_log_prefix_ = ss.str();
|
delay_log_prefix_ = ss1.str();
|
||||||
|
|
||||||
|
std::stringstream ss2;
|
||||||
|
ss2 << "Objective_function_" << flow_id << "#2";
|
||||||
|
metric_log_prefix_ = ss2.str();
|
||||||
|
|
||||||
|
std::stringstream ss3;
|
||||||
|
ss3 << "Packet_Loss_" << flow_id << "#2";
|
||||||
|
packet_loss_log_prefix_ = ss3.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketReceiver::~PacketReceiver() {
|
PacketReceiver::~PacketReceiver() {
|
||||||
@ -61,6 +81,12 @@ void PacketReceiver::RunFor(int64_t time_ms, Packets* in_out) {
|
|||||||
int64_t send_time_ms = (media_packet->creation_time_us() + 500) / 1000;
|
int64_t send_time_ms = (media_packet->creation_time_us() + 500) / 1000;
|
||||||
delay_stats_.Push(arrival_time_ms - send_time_ms);
|
delay_stats_.Push(arrival_time_ms - send_time_ms);
|
||||||
PlotDelay(arrival_time_ms, send_time_ms);
|
PlotDelay(arrival_time_ms, send_time_ms);
|
||||||
|
PlotObjectiveFunction(arrival_time_ms);
|
||||||
|
PlotPacketLoss(arrival_time_ms);
|
||||||
|
|
||||||
|
total_delay_ms_ += arrival_time_ms - send_time_ms;
|
||||||
|
total_throughput_ += media_packet->payload_size();
|
||||||
|
++number_packets_;
|
||||||
|
|
||||||
bwe_receiver_->ReceivePacket(arrival_time_ms, *media_packet);
|
bwe_receiver_->ReceivePacket(arrival_time_ms, *media_packet);
|
||||||
FeedbackPacket* fb = bwe_receiver_->GetFeedback(arrival_time_ms);
|
FeedbackPacket* fb = bwe_receiver_->GetFeedback(arrival_time_ms);
|
||||||
@ -87,6 +113,37 @@ void PacketReceiver::PlotDelay(int64_t arrival_time_ms, int64_t send_time_ms) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double PacketReceiver::ObjectiveFunction() {
|
||||||
|
const double kDelta = 1.0; // Delay penalty factor.
|
||||||
|
double throughput_metric = log(static_cast<double>(total_throughput_));
|
||||||
|
double delay_penalty = kDelta * log(static_cast<double>(total_delay_ms_));
|
||||||
|
return throughput_metric - delay_penalty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketReceiver::PlotObjectiveFunction(int64_t arrival_time_ms) {
|
||||||
|
static const int kMetricPlotIntervalMs = 1000;
|
||||||
|
if (!plot_objective_function_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (arrival_time_ms - last_metric_plot_ms_ > kMetricPlotIntervalMs) {
|
||||||
|
BWE_TEST_LOGGING_PLOT(1, metric_log_prefix_, arrival_time_ms,
|
||||||
|
ObjectiveFunction());
|
||||||
|
last_metric_plot_ms_ = arrival_time_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketReceiver::PlotPacketLoss(int64_t arrival_time_ms) {
|
||||||
|
static const int kPacketLossPlotIntervalMs = 500;
|
||||||
|
if (!plot_packet_loss_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (arrival_time_ms - last_packet_loss_plot_ms_ > kPacketLossPlotIntervalMs) {
|
||||||
|
BWE_TEST_LOGGING_PLOT(2, packet_loss_log_prefix_, arrival_time_ms,
|
||||||
|
bwe_receiver_->RecentPacketLossRatio());
|
||||||
|
last_packet_loss_plot_ms_ = arrival_time_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Stats<double> PacketReceiver::GetDelayStats() const {
|
Stats<double> PacketReceiver::GetDelayStats() const {
|
||||||
return delay_stats_;
|
return delay_stats_;
|
||||||
}
|
}
|
||||||
|
@ -40,14 +40,27 @@ class PacketReceiver : public PacketProcessor {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void PlotDelay(int64_t arrival_time_ms, int64_t send_time_ms);
|
void PlotDelay(int64_t arrival_time_ms, int64_t send_time_ms);
|
||||||
|
void PlotObjectiveFunction(int64_t arrival_time_ms);
|
||||||
|
void PlotPacketLoss(int64_t arrival_time_ms);
|
||||||
|
double ObjectiveFunction();
|
||||||
|
|
||||||
int64_t now_ms_;
|
int64_t now_ms_;
|
||||||
std::string delay_log_prefix_;
|
std::string delay_log_prefix_;
|
||||||
|
std::string metric_log_prefix_;
|
||||||
|
std::string packet_loss_log_prefix_;
|
||||||
int64_t last_delay_plot_ms_;
|
int64_t last_delay_plot_ms_;
|
||||||
|
int64_t last_metric_plot_ms_;
|
||||||
|
int64_t last_packet_loss_plot_ms_;
|
||||||
bool plot_delay_;
|
bool plot_delay_;
|
||||||
|
bool plot_objective_function_;
|
||||||
|
bool plot_packet_loss_;
|
||||||
Stats<double> delay_stats_;
|
Stats<double> delay_stats_;
|
||||||
rtc::scoped_ptr<BweReceiver> bwe_receiver_;
|
rtc::scoped_ptr<BweReceiver> bwe_receiver_;
|
||||||
|
|
||||||
|
int64_t total_delay_ms_;
|
||||||
|
size_t total_throughput_;
|
||||||
|
int number_packets_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PacketReceiver);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(PacketReceiver);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user