Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/page/test/bandwidthDetection/README.md
Original file line number Diff line number Diff line change
@@ -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.
204 changes: 204 additions & 0 deletions src/page/test/bandwidthDetection/bandwidthDetection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@

//***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);

const data = {
beganAt: now,
returnBy: now + maxMillis,
url: baseURL + "/bandwidthdetection/detect",
downloadResults: [],
requests:[],
resolve: resolve,
reject: reject
};

for (var i = 0; i < CONCURRENT_CONNECTIONS; i++) {
createDownloader(data);
}
});
}

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);
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);

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 < CONCURRENT_CONNECTIONS){
createDownloader(data);
}
} else { // Time's up. Once everything finishes, return the results
if(data.requests.length < 1){
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");
}
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 });
}
}
}

//***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 now = Date.now();
const maxMillis = Math.floor(maxSeconds * 1000);

const data = {
beganAt: now,
returnBy: now + maxMillis,
url: baseURL + "/bandwidthdetection/detect",
uploadResults: [],
requests:[],
resolve: resolve,
reject: reject
};

for (var i = 0; i < CONCURRENT_CONNECTIONS; i++) {
createUploader(data);
}
});
}

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 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("POST", data.url, true);
request.timeout = 5000;
//send 40kB of random data
request.send( fortyKiloString() );
}

function uploadLoop(data) {
const now = Date.now();

if (now < data.returnBy) { //We have more time, keep uploading
while(data.requests.length < CONCURRENT_CONNECTIONS){
createUploader(data);
}
} else { // Time's up. Once everything finishes, return the results
if(data.requests.length < 1){
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");
}
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 });
}
}
}

//***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.download;
return checkUploadSpeed(baseURL, halfMaxSeconds);
})
.then(result => {
ret.upload = result.upload;
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() {
const out = [];
for (let i = 0; i < 40960; i++){
out.push( allowedChar.charAt(Math.floor(Math.random() * allowedChar.length)) );
}
return out.join('');
}
24 changes: 24 additions & 0 deletions src/page/test/bandwidthDetection/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
{{> meta title='Bandwidth Detection Test'}}
{{> header-scripts}}
{{> header-stylesheets}}
</head>
<body>
<div id="app">
{{> version }}
{{> settings-link}}
{{> test-info testTitle='Bandwidth Detection Test'}}
{{> status-field-publisher}}
{{> statistics-field}}
<div class="centered">
<video id="red5pro-publisher" controls autoplay playsinline muted></video>
</div>
</div>
<p id="speed-check-print">Speed test in progress...</p>
{{> body-scripts}}
<script src="bandwidthDetection.js"></script>
<script src="index.js"></script>
</body>
</html>
Loading