TeaWeb/web/app/connection/LegacySupportVoiceBridge.ts
2021-01-22 13:34:43 +01:00

291 lines
No EOL
9.6 KiB
TypeScript

import {
AbstractVoiceConnection,
VoiceConnectionStatus,
WhisperSessionInitializer
} from "tc-shared/connection/VoiceConnection";
import {VoiceConnection} from "tc-backend/web/legacy/voice/VoiceHandler";
import {RecorderProfile} from "tc-shared/voice/RecorderProfile";
import {VoiceClient} from "tc-shared/voice/VoiceClient";
import {WhisperSession, WhisperTarget} from "tc-shared/voice/VoiceWhisper";
import {AbstractServerConnection, ConnectionStatistics} from "tc-shared/connection/ConnectionBase";
import {EventDispatchType, Registry} from "tc-shared/events";
import {VoicePlayerEvents, VoicePlayerLatencySettings, VoicePlayerState} from "tc-shared/voice/VoicePlayer";
import { tr } from "tc-shared/i18n/localize";
import {RtpVoiceConnection} from "tc-backend/web/voice/Connection";
class ProxiedVoiceClient implements VoiceClient {
readonly clientId: number;
readonly events: Registry<VoicePlayerEvents>;
handle: VoiceClient;
private volume: number;
private latencySettings: VoicePlayerLatencySettings | undefined;
private eventDisconnect: () => void;
constructor(clientId: number) {
this.clientId = clientId;
this.events = new Registry<VoicePlayerEvents>();
this.volume = 1;
}
setHandle(handle: VoiceClient | undefined) {
if(this.eventDisconnect) {
this.eventDisconnect();
this.eventDisconnect = undefined;
}
this.handle = handle;
if(this.latencySettings) {
this.handle?.setLatencySettings(this.latencySettings);
}
this.handle?.setVolume(this.volume);
if(this.handle) {
const targetEvents = this.events;
this.eventDisconnect = this.handle.events.registerConsumer({
handleEvent(mode: EventDispatchType, type: string, data: any) {
switch (mode) {
case "later":
targetEvents.fire_later(type as any, data);
break;
case "react":
targetEvents.fire_react(type as any, data);
break;
case "sync":
targetEvents.fire(type as any, data);
break;
}
}
});
}
}
abortReplay() {
this.handle?.abortReplay();
}
flushBuffer() {
this.handle?.flushBuffer();
}
getClientId(): number {
return this.clientId;
}
getLatencySettings(): Readonly<VoicePlayerLatencySettings> {
return this.handle?.getLatencySettings() || this.latencySettings || { maxBufferTime: 200, minBufferTime: 10 };
}
getState(): VoicePlayerState {
return this.handle ? this.handle.getState() : VoicePlayerState.STOPPED;
}
getVolume(): number {
return this.handle?.getVolume() || this.volume;
}
resetLatencySettings() {
this.handle.resetLatencySettings();
this.latencySettings = undefined;
}
setLatencySettings(settings: VoicePlayerLatencySettings) {
this.latencySettings = settings;
this.handle?.setLatencySettings(this.latencySettings);
}
setVolume(volume: number) {
this.volume = volume;
this.handle?.setVolume(volume);
}
}
export class LegacySupportVoiceBridge extends AbstractVoiceConnection {
private readonly newVoiceBride: RtpVoiceConnection;
private readonly oldVoiceBridge: VoiceConnection;
private activeBridge: AbstractVoiceConnection;
private disconnectEvents: () => void;
private encoderCodec: number;
private currentRecorder: RecorderProfile;
private registeredClients: ProxiedVoiceClient[] = [];
constructor(connection: AbstractServerConnection, oldVoiceBridge: VoiceConnection, newVoiceBride: RtpVoiceConnection) {
super(connection);
this.oldVoiceBridge = oldVoiceBridge;
this.newVoiceBride = newVoiceBride;
}
async setVoiceBridge(type: "old" | "new" | "unset") {
const oldState = this.getConnectionState();
this.registeredClients.forEach(e => {
if(e.handle) {
this.activeBridge.unregisterVoiceClient(e.handle);
e.setHandle(undefined);
}
});
if(this.disconnectEvents) {
this.disconnectEvents();
this.disconnectEvents = undefined;
}
this.activeBridge = type === "old" ? this.oldVoiceBridge : type === "new" ? this.newVoiceBride : undefined;
if(this.activeBridge) {
const targetEvents = this.events;
this.disconnectEvents = this.activeBridge.events.registerConsumer({
handleEvent(mode: EventDispatchType, type: string, data: any) {
switch (mode) {
case "later":
targetEvents.fire_later(type as any, data);
break;
case "react":
targetEvents.fire_react(type as any, data);
break;
case "sync":
targetEvents.fire(type as any, data);
break;
}
}
});
this.registeredClients.forEach(e => {
if(!e.handle) {
e.setHandle(this.activeBridge.registerVoiceClient(e.clientId));
}
});
await this.activeBridge.acquireVoiceRecorder(this.currentRecorder);
/* FIXME: Fire only if the state changed */
this.events.fire("notify_connection_status_changed", { oldStatus: oldState, newStatus: this.activeBridge.getConnectionState() });
this.events.fire("notify_voice_replay_state_change", { replaying: this.activeBridge.isReplayingVoice() });
} else {
/* FIXME: Fire only if the state changed */
this.events.fire("notify_connection_status_changed", { oldStatus: oldState, newStatus: VoiceConnectionStatus.Disconnected });
this.events.fire("notify_voice_replay_state_change", { replaying: false });
}
}
acquireVoiceRecorder(recorder: RecorderProfile | undefined): Promise<void> {
this.currentRecorder = recorder;
return this.activeBridge?.acquireVoiceRecorder(recorder);
}
decodingSupported(codec: number): boolean {
return !!this.activeBridge?.decodingSupported(codec);
}
encodingSupported(codec: number): boolean {
return !!this.activeBridge?.encodingSupported(codec);
}
dropWhisperSession(session: WhisperSession) {
this.activeBridge?.dropWhisperSession(session);
}
getConnectionState(): VoiceConnectionStatus {
return this.activeBridge ? this.activeBridge.getConnectionState() : VoiceConnectionStatus.Disconnected;
}
getConnectionStats(): Promise<ConnectionStatistics> {
return this.activeBridge ? this.activeBridge.getConnectionStats() : Promise.resolve({
bytesSend: 0,
bytesReceived: 0
});
}
getEncoderCodec(): number {
return this.activeBridge ? this.activeBridge.getEncoderCodec() : this.encoderCodec;
}
getFailedMessage(): string {
return this.activeBridge?.getFailedMessage();
}
getRetryTimestamp(): number | 0 {
return this.activeBridge ? this.activeBridge.getRetryTimestamp() : 0;
}
getWhisperSessionInitializer(): WhisperSessionInitializer | undefined {
return this.activeBridge?.getWhisperSessionInitializer();
}
getWhisperSessions(): WhisperSession[] {
return this.activeBridge?.getWhisperSessions() || [];
}
getWhisperTarget(): WhisperTarget | undefined {
return this.activeBridge?.getWhisperTarget();
}
isReplayingVoice(): boolean {
return !!this.activeBridge?.isReplayingVoice();
}
availableVoiceClients(): VoiceClient[] {
return this.registeredClients;
}
registerVoiceClient(clientId: number) {
if(this.registeredClients.findIndex(e => e.clientId === clientId) !== -1) {
throw tr("voice client already exists");
}
const client = new ProxiedVoiceClient(clientId);
client.setHandle(this.activeBridge?.registerVoiceClient(clientId));
this.registeredClients.push(client);
return client;
}
setEncoderCodec(codec: number) {
this.encoderCodec = codec;
this.newVoiceBride.setEncoderCodec(codec);
this.oldVoiceBridge.setEncoderCodec(codec);
}
setWhisperSessionInitializer(initializer: WhisperSessionInitializer | undefined) {
this.newVoiceBride.setWhisperSessionInitializer(initializer);
this.oldVoiceBridge.setWhisperSessionInitializer(initializer);
}
startWhisper(target: WhisperTarget): Promise<void> {
return this.activeBridge ? this.activeBridge.startWhisper(target) : Promise.reject(tr("voice bridge not connected"));
}
stopAllVoiceReplays() {
this.activeBridge?.stopAllVoiceReplays();
}
stopWhisper() {
this.oldVoiceBridge?.stopWhisper();
this.newVoiceBride?.stopWhisper();
}
unregisterVoiceClient(client: VoiceClient) {
if(!(client instanceof ProxiedVoiceClient)) {
throw tr("invalid voice client");
}
const index = this.registeredClients.indexOf(client);
if(index === -1) { return; }
this.registeredClients.splice(index, 1);
if(client.handle) {
this.activeBridge?.unregisterVoiceClient(client.handle);
}
}
voiceRecorder(): RecorderProfile {
return this.currentRecorder;
}
}