Bugfix/kasm 2053 video quality (#19)

* KASM-2053 Fixes previous issues where there was no difference between medium and high
* Expands quality settings to adjust more rendering settings
* Adds an extreme quality setting and custom
* Adds the quality setting to the noVNC control panel, was previously only exposed in the backend for integration with Kasm Workspaces
* Switching quality settings no longer requires reconnecting.
This commit is contained in:
mmcclaskey 2021-11-08 12:57:24 -05:00 committed by GitHub
parent e11e76e2c7
commit 2faf2049c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 214 additions and 137 deletions

148
app/ui.js
View File

@ -206,6 +206,7 @@ const UI = {
UI.initSetting('treat_lossless', 7); UI.initSetting('treat_lossless', 7);
UI.initSetting('jpeg_video_quality', 5); UI.initSetting('jpeg_video_quality', 5);
UI.initSetting('webp_video_quality', 5); UI.initSetting('webp_video_quality', 5);
UI.initSetting('video_quality', 2);
UI.initSetting('anti_aliasing', 0); UI.initSetting('anti_aliasing', 0);
UI.initSetting('video_area', 65); UI.initSetting('video_area', 65);
UI.initSetting('video_time', 5); UI.initSetting('video_time', 5);
@ -228,14 +229,12 @@ const UI = {
UI.initSetting('enable_perf_stats', false); UI.initSetting('enable_perf_stats', false);
if (WebUtil.isInsideKasmVDI()) { if (WebUtil.isInsideKasmVDI()) {
UI.initSetting('video_quality', 1);
UI.initSetting('clipboard_up', false); UI.initSetting('clipboard_up', false);
UI.initSetting('clipboard_down', false); UI.initSetting('clipboard_down', false);
UI.initSetting('clipboard_seamless', false); UI.initSetting('clipboard_seamless', false);
UI.initSetting('enable_webp', false); UI.initSetting('enable_webp', false);
UI.initSetting('resize', 'off'); UI.initSetting('resize', 'off');
} else { } else {
UI.initSetting('video_quality', 3);
UI.initSetting('clipboard_up', true); UI.initSetting('clipboard_up', true);
UI.initSetting('clipboard_down', true); UI.initSetting('clipboard_down', true);
UI.initSetting('clipboard_seamless', true); UI.initSetting('clipboard_seamless', true);
@ -244,6 +243,7 @@ const UI = {
} }
UI.setupSettingLabels(); UI.setupSettingLabels();
UI.updateQuality();
}, },
// Adds a link to the label elements on the corresponding input elements // Adds a link to the label elements on the corresponding input elements
setupSettingLabels() { setupSettingLabels() {
@ -430,6 +430,8 @@ const UI = {
UI.addSettingChangeHandler('treat_lossless', UI.updateQuality); UI.addSettingChangeHandler('treat_lossless', UI.updateQuality);
UI.addSettingChangeHandler('anti_aliasing'); UI.addSettingChangeHandler('anti_aliasing');
UI.addSettingChangeHandler('anti_aliasing', UI.updateQuality); UI.addSettingChangeHandler('anti_aliasing', UI.updateQuality);
UI.addSettingChangeHandler('video_quality');
UI.addSettingChangeHandler('video_quality', UI.updateQuality);
UI.addSettingChangeHandler('jpeg_video_quality'); UI.addSettingChangeHandler('jpeg_video_quality');
UI.addSettingChangeHandler('jpeg_video_quality', UI.updateQuality); UI.addSettingChangeHandler('jpeg_video_quality', UI.updateQuality);
UI.addSettingChangeHandler('webp_video_quality'); UI.addSettingChangeHandler('webp_video_quality');
@ -862,10 +864,14 @@ const UI = {
}, },
// Set the new value, update and disable form control setting // Set the new value, update and disable form control setting
forceSetting(name, val) { forceSetting(name, val, disable=true) {
WebUtil.setSetting(name, val); WebUtil.setSetting(name, val);
UI.updateSetting(name); UI.updateSetting(name);
UI.disableSetting(name); if (disable) {
UI.disableSetting(name);
} else {
UI.enableSetting(name);
}
}, },
// Update cookie and form control setting. If value is not set, then // Update cookie and form control setting. If value is not set, then
@ -880,6 +886,7 @@ const UI = {
ctrl.checked = value; ctrl.checked = value;
} else if (typeof ctrl.options !== 'undefined') { } else if (typeof ctrl.options !== 'undefined') {
value = String(value);
for (let i = 0; i < ctrl.options.length; i += 1) { for (let i = 0; i < ctrl.options.length; i += 1) {
if (ctrl.options[i].value === value) { if (ctrl.options[i].value === value) {
ctrl.selectedIndex = i; ctrl.selectedIndex = i;
@ -974,6 +981,7 @@ const UI = {
UI.updateSetting('anti_aliasing', 0); UI.updateSetting('anti_aliasing', 0);
UI.updateSetting('jpeg_video_quality', 5); UI.updateSetting('jpeg_video_quality', 5);
UI.updateSetting('webp_video_quality', 5); UI.updateSetting('webp_video_quality', 5);
UI.updateSetting('video_quality', 2);
UI.updateSetting('video_area', 65); UI.updateSetting('video_area', 65);
UI.updateSetting('video_time', 5); UI.updateSetting('video_time', 5);
UI.updateSetting('video_out_time', 3); UI.updateSetting('video_out_time', 3);
@ -1289,7 +1297,7 @@ const UI = {
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression')); UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
UI.rfb.showDotCursor = UI.getSetting('show_dot'); UI.rfb.showDotCursor = UI.getSetting('show_dot');
UI.rfb.idleDisconnect = UI.getSetting('idle_disconnect'); UI.rfb.idleDisconnect = UI.getSetting('idle_disconnect');
UI.rfb.videoQuality = UI.getSetting('video_quality'); UI.rfb.videoQuality = parseInt(UI.getSetting('video_quality'));
UI.rfb.antiAliasing = UI.getSetting('anti_aliasing'); UI.rfb.antiAliasing = UI.getSetting('anti_aliasing');
UI.rfb.clipboardUp = UI.getSetting('clipboard_up'); UI.rfb.clipboardUp = UI.getSetting('clipboard_up');
UI.rfb.clipboardDown = UI.getSetting('clipboard_down'); UI.rfb.clipboardDown = UI.getSetting('clipboard_down');
@ -1395,6 +1403,7 @@ const UI = {
return; return;
} }
UI.connect(null, UI.reconnectPassword); UI.connect(null, UI.reconnectPassword);
}, },
@ -1462,6 +1471,11 @@ const UI = {
UI.openControlbar(); UI.openControlbar();
UI.openConnectPanel(); UI.openConnectPanel();
if (UI.forceReconnect) {
UI.forceReconnect = false;
UI.connect(null, UI.reconnectPassword);
}
}, },
securityFailed(e) { securityFailed(e) {
@ -1494,7 +1508,8 @@ const UI = {
} }
break; break;
case 'setvideoquality': case 'setvideoquality':
UI.rfb.videoQuality = event.data.value; UI.forceSetting('video_quality', parseInt(event.data.value), false);
UI.updateQuality();
break; break;
} }
} }
@ -1704,29 +1719,108 @@ const UI = {
* ------v------*/ * ------v------*/
updateQuality() { updateQuality() {
if (!UI.rfb) return; let present_mode = parseInt(UI.getSetting('video_quality'));
if (!UI.updatingSettings) { // video_quality preset values
// avoid sending too many, will only apply when there are changes switch (present_mode) {
setTimeout(function() { case 10: //custom
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); UI.enableSetting('dynamic_quality_min');
UI.rfb.antiAliasing = parseInt(UI.getSetting('anti_aliasing')); UI.enableSetting('dynamic_quality_max');
UI.rfb.dynamicQualityMin = parseInt(UI.getSetting('dynamic_quality_min')); UI.enableSetting('treat_lossless');
UI.rfb.dynamicQualityMax = parseInt(UI.getSetting('dynamic_quality_max')); UI.enableSetting('video_time');
UI.rfb.jpegVideoQuality = parseInt(UI.getSetting('jpeg_video_quality')); UI.enableSetting('video_area');
UI.rfb.webpVideoQuality = parseInt(UI.getSetting('webp_video_quality')); UI.enableSetting('max_video_resolution_x');
UI.rfb.videoArea = parseInt(UI.getSetting('video_area')); UI.enableSetting('max_video_resolution_y');
UI.rfb.videoTime = parseInt(UI.getSetting('video_time')); UI.enableSetting('jpeg_video_quality');
UI.rfb.videoOutTime = parseInt(UI.getSetting('video_out_time')); UI.enableSetting('webp_video_quality');
UI.rfb.videoScaling = parseInt(UI.getSetting('video_scaling')); UI.enableSetting('framerate');
UI.rfb.treatLossless = parseInt(UI.getSetting('treat_lossless')); UI.enableSetting('video_scaling');
UI.rfb.maxVideoResolutionX = parseInt(UI.getSetting('max_video_resolution_x')); UI.enableSetting('video_out_time');
UI.rfb.maxVideoResolutionY = parseInt(UI.getSetting('max_video_resolution_y')); UI.showStatus("Refresh or reconnect to apply changes.");
UI.rfb.frameRate = parseInt(UI.getSetting('framerate')); return;
UI.rfb.enableWebP = UI.getSetting('enable_webp'); case 4: //extreme
UI.showStatus("Refresh page to apply encoding changes."); UI.forceSetting('dynamic_quality_min', 8);
}, 2000); UI.forceSetting('dynamic_quality_max', 9);
UI.forceSetting('framerate', 30);
UI.forceSetting('treat_lossless', 8);
// effectively disables video mode
UI.forceSetting('video_time', 100);
UI.forceSetting('video_area', 100);
// go ahead and set video mode settings, won't be used
UI.forceSetting('max_video_resolution_x', 1920);
UI.forceSetting('max_video_resolution_y', 1080);
UI.forceSetting('jpeg_video_quality', 8);
UI.forceSetting('webp_video_quality', 8);
UI.forceSetting('video_scaling', 0);
UI.forceSetting('video_out_time', 3);
break;
case 3: // high
UI.forceSetting('jpeg_video_quality', 8);
UI.forceSetting('webp_video_quality', 8);
UI.forceSetting('dynamic_quality_min', 7);
UI.forceSetting('dynamic_quality_max', 9);
UI.forceSetting('max_video_resolution_x', 1920);
UI.forceSetting('max_video_resolution_y', 1080);
UI.forceSetting('framerate', 30);
UI.forceSetting('treat_lossless', 8);
UI.forceSetting('video_time', 5);
UI.forceSetting('video_area', 65);
UI.forceSetting('video_scaling', 0);
UI.forceSetting('video_out_time', 3);
break;
case 1: // low, resolution capped at 720p keeping aspect ratio
UI.forceSetting('jpeg_video_quality', 5);
UI.forceSetting('webp_video_quality', 4);
UI.forceSetting('dynamic_quality_min', 3);
UI.forceSetting('dynamic_quality_max', 7);
UI.forceSetting('max_video_resolution_x', 960);
UI.forceSetting('max_video_resolution_y', 540);
UI.forceSetting('framerate', 22);
UI.forceSetting('treat_lossless', 7);
UI.forceSetting('video_time', 5);
UI.forceSetting('video_area', 65);
UI.forceSetting('video_scaling', 0);
UI.forceSetting('video_out_time', 3);
break;
case 2: // medium
case 0: // static resolution, but same settings as medium
default:
UI.forceSetting('jpeg_video_quality', 7);
UI.forceSetting('webp_video_quality', 7);
UI.forceSetting('dynamic_quality_min', 4);
UI.forceSetting('dynamic_quality_max', 9);
UI.forceSetting('max_video_resolution_x', 960);
UI.forceSetting('max_video_resolution_y', 540);
UI.forceSetting('framerate', 24);
UI.forceSetting('treat_lossless', 7);
UI.forceSetting('video_time', 5);
UI.forceSetting('video_area', 65);
UI.forceSetting('video_scaling', 0);
UI.forceSetting('video_out_time', 3);
break;
}
if (UI.rfb) {
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
UI.rfb.antiAliasing = parseInt(UI.getSetting('anti_aliasing'));
UI.rfb.dynamicQualityMin = parseInt(UI.getSetting('dynamic_quality_min'));
UI.rfb.dynamicQualityMax = parseInt(UI.getSetting('dynamic_quality_max'));
UI.rfb.jpegVideoQuality = parseInt(UI.getSetting('jpeg_video_quality'));
UI.rfb.webpVideoQuality = parseInt(UI.getSetting('webp_video_quality'));
UI.rfb.videoArea = parseInt(UI.getSetting('video_area'));
UI.rfb.videoTime = parseInt(UI.getSetting('video_time'));
UI.rfb.videoOutTime = parseInt(UI.getSetting('video_out_time'));
UI.rfb.videoScaling = parseInt(UI.getSetting('video_scaling'));
UI.rfb.treatLossless = parseInt(UI.getSetting('treat_lossless'));
UI.rfb.maxVideoResolutionX = parseInt(UI.getSetting('max_video_resolution_x'));
UI.rfb.maxVideoResolutionY = parseInt(UI.getSetting('max_video_resolution_y'));
UI.rfb.frameRate = parseInt(UI.getSetting('framerate'));
UI.rfb.enableWebP = UI.getSetting('enable_webp');
UI.rfb.videoQuality = parseInt(UI.getSetting('video_quality'));
// Gracefully update settings server side
UI.rfb.updateConnectionSettings();
} }
}, },

View File

@ -343,10 +343,21 @@ export default class RFB extends EventTargetMixin {
set clipboardBinary(val) { this._clipboardMode = val; } set clipboardBinary(val) { this._clipboardMode = val; }
get videoQuality() { return this._videoQuality; } get videoQuality() { return this._videoQuality; }
set videoQuality(quality) { this._videoQuality = quality; } set videoQuality(quality)
{
//if changing to or from a video quality mode that uses a fixed resolution server side
if (this._videoQuality <= 1 || quality <= 1) {
this._pendingApplyResolutionChange = true;
}
this._videoQuality = quality;
this._pendingApplyEncodingChanges = true;
}
get preferBandwidth() { return this._preferBandwidth; } get preferBandwidth() { return this._preferBandwidth; }
set preferBandwidth(val) { this._preferBandwidth = val; } set preferBandwidth(val) {
this._preferBandwidth = val;
this._pendingApplyEncodingChanges = true;
}
get viewOnly() { return this._viewOnly; } get viewOnly() { return this._viewOnly; }
set viewOnly(viewOnly) { set viewOnly(viewOnly) {
@ -411,9 +422,7 @@ export default class RFB extends EventTargetMixin {
return; return;
} }
this._enableWebP = enabled; this._enableWebP = enabled;
if (this._rfbConnectionState === 'connected') { this._pendingApplyEncodingChanges = true;
this._sendEncodings();
}
} }
get antiAliasing() { return this._display.antiAliasing; } get antiAliasing() { return this._display.antiAliasing; }
@ -433,10 +442,7 @@ export default class RFB extends EventTargetMixin {
} }
this._jpegVideoQuality = qualityLevel; this._jpegVideoQuality = qualityLevel;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get webpVideoQuality() { return this._webpVideoQuality; } get webpVideoQuality() { return this._webpVideoQuality; }
@ -451,10 +457,7 @@ export default class RFB extends EventTargetMixin {
} }
this._webpVideoQuality = qualityLevel; this._webpVideoQuality = qualityLevel;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get treatLossless() { return this._treatLossless; } get treatLossless() { return this._treatLossless; }
@ -469,10 +472,6 @@ export default class RFB extends EventTargetMixin {
} }
this._treatLossless = qualityLevel; this._treatLossless = qualityLevel;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get dynamicQualityMin() { return this._dynamicQualityMin; } get dynamicQualityMin() { return this._dynamicQualityMin; }
@ -487,10 +486,7 @@ export default class RFB extends EventTargetMixin {
} }
this._dynamicQualityMin = qualityLevel; this._dynamicQualityMin = qualityLevel;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get dynamicQualityMax() { return this._dynamicQualityMax; } get dynamicQualityMax() { return this._dynamicQualityMax; }
@ -505,10 +501,7 @@ export default class RFB extends EventTargetMixin {
} }
this._dynamicQualityMax = qualityLevel; this._dynamicQualityMax = qualityLevel;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get videoArea() { get videoArea() {
@ -525,10 +518,7 @@ export default class RFB extends EventTargetMixin {
} }
this._videoArea = area; this._videoArea = area;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get videoTime() { get videoTime() {
@ -545,10 +535,7 @@ export default class RFB extends EventTargetMixin {
} }
this._videoTime = value; this._videoTime = value;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get videoOutTime() { get videoOutTime() {
@ -565,10 +552,7 @@ export default class RFB extends EventTargetMixin {
} }
this._videoOutTime = value; this._videoOutTime = value;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get videoScaling() { get videoScaling() {
@ -585,10 +569,7 @@ export default class RFB extends EventTargetMixin {
} }
this._videoScaling = value; this._videoScaling = value;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get frameRate() { return this._frameRate; } get frameRate() { return this._frameRate; }
@ -603,10 +584,7 @@ export default class RFB extends EventTargetMixin {
} }
this._frameRate = value; this._frameRate = value;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get maxVideoResolutionX() { return this._maxVideoResolutionX; } get maxVideoResolutionX() { return this._maxVideoResolutionX; }
@ -621,13 +599,7 @@ export default class RFB extends EventTargetMixin {
} }
this._maxVideoResolutionX = value; this._maxVideoResolutionX = value;
this._pendingApplyVideoRes = true;
if (this._rfbConnectionState === 'connected') {
RFB.messages.setMaxVideoResolution(this._sock,
this._maxVideoResolutionX,
this._maxVideoResolutionY);
}
} }
get maxVideoResolutionY() { return this._maxVideoResolutionY; } get maxVideoResolutionY() { return this._maxVideoResolutionY; }
@ -642,12 +614,7 @@ export default class RFB extends EventTargetMixin {
} }
this._maxVideoResolutionY = value; this._maxVideoResolutionY = value;
this._pendingApplyVideoRes = true;
if (this._rfbConnectionState === 'connected') {
RFB.messages.setMaxVideoResolution(this._sock,
this._maxVideoResolutionX,
this._maxVideoResolutionY);
}
} }
get qualityLevel() { get qualityLevel() {
@ -664,10 +631,7 @@ export default class RFB extends EventTargetMixin {
} }
this._qualityLevel = qualityLevel; this._qualityLevel = qualityLevel;
this._pendingApplyEncodingChanges = true;
if (this._rfbConnectionState === 'connected') {
this._sendEncodings();
}
} }
get compressionLevel() { get compressionLevel() {
@ -692,6 +656,31 @@ export default class RFB extends EventTargetMixin {
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
/*
This function must be called after changing any properties that effect rendering quality
*/
updateConnectionSettings() {
if (this._rfbConnectionState === 'connected') {
if (this._pendingApplyVideoRes) {
RFB.messages.setMaxVideoResolution(this._sock, this._maxVideoResolutionX, this._maxVideoResolutionY);
}
if (this._pendingApplyResolutionChange) {
this._requestRemoteResize();
}
if (this._pendingApplyEncodingChanges) {
this._sendEncodings();
}
this._pendingApplyVideoRes = false;
this._pendingApplyEncodingChanges = false;
this._pendingApplyResolutionChange = false;
}
}
disconnect() { disconnect() {
this._updateConnectionState('disconnecting'); this._updateConnectionState('disconnecting');
this._sock.off('error'); this._sock.off('error');
@ -2220,16 +2209,6 @@ export default class RFB extends EventTargetMixin {
var quality = 6; var quality = 6;
var compression = 2; var compression = 2;
var screensize = this._screenSize(false); var screensize = this._screenSize(false);
if (this.videoQuality == 1) {
if (screensize.w > 1280) {
quality = 8; //higher quality needed because scaling enlarges artifacts
} else {
quality = 3; //twice the compression ratio as default, but not horrible quality
}
compression = 6;
} else if (this.videoQuality == 3) {
quality = 8
}
encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel); encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel);
encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel); encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel);

View File

@ -224,14 +224,25 @@
</li> </li>
<li><hr></li> <li><hr></li>
<li> <li>
<div class="noVNC_expander">Video Mode Settings</div> <div class="noVNC_expander">Stream Quality</div>
<div><ul> <div><ul>
<li> <li>
<label for="noVNC_setting_video_quality">Video Quality:</label> <label for="noVNC_setting_video_quality">Preset Modes:</label>
<select id="noVNC_setting_video_quality" name="vncVideoQuality"> <select id="noVNC_setting_video_quality" name="vncVideoQuality">
<option value=0>Low</option> <option value=0>Static</option>
<option value=1>Medium</option> <option value=1>Low</option>
<option value=2>High</option> <option value=2>Medium</option>
<option value=3>High</option>
<option value=4>Extreme</option>
<option value=10>Custom</option>
</select>
</li>
<li>
<label for="noVNC_setting_anti_aliasing">Anti-Aliasing:</label>
<select id="noVNC_setting_anti_aliasing" name="vncAntiAliasing">
<option value=0>Auto Dynamic</option>
<option value=1>On</option>
<option value=2>Off</option>
</select> </select>
</li> </li>
<li> <li>
@ -244,6 +255,30 @@
<input id="noVNC_setting_webp_video_quality" type="range" min="0" max="9" value="5" onchange="noVNC_setting_webp_video_quality_output.value=value"> <input id="noVNC_setting_webp_video_quality" type="range" min="0" max="9" value="5" onchange="noVNC_setting_webp_video_quality_output.value=value">
<output id="noVNC_setting_webp_video_quality_output">5</output> <output id="noVNC_setting_webp_video_quality_output">5</output>
</li> </li>
<li style="display: none;">
<label for="noVNC_setting_quality">Quality:</label>
<input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
</li>
<li>
<label for="noVNC_setting_dynamic_quality_min">Dynamic Quality Min:</label>
<input id="noVNC_setting_dynamic_quality_min" type="range" min="0" max="9" value="3" onchange="noVNC_setting_dynamic_quality_min_output.value=value">
<output id="noVNC_setting_dynamic_quality_min_output">3</output>
</li>
<li>
<label for="noVNC_setting_dynamic_quality_max">Dynamic Quality Max:</label>
<input id="noVNC_setting_dynamic_quality_max" type="range" min="0" max="9" value="9" onchange="noVNC_setting_dynamic_quality_max_output.value=value">
<output id="noVNC_setting_dynamic_quality_max_output">9</output>
</li>
<li>
<label for="noVNC_setting_treat_lossless">Treat Lossless:</label>
<input id="noVNC_setting_treat_lossless" type="range" min="0" max="9" value="7" onchange="noVNC_setting_treat_lossless_output.value=value">
<output id="noVNC_setting_treat_lossless_output">7</output>
</li>
<li>
<label for="noVNC_setting_framerate">Frame Rate:</label>
<input id="noVNC_setting_framerate" type="number" min="1" max="120" value="30">
</li>
<li> <li>
<label for="noVNC_setting_video_area">Video Area:</label> <label for="noVNC_setting_video_area">Video Area:</label>
<input id="noVNC_setting_video_area" type="range" min="0" max="100" value="65" onchange="noVNC_setting_video_area_output.value=value"> <input id="noVNC_setting_video_area" type="range" min="0" max="100" value="65" onchange="noVNC_setting_video_area_output.value=value">
@ -268,11 +303,11 @@
</select> </select>
</li> </li>
<li> <li>
<label for="noVNC_setting_max_video_resolution_x">Max Width:</label> <label for="noVNC_setting_max_video_resolution_x">Video Mode Width:</label>
<input id="noVNC_setting_max_video_resolution_x" type="number" min="100" max="3840" value="960"> <input id="noVNC_setting_max_video_resolution_x" type="number" min="100" max="3840" value="960">
</li> </li>
<li> <li>
<label for="noVNC_setting_max_video_resolution_y">Max Height:</label> <label for="noVNC_setting_max_video_resolution_y">Video Mode Height:</label>
<input id="noVNC_setting_max_video_resolution_y" type="number" min="100" max="2160" value="540"> <input id="noVNC_setting_max_video_resolution_y" type="number" min="100" max="2160" value="540">
</li> </li>
</ul></div> </ul></div>
@ -281,37 +316,6 @@
<li> <li>
<div class="noVNC_expander">Advanced</div> <div class="noVNC_expander">Advanced</div>
<div><ul> <div><ul>
<li style="display: none;">
<label for="noVNC_setting_quality">Quality:</label>
<input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
</li>
<li>
<label for="noVNC_setting_anti_aliasing">Smoothing:</label>
<select id="noVNC_setting_anti_aliasing" name="vncAntiAliasing">
<option value=0>Auto Dynamic</option>
<option value=1>On</option>
<option value=2>Off</option>
</select>
</li>
<li>
<label for="noVNC_setting_dynamic_quality_min">Dynamic Quality Min:</label>
<input id="noVNC_setting_dynamic_quality_min" type="range" min="0" max="9" value="3" onchange="noVNC_setting_dynamic_quality_min_output.value=value">
<output id="noVNC_setting_dynamic_quality_min_output">3</output>
</li>
<li>
<label for="noVNC_setting_dynamic_quality_max">Dynamic Quality Max:</label>
<input id="noVNC_setting_dynamic_quality_max" type="range" min="0" max="9" value="9" onchange="noVNC_setting_dynamic_quality_max_output.value=value">
<output id="noVNC_setting_dynamic_quality_max_output">9</output>
</li>
<li>
<label for="noVNC_setting_treat_lossless">Treat Lossless:</label>
<input id="noVNC_setting_treat_lossless" type="range" min="0" max="9" value="7" onchange="noVNC_setting_treat_lossless_output.value=value">
<output id="noVNC_setting_treat_lossless_output">7</output>
</li>
<li>
<label for="noVNC_setting_framerate">Frame Rate:</label>
<input id="noVNC_setting_framerate" type="number" min="1" max="120" value="30">
</li>
<li> <li>
<label for="noVNC_setting_compression">Compression level:</label> <label for="noVNC_setting_compression">Compression level:</label>
<input id="noVNC_setting_compression" type="range" min="0" max="9" value="2"> <input id="noVNC_setting_compression" type="range" min="0" max="9" value="2">