-
Notifications
You must be signed in to change notification settings - Fork 344
Multistream Comprehensive Guide
Caution
This documentation may no longer be current. Click here to view the updated content on our Developer Portal.
Developers have a variety of options to customize how they choose to display remote videos with multistream media.
The initial step in utilizing Multistream is to include enableMultistream within the joinOptions parameter for both the meeting.join()
and meeting.joinWithMedia()
methods.
Example
const joinOptions = {
enableMultistream: true,
...
};
await meeting.join(joinOptions);
// or
await meeting.joinWithMedia({joinOptions, mediaOptions});
This article is divided into three sections:
- Layouts: Determines the preferred visualization of the remote streams.
- Events: Monitors all remote streams and additional events.
- Methods and RemoteMediaManager: Facilitates layout updates and other operations.
Terminology
Throughout this document we'll refer to the following terms:
- Layout: An arrangement of remote streams in the user interface.
- Pane: An individual element within the layout grid.
- Remote Media: The media stream and associated details.
- Remote Media Group: A collection of remote media as a group, along with additional helper APIs.
- CSI: A unique identifier linked to a stream, connecting a member participant with that stream.
A "layout" refers to the arrangement of remote streams in the user interface. Multistream allows you to customize this layout. To do so, call addMedia()
with the desired layout details. The existing arguments for addMedia()
are unaffected by this process. For more information, refer to this documentation.
The AddMediaOptions
now includes a new property, remoteMediaManagerConfig
, for multistream functionality. To implement a custom layout, you can use the same data format as shown in the predefined layouts.
The following are the default configuration values for remoteMediaManagerConfig
, which are used if no overrides are provided:
const remoteMediaManagerConfig = {
audio: {
numOfActiveSpeakerStreams: 3,
numOfScreenShareStreams: 1,
},
video: {
preferLiveVideo: true,
initialLayoutId: 'AllEqual',
layouts: {
AllEqual: AllEqualLayout,
OnePlusFive: OnePlusFiveLayout,
Single: SingleLayout,
Stage: Stage2x2With6ThumbnailsLayout,
ScreenShareView: RemoteScreenShareWithSmallThumbnailsLayout,
}
}
}
A detailed explanation of this object can be found here.
Here are the predefined layouts for the default configuration:
Type | Description |
---|---|
AllEqual |
All equal (max 9). |
OnePlusFive |
One big pane + five small. |
Single |
Single Video pane. |
Stage |
Stage with thumbnails. |
ScreenShareView |
Screen share with thumbnails. |
The following setions describe each available layout in detail.
An "all equal" grid, with size up to 3 x 3 = 9.
Example
const AllEqualLayout: VideoLayout = {
activeSpeakerVideoPaneGroups: [
{
id: 'main',
numPanes: 9,
size: 'best',
priority: 255,
},
],
};
A layout with one big pane for the highest-priority active speaker and 5 small panes for other active speakers.
Example
const OnePlusFiveLayout: VideoLayout = {
activeSpeakerVideoPaneGroups: [
{
id: 'mainBigOne',
numPanes: 1,
size: 'large',
priority: 255,
},
{
id: 'secondarySetOfSmallPanes',
numPanes: 5,
size: 'very small',
priority: 254,
},
],
};
A layout with a single remote active speaker video pane.
Example
const SingleLayout: VideoLayout = {
activeSpeakerVideoPaneGroups: [
{
id: 'main',
numPanes: 1,
size: 'best',
priority: 255,
},
],
};
A staged layout with four pre-selected meeting participants in the main 2x2 grid and 6 small panes for other active speakers at the top.
The predetermined participants can be included using the CSI value, which can be updated once the values are accessible after joining the meeting.
Example
const Stage2x2With6ThumbnailsLayout: VideoLayout = {
activeSpeakerVideoPaneGroups: [
{
id: 'thumbnails',
numPanes: 6,
size: 'thumbnail',
priority: 255,
},
],
memberVideoPanes: [
{id: 'stage-1', size: 'medium', csi: undefined},
{id: 'stage-2', size: 'medium', csi: undefined},
{id: 'stage-3', size: 'medium', csi: undefined},
{id: 'stage-4', size: 'medium', csi: undefined},
],
};
A strip of eight small video panes (thumbnails) displayed at the top of a remote screenshare.
Example
const RemoteScreenShareWithSmallThumbnailsLayout: VideoLayout = {
screenShareVideo: {size: 'best'},
activeSpeakerVideoPaneGroups: [
{
id: 'thumbnails',
numPanes: 8,
size: 'thumbnail',
priority: 255,
},
],
};
This object configures the RemoteMediaManager
. Based on this configuration, you will receive the multistream remote media.
{
audio: {
numOfActiveSpeakerStreams: number;
numOfScreenShareStreams: number;
};
video: {
preferLiveVideo: boolean;
initialLayoutId: LayoutId;
layouts: {[key: LayoutId]: VideoLayout};
};
}
Name | Description | Type |
---|---|---|
numOfActiveSpeakerStreams |
The maximum number of speakers that can be heard simultaneously. | Number |
numOfScreenShareStreams |
The maximum number of screenshare streams. Typically, one should suffice as only one person can present at a time in Webex. | Number |
Name | Description | Type |
---|---|---|
preferLiveVideo |
If set to true, the server prioritizes sending streams of participants with their video enabled over those that don't send video. | boolean |
initialLayoutId |
One of the keys defined within the layouts object. | string |
layouts |
Key-value pairs where the value determines the layout. Additional details are provided in the following table. | object |
Name | Description | Type |
---|---|---|
screenShareVideo |
Defines the screenshare stream. | { size: PaneSize } |
activeSpeakerVideoPaneGroups |
Defines the active speaker panes. | Array<ActiveSpeakerPaneObject> |
memberVideoPanes |
Defines the member video panes. | Array<MemberPaneObject> |
PaneSize | Description |
---|---|
thumbnail |
The smallest possible resolution, 90p or less. |
very small |
180p or less. |
small |
360p or less. |
medium |
720p or less. |
large |
1080p or less. |
best |
Highest possible resolution. |
Name | Description | Type |
---|---|---|
id |
An arbitrary value. | string |
numPanes |
The number of streams that you will receive. | number |
size |
The size of each stream. |
PaneSize (string) |
priority |
The priority of the streams. The most recent active speaker has the highest priority. | number (0-255) |
Name | Description | Type |
---|---|---|
id |
An arbitrary value (e.g., stage-1 ). |
string |
size |
The size of the stream. |
PaneSize (string) |
csi |
A unique identifier for a stream. When a client generates a stream, it assigns a CSI value to it. | number |
Member video panes can only be accessed within the stage layout, which allows for the pinning of specific participants' positions in the layout. The stage layout is created when memberVideoPanes is included in the layout configuration. In this case, developers are responsible for managing and displaying participants as they join or leave. This is different from activeSpeakerVideoPaneGroups, where a consistent list of streams featuring active speakers/participants is always received. Additionally, the active speaker video pane can be pinned. Therefore, participants can be pinned either by using memberVideoPanes or by invoking pinActiveSpeakerVideoPane(remoteMedia, csi) for activeSpeakerVideoPaneGroups.
Each member or participant can have multiple streams. These streams can be of various types such as audio, video, screenshareVideo, or screenshareAudio. Each stream is uniquely identified by a CSI. A member can have more than one stream of the same type. For instance, a user can have two video streams (each with a unique CSI) if they join the meeting using multiple devices.
In most scenarios, you'll primarily utilize activeSpeakerVideoPaneGroups
.
Events can be listened to using the meeting.on()
method.
Here's an example:
meeting.on('event name', (data) => {
console.log(data);
});
meeting.on('media:remoteVideo:layoutChanged', ({
layoutId, activeSpeakerVideoPanes, memberVideoPanes, screenShareVideo
}) => {
console.log('layoutId: ', layoutId);
console.log('activeSpeakerVideoPanes:', activeSpeakerVideoPanes);
console.log('memberVideoPanes:', memberVideoPanes);
console.log('screenShareVideo:', screenShareVideo);
});
The following table and sections elaborate on the media events received during a meeting that pertain to multistream.
Event name | Data Received |
---|---|
media:remoteAudio:created |
List of audio streams (remains unchanged by layout changes). |
media:remoteScreenShareAudio:created |
Object representing the screen share audio media group. |
media:remoteVideo:layoutChanged |
{ layoutId, activeSpeakerVideoPanes, memberVideoPanes, screenShareVideo } . |
media:remoteVideoSourceCountChanged |
Number of remote video sources ({ numTotalSource, numLiveSources } ). |
media:remoteAudioSourceCountChanged |
Number of remote audio sources ({ numTotalSource, numLiveSources } ). |
media:activeSpeakerChanged |
List of member IDs for active speakers ({ memberIds } ). |
To obtain the list of remote media elements from the group, invoke getRemoteMedia()
.
const remoteMedia = screenShareAudioMediaGroup.getRemoteMedia()[0];
const remoteMediaStream = remoteMedia.stream;
Asynchronous: No
Parameters
Name | Description | Type | Mandatory |
---|---|---|---|
filter |
A string used to filter and retrieve a specific type of remote media. |
all (Default), pinned , unpinned
|
No |
Returns: Array<RemoteMedia>
Data Received | Description |
---|---|
layoutId |
Options: AllEqual , OnePlusFive , Single , Stage , ScreenShareView
|
activeSpeakerVideoPanes |
Contains: groupId , groupRemoteMedia
|
memberVideoPanes |
Contains: paneId , remoteMedia
|
screenShareVideo |
RemoteMedia object: { id, stream, sourceState (either 'no source' or 'live'), memberId }
|
Data Received | Description |
---|---|
numTotalSource |
Total number of all video sources. |
numLiveSources |
Total number of live video sources. |
Data Received | Description |
---|---|
numTotalSource |
Total number of all audio sources. |
numLiveSources |
Total number of live audio sources. |
The following table and sections elaborate on the meeting events received during a meeting that pertain to multistream.
Event name | Description |
---|---|
meeting:stoppedSharingLocal |
The client has stopped screen sharing and no longer has control over it. This can occur when another participant starts sharing and takes control, or if the user stops sharing. |
meeting:startedSharingRemote |
Another participant in the meeting has started screen sharing and now has control over it. |
meeting:stoppedSharingRemote |
Another participant in the meeting has stopped screen sharing and no longer has control over it. |
The remoteMedia
object may include streams from various participants and can dynamically update as different participants begin speaking. The memberId
within the remoteMedia
object allows for the display of corresponding names or data alongside the stream. This memberId
can be utilized to retrieve the name of the participant.
Events can be listened to using the remoteMedia.on()
method.
Example
remoteMedia.on('Event name', (data) => {
console.log(data);
});
Event name | Description |
---|---|
sourceUpdate |
Triggered when the source of the remoteMedia changes, such as when another participant begins speaking. |
stopped |
Triggered when the participant has deactivated their stream. |
With sourceUpdate
, you can inspect the memberId
and sourceState
to obtain the most recent information about the remoteMedia
.
Here's the possible information received during the sourceUpdate
event:
Data Received | Description |
---|---|
no source |
No video is available. |
invalid source |
The source is invalid. |
live |
The video is available. |
avatar |
The camera is muted or there is no video. An avatar can be shown. |
bandwidth disabled |
There is insufficient bandwidth to show the video. An avatar can be shown. |
policy violation |
Video is restricted due to a policy violation. |
Multiple methods are available for handling various scenarios and updating the layout.
const csiList = meeting.members.getCsisForMember(memberId, mediaType='video', mediaContent='main');
Asynchronous: No
Parameters
Name | Description | Type | Mandatory |
---|---|---|---|
memberId |
Member's ID. | string | Yes |
mediaType |
The media type. |
audio , video (Default) |
No |
mediaContent |
Type of media content. |
main (Default), slides
|
No |
Returns: Array<number>
const member = meeting.members.findMemberByCsi(csi);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
csi |
CSI number linked to the stream. | number | Yes |
Returns: Member
In multistream meetings, the meeting.addMedia()
API establishes the media connection. It configures streams to receive remote media based on the remoteMediaManagerConfig
provided as an option.
The Meeting.addMedia()
method accepts an optional configuration object to set up the RemoteMediaManager
:
meeting.addMedia({
...,
remoteMediaManagerConfig?: RemoteMediaManagerConfiguration;
})
RemoteMediaManager configuration object
{
audio: {
numOfActiveSpeakerStreams: number; // number of audio streams we want to receive
numOfScreenShareStreams: number; // 1 should be enough because in Webex only 1 person at a time can be presenting screen share
};
video: {
preferLiveVideo: boolean; // applies to all pane groups with active speaker policy
initialLayoutId: LayoutId;
layouts: {[key: LayoutId]: VideoLayout}; // a map of all available layouts, a layout can be set via setLayout() method
};
}
This object's details are outlined in the Explanation of the RemoteMediaManager Object section.
meeting.remoteMediaManager.setRemoteVideoCsi(remoteMedia, csi);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
remoteMedia |
RemoteMedia object. |
RemoteMedia | Yes |
csi |
A new CSI value, can be null if we want to stop receiving media. | number, null | Yes |
Returns: void
const remoteMedia = await meeting.remoteMediaManager.addMemberVideoPane(
{ id: PaneId; size: PaneSize; csi?: CSI; }
);
Asynchronous: Yes
Parameters: An object with following parameters
Name | Description | Type | Mandatory |
---|---|---|---|
id |
Pane ID. | string | Yes |
size |
Size of the video pane. |
thumbnail , very small , small , medium , large , best
|
Yes |
csi |
CSI number associated with the stream. | number | No |
Returns: Promise<RemoteMedia>
const layoutId = meeting.remoteMediaManager?.getLayoutId();
Asynchronous: No
Parameters: No
Returns: string
Calling this method triggers the media:remoteVideo:layoutChanged
event.
await meeting.remoteMediaManager.setLayout(layoutId);
Asynchronous: Yes
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
layoutId |
ID of the defined layout. | string | Yes |
Returns: Promise<void>
meeting.remoteMediaManager.setPreferLiveVideo(preferLiveVideo);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
preferLiveVideo |
If enabled, this option will prioritize retrieving streams with active video. | boolean | Yes |
Returns - void
Set CSIs for multiple RemoteMedia
instances belonging to a RemoteMediaGroup
.
meeting.remoteMediaManager.setActiveSpeakerCsis(remoteMediaCsis);
Asynchronous: No
Parameters: An array of object with the following properties, for example, Array<{ remoteMedia, csi }>
.
Name | Description | Type | Mandatory |
---|---|---|---|
remoteMedia |
The remote media object. | RemoteMedia | Yes |
csi |
CSI number associated with the stream. | number | No |
Returns: void
Set a new CSI on a given remote media object
meeting.remoteMediaManager.setRemoteVideoCsi(remoteMedia, csi);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
remoteMedia |
The remote media object to be altered. | RemoteMedia | Yes |
csi |
A new CSI value. It can be null if you wish to stop receiving media. |
number , null
|
Yes |
Returns - void
Add a new member video pane to the currently selected layout.
const remoteMedia = await meeting.remoteMediaManager.addMemberVideoPane(newPane);
Asynchronous: Yes
Parameters: An object with the following properties
Name | Description | Type | Mandatory |
---|---|---|---|
id |
Pane ID. | string | Yes |
size |
Pane size. |
thumbnail , very small , small , medium , large , best
|
Yes |
csi |
New CSI value. | number | No |
Returns - Promise<RemoteMedia>
Remove a member video pane from the currently selected layout.
await meeting.remoteMediaManager.removeMemberVideoPane(paneId);
Asynchronous: Yes
Parameters: An object with the following properties:
Name | Description | Type | Mandatory |
---|---|---|---|
paneId |
Pane ID. | string | Yes |
Returns - Promise<void>
Pin an active speaker remote media object to the given CSI value.
meeting.remoteMediaManager.pinActiveSpeakerVideoPane(remoteMedia, csi);
This function pins an active speaker's remote media object to a specified CSI value. Consequently, the remote media will only play audio/video from that specific CSI. This remains in effect until either the unpinActiveSpeakerVideoPane()
function is called or the current layout is altered.
Asynchronous: No
Parameters: An object with the following properties
Name | Description | Type | Mandatory |
---|---|---|---|
remoteMedia |
Reference to the remote media object. | RemoteMedia |
Yes |
csi |
CSI value to pin to. If undefined, the current CSI value is used. | number | No |
Returns - void
Unpin a remote media object from the specific CSI value to which it was previously pinned.
meeting.remoteMediaManager.unpinActiveSpeakerVideoPane(remoteMedia);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
remoteMedia |
Remote media object reference. | RemoteMedia |
Yes |
Returns - void
Determine whether a given remote media object is part of an active speaker group and if it has been pinned.
const isPinned = meeting.remoteMediaManager.isPinned(remoteMedia);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
remoteMedia |
Remote media object reference. | RemoteMedia |
Yes |
Returns - boolean
Clients can specify a size hint if they are displaying remote video on screens of varying sizes and need to adjust the video resolution for optimal display or bandwidth conservation. When the remote receives this hint, it starts transmitting the video at the requested resolution.
setSizeHint()
can be invoked on the remoteMedia
object. Each video stream corresponds to a remoteMedia
object.
remoteMedia.setSizeHint(width, height);
Asynchronous: No
Parameters:
Name | Description | Type | Mandatory |
---|---|---|---|
width |
Width of the video element. | number | Yes |
height |
Height of the video element. | number | Yes |
Returns - void
To explore Multistream functionality, feel free to explore our Meeting Samples App.
Under Manage Meeting section, check the box with label Use a multistream connection
before clicking on Join Meeting
or Join with Media
.
Caution
- Introducing the Webex Web Calling SDK
- Core Concepts
- Quickstart guide
- Authorization
- Basic Features
- Advanced Features
- Introduction
- Quickstart Guide
- Basic Features
- Advanced Features
- Multistream
- Migrating SDK version 1 or 2 to version 3