Added legacy support for the non 1.5.0 TeaSpeak server voice bridge and fixed the client status updates

canary
WolverinDEV 2020-11-17 14:27:46 +01:00
parent 769563757e
commit 8be3943d00
24 changed files with 359 additions and 37 deletions

View File

@ -175,6 +175,7 @@ export class ConnectionHandler {
lastChannelCodecWarned: -1
};
private clientStatusUnsync = false;
private inputHardwareState: InputHardwareState = InputHardwareState.MISSING;
@ -746,14 +747,17 @@ export class ConnectionHandler {
{
const currentClientProperties = this.getClient().properties;
for(const key of Object.keys(localClientUpdates)) {
if(currentClientProperties[key] === localClientUpdates[key])
if(currentClientProperties[key] === localClientUpdates[key] && !this.clientStatusUnsync)
delete localClientUpdates[key];
}
if(Object.keys(localClientUpdates).length > 0) {
this.serverConnection.send_command("clientupdate", localClientUpdates).catch(error => {
this.serverConnection.send_command("clientupdate", localClientUpdates).then(() => {
this.clientStatusUnsync = false;
}).catch(error => {
log.warn(LogCategory.GENERAL, tr("Failed to update client audio hardware properties. Error: %o"), error);
this.log.log(EventType.ERROR_CUSTOM, { message: tr("Failed to update audio hardware properties.") });
this.clientStatusUnsync = true;
/* Update these properties anyways (for case the server fails to handle the command) */
const updates = [];

View File

@ -11,7 +11,8 @@ export enum ServerFeature {
ERROR_BULKS= "error-bulks", /* Current version is 1 */
ADVANCED_CHANNEL_CHAT= "advanced-channel-chat", /* Current version is 1 */
LOG_QUERY= "log-query", /* Current version is 1 */
WHISPER_ECHO = "whisper-echo" /* Current version is 1 */
WHISPER_ECHO = "whisper-echo", /* Current version is 1 */
VIDEO = "video"
}
export interface ServerFeatureEvents {

View File

@ -69,7 +69,7 @@ export abstract class AbstractVoiceConnection {
abstract encodingSupported(codec: number) : boolean;
abstract decodingSupported(codec: number) : boolean;
abstract registerVoiceClient(clientId: number);
abstract registerVoiceClient(clientId: number) : VoiceClient;
abstract availableVoiceClients() : VoiceClient[];
abstract unregisterVoiceClient(client: VoiceClient);

2
web/.gitignore vendored
View File

@ -8,4 +8,4 @@ app/**/*.css.map
app/**/*.js
app/**/*.js.map
!app/audio-lib/worker/async_require.js
!app/legacy/audio-lib/worker/async_require.js

View File

@ -0,0 +1,246 @@
import {
AbstractVoiceConnection,
VoiceConnectionStatus,
WhisperSessionInitializer
} from "tc-shared/connection/VoiceConnection";
import {RtpVoiceConnection} from "tc-backend/web/rtc/voice/Connection";
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 {Registry} from "tc-shared/events";
import {VoicePlayerEvents, VoicePlayerLatencySettings, VoicePlayerState} from "tc-shared/voice/VoicePlayer";
class ProxiedVoiceClient implements VoiceClient {
readonly clientId: number;
readonly events: Registry<VoicePlayerEvents>;
handle: VoiceClient;
private volume: number;
private latencySettings: VoicePlayerLatencySettings | undefined;
constructor(clientId: number) {
this.clientId = clientId;
this.events = new Registry<VoicePlayerEvents>();
this.volume = 1;
}
setHandle(handle: VoiceClient | undefined) {
this.handle?.events.disconnectAll(this.events);
this.handle = handle;
if(this.latencySettings) {
this.handle?.setLatencySettings(this.latencySettings);
}
this.handle?.setVolume(this.volume);
this.handle?.events.connectAll(this.events);
}
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 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);
}
});
this.activeBridge?.events.disconnectAll(this.events);
this.activeBridge = type === "old" ? this.oldVoiceBridge : type === "new" ? this.newVoiceBride : undefined;
if(this.activeBridge) {
this.activeBridge.events.connectAll(this.events);
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;
}
}

View File

@ -1,8 +1,9 @@
import {
AbstractServerConnection,
CommandOptionDefaults,
CommandOptions, ConnectionStatistics,
CommandOptions,
ConnectionStateListener,
ConnectionStatistics,
} from "tc-shared/connection/ConnectionBase";
import {ConnectionHandler, ConnectionState, DisconnectReason} from "tc-shared/ConnectionHandler";
import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler";
@ -10,7 +11,7 @@ import {ConnectionCommandHandler, ServerConnectionCommandBoss} from "tc-shared/c
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
import {settings, Settings} from "tc-shared/settings";
import * as log from "tc-shared/log";
import {LogCategory, logTrace} from "tc-shared/log";
import {LogCategory, logDebug, logError, logTrace} from "tc-shared/log";
import {Regex} from "tc-shared/ui/modal/ModalConnect";
import {AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler";
import {EventType} from "tc-shared/ui/frames/log/Definitions";
@ -22,6 +23,9 @@ import {RTCConnection} from "tc-backend/web/rtc/Connection";
import {RtpVoiceConnection} from "tc-backend/web/rtc/voice/Connection";
import {RtpVideoConnection} from "tc-backend/web/rtc/video/Connection";
import {VideoConnection} from "tc-shared/connection/VideoConnection";
import {VoiceConnection} from "tc-backend/web/legacy/voice/VoiceHandler";
import {LegacySupportVoiceBridge} from "tc-backend/web/connection/LegacySupportVoiceBridge";
import {ServerFeature} from "tc-shared/connection/ServerFeatures";
class ReturnListener<T> {
resolve: (value?: T | PromiseLike<T>) => void;
@ -50,6 +54,10 @@ export class ServerConnection extends AbstractServerConnection {
private voiceConnection: RtpVoiceConnection;
private videoConnection: RtpVideoConnection;
/* legacy */
private oldVoiceConnection: VoiceConnection;
private legacyVoiceConnection: LegacySupportVoiceBridge;
private pingStatistics = {
thread_id: 0,
@ -77,6 +85,9 @@ export class ServerConnection extends AbstractServerConnection {
this.rtcConnection = new RTCConnection(this);
this.voiceConnection = new RtpVoiceConnection(this, this.rtcConnection);
this.videoConnection = new RtpVideoConnection(this.rtcConnection);
this.oldVoiceConnection = new VoiceConnection(this);
this.legacyVoiceConnection = new LegacySupportVoiceBridge(this, this.oldVoiceConnection, this.voiceConnection);
}
destroy() {
@ -107,6 +118,11 @@ export class ServerConnection extends AbstractServerConnection {
this.voiceConnection && this.voiceConnection.destroy();
this.voiceConnection = undefined;
this.oldVoiceConnection?.destroy();
this.oldVoiceConnection = undefined;
this.legacyVoiceConnection = undefined;
this.commandHandlerBoss && this.commandHandlerBoss.destroy();
this.commandHandlerBoss = undefined;
@ -334,9 +350,7 @@ export class ServerConnection extends AbstractServerConnection {
});
if(json["command"] === "initserver") {
this.pingStatistics.thread_id = setInterval(() => this.doNextPing(), this.pingStatistics.interval) as any;
this.doNextPing();
this.updateConnectionState(ConnectionState.CONNECTED);
this.handleServerInit();
}
/* devel-block(log-networking-commands) */
group.end();
@ -350,9 +364,7 @@ export class ServerConnection extends AbstractServerConnection {
});
if(command.command === "initserver") {
this.pingStatistics.thread_id = setInterval(() => this.doNextPing(), this.pingStatistics.interval) as any;
this.doNextPing();
this.updateConnectionState(ConnectionState.CONNECTED);
this.handleServerInit();
}
} else if(json["type"] === "ping") {
this.sendData(JSON.stringify({
@ -369,6 +381,8 @@ export class ServerConnection extends AbstractServerConnection {
this.pingStatistics.currentNativeValue = parseInt(json["ping_native"]) / 1000; /* we're getting it in microseconds and not milliseconds */
//log.debug(LogCategory.NETWORKING, tr("Received new pong. Updating ping to: JS: %o Native: %o"), this._ping.value.toFixed(3), this._ping.value_native.toFixed(3));
}
} else if(json["type"] === "WebRTC") {
this.oldVoiceConnection?.handleControlPacket(json);
} else {
log.warn(LogCategory.NETWORKING, tr("Unknown command type %o"), json["type"]);
}
@ -386,6 +400,37 @@ export class ServerConnection extends AbstractServerConnection {
this.socket.sendMessage(data);
}
private handleServerInit() {
this.pingStatistics.thread_id = setInterval(() => this.doNextPing(), this.pingStatistics.interval) as any;
this.doNextPing();
this.updateConnectionState(ConnectionState.CONNECTED);
this.client.serverFeatures.awaitFeatures().then(succeeded => {
if(!succeeded) {
/* something like a disconnect happened or so */
return;
}
if(this.client.serverFeatures.supportsFeature(ServerFeature.VIDEO, 1)) {
this.legacyVoiceConnection.setVoiceBridge("new").then(() => {
this.rtcConnection.doInitialSetup();
}).catch(error => {
logError(LogCategory.VOICE, tr("Failed to setup the voice bridge: %o"), error);
/* FIXME: Some kind of error modal? */
});
} else{
/* old voice connection */
logDebug(LogCategory.NETWORKING, tr("Using legacy voice connection for TeaSpeak server bellow 1.4.5"));
this.legacyVoiceConnection.setVoiceBridge("old").then(() => {
this.oldVoiceConnection.startVoiceBridge();
this.rtcConnection.setNotSupported();
}).catch(error => {
logError(LogCategory.VOICE, tr("Failed to setup the old voice bridge: %o"), error);
/* FIXME: Some kind of error modal? */
});
}
});
}
private static commandDataToJson(input: any) : string {
return JSON.stringify(input, (key, value) => {
switch (typeof value) {
@ -444,7 +489,7 @@ export class ServerConnection extends AbstractServerConnection {
}
getVoiceConnection(): AbstractVoiceConnection {
return this.voiceConnection /* || this.dummyVoiceConnection; */
return this.legacyVoiceConnection;
}
getVideoConnection(): VideoConnection {

View File

@ -4,7 +4,7 @@ import "webcrypto-liner";
import "./index.scss";
import "./FileTransfer";
import "./audio-lib";
import "./legacy/audio-lib";
import "./hooks/ServerConnection";
import "./hooks/ExternalModal";

View File

@ -1,4 +1,4 @@
import {AudioLibrary} from "tc-backend/web/audio-lib/index";
import {AudioLibrary} from "./index";
import {LogCategory, logWarn} from "tc-shared/log";
export class AudioClient {

View File

@ -5,8 +5,8 @@ import {
AWMessageRelations,
AWNotifies,
AWNotifiesWorker
} from "tc-backend/web/audio-lib/WorkerMessages";
import {AudioClient} from "tc-backend/web/audio-lib/AudioClient";
} from "./WorkerMessages";
import {AudioClient} from "./AudioClient";
import {LogCategory, logWarn} from "tc-shared/log";
import * as loader from "tc-loader";
import {Stage} from "tc-loader";

View File

@ -8,7 +8,7 @@ import {
AWMessageRelations,
AWNotifies,
AWNotifiesWorker
} from "tc-backend/web/audio-lib/WorkerMessages";
} from "../WorkerMessages";
import {AudioLibrary, getAudioLibraryInstance} from "./async_require";

View File

@ -1,5 +1,5 @@
import {VoiceClient} from "tc-shared/voice/VoiceClient";
import {WebVoicePlayer} from "tc-backend/web/voice/VoicePlayer";
import {WebVoicePlayer} from "./VoicePlayer";
export class VoiceClientController extends WebVoicePlayer implements VoiceClient {
private readonly clientId: number;

View File

@ -1,7 +1,7 @@
import * as log from "tc-shared/log";
import {LogCategory, logDebug, logError, logInfo, logTrace, logWarn} from "tc-shared/log";
import * as aplayer from "../audio/player";
import {ServerConnection} from "../connection/ServerConnection";
import * as aplayer from "../../audio/player";
import {ServerConnection} from "../../connection/ServerConnection";
import {RecorderProfile} from "tc-shared/voice/RecorderProfile";
import {VoiceClientController} from "./VoiceClient";
import {tr} from "tc-shared/i18n/localize";
@ -23,7 +23,7 @@ import {
WhisperTarget
} from "tc-shared/voice/VoiceWhisper";
import {VoiceClient} from "tc-shared/voice/VoiceClient";
import {WebWhisperSession} from "tc-backend/web/voice/VoiceWhisper";
import {WebWhisperSession} from "./VoiceWhisper";
import {VoicePlayerState} from "tc-shared/voice/VoicePlayer";
type CancelableWhisperTarget = WhisperTarget & { canceled: boolean };
@ -83,6 +83,10 @@ export class VoiceConnection extends AbstractVoiceConnection {
return this.failedConnectionMessage;
}
getRetryTimestamp(): number | 0 {
return 0;
}
destroy() {
this.connection.events.off(this.serverConnectionStateListener);
this.dropVoiceBridge();
@ -156,8 +160,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
this.events.fire("notify_recorder_changed");
}
private startVoiceBridge() {
return; /* We're not doing this currently */
public startVoiceBridge() {
if(!aplayer.initialized()) {
logDebug(LogCategory.VOICE, tr("Audio player isn't initialized yet. Waiting for it to initialize."));
if(!this.awaitingAudioInitialize) {
@ -319,7 +322,8 @@ export class VoiceConnection extends AbstractVoiceConnection {
private handleServerConnectionStateChanged(event: ServerConnectionEvents["notify_connection_state_changed"]) {
if(event.newState === ConnectionState.CONNECTED) {
this.startVoiceBridge();
/* startVoiceBridge() will be called by the server connection if we're using this old voice bridge */
/* this.startVoiceBridge(); */
} else {
this.connectAttemptCounter = 0;
this.lastConnectAttempt = 0;

View File

@ -4,11 +4,11 @@ import {
VoicePlayerLatencySettings,
VoicePlayerState
} from "tc-shared/voice/VoicePlayer";
import {AudioClient} from "tc-backend/web/audio-lib/AudioClient";
import {AudioClient} from "../audio-lib/AudioClient";
import {AudioResampler} from "./AudioResampler";
import {Registry} from "tc-shared/events";
import * as aplayer from "tc-backend/web/audio/player";
import {getAudioLibrary} from "tc-backend/web/audio-lib";
import {getAudioLibrary} from "../audio-lib";
import {LogCategory, logDebug, logError, logWarn} from "tc-shared/log";
const kDefaultLatencySettings = {

View File

@ -2,8 +2,8 @@ import {WhisperSession, WhisperSessionEvents, WhisperSessionState} from "tc-shar
import {Registry} from "tc-shared/events";
import {VoicePlayer, VoicePlayerState} from "tc-shared/voice/VoicePlayer";
import {WhisperSessionInitializeData} from "tc-shared/connection/VoiceConnection";
import {VoiceWhisperPacket} from "tc-backend/web/voice/bridge/VoiceBridge";
import {WebVoicePlayer} from "tc-backend/web/voice/VoicePlayer";
import {VoiceWhisperPacket} from "./bridge/VoiceBridge";
import {WebVoicePlayer} from "./VoicePlayer";
const kMaxUninitializedBuffers = 10;
export class WebWhisperSession implements WhisperSession {

View File

@ -4,7 +4,7 @@ import * as log from "tc-shared/log";
import {LogCategory} from "tc-shared/log";
import {tr} from "tc-shared/i18n/localize";
import {WebRTCVoiceBridge} from "./WebRTCVoiceBridge";
import {VoiceWhisperPacket} from "tc-backend/web/voice/bridge/VoiceBridge";
import {VoiceWhisperPacket} from "./VoiceBridge";
import {CryptoHelper} from "tc-shared/profiles/identities/TeamSpeakIdentity";
import arraybuffer_to_string = CryptoHelper.arraybuffer_to_string;

View File

@ -15,6 +15,7 @@ import {
} from "tc-backend/web/rtc/RemoteTrack";
import {SdpCompressor, SdpProcessor} from "tc-backend/web/rtc/SdpUtils";
import {context} from "tc-backend/web/audio/player";
import {ErrorCode} from "tc-shared/connection/ErrorCode";
const kSdpCompressionMode = 1;
@ -218,7 +219,8 @@ export enum RTPConnectionState {
DISCONNECTED,
CONNECTING,
CONNECTED,
FAILED
FAILED,
NOT_SUPPORTED
}
class InternalRemoteRTPAudioTrack extends RemoteRTPAudioTrack {
@ -560,6 +562,11 @@ export class RTCConnection {
});
}
public setNotSupported() {
this.reset(false);
this.updateConnectionState(RTPConnectionState.NOT_SUPPORTED);
}
private updateConnectionState(newState: RTPConnectionState) {
if(this.connectionState === newState) { return; }
@ -615,7 +622,10 @@ export class RTCConnection {
private enableDtx(_sender: RTCRtpSender) { }
private doInitialSetup() {
public doInitialSetup() {
/* initialize rtc connection */
this.retryCalculator.reset();
if(!('RTCPeerConnection' in window)) {
this.handleFatalError(tr("WebRTC has been disabled (RTCPeerConnection is not defined)"), false);
return;
@ -716,6 +726,10 @@ export class RTCConnection {
} catch (error) {
if(this.peer !== peer) { return; }
if(error instanceof CommandResult) {
if(error.id === ErrorCode.COMMAND_NOT_FOUND) {
this.setNotSupported();
return;
}
error = error.formattedMessage();
}
logWarn(LogCategory.VOICE, tr("Failed to initialize RTP connection: %o"), error);
@ -729,9 +743,7 @@ export class RTCConnection {
private handleConnectionStateChanged(event: ServerConnectionEvents["notify_connection_state_changed"]) {
if(event.newState === ConnectionState.CONNECTED) {
/* initialize rtc connection */
this.retryCalculator.reset();
this.doInitialSetup();
/* will be called by the server connection handler */
} else {
this.reset(true);
}

View File

@ -15,6 +15,7 @@ import {RtpVideoClient} from "tc-backend/web/rtc/video/VideoClient";
import {tr} from "tc-shared/i18n/localize";
import {ConnectionState} from "tc-shared/ConnectionHandler";
import {ConnectionStatistics} from "tc-shared/connection/ConnectionBase";
import {VoiceConnectionStatus} from "tc-shared/connection/VoiceConnection";
type VideoBroadcast = {
readonly source: VideoSource;
@ -240,6 +241,10 @@ export class RtpVideoConnection implements VideoConnection {
case RTPConnectionState.FAILED:
this.setConnectionState(VideoConnectionStatus.Failed);
break;
case RTPConnectionState.NOT_SUPPORTED:
this.setConnectionState(VideoConnectionStatus.Unsupported);
break;
}
}

View File

@ -256,8 +256,9 @@ export class RtpVoiceConnection extends AbstractVoiceConnection {
}
unregisterVoiceClient(client: VoiceClient) {
if(!(client instanceof RtpVoiceClient))
if(!(client instanceof RtpVoiceClient)) {
throw "Invalid client type";
}
console.error("Destroy voice client %d", client.getClientId());
client.events.off("notify_state_changed", this.voiceClientStateChangedEventListener);
@ -357,6 +358,10 @@ export class RtpVoiceConnection extends AbstractVoiceConnection {
this.localFailedReason = undefined;
this.setConnectionState(VoiceConnectionStatus.Failed);
break;
case RTPConnectionState.NOT_SUPPORTED:
this.setConnectionState(VoiceConnectionStatus.ServerUnsupported);
break;
}
}