electron-window-rtc
Version:
Exchange media streams between Electron windows with WebRTC.
241 lines (240 loc) • 7.74 kB
JavaScript
var p = Object.defineProperty;
var w = (s, i, e) => i in s ? p(s, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[i] = e;
var h = (s, i, e) => w(s, typeof i != "symbol" ? i + "" : i, e);
var a = /* @__PURE__ */ ((s) => (s.Log = "electron-window-rtc:log", s.Signal = "electron-window-rtc:signal", s.GetOwnWindowName = "electron-window-rtc:registered-window:own-name", s.GetRegisteredWindows = "electron-window-rtc:registered-windows:get", s))(a || {});
class m {
constructor() {
h(this, "listeners", {});
}
dispose() {
for (const i in this.listeners)
this.listeners[i].length = 0;
}
on(i, e) {
this.listeners[i] || (this.listeners[i] = []), this.listeners[i].push(e);
}
off(i, e) {
if (this.listeners[i]) {
if (e) {
const t = this.listeners[i].indexOf(e);
t >= 0 && this.listeners[i].splice(t, 1);
return;
}
delete this.listeners[i];
}
}
emit(i, e) {
if (this.listeners["*"])
for (const t of this.listeners["*"])
t({
channel: i,
...e
});
if (this.listeners[i])
for (const t of this.listeners[i])
t(e);
}
}
let o;
const S = (s) => {
o = s;
};
class g extends m {
constructor(e, t) {
super();
h(this, "connection");
h(this, "senderStream");
h(this, "_signalCallback");
this.name = e, this.peer = t, this.connection = new RTCPeerConnection(), this._signalCallback = this.signalCallback.bind(this), o.on(a.Signal, this._signalCallback), this.connection.onicecandidate = async (n) => {
if (n.candidate) {
const c = await o.invoke(a.Signal, {
channel: "candidate",
sender: this.name,
receiver: this.peer,
payload: JSON.stringify(n.candidate)
});
c && this.dispatch("error", c), this.dispatch("icecandidate", n);
}
}, this.connection.oniceconnectionstatechange = async (n) => {
(this.connection.iceConnectionState === "failed" || this.connection.iceConnectionState === "disconnected") && this.connection.restartIce(), this.dispatch("iceconnectionstatechange", n);
}, this.connection.onicecandidateerror = (n) => {
this.dispatch("icecandidateerror", n);
}, this.connection.onicegatheringstatechange = async (n) => {
this.dispatch("icegatheringstatechange", n);
}, this.connection.onnegotiationneeded = async (n) => {
this.dispatch("negotiationneeded", n);
}, this.connection.onsignalingstatechange = (n) => {
this.dispatch("signalingstatechange", n);
}, this.connection.ontrack = (n) => {
this.dispatch("track", n);
};
}
static async with(e) {
if (!o)
throw new Error(
"Ipc functions are not defined. Call 'defineIpc' method early."
);
const t = e.trim();
if (!(await o.invoke(a.GetRegisteredWindows)).includes(e))
throw new Error(`Peer window with name '${e}' was not registered.`);
const c = await o.invoke(a.GetOwnWindowName);
if (!c)
throw new Error(
"Unable to get own registered window name. Window may not have been properly registered."
);
return new g(c, t);
}
dispose() {
o.invoke(a.Signal, {
channel: "peer-left",
sender: this.name,
receiver: this.peer,
payload: void 0
}).then((t) => {
t && this.dispatch("error", t);
}), this.dispatch("leave");
const e = this.connection.getSenders();
for (const t of e)
t.track && (t.track.enabled = !1, this.connection.removeTrack(t));
o.removeListener(a.Signal, this._signalCallback), this.removeWebRTCListeners(), this.connection.close(), super.dispose();
}
/*
public async addTrack(
track: MediaStreamTrack,
constraints?: MediaTrackConstraints
): Promise<void> {
if (constraints) {
track.applyConstraints(constraints);
}
}
*/
async addStream(e, t = {
audio: 5e3,
video: 5e3
}) {
this.senderStream || (this.senderStream = new MediaStream());
for (const r of e.getTracks())
this.connection.addTrack(r, this.senderStream);
const n = await this.connection.createOffer();
this.connection.setLocalDescription(n);
const c = this.connection.getSenders();
for (const r of c)
if (r.track) {
if (t.video && r.track.kind === "video") {
const d = r.getParameters();
for (const l of d.encodings)
l.maxBitrate = /* Kbps */
t.video * /* Mbps */
1e3;
r.setParameters(d);
continue;
}
if (t.audio && r.track.kind === "audio") {
const d = r.getParameters();
for (const l of d.encodings)
l.maxBitrate = /* Kbps */
t.audio * /* Mbps */
1e3;
r.setParameters(d);
continue;
}
}
const f = await o.invoke(a.Signal, {
channel: "offer",
sender: this.name,
receiver: this.peer,
payload: JSON.stringify(n)
});
f && this.dispatch("error", f), this.dispatch("sent-offer", n);
}
async requestOffer() {
const e = await o.invoke(a.Signal, {
channel: "request-offer",
sender: this.name,
receiver: this.peer
});
e && this.dispatch("error", e), this.dispatch("request-offer");
}
get stream() {
return this.senderStream;
}
async signalCallback(e, t) {
switch (t.channel) {
case "request-offer": {
const n = await this.connection.createOffer();
this.connection.setLocalDescription(n);
const c = await o.invoke(a.Signal, {
channel: "offer",
sender: this.name,
receiver: this.peer,
payload: JSON.stringify(n)
});
c && this.dispatch("error", c), this.emit("sent-offer", {
local: this.name,
remote: this.peer,
payload: n
});
break;
}
case "offer": {
const n = JSON.parse(t.payload);
await this.handleOffer(n);
break;
}
case "answer": {
const n = JSON.parse(t.payload);
this.handleAnswer(n);
break;
}
case "candidate": {
const n = JSON.parse(t.payload);
this.handleCandidate(n);
break;
}
case "peer-left": {
t.sender === this.peer && this.handleLeave();
break;
}
}
}
async handleOffer(e) {
if (this.connection.signalingState === "closed")
return;
this.connection.setRemoteDescription(e);
let t = await this.connection.createAnswer();
await this.connection.setLocalDescription(t);
const n = await o.invoke(a.Signal, {
channel: "answer",
sender: this.name,
receiver: this.peer,
payload: JSON.stringify(this.connection.localDescription)
});
n && this.dispatch("error", n), this.dispatch("received-offer", {
offer: e,
answer: t
});
}
handleAnswer(e) {
this.connection.signalingState === "closed" || this.connection.signalingState === "stable" || (this.connection.setRemoteDescription(e), this.dispatch("received-answer", e));
}
handleCandidate(e) {
this.connection.iceConnectionState !== "closed" && (this.connection.addIceCandidate(e), this.dispatch("received-candidate", e));
}
handleLeave() {
this.dispatch("peer-left");
}
removeWebRTCListeners() {
this.connection.close(), this.connection.onicecandidate = null, this.connection.onicecandidateerror = null, this.connection.oniceconnectionstatechange = null, this.connection.ontrack = null;
}
dispatch(e, t) {
this.emit(e, {
local: this.name,
remote: this.peer,
payload: t
});
}
}
export {
g as WindowRTCPeerConnection,
S as defineIpc
};