管理端页面补充
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user