{autotoc enabled=yes}

Video display calulator

Introduction

The Video Display Calculater API is part of the Javascript APIs. Front-end Javascript developers can override the default video positioning by implementing their own layout and position the participant videos freely in the video container.

Videos are displayed depending on the container size of the area where the meeting room places the videos. The position of each participant video is defined in absolute terms (x, y, width and height) relative to the main container.

Check out the Javascript APIs and iFrame embedding documentation pages to learn more about these topics.

Glossary

Term

Definition

Video, Participant Video

The video stream of a participant.

Container, Video Container

The area of the meeting room in which all videos are displayed

Primary and secondary display area

The presentation room layout has two distinct areas within the video container: the primary and secondary area.

Note: There is no secondary area in the classic meeting room layout, the primary area always covers the entire container.

By default, all videos are displayed in the primary area. In this case, the primary are covers 100% of the video container. The moderator can move videos between the primary and the secondary area. As soon as at least one video is displayed in the secondary are the container is split into two parts: the primary area which consist of 85% of the container and the secondary area which uses the remaining 15% of the container.

The secondary area can be moved around all corners (top, right, bottom, left)

Calculating video positions

The video display calculator is triggered whenever the video positions should be updated. You have to calculate the absolute position and size of each video within the container and return an array of Video Position objects.

The system can display up to 12 video containers. Base on your own rules and the number of participants, you will choose to show or hide them.

Input parameters

The video display calculator receives the following paramters:

Parameter name

Data type

Explanation

containerWidth

number

The width of the video container, in pixels

containerHeight

number

The height of the video container, in pixels

hasSecondaryArea

boolean

True if secondary display is visible, false otherwise.

secondaryPosition

SecondaryVideoPosition

The region where the secondary video display area is positioned

secondarySize

SecondaryVideoSize

The size of the video container, in per percentage of the width or height of the entire container

primaryParticipants

string[]

The list of participant IDs attached to the primary area

secondaryParticipants

string[]

The list of participant IDs attached to the secondary area

participantsOrder

string[]

The ordered list of participant IDs

particpantsMediaInformation

IParticipantsMediaInformation

An object containing IMediaInformation for each of the participants in participantsOrder. IMediaInformation contains information about the size and state of the participants video.

hasSelfView

boolean

True if there is a self-view, false otherwise

isSelfviewInSecondary

boolean

True if the self view is displayed in the secondary container, false otherwise

isTVMode

boolean

True if the meeting room is displayed in the TV mode, false otherwise

 

Important note: The self view participant ID is NOT part of any participant ID array. If there is a self view (hasSelfView === true) you may decide to add the self view. Use the string myself to add the selfview to the output data structure.

Output data structure

The output structure contains an array of video positions whereas the position elements will be applied in that order to the available video containers. The output structure also consist of an array of booleans, indicating if an element should be hidden or not.

Parameter name

Data type

Explanation

videoPositions

IVideoPosition[]

An array of IVideoPosition objects for all visible video containers

hiddenContainers

boolean[]

An array of size 12 with false if the video should be displayed and true otherwise


The video position interface is defined as follows:

Parameter name

Data type

Explanation

participantId

string

The participant ID of the video which should be displayed in this container

width

number

The width of the video container, in pixels

height

number

The height of the video container, in pixels

top

number

The number of pixels from the top of the container

left

number

The number of pixels from the left of the container

zIndex

number

The z-Index of the element, if you want to position video displays on top of each other

cssClasses

string[]

CSS classes which will be applied to the video container. Important: The CSS classes MUST be prefixed with 'video-container-'

Error handling

If the video display calculator throws an error or returns an empty object the system will revert to the internal position calculator instead.

Example

The following example code places the first participant in the center of the screen, all other participants in one row above the first participant and the self view below the participant:

This example does not use the secondary area, all videos are displayed in the primary area.


