UNPKG

roomkit-web-vue3

Version:

<h1 align="center"> TUIRoomKit</h1> Conference (TUIRoomKit) is a product suitable for multi-person audio and video conversation scenarios such as business meetings, webinars, and online education. By integrating this product, you can add room management,

471 lines (431 loc) 13.4 kB
import TUIRoomEngine, { TRTCVideoEncParam, TRTCVideoResolution, TUIRoomEvents, TUIRoomInfo, TUIRoomType, TUISeatMode, TUISeatInfo, TUIVideoStreamType, TUIUserInfo, } from '@tencentcloud/tuiroom-engine-js'; import { EventType, IRoomService, RoomParam } from '../types'; import { isMobile, isWeChat } from '../../utils/environment'; import logger from '../../utils/common/logger'; import { useAudioDeviceState, useVideoDeviceState } from '../../core'; const { microphone, speaker } = useAudioDeviceState(); const { camera } = useVideoDeviceState(); const logPrefix = '[RoomService:roomActionManager]'; const smallParam = new TRTCVideoEncParam(); smallParam.videoResolution = TRTCVideoResolution.TRTCVideoResolution_640_360; smallParam.videoFps = 10; smallParam.videoBitrate = 550; export interface DeviceParams { isOpenCamera?: boolean; isOpenMicrophone?: boolean; defaultCameraId?: string; defaultMicrophoneId?: string; defaultSpeakerId?: string; } export interface StartParams extends DeviceParams { roomName?: string; isSeatEnabled?: boolean; password?: string; } export interface JoinParams extends DeviceParams { password?: string; } export type RoomParamsInfo = { roomId: string; roomType: TUIRoomType; }; export class RoomActionManager { private service: IRoomService; private userListNextSequence = 0; public hasMoreUsers = false; constructor(service: IRoomService) { this.service = service; this.onRoomUserCountChanged = this.onRoomUserCountChanged.bind(this); this.bindRoomEngineEvents(); } private onRoomUserCountChanged(eventInfo: { roomId: string; userCount: number; }) { this.service.roomStore.setRoomMemberCount(eventInfo.userCount); } private bindRoomEngineEvents() { TUIRoomEngine.once('ready', () => { this.service.roomEngine.instance?.on( TUIRoomEvents.onRoomUserCountChanged, this.onRoomUserCountChanged ); }); } public unbindRoomEngineEvents() { this.service.roomEngine.instance?.off( TUIRoomEvents.onRoomUserCountChanged, this.onRoomUserCountChanged ); } public async start(roomId: string, params: StartParams = {}) { const { roomName, isSeatEnabled = false, isOpenCamera = false, isOpenMicrophone = false, defaultCameraId, defaultMicrophoneId, defaultSpeakerId, password, } = params; const roomMode = isSeatEnabled ? 'SpeakAfterTakingSeat' : 'FreeToSpeak'; this.service.roomStore.resetRoomData(); await this.createRoom({ roomId, roomName, roomMode, roomParam: { isOpenCamera, isOpenMicrophone, defaultCameraId, defaultMicrophoneId, defaultSpeakerId, password, }, }); await this.enterRoom({ roomId, roomParam: { isOpenCamera, isOpenMicrophone, defaultCameraId, defaultMicrophoneId, defaultSpeakerId, password, }, }); this.service.emit(EventType.ROOM_START, { roomId }); } public async join(roomId: string, params: JoinParams = {}) { const { isOpenCamera = false, isOpenMicrophone = false, defaultCameraId, defaultMicrophoneId, defaultSpeakerId, password, } = params; this.service.roomStore.resetRoomData(); await this.enterRoom({ roomId, roomParam: { isOpenCamera, isOpenMicrophone, defaultCameraId, defaultMicrophoneId, defaultSpeakerId, password, }, }); this.service.emit(EventType.ROOM_JOIN, { roomId }); } public async leaveRoom() { try { this.closeMediaBeforeLeave(); const response = await this.service.roomEngine.instance?.exitRoom(); this.service.resetStore(); logger.log(`${logPrefix}leaveRoom:`, response); this.service.emit(EventType.ROOM_LEAVE, response); } catch (error) { logger.error(`${logPrefix}leaveRoom error:`, error); } } public async dismissRoom() { try { logger.log(`${logPrefix}dismissRoom: enter`); this.closeMediaBeforeLeave(); await this.service.roomEngine.instance?.destroyRoom(); this.service.resetStore(); this.service.emit(EventType.ROOM_DISMISS, {}); } catch (error) { logger.error(`${logPrefix}dismissRoom error:`, error); } } public async createRoom(options: { roomId: string; roomName?: string; roomMode?: 'FreeToSpeak' | 'SpeakAfterTakingSeat'; roomParam?: StartParams; }) { try { const { roomId, roomName, roomMode, roomParam } = options; const roomParams = { roomId, roomName, roomType: TUIRoomType.kConference, isSeatEnabled: roomMode !== 'FreeToSpeak', seatMode: roomMode === 'SpeakAfterTakingSeat' ? TUISeatMode.kApplyToTake : undefined, password: roomParam?.password || '', }; await this.handleRoomCreation(roomParams, options); } catch (error) { logger.error(`${logPrefix}createRoom error:`, error); this.service.errorHandler.handleError(error, 'createRoom'); throw error; } } private async handleRoomCreation(roomParams: any, options: any) { const { roomEngine } = this.service; if (!roomEngine.instance) { return; } this.service.basicStore.setRoomId(roomParams.roomId); logger.debug(`${logPrefix}createRoom:`, roomParams, options); await roomEngine.instance?.createRoom(roomParams); } public async enterRoom(options: { roomId: string; roomParam?: RoomParam }) { try { const { roomId, roomParam } = options; const roomInfo = await this.doEnterRoom({ roomId, roomType: TUIRoomType.kConference, password: roomParam?.password || '', }); this.service.roomStore.setRoomInfo(roomInfo); // await this.getUserList(); await this.syncUserInfo(this.service.basicStore.userId); await this.fetchAttendeeList(roomId); await this.getInvitationList(roomId); if (roomInfo.isSeatEnabled) { await this.getSeatList(); this.service.roomStore.isMaster && (await this.service.roomEngine.instance?.takeSeat({ seatIndex: -1, timeout: 0, })); } this.setRoomParams(roomParam); } catch (error) { logger.error(`${logPrefix}enterRoom error:`, error); this.service.errorHandler.handleError(error, 'enterRoom'); throw error; } } private async setRoomParams(roomParam?: RoomParam) { if (!roomParam) { return; } const { isOpenCamera, isOpenMicrophone, defaultCameraId, defaultMicrophoneId, defaultSpeakerId, } = roomParam; if (defaultCameraId) { camera.setCurrentDevice({ deviceId: defaultCameraId }); } if (defaultMicrophoneId) { microphone.setCurrentDevice({ deviceId: defaultMicrophoneId }); } if (defaultSpeakerId) { speaker.setCurrentDevice({ deviceId: defaultSpeakerId }); } const { isMaster, isMicrophoneDisableForAllUser, isCameraDisableForAllUser, isFreeSpeakMode, } = this.service.roomStore; const isCanOpenMicrophone = isMaster || (!isMicrophoneDisableForAllUser && isFreeSpeakMode); if (isCanOpenMicrophone) { if (isOpenMicrophone) { await microphone.openLocalMicrophone(); await microphone.unmuteLocalAudio(); } else { await microphone.muteLocalAudio(); } } // 是否可以自动打开摄像头 const isCanOpenCamera = isMaster || (!isCameraDisableForAllUser && isFreeSpeakMode); if (isCanOpenCamera && isOpenCamera) { camera.openLocalCamera(); } } private async doEnterRoom(params: { roomId: string; roomType: TUIRoomType; password: string; }) { const { roomEngine } = this.service; const { roomId, roomType, password } = params; this.service.basicStore.setRoomId(roomId); const isH5 = isMobile && !isWeChat; const trtcCloud = roomEngine.instance?.getTRTCCloud(); trtcCloud?.setDefaultStreamRecvMode(true, false); const roomInfo = (await roomEngine.instance?.enterRoom({ roomId, roomType, options: { password, }, })) as TUIRoomInfo; // roomEngine enabled small stream by default in enterRoom api trtcCloud?.enableSmallVideoStream(!isH5, smallParam); microphone.muteLocalAudio(); if (!roomInfo.isSeatEnabled) { microphone.openLocalMicrophone(); } return roomInfo; } private async getUserList() { const { roomEngine } = this.service; try { const result = (await roomEngine.instance?.getUserList({ nextSequence: 0, })) as any; result.userInfoList.forEach((user: TUIUserInfo) => { this.service.roomStore.addUserInfo( Object.assign(user, { isInRoom: true }) ); if (this.service.roomStore.isFreeSpeakMode) { this.service.roomStore.addStreamInfo( user.userId, TUIVideoStreamType.kCameraStream ); } }); this.userListNextSequence = result.nextSequence; this.hasMoreUsers = result.nextSequence !== 0; } catch (error: any) { logger.error('TUIRoomEngine.getUserList', error.code, error.message); } } public async loadMoreUsers() { if (!this.hasMoreUsers) return; const { roomEngine } = this.service; try { const result = (await roomEngine.instance?.getUserList({ nextSequence: this.userListNextSequence, })) as any; result.userInfoList.forEach((user: TUIUserInfo) => { this.service.roomStore.addUserInfo( Object.assign(user, { isInRoom: true }) ); if (this.service.roomStore.isFreeSpeakMode) { this.service.roomStore.addStreamInfo( user.userId, TUIVideoStreamType.kCameraStream ); } }); this.userListNextSequence = result.nextSequence; this.hasMoreUsers = result.nextSequence !== 0; } catch (error: any) { logger.error('TUIRoomEngine.loadMoreUsers', error.code, error.message); } } private async getInvitationList( roomId: string, cursor = '', result: any[] = [] ) { const res = await this.service.conferenceInvitationManager.getInvitationList({ roomId, cursor, count: 20, }); if (!res?.invitationList) return []; // eslint-disable-next-line no-unsafe-optional-chaining result.push(...res?.invitationList); if (res.cursor !== '') { await this.getInvitationList(roomId, res.cursor, result); } const list = result.map(({ invitee, status }) => ({ ...invitee, status, })); this.service.roomStore.updateInviteeList(list as any); } private async fetchAttendeeList( roomId: string, cursor = '', result: any[] = [] ) { const res = await this.service.scheduleConferenceManager.fetchAttendeeList({ roomId, cursor, count: 20, }); if (!res?.attendeeList) return []; // eslint-disable-next-line no-unsafe-optional-chaining result.push(...res?.attendeeList); if (res.cursor !== '') { await this.fetchAttendeeList(roomId, res.cursor, result); } const inviteeList = result.filter(user => { return !this.service.roomStore.userList.some( item => item.userId === user.userId ); }); this.service.roomStore.updateInviteeList(inviteeList); } private async syncUserInfo(userId: string) { const { roomEngine } = this.service; const userInfo = (await roomEngine.instance?.getUserInfo({ userId, })) as any; const { isMessageDisabled } = userInfo; this.service.chatStore.setSendMessageDisableChanged(isMessageDisabled); } private async getSeatList() { const { roomEngine } = this.service; try { const seatList = (await roomEngine.instance?.getSeatList()) as any; await Promise.all( seatList.map(async (seat: TUISeatInfo) => { const { userId } = seat; if (!userId) { return; } const user = this.service.roomStore.userInfoObj[userId]; if (user && user.userName) { this.service.roomStore.updateUserInfo({ userId, onSeat: true }); } else { const userInfo = (await roomEngine.instance?.getUserInfo({ userId, })) as any; this.service.roomStore.addUserInfo( Object.assign(userInfo, { onSeat: true, isInRoom: true }) ); } this.service.roomStore.addStreamInfo( userId, TUIVideoStreamType.kCameraStream ); }) ); } catch (error: any) { logger.error('TUIRoomEngine.getSeatList', error.code, error.message); } } private closeMediaBeforeLeave() { const { roomEngine } = this.service; if (this.service.roomStore.localUser.hasAudioStream) { roomEngine.instance?.closeLocalMicrophone(); } if (this.service.roomStore.localUser.hasVideoStream) { roomEngine.instance?.closeLocalCamera(); } } public async fetchRoomInfo(options?: RoomParamsInfo) { return await this.service.roomEngine.instance?.fetchRoomInfo(options); } }