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

Stats

+
+ + + \ 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