@observertc/observer-js 0.41.6 → 0.42.0

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 (61) hide show
  1. package/lib/ObservedCall.d.ts +10 -3
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +28 -0
  4. package/lib/ObservedClient.d.ts +4 -0
  5. package/lib/ObservedClient.d.ts.map +1 -1
  6. package/lib/ObservedClient.js +81 -32
  7. package/lib/ObservedInboundAudioTrack.d.ts +94 -0
  8. package/lib/ObservedInboundAudioTrack.d.ts.map +1 -0
  9. package/lib/ObservedInboundAudioTrack.js +215 -0
  10. package/lib/ObservedInboundVideoTrack.d.ts +98 -0
  11. package/lib/ObservedInboundVideoTrack.d.ts.map +1 -0
  12. package/lib/{ObservedInboundTrack.js → ObservedInboundVideoTrack.js} +53 -44
  13. package/lib/ObservedOutboundAudioTrack.d.ts +85 -0
  14. package/lib/ObservedOutboundAudioTrack.d.ts.map +1 -0
  15. package/lib/{ObservedOutboundTrack.js → ObservedOutboundAudioTrack.js} +35 -58
  16. package/lib/ObservedOutboundVideoTrack.d.ts +88 -0
  17. package/lib/ObservedOutboundVideoTrack.d.ts.map +1 -0
  18. package/lib/ObservedOutboundVideoTrack.js +204 -0
  19. package/lib/ObservedPeerConnection.d.ts +26 -16
  20. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  21. package/lib/ObservedPeerConnection.js +157 -86
  22. package/lib/RemoteTrackAssigner.d.ts +14 -0
  23. package/lib/RemoteTrackAssigner.d.ts.map +1 -0
  24. package/lib/RemoteTrackAssigner.js +52 -0
  25. package/lib/common/CalculatedScore.d.ts +79 -0
  26. package/lib/common/CalculatedScore.d.ts.map +1 -0
  27. package/lib/common/CalculatedScore.js +155 -0
  28. package/lib/common/CallEventType.d.ts +13 -1
  29. package/lib/common/CallEventType.d.ts.map +1 -1
  30. package/lib/common/CallEventType.js +12 -0
  31. package/lib/common/QualityScoreUtils.d.ts +1 -0
  32. package/lib/common/QualityScoreUtils.d.ts.map +1 -0
  33. package/lib/common/QualityScoreUtils.js +1 -0
  34. package/lib/common/utils.d.ts +1 -0
  35. package/lib/common/utils.d.ts.map +1 -1
  36. package/lib/common/utils.js +14 -1
  37. package/lib/index.d.ts +4 -2
  38. package/lib/index.d.ts.map +1 -1
  39. package/lib/monitors/ClientQualityMonitor.d.ts +1 -0
  40. package/lib/monitors/ClientQualityMonitor.d.ts.map +1 -0
  41. package/lib/monitors/ClientQualityMonitor.js +1 -0
  42. package/package.json +1 -1
  43. package/src/ObservedCall.ts +40 -4
  44. package/src/ObservedClient.ts +77 -17
  45. package/src/ObservedInboundAudioTrack.ts +336 -0
  46. package/src/{ObservedInboundTrack.ts → ObservedInboundVideoTrack.ts} +85 -66
  47. package/src/{ObservedOutboundTrack.ts → ObservedOutboundAudioTrack.ts} +59 -74
  48. package/src/ObservedOutboundVideoTrack.ts +326 -0
  49. package/src/ObservedPeerConnection.ts +205 -131
  50. package/src/RemoteTrackAssigner.ts +68 -0
  51. package/src/common/CalculatedScore.ts +180 -0
  52. package/src/common/CallEventType.ts +14 -1
  53. package/src/common/QualityScoreUtils.ts +0 -0
  54. package/src/common/utils.ts +17 -1
  55. package/src/index.ts +4 -2
  56. package/src/monitors/CallSummaryMonitor.ts +3 -3
  57. package/src/monitors/ClientQualityMonitor.ts +0 -0
  58. package/lib/ObservedInboundTrack.d.ts +0 -96
  59. package/lib/ObservedInboundTrack.d.ts.map +0 -1
  60. package/lib/ObservedOutboundTrack.d.ts +0 -84
  61. package/lib/ObservedOutboundTrack.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=QualityScoreUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QualityScoreUtils.d.ts","sourceRoot":"","sources":["../../src/common/QualityScoreUtils.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ "use strict";
