Website Integration

Because every game exposes Genvid differently, every game requires a customized website. This includes both the skinning aspect (HTML and CSS) as well as the functional side (JavaScript). You also need to run a web server hosting the web application for the browser.

Since there are multiple ways of creating websites, this section only covers steps common to the Genvid MILE SDK.

JavaScript API

Genvid provides two libraries:

  • genvid Contains the logic necessary to connect to the leaf, decode its stream, create a video player, and sync data and video.
  • genvid-math Provides several geometry computation utility functions that you may need to create a WebGL overlay for example. You can find examples using this lib in the Samples.

These two libraries each come in three different bundles:

  • NPM Package archive A gzipped tarball file that you can import as if it were a local NPM package. You can then import your dependencies using the ES6 syntax and bundle your project using the method of your choice. You will have access to type definitions.
  • ES5 js module This allows you to import the libraries, such as by exposing the global variables genvid and genvidMath. (Note: You won’t have access to the type definitions.) All our Cube Samples go through the Web Sample which uses this method.
  • UMD js module A UMD module that allows you to import the libraries in many ways, such as by exposing the global variables genvid and genvidMath. You won’t have access to the type definitions. Only our Twitch extension sample uses this method.

Note

The preferred method to use the genvid and genvid-math dependencies is now by importing our ES5 js modules. All the Cube Samples use the Web Sample, which illustrates this method.

We provide older type definitions for backwards compatibility in the api/web/legacy_types folder. However, we recommend following the Web Sample method for newer non-Twitch Extension projects. (The Twitch Extension Development Sample uses the UMD js module.)

Frontend integration

The first step in integrating Genvid into your frontend is to instantiate a genvidClient.IGenvidClient() object using the genvidClient.createGenvidClient():

let client = genvid.createGenvidClient(streamInfo, websocketURL, websocketToken, video_player_id);

The first parameter, streamInfo, corresponds to a genvidClient.IStreamInfo() structure, typically returned by the backend service from the POST /disco/stream/join call.

The next two parameters, websocketURL and websocketToken, are the two values specifying the websocket address and security token, respectively. They are provided by the backend through the POST /disco/stream/join call.

The last parameter, video_player_id, is a string referencing an HTML element that you want to use for the live streaming video player. When the IGenvidClient() creates the video player, it will replace this HTML element with the one from the live streaming service.

onVideoPlayerReady callback

When you create the player, IGenvidClient() calls the function specified with genvidClient.IGenvidClient.onVideoPlayerReady(). This is typically used to hook up the overlay.

client.onVideoPlayerReady( function(elem) { console.log('Create the overlay!'); } );

onAuthenticated callback

The onAuthenticated() callback tells the user when the IGenvidClient() successfully connects to the Genvid Services:

client.onAuthenticated( function(succeed) { if (succeeded) { console.log('Connected!'); } });

onStreamsReceived callback

The onStreamsReceived() callback is called as soon as the webclient receives new game data and annotations from the websocket connection to the leaf. As opposite to onDraw(), the data is not synchronized to the video being played.

The data given to that callback is the one the game sent using Genvid_SubmitGameData() and Genvid_SubmitAnnotation().

This callback is useful for decoding, analyzing, or any other costly processing you need to do on the data before it’s rendered: for each data frame, generally you can use the user field to store the data transformed by your processing. You can later access that field in the onDraw() method. We don’t advise you to make that processing at rendering time (onDraw()), since it can add latency to your overlay.

client.onStreamsReceived((streams) => { myGameOverlay.onStreamsReceived(streams); });

onNotificationsReceived callback

This method allows for registering a callback that will receive notifications as soon as they are received from the websocket connection to the Leaf.

Notifications can be sent from the game or the Webgateway API.

Notifications are used to transfer information as fast as possible and are not synchronized to the video streams, contrary to game data and annotations.

They are sent from the game using the native SDK function Genvid_SubmitNotification().

client.onNotificationsReceived((notifications) => { myGameOverlay.onNotificationsReceived(notifications); });

onDraw callback

The onDraw() method allows the registration of a callback that will be called regularly at a configurable framerate (defaults to 30 times per second) and carries the game data and annotations synchonized to the current video stream being played. You can usually use this to access the game data necessary to render your overlay or any data you need to be synchronized to the video stream:

client.onDraw((frame) => { myGameOverlay.onDraw(frame); });

Note

If you use onStreamsReceived() to transform the data, you will be accessing the transformed data in the onDraw() callback.

When the onDraw() callback is invoked, it receives a genvidClient.IDataFrame() object as a parameter. It contains the timecode for this video frame, the data streams coming from every session connected, and information about the video composition. The frame data is organized in the following ways:

  • You can obtain data from the sessions member. That member contains a list of streams and a list of annotations, both of which are classified by their respective session IDs.
  • The genvidClient.IDataFrame() interface also gives direct access to its streams and annotations members. However, those are deprecated and you shouldn’t use them. Use the sessions member described above to be in compliance with the new multi-session order.

