OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-call / client / call / webrtc.js

160 lines · javascript ref:
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())
  }
}