export class stream { constructor(parent) { this.parent = parent; this.janus; this.sfu; this.janus2; this.sfu2; this.dish = new Dish($("#videoContainer")[0]); this.dish.append(); this.dish.resize(); this.myid; this.pvtid; this.pvtid2; window.addEventListener("resize", () => { this.dish.resize(); }); Janus.init({debug: "none"}); } async init(type=false) { let done = new Promise(resolve => { let instance = "main"; let j = this; let janus = j.janus; if (type == "screen") { janus = j.janus2; instance = "screen"; } if (janus) { resolve(); } else { if (type == "screen") { j.janus2 = new Janus({ server: j.parent.streamURL, success: () => { j.janus2.attach({ plugin: "janus.plugin.videoroom", success: (pluginHandle) => { this.handle(instance, "success", pluginHandle).then(() => { resolve(); }); }, error: (error) => { this.handle(instance, "error", error).then(() => { resolve(); }); }, onmessage: (msg, jsep) => { if (["joined", "event"].includes(msg["videoroom"])) { if (msg["videoroom"] == "joined") { this.pvtid2 = msg["private_id"]; } this.handle(instance, "message", msg, jsep); } if (jsep) { this.sfu2.handleRemoteJsep({ jsep: jsep }); } }, onlocalstream: (stream) => { this.handle(instance, "local", stream); j.parent.ui.setMute("toggleScreen", 0); }, oncleanup: () => { this.handle(instance, "cleanup", { display: j.parent.domain }); j.parent.ui.setMute("toggleScreen", 1); } }); } }); } else { j.janus = new Janus({ server: j.parent.streamURL, success: () => { j.janus.attach({ plugin: "janus.plugin.videoroom", success: (pluginHandle) => { this.handle(instance, "success", pluginHandle).then(() => { resolve(); }); }, error: (error) => { this.handle(instance, "error", error).then(() => { resolve(); }); }, onmessage: (msg, jsep) => { if (["joined", "event", "talking", "stopped-talking"].includes(msg["videoroom"])) { if (msg["videoroom"] == "joined") { this.myid = msg["id"]; this.pvtid = msg["private_id"]; } this.handle(instance, "message", msg, jsep); } if (jsep) { this.sfu.handleRemoteJsep({ jsep: jsep }); } }, onlocalstream: (stream) => { this.handle(instance, "local", stream); }, oncleanup: () => { this.handle(instance, "cleanup", { display: j.parent.domain }); } }); } }); } } }); return await done; } subscribe(publisher) { let j = this; let feed; j.janus.attach({ plugin: "janus.plugin.videoroom", opaqueId: j.parent.domain, success: (pluginHandle) => { feed = pluginHandle; let message = { request: "join", room: j.parent.conversation, ptype: "subscriber", feed: publisher.id, private_id: j.pvtid }; feed.send({ message: message }); }, onmessage: function(msg, jsep) { if (jsep) { Janus.debug("Handling SDP as well...", jsep); feed.createAnswer({ jsep: jsep, media: { audioSend: false, videoSend: false }, success: function(jsep) { Janus.debug("Got SDP!", jsep); let message = { request: "start", room: j.parent.conversation }; feed.send({ message: message, jsep: jsep }); }, error: function(error) { Janus.error("WebRTC error:", error); } }); } }, onremotestream: (stream) => { publisher.stream = stream; j.handle("video", "remote", publisher); }, oncleanup: () => { j.handle("video", "cleanup", publisher); } }); } async handle(instance, type, data=false, jsep=false) { let done = new Promise(resolve => { switch (type) { case "success": if (instance == "screen") { this.sfu2 = data; this.register(this.sfu2); } else { this.sfu = data; if (Object.keys(this.parent.currentVideoUsers()).includes(this.parent.domain)) { this.register(this.sfu); } else { this.publishers(this.sfu); } } resolve(); break; case "error": resolve(); break; case "message": switch (data.videoroom) { case "talking": this.talking(data.id, true); break; case "stopped-talking": this.talking(data.id, false); break; default: $.each(data["publishers"], (k, p) => { if (Object.keys(this.parent.currentVideoUsers()).includes(p.display)) { this.subscribe(p); } }); break; } resolve(); break; case "local": if (instance == "screen") { this.attachScreen(this.parent.domain, data); } else { this.addCam(this.parent.domain, this.myid); this.attachVideo(this.parent.domain, data); } resolve(); break; case "remote": if (data.audio_codec || "talking" in data) { this.addCam(data.display, data.id); this.attachVideo(data.display, data.stream); } else { this.attachScreen(data.display, data.stream); } resolve(); break; case "cleanup": if ((data.audio_codec || !data.id) && instance !== "screen") { this.removeCam(data.display); } else { this.removeScreen(); } resolve(); break; } }); return await done; } getRandomNumber(digit) { return Math.random().toFixed(digit).split('.')[1]; } register(sfu) { let message = { request: "join", room: this.parent.conversation, ptype: "publisher", id: this.getRandomNumber(16), display: this.parent.domain }; sfu.send({ message: message }); } publishers(sfu) { let message = { request: "listparticipants", room: this.parent.conversation }; sfu.send({ "message" : message, success: (result) => { $.each(result.participants, (k, p) => { if (Object.keys(this.parent.currentVideoUsers()).includes(p.display)) { this.subscribe(p); } }); } }); } leave(sfu) { let message = { request: "leave" }; sfu.send({ message: message }); } async publish(type=false) { let sfu = this.sfu; let published = "main"; let media = { video: "video", audioRecv: false, videoRecv: false, audioSend: true, videoSend: true }; let message = { request: "configure", audio: true, video: true }; let done = new Promise(resolve => { if (type == "screen") { this.init("screen").then(() => { sfu = this.sfu2; published = "screen"; media = { video: "screen", audioRecv: false, videoRecv: false, audioSend: false, videoSend: true }; message = { request: "configure", audio: false, video: true }; resolve(); }); } else { resolve(); } }); await done; sfu.createOffer({ media: media, success: (jsep) => { sfu.send({ message: message, jsep: jsep }); if (type !== "screen") { sfu.muteVideo(); sfu.muteAudio(); } } }); } unpublish(type=false) { let message = { request: "unpublish" }; if (type) { switch (type) { case "video": if (this.sfu) { this.sfu.send({ message: message }); } break; case "screen": if (this.sfu2) { this.sfu2.send({ message: message }); } break; } } else { if (this.sfu) { this.sfu.send({ message: message }); } if (this.sfu2) { this.sfu2.send({ message: message }); } $(".controls > .button").addClass("muted"); } } toggleScreen() { let muted = $(".controls .button[data-action=toggleScreen]").hasClass("muted"); if (muted) { if (!$(".screen").hasClass("shown")) { this.publish("screen"); } } else { this.unpublish("screen"); } this.dish.resize(); } addCam(id, jid) { if (!$(`.cam[data-id=${id}]`).length) { let html = $(`
`); let row = $(`#users .users tr[data-id="${id}"]`).clone(); row.append($(`
`)); html.find("table").append(row); let row2 = $(`#users .users tr[data-id="${id}"]`).clone(); html.find(".background").append(row2); if (id == this.parent.domain) { html.find("video")[0].muted = true; html.addClass("me"); } $(".videoHolder .cams").append(html); } if (id && jid) { this.parent.ui.setData($(`.cam[data-id=${id}]`), "jid", jid); } this.dish.resize(); } attachVideo(id, video) { Janus.attachMediaStream($(`.cam[data-id=${id}] video`).get(0), video); this.dish.resize(); this.dish.resize(); } attachScreen(id, video) { let html = $(`.videoHolder .screen`); let table = html.find("table"); let row = $(`#users .users tr[data-id="${id}"]`).clone(); table.empty(); table.append(row); let screen = html.find("video").get(0); screen.muted = true; Janus.attachMediaStream(screen, video); $(".screen").addClass("shown"); this.dish.resize(); this.dish.resize(); } removeCam(id) { $(`.cam[data-id=${id}]`).remove(); this.dish.resize(); } removeScreen() { $(".screen").removeClass("shown"); this.dish.resize(); } mute(type, bool) { switch (type) { case "audio": if (bool) { this.sfu.muteAudio(); } else { this.sfu.unmuteAudio(); } break; case "video": if (bool) { this.sfu.muteVideo(); } else { this.sfu.unmuteVideo(); } break; } } talking(id, bool) { if (bool) { $(`.cam[data-jid=${id}]`).addClass("talking"); } else { $(`.cam[data-jid=${id}]`).removeClass("talking"); } } close() { $(".cam").remove(); if (this.janus2) { this.leave(this.sfu2); this.sfu2 = null; this.janus2 = null; } if (this.janus) { this.leave(this.sfu); this.sfu = null; this.janus = null; } } }