@@ -5,4 +5,5 @@ export type Writable<T> = T extends object ? {
5
5
  export declare function iteratorConverter<T>(generator: Generator<T, void, undefined>): IterableIterator<T>;
6
6
  export declare function asyncIteratorConverter<T>(generator: AsyncGenerator<T, void, undefined>): AsyncIterableIterator<T>;
7
7
  export declare function isValidUuid(str: string): boolean;
8
+ export declare function getMedian(arr: number[]): number;
8
9
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/common/utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAE/E,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,GAAG;IAAE,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,CAAC,CAAC;AAE9F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CASlG;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CASjH;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIhD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/common/utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAE/E,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,GAAG;IAAE,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,CAAC,CAAC;AAE9F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CASlG;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CASjH;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIhD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAc/C"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isValidUuid = exports.asyncIteratorConverter = exports.iteratorConverter = void 0;
3
+ exports.getMedian = exports.isValidUuid = exports.asyncIteratorConverter = exports.iteratorConverter = void 0;
4
4
  function iteratorConverter(generator) {
5
5
  return {
6
6
  [Symbol.iterator]() {
@@ -28,3 +28,16 @@ function isValidUuid(str) {
28
28
  return regexp.test(str);
29
29
  }
30
30
  exports.isValidUuid = isValidUuid;
31
+ function getMedian(arr) {
32
+ // Sort the array in ascending order
33
+ const sortedArr = arr.slice().sort((a, b) => a - b);
34
+ // Calculate the middle index
35
+ const mid = Math.floor(sortedArr.length / 2);
36
+ // If the array length is odd, return the middle element
37
+ if (sortedArr.length % 2 !== 0) {
38
+ return sortedArr[mid];
39
+ }
40
+ // If the array length is even, return the average of the two middle elements
41
+ return (sortedArr[mid - 1] + sortedArr[mid]) / 2;
42
+ }
43
+ exports.getMedian = getMedian;
package/lib/index.d.ts CHANGED
@@ -9,8 +9,10 @@ export type { ClientIssueMonitorConfig, ClientIssueMonitorEmittedIssueEvent, Cli
9
9
  export type { ObservedCall, ObservedCallModel } from './ObservedCall';
10
10
  export type { ObservedClient, ObservedClientModel } from './ObservedClient';
11
11
  export type { ObservedPeerConnection, ObservedPeerConnectionModel, ObservedPeerConnectionEvents } from './ObservedPeerConnection';
12
- export type { ObservedInboundTrack, ObservedInboundTrackModel, ObservedInboundTrackEvents } from './ObservedInboundTrack';
13
- export type { ObservedOutboundTrack, ObservedOutboundTrackModel, ObservedOutboundTrackEvents } from './ObservedOutboundTrack';
12
+ export type { ObservedInboundAudioTrack, ObservedInboundAudioTrackModel, ObservedInboundAudioTrackEvents } from './ObservedInboundAudioTrack';
13
+ export type { ObservedOutboundAudioTrack, ObservedOutboundAudioTrackModel, ObservedOutboundAudioTrackEvents } from './ObservedOutboundAudioTrack';
14
+ export type { ObservedInboundVideoTrack, ObservedInboundVideoTrackModel, ObservedInboundVideoTrackEvents } from './ObservedInboundVideoTrack';
15
+ export type { ObservedOutboundVideoTrack, ObservedOutboundVideoTrackModel, ObservedOutboundVideoTrackEvents } from './ObservedOutboundVideoTrack';
14
16
  export type { CallSummary, ClientSummary, ClientIssue } from './monitors/CallSummary';
15
17
  export type { ObserverSinkContext } from './common/types';
16
18
  export type { SfuServerMonitorMetricsRecord } from './monitors/SfuServerMonitor';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAC;AAE9D,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAExD,YAAY,EACX,OAAO,EACP,YAAY,EACZ,SAAS,EACT,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,WAAW,GACX,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACX,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,EAC7B,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACX,wBAAwB,EACxB,mCAAmC,EACnC,kBAAkB,EAClB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,YAAY,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAClI,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAC1H,YAAY,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAC9H,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACtF,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACjF,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAClH,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,yBAAyB,EACzB,mCAAmC,EACnC,kCAAkC,EAClC,oCAAoC,GACpC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEpG,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACtD,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,YAE9D;AAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAA8B,MAAM,iBAAiB,CAAC;AAC/E,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAG3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAC;AAE9D,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAExD,YAAY,EACX,OAAO,EACP,YAAY,EACZ,SAAS,EACT,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,WAAW,GACX,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACX,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,EAC7B,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACX,wBAAwB,EACxB,mCAAmC,EACnC,kBAAkB,EAClB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,YAAY,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAClI,YAAY,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAC9I,YAAY,EAAE,0BAA0B,EAAE,+BAA+B,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAClJ,YAAY,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAC9I,YAAY,EAAE,0BAA0B,EAAE,+BAA+B,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAClJ,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACtF,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACjF,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAClH,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,yBAAyB,EACzB,mCAAmC,EACnC,kCAAkC,EAClC,oCAAoC,GACpC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEpG,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACtD,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,YAE9D;AAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAA8B,MAAM,iBAAiB,CAAC;AAC/E,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAG3D"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=ClientQualityMonitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientQualityMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/ClientQualityMonitor.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ "use strict";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@observertc/observer-js",
3
- "version": "0.41.6",
3
+ "version": "0.42.0",
4
4
  "description": "Server Side NodeJS Library for processing ObserveRTC Samples",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,13 +1,15 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { ObservedClient, ObservedClientEvents, ObservedClientModel } from './ObservedClient';
3
- import { ObservedOutboundTrack } from './ObservedOutboundTrack';
4
3
  import { Observer } from './Observer';
5
4
  import { createClientJoinedEventReport } from './common/callEventReports';
6
- import { PartialBy } from './common/utils';
5
+ import { getMedian, PartialBy } from './common/utils';
7
6
  import { CallEventReport } from '@observertc/report-schemas-js';
8
7
  import { createProcessor } from './common/Middleware';
9
8
  import { ClientSample } from '@observertc/sample-schemas-js';
10
9
  import { ClientIssue } from './monitors/CallSummary';
10
+ import { ObservedOutboundAudioTrack } from './ObservedOutboundAudioTrack';
11
+ import { ObservedOutboundVideoTrack } from './ObservedOutboundVideoTrack';
12
+ import { CalculatedScore } from './common/CalculatedScore';
11
13
 
12
14
  type ClientIssueDetectionConfig = Pick<ClientIssue, 'severity'>;
13
15
 
@@ -38,8 +40,13 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
38
40
 
39
41
  private readonly _clients = new Map<string, ObservedClient>();
40
42
 
41
- public readonly sfuStreamIdToOutboundAudioTrack = new Map<string | number, ObservedOutboundTrack<'audio'>>();
42
- public readonly sfuStreamIdToOutboundVideoTrack = new Map<string | number, ObservedOutboundTrack<'video'>>();
43
+ public _scoreData?: {
44
+ score: CalculatedScore,
45
+ calculated: number,
46
+ };
47
+
48
+ public readonly sfuStreamIdToOutboundAudioTrack = new Map<string | number, ObservedOutboundAudioTrack>();
49
+ public readonly sfuStreamIdToOutboundVideoTrack = new Map<string | number, ObservedOutboundVideoTrack>();
43
50
  private _closed = false;
44
51
  private _updated = Date.now();
45
52
  private _ended?: number;
@@ -84,6 +91,35 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
84
91
  return this._clients;
85
92
  }
86
93
 
94
+ public get score(): CalculatedScore | undefined {
95
+ const now = Date.now();
96
+
97
+ if (now - 15000 < (this._scoreData?.calculated ?? 0)) {
98
+ return this._scoreData?.score;
99
+ } else if (this.clients.size < 1) {
100
+ return {
101
+ remarks: [ {
102
+ severity: 'none',
103
+ text: 'No clients',
104
+ } ],
105
+ score: 5.0,
106
+ timestamp: now,
107
+ };
108
+ }
109
+ const scores = [ ...this.clients.values() ].map((client) => client.score?.score ?? 5.0);
110
+
111
+ this._scoreData = {
112
+ score: {
113
+ remarks: [],
114
+ score: getMedian(scores),
115
+ timestamp: now,
116
+ },
117
+ calculated: now,
118
+ };
119
+
120
+ return this._scoreData.score;
121
+ }
122
+
87
123
  public get closed() {
88
124
  return this._closed;
89
125
  }
@@ -11,6 +11,7 @@ import { CallEventType } from './common/CallEventType';
11
11
  import { ObservedSfu } from './ObservedSfu';
12
12
  import { ClientIssue } from './monitors/CallSummary';
13
13
  import { CallEventReport } from '@observertc/report-schemas-js';
14
+ import { CalculatedScore } from './common/CalculatedScore';
14
15
 
15
16
  const logger = createLogger('ObservedClient');
16
17
 
@@ -59,6 +60,7 @@ export type ObservedClientEvents = {
59
60
  rejoined: [{
60
61
  lastJoined: number,
61
62
  }],
63
+ score: [CalculatedScore],
62
64
  };
63
65
 
64
66
  export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
@@ -84,6 +86,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
84
86
  private _timeZoneOffsetInHours?: number;
85
87
  private _left?: number;
86
88
 
89
+ public score?: CalculatedScore;
87
90
  public usingTURN = false;
88
91
  public availableOutgoingBitrate = 0;
89
92
  public availableIncomingBitrate = 0;
@@ -238,7 +241,6 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
238
241
  if (!this._left) {
239
242
  this._addClientLeftReport();
240
243
  }
241
-
242
244
  Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
243
245
 
244
246
  this.emit('close');
@@ -302,8 +304,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
302
304
  logger.warn(`Error adding client issue: ${(err as Error)?.message}`);
303
305
  }
304
306
 
305
- this.issues.push(issue);
306
- this.emit('issue', issue);
307
+ this._addAndEmitIssue(issue);
307
308
  }
308
309
 
309
310
  public accept(sample: ClientSample): void {
@@ -515,9 +516,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
515
516
  trackId: callEvent.mediaTrackId,
516
517
  attachments: callEvent.attachments ? JSON.parse(callEvent.attachments) : undefined,
517
518
  };
518
-
519
- this.issues.push(issue);
520
- this.emit('issue', issue);
519
+
520
+ this._addAndEmitIssue(issue);
521
521
  } catch (err) {
522
522
  logger.warn(`Error parsing client issue: ${(err as Error)?.message}`);
523
523
  }
@@ -619,7 +619,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
619
619
  type: CallMetaType.MEDIA_DEVICE,
620
620
  payload: mediaDevice,
621
621
  }, this.userId);
