Add ability to set compression level

Fixes github issue #1382.
This commit is contained in:
Samuel Mannehed 2020-05-01 16:14:15 +02:00
parent a672168d4d
commit 479d8cefd1
6 changed files with 155 additions and 1 deletions

View File

@ -162,6 +162,7 @@ const UI = {
UI.initSetting('view_clip', false); UI.initSetting('view_clip', false);
UI.initSetting('resize', 'off'); UI.initSetting('resize', 'off');
UI.initSetting('quality', 6); UI.initSetting('quality', 6);
UI.initSetting('compression', 2);
UI.initSetting('shared', true); UI.initSetting('shared', true);
UI.initSetting('view_only', false); UI.initSetting('view_only', false);
UI.initSetting('show_dot', false); UI.initSetting('show_dot', false);
@ -350,6 +351,8 @@ const UI = {
UI.addSettingChangeHandler('resize', UI.updateViewClip); UI.addSettingChangeHandler('resize', UI.updateViewClip);
UI.addSettingChangeHandler('quality'); UI.addSettingChangeHandler('quality');
UI.addSettingChangeHandler('quality', UI.updateQuality); UI.addSettingChangeHandler('quality', UI.updateQuality);
UI.addSettingChangeHandler('compression');
UI.addSettingChangeHandler('compression', UI.updateCompression);
UI.addSettingChangeHandler('view_clip'); UI.addSettingChangeHandler('view_clip');
UI.addSettingChangeHandler('view_clip', UI.updateViewClip); UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
UI.addSettingChangeHandler('shared'); UI.addSettingChangeHandler('shared');
@ -841,6 +844,7 @@ const UI = {
UI.updateSetting('view_clip'); UI.updateSetting('view_clip');
UI.updateSetting('resize'); UI.updateSetting('resize');
UI.updateSetting('quality'); UI.updateSetting('quality');
UI.updateSetting('compression');
UI.updateSetting('shared'); UI.updateSetting('shared');
UI.updateSetting('view_only'); UI.updateSetting('view_only');
UI.updateSetting('path'); UI.updateSetting('path');
@ -1043,6 +1047,7 @@ const UI = {
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
UI.rfb.showDotCursor = UI.getSetting('show_dot'); UI.rfb.showDotCursor = UI.getSetting('show_dot');
UI.updateViewOnly(); // requires UI.rfb UI.updateViewOnly(); // requires UI.rfb
@ -1349,6 +1354,18 @@ const UI = {
/* ------^------- /* ------^-------
* /QUALITY * /QUALITY
* ============== * ==============
* COMPRESSION
* ------v------*/
updateCompression() {
if (!UI.rfb) return;
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
},
/* ------^-------
* /COMPRESSION
* ==============
* KEYBOARD * KEYBOARD
* ------v------*/ * ------v------*/

View File

@ -278,6 +278,7 @@ export default class RFB extends EventTargetMixin {
} }
this._qualityLevel = 6; this._qualityLevel = 6;
this._compressionLevel = 2;
} }
// ===== PROPERTIES ===== // ===== PROPERTIES =====
@ -360,6 +361,26 @@ export default class RFB extends EventTargetMixin {
} }
} }
get compressionLevel() {
return this._compressionLevel;
}
set compressionLevel(compressionLevel) {
if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {
Log.Error("compressionLevel must be an integer between 0 and 9");
return;
}
if (this._compressionLevel === compressionLevel) {
return;
}
this._compressionLevel = compressionLevel;
if (this._rfb_connection_state === 'connected') {
this._sendEncodings();
}
}
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
disconnect() { disconnect() {
@ -1411,7 +1432,7 @@ export default class RFB extends EventTargetMixin {
// Psuedo-encoding settings // Psuedo-encoding settings
encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel); encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel);
encs.push(encodings.pseudoEncodingCompressLevel0 + 2); encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel);
encs.push(encodings.pseudoEncodingDesktopSize); encs.push(encodings.pseudoEncodingDesktopSize);
encs.push(encodings.pseudoEncodingLastRect); encs.push(encodings.pseudoEncodingLastRect);

