mirror of
https://github.com/overte-org/overte.git
synced 2025-08-16 22:57:14 +02:00
add a PacketTimeWindow to estimate bandwidth and report speed
This commit is contained in:
parent
937e46abf2
commit
cf30426636
7 changed files with 168 additions and 31 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
101
libraries/networking/src/udt/PacketTimeWindow.cpp
Normal file
101
libraries/networking/src/udt/PacketTimeWindow.cpp
Normal 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;
|
||||
}
|
||||
}
|
48
libraries/networking/src/udt/PacketTimeWindow.h
Normal file
48
libraries/networking/src/udt/PacketTimeWindow.h
Normal 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
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue