mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 07:44:11 +02:00
Merge branch 'atp' of https://github.com/birarda/hifi into protocol
This commit is contained in:
commit
8e7580d58d
12 changed files with 261 additions and 340 deletions
|
@ -195,8 +195,8 @@ void AssignmentClientMonitor::checkSpares() {
|
||||||
SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
|
SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
|
||||||
childNode->activateLocalSocket();
|
childNode->activateLocalSocket();
|
||||||
|
|
||||||
QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
auto diePacket { NLPacket::create(PacketType::StopNode); }
|
||||||
nodeList->writeUnverifiedDatagram(diePacket, childNode);
|
nodeList->sendPacket(diePacket, childNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,8 +229,9 @@ void AssignmentClientMonitor::readPendingDatagrams() {
|
||||||
} else {
|
} else {
|
||||||
// tell unknown assignment-client child to exit.
|
// tell unknown assignment-client child to exit.
|
||||||
qDebug() << "asking unknown child to exit.";
|
qDebug() << "asking unknown child to exit.";
|
||||||
QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
|
||||||
nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr);
|
auto diePacket { NL::create(PacketType::StopNode); }
|
||||||
|
nodeList->sendPacket(diePacket, childNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -509,24 +509,20 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
||||||
|
|
||||||
if (sendData) {
|
if (sendData) {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
int numBytesEnvPacketHeader = nodeList->populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment);
|
auto envPacket = NLPacket::create(PacketType::AudioEnvironment);
|
||||||
char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader;
|
|
||||||
|
|
||||||
unsigned char bitset = 0;
|
unsigned char bitset = 0;
|
||||||
if (hasReverb) {
|
if (hasReverb) {
|
||||||
setAtBit(bitset, HAS_REVERB_BIT);
|
setAtBit(bitset, HAS_REVERB_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(envDataAt, &bitset, sizeof(unsigned char));
|
envPacket.write(&bitset, sizeof(unsigned char));
|
||||||
envDataAt += sizeof(unsigned char);
|
|
||||||
|
|
||||||
if (hasReverb) {
|
if (hasReverb) {
|
||||||
memcpy(envDataAt, &reverbTime, sizeof(float));
|
envPacket.write(&reverbTime, sizeof(float));
|
||||||
envDataAt += sizeof(float);
|
envPacket.write(&wetLevel, sizeof(float));
|
||||||
memcpy(envDataAt, &wetLevel, sizeof(float));
|
|
||||||
envDataAt += sizeof(float);
|
|
||||||
}
|
}
|
||||||
nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node);
|
nodeList->sendPacket(envPacket, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,8 +708,6 @@ void AudioMixer::run() {
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
timer.start();
|
timer.start();
|
||||||
|
|
||||||
char clientMixBuffer[MAX_PACKET_SIZE];
|
|
||||||
|
|
||||||
int usecToSleep = AudioConstants::NETWORK_FRAME_USECS;
|
int usecToSleep = AudioConstants::NETWORK_FRAME_USECS;
|
||||||
|
|
||||||
const int TRAILING_AVERAGE_FRAMES = 100;
|
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||||
|
@ -791,8 +785,8 @@ void AudioMixer::run() {
|
||||||
// if the stream should be muted, send mute packet
|
// if the stream should be muted, send mute packet
|
||||||
if (nodeData->getAvatarAudioStream()
|
if (nodeData->getAvatarAudioStream()
|
||||||
&& shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) {
|
&& shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) {
|
||||||
QByteArray packet = nodeList->byteArrayWithPopulatedHeader(PacketTypeNoisyMute);
|
auto mutePacket { NLPacket::create(PacketType::NoisyMute); }
|
||||||
nodeList->writeDatagram(packet, node);
|
nodeList->sendPacket(mutePacket, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->getType() == NodeType::Agent && node->getActiveSocket()
|
if (node->getType() == NodeType::Agent && node->getActiveSocket()
|
||||||
|
@ -800,41 +794,36 @@ void AudioMixer::run() {
|
||||||
|
|
||||||
int streamsMixed = prepareMixForListeningNode(node.data());
|
int streamsMixed = prepareMixForListeningNode(node.data());
|
||||||
|
|
||||||
char* mixDataAt;
|
std::unique_ptr<NLPacket> mixPacket;
|
||||||
|
|
||||||
if (streamsMixed > 0) {
|
if (streamsMixed > 0) {
|
||||||
// pack header
|
int mixPacketBytes = sizeof(quint16) + AudioConstants::NETWORK_FRAME_BYTES_STEREO;
|
||||||
int numBytesMixPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio);
|
mixPacket = NLPacket::create(PacketType::MixedAudio);
|
||||||
mixDataAt = clientMixBuffer + numBytesMixPacketHeader;
|
|
||||||
|
|
||||||
// pack sequence number
|
// pack sequence number
|
||||||
quint16 sequence = nodeData->getOutgoingSequenceNumber();
|
quint16 sequence = nodeData->getOutgoingSequenceNumber();
|
||||||
memcpy(mixDataAt, &sequence, sizeof(quint16));
|
mixPacket.write(&sequence, sizeof(quint16));
|
||||||
mixDataAt += sizeof(quint16);
|
|
||||||
|
|
||||||
// pack mixed audio samples
|
// pack mixed audio samples
|
||||||
memcpy(mixDataAt, _mixSamples, AudioConstants::NETWORK_FRAME_BYTES_STEREO);
|
mixPacket.write(mixSamples, AudioConstants::NETWORK_FRAME_BYTES_STEREO);
|
||||||
mixDataAt += AudioConstants::NETWORK_FRAME_BYTES_STEREO;
|
|
||||||
} else {
|
} else {
|
||||||
// pack header
|
int silentPacketBytes = sizeof(quint16) + sizeof(quint16);
|
||||||
int numBytesPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame);
|
mixPacket = NLPacket::create(PacketType::SilentAudioFrame);
|
||||||
mixDataAt = clientMixBuffer + numBytesPacketHeader;
|
|
||||||
|
|
||||||
// pack sequence number
|
// pack sequence number
|
||||||
quint16 sequence = nodeData->getOutgoingSequenceNumber();
|
quint16 sequence = nodeData->getOutgoingSequenceNumber();
|
||||||
memcpy(mixDataAt, &sequence, sizeof(quint16));
|
mixPacket.write(&sequence, sizeof(quint16));
|
||||||
mixDataAt += sizeof(quint16);
|
|
||||||
|
|
||||||
// pack number of silent audio samples
|
// pack number of silent audio samples
|
||||||
quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
|
quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
|
||||||
memcpy(mixDataAt, &numSilentSamples, sizeof(quint16));
|
mixPacket.write(&numSilentSamples, sizeof(quint16));
|
||||||
mixDataAt += sizeof(quint16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send audio environment
|
// Send audio environment
|
||||||
sendAudioEnvironmentPacket(node);
|
sendAudioEnvironmentPacket(node);
|
||||||
|
|
||||||
// send mixed audio packet
|
// send mixed audio packet
|
||||||
nodeList->writeDatagram(clientMixBuffer, mixDataAt - clientMixBuffer, node);
|
nodeList->sendPacket(mixPacket, node);
|
||||||
nodeData->incrementOutgoingMixedAudioSequenceNumber();
|
nodeData->incrementOutgoingMixedAudioSequenceNumber();
|
||||||
|
|
||||||
// send an audio stream stats packet if it's time
|
// send an audio stream stats packet if it's time
|
||||||
|
|
|
@ -111,7 +111,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
|
||||||
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
|
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
|
||||||
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
|
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
|
||||||
PositionalAudioStream* stream = i.value();
|
PositionalAudioStream* stream = i.value();
|
||||||
|
|
||||||
if (stream->popFrames(1, true) > 0) {
|
if (stream->popFrames(1, true) > 0) {
|
||||||
stream->updateLastPopOutputLoudnessAndTrailingLoudness();
|
stream->updateLastPopOutputLoudnessAndTrailingLoudness();
|
||||||
}
|
}
|
||||||
|
@ -147,53 +147,46 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer&
|
||||||
// since audio stream stats packets are sent periodically, this is a good place to remove our dead injected streams.
|
// since audio stream stats packets are sent periodically, this is a good place to remove our dead injected streams.
|
||||||
removeDeadInjectedStreams();
|
removeDeadInjectedStreams();
|
||||||
|
|
||||||
char packet[MAX_PACKET_SIZE];
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// The append flag is a boolean value that will be packed right after the header. The first packet sent
|
// 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.
|
// 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
|
// 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.
|
// it receives a packet with an appendFlag of 0. This prevents the buildup of dead audio stream stats in the client.
|
||||||
quint8 appendFlag = 0;
|
quint8 appendFlag = 0;
|
||||||
|
|
||||||
// pack header
|
|
||||||
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
|
||||||
char* headerEndAt = packet + numBytesPacketHeader;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// pack and send stream stats packets until all audio streams' stats are sent
|
// pack and send stream stats packets until all audio streams' stats are sent
|
||||||
int numStreamStatsRemaining = _audioStreams.size();
|
int numStreamStatsRemaining = _audioStreams.size();
|
||||||
QHash<QUuid, PositionalAudioStream*>::ConstIterator audioStreamsIterator = _audioStreams.constBegin();
|
QHash<QUuid, PositionalAudioStream*>::ConstIterator audioStreamsIterator = _audioStreams.constBegin();
|
||||||
while (numStreamStatsRemaining > 0) {
|
while (numStreamStatsRemaining > 0) {
|
||||||
|
|
||||||
char* dataAt = headerEndAt;
|
auto statsPacket { NLPacket::create(PacketType::AudioStreamStats); }
|
||||||
|
|
||||||
// pack the append flag
|
// pack the append flag in this packet
|
||||||
memcpy(dataAt, &appendFlag, sizeof(quint8));
|
statsPacket.write(&appendFlag, sizeof(quint8));
|
||||||
appendFlag = 1;
|
appendFlag = 1;
|
||||||
dataAt += sizeof(quint8);
|
|
||||||
|
int numStreamStatsRoomFor = (statsPacket.size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
|
||||||
|
|
||||||
// calculate and pack the number of stream stats to follow
|
// calculate and pack the number of stream stats to follow
|
||||||
quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor);
|
quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor);
|
||||||
memcpy(dataAt, &numStreamStatsToPack, sizeof(quint16));
|
statsPacket.write(&numStreamStatsToPack, sizeof(quint16));
|
||||||
dataAt += sizeof(quint16);
|
|
||||||
|
|
||||||
// pack the calculated number of stream stats
|
// pack the calculated number of stream stats
|
||||||
for (int i = 0; i < numStreamStatsToPack; i++) {
|
for (int i = 0; i < numStreamStatsToPack; i++) {
|
||||||
PositionalAudioStream* stream = audioStreamsIterator.value();
|
PositionalAudioStream* stream = audioStreamsIterator.value();
|
||||||
|
|
||||||
stream->perSecondCallbackForUpdatingStats();
|
stream->perSecondCallbackForUpdatingStats();
|
||||||
|
|
||||||
AudioStreamStats streamStats = stream->getAudioStreamStats();
|
AudioStreamStats streamStats = stream->getAudioStreamStats();
|
||||||
memcpy(dataAt, &streamStats, sizeof(AudioStreamStats));
|
statsPacket.write(&streamStats, sizeof(AudioStreamStats));
|
||||||
dataAt += sizeof(AudioStreamStats);
|
|
||||||
|
|
||||||
audioStreamsIterator++;
|
audioStreamsIterator++;
|
||||||
}
|
}
|
||||||
numStreamStatsRemaining -= numStreamStatsToPack;
|
numStreamStatsRemaining -= numStreamStatsToPack;
|
||||||
|
|
||||||
// send the current packet
|
// send the current packet
|
||||||
nodeList->writeDatagram(packet, dataAt - packet, destinationNode);
|
nodeList->sendPacket(statsPacket, destinationNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +213,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const {
|
||||||
result["downstream"] = downstreamStats;
|
result["downstream"] = downstreamStats;
|
||||||
|
|
||||||
AvatarAudioStream* avatarAudioStream = getAvatarAudioStream();
|
AvatarAudioStream* avatarAudioStream = getAvatarAudioStream();
|
||||||
|
|
||||||
if (avatarAudioStream) {
|
if (avatarAudioStream) {
|
||||||
QJsonObject upstreamStats;
|
QJsonObject upstreamStats;
|
||||||
|
|
||||||
|
@ -246,7 +239,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const {
|
||||||
} else {
|
} else {
|
||||||
result["upstream"] = "mic unknown";
|
result["upstream"] = "mic unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
|
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
|
||||||
QJsonArray injectorArray;
|
QJsonArray injectorArray;
|
||||||
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
|
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
|
||||||
|
@ -270,7 +263,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const {
|
||||||
upstreamStats["min_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMin);
|
upstreamStats["min_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMin);
|
||||||
upstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax);
|
upstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax);
|
||||||
upstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage);
|
upstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage);
|
||||||
|
|
||||||
injectorArray.push_back(upstreamStats);
|
injectorArray.push_back(upstreamStats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +297,7 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS
|
||||||
streamStats._desiredJitterBufferFrames,
|
streamStats._desiredJitterBufferFrames,
|
||||||
streamStats._framesAvailableAverage,
|
streamStats._framesAvailableAverage,
|
||||||
streamStats._framesAvailable);
|
streamStats._framesAvailable);
|
||||||
|
|
||||||
printf(" Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u\n",
|
printf(" Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u\n",
|
||||||
streamStats._starveCount,
|
streamStats._starveCount,
|
||||||
streamStats._consecutiveNotMixedCount,
|
streamStats._consecutiveNotMixedCount,
|
||||||
|
@ -323,10 +316,10 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) {
|
PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) {
|
||||||
if (!_listenerSourcePairData.contains(sourceUUID)) {
|
if (!_listenerSourcePairData.contains(sourceUUID)) {
|
||||||
PerListenerSourcePairData* newData = new PerListenerSourcePairData();
|
PerListenerSourcePairData* newData = new PerListenerSourcePairData();
|
||||||
_listenerSourcePairData[sourceUUID] = newData;
|
_listenerSourcePairData[sourceUUID] = newData;
|
||||||
}
|
}
|
||||||
return _listenerSourcePairData[sourceUUID];
|
return _listenerSourcePairData[sourceUUID];
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
||||||
|
|
||||||
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60;
|
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60;
|
||||||
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
|
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
|
||||||
|
|
||||||
AvatarMixer::AvatarMixer(const QByteArray& packet) :
|
AvatarMixer::AvatarMixer(const QByteArray& packet) :
|
||||||
|
@ -63,73 +63,70 @@ const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
|
||||||
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
|
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
|
||||||
// if the avatar is not in view or in the keyhole.
|
// if the avatar is not in view or in the keyhole.
|
||||||
void AvatarMixer::broadcastAvatarData() {
|
void AvatarMixer::broadcastAvatarData() {
|
||||||
|
|
||||||
int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp;
|
int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp;
|
||||||
|
|
||||||
++_numStatFrames;
|
++_numStatFrames;
|
||||||
|
|
||||||
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
|
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
|
||||||
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
|
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
|
||||||
|
|
||||||
const float RATIO_BACK_OFF = 0.02f;
|
const float RATIO_BACK_OFF = 0.02f;
|
||||||
|
|
||||||
const int TRAILING_AVERAGE_FRAMES = 100;
|
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||||
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
|
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
|
||||||
|
|
||||||
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
||||||
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
||||||
|
|
||||||
// NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was
|
// NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was
|
||||||
// able to sleep. This will eventually be used to ask for an additional avatar-mixer to help out. Currently the value
|
// able to sleep. This will eventually be used to ask for an additional avatar-mixer to help out. Currently the value
|
||||||
// is unused as it is assumed this should not be hit before the avatar-mixer hits the desired bandwidth limit per client.
|
// is unused as it is assumed this should not be hit before the avatar-mixer hits the desired bandwidth limit per client.
|
||||||
// It is reported in the domain-server stats for the avatar-mixer.
|
// It is reported in the domain-server stats for the avatar-mixer.
|
||||||
|
|
||||||
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
||||||
+ (idleTime * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_MSECS);
|
+ (idleTime * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_MSECS);
|
||||||
|
|
||||||
float lastCutoffRatio = _performanceThrottlingRatio;
|
float lastCutoffRatio = _performanceThrottlingRatio;
|
||||||
bool hasRatioChanged = false;
|
bool hasRatioChanged = false;
|
||||||
|
|
||||||
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
|
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
|
||||||
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
|
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
|
||||||
// we're struggling - change our performance throttling ratio
|
// we're struggling - change our performance throttling ratio
|
||||||
_performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio));
|
_performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio));
|
||||||
|
|
||||||
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
||||||
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
||||||
hasRatioChanged = true;
|
hasRatioChanged = true;
|
||||||
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) {
|
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) {
|
||||||
// we've recovered and can back off the performance throttling
|
// we've recovered and can back off the performance throttling
|
||||||
_performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF;
|
_performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF;
|
||||||
|
|
||||||
if (_performanceThrottlingRatio < 0) {
|
if (_performanceThrottlingRatio < 0) {
|
||||||
_performanceThrottlingRatio = 0;
|
_performanceThrottlingRatio = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
||||||
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
||||||
hasRatioChanged = true;
|
hasRatioChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasRatioChanged) {
|
if (hasRatioChanged) {
|
||||||
framesSinceCutoffEvent = 0;
|
framesSinceCutoffEvent = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasRatioChanged) {
|
if (!hasRatioChanged) {
|
||||||
++framesSinceCutoffEvent;
|
++framesSinceCutoffEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QByteArray mixedAvatarByteArray;
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
int numPacketHeaderBytes = nodeList->populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
|
|
||||||
|
// setup for distributed random floating point values
|
||||||
// setup for distributed random floating point values
|
|
||||||
std::random_device randomDevice;
|
std::random_device randomDevice;
|
||||||
std::mt19937 generator(randomDevice());
|
std::mt19937 generator(randomDevice());
|
||||||
std::uniform_real_distribution<float> distribution;
|
std::uniform_real_distribution<float> distribution;
|
||||||
|
|
||||||
nodeList->eachMatchingNode(
|
nodeList->eachMatchingNode(
|
||||||
[&](const SharedNodePointer& node)->bool {
|
[&](const SharedNodePointer& node)->bool {
|
||||||
if (!node->getLinkedData()) {
|
if (!node->getLinkedData()) {
|
||||||
|
@ -150,25 +147,22 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
++_sumListeners;
|
++_sumListeners;
|
||||||
|
|
||||||
// reset packet pointers for this node
|
|
||||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
|
||||||
|
|
||||||
AvatarData& avatar = nodeData->getAvatar();
|
AvatarData& avatar = nodeData->getAvatar();
|
||||||
glm::vec3 myPosition = avatar.getPosition();
|
glm::vec3 myPosition = avatar.getPosition();
|
||||||
|
|
||||||
// reset the internal state for correct random number distribution
|
// reset the internal state for correct random number distribution
|
||||||
distribution.reset();
|
distribution.reset();
|
||||||
|
|
||||||
// reset the max distance for this frame
|
// reset the max distance for this frame
|
||||||
float maxAvatarDistanceThisFrame = 0.0f;
|
float maxAvatarDistanceThisFrame = 0.0f;
|
||||||
|
|
||||||
// reset the number of sent avatars
|
// reset the number of sent avatars
|
||||||
nodeData->resetNumAvatarsSentLastFrame();
|
nodeData->resetNumAvatarsSentLastFrame();
|
||||||
|
|
||||||
// keep a counter of the number of considered avatars
|
// keep a counter of the number of considered avatars
|
||||||
int numOtherAvatars = 0;
|
int numOtherAvatars = 0;
|
||||||
|
|
||||||
// keep track of outbound data rate specifically for avatar data
|
// keep track of outbound data rate specifically for avatar data
|
||||||
int numAvatarDataBytes = 0;
|
int numAvatarDataBytes = 0;
|
||||||
|
|
||||||
|
@ -185,7 +179,7 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
// bandwidth to this node. We do this once a second, which is also the window for
|
// bandwidth to this node. We do this once a second, which is also the window for
|
||||||
// the bandwidth reported by node->getOutboundBandwidth();
|
// the bandwidth reported by node->getOutboundBandwidth();
|
||||||
if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) {
|
if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) {
|
||||||
|
|
||||||
const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f;
|
const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f;
|
||||||
const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO);
|
const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO);
|
||||||
const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f));
|
const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f));
|
||||||
|
@ -195,22 +189,22 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
|
|
||||||
if (avatarDataRateLastSecond > _maxKbpsPerNode) {
|
if (avatarDataRateLastSecond > _maxKbpsPerNode) {
|
||||||
|
|
||||||
// is the FRD greater than the farthest avatar?
|
// is the FRD greater than the farthest avatar?
|
||||||
// if so, before we calculate anything, set it to that distance
|
// if so, before we calculate anything, set it to that distance
|
||||||
currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance());
|
currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance());
|
||||||
|
|
||||||
// we're adjusting the full rate distance to target a bandwidth in the middle
|
// we're adjusting the full rate distance to target a bandwidth in the middle
|
||||||
// of the hysterisis gap
|
// of the hysterisis gap
|
||||||
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
|
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
|
||||||
|
|
||||||
nodeData->setFullRateDistance(currentFullRateDistance);
|
nodeData->setFullRateDistance(currentFullRateDistance);
|
||||||
nodeData->resetNumFramesSinceFRDAdjustment();
|
nodeData->resetNumFramesSinceFRDAdjustment();
|
||||||
} else if (currentFullRateDistance < nodeData->getMaxAvatarDistance()
|
} else if (currentFullRateDistance < nodeData->getMaxAvatarDistance()
|
||||||
&& avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) {
|
&& avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) {
|
||||||
// we are constrained AND we've recovered to below the acceptable ratio
|
// we are constrained AND we've recovered to below the acceptable ratio
|
||||||
// lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap
|
// lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap
|
||||||
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
|
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
|
||||||
|
|
||||||
nodeData->setFullRateDistance(currentFullRateDistance);
|
nodeData->setFullRateDistance(currentFullRateDistance);
|
||||||
nodeData->resetNumFramesSinceFRDAdjustment();
|
nodeData->resetNumFramesSinceFRDAdjustment();
|
||||||
}
|
}
|
||||||
|
@ -218,6 +212,9 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
nodeData->incrementNumFramesSinceFRDAdjustment();
|
nodeData->incrementNumFramesSinceFRDAdjustment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup a PacketList for the avatarPackets
|
||||||
|
PacketList avatarPacketList(PacketType::AvatarData);
|
||||||
|
|
||||||
// this is an AGENT we have received head data from
|
// this is an AGENT we have received head data from
|
||||||
// send back a packet with other active node data to this node
|
// send back a packet with other active node data to this node
|
||||||
nodeList->eachMatchingNode(
|
nodeList->eachMatchingNode(
|
||||||
|
@ -233,16 +230,16 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
},
|
},
|
||||||
[&](const SharedNodePointer& otherNode) {
|
[&](const SharedNodePointer& otherNode) {
|
||||||
++numOtherAvatars;
|
++numOtherAvatars;
|
||||||
|
|
||||||
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
MutexTryLocker lock(otherNodeData->getMutex());
|
MutexTryLocker lock(otherNodeData->getMutex());
|
||||||
if (!lock.isLocked()) {
|
if (!lock.isLocked()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarData& otherAvatar = otherNodeData->getAvatar();
|
AvatarData& otherAvatar = otherNodeData->getAvatar();
|
||||||
// Decide whether to send this avatar's data based on it's distance from us
|
// Decide whether to send this avatar's data based on it's distance from us
|
||||||
|
|
||||||
// The full rate distance is the distance at which EVERY update will be sent for this avatar
|
// The full rate distance is the distance at which EVERY update will be sent for this avatar
|
||||||
// at twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
// at twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
||||||
glm::vec3 otherPosition = otherAvatar.getPosition();
|
glm::vec3 otherPosition = otherAvatar.getPosition();
|
||||||
|
@ -251,24 +248,24 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
// potentially update the max full rate distance for this frame
|
// potentially update the max full rate distance for this frame
|
||||||
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
|
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
|
||||||
|
|
||||||
if (distanceToAvatar != 0.0f
|
if (distanceToAvatar != 0.0f
|
||||||
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||||
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData);
|
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData);
|
||||||
|
|
||||||
if (lastSeqToReceiver > lastSeqFromSender) {
|
if (lastSeqToReceiver > lastSeqFromSender) {
|
||||||
// Did we somehow get out of order packets from the sender?
|
// Did we somehow get out of order packets from the sender?
|
||||||
// We don't expect this to happen - in RELEASE we add this to a trackable stat
|
// We don't expect this to happen - in RELEASE we add this to a trackable stat
|
||||||
// and in DEBUG we crash on the assert
|
// and in DEBUG we crash on the assert
|
||||||
|
|
||||||
otherNodeData->incrementNumOutOfOrderSends();
|
otherNodeData->incrementNumOutOfOrderSends();
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we haven't already sent this data from this sender to this receiver
|
// make sure we haven't already sent this data from this sender to this receiver
|
||||||
// or that somehow we haven't sent
|
// or that somehow we haven't sent
|
||||||
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
|
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
|
||||||
|
@ -277,76 +274,70 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
|
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
|
||||||
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
|
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
|
||||||
++numAvatarsWithSkippedFrames;
|
++numAvatarsWithSkippedFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're going to send this avatar
|
// we're going to send this avatar
|
||||||
|
|
||||||
// increment the number of avatars sent to this reciever
|
// increment the number of avatars sent to this reciever
|
||||||
nodeData->incrementNumAvatarsSentLastFrame();
|
nodeData->incrementNumAvatarsSentLastFrame();
|
||||||
|
|
||||||
// set the last sent sequence number for this sender on the receiver
|
// set the last sent sequence number for this sender on the receiver
|
||||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||||
otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData));
|
otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData));
|
||||||
|
|
||||||
QByteArray avatarByteArray;
|
// start a new segment in the PacketList for this avatar
|
||||||
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
avatarPacketList.startSegment();
|
||||||
avatarByteArray.append(otherAvatar.toByteArray());
|
|
||||||
|
numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122());
|
||||||
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
|
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray());
|
||||||
nodeList->writeDatagram(mixedAvatarByteArray, node);
|
|
||||||
|
avatarPacketList.endSegment();
|
||||||
|
|
||||||
numAvatarDataBytes += mixedAvatarByteArray.size();
|
|
||||||
|
|
||||||
// reset the packet
|
|
||||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the avatar into the mixedAvatarByteArray packet
|
|
||||||
mixedAvatarByteArray.append(avatarByteArray);
|
|
||||||
|
|
||||||
// if the receiving avatar has just connected make sure we send out the mesh and billboard
|
// if the receiving avatar has just connected make sure we send out the mesh and billboard
|
||||||
// for this avatar (assuming they exist)
|
// for this avatar (assuming they exist)
|
||||||
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
|
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
|
||||||
|
|
||||||
// we will also force a send of billboard or identity packet
|
// we will also force a send of billboard or identity packet
|
||||||
// if either has changed in the last frame
|
// if either has changed in the last frame
|
||||||
|
|
||||||
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
||||||
&& (forceSend
|
&& (forceSend
|
||||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
||||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||||
QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
|
|
||||||
billboardPacket.append(otherNode->getUUID().toRfc4122());
|
|
||||||
billboardPacket.append(otherNodeData->getAvatar().getBillboard());
|
|
||||||
|
|
||||||
nodeList->writeDatagram(billboardPacket, node);
|
auto billboardPacket { NLPacket::create(PacketType::AvatarBillboard); }
|
||||||
|
billboardPacket.write(otherNode->getUUID().toRfc4122());
|
||||||
|
billboardPacket.write(otherNodeData->getAvatar().getBillboard());
|
||||||
|
|
||||||
|
nodeList->sendPacket(billboardPacket, node);
|
||||||
|
|
||||||
++_sumBillboardPackets;
|
++_sumBillboardPackets;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
||||||
&& (forceSend
|
&& (forceSend
|
||||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||||
|
|
||||||
QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
auto identityPacket { NLPacket::create(PacketType::AvatarIdentity); }
|
||||||
|
|
||||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
||||||
identityPacket.append(individualData);
|
|
||||||
|
identityPacket.write(individualData);
|
||||||
nodeList->writeDatagram(identityPacket, node);
|
|
||||||
|
nodeList->sendPacket(identityPacket, node);
|
||||||
|
|
||||||
++_sumIdentityPackets;
|
++_sumIdentityPackets;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// send the last packet
|
// send the avatar data PacketList
|
||||||
nodeList->writeDatagram(mixedAvatarByteArray, node);
|
nodeList->sendPacketList(avatarPacketList, node);
|
||||||
|
|
||||||
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||||
nodeData->recordSentAvatarData(numAvatarDataBytes + mixedAvatarByteArray.size());
|
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
||||||
|
|
||||||
// record the number of avatars held back this frame
|
// record the number of avatars held back this frame
|
||||||
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
||||||
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
||||||
|
@ -359,7 +350,7 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
|
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,9 +361,9 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||||
|
|
||||||
// this was an avatar we were sending to other people
|
// this was an avatar we were sending to other people
|
||||||
// send a kill packet for it to our other nodes
|
// send a kill packet for it to our other nodes
|
||||||
QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
|
auto killPacket { NLPacket::create(PacketType::KillAvatar); }
|
||||||
killPacket += killedNode->getUUID().toRfc4122();
|
killPacket.write(killedNode->getUUID().toRfc4122());
|
||||||
|
|
||||||
nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent);
|
nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent);
|
||||||
|
|
||||||
// we also want to remove sequence number data for this avatar on our other avatars
|
// we also want to remove sequence number data for this avatar on our other avatars
|
||||||
|
@ -402,9 +393,9 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||||
void AvatarMixer::readPendingDatagrams() {
|
void AvatarMixer::readPendingDatagrams() {
|
||||||
QByteArray receivedPacket;
|
QByteArray receivedPacket;
|
||||||
HifiSockAddr senderSockAddr;
|
HifiSockAddr senderSockAddr;
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
|
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
|
||||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||||
switch (packetTypeForPacket(receivedPacket)) {
|
switch (packetTypeForPacket(receivedPacket)) {
|
||||||
|
@ -413,14 +404,14 @@ void AvatarMixer::readPendingDatagrams() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PacketTypeAvatarIdentity: {
|
case PacketTypeAvatarIdentity: {
|
||||||
|
|
||||||
// check if we have a matching node in our list
|
// check if we have a matching node in our list
|
||||||
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
|
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||||
|
|
||||||
if (avatarNode && avatarNode->getLinkedData()) {
|
if (avatarNode && avatarNode->getLinkedData()) {
|
||||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||||
AvatarData& avatar = nodeData->getAvatar();
|
AvatarData& avatar = nodeData->getAvatar();
|
||||||
|
|
||||||
// parse the identity packet and update the change timestamp if appropriate
|
// parse the identity packet and update the change timestamp if appropriate
|
||||||
if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) {
|
if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) {
|
||||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
||||||
|
@ -430,20 +421,20 @@ void AvatarMixer::readPendingDatagrams() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PacketTypeAvatarBillboard: {
|
case PacketTypeAvatarBillboard: {
|
||||||
|
|
||||||
// check if we have a matching node in our list
|
// check if we have a matching node in our list
|
||||||
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
|
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||||
|
|
||||||
if (avatarNode && avatarNode->getLinkedData()) {
|
if (avatarNode && avatarNode->getLinkedData()) {
|
||||||
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||||
AvatarData& avatar = nodeData->getAvatar();
|
AvatarData& avatar = nodeData->getAvatar();
|
||||||
|
|
||||||
// parse the billboard packet and update the change timestamp if appropriate
|
// parse the billboard packet and update the change timestamp if appropriate
|
||||||
if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) {
|
if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) {
|
||||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
||||||
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
|
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -463,15 +454,15 @@ void AvatarMixer::readPendingDatagrams() {
|
||||||
void AvatarMixer::sendStatsPacket() {
|
void AvatarMixer::sendStatsPacket() {
|
||||||
QJsonObject statsObject;
|
QJsonObject statsObject;
|
||||||
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
|
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
|
||||||
|
|
||||||
statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames;
|
statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames;
|
||||||
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
|
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
|
||||||
|
|
||||||
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
|
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
|
||||||
statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;
|
statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;
|
||||||
|
|
||||||
QJsonObject avatarsObject;
|
QJsonObject avatarsObject;
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
// add stats for each listerner
|
// add stats for each listerner
|
||||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||||
|
@ -482,7 +473,7 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
// add the key to ask the domain-server for a username replacement, if it has it
|
// add the key to ask the domain-server for a username replacement, if it has it
|
||||||
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth();
|
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth();
|
||||||
|
|
||||||
AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
|
AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
if (clientData) {
|
if (clientData) {
|
||||||
MutexTryLocker lock(clientData->getMutex());
|
MutexTryLocker lock(clientData->getMutex());
|
||||||
|
@ -490,9 +481,9 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
clientData->loadJSONStats(avatarStats);
|
clientData->loadJSONStats(avatarStats);
|
||||||
|
|
||||||
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
|
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
|
||||||
avatarStats["delta_full_vs_avatar_data_kbps"] =
|
avatarStats["delta_full_vs_avatar_data_kbps"] =
|
||||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
|
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats;
|
avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats;
|
||||||
|
@ -500,7 +491,7 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
|
|
||||||
statsObject["avatars"] = avatarsObject;
|
statsObject["avatars"] = avatarsObject;
|
||||||
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||||
|
|
||||||
_sumListeners = 0;
|
_sumListeners = 0;
|
||||||
_sumBillboardPackets = 0;
|
_sumBillboardPackets = 0;
|
||||||
_sumIdentityPackets = 0;
|
_sumIdentityPackets = 0;
|
||||||
|
@ -509,41 +500,41 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
|
|
||||||
void AvatarMixer::run() {
|
void AvatarMixer::run() {
|
||||||
ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
|
ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||||
|
|
||||||
nodeList->linkedDataCreateCallback = [] (Node* node) {
|
nodeList->linkedDataCreateCallback = [] (Node* node) {
|
||||||
node->setLinkedData(new AvatarMixerClientData());
|
node->setLinkedData(new AvatarMixerClientData());
|
||||||
};
|
};
|
||||||
|
|
||||||
// setup the timer that will be fired on the broadcast thread
|
// setup the timer that will be fired on the broadcast thread
|
||||||
_broadcastTimer = new QTimer;
|
_broadcastTimer = new QTimer;
|
||||||
_broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS);
|
_broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS);
|
||||||
_broadcastTimer->moveToThread(&_broadcastThread);
|
_broadcastTimer->moveToThread(&_broadcastThread);
|
||||||
|
|
||||||
// connect appropriate signals and slots
|
// connect appropriate signals and slots
|
||||||
connect(_broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection);
|
connect(_broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection);
|
||||||
connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start()));
|
connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start()));
|
||||||
|
|
||||||
// wait until we have the domain-server settings, otherwise we bail
|
// wait until we have the domain-server settings, otherwise we bail
|
||||||
DomainHandler& domainHandler = nodeList->getDomainHandler();
|
DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||||
|
|
||||||
qDebug() << "Waiting for domain settings from domain-server.";
|
qDebug() << "Waiting for domain settings from domain-server.";
|
||||||
|
|
||||||
// block until we get the settingsRequestComplete signal
|
// block until we get the settingsRequestComplete signal
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||||
domainHandler.requestDomainSettings();
|
domainHandler.requestDomainSettings();
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
if (domainHandler.getSettingsObject().isEmpty()) {
|
||||||
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
||||||
setFinished(true);
|
setFinished(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the settings to pull out the values we need
|
// parse the settings to pull out the values we need
|
||||||
parseDomainServerSettings(domainHandler.getSettingsObject());
|
parseDomainServerSettings(domainHandler.getSettingsObject());
|
||||||
|
|
||||||
|
@ -554,13 +545,13 @@ void AvatarMixer::run() {
|
||||||
void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
||||||
const QString AVATAR_MIXER_SETTINGS_KEY = "avatar_mixer";
|
const QString AVATAR_MIXER_SETTINGS_KEY = "avatar_mixer";
|
||||||
const QString NODE_SEND_BANDWIDTH_KEY = "max_node_send_bandwidth";
|
const QString NODE_SEND_BANDWIDTH_KEY = "max_node_send_bandwidth";
|
||||||
|
|
||||||
const float DEFAULT_NODE_SEND_BANDWIDTH = 1.0f;
|
const float DEFAULT_NODE_SEND_BANDWIDTH = 1.0f;
|
||||||
QJsonValue nodeBandwidthValue = domainSettings[AVATAR_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY];
|
QJsonValue nodeBandwidthValue = domainSettings[AVATAR_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY];
|
||||||
if (!nodeBandwidthValue.isDouble()) {
|
if (!nodeBandwidthValue.isDouble()) {
|
||||||
qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value";
|
qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value";
|
||||||
}
|
}
|
||||||
|
|
||||||
_maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA;
|
_maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA;
|
||||||
qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps.";
|
qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -573,33 +573,34 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
||||||
QDataStream packetStream(packet);
|
QDataStream packetStream(packet);
|
||||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
|
QUuid connectUUID;
|
||||||
|
packetStream >> connectUUID;
|
||||||
|
|
||||||
parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
|
parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
|
||||||
|
|
||||||
QUuid packetUUID = uuidFromPacketHeader(packet);
|
|
||||||
|
|
||||||
// check if this connect request matches an assignment in the queue
|
// check if this connect request matches an assignment in the queue
|
||||||
bool isAssignment = _pendingAssignedNodes.contains(packetUUID);
|
bool isAssignment = _pendingAssignedNodes.contains(connectUUID);
|
||||||
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
||||||
PendingAssignedNodeData* pendingAssigneeData = NULL;
|
PendingAssignedNodeData* pendingAssigneeData = NULL;
|
||||||
|
|
||||||
if (isAssignment) {
|
if (isAssignment) {
|
||||||
pendingAssigneeData = _pendingAssignedNodes.value(packetUUID);
|
pendingAssigneeData = _pendingAssignedNodes.value(connectUUID);
|
||||||
|
|
||||||
if (pendingAssigneeData) {
|
if (pendingAssigneeData) {
|
||||||
matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType);
|
matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType);
|
||||||
|
|
||||||
if (matchingQueuedAssignment) {
|
if (matchingQueuedAssignment) {
|
||||||
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID)
|
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID)
|
||||||
<< "matches unfulfilled assignment"
|
<< "matches unfulfilled assignment"
|
||||||
<< uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID());
|
<< uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID());
|
||||||
|
|
||||||
// remove this unique assignment deployment from the hash of pending assigned nodes
|
// remove this unique assignment deployment from the hash of pending assigned nodes
|
||||||
// cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList
|
// cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList
|
||||||
_pendingAssignedNodes.remove(packetUUID);
|
_pendingAssignedNodes.remove(connectUUID);
|
||||||
} else {
|
} else {
|
||||||
// this is a node connecting to fulfill an assignment that doesn't exist
|
// this is a node connecting to fulfill an assignment that doesn't exist
|
||||||
// don't reply back to them so they cycle back and re-request an assignment
|
// don't reply back to them so they cycle back and re-request an assignment
|
||||||
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID);
|
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -621,9 +622,8 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
||||||
QByteArray utfString = reason.toUtf8();
|
QByteArray utfString = reason.toUtf8();
|
||||||
int payloadSize = utfString.size();
|
int payloadSize = utfString.size();
|
||||||
|
|
||||||
auto connectionDeniedPacket = NodeListPacket::make(PacketType::DomainConnectionDenied, payloadSize);
|
auto connectionDeniedPacket = NLPacket::make(PacketType::DomainConnectionDenied, payloadSize);
|
||||||
|
connectionDeniedPacket.write(utfString);
|
||||||
memcpy(connectionDeniedPacket.payload().data(), utfString.data(), utfString.size());
|
|
||||||
|
|
||||||
// tell client it has been refused.
|
// tell client it has been refused.
|
||||||
limitedNodeList->sendPacket(std::move(connectionDeniedPacket, senderSockAddr);
|
limitedNodeList->sendPacket(std::move(connectionDeniedPacket, senderSockAddr);
|
||||||
|
@ -638,18 +638,18 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
||||||
QUuid nodeUUID;
|
QUuid nodeUUID;
|
||||||
|
|
||||||
HifiSockAddr discoveredSocket = senderSockAddr;
|
HifiSockAddr discoveredSocket = senderSockAddr;
|
||||||
SharedNetworkPeer connectedPeer = _icePeers.value(packetUUID);
|
SharedNetworkPeer connectedPeer = _icePeers.value(connectUUID);
|
||||||
|
|
||||||
if (connectedPeer) {
|
if (connectedPeer) {
|
||||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||||
nodeUUID = packetUUID;
|
nodeUUID = connectUUID;
|
||||||
|
|
||||||
if (connectedPeer->getActiveSocket()) {
|
if (connectedPeer->getActiveSocket()) {
|
||||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we got a packetUUID we didn't recognize, just add the node
|
// we got a connectUUID we didn't recognize, just add the node with a new UUID
|
||||||
nodeUUID = QUuid::createUuid();
|
nodeUUID = QUuid::createUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,54 +153,56 @@ void AudioInjector::injectToMixer() {
|
||||||
// make sure we actually have samples downloaded to inject
|
// make sure we actually have samples downloaded to inject
|
||||||
if (_audioData.size()) {
|
if (_audioData.size()) {
|
||||||
|
|
||||||
|
auto audioPacket { NLPacket::create(PacketType::InjectAudio); }
|
||||||
|
|
||||||
// setup the packet for injected audio
|
// setup the packet for injected audio
|
||||||
QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio);
|
QDataStream audioPacketStream(&audioPacket);
|
||||||
QDataStream packetStream(&injectAudioPacket, QIODevice::Append);
|
|
||||||
|
|
||||||
// pack some placeholder sequence number for now
|
// pack some placeholder sequence number for now
|
||||||
int numPreSequenceNumberBytes = injectAudioPacket.size();
|
audioPacketStream << (quint16) 0;
|
||||||
packetStream << (quint16)0;
|
|
||||||
|
|
||||||
// pack stream identifier (a generated UUID)
|
// pack stream identifier (a generated UUID)
|
||||||
packetStream << QUuid::createUuid();
|
audioPacketStream << QUuid::createUuid();
|
||||||
|
|
||||||
// pack the stereo/mono type of the stream
|
// pack the stereo/mono type of the stream
|
||||||
packetStream << _options.stereo;
|
audioPacketStream << _options.stereo;
|
||||||
|
|
||||||
// pack the flag for loopback
|
// pack the flag for loopback
|
||||||
uchar loopbackFlag = (uchar) true;
|
uchar loopbackFlag = (uchar) true;
|
||||||
packetStream << loopbackFlag;
|
audioPacketStream << loopbackFlag;
|
||||||
|
|
||||||
// pack the position for injected audio
|
// pack the position for injected audio
|
||||||
int positionOptionOffset = injectAudioPacket.size();
|
int positionOptionOffset = audioPacket.pos();
|
||||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.position),
|
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.position),
|
||||||
sizeof(_options.position));
|
sizeof(_options.position));
|
||||||
|
|
||||||
// pack our orientation for injected audio
|
// pack our orientation for injected audio
|
||||||
int orientationOptionOffset = injectAudioPacket.size();
|
int orientationOptionOffset = audioPacket.pos();
|
||||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation),
|
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation),
|
||||||
sizeof(_options.orientation));
|
sizeof(_options.orientation));
|
||||||
|
|
||||||
// pack zero for radius
|
// pack zero for radius
|
||||||
float radius = 0;
|
float radius = 0;
|
||||||
packetStream << radius;
|
audioPacketStream << radius;
|
||||||
|
|
||||||
// pack 255 for attenuation byte
|
// pack 255 for attenuation byte
|
||||||
int volumeOptionOffset = injectAudioPacket.size();
|
int volumeOptionOffset = audioPacket.pos();
|
||||||
quint8 volume = MAX_INJECTOR_VOLUME * _options.volume;
|
quint8 volume = MAX_INJECTOR_VOLUME * _options.volume;
|
||||||
packetStream << volume;
|
audioPacketStream << volume;
|
||||||
|
|
||||||
packetStream << _options.ignorePenumbra;
|
audioPacketStream << _options.ignorePenumbra;
|
||||||
|
|
||||||
|
int audioDataOffset = audioPacket.pos();
|
||||||
|
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
timer.start();
|
timer.start();
|
||||||
int nextFrame = 0;
|
int nextFrame = 0;
|
||||||
|
|
||||||
int numPreAudioDataBytes = injectAudioPacket.size();
|
|
||||||
bool shouldLoop = _options.loop;
|
bool shouldLoop = _options.loop;
|
||||||
|
|
||||||
// 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 outgoingInjectedAudioSequenceNumber = 0;
|
quint16 outgoingInjectedAudioSequenceNumber = 0;
|
||||||
|
|
||||||
while (_currentSendPosition < _audioData.size() && !_shouldStop) {
|
while (_currentSendPosition < _audioData.size() && !_shouldStop) {
|
||||||
|
|
||||||
int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
|
int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
|
||||||
|
@ -214,31 +216,29 @@ void AudioInjector::injectToMixer() {
|
||||||
}
|
}
|
||||||
_loudness /= (float)(bytesToCopy / sizeof(int16_t));
|
_loudness /= (float)(bytesToCopy / sizeof(int16_t));
|
||||||
|
|
||||||
memcpy(injectAudioPacket.data() + positionOptionOffset,
|
audioPacket.seek(positionOptionOffset);
|
||||||
&_options.position,
|
audioPacket.write(&_options.position, sizeof(_options.position));
|
||||||
sizeof(_options.position));
|
|
||||||
memcpy(injectAudioPacket.data() + orientationOptionOffset,
|
|
||||||
&_options.orientation,
|
|
||||||
sizeof(_options.orientation));
|
|
||||||
volume = MAX_INJECTOR_VOLUME * _options.volume;
|
|
||||||
memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume));
|
|
||||||
|
|
||||||
// resize the QByteArray to the right size
|
audioPacket.seek(orientationOptionOffset);
|
||||||
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
|
audioPacket.write(&_options.orientation, sizeof(_options.orientation));
|
||||||
|
|
||||||
|
volume = MAX_INJECTOR_VOLUME * _options.volume;
|
||||||
|
audioPacket.seek(volumeOptionOffset);
|
||||||
|
audioPacket.write(&volume, sizeof(volume));
|
||||||
|
|
||||||
|
audioPacket.seek(audioDataOffset);
|
||||||
|
|
||||||
// pack the sequence number
|
// pack the sequence number
|
||||||
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes,
|
audioPacket.write(&outgoingInjectedAudioSequenceNumber, sizeof(quint16));
|
||||||
&outgoingInjectedAudioSequenceNumber, 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,
|
audioPacket.write(_audioData.data() + _currentSendPosition, bytesToCopy);
|
||||||
_audioData.data() + _currentSendPosition, bytesToCopy);
|
|
||||||
|
|
||||||
// grab our audio mixer from the NodeList, if it exists
|
// grab our audio mixer from the NodeList, if it exists
|
||||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||||
|
|
||||||
// send off this audio packet
|
// send off this audio packet
|
||||||
nodeList->writeDatagram(injectAudioPacket, audioMixer);
|
nodeList->sendUnreliablePacket(audioPacket, audioMixer);
|
||||||
outgoingInjectedAudioSequenceNumber++;
|
outgoingInjectedAudioSequenceNumber++;
|
||||||
|
|
||||||
_currentSendPosition += bytesToCopy;
|
_currentSendPosition += bytesToCopy;
|
||||||
|
|
|
@ -508,22 +508,22 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
// unsigned LimitedNodeList::broadcastToNodes(PacketList& packetList, const NodeSet& destinationNodeTypes) {
|
||||||
unsigned n = 0;
|
// unsigned n = 0;
|
||||||
|
//
|
||||||
|
// eachNode([&](const SharedNodePointer& node){
|
||||||
|
// if (destinationNodeTypes.contains(node->getType())) {
|
||||||
|
// writeDatagram(packet, node);
|
||||||
|
// ++n;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return n;
|
||||||
|
// }
|
||||||
|
|
||||||
eachNode([&](const SharedNodePointer& node){
|
NLPacket&& LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
||||||
if (destinationNodeTypes.contains(node->getType())) {
|
|
||||||
writeDatagram(packet, node);
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeListPacket&& LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
|
||||||
int packetSize = sizeof(PingType_t) + sizeof(quint64);
|
int packetSize = sizeof(PingType_t) + sizeof(quint64);
|
||||||
NodeListPacket pingPacket = NodeListPacket::create(PacketType::Ping, packetSize);
|
auto pingPacket { NLPacket::create(PacketType::Ping, packetSize); }
|
||||||
|
|
||||||
QDataStream packetStream(&pingPacket.payload(), QIODevice::Append);
|
QDataStream packetStream(&pingPacket.payload(), QIODevice::Append);
|
||||||
|
|
||||||
|
@ -533,7 +533,7 @@ NodeListPacket&& LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
||||||
return pingPacket;
|
return pingPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeListPacket&& LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
NLPacket&& LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
||||||
QDataStream pingPacketStream(pingPacket);
|
QDataStream pingPacketStream(pingPacket);
|
||||||
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||||
|
|
||||||
|
@ -542,41 +542,41 @@ NodeListPacket&& LimitedNodeList::constructPingReplyPacket(const QByteArray& pin
|
||||||
|
|
||||||
quint64 timeFromOriginalPing;
|
quint64 timeFromOriginalPing;
|
||||||
pingPacketStream >> timeFromOriginalPing;
|
pingPacketStream >> timeFromOriginalPing;
|
||||||
|
|
||||||
int packetSize = sizeof(PingType_t) + sizeof(quint64) + sizeof(quint64);
|
int packetSize = sizeof(PingType_t) + sizeof(quint64) + sizeof(quint64);
|
||||||
|
|
||||||
NodeListPacket replyPacket = NodeListPacket::create(PacketType::Ping, packetSize);
|
auto replyPacket { NLPacket::create(PacketType::Ping, packetSize); }
|
||||||
|
|
||||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||||
|
|
||||||
return replyPacket;
|
return replyPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeListPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID) {
|
NLPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID) {
|
||||||
int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t);
|
int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t);
|
||||||
|
|
||||||
NodeListPacket icePingPacket = NodeListPacket::create(PacketType::ICEPing, packetSize);
|
auto icePingPacket { NLPacket::create(PacketType::ICEPing, packetSize); }
|
||||||
|
|
||||||
icePingPacket.payload().replace(0, NUM_BYTES_RFC4122_UUID, iceID.toRfc4122().data());
|
icePingPacket.payload().replace(0, NUM_BYTES_RFC4122_UUID, iceID.toRfc4122().data());
|
||||||
memcpy(icePingPacket.payload() + NUM_BYTES_RFC4122_UUID, &pingType, sizeof(PingType_t));
|
memcpy(icePingPacket.payload() + NUM_BYTES_RFC4122_UUID, &pingType, sizeof(PingType_t));
|
||||||
|
|
||||||
return icePingPacket;
|
return icePingPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeListPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID) {
|
NLPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID) {
|
||||||
// pull out the ping type so we can reply back with that
|
// pull out the ping type so we can reply back with that
|
||||||
PingType_t pingType;
|
PingType_t pingType;
|
||||||
|
|
||||||
memcpy(&pingType, pingPacket.data() + NUM_BYTES_RFC4122_UUID, sizeof(PingType_t));
|
memcpy(&pingType, pingPacket.data() + NUM_BYTES_RFC4122_UUID, sizeof(PingType_t));
|
||||||
|
|
||||||
int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t);
|
int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t);
|
||||||
NodeListPacket icePingReplyPacket = NodeListPacket::create(PacketType::ICEPingReply, packetSize);
|
auto icePingReplyPacket { NLPacket::create(PacketType::ICEPingReply, packetSize); }
|
||||||
|
|
||||||
// pack the ICE ID and then the ping type
|
// pack the ICE ID and then the ping type
|
||||||
memcpy(icePingReplyPacket.payload(), iceID.toRfc4122().data(), NUM_BYTES_RFC4122_UUID);
|
memcpy(icePingReplyPacket.payload(), iceID.toRfc4122().data(), NUM_BYTES_RFC4122_UUID);
|
||||||
memcpy(icePingReplyPacket.payload() + NUM_BYTES_RFC4122_UUID, &pingType, sizeof(PingType_t));
|
memcpy(icePingReplyPacket.payload() + NUM_BYTES_RFC4122_UUID, &pingType, sizeof(PingType_t));
|
||||||
|
|
||||||
return icePingReplyPacket;
|
return icePingReplyPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,14 @@ public:
|
||||||
//
|
//
|
||||||
// qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
// qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||||
// const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
// const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||||
|
//
|
||||||
|
|
||||||
|
qint64 sendUnreliablePacket(NLPacket& packet, const SharedNodePointer& destinationNode) {};
|
||||||
|
qint64 sendUnreliablePacket(NLPacket& packet, const HifiSockAddr& sockAddr) {};
|
||||||
|
qint64 sendPacket(NLPacket&& packet, const SharedNodePointer& destinationNode) {};
|
||||||
|
qint64 sendPacket(NLPacket&& packet, const HifiSockAddr& sockAddr) {};
|
||||||
|
qint64 sendPacketList(PacketList& packetList, const SharedNodePointer& destinationNode) {};
|
||||||
|
qint64 sendPacketList(PacketList& packetList, const HifiSockAddr& sockAddr) {};
|
||||||
|
|
||||||
void (*linkedDataCreateCallback)(Node *);
|
void (*linkedDataCreateCallback)(Node *);
|
||||||
|
|
||||||
|
@ -165,17 +173,17 @@ public:
|
||||||
int updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray& packet);
|
int updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray& packet);
|
||||||
int findNodeAndUpdateWithDataFromPacket(const QByteArray& packet);
|
int findNodeAndUpdateWithDataFromPacket(const QByteArray& packet);
|
||||||
|
|
||||||
unsigned broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
|
unsigned broadcastToNodes(PacketList& packetList, const NodeSet& destinationNodeTypes) {};
|
||||||
SharedNodePointer soloNodeOfType(char nodeType);
|
SharedNodePointer soloNodeOfType(char nodeType);
|
||||||
|
|
||||||
void getPacketStats(float &packetsPerSecond, float &bytesPerSecond);
|
void getPacketStats(float &packetsPerSecond, float &bytesPerSecond);
|
||||||
void resetPacketStats();
|
void resetPacketStats();
|
||||||
|
|
||||||
NodeListPacket&& constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
NLPacket&& constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
||||||
NodeListPacket&& constructPingReplyPacket(const QByteArray& pingPacket);
|
NLPacket&& constructPingReplyPacket(const QByteArray& pingPacket);
|
||||||
|
|
||||||
NodeListPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
|
NLPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
|
||||||
NodeListPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID);
|
NLPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID);
|
||||||
|
|
||||||
virtual bool processSTUNResponse(const QByteArray& packet);
|
virtual bool processSTUNResponse(const QByteArray& packet);
|
||||||
|
|
||||||
|
|
|
@ -310,7 +310,7 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
bool isUsingDTLS = false;
|
bool isUsingDTLS = false;
|
||||||
|
|
||||||
PacketType::Value domainPacketType = !_domainHandler.isConnected()
|
PacketType::Value domainPacketType = !_domainHandler.isConnected()
|
||||||
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
? PacketType::DomainConnectRequest : PacketType::DomainListRequest;
|
||||||
|
|
||||||
if (!_domainHandler.isConnected()) {
|
if (!_domainHandler.isConnected()) {
|
||||||
qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname();
|
qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname();
|
||||||
|
@ -329,24 +329,26 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct the DS check in packet
|
auto domainPacket { NLPacket::create(domainPacketType); }
|
||||||
QUuid packetUUID = _sessionUUID;
|
QDataStream packetStream(&domainPacket->getPayload);
|
||||||
|
|
||||||
|
if (domainPacketType == PacketType::DomainConnectRequest) {
|
||||||
|
QUuid connectUUID;
|
||||||
|
|
||||||
if (domainPacketType == PacketTypeDomainConnectRequest) {
|
|
||||||
if (!_domainHandler.getAssignmentUUID().isNull()) {
|
if (!_domainHandler.getAssignmentUUID().isNull()) {
|
||||||
// this is a connect request and we're an assigned node
|
// this is a connect request and we're an assigned node
|
||||||
// so set our packetUUID as the assignment UUID
|
// so set our packetUUID as the assignment UUID
|
||||||
packetUUID = _domainHandler.getAssignmentUUID();
|
connectUUID = _domainHandler.getAssignmentUUID();
|
||||||
} else if (_domainHandler.requiresICE()) {
|
} else if (_domainHandler.requiresICE()) {
|
||||||
// this is a connect request and we're an interface client
|
// this is a connect request and we're an interface client
|
||||||
// that used ice to discover the DS
|
// that used ice to discover the DS
|
||||||
// so send our ICE client UUID with the connect request
|
// so send our ICE client UUID with the connect request
|
||||||
packetUUID = _domainHandler.getICEClientID();
|
connectUUID = _domainHandler.getICEClientID();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID);
|
// pack the connect UUID for this connect request
|
||||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
packetStream << connectUUID;
|
||||||
|
}
|
||||||
|
|
||||||
// pack our data to send to the domain-server
|
// pack our data to send to the domain-server
|
||||||
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
|
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
|
||||||
|
@ -367,7 +369,7 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);
|
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);
|
||||||
|
|
||||||
if (!isUsingDTLS) {
|
if (!isUsingDTLS) {
|
||||||
writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr());
|
sendPacket(domainPacket, _domainHandler.getSockAddr());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||||
|
@ -410,7 +412,7 @@ void NodeList::sendDSPathQuery(const QString& newPath) {
|
||||||
// only send a path query if we know who our DS is or is going to be
|
// only send a path query if we know who our DS is or is going to be
|
||||||
if (_domainHandler.isSocketKnown()) {
|
if (_domainHandler.isSocketKnown()) {
|
||||||
// construct the path query packet
|
// construct the path query packet
|
||||||
QByteArray pathQueryPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerPathQuery);
|
auto pathQueryPacket = NLPacket::create(PacketType::DomainServerPathQuery);
|
||||||
|
|
||||||
// get the UTF8 representation of path query
|
// get the UTF8 representation of path query
|
||||||
QByteArray pathQueryUTF8 = newPath.toUtf8();
|
QByteArray pathQueryUTF8 = newPath.toUtf8();
|
||||||
|
@ -418,18 +420,18 @@ void NodeList::sendDSPathQuery(const QString& newPath) {
|
||||||
// get the size of the UTF8 representation of the desired path
|
// get the size of the UTF8 representation of the desired path
|
||||||
quint16 numPathBytes = pathQueryUTF8.size();
|
quint16 numPathBytes = pathQueryUTF8.size();
|
||||||
|
|
||||||
if (pathQueryPacket.size() + numPathBytes + sizeof(numPathBytes) < MAX_PACKET_SIZE) {
|
if (numPathBytes + sizeof(numPathBytes) < pathQueryPacket.size() ) {
|
||||||
// append the size of the path to the query packet
|
// append the size of the path to the query packet
|
||||||
pathQueryPacket.append(reinterpret_cast<char*>(&numPathBytes), sizeof(numPathBytes));
|
pathQueryPacket.write(&numPathBytes, sizeof(numPathBytes));
|
||||||
|
|
||||||
// append the path itself to the query packet
|
// append the path itself to the query packet
|
||||||
pathQueryPacket.append(pathQueryUTF8);
|
pathQueryPacket.write(pathQueryUTF8);
|
||||||
|
|
||||||
qCDebug(networking) << "Sending a path query packet for path" << newPath << "to domain-server at"
|
qCDebug(networking) << "Sending a path query packet for path" << newPath << "to domain-server at"
|
||||||
<< _domainHandler.getSockAddr();
|
<< _domainHandler.getSockAddr();
|
||||||
|
|
||||||
// send off the path query
|
// send off the path query
|
||||||
writeUnverifiedDatagram(pathQueryPacket, _domainHandler.getSockAddr());
|
sendPacket(pathQueryPacket, _domainHandler.getSockAddr());
|
||||||
} else {
|
} else {
|
||||||
qCDebug(networking) << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." <<
|
qCDebug(networking) << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." <<
|
||||||
"Will not send query.";
|
"Will not send query.";
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
//
|
|
||||||
// PacketPayload.cpp
|
|
||||||
// libraries/networking/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 07/06/15.
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "PacketPayload.h"
|
|
||||||
|
|
||||||
PacketPayload::PacketPayload(char* data, int maxBytes) :
|
|
||||||
_data(data)
|
|
||||||
_maxBytes(maxBytes)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int PacketPayload::append(const char* src, int srcBytes) {
|
|
||||||
// this is a call to write at the current index
|
|
||||||
int numWrittenBytes = write(src, srcBytes, _index);
|
|
||||||
|
|
||||||
if (numWrittenBytes > 0) {
|
|
||||||
// we wrote some bytes, push the index
|
|
||||||
_index += numWrittenBytes;
|
|
||||||
return numWrittenBytes;
|
|
||||||
} else {
|
|
||||||
return numWrittenBytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const int PACKET_WRITE_ERROR = -1;
|
|
||||||
|
|
||||||
int PacketPayload::write(const char* src, int srcBytes, int index) {
|
|
||||||
if (index >= _maxBytes) {
|
|
||||||
// we were passed a bad index, return -1
|
|
||||||
return PACKET_WRITE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we have the space required to write this block
|
|
||||||
int bytesAvailable = _maxBytes - index;
|
|
||||||
|
|
||||||
if (bytesAvailable < srcBytes) {
|
|
||||||
// good to go - write the data
|
|
||||||
memcpy(_data + index, src, srcBytes);
|
|
||||||
|
|
||||||
// should this cause us to push our index (is this the farthest we've written in data)?
|
|
||||||
_index = std::max(_data + index + srcBytes, _index);
|
|
||||||
|
|
||||||
// return the number of bytes written
|
|
||||||
return srcBytes;
|
|
||||||
} else {
|
|
||||||
// not enough space left for this write - return an error
|
|
||||||
return PACKET_WRITE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ const QSet<PacketType::Value> NON_VERIFIED_PACKETS = QSet<PacketType::Value>()
|
||||||
|
|
||||||
const QSet<PacketType::Value> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType::Value>() << AvatarData;
|
const QSet<PacketType::Value> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType::Value>() << AvatarData;
|
||||||
|
|
||||||
const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>() << ICEPing << ICEPingReply;
|
const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>()
|
||||||
|
<< ICEPing << ICEPingReply << DomainConnectRequest;
|
||||||
|
|
||||||
int arithmeticCodingValueFromBuffer(const char* checkValue) {
|
int arithmeticCodingValueFromBuffer(const char* checkValue) {
|
||||||
if (((uchar) *checkValue) < 255) {
|
if (((uchar) *checkValue) < 255) {
|
||||||
|
|
|
@ -47,18 +47,15 @@ qint64 writeData(const char* data, qint64 maxSize) {
|
||||||
|
|
||||||
if (!_isOrdered) {
|
if (!_isOrdered) {
|
||||||
auto newPacket = T::create(_packetType);
|
auto newPacket = T::create(_packetType);
|
||||||
PacketPayload& newPayload = newPacket.getPayload();
|
|
||||||
|
|
||||||
if (_segmentStartIndex >= 0) {
|
if (_segmentStartIndex >= 0) {
|
||||||
// We in the process of writing a segment for an unordered PacketList.
|
// We in the process of writing a segment for an unordered PacketList.
|
||||||
// We need to try and pull the first part of the segment out to our new packet
|
// We need to try and pull the first part of the segment out to our new packet
|
||||||
|
|
||||||
PacketPayload& currentPayload = _currentPacket->getPayload();
|
|
||||||
|
|
||||||
// check now to see if this is an unsupported write
|
// check now to see if this is an unsupported write
|
||||||
int numBytesToEnd = currentPayload.size() - _segmentStartIndex;
|
int numBytesToEnd = _currentPacket.size() - _segmentStartIndex;
|
||||||
|
|
||||||
if ((newPayload.size() - numBytesToEnd) < maxSize) {
|
if ((newPacket.size() - numBytesToEnd) < maxSize) {
|
||||||
// this is an unsupported case - the segment is bigger than the size of an individual packet
|
// this is an unsupported case - the segment is bigger than the size of an individual packet
|
||||||
// but the PacketList is not going to be sent ordered
|
// but the PacketList is not going to be sent ordered
|
||||||
qDebug() << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is"
|
qDebug() << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is"
|
||||||
|
@ -67,20 +64,20 @@ qint64 writeData(const char* data, qint64 maxSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy from currentPacket where the segment started to the beginning of the newPacket
|
// copy from currentPacket where the segment started to the beginning of the newPacket
|
||||||
newPayload.write(currentPacket.constData() + _segmentStartIndex, numBytesToEnd);
|
newPacket.write(currentPacket.constData() + _segmentStartIndex, numBytesToEnd);
|
||||||
|
|
||||||
// the current segment now starts at the beginning of the new packet
|
// the current segment now starts at the beginning of the new packet
|
||||||
_segmentStartIndex = 0;
|
_segmentStartIndex = 0;
|
||||||
|
|
||||||
// shrink the current payload to the actual size of the packet
|
// shrink the current payload to the actual size of the packet
|
||||||
currentPayload.setSizeUsed(_segmentStartIndex);
|
currentPacket.setSizeUsed(_segmentStartIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// move the current packet to our list of packets
|
// move the current packet to our list of packets
|
||||||
_packets.insert(std::move(_currentPacket));
|
_packets.insert(std::move(_currentPacket));
|
||||||
|
|
||||||
// write the data to the newPacket
|
// write the data to the newPacket
|
||||||
newPayload.write(data, maxSize);
|
newPacket.write(data, maxSize);
|
||||||
|
|
||||||
// set our current packet to the new packet
|
// set our current packet to the new packet
|
||||||
_currentPacket = newPacket;
|
_currentPacket = newPacket;
|
||||||
|
@ -90,9 +87,8 @@ qint64 writeData(const char* data, qint64 maxSize) {
|
||||||
} else {
|
} else {
|
||||||
// we're an ordered PacketList - let's fit what we can into the current packet and then put the leftover
|
// we're an ordered PacketList - let's fit what we can into the current packet and then put the leftover
|
||||||
// into a new packet
|
// into a new packet
|
||||||
PacketPayload& currentPayload = _currentPacket.getPayload();
|
|
||||||
|
|
||||||
int numBytesToEnd = _currentPayload.size() - _currentPayload.pos();
|
int numBytesToEnd = _currentPacket.size() - _currentPacket.sizeUsed();
|
||||||
_currentPacket.write(data, numBytesToEnd);
|
_currentPacket.write(data, numBytesToEnd);
|
||||||
|
|
||||||
// move the current packet to our list of packets
|
// move the current packet to our list of packets
|
||||||
|
|
Loading…
Reference in a new issue