added stdev method of jitter calc to InboundAudioStream

This commit is contained in:
wangyix 2014-07-28 11:41:08 -07:00
parent 45b4777e60
commit 9bbd055404
8 changed files with 66 additions and 42 deletions

View file

@ -205,7 +205,7 @@ QString AudioMixerClientData::getAudioStreamStatsString() const {
if (avatarRingBuffer) { if (avatarRingBuffer) {
AudioStreamStats streamStats = avatarRingBuffer->getAudioStreamStats(); AudioStreamStats streamStats = avatarRingBuffer->getAudioStreamStats();
result += " UPSTREAM.mic.desired:" + QString::number(streamStats._ringBufferDesiredJitterBufferFrames) result += " UPSTREAM.mic.desired:" + QString::number(streamStats._ringBufferDesiredJitterBufferFrames)
+ " desired_calc:" + QString::number(avatarRingBuffer->getCalculatedDesiredJitterBufferFrames()) + " desired_calc:" + QString::number(avatarRingBuffer->getCalculatedJitterBufferFrames())
+ " available_avg_10s:" + QString::number(streamStats._ringBufferFramesAvailableAverage) + " available_avg_10s:" + QString::number(streamStats._ringBufferFramesAvailableAverage)
+ " available:" + QString::number(streamStats._ringBufferFramesAvailable) + " available:" + QString::number(streamStats._ringBufferFramesAvailable)
+ " starves:" + QString::number(streamStats._ringBufferStarveCount) + " starves:" + QString::number(streamStats._ringBufferStarveCount)
@ -225,11 +225,11 @@ QString AudioMixerClientData::getAudioStreamStatsString() const {
} }
QHash<QUuid, PositionalAudioRingBuffer*>::ConstIterator i; QHash<QUuid, PositionalAudioRingBuffer*>::ConstIterator i;
for (i = _ringBuffers.constBegin(); i != _ringBuffers.constEnd; i++) { for (i = _ringBuffers.constBegin(); i != _ringBuffers.constEnd(); i++) {
if (i.value()->getType() == PositionalAudioRingBuffer::Injector) { if (i.value()->getType() == PositionalAudioRingBuffer::Injector) {
AudioStreamStats streamStats = i.value()->getAudioStreamStats(); AudioStreamStats streamStats = i.value()->getAudioStreamStats();
result += " UPSTREAM.inj.desired:" + QString::number(streamStats._ringBufferDesiredJitterBufferFrames) result += " UPSTREAM.inj.desired:" + QString::number(streamStats._ringBufferDesiredJitterBufferFrames)
+ " desired_calc:" + QString::number(i.value()->getCalculatedDesiredJitterBufferFrames()) + " desired_calc:" + QString::number(i.value()->getCalculatedJitterBufferFrames())
+ " available_avg_10s:" + QString::number(streamStats._ringBufferFramesAvailableAverage) + " available_avg_10s:" + QString::number(streamStats._ringBufferFramesAvailableAverage)
+ " available:" + QString::number(streamStats._ringBufferFramesAvailable) + " available:" + QString::number(streamStats._ringBufferFramesAvailable)
+ " starves:" + QString::number(streamStats._ringBufferStarveCount) + " starves:" + QString::number(streamStats._ringBufferStarveCount)

View file

@ -76,9 +76,9 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
// this delay will slowly add up and the longer someone runs, they more delayed their audio will be. // this delay will slowly add up and the longer someone runs, they more delayed their audio will be.
_inputRingBuffer(0), _inputRingBuffer(0),
#ifdef _WIN32 #ifdef _WIN32
_ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, 100, true), _ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, 100, true, true),
#else #else
_ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, 10, true), // DO NOT CHANGE THIS UNLESS YOU SOLVE THE AUDIO DEVICE DRIFT PROBLEM!!! _ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, 10, true, true), // DO NOT CHANGE THIS UNLESS YOU SOLVE THE AUDIO DEVICE DRIFT PROBLEM!!!
#endif #endif
_isStereoInput(false), _isStereoInput(false),
_averagedLatency(0.0), _averagedLatency(0.0),

View file

@ -12,11 +12,14 @@
#include "InboundAudioStream.h" #include "InboundAudioStream.h"
#include "PacketHeaders.h" #include "PacketHeaders.h"
InboundAudioStream::InboundAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers) : InboundAudioStream::InboundAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers, bool useStDevForJitterCalc) :
_ringBuffer(numFrameSamples, false, numFramesCapacity), _ringBuffer(numFrameSamples, false, numFramesCapacity),
_lastPopSucceeded(false), _lastPopSucceeded(false),
_lastPopOutput(), _lastPopOutput(),
_dynamicJitterBuffers(dynamicJitterBuffers), _dynamicJitterBuffers(dynamicJitterBuffers),
_useStDevForJitterCalc(useStDevForJitterCalc),
_calculatedJitterBufferFramesUsingMaxGap(0),
_calculatedJitterBufferFramesUsingStDev(0),
_desiredJitterBufferFrames(1), _desiredJitterBufferFrames(1),
_isStarved(true), _isStarved(true),
_hasStarted(false), _hasStarted(false),
@ -143,46 +146,51 @@ void InboundAudioStream::starved() {
_starveCount++; _starveCount++;
} }
int InboundAudioStream::clampDesiredJitterBufferFramesValue(int desired) const {
int InboundAudioStream::getCalculatedDesiredJitterBufferFrames() const { const int MIN_FRAMES_DESIRED = 0;
const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; const int MAX_FRAMES_DESIRED = _ringBuffer.getFrameCapacity();
return glm::clamp(desired, MIN_FRAMES_DESIRED, MAX_FRAMES_DESIRED);
int calculatedDesiredJitterBufferFrames = ceilf((float)_interframeTimeGapStatsForJitterCalc.getWindowMax() / USECS_PER_FRAME);
if (calculatedDesiredJitterBufferFrames < 1) {
calculatedDesiredJitterBufferFrames = 1;
}
return calculatedDesiredJitterBufferFrames;
} }
SequenceNumberStats::ArrivalInfo InboundAudioStream::frameReceivedUpdateNetworkStats(quint16 sequenceNumber, const QUuid& senderUUID) { SequenceNumberStats::ArrivalInfo InboundAudioStream::frameReceivedUpdateNetworkStats(quint16 sequenceNumber, const QUuid& senderUUID) {
const int NUM_INITIAL_PACKETS_DISCARD = 3; // track the sequence number we received
SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequenceNumber, senderUUID); SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequenceNumber, senderUUID);
// update the two time gap stats we're keeping // update our timegap stats and desired jitter buffer frames if necessary
// discard the first few packets we receive since they usually have gaps that aren't represensative of normal jitter
const int NUM_INITIAL_PACKETS_DISCARD = 3;
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();
if (_incomingSequenceNumberStats.getNumReceived() >= NUM_INITIAL_PACKETS_DISCARD) { if (_incomingSequenceNumberStats.getNumReceived() > NUM_INITIAL_PACKETS_DISCARD) {
quint64 gap = now - _lastFrameReceivedTime; quint64 gap = now - _lastFrameReceivedTime;
_interframeTimeGapStatsForJitterCalc.update(gap);
_interframeTimeGapStatsForStatsPacket.update(gap); _interframeTimeGapStatsForStatsPacket.update(gap);
}
_lastFrameReceivedTime = now;
// recalculate the _desiredJitterBufferFrames if _interframeTimeGapStatsForJitterCalc has updated stats for us const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE;
if (_interframeTimeGapStatsForJitterCalc.getNewStatsAvailableFlag()) {
if (!_dynamicJitterBuffers) {
_desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence
} else {
_desiredJitterBufferFrames = getCalculatedDesiredJitterBufferFrames();
const int maxDesired = _ringBuffer.getFrameCapacity() - 1; // update stats for Freddy's method of jitter calc
if (_desiredJitterBufferFrames > maxDesired) { _interframeTimeGapStatsForJitterCalc.update(gap);
_desiredJitterBufferFrames = maxDesired; if (_interframeTimeGapStatsForJitterCalc.getNewStatsAvailableFlag()) {
_calculatedJitterBufferFramesUsingMaxGap = ceilf((float)_interframeTimeGapStatsForJitterCalc.getWindowMax() / USECS_PER_FRAME);
_interframeTimeGapStatsForJitterCalc.clearNewStatsAvailableFlag();
if (_dynamicJitterBuffers && !_useStDevForJitterCalc) {
_desiredJitterBufferFrames = clampDesiredJitterBufferFramesValue(_calculatedJitterBufferFramesUsingMaxGap);
}
}
// update stats for Philip's method of jitter calc
_stdev.addValue(gap);
const int STANDARD_DEVIATION_SAMPLE_COUNT = 500;
if (_stdev.getSamples() > STANDARD_DEVIATION_SAMPLE_COUNT) {
const float NUM_STANDARD_DEVIATIONS = 3.0f;
_calculatedJitterBufferFramesUsingStDev = (int)ceilf(2 * (NUM_STANDARD_DEVIATIONS * _stdev.getStDev()) / USECS_PER_FRAME) + 1;
_stdev.reset();
if (_dynamicJitterBuffers && _useStDevForJitterCalc) {
_desiredJitterBufferFrames = clampDesiredJitterBufferFramesValue(_calculatedJitterBufferFramesUsingStDev);
} }
} }
_interframeTimeGapStatsForJitterCalc.clearNewStatsAvailableFlag();
} }
_lastFrameReceivedTime = now;
return arrivalInfo; return arrivalInfo;
} }

