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) {
AudioStreamStats streamStats = avatarRingBuffer->getAudioStreamStats();
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:" + QString::number(streamStats._ringBufferFramesAvailable)
+ " starves:" + QString::number(streamStats._ringBufferStarveCount)
@ -225,11 +225,11 @@ QString AudioMixerClientData::getAudioStreamStatsString() const {
}
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) {
AudioStreamStats streamStats = i.value()->getAudioStreamStats();
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:" + QString::number(streamStats._ringBufferFramesAvailable)
+ " 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.
_inputRingBuffer(0),
#ifdef _WIN32
_ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, 100, true),
_ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, 100, true, true),
#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
_isStereoInput(false),
_averagedLatency(0.0),

View file

@ -12,11 +12,14 @@
#include "InboundAudioStream.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),
_lastPopSucceeded(false),
_lastPopOutput(),
_dynamicJitterBuffers(dynamicJitterBuffers),
_useStDevForJitterCalc(useStDevForJitterCalc),
_calculatedJitterBufferFramesUsingMaxGap(0),
_calculatedJitterBufferFramesUsingStDev(0),
_desiredJitterBufferFrames(1),
_isStarved(true),
_hasStarted(false),
@ -143,46 +146,51 @@ void InboundAudioStream::starved() {
_starveCount++;
}
int InboundAudioStream::getCalculatedDesiredJitterBufferFrames() const {
const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE;
int calculatedDesiredJitterBufferFrames = ceilf((float)_interframeTimeGapStatsForJitterCalc.getWindowMax() / USECS_PER_FRAME);
if (calculatedDesiredJitterBufferFrames < 1) {
calculatedDesiredJitterBufferFrames = 1;
}
return calculatedDesiredJitterBufferFrames;
int InboundAudioStream::clampDesiredJitterBufferFramesValue(int desired) const {
const int MIN_FRAMES_DESIRED = 0;
const int MAX_FRAMES_DESIRED = _ringBuffer.getFrameCapacity();
return glm::clamp(desired, MIN_FRAMES_DESIRED, MAX_FRAMES_DESIRED);
}
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);
// 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();
if (_incomingSequenceNumberStats.getNumReceived() >= NUM_INITIAL_PACKETS_DISCARD) {
if (_incomingSequenceNumberStats.getNumReceived() > NUM_INITIAL_PACKETS_DISCARD) {
quint64 gap = now - _lastFrameReceivedTime;
_interframeTimeGapStatsForJitterCalc.update(gap);
_interframeTimeGapStatsForStatsPacket.update(gap);
}
_lastFrameReceivedTime = now;
// recalculate the _desiredJitterBufferFrames if _interframeTimeGapStatsForJitterCalc has updated stats for us
if (_interframeTimeGapStatsForJitterCalc.getNewStatsAvailableFlag()) {
if (!_dynamicJitterBuffers) {
_desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence
} else {
_desiredJitterBufferFrames = getCalculatedDesiredJitterBufferFrames();
const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE;
const int maxDesired = _ringBuffer.getFrameCapacity() - 1;
if (_desiredJitterBufferFrames > maxDesired) {
_desiredJitterBufferFrames = maxDesired;
// update stats for Freddy's method of jitter calc
_interframeTimeGapStatsForJitterCalc.update(gap);
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;
}

View file

@ -18,6 +18,7 @@
#include "SequenceNumberStats.h"
#include "AudioStreamStats.h"
#include "PacketHeaders.h"
#include "StdDev.h"
// the time gaps stats for _desiredJitterBufferFrames calculation
// 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 {
Q_OBJECT
public:
InboundAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers);
InboundAudioStream(int numFrameSamples, int numFramesCapacity, bool dynamicJitterBuffers, bool useStDevForJitterCalc = false);
void reset();
void resetStats();
@ -67,7 +68,15 @@ public:
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 getNumFrameSamples() const { return _ringBuffer.getNumFrameSamples(); }
@ -86,6 +95,8 @@ public:
private:
void starved();
int clampDesiredJitterBufferFramesValue(int desired) const;
protected:
// disallow copying of InboundAudioStream objects
InboundAudioStream(const InboundAudioStream&);
@ -110,6 +121,10 @@ protected:
AudioRingBuffer::ConstIterator _lastPopOutput;
bool _dynamicJitterBuffers;
bool _useStDevForJitterCalc;
int _calculatedJitterBufferFramesUsingMaxGap;
int _calculatedJitterBufferFramesUsingStDev;
int _desiredJitterBufferFrames;
bool _isStarved;
@ -125,6 +140,7 @@ protected:
quint64 _lastFrameReceivedTime;
MovingMinMaxAvg<quint64> _interframeTimeGapStatsForJitterCalc;
StDev _stdev;
MovingMinMaxAvg<quint64> _interframeTimeGapStatsForStatsPacket;
// TODO: change this to time-weighted moving avg

View file

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

View file

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

View file

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

View file

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