Merge pull request #3067 from ZappoMan/serverSideAudioMixerStats

adding more stats & allow config setting to enable/disable dynamic jitter buffers
This commit is contained in:
Stephen Birarda 2014-06-24 10:49:35 -07:00
commit c46453c97f
11 changed files with 72 additions and 25 deletions

View file

@ -64,6 +64,8 @@ void attachNewBufferToNode(Node *newNode) {
} }
} }
bool AudioMixer::_useDynamicJitterBuffers = false;
AudioMixer::AudioMixer(const QByteArray& packet) : AudioMixer::AudioMixer(const QByteArray& packet) :
ThreadedAssignment(packet), ThreadedAssignment(packet),
_trailingSleepRatio(1.0f), _trailingSleepRatio(1.0f),
@ -502,6 +504,16 @@ void AudioMixer::run() {
<< QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z); << QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z);
} }
// check the payload to see if we have asked for dynamicJitterBuffer support
const QString DYNAMIC_JITTER_BUFFER_REGEX_STRING = "--dynamicJitterBuffer";
QRegExp dynamicJitterBufferMatch(DYNAMIC_JITTER_BUFFER_REGEX_STRING);
if (dynamicJitterBufferMatch.indexIn(_payload) != -1) {
qDebug() << "Enable dynamic jitter buffers.";
_useDynamicJitterBuffers = true;
} else {
qDebug() << "Dynamic jitter buffers disabled, using old behavior.";
}
int nextFrame = 0; int nextFrame = 0;
QElapsedTimer timer; QElapsedTimer timer;
timer.start(); timer.start();

View file

@ -34,6 +34,9 @@ public slots:
void readPendingDatagrams(); void readPendingDatagrams();
void sendStatsPacket(); void sendStatsPacket();
static bool getUseDynamicJitterBuffers() { return _useDynamicJitterBuffers; }
private: private:
/// adds one buffer to the mix for a listening node /// adds one buffer to the mix for a listening node
void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd, void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd,
@ -54,6 +57,7 @@ private:
int _sumMixes; int _sumMixes;
AABox* _sourceUnattenuatedZone; AABox* _sourceUnattenuatedZone;
AABox* _listenerUnattenuatedZone; AABox* _listenerUnattenuatedZone;
static bool _useDynamicJitterBuffers;
}; };
#endif // hifi_AudioMixer_h #endif // hifi_AudioMixer_h

View file