View File

@ -69,6 +69,14 @@ protocol stream.
Value `0` implies low quality and `9` implies high quality. Value `0` implies low quality and `9` implies high quality.
Default value is `6`. Default value is `6`.
`compressionLevel`
- Is an `int` in range `[0-9]` controlling the desired compression
level. Value `0` means no compression. Level 1 uses a minimum of CPU
resources and achieves weak compression ratios, while level 9 offers
best compression but is slow in terms of CPU consumption on the server
side. Use high levels with very slow network connections.
Default value is `2`.
`capabilities` *Read only* `capabilities` *Read only*
- Is an `Object` indicating which optional extensions are available - Is an `Object` indicating which optional extensions are available
on the server. Some methods may only be called if the corresponding on the server. Some methods may only be called if the corresponding

View File

@ -63,6 +63,8 @@ query string. Currently the following options are available:
* `quality` - The session JPEG quality level. Can be `0` to `9`. * `quality` - The session JPEG quality level. Can be `0` to `9`.
* `compression` - The session compression level. Can be `0` to `9`.
* `show_dot` - If a dot cursor should be shown when the remote server provides * `show_dot` - If a dot cursor should be shown when the remote server provides
no local cursor, or provides a fully-transparent (invisible) cursor. no local cursor, or provides a fully-transparent (invisible) cursor.

View File

@ -2967,6 +2967,108 @@ describe('Remote Frame Buffer Protocol Client', function () {
expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality);
}); });
}); });
describe('Compression level setting', function () {
const defaultCompression = 2;
let client;
beforeEach(function () {
client = make_rfb();
sinon.spy(RFB.messages, "clientEncodings");
});
afterEach(function () {
RFB.messages.clientEncodings.restore();
});
it(`should equal ${defaultCompression} by default`, function () {
expect(client._compressionLevel).to.equal(defaultCompression);
});
it('should ignore non-integers when set', function () {
client.compressionLevel = '1';
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client.compressionLevel = 1.5;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client.compressionLevel = null;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client.compressionLevel = undefined;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client.compressionLevel = {};
expect(RFB.messages.clientEncodings).to.not.have.been.called;
});
it('should ignore integers out of range [0, 9]', function () {
client.compressionLevel = -1;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client.compressionLevel = 10;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
});
it('should send clientEncodings with new compression value', function () {
let newCompression;
newCompression = 5;
client.compressionLevel = newCompression;
expect(client.compressionLevel).to.equal(newCompression);
expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
});
it('should not send clientEncodings if compression is the same', function () {
let newCompression;
newCompression = 9;
client.compressionLevel = newCompression;
expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
RFB.messages.clientEncodings.resetHistory();
client.compressionLevel = newCompression;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
});
it('should not send clientEncodings if not in connected state', function () {
let newCompression;
client._rfb_connection_state = '';
newCompression = 7;
client.compressionLevel = newCompression;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client._rfb_connection_state = 'connnecting';
newCompression = 6;
client.compressionLevel = newCompression;
expect(RFB.messages.clientEncodings).to.not.have.been.called;
RFB.messages.clientEncodings.resetHistory();
client._rfb_connection_state = 'connected';
newCompression = 5;
client.compressionLevel = newCompression;
expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
});
});
}); });
describe('RFB messages', function () { describe('RFB messages', function () {

View File

@ -211,6 +211,10 @@
<label for="noVNC_setting_quality">Quality:</label> <label for="noVNC_setting_quality">Quality:</label>
<input id="noVNC_setting_quality" type="range" min="0" max="9" value="6"> <input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
</li> </li>
<li>
<label for="noVNC_setting_compression">Compression level:</label>
<input id="noVNC_setting_compression" type="range" min="0" max="9" value="2">
</li>
<li><hr></li> <li><hr></li>
<li> <li>
<label for="noVNC_setting_repeaterID">Repeater ID:</label> <label for="noVNC_setting_repeaterID">Repeater ID:</label>