Skip to content

Video Container

The VideoContainer component performs three tasks:

  • Obtain compatible video layouts: from the video manifest, and taking into account the configuration of the video layout plugins, a list of layouts that are compatible with the current video configuration will be generated. It also provides the necessary API to retrieving the supported layouts and activate them.
  • Access playback controls: provides the API needed to access the video playback controls.
  • Access soft trimming settings: provides the API to enable, disable and configure video soft trimming.

Note: in Paella Player 8 (@asicupv/paella-core >= 2.0) you can access to the basic playback functions through the Paella instance. These functions are available in the Paella instance for simplicity, but is still valid to use the videoContainer instance to access them. In fact, the playback functions of the Paella instance are implemented as a wrapper of the videoContainer functions.

Access to the VideoContainer component is through the Paella Player instance:

import { Paella } from '@asicupv/paella-core';
const player = new Paella('playerContainer');
...
const { videoContainer } = player;

Content identifiers, hereafter contentId, identify a video layout configuration. They are configured in the config.json file, in the configuration options of the layout plugins, as indicated in this document. The validContent array contains the different layout settings that can be applied, depending on the content section of the video manifest:

config.json:

"plugins": {
"es.upv.paella.singleVideo": {
"enabled": true,
"validContent": [
{
"id": "presenter", <<< content identifier
"content": ["presenter"], <<< valid content
"icon": "present-mode-2.svg",
"title": "Presenter"
},
{
"id": "presentation", <<< content identifier
"content": ["presentation"], <<< valid content
"icon": "present-mode-1.svg",
"title": "Presentation"
}
]
},
"es.upv.paella.dualVideo": {
"enabled": true,
"validContent": [
{
"id": "presenter-presentation", <<< content identifier
"content": ["presenter","presentation"], <<< valid content
"icon": "present-mode-3.svg",
"title": "Presenter and presentation"
}
]
},

In the content configuration (validContent), the most relevant attributes are id and content. The first one is the unique content identifier. This identifier is the one we will use in the API to activate a layout. The second one is the content array that will be extracted from the video manifest. It will contain as many elements as streams supported by the video layout plugin.

Each video stream in the video manifest is tagged with this attribute. The videoContainer will cross-reference the data in the video manifest with the configuration data of all the video layout plugins, and from this it will get an array with the content identifiers that are applicable.

data.json (video manifest):

"streams": [
{
"sources": {
"mp4": [
{
"src": "presentation.mp4",
"mimetype": "video/mp4",
"res": {
"w": "1442",
"h": "1080"
}
}
]
},
"preview": "presentation_cut.jpg",
"content":"presentation" <<<< Content identifier
},
{
"sources": {
"mp4": [
{
"src": "presenter.mp4",
"mimetype": "video/mp4",
"res": {
"w": "1920",
"h": "1080"
}
}
]
},
"preview": "presenter_cut.jpg",
"content":"presenter", <<<< Content identifier
}
],

For more information, see the documentation about video layouts.

videoContainer.validContentIds: Gets the contentId array that can be activated.

player.videoContainer.validContentIds
> ["presentation","presenter","presenter-presentation"]

videoContainer.validContentSettings: Obtains the configuration of the layouts that are applicable. Includes all the information of the validContent elements in the configuration file.

player.videoContainer.validContentSettings
> [
{
id: "presenter",
content: ["presenter"],
icon: "present-mode-2.svg",
title: "Presenter"
},
{
id: "presentation",
content: ["presentation"],
icon: "present-mode-1.svg",
title: "Presentation"
},
{
id: "presenter-presentation",
content: ["presenter","presentation"],
icon: "present-mode-3.svg",
title: "Presenter and presentation"
}
]

videoContainer.layoutId: Gets the current layout identifier

async videoContainer.setLayout(contentId): Activates a layout, identified by its contentId

const firstContentId = player.videoContainer.validContentIds[0];
if (firstContentId) {
await player.videoContainer.setLayout(firstContentId);
}

This code is extracted from the en.upv.paella.layoutSelector plugin, which is one of the plugins available in the @asicupv/paella-basic-plugins package. We have left only the parts of the code relevant to list the available layouts and activate it. For more information, you can check the documentation about menu button plugins.

export default class LayoutSelectorPlugin extends MenuButtonPlugin {
...
async getMenu() {
// We build the menu based on the configuration of the layouts
// available for the current video.
const contentSettings = this.player.videoContainer.validContentSettings;
return Promise.all(await contentSettings.map(async item => {
const configPath = utils.joinPath([this.player.configResourcesUrl, item.icon]);
const icon = await utils.loadSvgIcon(configPath);
return {
id: item.id,
title: item.title,
icon: icon,
selected: this.player.videoContainer.layoutId === item.id
}
}));
}
...
itemSelected(itemData) {
this.player.videoContainer.setLayout(itemData.id);
}
}

async videoContainer.play(): Starts video playback. Triggers the Events.PLAY event.

async videoContainer.pause(): Pause video playback. Trigger the Events.PAUSE event.

async videoContainer.stop(): Stops the video and resets the initial time to zero. Triggers the Events.STOP event.

async videoContainer.paused(): Returns true if the video is paused.

async videoContainer.setCurrentTime(t): Seek to the specified time, in seconds. Triggers the Events.SEEK event.

async videoContainer.currentTime(): Returns the current playback time, in seconds

async videoContainer.volume(): Returns the volume, as a number between 0 and 1. Triggers the Events.VOLUME_CHANGED event.

async videoContainer.setVolume(v): Sets the volume to the specified intensity, from 0 to 1. This API only works on desktop browsers.

async videoContainer.duration:Returns the video duration.

videoContainer.isLiveStream: Returns true if some of the video streams is of type hlsLive.

Soft trimming allows you to modify the start and end time of the video, in order to play only an intermediate part. It is useful to trim a central part of a video, without having to process it. For example, it allows to prepare a video to display only the relevant part of an event recorded without production, eliminating the entrance and exit of the audience.

The soft trimming APIs only allow to set the initial and final instant of the video, i.e., we cannot eliminate intermediate parts. There is also no mechanism to store or retrieve the trimming. However, we can write a plugin that loads this data from a server, and activate the trimming using these APIs.

Soft trimming can be enabled, disabled and modified at runtime, and the changes will be visible immediately.

Sets the trimming values for the video. If we set enabled=true, but the start time is greater than or equal to the end time, trimming will remain disabled.

If soft trimming is enabled, the timeline interaction functions will use moments relative to the trimming we have configured. For example, if the video lasts 900 seconds and we activate a trimming between instant 100 and instant 160, the video will last 60 seconds. If we execute setCurrentTime(30), we will be placing the current time instant in the second 130, that is trimming.start + 30.

To get the actual current time values while keeping trimming enabled, we can use the APIs of stream provider.

videoContainer.isTrimEnabled: returns true if the soft trimming is enabled.

videoContainer.trimStart: returns the start instant of the trimming, in seconds

videoContainer.trimEnd: returns the end instant of the trimming, in seconds

async videoContainer.setTrimming({ enabled, start, end }): sets the trimming values for the video. If we set enabled=true, but the start time is greater than or equal to the end time, trimming will remain disabled.

The VideoContainer objects implements some functions to add element based in the current layout video positions:

videoContainer.appendChild(element, rect = null, zIndex = 1): Add an HTML element to the video container DOM node. This function will add the element to the same node that contains the independent video players.

  • The rect parameter is an object with the size and position of the element to be added, in pixels, relative to the video container element. If this parameter is set, the element style will be set in accordance with the specified rectangle settings ({ x, y, width, height }).
  • The zIndex parameter will be set to the element style. If the zIndex is set to null, the element will remain behind the layout elements.
  • If the rect parameter is null, the zIndex parameter will be ignored.
  • The rect coordinates, in pixels, will be transformed to %, so that the element remains in the same position when resizing.
  • This function returns the element added (the same element passed in element parameter)

videoContainer.removeChild(element): Removes an element from the video container.

videoContainer.getVideoRect(target=null): Returns the video rectangle of an element of the video container:

  • if target is null, this call will return the size of the video container itself.
  • target can also be a string specifying the content attribute of the independent video stream that we want to get.

Note that when the user changes the layout of the video, all elements added with this API will remain unchanged. If you are implementing a plugin that must support a layout switch, you need to listen to the Event.LAYOUT_CHANGED event to perform the actions you need to do in case of this event.

Example: add a translucent red rectangle over the “presenter” video

Section titled “Example: add a translucent red rectangle over the “presenter” video”

You can test the following code in the JavaScript debug console:

var p = __paella_instances__[0].videoContainer.getVideoRect("presenter");
var e = __paella_instances__[0].videoContainer.appendChild(document.createElement('div'), p);
e.style.backgroundColor = 'rgba(255,0,0,0.4)';

Note: remember that you never may use paella_instances for production code. the above example has only been exposed so that you can test the API in a simple way.

It is possible to make the video container remember certain user parameters so that they can be restored in a later session. These values are not restored unless specified in the paella-core configuration:

  • playback rate: store the las video playback rate selected by the user.
  • volume: store the last volume level selected by the user.
  • video layout: store the las video layout set. This setting was stored in previous versions of paella-core, but from 1.19 is configurable. This property can be configured to be saved globally (for all videos) or specific to the current video identifier.
  • last known time: store the last known time for each video identifier. You can set the number of seconds remaining in the video so that the video starts playing from the beginning. For example, if there are 5 seconds left in the video, the next time the video loads it will start from the beginning.
{
...
"videoContainer": {
...
"restorePlaybackRate": true,
"restoreVolume": true,
"restoreVideoLayout": {
"enabled": true,
"global": true
},
"restoreLastTime": {
"enabled": true,
"remainingSeconds": 5
}
}
}

The preferences API is used to store these settings. The values of these properties are always saved, but will only be restored if the option is activated in the configuration, except for the last known time instant, which is only saved if the option is active.

The video container can be customised through the player configuration in two ways:

  • It can be displayed in a layer below the playback bar, so that the playback bar is drawn over the video. During playback, after a few seconds of mouse inactivity, the playback bar is hidden and the entire video container is visible. This option is more recommended for environments where the player has more limited space, as the playback bar does not take up space.
  • It can be displayed on the same layer as the playback bar, on top of it. This way, the playbar does not hide the video. This option is more suitable if there are no space limitations on the screen, as the video is not hidden by the playbar.

config.json:

{
"videoContainer": {
"overPlaybackBar": true
},
...
}

This system works by adding or removing a CSS class to the video container:

Video container under playback bar:

<div class="video-container">
...

Video container over the playback bar:

<div class="video-container over-playback-bar">

Check the documentation about styling Paella Player to learn how to change the CSS styles of the video container.

In dynamic layouts, the alignment of the video streams is configured via the configuration file, with the landscapeVerticalAlignment and portraitHorizontalAlignment properties. For more information, see the section on dynamic layouts.

{
"videoContainer": {
"dynamicLayout" : {
"portraitHorizontalAlignment": "align-center", // align-center (default), align-left, align-right
"landscapeVerticalAlignment": "align-center" // align-center (default), align-top, align-bottom
}
...
}
}