@ -16,6 +16,7 @@
#include "InjectedAudioRingBuffer.h" #include "InjectedAudioRingBuffer.h"
#include "AudioMixer.h"
#include "AudioMixerClientData.h" #include "AudioMixerClientData.h"
AudioMixerClientData::AudioMixerClientData() : AudioMixerClientData::AudioMixerClientData() :
@ -65,7 +66,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
if (!avatarRingBuffer) { if (!avatarRingBuffer) {
// we don't have an AvatarAudioRingBuffer yet, so add it // we don't have an AvatarAudioRingBuffer yet, so add it
avatarRingBuffer = new AvatarAudioRingBuffer(isStereo); avatarRingBuffer = new AvatarAudioRingBuffer(isStereo, AudioMixer::getUseDynamicJitterBuffers());
_ringBuffers.push_back(avatarRingBuffer); _ringBuffers.push_back(avatarRingBuffer);
} }
@ -88,7 +89,8 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
if (!matchingInjectedRingBuffer) { if (!matchingInjectedRingBuffer) {
// we don't have a matching injected audio ring buffer, so add it // we don't have a matching injected audio ring buffer, so add it
matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier); matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier,
AudioMixer::getUseDynamicJitterBuffers());
_ringBuffers.push_back(matchingInjectedRingBuffer); _ringBuffers.push_back(matchingInjectedRingBuffer);
} }
@ -144,13 +146,17 @@ QString AudioMixerClientData::getJitterBufferStats() const {
AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer();
if (avatarRingBuffer) { if (avatarRingBuffer) {
int desiredJitterBuffer = avatarRingBuffer->getDesiredJitterBufferFrames(); int desiredJitterBuffer = avatarRingBuffer->getDesiredJitterBufferFrames();
int calculatedJitterBuffer = avatarRingBuffer->getCalculatedDesiredJitterBufferFrames();
int currentJitterBuffer = avatarRingBuffer->getCurrentJitterBufferFrames(); int currentJitterBuffer = avatarRingBuffer->getCurrentJitterBufferFrames();
int resetCount = avatarRingBuffer->getResetCount();
int samplesAvailable = avatarRingBuffer->samplesAvailable(); int samplesAvailable = avatarRingBuffer->samplesAvailable();
int framesAvailable = (samplesAvailable / avatarRingBuffer->getSamplesPerFrame()); int framesAvailable = (samplesAvailable / avatarRingBuffer->getSamplesPerFrame());
result += "mic.desired:" + QString::number(desiredJitterBuffer) result += "mic.desired:" + QString::number(desiredJitterBuffer)
+ " calculated:" + QString::number(calculatedJitterBuffer)
+ " current:" + QString::number(currentJitterBuffer) + " current:" + QString::number(currentJitterBuffer)
+ " available:" + QString::number(framesAvailable) + " available:" + QString::number(framesAvailable)
+ " samples:" + QString::number(samplesAvailable); + " samples:" + QString::number(samplesAvailable)
+ " resets:" + QString::number(resetCount);
} else { } else {
result = "mic unknown"; result = "mic unknown";
} }
@ -158,13 +164,17 @@ QString AudioMixerClientData::getJitterBufferStats() const {
for (int i = 0; i < _ringBuffers.size(); i++) { for (int i = 0; i < _ringBuffers.size(); i++) {
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector) {
int desiredJitterBuffer = _ringBuffers[i]->getDesiredJitterBufferFrames(); int desiredJitterBuffer = _ringBuffers[i]->getDesiredJitterBufferFrames();
int calculatedJitterBuffer = _ringBuffers[i]->getCalculatedDesiredJitterBufferFrames();
int currentJitterBuffer = _ringBuffers[i]->getCurrentJitterBufferFrames(); int currentJitterBuffer = _ringBuffers[i]->getCurrentJitterBufferFrames();
int resetCount = _ringBuffers[i]->getResetCount();
int samplesAvailable = _ringBuffers[i]->samplesAvailable(); int samplesAvailable = _ringBuffers[i]->samplesAvailable();
int framesAvailable = (samplesAvailable / _ringBuffers[i]->getSamplesPerFrame()); int framesAvailable = (samplesAvailable / _ringBuffers[i]->getSamplesPerFrame());
result += "| injected["+QString::number(i)+"].desired:" + QString::number(desiredJitterBuffer) result += "| injected["+QString::number(i)+"].desired:" + QString::number(desiredJitterBuffer)
+ " calculated:" + QString::number(calculatedJitterBuffer)
+ " current:" + QString::number(currentJitterBuffer) + " current:" + QString::number(currentJitterBuffer)
+ " available:" + QString::number(framesAvailable) + " available:" + QString::number(framesAvailable)
+ " samples:" + QString::number(samplesAvailable); + " samples:" + QString::number(samplesAvailable)
+ " resets:" + QString::number(resetCount);
} }
} }

View file

@ -13,8 +13,8 @@
#include "AvatarAudioRingBuffer.h" #include "AvatarAudioRingBuffer.h"
AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo) : AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo, bool dynamicJitterBuffer) :
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Microphone, isStereo) { PositionalAudioRingBuffer(PositionalAudioRingBuffer::Microphone, isStereo, dynamicJitterBuffer) {
} }

View file

@ -18,7 +18,7 @@
class AvatarAudioRingBuffer : public PositionalAudioRingBuffer { class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
public: public:
AvatarAudioRingBuffer(bool isStereo = false); AvatarAudioRingBuffer(bool isStereo = false, bool dynamicJitterBuffer = false);
int parseData(const QByteArray& packet); int parseData(const QByteArray& packet);
private: private:

View file

@ -21,6 +21,7 @@
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) : AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) :
NodeData(), NodeData(),
_resetCount(0),
_sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES), _sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES),
_numFrameSamples(numFrameSamples), _numFrameSamples(numFrameSamples),
_isStarved(true), _isStarved(true),
@ -128,6 +129,7 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) {
_endOfLastWrite = _buffer; _endOfLastWrite = _buffer;
_nextOutput = _buffer; _nextOutput = _buffer;
_isStarved = true; _isStarved = true;
_resetCount++;
} }
if (_endOfLastWrite + samplesToCopy <= _buffer + _sampleCapacity) { if (_endOfLastWrite + samplesToCopy <= _buffer + _sampleCapacity) {

View file

@ -71,6 +71,7 @@ public:
bool isStarved() const { return _isStarved; } bool isStarved() const { return _isStarved; }
void setIsStarved(bool isStarved) { _isStarved = isStarved; } void setIsStarved(bool isStarved) { _isStarved = isStarved; }
int getResetCount() const { return _resetCount; } /// how many times has the ring buffer written past the end and reset
bool hasStarted() const { return _hasStarted; } bool hasStarted() const { return _hasStarted; }
void addSilentFrame(int numSilentSamples); void addSilentFrame(int numSilentSamples);
@ -80,6 +81,8 @@ protected:
AudioRingBuffer& operator= (const AudioRingBuffer&); AudioRingBuffer& operator= (const AudioRingBuffer&);
int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const; int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const;
int _resetCount; /// how many times has the ring buffer written past the end and done a reset
int _sampleCapacity; int _sampleCapacity;
int _numFrameSamples; int _numFrameSamples;

View file

@ -19,8 +19,8 @@
#include "InjectedAudioRingBuffer.h" #include "InjectedAudioRingBuffer.h"
InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) : InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier, bool dynamicJitterBuffer) :
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector), PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector, /* isStereo=*/ false , dynamicJitterBuffer),
_streamIdentifier(streamIdentifier), _streamIdentifier(streamIdentifier),
_radius(0.0f), _radius(0.0f),
_attenuationRatio(0) _attenuationRatio(0)

