mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 01:04:06 +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;
|
||||
|
||||
char audioStreamStatsPacket[MAX_PACKET_SIZE];
|
||||
bool sendAudioStreamStats = false;
|
||||
quint64 now = usecTimestampNow();
|
||||
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
|
||||
if (sendAudioStreamStats) {
|
||||
int numBytesAudioStreamStatsPacket = nodeData->encodeAudioStreamStatsPacket(audioStreamStatsPacket);
|
||||
nodeList->writeDatagram(audioStreamStatsPacket, numBytesAudioStreamStatsPacket, node);
|
||||
nodeData->sendAudioStreamStatsPackets(node);
|
||||
}
|
||||
|
||||
++_sumListeners;
|
||||
|
|
|
@ -156,41 +156,76 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixerClientData::getAudioStreamStats(AudioStreamStats& stats) const {
|
||||
int avatarJitterBufferFrames = 0;
|
||||
int maxJitterBufferFrames = 0;
|
||||
int sumJitterBufferFrames = 0;
|
||||
void AudioMixerClientData::getAudioStreamStatsOfStream(const PositionalAudioRingBuffer* ringBuffer, AudioStreamStats& stats) const {
|
||||
const SequenceNumberStats* streamSequenceNumberStats = &_incomingAvatarAudioSequenceNumberStats;
|
||||
|
||||
for (int i = 0; i < _ringBuffers.size(); i++) {
|
||||
|
||||
int bufferJitterFrames = _ringBuffers[i]->getCurrentJitterBufferFrames();
|
||||
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Microphone) {
|
||||
avatarJitterBufferFrames = bufferJitterFrames;
|
||||
}
|
||||
|
||||
if (bufferJitterFrames > maxJitterBufferFrames) {
|
||||
maxJitterBufferFrames = bufferJitterFrames;
|
||||
}
|
||||
|
||||
sumJitterBufferFrames += bufferJitterFrames;
|
||||
stats._streamType = ringBuffer->getType();
|
||||
if (stats._streamType == PositionalAudioRingBuffer::Injector) {
|
||||
stats._streamIdentifier = ((InjectedAudioRingBuffer*)ringBuffer)->getStreamIdentifier();
|
||||
streamSequenceNumberStats = &_incomingInjectedAudioSequenceNumberStatsMap.value(stats._streamIdentifier);
|
||||
}
|
||||
|
||||
stats._avatarJitterBufferFrames = avatarJitterBufferFrames;
|
||||
stats._maxJitterBufferFrames = maxJitterBufferFrames;
|
||||
stats._avgJitterBufferFrames = (float)sumJitterBufferFrames / (float)_ringBuffers.size();
|
||||
stats._jitterBufferFrames = ringBuffer->getCurrentJitterBufferFrames();
|
||||
|
||||
stats._packetsReceived = streamSequenceNumberStats->getNumReceived();
|
||||
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);
|
||||
char* dataAt = packet + numBytesPacketHeader;
|
||||
char* headerEndAt = packet + numBytesPacketHeader;
|
||||
|
||||
// pack jitter buffer stats
|
||||
AudioStreamStats stats;
|
||||
getAudioStreamStats(stats);
|
||||
memcpy(dataAt, &stats, sizeof(AudioStreamStats));
|
||||
dataAt += sizeof(AudioStreamStats);
|
||||
// calculate how many stream stat structs we can fit in each packet
|
||||
const int numStreamStatsRoomFor = (MAX_PACKET_SIZE - numBytesPacketHeader - sizeof(quint8) - sizeof(quint16)) / 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 {
|
||||
|
|
|
@ -32,9 +32,9 @@ public:
|
|||
void checkBuffersBeforeFrameSend(AABox* checkSourceZone = NULL, AABox* listenerZone = NULL);
|
||||
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;
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
|||
_scopeInput(0),
|
||||
_scopeOutputLeft(0),
|
||||
_scopeOutputRight(0),
|
||||
_audioMixerStreamStats(),
|
||||
_audioMixerAvatarStreamStats(),
|
||||
_outgoingAvatarAudioSequenceNumber(0)
|
||||
{
|
||||
// clear the array of locally injected samples
|
||||
|
@ -720,9 +720,29 @@ void Audio::parseAudioStreamStatsPacket(const QByteArray& packet) {
|
|||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||
const char* dataAt = packet.constData() + numBytesPacketHeader;
|
||||
|
||||
// parse audio mixer jitter buffer stats
|
||||
memcpy(&_audioMixerStreamStats, dataAt, sizeof(AudioStreamStats));
|
||||
dataAt += sizeof(AudioStreamStats);
|
||||
// parse the appendFlag, clear injected audio stream stats if 0
|
||||
quint8 appendFlag = *(reinterpret_cast<const quint16*>(dataAt));
|
||||
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
|
||||
|
|
|
@ -104,7 +104,8 @@ public slots:
|
|||
float getInputVolume() const { return (_audioInput) ? _audioInput->volume() : 0.0f; }
|
||||
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:
|
||||
bool muteToggled();
|
||||
|
@ -237,7 +238,8 @@ private:
|
|||
QByteArray* _scopeOutputLeft;
|
||||
QByteArray* _scopeOutputRight;
|
||||
|
||||
AudioStreamStats _audioMixerStreamStats;
|
||||
AudioStreamStats _audioMixerAvatarStreamStats;
|
||||
QHash<QUuid, AudioStreamStats> _audioMixerInjectedStreamStatsMap;
|
||||
|
||||
quint16 _outgoingAvatarAudioSequenceNumber;
|
||||
SequenceNumberStats _incomingMixedAudioSequenceNumberStats;
|
||||
|
|
|
@ -286,11 +286,16 @@ void Stats::display(
|
|||
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);
|
||||
horizontalOffset += 5;
|
||||
|
||||
Audio* audio = Application::getInstance()->getAudio();
|
||||
|
||||
|
||||
char audioJitter[30];
|
||||
sprintf(audioJitter,
|
||||
|
@ -322,21 +327,29 @@ void Stats::display(
|
|||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color);
|
||||
|
||||
|
||||
static const float MSECS_PER_FRAME = (float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * (float)MSECS_PER_SECOND / (float)SAMPLE_RATE;
|
||||
|
||||
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);
|
||||
|
||||
char* audioMixerJitterBuffersStatsLabel = "AudioMixer stream stats:";
|
||||
char* audioMixerJitterBuffersStatsLabel2 = "early/late/lost, jframes";
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
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;
|
||||
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;
|
||||
|
|
|
@ -12,15 +12,35 @@
|
|||
#ifndef hifi_AudioStreamStats_h
|
||||
#define hifi_AudioStreamStats_h
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
class AudioStreamStats {
|
||||
public:
|
||||
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;
|
||||
quint16 _maxJitterBufferFrames;
|
||||
float _avgJitterBufferFrames;
|
||||
PositionalAudioRingBuffer::Type _streamType;
|
||||
QUuid _streamIdentifier;
|
||||
|
||||
quint16 _jitterBufferFrames;
|
||||
|
||||
quint32 _packetsReceived;
|
||||
quint32 _packetsUnreasonable;
|
||||
quint32 _packetsEarly;
|
||||
quint32 _packetsLate;
|
||||
quint32 _packetsLost;
|
||||
quint32 _packetsRecovered;
|
||||
quint32 _packetsDuplicate;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioStreamStats_h
|
||||
|
|
Loading…
Reference in a new issue