@ohos-ports/genesys-cloud-streaming-client 20.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/cjs/alerting-leader.d.ts +29 -0
- package/dist/cjs/alerting-leader.js +163 -0
- package/dist/cjs/client.d.ts +84 -0
- package/dist/cjs/client.js +680 -0
- package/dist/cjs/connection-manager.d.ts +18 -0
- package/dist/cjs/connection-manager.js +120 -0
- package/dist/cjs/connection-transfer.d.ts +7 -0
- package/dist/cjs/connection-transfer.js +20 -0
- package/dist/cjs/http-client.d.ts +18 -0
- package/dist/cjs/http-client.js +230 -0
- package/dist/cjs/index.d.ts +10 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/messenger.d.ts +37 -0
- package/dist/cjs/messenger.js +54 -0
- package/dist/cjs/notifications.d.ts +94 -0
- package/dist/cjs/notifications.js +495 -0
- package/dist/cjs/ping.d.ts +21 -0
- package/dist/cjs/ping.js +60 -0
- package/dist/cjs/polyfills.d.ts +0 -0
- package/dist/cjs/polyfills.js +6 -0
- package/dist/cjs/server-monitor.d.ts +16 -0
- package/dist/cjs/server-monitor.js +44 -0
- package/dist/cjs/stanza-definitions/webrtc-signaling.d.ts +29 -0
- package/dist/cjs/stanza-definitions/webrtc-signaling.js +113 -0
- package/dist/cjs/stanza-definitions/xep0051.d.ts +19 -0
- package/dist/cjs/stanza-definitions/xep0051.js +28 -0
- package/dist/cjs/stats-formatter.d.ts +6 -0
- package/dist/cjs/stats-formatter.js +63 -0
- package/dist/cjs/types/genesys-cloud-media-session.d.ts +64 -0
- package/dist/cjs/types/genesys-cloud-media-session.js +353 -0
- package/dist/cjs/types/interfaces.d.ts +310 -0
- package/dist/cjs/types/interfaces.js +22 -0
- package/dist/cjs/types/libs.d.ts +32 -0
- package/dist/cjs/types/libs.js +1 -0
- package/dist/cjs/types/media-session.d.ts +76 -0
- package/dist/cjs/types/media-session.js +2 -0
- package/dist/cjs/types/named-agent.d.ts +13 -0
- package/dist/cjs/types/named-agent.js +2 -0
- package/dist/cjs/types/offline-error.d.ts +2 -0
- package/dist/cjs/types/offline-error.js +5 -0
- package/dist/cjs/types/sasl-error.d.ts +8 -0
- package/dist/cjs/types/sasl-error.js +12 -0
- package/dist/cjs/types/stanza-media-session.d.ts +56 -0
- package/dist/cjs/types/stanza-media-session.js +255 -0
- package/dist/cjs/types/streaming-client-extension.d.ts +14 -0
- package/dist/cjs/types/streaming-client-extension.js +2 -0
- package/dist/cjs/types/timeout-error.d.ts +3 -0
- package/dist/cjs/types/timeout-error.js +9 -0
- package/dist/cjs/types/user-cancelled-error.d.ts +2 -0
- package/dist/cjs/types/user-cancelled-error.js +5 -0
- package/dist/cjs/utils.d.ts +34 -0
- package/dist/cjs/utils.js +202 -0
- package/dist/cjs/webrtc.d.ts +126 -0
- package/dist/cjs/webrtc.js +891 -0
- package/dist/deploy-info.json +21 -0
- package/dist/es/alerting-leader.d.ts +29 -0
- package/dist/es/alerting-leader.js +177 -0
- package/dist/es/client.d.ts +84 -0
- package/dist/es/client.js +699 -0
- package/dist/es/connection-manager.d.ts +18 -0
- package/dist/es/connection-manager.js +124 -0
- package/dist/es/connection-transfer.d.ts +7 -0
- package/dist/es/connection-transfer.js +16 -0
- package/dist/es/http-client.d.ts +18 -0
- package/dist/es/http-client.js +194 -0
- package/dist/es/index.bundle.js +39629 -0
- package/dist/es/index.d.ts +10 -0
- package/dist/es/index.js +10 -0
- package/dist/es/messenger.d.ts +37 -0
- package/dist/es/messenger.js +62 -0
- package/dist/es/notifications.d.ts +94 -0
- package/dist/es/notifications.js +507 -0
- package/dist/es/ping.d.ts +21 -0
- package/dist/es/ping.js +68 -0
- package/dist/es/polyfills.d.ts +0 -0
- package/dist/es/polyfills.js +6 -0
- package/dist/es/server-monitor.d.ts +16 -0
- package/dist/es/server-monitor.js +41 -0
- package/dist/es/stanza-definitions/webrtc-signaling.d.ts +29 -0
- package/dist/es/stanza-definitions/webrtc-signaling.js +110 -0
- package/dist/es/stanza-definitions/xep0051.d.ts +19 -0
- package/dist/es/stanza-definitions/xep0051.js +25 -0
- package/dist/es/stats-formatter.d.ts +6 -0
- package/dist/es/stats-formatter.js +54 -0
- package/dist/es/types/genesys-cloud-media-session.d.ts +64 -0
- package/dist/es/types/genesys-cloud-media-session.js +379 -0
- package/dist/es/types/interfaces.d.ts +310 -0
- package/dist/es/types/interfaces.js +19 -0
- package/dist/es/types/libs.d.ts +32 -0
- package/dist/es/types/libs.js +1 -0
- package/dist/es/types/media-session.d.ts +76 -0
- package/dist/es/types/media-session.js +1 -0
- package/dist/es/types/named-agent.d.ts +13 -0
- package/dist/es/types/named-agent.js +1 -0
- package/dist/es/types/offline-error.d.ts +2 -0
- package/dist/es/types/offline-error.js +2 -0
- package/dist/es/types/sasl-error.d.ts +8 -0
- package/dist/es/types/sasl-error.js +9 -0
- package/dist/es/types/stanza-media-session.d.ts +56 -0
- package/dist/es/types/stanza-media-session.js +264 -0
- package/dist/es/types/streaming-client-extension.d.ts +14 -0
- package/dist/es/types/streaming-client-extension.js +1 -0
- package/dist/es/types/timeout-error.d.ts +3 -0
- package/dist/es/types/timeout-error.js +5 -0
- package/dist/es/types/user-cancelled-error.d.ts +2 -0
- package/dist/es/types/user-cancelled-error.js +2 -0
- package/dist/es/utils.d.ts +34 -0
- package/dist/es/utils.js +192 -0
- package/dist/es/webrtc.d.ts +126 -0
- package/dist/es/webrtc.js +917 -0
- package/dist/manifest.json +19 -0
- package/dist/npm/CHANGELOG.md +637 -0
- package/dist/npm/README.md +47 -0
- package/dist/npm/alerting-leader.d.ts +29 -0
- package/dist/npm/alerting-leader.js +163 -0
- package/dist/npm/client.d.ts +84 -0
- package/dist/npm/client.js +680 -0
- package/dist/npm/connection-manager.d.ts +18 -0
- package/dist/npm/connection-manager.js +120 -0
- package/dist/npm/connection-transfer.d.ts +7 -0
- package/dist/npm/connection-transfer.js +20 -0
- package/dist/npm/http-client.d.ts +18 -0
- package/dist/npm/http-client.js +230 -0
- package/dist/npm/index.d.ts +10 -0
- package/dist/npm/index.js +31 -0
- package/dist/npm/messenger.d.ts +37 -0
- package/dist/npm/messenger.js +54 -0
- package/dist/npm/module.js +10 -0
- package/dist/npm/notifications.d.ts +94 -0
- package/dist/npm/notifications.js +495 -0
- package/dist/npm/ping.d.ts +21 -0
- package/dist/npm/ping.js +60 -0
- package/dist/npm/polyfills.d.ts +0 -0
- package/dist/npm/polyfills.js +6 -0
- package/dist/npm/server-monitor.d.ts +16 -0
- package/dist/npm/server-monitor.js +44 -0
- package/dist/npm/stanza-definitions/webrtc-signaling.d.ts +29 -0
- package/dist/npm/stanza-definitions/webrtc-signaling.js +113 -0
- package/dist/npm/stanza-definitions/xep0051.d.ts +19 -0
- package/dist/npm/stanza-definitions/xep0051.js +28 -0
- package/dist/npm/stats-formatter.d.ts +6 -0
- package/dist/npm/stats-formatter.js +63 -0
- package/dist/npm/types/genesys-cloud-media-session.d.ts +64 -0
- package/dist/npm/types/genesys-cloud-media-session.js +353 -0
- package/dist/npm/types/interfaces.d.ts +310 -0
- package/dist/npm/types/interfaces.js +22 -0
- package/dist/npm/types/libs.d.ts +32 -0
- package/dist/npm/types/libs.js +1 -0
- package/dist/npm/types/media-session.d.ts +76 -0
- package/dist/npm/types/media-session.js +2 -0
- package/dist/npm/types/named-agent.d.ts +13 -0
- package/dist/npm/types/named-agent.js +2 -0
- package/dist/npm/types/offline-error.d.ts +2 -0
- package/dist/npm/types/offline-error.js +5 -0
- package/dist/npm/types/sasl-error.d.ts +8 -0
- package/dist/npm/types/sasl-error.js +12 -0
- package/dist/npm/types/stanza-media-session.d.ts +56 -0
- package/dist/npm/types/stanza-media-session.js +255 -0
- package/dist/npm/types/streaming-client-extension.d.ts +14 -0
- package/dist/npm/types/streaming-client-extension.js +2 -0
- package/dist/npm/types/timeout-error.d.ts +3 -0
- package/dist/npm/types/timeout-error.js +9 -0
- package/dist/npm/types/user-cancelled-error.d.ts +2 -0
- package/dist/npm/types/user-cancelled-error.js +5 -0
- package/dist/npm/utils.d.ts +34 -0
- package/dist/npm/utils.js +207 -0
- package/dist/npm/webrtc.d.ts +126 -0
- package/dist/npm/webrtc.js +893 -0
- package/dist/streaming-client.browser.js +2 -0
- package/dist/streaming-client.browser.js.LICENSE.txt +12 -0
- package/dist/v20/streaming-client.browser.js +2 -0
- package/dist/v20/streaming-client.browser.js.LICENSE.txt +12 -0
- package/dist/v20.1.0/streaming-client.browser.js +2 -0
- package/dist/v20.1.0/streaming-client.browser.js.LICENSE.txt +12 -0
- package/package.json +132 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StanzaMediaSession = void 0;
|
|
7
|
+
/* istanbul ignore file */
|
|
8
|
+
const events_1 = __importDefault(require("events"));
|
|
9
|
+
const jingle_1 = require("stanza/jingle");
|
|
10
|
+
const Constants_1 = require("stanza/Constants");
|
|
11
|
+
const webrtc_stats_gatherer_1 = __importDefault(require("webrtc-stats-gatherer"));
|
|
12
|
+
const interfaces_1 = require("./interfaces");
|
|
13
|
+
const loggingOverrides = {
|
|
14
|
+
'Discovered new ICE candidate': { skipMessage: true }
|
|
15
|
+
};
|
|
16
|
+
// because we are trying to unify the interfaces between stanza and genesys media session
|
|
17
|
+
// we kind of have to hack the stanza one so it works with typescript
|
|
18
|
+
class StanzaMediaSession extends jingle_1.MediaSession {
|
|
19
|
+
constructor(params) {
|
|
20
|
+
super(params.options);
|
|
21
|
+
this.iceCandidatesDiscovered = 0;
|
|
22
|
+
this.iceCandidatesReceivedFromPeer = 0;
|
|
23
|
+
this.reinvite = false;
|
|
24
|
+
this.logger = params.logger;
|
|
25
|
+
this.conversationId = params.conversationId;
|
|
26
|
+
this.peerConnection = this.pc;
|
|
27
|
+
this.fromUserId = params.fromUserId;
|
|
28
|
+
this.meetingId = params.meetingId;
|
|
29
|
+
this.id = this.sid;
|
|
30
|
+
this.originalRoomJid = params.originalRoomJid;
|
|
31
|
+
this.sessionType = interfaces_1.SessionTypes[params.sessionType];
|
|
32
|
+
this.ignoreHostCandidatesFromRemote = !!params.ignoreHostCandidatesFromRemote;
|
|
33
|
+
this.allowIPv6 = !!params.allowIPv6;
|
|
34
|
+
this.allowTCP = !!params.allowTCP;
|
|
35
|
+
this.privAnswerMode = params.privAnswerMode;
|
|
36
|
+
// babel does not like the typescript recipe for multiple extends so we are hacking this one
|
|
37
|
+
// referencing https://github.com/babel/babel/issues/798
|
|
38
|
+
const eventEmitter = new events_1.default();
|
|
39
|
+
Object.keys(eventEmitter.__proto__).forEach((name) => {
|
|
40
|
+
this[name] = eventEmitter[name];
|
|
41
|
+
});
|
|
42
|
+
if (!params.optOutOfWebrtcStatsTelemetry) {
|
|
43
|
+
this.setupStatsGatherer();
|
|
44
|
+
}
|
|
45
|
+
this.pc.addEventListener('connectionstatechange', this.onConnectionStateChange.bind(this));
|
|
46
|
+
this.pc.addEventListener('icecandidateerror', this.onIceCandidateError.bind(this));
|
|
47
|
+
}
|
|
48
|
+
getLogDetails() {
|
|
49
|
+
return {
|
|
50
|
+
conversationId: this.conversationId,
|
|
51
|
+
sessionId: this.id,
|
|
52
|
+
sessionType: this.sessionType
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async onTransportInfo(changes, cb) {
|
|
56
|
+
var _a, _b;
|
|
57
|
+
const transport = (_a = changes.contents) === null || _a === void 0 ? void 0 : _a[0].transport;
|
|
58
|
+
const candidates = transport === null || transport === void 0 ? void 0 : transport.candidates;
|
|
59
|
+
if (this.ignoreHostCandidatesFromRemote) {
|
|
60
|
+
const nonHostCandidates = candidates === null || candidates === void 0 ? void 0 : candidates.filter(candidate => candidate.type !== 'host');
|
|
61
|
+
if ((nonHostCandidates === null || nonHostCandidates === void 0 ? void 0 : nonHostCandidates.length) !== ((_b = transport === null || transport === void 0 ? void 0 : transport.candidates) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
62
|
+
this._log('info', 'Ignoring remote host ice candidates');
|
|
63
|
+
transport.candidates = nonHostCandidates;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (candidates) {
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
this._log('info', 'Received candidate from peer', { candidateType: candidate.type });
|
|
69
|
+
this.iceCandidatesReceivedFromPeer++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return super.onTransportInfo(changes, cb);
|
|
73
|
+
}
|
|
74
|
+
_log(level, message, ...data) {
|
|
75
|
+
const redactionInfo = loggingOverrides[message];
|
|
76
|
+
if (redactionInfo === null || redactionInfo === void 0 ? void 0 : redactionInfo.skipMessage) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let details;
|
|
80
|
+
if (data.length) {
|
|
81
|
+
const firstItem = data[0];
|
|
82
|
+
// if first item is an object, merge the details, else wrap them
|
|
83
|
+
if (typeof firstItem === 'object' &&
|
|
84
|
+
!Array.isArray(firstItem) &&
|
|
85
|
+
firstItem !== null) {
|
|
86
|
+
Object.assign(data[0], this.getLogDetails());
|
|
87
|
+
details = data;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
details = { ...this.getLogDetails(), data: firstItem };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
details = [...data, this.getLogDetails()];
|
|
95
|
+
}
|
|
96
|
+
this.logger[level](message, details);
|
|
97
|
+
}
|
|
98
|
+
async end(reason = 'success', silent = false) {
|
|
99
|
+
this.state = 'ended';
|
|
100
|
+
this.processingQueue.kill();
|
|
101
|
+
if (typeof reason === 'string') {
|
|
102
|
+
reason = { condition: reason };
|
|
103
|
+
}
|
|
104
|
+
if (!silent) {
|
|
105
|
+
this._log('info', 'sending jingle session-terminate');
|
|
106
|
+
this.send('session-terminate', { reason });
|
|
107
|
+
}
|
|
108
|
+
// After sending session-terminate, wait for the peer connection to die -> if it doesn't, we will manually close it.
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
if (this.pc.connectionState === 'connected' || this.pc.connectionState === 'connecting') {
|
|
111
|
+
this.pc.close();
|
|
112
|
+
}
|
|
113
|
+
}, 2000);
|
|
114
|
+
this.parent.emit('terminated', this, reason);
|
|
115
|
+
this.parent.forgetSession(this);
|
|
116
|
+
}
|
|
117
|
+
setupStatsGatherer() {
|
|
118
|
+
this.statsGatherer = new webrtc_stats_gatherer_1.default(this.pc);
|
|
119
|
+
this.statsGatherer.on('stats', this.emit.bind(this, 'stats'));
|
|
120
|
+
}
|
|
121
|
+
onIceStateChange() {
|
|
122
|
+
const iceState = this.pc.iceConnectionState;
|
|
123
|
+
const sessionId = this.id;
|
|
124
|
+
const conversationId = this.conversationId;
|
|
125
|
+
const sessionType = this.sessionType;
|
|
126
|
+
this._log('info', 'ICE state changed: ', { iceState, sessionId, conversationId });
|
|
127
|
+
if (iceState === 'disconnected') {
|
|
128
|
+
// this means we actually connected at one point
|
|
129
|
+
if (this.pc.signalingState === 'stable') {
|
|
130
|
+
this.interruptionStart = new Date();
|
|
131
|
+
this._log('info', 'Connection state is interrupted', { sessionId, conversationId, sessionType });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (iceState === 'connected') {
|
|
135
|
+
this._log('info', 'sending session-info: active');
|
|
136
|
+
this.send(Constants_1.JingleAction.SessionInfo, {
|
|
137
|
+
info: {
|
|
138
|
+
infoType: Constants_1.JINGLE_INFO_ACTIVE
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
this._setupDataChannel();
|
|
142
|
+
}
|
|
143
|
+
else if (iceState === 'failed') {
|
|
144
|
+
this._log('info', 'ICE connection failed', {
|
|
145
|
+
candidatesDiscovered: this.iceCandidatesDiscovered,
|
|
146
|
+
candidatesReceivedFromPeer: this.iceCandidatesReceivedFromPeer
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
super.onIceStateChange();
|
|
150
|
+
}
|
|
151
|
+
onConnectionStateChange() {
|
|
152
|
+
const connectionState = this.pc.connectionState;
|
|
153
|
+
const sessionId = this.id;
|
|
154
|
+
const conversationId = this.conversationId;
|
|
155
|
+
const sessionType = this.sessionType;
|
|
156
|
+
this._log('info', 'Connection state changed: ', { connectionState, sessionId, conversationId, sessionType });
|
|
157
|
+
if (this.interruptionStart) {
|
|
158
|
+
if (connectionState === 'connected') {
|
|
159
|
+
const diff = new Date().getTime() - this.interruptionStart.getTime();
|
|
160
|
+
this._log('info', 'Connection was interrupted but was successfully recovered/connected', { sessionId, conversationId, sessionType, timeToRecover: diff });
|
|
161
|
+
this.interruptionStart = undefined;
|
|
162
|
+
}
|
|
163
|
+
else if (connectionState === 'failed') {
|
|
164
|
+
this._log('info', 'Connection was interrupted and failed to recover', { sessionId, conversationId, sessionType });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
onIceCandidateError(ev) {
|
|
169
|
+
const event = ev;
|
|
170
|
+
this._log('error', 'IceCandidateError', {
|
|
171
|
+
errorCode: event.errorCode,
|
|
172
|
+
errorText: event.errorText,
|
|
173
|
+
url: event.url
|
|
174
|
+
});
|
|
175
|
+
console.error('IceCandidateError', event);
|
|
176
|
+
}
|
|
177
|
+
onIceCandidate(e) {
|
|
178
|
+
if (e.candidate) {
|
|
179
|
+
if (!this.allowTCP && e.candidate.protocol === 'tcp') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!this.allowIPv6) {
|
|
183
|
+
const addressRegex = /.+udp [^ ]+ ([^ ]+).*typ host/;
|
|
184
|
+
const matches = addressRegex.exec(e.candidate.candidate);
|
|
185
|
+
const ipv4Regex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
|
|
186
|
+
if (matches && !matches[1].match(ipv4Regex)) {
|
|
187
|
+
this._log('debug', 'Filtering out IPv6 candidate', e.candidate.candidate);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// this has too much information and can only be logged locally (debug)
|
|
192
|
+
this._log('debug', 'Processing ice candidate', e.candidate.candidate);
|
|
193
|
+
// this one is info level so it can go to the server
|
|
194
|
+
this._log('info', 'Discovered ice candidate to send to peer', { type: e.candidate.type });
|
|
195
|
+
this.iceCandidatesDiscovered++;
|
|
196
|
+
}
|
|
197
|
+
return super.onIceCandidate(e);
|
|
198
|
+
}
|
|
199
|
+
onIceEndOfCandidates() {
|
|
200
|
+
super.onIceEndOfCandidates();
|
|
201
|
+
this.emit('endOfCandidates');
|
|
202
|
+
}
|
|
203
|
+
addTrack(track, stream) {
|
|
204
|
+
if (track.kind === 'audio') {
|
|
205
|
+
this.includesAudio = true;
|
|
206
|
+
}
|
|
207
|
+
if (track.kind === 'video') {
|
|
208
|
+
this.includesVideo = true;
|
|
209
|
+
}
|
|
210
|
+
return this.processLocal('addtrack', async () => {
|
|
211
|
+
// find an available sender with the correct type
|
|
212
|
+
const availableTransceiver = this.pc.getTransceivers().find((transceiver) => {
|
|
213
|
+
var _a;
|
|
214
|
+
return !transceiver.sender.track && ((_a = transceiver.receiver.track) === null || _a === void 0 ? void 0 : _a.kind) === track.kind;
|
|
215
|
+
});
|
|
216
|
+
if (availableTransceiver) {
|
|
217
|
+
return availableTransceiver.sender.replaceTrack(track);
|
|
218
|
+
}
|
|
219
|
+
this.pc.addTrack(track, stream);
|
|
220
|
+
return;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
_setupDataChannel() {
|
|
224
|
+
var _a;
|
|
225
|
+
// this shouldn't happen, but we shouldn't set the datachannel up more than once
|
|
226
|
+
if (this.dataChannel) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// do nothing if a datachannel wasn't offered
|
|
230
|
+
if ((_a = this.pc.localDescription) === null || _a === void 0 ? void 0 : _a.sdp.includes('webrtc-datachannel')) {
|
|
231
|
+
this._log('info', 'creating data channel');
|
|
232
|
+
this.dataChannel = this.pc.createDataChannel('videoConferenceControl');
|
|
233
|
+
this.dataChannel.addEventListener('open', () => {
|
|
234
|
+
this._log('info', 'data channel opened');
|
|
235
|
+
});
|
|
236
|
+
this.dataChannel.addEventListener('message', this._handleDataChannelMessage.bind(this));
|
|
237
|
+
this.dataChannel.addEventListener('close', () => {
|
|
238
|
+
this._log('info', 'closing data channel');
|
|
239
|
+
});
|
|
240
|
+
this.dataChannel.addEventListener('error', (error) => {
|
|
241
|
+
this._log('error', 'Error occurred with the data channel', error);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
_handleDataChannelMessage(event) {
|
|
246
|
+
try {
|
|
247
|
+
const message = JSON.parse(event.data);
|
|
248
|
+
this.emit('dataChannelMessage', message);
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
this._log('error', 'Failed to parse data channel message', { error: e });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
exports.StanzaMediaSession = StanzaMediaSession;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TokenBucket } from 'limiter';
|
|
2
|
+
import { IQ, Message } from 'stanza/protocol';
|
|
3
|
+
import { NamedAgent } from './named-agent';
|
|
4
|
+
export interface StreamingClientExtension {
|
|
5
|
+
handleIq?: (iq: IQ) => void;
|
|
6
|
+
handleMessage?: (message: Message) => void;
|
|
7
|
+
tokenBucket?: TokenBucket;
|
|
8
|
+
on?: (eventName: string, ...args: any[]) => void;
|
|
9
|
+
expose?: {
|
|
10
|
+
[fnName: string]: any;
|
|
11
|
+
};
|
|
12
|
+
configureNewStanzaInstance?: (stanzaInstance: NamedAgent) => Promise<any>;
|
|
13
|
+
handleStanzaInstanceChange: (stanzaInstance: NamedAgent) => void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StreamingClientErrorTypes, StreamingSubscriptionErrorDetails } from './types/interfaces';
|
|
2
|
+
export declare class StreamingClientError extends Error {
|
|
3
|
+
type: StreamingClientErrorTypes;
|
|
4
|
+
details?: unknown;
|
|
5
|
+
constructor(type: StreamingClientErrorTypes | null, messageOrError: string | Error, details?: unknown);
|
|
6
|
+
}
|
|
7
|
+
export declare class StreamingSubscriptionError extends Error {
|
|
8
|
+
readonly topic?: string | undefined;
|
|
9
|
+
readonly operation?: "subscribe" | "unsubscribe" | undefined;
|
|
10
|
+
name: string;
|
|
11
|
+
missingPermissions?: string[];
|
|
12
|
+
constructor(message: string, topic?: string | undefined, operation?: "subscribe" | "unsubscribe" | undefined, details?: StreamingSubscriptionErrorDetails);
|
|
13
|
+
}
|
|
14
|
+
export declare function timeoutPromise(fn: Function, timeoutMs: number, msg: string, details?: any): Promise<any>;
|
|
15
|
+
export declare function delay(ms: number): Promise<void>;
|
|
16
|
+
export declare function splitIntoIndividualTopics(topicString: string): string[];
|
|
17
|
+
export declare const isAcdJid: (jid: string) => boolean;
|
|
18
|
+
export declare const isScreenRecordingJid: (jid: string) => boolean;
|
|
19
|
+
export declare const isLiveScreenMonitoringJid: (jid: string) => boolean;
|
|
20
|
+
export declare const isSoftphoneJid: (jid: string) => boolean;
|
|
21
|
+
export declare const isVideoJid: (jid: string) => boolean;
|
|
22
|
+
export declare type RetryPromise<T = any> = {
|
|
23
|
+
promise: Promise<T>;
|
|
24
|
+
cancel: (reason?: string | Error) => void;
|
|
25
|
+
complete: (value?: T) => void;
|
|
26
|
+
hasCompleted: () => boolean;
|
|
27
|
+
_id: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function retryPromise<T = any>(promiseFn: () => Promise<T>, retryFn: (error?: Error | any) => boolean | number, retryInterval?: number, logger?: any): RetryPromise<T>;
|
|
30
|
+
export declare const parseJwt: (token: string) => any;
|
|
31
|
+
export declare function calculatePayloadSize(trace: any): number;
|
|
32
|
+
export declare function getUfragFromSdp(sdp: string | undefined): string | null;
|
|
33
|
+
export declare function getIcePwdFromSdp(sdp: string | undefined): string | null;
|
|
34
|
+
export declare function iceIsDifferent(sdp1: string | undefined, sdp2: string | undefined): boolean;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.iceIsDifferent = exports.getIcePwdFromSdp = exports.getUfragFromSdp = exports.calculatePayloadSize = exports.parseJwt = exports.retryPromise = exports.isVideoJid = exports.isSoftphoneJid = exports.isLiveScreenMonitoringJid = exports.isScreenRecordingJid = exports.isAcdJid = exports.splitIntoIndividualTopics = exports.delay = exports.timeoutPromise = exports.StreamingSubscriptionError = exports.StreamingClientError = void 0;
|
|
4
|
+
const timeout_error_1 = require("./types/timeout-error");
|
|
5
|
+
const interfaces_1 = require("./types/interfaces");
|
|
6
|
+
class StreamingClientError extends Error {
|
|
7
|
+
constructor(type, messageOrError, details) {
|
|
8
|
+
let message;
|
|
9
|
+
if (messageOrError instanceof Error) {
|
|
10
|
+
message = messageOrError.message;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
message = messageOrError;
|
|
14
|
+
}
|
|
15
|
+
super(message);
|
|
16
|
+
if (messageOrError instanceof Error) {
|
|
17
|
+
this.name = messageOrError.name;
|
|
18
|
+
}
|
|
19
|
+
this.type = type !== null && type !== void 0 ? type : interfaces_1.StreamingClientErrorTypes.generic;
|
|
20
|
+
this.details = details;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.StreamingClientError = StreamingClientError;
|
|
24
|
+
class StreamingSubscriptionError extends Error {
|
|
25
|
+
constructor(message, topic, operation, details) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.topic = topic;
|
|
28
|
+
this.operation = operation;
|
|
29
|
+
this.name = 'StreamingSubscriptionError';
|
|
30
|
+
this.missingPermissions = details === null || details === void 0 ? void 0 : details.missingPermissions;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.StreamingSubscriptionError = StreamingSubscriptionError;
|
|
34
|
+
/* istanbul ignore next */
|
|
35
|
+
function timeoutPromise(fn, timeoutMs, msg, details) {
|
|
36
|
+
return new Promise(function (resolve, reject) {
|
|
37
|
+
const timeout = setTimeout(function () {
|
|
38
|
+
const err = new timeout_error_1.TimeoutError(`Timeout: ${msg}`);
|
|
39
|
+
err.details = details;
|
|
40
|
+
reject(err);
|
|
41
|
+
}, timeoutMs);
|
|
42
|
+
const done = function (resolvedValue) {
|
|
43
|
+
clearTimeout(timeout);
|
|
44
|
+
resolve(resolvedValue);
|
|
45
|
+
};
|
|
46
|
+
fn(done, reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
exports.timeoutPromise = timeoutPromise;
|
|
50
|
+
function delay(ms) {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
setTimeout(resolve, ms);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
exports.delay = delay;
|
|
56
|
+
function splitIntoIndividualTopics(topicString) {
|
|
57
|
+
const topics = [];
|
|
58
|
+
if (topicString.includes('?')) {
|
|
59
|
+
const split = topicString.split('?');
|
|
60
|
+
const prefix = split[0];
|
|
61
|
+
const postfixes = split[1] && split[1].split('&');
|
|
62
|
+
if (postfixes && postfixes.length) {
|
|
63
|
+
postfixes.forEach(postfix => {
|
|
64
|
+
topics.push(`${prefix}.${postfix}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
topics.push(topicString);
|
|
70
|
+
}
|
|
71
|
+
return topics;
|
|
72
|
+
}
|
|
73
|
+
exports.splitIntoIndividualTopics = splitIntoIndividualTopics;
|
|
74
|
+
const isAcdJid = function (jid) {
|
|
75
|
+
return jid.startsWith('acd-') && !(0, exports.isSoftphoneJid)(jid);
|
|
76
|
+
};
|
|
77
|
+
exports.isAcdJid = isAcdJid;
|
|
78
|
+
const isScreenRecordingJid = function (jid) {
|
|
79
|
+
return jid.startsWith('screenrecording-') && !(0, exports.isSoftphoneJid)(jid);
|
|
80
|
+
};
|
|
81
|
+
exports.isScreenRecordingJid = isScreenRecordingJid;
|
|
82
|
+
const isLiveScreenMonitoringJid = function (jid) {
|
|
83
|
+
return jid.startsWith('livemonitor-') && !(0, exports.isSoftphoneJid)(jid);
|
|
84
|
+
};
|
|
85
|
+
exports.isLiveScreenMonitoringJid = isLiveScreenMonitoringJid;
|
|
86
|
+
const isSoftphoneJid = function (jid) {
|
|
87
|
+
if (!jid) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return !!jid.match(/.*@.*gjoll.*/i);
|
|
91
|
+
};
|
|
92
|
+
exports.isSoftphoneJid = isSoftphoneJid;
|
|
93
|
+
const isVideoJid = function (jid) {
|
|
94
|
+
return !!(jid && jid.match(/@conference/) && !(0, exports.isAcdJid)(jid));
|
|
95
|
+
};
|
|
96
|
+
exports.isVideoJid = isVideoJid;
|
|
97
|
+
function retryPromise(promiseFn,
|
|
98
|
+
// if a number is returned, that's how long we will wait before retrying (in milliseconds)
|
|
99
|
+
retryFn, retryInterval = 15000, logger = console) {
|
|
100
|
+
let timeout;
|
|
101
|
+
let cancel;
|
|
102
|
+
let complete;
|
|
103
|
+
let tryPromiseFn;
|
|
104
|
+
let _hasCompleted = false;
|
|
105
|
+
const promise = new Promise((resolve, reject) => {
|
|
106
|
+
tryPromiseFn = async () => {
|
|
107
|
+
try {
|
|
108
|
+
const val = await promiseFn();
|
|
109
|
+
complete(val);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
let timeToWait = retryInterval;
|
|
113
|
+
const retryValue = retryFn(error);
|
|
114
|
+
if (Number.isInteger(retryValue)) {
|
|
115
|
+
timeToWait = retryValue;
|
|
116
|
+
}
|
|
117
|
+
if (retryValue !== false) {
|
|
118
|
+
logger.debug('Retrying promise', error);
|
|
119
|
+
timeout = setTimeout(tryPromiseFn, timeToWait);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
cancel(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
complete = (value) => {
|
|
127
|
+
clearTimeout(timeout);
|
|
128
|
+
_hasCompleted = true;
|
|
129
|
+
resolve(value);
|
|
130
|
+
};
|
|
131
|
+
cancel = (reason) => {
|
|
132
|
+
clearTimeout(timeout);
|
|
133
|
+
_hasCompleted = true;
|
|
134
|
+
reject(reason);
|
|
135
|
+
};
|
|
136
|
+
tryPromiseFn();
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
promise,
|
|
140
|
+
cancel,
|
|
141
|
+
complete,
|
|
142
|
+
_id: globalThis.crypto.randomUUID(),
|
|
143
|
+
hasCompleted: () => _hasCompleted
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
exports.retryPromise = retryPromise;
|
|
147
|
+
// from https://stackoverflow.com/questions/38552003/how-to-decode-jwt-token-in-javascript
|
|
148
|
+
const parseJwt = (token) => {
|
|
149
|
+
const base64Url = token.split('.')[1];
|
|
150
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
151
|
+
const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
|
|
152
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
153
|
+
}).join(''));
|
|
154
|
+
return JSON.parse(jsonPayload);
|
|
155
|
+
};
|
|
156
|
+
exports.parseJwt = parseJwt;
|
|
157
|
+
function calculatePayloadSize(trace) {
|
|
158
|
+
const str = JSON.stringify(trace);
|
|
159
|
+
// http://stackoverflow.com/questions/5515869/string-length-in-bytes-in-javascript
|
|
160
|
+
// Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
|
|
161
|
+
const m = encodeURIComponent(str).match(/%[89ABab]/g);
|
|
162
|
+
return str.length + (m ? m.length : 0);
|
|
163
|
+
}
|
|
164
|
+
exports.calculatePayloadSize = calculatePayloadSize;
|
|
165
|
+
function getUfragFromSdp(sdp) {
|
|
166
|
+
if (!sdp) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const regex = /a=ice-ufrag:(\S+)/;
|
|
170
|
+
const match = sdp.match(regex);
|
|
171
|
+
return match ? match[1] : null;
|
|
172
|
+
}
|
|
173
|
+
exports.getUfragFromSdp = getUfragFromSdp;
|
|
174
|
+
function getIcePwdFromSdp(sdp) {
|
|
175
|
+
if (!sdp) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const regex = /a=ice-pwd:(\S+)/;
|
|
179
|
+
const match = sdp.match(regex);
|
|
180
|
+
return match ? match[1] : null;
|
|
181
|
+
}
|
|
182
|
+
exports.getIcePwdFromSdp = getIcePwdFromSdp;
|
|
183
|
+
function iceIsDifferent(sdp1, sdp2) {
|
|
184
|
+
return getUfragFromSdp(sdp1) !== getUfragFromSdp(sdp2) || getIcePwdFromSdp(sdp1) !== getIcePwdFromSdp(sdp2);
|
|
185
|
+
}
|
|
186
|
+
exports.iceIsDifferent = iceIsDifferent;
|
|
187
|
+
// unsed, but handy. no test coverage until used
|
|
188
|
+
// function mergeOptions (destination, provided) {
|
|
189
|
+
// for (var key in provided) {
|
|
190
|
+
// let value = provided[key];
|
|
191
|
+
// if (value instanceof Object) {
|
|
192
|
+
// if (!destination[key]) {
|
|
193
|
+
// destination[key] = {};
|
|
194
|
+
// }
|
|
195
|
+
// mergeOptions(destination[key], value);
|
|
196
|
+
// } else {
|
|
197
|
+
// destination[key] = provided[key];
|
|
198
|
+
// }
|
|
199
|
+
// }
|
|
200
|
+
//
|
|
201
|
+
// return destination;
|
|
202
|
+
// }
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { IQ } from 'stanza/protocol';
|
|
4
|
+
import { LRUCache } from 'lru-cache';
|
|
5
|
+
import { SessionManager } from 'stanza/jingle';
|
|
6
|
+
import { SessionOpts } from 'stanza/jingle/Session';
|
|
7
|
+
import { Client } from './client';
|
|
8
|
+
import { ExtendedRTCIceServer, IClientOptions, SessionTypes, IPendingSession, StreamingClientExtension, NRProxyStat, InsightAction } from './types/interfaces';
|
|
9
|
+
import { NamedAgent } from './types/named-agent';
|
|
10
|
+
import { StanzaMediaSession } from './types/stanza-media-session';
|
|
11
|
+
import { IMediaSession } from './types/media-session';
|
|
12
|
+
import Logger from 'genesys-cloud-client-logger';
|
|
13
|
+
export interface InitRtcSessionOptions {
|
|
14
|
+
stream?: MediaStream;
|
|
15
|
+
provideVideo?: boolean;
|
|
16
|
+
provideAudio?: boolean;
|
|
17
|
+
mediaPurpose?: string;
|
|
18
|
+
jid?: string;
|
|
19
|
+
conversationId?: string;
|
|
20
|
+
sourceCommunicationId?: string;
|
|
21
|
+
videoGuest?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare class WebrtcExtension extends EventEmitter implements StreamingClientExtension {
|
|
24
|
+
client: Client;
|
|
25
|
+
ignoredSessions: LRUCache<string, boolean, unknown>;
|
|
26
|
+
private earlyIceCandidates;
|
|
27
|
+
logger: Logger;
|
|
28
|
+
pendingSessions: {
|
|
29
|
+
[sessionId: string]: IPendingSession;
|
|
30
|
+
};
|
|
31
|
+
config: {
|
|
32
|
+
allowIPv6: boolean;
|
|
33
|
+
optOutOfWebrtcStatsTelemetry?: boolean;
|
|
34
|
+
};
|
|
35
|
+
private statsArr;
|
|
36
|
+
private throttleSendStatsInterval;
|
|
37
|
+
private currentMaxStatSize;
|
|
38
|
+
private statsSizeDecreaseAmount;
|
|
39
|
+
private statBuffer;
|
|
40
|
+
private throttledSendStats;
|
|
41
|
+
private discoRetries;
|
|
42
|
+
private refreshIceServersRetryPromise;
|
|
43
|
+
private refreshIceServersTimer;
|
|
44
|
+
private iceServers;
|
|
45
|
+
private stanzaInstance?;
|
|
46
|
+
private stanzaSessions;
|
|
47
|
+
private webrtcSessions;
|
|
48
|
+
private reinviteCache;
|
|
49
|
+
private sessionsMap;
|
|
50
|
+
get jid(): string | undefined;
|
|
51
|
+
constructor(client: Client, clientOptions: IClientOptions);
|
|
52
|
+
private onOnlineStatusChange;
|
|
53
|
+
handleStanzaInstanceChange(stanzaInstance: NamedAgent): Promise<void>;
|
|
54
|
+
configureNewStanzaInstance(stanzaInstance: NamedAgent): Promise<void>;
|
|
55
|
+
private configureStanzaIceServers;
|
|
56
|
+
private handleGenesysOffer;
|
|
57
|
+
private applyEarlyIceCandidates;
|
|
58
|
+
private handleGenesysRenegotiate;
|
|
59
|
+
private handleGenesysIceCandidate;
|
|
60
|
+
private handleGenesysTerminate;
|
|
61
|
+
private getSessionById;
|
|
62
|
+
sendIq(iq: IQ): Promise<any>;
|
|
63
|
+
handleMessage(msg: any): Promise<void>;
|
|
64
|
+
handleGenesysWebrtcStanza(iq: IQ): Promise<boolean | void>;
|
|
65
|
+
prepareSession(options: SessionOpts): StanzaMediaSession | undefined;
|
|
66
|
+
proxyStatsForSession(session: IMediaSession): void;
|
|
67
|
+
addStatToQueue<T extends {
|
|
68
|
+
_eventType: string;
|
|
69
|
+
}>(stat: InsightAction<T>): void;
|
|
70
|
+
getLogDetailsForPendingSessionId(sessionId: string): {
|
|
71
|
+
conversationId?: string;
|
|
72
|
+
sessionId: string;
|
|
73
|
+
sessionType?: SessionTypes;
|
|
74
|
+
};
|
|
75
|
+
sendStatsImmediately(): void;
|
|
76
|
+
sendStats(): Promise<void>;
|
|
77
|
+
addEventListeners(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Stanza Handlers
|
|
80
|
+
*/
|
|
81
|
+
private handlePropose;
|
|
82
|
+
private handleRetract;
|
|
83
|
+
/**
|
|
84
|
+
* Inform the client that another client has already taken care of the pendingSession
|
|
85
|
+
*/
|
|
86
|
+
private handledIncomingRtcSession;
|
|
87
|
+
/**
|
|
88
|
+
* Exposed Api
|
|
89
|
+
*/
|
|
90
|
+
initiateRtcSession(opts: InitRtcSessionOptions): Promise<any>;
|
|
91
|
+
acceptRtcSession(sessionId: string): Promise<void>;
|
|
92
|
+
rejectRtcSession(sessionId: string, ignore?: boolean): Promise<void>;
|
|
93
|
+
rtcSessionAccepted(sessionId: string): Promise<void>;
|
|
94
|
+
notifyScreenShareStart(session: IMediaSession): Promise<void>;
|
|
95
|
+
notifyScreenShareStop(session: IMediaSession): Promise<void>;
|
|
96
|
+
cancelRtcSession(sessionId: string): Promise<void>;
|
|
97
|
+
refreshIceServers(): Promise<ExtendedRTCIceServer[]>;
|
|
98
|
+
_refreshIceServers(stanzaInstance?: NamedAgent): Promise<ExtendedRTCIceServer[]>;
|
|
99
|
+
setIceServers(iceServers: any[], stanzaInstance: NamedAgent): void;
|
|
100
|
+
getIceTransportPolicy(): RTCIceTransportPolicy | undefined;
|
|
101
|
+
setIceTransportPolicy(policy: 'relay' | 'all', stanzaInstance: NamedAgent): void;
|
|
102
|
+
getSessionTypeByJid(jid: string): SessionTypes;
|
|
103
|
+
getSessionManager(): SessionManager | undefined;
|
|
104
|
+
getAllSessions(): IMediaSession[];
|
|
105
|
+
proxyNRStat(stat: NRProxyStat): void;
|
|
106
|
+
get expose(): WebrtcExtensionAPI;
|
|
107
|
+
}
|
|
108
|
+
export interface WebrtcExtensionAPI {
|
|
109
|
+
on: (event: string, handler: (...args: any) => void) => void;
|
|
110
|
+
once: (event: string, handler: (...args: any) => void) => void;
|
|
111
|
+
off: (event: string, handler: (...args: any) => void) => void;
|
|
112
|
+
removeAllListeners(event?: string | symbol): void;
|
|
113
|
+
removeListener(event: string | symbol, listener: (...args: any[]) => void): void;
|
|
114
|
+
refreshIceServers(): Promise<any[]>;
|
|
115
|
+
acceptRtcSession(sessionId: string): void;
|
|
116
|
+
rejectRtcSession(sessionId: string, ignore?: boolean): void;
|
|
117
|
+
cancelRtcSession(sessionId: string): void;
|
|
118
|
+
rtcSessionAccepted(sessionId: string): void;
|
|
119
|
+
initiateRtcSession(opts: InitRtcSessionOptions): Promise<void>;
|
|
120
|
+
notifyScreenShareStart(session: IMediaSession): void;
|
|
121
|
+
notifyScreenShareStop(session: IMediaSession): void;
|
|
122
|
+
getSessionTypeByJid(jid: string): SessionTypes;
|
|
123
|
+
getSessionManager: () => SessionManager | undefined;
|
|
124
|
+
getAllSessions: () => IMediaSession[];
|
|
125
|
+
proxyNRStat: (stat: NRProxyStat) => void;
|
|
126
|
+
}
|