622
-
622
+
623
623
  this.reports.addCallMetaReport(callMetaReport);
624
624
  mediaDevice.label && this.mediaDevices.push(mediaDevice.label);
625
625
  }
@@ -634,7 +634,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
634
634
  type: CallMetaType.MEDIA_SOURCE,
635
635
  payload: mediaSource,
636
636
  }, this.userId);
637
-
637
+
638
638
  this.reports.addCallMetaReport(callMetaReport);
639
639
  }
640
640
 
@@ -670,10 +670,17 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
670
670
 
671
671
  inboundAudioTrack.update(track, sample.timestamp);
672
672
 
673
- const remoteOutboundTrack = this.call.sfuStreamIdToOutboundAudioTrack.get(track.sfuStreamId ?? '');
674
-
675
- if (remoteOutboundTrack && inboundAudioTrack.sfuStreamId && !remoteOutboundTrack.remoteInboundTracks.has(inboundAudioTrack.sfuStreamId)) {
676
- remoteOutboundTrack.connectInboundTrack(inboundAudioTrack);
673
+ if (!inboundAudioTrack.remoteOutboundTrack) {
674
+ const remoteOutboundTrack = this.call.sfuStreamIdToOutboundAudioTrack.get(inboundAudioTrack.sfuStreamId ?? '');
675
+
676
+ if (remoteOutboundTrack) {
677
+ inboundAudioTrack.remoteOutboundTrack = remoteOutboundTrack;
678
+
679
+ inboundAudioTrack.once('close', () => {
680
+ remoteOutboundTrack?.remoteInboundTracks.delete(inboundAudioTrack.trackId ?? '');
681
+ });
682
+ remoteOutboundTrack.remoteInboundTracks.set(inboundAudioTrack.trackId ?? '', inboundAudioTrack);
683
+ }
677
684
  }
678
685
  } catch (err) {
679
686
  logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
@@ -705,11 +712,18 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
705
712
  });
