Twilioapp/src/services/twilioService.ts
2025-06-28 17:07:18 +08:00

194 lines
5.2 KiB
TypeScript

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<string> {
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<Room> {
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();