diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index f7953cdf41..9e7cf53e2f 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -67,7 +67,11 @@ void attachNewBufferToNode(Node *newNode) {
AudioMixer::AudioMixer(const QByteArray& packet) :
ThreadedAssignment(packet),
_trailingSleepRatio(1.0f),
- _minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f)
+ _minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
+ _performanceThrottling(0.0f),
+ _numStatFrames(0),
+ _sumListeners(0),
+ _sumMixes(0)
{
}
@@ -95,6 +99,8 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
return;
}
+ ++_sumMixes;
+
glm::quat inverseOrientation = glm::inverse(listeningNodeBuffer->getOrientation());
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
@@ -352,8 +358,21 @@ void AudioMixer::readPendingDatagrams() {
void AudioMixer::sendStatsPacket() {
static QJsonObject statsObject;
- statsObject["trailing_sleep"] = _trailingSleepRatio;
- statsObject["min_audability_threshold"] = _minAudibilityThreshold;
+ statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f;
+ statsObject["performance_throttling"] = _performanceThrottling;
+
+ statsObject["average_listeners_per_frame"] = _sumListeners / (float) _numStatFrames;
+
+ if (_sumListeners > 0) {
+ statsObject["average_mixes_per_listener"] = _sumMixes / (float) _sumListeners;
+ } else {
+ statsObject["average_mixes_per_listener"] = 0.0;
+ }
+
+
+ _sumListeners = 0;
+ _sumMixes = 0;
+ _numStatFrames = 0;
NodeList::getInstance()->sendStatsToDomainServer(statsObject);
}
@@ -382,7 +401,6 @@ void AudioMixer::run() {
+ numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)];
int usecToSleep = BUFFER_SEND_INTERVAL_USECS;
- float audabilityCutoffRatio = 0;
const int TRAILING_AVERAGE_FRAMES = 100;
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
@@ -410,7 +428,7 @@ void AudioMixer::run() {
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) BUFFER_SEND_INTERVAL_USECS);
- float lastCutoffRatio = audabilityCutoffRatio;
+ float lastCutoffRatio = _performanceThrottling;
bool hasRatioChanged = false;
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
@@ -420,27 +438,27 @@ void AudioMixer::run() {
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
// we're struggling - change our min required loudness to reduce some load
- audabilityCutoffRatio = audabilityCutoffRatio + (0.5f * (1.0f - audabilityCutoffRatio));
+ _performanceThrottling = _performanceThrottling + (0.5f * (1.0f - _performanceThrottling));
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
- << lastCutoffRatio << "and is now" << audabilityCutoffRatio;
+ << lastCutoffRatio << "and is now" << _performanceThrottling;
hasRatioChanged = true;
- } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && audabilityCutoffRatio != 0) {
+ } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottling != 0) {
// we've recovered and can back off the required loudness
- audabilityCutoffRatio = audabilityCutoffRatio - RATIO_BACK_OFF;
+ _performanceThrottling = _performanceThrottling - RATIO_BACK_OFF;
- if (audabilityCutoffRatio < 0) {
- audabilityCutoffRatio = 0;
+ if (_performanceThrottling < 0) {
+ _performanceThrottling = 0;
}
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
- << lastCutoffRatio << "and is now" << audabilityCutoffRatio;
+ << lastCutoffRatio << "and is now" << _performanceThrottling;
hasRatioChanged = true;
}
if (hasRatioChanged) {
// set out min audability threshold from the new ratio
- _minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - audabilityCutoffRatio));
+ _minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - _performanceThrottling));
qDebug() << "Minimum audability required to be mixed is now" << _minAudibilityThreshold;
framesSinceCutoffEvent = 0;
@@ -460,6 +478,8 @@ void AudioMixer::run() {
memcpy(clientMixBuffer + numBytesPacketHeader, _clientSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO);
nodeList->writeDatagram(clientMixBuffer, NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader, node);
+
+ ++_sumListeners;
}
}
@@ -470,6 +490,8 @@ void AudioMixer::run() {
}
}
+ ++_numStatFrames;
+
QCoreApplication::processEvents();
if (_isFinished) {
diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h
index 0f88701303..0b1de6f069 100644
--- a/assignment-client/src/audio/AudioMixer.h
+++ b/assignment-client/src/audio/AudioMixer.h
@@ -44,6 +44,10 @@ private:
float _trailingSleepRatio;
float _minAudibilityThreshold;
+ float _performanceThrottling;
+ int _numStatFrames;
+ int _sumListeners;
+ int _sumMixes;
};
#endif /* defined(__hifi__AudioMixer__) */
diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index d4dfa80724..4990de2b55 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -236,7 +236,7 @@ void OctreeServer::initHTTPManager(int port) {
_httpManager = new HTTPManager(port, documentRoot, this, this);
}
-bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
+bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
#ifdef FORCE_CRASH
if (connection->requestOperation() == QNetworkAccessManager::GetOperation
@@ -259,9 +259,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
bool showStats = false;
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
- if (path == "/") {
+ if (url.path() == "/") {
showStats = true;
- } else if (path == "/resetStats") {
+ } else if (url.path() == "/resetStats") {
_octreeInboundPacketProcessor->resetStats();
resetSendingStats();
showStats = true;
diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h
index 12091170d9..2664499b6a 100644
--- a/assignment-client/src/octree/OctreeServer.h
+++ b/assignment-client/src/octree/OctreeServer.h
@@ -97,7 +97,7 @@ public:
static void trackPacketSendingTime(float time);
static float getAveragePacketSendingTime() { return _averagePacketSendingTime.getAverage(); }
- bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
+ bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
virtual void aboutToFinish();
diff --git a/domain-server/resources/web/footer.html b/domain-server/resources/web/footer.html
index 08ea9fba66..d1a3fc29e8 100644
--- a/domain-server/resources/web/footer.html
+++ b/domain-server/resources/web/footer.html
@@ -1,3 +1,3 @@
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html
index 83d7ae5c23..2be603b00e 100644
--- a/domain-server/resources/web/header.html
+++ b/domain-server/resources/web/header.html
@@ -4,8 +4,8 @@
domain-server
-
-
+
+
\ No newline at end of file
diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js
index e32f78a63e..ae5095592b 100644
--- a/domain-server/resources/web/js/tables.js
+++ b/domain-server/resources/web/js/tables.js
@@ -7,7 +7,7 @@ $(document).ready(function(){
$.each(json.nodes, function (uuid, data) {
nodesTableBody += "
";
nodesTableBody += "" + data.type + " | ";
- nodesTableBody += "" + uuid + " | ";
+ nodesTableBody += "" + uuid + " | ";
nodesTableBody += "" + (data.pool ? data.pool : "") + " | ";
nodesTableBody += "" + data.public.ip + ":" + data.public.port + " | ";
nodesTableBody += "" + data.local.ip + ":" + data.local.port + " | ";
diff --git a/domain-server/resources/web/stats/index.shtml b/domain-server/resources/web/stats/index.shtml
new file mode 100644
index 0000000000..62115d18fe
--- /dev/null
+++ b/domain-server/resources/web/stats/index.shtml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/domain-server/resources/web/stats/js/stats.js b/domain-server/resources/web/stats/js/stats.js
new file mode 100644
index 0000000000..e82668ea9e
--- /dev/null
+++ b/domain-server/resources/web/stats/js/stats.js
@@ -0,0 +1,31 @@
+function qs(key) {
+ key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); // escape RegEx meta chars
+ var match = location.search.match(new RegExp("[?&]"+key+"=([^&]+)(&|$)"));
+ return match && decodeURIComponent(match[1].replace(/\+/g, " "));
+}
+
+$(document).ready(function(){
+ // setup a function to grab the nodeStats
+ function getNodeStats() {
+
+ var uuid = qs("uuid");
+
+ var statsTableBody = "";
+
+ $.getJSON("/nodes/" + uuid + ".json", function(json){
+ $.each(json, function (key, value) {
+ statsTableBody += "
";
+ statsTableBody += "" + key + " | ";
+ statsTableBody += "" + value + " | ";
+ statsTableBody += "
";
+ });
+
+ $('#stats-table tbody').html(statsTableBody);
+ });
+ }
+
+ // do the first GET on page load
+ getNodeStats();
+ // grab the new assignments JSON every second
+ var getNodeStatsInterval = setInterval(getNodeStats, 1000);
+});
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 1b867501d2..0a8b70a673 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -651,14 +651,14 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
return nodeJson;
}
-bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
+bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
const QString JSON_MIME_TYPE = "application/json";
const QString URI_ASSIGNMENT = "/assignment";
const QString URI_NODES = "/nodes";
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
- if (path == "/assignments.json") {
+ if (url.path() == "/assignments.json") {
// user is asking for json list of assignments
// setup the JSON
@@ -702,7 +702,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
// we've processed this request
return true;
- } else if (path == QString("%1.json").arg(URI_NODES)) {
+ } else if (url.path() == QString("%1.json").arg(URI_NODES)) {
// setup the JSON
QJsonObject rootJSON;
QJsonObject nodesJSON;
@@ -727,10 +727,10 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
return true;
} else {
const QString NODE_REGEX_STRING =
- QString("\\%1\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\/?$").arg(URI_NODES);
+ QString("\\%1\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}).json\\/?$").arg(URI_NODES);
QRegExp nodeShowRegex(NODE_REGEX_STRING);
- if (nodeShowRegex.indexIn(path) != -1) {
+ if (nodeShowRegex.indexIn(url.path()) != -1) {
QUuid matchingUUID = QUuid(nodeShowRegex.cap(1));
// see if we have a node that matches this ID
@@ -740,7 +740,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
QJsonDocument statsDocument(reinterpret_cast
(matchingNode->getLinkedData())
->getStatsJSONObject());
- // send the resposne
+ // send the response
connection->respond(HTTPConnection::StatusCode200, statsDocument.toJson(), qPrintable(JSON_MIME_TYPE));
// tell the caller we processed the request
@@ -749,7 +749,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
}
}
} else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {
- if (path == URI_ASSIGNMENT) {
+ if (url.path() == URI_ASSIGNMENT) {
// this is a script upload - ask the HTTPConnection to parse the form data
QList formData = connection->parseFormData();
@@ -796,11 +796,11 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
return true;
}
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
- if (path.startsWith(URI_NODES)) {
+ if (url.path().startsWith(URI_NODES)) {
// this is a request to DELETE a node by UUID
// pull the UUID from the url
- QUuid deleteUUID = QUuid(path.mid(URI_NODES.size() + sizeof('/')));
+ QUuid deleteUUID = QUuid(url.path().mid(URI_NODES.size() + sizeof('/')));
if (!deleteUUID.isNull()) {
SharedNodePointer nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 2d253cc41c..597be7f50d 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -30,7 +30,7 @@ public:
bool requiresAuthentication() const { return !_nodeAuthenticationURL.isEmpty(); }
- bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
+ bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
void exit(int retCode = 0);
diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp
index 50ce72e0cd..8fc0a25dca 100755
--- a/libraries/embedded-webserver/src/HTTPConnection.cpp
+++ b/libraries/embedded-webserver/src/HTTPConnection.cpp
@@ -180,7 +180,7 @@ void HTTPConnection::readHeaders() {
QByteArray clength = _requestHeaders.value("Content-Length");
if (clength.isEmpty()) {
- _parentManager->handleHTTPRequest(this, _requestUrl.path());
+ _parentManager->handleHTTPRequest(this, _requestUrl);
} else {
_requestContent.resize(clength.toInt());
diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp
index a217555a78..d106b6df59 100755
--- a/libraries/embedded-webserver/src/HTTPManager.cpp
+++ b/libraries/embedded-webserver/src/HTTPManager.cpp
@@ -15,15 +15,15 @@
#include "HTTPConnection.h"
#include "HTTPManager.h"
-bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
- if (_requestHandler && _requestHandler->handleHTTPRequest(connection, path)) {
+bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
+ if (_requestHandler && _requestHandler->handleHTTPRequest(connection, url)) {
// this request was handled by our _requestHandler object
// so we don't need to attempt to do so in the document root
return true;
}
// check to see if there is a file to serve from the document root for this path
- QString subPath = path;
+ QString subPath = url.path();
// remove any slash at the beginning of the path
if (subPath.startsWith('/')) {
@@ -38,6 +38,10 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p
// this could be a directory with a trailing slash
// send a redirect to the path with a slash so we can
QString redirectLocation = '/' + subPath + '/';
+
+ if (!url.query().isEmpty()) {
+ redirectLocation += "?" + url.query();
+ }
QHash redirectHeader;
redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8());
diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h
index e3b44e7cdc..a8f9d723fa 100755
--- a/libraries/embedded-webserver/src/HTTPManager.h
+++ b/libraries/embedded-webserver/src/HTTPManager.h
@@ -20,7 +20,7 @@ class HTTPConnection;
class HTTPRequestHandler {
public:
/// Handles an HTTP request.
- virtual bool handleHTTPRequest(HTTPConnection* connection, const QString& path) = 0;
+ virtual bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url) = 0;
};
/// Handles HTTP connections
@@ -30,7 +30,7 @@ public:
/// Initializes the manager.
HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0);
- bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
+ bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
protected slots:
/// Accepts all pending connections