unityController - 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.
In This Section
onConnect¶
onConnect()
The onConnect method starts the connection to the services. If the connection
executes properly, we proceed to the on_channel_join
method with the
IChannelJoinResponse()
information found.
// Start the connection to the services
onConnect() {
let promise = $.post("/api/public/channels/join", {}, (joinRep) => {
this.on_channel_join(<genvid.IChannelJoinResponse>joinRep);
});
promise.fail((err) => {
alert("Can't get the stream info:" + err);
});
}
on_channel_join¶
on_channel_join(info: genvid.IChannelJoinResponse)
The on_channel_join method creates the Genvid client after connecting to the
services and channel. We use the information found during this process and the
video_player_id
sent during the class-creation process to create the
client (it is an argument to the unityController 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.
// Create the genvid Client and the function listening to it
private on_channel_join(joinRep: genvid.IChannelJoinResponse) {
this.streamInfo = joinRep.info;
this.client = genvid.createGenvidClient(this.streamInfo, joinRep.uri, joinRep.token, this.video_player_id);
this.client.onVideoPlayerReady((elem) => { this.on_video_player_ready(elem); });
this.client.onStreamsReceived((streams) => { this.on_streams_received(streams); });
this.client.onNotificationsReceived(this.on_notifications_received.bind(this));
this.client.onDraw((frame) => { this.on_new_frame(frame); });
this.client.onDisconnect(() => {this.on_disconnect_detected();});
this.client.start();
}
on_disconnect_detected¶
on_disconnect_detected()
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.
this.client.onDisconnect(() => {this.on_disconnect_detected();});
Now we instruct the script to launch reconnection attempts when we’re notified the client socket closed.
private on_disconnect_detected(){
let promise = $.post("/api/public/channels/join", {}, (joinRep) => {
this.client.reconnect(joinRep.info, joinRep.uri, joinRep.token);
this.resetFibNums();
});
promise.fail( async () => {
await this.sleep(this.getNewSleepDuration());
this.on_disconnect_detected();
});
}
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.
private sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
private getNewSleepDuration() {
this.fibNumResult = this.fibNumOne + this.fibNumTwo;
this.fibNumOne = this.fibNumTwo;
this.fibNumTwo = this.fibNumResult;
this.delayBetweenReconnectionAttempts = this.fibNumResult + (this.fibNumResult * Math.floor(Math.random() * 10) * 0.01); // + 0 to 10%
return this.delayBetweenReconnectionAttempts * 1000; // milliseconds
}
private resetFibNums() {
this.fibNumOne = 0;
this.fibNumTwo = 1;
}
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.
on_video_player_ready¶
on_video_player_ready(_elem: HTMLElement)
This method is used to initiate several variables, create the WebGL context, and add several event listeners.
We get most of the Div and Link elements in this function via jQuery.
this.video_player = this.client.videoPlayer; this.timeLocalDiv = <HTMLDivElement>document.querySelector("#time_local"); this.timeVideoDiv = <HTMLDivElement>document.querySelector("#time_video"); this.timeVideoRawDiv = <HTMLDivElement>document.querySelector("#time_video_raw"); this.timeComposeDiv = <HTMLDivElement>document.querySelector("#time_compose"); this.timeComposeLastDiv = <HTMLDivElement>document.querySelector("#time_compose_last"); this.timeStreamDiv = <HTMLDivElement>document.querySelector("#time_stream"); this.latencyDiv = <HTMLDivElement>document.querySelector("#latency"); this.delayOffsetDiv = <HTMLDivElement>document.querySelector("#delay_offset"); this.controlsDiv = <HTMLDivElement>document.querySelector("#game-controls"); this.timeCamSceneDiv = <HTMLDivElement>document.querySelector("#timeCamScene_overlay"); this.promptOverlay = <HTMLDivElement>document.querySelector("#prompt_overlay"); this.videoOverlay = <HTMLDivElement>document.querySelector("#video_overlay"); this.canvas3d = <HTMLCanvasElement>document.querySelector("#canvas_overlay_3d"); this.mouseOverlay = <HTMLDivElement>document.querySelector("#mouse_overlay"); this.genvidOverlayButton = <HTMLLinkElement>document.querySelector("#genvid_overlay_button"); this.genvidOverlay = <HTMLDivElement>document.querySelector("#genvid_overlay"); this.help_overlay = <HTMLDivElement>document.querySelector("#help_overlay"); this.helpButton = <HTMLLinkElement>document.querySelector("#help_button"); this.fullScreenDiv = <HTMLDivElement>document.querySelector(".fullscreen-button"); this.fullScreenElement = <HTMLElement>document.querySelector(".fa-expand");
We create the WebGLContext with canvas3d selected.
We add all the window-change event-listeners (fullscreen, resize).
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.hideOverlay(); this.client.videoPlayer.addEventListener(genvid.PlayerEvents.PAUSE, () => { this.hideOverlay(); }); this.client.videoPlayer.addEventListener(genvid.PlayerEvents.PLAYING, () => { this.showOverlay(); }); this.genvidWebGL = genvid.createWebGLContext(this.canvas3d); // Need to assign before any resize. document.addEventListener("fullscreenchange", () => { this.onResize(); }); document.addEventListener("webkitfullscreenchange", () => { this.onResize(); }); document.addEventListener("mozfullscreenchange", () => { this.onResize(); }); _elem.addEventListener("resize", () => { this.onResize(); }); window.addEventListener("resize", () => { this.onResize(); }, true); window.addEventListener("orientationchange", () => { this.onResize(); }, true); window.addEventListener("sizemodechange", () => { this.onResize(); }, true); window.setInterval(() => { this.onResize(); }, 1000); // Just a safety, in case something goes wrong. this.mouseOverlay.addEventListener("click", (event) => { this.clickCube(event); }, false); this.genvidOverlayButton.addEventListener("click", (_event) => { this.toggleGenvidOverlay(); }, false); this.helpButton.addEventListener("click", (_event) => { this.onHelpActivation(); }, false); this.fullScreenDiv.addEventListener("click", (_event) => { this.toggleFullScreen(); }, false);
We do an onResize() to fit the overlays to the size of the window. (This is required otherwise the overlays are too small.)
Finally, we initialize the WebGL overlay.
this.onResize(); // Initialize graphics stuff. this.genvidWebGL.clear(); let gl = this.genvidWebGL.gl; gl.disable(gl.DEPTH_TEST); this.gfx_initShaders(); this.gfx_initRenderCommands(); const muteIcon = document.getElementById("mute-button"); muteIcon.addEventListener("click", () => this.onMute()); if (!this.video_player.getMuted()) { this.onMute(); } this.videoReady = true;