const { EventEmitter: e } = require("events"),
t = require("sdp-transform"),
r = require("safety-catch"),
{ createTracer: i } = require("hypertrace"),
s = require("signal-promise"),
n = "127.0.0.1"
module.exports = class extends e {
constructor(e, t, n, a, c, o, { onerror: h = r, parentTracer: d } = {}) {
;(super(),
(this.swarmId = t),
(this.proxyPort = n),
(this.isPolite = e),
(this.remoteStreams = new Map()),
(this.remoteCameraStreamId = null),
(this.remoteScreenStreamId = null),
(this.tracer = i(this, { parent: d })),
(this._trackReady = this.isPolite ? new s() : null),
(this._closed = !1),
(this._onerror = h),
(this._cameraStreamId = a),
(this._screenStreamId = c),
(this._remotes = o),
(this._peer = new RTCPeerConnection({
iceServers: [],
rtcpMuxPolicy: "require",
sdpSemantics: "unified-plan",
})),
(this._onconnectionstatechange = this._onConnectionStateChange.bind(this)),
this._peer.addEventListener("connectionstatechange", this._onconnectionstatechange),
(this._ontrack = this._onTrack.bind(this)),
this._peer.addEventListener("track", this._ontrack),
(this._videoTransceiver = null),
(this._audioTransceiver = null),
(this._screenTransceiver = null),
this.isPolite ||
(this._peer.addTransceiver("video"),
this._peer.addTransceiver("audio"),
this._peer.addTransceiver("video"),
this._sendSignal()))
}
_rewriteSDP(e) {
const r = t.parse(e)
for (const e of r.media)
((e.port = e.rtcp.port = this.proxyPort),
(e.connection.ip = e.rtcp.address = n),
(e.connection.version = e.rtcp.ipVer = 4),
(e.candidates = [
{
ip: n,
port: this.proxyPort,
component: 1,
foundation: 1,
generation: 0,
priority: 1,
type: "host",
transport: "udp",
"network-cost": 50,
"network-id": 2,
},
]))
return t.write(r)
}
async _sendSignal() {
if (this.closed) return
if (this.isPolite && null === this._peer.remoteDescription) return
if (!this.isPolite && null !== this._peer.localDescription) return
if ((await this._peer.setLocalDescription(), this.closed)) return
const { type: e, sdp: t } = this._peer.localDescription
this.emit("signal", this, {
cameraStreamId: this._cameraStreamId,
screenStreamId: this._screenStreamId,
description: { type: e, sdp: t },
})
}
async _onConnectionStateChange() {
const e = this._peer.connectionState
"connected" === e ? this._maybeEmitReady() : ("failed" === e || "closed" === e) && this.close()
}
_onTrack({ track: e, transceiver: t }) {
;((t.direction = "sendrecv"),
"0" === t.mid
? ((this._videoTransceiver = t),
null !== this.remoteCameraStreamId &&
this._remotes.getStatus(this.remoteCameraStreamId).stream.addTrack(e))
: "1" === t.mid
? ((this._audioTransceiver = t),
null !== this.remoteCameraStreamId &&
this._remotes.getStatus(this.remoteCameraStreamId).stream.addTrack(e))
: "2" === t.mid &&
((this._screenTransceiver = t),
null !== this.remoteScreenStreamId &&
(this._remotes.getStatus(this.remoteScreenStreamId).stream.addTrack(e),
this._remotes.emitChangeOnTrackAdded(this.remoteScreenStreamId))),
this.isPolite && this._trackReady.notify(),
this._maybeEmitReady())
}
_maybeEmitReady() {
this.ready && this.emit("ready", this)
}
get ready() {
return (
null !== this._videoTransceiver &&
null !== this._audioTransceiver &&
null !== this._screenTransceiver &&
"connected" === this._peer.connectionState
)
}
get closed() {
return this._closed || "closed" === this._peer.connectionState
}
async handleSignalMessage({
cameraStreamId: e = null,
screenStreamId: t = null,
description: r,
}) {
try {
if (null !== this._peer.remoteDescription)
return console.warn("handleSignalMessage: remoteDescription was already set")
if (
((this.remoteCameraStreamId = e),
(this.remoteScreenStreamId = t),
(r.sdp = this._rewriteSDP(r.sdp)),
await this._peer.setRemoteDescription(r),
this.closed || !this.isPolite)
)
return
for (
;
null === this._videoTransceiver ||
null === this._audioTransceiver ||
null === this._screenTransceiver;
)
if ((await this._trackReady.wait(), this.closed)) return
await this._sendSignal()
} catch (e) {
;(this.tracer.trace({ error: e }), this._onerror(e))
}
}
async setVideoTrack(e) {
this.closed || (await this._videoTransceiver.sender.replaceTrack(e))
}
async setAudioTrack(e) {
this.closed || (await this._audioTransceiver.sender.replaceTrack(e))
}
async setScreenTrack(e) {
this.closed || (await this._screenTransceiver.sender.replaceTrack(e))
}
close() {
this._closed ||
((this._closed = !0),
this.isPolite && this._trackReady.notify(),
"closed" !== this._peer.connectionState && this._peer.close(),
this.emit("close", this),
this._peer.removeEventListener("track", this._ontrack),
this._peer.removeEventListener("connectionstatechange", this._onconnectionstatechange),
this.remoteStreams.clear(),
this.removeAllListeners())
}
}