mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 07:49:13 +02:00
Merge pull request #3067 from ZappoMan/serverSideAudioMixerStats
adding more stats & allow config setting to enable/disable dynamic jitter buffers
This commit is contained in:
commit
c46453c97f
11 changed files with 72 additions and 25 deletions
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue