From 402f88ad78427d87625605217e9c24c7dedfeafc Mon Sep 17 00:00:00 2001 From: David Heimann Date: Mon, 19 Nov 2018 17:40:36 -0500 Subject: [PATCH 1/3] Pre-test bandwidth detection base Addresses LIMELIGHT-506 --- .../bandwidthDetection/bandwidthDetection.js | 204 ++++++++++++++++++ src/page/test/bandwidthDetection/index.html | 24 +++ src/page/test/bandwidthDetection/index.js | 186 ++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 src/page/test/bandwidthDetection/bandwidthDetection.js create mode 100644 src/page/test/bandwidthDetection/index.html create mode 100644 src/page/test/bandwidthDetection/index.js diff --git a/src/page/test/bandwidthDetection/bandwidthDetection.js b/src/page/test/bandwidthDetection/bandwidthDetection.js new file mode 100644 index 00000000..a4303890 --- /dev/null +++ b/src/page/test/bandwidthDetection/bandwidthDetection.js @@ -0,0 +1,204 @@ + +//***DOWNLOAD*** + +function checkDownloadSpeed (baseURL, maxSeconds) { + + return new Promise( (resolve, reject) => { + const now = Date.now(); + const maxMillis = Math.floor(maxSeconds * 1000); + + const data = { + beganAt: now, + returnBy: now + maxMillis, + url: "http://" + baseURL + ":5080/bandwidthdetection/detect", + downloadResults: [], + requests:[], + resolve: resolve, + reject: reject + } + + for (var i = 0; i < 4; i++) { + createDownloader(data); + } + + data.intervalHandle = setInterval( () =>{ downloadLoop(data); }, 50 ); + }); +} + +function createDownloader(data) { + const request = new XMLHttpRequest(); + request.onreadystatechange = () => { + if(request.readyState === 4){ + if(request.status === 200){ //successful - add up the speed results as appropriate + data.downloadResults.push(request.response.size); + } else{ //unsuccesful, ignore the attempt, I guess? We'll try again plenty, I'm sure. + console.warn("Download detection failed with the following status: " + request.statusText); + } + removeRequest(data, request); + } + }; + request.ontimeout = () => { //Pencils down, time to give the data back. + removeRequest(data, request); + } + + data.requests.push(request); + + request.open("GET", data.url, true); + request.responseType = "blob"; + request.timeout = 5000; + request.send(null); +} + +function downloadLoop(data) { + const = now = Date.now(); + + if (now < data.returnBy) { //We have more time, keep downloading + while(data.requests.length < 4){ + createDownloader(data); + } + } else { // Time's up. Once everything finishes, return the results + if(data.requests.length < 1){ + clearInterval(data.intervalHandle); + const totalSeconds = (Date.now() - data.beganAt) / 1000.0; + let totalBytes = 0; + for (var i = 0; i < data.downloadResults.length; i++) { + totalBytes += data.downloadResults[i]; + } + if(totalBytes == 0){ + data.reject("There was a problem with the download test, the server sent no data"); + } + const kBpS = (totalBytes / 1024.0) / totalSeconds; + console.log("Download detection finished with speed result of " + kBpS + "KBpS"); + data.resolve({ download: kBpS }); + } + } +} + +//***UPLOAD*** + +function checkUploadSpeed (baseURL, maxSeconds) { + return new Promise( (resolve, reject) => { + const fatString = fortyKiloString(); + const now = Date.now(); + const maxMillis = Math.floor(maxSeconds * 1000); + + const data = { + beganAt: now, + returnBy: now + maxMillis, + url: "http://" + baseURL + ":5080/bandwidthdetection/detect", + upResults: [], + requests:[], + resolve: resolve, + reject: reject, + payload: fatString + } + + for (var i = 0; i < 4; i++) { + createUploader(data); + } + + data.intervalHandle = setInterval( () =>{ uploadLoop(data); }, 50 ); + }); +} + +function createUploader(data) { + const request = new XMLHttpRequest(); + request.onreadystatechange = () => { + if(request.readyState === 4){ + if(request.status === 200){ //successful - add up the speed results as appropriate + data.uploadResults.push(40 * 1024); //upload is always 40KB, might as well re-use logic + } else{ //unsuccesful, ignore the attempt, we'll try again plenty + console.warn("Upload detection failed with the following status: " + request.statusText); + } + removeRequest(data, request); + } + }; + request.ontimeout = () => { + removeRequest(data, request); + } + + data.requests.push(request); + + request.open("GET", data.url, true); + request.responseType = "blob"; + request.timeout = 5000; + //prefix keeps the data unique, saved bulk spares us the ~160ms generation time + request.send( [uploadPrefix(), data.payload, uploadPrefix()].join('') ); +} + +function uploadLoop(data) { + const now = Date.now(); + + if (now < data.returnBy) { //We have more time, keep uploading + while(data.requests.length < 4){ + createUploader(data); + } + } else { // Time's up. Once everything finishes, return the results + if(data.requests.length < 1){ + clearInterval(data.intervalHandle); + const totalSeconds = (Date.now() - data.beganAt) / 1000.0; + let totalBytes = 0; + for (var i = 0; i < data.uploadResults.length; i++) { + totalBytes += data.uploadResults[i]; + } + if(totalBytes == 0){ + data.reject("There was a problem with the upload test, no data reached the server"); + } + const kBpS = (totalBytes / 1024.0) / totalSeconds; + console.log("Upload detection finished with speed result of " + kBpS + "KBpS"); + data.resolve({ upload: kBpS }); + } + } +} + +//***BOTH*** + +function checkSpeeds (baseURL, maxSeconds) { + + return new Promise(function(resolve, reject) { + + const halfMaxSeconds = maxSeconds / 2.0; + const ret = { upload: -1, download: -1 }; + + checkDownloadSpeed(baseURL, halfMaxSeconds) + .then(result => { + ret.download = result; + return checkUploadSpeed(baseURL, halfMaxSeconds); + }) + .then(result => { + ret.upload = result; + resolve(ret); + }) + .catch(error => { + reject(error); + }) + }); +} + +//***SUPPORT*** + +function removeRequest(data, request) { + for (let i = 0; i < data.requests.length; i++) { + if(data.requests[i] === request){ + data.requests.splice(i, 1); + return; + } + } +} + +var allowedChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +function fortyKiloString() { //not exactly 40KB - expected to have another random 30 to front and back + const out = []; + for (let i = 0; i < 40900; i++){ + out.push( allowedChar.charAt(Math.floor(Math.random() * allowedChar.length)) ); + } + return out.join(''); +} + +function uploadPrefix() { + const out = []; + for (let i = 0; i < 30; i++){ + out.push( allowedChar.charAt(Math.floor(Math.random() * allowedChar.length)) ); + } + return out.join(''); +} diff --git a/src/page/test/bandwidthDetection/index.html b/src/page/test/bandwidthDetection/index.html new file mode 100644 index 00000000..8bef558b --- /dev/null +++ b/src/page/test/bandwidthDetection/index.html @@ -0,0 +1,24 @@ + + + + {{> meta title='Bandwidth Detection Test'}} + {{> header-scripts}} + {{> header-stylesheets}} + + +
+ {{> version }} + {{> settings-link}} + {{> test-info testTitle='Bandwidth Detection Test'}} + {{> status-field-publisher}} + {{> statistics-field}} +
+ +
+
+

+ {{> body-scripts}} + + + + diff --git a/src/page/test/bandwidthDetection/index.js b/src/page/test/bandwidthDetection/index.js new file mode 100644 index 00000000..1fa094da --- /dev/null +++ b/src/page/test/bandwidthDetection/index.js @@ -0,0 +1,186 @@ +(function(window, document, red5prosdk) { + 'use strict'; + + var serverSettings = (function() { + var settings = sessionStorage.getItem('r5proServerSettings'); + try { + return JSON.parse(settings); + } + catch (e) { + console.error('Could not read server settings from sessionstorage: ' + e.message); + } + return {}; + })(); + + var configuration = (function () { + var conf = sessionStorage.getItem('r5proTestBed'); + try { + return JSON.parse(conf); + } + catch (e) { + console.error('Could not read testbed configuration from sessionstorage: ' + e.message); + } + return {} + })(); + + red5prosdk.setLogLevel(configuration.verboseLogging ? red5prosdk.LOG_LEVELS.TRACE : red5prosdk.LOG_LEVELS.WARN); + + + var targetPublisher; + + var updateStatusFromEvent = window.red5proHandlePublisherEvent; // defined in src/template/partial/status-field-publisher.hbs + var streamTitle = document.getElementById('stream-title'); + var statisticsField = document.getElementById('statistics-field'); + + var protocol = serverSettings.protocol; + var isSecure = protocol == 'https'; + function getSocketLocationFromProtocol () { + return !isSecure + ? {protocol: 'ws', port: serverSettings.wsport} + : {protocol: 'wss', port: serverSettings.wssport}; + } + + function onBitrateUpdate (bitrate, packetsSent) { + statisticsField.innerText = 'Bitrate: ' + Math.floor(bitrate) + '. Packets Sent: ' + packetsSent + '.'; + } + + function onPublisherEvent (event) { + console.log('[Red5ProPublisher] ' + event.type + '.'); + updateStatusFromEvent(event); + } + function onPublishFail (message) { + console.error('[Red5ProPublisher] Publish Error :: ' + message); + } + function onPublishSuccess (publisher) { + console.log('[Red5ProPublisher] Publish Complete.'); + try { + window.trackBitrate(publisher.getPeerConnection(), onBitrateUpdate); + } + catch (e) { + // no tracking for you! + } + } + function onUnpublishFail (message) { + console.error('[Red5ProPublisher] Unpublish Error :: ' + message); + } + function onUnpublishSuccess () { + console.log('[Red5ProPublisher] Unpublish Complete.'); + } + + function getAuthenticationParams () { + var auth = configuration.authentication; + return auth && auth.enabled + ? { + connectionParams: { + username: auth.username, + password: auth.password + } + } + : {}; + } + + function getUserMediaConfiguration () { + return { + mediaConstraints: { + audio: configuration.useAudio ? configuration.mediaConstraints.audio : false, + video: configuration.useVideo ? configuration.mediaConstraints.video : false + } + }; + } + + function getRTMPMediaConfiguration () { + return { + mediaConstraints: { + audio: configuration.useAudio ? configuration.mediaConstraints.audio : false, + video: configuration.useVideo ? { + width: configuration.cameraWidth, + height: configuration.cameraHeight + } : false + } + } + } + + function unpublish () { + return new Promise(function (resolve, reject) { + var publisher = targetPublisher; + publisher.unpublish() + .then(function () { + onUnpublishSuccess(); + resolve(); + }) + .catch(function (error) { + var jsonError = typeof error === 'string' ? error : JSON.stringify(error, 2, null); + onUnpublishFail('Unmount Error ' + jsonError); + reject(error); + }); + }); + } + + var config = Object.assign({}, + configuration, + getAuthenticationParams(), + getUserMediaConfiguration()); + + var rtcConfig = Object.assign({}, config, { + protocol: getSocketLocationFromProtocol().protocol, + port: getSocketLocationFromProtocol().port, + streamName: config.stream1, + }); + var rtmpConfig = Object.assign({}, config, { + protocol: 'rtmp', + port: serverSettings.rtmpport, + streamName: config.stream1, + backgroundColor: '#000000', + swf: '../../lib/red5pro/red5pro-publisher.swf', + swfobjectURL: '../../lib/swfobject/swfobject.js', + productInstallURL: '../../lib/swfobject/playerProductInstall.swf' + }, getRTMPMediaConfiguration()); + var publishOrder = config.publisherFailoverOrder + .split(',') + .map(function (item) { + return item.trim() + }); + + if (window.query('view')) { + publishOrder = [window.query('view')]; + } + + checkSpeeds(config.host, 5.0) + .then( result => { + document.getElementById("speed-check-print").innerText = "Bandwidth Detection complete," + + "Uploading at: " + result.upload + " and downloading at: " + result.download; + + var publisher = new red5prosdk.Red5ProPublisher(); + return publisher.setPublishOrder(publishOrder) + }) + .init({ + rtc: rtcConfig, + rtmp: rtmpConfig + }) + .then(function (publisherImpl) { + streamTitle.innerText = configuration.stream1; + targetPublisher = publisherImpl; + targetPublisher.on('*', onPublisherEvent); + return targetPublisher.publish(); + }) + .then(function () { + onPublishSuccess(targetPublisher); + }) + .catch(function (error) { + var jsonError = typeof error === 'string' ? error : JSON.stringify(error, null, 2); + console.error('[Red5ProPublisher] :: Error in publishing - ' + jsonError); + onPublishFail(jsonError); + }); + + window.addEventListener('beforeunload', function() { + function clearRefs () { + if (targetPublisher) { + targetPublisher.off('*', onPublisherEvent); + } + targetPublisher = undefined; + } + unpublish().then(clearRefs).catch(clearRefs); + window.untrackBitrate(); + }); + +})(this, document, window.red5prosdk); From 368ec6439d54a23b5e566e39081382a0e33b9b9c Mon Sep 17 00:00:00 2001 From: David Heimann Date: Tue, 20 Nov 2018 17:58:05 -0500 Subject: [PATCH 2/3] Test ready --- .../bandwidthDetection/bandwidthDetection.js | 73 +++++++++++-------- src/page/test/bandwidthDetection/index.html | 2 +- src/page/test/bandwidthDetection/index.js | 45 ++++++------ src/page/testbed-menu.html | 1 + 4 files changed, 69 insertions(+), 52 deletions(-) diff --git a/src/page/test/bandwidthDetection/bandwidthDetection.js b/src/page/test/bandwidthDetection/bandwidthDetection.js index a4303890..0945f95c 100644 --- a/src/page/test/bandwidthDetection/bandwidthDetection.js +++ b/src/page/test/bandwidthDetection/bandwidthDetection.js @@ -1,8 +1,13 @@ //***DOWNLOAD*** +const CONCURRENT_CONNECTIONS = 4; + function checkDownloadSpeed (baseURL, maxSeconds) { + const isSecure = window.location.protocol.includes("https"); + baseURL = isSecure ? "https://" + baseURL : "http://" + baseURL + ":5080"; + return new Promise( (resolve, reject) => { const now = Date.now(); const maxMillis = Math.floor(maxSeconds * 1000); @@ -10,18 +15,18 @@ function checkDownloadSpeed (baseURL, maxSeconds) { const data = { beganAt: now, returnBy: now + maxMillis, - url: "http://" + baseURL + ":5080/bandwidthdetection/detect", + url: baseURL + "/bandwidthdetection/detect", downloadResults: [], requests:[], resolve: resolve, reject: reject - } + }; - for (var i = 0; i < 4; i++) { + for (var i = 0; i < CONCURRENT_CONNECTIONS; i++) { createDownloader(data); } - data.intervalHandle = setInterval( () =>{ downloadLoop(data); }, 50 ); + // data.intervalHandle = setInterval( () =>{ downloadLoop(data); }, 50 ); }); } @@ -35,11 +40,14 @@ function createDownloader(data) { console.warn("Download detection failed with the following status: " + request.statusText); } removeRequest(data, request); + downloadLoop(data); } }; request.ontimeout = () => { //Pencils down, time to give the data back. + console.warn("Download detection timed out"); removeRequest(data, request); - } + downloadLoop(data); + }; data.requests.push(request); @@ -50,15 +58,15 @@ function createDownloader(data) { } function downloadLoop(data) { - const = now = Date.now(); + const now = Date.now(); if (now < data.returnBy) { //We have more time, keep downloading - while(data.requests.length < 4){ + while(data.requests.length < CONCURRENT_CONNECTIONS){ createDownloader(data); } } else { // Time's up. Once everything finishes, return the results if(data.requests.length < 1){ - clearInterval(data.intervalHandle); + // clearInterval(data.intervalHandle); const totalSeconds = (Date.now() - data.beganAt) / 1000.0; let totalBytes = 0; for (var i = 0; i < data.downloadResults.length; i++) { @@ -67,9 +75,10 @@ function downloadLoop(data) { if(totalBytes == 0){ data.reject("There was a problem with the download test, the server sent no data"); } - const kBpS = (totalBytes / 1024.0) / totalSeconds; - console.log("Download detection finished with speed result of " + kBpS + "KBpS"); - data.resolve({ download: kBpS }); + console.log("Downloaded " + totalBytes + " bytes in " + totalSeconds + " seconds"); + const kbpS = ((totalBytes * 8) / 1024.0) / totalSeconds; + console.log("Download detection finished with speed result of " + kbpS + "KBpS"); + data.resolve({ download: kbpS }); } } } @@ -77,6 +86,9 @@ function downloadLoop(data) { //***UPLOAD*** function checkUploadSpeed (baseURL, maxSeconds) { + const isSecure = window.location.protocol.includes("https"); + baseURL = isSecure ? "https://" + baseURL : "http://" + baseURL + ":5080"; + return new Promise( (resolve, reject) => { const fatString = fortyKiloString(); const now = Date.now(); @@ -85,19 +97,19 @@ function checkUploadSpeed (baseURL, maxSeconds) { const data = { beganAt: now, returnBy: now + maxMillis, - url: "http://" + baseURL + ":5080/bandwidthdetection/detect", - upResults: [], + url: baseURL + "/bandwidthdetection/detect", + uploadResults: [], requests:[], resolve: resolve, reject: reject, payload: fatString - } + }; - for (var i = 0; i < 4; i++) { + for (var i = 0; i < CONCURRENT_CONNECTIONS; i++) { createUploader(data); } - data.intervalHandle = setInterval( () =>{ uploadLoop(data); }, 50 ); + // data.intervalHandle = setInterval( () =>{ uploadLoop(data); }, 50 ); }); } @@ -106,23 +118,25 @@ function createUploader(data) { request.onreadystatechange = () => { if(request.readyState === 4){ if(request.status === 200){ //successful - add up the speed results as appropriate - data.uploadResults.push(40 * 1024); //upload is always 40KB, might as well re-use logic + data.uploadResults.push(40 * 1024); //upload is always 40KB, might as well re-use logic from download } else{ //unsuccesful, ignore the attempt, we'll try again plenty console.warn("Upload detection failed with the following status: " + request.statusText); } removeRequest(data, request); + uploadLoop(data); } }; request.ontimeout = () => { + console.warn("Upload detection timed out"); removeRequest(data, request); - } + uploadLoop(data); + }; data.requests.push(request); - request.open("GET", data.url, true); - request.responseType = "blob"; + request.open("POST", data.url, true); request.timeout = 5000; - //prefix keeps the data unique, saved bulk spares us the ~160ms generation time + //prefix keeps the data unique, saved bulk spares us the generation time request.send( [uploadPrefix(), data.payload, uploadPrefix()].join('') ); } @@ -130,12 +144,12 @@ function uploadLoop(data) { const now = Date.now(); if (now < data.returnBy) { //We have more time, keep uploading - while(data.requests.length < 4){ + while(data.requests.length < CONCURRENT_CONNECTIONS){ createUploader(data); } } else { // Time's up. Once everything finishes, return the results if(data.requests.length < 1){ - clearInterval(data.intervalHandle); + // clearInterval(data.intervalHandle); const totalSeconds = (Date.now() - data.beganAt) / 1000.0; let totalBytes = 0; for (var i = 0; i < data.uploadResults.length; i++) { @@ -144,9 +158,10 @@ function uploadLoop(data) { if(totalBytes == 0){ data.reject("There was a problem with the upload test, no data reached the server"); } - const kBpS = (totalBytes / 1024.0) / totalSeconds; - console.log("Upload detection finished with speed result of " + kBpS + "KBpS"); - data.resolve({ upload: kBpS }); + console.log("Uploaded " + totalBytes + " bytes in " + totalSeconds + " seconds"); + const kbpS = ((totalBytes * 8) / 1024.0) / totalSeconds; + console.log("Upload detection finished with speed result of " + kbpS + "KbpS"); + data.resolve({ upload: kbpS }); } } } @@ -162,16 +177,16 @@ function checkSpeeds (baseURL, maxSeconds) { checkDownloadSpeed(baseURL, halfMaxSeconds) .then(result => { - ret.download = result; + ret.download = result.download; return checkUploadSpeed(baseURL, halfMaxSeconds); }) .then(result => { - ret.upload = result; + ret.upload = result.upload; resolve(ret); }) .catch(error => { reject(error); - }) + }); }); } diff --git a/src/page/test/bandwidthDetection/index.html b/src/page/test/bandwidthDetection/index.html index 8bef558b..6a871524 100644 --- a/src/page/test/bandwidthDetection/index.html +++ b/src/page/test/bandwidthDetection/index.html @@ -16,7 +16,7 @@ -

+

Speed test in progress...

{{> body-scripts}} diff --git a/src/page/test/bandwidthDetection/index.js b/src/page/test/bandwidthDetection/index.js index 1fa094da..3091187b 100644 --- a/src/page/test/bandwidthDetection/index.js +++ b/src/page/test/bandwidthDetection/index.js @@ -148,29 +148,30 @@ checkSpeeds(config.host, 5.0) .then( result => { document.getElementById("speed-check-print").innerText = "Bandwidth Detection complete," + - "Uploading at: " + result.upload + " and downloading at: " + result.download; - + "Uploading at: " + (Math.round(result.upload*100)/100.0) + "KbpS and downloading at: " + + (Math.round(result.download*100)/100.0) + "KbpS"; + var publisher = new red5prosdk.Red5ProPublisher(); - return publisher.setPublishOrder(publishOrder) - }) - .init({ - rtc: rtcConfig, - rtmp: rtmpConfig - }) - .then(function (publisherImpl) { - streamTitle.innerText = configuration.stream1; - targetPublisher = publisherImpl; - targetPublisher.on('*', onPublisherEvent); - return targetPublisher.publish(); - }) - .then(function () { - onPublishSuccess(targetPublisher); - }) - .catch(function (error) { - var jsonError = typeof error === 'string' ? error : JSON.stringify(error, null, 2); - console.error('[Red5ProPublisher] :: Error in publishing - ' + jsonError); - onPublishFail(jsonError); - }); + publisher.setPublishOrder(publishOrder) + .init({ + rtc: rtcConfig, + rtmp: rtmpConfig + }) + .then(function (publisherImpl) { + streamTitle.innerText = configuration.stream1; + targetPublisher = publisherImpl; + targetPublisher.on('*', onPublisherEvent); + return targetPublisher.publish(); + }) + .then(function () { + onPublishSuccess(targetPublisher); + }) + .catch(function (error) { + var jsonError = typeof error === 'string' ? error : JSON.stringify(error, null, 2); + console.error('[Red5ProPublisher] :: Error in publishing - ' + jsonError); + onPublishFail(jsonError); + }); + }); window.addEventListener('beforeunload', function() { function clearRefs () { diff --git a/src/page/testbed-menu.html b/src/page/testbed-menu.html index d255197b..7e997585 100644 --- a/src/page/testbed-menu.html +++ b/src/page/testbed-menu.html @@ -37,6 +37,7 @@

Red5 Pro HTML Testbed

  • Publish - Image Capture (WebRTC)
  • Publish - ScreenShare (WebRTC)
  • Publish - Server Call (WebRTC)
  • +
  • Publish - Bandwidth Detection
  • Subscribe
  • Subscribe - Authentication
  • Subscribe - Remote Call
  • From ffaa511d8083ea4fca304c024adce6931fb5b947 Mon Sep 17 00:00:00 2001 From: David Heimann Date: Wed, 21 Nov 2018 14:00:44 -0500 Subject: [PATCH 3/3] Added readme for Bandwidth Detection test --- src/page/test/bandwidthDetection/README.md | 27 +++++++++++++++++++ .../bandwidthDetection/bandwidthDetection.js | 27 +++++-------------- 2 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 src/page/test/bandwidthDetection/README.md diff --git a/src/page/test/bandwidthDetection/README.md b/src/page/test/bandwidthDetection/README.md new file mode 100644 index 00000000..2552181c --- /dev/null +++ b/src/page/test/bandwidthDetection/README.md @@ -0,0 +1,27 @@ +# Bandwidth Detection using Red5 Pro + +This is an example of using the bandwidth detection webapp on a Red5 Pro server to determine the available bandwidth for streaming. + +Note that because this speed test is between the end-user's browser and the Red5 Pro server, the speed measured won't be the same as that from other sources. Not only is the user's connection speed tested, the specific speed that can be expected between the client and server is tested. Since all of the factors that will affect the connection - including distance, ssh encryption - if any, and server load - will also affect responses to this speed test, it can give you a better idea of a realistic streaming quality limit than a generic optimized test to a nearby server. + +**Please refer to the [Basic Publisher Documentation](../publisher/README.md) to learn more about the basic setup.** + +## Example Code +- **[index.html](index.html)** +- **[index.js](index.js)** +- **[bandwidthDetection.js](bandwidthDetection.js)** + +# Running The Speed Test +There are three functions available for testing different speeds: `checkDownloadSpeed`, `checkUploadSpeed`, and `checkSpeeds` which will call both in sequence and return the combined result. They each take a url, and a maximum time to spend on the test - in seconds. Note that for checkSpeeds, this time is split evenly between the upload and download tests. This returns a Promise which resolves with an object, holing the results as floats in its `upload` and/or `download` property, as appropriate. + +``` +checkSpeeds(config.host, 5.0) + .then( result => { + document.getElementById("speed-check-print").innerText = "Bandwidth Detection complete," + + "Uploading at: " + (Math.round( result.upload *100)/100.0) + "KbpS and downloading at: " + + (Math.round( result.download *100)/100.0) + "KbpS"; +``` + +[index.js #148](index.js#L148) + +From there, the results can be used to determine the target bandwidth for the stream, or to determine if the client has the bandwidth to successfully subscribe to a stream. Also be aware that this check should be done before starting any streams, as the concentration of data can conflict with any streams in progress. diff --git a/src/page/test/bandwidthDetection/bandwidthDetection.js b/src/page/test/bandwidthDetection/bandwidthDetection.js index 0945f95c..b4d78f45 100644 --- a/src/page/test/bandwidthDetection/bandwidthDetection.js +++ b/src/page/test/bandwidthDetection/bandwidthDetection.js @@ -25,8 +25,6 @@ function checkDownloadSpeed (baseURL, maxSeconds) { for (var i = 0; i < CONCURRENT_CONNECTIONS; i++) { createDownloader(data); } - - // data.intervalHandle = setInterval( () =>{ downloadLoop(data); }, 50 ); }); } @@ -66,7 +64,6 @@ function downloadLoop(data) { } } else { // Time's up. Once everything finishes, return the results if(data.requests.length < 1){ - // clearInterval(data.intervalHandle); const totalSeconds = (Date.now() - data.beganAt) / 1000.0; let totalBytes = 0; for (var i = 0; i < data.downloadResults.length; i++) { @@ -86,11 +83,11 @@ function downloadLoop(data) { //***UPLOAD*** function checkUploadSpeed (baseURL, maxSeconds) { + const isSecure = window.location.protocol.includes("https"); baseURL = isSecure ? "https://" + baseURL : "http://" + baseURL + ":5080"; return new Promise( (resolve, reject) => { - const fatString = fortyKiloString(); const now = Date.now(); const maxMillis = Math.floor(maxSeconds * 1000); @@ -101,15 +98,12 @@ function checkUploadSpeed (baseURL, maxSeconds) { uploadResults: [], requests:[], resolve: resolve, - reject: reject, - payload: fatString + reject: reject }; for (var i = 0; i < CONCURRENT_CONNECTIONS; i++) { createUploader(data); } - - // data.intervalHandle = setInterval( () =>{ uploadLoop(data); }, 50 ); }); } @@ -136,8 +130,8 @@ function createUploader(data) { request.open("POST", data.url, true); request.timeout = 5000; - //prefix keeps the data unique, saved bulk spares us the generation time - request.send( [uploadPrefix(), data.payload, uploadPrefix()].join('') ); + //send 40kB of random data + request.send( fortyKiloString() ); } function uploadLoop(data) { @@ -149,7 +143,6 @@ function uploadLoop(data) { } } else { // Time's up. Once everything finishes, return the results if(data.requests.length < 1){ - // clearInterval(data.intervalHandle); const totalSeconds = (Date.now() - data.beganAt) / 1000.0; let totalBytes = 0; for (var i = 0; i < data.uploadResults.length; i++) { @@ -202,17 +195,9 @@ function removeRequest(data, request) { } var allowedChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -function fortyKiloString() { //not exactly 40KB - expected to have another random 30 to front and back - const out = []; - for (let i = 0; i < 40900; i++){ - out.push( allowedChar.charAt(Math.floor(Math.random() * allowedChar.length)) ); - } - return out.join(''); -} - -function uploadPrefix() { +function fortyKiloString() { const out = []; - for (let i = 0; i < 30; i++){ + for (let i = 0; i < 40960; i++){ out.push( allowedChar.charAt(Math.floor(Math.random() * allowedChar.length)) ); } return out.join('');