mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 01:04:06 +02:00
added stdev method of jitter calc to InboundAudioStream
This commit is contained in:
parent
45b4777e60
commit
9bbd055404
8 changed files with 66 additions and 42 deletions
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue