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:
parent
f353dd59b5
commit
c81591d63f
@ -82,6 +82,7 @@
|
||||
'test/packet.h',
|
||||
'test/estimators/nada.cc',
|
||||
'test/estimators/nada.h',
|
||||
'test/estimators/nada_unittest.cc',
|
||||
'test/estimators/remb.cc',
|
||||
'test/estimators/remb.h',
|
||||
'test/estimators/send_side.cc',
|
||||
|
@ -178,9 +178,10 @@ void MediaPacket::SetAbsSendTimeMs(int64_t abs_send_time_ms) {
|
||||
|
||||
RembFeedback::RembFeedback(int flow_id,
|
||||
int64_t send_time_us,
|
||||
int64_t last_send_time_ms,
|
||||
uint32_t estimated_bps,
|
||||
RTCPReportBlock report_block)
|
||||
: FeedbackPacket(flow_id, send_time_us),
|
||||
: FeedbackPacket(flow_id, send_time_us, last_send_time_ms),
|
||||
estimated_bps_(estimated_bps),
|
||||
report_block_(report_block) {
|
||||
}
|
||||
@ -188,8 +189,9 @@ RembFeedback::RembFeedback(int flow_id,
|
||||
SendSideBweFeedback::SendSideBweFeedback(
|
||||
int flow_id,
|
||||
int64_t send_time_us,
|
||||
int64_t last_send_time_ms,
|
||||
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) {
|
||||
}
|
||||
|
||||
|
@ -14,11 +14,10 @@
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.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/test/testsupport/fileutils.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
@ -947,7 +946,7 @@ TEST(BweTestFramework_VideoSenderTest, FeedbackIneffective) {
|
||||
TestVideoSender(&sender, 9998, 1000, 500, 1025000);
|
||||
|
||||
// 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.push_back(feedback);
|
||||
sender.RunFor(0, &packets);
|
||||
@ -962,7 +961,7 @@ TEST(BweTestFramework_AdaptiveVideoSenderTest, FeedbackChangesBitrate) {
|
||||
TestVideoSender(&sender, 9998, 1000, 500, 1025000);
|
||||
|
||||
// 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.push_back(feedback);
|
||||
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
|
||||
// the same.
|
||||
feedback = new RembFeedback(0, 0, 820000, RTCPReportBlock());
|
||||
feedback = new RembFeedback(0, 0, 0, 820000, RTCPReportBlock());
|
||||
packets.push_back(feedback);
|
||||
sender.RunFor(10000, &packets);
|
||||
EXPECT_EQ(820000u, source.bits_per_second());
|
||||
@ -987,7 +986,7 @@ TEST(BweTestFramework_AdaptiveVideoSenderTest, Paced_FeedbackChangesBitrate) {
|
||||
TestVideoSender(&sender, 9998, 1000, 500, 1025000);
|
||||
|
||||
// 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.push_back(feedback);
|
||||
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
|
||||
// the same.
|
||||
feedback = new RembFeedback(0, 0, 820000, RTCPReportBlock());
|
||||
feedback = new RembFeedback(0, 0, 0, 820000, RTCPReportBlock());
|
||||
packets.push_back(feedback);
|
||||
sender.RunFor(10000, &packets);
|
||||
EXPECT_EQ(820000u, source.bits_per_second());
|
||||
|
@ -6,18 +6,28 @@
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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 <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/nada.h"
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
|
||||
const int NadaBweReceiver::kMedian;
|
||||
|
||||
NadaBweReceiver::NadaBweReceiver(int flow_id)
|
||||
: BweReceiver(flow_id),
|
||||
clock_(0),
|
||||
@ -25,7 +35,10 @@ NadaBweReceiver::NadaBweReceiver(int flow_id)
|
||||
recv_stats_(ReceiveStatistics::Create(&clock_)),
|
||||
baseline_delay_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() {
|
||||
@ -33,34 +46,51 @@ NadaBweReceiver::~NadaBweReceiver() {
|
||||
|
||||
void NadaBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
||||
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());
|
||||
recv_stats_->IncomingPacket(media_packet.header(),
|
||||
media_packet.payload_size(), false);
|
||||
int64_t delay_ms = arrival_time_ms - media_packet.creation_time_us() / 1000;
|
||||
// TODO(holmer): The min should time out after 10 minutes.
|
||||
if (delay_ms < baseline_delay_ms_) {
|
||||
baseline_delay_ms_ = delay_ms;
|
||||
int64_t delay_ms = arrival_time_ms -
|
||||
media_packet.creation_time_us() / 1000; // Refered as x_n.
|
||||
// The min should be updated within the first 10 minutes.
|
||||
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) {
|
||||
if (now_ms - last_feedback_ms_ < 100)
|
||||
return NULL;
|
||||
const int64_t kPacketLossPenaltyMs = 1000; // Referred as d_L.
|
||||
|
||||
StatisticianMap statisticians = recv_stats_->GetActiveStatisticians();
|
||||
int64_t loss_signal_ms = 0.0f;
|
||||
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;
|
||||
}
|
||||
if (now_ms - last_feedback_ms_ < 100) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
if (last_feedback_ms_ > 0) {
|
||||
@ -69,14 +99,124 @@ FeedbackPacket* NadaBweReceiver::GetFeedback(int64_t now_ms) {
|
||||
}
|
||||
last_feedback_ms_ = now_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)
|
||||
: clock_(clock),
|
||||
observer_(observer),
|
||||
bitrate_kbps_(kbps),
|
||||
last_feedback_ms_(0) {
|
||||
: clock_(clock), observer_(observer), bitrate_kbps_(kbps) {
|
||||
}
|
||||
|
||||
NadaBweSender::NadaBweSender(BitrateObserver* observer, Clock* clock)
|
||||
: clock_(clock), observer_(observer), bitrate_kbps_(kMinRefRateKbps) {
|
||||
}
|
||||
|
||||
NadaBweSender::~NadaBweSender() {
|
||||
@ -88,31 +228,32 @@ 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_;
|
||||
|
||||
// TODO(holmer): Implement special start-up behavior.
|
||||
|
||||
const float kEta = 2.0f;
|
||||
const float kTaoO = 500.0f;
|
||||
float x_hat = fb.congestion_signal() + kEta * kTaoO * fb.derivative();
|
||||
const int kMaxRefRateKbps = 1500; // Referred as R_max.
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
float delta_s = now_ms - last_feedback_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;
|
||||
const float kReferenceDelayS = 10.0f;
|
||||
float kTheta =
|
||||
kPriorityWeight * (kMaxBitrateKbps - kMinBitrateKbps) * kReferenceDelayS;
|
||||
// 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);
|
||||
|
||||
const float kKappa = 1.0f;
|
||||
bitrate_kbps_ = bitrate_kbps_ +
|
||||
kKappa * delta_s / (kTaoO * kTaoO) *
|
||||
(kTheta - (bitrate_kbps_ - kMinBitrateKbps) * x_hat) +
|
||||
0.5f;
|
||||
bitrate_kbps_ = std::min(bitrate_kbps_, kMaxBitrateKbps);
|
||||
bitrate_kbps_ = std::max(bitrate_kbps_, kMinBitrateKbps);
|
||||
|
||||
observer_->OnNetworkChanged(1000 * bitrate_kbps_, 0, 0);
|
||||
// Independent limits for those variables.
|
||||
// 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);
|
||||
} else {
|
||||
GradualRateUpdate(fb, kMaxRefRateKbps, delta_s);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t NadaBweSender::TimeUntilNextProcess() {
|
||||
@ -123,6 +264,75 @@ int NadaBweSender::Process() {
|
||||
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 testing
|
||||
} // namespace webrtc
|
||||
|
@ -6,12 +6,23 @@
|
||||
* 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.
|
||||
*/
|
||||
*
|
||||
*/
|
||||
|
||||
// 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_
|
||||
#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/voice_engine/channel.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -20,6 +31,65 @@ class ReceiveStatistics;
|
||||
namespace testing {
|
||||
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 {
|
||||
public:
|
||||
explicit NadaBweReceiver(int flow_id);
|
||||
@ -28,32 +98,59 @@ class NadaBweReceiver : public BweReceiver {
|
||||
void ReceivePacket(int64_t arrival_time_ms,
|
||||
const MediaPacket& media_packet) 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:
|
||||
SimulatedClock clock_;
|
||||
int64_t last_feedback_ms_;
|
||||
rtc::scoped_ptr<ReceiveStatistics> recv_stats_;
|
||||
int64_t baseline_delay_ms_;
|
||||
int64_t delay_signal_ms_;
|
||||
int64_t baseline_delay_ms_; // Referred as d_f.
|
||||
int64_t delay_signal_ms_; // Referred as d_n.
|
||||
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 {
|
||||
public:
|
||||
NadaBweSender(int kbps, BitrateObserver* observer, Clock* clock);
|
||||
NadaBweSender(BitrateObserver* observer, Clock* clock);
|
||||
virtual ~NadaBweSender();
|
||||
|
||||
int GetFeedbackIntervalMs() const override;
|
||||
// Updates the min_feedback_delay_ms_ and the min_round_trip_time_ms_.
|
||||
void GiveFeedback(const FeedbackPacket& feedback) override;
|
||||
void OnPacketsSent(const Packets& packets) override {}
|
||||
int64_t TimeUntilNextProcess() 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:
|
||||
Clock* const clock_;
|
||||
BitrateObserver* const observer_;
|
||||
int bitrate_kbps_;
|
||||
int64_t last_feedback_ms_;
|
||||
// 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;
|
||||
// Referred as RTT_0, initialized as an upper bound.
|
||||
int64_t min_round_trip_time_ms_ = 500;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(NadaBweSender);
|
||||
};
|
||||
|
@ -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
|
@ -111,8 +111,10 @@ FeedbackPacket* RembReceiver::GetFeedback(int64_t now_ms) {
|
||||
if (!statisticians.empty()) {
|
||||
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;
|
||||
RTC_UNUSED(estimated_kbps);
|
||||
|
@ -70,6 +70,7 @@ class RembReceiver : public BweReceiver, public RemoteBitrateObserver {
|
||||
SimulatedClock clock_;
|
||||
rtc::scoped_ptr<ReceiveStatistics> recv_stats_;
|
||||
int64_t latest_estimate_bps_;
|
||||
int64_t last_feedback_ms_;
|
||||
rtc::scoped_ptr<RemoteBitrateEstimator> estimator_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(RembReceiver);
|
||||
|
@ -126,9 +126,10 @@ void SendSideBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
||||
FeedbackPacket* SendSideBweReceiver::GetFeedback(int64_t now_ms) {
|
||||
if (now_ms - last_feedback_ms_ < 100)
|
||||
return NULL;
|
||||
int64_t latest_send_time_ms = last_feedback_ms_;
|
||||
last_feedback_ms_ = now_ms;
|
||||
FeedbackPacket* fb =
|
||||
new SendSideBweFeedback(flow_id_, now_ms * 1000, packet_feedback_vector_);
|
||||
FeedbackPacket* fb = new SendSideBweFeedback(
|
||||
flow_id_, now_ms * 1000, latest_send_time_ms, packet_feedback_vector_);
|
||||
packet_feedback_vector_.clear();
|
||||
return fb;
|
||||
}
|
||||
|
@ -35,10 +35,9 @@ void TcpBweReceiver::ReceivePacket(int64_t arrival_time_ms,
|
||||
}
|
||||
|
||||
FeedbackPacket* TcpBweReceiver::GetFeedback(int64_t now_ms) {
|
||||
// if (now_ms - last_feedback_ms_ < 100)
|
||||
// return NULL;
|
||||
FeedbackPacket* fb =
|
||||
new TcpFeedback(flow_id_, now_ms * 1000, last_feedback_ms_, acks_);
|
||||
last_feedback_ms_ = now_ms;
|
||||
FeedbackPacket* fb = new TcpFeedback(flow_id_, now_ms * 1000, acks_);
|
||||
acks_.clear();
|
||||
return fb;
|
||||
}
|
||||
|
@ -39,12 +39,17 @@ class Packet {
|
||||
virtual int64_t send_time_us() const { return send_time_us_; }
|
||||
virtual size_t payload_size() const { return payload_size_; }
|
||||
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_;
|
||||
int64_t creation_time_us_; // Time when the packet was created.
|
||||
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 {
|
||||
@ -69,6 +74,8 @@ class MediaPacket : public Packet {
|
||||
void SetAbsSendTimeMs(int64_t abs_send_time_ms);
|
||||
const RTPHeader& header() const { return header_; }
|
||||
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:
|
||||
static const int kAbsSendTimeFraction = 18;
|
||||
@ -81,17 +88,25 @@ class MediaPacket : public Packet {
|
||||
|
||||
class FeedbackPacket : public Packet {
|
||||
public:
|
||||
FeedbackPacket(int flow_id, int64_t send_time_us)
|
||||
: Packet(flow_id, send_time_us, 0) {}
|
||||
FeedbackPacket(int flow_id,
|
||||
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 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 {
|
||||
public:
|
||||
RembFeedback(int flow_id,
|
||||
int64_t send_time_us,
|
||||
int64_t latest_send_time_ms,
|
||||
uint32_t estimated_bps,
|
||||
RTCPReportBlock report_block);
|
||||
virtual ~RembFeedback() {}
|
||||
@ -109,6 +124,7 @@ class SendSideBweFeedback : public FeedbackPacket {
|
||||
typedef std::map<uint16_t, int64_t> ArrivalTimesMap;
|
||||
SendSideBweFeedback(int flow_id,
|
||||
int64_t send_time_us,
|
||||
int64_t latest_send_time_ms,
|
||||
const std::vector<PacketInfo>& packet_feedback_vector);
|
||||
virtual ~SendSideBweFeedback() {}
|
||||
|
||||
@ -123,28 +139,45 @@ class SendSideBweFeedback : public FeedbackPacket {
|
||||
class NadaFeedback : public FeedbackPacket {
|
||||
public:
|
||||
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,
|
||||
float derivative)
|
||||
: FeedbackPacket(flow_id, send_time_us),
|
||||
float derivative,
|
||||
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),
|
||||
derivative_(derivative) {}
|
||||
derivative_(derivative),
|
||||
receiving_rate_(receiving_rate) {}
|
||||
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_; }
|
||||
float derivative() const { return derivative_; }
|
||||
float receiving_rate() const { return receiving_rate_; }
|
||||
|
||||
private:
|
||||
int64_t congestion_signal_;
|
||||
float derivative_;
|
||||
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 congestion_signal_; // Referred as x_n.
|
||||
float derivative_; // Referred as x'_n.
|
||||
float receiving_rate_; // Referred as R_r.
|
||||
};
|
||||
|
||||
class TcpFeedback : public FeedbackPacket {
|
||||
public:
|
||||
TcpFeedback(int flow_id,
|
||||
int64_t send_time_us,
|
||||
int64_t latest_send_time_ms,
|
||||
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() {}
|
||||
|
||||
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*>::iterator PacketsIt;
|
||||
typedef std::list<Packet*>::const_iterator PacketsConstIt;
|
||||
|
||||
} // namespace bwe
|
||||
} // namespace testing
|
||||
} // namespace webrtc
|
||||
|
@ -40,14 +40,16 @@ std::list<FeedbackPacket*> GetFeedbackPackets(Packets* in_out,
|
||||
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,
|
||||
VideoSource* source,
|
||||
BandwidthEstimatorType estimator_type)
|
||||
: 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),
|
||||
bwe_(CreateBweSender(estimator_type,
|
||||
source_->bits_per_second() / 1000,
|
||||
@ -60,10 +62,10 @@ VideoSender::~VideoSender() {
|
||||
}
|
||||
|
||||
void VideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
int64_t now_ms = clock_.TimeInMilliseconds();
|
||||
std::list<FeedbackPacket*> feedbacks =
|
||||
GetFeedbackPackets(in_out, now_ms + time_ms, source_->flow_id());
|
||||
std::list<FeedbackPacket*> feedbacks = GetFeedbackPackets(
|
||||
in_out, clock_.TimeInMilliseconds() + time_ms, source_->flow_id());
|
||||
ProcessFeedbackAndGeneratePackets(time_ms, &feedbacks, in_out);
|
||||
SetSenderTimestamps(in_out);
|
||||
}
|
||||
|
||||
void VideoSender::ProcessFeedbackAndGeneratePackets(
|
||||
@ -190,6 +192,7 @@ void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
}
|
||||
} while (clock_.TimeInMilliseconds() < end_time_ms);
|
||||
QueuePackets(in_out, end_time_ms * 1000);
|
||||
SetSenderTimestamps(in_out);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (now_ms_ + time_ms < offset_ms_) {
|
||||
now_ms_ += time_ms;
|
||||
if (clock_.TimeInMilliseconds() + time_ms < offset_ms_) {
|
||||
clock_.AdvanceTimeMilliseconds(time_ms);
|
||||
return;
|
||||
}
|
||||
BWE_TEST_LOGGING_CONTEXT("Sender");
|
||||
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
|
||||
// number of packets in_flight_ and the max number of packets in flight
|
||||
// (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);
|
||||
}
|
||||
|
||||
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);
|
||||
now_ms_ += time_ms;
|
||||
clock_.AdvanceTimeMilliseconds(time_ms);
|
||||
SetSenderTimestamps(in_out);
|
||||
}
|
||||
|
||||
void TcpSender::SendPackets(Packets* in_out) {
|
||||
@ -298,6 +310,7 @@ void TcpSender::SendPackets(Packets* in_out) {
|
||||
Packets generated = GeneratePackets(packets_to_send);
|
||||
for (Packet* packet : generated)
|
||||
in_flight_.insert(InFlight(*static_cast<MediaPacket*>(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());
|
||||
|
||||
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) {
|
||||
HandleLoss();
|
||||
@ -329,7 +342,7 @@ void TcpSender::UpdateCongestionControl(const FeedbackPacket* fb) {
|
||||
int TcpSender::TriggerTimeouts() {
|
||||
int timed_out = 0;
|
||||
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++);
|
||||
++timed_out;
|
||||
} else {
|
||||
@ -347,7 +360,8 @@ void TcpSender::HandleLoss() {
|
||||
Packets TcpSender::GeneratePackets(size_t num_packets) {
|
||||
Packets generated;
|
||||
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_++));
|
||||
}
|
||||
return generated;
|
||||
|
@ -28,7 +28,11 @@ namespace bwe {
|
||||
class PacketSender : public PacketProcessor {
|
||||
public:
|
||||
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() {}
|
||||
// Call GiveFeedback() with the returned interval in milliseconds, provided
|
||||
// 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
|
||||
// have to be regenerated.
|
||||
virtual int GetFeedbackIntervalMs() const = 0;
|
||||
void SetSenderTimestamps(Packets* in_out);
|
||||
|
||||
protected:
|
||||
SimulatedClock clock_;
|
||||
};
|
||||
|
||||
class VideoSender : public PacketSender, public BitrateObserver {
|
||||
@ -60,7 +68,6 @@ class VideoSender : public PacketSender, public BitrateObserver {
|
||||
std::list<FeedbackPacket*>* feedbacks,
|
||||
Packets* generated);
|
||||
|
||||
SimulatedClock clock_;
|
||||
VideoSource* source_;
|
||||
rtc::scoped_ptr<BweSender> bwe_;
|
||||
int64_t start_of_run_ms_;
|
||||
@ -107,7 +114,6 @@ class TcpSender : public PacketSender {
|
||||
public:
|
||||
TcpSender(PacketProcessorListener* listener, int flow_id, int64_t offset_ms)
|
||||
: PacketSender(listener, flow_id),
|
||||
now_ms_(0),
|
||||
cwnd_(10),
|
||||
ssthresh_(std::numeric_limits<int>::max()),
|
||||
ack_received_(false),
|
||||
@ -146,7 +152,6 @@ class TcpSender : public PacketSender {
|
||||
void HandleLoss();
|
||||
Packets GeneratePackets(size_t num_packets);
|
||||
|
||||
int64_t now_ms_;
|
||||
float cwnd_;
|
||||
int ssthresh_;
|
||||
std::set<InFlight> in_flight_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user