UNPKG

@skyway-sdk/core

Version:

The official Next Generation JavaScript SDK for SkyWay

295 lines 14.4 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.P2PConnection = void 0; const common_1 = require("@skyway-sdk/common"); const uuid_1 = require("uuid"); const errors_1 = require("../../../../errors"); const publication_1 = require("../../../../publication"); const util_1 = require("../../../../util"); const receiver_1 = require("./receiver"); const sender_1 = require("./sender"); const log = new common_1.Logger('packages/core/src/plugin/internal/person/connection/index.ts'); /**@internal */ class P2PConnection { /**@internal */ constructor(_iceManager, _signaling, _analytics, _context, channelId, localPerson, remoteMember) { this._iceManager = _iceManager; this._signaling = _signaling; this._analytics = _analytics; this._context = _context; this.channelId = channelId; this.localPerson = localPerson; this.remoteMember = remoteMember; this.id = (0, uuid_1.v4)(); this.type = 'p2p'; this.onDisconnect = new common_1.Event(); this.onClose = new common_1.Event(); this.closed = false; this.disconnected = false; this._log = log.createBlock({ id: this.id, localPersonId: this.localPerson.id, }); this._pubsubQueue = new common_1.PromiseQueue(); this.sendSubscriptionStatsReportTimers = new Map(); this._waitingSendSubscriptionStatsReportsFromPublish = new Map(); this._waitingSendSubscriptionStatsReportsFromSubscribe = []; this.sender = new sender_1.Sender(this._context, this._iceManager, this._signaling, this._analytics, this.localPerson, this.remoteMember); this.receiver = new receiver_1.Receiver(this._context, this._iceManager, this._signaling, this._analytics, this.localPerson, this.remoteMember); this.sender.onDisconnect.once(() => { this.disconnected = true; this.onDisconnect.emit(); }); this.receiver.onDisconnect.once(() => { this.disconnected = true; this.onDisconnect.emit(); }); if (this._analytics) { this._analytics.onConnectionStateChanged.add((state) => { // AnalyticsServerに初回接続できなかった場合のsendSubscriptionStatsReportタイマー再セット処理 if (state !== 'connected') return; if (this._waitingSendSubscriptionStatsReportsFromPublish.size > 0) { for (const [subscriptionId, publicationId] of this ._waitingSendSubscriptionStatsReportsFromPublish) { const publication = this.sender.publications[publicationId]; if (publication) { this.startSendSubscriptionStatsReportTimer(publication, subscriptionId); } } this._waitingSendSubscriptionStatsReportsFromPublish.clear(); } if (this._waitingSendSubscriptionStatsReportsFromSubscribe.length > 0) { for (const subscriptionId of this ._waitingSendSubscriptionStatsReportsFromSubscribe) { const subscription = this.receiver.subscriptions[subscriptionId]; if (subscription) { this.startSendSubscriptionStatsReportTimer(subscription, subscriptionId); } } this._waitingSendSubscriptionStatsReportsFromSubscribe = []; } }); } } /** * @internal * @throws {SkyWayError} */ startPublishing(publication, subscriptionId) { return __awaiter(this, void 0, void 0, function* () { yield this._pubsubQueue.push(() => __awaiter(this, void 0, void 0, function* () { this._log.debug('startPublishing', { publication }); yield this.sender.add(publication); })); if (this._analytics && !this._analytics.isClosed()) { // 再送時に他の処理をブロックしないためにawaitしない void this._analytics.client.sendBindingRtcPeerConnectionToSubscription({ subscriptionId: subscriptionId, role: 'sender', rtcPeerConnectionId: this.sender.rtcPeerConnectionId, }); if (this._analytics.client.isConnectionEstablished()) { this.startSendSubscriptionStatsReportTimer(publication, subscriptionId); } else { // AnalyticsServerに初回接続できなかった場合はキューに入れる this._waitingSendSubscriptionStatsReportsFromPublish.set(subscriptionId, publication.id); } } }); } /**@internal */ stopPublishing(publication) { return __awaiter(this, void 0, void 0, function* () { yield this._pubsubQueue.push(() => __awaiter(this, void 0, void 0, function* () { this._log.debug('<stopPublishing> start', { publication }); this.sender .remove(publication.id) .then(() => { this._log.debug('<stopPublishing> removed', { publication }); }) .catch((e) => { this._log.error('<stopPublishing> remove failed', e, { publication }); }); this._closeIfNeeded(); this._log.debug('<stopPublishing> end', { publication }); })); // publication(=stream)のidをkeyとして一致するタイマーを取得する const sendSubscriptionStatsReportTimer = this.sendSubscriptionStatsReportTimers.get(publication.id); if (sendSubscriptionStatsReportTimer) { clearInterval(sendSubscriptionStatsReportTimer); this.sendSubscriptionStatsReportTimers.delete(publication.id); } }); } /**@internal */ startSubscribing(subscription) { return __awaiter(this, void 0, void 0, function* () { yield this._pubsubQueue.push(() => __awaiter(this, void 0, void 0, function* () { this._log.debug('startSubscribing', { subscription }); this.receiver.add(subscription); const publicationId = subscription.publication.id; let stream = this.receiver.streams[publicationId]; if (!stream) { yield this.receiver.onStreamAdded .watch((res) => res.publicationId === publicationId, this._context.config.rtcConfig.timeout) .catch(() => { throw (0, util_1.createError)({ operationName: 'P2PConnection.startSubscribing', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onStreamAdded' }), path: log.prefix, context: this._context, channel: this.localPerson.channel, payload: { subscription }, }); }); stream = this.receiver.streams[publicationId]; } stream.setIsEnabled(subscription.publication.state === 'enabled'); subscription.codec = stream.codec; subscription._setStream(stream); if (this._analytics && !this._analytics.isClosed()) { // 再送時に他の処理をブロックしないためにawaitしない void this._analytics.client.sendBindingRtcPeerConnectionToSubscription({ subscriptionId: subscription.id, role: 'receiver', rtcPeerConnectionId: this.receiver.rtcPeerConnectionId, }); if (this._analytics.client.isConnectionEstablished()) { this.startSendSubscriptionStatsReportTimer(subscription, subscription.id); } else { // AnalyticsServerに初回接続できなかった場合はキューに入れる this._waitingSendSubscriptionStatsReportsFromSubscribe.push(subscription.id); } } })); }); } /**@internal */ stopSubscribing(subscription) { return __awaiter(this, void 0, void 0, function* () { yield this._pubsubQueue.push(() => __awaiter(this, void 0, void 0, function* () { this._log.debug('stopSubscribing', { subscription }); this.receiver.remove(subscription.id); this._closeIfNeeded(); })); // subscription(=stream)のidをkeyとして一致するタイマーを取得する const sendSubscriptionStatsReportTimer = this.sendSubscriptionStatsReportTimers.get(subscription.id); if (sendSubscriptionStatsReportTimer) { clearInterval(sendSubscriptionStatsReportTimer); this.sendSubscriptionStatsReportTimers.delete(subscription.id); } }); } _closeIfNeeded() { if (this.sender.hasMedia || this.receiver.hasMedia) return; this.close({ reason: 'no media' }); } getStats(content) { return __awaiter(this, void 0, void 0, function* () { const stream = content.stream; if (!stream) { throw (0, util_1.createError)({ operationName: 'P2PConnection.getStats', info: Object.assign(Object.assign({}, errors_1.errors.invalidArgumentValue), { detail: 'Subscription or Publication must has stream' }), path: log.prefix, context: this._context, channel: this.localPerson.channel, }); } if (stream.side === 'local') { if (stream.contentType === 'data') { return this.sender.pc.getStats(); } return this.sender.pc.getStats(stream.track); } else { if (stream.contentType === 'data') { return this.receiver.pc.getStats(); } return this.receiver.pc.getStats(stream.track); } }); } /**@internal */ close({ reason } = {}) { if (this.closed) { return; } this.closed = true; this._log.debug('closed', { endpointId: this.remoteMember.id, reason, sender: this.sender.id, receiver: this.receiver.id, id: this.id, }); this.sender.close(); this.receiver.close(); for (const timer of this.sendSubscriptionStatsReportTimers.values()) { clearInterval(timer); } this.sendSubscriptionStatsReportTimers.clear(); this._waitingSendSubscriptionStatsReportsFromPublish.clear(); this._waitingSendSubscriptionStatsReportsFromSubscribe = []; this.onClose.emit(); } startSendSubscriptionStatsReportTimer(stream, subscriptionId) { if (this._analytics) { const role = stream instanceof publication_1.PublicationImpl ? 'sender' : 'receiver'; const intervalSec = this._analytics.client.getIntervalSec(); this.sendSubscriptionStatsReportTimers.set(stream.id, setInterval(() => __awaiter(this, void 0, void 0, function* () { if (!this._analytics) { throw (0, util_1.createError)({ operationName: 'P2PConnection.sendSubscriptionStatsReportTimer', info: Object.assign(Object.assign({}, errors_1.errors.missingProperty), { detail: 'AnalyticsSession not exist' }), path: log.prefix, context: this._context, channel: this.localPerson.channel, }); } // AnalyticsSessionがcloseされていたらタイマーを止める if (this._analytics.isClosed()) { const subscriptionStatsReportTimer = this.sendSubscriptionStatsReportTimers.get(stream.id); if (subscriptionStatsReportTimer) { clearInterval(subscriptionStatsReportTimer); this.sendSubscriptionStatsReportTimers.delete(stream.id); } return; } const stats = yield this.getStats(stream); if (stats) { // 再送時に他の処理をブロックしないためにawaitしない void this._analytics.client.sendSubscriptionStatsReport(stats, { subscriptionId: subscriptionId, role: role, contentType: stream.contentType, createdAt: Date.now(), }); } }), intervalSec * 1000)); } } } exports.P2PConnection = P2PConnection; const p2pMessageKinds = [ 'senderProduceMessage', 'senderUnproduceMessage', 'receiverAnswerMessage', 'iceCandidateMessage', 'senderRestartIceMessage', 'ping', ]; //# sourceMappingURL=index.js.map