mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 20:31:29 +02:00
modified AudioStreamStats to per-stream with seq stats
added AudioStreamStats info to interface overlay stats
This commit is contained in:
parent
de3c1ebf0f
commit
d2f86278b2
7 changed files with 144 additions and 56 deletions
|
@ -591,7 +591,6 @@ void AudioMixer::run() {
|
||||||
|
|
||||||
const quint64 TOO_LONG_SINCE_LAST_SEND_AUDIO_STREAM_STATS = 1 * USECS_PER_SECOND;
|
const quint64 TOO_LONG_SINCE_LAST_SEND_AUDIO_STREAM_STATS = 1 * USECS_PER_SECOND;
|
||||||
|
|
||||||
char audioStreamStatsPacket[MAX_PACKET_SIZE];
|
|
||||||
bool sendAudioStreamStats = false;
|
bool sendAudioStreamStats = false;
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
if (now - _lastSendAudioStreamStatsTime > TOO_LONG_SINCE_LAST_SEND_AUDIO_STREAM_STATS) {
|
if (now - _lastSendAudioStreamStatsTime > TOO_LONG_SINCE_LAST_SEND_AUDIO_STREAM_STATS) {
|
||||||
|
@ -626,8 +625,7 @@ void AudioMixer::run() {
|
||||||
|
|
||||||
// send an audio stream stats packet if it's time
|
// send an audio stream stats packet if it's time
|
||||||
if (sendAudioStreamStats) {
|
if (sendAudioStreamStats) {
|
||||||
int numBytesAudioStreamStatsPacket = nodeData->encodeAudioStreamStatsPacket(audioStreamStatsPacket);
|
nodeData->sendAudioStreamStatsPackets(node);
|
||||||
nodeList->writeDatagram(audioStreamStatsPacket, numBytesAudioStreamStatsPacket, node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++_sumListeners;
|
++_sumListeners;
|
||||||
|
|
|
@ -156,41 +156,76 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixerClientData::getAudioStreamStats(AudioStreamStats& stats) const {
|
void AudioMixerClientData::getAudioStreamStatsOfStream(const PositionalAudioRingBuffer* ringBuffer, AudioStreamStats& stats) const {
|
||||||
int avatarJitterBufferFrames = 0;
|
const SequenceNumberStats* streamSequenceNumberStats = &_incomingAvatarAudioSequenceNumberStats;
|
||||||
int maxJitterBufferFrames = 0;
|
|
||||||
int sumJitterBufferFrames = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < _ringBuffers.size(); i++) {
|
stats._streamType = ringBuffer->getType();
|
||||||
|
if (stats._streamType == PositionalAudioRingBuffer::Injector) {
|
||||||
int bufferJitterFrames = _ringBuffers[i]->getCurrentJitterBufferFrames();
|
stats._streamIdentifier = ((InjectedAudioRingBuffer*)ringBuffer)->getStreamIdentifier();
|
||||||
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Microphone) {
|
streamSequenceNumberStats = &_incomingInjectedAudioSequenceNumberStatsMap.value(stats._streamIdentifier);
|
||||||
avatarJitterBufferFrames = bufferJitterFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufferJitterFrames > maxJitterBufferFrames) {
|
|
||||||
maxJitterBufferFrames = bufferJitterFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
sumJitterBufferFrames += bufferJitterFrames;
|
|
||||||
}
|
}
|
||||||
|
stats._jitterBufferFrames = ringBuffer->getCurrentJitterBufferFrames();
|
||||||
stats._avatarJitterBufferFrames = avatarJitterBufferFrames;
|
|
||||||
stats._maxJitterBufferFrames = maxJitterBufferFrames;
|
stats._packetsReceived = streamSequenceNumberStats->getNumReceived();
|
||||||
stats._avgJitterBufferFrames = (float)sumJitterBufferFrames / (float)_ringBuffers.size();
|
stats._packetsUnreasonable = streamSequenceNumberStats->getNumUnreasonable();
|
||||||
|
stats._packetsEarly = streamSequenceNumberStats->getNumEarly();
|
||||||
|
stats._packetsLate = streamSequenceNumberStats->getNumLate();
|
||||||
|
stats._packetsLost = streamSequenceNumberStats->getNumLost();
|
||||||
|
stats._packetsRecovered = streamSequenceNumberStats->getNumRecovered();
|
||||||
|
stats._packetsDuplicate = streamSequenceNumberStats->getNumDuplicate();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioMixerClientData::encodeAudioStreamStatsPacket(char* packet) const {
|
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) const {
|
||||||
|
|
||||||
|
char packet[MAX_PACKET_SIZE];
|
||||||
|
AudioStreamStats streamStats;
|
||||||
|
|
||||||
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
// The append flag is a boolean value that will be packed right after the header. The first packet sent
|
||||||
|
// inside this method will have 0 for this flag, while every subsequent packet will have 1 for this flag.
|
||||||
|
// The sole purpose of this flag is so the client can clear its map of injected audio stream stats when
|
||||||
|
// it receives a packet with an appendFlag of 0. This prevents the buildup of dead audio stream stats in the client.
|
||||||
|
quint8 appendFlag = 0;
|
||||||
|
|
||||||
|
// pack header
|
||||||
int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
||||||
char* dataAt = packet + numBytesPacketHeader;
|
char* headerEndAt = packet + numBytesPacketHeader;
|
||||||
|
|
||||||
// pack jitter buffer stats
|
// calculate how many stream stat structs we can fit in each packet
|
||||||
AudioStreamStats stats;
|
const int numStreamStatsRoomFor = (MAX_PACKET_SIZE - numBytesPacketHeader - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
|
||||||
getAudioStreamStats(stats);
|
|
||||||
memcpy(dataAt, &stats, sizeof(AudioStreamStats));
|
|
||||||
dataAt += sizeof(AudioStreamStats);
|
|
||||||
|
|
||||||
return dataAt - packet;
|
// pack and send stream stats packets until all ring buffers' stats are sent
|
||||||
|
int numStreamStatsRemaining = _ringBuffers.size();
|
||||||
|
QList<PositionalAudioRingBuffer*>::ConstIterator ringBuffersIterator = _ringBuffers.constBegin();
|
||||||
|
while (numStreamStatsRemaining > 0) {
|
||||||
|
|
||||||
|
char* dataAt = headerEndAt;
|
||||||
|
|
||||||
|
// pack the append flag
|
||||||
|
memcpy(dataAt, &appendFlag, sizeof(quint8));
|
||||||
|
appendFlag = 1;
|
||||||
|
dataAt += sizeof(quint8);
|
||||||
|
|
||||||
|
// calculate and pack the number of stream stats to follow
|
||||||
|
quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor);
|
||||||
|
memcpy(dataAt, &numStreamStatsToPack, sizeof(quint16));
|
||||||
|
dataAt += sizeof(quint16);
|
||||||
|
|
||||||
|
// pack the calculated number of stream stats
|
||||||
|
for (int i = 0; i < numStreamStatsToPack; i++) {
|
||||||
|
getAudioStreamStatsOfStream(*ringBuffersIterator, streamStats);
|
||||||
|
|
||||||
|
memcpy(dataAt, &streamStats, sizeof(AudioStreamStats));
|
||||||
|
dataAt += sizeof(AudioStreamStats);
|
||||||
|
|
||||||
|
ringBuffersIterator++;
|
||||||
|
}
|
||||||
|
numStreamStatsRemaining -= numStreamStatsToPack;
|
||||||
|
|
||||||
|
// send the current packet
|
||||||
|
nodeList->writeDatagram(packet, dataAt - packet, destinationNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AudioMixerClientData::getJitterBufferStatsString() const {
|
QString AudioMixerClientData::getJitterBufferStatsString() const {
|
||||||
|
|
|
@ -32,9 +32,9 @@ public:
|
||||||
void checkBuffersBeforeFrameSend(AABox* checkSourceZone = NULL, AABox* listenerZone = NULL);
|
void checkBuffersBeforeFrameSend(AABox* checkSourceZone = NULL, AABox* listenerZone = NULL);
|
||||||
void pushBuffersAfterFrameSend();
|
void pushBuffersAfterFrameSend();
|
||||||
|
|
||||||
void getAudioStreamStats(AudioStreamStats& stats) const;
|
void getAudioStreamStatsOfStream(const PositionalAudioRingBuffer* ringBuffer, AudioStreamStats& stats) const;
|
||||||
|
|
||||||
int encodeAudioStreamStatsPacket(char* packet) const;
|
void sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) const;
|
||||||
|
|
||||||
QString getJitterBufferStatsString() const;
|
QString getJitterBufferStatsString() const;
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
||||||
_scopeInput(0),
|
_scopeInput(0),
|
||||||
_scopeOutputLeft(0),
|
_scopeOutputLeft(0),
|
||||||
_scopeOutputRight(0),
|
_scopeOutputRight(0),
|
||||||
_audioMixerStreamStats(),
|
_audioMixerAvatarStreamStats(),
|
||||||
_outgoingAvatarAudioSequenceNumber(0)
|
_outgoingAvatarAudioSequenceNumber(0)
|
||||||
{
|
{
|
||||||
// clear the array of locally injected samples
|
// clear the array of locally injected samples
|
||||||
|
@ -720,9 +720,29 @@ void Audio::parseAudioStreamStatsPacket(const QByteArray& packet) {
|
||||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||||
const char* dataAt = packet.constData() + numBytesPacketHeader;
|
const char* dataAt = packet.constData() + numBytesPacketHeader;
|
||||||
|
|
||||||
// parse audio mixer jitter buffer stats
|
// parse the appendFlag, clear injected audio stream stats if 0
|
||||||
memcpy(&_audioMixerStreamStats, dataAt, sizeof(AudioStreamStats));
|
quint8 appendFlag = *(reinterpret_cast<const quint16*>(dataAt));
|
||||||
dataAt += sizeof(AudioStreamStats);
|
dataAt += sizeof(quint8);
|
||||||
|
if (!appendFlag) {
|
||||||
|
_audioMixerInjectedStreamStatsMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the number of stream stats structs to follow
|
||||||
|
quint16 numStreamStats = *(reinterpret_cast<const quint16*>(dataAt));
|
||||||
|
dataAt += sizeof(quint16);
|
||||||
|
|
||||||
|
// parse the stream stats
|
||||||
|
AudioStreamStats streamStats;
|
||||||
|
for (quint16 i = 0; i < numStreamStats; i++) {
|
||||||
|
memcpy(&streamStats, dataAt, sizeof(AudioStreamStats));
|
||||||
|
dataAt += sizeof(AudioStreamStats);
|
||||||
|
|
||||||
|
if (streamStats._streamType == PositionalAudioRingBuffer::Microphone) {
|
||||||
|
_audioMixerAvatarStreamStats = streamStats;
|
||||||
|
} else {
|
||||||
|
_audioMixerInjectedStreamStatsMap[streamStats._streamIdentifier] = streamStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: numSamples is the total number of single channel samples, since callers will always call this with stereo
|
// NOTE: numSamples is the total number of single channel samples, since callers will always call this with stereo
|
||||||
|
|
|
@ -104,7 +104,8 @@ public slots:
|
||||||
float getInputVolume() const { return (_audioInput) ? _audioInput->volume() : 0.0f; }
|
float getInputVolume() const { return (_audioInput) ? _audioInput->volume() : 0.0f; }
|
||||||
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); }
|
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); }
|
||||||
|
|
||||||
const AudioStreamStats& getAudioMixerStreamStats() const { return _audioMixerStreamStats; }
|
const AudioStreamStats& getAudioMixerAvatarStreamStats() const { return _audioMixerAvatarStreamStats; }
|
||||||
|
const QHash<QUuid, AudioStreamStats>& getAudioMixerInjectedStreamStatsMap() const { return _audioMixerInjectedStreamStatsMap; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
bool muteToggled();
|
bool muteToggled();
|
||||||
|
@ -237,7 +238,8 @@ private:
|
||||||
QByteArray* _scopeOutputLeft;
|
QByteArray* _scopeOutputLeft;
|
||||||
QByteArray* _scopeOutputRight;
|
QByteArray* _scopeOutputRight;
|
||||||
|
|
||||||
AudioStreamStats _audioMixerStreamStats;
|
AudioStreamStats _audioMixerAvatarStreamStats;
|
||||||
|
QHash<QUuid, AudioStreamStats> _audioMixerInjectedStreamStatsMap;
|
||||||
|
|
||||||
quint16 _outgoingAvatarAudioSequenceNumber;
|
quint16 _outgoingAvatarAudioSequenceNumber;
|
||||||
SequenceNumberStats _incomingMixedAudioSequenceNumberStats;
|
SequenceNumberStats _incomingMixedAudioSequenceNumberStats;
|
||||||
|
|
|
@ -286,11 +286,16 @@ void Stats::display(
|
||||||
pingVoxel = totalPingVoxel/voxelServerCount;
|
pingVoxel = totalPingVoxel/voxelServerCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = _expanded ? 6 : 3;
|
|
||||||
|
Audio* audio = Application::getInstance()->getAudio();
|
||||||
|
const AudioStreamStats& audioMixerAvatarStreamStats = audio->getAudioMixerAvatarStreamStats();
|
||||||
|
const QHash<QUuid, AudioStreamStats>& audioMixerInjectedStreamStatsMap = audio->getAudioMixerInjectedStreamStatsMap();
|
||||||
|
|
||||||
|
lines = _expanded ? 7 + audioMixerInjectedStreamStatsMap.size(): 3;
|
||||||
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||||
horizontalOffset += 5;
|
horizontalOffset += 5;
|
||||||
|
|
||||||
Audio* audio = Application::getInstance()->getAudio();
|
|
||||||
|
|
||||||
char audioJitter[30];
|
char audioJitter[30];
|
||||||
sprintf(audioJitter,
|
sprintf(audioJitter,
|
||||||
|
@ -322,21 +327,29 @@ void Stats::display(
|
||||||
verticalOffset += STATS_PELS_PER_LINE;
|
verticalOffset += STATS_PELS_PER_LINE;
|
||||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color);
|
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color);
|
||||||
|
|
||||||
|
char* audioMixerJitterBuffersStatsLabel = "AudioMixer stream stats:";
|
||||||
static const float MSECS_PER_FRAME = (float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * (float)MSECS_PER_SECOND / (float)SAMPLE_RATE;
|
char* audioMixerJitterBuffersStatsLabel2 = "early/late/lost, jframes";
|
||||||
|
|
||||||
const AudioStreamStats& audioMixerStreamStats =
|
|
||||||
Application::getInstance()->getAudio()->getAudioMixerStreamStats();
|
|
||||||
|
|
||||||
char* audioMixerJitterBuffersStatsLabel = "AudioMixer j-buffers msecs:";
|
|
||||||
char audioMixerJitterBuffersStats[30];
|
|
||||||
sprintf(audioMixerJitterBuffersStats, "mic/max/avg: %.1f / %.1f / %.1f", audioMixerStreamStats._avatarJitterBufferFrames * MSECS_PER_FRAME,
|
|
||||||
audioMixerStreamStats._maxJitterBufferFrames * MSECS_PER_FRAME, audioMixerStreamStats._avgJitterBufferFrames * MSECS_PER_FRAME);
|
|
||||||
|
|
||||||
verticalOffset += STATS_PELS_PER_LINE;
|
verticalOffset += STATS_PELS_PER_LINE;
|
||||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerJitterBuffersStatsLabel, color);
|
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerJitterBuffersStatsLabel, color);
|
||||||
|
verticalOffset += STATS_PELS_PER_LINE;
|
||||||
|
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerJitterBuffersStatsLabel2, color);
|
||||||
|
|
||||||
|
char audioMixerJitterBuffersStats[30];
|
||||||
|
sprintf(audioMixerJitterBuffersStats, "mic: %d/%d/%d, %d", audioMixerAvatarStreamStats._packetsEarly,
|
||||||
|
audioMixerAvatarStreamStats._packetsLate, audioMixerAvatarStreamStats._packetsLost,
|
||||||
|
audioMixerAvatarStreamStats._jitterBufferFrames);
|
||||||
|
|
||||||
verticalOffset += STATS_PELS_PER_LINE;
|
verticalOffset += STATS_PELS_PER_LINE;
|
||||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerJitterBuffersStats, color);
|
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerJitterBuffersStats, color);
|
||||||
|
|
||||||
|
foreach(AudioStreamStats injectedStreamStats, audioMixerInjectedStreamStatsMap) {
|
||||||
|
sprintf(audioMixerJitterBuffersStats, "inj: %d/%d/%d, %d", injectedStreamStats._packetsEarly,
|
||||||
|
injectedStreamStats._packetsLate, injectedStreamStats._packetsLost, injectedStreamStats._jitterBufferFrames);
|
||||||
|
|
||||||
|
verticalOffset += STATS_PELS_PER_LINE;
|
||||||
|
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerJitterBuffersStats, color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verticalOffset = 0;
|
verticalOffset = 0;
|
||||||
|
|
|
@ -12,15 +12,35 @@
|
||||||
#ifndef hifi_AudioStreamStats_h
|
#ifndef hifi_AudioStreamStats_h
|
||||||
#define hifi_AudioStreamStats_h
|
#define hifi_AudioStreamStats_h
|
||||||
|
|
||||||
|
#include "PositionalAudioRingBuffer.h"
|
||||||
|
|
||||||
class AudioStreamStats {
|
class AudioStreamStats {
|
||||||
public:
|
public:
|
||||||
AudioStreamStats()
|
AudioStreamStats()
|
||||||
: _avatarJitterBufferFrames(0), _maxJitterBufferFrames(0), _avgJitterBufferFrames(0)
|
: _streamType(PositionalAudioRingBuffer::Microphone),
|
||||||
|
_streamIdentifier(),
|
||||||
|
_jitterBufferFrames(0),
|
||||||
|
_packetsReceived(0),
|
||||||
|
_packetsUnreasonable(0),
|
||||||
|
_packetsEarly(0),
|
||||||
|
_packetsLate(0),
|
||||||
|
_packetsLost(0),
|
||||||
|
_packetsRecovered(0),
|
||||||
|
_packetsDuplicate(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
quint16 _avatarJitterBufferFrames;
|
PositionalAudioRingBuffer::Type _streamType;
|
||||||
quint16 _maxJitterBufferFrames;
|
QUuid _streamIdentifier;
|
||||||
float _avgJitterBufferFrames;
|
|
||||||
|
quint16 _jitterBufferFrames;
|
||||||
|
|
||||||
|
quint32 _packetsReceived;
|
||||||
|
quint32 _packetsUnreasonable;
|
||||||
|
quint32 _packetsEarly;
|
||||||
|
quint32 _packetsLate;
|
||||||
|
quint32 _packetsLost;
|
||||||
|
quint32 _packetsRecovered;
|
||||||
|
quint32 _packetsDuplicate;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AudioStreamStats_h
|
#endif // hifi_AudioStreamStats_h
|
||||||
|
|
Loading…
Reference in a new issue