From efd1f8a4f2acbd40833aa2a5fec2ed4a598e6753 Mon Sep 17 00:00:00 2001 From: Andrey Trebler Date: Mon, 10 Feb 2020 12:44:36 +0100 Subject: [PATCH] adds qualityLevel property to RFB class for updating JPEG quality level encoding on the fly --- core/rfb.js | 24 +++++++++- core/util/polyfill.js | 9 +++- docs/API.md | 5 +++ tests/test.rfb.js | 102 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 2 deletions(-) diff --git a/core/rfb.js b/core/rfb.js index 536ea25..72f279a 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -275,6 +275,8 @@ export default class RFB extends EventTargetMixin { Log.Warn("Specifying showDotCursor as a RFB constructor argument is deprecated"); this._showDotCursor = options.showDotCursor; } + + this._qualityLevel = 6; } // ===== PROPERTIES ===== @@ -337,6 +339,26 @@ export default class RFB extends EventTargetMixin { get background() { return this._screen.style.background; } set background(cssValue) { this._screen.style.background = cssValue; } + get qualityLevel() { + return this._qualityLevel; + } + set qualityLevel(qualityLevel) { + if (!Number.isInteger(qualityLevel) || qualityLevel < 0 || qualityLevel > 9) { + Log.Error("qualityLevel must be an integer between 0 and 9"); + return; + } + + if (this._qualityLevel === qualityLevel) { + return; + } + + this._qualityLevel = qualityLevel; + + if (this._rfb_connection_state === 'connected') { + this._sendEncodings(); + } + } + // ===== PUBLIC METHODS ===== disconnect() { @@ -1294,7 +1316,7 @@ export default class RFB extends EventTargetMixin { encs.push(encodings.encodingRaw); // Psuedo-encoding settings - encs.push(encodings.pseudoEncodingQualityLevel0 + 6); + encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel); encs.push(encodings.pseudoEncodingCompressLevel0 + 2); encs.push(encodings.pseudoEncodingDesktopSize); diff --git a/core/util/polyfill.js b/core/util/polyfill.js index 648ceeb..0e458c8 100644 --- a/core/util/polyfill.js +++ b/core/util/polyfill.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors + * Copyright (C) 2020 The noVNC Authors * Licensed under MPL 2.0 or any later version (see LICENSE.txt) */ @@ -52,3 +52,10 @@ if (typeof Object.assign != 'function') { window.CustomEvent = CustomEvent; } })(); + +/* Number.isInteger() (taken from MDN) */ +Number.isInteger = Number.isInteger || function isInteger(value) { + return typeof value === 'number' && + isFinite(value) && + Math.floor(value) === value; +}; diff --git a/docs/API.md b/docs/API.md index aa6337f..1b00179 100644 --- a/docs/API.md +++ b/docs/API.md @@ -64,6 +64,11 @@ protocol stream. to the element containing the remote session screen. The default value is `rgb(40, 40, 40)` (solid gray color). +`qualityLevel` + - Is an `int` in range `[0-9]` controlling the desired JPEG quality. + Value `0` implies low quality and `9` implies high quality. + Default value is `6`. + `capabilities` *Read only* - Is an `Object` indicating which optional extensions are available on the server. Some methods may only be called if the corresponding diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 41232ae..4e27393 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -2865,6 +2865,108 @@ describe('Remote Frame Buffer Protocol Client', function () { // error events do nothing }); }); + + describe('Quality level setting', function () { + const defaultQuality = 6; + + let client; + + beforeEach(function () { + client = make_rfb(); + sinon.spy(RFB.messages, "clientEncodings"); + }); + + afterEach(function () { + RFB.messages.clientEncodings.restore(); + }); + + it(`should equal ${defaultQuality} by default`, function () { + expect(client._qualityLevel).to.equal(defaultQuality); + }); + + it('should ignore non-integers when set', function () { + client.qualityLevel = '1'; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.qualityLevel = 1.5; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.qualityLevel = null; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.qualityLevel = undefined; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.qualityLevel = {}; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + }); + + it('should ignore integers out of range [0, 9]', function () { + client.qualityLevel = -1; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.qualityLevel = 10; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + }); + + it('should send clientEncodings with new quality value', function () { + let newQuality; + + newQuality = 8; + client.qualityLevel = newQuality; + expect(client.qualityLevel).to.equal(newQuality); + expect(RFB.messages.clientEncodings).to.have.been.calledOnce; + expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); + }); + + it('should not send clientEncodings if quality is the same', function () { + let newQuality; + + newQuality = 2; + client.qualityLevel = newQuality; + expect(RFB.messages.clientEncodings).to.have.been.calledOnce; + expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); + + RFB.messages.clientEncodings.resetHistory(); + + client.qualityLevel = newQuality; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + }); + + it('should not send clientEncodings if not in connected state', function () { + let newQuality; + + client._rfb_connection_state = ''; + newQuality = 2; + client.qualityLevel = newQuality; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client._rfb_connection_state = 'connnecting'; + newQuality = 6; + client.qualityLevel = newQuality; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client._rfb_connection_state = 'connected'; + newQuality = 5; + client.qualityLevel = newQuality; + expect(RFB.messages.clientEncodings).to.have.been.calledOnce; + expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); + }); + }); }); describe('RFB messages', function () { -- 2.39.2