All files / src StreamClient.js

100% Statements 71/71
100% Branches 9/9
100% Functions 2/2
100% Lines 71/71

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 721x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 34x 34x 34x 34x 34x 34x 34x 34x 34x 34x 1x 1x 1x 86x 86x 42x 42x 42x 44x 44x 86x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 4x 4x 24x 24x 24x 4x 4x 24x 24x 24x 24x 86x 1x 1x 1x  
// @ts-check
// SPDX-License-Identifier: Zlib
// SPDX-FileCopyrightText: 2024 CERN (home.cern)
 
const DELTA = 10;
 
class StreamClient {
 
  /**
   * @param {{ throttle: number|undefined, cb: (arg: Buffer|Error) => void}} arg
   */
  constructor({ throttle, cb }) {
 
    /** @type {number|undefined} */
    this.throttle = Number(throttle);
 
    /** @type {(Buffer|Error) => void} */
    this.cb = cb;
 
    /** @type {number} */
    this.next = 0; // last received frame timestamp
  }
 
  /** @param {Buffer|Error} data */
  throttledCB(data) {
 
    if (!this.throttle) {
      this.cb(data);
      return;
    }
 
    // Otherwise, we check if the required time interval has passed
    if (this.next - DELTA < Date.now()) {
 
      /**
       * We initialize this.next at 0 to have no throttle before the first frame,
       * so on the first receival we will need to initialize it.
       */
      const uninitialized = this.next === 0;
 
      /**
       * We also absolutely want to avoid the this.next timer to get overdue again
       * and again if we have some frames lost because when we will finally receive
       * again, all the next frames will be accepted in a row, breaking the framerate.
       *
       * normal  | <----------> (2) <----------> (3) <-------
       *       [1]____|______|_____[2]_____|_____[3]_____|
       *                     2nd accepted       3rd accepted
       *
       * overdue | <----------> (2) <----------> (3) <-------
       *       [1]____?______?______?______?____[2]____[3]
       *                 missing frames     two accepted at once!
      */
      const overdue = this.next < Date.now() - this.throttle;
 
      if (overdue) {
        console.warn("The client throttling was overdue and was reset");
      }
 
      // In both cases, we want to reset the this.next as `now + throttle`
      if (uninitialized || overdue) {
        this.next = Date.now();
      }
 
      this.next += this.throttle;
      this.cb(data);
    }
  }
}
 
export default StreamClient;