View file

@ -18,6 +18,7 @@
#include "SequenceNumberStats.h" #include "SequenceNumberStats.h"
#include "AudioStreamStats.h" #include "AudioStreamStats.h"
#include "PacketHeaders.h" #include "PacketHeaders.h"
#include "StdDev.h"
// the time gaps stats for _desiredJitterBufferFrames calculation // the time gaps stats for _desiredJitterBufferFrames calculation
// will recalculate the max for the past 5000 samples every 500 samples // will recalculate the max for the past 5000 samples every 500 samples
@ -44,7 +45,7 @@ const int INBOUND_RING_BUFFER_FRAME_CAPACITY = 100;
class InboundAudioStream : public NodeData { class InboundAudioStream : public NodeData {
Q_OBJECT Q_OBJECT
public: public:
InboundAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers); InboundAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers, bool useStDevForJitterCalc = false);
void reset(); void reset();
void resetStats(); void resetStats();
@ -67,7 +68,15 @@ public:
virtual AudioStreamStats getAudioStreamStats() const; virtual AudioStreamStats getAudioStreamStats() const;
int getCalculatedDesiredJitterBufferFrames() const; /// returns the desired number of jitter buffer frames under the dyanmic jitter buffers scheme
int getCalculatedJitterBufferFrames() const { return _useStDevForJitterCalc ?
_calculatedJitterBufferFramesUsingStDev : _calculatedJitterBufferFramesUsingMaxGap; };
/// returns the desired number of jitter buffer frames using Philip's method
int getCalculatedJitterBufferFramesUsingStDev() const { return _calculatedJitterBufferFramesUsingStDev; }
/// returns the desired number of jitter buffer frames using Freddy's method
int getCalculatedJitterBufferFramesUsingMaxGap() const { return _calculatedJitterBufferFramesUsingMaxGap; }
int getDesiredJitterBufferFrames() const { return _desiredJitterBufferFrames; } int getDesiredJitterBufferFrames() const { return _desiredJitterBufferFrames; }
int getNumFrameSamples() const { return _ringBuffer.getNumFrameSamples(); } int getNumFrameSamples() const { return _ringBuffer.getNumFrameSamples(); }
@ -86,6 +95,8 @@ public:
private: private:
void starved(); void starved();
int clampDesiredJitterBufferFramesValue(int desired) const;
protected: protected:
// disallow copying of InboundAudioStream objects // disallow copying of InboundAudioStream objects
InboundAudioStream(const InboundAudioStream&); InboundAudioStream(const InboundAudioStream&);
@ -110,6 +121,10 @@ protected:
AudioRingBuffer::ConstIterator _lastPopOutput; AudioRingBuffer::ConstIterator _lastPopOutput;
bool _dynamicJitterBuffers; bool _dynamicJitterBuffers;
bool _useStDevForJitterCalc;
int _calculatedJitterBufferFramesUsingMaxGap;
int _calculatedJitterBufferFramesUsingStDev;
int _desiredJitterBufferFrames; int _desiredJitterBufferFrames;
bool _isStarved; bool _isStarved;
@ -125,6 +140,7 @@ protected:
quint64 _lastFrameReceivedTime; quint64 _lastFrameReceivedTime;
MovingMinMaxAvg<quint64> _interframeTimeGapStatsForJitterCalc; MovingMinMaxAvg<quint64> _interframeTimeGapStatsForJitterCalc;
StDev _stdev;
MovingMinMaxAvg<quint64> _interframeTimeGapStatsForStatsPacket; MovingMinMaxAvg<quint64> _interframeTimeGapStatsForStatsPacket;
// TODO: change this to time-weighted moving avg // TODO: change this to time-weighted moving avg

