You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2017/04/21 05:28:46 UTC

[2/5] incubator-guacamole-client git commit: GUACAMOLE-250: Automatically store keyframes while recordings are being played.

GUACAMOLE-250: Automatically store keyframes while recordings are being played.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/9d5e1111
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/9d5e1111
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/9d5e1111

Branch: refs/heads/master
Commit: 9d5e1111a674ba0df66e6839488b1c5a261230f0
Parents: 1fcb5f2
Author: Michael Jumper <mj...@apache.org>
Authored: Sat Apr 15 16:04:12 2017 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Sat Apr 15 17:12:29 2017 -0700

----------------------------------------------------------------------
 .../src/main/webapp/modules/SessionRecording.js | 100 ++++++++++++++++++-
 1 file changed, 99 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/9d5e1111/guacamole-common-js/src/main/webapp/modules/SessionRecording.js
----------------------------------------------------------------------
diff --git a/guacamole-common-js/src/main/webapp/modules/SessionRecording.js b/guacamole-common-js/src/main/webapp/modules/SessionRecording.js
index 5922f17..aa9e335 100644
--- a/guacamole-common-js/src/main/webapp/modules/SessionRecording.js
+++ b/guacamole-common-js/src/main/webapp/modules/SessionRecording.js
@@ -42,6 +42,25 @@ Guacamole.SessionRecording = function SessionRecording(tunnel) {
     var recording = this;
 
     /**
+     * The minimum number of characters which must have been read between
+     * keyframes.
+     *
+     * @private
+     * @constant
+     * @type {Number}
+     */
+    var KEYFRAME_CHAR_INTERVAL = 16384;
+
+    /**
+     * The minimum number of milliseconds which must elapse between keyframes.
+     *
+     * @private
+     * @constant
+     * @type {Number}
+     */
+    var KEYFRAME_TIME_INTERVAL = 5000;
+
+    /**
      * All frames parsed from the provided tunnel.
      *
      * @private
@@ -59,6 +78,24 @@ Guacamole.SessionRecording = function SessionRecording(tunnel) {
     var instructions = [];
 
     /**
+     * The approximate number of characters which have been read from the
+     * provided tunnel since the last frame was flagged for use as a keyframe.
+     *
+     * @private
+     * @type {Number}
+     */
+    var charactersSinceLastKeyframe = 0;
+
+    /**
+     * The timestamp of the last frame which was flagged for use as a keyframe.
+     * If no timestamp has yet been flagged, this will be 0.
+     *
+     * @private
+     * @type {Number}
+     */
+    var lastKeyframeTimestamp = 0;
+
+    /**
      * Tunnel which feeds arbitrary instructions to the client used by this
      * Guacamole.SessionRecording for playback of the session recording.
      *
@@ -123,7 +160,9 @@ Guacamole.SessionRecording = function SessionRecording(tunnel) {
     tunnel.oninstruction = function handleInstruction(opcode, args) {
 
         // Store opcode and arguments for received instruction
-        instructions.push(new Guacamole.SessionRecording._Frame.Instruction(opcode, args.slice()));
+        var instruction = new Guacamole.SessionRecording._Frame.Instruction(opcode, args.slice());
+        instructions.push(instruction);
+        charactersSinceLastKeyframe += instruction.getSize();
 
         // Once a sync is received, store all instructions since the last
         // frame as a new frame
@@ -136,6 +175,16 @@ Guacamole.SessionRecording = function SessionRecording(tunnel) {
             var frame = new Guacamole.SessionRecording._Frame(timestamp, instructions);
             frames.push(frame);
 
+            // This frame should eventually become a keyframe if enough data
+            // has been processed and enough recording time has elapsed, or if
+            // this is the absolute first frame
+            if (frames.length === 1 || (charactersSinceLastKeyframe >= KEYFRAME_CHAR_INTERVAL
+                    && timestamp - lastKeyframeTimestamp >= KEYFRAME_TIME_INTERVAL)) {
+                frame.keyframe = true;
+                lastKeyframeTimestamp = timestamp;
+                charactersSinceLastKeyframe = 0;
+            }
+
             // Clear set of instructions in preparation for next frame
             instructions = [];
 
@@ -189,6 +238,13 @@ Guacamole.SessionRecording = function SessionRecording(tunnel) {
             playbackTunnel.receiveInstruction(instruction.opcode, instruction.args);
         }
 
+        // Store client state if frame is flagged as a keyframe
+        if (frame.keyframe && !frame.clientState) {
+            playbackClient.exportState(function storeClientState(state) {
+                frame.clientState = state;
+            });
+        }
+
     };
 
     /**
@@ -472,6 +528,17 @@ Guacamole.SessionRecording = function SessionRecording(tunnel) {
 Guacamole.SessionRecording._Frame = function _Frame(timestamp, instructions) {
 
     /**
+     * Whether this frame should be used as a keyframe if possible. This value
+     * is purely advisory. The stored clientState must eventually be manually
+     * set for the frame to be used as a keyframe. By default, frames are not
+     * keyframes.
+     *
+     * @type {Boolean}
+     * @default false
+     */
+    this.keyframe = false;
+
+    /**
      * The timestamp of this frame, as dictated by the "sync" instruction which
      * terminates the frame.
      *
@@ -493,6 +560,7 @@ Guacamole.SessionRecording._Frame = function _Frame(timestamp, instructions) {
      * be null.
      *
      * @type {Object}
+     * @default null
      */
     this.clientState = null;
 
@@ -513,6 +581,14 @@ Guacamole.SessionRecording._Frame = function _Frame(timestamp, instructions) {
 Guacamole.SessionRecording._Frame.Instruction = function Instruction(opcode, args) {
 
     /**
+     * Reference to this Guacamole.SessionRecording._Frame.Instruction.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording._Frame.Instruction}
+     */
+    var instruction = this;
+
+    /**
      * The opcode of this Guacamole instruction.
      *
      * @type {String}
@@ -526,6 +602,28 @@ Guacamole.SessionRecording._Frame.Instruction = function Instruction(opcode, arg
      */
     this.args = args;
 
+    /**
+     * Returns the approximate number of characters which make up this
+     * instruction. This value is only approximate as it excludes the length
+     * prefixes and various delimiters used by the Guacamole protocol; only
+     * the content of the opcode and each argument is taken into account.
+     *
+     * @returns {Number}
+     *     The approximate size of this instruction, in characters.
+     */
+    this.getSize = function getSize() {
+
+        // Init with length of opcode
+        var size = instruction.opcode.length;
+
+        // Add length of all arguments
+        for (var i = 0; i < instruction.args.length; i++)
+            size += instruction.args[i].length;
+
+        return size;
+
+    };
+
 };
 
 /**