summary refs log tree commit diff
path: root/src/webrtc/opcodes/Video.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/webrtc/opcodes/Video.ts')
-rw-r--r--src/webrtc/opcodes/Video.ts118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/webrtc/opcodes/Video.ts b/src/webrtc/opcodes/Video.ts
new file mode 100644
index 00000000..ff20d5a9
--- /dev/null
+++ b/src/webrtc/opcodes/Video.ts
@@ -0,0 +1,118 @@
+import { Payload, Send, WebSocket } from "@fosscord/gateway";
+import { validateSchema, VoiceVideoSchema } from "@fosscord/util";
+import { channels, getClients, VoiceOPCodes } from "@fosscord/webrtc";
+import { IncomingStreamTrack, SSRCs } from "medooze-media-server";
+import SemanticSDP from "semantic-sdp";
+
+export async function onVideo(this: WebSocket, payload: Payload) {
+	if (!this.client) return;
+	const { transport, channel_id } = this.client;
+	if (!transport) return;
+	const d = validateSchema("VoiceVideoSchema", payload.d) as VoiceVideoSchema;
+
+	await Send(this, { op: VoiceOPCodes.MEDIA_SINK_WANTS, d: { any: 100 } });
+
+	const id = "stream" + this.user_id;
+
+	var stream = this.client.in.stream!;
+	if (!stream) {
+		stream = this.client.transport!.createIncomingStream(
+			// @ts-ignore
+			SemanticSDP.StreamInfo.expand({
+				id,
+				// @ts-ignore
+				tracks: []
+			})
+		);
+		this.client.in.stream = stream;
+
+		const interval = setInterval(() => {
+			for (const track of stream.getTracks()) {
+				for (const layer of Object.values(track.getStats())) {
+					console.log(track.getId(), layer.total);
+				}
+			}
+		}, 5000);
+
+		stream.on("stopped", () => {
+			console.log("stream stopped");
+			clearInterval(interval);
+		});
+		this.on("close", () => {
+			transport!.stop();
+		});
+		const out = transport.createOutgoingStream(
+			// @ts-ignore
+			SemanticSDP.StreamInfo.expand({
+				id: "out" + this.user_id,
+				// @ts-ignore
+				tracks: []
+			})
+		);
+		this.client.out.stream = out;
+
+		const clients = channels.get(channel_id)!;
+
+		clients.forEach((client) => {
+			if (client.websocket.user_id === this.user_id) return;
+			if (!client.in.stream) return;
+
+			client.in.stream?.getTracks().forEach((track) => {
+				attachTrack.call(this, track, client.websocket.user_id);
+			});
+		});
+	}
+
+	if (d.audio_ssrc) {
+		handleSSRC.call(this, "audio", { media: d.audio_ssrc, rtx: d.audio_ssrc + 1 });
+	}
+	if (d.video_ssrc && d.rtx_ssrc) {
+		handleSSRC.call(this, "video", { media: d.video_ssrc, rtx: d.rtx_ssrc });
+	}
+}
+
+function attachTrack(this: WebSocket, track: IncomingStreamTrack, user_id: string) {
+	if (!this.client) return;
+	const outTrack = this.client.transport!.createOutgoingStreamTrack(track.getMedia());
+	outTrack.attachTo(track);
+	this.client.out.stream!.addTrack(outTrack);
+	var ssrcs = this.client.out.tracks.get(user_id)!;
+	if (!ssrcs) ssrcs = this.client.out.tracks.set(user_id, { audio_ssrc: 0, rtx_ssrc: 0, video_ssrc: 0 }).get(user_id)!;
+
+	if (track.getMedia() === "audio") {
+		ssrcs.audio_ssrc = outTrack.getSSRCs().media!;
+	} else if (track.getMedia() === "video") {
+		ssrcs.video_ssrc = outTrack.getSSRCs().media!;
+		ssrcs.rtx_ssrc = outTrack.getSSRCs().rtx!;
+	}
+
+	Send(this, {
+		op: VoiceOPCodes.VIDEO,
+		d: {
+			user_id: user_id,
+			...ssrcs
+		} as VoiceVideoSchema
+	});
+}
+
+function handleSSRC(this: WebSocket, type: "audio" | "video", ssrcs: SSRCs) {
+	if (!this.client) return;
+	const stream = this.client.in.stream!;
+	const transport = this.client.transport!;
+
+	const id = type + ssrcs.media;
+	var track = stream.getTrack(id);
+	if (!track) {
+		console.log("createIncomingStreamTrack", id);
+		track = transport.createIncomingStreamTrack(type, { id, ssrcs });
+		stream.addTrack(track);
+
+		const clients = getClients(this.client.channel_id)!;
+		clients.forEach((client) => {
+			if (client.websocket.user_id === this.user_id) return;
+			if (!client.out.stream) return;
+
+			attachTrack.call(this, track, client.websocket.user_id);
+		});
+	}
+}
\ No newline at end of file