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:
Brad Hefta-Gaub 2014-07-03 13:32:31 -07:00
commit dbdce32b68
18 changed files with 74 additions and 39 deletions

View file

@ -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);
}

View file

@ -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) {

View file

@ -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);

View file

@ -42,7 +42,7 @@ OctreeQueryNode::OctreeQueryNode() :
_lastRootTimestamp(0),
_myPacketType(PacketTypeUnknown),
_isShuttingDown(false),
_sentPacketHistory(1000)
_sentPacketHistory()
{
}

View file

@ -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();

View file

@ -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),

View file

@ -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);

View file

@ -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) {

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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
}
}
}

View file

@ -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;

View file

@ -168,6 +168,7 @@ public:
float getIncomingFlightTimeAverage() { return _incomingFlightTimeAverage.getAverage(); }
const SequenceNumberStats& getIncomingOctreeSequenceNumberStats() const { return _incomingOctreeSequenceNumberStats; }
SequenceNumberStats& getIncomingOctreeSequenceNumberStats() { return _incomingOctreeSequenceNumberStats; }
private:

View file

@ -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;

View file

@ -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;
}
}