UNPKG

@signalwire/js

Version:
305 lines (270 loc) 7.86 kB
import { BaseComponentContract, BaseConnectionContract, connect, EventEmitter, extendComponent, Rooms, validateEventsToSubscribe, VideoAuthorization, VideoLayoutChangedEventParams, } from '@signalwire/core' import { BaseConnectionOptions } from '@signalwire/webrtc' import { BaseRoomSessionConnection, BaseRoomSessionOptions, } from '../BaseRoomSession' import { RoomSessionDevice, RoomSessionDeviceAPI, RoomSessionDeviceConnection, RoomSessionDeviceEvents, } from '../RoomSessionDevice' import * as workers from './workers' import { AddCameraOptions, AddDeviceOptions, AddMicrophoneOptions, CreateScreenShareObjectOptions, VideoRoomSessionMethods, VideoRoomSessionEvents, VideoRoomSessionContract, BaseRoomSessionContract, } from '../utils/interfaces' export interface VideoRoomSession extends VideoRoomSessionContract, VideoRoomSessionMethods, BaseRoomSessionContract, BaseConnectionContract<VideoRoomSessionEvents>, BaseComponentContract {} export interface VideoRoomSessionOptions extends BaseRoomSessionOptions {} export class VideoRoomSessionConnection extends BaseRoomSessionConnection<VideoRoomSessionEvents> implements VideoRoomSessionContract { private _deviceList = new Set<RoomSessionDevice>() private _currentLayoutEvent: VideoLayoutChangedEventParams constructor(options: VideoRoomSessionOptions) { super(options) this.initWorker() } set currentLayoutEvent(event: VideoLayoutChangedEventParams) { this._currentLayoutEvent = event } get currentLayoutEvent() { return this._currentLayoutEvent } get currentLayout() { return this._currentLayoutEvent?.layout } get currentPosition() { return this._currentLayoutEvent?.layout.layers.find( (layer) => layer.member_id === this.memberId )?.position } get deviceList() { return Array.from(this._deviceList) } get interactivityMode() { return this.select(({ session }) => { const { authorization } = session return (authorization as VideoAuthorization)?.join_as ?? '' }) } get permissions() { return this.select(({ session }) => { const { authorization } = session return (authorization as VideoAuthorization)?.room?.scopes ?? [] }) } private initWorker() { this.runWorker('videoWorker', { worker: workers.videoWorker, }) } /** @internal */ protected override getSubscriptions() { const eventNamesWithPrefix = this.eventNames().map((event) => { return `video.${String(event)}` }) return validateEventsToSubscribe( eventNamesWithPrefix ) as EventEmitter.EventNames<{}>[] } /** @internal */ protected _finalize() { this._deviceList.clear() super._finalize() } /** @internal */ override async hangup(id?: string) { this._deviceList.forEach((device) => { device.leave() }) return super.hangup(id) } join() { return super.invite<VideoRoomSession>() } /** * @deprecated Use {@link getLayouts} instead. `getLayoutList` will * be removed in v3.0.0 */ getLayoutList() { // @ts-expect-error return this.getLayouts() } /** * @deprecated Use {@link getMembers} instead. `getMemberList` will * be removed in v3.0.0 */ getMemberList() { // @ts-expect-error return this.getMembers() } /** @deprecated Use {@link startScreenShare} instead. */ async createScreenShareObject(opts: CreateScreenShareObjectOptions = {}) { return this.startScreenShare(opts) } /** * Allow to add a camera to the room. */ addCamera(opts: AddCameraOptions = {}) { const { autoJoin = true, ...video } = opts return this.addDevice({ autoJoin, video, }) } /** * Allow to add a microphone to the room. */ addMicrophone(opts: AddMicrophoneOptions = {}) { const { autoJoin = true, ...audio } = opts return this.addDevice({ autoJoin, audio, }) } /** * Allow to add additional devices to the room like cameras or microphones. */ async addDevice(opts: AddDeviceOptions = {}) { return new Promise<RoomSessionDevice>(async (resolve, reject) => { const { autoJoin = true, audio = false, video = false } = opts if (!audio && !video) { throw new TypeError( 'At least one of `audio` or `video` must be requested.' ) } const options: BaseConnectionOptions = { ...this.options, localStream: undefined, remoteStream: undefined, audio, video, additionalDevice: true, recoverCall: false, userVariables: { ...(this.options?.userVariables || {}), memberCallId: this.callId, memberId: this.memberId, }, } const roomDevice = connect< RoomSessionDeviceEvents, RoomSessionDeviceConnection, RoomSessionDevice >({ store: this.store, Component: RoomSessionDeviceAPI, })(options) roomDevice.once('destroy', () => { roomDevice.emit('room.left') this._deviceList.delete(roomDevice) }) try { roomDevice.runWorker('childMemberJoinedWorker', { worker: workers.childMemberJoinedWorker, onDone: () => resolve(roomDevice), onFail: reject, initialState: { parentId: this.memberId, }, }) this._deviceList.add(roomDevice) if (autoJoin) { return await roomDevice.join() } return resolve(roomDevice) } catch (error) { this.logger.error('RoomDevice Error', error) reject(error) } }) } } export const VideoRoomSessionAPI = extendComponent< VideoRoomSessionConnection, VideoRoomSessionMethods >(VideoRoomSessionConnection, { audioMute: Rooms.audioMuteMember, audioUnmute: Rooms.audioUnmuteMember, videoMute: Rooms.videoMuteMember, videoUnmute: Rooms.videoUnmuteMember, deaf: Rooms.deafMember, undeaf: Rooms.undeafMember, setInputVolume: Rooms.setInputVolumeMember, setOutputVolume: Rooms.setOutputVolumeMember, setMicrophoneVolume: Rooms.setInputVolumeMember, setSpeakerVolume: Rooms.setOutputVolumeMember, setInputSensitivity: Rooms.setInputSensitivityMember, removeMember: Rooms.removeMember, removeAllMembers: Rooms.removeAllMembers, getMembers: Rooms.getMembers, getLayouts: Rooms.getLayouts, setLayout: Rooms.setLayout, setPositions: Rooms.setPositions, setMemberPosition: Rooms.setMemberPosition, hideVideoMuted: Rooms.hideVideoMuted, showVideoMuted: Rooms.showVideoMuted, getRecordings: Rooms.getRecordings, startRecording: Rooms.startRecording, getPlaybacks: Rooms.getPlaybacks, play: Rooms.play, setHideVideoMuted: Rooms.setHideVideoMuted, getMeta: Rooms.getMeta, setMeta: Rooms.setMeta, updateMeta: Rooms.updateMeta, deleteMeta: Rooms.deleteMeta, getMemberMeta: Rooms.getMemberMeta, setMemberMeta: Rooms.setMemberMeta, updateMemberMeta: Rooms.updateMemberMeta, deleteMemberMeta: Rooms.deleteMemberMeta, promote: Rooms.promote, demote: Rooms.demote, getStreams: Rooms.getStreams, startStream: Rooms.startStream, lock: Rooms.lock, unlock: Rooms.unlock, setRaisedHand: Rooms.setRaisedHand, setPrioritizeHandraise: Rooms.setPrioritizeHandraise, }) export const isVideoRoomSession = (room: unknown): room is VideoRoomSession => { return room instanceof VideoRoomSessionConnection } /** @internal */ export const createVideoRoomSessionObject = ( params: VideoRoomSessionOptions ): VideoRoomSession => { const room = connect< VideoRoomSessionEvents, VideoRoomSessionConnection, VideoRoomSession >({ store: params.store, customSagas: params.customSagas, Component: VideoRoomSessionAPI, })(params) return room }