const videoDisplayCalculator = {
  calculatePositions: (containerWidth,
    containerHeight,
    hasSecondaryArea,
    secondaryPosition,
    secondarySize,
    primaryParticipants,
    secondaryParticipants,
    participantsOrder,
    particpantsMediaInformation,
    hasSelfView,
    isSelfviewInSecondary,
    isTVMode) => {

    const result = {
      videoPositions: [],
      hiddenContainers: Array(12).fill(true)
    }

    // We want to display the videos with a 4:3 format
    const threeToFour = 3 / 4;
    // The main video is placed in the center, 65% of the container size;
    const mainContainerSizePercentage = 0.65

    let mainContainerWidth = 0;
    let mainContainerHeight = 0;
    let mainContainerTop = 0;
    let mainContainerLeft = 0;

    if (Array.isArray(participantsOrder) && participantsOrder.length > 0) {
      mainContainerWidth = containerWidth * mainContainerSizePercentage;
      mainContainerHeight = mainContainerWidth * threeToFour;
      if (mainContainerHeight > containerHeight * mainContainerSizePercentage) {
        mainContainerHeight = containerHeight * mainContainerSizePercentage;
        mainContainerWidth = mainContainerHeight / threeToFour;
      }

      mainContainerTop = (containerHeight - mainContainerHeight) / 2;
      mainContainerLeft = (containerWidth - mainContainerWidth) / 2;

      // Adding the main video
      result.videoPositions.push({
        participantId: participantsOrder[0],
        width: mainContainerWidth,
        height: mainContainerHeight,
        top: mainContainerTop,
        left: mainContainerLeft,
        zIndex: 1
      });
      result.hiddenContainers[0] = false;

      const numberOfOtherVideos = participantsOrder.length - 1;

      if (numberOfOtherVideos > 0) {
        // We have other videos
        // Adding all other videos in the top row;
        let otherContainersWidth = containerWidth / numberOfOtherVideos;
        let otherContainersHeight = otherContainersWidth * threeToFour;
        if (otherContainersHeight > mainContainerTop) {
          otherContainersHeight = mainContainerTop;
          otherContainersWidth = otherContainersHeight / threeToFour;
        }

        const otherContainersTop = (mainContainerTop - otherContainersHeight) / 2;
        let otherContainersLeft = (containerWidth / 2) - ((otherContainersWidth * numberOfOtherVideos) / 2);

        for (let i = 1; i < participantsOrder.length; i++) {
          result.videoPositions.push({
            participantId: participantsOrder[i],
            width: otherContainersWidth,
            height: otherContainersHeight,
            top: otherContainersTop,
            left: otherContainersLeft,
            zIndex: 1
          });
          result.hiddenContainers[i] = false;

          otherContainersLeft += otherContainersWidth;
        }
      }
    }

    if (hasSecondaryArea) {
      // Todo
    }

    if (hasSelfView) {
      // We have a self view, add the self video below the main video
      const selfViewHeight = containerHeight - mainContainerTop - mainContainerHeight;
      const selfViewWidth = selfViewHeight / threeToFour;

      result.videoPositions.push({
        participantId: "myself", // Note: the self view participant ID is always 'myself'
        width: selfViewWidth,
        height: selfViewHeight,
        top: containerHeight - selfViewHeight,
        left: (containerWidth / 2) - (selfViewWidth / 2),
        zIndex: 1
      });
    }

    return result;
  }
}

window["wlvmrApiReady"] = (handler) => {
  if (!window[handler]) {
    console.error(`Handler window.${handler} not found!`);
    return;
  }
  window[handler].setVideoDisplayCalculator(videoDisplayCalculator);
}

CODE

Type definitions

The following input and output types are used to interact with the Javascript API.

Interfaces

interface IVideoPosition {
  participantId: string;
  width: number;
  height: number;
  top: number;
  left: number;
  zIndex: number;
  // Important: CSS class names MUST be prefixed with 'video-container-'
  cssClasses?: string[]
}

interface IVideoDisplayConfig {
  videoPositions: IVideoPosition[],
  hiddenContainers: boolean[]
}


export interface IMediaInformation {
    audioMuted?: boolean;
    videoMuted?: boolean;
    hasAudio?: boolean;
    hadAudio?: boolean;
    offersVideo?: boolean;
    hasVideo?: boolean;
    hadVideo?: boolean;
    videoWidth?: number;
    videoHeight?: number;
}

export interface IParticipantsMediaInformation {
    [participantId: string]: IMediaInformation
}

interface IVideoDisplayCalculator {
  calculatePositions(
    containerWidth: number,
    containerHeight: number,
    hasSecondaryArea: boolean,
    secondaryPosition: SecondaryVideoPosition,
    secondarySize: SecondaryVideoSize,
    primaryParticipants: string[],
    secondaryParticipants: string[],
    participantsOrder: string[],
    particpantsMediaInformation: IParticipantsMediaInformation,
    hasSelfView: boolean,
    isSelfviewInSecondary: boolean,
    isTVMode: boolean
  ): IVideoDisplayConfig;
}
CODE