View file

@ -1,8 +1,8 @@
#include "InboundMixedAudioStream.h" #include "InboundMixedAudioStream.h"
InboundMixedAudioStream::InboundMixedAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers) InboundMixedAudioStream::InboundMixedAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers, bool useStDevForJitterCalc)
: InboundAudioStream(numFrameSamples, numFramesCapacity, dynamicJitterBuffers) : InboundAudioStream(numFrameSamples, numFramesCapacity, dynamicJitterBuffers, useStDevForJitterCalc)
{ {
} }

View file

@ -4,7 +4,7 @@
class InboundMixedAudioStream : public InboundAudioStream { class InboundMixedAudioStream : public InboundAudioStream {
public: public:
InboundMixedAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers); InboundMixedAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers, bool useStDevForJitterCalc = false);
float getNextOutputFrameLoudness() const { return _ringBuffer.getNextOutputFrameLoudness(); } float getNextOutputFrameLoudness() const { return _ringBuffer.getNextOutputFrameLoudness(); }

View file

@ -29,7 +29,7 @@ void StDev::addValue(float v) {
if (sampleCount == MAX_STDEV_SAMPLES) sampleCount = 0; if (sampleCount == MAX_STDEV_SAMPLES) sampleCount = 0;
} }
float StDev::getAverage() { float StDev::getAverage() const {
float average = 0; float average = 0;
for (int i = 0; i < sampleCount; i++) { for (int i = 0; i < sampleCount; i++) {
average += data[i]; average += data[i];
@ -49,7 +49,7 @@ float StDev::getMax() {
else return 0; else return 0;
}*/ }*/
float StDev::getStDev() { float StDev::getStDev() const {
float average = getAverage(); float average = getAverage();
float stdev = 0; float stdev = 0;
for (int i = 0; i < sampleCount; i++) { for (int i = 0; i < sampleCount; i++) {

View file

@ -17,8 +17,8 @@ class StDev {
StDev(); StDev();
void reset(); void reset();
void addValue(float v); void addValue(float v);
float getAverage(); float getAverage() const;
float getStDev(); float getStDev() const;
int getSamples() const { return sampleCount; } int getSamples() const { return sampleCount; }
private: private:
float * data; float * data;