extract stuff to external library
This commit is contained in:
parent
77b8d45543
commit
b13198ce66
@ -67,6 +67,7 @@
|
||||
"husky": "^9.1.7",
|
||||
"prettier": "^3.5.3",
|
||||
"pretty-quick": "^4.1.1",
|
||||
"spacebar-webrtc-types": "github:dank074/spacebar-webrtc-types",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -124,8 +125,6 @@
|
||||
"optionalDependencies": {
|
||||
"@yukikaze-bot/erlpack": "^1.0.1",
|
||||
"jimp": "^1.6.0",
|
||||
"@dank074/medooze-media-server": "1.156.4",
|
||||
"semantic-sdp": "^3.31.1",
|
||||
"mysql": "^2.18.1",
|
||||
"nodemailer-mailgun-transport": "^2.1.5",
|
||||
"nodemailer-mailjet-transport": "github:n0script22/nodemailer-mailjet-transport",
|
||||
|
@ -21,7 +21,12 @@ import dotenv from "dotenv";
|
||||
import http from "http";
|
||||
import ws from "ws";
|
||||
import { Connection } from "./events/Connection";
|
||||
import { mediaServer } from "./util/MediaServer";
|
||||
import {
|
||||
mediaServer,
|
||||
WRTC_PORT_MAX,
|
||||
WRTC_PORT_MIN,
|
||||
WRTC_PUBLIC_IP,
|
||||
} from "./util/MediaServer";
|
||||
import { green, yellow } from "picocolors";
|
||||
dotenv.config();
|
||||
|
||||
@ -77,7 +82,7 @@ export class Server {
|
||||
console.log(`[WebRTC] ${yellow("WEBRTC disabled")}`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
await mediaServer.start();
|
||||
await mediaServer.start(WRTC_PUBLIC_IP, WRTC_PORT_MIN, WRTC_PORT_MAX);
|
||||
if (!this.server.listening) {
|
||||
this.server.listen(this.port);
|
||||
console.log(`[WebRTC] ${green(`online on 0.0.0.0:${this.port}`)}`);
|
||||
|
@ -1,274 +0,0 @@
|
||||
import { CodecInfo, MediaInfo, SDPInfo } from "semantic-sdp";
|
||||
import { SignalingDelegate } from "../util/SignalingDelegate";
|
||||
import { Codec, WebRtcClient } from "../util/WebRtcClient";
|
||||
import { MediaServer, Endpoint } from "@dank074/medooze-media-server";
|
||||
import { VoiceRoom } from "./VoiceRoom";
|
||||
import { MedoozeWebRtcClient } from "./MedoozeWebRtcClient";
|
||||
|
||||
export class MedoozeSignalingDelegate implements SignalingDelegate {
|
||||
private _rooms: Map<string, VoiceRoom> = new Map();
|
||||
private _ip: string;
|
||||
private _port: number;
|
||||
private _endpoint: Endpoint;
|
||||
|
||||
public start(): Promise<void> {
|
||||
MediaServer.enableLog(true);
|
||||
|
||||
this._ip = process.env.PUBLIC_IP || "127.0.0.1";
|
||||
|
||||
try {
|
||||
const range = process.env.WEBRTC_PORT_RANGE || "3690-3960";
|
||||
var ports = range.split("-");
|
||||
const min = Number(ports[0]);
|
||||
const max = Number(ports[1]);
|
||||
|
||||
MediaServer.setPortRange(min, max);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Invalid env var: WEBRTC_PORT_RANGE",
|
||||
process.env.WEBRTC_PORT_RANGE,
|
||||
error,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//MediaServer.setAffinity(2)
|
||||
this._endpoint = MediaServer.createEndpoint(this._ip);
|
||||
this._port = this._endpoint.getLocalPort();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public join(
|
||||
rtcServerId: string,
|
||||
userId: string,
|
||||
ws: any,
|
||||
type: "guild-voice" | "dm-voice" | "stream",
|
||||
): WebRtcClient<any> {
|
||||
// make sure user isn't already in a room of the same type
|
||||
// user can be in two simultanous rooms of different type though (can be in a voice channel and watching a stream for example)
|
||||
const rooms = this.rooms
|
||||
.values()
|
||||
.filter((room) =>
|
||||
type === "stream"
|
||||
? room.type === "stream"
|
||||
: room.type === "dm-voice" || room.type === "guild-voice",
|
||||
);
|
||||
let existingClient;
|
||||
|
||||
for (const room of rooms) {
|
||||
let result = room.getClientById(userId);
|
||||
if (result) {
|
||||
existingClient = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingClient) {
|
||||
console.log("client already connected, disconnect..");
|
||||
this.onClientClose(existingClient);
|
||||
}
|
||||
|
||||
if (!this._rooms.has(rtcServerId)) {
|
||||
console.debug("no channel created, creating one...");
|
||||
this.createRoom(rtcServerId, type);
|
||||
}
|
||||
|
||||
const room = this._rooms.get(rtcServerId)!;
|
||||
|
||||
const client = new MedoozeWebRtcClient(userId, rtcServerId, ws, room);
|
||||
|
||||
room?.onClientJoin(client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async onOffer(
|
||||
client: WebRtcClient<any>,
|
||||
sdpOffer: string,
|
||||
codecs: Codec[],
|
||||
): Promise<string> {
|
||||
const room = this._rooms.get(client.rtc_server_id);
|
||||
|
||||
if (!room) {
|
||||
console.error(
|
||||
"error, client sent an offer but has not authenticated",
|
||||
);
|
||||
Promise.reject();
|
||||
}
|
||||
|
||||
const offer = SDPInfo.parse("m=audio\n" + sdpOffer);
|
||||
|
||||
const rtpHeaders = new Map(offer.medias[0].extensions);
|
||||
|
||||
const getIdForHeader = (
|
||||
rtpHeaders: Map<number, string>,
|
||||
headerUri: string,
|
||||
) => {
|
||||
for (const [key, value] of rtpHeaders) {
|
||||
if (value == headerUri) return key;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const audioMedia = new MediaInfo("0", "audio");
|
||||
const audioCodec = new CodecInfo(
|
||||
"opus",
|
||||
codecs.find((val) => val.name == "opus")?.payload_type ?? 111,
|
||||
);
|
||||
audioCodec.addParam("minptime", "10");
|
||||
audioCodec.addParam("usedtx", "1");
|
||||
audioCodec.addParam("useinbandfec", "1");
|
||||
audioCodec.setChannels(2);
|
||||
audioMedia.addCodec(audioCodec);
|
||||
|
||||
audioMedia.addExtension(
|
||||
getIdForHeader(
|
||||
rtpHeaders,
|
||||
"urn:ietf:params:rtp-hdrext:ssrc-audio-level",
|
||||
),
|
||||
"urn:ietf:params:rtp-hdrext:ssrc-audio-level",
|
||||
);
|
||||
if (audioCodec.type === 111)
|
||||
// if this is chromium, apply this header
|
||||
audioMedia.addExtension(
|
||||
getIdForHeader(
|
||||
rtpHeaders,
|
||||
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
),
|
||||
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
);
|
||||
|
||||
const videoMedia = new MediaInfo("1", "video");
|
||||
const videoCodec = new CodecInfo(
|
||||
"H264",
|
||||
codecs.find((val) => val.name == "H264")?.payload_type ?? 102,
|
||||
);
|
||||
videoCodec.setRTX(
|
||||
codecs.find((val) => val.name == "H264")?.rtx_payload_type ?? 103,
|
||||
);
|
||||
videoCodec.addParam("level-asymmetry-allowed", "1");
|
||||
videoCodec.addParam("packetization-mode", "1");
|
||||
videoCodec.addParam("profile-level-id", "42e01f");
|
||||
videoCodec.addParam("x-google-max-bitrate", "2500");
|
||||
videoMedia.addCodec(videoCodec);
|
||||
|
||||
videoMedia.addExtension(
|
||||
getIdForHeader(
|
||||
rtpHeaders,
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
|
||||
),
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
|
||||
);
|
||||
videoMedia.addExtension(
|
||||
getIdForHeader(rtpHeaders, "urn:ietf:params:rtp-hdrext:toffset"),
|
||||
"urn:ietf:params:rtp-hdrext:toffset",
|
||||
);
|
||||
videoMedia.addExtension(
|
||||
getIdForHeader(
|
||||
rtpHeaders,
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
|
||||
),
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
|
||||
);
|
||||
videoMedia.addExtension(
|
||||
getIdForHeader(
|
||||
rtpHeaders,
|
||||
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
),
|
||||
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
);
|
||||
|
||||
if (audioCodec.type === 111)
|
||||
// if this is chromium, apply this header
|
||||
videoMedia.addExtension(
|
||||
getIdForHeader(rtpHeaders, "urn:3gpp:video-orientation"),
|
||||
"urn:3gpp:video-orientation",
|
||||
);
|
||||
|
||||
offer.medias = [audioMedia, videoMedia];
|
||||
|
||||
const transport = this._endpoint.createTransport(offer);
|
||||
|
||||
transport.setRemoteProperties(offer);
|
||||
|
||||
room?.onClientOffer(client, transport);
|
||||
|
||||
const dtls = transport.getLocalDTLSInfo();
|
||||
const ice = transport.getLocalICEInfo();
|
||||
const fingerprint = dtls.getHash() + " " + dtls.getFingerprint();
|
||||
const candidates = transport.getLocalCandidates();
|
||||
const candidate = candidates[0];
|
||||
|
||||
const answer =
|
||||
`m=audio ${this.port} ICE/SDP\n` +
|
||||
`a=fingerprint:${fingerprint}\n` +
|
||||
`c=IN IP4 ${this.ip}\n` +
|
||||
`a=rtcp:${this.port}\n` +
|
||||
`a=ice-ufrag:${ice.getUfrag()}\n` +
|
||||
`a=ice-pwd:${ice.getPwd()}\n` +
|
||||
`a=fingerprint:${fingerprint}\n` +
|
||||
`a=candidate:1 1 ${candidate.getTransport()} ${candidate.getFoundation()} ${candidate.getAddress()} ${candidate.getPort()} typ host\n`;
|
||||
|
||||
return Promise.resolve(answer);
|
||||
}
|
||||
|
||||
public onClientClose = (client: WebRtcClient<any>) => {
|
||||
this._rooms.get(client.rtc_server_id)?.onClientLeave(client);
|
||||
};
|
||||
|
||||
public updateSDP(offer: string): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
public createRoom(
|
||||
rtcServerId: string,
|
||||
type: "guild-voice" | "dm-voice" | "stream",
|
||||
): void {
|
||||
this._rooms.set(rtcServerId, new VoiceRoom(rtcServerId, type, this));
|
||||
}
|
||||
|
||||
public disposeRoom(rtcServerId: string): void {
|
||||
const room = this._rooms.get(rtcServerId);
|
||||
room?.dispose();
|
||||
this._rooms.delete(rtcServerId);
|
||||
}
|
||||
|
||||
get rooms(): Map<string, VoiceRoom> {
|
||||
return this._rooms;
|
||||
}
|
||||
|
||||
public getClientsForRtcServer(rtcServerId: string): Set<WebRtcClient<any>> {
|
||||
if (!this._rooms.has(rtcServerId)) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
return new Set(this._rooms.get(rtcServerId)?.clients.values())!;
|
||||
}
|
||||
|
||||
private getClientForUserId = (
|
||||
userId: string,
|
||||
): MedoozeWebRtcClient | undefined => {
|
||||
for (const channel of this.rooms.values()) {
|
||||
let result = channel.getClientById(userId);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
get ip(): string {
|
||||
return this._ip;
|
||||
}
|
||||
get port(): number {
|
||||
return this._port;
|
||||
}
|
||||
|
||||
get endpoint(): Endpoint {
|
||||
return this._endpoint;
|
||||
}
|
||||
|
||||
public stop(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
import {
|
||||
IncomingStream,
|
||||
OutgoingStream,
|
||||
Transport,
|
||||
} from "@dank074/medooze-media-server";
|
||||
import { SSRCs, WebRtcClient } from "webrtc/util";
|
||||
import { VoiceRoom } from "./VoiceRoom";
|
||||
|
||||
export class MedoozeWebRtcClient implements WebRtcClient<any> {
|
||||
websocket: any;
|
||||
user_id: string;
|
||||
rtc_server_id: string;
|
||||
webrtcConnected: boolean;
|
||||
public transport?: Transport;
|
||||
public incomingStream?: IncomingStream;
|
||||
public outgoingStream?: OutgoingStream;
|
||||
public room?: VoiceRoom;
|
||||
public isStopped?: boolean;
|
||||
|
||||
constructor(
|
||||
userId: string,
|
||||
rtcServerId: string,
|
||||
websocket: any,
|
||||
room: VoiceRoom,
|
||||
) {
|
||||
this.user_id = userId;
|
||||
this.rtc_server_id = rtcServerId;
|
||||
this.websocket = websocket;
|
||||
this.room = room;
|
||||
this.webrtcConnected = false;
|
||||
this.isStopped = false;
|
||||
}
|
||||
|
||||
public isProducingAudio(): boolean {
|
||||
if (!this.webrtcConnected) return false;
|
||||
const audioTrack = this.incomingStream?.getTrack(
|
||||
`audio-${this.user_id}`,
|
||||
);
|
||||
|
||||
if (audioTrack) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public isProducingVideo(): boolean {
|
||||
if (!this.webrtcConnected) return false;
|
||||
const videoTrack = this.incomingStream?.getTrack(
|
||||
`video-${this.user_id}`,
|
||||
);
|
||||
|
||||
if (videoTrack) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getIncomingStreamSSRCs(): SSRCs {
|
||||
if (!this.webrtcConnected)
|
||||
return { audio_ssrc: 0, video_ssrc: 0, rtx_ssrc: 0 };
|
||||
|
||||
const audioTrack = this.incomingStream?.getTrack(
|
||||
`audio-${this.user_id}`,
|
||||
);
|
||||
const audio_ssrc =
|
||||
audioTrack?.getSSRCs()[audioTrack.getDefaultEncoding().id];
|
||||
const videoTrack = this.incomingStream?.getTrack(
|
||||
`video-${this.user_id}`,
|
||||
);
|
||||
const video_ssrc =
|
||||
videoTrack?.getSSRCs()[videoTrack.getDefaultEncoding().id];
|
||||
|
||||
return {
|
||||
audio_ssrc: audio_ssrc?.media ?? 0,
|
||||
video_ssrc: video_ssrc?.media ?? 0,
|
||||
rtx_ssrc: video_ssrc?.rtx ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
public getOutgoingStreamSSRCsForUser(user_id: string): SSRCs {
|
||||
const outgoingStream = this.outgoingStream;
|
||||
|
||||
const audioTrack = outgoingStream?.getTrack(`audio-${user_id}`);
|
||||
const audio_ssrc = audioTrack?.getSSRCs();
|
||||
const videoTrack = outgoingStream?.getTrack(`video-${user_id}`);
|
||||
const video_ssrc = videoTrack?.getSSRCs();
|
||||
|
||||
return {
|
||||
audio_ssrc: audio_ssrc?.media ?? 0,
|
||||
video_ssrc: video_ssrc?.media ?? 0,
|
||||
rtx_ssrc: video_ssrc?.rtx ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
public publishTrack(type: "audio" | "video", ssrc: SSRCs) {
|
||||
if (!this.transport) return;
|
||||
|
||||
const id = `${type}-${this.user_id}`;
|
||||
const existingTrack = this.incomingStream?.getTrack(id);
|
||||
|
||||
if (existingTrack) {
|
||||
console.error(`error: attempted to create duplicate track ${id}`);
|
||||
return;
|
||||
}
|
||||
let ssrcs;
|
||||
if (type === "audio") {
|
||||
ssrcs = { media: ssrc.audio_ssrc! };
|
||||
} else {
|
||||
ssrcs = { media: ssrc.video_ssrc!, rtx: ssrc.rtx_ssrc };
|
||||
}
|
||||
const track = this.transport?.createIncomingStreamTrack(
|
||||
type,
|
||||
{ id, ssrcs: ssrcs, media: type },
|
||||
this.incomingStream,
|
||||
);
|
||||
|
||||
//this.channel?.onClientPublishTrack(this, track, ssrcs);
|
||||
}
|
||||
|
||||
public subscribeToTrack(user_id: string, type: "audio" | "video") {
|
||||
if (!this.transport) return;
|
||||
|
||||
const id = `${type}-${user_id}`;
|
||||
|
||||
const otherClient = this.room?.getClientById(user_id);
|
||||
const incomingStream = otherClient?.incomingStream;
|
||||
const incomingTrack = incomingStream?.getTrack(id);
|
||||
|
||||
if (!incomingTrack) {
|
||||
console.error(`error subscribing, not track found ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let ssrcs;
|
||||
if (type === "audio") {
|
||||
ssrcs = {
|
||||
media: otherClient?.getIncomingStreamSSRCs().audio_ssrc!,
|
||||
};
|
||||
} else {
|
||||
ssrcs = {
|
||||
media: otherClient?.getIncomingStreamSSRCs().video_ssrc!,
|
||||
rtx: otherClient?.getIncomingStreamSSRCs().rtx_ssrc,
|
||||
};
|
||||
}
|
||||
|
||||
const outgoingTrack = this.transport?.createOutgoingStreamTrack(
|
||||
incomingTrack.media,
|
||||
{ id, ssrcs, media: incomingTrack.media },
|
||||
this.outgoingStream,
|
||||
);
|
||||
|
||||
outgoingTrack?.attachTo(incomingTrack);
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
import { MedoozeSignalingDelegate } from "./MedoozeSignalingDelegate";
|
||||
import {
|
||||
IncomingStreamTrack,
|
||||
SSRCs,
|
||||
Transport,
|
||||
} from "@dank074/medooze-media-server";
|
||||
import { MedoozeWebRtcClient } from "./MedoozeWebRtcClient";
|
||||
import { StreamInfo } from "semantic-sdp";
|
||||
|
||||
export class VoiceRoom {
|
||||
private _clients: Map<string, MedoozeWebRtcClient>;
|
||||
private _id: string;
|
||||
private _sfu: MedoozeSignalingDelegate;
|
||||
private _type: "guild-voice" | "dm-voice" | "stream";
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
type: "guild-voice" | "dm-voice" | "stream",
|
||||
sfu: MedoozeSignalingDelegate,
|
||||
) {
|
||||
this._id = id;
|
||||
this._type = type;
|
||||
this._clients = new Map();
|
||||
this._sfu = sfu;
|
||||
}
|
||||
|
||||
onClientJoin = (client: MedoozeWebRtcClient) => {
|
||||
// do shit here
|
||||
this._clients.set(client.user_id, client);
|
||||
};
|
||||
|
||||
onClientOffer = (client: MedoozeWebRtcClient, transport: Transport) => {
|
||||
client.transport = transport;
|
||||
|
||||
client.transport.on("dtlsstate", (state, self) => {
|
||||
if (state === "connected") {
|
||||
client.webrtcConnected = true;
|
||||
console.log("connected");
|
||||
}
|
||||
});
|
||||
|
||||
client.incomingStream = transport.createIncomingStream(
|
||||
new StreamInfo(`in-${client.user_id}`),
|
||||
);
|
||||
|
||||
client.outgoingStream = transport.createOutgoingStream(
|
||||
new StreamInfo(`out-${client.user_id}`),
|
||||
);
|
||||
|
||||
client.webrtcConnected = true;
|
||||
|
||||
// subscribe to all current streams from this channel
|
||||
// for(const otherClient of this._clients.values()) {
|
||||
// const incomingStream = otherClient.incomingStream
|
||||
|
||||
// if(!incomingStream) continue;
|
||||
|
||||
// for(const track of (incomingStream.getTracks())) {
|
||||
// client.subscribeToTrack(otherClient.user_id, track.media)
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
onClientLeave = (client: MedoozeWebRtcClient) => {
|
||||
console.log("stopping client");
|
||||
this._clients.delete(client.user_id);
|
||||
|
||||
// stop the client
|
||||
if (!client.isStopped) {
|
||||
client.isStopped = true;
|
||||
|
||||
for (const otherClient of this.clients.values()) {
|
||||
//remove outgoing track for this user
|
||||
otherClient.outgoingStream
|
||||
?.getTrack(`audio-${client.user_id}`)
|
||||
?.stop();
|
||||
otherClient.outgoingStream
|
||||
?.getTrack(`video-${client.user_id}`)
|
||||
?.stop();
|
||||
}
|
||||
|
||||
client.incomingStream?.stop();
|
||||
client.outgoingStream?.stop();
|
||||
|
||||
client.transport?.stop();
|
||||
client.room = undefined;
|
||||
client.incomingStream = undefined;
|
||||
client.outgoingStream = undefined;
|
||||
client.transport = undefined;
|
||||
client.websocket = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
get clients(): Map<string, MedoozeWebRtcClient> {
|
||||
return this._clients;
|
||||
}
|
||||
|
||||
getClientById = (id: string) => {
|
||||
return this._clients.get(id);
|
||||
};
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get type(): "guild-voice" | "dm-voice" | "stream" {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
const clients = this._clients.values();
|
||||
for (const client of clients) {
|
||||
this.onClientLeave(client);
|
||||
}
|
||||
this._clients.clear();
|
||||
this._sfu = undefined!;
|
||||
this._clients = undefined!;
|
||||
}
|
||||
}
|
@ -20,10 +20,10 @@ import {
|
||||
mediaServer,
|
||||
VoiceOPCodes,
|
||||
VoicePayload,
|
||||
WebRtcClient,
|
||||
WebRtcWebSocket,
|
||||
Send,
|
||||
} from "@spacebar/webrtc";
|
||||
import type { WebRtcClient } from "spacebar-webrtc-types";
|
||||
|
||||
export async function onVideo(this: WebRtcWebSocket, payload: VoicePayload) {
|
||||
if (!this.webRtcClient || !this.webRtcClient.webrtcConnected) return;
|
||||
|
@ -16,25 +16,49 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//import { MedoozeSignalingDelegate } from "../medooze/MedoozeSignalingDelegate";
|
||||
import { SignalingDelegate } from "./SignalingDelegate";
|
||||
import type { SignalingDelegate } from "spacebar-webrtc-types";
|
||||
import { green, red } from "picocolors";
|
||||
|
||||
export let mediaServer: SignalingDelegate;
|
||||
|
||||
export const WRTC_PUBLIC_IP = process.env.WRTC_PUBLIC_IP ?? "127.0.0.1";
|
||||
export const WRTC_PORT_MIN = process.env.WRTC_PORT_MIN
|
||||
? parseInt(process.env.WRTC_PORT_MIN)
|
||||
: 2000;
|
||||
export const WRTC_PORT_MAX = process.env.WRTC_PORT_MAX
|
||||
? parseInt(process.env.WRTC_PORT_MAX)
|
||||
: 65000;
|
||||
|
||||
const selectedWrtcLibrary = process.env.WRTC_LIBRARY;
|
||||
|
||||
// could not find a way to hide stack trace from base Error object
|
||||
class NoConfiguredLibraryError implements Error {
|
||||
name: string;
|
||||
message: string;
|
||||
stack?: string | undefined;
|
||||
cause?: unknown;
|
||||
|
||||
constructor(message: string) {
|
||||
this.name = "NoConfiguredLibraryError";
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
//mediaServer = require('../medooze/MedoozeSignalingDelegate');
|
||||
mediaServer = new (
|
||||
await import("../medooze/MedoozeSignalingDelegate")
|
||||
).MedoozeSignalingDelegate();
|
||||
//mediaServer = require('medooze-spacebar-wrtc');
|
||||
if (!selectedWrtcLibrary)
|
||||
throw new NoConfiguredLibraryError("No library configured in .env");
|
||||
|
||||
mediaServer = new // @ts-ignore
|
||||
(await import(selectedWrtcLibrary)).default();
|
||||
|
||||
console.log(
|
||||
`[WebRTC] ${green("Succesfully loaded MedoozeSignalingDelegate")}`,
|
||||
`[WebRTC] ${green(`Succesfully loaded ${selectedWrtcLibrary}`)}`,
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
console.log(
|
||||
`[WebRTC] ${red("Failed to import MedoozeSignalingDelegate")}`,
|
||||
`[WebRTC] ${red(`Failed to import ${selectedWrtcLibrary}: ${error instanceof NoConfiguredLibraryError ? error.message : ""}`)}`,
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { Codec, WebRtcClient } from "./WebRtcClient";
|
||||
|
||||
export interface SignalingDelegate {
|
||||
start: () => Promise<void>;
|
||||
stop: () => Promise<void>;
|
||||
join<T>(
|
||||
rtcServerId: string,
|
||||
userId: string,
|
||||
ws: T,
|
||||
type: "guild-voice" | "dm-voice" | "stream",
|
||||
): WebRtcClient<T>;
|
||||
onOffer<T>(
|
||||
client: WebRtcClient<T>,
|
||||
offer: string,
|
||||
codecs: Codec[],
|
||||
): Promise<string>;
|
||||
onClientClose<T>(client: WebRtcClient<T>): void;
|
||||
updateSDP(offer: string): void;
|
||||
getClientsForRtcServer<T>(rtcServerId: string): Set<WebRtcClient<T>>;
|
||||
get ip(): string;
|
||||
get port(): number;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
export interface WebRtcClient<T> {
|
||||
websocket: T;
|
||||
user_id: string;
|
||||
rtc_server_id: string;
|
||||
webrtcConnected: boolean;
|
||||
getIncomingStreamSSRCs: () => SSRCs;
|
||||
getOutgoingStreamSSRCsForUser: (user_id: string) => SSRCs;
|
||||
isProducingAudio: () => boolean;
|
||||
isProducingVideo: () => boolean;
|
||||
publishTrack: (type: "audio" | "video", ssrc: SSRCs) => void;
|
||||
subscribeToTrack: (user_id: string, type: "audio" | "video") => void;
|
||||
}
|
||||
|
||||
export interface SSRCs {
|
||||
audio_ssrc?: number;
|
||||
video_ssrc?: number;
|
||||
rtx_ssrc?: number;
|
||||
}
|
||||
|
||||
export interface RtpHeader {
|
||||
uri: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface Codec {
|
||||
name: "opus" | "VP8" | "VP9" | "H264";
|
||||
type: "audio" | "video";
|
||||
priority: number;
|
||||
payload_type: number;
|
||||
rtx_payload_type?: number;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { WebSocket } from "@spacebar/gateway";
|
||||
import { WebRtcClient } from "./WebRtcClient";
|
||||
import type { WebRtcClient } from "spacebar-webrtc-types";
|
||||
|
||||
export interface WebRtcWebSocket extends WebSocket {
|
||||
type: "guild-voice" | "dm-voice" | "stream";
|
||||
|
@ -18,6 +18,5 @@
|
||||
|
||||
export * from "./Constants";
|
||||
export * from "./MediaServer";
|
||||
export * from "./WebRtcClient";
|
||||
export * from "./WebRtcWebSocket";
|
||||
export * from "./Send";
|
||||
|
Loading…
x
Reference in New Issue
Block a user