TeaWeb/web/app/voice/WhisperClient.ts

168 lines
5.3 KiB
TypeScript

import {WhisperSession, WhisperSessionEvents, WhisperSessionState} from "tc-shared/voice/VoiceWhisper";
import {Registry} from "tc-shared/events";
import {VoicePlayerState} from "tc-shared/voice/VoicePlayer";
import {WhisperSessionInitializeData} from "tc-shared/connection/VoiceConnection";
import {VoicePlayer} from "./VoicePlayer";
import {RemoteRTPAudioTrack, TrackClientInfo} from "tc-shared/connection/rtc/RemoteTrack";
import { tr, tra } from "tc-shared/i18n/localize";
export class RtpWhisperSession implements WhisperSession {
readonly events: Registry<WhisperSessionEvents>;
private readonly assignmentInfo: TrackClientInfo;
private track: RemoteRTPAudioTrack;
private globallyMuted: boolean;
private sessionState: WhisperSessionState;
private sessionBlocked: boolean;
private sessionTimeout: number;
private sessionTimeoutId: number;
private lastWhisperTimestamp: number;
private voicePlayer: VoicePlayer;
constructor(track: RemoteRTPAudioTrack, info: TrackClientInfo) {
this.events = new Registry<WhisperSessionEvents>();
this.track = track;
this.assignmentInfo = info;
this.sessionState = WhisperSessionState.INITIALIZING;
this.globallyMuted = false;
}
getClientId(): number {
return this.assignmentInfo.client_id;
}
getClientName(): string | undefined {
return this.assignmentInfo.client_name;
}
getClientUniqueId(): string | undefined {
return this.assignmentInfo.client_unique_id;
}
getLastWhisperTimestamp(): number {
return this.lastWhisperTimestamp;
}
getSessionState(): WhisperSessionState {
return this.sessionState;
}
getSessionTimeout(): number {
return this.sessionTimeout;
}
getVoicePlayer(): VoicePlayer | undefined {
return this.voicePlayer;
}
setSessionTimeout(timeout: number) {
this.sessionTimeout = timeout;
this.resetSessionTimeout();
}
isBlocked(): boolean {
return this.sessionBlocked;
}
setBlocked(blocked: boolean) {
this.sessionBlocked = blocked;
}
initializeFromData(data: WhisperSessionInitializeData) {
this.assignmentInfo.client_name = data.clientName;
this.assignmentInfo.client_unique_id = data.clientUniqueId;
this.sessionBlocked = data.blocked;
this.sessionTimeout = data.sessionTimeout;
this.voicePlayer = new VoicePlayer();
this.voicePlayer.setRtpTrack(this.track);
this.voicePlayer.setGloballyMuted(this.globallyMuted);
this.voicePlayer.setVolume(data.volume);
this.voicePlayer.events.on("notify_state_changed", event => {
if(event.newState === VoicePlayerState.BUFFERING) {
return;
}
this.resetSessionTimeout();
if(event.newState === VoicePlayerState.PLAYING || event.newState === VoicePlayerState.STOPPING) {
this.setSessionState(WhisperSessionState.PLAYING);
} else {
this.setSessionState(WhisperSessionState.PAUSED);
}
});
this.setSessionState(WhisperSessionState.PAUSED);
}
initializeFailed() {
this.setSessionState(WhisperSessionState.INITIALIZE_FAILED);
/* if we're receiving nothing for more than 5 seconds we can try it again */
this.sessionTimeout = 5000;
this.resetSessionTimeout();
}
destroy() {
clearTimeout(this.sessionTimeoutId);
this.events.destroy();
this.voicePlayer?.destroy();
this.voicePlayer = undefined;
}
setSessionState(state: WhisperSessionState) {
if(this.sessionState === state) {
return;
}
const oldState = this.sessionState;
this.sessionState = state;
this.events.fire("notify_state_changed", { oldState: oldState, newState: state });
}
private resetSessionTimeout() {
clearTimeout(this.sessionTimeoutId);
if(this.sessionState === WhisperSessionState.PLAYING) {
/* no need to reschedule a session timeout if we're currently playing */
return;
} else if(this.sessionState === WhisperSessionState.INITIALIZING) {
/* we're still initializing; a session timeout hasn't been set */
return;
}
this.sessionTimeoutId = setTimeout(() => {
this.events.fire("notify_timed_out");
}, Math.max(this.sessionTimeout, 1000));
}
setRtpTrack(track: RemoteRTPAudioTrack | undefined) {
if(track) {
const info = track.getCurrentAssignment();
if(!info) {
throw tr("Target track missing an assignment");
}
if(info.client_id !== this.assignmentInfo.client_id) {
throw tra("Target track does not belong to current whisper session owner (Owner: {}, Track owner: {})", this.assignmentInfo.client_id, info.client_name);
}
this.assignmentInfo.client_name = info.client_name;
this.assignmentInfo.client_unique_id = info.client_unique_id;
}
this.track = track;
this.voicePlayer?.setRtpTrack(track);
}
getRtpTrack() {
return this.voicePlayer.getRtpTrack();
}
setGloballyMuted(muted: boolean) {
this.globallyMuted = muted;
this.voicePlayer?.setGloballyMuted(muted);
}
}