mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #3107 from wangyix/master
prune missing sequence numbers before sending NACK; AudioRingBuffer frame length can now be specified;
This commit is contained in:
commit
dbdce32b68
18 changed files with 74 additions and 39 deletions
|
@ -101,8 +101,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
|
|||
|
||||
if (!matchingInjectedRingBuffer) {
|
||||
// we don't have a matching injected audio ring buffer, so add it
|
||||
matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier,
|
||||
AudioMixer::getUseDynamicJitterBuffers());
|
||||
matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier, AudioMixer::getUseDynamicJitterBuffers());
|
||||
_ringBuffers.push_back(matchingInjectedRingBuffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -216,9 +216,13 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
}
|
||||
|
||||
const SharedNodePointer& destinationNode = NodeList::getInstance()->getNodeHash().value(nodeUUID);
|
||||
const QSet<unsigned short int>& missingSequenceNumbers = nodeStats.getIncomingEditSequenceNumberStats().getMissingSet();
|
||||
|
||||
// retrieve sequence number stats of node, prune its missing set
|
||||
SequenceNumberStats& sequenceNumberStats = nodeStats.getIncomingEditSequenceNumberStats();
|
||||
sequenceNumberStats.pruneMissingSet();
|
||||
|
||||
// construct nack packet(s) for this node
|
||||
const QSet<unsigned short int>& missingSequenceNumbers = sequenceNumberStats.getMissingSet();
|
||||
int numSequenceNumbersAvailable = missingSequenceNumbers.size();
|
||||
QSet<unsigned short int>::const_iterator missingSequenceNumberIterator = missingSequenceNumbers.constBegin();
|
||||
while (numSequenceNumbersAvailable > 0) {
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
|
||||
|
||||
const SequenceNumberStats& getIncomingEditSequenceNumberStats() const { return _incomingEditSequenceNumberStats; }
|
||||
SequenceNumberStats& getIncomingEditSequenceNumberStats() { return _incomingEditSequenceNumberStats; }
|
||||
|
||||
void trackInboundPacket(unsigned short int incomingSequence, quint64 transitTime,
|
||||
int editsInPacket, quint64 processTime, quint64 lockWaitTime);
|
||||
|
|
|
@ -42,7 +42,7 @@ OctreeQueryNode::OctreeQueryNode() :
|
|||
_lastRootTimestamp(0),
|
||||
_myPacketType(PacketTypeUnknown),
|
||||
_isShuttingDown(false),
|
||||
_sentPacketHistory(1000)
|
||||
_sentPacketHistory()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -2184,11 +2184,11 @@ int Application::sendNackPackets() {
|
|||
_octreeSceneStatsLock.unlock();
|
||||
continue;
|
||||
}
|
||||
OctreeSceneStats& stats = _octreeServerSceneStats[nodeUUID];
|
||||
|
||||
// make copy of missing sequence numbers from stats
|
||||
const QSet<OCTREE_PACKET_SEQUENCE> missingSequenceNumbers =
|
||||
stats.getIncomingOctreeSequenceNumberStats().getMissingSet();
|
||||
// get sequence number stats of node, prune its missing set, and make a copy of the missing set
|
||||
SequenceNumberStats& sequenceNumberStats = _octreeServerSceneStats[nodeUUID].getIncomingOctreeSequenceNumberStats();
|
||||
sequenceNumberStats.pruneMissingSet();
|
||||
const QSet<OCTREE_PACKET_SEQUENCE> missingSequenceNumbers = sequenceNumberStats.getMissingSet();
|
||||
|
||||
_octreeSceneStatsLock.unlock();
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
|||
_proceduralAudioOutput(NULL),
|
||||
_proceduralOutputDevice(NULL),
|
||||
_inputRingBuffer(0),
|
||||
_ringBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL),
|
||||
_ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO),
|
||||
_isStereoInput(false),
|
||||
_averagedLatency(0.0),
|
||||
_measuredJitter(0),
|
||||
|
@ -93,7 +93,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
|||
_processSpatialAudio(false),
|
||||
_spatialAudioStart(0),
|
||||
_spatialAudioFinish(0),
|
||||
_spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, true), // random access mode
|
||||
_spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, true), // random access mode
|
||||
_scopeEnabled(false),
|
||||
_scopeEnabledPause(false),
|
||||
_scopeInputOffset(0),
|
||||
|
|
|
@ -149,13 +149,19 @@ void DatagramProcessor::processDatagrams() {
|
|||
break;
|
||||
}
|
||||
case PacketTypeVoxelEditNack:
|
||||
application->_voxelEditSender.processNackPacket(incomingPacket);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||
application->_voxelEditSender.processNackPacket(incomingPacket);
|
||||
}
|
||||
break;
|
||||
case PacketTypeParticleEditNack:
|
||||
application->_particleEditSender.processNackPacket(incomingPacket);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||
application->_particleEditSender.processNackPacket(incomingPacket);
|
||||
}
|
||||
break;
|
||||
case PacketTypeModelEditNack:
|
||||
application->_modelEditSender.processNackPacket(incomingPacket);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||
application->_modelEditSender.processNackPacket(incomingPacket);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
||||
|
|
|
@ -19,10 +19,11 @@
|
|||
#include "AudioRingBuffer.h"
|
||||
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) :
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
|
||||
NodeData(),
|
||||
_overflowCount(0),
|
||||
_sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES),
|
||||
_frameCapacity(numFramesCapacity),
|
||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||
_isFull(false),
|
||||
_numFrameSamples(numFrameSamples),
|
||||
_isStarved(true),
|
||||
|
@ -48,6 +49,8 @@ AudioRingBuffer::~AudioRingBuffer() {
|
|||
}
|
||||
|
||||
void AudioRingBuffer::reset() {
|
||||
_overflowCount = 0;
|
||||
_isFull = false;
|
||||
_endOfLastWrite = _buffer;
|
||||
_nextOutput = _buffer;
|
||||
_isStarved = true;
|
||||
|
@ -55,13 +58,13 @@ void AudioRingBuffer::reset() {
|
|||
|
||||
void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
|
||||
delete[] _buffer;
|
||||
_sampleCapacity = numFrameSamples * RING_BUFFER_LENGTH_FRAMES;
|
||||
_sampleCapacity = numFrameSamples * _frameCapacity;
|
||||
_numFrameSamples = numFrameSamples;
|
||||
_buffer = new int16_t[_sampleCapacity];
|
||||
if (_randomAccessMode) {
|
||||
memset(_buffer, 0, _sampleCapacity * sizeof(int16_t));
|
||||
}
|
||||
_nextOutput = _buffer;
|
||||
_endOfLastWrite = _buffer;
|
||||
reset();
|
||||
}
|
||||
|
||||
int AudioRingBuffer::parseData(const QByteArray& packet) {
|
||||
|
|
|
@ -31,15 +31,15 @@ const int NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL = NETWORK_BUFFER_LENGTH_BYTE
|
|||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
/ (float) SAMPLE_RATE) * 1000 * 1000);
|
||||
|
||||
const short RING_BUFFER_LENGTH_FRAMES = 10;
|
||||
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
const int MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||
|
||||
const int DEFAULT_RING_BUFFER_FRAME_CAPACITY = 10;
|
||||
|
||||
class AudioRingBuffer : public NodeData {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioRingBuffer(int numFrameSamples, bool randomAccessMode = false);
|
||||
AudioRingBuffer(int numFrameSamples, bool randomAccessMode = false, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
|
||||
~AudioRingBuffer();
|
||||
|
||||
void reset();
|
||||
|
@ -84,6 +84,7 @@ protected:
|
|||
|
||||
int _overflowCount; /// how many times has the ring buffer has overwritten old data
|
||||
|
||||
int _frameCapacity;
|
||||
int _sampleCapacity;
|
||||
bool _isFull;
|
||||
int _numFrameSamples;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "InjectedAudioRingBuffer.h"
|
||||
|
||||
InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier, bool dynamicJitterBuffer) :
|
||||
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector, /* isStereo=*/ false , dynamicJitterBuffer),
|
||||
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector, false, dynamicJitterBuffer),
|
||||
_streamIdentifier(streamIdentifier),
|
||||
_radius(0.0f),
|
||||
_attenuationRatio(0)
|
||||
|
|
|
@ -85,10 +85,10 @@ quint64 InterframeTimeGapStats::getWindowMaxGap() {
|
|||
}
|
||||
|
||||
|
||||
PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type,
|
||||
bool isStereo, bool dynamicJitterBuffers) :
|
||||
PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo, bool dynamicJitterBuffers) :
|
||||
|
||||
AudioRingBuffer(isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL),
|
||||
AudioRingBuffer(isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
|
||||
false, AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY),
|
||||
_type(type),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
|
@ -98,7 +98,7 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::
|
|||
_isStereo(isStereo),
|
||||
_listenerUnattenuatedZone(NULL),
|
||||
_desiredJitterBufferFrames(1),
|
||||
_currentJitterBufferFrames(0),
|
||||
_currentJitterBufferFrames(-1),
|
||||
_dynamicJitterBuffers(dynamicJitterBuffers)
|
||||
{
|
||||
}
|
||||
|
@ -216,8 +216,8 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() {
|
|||
// if the buffer doesn't have a full frame of samples to take for mixing, it is starved
|
||||
_isStarved = true;
|
||||
|
||||
// set to 0 to indicate the jitter buffer is starved
|
||||
_currentJitterBufferFrames = 0;
|
||||
// set to -1 to indicate the jitter buffer is starved
|
||||
_currentJitterBufferFrames = -1;
|
||||
|
||||
// reset our _shouldOutputStarveDebug to true so the next is printed
|
||||
_shouldOutputStarveDebug = true;
|
||||
|
@ -261,7 +261,7 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() {
|
|||
if (_desiredJitterBufferFrames < 1) {
|
||||
_desiredJitterBufferFrames = 1;
|
||||
}
|
||||
const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1;
|
||||
const int maxDesired = _frameCapacity - 1;
|
||||
if (_desiredJitterBufferFrames > maxDesired) {
|
||||
_desiredJitterBufferFrames = maxDesired;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ private:
|
|||
bool _newWindowMaxGapAvailable;
|
||||
};
|
||||
|
||||
const int AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY = 100;
|
||||
|
||||
class PositionalAudioRingBuffer : public AudioRingBuffer {
|
||||
public:
|
||||
enum Type {
|
||||
|
@ -103,6 +105,11 @@ protected:
|
|||
int _desiredJitterBufferFrames;
|
||||
int _currentJitterBufferFrames;
|
||||
bool _dynamicJitterBuffers;
|
||||
|
||||
// extra stats
|
||||
int _starveCount;
|
||||
int _silentFramesDropped;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_PositionalAudioRingBuffer_h
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
#include <qbytearray.h>
|
||||
#include <qvector.h>
|
||||
|
||||
#include "SequenceNumberStats.h"
|
||||
|
||||
class SentPacketHistory {
|
||||
|
||||
public:
|
||||
SentPacketHistory(int size = 1000);
|
||||
SentPacketHistory(int size = MAX_REASONABLE_SEQUENCE_GAP);
|
||||
|
||||
void packetSent(uint16_t sequenceNumber, const QByteArray& packet);
|
||||
const QByteArray* getPacket(uint16_t sequenceNumber) const;
|
||||
|
|
|
@ -39,7 +39,6 @@ void SequenceNumberStats::reset() {
|
|||
}
|
||||
|
||||
static const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
|
||||
static const int MAX_REASONABLE_SEQUENCE_GAP = 1000; // this must be less than UINT16_RANGE / 2 for rollover handling to work
|
||||
|
||||
void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderUUID, const bool wantExtraDebugging) {
|
||||
|
||||
|
@ -95,6 +94,7 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderU
|
|||
|
||||
_numEarly++;
|
||||
_numLost += (incomingInt - expectedInt);
|
||||
_lastReceived = incoming;
|
||||
|
||||
// add all sequence numbers that were skipped to the missing sequence numbers list
|
||||
for (int missingInt = expectedInt; missingInt < incomingInt; missingInt++) {
|
||||
|
@ -106,14 +106,14 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderU
|
|||
if (_missingSet.size() > MAX_REASONABLE_SEQUENCE_GAP) {
|
||||
pruneMissingSet(wantExtraDebugging);
|
||||
}
|
||||
|
||||
_lastReceived = incoming;
|
||||
} else { // late
|
||||
if (wantExtraDebugging) {
|
||||
qDebug() << "this packet is later than expected...";
|
||||
}
|
||||
_numLate++;
|
||||
|
||||
// do not update _lastReceived; it shouldn't become smaller
|
||||
|
||||
// remove this from missing sequence number if it's in there
|
||||
if (_missingSet.remove(incoming)) {
|
||||
if (wantExtraDebugging) {
|
||||
|
@ -127,8 +127,6 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderU
|
|||
}
|
||||
_numDuplicate++;
|
||||
}
|
||||
|
||||
// do not update _incomingLastSequence; it shouldn't become smaller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
#include "SharedUtil.h"
|
||||
#include <quuid.h>
|
||||
|
||||
const int MAX_REASONABLE_SEQUENCE_GAP = 1000;
|
||||
|
||||
class SequenceNumberStats {
|
||||
public:
|
||||
SequenceNumberStats();
|
||||
|
||||
void reset();
|
||||
|
||||
void sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false);
|
||||
void pruneMissingSet(const bool wantExtraDebugging = false);
|
||||
|
||||
quint32 getNumReceived() const { return _numReceived; }
|
||||
quint32 getNumUnreasonable() const { return _numUnreasonable; }
|
||||
|
@ -34,8 +36,6 @@ public:
|
|||
const QSet<quint16>& getMissingSet() const { return _missingSet; }
|
||||
|
||||
private:
|
||||
void pruneMissingSet(const bool wantExtraDebugging);
|
||||
|
||||
quint16 _lastReceived;
|
||||
QSet<quint16> _missingSet;
|
||||
|
||||
|
|
|
@ -168,6 +168,7 @@ public:
|
|||
float getIncomingFlightTimeAverage() { return _incomingFlightTimeAverage.getAverage(); }
|
||||
|
||||
const SequenceNumberStats& getIncomingOctreeSequenceNumberStats() const { return _incomingOctreeSequenceNumberStats; }
|
||||
SequenceNumberStats& getIncomingOctreeSequenceNumberStats() { return _incomingOctreeSequenceNumberStats; }
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ void AudioRingBufferTests::runAllTests() {
|
|||
int readIndexAt;
|
||||
|
||||
|
||||
AudioRingBuffer ringBuffer(10); // makes buffer of 100 int16_t samples
|
||||
AudioRingBuffer ringBuffer(10, false, 10); // makes buffer of 100 int16_t samples
|
||||
for (int T = 0; T < 300; T++) {
|
||||
|
||||
writeIndexAt = 0;
|
||||
|
|
|
@ -115,6 +115,11 @@ void SequenceNumberStatsTests::earlyLateTest() {
|
|||
}
|
||||
}
|
||||
stats.reset();
|
||||
numSent = 0;
|
||||
numEarly = 0;
|
||||
numLate = 0;
|
||||
numLost = 0;
|
||||
numRecovered = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +208,11 @@ void SequenceNumberStatsTests::duplicateTest() {
|
|||
}
|
||||
}
|
||||
stats.reset();
|
||||
numSent = 0;
|
||||
numDuplicate = 0;
|
||||
numEarly = 0;
|
||||
numLate = 0;
|
||||
numLost = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,5 +273,8 @@ void SequenceNumberStatsTests::pruneTest() {
|
|||
}
|
||||
}
|
||||
stats.reset();
|
||||
numSent = 0;
|
||||
numEarly = 0;
|
||||
numLost = 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue