added seq number to all Audio types, untested

This commit is contained in:
wangyix 2014-06-26 16:52:23 -07:00
parent 97139b0bd0
commit cb48825561
14 changed files with 117 additions and 29 deletions

View file

@ -147,6 +147,8 @@ void Agent::readPendingDatagrams() {
} }
} else if (datagramPacketType == PacketTypeMixedAudio) { } else if (datagramPacketType == PacketTypeMixedAudio) {
// TODO: track sequence numbers for mixed audio???
// parse the data and grab the average loudness // parse the data and grab the average loudness
_receivedAudioBuffer.parseData(receivedPacket); _receivedAudioBuffer.parseData(receivedPacket);

View file

@ -519,7 +519,7 @@ void AudioMixer::run() {
QElapsedTimer timer; QElapsedTimer timer;
timer.start(); timer.start();
char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO + sizeof(quint16)
+ numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)]; + numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)];
int usecToSleep = BUFFER_SEND_INTERVAL_USECS; int usecToSleep = BUFFER_SEND_INTERVAL_USECS;
@ -602,18 +602,32 @@ void AudioMixer::run() {
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData()
&& ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) { && ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) {
AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData();
prepareMixForListeningNode(node.data()); prepareMixForListeningNode(node.data());
// pack header
int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio);
char* dataAt = clientMixBuffer + numBytesPacketHeader;
memcpy(clientMixBuffer + numBytesPacketHeader, _clientSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO); // pack sequence number
nodeList->writeDatagram(clientMixBuffer, NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader, node); quint16 sequence = nodeData->getOutgoingSequenceNumber();
memcpy(dataAt, &sequence, sizeof(quint16));
dataAt += sizeof(quint16);
// pack mixed audio samples
memcpy(dataAt, _clientSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO);
dataAt += NETWORK_BUFFER_LENGTH_BYTES_STEREO;
// send mixed audio packet
nodeList->writeDatagram(clientMixBuffer, dataAt - clientMixBuffer, node);
nodeData->incrementOutgoingSequenceNumber();
// 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 numBytesWritten = ((AudioMixerClientData*)node->getLinkedData()) int numBytesAudioStreamStatsPacket = nodeData->encodeAudioStreamStatsPacket(audioStreamStatsPacket);
->encodeAudioStreamStatsPacket(audioStreamStatsPacket); nodeList->writeDatagram(audioStreamStatsPacket, numBytesAudioStreamStatsPacket, node);
nodeList->writeDatagram(audioStreamStatsPacket, numBytesWritten, node);
} }
++_sumListeners; ++_sumListeners;

View file

@ -20,7 +20,9 @@
#include "AudioMixerClientData.h" #include "AudioMixerClientData.h"
AudioMixerClientData::AudioMixerClientData() : AudioMixerClientData::AudioMixerClientData() :
_ringBuffers() _ringBuffers(),
_outgoingSequenceNumber(0),
_incomingAvatarSequenceNumberStats()
{ {
} }
@ -49,13 +51,14 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
int numBytesPacketHeader = numBytesForPacketHeader(packet); int numBytesPacketHeader = numBytesForPacketHeader(packet);
const char* sequenceAt = packet.constData() + numBytesPacketHeader; const char* sequenceAt = packet.constData() + numBytesPacketHeader;
quint16 sequence = *(reinterpret_cast<const quint16*>(sequenceAt)); quint16 sequence = *(reinterpret_cast<const quint16*>(sequenceAt));
_sequenceNumberStats.sequenceNumberReceived(sequence);
PacketType packetType = packetTypeForPacket(packet); PacketType packetType = packetTypeForPacket(packet);
if (packetType == PacketTypeMicrophoneAudioWithEcho if (packetType == PacketTypeMicrophoneAudioWithEcho
|| packetType == PacketTypeMicrophoneAudioNoEcho || packetType == PacketTypeMicrophoneAudioNoEcho
|| packetType == PacketTypeSilentAudioFrame) { || packetType == PacketTypeSilentAudioFrame) {
_incomingAvatarSequenceNumberStats.sequenceNumberReceived(sequence);
// grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist) // grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist)
AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer();
@ -85,6 +88,8 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
// grab the stream identifier for this injected audio // grab the stream identifier for this injected audio
QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet), NUM_BYTES_RFC4122_UUID)); QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet), NUM_BYTES_RFC4122_UUID));
_incomingInjectedSequenceNumberStatsMap[streamIdentifier].sequenceNumberReceived(sequence);
InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL;
for (int i = 0; i < _ringBuffers.size(); i++) { for (int i = 0; i < _ringBuffers.size(); i++) {
@ -140,6 +145,9 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() {
} else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector
&& audioBuffer->hasStarted() && audioBuffer->isStarved()) { && audioBuffer->hasStarted() && audioBuffer->isStarved()) {
// this is an empty audio buffer that has starved, safe to delete // this is an empty audio buffer that has starved, safe to delete
// also delete its sequence number stats
QUuid streamIdentifier = ((InjectedAudioRingBuffer*)audioBuffer)->getStreamIdentifier();
_incomingInjectedSequenceNumberStatsMap.remove(streamIdentifier);
delete audioBuffer; delete audioBuffer;
i = _ringBuffers.erase(i); i = _ringBuffers.erase(i);
continue; continue;

View file

@ -38,11 +38,17 @@ public:
QString getJitterBufferStatsString() const; QString getJitterBufferStatsString() const;
const SequenceNumberStats& getSequenceNumberStats() const { return _sequenceNumberStats; } void incrementOutgoingSequenceNumber() { _outgoingSequenceNumber++; }
quint16 getOutgoingSequenceNumber() const { return _outgoingSequenceNumber; }
//const SequenceNumberStats& getIncomingSequenceNumberStats() const { return _incomingSequenceNumberStats; }
private: private:
QList<PositionalAudioRingBuffer*> _ringBuffers; QList<PositionalAudioRingBuffer*> _ringBuffers;
SequenceNumberStats _sequenceNumberStats;
quint16 _outgoingSequenceNumber;
SequenceNumberStats _incomingAvatarSequenceNumberStats;
QHash<QUuid, SequenceNumberStats> _incomingInjectedSequenceNumberStatsMap;
}; };
#endif // hifi_AudioMixerClientData_h #endif // hifi_AudioMixerClientData_h

View file

@ -3608,6 +3608,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
// when the application is about to quit, stop our script engine so it unwinds properly // when the application is about to quit, stop our script engine so it unwinds properly
connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop()));
NodeList* nodeList = NodeList::getInstance();
connect(nodeList, &NodeList::nodeKilled, scriptEngine, &ScriptEngine::nodeKilled);
scriptEngine->moveToThread(workerThread); scriptEngine->moveToThread(workerThread);
// Starts an event loop, and emits workerThread->started() // Starts an event loop, and emits workerThread->started()

View file

@ -104,7 +104,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
_scopeOutputLeft(0), _scopeOutputLeft(0),
_scopeOutputRight(0), _scopeOutputRight(0),
_audioMixerJitterBufferStats(), _audioMixerJitterBufferStats(),
_sequenceNumber(0) _outgoingSequenceNumber(0)
{ {
// clear the array of locally injected samples // clear the array of locally injected samples
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
@ -120,7 +120,7 @@ void Audio::init(QGLWidget *parent) {
void Audio::reset() { void Audio::reset() {
_ringBuffer.reset(); _ringBuffer.reset();
_sequenceNumber = 0; _outgoingSequenceNumber = 0;
} }
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
@ -656,8 +656,8 @@ void Audio::handleAudioInput() {
char* currentPacketPtr = audioDataPacket + populatePacketHeader(audioDataPacket, packetType); char* currentPacketPtr = audioDataPacket + populatePacketHeader(audioDataPacket, packetType);
// pack seq number // pack sequence number
memcpy(currentPacketPtr, &_sequenceNumber, sizeof(quint16)); memcpy(currentPacketPtr, &_outgoingSequenceNumber, sizeof(quint16));
currentPacketPtr += sizeof(quint16); currentPacketPtr += sizeof(quint16);
// set the mono/stereo byte // set the mono/stereo byte
@ -672,13 +672,13 @@ void Audio::handleAudioInput() {
currentPacketPtr += sizeof(headOrientation); currentPacketPtr += sizeof(headOrientation);
nodeList->writeDatagram(audioDataPacket, numAudioBytes + leadingBytes, audioMixer); nodeList->writeDatagram(audioDataPacket, numAudioBytes + leadingBytes, audioMixer);
_sequenceNumber++; _outgoingSequenceNumber++;
Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO) Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO)
.updateValue(numAudioBytes + leadingBytes); .updateValue(numAudioBytes + leadingBytes);
} else { } else {
// reset seq numbers if there's no connection with an audiomixer // reset seq numbers if there's no connection with an audiomixer
_sequenceNumber = 0; _outgoingSequenceNumber = 0;
} }
delete[] inputAudioSamples; delete[] inputAudioSamples;
} }
@ -827,6 +827,14 @@ void Audio::toggleStereoInput() {
} }
void Audio::processReceivedAudio(const QByteArray& audioByteArray) { void Audio::processReceivedAudio(const QByteArray& audioByteArray) {
// parse sequence number for this packet
int numBytesPacketHeader = numBytesForPacketHeader(audioByteArray);
const char* sequenceAt = audioByteArray.constData() + numBytesPacketHeader;
quint16 sequence = *((quint16*)sequenceAt);
_incomingSequenceNumberStats.sequenceNumberReceived(sequence);
// parse audio data
_ringBuffer.parseData(audioByteArray); _ringBuffer.parseData(audioByteArray);
float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate()) float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate())

