import { EventMap } from 'typed-emitter'; import { MediaType, NamedMediaGroup } from '@webex/internal-media-core'; import EventsScope from '../common/events/events-scope'; import { RemoteMedia, RemoteVideoResolution } from './remoteMedia'; import { CSI } from './receiveSlot'; import { ReceiveSlotManager } from './receiveSlotManager'; import { RemoteMediaGroup } from './remoteMediaGroup'; import { MediaRequestManager } from './mediaRequestManager'; export type PaneSize = RemoteVideoResolution; export type LayoutId = string; export type PaneId = string; export type PaneGroupId = string; export interface ActiveSpeakerVideoPaneGroup { id: PaneGroupId; numPanes: number; size: PaneSize; priority: number; } export interface MemberVideoPane { id: PaneId; size: PaneSize; csi?: CSI; } export interface VideoLayout { screenShareVideo?: { size: PaneSize; }; activeSpeakerVideoPaneGroups?: ActiveSpeakerVideoPaneGroup[]; memberVideoPanes?: MemberVideoPane[]; } export interface Configuration { audio: { numOfActiveSpeakerStreams: number; numOfScreenShareStreams: number; }; video: { preferLiveVideo: boolean; initialLayoutId: LayoutId; layouts: { [key: LayoutId]: VideoLayout; }; }; namedMediaGroup?: NamedMediaGroup; } /** * Default configuration: * - uses 3 audio streams * - prefers active speakers with live video (e.g. are not audio only or video muted) over active speakers without live video * - has a few layouts defined, including 1 that contains remote screen share (ScreenShareView) */ export declare const DefaultConfiguration: Configuration; export declare enum Event { AudioCreated = "AudioCreated", InterpretationAudioCreated = "InterpretationAudioCreated", ScreenShareAudioCreated = "ScreenShareAudioCreated", VideoLayoutChanged = "VideoLayoutChanged" } export interface VideoLayoutChangedEventData { layoutId: LayoutId; activeSpeakerVideoPanes: { [key: PaneGroupId]: RemoteMediaGroup; }; memberVideoPanes: { [key: PaneId]: RemoteMedia; }; screenShareVideo?: RemoteMedia; } export interface Events extends EventMap { [Event.AudioCreated]: (audio: RemoteMediaGroup) => void; [Event.ScreenShareAudioCreated]: (screenShareAudio: RemoteMediaGroup) => void; [Event.VideoLayoutChanged]: (data: VideoLayoutChangedEventData) => void; } /** * A helper class that manages all remote audio/video streams in order to achieve a predefined set of layouts. * It also creates a fixed number of audio streams and these don't change during the meeting. * * Things that RemoteMediaManager does: * - owns the receive slots (creates them when needed, and re-uses them when switching layouts) * - constructs appropriate RemoteMedia and RemoteMediaGroup objects and sends appropriate mediaRequests */ export declare class RemoteMediaManager extends EventsScope { private config; private started; private receiveSlotManager; private mediaRequestManagers; private currentLayout?; private slots; private media; private receiveSlotAllocations; private currentLayoutId?; /** * Constructor * * @param {ReceiveSlotManager} receiveSlotManager * @param {{audio: MediaRequestManager, video: mediaRequestManagers}} mediaRequestManagers * @param {Configuration} config Configuration describing what video layouts to use during the meeting */ constructor(receiveSlotManager: ReceiveSlotManager, mediaRequestManagers: { audio: MediaRequestManager; video: MediaRequestManager; screenShareAudio: MediaRequestManager; screenShareVideo: MediaRequestManager; }, config?: Configuration); /** * Checks if configuration is valid, throws an error if it's not */ private checkConfigValidity; /** * Starts the RemoteMediaManager. * * @returns {Promise} */ start(): Promise; /** * Releases all the used resources (like allocated receive slots). This function needs * to be called when we leave the meeting, etc. */ stop(): void; /** * Returns the total number of main video panes required for a given layout * * @param {VideoLayout} layout * @returns {number} */ private getRequiredNumVideoSlotsForLayout; /** * Allocates the maximum number of panes that any of the configured layouts will require. * We do this at the beginning, because it's more efficient (much faster) then allocating receive slots * later, after the SDP exchange was done. */ private preallocateVideoReceiveSlots; /** * Changes the layout (triggers Event.VideoLayoutChanged) * * @param {LayoutId} layoutId new layout id * @returns {Promise} */ setLayout(layoutId: LayoutId): Promise; /** * Returns the currently selected layout id * * @returns {LayoutId} */ getLayoutId(): LayoutId | undefined; /** * sets the preferLiveVideo */ setPreferLiveVideo(preferLiveVideo: boolean): void; /** * Sets CSIs for multiple RemoteMedia instances belonging to RemoteMediaGroup. * For each entry in the remoteMediaCsis array: * - if csi is specified, the RemoteMedia instance is pinned to that CSI * - if csi is undefined, the RemoteMedia instance is unpinned */ setActiveSpeakerCsis(remoteMediaCsis: { remoteMedia: RemoteMedia; csi?: number; }[]): void; /** * Sets which named media group need receiving * @param {MediaType} mediaType of the stream * @param {number} languageCode of the stream. If the languageId is 0, the named media group request will be canceled, * and only receive the main audio stream. * @returns {void} */ setReceiveNamedMediaGroup(mediaType: MediaType, languageId: number): Promise; /** * Creates the audio slots */ private createAudioMedia; /** * Creates the audio slots for named media */ private createInterpretationAudioMedia; /** * Creates receive slots required for receiving screen share audio and video */ private createScreenShareReceiveSlots; /** * Creates RemoteMedia objects for screen share */ private createScreenShareAudioMedia; /** * Goes over all receiver-selected slots and keeps only the ones that are required by a given layout, * the rest are all moved to the "unused" list */ private trimReceiverSelectedSlots; /** * Releases all the "unused" video slots. */ private releaseUnusedVideoSlots; /** * Allocates receive slots to all active speaker video panes * in the current selected layout. * * Allocation tries to keep the same order of the slots between the previous * layout and the new one. Sorting helps making sure that highest priority slots * go in the same order in the new layout. */ private allocateSlotsToActiveSpeakerPaneGroups; /** * Allocates receive slots to all receiver selected video panes * in the current selected layout */ private allocateSlotsToReceiverSelectedVideoPaneGroups; /** * Ensures that we have enough slots for the current layout. */ private refillRequiredSlotsIfNeeded; /** * Move all active speaker slots to "unused" */ private trimActiveSpeakerSlots; /** * Logs the state of the receive slots */ private logMainVideoReceiveSlots; /** logs main audio slots */ private logMainAudioReceiveSlots; /** logs slides video slots */ private logSlidesVideoReceiveSlots; /** logs slides audio slots */ private logSlidesAudioReceiveSlots; /** Logs all current receive slots */ logAllReceiveSlots(): void; /** * Makes sure we have the right number of receive slots created for the current layout * and allocates them to the right video panes / pane groups * * @returns {Promise} */ private updateVideoReceiveSlots; /** * Creates new RemoteMedia and RemoteMediaGroup objects for the current layout * and sends the media requests for all of them. */ private updateVideoRemoteMediaObjects; /** * Checks if current layout requires a screen share. * If it does, it creates new RemoteMediaGroup object for screen share * and sends the media requests for it. * If it doesn't, it makes sure we clean up any RemoteMediaGroup objects * created earlier for screen share (for previous layout). */ private updateScreenShareVideoRemoteMediaObject; /** * Invalidates all remote media objects belonging to currently selected layout */ private invalidateCurrentRemoteMedia; /** emits Event.VideoLayoutChanged */ private emitVideoLayoutChangedEvent; /** * Set multiple remote video CSIs at once * @param remoteMediaCsis The remote medias and CSIs to set them to * @returns {void} */ setRemoteVideoCsis(remoteMediaCsis: { remoteMedia: RemoteMedia; csi?: CSI | null; }[]): void; /** * Sets a new CSI on a given remote media object * * @param {RemoteMedia} remoteMedia remote Media object to modify * @param {CSI} csi new CSI value, can be null if we want to stop receiving media */ setRemoteVideoCsi(remoteMedia: RemoteMedia, csi?: CSI | null): void; /** * Adds a new member video pane to the currently selected layout. * * Changes to the layout are lost after a layout change. * * @param {MemberVideoPane} newPane * @returns {Promise} */ addMemberVideoPane(newPane: MemberVideoPane): Promise; /** * Removes a member video pane from the currently selected layout. * * Changes to the layout are lost after a layout change. * * @param {PaneId} paneId pane id of the pane to remove * @returns {Promise} */ removeMemberVideoPane(paneId: PaneId): Promise; /** * Pins an active speaker remote media object to the given CSI value. From that moment * onwards the remote media will only play audio/video from that specific CSI until * unpinActiveSpeakerVideoPane() is called or current layout is changed. * * @param {RemoteMedia} remoteMedia remote media object reference * @param {CSI} csi CSI value to pin to, if undefined, then current CSI value is used */ pinActiveSpeakerVideoPane(remoteMedia: RemoteMedia, csi?: CSI): void; /** * Unpins a remote media object from the fixed CSI value it was pinned to. * * @param {RemoteMedia} remoteMedia remote media object reference */ unpinActiveSpeakerVideoPane(remoteMedia: RemoteMedia): void; /** * Returns true if a given remote media object belongs to an active speaker group and has been pinned. * Throws an error if the remote media object doesn't belong to any active speaker remote media group. * * @param {RemoteMedia} remoteMedia remote media object * @returns {boolean} */ isPinned(remoteMedia: RemoteMedia): boolean; }