706
713
 
707
714
  inboundVideoTrack.update(track, sample.timestamp);
708
-
709
- const remoteOutboundTrack = this.call.sfuStreamIdToOutboundVideoTrack.get(inboundVideoTrack.sfuStreamId ?? '');
710
-
711
- if (remoteOutboundTrack && inboundVideoTrack.sfuStreamId && !remoteOutboundTrack.remoteInboundTracks.has(inboundVideoTrack.sfuStreamId)) {
712
- remoteOutboundTrack.connectInboundTrack(inboundVideoTrack);
715
+
716
+ if (!inboundVideoTrack.remoteOutboundTrack) {
717
+ const remoteOutboundTrack = this.call.sfuStreamIdToOutboundVideoTrack.get(inboundVideoTrack.sfuStreamId ?? '');
718
+
719
+ if (remoteOutboundTrack) {
720
+ inboundVideoTrack.remoteOutboundTrack = remoteOutboundTrack;
721
+
722
+ inboundVideoTrack.once('close', () => {
723
+ remoteOutboundTrack?.remoteInboundTracks.delete(inboundVideoTrack.trackId ?? '');
724
+ });
725
+ remoteOutboundTrack.remoteInboundTracks.set(inboundVideoTrack.trackId ?? '', inboundVideoTrack);
726
+ }
713
727
  }
714
728
  } catch (err) {
715
729
  logger.error(`Error creating inbound video track: ${(err as Error)?.message}`);
@@ -734,6 +748,11 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
734
748
  });
