mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 04:57:23 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
33aabe4e18
15 changed files with 198 additions and 75 deletions
|
@ -120,10 +120,12 @@ void Agent::readPendingDatagrams() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::run() {
|
const QString AGENT_LOGGING_NAME = "agent";
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
|
||||||
nodeList->setOwnerType(NodeType::Agent);
|
|
||||||
|
|
||||||
|
void Agent::run() {
|
||||||
|
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||||
|
|
||||||
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
|
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
|
||||||
|
|
||||||
// figure out the URL for the script for this agent assignment
|
// figure out the URL for the script for this agent assignment
|
||||||
|
@ -148,17 +150,6 @@ void Agent::run() {
|
||||||
|
|
||||||
qDebug() << "Downloaded script:" << scriptContents;
|
qDebug() << "Downloaded script:" << scriptContents;
|
||||||
|
|
||||||
timeval startTime;
|
|
||||||
gettimeofday(&startTime, NULL);
|
|
||||||
|
|
||||||
QTimer* domainServerTimer = new QTimer(this);
|
|
||||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
|
||||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
|
||||||
|
|
||||||
QTimer* silentNodeTimer = new QTimer(this);
|
|
||||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
|
||||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
|
||||||
|
|
||||||
// setup an Avatar for the script to use
|
// setup an Avatar for the script to use
|
||||||
AvatarData scriptedAvatar;
|
AvatarData scriptedAvatar;
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ AudioMixer::AudioMixer(const QByteArray& packet) :
|
||||||
ThreadedAssignment(packet),
|
ThreadedAssignment(packet),
|
||||||
_trailingSleepRatio(1.0f),
|
_trailingSleepRatio(1.0f),
|
||||||
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
|
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
|
||||||
_performanceThrottling(0.0f),
|
_performanceThrottlingRatio(0.0f),
|
||||||
_numStatFrames(0),
|
_numStatFrames(0),
|
||||||
_sumListeners(0),
|
_sumListeners(0),
|
||||||
_sumMixes(0)
|
_sumMixes(0)
|
||||||
|
@ -359,35 +359,29 @@ void AudioMixer::readPendingDatagrams() {
|
||||||
void AudioMixer::sendStatsPacket() {
|
void AudioMixer::sendStatsPacket() {
|
||||||
static QJsonObject statsObject;
|
static QJsonObject statsObject;
|
||||||
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f;
|
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f;
|
||||||
statsObject["performance_throttling"] = _performanceThrottling;
|
statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;
|
||||||
|
|
||||||
statsObject["average_listeners_per_frame"] = _sumListeners / (float) _numStatFrames;
|
statsObject["average_listeners_per_frame"] = (float) _sumListeners / (float) _numStatFrames;
|
||||||
|
|
||||||
if (_sumListeners > 0) {
|
if (_sumListeners > 0) {
|
||||||
statsObject["average_mixes_per_listener"] = _sumMixes / (float) _sumListeners;
|
statsObject["average_mixes_per_listener"] = (float) _sumMixes / (float) _sumListeners;
|
||||||
} else {
|
} else {
|
||||||
statsObject["average_mixes_per_listener"] = 0.0;
|
statsObject["average_mixes_per_listener"] = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||||
|
|
||||||
_sumListeners = 0;
|
_sumListeners = 0;
|
||||||
_sumMixes = 0;
|
_sumMixes = 0;
|
||||||
_numStatFrames = 0;
|
_numStatFrames = 0;
|
||||||
|
|
||||||
NodeList::getInstance()->sendStatsToDomainServer(statsObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::run() {
|
void AudioMixer::run() {
|
||||||
|
|
||||||
commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);
|
ThreadedAssignment::commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
// send a stats packet every 1 second
|
|
||||||
QTimer* statsTimer = new QTimer(this);
|
|
||||||
connect(statsTimer, SIGNAL(timeout()), this, SLOT(sendStatsPacket()));
|
|
||||||
statsTimer->start(1000);
|
|
||||||
|
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||||
|
|
||||||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||||
|
@ -428,47 +422,39 @@ void AudioMixer::run() {
|
||||||
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
||||||
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) BUFFER_SEND_INTERVAL_USECS);
|
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) BUFFER_SEND_INTERVAL_USECS);
|
||||||
|
|
||||||
float lastCutoffRatio = _performanceThrottling;
|
float lastCutoffRatio = _performanceThrottlingRatio;
|
||||||
bool hasRatioChanged = false;
|
bool hasRatioChanged = false;
|
||||||
|
|
||||||
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
|
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
|
||||||
if (framesSinceCutoffEvent % TRAILING_AVERAGE_FRAMES == 0) {
|
|
||||||
qDebug() << "Current trailing sleep ratio:" << _trailingSleepRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
|
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
|
||||||
// we're struggling - change our min required loudness to reduce some load
|
// we're struggling - change our min required loudness to reduce some load
|
||||||
_performanceThrottling = _performanceThrottling + (0.5f * (1.0f - _performanceThrottling));
|
_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" << _performanceThrottling;
|
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
||||||
hasRatioChanged = true;
|
hasRatioChanged = true;
|
||||||
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottling != 0) {
|
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) {
|
||||||
// we've recovered and can back off the required loudness
|
// we've recovered and can back off the required loudness
|
||||||
_performanceThrottling = _performanceThrottling - RATIO_BACK_OFF;
|
_performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF;
|
||||||
|
|
||||||
if (_performanceThrottling < 0) {
|
if (_performanceThrottlingRatio < 0) {
|
||||||
_performanceThrottling = 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" << _performanceThrottling;
|
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
||||||
hasRatioChanged = true;
|
hasRatioChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasRatioChanged) {
|
if (hasRatioChanged) {
|
||||||
// set out min audability threshold from the new ratio
|
// set out min audability threshold from the new ratio
|
||||||
_minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - _performanceThrottling));
|
_minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - _performanceThrottlingRatio));
|
||||||
qDebug() << "Minimum audability required to be mixed is now" << _minAudibilityThreshold;
|
qDebug() << "Minimum audability required to be mixed is now" << _minAudibilityThreshold;
|
||||||
|
|
||||||
framesSinceCutoffEvent = 0;
|
framesSinceCutoffEvent = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasRatioChanged) {
|
|
||||||
++framesSinceCutoffEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ private:
|
||||||
|
|
||||||
float _trailingSleepRatio;
|
float _trailingSleepRatio;
|
||||||
float _minAudibilityThreshold;
|
float _minAudibilityThreshold;
|
||||||
float _performanceThrottling;
|
float _performanceThrottlingRatio;
|
||||||
int _numStatFrames;
|
int _numStatFrames;
|
||||||
int _sumListeners;
|
int _sumListeners;
|
||||||
int _sumMixes;
|
int _sumMixes;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QElapsedTimer>
|
#include <QtCore/QElapsedTimer>
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
|
@ -29,7 +30,11 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
||||||
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
|
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
|
||||||
|
|
||||||
AvatarMixer::AvatarMixer(const QByteArray& packet) :
|
AvatarMixer::AvatarMixer(const QByteArray& packet) :
|
||||||
ThreadedAssignment(packet)
|
ThreadedAssignment(packet),
|
||||||
|
_trailingSleepRatio(1.0f),
|
||||||
|
_performanceThrottlingRatio(0.0f),
|
||||||
|
_sumListeners(0),
|
||||||
|
_numStatFrames(0)
|
||||||
{
|
{
|
||||||
// make sure we hear about node kills so we can tell the other nodes
|
// make sure we hear about node kills so we can tell the other nodes
|
||||||
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
|
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
|
||||||
|
@ -48,7 +53,7 @@ void attachAvatarDataToNode(Node* newNode) {
|
||||||
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
|
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
|
||||||
// determine which avatars are included in the packet stream
|
// determine which avatars are included in the packet stream
|
||||||
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
|
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
|
||||||
void broadcastAvatarData() {
|
void AvatarMixer::broadcastAvatarData() {
|
||||||
static QByteArray mixedAvatarByteArray;
|
static QByteArray mixedAvatarByteArray;
|
||||||
|
|
||||||
int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
|
int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
|
||||||
|
@ -57,6 +62,7 @@ void broadcastAvatarData() {
|
||||||
|
|
||||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||||
if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()) {
|
if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()) {
|
||||||
|
++_sumListeners;
|
||||||
|
|
||||||
// reset packet pointers for this node
|
// reset packet pointers for this node
|
||||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
||||||
|
@ -78,7 +84,8 @@ void broadcastAvatarData() {
|
||||||
// at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
// at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
||||||
const float FULL_RATE_DISTANCE = 2.f;
|
const float FULL_RATE_DISTANCE = 2.f;
|
||||||
// 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
|
||||||
if ((distanceToAvatar == 0.f) || (randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) {
|
if ((distanceToAvatar == 0.f) || (randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)
|
||||||
|
* (1 - _performanceThrottlingRatio)) {
|
||||||
QByteArray avatarByteArray;
|
QByteArray avatarByteArray;
|
||||||
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
||||||
avatarByteArray.append(otherAvatar.toByteArray());
|
avatarByteArray.append(otherAvatar.toByteArray());
|
||||||
|
@ -241,11 +248,24 @@ void AvatarMixer::readPendingDatagrams() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarMixer::sendStatsPacket() {
|
||||||
|
QJsonObject statsObject;
|
||||||
|
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
|
||||||
|
|
||||||
|
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
|
||||||
|
statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;
|
||||||
|
|
||||||
|
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||||
|
|
||||||
|
_sumListeners = 0;
|
||||||
|
_numStatFrames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const qint64 AVATAR_IDENTITY_KEYFRAME_MSECS = 5000;
|
const qint64 AVATAR_IDENTITY_KEYFRAME_MSECS = 5000;
|
||||||
const qint64 AVATAR_BILLBOARD_KEYFRAME_MSECS = 5000;
|
const qint64 AVATAR_BILLBOARD_KEYFRAME_MSECS = 5000;
|
||||||
|
|
||||||
void AvatarMixer::run() {
|
void AvatarMixer::run() {
|
||||||
commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
|
ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||||
|
@ -263,12 +283,57 @@ void AvatarMixer::run() {
|
||||||
QElapsedTimer billboardTimer;
|
QElapsedTimer billboardTimer;
|
||||||
billboardTimer.start();
|
billboardTimer.start();
|
||||||
|
|
||||||
|
int usecToSleep = AVATAR_DATA_SEND_INTERVAL_USECS;
|
||||||
|
|
||||||
|
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||||
|
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
|
||||||
|
|
||||||
while (!_isFinished) {
|
while (!_isFinished) {
|
||||||
|
|
||||||
QCoreApplication::processEvents();
|
++_numStatFrames;
|
||||||
|
|
||||||
if (_isFinished) {
|
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
|
||||||
break;
|
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
|
||||||
|
|
||||||
|
const float RATIO_BACK_OFF = 0.02f;
|
||||||
|
|
||||||
|
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
||||||
|
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
||||||
|
|
||||||
|
if (usecToSleep < 0) {
|
||||||
|
usecToSleep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
||||||
|
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_USECS);
|
||||||
|
|
||||||
|
float lastCutoffRatio = _performanceThrottlingRatio;
|
||||||
|
bool hasRatioChanged = false;
|
||||||
|
|
||||||
|
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
|
||||||
|
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
|
||||||
|
// we're struggling - change our min required loudness to reduce some load
|
||||||
|
_performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio));
|
||||||
|
|
||||||
|
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
||||||
|
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
||||||
|
hasRatioChanged = true;
|
||||||
|
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) {
|
||||||
|
// we've recovered and can back off the required loudness
|
||||||
|
_performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF;
|
||||||
|
|
||||||
|
if (_performanceThrottlingRatio < 0) {
|
||||||
|
_performanceThrottlingRatio = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
||||||
|
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
|
||||||
|
hasRatioChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasRatioChanged) {
|
||||||
|
framesSinceCutoffEvent = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastAvatarData();
|
broadcastAvatarData();
|
||||||
|
@ -286,7 +351,13 @@ void AvatarMixer::run() {
|
||||||
billboardTimer.restart();
|
billboardTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
if (_isFinished) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||||
|
|
||||||
if (usecToSleep > 0) {
|
if (usecToSleep > 0) {
|
||||||
usleep(usecToSleep);
|
usleep(usecToSleep);
|
||||||
|
|
|
@ -24,6 +24,17 @@ public slots:
|
||||||
void nodeKilled(SharedNodePointer killedNode);
|
void nodeKilled(SharedNodePointer killedNode);
|
||||||
|
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
|
|
||||||
|
void sendStatsPacket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void broadcastAvatarData();
|
||||||
|
|
||||||
|
float _trailingSleepRatio;
|
||||||
|
float _performanceThrottlingRatio;
|
||||||
|
|
||||||
|
int _sumListeners;
|
||||||
|
int _numStatFrames;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__AvatarMixer__) */
|
#endif /* defined(__hifi__AvatarMixer__) */
|
||||||
|
|
|
@ -824,8 +824,8 @@ void OctreeServer::run() {
|
||||||
// Before we do anything else, create our tree...
|
// Before we do anything else, create our tree...
|
||||||
_tree = createTree();
|
_tree = createTree();
|
||||||
|
|
||||||
// change the logging target name while this is running
|
// use common init to setup common timers and logging
|
||||||
Logging::setTargetName(getMyLoggingServerTargetName());
|
commonInit(getMyLoggingServerTargetName(), getMyNodeType());
|
||||||
|
|
||||||
// Now would be a good time to parse our arguments, if we got them as assignment
|
// Now would be a good time to parse our arguments, if we got them as assignment
|
||||||
if (getPayload().size() > 0) {
|
if (getPayload().size() > 0) {
|
||||||
|
@ -988,14 +988,6 @@ void OctreeServer::run() {
|
||||||
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
|
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
|
||||||
}
|
}
|
||||||
qDebug() << "Now running... started at: " << localBuffer << utcBuffer;
|
qDebug() << "Now running... started at: " << localBuffer << utcBuffer;
|
||||||
|
|
||||||
QTimer* domainServerTimer = new QTimer(this);
|
|
||||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
|
||||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
|
||||||
|
|
||||||
QTimer* silentNodeTimer = new QTimer(this);
|
|
||||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
|
||||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeServer::nodeAdded(SharedNodePointer node) {
|
void OctreeServer::nodeAdded(SharedNodePointer node) {
|
||||||
|
|
|
@ -46,7 +46,6 @@ public:
|
||||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||||
virtual int sendSpecialPacket(const SharedNodePointer& node);
|
virtual int sendSpecialPacket(const SharedNodePointer& node);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _sendEnvironments;
|
bool _sendEnvironments;
|
||||||
bool _sendMinimalEnvironment;
|
bool _sendMinimalEnvironment;
|
||||||
|
|
|
@ -39,3 +39,7 @@
|
||||||
span.port {
|
span.port {
|
||||||
color: #666666;
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stale {
|
||||||
|
color: red;
|
||||||
|
}
|
|
@ -13,7 +13,13 @@ $(document).ready(function(){
|
||||||
var statsTableBody = "";
|
var statsTableBody = "";
|
||||||
|
|
||||||
$.getJSON("/nodes/" + uuid + ".json", function(json){
|
$.getJSON("/nodes/" + uuid + ".json", function(json){
|
||||||
$.each(json, function (key, value) {
|
|
||||||
|
// update the table header with the right node type
|
||||||
|
$('#stats-lead h3').html(json.node_type + " stats (" + uuid + ")");
|
||||||
|
|
||||||
|
delete json.node_type;
|
||||||
|
|
||||||
|
$.each(json, function(key, value) {
|
||||||
statsTableBody += "<tr>";
|
statsTableBody += "<tr>";
|
||||||
statsTableBody += "<td>" + key + "</td>";
|
statsTableBody += "<td>" + key + "</td>";
|
||||||
statsTableBody += "<td>" + value + "</td>";
|
statsTableBody += "<td>" + value + "</td>";
|
||||||
|
@ -21,6 +27,10 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#stats-table tbody').html(statsTableBody);
|
$('#stats-table tbody').html(statsTableBody);
|
||||||
|
}).fail(function(data) {
|
||||||
|
$('#stats-table td').each(function(){
|
||||||
|
$(this).addClass('stale');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -737,8 +737,13 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(matchingUUID);
|
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(matchingUUID);
|
||||||
if (matchingNode) {
|
if (matchingNode) {
|
||||||
// create a QJsonDocument with the stats QJsonObject
|
// create a QJsonDocument with the stats QJsonObject
|
||||||
QJsonDocument statsDocument(reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())
|
QJsonObject statsObject =
|
||||||
->getStatsJSONObject());
|
reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->getStatsJSONObject();
|
||||||
|
|
||||||
|
// add the node type to the JSON data for output purposes
|
||||||
|
statsObject["node_type"] = NodeType::getNodeTypeName(matchingNode->getType()).toLower().replace(' ', '-');
|
||||||
|
|
||||||
|
QJsonDocument statsDocument(statsObject);
|
||||||
|
|
||||||
// send the response
|
// send the response
|
||||||
connection->respond(HTTPConnection::StatusCode200, statsDocument.toJson(), qPrintable(JSON_MIME_TYPE));
|
connection->respond(HTTPConnection::StatusCode200, statsDocument.toJson(), qPrintable(JSON_MIME_TYPE));
|
||||||
|
|
|
@ -69,7 +69,10 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
||||||
_assignmentServerSocket(),
|
_assignmentServerSocket(),
|
||||||
_publicSockAddr(),
|
_publicSockAddr(),
|
||||||
_hasCompletedInitialSTUNFailure(false),
|
_hasCompletedInitialSTUNFailure(false),
|
||||||
_stunRequestsSinceSuccess(0)
|
_stunRequestsSinceSuccess(0),
|
||||||
|
_numCollectedPackets(0),
|
||||||
|
_numCollectedBytes(0),
|
||||||
|
_packetStatTimer()
|
||||||
{
|
{
|
||||||
_nodeSocket.bind(QHostAddress::AnyIPv4, newSocketListenPort);
|
_nodeSocket.bind(QHostAddress::AnyIPv4, newSocketListenPort);
|
||||||
qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort();
|
qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||||
|
@ -79,6 +82,8 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
||||||
|
|
||||||
// clear our NodeList when logout is requested
|
// clear our NodeList when logout is requested
|
||||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||||
|
|
||||||
|
_packetStatTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
||||||
|
@ -162,6 +167,10 @@ qint64 NodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& d
|
||||||
// setup the MD5 hash for source verification in the header
|
// setup the MD5 hash for source verification in the header
|
||||||
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
|
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
|
||||||
|
|
||||||
|
// stat collection for packets
|
||||||
|
++_numCollectedPackets;
|
||||||
|
_numCollectedBytes += datagram.size();
|
||||||
|
|
||||||
return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -854,6 +863,17 @@ SharedNodePointer NodeList::soloNodeOfType(char nodeType) {
|
||||||
return SharedNodePointer();
|
return SharedNodePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeList::getPacketStats(float& packetsPerSecond, float& bytesPerSecond) {
|
||||||
|
packetsPerSecond = (float) _numCollectedPackets / ((float) _packetStatTimer.elapsed() / 1000.0f);
|
||||||
|
bytesPerSecond = (float) _numCollectedBytes / ((float) _packetStatTimer.elapsed() / 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeList::resetPacketStats() {
|
||||||
|
_numCollectedPackets = 0;
|
||||||
|
_numCollectedBytes = 0;
|
||||||
|
_packetStatTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
void NodeList::removeSilentNodes() {
|
void NodeList::removeSilentNodes() {
|
||||||
|
|
||||||
_nodeHashMutex.lock();
|
_nodeHashMutex.lock();
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <unistd.h> // not on windows, not needed for mac or windows
|
#include <unistd.h> // not on windows, not needed for mac or windows
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
#include <QtCore/QMutex>
|
#include <QtCore/QMutex>
|
||||||
#include <QtCore/QSet>
|
#include <QtCore/QSet>
|
||||||
#include <QtCore/QSettings>
|
#include <QtCore/QSettings>
|
||||||
|
@ -120,6 +121,9 @@ public:
|
||||||
unsigned broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
|
unsigned broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
|
||||||
SharedNodePointer soloNodeOfType(char nodeType);
|
SharedNodePointer soloNodeOfType(char nodeType);
|
||||||
|
|
||||||
|
void getPacketStats(float &packetsPerSecond, float &bytesPerSecond);
|
||||||
|
void resetPacketStats();
|
||||||
|
|
||||||
void loadData(QSettings* settings);
|
void loadData(QSettings* settings);
|
||||||
void saveData(QSettings* settings);
|
void saveData(QSettings* settings);
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -155,6 +159,8 @@ private:
|
||||||
|
|
||||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||||
void requestAuthForDomainServer();
|
void requestAuthForDomainServer();
|
||||||
|
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||||
|
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||||
|
|
||||||
NodeHash _nodeHash;
|
NodeHash _nodeHash;
|
||||||
QMutex _nodeHashMutex;
|
QMutex _nodeHashMutex;
|
||||||
|
@ -168,9 +174,9 @@ private:
|
||||||
HifiSockAddr _publicSockAddr;
|
HifiSockAddr _publicSockAddr;
|
||||||
bool _hasCompletedInitialSTUNFailure;
|
bool _hasCompletedInitialSTUNFailure;
|
||||||
unsigned int _stunRequestsSinceSuccess;
|
unsigned int _stunRequestsSinceSuccess;
|
||||||
|
int _numCollectedPackets;
|
||||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
int _numCollectedBytes;
|
||||||
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
QElapsedTimer _packetStatTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__NodeList__) */
|
#endif /* defined(__hifi__NodeList__) */
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
@ -34,7 +35,7 @@ void ThreadedAssignment::setFinished(bool isFinished) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeType) {
|
void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats) {
|
||||||
// change the logging target name while the assignment is running
|
// change the logging target name while the assignment is running
|
||||||
Logging::setTargetName(targetName);
|
Logging::setTargetName(targetName);
|
||||||
|
|
||||||
|
@ -52,6 +53,31 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy
|
||||||
QTimer* silentNodeRemovalTimer = new QTimer(this);
|
QTimer* silentNodeRemovalTimer = new QTimer(this);
|
||||||
connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||||
silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||||
|
|
||||||
|
if (shouldSendStats) {
|
||||||
|
// send a stats packet every 1 second
|
||||||
|
QTimer* statsTimer = new QTimer(this);
|
||||||
|
connect(statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket);
|
||||||
|
statsTimer->start(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadedAssignment::addPacketStatsAndSendStatsPacket(QJsonObject &statsObject) {
|
||||||
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
float packetsPerSecond, bytesPerSecond;
|
||||||
|
nodeList->getPacketStats(packetsPerSecond, bytesPerSecond);
|
||||||
|
nodeList->resetPacketStats();
|
||||||
|
|
||||||
|
statsObject["packets_per_second"] = packetsPerSecond;
|
||||||
|
statsObject["bytes_per_second"] = bytesPerSecond;
|
||||||
|
|
||||||
|
nodeList->sendStatsToDomainServer(statsObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadedAssignment::sendStatsPacket() {
|
||||||
|
QJsonObject statsObject;
|
||||||
|
addPacketStatsAndSendStatsPacket(statsObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
||||||
|
|
|
@ -17,16 +17,18 @@ public:
|
||||||
ThreadedAssignment(const QByteArray& packet);
|
ThreadedAssignment(const QByteArray& packet);
|
||||||
void setFinished(bool isFinished);
|
void setFinished(bool isFinished);
|
||||||
virtual void aboutToFinish() { };
|
virtual void aboutToFinish() { };
|
||||||
|
void addPacketStatsAndSendStatsPacket(QJsonObject& statsObject);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/// threaded run of assignment
|
/// threaded run of assignment
|
||||||
virtual void run() = 0;
|
virtual void run() = 0;
|
||||||
virtual void deleteLater();
|
virtual void deleteLater();
|
||||||
virtual void readPendingDatagrams() = 0;
|
virtual void readPendingDatagrams() = 0;
|
||||||
|
virtual void sendStatsPacket();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr);
|
bool readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr);
|
||||||
void commonInit(const QString& targetName, NodeType_t nodeType);
|
void commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats = true);
|
||||||
bool _isFinished;
|
bool _isFinished;
|
||||||
private slots:
|
private slots:
|
||||||
void checkInWithDomainServerOrExit();
|
void checkInWithDomainServerOrExit();
|
||||||
|
|
Loading…
Reference in a new issue