@livedigital/client 2.26.0-stop-streams.2 → 2.26.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 (48) hide show
  1. package/dist/engine/DefaultEngineDependenciesFactory.d.ts +1 -1
  2. package/dist/engine/index.d.ts +0 -1
  3. package/dist/engine/media/tracks/BaseTrack.d.ts +0 -3
  4. package/dist/index.es.js +1 -1
  5. package/dist/index.js +2 -2
  6. package/dist/types/common.d.ts +0 -4
  7. package/dist/types/engine.d.ts +1 -2
  8. package/package.json +3 -2
  9. package/src/engine/DefaultEngineDependenciesFactory.ts +15 -14
  10. package/src/engine/index.ts +19 -22
  11. package/src/engine/media/index.ts +9 -16
  12. package/src/engine/media/tracks/BaseTrack.ts +5 -22
  13. package/src/types/common.ts +0 -5
  14. package/src/types/engine.ts +1 -2
  15. package/dist/engine/media/tracks/MediaStreamTrackManager.d.ts +0 -14
  16. package/dist/engine/wid/WebRTCIssueDetector.d.ts +0 -20
  17. package/dist/engine/wid/WebRTCIssueEmitter.d.ts +0 -11
  18. package/dist/engine/wid/detectors/AvailableOutgoingBitrateIssueDetector.d.ts +0 -6
  19. package/dist/engine/wid/detectors/FramesDroppedIssueDetector.d.ts +0 -7
  20. package/dist/engine/wid/detectors/FramesEncodedSentIssueDetector.d.ts +0 -7
  21. package/dist/engine/wid/detectors/InboundNetworkIssueDetector.d.ts +0 -7
  22. package/dist/engine/wid/detectors/NetworkMediaSyncIssueDetector.d.ts +0 -7
  23. package/dist/engine/wid/detectors/OutboundNetworkIssueDetector.d.ts +0 -7
  24. package/dist/engine/wid/detectors/QualityLimitationsIssueDetector.d.ts +0 -7
  25. package/dist/engine/wid/detectors/VideoCodecMismatchDetector.d.ts +0 -10
  26. package/dist/engine/wid/lib/NetworkScoresCalculator.d.ts +0 -9
  27. package/dist/engine/wid/lib/PeriodicWebRTCStatsReporter.d.ts +0 -20
  28. package/dist/engine/wid/lib/parser/CompositeRTCStatsParser.d.ts +0 -22
  29. package/dist/engine/wid/lib/parser/RTCStatsParser.d.ts +0 -20
  30. package/dist/engine/wid/lib/parser/utils.d.ts +0 -7
  31. package/dist/engine/wid/types.d.ts +0 -381
  32. package/src/engine/media/tracks/MediaStreamTrackManager.ts +0 -46
  33. package/src/engine/wid/WebRTCIssueDetector.ts +0 -136
  34. package/src/engine/wid/WebRTCIssueEmitter.ts +0 -16
  35. package/src/engine/wid/detectors/AvailableOutgoingBitrateIssueDetector.ts +0 -57
  36. package/src/engine/wid/detectors/FramesDroppedIssueDetector.ts +0 -65
  37. package/src/engine/wid/detectors/FramesEncodedSentIssueDetector.ts +0 -69
  38. package/src/engine/wid/detectors/InboundNetworkIssueDetector.ts +0 -120
  39. package/src/engine/wid/detectors/NetworkMediaSyncIssueDetector.ts +0 -60
  40. package/src/engine/wid/detectors/OutboundNetworkIssueDetector.ts +0 -96
  41. package/src/engine/wid/detectors/QualityLimitationsIssueDetector.ts +0 -63
  42. package/src/engine/wid/detectors/VideoCodecMismatchDetector.ts +0 -78
  43. package/src/engine/wid/lib/NetworkScoresCalculator.ts +0 -126
  44. package/src/engine/wid/lib/PeriodicWebRTCStatsReporter.ts +0 -66
  45. package/src/engine/wid/lib/parser/CompositeRTCStatsParser.ts +0 -82
  46. package/src/engine/wid/lib/parser/RTCStatsParser.ts +0 -274
  47. package/src/engine/wid/lib/parser/utils.ts +0 -40
  48. package/src/engine/wid/types.ts +0 -408