Composition data (an array of composition.ISourceCompositionData()) is information about what transformations were applied to the different video streams. Some of the ways you can use it include knowing what pixel of the video overlay matches which video source or to perform a clipping operation to prevent an overlay related to one video source overlaying another video.

The current streams contain only the latest frame for each stream. Annotations, on the other hand, hold all of the previous frames accumulated so far and will not be repeated. Although streams and annotations behave differently, both are cast to the genvidClient.IDataStreamFrame() interface.

The Genvid MILE SDK carries data in binary form using the field rawdata, which is a JavaScript ArrayBuffer(). You can interpret the data in any way, and the SDK provides a few utility routines which can help decoding.

For example, if the game sends JSON data encoded as UTF-8, the website code needs to decode the rawdata binary field as UTF-8 and parse the resulting string as JSON. You can use the accessor genvidClient.IDataStreamFrame.data() that, when accessed, will try to decode the genvidClient.IDataStreamFrame.rawdata() field using UTF-8.

Warning

If the data you’re sending does not support UTF-8 decoding, accessing the genvidClient.IDataStreamFrame.data() field will cause an exception.

onDraw(frame) {
   let stream = frame.streams["position"]
   let datastr = genvid.UTF8ToString(frame.rawdata);
   stream.user = JSON.parse(datastr);
   console.log(stream.user.myPosition);
}

Because Genvid sometimes repeats data (for example, when a stream has a low framerate value), we include a mechanism for avoiding decoding identical data multiple times. The IGenvidClient() includes the onStreamsReceived() function which passes a genvidClient.IDataStreams() collection upon reception of the data. The data streams contain a collection of video streams and their frames which you can modify before they get integrated into the Genvid synchronization engine. For example, parsing a collection to detect an upcoming event or removing the collection entirely.

You could also modify the function to decode differently based on the streamId, as long as you’re consistent with the format used when sending data from the game process (see Game-Data Streaming).

Once everything is ready in the game’s website code, you can start the GenvidClient streaming:

client.start();

The IGenvidClient() automatically uses configured callbacks and handles synchronization between the streaming video and the game data sent in the onDraw().

onDisconnect callback

The onDisconnect() callback gets triggered upon a websocket closing. You can bind functionality to this callback and be alerted when a socket closes:

client.onDisconnect(() => {this.onDisconnectDetected();});

reconnect callback

The reconnect() callback establishes a new websocket connection. It receives new stream info, a new leaf URI, and a new token as parameters.

client.reconnect(info, uri, token);

Overlay

While Genvid doesn’t provide a strict overlay API, it does expose everything necessary in the IGenvidClient() for the overlay to work. We also expose WebGL utility-routines which we use for all of our samples.

The main entry point to the overlay is the callback set using onDraw(). On regular intervals, the specified callback is invoked, with a frame of data for all streams existing in the game.

This callback receives the latest game-data frame for every stream. You have full control of what to do with that data: render some 3D highlights in a WebGL canvas, tweak HTML elements to display current game stats, adjust button visibility to allow new game events from the spectator, etc.

To facilitate WebGL rendering, Genvid provides a genvidWebGL.IWebGLContext() class which simplifies repetitive tasks in WebGL. You can create it with the genvidWebGL.createWebGLContext() method.

Events

You can send events back to the Genvid Services through the IGenvidClient() instance. See sendEvent() and sendEventObject() for more information.

Inside the Genvid services, for the sake of scalability, these direct events will be processed and reduced according to your own event configuration. An example of configuration can be seen here.

After that, the game will be able to subscribe to a reduced events stream.

For example, if 100,000 spectators send a “cheer” event in a short period of time, the game will receive a single object with a count value of 100,000 instead of receiving 100,000 messages in a similarly short period of time. This is the solution we provide in order to decouple the game machine load from the number of spectators watching a broadcast.

Google Chrome Autoplay Support

Google Chrome has a special policy for handling video playback. In our API, all video players that support it have the autoplay tag set. This enables starting video playback automatically on browsers like Firefox, as well as on websites with the Media Engagement Index set high enough.

If you want to enable automatic video-playback on most browsers, including Chrome, you can set the video to mute. You also need to ensure the overlay is hidden when the video is paused to avoid locking the user in front of a paused video. The following code is an example of how to do both.

client.onVideoPlayerReady( (elem) => {

    // Optional: Set to muted to autostart even on Chrome and also iOS.
    client.videoPlayer.setMuted(true);

    // Always safe to hide the overlay on startup.
    // The PLAYING event below will show it.
    myGameOverlay.hide();

    client.videoPlayer.addEventListener(genvid.PlayerEvents.PAUSE, () => {
      myGameOverlay.hide();
    });

    client.videoPlayer.addEventListener(genvid.PlayerEvents.PLAYING, () => {
      myGameOverlay.show();
    });
});