UNPKG

@skyway-sdk/core

Version:

The official Next Generation JavaScript SDK for SkyWay

288 lines 13.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.Peer = void 0; const common_1 = require("@skyway-sdk/common"); const token_1 = require("@skyway-sdk/token"); const errors_1 = require("../../../../errors"); const util_1 = require("../../../../util"); const util_2 = require("../util"); const log = new common_1.Logger('packages/core/src/plugin/internal/person/connection/peer.ts'); class Peer { constructor(_context, _iceManager, signaling, analytics, localPerson, endpoint, role) { var _a; this._context = _context; this._iceManager = _iceManager; this.signaling = signaling; this.analytics = analytics; this.localPerson = localPerson; this.endpoint = endpoint; this.role = role; this._pendingCandidates = []; this.pc = new RTCPeerConnection(Object.assign(Object.assign({}, this._context.config.rtcConfig), { iceTransportPolicy: this._context.config.rtcConfig.turnPolicy === 'turnOnly' ? 'relay' : undefined, iceServers: this._iceManager.iceServers })); this.onSignalingStateChanged = new common_1.Event(); this.onPeerConnectionStateChanged = new common_1.Event(); this.onDisconnect = new common_1.Event(); this.connected = false; this.disconnected = false; this.rtcPeerConnectionId = (0, token_1.uuidV4)(); this._onICECandidate = (ev) => __awaiter(this, void 0, void 0, function* () { if (ev.candidate == null || // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-expect-error firefox ev.candidate === '' || this.pc.connectionState === 'closed') { return; } const message = { kind: 'iceCandidateMessage', payload: { candidate: ev.candidate, role: this.role, }, }; log.debug('[start] send candidate', { message, localPerson: this.localPerson, }); if (this.localPerson._analytics && !this.localPerson._analytics.isClosed()) { // 再送時に他の処理をブロックしないためにawaitしない void this.localPerson._analytics.client.sendRtcPeerConnectionEventReport({ rtcPeerConnectionId: this.rtcPeerConnectionId, type: 'iceCandidate', data: { candidate: JSON.stringify(ev.candidate), }, createdAt: Date.now(), }); } try { yield this.signaling.send(this.endpoint, message); log.debug(`[end] send candidate`, { message, localPerson: this.localPerson, }); } catch (error) { log.warn(`[failed] send candidate`, (0, util_1.createWarnPayload)({ operationName: 'Peer._onICECandidate', channel: this.localPerson.channel, detail: '[failed] send candidate', payload: { message }, }), error); } }); this._onICECandidateError = (ev) => __awaiter(this, void 0, void 0, function* () { if (this.localPerson._analytics && !this.localPerson._analytics.isClosed()) { // 再送時に他の処理をブロックしないためにawaitしない void this.localPerson._analytics.client.sendRtcPeerConnectionEventReport({ rtcPeerConnectionId: this.rtcPeerConnectionId, type: 'iceCandidateError', data: { event: JSON.stringify(ev), }, createdAt: Date.now(), }); } }); this._onIceGatheringStateChange = () => __awaiter(this, void 0, void 0, function* () { if (this.localPerson._analytics && !this.localPerson._analytics.isClosed()) { const state = this.pc.iceGatheringState; // 再送時に他の処理をブロックしないためにawaitしない void this.localPerson._analytics.client.sendRtcPeerConnectionEventReport({ rtcPeerConnectionId: this.rtcPeerConnectionId, type: 'iceGatheringStateChange', data: { event: state, }, createdAt: Date.now(), }); } }); this._onConnectionStateChange = () => __awaiter(this, void 0, void 0, function* () { const state = this.pc.connectionState; if (this.localPerson._analytics && !this.localPerson._analytics.isClosed()) { // 再送時に他の処理をブロックしないためにawaitしない void this.localPerson._analytics.client.sendRtcPeerConnectionEventReport({ rtcPeerConnectionId: this.rtcPeerConnectionId, type: 'connectionStateChange', data: { connectionState: state, }, createdAt: Date.now(), }); } switch (state) { case 'connected': this.connected = true; this._pendingCandidates = []; break; } this.onPeerConnectionStateChanged.emit(this.pc.connectionState); }); this._onIceConnectionStateChange = () => __awaiter(this, void 0, void 0, function* () { if (this.localPerson._analytics && !this.localPerson._analytics.isClosed()) { const state = this.pc.iceConnectionState; // 再送時に他の処理をブロックしないためにawaitしない void this.localPerson._analytics.client.sendRtcPeerConnectionEventReport({ rtcPeerConnectionId: this.rtcPeerConnectionId, type: 'iceConnectionStateChange', data: { iceConnectionState: state, }, createdAt: Date.now(), }); } }); this._onSignalingStateChange = () => __awaiter(this, void 0, void 0, function* () { if (this.localPerson._analytics && !this.localPerson._analytics.isClosed()) { const state = this.pc.signalingState; // 再送時に他の処理をブロックしないためにawaitしない void this.localPerson._analytics.client.sendRtcPeerConnectionEventReport({ rtcPeerConnectionId: this.rtcPeerConnectionId, type: 'signalingStateChange', data: { signalingState: state, }, createdAt: Date.now(), }); } }); /**@throws {@link SkyWayError} */ this.waitForSignalingState = (state, /**ms */ timeout = 10000) => __awaiter(this, void 0, void 0, function* () { if (this.pc.signalingState === state) return; yield this.onSignalingStateChanged .watch(() => this.pc.signalingState === state, timeout) .catch((err) => { throw (0, util_1.createError)({ operationName: 'Peer.waitForSignalingState', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'waitForSignalingState timeout' }), path: log.prefix, context: this._context, channel: this.localPerson.channel, error: err, }); }); }); /**@throws {@link SkyWayError} */ this.waitForConnectionState = (state, /**ms */ timeout = 10000) => __awaiter(this, void 0, void 0, function* () { if (state === this.pc.connectionState) return; yield this.onPeerConnectionStateChanged .watch(() => state === this.pc.connectionState, timeout) .catch((err) => { throw (0, util_1.createError)({ operationName: 'Peer.waitForConnectionState', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'waitForConnectionState timeout' }), path: log.prefix, context: this._context, channel: this.localPerson.channel, error: err, }); }); }); /**@throws {@link SkyWayError} */ this.waitForStats = ({ track, cb, interval, timeout, logging, }) => __awaiter(this, void 0, void 0, function* () { interval !== null && interval !== void 0 ? interval : (interval = 100); timeout !== null && timeout !== void 0 ? timeout : (timeout = 10000); for (let elapsed = 0;; elapsed += interval) { if (elapsed >= timeout) { throw (0, util_1.createError)({ operationName: 'Peer.waitForStats', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'waitForStats timeout' }), path: log.prefix, context: this._context, channel: this.localPerson.channel, }); } const report = yield this.pc.getStats(track); const stats = (0, util_2.statsToJson)(report); if (logging) { log.debug('Peer.waitForStats', stats); } if (cb(stats)) { break; } yield new Promise((r) => setTimeout(r, interval)); } }); log.debug('peerConfig', this.pc.getConfiguration()); this.setPeerConnectionListener(); // suppress firefox [RTCPeerConnection is gone] Exception const peerIdentity = (_a = this.pc) === null || _a === void 0 ? void 0 : _a.peerIdentity; if (peerIdentity) { peerIdentity.catch((err) => { log.debug('firefox peerIdentity', err); }); } } setPeerConnectionListener() { this.pc.onicecandidate = this._onICECandidate; this.pc.onicecandidateerror = this._onICECandidateError; this.pc.onicegatheringstatechange = this._onIceGatheringStateChange; this.pc.onconnectionstatechange = this._onConnectionStateChange; this.pc.oniceconnectionstatechange = this._onIceConnectionStateChange; this.pc.onsignalingstatechange = () => { void this._onSignalingStateChange(); this.onSignalingStateChanged.emit(this.pc.signalingState); }; } unSetPeerConnectionListener() { this.pc.onicecandidate = null; this.pc.onicecandidateerror = null; this.pc.onicegatheringstatechange = null; this.pc.onconnectionstatechange = null; this.pc.oniceconnectionstatechange = null; this.pc.onsignalingstatechange = null; } handleCandidate(candidate) { return __awaiter(this, void 0, void 0, function* () { this._pendingCandidates.push(candidate); if (this.pc.remoteDescription) { yield this.resolveCandidates(); } }); } resolveCandidates() { return __awaiter(this, void 0, void 0, function* () { const candidates = [...this._pendingCandidates]; this._pendingCandidates = []; log.debug('addIceCandidates', candidates); yield Promise.all(candidates.map((candidate) => { if (this.pc.signalingState === 'closed') return Promise.resolve(); return this.pc.addIceCandidate(candidate).catch((err) => { log.warn('[failed] add ice candidate', (0, util_1.createWarnPayload)({ operationName: 'Peer.resolveCandidates', channel: this.localPerson.channel, detail: '[failed] send candidate', payload: { endpointId: this.endpoint.id }, }), err); }); })); }); } } exports.Peer = Peer; //# sourceMappingURL=peer.js.map