@voicenter-team/opensips-js 1.0.10

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 (49) hide show
  1. package/README.md +75 -0
  2. package/build/enum/call.event.listener.type.d.ts +7 -0
  3. package/build/enum/call.event.listener.type.js +10 -0
  4. package/build/enum/metric.keys.to.include.d.ts +2 -0
  5. package/build/enum/metric.keys.to.include.js +4 -0
  6. package/build/helpers/UA/index.d.ts +6 -0
  7. package/build/helpers/UA/index.js +9 -0
  8. package/build/helpers/audio.helper.d.ts +9 -0
  9. package/build/helpers/audio.helper.js +60 -0
  10. package/build/helpers/filter.helper.d.ts +2 -0
  11. package/build/helpers/filter.helper.js +14 -0
  12. package/build/helpers/time.helper.d.ts +16 -0
  13. package/build/helpers/time.helper.js +28 -0
  14. package/build/helpers/volume.helper.d.ts +2 -0
  15. package/build/helpers/volume.helper.js +76 -0
  16. package/build/helpers/webrtcmetrics/collector.d.ts +32 -0
  17. package/build/helpers/webrtcmetrics/collector.js +282 -0
  18. package/build/helpers/webrtcmetrics/engine.d.ts +20 -0
  19. package/build/helpers/webrtcmetrics/engine.js +164 -0
  20. package/build/helpers/webrtcmetrics/exporter.d.ts +116 -0
  21. package/build/helpers/webrtcmetrics/exporter.js +528 -0
  22. package/build/helpers/webrtcmetrics/extractor.d.ts +1 -0
  23. package/build/helpers/webrtcmetrics/extractor.js +976 -0
  24. package/build/helpers/webrtcmetrics/index.d.ts +63 -0
  25. package/build/helpers/webrtcmetrics/index.js +93 -0
  26. package/build/helpers/webrtcmetrics/metrics.d.ts +2 -0
  27. package/build/helpers/webrtcmetrics/metrics.js +8 -0
  28. package/build/helpers/webrtcmetrics/probe.d.ts +76 -0
  29. package/build/helpers/webrtcmetrics/probe.js +153 -0
  30. package/build/helpers/webrtcmetrics/utils/config.d.ts +12 -0
  31. package/build/helpers/webrtcmetrics/utils/config.js +28 -0
  32. package/build/helpers/webrtcmetrics/utils/helper.d.ts +13 -0
  33. package/build/helpers/webrtcmetrics/utils/helper.js +134 -0
  34. package/build/helpers/webrtcmetrics/utils/log.d.ts +7 -0
  35. package/build/helpers/webrtcmetrics/utils/log.js +71 -0
  36. package/build/helpers/webrtcmetrics/utils/models.d.ts +309 -0
  37. package/build/helpers/webrtcmetrics/utils/models.js +298 -0
  38. package/build/helpers/webrtcmetrics/utils/score.d.ts +4 -0
  39. package/build/helpers/webrtcmetrics/utils/score.js +235 -0
  40. package/build/helpers/webrtcmetrics/utils/shortUUId.d.ts +1 -0
  41. package/build/helpers/webrtcmetrics/utils/shortUUId.js +7 -0
  42. package/build/index.d.ts +170 -0
  43. package/build/index.js +849 -0
  44. package/package.json +61 -0
  45. package/src/types/declarations.d.ts +6 -0
  46. package/src/types/generic.d.ts +1 -0
  47. package/src/types/listeners.d.ts +42 -0
  48. package/src/types/rtc.d.ts +133 -0
  49. package/src/types/webrtcmetrics.d.ts +64 -0
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # Getting started
2
+ ## Installation
3
+ Using npm:
4
+ ```shell
5
+ $ npm i @voicenter-team/opensips-js
6
+ ```
7
+
8
+ ## Usage
9
+ Firstly lets import the library and create the OpenSIPS instance:
10
+ ```javascript
11
+ import OpenSIPSJS from '@voicenter-team/opensips-js'
12
+
13
+ const openSIPSJS = new OpenSIPSJS({
14
+ configuration: {
15
+ session_timers: false,
16
+ uri: 'sip:extension_user@domain',
17
+ password: 'password',
18
+ },
19
+ socketInterfaces: [ 'wss://domain' ],
20
+ sipDomain: 'domain',
21
+ sipOptions: {
22
+ session_timers: false,
23
+ extraHeaders: [ 'X-Bar: bar' ],
24
+ pcConfig: {},
25
+ },
26
+ })
27
+ ```
28
+
29
+ Then you will be able to call next methods on openSIPSJS instance:
30
+
31
+ ### Methods
32
+ - `async setMediaDevices(setDefaults: Boolean = false)` - will set up media devices
33
+ - `async setMicrophone(deviceId: String)` - set passed device as input device for calls
34
+ - `async setSpeaker(deviceId: String)` - set passed device as output device for calls
35
+ - `async setCurrentActiveRoomId(roomId: Number)` - move to the room
36
+ - `doCallHold({callId: Number, toHold: Boolean, automatic: Boolean})` - hold/unhold call by id
37
+ - `doCall(target: String, addToCurrentRoom: Boolean)` - call to the target. If addToCurrentRoom is true then the call will be added to the user's current room
38
+ - `callTerminate(callId: String)` - terminate call
39
+ - `callTransfer({callId: String, target: String})` - transfer call to target
40
+ - `callMerge(roomId: Number)` - merge calls in specific room
41
+ - `callAnswer(callId: String)` - answer the call
42
+ - `setMetricsConfig(config: WebrtcMetricsConfigType)` - set the metric config (used for audio quality indicator)
43
+ - `doMute(muted: Boolean)` - set the agent muteness
44
+ - `setDND(value: Boolean)` - set the agent "Do not disturb" status
45
+ - `async callChangeRoom({callId: String, roomId: Number})` - move call to the room
46
+ - `callMove({callId: String, roomId: Number})` - Same as callChangeRoom. Move call to the specific room
47
+ - `subscribe({type: String, listener: function})` - subscribe to an event. Available events: `new_call`, `ended`, `progress`, `failed`, `confirmed`
48
+ - `removeIListener(type: String)` - remove event listener
49
+
50
+ WebrtcMetricsConfigType
51
+
52
+ | Parameter | Type |
53
+ |----------------|------------------------|
54
+ | `refreshEvery` | `number \| undefined` |
55
+ | `startAfter` | `number \| undefined` |
56
+ | `startAfter` | `number \| undefined` |
57
+ | `verbose` | `boolean \| undefined` |
58
+ | `pname` | `string \| undefined` |
59
+ | `cid` | `string \| undefined` |
60
+ | `uid` | `string \| undefined` |
61
+ | `record` | `boolean \| undefined` |
62
+ | `ticket` | `boolean \| undefined` |
63
+
64
+ Also there are next public fields on openSIPSJS instance:
65
+ ### Fields
66
+ - `getActiveRooms: { [key: number]: IRoom }` - returns an object of active rooms where key is room id and value is room data
67
+ - `sipDomain: String` - returns sip domain
68
+ - `sipOptions: Object` - returns sip options
69
+ - `getInputDeviceList: []` - returns list of input devices
70
+ - `getOutputDeviceList: []` - returns list of output devices
71
+ - `currentActiveRoomId: Number` - returns current active room id
72
+ - `selectedInputDevice: String` - returns current selected input device id
73
+ - `selectedOutputDevice: String` - returns current selected output device id
74
+ - `isDND: Boolean` - returns if the agent is in "Do not disturb" status
75
+ - `isMuted: Boolean` - returns if the agent is muted
@@ -0,0 +1,7 @@
1
+ export declare const CALL_EVENT_LISTENER_TYPE: {
2
+ NEW_CALL: string;
3
+ CALL_CONFIRMED: string;
4
+ CALL_FAILED: string;
5
+ CALL_PROGRESS: string;
6
+ CALL_ENDED: string;
7
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CALL_EVENT_LISTENER_TYPE = void 0;
4
+ exports.CALL_EVENT_LISTENER_TYPE = {
5
+ NEW_CALL: 'new_call',
6
+ CALL_CONFIRMED: 'confirmed',
7
+ CALL_FAILED: 'failed',
8
+ CALL_PROGRESS: 'progress',
9
+ CALL_ENDED: 'ended'
10
+ };
@@ -0,0 +1,2 @@
1
+ import { ProbeMetricInType } from '@/types/webrtcmetrics';
2
+ export declare const METRIC_KEYS_TO_INCLUDE: (keyof ProbeMetricInType)[];
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.METRIC_KEYS_TO_INCLUDE = void 0;
4
+ exports.METRIC_KEYS_TO_INCLUDE = ['mos_in', 'codec_in', 'delta_KBytes_in', 'delta_kbs_in', 'delta_jitter_ms_in', 'delta_packets_lost_in'];
@@ -0,0 +1,6 @@
1
+ import { UA } from 'jssip';
2
+ import { CallOptionsExtended } from '@/types/rtc';
3
+ import { RTCSession } from 'jssip/lib/RTCSession';
4
+ export default class UAExtended extends UA {
5
+ call(target: string, options?: CallOptionsExtended): RTCSession;
6
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jssip_1 = require("jssip");
4
+ class UAExtended extends jssip_1.UA {
5
+ call(target, options) {
6
+ return super.call(target, options);
7
+ }
8
+ }
9
+ exports.default = UAExtended;
@@ -0,0 +1,9 @@
1
+ import { ICall, MediaEvent } from '@/types/rtc';
2
+ import { Writeable } from '@/types/generic';
3
+ type ICallKey = keyof ICall;
4
+ declare const CALL_KEYS_TO_INCLUDE: Array<ICallKey>;
5
+ export type ICallSimplified = Writeable<Pick<ICall, typeof CALL_KEYS_TO_INCLUDE[number]>>;
6
+ export declare function simplifyCallObject(call: ICall): ICallSimplified;
7
+ export declare function processAudioVolume(stream: MediaStream, volume: number): MediaStream;
8
+ export declare function syncStream(event: MediaEvent, call: ICall, outputDevice: string, volume: number): void;
9
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncStream = exports.processAudioVolume = exports.simplifyCallObject = void 0;
4
+ const CALL_KEYS_TO_INCLUDE = [
5
+ 'roomId',
6
+ '_audioMuted',
7
+ '_cancel_reason',
8
+ '_contact',
9
+ 'direction',
10
+ '_end_time',
11
+ '_eventsCount',
12
+ '_from_tag',
13
+ '_id',
14
+ '_is_canceled',
15
+ '_is_confirmed',
16
+ '_late_sdp',
17
+ '_localHold',
18
+ '_videoMuted',
19
+ 'status',
20
+ 'start_time',
21
+ '_remote_identity',
22
+ 'audioTag',
23
+ //'audioQuality',
24
+ 'isOnHold',
25
+ //'originalStream',
26
+ 'localMuted'
27
+ ];
28
+ function simplifyCallObject(call) {
29
+ const simplified = {};
30
+ CALL_KEYS_TO_INCLUDE.forEach(key => {
31
+ if (call[key] !== undefined) {
32
+ simplified[key] = call[key];
33
+ }
34
+ });
35
+ simplified.localHold = call._localHold;
36
+ return simplified;
37
+ }
38
+ exports.simplifyCallObject = simplifyCallObject;
39
+ function processAudioVolume(stream, volume) {
40
+ const audioContext = new AudioContext();
41
+ const audioSource = audioContext.createMediaStreamSource(stream);
42
+ const audioDestination = audioContext.createMediaStreamDestination();
43
+ const gainNode = audioContext.createGain();
44
+ audioSource.connect(gainNode);
45
+ gainNode.connect(audioDestination);
46
+ gainNode.gain.value = volume;
47
+ return audioDestination.stream;
48
+ }
49
+ exports.processAudioVolume = processAudioVolume;
50
+ function syncStream(event, call, outputDevice, volume) {
51
+ const audio = document.createElement('audio');
52
+ audio.id = call._id;
53
+ audio.className = 'audioTag';
54
+ audio.srcObject = event.stream;
55
+ audio.setSinkId(outputDevice);
56
+ audio.volume = volume;
57
+ audio.play();
58
+ call.audioTag = audio;
59
+ }
60
+ exports.syncStream = syncStream;
@@ -0,0 +1,2 @@
1
+ import { ProbeMetricInType } from '@/types/webrtcmetrics';
2
+ export declare function filterObjectKeys(fullObj: ProbeMetricInType, keys: Array<keyof ProbeMetricInType>): ProbeMetricInType;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterObjectKeys = void 0;
4
+ function filterObjectKeys(fullObj, keys) {
5
+ return Object.keys(fullObj)
6
+ .filter((key) => keys.includes(key))
7
+ .reduce((obj, key) => {
8
+ const k = key;
9
+ //const o = obj as ProbeMetricInType
10
+ //o[k] = fullObj[k] //as ProbeMetricInType[keyof ProbeMetricInType]
11
+ return Object.assign(Object.assign({}, obj), { [k]: fullObj[k] });
12
+ }, {});
13
+ }
14
+ exports.filterObjectKeys = filterObjectKeys;
@@ -0,0 +1,16 @@
1
+ export interface ITimeData {
2
+ callId: string;
3
+ hours: number;
4
+ minutes: number;
5
+ seconds: number;
6
+ formatted: string;
7
+ }
8
+ export type TempTimeData = Omit<ITimeData, 'callId'> & {
9
+ callId: string | undefined;
10
+ };
11
+ export declare function setupTime(time: TempTimeData): {
12
+ seconds: number;
13
+ minutes: number;
14
+ hours: number;
15
+ formatted: string;
16
+ };
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupTime = void 0;
4
+ function formatTime(time) {
5
+ return time < 10 ? `0${time}` : `${time}`;
6
+ }
7
+ function setupTime(time) {
8
+ let hours = time.hours || 0;
9
+ let minutes = time.minutes || 0;
10
+ let seconds = time.seconds || 0;
11
+ seconds++;
12
+ if (seconds === 60) {
13
+ seconds = 0;
14
+ minutes++;
15
+ if (minutes === 60) {
16
+ minutes = 0;
17
+ hours++;
18
+ }
19
+ }
20
+ const formatted = `${formatTime(hours)}:${formatTime(minutes)}:${formatTime(seconds)}`;
21
+ return {
22
+ seconds,
23
+ minutes,
24
+ hours,
25
+ formatted
26
+ };
27
+ }
28
+ exports.setupTime = setupTime;
@@ -0,0 +1,2 @@
1
+ export declare const runIndicator: (stream: MediaStream, deviceId: string) => void;
2
+ export declare const clearVolumeInterval: () => void;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clearVolumeInterval = exports.runIndicator = void 0;
4
+ const height = 20;
5
+ const lineWidth = 4;
6
+ let interval = undefined;
7
+ const runIndicator = (stream, deviceId) => {
8
+ if (stream && stream.getTracks().length) {
9
+ //console.log('RUN INDICATOR IF')
10
+ getVolumeLevelBar(stream, deviceId);
11
+ }
12
+ else {
13
+ //console.log('RUN INDICATOR ELSE')
14
+ (0, exports.clearVolumeInterval)();
15
+ }
16
+ };
17
+ exports.runIndicator = runIndicator;
18
+ const clearVolumeInterval = () => {
19
+ clearInterval(interval);
20
+ };
21
+ exports.clearVolumeInterval = clearVolumeInterval;
22
+ const getMaxSmallIndicatorHeight = (value) => {
23
+ const halfLineHeight = height / 4;
24
+ return value < halfLineHeight ? value : halfLineHeight;
25
+ };
26
+ const getVolumeLevelBar = (stream, deviceId) => {
27
+ //console.log('IN GET VOLUME LEVEL BAR')
28
+ //console.log('TRACKS LENGTH', stream.getTracks().length)
29
+ clearInterval(interval);
30
+ const audioContext = new AudioContext();
31
+ const analyser = audioContext.createAnalyser();
32
+ const microphone = audioContext.createMediaStreamSource(stream);
33
+ const javascriptNode = audioContext.createScriptProcessor(2048, 1, 1);
34
+ analyser.smoothingTimeConstant = 0.8;
35
+ analyser.fftSize = 1024;
36
+ microphone.connect(analyser);
37
+ analyser.connect(javascriptNode);
38
+ javascriptNode.connect(audioContext.destination);
39
+ const canvas = document.getElementById(`canvas-${deviceId}`);
40
+ if (!canvas) {
41
+ return;
42
+ }
43
+ const indicatorWidth = lineWidth * 5;
44
+ const halfLineHeight = height / 2;
45
+ canvas.setAttribute('width', `${indicatorWidth}`);
46
+ canvas.setAttribute('height', `${height}`);
47
+ const canvasContext = canvas.getContext('2d');
48
+ interval = setInterval(() => {
49
+ if (!canvasContext) {
50
+ return;
51
+ }
52
+ const array = new Uint8Array(analyser.frequencyBinCount);
53
+ analyser.getByteFrequencyData(array);
54
+ let values = 0;
55
+ const length = array.length;
56
+ for (let i = 0; i < length; i++) {
57
+ values += (array[i]);
58
+ }
59
+ const average = values / length;
60
+ //console.log('average', average)
61
+ canvasContext.fillStyle = 'blue'; //getComputedStyle(document.body).getPropertyValue('--primary-actions')
62
+ const halfValue = average / 2;
63
+ canvasContext.clearRect(0, halfLineHeight, lineWidth, halfLineHeight);
64
+ canvasContext.fillRect(0, halfLineHeight, lineWidth, getMaxSmallIndicatorHeight(halfValue));
65
+ canvasContext.clearRect(0, halfLineHeight, lineWidth, -halfLineHeight);
66
+ canvasContext.fillRect(0, halfLineHeight, lineWidth, 0 - getMaxSmallIndicatorHeight(halfValue));
67
+ canvasContext.clearRect(lineWidth * 2, halfLineHeight, lineWidth, halfLineHeight);
68
+ canvasContext.fillRect(lineWidth * 2, halfLineHeight, lineWidth, average);
69
+ canvasContext.clearRect(lineWidth * 2, halfLineHeight, lineWidth, -halfLineHeight);
70
+ canvasContext.fillRect(lineWidth * 2, halfLineHeight, lineWidth, 0 - average);
71
+ canvasContext.clearRect(lineWidth * 4, halfLineHeight, lineWidth, halfLineHeight);
72
+ canvasContext.fillRect(lineWidth * 4, halfLineHeight, lineWidth, getMaxSmallIndicatorHeight(halfValue));
73
+ canvasContext.clearRect(lineWidth * 4, halfLineHeight, lineWidth, -halfLineHeight);
74
+ canvasContext.fillRect(lineWidth * 4, halfLineHeight, lineWidth, 0 - getMaxSmallIndicatorHeight(halfValue));
75
+ }, 200);
76
+ };
@@ -0,0 +1,32 @@
1
+ export default class Collector {
2
+ constructor(cfg: any, refProbeId: any);
3
+ _callbacks: {
4
+ onreport: null;
5
+ onticket: null;
6
+ };
7
+ _id: string;
8
+ _moduleName: string;
9
+ _probeId: any;
10
+ _config: any;
11
+ _exporter: Exporter;
12
+ _state: string;
13
+ analyze(stats: any, previousReport: any, beforeLastReport: any, referenceReport: any): any;
14
+ takeReferenceStats(): Promise<any>;
15
+ collectStats(): Promise<any>;
16
+ start(): Promise<void>;
17
+ set state(arg: string);
18
+ get state(): string;
19
+ _startedTime: Date | undefined;
20
+ mute(): Promise<void>;
21
+ unmute(): Promise<void>;
22
+ stop(forced: any): Promise<void>;
23
+ _stoppedTime: Date | undefined;
24
+ registerCallback(name: any, callback: any, context: any): void;
25
+ unregisterCallback(name: any): void;
26
+ fireOnReport(report: any): void;
27
+ fireOnTicket(ticket: any): void;
28
+ updateConfig(config: any): void;
29
+ addCustomEvent(at: any, category: any, name: any, description: any): void;
30
+ registerToPCEvents(): Promise<void>;
31
+ }
32
+ import Exporter from "./exporter";
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const exporter_1 = __importDefault(require("./exporter"));
16
+ const extractor_1 = require("./extractor");
17
+ const score_1 = require("./utils/score");
18
+ const models_1 = require("./utils/models");
19
+ const helper_1 = require("./utils/helper");
20
+ const log_1 = require("./utils/log");
21
+ class Collector {
22
+ constructor(cfg, refProbeId) {
23
+ this._callbacks = {
24
+ onreport: null,
25
+ onticket: null,
26
+ };
27
+ this._id = (0, helper_1.createCollectorId)();
28
+ this._moduleName = this._id;
29
+ this._probeId = refProbeId;
30
+ this._config = cfg;
31
+ this._exporter = new exporter_1.default(cfg);
32
+ this._state = models_1.COLLECTOR_STATE.IDLE;
33
+ this.registerToPCEvents();
34
+ (0, log_1.info)(this._moduleName, `new collector created for probe ${this._probeId}`);
35
+ }
36
+ analyze(stats, previousReport, beforeLastReport, referenceReport) {
37
+ const getDefaultSSRCMetric = (kind, reportType) => {
38
+ if (kind === models_1.VALUE.AUDIO) {
39
+ if (reportType === models_1.TYPE.INBOUND_RTP) {
40
+ return Object.assign({}, models_1.defaultAudioMetricIn);
41
+ }
42
+ return Object.assign({}, models_1.defaultAudioMetricOut);
43
+ }
44
+ if (reportType === models_1.TYPE.INBOUND_RTP) {
45
+ return Object.assign({}, models_1.defaultVideoMetricIn);
46
+ }
47
+ return Object.assign({}, models_1.defaultVideoMetricOut);
48
+ };
49
+ const report = (0, models_1.getDefaultMetric)(previousReport);
50
+ report.pname = this._config.pname;
51
+ report.call_id = this._config.cid;
52
+ report.user_id = this._config.uid;
53
+ report.count = previousReport ? previousReport.count + 1 : 1;
54
+ let timestamp = null;
55
+ stats.forEach((stat) => {
56
+ if (!timestamp && stat.timestamp) {
57
+ timestamp = stat.timestamp;
58
+ }
59
+ const values = (0, extractor_1.extract)(stat, report, report.pname, referenceReport);
60
+ values.forEach((data) => {
61
+ if (data.value && data.type) {
62
+ if (data.ssrc) {
63
+ let ssrcReport = report[data.type][data.ssrc];
64
+ if (!ssrcReport) {
65
+ ssrcReport = getDefaultSSRCMetric(data.type, stat.type);
66
+ ssrcReport.ssrc = data.ssrc;
67
+ report[data.type][data.ssrc] = (ssrcReport);
68
+ }
69
+ Object.keys(data.value).forEach((key) => {
70
+ ssrcReport[key] = data.value[key];
71
+ });
72
+ }
73
+ else {
74
+ Object.keys(data.value).forEach((key) => {
75
+ report[data.type][key] = data.value[key];
76
+ });
77
+ }
78
+ }
79
+ });
80
+ });
81
+ report.timestamp = timestamp;
82
+ Object.keys(report[models_1.VALUE.AUDIO]).forEach((key) => {
83
+ const ssrcReport = report[models_1.VALUE.AUDIO][key];
84
+ if (ssrcReport.direction === models_1.DIRECTION.INBOUND) {
85
+ ssrcReport.mos_emodel_in = (0, score_1.computeEModelMOS)(report, models_1.VALUE.AUDIO, previousReport, beforeLastReport, ssrcReport.ssrc);
86
+ ssrcReport.mos_in = (0, score_1.computeMOS)(report, models_1.VALUE.AUDIO, previousReport, beforeLastReport, ssrcReport.ssrc);
87
+ }
88
+ else {
89
+ ssrcReport.mos_emodel_out = (0, score_1.computeEModelMOSForOutgoing)(report, models_1.VALUE.AUDIO, previousReport, beforeLastReport, ssrcReport.ssrc);
90
+ ssrcReport.mos_out = (0, score_1.computeMOSForOutgoing)(report, models_1.VALUE.AUDIO, previousReport, beforeLastReport, ssrcReport.ssrc);
91
+ }
92
+ });
93
+ return report;
94
+ }
95
+ takeReferenceStats() {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ return new Promise((resolve, reject) => {
98
+ const preWaitTime = Date.now();
99
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () {
100
+ try {
101
+ const waitTime = Date.now() - preWaitTime;
102
+ const preTime = Date.now();
103
+ const reports = yield this._config.pc.getStats();
104
+ const referenceReport = this.analyze(reports, null, null, null);
105
+ const postTime = Date.now();
106
+ referenceReport.experimental.time_to_measure_ms = postTime - preTime;
107
+ referenceReport.experimental.time_to_wait_ms = waitTime;
108
+ this._exporter.saveReferenceReport(referenceReport);
109
+ (0, log_1.debug)(this._moduleName, `got reference report for probe ${this._probeId}`);
110
+ resolve();
111
+ }
112
+ catch (err) {
113
+ reject(err);
114
+ }
115
+ }), this._config.startAfter);
116
+ });
117
+ });
118
+ }
119
+ collectStats() {
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ try {
122
+ if (this._state !== models_1.COLLECTOR_STATE.RUNNING || !this._config.pc) {
123
+ (0, log_1.debug)(this._moduleName, `report discarded (too late) for probe ${this._probeId}`);
124
+ return null;
125
+ }
126
+ // Take into account last report in case no report have been generated (eg: candidate-pair)
127
+ const preTime = Date.now();
128
+ const reports = yield this._config.pc.getStats();
129
+ const report = this.analyze(reports, this._exporter.getLastReport(), this._exporter.getBeforeLastReport(), this._exporter.getReferenceReport());
130
+ const postTime = Date.now();
131
+ report.experimental.time_to_measure_ms = postTime - preTime;
132
+ this._exporter.addReport(report);
133
+ (0, log_1.debug)(this._moduleName, `got report for probe ${this._probeId}#${this._exporter.getReportsNumber() + 1}`);
134
+ this.fireOnReport(report);
135
+ return report;
136
+ }
137
+ catch (err) {
138
+ (0, log_1.error)(this._moduleName, `got error ${err}`);
139
+ return null;
140
+ }
141
+ });
142
+ }
143
+ start() {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ (0, log_1.debug)(this._moduleName, "starting");
146
+ this.state = models_1.COLLECTOR_STATE.RUNNING;
147
+ this._startedTime = this._exporter.start();
148
+ (0, log_1.debug)(this._moduleName, "started");
149
+ });
150
+ }
151
+ mute() {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ this.state = models_1.COLLECTOR_STATE.MUTED;
154
+ (0, log_1.debug)(this._moduleName, "muted");
155
+ });
156
+ }
157
+ unmute() {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ this.state = models_1.COLLECTOR_STATE.RUNNING;
160
+ (0, log_1.debug)(this._moduleName, "unmuted");
161
+ });
162
+ }
163
+ stop(forced) {
164
+ return __awaiter(this, void 0, void 0, function* () {
165
+ (0, log_1.debug)(this._moduleName, `stopping${forced ? " by watchdog" : ""}...`);
166
+ this._stoppedTime = this._exporter.stop();
167
+ this.state = models_1.COLLECTOR_STATE.IDLE;
168
+ if (this._config.ticket) {
169
+ const { ticket } = this._exporter;
170
+ this.fireOnTicket(ticket);
171
+ }
172
+ this._exporter.reset();
173
+ (0, log_1.debug)(this._moduleName, "stopped");
174
+ });
175
+ }
176
+ registerCallback(name, callback, context) {
177
+ if (name in this._callbacks) {
178
+ this._callbacks[name] = {
179
+ callback,
180
+ context,
181
+ };
182
+ (0, log_1.debug)(this._moduleName, `registered callback '${name}'`);
183
+ }
184
+ else {
185
+ (0, log_1.error)(this._moduleName, `can't register callback for '${name}' - not found`);
186
+ }
187
+ }
188
+ unregisterCallback(name) {
189
+ if (name in this._callbacks) {
190
+ this._callbacks[name] = null;
191
+ delete this._callbacks[name];
192
+ (0, log_1.debug)(this._moduleName, `unregistered callback '${name}'`);
193
+ }
194
+ else {
195
+ (0, log_1.error)(this._moduleName, `can't unregister callback for '${name}' - not found`);
196
+ }
197
+ }
198
+ fireOnReport(report) {
199
+ if (this._callbacks.onreport) {
200
+ (0, helper_1.call)(this._callbacks.onreport.callback, this._callbacks.onreport.context, report);
201
+ }
202
+ }
203
+ fireOnTicket(ticket) {
204
+ if (this._callbacks.onticket) {
205
+ (0, helper_1.call)(this._callbacks.onticket.callback, this._callbacks.onticket.context, ticket);
206
+ }
207
+ }
208
+ updateConfig(config) {
209
+ this._config = config;
210
+ this._exporter.updateConfig(config);
211
+ }
212
+ get state() {
213
+ return this._state;
214
+ }
215
+ set state(newState) {
216
+ this._state = newState;
217
+ (0, log_1.debug)(this._moduleName, `state changed to ${newState}`);
218
+ }
219
+ addCustomEvent(at, category, name, description) {
220
+ this._exporter.addCustomEvent({
221
+ at: typeof at === "object" ? at.toJSON() : at,
222
+ category,
223
+ name,
224
+ description,
225
+ });
226
+ }
227
+ registerToPCEvents() {
228
+ return __awaiter(this, void 0, void 0, function* () {
229
+ const { pc } = this._config;
230
+ navigator.mediaDevices.ondevicechange = () => __awaiter(this, void 0, void 0, function* () {
231
+ try {
232
+ const devices = yield navigator.mediaDevices.enumerateDevices();
233
+ this.addCustomEvent(new Date().toJSON(), "device", `${devices.length} devices found`, "Media Devices state");
234
+ // eslint-disable-next-line no-empty
235
+ }
236
+ catch (err) {
237
+ (0, log_1.error)(this._moduleName, "can't get devices");
238
+ }
239
+ });
240
+ if (pc) {
241
+ pc.oniceconnectionstatechange = () => {
242
+ const value = pc.iceConnectionState;
243
+ if (value === models_1.ICE_CONNECTION_STATE.CONNECTED ||
244
+ value === models_1.ICE_CONNECTION_STATE.COMPLETED) {
245
+ this.addCustomEvent(new Date().toJSON(), "call", value, "ICE connection state");
246
+ }
247
+ else if (value === models_1.ICE_CONNECTION_STATE.DISCONNECTED ||
248
+ value === models_1.ICE_CONNECTION_STATE.FAILED) {
249
+ this.addCustomEvent(new Date().toJSON(), "call", value, "ICE connection state");
250
+ }
251
+ else if (value === models_1.ICE_CONNECTION_STATE.CLOSED) {
252
+ this.addCustomEvent(new Date().toJSON(), "call", "ended", "ICE connection state");
253
+ }
254
+ };
255
+ pc.onicegatheringstatechange = () => {
256
+ const value = pc.iceGatheringState;
257
+ this.addCustomEvent(new Date().toJSON(), "call", value, "ICE gathering state");
258
+ };
259
+ pc.ontrack = (e) => {
260
+ this.addCustomEvent(new Date().toJSON(), "call", `${e.track.kind}track`, "MediaStreamTrack received");
261
+ };
262
+ pc.onnegotiationneeded = () => {
263
+ this.addCustomEvent(new Date().toJSON(), "call", "negotiation", "Media changed");
264
+ };
265
+ const receivers = pc.getReceivers();
266
+ if (receivers && receivers.length > 0) {
267
+ const receiver = receivers[0];
268
+ const { transport } = receiver;
269
+ if (transport) {
270
+ const { iceTransport } = transport;
271
+ if (iceTransport) {
272
+ iceTransport.onselectedcandidatepairchange = () => {
273
+ this.addCustomEvent(new Date().toJSON(), "call", "transport", "Candidates Pair changed");
274
+ };
275
+ }
276
+ }
277
+ }
278
+ }
279
+ });
280
+ }
281
+ }
282
+ exports.default = Collector;