@@ -1,82 +0,0 @@
1
- /* eslint-disable class-methods-use-this */
2
- import {
3
- CompositeStatsParser, ConnectionInfo, StatsParser, StatsReportItem,
4
- } from '../../types';
5
- import { checkIsConnectionClosed } from './utils';
6
-
7
- export interface AddConnectionPayload {
8
- id?: string;
9
- pc: RTCPeerConnection;
10
- }
11
-
12
- export interface RemoveConnectionPayload {
13
- pc: RTCPeerConnection;
14
- }
15
-
16
- interface CompositeRTCStatsParserParams {
17
- statsParser: StatsParser;
18
- }
19
-
20
- class CompositeRTCStatsParser implements CompositeStatsParser {
21
- private readonly connections: ConnectionInfo[] = [];
22
-
23
- private readonly statsParser: StatsParser;
24
-
25
- constructor(params: CompositeRTCStatsParserParams) {
26
- this.statsParser = params.statsParser;
27
- }
28
-
29
- listConnections(): ConnectionInfo[] {
30
- return [...this.connections];
31
- }
32
-
33
- addPeerConnection(payload: AddConnectionPayload): void {
34
- this.connections.push({
35
- id: payload.id ?? String(Date.now() + Math.random().toString(32)),
36
- pc: payload.pc,
37
- });
38
- }
39
-
40
- removePeerConnection(payload: RemoveConnectionPayload): void {
41
- const pcIdxToDelete = this.connections.findIndex(({ pc }) => pc === payload.pc);
42
-
43
- if (pcIdxToDelete >= 0) {
44
- this.removeConnectionsByIndexes([pcIdxToDelete]);
45
- }
46
- }
47
-
48
- async parse(): Promise<StatsReportItem[]> {
49
- // DESC order to remove elements afterwards without index shifting
50
- const closedConnectionsIndexesDesc: number[] = [];
51
-
52
- const statsPromises = this.connections.map(
53
- async (
54
- info,
55
- index: number,
56
- ): Promise<StatsReportItem | undefined> => {
57
- if (checkIsConnectionClosed(info.pc)) {
58
- closedConnectionsIndexesDesc.unshift(index);
59
- return undefined;
60
- }
61
-
62
- return this.statsParser.parse(info);
63
- },
64
- );
65
-
66
- if (closedConnectionsIndexesDesc.length) {
67
- this.removeConnectionsByIndexes(closedConnectionsIndexesDesc);
68
- }
69
-
70
- const statsItemsByPC = await Promise.all(statsPromises);
71
-
72
- return statsItemsByPC.filter((item) => item !== undefined) as StatsReportItem[];
73
- }
74
-
75
- private removeConnectionsByIndexes(closedConnectionsIndexesDesc: number[]) {
76
- closedConnectionsIndexesDesc.forEach((idx) => {
77
- this.connections.splice(idx, 1);
78
- });
79
- }
80
- }
81
-
82
- export default CompositeRTCStatsParser;
@@ -1,274 +0,0 @@
1
- /* eslint-disable class-methods-use-this */
2
- /* eslint-disable no-param-reassign */
3
- import {
4
- ConnectionInfo,
5
- ParsedConnectionStats,
6
- ParsedInboundAudioStreamStats,
7
- ParsedInboundVideoStreamStats,
8
- ParsedOutboundAudioStreamStats,
9
- ParsedOutboundVideoStreamStats,
10
- ParsedRemoteInboundStreamStats,
11
- ParsedRemoteOutboundStreamStats,
12
- RemoteParsedStats, StatsParser,
13
- StatsReportItem,
14
- WebRTCStatsParsed,
15
- } from '../../types';
16
- import { Logger } from '../../../../types/common';
17
- import { checkIsConnectionClosed, calcBitrate } from './utils';
18
-
19
- interface WebRTCStatsParserParams {
20
- logger: Logger;
21
- }
22
-
23
- class RTCStatsParser implements StatsParser {
24
- private readonly prevStats = new Map<string, { stats: WebRTCStatsParsed, ts: number } | undefined>();
25
-
26
- private readonly allowedReportTypes: RTCStatsType[] = [
27
- 'candidate-pair',
28
- 'inbound-rtp',
29
- 'outbound-rtp',
30
- 'remote-outbound-rtp',
31
- 'remote-inbound-rtp',
32
- 'track',
33
- 'transport',
34
- ];
35
-
36
- private readonly logger: Logger;
37
-
38
- constructor(params: WebRTCStatsParserParams) {
39
- this.logger = params.logger;
40
- }
41
-
42
- async parse(connection: ConnectionInfo): Promise<StatsReportItem | undefined> {
43
- if (checkIsConnectionClosed(connection.pc)) {
44
- this.logger.debug('Skip stats parsing. Connection is closed.', { connection });
45
- return undefined;
46
- }
47
-
48
- return this.getConnectionStats(connection);
49
- }
50
-
51
- private async getConnectionStats(info: ConnectionInfo): Promise<StatsReportItem | undefined> {
52
- const { pc, id } = info;
53
-
54
- try {
55
- const beforeGetStats = Date.now();
56
- const receiversStats = await Promise.all(pc.getReceivers().map((r) => r.getStats()));
57
- const sendersStats = await Promise.all(pc.getSenders().map((r) => r.getStats()));
58
- const stats = this.mapReportsStats([...receiversStats, ...sendersStats], info);
59
-
60
- return {
61
- id,
62
- stats,
63
- timeTaken: Date.now() - beforeGetStats,
64
- };
65
- } catch (error: unknown) {
66
- this.logger.error('Failed to get stats for PC', { id, pc, error });
67
- return undefined;
68
- }
69
- }
70
-
71
- private mapReportsStats(reports: RTCStatsReport[], connectionData: ConnectionInfo): WebRTCStatsParsed {
72
- const mappedStats: WebRTCStatsParsed = {
73
- audio: {
74
- inbound: [],
75
- outbound: [],
76
- },
77
- video: {
78
- inbound: [],
79
- outbound: [],
80
- },
81
- connection: {} as ParsedConnectionStats,
82
- remote: {
83
- video: {
84
- inbound: [],
85
- outbound: [],
86
- },
87
- audio: {
88
- inbound: [],
89
- outbound: [],
90
- },
91
- },
92
- };
93
-
94
- reports.forEach((rtcStats: RTCStatsReport) => {
95
- rtcStats.forEach((reportItem: Record<string, unknown>) => {
96
- if (!this.allowedReportTypes.includes(reportItem.type as RTCStatsType)) {
97
- return;
98
- }
99
-
100
- this.updateMappedStatsWithReportItemData(reportItem, mappedStats, rtcStats);
101
- });
102
- });
103
-
104
- const prevStatsData = this.prevStats.get(connectionData.id);
105
-
106
- if (prevStatsData) {
107
- this.propagateStatsWithRateValues(mappedStats, prevStatsData.stats);
108
- }
109
-
110
- this.prevStats.set(connectionData.id, {
111
- stats: mappedStats,
112
- ts: Date.now(),
113
- });
114
-
115
- return mappedStats;
116
- }
117
-
118
- private updateMappedStatsWithReportItemData(
119
- statsItem: Record<string, unknown>,
120
- mappedStats: WebRTCStatsParsed,
121
- stats: RTCStatsReport,
122
- ): void {
123
- const type = statsItem.type as RTCStatsType;
124
-
125
- if (type === 'candidate-pair' && statsItem.state === 'succeeded' && statsItem.nominated) {
126
- mappedStats.connection = this.prepareConnectionStats(statsItem, stats);
127
- return;
128
- }
129
-
130
- const mediaType = this.getMediaType(statsItem);
131
- if (!mediaType) {
132
- return;
133
- }
134
-
135
- if (type === 'outbound-rtp') {
136
- const trackInfo = stats.get(statsItem.trackId as string)
137
- || stats.get(statsItem.mediaSourceId as string) || {};
138
-
139
- const statsToAdd = {
140
- ...statsItem,
141
- track: { ...trackInfo },
142
- };
143
-
144
- if (mediaType === 'audio') {
145
- mappedStats[mediaType].outbound.push(statsToAdd as ParsedOutboundAudioStreamStats);
146
- } else {
147
- mappedStats[mediaType].outbound.push(statsToAdd as ParsedOutboundVideoStreamStats);
148
- }
149
- return;
150
- }
151
-
152
- if (type === 'inbound-rtp') {
153
- const trackInfo = stats.get(statsItem.trackId as string)
154
- || stats.get(statsItem.mediaSourceId as string) || {};
155
-
156
- this.mapConnectionStatsIfNecessary(mappedStats, statsItem, stats);
157
-
158
- const statsToAdd = {
159
- ...statsItem,
160
- track: { ...trackInfo },
161
- };
162
-
163
- if (mediaType === 'audio') {
164
- mappedStats[mediaType].inbound.push(statsToAdd as ParsedInboundAudioStreamStats);
165
- } else {
166
- mappedStats[mediaType].inbound.push(statsToAdd as ParsedInboundVideoStreamStats);
167
- }
168
- return;
169
- }
170
-
171
- if (type === 'remote-outbound-rtp') {
172
- (mappedStats.remote as RemoteParsedStats)[mediaType].outbound
173
- .push({ ...statsItem } as ParsedRemoteOutboundStreamStats);
174
- return;
175
- }
176
-
177
- if (type === 'remote-inbound-rtp') {
178
- this.mapConnectionStatsIfNecessary(mappedStats, statsItem, stats);
179
-
180
- (mappedStats.remote as RemoteParsedStats)[mediaType].inbound
181
- .push({ ...statsItem } as ParsedRemoteInboundStreamStats);
182
- }
183
- }
184
-
185
- private getMediaType(reportItem: Record<string, unknown>): 'audio' | 'video' | undefined {
186
- const mediaType = (reportItem.mediaType || reportItem.kind) as 'audio' | 'video';
187
-
188
- if (!['audio', 'video'].includes(mediaType)) {
189
- const { id: reportId } = reportItem;
190
-
191
- if (!reportId) {
192
- return undefined;
193
- }
194
-
195
- // Check for Safari browser as it does not have kind and mediaType props
196
- if (String(reportId).includes('Video')) {
197
- return 'video';
198
- }
199
-
200
- if (String(reportId).includes('Audio')) {
201
- return 'audio';
202
- }
203
-
204
- return undefined;
205
- }
206
-
207
- return mediaType;
208
- }
209
-
210
- private propagateStatsWithRateValues(newStats: WebRTCStatsParsed, prevStats: WebRTCStatsParsed) {
211
- newStats.audio.inbound.forEach((report) => {
212
- const prev = prevStats.audio.inbound.find(({ id }) => id === report.id);
213
- report.bitrate = calcBitrate(report, prev, 'bytesReceived');
214
- report.packetRate = calcBitrate(report, prev, 'packetsReceived');
215
- });
216
-
217
- newStats.audio.outbound.forEach((report) => {
218
- const prev = prevStats.audio.outbound.find(({ id }) => id === report.id);
219
- report.bitrate = calcBitrate(report, prev, 'bytesSent');
220
- report.packetRate = calcBitrate(report, prev, 'packetsSent');
221
- });
222
-
223
- newStats.video.inbound.forEach((report) => {
224
- const prev = prevStats.video.inbound.find(({ id }) => id === report.id);
225
- report.bitrate = calcBitrate(report, prev, 'bytesReceived');
226
- report.packetRate = calcBitrate(report, prev, 'packetsReceived');
227
- });
228
-
229
- newStats.video.outbound.forEach((report) => {
230
- const prev = prevStats.video.outbound.find(({ id }) => id === report.id);
231
- report.bitrate = calcBitrate(report, prev, 'bytesSent');
232
- report.packetRate = calcBitrate(report, prev, 'packetsSent');
233
- });
234
- }
235
-
236
- private mapConnectionStatsIfNecessary(
237
- mappedStats: WebRTCStatsParsed,
238
- statsItem: Record<string, unknown>,
239
- stats: RTCStatsReport,
240
- ) {
241
- if (mappedStats.connection.id || !statsItem.transportId) {
242
- return;
243
- }
244
-
245
- const transportStats = stats.get(statsItem.transportId as string);
246
-
247
- if (transportStats && transportStats.selectedCandidatePairId) {
248
- const candidatePair = stats.get(transportStats.selectedCandidatePairId);
249
- mappedStats.connection = this.prepareConnectionStats(candidatePair, stats);
250
- }
251
- }
252
-
253
- private prepareConnectionStats(candidatePair: Record<string, unknown>, stats: RTCStatsReport): ParsedConnectionStats {
254
- if (!(candidatePair && stats)) {
255
- return {} as ParsedConnectionStats;
256
- }
257
-
258
- const connectionStats = { ...candidatePair };
259
-
260
- if (connectionStats.remoteCandidateId) {
261
- const candidate = stats.get(connectionStats.remoteCandidateId as string);
262
- connectionStats.remote = { ...candidate };
263
- }
264
-
265
- if (connectionStats.localCandidateId) {
266
- const candidate = stats.get(connectionStats.localCandidateId as string);
267
- connectionStats.local = { ...candidate };
268
- }
269
-
270
- return connectionStats as ParsedConnectionStats;
271
- }
272
- }
273
-
274
- export default RTCStatsParser;
@@ -1,40 +0,0 @@
1
- interface WithTS {
2
- timestamp: number;
3
- }
4
-
5
- export const checkIsConnectionClosed = (
6
- pc: RTCPeerConnection,
7
- ): boolean => pc.iceConnectionState === 'closed' || pc.connectionState === 'closed';
8
-
9
- export const calcValueRate = <T extends WithTS>(
10
- stats: T,
11
- prevStats: T | undefined,
12
- statPropName: keyof T,
13
- ): number => {
14
- if (!prevStats) {
15
- return 0;
16
- }
17
-
18
- const currentVal = stats[statPropName];
19
- const prevVal = prevStats[statPropName];
20
-
21
- if (currentVal == null || prevVal == null) {
22
- return 0;
23
- }
24
-
25
- // Time is in such format: 1657105307362.007 (mcs after dot)
26
- const timeDiffMs = (Math.floor(stats.timestamp) - Math.floor(prevStats.timestamp));
27
-
28
- if (timeDiffMs === 0) {
29
- return 0;
30
- }
31
-
32
- const valDiff = Number(currentVal) - Number(prevVal);
33
- return (valDiff / timeDiffMs) * 1000;
34
- };
35
-
36
- export const calcBitrate = <T extends WithTS>(
37
- stats: T,
38
- prevStats: T | undefined,
39
- statPropName: keyof T,
40
- ): number => 8 * calcValueRate(stats, prevStats, statPropName);