Enums

 enum SecondaryVideoSize {  small = 0.15,  large = 0.5}

Types

type SecondaryVideoPosition = "none" | "left" | "top" | "right" | "bottom";

Custom Tools

Overview

Custom tools allow integrators and developers to create custom tools within the meeting room. These tools consist of a label and an iFrame URL

Configuration

Custom tools are configured in the system settings of a white label instance. A white label instance can only have one custom tool configuration. By default, no custom tool is configured.

Custom tools can either be configured as a static iFrame URL or through an API endpoint.

iFrame URL

A static iFrame tool configuration consists of the iFrame URL and the tool label:

The iFrame must be served with HTTPS. Furthermore, the webserver must send the correct CORS headers to allow the browser to display the iFrame within the meeting room.

The label is either a fixed string or a pipe-separated string of translations. Examples:

Label

Explanation

My Custom Tool

The label shown in the interface will always read “My Custom Tool”, independent of the user interface language of the web meeting.

en:My Custom Tool|de-CH:Mein eigenes Tool|fr: Mon outil personnalisé

A user interface in English will show “My Custom Tool”, the user interface in German will show “Mein eigenes Tool”, etc. A user interface in Spanish (a language not defined in the label configuration) will either show “My Custom Tool” if an English label is available, or the label first defined in the list.

The iFrame will be embedded in the meeting room with additional query parameters to allow the developers to customize the iFrame depending on these additional query parameters. The query parameters added to the iFrame URL are the following:

Query parameter name

Explanation

participantId

The unique ID of the meeting participant

participantName

The name of the participant

meetingToken

The meeting ID in the form of “0000-0000-0000-0000”

meetingId

The meeting ID in the form of “5349b4ddd2781d08c09890f3”

moderatorToken

Optional, only if available for this specific user.

culture

The locale code of the participant at the time when the participant enters the meeting room, for example “en” or “en-US” or “de-DE”, etc

API

The API definition of the custom tool allows for more flexibility on the developer’s side. The developer can decide for each participant if a tool should be displayed or not, and which tool should be displayed. It is even possible to define multiple separate tools (up to 5).


The API tool needs to be configured in the system settings of a white label instance:

Once configured the web meeting server API will call this end point for each participant who joins the meeting to retrieve the list of tools which should be displayed to this specific participant.

The workflow is summaries as shown below:

The API request initiated by the web meeting server is an HTTP GET request with the following query parameters:

Query parameter name

Explanation

participantId

The unique ID of the meeting participant

participantName

The name of the participant

meetingToken

The meeting ID in the form of “0000-0000-0000-0000”

meetingId

The meeting ID in the form of “5349b4ddd2781d08c09890f3”

moderatorToken

Optional, only if available for this specific user.

culture

The locale code of the participant at the time when the participant enters the meeting room, for example “en” or “en-US” or “de-DE”, etc

The API secret defined in the system configuration will be sent as an HTTP header “X-API-KEY”.

The API request expects an HTTP 200 response code and a JSON Array object consisting of a list of tool objects. A tool object is defined as:

Property name

Explanation

iFrameUrl

An URL of an iFrame to be displayed

toolIcon

SVG string

labels

An array of “label” objects

Whereas a label object is defined as

Property name

Explanation

culture

The locale of the label, for example “en” or “en-US” or “de-DE”, etc

label

String, the label displayed, e.g. “My Custom Tool”

Example request:

curl 'https://<CUSTOM-TOOL-API-URL>?participantId=XXXXX\
&participantName=Joe%20Doe&meetingId=0000-0000-0000-0000\
&meetingToken=YYYYY&culture=en' \
  -H 'X-API-KEY: <CUSTOM-TOOL-API-Key>' \
  -H 'accept: application/json, text/plain, */*'
CODE

Example response:

[
  {
    "iFrameUrl": "https://www.example.com/custom-tool-1",
    "toolIcon": "<svg>…</svg>",
    "labels": [
      {
        "culture": "en-US",
        "label": "Custom tool 1"
      },
      {
        "culture": "de",
        "label": "Spezialtool 1"
      }
    ]
  }
]
CODE