@stream-io/video-client 1.18.7 → 1.18.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +97 -169
- package/dist/index.browser.es.js +89 -52
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +89 -52
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +89 -52
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +10 -1
- package/dist/src/devices/ScreenShareManager.d.ts +1 -3
- package/dist/src/gen/video/sfu/event/events.d.ts +1 -19
- package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +2 -21
- package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +1 -9
- package/dist/src/helpers/promise.d.ts +2 -2
- package/package.json +10 -11
- package/src/Call.ts +61 -10
- package/src/StreamSfuClient.ts +9 -3
- package/src/StreamVideoClient.ts +4 -5
- package/src/__tests__/Call.test.ts +1 -1
- package/src/coordinator/connection/client.ts +2 -3
- package/src/coordinator/connection/connection.ts +14 -14
- package/src/coordinator/connection/signing.ts +1 -1
- package/src/devices/BrowserPermission.ts +3 -2
- package/src/devices/ScreenShareManager.ts +1 -3
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +1 -1
- package/src/devices/__tests__/MicrophoneManager.test.ts +4 -4
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +4 -4
- package/src/devices/devices.ts +3 -1
- package/src/events/__tests__/call.test.ts +42 -57
- package/src/events/__tests__/internal.test.ts +8 -13
- package/src/events/__tests__/mutes.test.ts +7 -3
- package/src/events/__tests__/participant.test.ts +16 -20
- package/src/events/__tests__/speaker.test.ts +6 -6
- package/src/gen/coordinator/index.ts +1 -1
- package/src/gen/video/sfu/event/events.ts +22 -20
- package/src/gen/video/sfu/models/models.ts +0 -1
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +27 -23
- package/src/gen/video/sfu/signal_rpc/signal.ts +13 -11
- package/src/helpers/RNSpeechDetector.ts +3 -4
- package/src/helpers/__tests__/DynascaleManager.test.ts +27 -26
- package/src/helpers/__tests__/clientUtils.test.ts +0 -1
- package/src/helpers/client-details.ts +1 -1
- package/src/helpers/promise.ts +4 -4
- package/src/rtc/Dispatcher.ts +1 -1
- package/src/rtc/Publisher.ts +2 -2
- package/src/rtc/__tests__/Publisher.test.ts +8 -8
- package/src/rtc/__tests__/Subscriber.test.ts +9 -9
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +2 -2
- package/src/rtc/helpers/__tests__/sdp.test.ts +3 -3
- package/src/stats/CallStateStatsReporter.ts +2 -3
- package/src/store/__tests__/CallState.test.ts +59 -115
- package/src/timers/worker.ts +0 -4
|
@@ -1,31 +1,35 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
1
|
// @generated by protobuf-ts 2.9.4 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
|
|
3
2
|
// @generated from protobuf file "video/sfu/signal_rpc/signal.proto" (package "stream.video.sfu.signal", syntax proto3)
|
|
4
3
|
// tslint:disable
|
|
5
|
-
import type {
|
|
6
|
-
|
|
4
|
+
import type {
|
|
5
|
+
RpcOptions,
|
|
6
|
+
RpcTransport,
|
|
7
|
+
ServiceInfo,
|
|
8
|
+
UnaryCall,
|
|
9
|
+
} from '@protobuf-ts/runtime-rpc';
|
|
10
|
+
import { stackIntercept } from '@protobuf-ts/runtime-rpc';
|
|
11
|
+
import type {
|
|
12
|
+
ICERestartRequest,
|
|
13
|
+
ICERestartResponse,
|
|
14
|
+
ICETrickleResponse,
|
|
15
|
+
SendAnswerRequest,
|
|
16
|
+
SendAnswerResponse,
|
|
17
|
+
SendStatsRequest,
|
|
18
|
+
SendStatsResponse,
|
|
19
|
+
SetPublisherRequest,
|
|
20
|
+
SetPublisherResponse,
|
|
21
|
+
StartNoiseCancellationRequest,
|
|
22
|
+
StartNoiseCancellationResponse,
|
|
23
|
+
StopNoiseCancellationRequest,
|
|
24
|
+
StopNoiseCancellationResponse,
|
|
25
|
+
UpdateMuteStatesRequest,
|
|
26
|
+
UpdateMuteStatesResponse,
|
|
27
|
+
UpdateSubscriptionsRequest,
|
|
28
|
+
UpdateSubscriptionsResponse,
|
|
29
|
+
} from './signal';
|
|
7
30
|
import { SignalServer } from './signal';
|
|
8
|
-
import type { StopNoiseCancellationResponse } from './signal';
|
|
9
|
-
import type { StopNoiseCancellationRequest } from './signal';
|
|
10
|
-
import type { StartNoiseCancellationResponse } from './signal';
|
|
11
|
-
import type { StartNoiseCancellationRequest } from './signal';
|
|
12
|
-
import type { SendStatsResponse } from './signal';
|
|
13
|
-
import type { SendStatsRequest } from './signal';
|
|
14
|
-
import type { ICERestartResponse } from './signal';
|
|
15
|
-
import type { ICERestartRequest } from './signal';
|
|
16
|
-
import type { UpdateMuteStatesResponse } from './signal';
|
|
17
|
-
import type { UpdateMuteStatesRequest } from './signal';
|
|
18
|
-
import type { UpdateSubscriptionsResponse } from './signal';
|
|
19
|
-
import type { UpdateSubscriptionsRequest } from './signal';
|
|
20
|
-
import type { ICETrickleResponse } from './signal';
|
|
21
31
|
import type { ICETrickle } from '../models/models';
|
|
22
|
-
|
|
23
|
-
import type { SendAnswerRequest } from './signal';
|
|
24
|
-
import { stackIntercept } from '@protobuf-ts/runtime-rpc';
|
|
25
|
-
import type { SetPublisherResponse } from './signal';
|
|
26
|
-
import type { SetPublisherRequest } from './signal';
|
|
27
|
-
import type { UnaryCall } from '@protobuf-ts/runtime-rpc';
|
|
28
|
-
import type { RpcOptions } from '@protobuf-ts/runtime-rpc';
|
|
32
|
+
|
|
29
33
|
/**
|
|
30
34
|
* @generated from protobuf service stream.video.sfu.signal.SignalServer
|
|
31
35
|
*/
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
1
|
// @generated by protobuf-ts 2.9.4 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
|
|
3
2
|
// @generated from protobuf file "video/sfu/signal_rpc/signal.proto" (package "stream.video.sfu.signal", syntax proto3)
|
|
4
3
|
// tslint:disable
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AndroidState,
|
|
6
|
+
AppleState,
|
|
7
|
+
Error,
|
|
8
|
+
ICETrickle,
|
|
9
|
+
InputDevices,
|
|
10
|
+
PeerType,
|
|
11
|
+
TrackInfo,
|
|
12
|
+
TrackType,
|
|
13
|
+
VideoDimension,
|
|
14
|
+
WebsocketReconnectStrategy,
|
|
15
|
+
} from '../models/models';
|
|
6
16
|
import { ServiceType } from '@protobuf-ts/runtime-rpc';
|
|
7
17
|
import { MessageType } from '@protobuf-ts/runtime';
|
|
8
|
-
|
|
9
|
-
import { VideoDimension } from '../models/models';
|
|
10
|
-
import { TrackType } from '../models/models';
|
|
11
|
-
import { PeerType } from '../models/models';
|
|
12
|
-
import { AppleState } from '../models/models';
|
|
13
|
-
import { AndroidState } from '../models/models';
|
|
14
|
-
import { InputDevices } from '../models/models';
|
|
15
|
-
import { WebsocketReconnectStrategy } from '../models/models';
|
|
16
|
-
import { Error } from '../models/models';
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* @generated from protobuf message stream.video.sfu.signal.StartNoiseCancellationRequest
|
|
19
21
|
*/
|
|
@@ -33,7 +33,7 @@ export class RNSpeechDetector {
|
|
|
33
33
|
e.streams[0].getTracks().forEach((track) => {
|
|
34
34
|
// In RN, the remote track is automatically added to the audio output device
|
|
35
35
|
// so we need to mute it to avoid hearing the audio back
|
|
36
|
-
// @ts-
|
|
36
|
+
// @ts-expect-error _setVolume is a private method in react-native-webrtc
|
|
37
37
|
track._setVolume(0);
|
|
38
38
|
});
|
|
39
39
|
});
|
|
@@ -79,10 +79,9 @@ export class RNSpeechDetector {
|
|
|
79
79
|
const initialBaselineNoiseLevel = 0.13;
|
|
80
80
|
let baselineNoiseLevel = initialBaselineNoiseLevel;
|
|
81
81
|
let speechDetected = false;
|
|
82
|
-
let intervalId: NodeJS.Timeout | undefined;
|
|
83
82
|
let speechTimer: NodeJS.Timeout | undefined;
|
|
84
83
|
let silenceTimer: NodeJS.Timeout | undefined;
|
|
85
|
-
|
|
84
|
+
const audioLevelHistory: number[] = []; // Store recent audio levels for smoother detection
|
|
86
85
|
const historyLength = 10;
|
|
87
86
|
const silenceThreshold = 1.1;
|
|
88
87
|
const resetThreshold = 0.9;
|
|
@@ -157,7 +156,7 @@ export class RNSpeechDetector {
|
|
|
157
156
|
};
|
|
158
157
|
|
|
159
158
|
// Call checkAudioLevel periodically (every 100ms)
|
|
160
|
-
intervalId = setInterval(checkAudioLevel, 100);
|
|
159
|
+
const intervalId = setInterval(checkAudioLevel, 100);
|
|
161
160
|
|
|
162
161
|
return () => {
|
|
163
162
|
clearInterval(intervalId);
|
|
@@ -42,7 +42,7 @@ describe('DynascaleManager', () => {
|
|
|
42
42
|
},
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
// @ts-
|
|
45
|
+
// @ts-expect-error incomplete data
|
|
46
46
|
call.state.updateOrAddParticipant('session-id', {
|
|
47
47
|
userId: 'user-id',
|
|
48
48
|
sessionId: 'session-id',
|
|
@@ -90,9 +90,9 @@ describe('DynascaleManager', () => {
|
|
|
90
90
|
|
|
91
91
|
beforeEach(() => {
|
|
92
92
|
videoElement = document.createElement('video');
|
|
93
|
-
// @ts-
|
|
93
|
+
// @ts-expect-error private property
|
|
94
94
|
videoElement.clientWidth = 100;
|
|
95
|
-
// @ts-
|
|
95
|
+
// @ts-expect-error private property
|
|
96
96
|
videoElement.clientHeight = 100;
|
|
97
97
|
});
|
|
98
98
|
|
|
@@ -100,17 +100,16 @@ describe('DynascaleManager', () => {
|
|
|
100
100
|
vi.useFakeTimers();
|
|
101
101
|
const audioElement = document.createElement('audio');
|
|
102
102
|
const play = vi.spyOn(audioElement, 'play').mockResolvedValue();
|
|
103
|
-
// @ts-expect-error setSinkId is not defined in types
|
|
104
103
|
audioElement.setSinkId = vi.fn();
|
|
105
104
|
|
|
106
|
-
// @ts-
|
|
105
|
+
// @ts-expect-error incomplete data
|
|
107
106
|
call.state.updateOrAddParticipant('session-id', {
|
|
108
107
|
userId: 'user-id',
|
|
109
108
|
sessionId: 'session-id',
|
|
110
109
|
publishedTracks: [],
|
|
111
110
|
});
|
|
112
111
|
|
|
113
|
-
// @ts-
|
|
112
|
+
// @ts-expect-error incomplete data
|
|
114
113
|
call.state.updateOrAddParticipant('session-id-local', {
|
|
115
114
|
userId: 'user-id-local',
|
|
116
115
|
sessionId: 'session-id-local',
|
|
@@ -134,15 +133,10 @@ describe('DynascaleManager', () => {
|
|
|
134
133
|
|
|
135
134
|
expect(play).toHaveBeenCalled();
|
|
136
135
|
expect(audioElement.srcObject).toBe(mediaStream);
|
|
137
|
-
|
|
138
136
|
expect(audioElement.volume).toBe(1);
|
|
139
|
-
|
|
140
|
-
// @ts-expect-error setSinkId is not defined in types
|
|
141
137
|
expect(audioElement.setSinkId).not.toHaveBeenCalled();
|
|
142
138
|
|
|
143
139
|
call.speaker.select('different-device-id');
|
|
144
|
-
|
|
145
|
-
// @ts-expect-error setSinkId is not defined in types
|
|
146
140
|
expect(audioElement.setSinkId).toHaveBeenCalledWith(
|
|
147
141
|
'different-device-id',
|
|
148
142
|
);
|
|
@@ -164,7 +158,7 @@ describe('DynascaleManager', () => {
|
|
|
164
158
|
const audioElement = document.createElement('audio');
|
|
165
159
|
const play = vi.spyOn(audioElement, 'play').mockResolvedValue();
|
|
166
160
|
|
|
167
|
-
// @ts-
|
|
161
|
+
// @ts-expect-error incomplete data
|
|
168
162
|
call.state.updateOrAddParticipant('session-id', {
|
|
169
163
|
userId: 'user-id',
|
|
170
164
|
sessionId: 'session-id',
|
|
@@ -199,7 +193,7 @@ describe('DynascaleManager', () => {
|
|
|
199
193
|
'updateParticipantTracks',
|
|
200
194
|
);
|
|
201
195
|
|
|
202
|
-
// @ts-
|
|
196
|
+
// @ts-expect-error incomplete data
|
|
203
197
|
call.state.updateOrAddParticipant('session-id', {
|
|
204
198
|
userId: 'user-id',
|
|
205
199
|
sessionId: 'session-id',
|
|
@@ -257,7 +251,7 @@ describe('DynascaleManager', () => {
|
|
|
257
251
|
);
|
|
258
252
|
const play = vi.spyOn(videoElement, 'play').mockResolvedValue();
|
|
259
253
|
|
|
260
|
-
// @ts-
|
|
254
|
+
// @ts-expect-error incomplete data
|
|
261
255
|
call.state.updateOrAddParticipant('session-id', {
|
|
262
256
|
userId: 'user-id',
|
|
263
257
|
sessionId: 'session-id',
|
|
@@ -297,7 +291,7 @@ describe('DynascaleManager', () => {
|
|
|
297
291
|
});
|
|
298
292
|
|
|
299
293
|
it('video: should update subscription when element becomes visible', () => {
|
|
300
|
-
// @ts-
|
|
294
|
+
// @ts-expect-error incomplete data
|
|
301
295
|
call.state.updateOrAddParticipant('session-id', {
|
|
302
296
|
userId: 'user-id',
|
|
303
297
|
sessionId: 'session-id',
|
|
@@ -374,7 +368,7 @@ describe('DynascaleManager', () => {
|
|
|
374
368
|
});
|
|
375
369
|
|
|
376
370
|
it('video: should update subscription when element resizes', () => {
|
|
377
|
-
// @ts-
|
|
371
|
+
// @ts-expect-error incomplete data
|
|
378
372
|
call.state.updateOrAddParticipant('session-id', {
|
|
379
373
|
userId: 'user-id',
|
|
380
374
|
sessionId: 'session-id',
|
|
@@ -385,12 +379,16 @@ describe('DynascaleManager', () => {
|
|
|
385
379
|
},
|
|
386
380
|
});
|
|
387
381
|
|
|
388
|
-
|
|
382
|
+
const updateSubscription = vi.spyOn(
|
|
383
|
+
call.state,
|
|
384
|
+
'updateParticipantTracks',
|
|
385
|
+
);
|
|
389
386
|
|
|
390
|
-
let resizeObserverCallback: ResizeObserverCallback
|
|
387
|
+
let resizeObserverCallback: ResizeObserverCallback | undefined =
|
|
388
|
+
undefined;
|
|
391
389
|
window.ResizeObserver = class ResizeObserver {
|
|
392
390
|
observe = vi.fn().mockImplementation(() => {
|
|
393
|
-
// @ts-
|
|
391
|
+
// @ts-expect-error simulate initial trigger
|
|
394
392
|
resizeObserverCallback();
|
|
395
393
|
});
|
|
396
394
|
unobserve = vi.fn();
|
|
@@ -416,12 +414,12 @@ describe('DynascaleManager', () => {
|
|
|
416
414
|
},
|
|
417
415
|
});
|
|
418
416
|
|
|
419
|
-
// @ts-
|
|
417
|
+
// @ts-expect-error simulate resize
|
|
420
418
|
videoElement.clientHeight = 101;
|
|
421
|
-
// @ts-
|
|
419
|
+
// @ts-expect-error simulate resize
|
|
422
420
|
videoElement.clientWidth = 101;
|
|
423
421
|
|
|
424
|
-
// @ts-
|
|
422
|
+
// @ts-expect-error simulate resize
|
|
425
423
|
resizeObserverCallback();
|
|
426
424
|
|
|
427
425
|
expect(updateSubscription).toHaveBeenCalledWith('videoTrack', {
|
|
@@ -436,7 +434,7 @@ describe('DynascaleManager', () => {
|
|
|
436
434
|
});
|
|
437
435
|
|
|
438
436
|
it('video: should unsubscribe when element dimensions are zero', () => {
|
|
439
|
-
// @ts-
|
|
437
|
+
// @ts-expect-error incomplete data
|
|
440
438
|
call.state.updateOrAddParticipant('session-id', {
|
|
441
439
|
userId: 'user-id',
|
|
442
440
|
sessionId: 'session-id',
|
|
@@ -447,11 +445,14 @@ describe('DynascaleManager', () => {
|
|
|
447
445
|
},
|
|
448
446
|
});
|
|
449
447
|
|
|
450
|
-
|
|
448
|
+
const updateSubscription = vi.spyOn(
|
|
449
|
+
call.state,
|
|
450
|
+
'updateParticipantTracks',
|
|
451
|
+
);
|
|
451
452
|
|
|
452
|
-
// @ts-
|
|
453
|
+
// @ts-expect-error simulate resize
|
|
453
454
|
videoElement.clientHeight = 0;
|
|
454
|
-
// @ts-
|
|
455
|
+
// @ts-expect-error simulate resize
|
|
455
456
|
videoElement.clientWidth = 0;
|
|
456
457
|
|
|
457
458
|
const cleanup = dynascaleManager.bindVideoElement(
|
package/src/helpers/promise.ts
CHANGED
|
@@ -50,8 +50,8 @@ export type PromiseWithResolvers<T> = {
|
|
|
50
50
|
promise: Promise<T>;
|
|
51
51
|
resolve: (value: T | PromiseLike<T>) => void;
|
|
52
52
|
reject: (reason: any) => void;
|
|
53
|
-
isResolved: boolean;
|
|
54
|
-
isRejected: boolean;
|
|
53
|
+
isResolved: () => boolean;
|
|
54
|
+
isRejected: () => boolean;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -85,7 +85,7 @@ export const promiseWithResolvers = <T = void>(): PromiseWithResolvers<T> => {
|
|
|
85
85
|
promise,
|
|
86
86
|
resolve: resolver,
|
|
87
87
|
reject: rejecter,
|
|
88
|
-
isResolved,
|
|
89
|
-
isRejected,
|
|
88
|
+
isResolved: () => isResolved,
|
|
89
|
+
isRejected: () => isRejected,
|
|
90
90
|
};
|
|
91
91
|
};
|
package/src/rtc/Dispatcher.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type DispatchableMessage<K extends SfuEventKinds> = {
|
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
const sfuEventKinds:
|
|
23
|
+
const sfuEventKinds: Record<SfuEventKinds, undefined> = {
|
|
24
24
|
subscriberOffer: undefined,
|
|
25
25
|
publisherAnswer: undefined,
|
|
26
26
|
connectionQualityChanged: undefined,
|
package/src/rtc/Publisher.ts
CHANGED
|
@@ -250,8 +250,8 @@ export class Publisher extends BasePeerConnection {
|
|
|
250
250
|
? // for SVC, we only have one layer (q) and often rid is omitted
|
|
251
251
|
enabledLayers[0]
|
|
252
252
|
: // for non-SVC, we need to find the layer by rid (simulcast)
|
|
253
|
-
enabledLayers.find((l) => l.name === encoder.rid) ??
|
|
254
|
-
(params.encodings.length === 1 ? enabledLayers[0] : undefined);
|
|
253
|
+
(enabledLayers.find((l) => l.name === encoder.rid) ??
|
|
254
|
+
(params.encodings.length === 1 ? enabledLayers[0] : undefined));
|
|
255
255
|
|
|
256
256
|
// flip 'active' flag only when necessary
|
|
257
257
|
const shouldActivate = !!layer?.active;
|
|
@@ -51,7 +51,7 @@ describe('Publisher', () => {
|
|
|
51
51
|
// @ts-expect-error readonly field
|
|
52
52
|
sfuClient.iceTrickleBuffer = new IceTrickleBuffer();
|
|
53
53
|
|
|
54
|
-
// @ts-
|
|
54
|
+
// @ts-expect-error private field
|
|
55
55
|
sfuClient['sessionId'] = sessionId;
|
|
56
56
|
|
|
57
57
|
state = new CallState();
|
|
@@ -84,7 +84,7 @@ describe('Publisher', () => {
|
|
|
84
84
|
describe('Publishing', () => {
|
|
85
85
|
it('should throw when publishing ended tracks', async () => {
|
|
86
86
|
const track = new MediaStreamTrack();
|
|
87
|
-
// @ts-
|
|
87
|
+
// @ts-expect-error readonly field
|
|
88
88
|
track.readyState = 'ended';
|
|
89
89
|
await expect(publisher.publish(track, TrackType.VIDEO)).rejects.toThrow();
|
|
90
90
|
});
|
|
@@ -128,7 +128,7 @@ describe('Publisher', () => {
|
|
|
128
128
|
vi.spyOn(track, 'clone').mockReturnValue(clone);
|
|
129
129
|
|
|
130
130
|
const transceiver = new RTCRtpTransceiver();
|
|
131
|
-
// @ts-
|
|
131
|
+
// @ts-expect-error test setup
|
|
132
132
|
transceiver.sender.track = track;
|
|
133
133
|
publisher['transceiverCache'].add(
|
|
134
134
|
publisher['publishOptions'][0],
|
|
@@ -218,9 +218,9 @@ describe('Publisher', () => {
|
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
it(`should drop consequent ICE restart requests`, async () => {
|
|
221
|
-
// @ts-
|
|
221
|
+
// @ts-expect-error private method
|
|
222
222
|
publisher['pc'].signalingState = 'have-local-offer';
|
|
223
|
-
// @ts-
|
|
223
|
+
// @ts-expect-error private method
|
|
224
224
|
vi.spyOn(publisher, 'negotiate').mockResolvedValue();
|
|
225
225
|
|
|
226
226
|
await publisher.restartIce();
|
|
@@ -229,7 +229,7 @@ describe('Publisher', () => {
|
|
|
229
229
|
|
|
230
230
|
it(`should perform ICE restart when connection state changes to 'failed'`, () => {
|
|
231
231
|
vi.spyOn(publisher, 'restartIce').mockResolvedValue();
|
|
232
|
-
// @ts-
|
|
232
|
+
// @ts-expect-error private api
|
|
233
233
|
publisher['pc'].iceConnectionState = 'failed';
|
|
234
234
|
publisher['onIceConnectionStateChange']();
|
|
235
235
|
expect(publisher.restartIce).toHaveBeenCalled();
|
|
@@ -237,7 +237,7 @@ describe('Publisher', () => {
|
|
|
237
237
|
|
|
238
238
|
it(`should perform ICE restart when connection state changes to 'disconnected'`, () => {
|
|
239
239
|
vi.spyOn(publisher, 'restartIce').mockResolvedValue();
|
|
240
|
-
// @ts-
|
|
240
|
+
// @ts-expect-error private api
|
|
241
241
|
publisher['pc'].iceConnectionState = 'disconnected';
|
|
242
242
|
publisher['onIceConnectionStateChange']();
|
|
243
243
|
expect(publisher.restartIce).toHaveBeenCalled();
|
|
@@ -564,7 +564,7 @@ describe('Publisher', () => {
|
|
|
564
564
|
|
|
565
565
|
const track = new MediaStreamTrack();
|
|
566
566
|
const transceiver = new RTCRtpTransceiver();
|
|
567
|
-
// @ts-
|
|
567
|
+
// @ts-expect-error test setup
|
|
568
568
|
transceiver.sender.track = track;
|
|
569
569
|
|
|
570
570
|
publisher['transceiverCache'].add(publishOptions[0], transceiver);
|
|
@@ -20,7 +20,7 @@ vi.mock('../../StreamSfuClient', () => {
|
|
|
20
20
|
describe('Subscriber', () => {
|
|
21
21
|
let sfuClient: StreamSfuClient;
|
|
22
22
|
let subscriber: Subscriber;
|
|
23
|
-
|
|
23
|
+
const state = new CallState();
|
|
24
24
|
let dispatcher: Dispatcher;
|
|
25
25
|
|
|
26
26
|
beforeEach(() => {
|
|
@@ -61,7 +61,7 @@ describe('Subscriber', () => {
|
|
|
61
61
|
describe('Subscriber ICE restart', () => {
|
|
62
62
|
it(`should drop consequent ICE restart requests`, async () => {
|
|
63
63
|
sfuClient.iceRestart = vi.fn();
|
|
64
|
-
// @ts-
|
|
64
|
+
// @ts-expect-error - private field
|
|
65
65
|
subscriber['pc'].signalingState = 'have-remote-offer';
|
|
66
66
|
|
|
67
67
|
await subscriber.restartIce();
|
|
@@ -70,7 +70,7 @@ describe('Subscriber', () => {
|
|
|
70
70
|
|
|
71
71
|
it('should skip ICE restart when connection is still new', async () => {
|
|
72
72
|
sfuClient.iceRestart = vi.fn();
|
|
73
|
-
// @ts-
|
|
73
|
+
// @ts-expect-error - private field
|
|
74
74
|
subscriber['pc'].connectionState = 'new';
|
|
75
75
|
|
|
76
76
|
await subscriber.restartIce();
|
|
@@ -79,7 +79,7 @@ describe('Subscriber', () => {
|
|
|
79
79
|
|
|
80
80
|
it('should ask the SFU for ICE restart', async () => {
|
|
81
81
|
sfuClient.iceRestart = vi.fn();
|
|
82
|
-
// @ts-
|
|
82
|
+
// @ts-expect-error - private field
|
|
83
83
|
subscriber['pc'].connectionState = 'connected';
|
|
84
84
|
|
|
85
85
|
await subscriber.restartIce();
|
|
@@ -90,7 +90,7 @@ describe('Subscriber', () => {
|
|
|
90
90
|
|
|
91
91
|
it(`should perform ICE restart when connection state changes to 'failed'`, () => {
|
|
92
92
|
vi.spyOn(subscriber, 'restartIce').mockResolvedValue();
|
|
93
|
-
// @ts-
|
|
93
|
+
// @ts-expect-error - private field
|
|
94
94
|
subscriber['pc'].iceConnectionState = 'failed';
|
|
95
95
|
subscriber['onIceConnectionStateChange']();
|
|
96
96
|
expect(subscriber.restartIce).toHaveBeenCalled();
|
|
@@ -98,7 +98,7 @@ describe('Subscriber', () => {
|
|
|
98
98
|
|
|
99
99
|
it(`should perform ICE restart when connection state changes to 'disconnected'`, () => {
|
|
100
100
|
vi.spyOn(subscriber, 'restartIce').mockResolvedValue();
|
|
101
|
-
// @ts-
|
|
101
|
+
// @ts-expect-error - private field
|
|
102
102
|
subscriber['pc'].iceConnectionState = 'disconnected';
|
|
103
103
|
subscriber['onIceConnectionStateChange']();
|
|
104
104
|
expect(subscriber.restartIce).toHaveBeenCalled();
|
|
@@ -109,7 +109,7 @@ describe('Subscriber', () => {
|
|
|
109
109
|
it('should add unknown tracks to the to the call state', () => {
|
|
110
110
|
const mediaStream = new MediaStream();
|
|
111
111
|
const mediaStreamTrack = new MediaStreamTrack();
|
|
112
|
-
// @ts-
|
|
112
|
+
// @ts-expect-error - mock
|
|
113
113
|
mediaStream.id = '123:TRACK_TYPE_VIDEO';
|
|
114
114
|
|
|
115
115
|
const registerOrphanedTrackSpy = vi.spyOn(state, 'registerOrphanedTrack');
|
|
@@ -131,7 +131,7 @@ describe('Subscriber', () => {
|
|
|
131
131
|
it('should assign known tracks to the participant', () => {
|
|
132
132
|
const mediaStream = new MediaStream();
|
|
133
133
|
const mediaStreamTrack = new MediaStreamTrack();
|
|
134
|
-
// @ts-
|
|
134
|
+
// @ts-expect-error - mock
|
|
135
135
|
mediaStream.id = '123:TRACK_TYPE_VIDEO';
|
|
136
136
|
|
|
137
137
|
const registerOrphanedTrackSpy = vi.spyOn(state, 'registerOrphanedTrack');
|
|
@@ -156,7 +156,7 @@ describe('Subscriber', () => {
|
|
|
156
156
|
it('should replace participant stream when a new one arrives', () => {
|
|
157
157
|
const mediaStream = new MediaStream();
|
|
158
158
|
const mediaStreamTrack = new MediaStreamTrack();
|
|
159
|
-
// @ts-
|
|
159
|
+
// @ts-expect-error - mock
|
|
160
160
|
mediaStream.id = '123:TRACK_TYPE_VIDEO';
|
|
161
161
|
|
|
162
162
|
const updateParticipantSpy = vi.spyOn(state, 'updateParticipant');
|
|
@@ -51,7 +51,7 @@ vi.stubGlobal('MediaStreamTrack', MediaStreamTrackMock);
|
|
|
51
51
|
|
|
52
52
|
const RTCRtpTransceiverMock = vi.fn((): Partial<RTCRtpTransceiver> => {
|
|
53
53
|
return {
|
|
54
|
-
// @ts-
|
|
54
|
+
// @ts-expect-error - incomplete mock
|
|
55
55
|
sender: {
|
|
56
56
|
track: null,
|
|
57
57
|
replaceTrack: vi.fn(),
|
|
@@ -84,7 +84,7 @@ vi.stubGlobal('RTCRtpReceiver', RTCRtpReceiverMock);
|
|
|
84
84
|
const RTCRtpSenderMock = vi.fn((): Partial<typeof RTCRtpSender> => {
|
|
85
85
|
return {
|
|
86
86
|
getCapabilities: vi.fn(),
|
|
87
|
-
// @ts-
|
|
87
|
+
// @ts-expect-error - incomplete mock
|
|
88
88
|
track: vi.fn(),
|
|
89
89
|
};
|
|
90
90
|
});
|
|
@@ -29,7 +29,7 @@ a=rtcp-fb:96 nack pli
|
|
|
29
29
|
describe('sdp', () => {
|
|
30
30
|
it('should extract mid from transceiver', () => {
|
|
31
31
|
const transceiver = new RTCRtpTransceiver();
|
|
32
|
-
// @ts-
|
|
32
|
+
// @ts-expect-error - mid is a readonly property
|
|
33
33
|
transceiver.mid = '10';
|
|
34
34
|
expect(extractMid(transceiver, -1, '')).toBe('10');
|
|
35
35
|
});
|
|
@@ -40,7 +40,7 @@ describe('sdp', () => {
|
|
|
40
40
|
|
|
41
41
|
it('should extract mid from SDP', () => {
|
|
42
42
|
const track = new MediaStreamTrack();
|
|
43
|
-
// @ts-
|
|
43
|
+
// @ts-expect-error - id is a readonly property
|
|
44
44
|
track.id = '8d240fd6-26a1-40f6-a769-4d7d24cfd286';
|
|
45
45
|
const transceiver = new RTCRtpTransceiver();
|
|
46
46
|
vi.spyOn(transceiver.sender, 'track', 'get').mockReturnValue(track);
|
|
@@ -50,7 +50,7 @@ describe('sdp', () => {
|
|
|
50
50
|
|
|
51
51
|
it('should fallback to transceiverInitIndex when mid can not be found in SDP', () => {
|
|
52
52
|
const track = new MediaStreamTrack();
|
|
53
|
-
// @ts-
|
|
53
|
+
// @ts-expect-error - id is a readonly property
|
|
54
54
|
track.id = 'not-known';
|
|
55
55
|
const transceiver = new RTCRtpTransceiver();
|
|
56
56
|
vi.spyOn(transceiver.sender, 'track', 'get').mockReturnValue(track);
|
|
@@ -98,9 +98,9 @@ export const createStatsReporter = ({
|
|
|
98
98
|
for (const track of tracks) {
|
|
99
99
|
const report = await pc.getStats(track);
|
|
100
100
|
const stats = transform(report, {
|
|
101
|
-
|
|
102
|
-
trackKind: track.kind,
|
|
101
|
+
trackKind: track.kind as 'audio' | 'video',
|
|
103
102
|
kind,
|
|
103
|
+
publisher: undefined,
|
|
104
104
|
});
|
|
105
105
|
statsForStream.push(stats);
|
|
106
106
|
}
|
|
@@ -298,7 +298,6 @@ const transform = (
|
|
|
298
298
|
jitter: rtcStreamStats.jitter,
|
|
299
299
|
kind: rtcStreamStats.kind,
|
|
300
300
|
mediaSourceId: rtcStreamStats.mediaSourceId,
|
|
301
|
-
// @ts-ignore: available in Chrome only, TS doesn't recognize this
|
|
302
301
|
qualityLimitationReason: rtcStreamStats.qualityLimitationReason,
|
|
303
302
|
rid: rtcStreamStats.rid,
|
|
304
303
|
ssrc: rtcStreamStats.ssrc,
|