UNPKG

sinch-rtc

Version:

RTC JavaScript/Web SDK

296 lines 12.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PeerConnectionClient = void 0; const Event_1 = require("../utils/Event"); const jsep_1 = require("../session/jsep"); const PeerConnection_1 = require("./PeerConnection"); const PeerConnection_2 = require("./PeerConnection"); class PeerConnectionClient { static create(media, trackEvent, useRelayIceCandidatesOnly, inactivate, clientEventsCollector, observer, iceServers) { return new PeerConnectionClient(media, trackEvent, useRelayIceCandidatesOnly, inactivate, clientEventsCollector, observer, iceServers); } constructor(mediaController, trackEvent, useRelayIceCandidatesOnly, inactivate, clientEventsCollector, observer, iceServers) { this.mediaController = mediaController; this.trackEvent = trackEvent; this.useRelayIceCandidatesOnly = useRelayIceCandidatesOnly; this.inactivate = inactivate; this.clientEventsCollector = clientEventsCollector; this.observer = observer; this.iceServers = iceServers; this.DUMMY_INSTANCE_ID = ""; this.peerConnections = new Array(); this.onLocalJsepMessage = new Event_1.Event(); this.remoteStreams = new Map(); this.pendingTracks = new Map(); this.pendingRelayCandidates = new Array(); this.enabledTracks = new Map(); this.onSessionDescriptionFailed = (error) => { console.error("Failed to create Session Description", error); }; this.onTrack = (evt, instanceId) => { var _a; this.trackEvent.fire(evt.track); if (this.instanceId) { this.remoteMediaStream.addTrack(evt.track); } else { if (!this.pendingTracks.has(instanceId)) this.pendingTracks.set(instanceId, new Array()); (_a = this.pendingTracks.get(instanceId)) === null || _a === void 0 ? void 0 : _a.push(evt.track); } }; this.cleanup = () => { this.peerConnections.forEach((pc) => { pc.clearAnyScheduledTimer(); pc.close(); }); }; this.onIceCandidate = (instanceId, candidate) => { if (candidate === null || candidate === void 0 ? void 0 : candidate.candidate) { this.onLocalJsepMessage.fire(new jsep_1.JsepMessage("candidate", JSON.stringify({ cand: candidate.candidate, sdpMLI: candidate.sdpMLineIndex, sdpMid: candidate.sdpMid, }), instanceId)); } }; this.onJsepMessageReceived = (messages) => { messages.forEach((message) => { if (message.isOffer || message.isAnswer) this.onJsepDescription(message); else if (message.isCandidate) this.onJsepIceCandidate(message); else if (message.isJoin && message.instanceId) { this.setConnectedInstanceId(message.instanceId); } }); }; this.remoteMediaStream = new MediaStream(); /** * Single PeerConnectionClient doesn't always handle single PeerConnection instance. * Multiple PeerConnections are expected if the PeerConnectionClient is the caller and the callee has multiple instances registered. * * Thus, we start notifying about ICE state changes once the call is answered to know which PC instance we'll be dealing with. * If we don't do this we could mix states of multiple PC instances while on observer's level the implementation is designed to handle single peer's ICE state. * * For simplicity we don't differentiate cases when we know since start that only 1 PC is expected (client is the callee) and just start emitting * ICE state changes once the call is answered. */ this.shouldNotifyIceStateChanges = false; } setIceServers(iceServers) { this.iceServers = iceServers; } createCertificate() { return __awaiter(this, void 0, void 0, function* () { if (RTCPeerConnection.generateCertificate) return RTCPeerConnection.generateCertificate({ name: "ECDSA", namedCurve: "P-256", }); }); } onCallAnswered(_) { this.closeNonJoinedPCs(); this.startEmittingIceStateChanges(); } onCallEstablished(_) { // enable local tracks when call is established unless they have been paused. if (this.enabledTracks.get("audio") != false) this.enableSenderTracks(true, "audio"); if (this.enabledTracks.get("video") != false) this.enableSenderTracks(true, "video"); } enableSenderTracks(enabled, kind) { this.enabledTracks.set(kind, enabled); this.peerConnections.forEach((p) => { p.enableSenderTracks(enabled, kind); }); } updateMediaController(media) { this.mediaController = media; this.peerConnections.forEach((p) => { p.setLocalMedia(media); }); } start() { return __awaiter(this, void 0, void 0, function* () { this.certificate = yield this.createCertificate(); yield this.createPeerConnection(this.DUMMY_INSTANCE_ID); }); } onSessionDescriptionCreated(sd) { if (sd.sdp && sd.type) { this.originalOffer = this.originalOffer || sd; this.onLocalJsepMessage.fire(new jsep_1.JsepMessage(sd.type, sd.sdp)); } } insertDtmf(tones) { return __awaiter(this, void 0, void 0, function* () { let inserted = false; this.peerConnections.forEach((pc) => (inserted = inserted || pc.insertDtmf(tones))); return inserted; }); } addPendingTracks() { var _a; if (this.instanceId) (_a = this.pendingTracks .get(this.instanceId)) === null || _a === void 0 ? void 0 : _a.splice(0).forEach((track) => { this.remoteMediaStream.addTrack(track); }); } setConnectedInstanceId(instanceId) { this.instanceId = instanceId; this.addPendingTracks(); this.addPendingRelayCandidates(); } get remoteStream() { return this.remoteMediaStream; } mapIceCandidate(json) { const candidate = JSON.parse(json); return { candidate: candidate.cand, sdpMLineIndex: candidate.sdpMLI, sdpMid: candidate.sdpMid, }; } addPendingRelayCandidates() { var _a; if (this.instanceId) { const sdp = (_a = this.originalOffer) === null || _a === void 0 ? void 0 : _a.sdp; if (sdp) { this.getPeerConnection(this.instanceId).then((pc) => { this.pendingRelayCandidates .splice(0) .forEach((c) => pc.addIceCandidate(this.mapIceCandidate(c))); }); } } } onJsepIceCandidate(message) { return __awaiter(this, void 0, void 0, function* () { if (message.instanceId) (yield this.getPeerConnection(message.instanceId)).addIceCandidate(this.mapIceCandidate(message.data)); }); } onJsepDescription(message) { return __awaiter(this, void 0, void 0, function* () { if (!message.instanceId) return; if (message.type == "offer") this.instanceId = message.instanceId; yield (yield this.getPeerConnection(message.instanceId)).setRemoteDescription({ sdp: message.data, type: message.type, }); }); } findPeerConnection(instanceId) { return this.peerConnections.find((p) => { return p.instanceId == instanceId; }); } getDefaultPeerConnection(instanceId) { const pc = this.findPeerConnection(this.DUMMY_INSTANCE_ID); if (pc) pc.instanceId = instanceId; return pc; } getRTCConfig() { return { certificates: this.certificate ? [this.certificate] : undefined, iceServers: this.iceServers, iceTransportPolicy: this.useRelayIceCandidatesOnly ? PeerConnection_2.IceTransportPolicy.Relay : PeerConnection_2.IceTransportPolicy.All, }; } closeNonJoinedPCs() { this.peerConnections.forEach((pc) => { if (pc.instanceId != this.instanceId) { pc.clearAnyScheduledTimer(); pc.close(); } }); } startEmittingIceStateChanges() { this.shouldNotifyIceStateChanges = true; const instanceId = this.instanceId; if (instanceId == undefined) { console.error("Failed to find remote peer connection instance. Remote instance id not known"); return; } const pc = this.findPeerConnection(instanceId); if (pc == undefined) { console.error(`Failed to find remote peer connection instance with id ${instanceId}`); return; } this.onIceConnectionStateChanged(pc.getIceConnectionState()); } createPeerConnection(instanceId) { return __awaiter(this, void 0, void 0, function* () { const pc = new PeerConnection_1.PeerConnection(this, this.getRTCConfig(), this.inactivate, this.clientEventsCollector, instanceId, this.originalOffer); pc.setLocalMedia(this.mediaController); // Disable all local media until call is established or enabled if (!this.enabledTracks.get("video")) pc.enableSenderTracks(false, "video"); if (!this.enabledTracks.get("audio")) pc.enableSenderTracks(false, "audio"); this.peerConnections.push(pc); return pc; }); } getPeerConnection(instanceId) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; return ((_b = (_a = this.findPeerConnection(instanceId)) !== null && _a !== void 0 ? _a : this.getDefaultPeerConnection(instanceId)) !== null && _b !== void 0 ? _b : this.createPeerConnection(instanceId)); }); } onIceConnectionStateChanged(state) { if (!this.shouldNotifyIceStateChanges) { return; } this.observer.onIceConnectionStateChanged(state); } setLegacyRelayCandidates(candidates) { candidates.forEach((c) => this.pendingRelayCandidates.push(c)); } getPeerConnectionStats() { return __awaiter(this, void 0, void 0, function* () { if (!this.instanceId) { console.error("Cannot get Webrtc stats. Instance id is not set"); return null; } const peerConnection = this.findPeerConnection(this.instanceId); if (!peerConnection) { console.error("Could not find peer connection for instance", { instanceId: this.instanceId, }); return null; } try { return yield peerConnection.pc.getStats(null); } catch (error) { console.error("Failed to get statistics for peer connection", { instanceId: this.instanceId, error, }); return null; } }); } } exports.PeerConnectionClient = PeerConnectionClient; //# sourceMappingURL=PeerConnectionClient.js.map