Skip to content

Commit

Permalink
improved performance mitigation in case of high latency network
Browse files Browse the repository at this point in the history
  • Loading branch information
cedrozor committed Jun 9, 2018
1 parent bea940d commit 27998a6
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 22 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
2018-05-24 Version 1.9.0 (stable)
2018-06-09 Version 1.9.1 (stable)
reinstated the hotfix removed into version 1.9.0, forcing IP connection when using FQDN; fact is, some people still have issues even with openssl 1.1.x (unable to reproduce, may depend on a specific configuration). more details here: https://github.com/FreeRDP/FreeRDP/issues/4525#issuecomment-380635647
improved performance mitigation in case of high latency network

2018-05-24 Version 1.9.0 (stable)
resynced FreeRDP (version 2.0-RC2-dev3), now using the openssl 1.1.x architecture. Should fix any previously reported FQDN and NLA issues
added a pdf virtual printer; any document printed with the "Myrtille PDF" printer is downloaded to the browser and can be opened/saved/printed from there
pdf content is now downloaded into an hidden iframe, instead of a new tab, to prevent popup blockers to block it
Expand Down
14 changes: 10 additions & 4 deletions Myrtille.Web/js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,11 @@ function Config(
var imageEncoding = imageEncodingEnum.PNG; // image encoding
var imageQuality = 100; // image quality (%) higher = better; not applicable for PNG (lossless); tweaked dynamically to fit the available bandwidth if using JPEG, PNG_JPEG or WEBP encoding. for best user experience, fullscreen updates are always done in higher quality (75%), regardless of this setting and bandwidth
var imageQuantity = 100; // image quantity (%) less images = lower cpu and bandwidth usage / faster; more = smoother display (skipping images may result in some display inconsistencies). tweaked dynamically to fit the available bandwidth; possible values: 5, 10, 20, 25, 50, 100 (lower = higher drop rate)
var imageTweakLowerThreshold = 50; // tweak the image quality & quantity depending on the available bandwidth: lower threshold
var imageTweakHigherThreshold = 90; // tweak the image quality & quantity depending on the available bandwidth: higher threshold
var imageTweakBandwidthLowerThreshold = 50; // tweak the image quality & quantity depending on the available bandwidth (%): lower threshold
var imageTweakBandwidthHigherThreshold = 90; // tweak the image quality & quantity depending on the available bandwidth (%): higher threshold
var imageTweakLatencyLowerThreshold = 250; // tweak the image quantity depending on the average latency (ms): lower threshold
var imageTweakLatencyHigherThreshold = 750; // tweak the image quantity depending on the average latency (ms): higher threshold
var imageTweakLatencyCountPerSec = 20; // tweak the image quantity depending on the average latency and image count per second
var imageCountOk = 500; // reasonable number of images to display at once; for HTML4 (divs), used to clean the DOM (by requesting a fullscreen update) as too many divs may slow down the browser; not applicable for HTML5 (canvas)
var imageCountMax = 1000; // maximal number of images to display at once; for HTML4 (divs), used to clean the DOM (by reloading the page) as too many divs may slow down the browser; not applicable for HTML5 (canvas)
var imageMode = imageModeEnum.AUTO; // image mode
Expand Down Expand Up @@ -155,8 +158,11 @@ function Config(
this.setImageQuality = function(quality) { imageQuality = quality; };
this.getImageQuantity = function() { return imageQuantity; };
this.setImageQuantity = function(quantity) { imageQuantity = quantity; };
this.getImageTweakLowerThreshold = function() { return imageTweakLowerThreshold; };
this.getImageTweakHigherThreshold = function() { return imageTweakHigherThreshold; };
this.getImageTweakBandwidthLowerThreshold = function() { return imageTweakBandwidthLowerThreshold; };
this.getImageTweakBandwidthHigherThreshold = function() { return imageTweakBandwidthHigherThreshold; };
this.getImageTweakLatencyLowerThreshold = function() { return imageTweakLatencyLowerThreshold; };
this.getImageTweakLatencyHigherThreshold = function() { return imageTweakLatencyHigherThreshold; };
this.getImageTweakLatencyCountPerSec = function() { return imageTweakLatencyCountPerSec; };
this.getImageCountOk = function() { return imageCountOk; };
this.getImageCountMax = function() { return imageCountMax; };
this.getImageModeEnum = function() { return imageModeEnum; };
Expand Down
5 changes: 5 additions & 0 deletions Myrtille.Web/js/display.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ function Display(config, dialog)
{
//dialog.showDebug('checking image count per second');
dialog.showStat(dialog.getShowStatEnum().IMAGE_COUNT_PER_SEC, imgCountPerSec);
lastImgCountPerSec = imgCountPerSec;
imgCountPerSec = 0;
},
1000);
Expand Down Expand Up @@ -323,6 +324,10 @@ function Display(config, dialog)
// number of displayed images per second
var imgCountPerSec = 0;

// last number of displayed images per second
var lastImgCountPerSec = 0;
this.getLastImgCountPerSec = function() { return lastImgCountPerSec; };

// last displayed image
var imgIdx = 0;
this.getImgIdx = function() { return imgIdx; };
Expand Down
4 changes: 2 additions & 2 deletions Myrtille.Web/js/myrtille.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ this.pushImage = function(idx, posX, posY, width, height, format, quality, fulls
{
try
{
if (config.additionalLatency > 0)
if (config.getAdditionalLatency() > 0)
{
window.setTimeout(function() { processImage(idx, posX, posY, width, height, format, quality, fullscreen, base64Data); }, Math.round(config.additionalLatency / 2));
window.setTimeout(function() { processImage(idx, posX, posY, width, height, format, quality, fullscreen, base64Data); }, Math.round(config.getAdditionalLatency() / 2));
}
else
{
Expand Down
91 changes: 76 additions & 15 deletions Myrtille.Web/js/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ function Network(config, dialog, display)
//dialog.showDebug('checking bandwidth usage');
dialog.showStat(dialog.getShowStatEnum().BANDWIDTH_USAGE, Math.ceil(bandwidthUsage / 1024));

// throttle the image quality & quantity depending on the bandwidth usage
// throttle image quality & quantity depending on the bandwidth usage and average latency
tweakDisplay();

// reset bandwidth usage
Expand Down Expand Up @@ -445,36 +445,97 @@ function Network(config, dialog, display)
{
try
{
var commands = new Array();

/*
small bandwidth = lower quality
latency shouldn't affect image quality because each update, whatever its size, will be received within the same delay
for example, if latency = 100ms, both a 10KB and 100KB image will be received in 100ms (given the bandwidth is good enough)
*/

var tweak = false;

var bandwidthRatio = bandwidthUsage != null && bandwidthSize != null && bandwidthSize > 0 ? Math.round((bandwidthUsage * 100) / bandwidthSize) : 0;
if (bandwidthRatio >= config.getImageTweakHigherThreshold())
var bandwidthUsageRatio = bandwidthUsage != null && bandwidthSize != null && bandwidthSize > 0 ? Math.round((bandwidthUsage * 100) / bandwidthSize) : 0;
if (bandwidthUsageRatio >= config.getImageTweakBandwidthHigherThreshold())
{
config.setImageQuality(10);
config.setImageQuantity(25);
tweak = true;
if (config.getImageQuality() != 10)
{
config.setImageQuality(10);
tweak = true;
}
}
else if (bandwidthRatio >= config.getImageTweakLowerThreshold() && bandwidthRatio < config.getImageTweakHigherThreshold())
else if (bandwidthUsageRatio >= config.getImageTweakBandwidthLowerThreshold() && bandwidthUsageRatio < config.getImageTweakBandwidthHigherThreshold())
{
config.setImageQuality(25);
config.setImageQuantity(50);
tweak = true;
if (config.getImageQuality() != 25)
{
config.setImageQuality(25);
tweak = true;
}
}
else if (config.getImageQuality() != originalImageQuality || config.getImageQuantity() != originalImageQuantity)
else if (config.getImageQuality() != originalImageQuality)
{
config.setImageQuality(originalImageQuality);
config.setImageQuantity(originalImageQuantity);
tweak = true;
}

if (tweak)
{
var commands = new Array();

dialog.showDebug('tweaking display, image quality: ' + config.getImageQuality() + ', quantity: ' + config.getImageQuantity());
//dialog.showDebug('tweaking image quality: ' + config.getImageQuality());
commands.push(commandEnum.SET_IMAGE_QUALITY.text + config.getImageQuality());
}

/*
small bandwidth = lower quantity
latency is relevant there because each update, whatever its size, is affected by latency
sending many updates may result in an increasing lag, so better reduce their number if the latency goes high
for example, if latency = 100ms, 10 images will be received in 10 x 100 = 1000ms (1 sec)
this is mitigated when using websockets or long-polling, because the connection remains open, but the latency still applies
that said, it's also important to preserve the user experience as best as possible
dropping some updates may help to restrain the lag but it will also disrupt the display, and the user might think its actions aren't processed (!)
therefore, updates should only be dropped if their display rate is significant enough
*/

// computed latency and display rate may change outside of this function; use snapshot values
var latency = roundtripDurationAvg;
var ips = display.getLastImgCountPerSec();

//dialog.showDebug('latency: ' + latency + ', ips: ' + ips);

tweak = false;

if (bandwidthUsageRatio >= config.getImageTweakBandwidthHigherThreshold() ||
(latency >= config.getImageTweakLatencyHigherThreshold() && ips >= config.getImageTweakLatencyCountPerSec()))
{
if (config.getImageQuantity() != 25)
{
config.setImageQuantity(25);
tweak = true;
}
}
else if ((bandwidthUsageRatio >= config.getImageTweakBandwidthLowerThreshold() && bandwidthUsageRatio < config.getImageTweakBandwidthHigherThreshold()) ||
(latency >= config.getImageTweakLatencyLowerThreshold() && latency < config.getImageTweakLatencyHigherThreshold() && ips >= config.getImageTweakLatencyCountPerSec()))
{
if (config.getImageQuantity() != 50)
{
config.setImageQuantity(50);
tweak = true;
}
}
else if (config.getImageQuantity() != originalImageQuantity)
{
config.setImageQuantity(originalImageQuantity);
tweak = true;
}

if (tweak)
{
//dialog.showDebug('tweaking image quantity: ' + config.getImageQuantity());
commands.push(commandEnum.SET_IMAGE_QUANTITY.text + config.getImageQuantity());
}

if (commands.length > 0)
{
doSend(commands.toString());
}
}
Expand Down

0 comments on commit 27998a6

Please sign in to comment.