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; 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(); 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); } }