add a PacketTimeWindow to estimate bandwidth and report speed

This commit is contained in:
Stephen Birarda 2015-07-28 15:21:17 -07:00
parent 937e46abf2
commit cf30426636
7 changed files with 168 additions and 31 deletions

View file

@ -78,7 +78,9 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
// pack the available buffer size - must be a minimum of 2
if (wasCausedBySyncTimeout) {
// pack in the receive speed and bandwidth
// pack in the receive speed and estimatedBandwidth
ackPacket->writePrimitive(_receiveWindow.getPacketReceiveSpeed());
ackPacket->writePrimitive(_receiveWindow.getEstimatedBandwidth());
// record this as the last ACK send time
lastACKSendTime = high_resolution_clock::now();
@ -122,6 +124,16 @@ SequenceNumber Connection::nextACK() const {
}
void Connection::processReceivedSequenceNumber(SequenceNumber seq) {
// check if this is a packet pair we should estimate bandwidth from, or just a regular packet
if (((uint32_t) seq & 0xF) == 0) {
_receiveWindow.onProbePair1Arrival();
} else if (((uint32_t) seq & 0xF) == 1) {
_receiveWindow.onProbePair2Arrival();
} else {
_receiveWindow.onPacketArrival();
}
// If this is not the next sequence number, report loss
if (seq > _lastReceivedSequenceNumber + 1) {
if (_lastReceivedSequenceNumber + 1 == seq - 1) {
@ -170,10 +182,6 @@ void Connection::processControl(unique_ptr<ControlPacket> controlPacket) {
case ControlPacket::NAK:
processNAK(move(controlPacket));
break;
case ControlPacket::PacketPair: {
break;
}
}
}

View file

@ -16,6 +16,7 @@
#include <memory>
#include "LossList.h"
#include "PacketTimeWindow.h"
#include "SendQueue.h"
class HifiSockAddr;
@ -74,6 +75,8 @@ private:
SentACKMap _sentACKs; // Map of ACK sub-sequence numbers to ACKed sequence number and sent time
PacketTimeWindow _receiveWindow; // Window of received packets for bandwidth estimation and receive speed
std::unique_ptr<SendQueue> _sendQueue;
};

View file

@ -27,13 +27,6 @@ std::unique_ptr<ControlPacket> ControlPacket::create(Type type, qint64 size) {
}
}
ControlPacket::ControlPacketPair ControlPacket::createPacketPair(quint64 timestamp) {
// create each of the two packets in the packet pair
ControlPacketPair packetPair { std::unique_ptr<ControlPacket>(new ControlPacket(timestamp)),
std::unique_ptr<ControlPacket>(new ControlPacket(timestamp)) };
return packetPair;
}
qint64 ControlPacket::localHeaderSize() {
return sizeof(ControlBitAndType);
}
@ -64,20 +57,6 @@ ControlPacket::ControlPacket(Type type, qint64 size) :
writeControlBitAndType();
}
ControlPacket::ControlPacket(quint64 timestamp) :
BasePacket(localHeaderSize() + sizeof(timestamp)),
_type(Type::PacketPair)
{
adjustPayloadStartAndCapacity();
open(QIODevice::ReadWrite);
writeControlBitAndType();
// pack in the timestamp
writePrimitive(timestamp);
}
ControlPacket::ControlPacket(ControlPacket&& other) :
BasePacket(std::move(other))
{

View file

@ -25,17 +25,14 @@ class ControlPacket : public BasePacket {
Q_OBJECT
public:
using ControlBitAndType = uint32_t;
using ControlPacketPair = std::pair<std::unique_ptr<ControlPacket>, std::unique_ptr<ControlPacket>>;
enum Type : uint16_t {
ACK,
ACK2,
NAK,
PacketPair
NAK
};
static std::unique_ptr<ControlPacket> create(Type type, qint64 size = -1);
static ControlPacketPair createPacketPair(quint64 timestamp);
static qint64 localHeaderSize(); // Current level's header size
virtual qint64 totalHeadersSize() const; // Cumulated size of all the headers
@ -46,7 +43,6 @@ public:
private:
ControlPacket(Type type);
ControlPacket(Type type, qint64 size);
ControlPacket(quint64 timestamp);
ControlPacket(ControlPacket&& other);
ControlPacket(const ControlPacket& other) = delete;

View file

@ -0,0 +1,101 @@
//
// PacketTimeWindow.cpp
// libraries/networking/src/udt
//
// Created by Stephen Birarda on 2015-07-28.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PacketTimeWindow.h"
#include <cmath>
#include <NumericalConstants.h>
using namespace udt;
using namespace std::chrono;
PacketTimeWindow::PacketTimeWindow(int numPacketIntervals, int numProbeIntervals) :
_numPacketIntervals(numPacketIntervals),
_numProbeIntervals(numProbeIntervals),
_packetIntervals({ _numPacketIntervals }),
_probeIntervals({ _numProbeIntervals })
{
}
int32_t meanOfMedianFilteredValues(std::vector<int> intervals, int numValues, int valuesRequired = 0) {
// sort the intervals from smallest to largest
std::sort(intervals.begin(), intervals.end());
int median = 0;
if (numValues % 2 == 0) {
median = intervals[numValues / 2];
} else {
median = (intervals[(numValues / 2) - 1] + intervals[numValues / 2]) / 2;
}
int count = 0;
int sum = 0;
int upperBound = median * 8;
int lowerBound = median / 8;
for (auto& interval : intervals) {
if ((interval < upperBound) && interval > lowerBound) {
++count;
sum += interval;
}
}
if (count >= valuesRequired) {
return (int32_t) ceil((double) USECS_PER_MSEC / ((double) sum) / ((double) count));
} else {
return 0;
}
}
int32_t PacketTimeWindow::getPacketReceiveSpeed() const {
// return the mean value of median filtered values (per second) - or zero if there are too few filtered values
return meanOfMedianFilteredValues(_packetIntervals, _numPacketIntervals, _numPacketIntervals / 2);
}
int32_t PacketTimeWindow::getEstimatedBandwidth() const {
// return mean value of median filtered values (per second)
return meanOfMedianFilteredValues(_probeIntervals, _numProbeIntervals);
}
void PacketTimeWindow::onPacketArrival() {
// take the current time
auto now = high_resolution_clock::now();
// record the interval between this packet and the last one
_packetIntervals[_currentPacketInterval++] = duration_cast<microseconds>(now - _lastPacketTime).count();
// reset the currentPacketInterval index when it wraps
if (_currentPacketInterval == _numPacketIntervals) {
_currentPacketInterval = 0;
}
// remember this as the last packet arrival time
_lastPacketTime = now;
}
void PacketTimeWindow::onProbePair1Arrival() {
// take the current time as the first probe time
_firstProbeTime = high_resolution_clock::now();
}
void PacketTimeWindow::onProbePair2Arrival() {
// store the interval between the two probes
auto now = high_resolution_clock::now();
_probeIntervals[_currentProbeInterval++] = duration_cast<microseconds>(now - _firstProbeTime).count();
// reset the currentProbeInterval index when it wraps
if (_currentProbeInterval == _numProbeIntervals) {
_currentProbeInterval = 0;
}
}

View file

@ -0,0 +1,48 @@
//
// PacketTimeWindow.h
// libraries/networking/src/udt
//
// Created by Stephen Birarda on 2015-07-28.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_PacketTimeWindow_h
#define hifi_PacketTimeWindow_h
#include <chrono>
#include <vector>
namespace udt {
class PacketTimeWindow {
public:
PacketTimeWindow(int numPacketIntervals = 16, int numProbeIntervals = 16);
void onPacketArrival();
void onProbePair1Arrival();
void onProbePair2Arrival();
int32_t getPacketReceiveSpeed() const;
int32_t getEstimatedBandwidth() const;
private:
int _numPacketIntervals { 0 }; // the number of packet intervals to store
int _numProbeIntervals { 0 }; // the number of probe intervals to store
int _currentPacketInterval { 0 }; // index for the current packet interval
int _currentProbeInterval { 0 }; // index for the current probe interval
std::vector<int> _packetIntervals; // vector of microsecond intervals between packet arrivals
std::vector<int> _probeIntervals; // vector of microsecond intervals between probe pair arrivals
std::chrono::high_resolution_clock::time_point _lastPacketTime; // the time_point when last packet arrived
std::chrono::high_resolution_clock::time_point _firstProbeTime; // the time_point when first probe in pair arrived
};
}
#endif // hifi_PacketTimeWindow_h

View file

@ -174,6 +174,8 @@ void SendQueue::sendNextPacket() {
_packets.pop_front();
}
// check if we need to fire off a packet pair - we do this
// How long before next packet send
auto timeToSleep = (sendTime + _packetSendPeriod) - msecTimestampNow(); // msec
_sendTimer->start(std::max((quint64)0, timeToSleep));