View file

@ -239,7 +239,8 @@ private:
AudioMixerJitterBuffersStats _audioMixerJitterBufferStats; AudioMixerJitterBuffersStats _audioMixerJitterBufferStats;
quint16 _sequenceNumber; quint16 _outgoingSequenceNumber;
SequenceNumberStats _incomingSequenceNumberStats;
}; };

View file

@ -61,6 +61,11 @@ void AudioInjector::injectAudio() {
QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio); QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio);
QDataStream packetStream(&injectAudioPacket, QIODevice::Append); QDataStream packetStream(&injectAudioPacket, QIODevice::Append);
// skip sequence number for now
int numPreSequenceNumberBytes = injectAudioPacket.size();
packetStream.skipRawData(sizeof(quint16));
// pack stream identifier (a generated UUID)
packetStream << QUuid::createUuid(); packetStream << QUuid::createUuid();
// pack the flag for loopback // pack the flag for loopback
@ -91,6 +96,7 @@ void AudioInjector::injectAudio() {
bool shouldLoop = _options.getLoop(); bool shouldLoop = _options.getLoop();
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
quint16 outgoingSequenceNumber = 0;
while (currentSendPosition < soundByteArray.size() && !_shouldStop) { while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
@ -98,6 +104,9 @@ void AudioInjector::injectAudio() {
// resize the QByteArray to the right size // resize the QByteArray to the right size
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
// pack the sequence number
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingSequenceNumber, sizeof(quint16));
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy); memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy);
@ -107,6 +116,7 @@ void AudioInjector::injectAudio() {
// send off this audio packet // send off this audio packet
nodeList->writeDatagram(injectAudioPacket, audioMixer); nodeList->writeDatagram(injectAudioPacket, audioMixer);
outgoingSequenceNumber++;
currentSendPosition += bytesToCopy; currentSendPosition += bytesToCopy;

View file

@ -66,6 +66,7 @@ void AudioRingBuffer::resizeForFrameSize(qint64 numFrameSamples) {
} }
int AudioRingBuffer::parseData(const QByteArray& packet) { int AudioRingBuffer::parseData(const QByteArray& packet) {
// skip packet header and sequence number
int numBytesBeforeAudioData = numBytesForPacketHeader(packet) + sizeof(quint16); int numBytesBeforeAudioData = numBytesForPacketHeader(packet) + sizeof(quint16);
return writeData(packet.data() + numBytesBeforeAudioData, packet.size() - numBytesBeforeAudioData); return writeData(packet.data() + numBytesBeforeAudioData, packet.size() - numBytesBeforeAudioData);
} }

View file

