Video display calculator

Introduction

The Video Display Calculator 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

TermDefinition
Video, Participant VideoThe video stream of a participant.
Container, Video ContainerThe 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.

Primary and secondary area

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 nameData typeExplanation
containerWidthnumberThe width of the video container, in pixels
containerHeightnumberThe height of the video container, in pixels
hasSecondaryAreabooleanTrue if secondary display is visible, false otherwise.
secondaryPositionSecondaryVideoPositionThe region where the secondary video display area is positioned
secondarySizeSecondaryVideoSizeThe size of the video container, in per percentage of the width or height of the entire container
primaryParticipantsstring[]The list of participant IDs attached to the primary area
secondaryParticipantsstring[]The list of participant IDs attached to the secondary area
participantsOrderstring[]The ordered list of participant IDs
particpantsMediaInformationIParticipantsMediaInformationAn object containing IMediaInformation for each of the participants in participantsOrder. IMediaInformation contains information about the size and state of the participants video.
hasSelfViewbooleanTrue if there is a self-view, false otherwise
isSelfviewInSecondarybooleanTrue if the self view is displayed in the secondary container, false otherwise
isTVModebooleanTrue 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 nameData typeExplanation
videoPositionsIVideoPosition[]An array of IVideoPosition objects for all visible video containers
hiddenContainersboolean[]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 nameData typeExplanation
participantIdstringThe participant ID of the video which should be displayed in this container
widthnumberThe width of the video container, in pixels
heightnumberThe height of the video container, in pixels
topnumberThe number of pixels from the top of the container
leftnumberThe number of pixels from the left of the container
zIndexnumberThe z-Index of the element, if you want to position video displays on top of each other
cssClassesstring[]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:

Example video display

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);
}

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;
}

Enums

enum SecondaryVideoSize {
  small = 0.15,
  large = 0.5
}

Types

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

Not sure how to best implement your project?

Contact our team to discuss the details.

+41 43 500 11 82
+1 646 583 2652