735
749
 
736
750
  outboundAudioTrack.update(track, sample.timestamp);
751
+
752
+ if (outboundAudioTrack.sfuStreamId && !this.call.sfuStreamIdToOutboundAudioTrack.has(outboundAudioTrack.sfuStreamId)) {
753
+ this.call.sfuStreamIdToOutboundAudioTrack.set(outboundAudioTrack.sfuStreamId, outboundAudioTrack);
754
+ }
755
+
737
756
  } catch (err) {
738
757
  logger.error(`Error creating outbound audio track: ${(err as Error)?.message}`);
739
758
  }
@@ -759,6 +778,10 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
759
778
 
760
779
  outboundVideoTrack.update(track, sample.timestamp);
761
780
 
781
+ if (outboundVideoTrack.sfuStreamId && !this.call.sfuStreamIdToOutboundVideoTrack.has(outboundVideoTrack.sfuStreamId)) {
782
+ this.call.sfuStreamIdToOutboundVideoTrack.set(outboundVideoTrack.sfuStreamId, outboundVideoTrack);
783
+ }
784
+
762
785
  } catch (err) {
763
786
  logger.error(`Error creating outbound video track: ${(err as Error)?.message}`);
764
787
  }
@@ -897,6 +920,9 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
897
920
  let sumRttInMs = 0;
898
921
  let anyPeerConnectionUsingTurn = false;
899
922
 
923
+ let minPcScore = Number.MAX_SAFE_INTEGER;
924
+ let maxPcScore = Number.MIN_SAFE_INTEGER;
925
+
900
926
  for (const peerConnection of this._peerConnections.values()) {
901
927
  if (peerConnection.closed) continue;
902
928
  peerConnection.updateMetrics();
@@ -916,8 +942,21 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
916
942
  sumRttInMs += peerConnection.avgRttInMs ?? 0;
917
943
 
918
944
  anyPeerConnectionUsingTurn ||= peerConnection.usingTURN;
945
+
946
+ minPcScore = Math.min(minPcScore, peerConnection.score?.score ?? Number.MAX_SAFE_INTEGER);
947
+ maxPcScore = Math.max(maxPcScore, peerConnection.score?.score ?? Number.MIN_SAFE_INTEGER);
919
948
  }
920
949
 
950
+ this.score = {
951
+ score: minPcScore,
952
+ remarks: [ {
953
+ severity: 'none',
954
+ text: `Min and max score of all peer connections: ${minPcScore}, ${maxPcScore}`,
955
+ } ],
956
+ timestamp: sample.timestamp,
957
+ };
958
+ this.emit('score', this.score);
959
+
921
960
  this.usingTURN = anyPeerConnectionUsingTurn === true;
922
961
 
923
962
  if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
@@ -951,6 +990,27 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
951
990
  });
952
991
  }
953
992
 
993
+ private _addAndEmitIssue(issue: ClientIssue) {
994
+ this.issues.push(issue);
995
+
996
+ if (issue.peerConnectionId) {
997
+ const peerConnection = this._peerConnections.get(issue.peerConnectionId);
998
+
999
+ if (peerConnection) {
1000
+
1001
+ if (issue.trackId) {
1002
+ const track = peerConnection.inboundAudioTracks.get(issue.trackId) ?? peerConnection.inboundVideoTracks.get(issue.trackId);
1003
+
1004
+ if (track) track.ωpendingIssuesForScores.push(issue);
1005
+ } else {
1006
+ peerConnection.ωpendingIssuesForScores.push(issue);
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ this.emit('issue', issue);
1012
+ }
1013
+
954
1014
  private _createPeerConnection(peerConnectionId: string, label?: string): ObservedPeerConnection {
955
1015
  const result = new ObservedPeerConnection({
956
1016
  peerConnectionId,