2021-06-23 09:52:33 -04:00
|
|
|
import EventEmitter from "eventemitter3";
|
2021-07-05 06:23:23 -04:00
|
|
|
|
2021-06-23 09:52:33 -04:00
|
|
|
import {
|
2021-07-05 06:25:20 -04:00
|
|
|
RtpCapabilities,
|
|
|
|
RtpParameters,
|
2021-06-23 09:52:33 -04:00
|
|
|
} from "mediasoup-client/lib/RtpParameters";
|
|
|
|
import { DtlsParameters } from "mediasoup-client/lib/Transport";
|
|
|
|
|
|
|
|
import {
|
2021-07-05 06:25:20 -04:00
|
|
|
AuthenticationResult,
|
|
|
|
Room,
|
|
|
|
TransportInitDataTuple,
|
|
|
|
WSCommandType,
|
|
|
|
WSErrorCode,
|
|
|
|
ProduceType,
|
|
|
|
ConsumerData,
|
2021-06-23 09:52:33 -04:00
|
|
|
} from "./Types";
|
|
|
|
|
|
|
|
interface SignalingEvents {
|
2021-07-05 06:25:20 -04:00
|
|
|
open: (event: Event) => void;
|
|
|
|
close: (event: CloseEvent) => void;
|
|
|
|
error: (event: Event) => void;
|
|
|
|
data: (data: any) => void;
|
2021-06-23 09:52:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export default class Signaling extends EventEmitter<SignalingEvents> {
|
2021-07-05 06:25:20 -04:00
|
|
|
ws?: WebSocket;
|
|
|
|
index: number;
|
|
|
|
pending: Map<number, (data: unknown) => void>;
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.index = 0;
|
|
|
|
this.pending = new Map();
|
|
|
|
}
|
|
|
|
|
|
|
|
connected(): boolean {
|
|
|
|
return (
|
|
|
|
this.ws !== undefined &&
|
|
|
|
this.ws.readyState !== WebSocket.CLOSING &&
|
|
|
|
this.ws.readyState !== WebSocket.CLOSED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
connect(address: string): Promise<void> {
|
|
|
|
this.disconnect();
|
|
|
|
this.ws = new WebSocket(address);
|
|
|
|
this.ws.onopen = (e) => this.emit("open", e);
|
|
|
|
this.ws.onclose = (e) => this.emit("close", e);
|
|
|
|
this.ws.onerror = (e) => this.emit("error", e);
|
|
|
|
this.ws.onmessage = (e) => this.parseData(e);
|
|
|
|
|
|
|
|
let finished = false;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this.once("open", () => {
|
|
|
|
if (finished) return;
|
|
|
|
finished = true;
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.once("error", () => {
|
|
|
|
if (finished) return;
|
|
|
|
finished = true;
|
|
|
|
reject();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
disconnect() {
|
|
|
|
if (
|
|
|
|
this.ws !== undefined &&
|
|
|
|
this.ws.readyState !== WebSocket.CLOSED &&
|
|
|
|
this.ws.readyState !== WebSocket.CLOSING
|
|
|
|
)
|
|
|
|
this.ws.close(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
private parseData(event: MessageEvent) {
|
|
|
|
if (typeof event.data !== "string") return;
|
|
|
|
const json = JSON.parse(event.data);
|
|
|
|
const entry = this.pending.get(json.id);
|
|
|
|
if (entry === undefined) {
|
|
|
|
this.emit("data", json);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry(json);
|
|
|
|
}
|
|
|
|
|
|
|
|
sendRequest(type: string, data?: any): Promise<any> {
|
|
|
|
if (this.ws === undefined || this.ws.readyState !== WebSocket.OPEN)
|
|
|
|
return Promise.reject({ error: WSErrorCode.NotConnected });
|
|
|
|
|
|
|
|
const ws = this.ws;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
if (this.index >= 2 ** 32) this.index = 0;
|
|
|
|
while (this.pending.has(this.index)) this.index++;
|
|
|
|
const onClose = (e: CloseEvent) => {
|
|
|
|
reject({
|
|
|
|
error: e.code,
|
|
|
|
message: e.reason,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const finishedFn = (data: any) => {
|
|
|
|
this.removeListener("close", onClose);
|
|
|
|
if (data.error)
|
|
|
|
reject({
|
|
|
|
error: data.error,
|
|
|
|
message: data.message,
|
|
|
|
data: data.data,
|
|
|
|
});
|
|
|
|
resolve(data.data);
|
|
|
|
};
|
|
|
|
|
|
|
|
this.pending.set(this.index, finishedFn);
|
|
|
|
this.once("close", onClose);
|
|
|
|
const json = {
|
|
|
|
id: this.index,
|
|
|
|
type: type,
|
|
|
|
data,
|
|
|
|
};
|
|
|
|
ws.send(JSON.stringify(json) + "\n");
|
|
|
|
this.index++;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
authenticate(token: string, roomId: string): Promise<AuthenticationResult> {
|
|
|
|
return this.sendRequest(WSCommandType.Authenticate, { token, roomId });
|
|
|
|
}
|
|
|
|
|
|
|
|
async roomInfo(): Promise<Room> {
|
|
|
|
const room = await this.sendRequest(WSCommandType.RoomInfo);
|
|
|
|
return {
|
|
|
|
id: room.id,
|
|
|
|
videoAllowed: room.videoAllowed,
|
|
|
|
users: new Map(Object.entries(room.users)),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
initializeTransports(
|
|
|
|
rtpCapabilities: RtpCapabilities,
|
|
|
|
): Promise<TransportInitDataTuple> {
|
|
|
|
return this.sendRequest(WSCommandType.InitializeTransports, {
|
|
|
|
mode: "SplitWebRTC",
|
|
|
|
rtpCapabilities,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
connectTransport(
|
|
|
|
id: string,
|
|
|
|
dtlsParameters: DtlsParameters,
|
|
|
|
): Promise<void> {
|
|
|
|
return this.sendRequest(WSCommandType.ConnectTransport, {
|
|
|
|
id,
|
|
|
|
dtlsParameters,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async startProduce(
|
|
|
|
type: ProduceType,
|
|
|
|
rtpParameters: RtpParameters,
|
|
|
|
): Promise<string> {
|
|
|
|
let result = await this.sendRequest(WSCommandType.StartProduce, {
|
|
|
|
type,
|
|
|
|
rtpParameters,
|
|
|
|
});
|
|
|
|
return result.producerId;
|
|
|
|
}
|
|
|
|
|
|
|
|
stopProduce(type: ProduceType): Promise<void> {
|
|
|
|
return this.sendRequest(WSCommandType.StopProduce, { type });
|
|
|
|
}
|
|
|
|
|
|
|
|
startConsume(userId: string, type: ProduceType): Promise<ConsumerData> {
|
|
|
|
return this.sendRequest(WSCommandType.StartConsume, { type, userId });
|
|
|
|
}
|
|
|
|
|
|
|
|
stopConsume(consumerId: string): Promise<void> {
|
|
|
|
return this.sendRequest(WSCommandType.StopConsume, { id: consumerId });
|
|
|
|
}
|
|
|
|
|
|
|
|
setConsumerPause(consumerId: string, paused: boolean): Promise<void> {
|
|
|
|
return this.sendRequest(WSCommandType.SetConsumerPause, {
|
|
|
|
id: consumerId,
|
|
|
|
paused,
|
|
|
|
});
|
|
|
|
}
|
2021-06-23 09:52:33 -04:00
|
|
|
}
|