import { connect, Room, LocalVideoTrack, LocalAudioTrack, RemoteParticipant, LocalParticipant } from 'twilio-video'; import { twilioConfig, videoOptions, RoomType, TOKEN_SERVER_URL } from '../config/twilio'; export interface TwilioToken { token: string; identity: string; roomName: string; } export interface VideoCallOptions { roomName: string; identity: string; roomType?: RoomType; audio?: boolean; video?: boolean; } export interface ParticipantInfo { identity: string; sid: string; isLocal: boolean; audioEnabled: boolean; videoEnabled: boolean; } export class TwilioService { private room: Room | null = null; private localVideoTrack: LocalVideoTrack | null = null; private localAudioTrack: LocalAudioTrack | null = null; // 获取访问令牌 async getAccessToken(identity: string, roomName: string): Promise { try { const response = await fetch(`${TOKEN_SERVER_URL}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ identity, roomName, apiKey: twilioConfig.apiKey, apiSecret: twilioConfig.apiSecret, }), }); if (!response.ok) { throw new Error(`Token request failed: ${response.statusText}`); } const data = await response.json(); return data.token; } catch (error) { console.error('Error getting access token:', error); throw error; } } // 连接到视频房间 async connectToRoom(options: VideoCallOptions): Promise { try { const token = await this.getAccessToken(options.identity, options.roomName); const connectOptions = { ...videoOptions, name: options.roomName, audio: options.audio ?? true, video: options.video ?? true, }; this.room = await connect(token, connectOptions); // 设置事件监听器 this.setupRoomEventListeners(); return this.room; } catch (error) { console.error('Error connecting to room:', error); throw error; } } // 断开连接 disconnect(): void { if (this.room) { this.room.disconnect(); this.room = null; } if (this.localVideoTrack) { this.localVideoTrack.stop(); this.localVideoTrack = null; } if (this.localAudioTrack) { this.localAudioTrack.stop(); this.localAudioTrack = null; } } // 切换音频 toggleAudio(): boolean { if (this.room && this.room.localParticipant) { const audioTrack = Array.from(this.room.localParticipant.audioTracks.values())[0]; if (audioTrack) { if (audioTrack.track.isEnabled) { audioTrack.track.disable(); } else { audioTrack.track.enable(); } return audioTrack.track.isEnabled; } } return false; } // 切换视频 toggleVideo(): boolean { if (this.room && this.room.localParticipant) { const videoTrack = Array.from(this.room.localParticipant.videoTracks.values())[0]; if (videoTrack) { if (videoTrack.track.isEnabled) { videoTrack.track.disable(); } else { videoTrack.track.enable(); } return videoTrack.track.isEnabled; } } return false; } // 获取参与者信息 getParticipants(): ParticipantInfo[] { if (!this.room) return []; const participants: ParticipantInfo[] = []; // 本地参与者 const localParticipant = this.room.localParticipant; participants.push({ identity: localParticipant.identity, sid: localParticipant.sid, isLocal: true, audioEnabled: Array.from(localParticipant.audioTracks.values()).some(track => track.track.isEnabled), videoEnabled: Array.from(localParticipant.videoTracks.values()).some(track => track.track.isEnabled), }); // 远程参与者 this.room.participants.forEach((participant: RemoteParticipant) => { participants.push({ identity: participant.identity, sid: participant.sid, isLocal: false, audioEnabled: Array.from(participant.audioTracks.values()).some(track => track.track && track.track.isEnabled), videoEnabled: Array.from(participant.videoTracks.values()).some(track => track.track && track.track.isEnabled), }); }); return participants; } // 获取当前房间 getCurrentRoom(): Room | null { return this.room; } // 设置房间事件监听器 private setupRoomEventListeners(): void { if (!this.room) return; this.room.on('participantConnected', (participant: RemoteParticipant) => { console.log('Participant connected:', participant.identity); }); this.room.on('participantDisconnected', (participant: RemoteParticipant) => { console.log('Participant disconnected:', participant.identity); }); this.room.on('disconnected', (room: Room) => { console.log('Disconnected from room:', room.name); }); this.room.on('reconnecting', (error: any) => { console.log('Reconnecting to room...', error); }); this.room.on('reconnected', () => { console.log('Reconnected to room'); }); } } export const twilioService = new TwilioService();