@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.
Files changed (176) hide show
  1. package/README.md +47 -0
  2. package/dist/cjs/alerting-leader.d.ts +29 -0
  3. package/dist/cjs/alerting-leader.js +163 -0
  4. package/dist/cjs/client.d.ts +84 -0
  5. package/dist/cjs/client.js +680 -0
  6. package/dist/cjs/connection-manager.d.ts +18 -0
  7. package/dist/cjs/connection-manager.js +120 -0
  8. package/dist/cjs/connection-transfer.d.ts +7 -0
  9. package/dist/cjs/connection-transfer.js +20 -0
  10. package/dist/cjs/http-client.d.ts +18 -0
  11. package/dist/cjs/http-client.js +230 -0
  12. package/dist/cjs/index.d.ts +10 -0
  13. package/dist/cjs/index.js +31 -0
  14. package/dist/cjs/messenger.d.ts +37 -0
  15. package/dist/cjs/messenger.js +54 -0
  16. package/dist/cjs/notifications.d.ts +94 -0
  17. package/dist/cjs/notifications.js +495 -0
  18. package/dist/cjs/ping.d.ts +21 -0
  19. package/dist/cjs/ping.js +60 -0
  20. package/dist/cjs/polyfills.d.ts +0 -0
  21. package/dist/cjs/polyfills.js +6 -0
  22. package/dist/cjs/server-monitor.d.ts +16 -0
  23. package/dist/cjs/server-monitor.js +44 -0
  24. package/dist/cjs/stanza-definitions/webrtc-signaling.d.ts +29 -0
  25. package/dist/cjs/stanza-definitions/webrtc-signaling.js +113 -0
  26. package/dist/cjs/stanza-definitions/xep0051.d.ts +19 -0
  27. package/dist/cjs/stanza-definitions/xep0051.js +28 -0
  28. package/dist/cjs/stats-formatter.d.ts +6 -0
  29. package/dist/cjs/stats-formatter.js +63 -0
  30. package/dist/cjs/types/genesys-cloud-media-session.d.ts +64 -0
  31. package/dist/cjs/types/genesys-cloud-media-session.js +353 -0
  32. package/dist/cjs/types/interfaces.d.ts +310 -0
  33. package/dist/cjs/types/interfaces.js +22 -0
  34. package/dist/cjs/types/libs.d.ts +32 -0
  35. package/dist/cjs/types/libs.js +1 -0
  36. package/dist/cjs/types/media-session.d.ts +76 -0
  37. package/dist/cjs/types/media-session.js +2 -0
  38. package/dist/cjs/types/named-agent.d.ts +13 -0
  39. package/dist/cjs/types/named-agent.js +2 -0
  40. package/dist/cjs/types/offline-error.d.ts +2 -0
  41. package/dist/cjs/types/offline-error.js +5 -0
  42. package/dist/cjs/types/sasl-error.d.ts +8 -0
  43. package/dist/cjs/types/sasl-error.js +12 -0
  44. package/dist/cjs/types/stanza-media-session.d.ts +56 -0
  45. package/dist/cjs/types/stanza-media-session.js +255 -0
  46. package/dist/cjs/types/streaming-client-extension.d.ts +14 -0
  47. package/dist/cjs/types/streaming-client-extension.js +2 -0
  48. package/dist/cjs/types/timeout-error.d.ts +3 -0
  49. package/dist/cjs/types/timeout-error.js +9 -0
  50. package/dist/cjs/types/user-cancelled-error.d.ts +2 -0
  51. package/dist/cjs/types/user-cancelled-error.js +5 -0
  52. package/dist/cjs/utils.d.ts +34 -0
  53. package/dist/cjs/utils.js +202 -0
  54. package/dist/cjs/webrtc.d.ts +126 -0
  55. package/dist/cjs/webrtc.js +891 -0
  56. package/dist/deploy-info.json +21 -0
  57. package/dist/es/alerting-leader.d.ts +29 -0
  58. package/dist/es/alerting-leader.js +177 -0
  59. package/dist/es/client.d.ts +84 -0
  60. package/dist/es/client.js +699 -0
  61. package/dist/es/connection-manager.d.ts +18 -0
  62. package/dist/es/connection-manager.js +124 -0
  63. package/dist/es/connection-transfer.d.ts +7 -0
  64. package/dist/es/connection-transfer.js +16 -0
  65. package/dist/es/http-client.d.ts +18 -0
  66. package/dist/es/http-client.js +194 -0
  67. package/dist/es/index.bundle.js +39629 -0
  68. package/dist/es/index.d.ts +10 -0
  69. package/dist/es/index.js +10 -0
  70. package/dist/es/messenger.d.ts +37 -0
  71. package/dist/es/messenger.js +62 -0
  72. package/dist/es/notifications.d.ts +94 -0
  73. package/dist/es/notifications.js +507 -0
  74. package/dist/es/ping.d.ts +21 -0
  75. package/dist/es/ping.js +68 -0
  76. package/dist/es/polyfills.d.ts +0 -0
  77. package/dist/es/polyfills.js +6 -0
  78. package/dist/es/server-monitor.d.ts +16 -0
  79. package/dist/es/server-monitor.js +41 -0
  80. package/dist/es/stanza-definitions/webrtc-signaling.d.ts +29 -0
  81. package/dist/es/stanza-definitions/webrtc-signaling.js +110 -0
  82. package/dist/es/stanza-definitions/xep0051.d.ts +19 -0
  83. package/dist/es/stanza-definitions/xep0051.js +25 -0
  84. package/dist/es/stats-formatter.d.ts +6 -0
  85. package/dist/es/stats-formatter.js +54 -0
  86. package/dist/es/types/genesys-cloud-media-session.d.ts +64 -0
  87. package/dist/es/types/genesys-cloud-media-session.js +379 -0
  88. package/dist/es/types/interfaces.d.ts +310 -0
  89. package/dist/es/types/interfaces.js +19 -0
  90. package/dist/es/types/libs.d.ts +32 -0
  91. package/dist/es/types/libs.js +1 -0
  92. package/dist/es/types/media-session.d.ts +76 -0
  93. package/dist/es/types/media-session.js +1 -0
  94. package/dist/es/types/named-agent.d.ts +13 -0
  95. package/dist/es/types/named-agent.js +1 -0
  96. package/dist/es/types/offline-error.d.ts +2 -0
  97. package/dist/es/types/offline-error.js +2 -0
  98. package/dist/es/types/sasl-error.d.ts +8 -0
  99. package/dist/es/types/sasl-error.js +9 -0
  100. package/dist/es/types/stanza-media-session.d.ts +56 -0
  101. package/dist/es/types/stanza-media-session.js +264 -0
  102. package/dist/es/types/streaming-client-extension.d.ts +14 -0
  103. package/dist/es/types/streaming-client-extension.js +1 -0
  104. package/dist/es/types/timeout-error.d.ts +3 -0
  105. package/dist/es/types/timeout-error.js +5 -0
  106. package/dist/es/types/user-cancelled-error.d.ts +2 -0
  107. package/dist/es/types/user-cancelled-error.js +2 -0
  108. package/dist/es/utils.d.ts +34 -0
  109. package/dist/es/utils.js +192 -0
  110. package/dist/es/webrtc.d.ts +126 -0
  111. package/dist/es/webrtc.js +917 -0
  112. package/dist/manifest.json +19 -0
  113. package/dist/npm/CHANGELOG.md +637 -0
  114. package/dist/npm/README.md +47 -0
  115. package/dist/npm/alerting-leader.d.ts +29 -0
  116. package/dist/npm/alerting-leader.js +163 -0
  117. package/dist/npm/client.d.ts +84 -0
  118. package/dist/npm/client.js +680 -0
  119. package/dist/npm/connection-manager.d.ts +18 -0
  120. package/dist/npm/connection-manager.js +120 -0
  121. package/dist/npm/connection-transfer.d.ts +7 -0
  122. package/dist/npm/connection-transfer.js +20 -0
  123. package/dist/npm/http-client.d.ts +18 -0
  124. package/dist/npm/http-client.js +230 -0
  125. package/dist/npm/index.d.ts +10 -0
  126. package/dist/npm/index.js +31 -0
  127. package/dist/npm/messenger.d.ts +37 -0
  128. package/dist/npm/messenger.js +54 -0
  129. package/dist/npm/module.js +10 -0
  130. package/dist/npm/notifications.d.ts +94 -0
  131. package/dist/npm/notifications.js +495 -0
  132. package/dist/npm/ping.d.ts +21 -0
  133. package/dist/npm/ping.js +60 -0
  134. package/dist/npm/polyfills.d.ts +0 -0
  135. package/dist/npm/polyfills.js +6 -0
  136. package/dist/npm/server-monitor.d.ts +16 -0
  137. package/dist/npm/server-monitor.js +44 -0
  138. package/dist/npm/stanza-definitions/webrtc-signaling.d.ts +29 -0
  139. package/dist/npm/stanza-definitions/webrtc-signaling.js +113 -0
  140. package/dist/npm/stanza-definitions/xep0051.d.ts +19 -0
  141. package/dist/npm/stanza-definitions/xep0051.js +28 -0
  142. package/dist/npm/stats-formatter.d.ts +6 -0
  143. package/dist/npm/stats-formatter.js +63 -0
  144. package/dist/npm/types/genesys-cloud-media-session.d.ts +64 -0
  145. package/dist/npm/types/genesys-cloud-media-session.js +353 -0
  146. package/dist/npm/types/interfaces.d.ts +310 -0
  147. package/dist/npm/types/interfaces.js +22 -0
  148. package/dist/npm/types/libs.d.ts +32 -0
  149. package/dist/npm/types/libs.js +1 -0
  150. package/dist/npm/types/media-session.d.ts +76 -0
  151. package/dist/npm/types/media-session.js +2 -0
  152. package/dist/npm/types/named-agent.d.ts +13 -0
  153. package/dist/npm/types/named-agent.js +2 -0
  154. package/dist/npm/types/offline-error.d.ts +2 -0
  155. package/dist/npm/types/offline-error.js +5 -0
  156. package/dist/npm/types/sasl-error.d.ts +8 -0
  157. package/dist/npm/types/sasl-error.js +12 -0
  158. package/dist/npm/types/stanza-media-session.d.ts +56 -0
  159. package/dist/npm/types/stanza-media-session.js +255 -0
  160. package/dist/npm/types/streaming-client-extension.d.ts +14 -0
  161. package/dist/npm/types/streaming-client-extension.js +2 -0
  162. package/dist/npm/types/timeout-error.d.ts +3 -0
  163. package/dist/npm/types/timeout-error.js +9 -0
  164. package/dist/npm/types/user-cancelled-error.d.ts +2 -0
  165. package/dist/npm/types/user-cancelled-error.js +5 -0
  166. package/dist/npm/utils.d.ts +34 -0
  167. package/dist/npm/utils.js +207 -0
  168. package/dist/npm/webrtc.d.ts +126 -0
  169. package/dist/npm/webrtc.js +893 -0
  170. package/dist/streaming-client.browser.js +2 -0
  171. package/dist/streaming-client.browser.js.LICENSE.txt +12 -0
  172. package/dist/v20/streaming-client.browser.js +2 -0
  173. package/dist/v20/streaming-client.browser.js.LICENSE.txt +12 -0
  174. package/dist/v20.1.0/streaming-client.browser.js +2 -0
  175. package/dist/v20.1.0/streaming-client.browser.js.LICENSE.txt +12 -0
  176. 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;