@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,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.definitions = void 0;
|
|
4
|
+
const jxt_1 = require("stanza/jxt");
|
|
5
|
+
const Namespaces_1 = require("stanza/Namespaces");
|
|
6
|
+
const NS_JINGLE_SIGNALING = 'urn:xmpp:jingle-message:0';
|
|
7
|
+
const proposeDefinition = {
|
|
8
|
+
aliases: ['message.propose'],
|
|
9
|
+
element: 'propose',
|
|
10
|
+
fields: {
|
|
11
|
+
conversationId: (0, jxt_1.attribute)('inin-cid'),
|
|
12
|
+
persistentConversationId: (0, jxt_1.attribute)('inin-persistent-cid'),
|
|
13
|
+
sdpOverXmpp: (0, jxt_1.booleanAttribute)('inin-sdp-over-xmpp'),
|
|
14
|
+
privAnswerMode: (0, jxt_1.attribute)('inin-priv-answer-mode'),
|
|
15
|
+
originalRoomJid: (0, jxt_1.attribute)('inin-ofrom'),
|
|
16
|
+
autoAnswer: (0, jxt_1.booleanAttribute)('inin-autoanswer'),
|
|
17
|
+
fromUserId: (0, jxt_1.attribute)('inin-user-id'),
|
|
18
|
+
sessionId: (0, jxt_1.attribute)('id'),
|
|
19
|
+
meetingId: (0, jxt_1.attribute)('inin-meeting-id')
|
|
20
|
+
},
|
|
21
|
+
namespace: NS_JINGLE_SIGNALING
|
|
22
|
+
};
|
|
23
|
+
const proceedDefinition = {
|
|
24
|
+
aliases: ['message.proceed'],
|
|
25
|
+
element: 'proceed',
|
|
26
|
+
fields: {
|
|
27
|
+
sessionId: (0, jxt_1.attribute)('id')
|
|
28
|
+
},
|
|
29
|
+
namespace: NS_JINGLE_SIGNALING
|
|
30
|
+
};
|
|
31
|
+
const sessionAcceptedDefinition = {
|
|
32
|
+
aliases: ['message.accept'],
|
|
33
|
+
element: 'accept',
|
|
34
|
+
fields: {
|
|
35
|
+
sessionId: (0, jxt_1.attribute)('id')
|
|
36
|
+
},
|
|
37
|
+
namespace: NS_JINGLE_SIGNALING
|
|
38
|
+
};
|
|
39
|
+
const sessionRejectedDefinition = {
|
|
40
|
+
aliases: ['message.reject'],
|
|
41
|
+
element: 'reject',
|
|
42
|
+
fields: {
|
|
43
|
+
sessionId: (0, jxt_1.attribute)('id')
|
|
44
|
+
},
|
|
45
|
+
namespace: NS_JINGLE_SIGNALING
|
|
46
|
+
};
|
|
47
|
+
const sessionRetractedDefinition = {
|
|
48
|
+
aliases: ['message.retract'],
|
|
49
|
+
element: 'retract',
|
|
50
|
+
fields: {
|
|
51
|
+
sessionId: (0, jxt_1.attribute)('id')
|
|
52
|
+
},
|
|
53
|
+
namespace: NS_JINGLE_SIGNALING
|
|
54
|
+
};
|
|
55
|
+
const screenStartDefinition = {
|
|
56
|
+
aliases: ['iq.jingle.screenstart'],
|
|
57
|
+
element: 'screen-start',
|
|
58
|
+
namespace: Namespaces_1.NS_JINGLE_RTP_INFO_1
|
|
59
|
+
};
|
|
60
|
+
const screenStopDefinition = {
|
|
61
|
+
aliases: ['iq.jingle.screenstop'],
|
|
62
|
+
element: 'screen-stop',
|
|
63
|
+
namespace: Namespaces_1.NS_JINGLE_RTP_INFO_1
|
|
64
|
+
};
|
|
65
|
+
// this allows parsing xml that looks something like this:
|
|
66
|
+
/*
|
|
67
|
+
<iq xmlns="jabber:client" [other stuff]>
|
|
68
|
+
<genesys-webrtc xmlns="genesys">{ "id": "whatver", "anyOtherFieldICareAbout": true }</genesys-webrtc>
|
|
69
|
+
</iq>
|
|
70
|
+
*/
|
|
71
|
+
const genesysWebrtc = {
|
|
72
|
+
path: 'iq',
|
|
73
|
+
namespace: Namespaces_1.NS_CLIENT,
|
|
74
|
+
element: 'iq',
|
|
75
|
+
fields: {
|
|
76
|
+
genesysWebrtc: (0, jxt_1.childJSON)('genesys', 'genesys-webrtc')
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const mediaMessage = {
|
|
80
|
+
path: 'message',
|
|
81
|
+
namespace: Namespaces_1.NS_CLIENT,
|
|
82
|
+
element: 'message',
|
|
83
|
+
fields: {
|
|
84
|
+
mediaMessage: (0, jxt_1.childJSON)('genesys', 'media-message')
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const upgradeMediaPresenceDefinition = {
|
|
88
|
+
aliases: ['presence.media'],
|
|
89
|
+
element: 'x',
|
|
90
|
+
fields: {
|
|
91
|
+
conversationId: (0, jxt_1.attribute)('conversationId'),
|
|
92
|
+
sourceCommunicationId: (0, jxt_1.attribute)('sourceCommunicationId'),
|
|
93
|
+
screenShare: (0, jxt_1.childAttribute)(null, 'mediastream', 'screenShare'),
|
|
94
|
+
video: (0, jxt_1.childAttribute)(null, 'mediastream', 'video'),
|
|
95
|
+
audio: (0, jxt_1.childAttribute)(null, 'mediastream', 'audio'),
|
|
96
|
+
listener: (0, jxt_1.childAttribute)(null, 'mediastream', 'listener'),
|
|
97
|
+
screenRecording: (0, jxt_1.childAttribute)(null, 'mediastream', 'screenRecording'),
|
|
98
|
+
videoGuest: (0, jxt_1.childAttribute)(null, 'mediastream', 'videoGuest')
|
|
99
|
+
},
|
|
100
|
+
namespace: 'orgspan:mediastream'
|
|
101
|
+
};
|
|
102
|
+
exports.definitions = [
|
|
103
|
+
proposeDefinition,
|
|
104
|
+
proceedDefinition,
|
|
105
|
+
sessionAcceptedDefinition,
|
|
106
|
+
sessionRejectedDefinition,
|
|
107
|
+
sessionRetractedDefinition,
|
|
108
|
+
screenStartDefinition,
|
|
109
|
+
screenStopDefinition,
|
|
110
|
+
upgradeMediaPresenceDefinition,
|
|
111
|
+
genesysWebrtc,
|
|
112
|
+
mediaMessage
|
|
113
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Stanzas } from 'stanza';
|
|
2
|
+
import { DefinitionOptions } from 'stanza/jxt';
|
|
3
|
+
declare module 'stanza' {
|
|
4
|
+
interface AgentEvents {
|
|
5
|
+
'iq:set:connectionTransfer': Stanzas.ReceivedIQ & {
|
|
6
|
+
query: ConnectionTransfer;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
declare module 'stanza/protocol' {
|
|
11
|
+
interface IQPayload {
|
|
12
|
+
query?: ConnectionTransfer;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export interface ConnectionTransfer {
|
|
16
|
+
domain?: string;
|
|
17
|
+
server?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const connectionTransfer: DefinitionOptions;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.connectionTransfer = void 0;
|
|
4
|
+
const jxt_1 = require("stanza/jxt");
|
|
5
|
+
// XEP-0051: Connection Transfer
|
|
6
|
+
// Source: https://xmpp.org/extensions/xep-0051.html
|
|
7
|
+
// Version: 0.2.1 (2022-03-08)
|
|
8
|
+
// ---------------------------
|
|
9
|
+
// We don't currently use domain or server
|
|
10
|
+
const NS_CONNECTION_TRANSFER = 'urn:xmpp:cxfr';
|
|
11
|
+
/*
|
|
12
|
+
<iq xmlns="jabber:client" id="<someId>" to="<toJID>" type="set">
|
|
13
|
+
<query xmlns="urn:xmpp:cxfr">
|
|
14
|
+
<domain>jabber.org</domain>
|
|
15
|
+
<server>123.123.123.122</server>
|
|
16
|
+
</query>
|
|
17
|
+
</iq>
|
|
18
|
+
*/
|
|
19
|
+
exports.connectionTransfer = {
|
|
20
|
+
path: 'iq.query',
|
|
21
|
+
aliases: ['iq.connectionTransfer'],
|
|
22
|
+
namespace: NS_CONNECTION_TRANSFER,
|
|
23
|
+
element: 'query',
|
|
24
|
+
fields: {
|
|
25
|
+
domain: (0, jxt_1.childText)(null, 'domain'),
|
|
26
|
+
server: (0, jxt_1.childText)(null, 'server')
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { StatsEvent } from 'webrtc-stats-gatherer';
|
|
2
|
+
import { FlatObject, InsightAction } from './types/interfaces';
|
|
3
|
+
export declare function formatStatsEvent(event: StatsEvent, extraDetails?: FlatObject): InsightAction<{
|
|
4
|
+
_eventType: string;
|
|
5
|
+
} & FlatObject>;
|
|
6
|
+
export declare function deepFlatten(obj: any, prefix?: string): any;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepFlatten = exports.formatStatsEvent = void 0;
|
|
4
|
+
function isGetStatsEvent(event) {
|
|
5
|
+
return event.name === 'getStats';
|
|
6
|
+
}
|
|
7
|
+
function prepGetStatsEvent(event) {
|
|
8
|
+
const details = {};
|
|
9
|
+
Object.assign(details, deepFlatten(event.tracks, 'localTrack'));
|
|
10
|
+
delete event.tracks;
|
|
11
|
+
Object.assign(details, deepFlatten(event.remoteTracks, 'remoteTrack'));
|
|
12
|
+
delete event.remoteTracks;
|
|
13
|
+
return details;
|
|
14
|
+
}
|
|
15
|
+
function formatStatsEvent(event, extraDetails = {}) {
|
|
16
|
+
const details = {
|
|
17
|
+
_eventType: event.name,
|
|
18
|
+
_eventTimestamp: new Date().getTime(),
|
|
19
|
+
...extraDetails
|
|
20
|
+
};
|
|
21
|
+
// anything that needs to be renamed or massaged
|
|
22
|
+
if (isGetStatsEvent(event)) {
|
|
23
|
+
Object.assign(details, prepGetStatsEvent(event));
|
|
24
|
+
}
|
|
25
|
+
// general case
|
|
26
|
+
Object.assign(details, deepFlatten(event));
|
|
27
|
+
delete details.name;
|
|
28
|
+
const formattedEvent = {
|
|
29
|
+
actionName: 'WebrtcStats',
|
|
30
|
+
details
|
|
31
|
+
};
|
|
32
|
+
return formattedEvent;
|
|
33
|
+
}
|
|
34
|
+
exports.formatStatsEvent = formatStatsEvent;
|
|
35
|
+
function deepFlatten(obj, prefix = '') {
|
|
36
|
+
const flatObj = {};
|
|
37
|
+
if (Array.isArray(obj)) {
|
|
38
|
+
for (let i = 0; i < obj.length; i++) {
|
|
39
|
+
Object.assign(flatObj, deepFlatten(obj[i], `${prefix}_[${i}]`));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (typeof obj !== 'object') {
|
|
43
|
+
flatObj[prefix] = obj;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
Object.keys(obj)
|
|
47
|
+
/* Filter all IP addresses before sending to NR.
|
|
48
|
+
https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats */
|
|
49
|
+
.filter(key => !['ip', 'address', 'relatedaddress'].includes(key.toLowerCase()))
|
|
50
|
+
.forEach((key) => {
|
|
51
|
+
const val = obj[key];
|
|
52
|
+
const nextPrefix = prefix ? `${prefix}_${key}` : key;
|
|
53
|
+
if (typeof val !== 'object' && !Array.isArray(val)) {
|
|
54
|
+
flatObj[nextPrefix] = val;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
Object.assign(flatObj, deepFlatten(val, nextPrefix));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return flatObj;
|
|
62
|
+
}
|
|
63
|
+
exports.deepFlatten = deepFlatten;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { JingleReasonCondition } from 'stanza/Constants';
|
|
2
|
+
import { JingleReason } from 'stanza/protocol';
|
|
3
|
+
import { ConnectionState, IGenesysCloudMediaSessionParams, IMediaSession, SessionState } from './media-session';
|
|
4
|
+
import { WebrtcExtension } from '../webrtc';
|
|
5
|
+
import { SessionTypes } from './interfaces';
|
|
6
|
+
export interface GenesysCloudMediaSession extends IMediaSession {
|
|
7
|
+
}
|
|
8
|
+
export declare class GenesysCloudMediaSession {
|
|
9
|
+
private webrtcExtension;
|
|
10
|
+
private statsGatherer?;
|
|
11
|
+
private iceCandidatesDiscovered;
|
|
12
|
+
private iceCandidatesReceivedFromPeer;
|
|
13
|
+
private interruptionStart?;
|
|
14
|
+
private logger;
|
|
15
|
+
private stateSyncTimeout?;
|
|
16
|
+
conversationId: string;
|
|
17
|
+
id: string;
|
|
18
|
+
fromJid: string;
|
|
19
|
+
peerID: string;
|
|
20
|
+
peerConnection: RTCPeerConnection;
|
|
21
|
+
fromUserId?: string;
|
|
22
|
+
originalRoomJid?: string;
|
|
23
|
+
sessionType: SessionTypes;
|
|
24
|
+
ignoreHostCandidatesFromRemote: boolean;
|
|
25
|
+
allowIPv6: boolean;
|
|
26
|
+
allowTCP: boolean;
|
|
27
|
+
dataChannel?: RTCDataChannel;
|
|
28
|
+
state: SessionState;
|
|
29
|
+
connectionState: ConnectionState;
|
|
30
|
+
privAnswerMode?: 'Auto';
|
|
31
|
+
constructor(webrtcExtension: WebrtcExtension, params: IGenesysCloudMediaSessionParams);
|
|
32
|
+
private keepStateInSyncWithPeerConnection;
|
|
33
|
+
setRemoteDescription(sdp: string): Promise<void>;
|
|
34
|
+
private getLogDetails;
|
|
35
|
+
private log;
|
|
36
|
+
private sendGenesysWebrtc;
|
|
37
|
+
end(reason?: JingleReasonCondition | JingleReason, silent?: boolean): Promise<void>;
|
|
38
|
+
setupStatsGatherer(): void;
|
|
39
|
+
protected onIceStateChange(): Promise<void>;
|
|
40
|
+
protected onConnectionStateChange(): void;
|
|
41
|
+
protected onIceCandidateError(ev: Event): void;
|
|
42
|
+
private createIceCandidate;
|
|
43
|
+
addRemoteIceCandidate(sdpFragment: string): Promise<void>;
|
|
44
|
+
protected onIceCandidate(e: RTCPeerConnectionIceEvent): Promise<any> | undefined;
|
|
45
|
+
onSessionTerminate(reason?: JingleReasonCondition): void;
|
|
46
|
+
addTrack(track: MediaStreamTrack, stream?: MediaStream): Promise<void>;
|
|
47
|
+
protected _setupDataChannel(): void;
|
|
48
|
+
_handleDataChannelMessage(event: MessageEvent): void;
|
|
49
|
+
accept(): Promise<any>;
|
|
50
|
+
mute(userId: string, type: 'audio' | 'video'): Promise<any>;
|
|
51
|
+
unmute(userId: string, type: 'audio' | 'video'): Promise<any>;
|
|
52
|
+
removeTrack(): Promise<void>;
|
|
53
|
+
hold(): Promise<void>;
|
|
54
|
+
resume(): Promise<void>;
|
|
55
|
+
toString(): {
|
|
56
|
+
connectionState: ConnectionState;
|
|
57
|
+
state: SessionState;
|
|
58
|
+
sessionType: SessionTypes;
|
|
59
|
+
fromJid: string;
|
|
60
|
+
conversationId: string;
|
|
61
|
+
id: string;
|
|
62
|
+
peerConnection: RTCPeerConnection;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
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.GenesysCloudMediaSession = void 0;
|
|
7
|
+
const webrtc_stats_gatherer_1 = __importDefault(require("webrtc-stats-gatherer"));
|
|
8
|
+
const events_1 = require("events");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
const interfaces_1 = require("./interfaces");
|
|
11
|
+
const loggingOverrides = {
|
|
12
|
+
'Discovered new ICE candidate': { skipMessage: true }
|
|
13
|
+
};
|
|
14
|
+
class GenesysCloudMediaSession {
|
|
15
|
+
constructor(webrtcExtension, params) {
|
|
16
|
+
this.webrtcExtension = webrtcExtension;
|
|
17
|
+
this.iceCandidatesDiscovered = 0;
|
|
18
|
+
this.iceCandidatesReceivedFromPeer = 0;
|
|
19
|
+
this.state = 'pending';
|
|
20
|
+
this.connectionState = 'starting';
|
|
21
|
+
this.id = params.id;
|
|
22
|
+
this.fromJid = params.fromJid;
|
|
23
|
+
this.peerID = params.peerID;
|
|
24
|
+
this.logger = params.logger;
|
|
25
|
+
this.conversationId = params.conversationId;
|
|
26
|
+
this.fromUserId = params.fromUserId;
|
|
27
|
+
this.pc = this.peerConnection = new RTCPeerConnection({
|
|
28
|
+
iceServers: params.iceServers,
|
|
29
|
+
iceTransportPolicy: params.iceTransportPolicy
|
|
30
|
+
});
|
|
31
|
+
this.originalRoomJid = params.originalRoomJid;
|
|
32
|
+
this.sessionType = interfaces_1.SessionTypes[params.sessionType];
|
|
33
|
+
this.ignoreHostCandidatesFromRemote = !!params.ignoreHostCandidatesFromRemote;
|
|
34
|
+
this.allowIPv6 = !!params.allowIPv6;
|
|
35
|
+
this.allowTCP = !!params.allowTCP;
|
|
36
|
+
this.reinvite = !!params.reinvite;
|
|
37
|
+
this.privAnswerMode = params.privAnswerMode;
|
|
38
|
+
this.meetingId = params.meetingId;
|
|
39
|
+
// babel does not like the typescript recipe for multiple extends so we are hacking this one
|
|
40
|
+
// referencing https://github.com/babel/babel/issues/798
|
|
41
|
+
const eventEmitter = new events_1.EventEmitter();
|
|
42
|
+
Object.keys(eventEmitter.__proto__).forEach((name) => {
|
|
43
|
+
this[name] = eventEmitter[name];
|
|
44
|
+
});
|
|
45
|
+
if (!params.optOutOfWebrtcStatsTelemetry) {
|
|
46
|
+
this.setupStatsGatherer();
|
|
47
|
+
}
|
|
48
|
+
this.peerConnection.addEventListener('icecandidate', this.onIceCandidate.bind(this));
|
|
49
|
+
this.peerConnection.addEventListener('iceconnectionstatechange', this.onIceStateChange.bind(this));
|
|
50
|
+
this.peerConnection.addEventListener('connectionstatechange', this.onConnectionStateChange.bind(this));
|
|
51
|
+
this.peerConnection.addEventListener('icecandidateerror', this.onIceCandidateError.bind(this));
|
|
52
|
+
// sync the session state after a silent disconnect, like putting the system to sleep.
|
|
53
|
+
}
|
|
54
|
+
keepStateInSyncWithPeerConnection() {
|
|
55
|
+
if (this.stateSyncTimeout) {
|
|
56
|
+
clearTimeout(this.stateSyncTimeout);
|
|
57
|
+
this.stateSyncTimeout = undefined;
|
|
58
|
+
}
|
|
59
|
+
const lastTime = Date.now();
|
|
60
|
+
const checkInterval = 2000;
|
|
61
|
+
const threshold = 2000;
|
|
62
|
+
this.stateSyncTimeout = setTimeout(() => {
|
|
63
|
+
const currentTime = Date.now();
|
|
64
|
+
const timeDiff = currentTime - lastTime;
|
|
65
|
+
if (timeDiff > checkInterval + threshold) {
|
|
66
|
+
this.log('warn', 'MediaSession detected timer anomally. Reasons include taxed resources or system sleep.');
|
|
67
|
+
// if we have a state mismatch
|
|
68
|
+
if (this.state !== 'ended' && ['failed', 'closed'].includes(this.peerConnection.connectionState)) {
|
|
69
|
+
this.log('warn', 'state mismatch between session.state and peerConnection.connectionState, manually terminating the session', { sessionId: this.id, conversationId: this.conversationId, sessionType: this.sessionType });
|
|
70
|
+
this.onSessionTerminate();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (this.state !== 'ended') {
|
|
74
|
+
this.keepStateInSyncWithPeerConnection();
|
|
75
|
+
}
|
|
76
|
+
}, checkInterval);
|
|
77
|
+
}
|
|
78
|
+
async setRemoteDescription(sdp) {
|
|
79
|
+
return this.peerConnection.setRemoteDescription({ sdp, type: 'offer' });
|
|
80
|
+
}
|
|
81
|
+
getLogDetails() {
|
|
82
|
+
return {
|
|
83
|
+
conversationId: this.conversationId,
|
|
84
|
+
sessionId: this.id,
|
|
85
|
+
sessionType: this.sessionType
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
log(level, message, details, options) {
|
|
89
|
+
if (!details) {
|
|
90
|
+
details = {};
|
|
91
|
+
}
|
|
92
|
+
const logDetails = { ...details, ...this.getLogDetails() };
|
|
93
|
+
this.logger[level](message, logDetails, options);
|
|
94
|
+
}
|
|
95
|
+
async sendGenesysWebrtc(info) {
|
|
96
|
+
info.id = info.id || globalThis.crypto.randomUUID();
|
|
97
|
+
info.jsonrpc = info.jsonrpc || '2.0';
|
|
98
|
+
const iq = {
|
|
99
|
+
type: 'set',
|
|
100
|
+
genesysWebrtc: info,
|
|
101
|
+
from: this.webrtcExtension.jid,
|
|
102
|
+
to: this.peerID
|
|
103
|
+
};
|
|
104
|
+
return this.webrtcExtension.sendIq(iq);
|
|
105
|
+
}
|
|
106
|
+
async end(reason = 'success', silent = false) {
|
|
107
|
+
this.state = 'ended';
|
|
108
|
+
const params = {
|
|
109
|
+
sessionId: this.id,
|
|
110
|
+
reason: reason.condition || reason
|
|
111
|
+
};
|
|
112
|
+
if (!silent) {
|
|
113
|
+
await (0, utils_1.timeoutPromise)((resolve, reject) => {
|
|
114
|
+
this.log('info', 'sending sdp terminate');
|
|
115
|
+
this.sendGenesysWebrtc({ jsonrpc: '2.0', method: 'terminate', params }).then(resolve, reject);
|
|
116
|
+
}, 2000, 'Timeout waiting for response to termination request', { sessionId: this.id, conversationId: this.conversationId, sessionType: this.sessionType }).catch((e) => this.logger.error(e));
|
|
117
|
+
}
|
|
118
|
+
this.onSessionTerminate(params.reason);
|
|
119
|
+
}
|
|
120
|
+
setupStatsGatherer() {
|
|
121
|
+
this.statsGatherer = new webrtc_stats_gatherer_1.default(this.peerConnection);
|
|
122
|
+
this.statsGatherer.on('stats', this.emit.bind(this, 'stats'));
|
|
123
|
+
}
|
|
124
|
+
async onIceStateChange() {
|
|
125
|
+
const iceState = this.peerConnection.iceConnectionState;
|
|
126
|
+
const sessionId = this.id;
|
|
127
|
+
const conversationId = this.conversationId;
|
|
128
|
+
const sessionType = this.sessionType;
|
|
129
|
+
this.log('info', 'ICE state changed: ', { iceState, sessionId, conversationId });
|
|
130
|
+
if (iceState === 'disconnected') {
|
|
131
|
+
// this means we actually connected at one point
|
|
132
|
+
if (this.peerConnection.signalingState === 'stable') {
|
|
133
|
+
this.interruptionStart = new Date();
|
|
134
|
+
this.log('info', 'Connection state is interrupted', { sessionId, conversationId, sessionType });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if (iceState === 'connected') {
|
|
138
|
+
this.log('info', 'sending session-info: active');
|
|
139
|
+
const params = {
|
|
140
|
+
sessionId: this.id,
|
|
141
|
+
status: 'active'
|
|
142
|
+
};
|
|
143
|
+
await this.sendGenesysWebrtc({
|
|
144
|
+
jsonrpc: '2.0',
|
|
145
|
+
params,
|
|
146
|
+
method: 'info'
|
|
147
|
+
});
|
|
148
|
+
this._setupDataChannel();
|
|
149
|
+
}
|
|
150
|
+
else if (iceState === 'failed') {
|
|
151
|
+
this.log('info', 'ICE connection failed', {
|
|
152
|
+
candidatesDiscovered: this.iceCandidatesDiscovered,
|
|
153
|
+
candidatesReceivedFromPeer: this.iceCandidatesReceivedFromPeer
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
onConnectionStateChange() {
|
|
158
|
+
const connectionState = this.connectionState = this.peerConnection.connectionState;
|
|
159
|
+
const sessionId = this.id;
|
|
160
|
+
const conversationId = this.conversationId;
|
|
161
|
+
const sessionType = this.sessionType;
|
|
162
|
+
if (connectionState === 'connected') {
|
|
163
|
+
this.keepStateInSyncWithPeerConnection();
|
|
164
|
+
}
|
|
165
|
+
this.log('info', 'Connection state changed: ', { connectionState, sessionId, conversationId, sessionType });
|
|
166
|
+
if (this.interruptionStart) {
|
|
167
|
+
if (connectionState === 'connected') {
|
|
168
|
+
const diff = new Date().getTime() - this.interruptionStart.getTime();
|
|
169
|
+
this.log('info', 'Connection was interrupted but was successfully recovered/connected', { sessionId, conversationId, sessionType, timeToRecover: diff });
|
|
170
|
+
this.interruptionStart = undefined;
|
|
171
|
+
}
|
|
172
|
+
else if (connectionState === 'failed') {
|
|
173
|
+
this.log('info', 'Connection was interrupted and failed to recover, cleaning up', { sessionId, conversationId, sessionType });
|
|
174
|
+
this.onSessionTerminate();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.emit('connectionState', connectionState);
|
|
178
|
+
}
|
|
179
|
+
onIceCandidateError(ev) {
|
|
180
|
+
const event = ev;
|
|
181
|
+
this.log('error', 'IceCandidateError', {
|
|
182
|
+
errorCode: event.errorCode,
|
|
183
|
+
errorText: event.errorText,
|
|
184
|
+
url: event.url
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/* istanbul ignore next */
|
|
188
|
+
createIceCandidate(sdpMid, candidateStr) {
|
|
189
|
+
return new RTCIceCandidate({
|
|
190
|
+
sdpMid,
|
|
191
|
+
candidate: candidateStr
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async addRemoteIceCandidate(sdpFragment) {
|
|
195
|
+
// matches the mline type. example: "m=video 9 RTP/AVP 0" should result in "video"
|
|
196
|
+
const sdpMid = sdpFragment.match(/m=([^\s]+)/)[1];
|
|
197
|
+
// matches the entire a= line without the "a="
|
|
198
|
+
const candidate = sdpFragment.match(/a=([^\\\r]+)/)[1];
|
|
199
|
+
const iceCandidate = this.createIceCandidate(sdpMid, candidate);
|
|
200
|
+
await this.peerConnection.addIceCandidate(iceCandidate);
|
|
201
|
+
}
|
|
202
|
+
// this is for ice candidates we harvest for ourself
|
|
203
|
+
onIceCandidate(e) {
|
|
204
|
+
var _a, _b;
|
|
205
|
+
const candidateString = (_a = e.candidate) === null || _a === void 0 ? void 0 : _a.candidate;
|
|
206
|
+
const sdpMid = (_b = e.candidate) === null || _b === void 0 ? void 0 : _b.sdpMid;
|
|
207
|
+
let sdpStr;
|
|
208
|
+
if (e.candidate) {
|
|
209
|
+
if (!this.allowTCP && e.candidate.protocol === 'tcp') {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (!this.allowIPv6) {
|
|
213
|
+
const addressRegex = /.+udp [^ ]+ ([^ ]+).*typ host/;
|
|
214
|
+
const matches = addressRegex.exec(e.candidate.candidate);
|
|
215
|
+
const ipv4Regex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
|
|
216
|
+
if (matches && !matches[1].match(ipv4Regex)) {
|
|
217
|
+
this.log('debug', 'Filtering out IPv6 candidate', e.candidate.candidate);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// this has too much information and can only be logged locally (debug)
|
|
222
|
+
this.log('debug', 'Processing ice candidate', e.candidate);
|
|
223
|
+
// this one is info level so it can go to the server
|
|
224
|
+
this.log('info', 'Discovered ice candidate to send to peer', { type: e.candidate.type });
|
|
225
|
+
this.iceCandidatesDiscovered++;
|
|
226
|
+
// media team asked us to send the mline and prefix the candidate with "a="
|
|
227
|
+
sdpStr = `m=${sdpMid} 9 RTP/AVP 0\r\na=${candidateString}\r\n`;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
sdpStr = 'a=end-of-candidates\r\n';
|
|
231
|
+
this.emit('endOfCandidates');
|
|
232
|
+
}
|
|
233
|
+
return this.sendGenesysWebrtc({
|
|
234
|
+
jsonrpc: '2.0',
|
|
235
|
+
method: 'iceCandidate',
|
|
236
|
+
params: {
|
|
237
|
+
sessionId: this.id,
|
|
238
|
+
sdp: sdpStr
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
onSessionTerminate(reason) {
|
|
243
|
+
this.state = 'ended';
|
|
244
|
+
if (this.peerConnection) {
|
|
245
|
+
this.peerConnection.close();
|
|
246
|
+
}
|
|
247
|
+
this.logger.info('emitting sdp media-session (terminate)');
|
|
248
|
+
this.emit('terminated', { condition: reason || 'success' });
|
|
249
|
+
}
|
|
250
|
+
async addTrack(track, stream) {
|
|
251
|
+
const availableTransceiver = this.peerConnection.getTransceivers().find((transceiver) => {
|
|
252
|
+
var _a;
|
|
253
|
+
return !transceiver.sender.track && ((_a = transceiver.receiver.track) === null || _a === void 0 ? void 0 : _a.kind) === track.kind;
|
|
254
|
+
});
|
|
255
|
+
if (availableTransceiver) {
|
|
256
|
+
return availableTransceiver.sender.replaceTrack(track);
|
|
257
|
+
}
|
|
258
|
+
this.peerConnection.addTrack(track, stream);
|
|
259
|
+
}
|
|
260
|
+
_setupDataChannel() {
|
|
261
|
+
var _a;
|
|
262
|
+
// this shouldn't happen, but we shouldn't set the datachannel up more than once
|
|
263
|
+
if (this.dataChannel) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// do nothing if a datachannel wasn't offered
|
|
267
|
+
if ((_a = this.peerConnection.remoteDescription) === null || _a === void 0 ? void 0 : _a.sdp.includes('webrtc-datachannel')) {
|
|
268
|
+
this.log('info', 'creating data channel');
|
|
269
|
+
this.dataChannel = this.peerConnection.createDataChannel('videoConferenceControl');
|
|
270
|
+
this.dataChannel.addEventListener('open', () => {
|
|
271
|
+
this.log('info', 'data channel opened');
|
|
272
|
+
});
|
|
273
|
+
this.dataChannel.addEventListener('message', this._handleDataChannelMessage.bind(this));
|
|
274
|
+
this.dataChannel.addEventListener('close', () => {
|
|
275
|
+
this.log('info', 'closing data channel');
|
|
276
|
+
});
|
|
277
|
+
this.dataChannel.addEventListener('error', (error) => {
|
|
278
|
+
this.log('error', 'Error occurred with the data channel', error);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
_handleDataChannelMessage(event) {
|
|
283
|
+
try {
|
|
284
|
+
const message = JSON.parse(event.data);
|
|
285
|
+
this.emit('dataChannelMessage', message);
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
this.log('error', 'Failed to parse data channel message', { error: e });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async accept() {
|
|
292
|
+
this.state = 'active';
|
|
293
|
+
const answer = await this.peerConnection.createAnswer();
|
|
294
|
+
await this.peerConnection.setLocalDescription(answer);
|
|
295
|
+
const params = {
|
|
296
|
+
sdp: answer.sdp,
|
|
297
|
+
sessionId: this.id
|
|
298
|
+
};
|
|
299
|
+
// Do not log the SDP payload to avoid logging sensitive information.
|
|
300
|
+
this.logger.info('sending sdp answer', { sessionId: this.id, conversationId: this.conversationId });
|
|
301
|
+
return this.sendGenesysWebrtc({
|
|
302
|
+
jsonrpc: '2.0',
|
|
303
|
+
method: 'answer',
|
|
304
|
+
params
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
async mute(userId, type) {
|
|
308
|
+
const params = {
|
|
309
|
+
sessionId: this.id,
|
|
310
|
+
type
|
|
311
|
+
};
|
|
312
|
+
return this.sendGenesysWebrtc({
|
|
313
|
+
jsonrpc: '2.0',
|
|
314
|
+
method: 'mute',
|
|
315
|
+
params
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
async unmute(userId, type) {
|
|
319
|
+
const params = {
|
|
320
|
+
sessionId: this.id,
|
|
321
|
+
type
|
|
322
|
+
};
|
|
323
|
+
return this.sendGenesysWebrtc({
|
|
324
|
+
jsonrpc: '2.0',
|
|
325
|
+
method: 'unmute',
|
|
326
|
+
params
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/* istanbul ignore next */
|
|
330
|
+
async removeTrack() {
|
|
331
|
+
throw new Error('Not Implemented');
|
|
332
|
+
}
|
|
333
|
+
/* istanbul ignore next */
|
|
334
|
+
async hold() {
|
|
335
|
+
throw new Error('Not Implemented');
|
|
336
|
+
}
|
|
337
|
+
/* istanbul ignore next */
|
|
338
|
+
async resume() {
|
|
339
|
+
throw new Error('Not Implemented');
|
|
340
|
+
}
|
|
341
|
+
toString() {
|
|
342
|
+
return {
|
|
343
|
+
connectionState: this.connectionState,
|
|
344
|
+
state: this.state,
|
|
345
|
+
sessionType: this.sessionType,
|
|
346
|
+
fromJid: this.fromJid,
|
|
347
|
+
conversationId: this.conversationId,
|
|
348
|
+
id: this.id,
|
|
349
|
+
peerConnection: this.peerConnection
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
exports.GenesysCloudMediaSession = GenesysCloudMediaSession;
|