@ -107,6 +107,9 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) {
// skip the packet header (includes the source UUID) // skip the packet header (includes the source UUID)
int readBytes = numBytesForPacketHeader(packet); int readBytes = numBytesForPacketHeader(packet);
// skip the sequence number
readBytes += sizeof(quint16);
// hop over the channel flag that has already been read in AudioMixerClientData // hop over the channel flag that has already been read in AudioMixerClientData
readBytes += sizeof(quint8); readBytes += sizeof(quint8);

View file

@ -50,6 +50,8 @@ PacketVersion versionForPacketType(PacketType type) {
case PacketTypeMicrophoneAudioNoEcho: case PacketTypeMicrophoneAudioNoEcho:
case PacketTypeMicrophoneAudioWithEcho: case PacketTypeMicrophoneAudioWithEcho:
case PacketTypeSilentAudioFrame: case PacketTypeSilentAudioFrame:
return 2;
case PacketTypeMixedAudio:
return 1; return 1;
case PacketTypeAvatarData: case PacketTypeAvatarData:
return 3; return 3;

View file

@ -59,7 +59,7 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, const bool wa
} }
} else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) { } else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) {
// ignore packet if gap is unreasonable // ignore packet if gap is unreasonable
qDebug() << "ignoring unreasonable packet... sequence:" << incoming qDebug() << "ignoring unreasonable sequence number:" << incoming
<< "previous:" << _lastReceived; << "previous:" << _lastReceived;
_numUnreasonable++; _numUnreasonable++;
return; return;

View file

@ -459,32 +459,55 @@ void ScriptEngine::run() {
} }
} }
QByteArray audioPacket = byteArrayWithPopulatedHeader(silentFrame char audioPacket[MAX_PACKET_SIZE];
? PacketTypeSilentAudioFrame
: PacketTypeMicrophoneAudioNoEcho);
QDataStream packetStream(&audioPacket, QIODevice::Append); // pack header
int numBytesPacketHeader = populatePacketHeader(audioPacket, silentFrame
? PacketTypeSilentAudioFrame
: PacketTypeMicrophoneAudioNoEcho);
char* dataAt = audioPacket + numBytesPacketHeader;
// skip over sequence number for now; will be packed when destination node is known
char* sequenceAt = dataAt;
dataAt += sizeof(quint16);
// use the orientation and position of this avatar for the source of this audio // use the orientation and position of this avatar for the source of this audio
packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3)); memcpy(dataAt, &_avatarData->getPosition(), sizeof(glm::vec3));
dataAt += sizeof(glm::vec3);
glm::quat headOrientation = _avatarData->getHeadOrientation(); glm::quat headOrientation = _avatarData->getHeadOrientation();
packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat)); memcpy(dataAt, &headOrientation, sizeof(glm::quat));
dataAt += sizeof(glm::quat);
if (silentFrame) { if (silentFrame) {
if (!_isListeningToAudioStream) { if (!_isListeningToAudioStream) {
// if we have a silent frame and we're not listening then just send nothing and break out of here // if we have a silent frame and we're not listening then just send nothing and break out of here
break; break;
} }
// write the number of silent samples so the audio-mixer can uphold timing // write the number of silent samples so the audio-mixer can uphold timing
packetStream.writeRawData(reinterpret_cast<const char*>(&SCRIPT_AUDIO_BUFFER_SAMPLES), sizeof(int16_t)); memcpy(dataAt, &SCRIPT_AUDIO_BUFFER_SAMPLES, sizeof(int16_t));
dataAt += sizeof(int16_t);
} else if (nextSoundOutput) { } else if (nextSoundOutput) {
// write the raw audio data // write the raw audio data
packetStream.writeRawData(reinterpret_cast<const char*>(nextSoundOutput), int numAvailableBytes = numAvailableSamples * sizeof(int16_t);
numAvailableSamples * sizeof(int16_t)); memcpy(dataAt, nextSoundOutput, numAvailableBytes);
dataAt += numAvailableBytes;
} }
nodeList->broadcastToNodes(audioPacket, NodeSet() << NodeType::AudioMixer); // write audio packet to AudioMixer nodes
int audioPacketSize = dataAt - audioPacket;
NodeList* nodeList = NodeList::getInstance();
foreach(const SharedNodePointer& node, nodeList->getNodeHash()) {
// only send to nodes of type AudioMixer
if (node->getType() == NodeType::AudioMixer) {
// pack sequence number
quint16 sequence = _outgoingSequenceNumbers[node->getUUID()]++;
memcpy(sequenceAt, &sequence, sizeof(quint16));
// send audio packet
nodeList->writeDatagram(audioPacket, audioPacketSize, node);
}
}
} }
} }
@ -658,3 +681,7 @@ void ScriptEngine::include(const QString& includeFile) {
emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString());
} }
} }
void ScriptEngine::nodeKilled(SharedNodePointer node) {
_outgoingSequenceNumbers.remove(node->getUUID());
}

View file

@ -100,6 +100,8 @@ public slots:
void include(const QString& includeFile); void include(const QString& includeFile);
void print(const QString& message); void print(const QString& message);
void nodeKilled(SharedNodePointer node);
signals: signals:
void update(float deltaTime); void update(float deltaTime);
void scriptEnding(); void scriptEnding();
@ -146,6 +148,7 @@ private:
ScriptUUID _uuidLibrary; ScriptUUID _uuidLibrary;
AnimationCache _animationCache; AnimationCache _animationCache;
QHash<QUuid, quint16> _outgoingSequenceNumbers;
}; };
#endif // hifi_ScriptEngine_h #endif // hifi_ScriptEngine_h