View file

@ -18,7 +18,7 @@
class InjectedAudioRingBuffer : public PositionalAudioRingBuffer { class InjectedAudioRingBuffer : public PositionalAudioRingBuffer {
public: public:
InjectedAudioRingBuffer(const QUuid& streamIdentifier = QUuid()); InjectedAudioRingBuffer(const QUuid& streamIdentifier = QUuid(), bool dynamicJitterBuffer = false);
int parseData(const QByteArray& packet); int parseData(const QByteArray& packet);

View file

@ -85,7 +85,9 @@ quint64 InterframeTimeGapStats::getWindowMaxGap() {
} }
PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo) : 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),
_type(type), _type(type),
_position(0.0f, 0.0f, 0.0f), _position(0.0f, 0.0f, 0.0f),
@ -96,7 +98,8 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::
_isStereo(isStereo), _isStereo(isStereo),
_listenerUnattenuatedZone(NULL), _listenerUnattenuatedZone(NULL),
_desiredJitterBufferFrames(1), _desiredJitterBufferFrames(1),
_currentJitterBufferFrames(0) _currentJitterBufferFrames(0),
_dynamicJitterBuffers(dynamicJitterBuffers)
{ {
} }
@ -233,21 +236,32 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() {
return true; return true;
} }
int PositionalAudioRingBuffer::getCalculatedDesiredJitterBufferFrames() const {
int calculatedDesiredJitterBufferFrames = 1;
const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE;
calculatedDesiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.peekWindowMaxGap() / USECS_PER_FRAME);
if (calculatedDesiredJitterBufferFrames < 1) {
calculatedDesiredJitterBufferFrames = 1;
}
return calculatedDesiredJitterBufferFrames;
}
void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() {
if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) { if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) {
if (!_dynamicJitterBuffers) {
_desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence _desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence
/* } else {
const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE;
_desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME); _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME);
if (_desiredJitterBufferFrames < 1) { if (_desiredJitterBufferFrames < 1) {
_desiredJitterBufferFrames = 1; _desiredJitterBufferFrames = 1;
}
const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1;
if (_desiredJitterBufferFrames > maxDesired) {
_desiredJitterBufferFrames = maxDesired;
}
} }
const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1;
if (_desiredJitterBufferFrames > maxDesired) {
_desiredJitterBufferFrames = maxDesired;
}
*/
} }
} }

View file

@ -50,7 +50,7 @@ public:
Injector Injector
}; };
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo = false); PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo = false, bool dynamicJitterBuffers = false);
int parseData(const QByteArray& packet); int parseData(const QByteArray& packet);
int parsePositionalData(const QByteArray& positionalByteArray); int parsePositionalData(const QByteArray& positionalByteArray);
@ -77,6 +77,7 @@ public:
int getSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; } int getSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; }
int getCalculatedDesiredJitterBufferFrames() const; /// returns what we would calculate our desired as if asked
int getDesiredJitterBufferFrames() const { return _desiredJitterBufferFrames; } int getDesiredJitterBufferFrames() const { return _desiredJitterBufferFrames; }
int getCurrentJitterBufferFrames() const { return _currentJitterBufferFrames; } int getCurrentJitterBufferFrames() const { return _currentJitterBufferFrames; }
@ -101,6 +102,7 @@ protected:
InterframeTimeGapStats _interframeTimeGapStats; InterframeTimeGapStats _interframeTimeGapStats;
int _desiredJitterBufferFrames; int _desiredJitterBufferFrames;
int _currentJitterBufferFrames; int _currentJitterBufferFrames;
bool _dynamicJitterBuffers;
}; };
#endif // hifi_PositionalAudioRingBuffer_h #endif // hifi_PositionalAudioRingBuffer_h