modified AudioStreamStats to per-stream with seq stats

added AudioStreamStats info to interface overlay stats
This commit is contained in:
wangyix 2014-06-30 12:31:58 -07:00
parent de3c1ebf0f
commit d2f86278b2
7 changed files with 144 additions and 56 deletions

View file

@ -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;

View file

@ -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 {

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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