WebController - Genvid Client Initialization

The Genvid Client initialization section contains most of the code needed to start the stream, initialize various variables, and assign several event listeners.

start

start()

The start method starts the connection to the services. If the connection executes properly, we proceed to the onChannelJoin method with the IChannelJoinResponse() information found.

  // Starts the connection to the services
  start() {
    fetch("/api/public/channels/join", {
      method: "POST",
    })
      .then((data) => data.json())
      .then((res) => {
        if (res.name === "Error") {
          genvid.error(`Failed getting stream info: ${res.message}. Retrying in ${this.fiboStartIterator.get()} seconds...`);
          setTimeout(() => {
            this.start();
            this.fiboStartIterator.next();
          }, this.fiboStartIterator.get() * 1000);
        } else {
          this.onChannelJoin(res);
        }
      })
      .catch((error) => genvid.error(`Can't get the stream info: ${error}`));
  }

onChannelJoin

onChannelJoin(joinRep)

The onChannelJoin method creates the Genvid client after connecting to the services and channel. We use the information found during this process and the videoPlayerId sent during the class-creation process to create the client (it is an argument to the WebController class).

Afterwards, we need to associate specific events to function in this class:

onVideoPlayerReady
Triggers when the video stream is ready (used to initialize content).
onStreamsReceived
Triggers when the stream content is received (used to get the timecode and
game data).
onNotificationsReceived
Triggers when a notification is received (used for the popularity).
onDraw
Triggers when drawing a new frame in the video.

We then start the client with the start() method.

  // Creates the genvid Client and the functions listening to it
  onChannelJoin(joinRep) {
    genvid.getConfig().framePerSeconds = 30; // Defines the rate at which onDraw is called (default being 30).
    this.genvidClient = genvid.createGenvidClient(
      joinRep.info,
      joinRep.uri,
      joinRep.token,
      this.videoPlayerId
    );

    this.genvidClient.onStreamsReceived((streams) =>
      this.onStreamsReceived(streams)
    );
    this.genvidClient.onDraw((frame) => this.onNewFrame(frame));
    this.genvidClient.onNotificationsReceived((notifications) => {
      this.onNotificationsReceived(notifications);
    });
    this.genvidClient.onDisconnect(() => this.onDisconnectDetected());
    this.genvidClient.onVideoPlayerReady((elem) =>
      this.onVideoPlayerReady(elem)
    );
    this.genvidClient.start();
  }

onDisconnectDetected

oDdisconnecDdetected()

In this sample, we use the onDisconnect() callback (exposed by the IGenvidClient() interface) to notify us when the client’s socket closes. We do so by binding our desired callback.

thD.client.oDisconnect(() => {this.onDisconnectDetected();});

Now we instruct the script to launch reconnection attempts when we’re notified the client socket closed.

  onDisconnectDetected() {
    genvid.info("Disconnected");
    fetch("/api/public/channels/join", {
      method: "POST",
    })
      .then((data) => data.json())
      .then((res) => {
        this.genvidClient.reconnect(res.info, res.uri, res.token);
        this.fibonacciIterator.reset();
      })
      .catch(() => {
        genvid.info(
          `Will reconnect in ${this.fibonacciIterator.get()} seconds`
        );
        return this.sleep(this.getNewSleepDuration()).then(() =>
          this.onDisconnectDetected()
        );
      });
  }

Here we launch an information request asking for a new leaf address, a new token, and new streamInfo.

If the request succeeds, we call reconnect() to establish a new websocket connection using the above information.

If the request fails (for example, if there is no leaf available), we wait and retry. We determine the waiting period using the result of an incremental Fibonacci operation.

  sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

Note that we add a ten-percent randomness factor to the result of the Fibonacci sequence. This avoids cases where two services launch connection attempts simultaneously. We reset the Fibonacci values on a successful connection.

onVideoPlayerReady

onVideoPlayerReady(videoPlayerElement)

This method is used to initiate several variables, create the WebGL context, and add several event listeners.

  1. We get most of the Div and Link elements in this function via jQuery.

        this.timeCamSceneDiv = document.querySelector("#timeCamScene_overlay");
        this.volumeDisplay = document.querySelector("#volume_display");
        this.videoOverlay = document.querySelector("#video_overlay");
        this.canvas3d = document.querySelector("#canvas_overlay_3d");
        this.genvidOverlayButton = document.querySelector("#genvid_overlay_button");
        this.genvidOverlay = document.querySelector("#genvid_overlay");
        this.helpOverlay = document.querySelector("#help_overlay");
        this.helpButton = document.querySelector("#help_button");
        this.fullScreenIcon = document.querySelector(".fa-expand");
        this.pipFrameDiv = document.querySelector("#pip_frame");
    
        const mouseOverlay = document.querySelector("#mouse_overlay");
        mouseOverlay.addEventListener(
          "click",
          (event) => this.clickScene(event),
          false
        );
    
        const fullScreenButton = document.querySelector(".fullscreen-button");
        fullScreenButton.addEventListener(
          "click",
          () => {
            this.toggleFullScreen();
          },
          false
        );
    
        // Debug overlay
        this.timeLocalDiv = document.querySelector("#time_local"); // browser clock current time
        this.timeVideoDiv = document.querySelector("#time_video"); // video player current time
        this.timeComposeLastDiv = document.querySelector("#time_compose_last");
        this.timeStreamDiv = document.querySelector("#time_stream");
        this.latencyDiv = document.querySelector("#latency");
        this.delayOffsetDiv = document.querySelector("#delay_offset");
    
    
        document.addEventListener("fullscreenchange", () => {
          this.onResize();
        });
        document.addEventListener("webkitfullscreenchange", () => {
          this.onResize();
        });
        document.addEventListener("mozfullscreenchange", () => {
          this.onResize();
        });
    
        window.addEventListener(
          "resize",
          () => {
            this.onResize();
          },
          true
        );
        window.addEventListener(
          "orientationchange",
          () => {
            this.onResize();
          },
          true
        );
        window.addEventListener(
          "sizemodechange",
          () => {
            this.onResize();
          },
          true
        );
    
        this.genvidOverlayButton.addEventListener(
          "click",
          () => this.toggleGenvidOverlay(),
          false
        );
        this.helpButton.addEventListener(
          "click",
          () => {
            this.onHelpActivation();
          },
          false
        );
        const muteIcon = document.getElementById("mute-button");
        muteIcon.addEventListener("click", () => this.toggleMute());
    
  2. We create the WebGLContext with canvas3d selected.

  3. We add all the window-change event-listeners (fullscreen, resize).

  4. We add the event listeners for the click interaction on the mouseOverlay (for clicking on the video objects), the Genvid button and the Help button.

          this.videoPlayer = this.genvidClient.videoPlayer;
          this.genvidClient.videoPlayer.addEventListener(
            genvid.PlayerEvents.PAUSE,
            () => this.hideOverlay()
          );
          this.genvidClient.videoPlayer.addEventListener(
            genvid.PlayerEvents.PLAYING,
            () => this.showOverlay()
          );
    
          videoPlayerElement.addEventListener("resize", () => {
            this.onResize();
          });
    
          // GENVID - init video player stop
    
          // We have to have a mute by default because of the autoplay policy
          if (!this.videoPlayer.getMuted()) {
            this.toggleMute();
          }
    
  5. We do an onResize() to fit the overlays to the size of the window. (This is required otherwise the overlays are too small.)

  6. Finally, we initialize the WebGL overlay.

        this.initThreeJS(this.canvas3d);