@stream-io/video-client 0.3.36 → 0.4.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.
- package/CHANGELOG.md +11 -0
- package/dist/index.browser.es.js +23 -107
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +22 -107
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +23 -107
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +0 -32
- package/dist/src/devices/InputMediaDeviceManager.d.ts +2 -2
- package/dist/src/permissions/PermissionsContext.d.ts +2 -1
- package/dist/src/rtc/flows/join.d.ts +2 -2
- package/dist/src/rtc/helpers/tracks.d.ts +1 -2
- package/dist/src/store/CallState.d.ts +7 -7
- package/dist/src/types.d.ts +1 -24
- package/package.json +1 -1
- package/src/Call.ts +7 -64
- package/src/devices/InputMediaDeviceManager.ts +1 -1
- package/src/helpers/DynascaleManager.ts +14 -27
- package/src/helpers/__tests__/DynascaleManager.test.ts +3 -22
- package/src/permissions/PermissionsContext.ts +7 -3
- package/src/rtc/Publisher.ts +1 -7
- package/src/rtc/__tests__/Publisher.test.ts +0 -4
- package/src/rtc/flows/join.ts +4 -19
- package/src/rtc/helpers/tracks.ts +1 -22
- package/src/store/CallState.ts +8 -17
- package/src/types.ts +1 -34
package/dist/src/Call.d.ts
CHANGED
|
@@ -252,38 +252,6 @@ export declare class Call {
|
|
|
252
252
|
* @param sessionId the sessionId to stop reporting for.
|
|
253
253
|
*/
|
|
254
254
|
stopReportingStatsFor: (sessionId: string) => void | undefined;
|
|
255
|
-
/**
|
|
256
|
-
* Sets the used audio output device (`audioOutputDeviceId` of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore).
|
|
257
|
-
*
|
|
258
|
-
* This method only stores the selection, if you're using custom UI components, you'll have to implement the audio switching, for more information see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/sinkId.
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* @param deviceId the selected device, `undefined` means the user wants to use the system's default audio output
|
|
262
|
-
*
|
|
263
|
-
* @deprecated use `call.speaker` instead
|
|
264
|
-
*/
|
|
265
|
-
setAudioOutputDevice: (deviceId?: string) => void;
|
|
266
|
-
/**
|
|
267
|
-
* Sets the `audioDeviceId` property of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore)).
|
|
268
|
-
*
|
|
269
|
-
* This method only stores the selection, if you want to start publishing a media stream call the [`publishAudioStream` method](#publishaudiostream) that will set `audioDeviceId` as well.
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* @param deviceId the selected device, pass `undefined` to clear the device selection
|
|
273
|
-
*
|
|
274
|
-
* @deprecated use call.microphone.select
|
|
275
|
-
*/
|
|
276
|
-
setAudioDevice: (deviceId?: string) => void;
|
|
277
|
-
/**
|
|
278
|
-
* Sets the `videoDeviceId` property of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore).
|
|
279
|
-
*
|
|
280
|
-
* This method only stores the selection, if you want to start publishing a media stream call the [`publishVideoStream` method](#publishvideostream) that will set `videoDeviceId` as well.
|
|
281
|
-
*
|
|
282
|
-
* @param deviceId the selected device, pass `undefined` to clear the device selection
|
|
283
|
-
*
|
|
284
|
-
* @deprecated use call.camera.select
|
|
285
|
-
*/
|
|
286
|
-
setVideoDevice: (deviceId?: string) => void;
|
|
287
255
|
/**
|
|
288
256
|
* Resets the last sent reaction for the user holding the given `sessionId`. This is a local action, it won't reset the reaction on the backend.
|
|
289
257
|
*
|
|
@@ -24,7 +24,7 @@ export declare abstract class InputMediaDeviceManager<T extends InputMediaDevice
|
|
|
24
24
|
*
|
|
25
25
|
* @returns an Observable that will be updated if a device is connected or disconnected
|
|
26
26
|
*/
|
|
27
|
-
listDevices(): Observable<MediaDeviceInfo[]>;
|
|
27
|
+
listDevices(): Observable<MediaDeviceInfo[] | undefined>;
|
|
28
28
|
/**
|
|
29
29
|
* Starts stream.
|
|
30
30
|
*/
|
|
@@ -57,7 +57,7 @@ export declare abstract class InputMediaDeviceManager<T extends InputMediaDevice
|
|
|
57
57
|
*/
|
|
58
58
|
select(deviceId: string | undefined): Promise<void>;
|
|
59
59
|
protected applySettingsToStream(): Promise<void>;
|
|
60
|
-
protected abstract getDevices(): Observable<MediaDeviceInfo[]>;
|
|
60
|
+
protected abstract getDevices(): Observable<MediaDeviceInfo[] | undefined>;
|
|
61
61
|
protected abstract getStream(constraints: C): Promise<MediaStream>;
|
|
62
62
|
protected abstract publishStream(stream: MediaStream): Promise<void>;
|
|
63
63
|
protected abstract stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
@@ -33,6 +33,7 @@ export declare class PermissionsContext {
|
|
|
33
33
|
* within the call.
|
|
34
34
|
*
|
|
35
35
|
* @param permission the permission to check for.
|
|
36
|
+
* @param settings the call settings to check against (optional).
|
|
36
37
|
*/
|
|
37
|
-
canRequest: (permission: OwnCapability) => boolean;
|
|
38
|
+
canRequest: (permission: OwnCapability, settings?: CallSettingsResponse | undefined) => boolean;
|
|
38
39
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JoinCallData,
|
|
1
|
+
import { JoinCallData, StreamVideoParticipant } from '../../types';
|
|
2
2
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
3
3
|
/**
|
|
4
4
|
* Collects all necessary information to join a call, talks to the coordinator
|
|
@@ -23,4 +23,4 @@ export declare const join: (httpClient: StreamClient, type: string, id: string,
|
|
|
23
23
|
* @param target the participant to reconcile into.
|
|
24
24
|
* @param source the participant to reconcile from.
|
|
25
25
|
*/
|
|
26
|
-
export declare const reconcileParticipantLocalState: (target: StreamVideoParticipant
|
|
26
|
+
export declare const reconcileParticipantLocalState: (target: StreamVideoParticipant, source?: StreamVideoParticipant) => StreamVideoParticipant;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { TrackType } from '../../gen/video/sfu/models/models';
|
|
2
|
-
import type {
|
|
2
|
+
import type { StreamVideoParticipant } from '../../types';
|
|
3
3
|
import { TrackMuteType } from '../../types';
|
|
4
4
|
export declare const trackTypeToParticipantStreamKey: (trackType: TrackType) => keyof StreamVideoParticipant;
|
|
5
|
-
export declare const trackTypeToDeviceIdKey: (trackType: TrackType) => keyof StreamVideoLocalParticipant | undefined;
|
|
6
5
|
export declare const muteTypeToTrackType: (muteType: TrackMuteType) => TrackType;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
2
|
import type { Patch } from './rxUtils';
|
|
3
|
-
import {
|
|
3
|
+
import { StreamVideoParticipant, StreamVideoParticipantPatch, StreamVideoParticipantPatches } from '../types';
|
|
4
4
|
import { CallStatsReport } from '../stats/types';
|
|
5
5
|
import { CallIngressResponse, CallResponse, CallSessionResponse, CallSettingsResponse, EgressResponse, MemberResponse, OwnCapability, ThumbnailResponse, UserResponse, VideoEvent } from '../gen/coordinator';
|
|
6
6
|
import { Pin } from '../gen/video/sfu/models/models';
|
|
@@ -99,7 +99,7 @@ export declare class CallState {
|
|
|
99
99
|
/**
|
|
100
100
|
* All participants of the current call (this includes the current user and other participants as well).
|
|
101
101
|
*/
|
|
102
|
-
participants$: Observable<
|
|
102
|
+
participants$: Observable<StreamVideoParticipant[]>;
|
|
103
103
|
/**
|
|
104
104
|
* Remote participants of the current call (this includes every participant except the logged-in user).
|
|
105
105
|
*/
|
|
@@ -107,7 +107,7 @@ export declare class CallState {
|
|
|
107
107
|
/**
|
|
108
108
|
* The local participant of the current call (the logged-in user).
|
|
109
109
|
*/
|
|
110
|
-
localParticipant$: Observable<
|
|
110
|
+
localParticipant$: Observable<StreamVideoParticipant | undefined>;
|
|
111
111
|
/**
|
|
112
112
|
* Pinned participants of the current call.
|
|
113
113
|
*/
|
|
@@ -289,7 +289,7 @@ export declare class CallState {
|
|
|
289
289
|
/**
|
|
290
290
|
* The list of participants in the current call.
|
|
291
291
|
*/
|
|
292
|
-
get participants():
|
|
292
|
+
get participants(): StreamVideoParticipant[];
|
|
293
293
|
/**
|
|
294
294
|
* Sets the list of participants in the current call.
|
|
295
295
|
*
|
|
@@ -301,7 +301,7 @@ export declare class CallState {
|
|
|
301
301
|
/**
|
|
302
302
|
* The local participant in the current call.
|
|
303
303
|
*/
|
|
304
|
-
get localParticipant():
|
|
304
|
+
get localParticipant(): StreamVideoParticipant | undefined;
|
|
305
305
|
/**
|
|
306
306
|
* The list of remote participants in the current call.
|
|
307
307
|
*/
|
|
@@ -437,7 +437,7 @@ export declare class CallState {
|
|
|
437
437
|
* Returns a new lookup table of participants indexed by their session ID.
|
|
438
438
|
*/
|
|
439
439
|
getParticipantLookupBySessionId: () => {
|
|
440
|
-
[sessionId: string]: StreamVideoParticipant |
|
|
440
|
+
[sessionId: string]: StreamVideoParticipant | undefined;
|
|
441
441
|
};
|
|
442
442
|
/**
|
|
443
443
|
* Updates a participant in the current call identified by the given `sessionId`.
|
|
@@ -449,7 +449,7 @@ export declare class CallState {
|
|
|
449
449
|
* @param patch the patch to apply to the participant.
|
|
450
450
|
* @returns the updated participant or `undefined` if the participant couldn't be found.
|
|
451
451
|
*/
|
|
452
|
-
updateParticipant: (sessionId: string, patch:
|
|
452
|
+
updateParticipant: (sessionId: string, patch: Partial<StreamVideoParticipant> | ((p: StreamVideoParticipant) => StreamVideoParticipantPatch)) => StreamVideoParticipant[] | undefined;
|
|
453
453
|
/**
|
|
454
454
|
* Updates a participant in the current call identified by the given `sessionId`.
|
|
455
455
|
* If a participant with matching `sessionId` can't be found, the provided
|
package/dist/src/types.d.ts
CHANGED
|
@@ -65,28 +65,6 @@ export interface StreamVideoParticipant extends Participant {
|
|
|
65
65
|
*/
|
|
66
66
|
viewportVisibilityState?: Record<VideoTrackType, VisibilityState>;
|
|
67
67
|
}
|
|
68
|
-
export interface StreamVideoLocalParticipant extends StreamVideoParticipant {
|
|
69
|
-
/**
|
|
70
|
-
* The device ID of the currently selected audio input device of the local participant (returned by the [MediaDevices API](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia))
|
|
71
|
-
*
|
|
72
|
-
* @deprecated use call.microphone.state.selectedDevice
|
|
73
|
-
*/
|
|
74
|
-
audioDeviceId?: string;
|
|
75
|
-
/**
|
|
76
|
-
* The device ID of the currently selected video input device of the local participant (returned by the [MediaDevices API](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia))
|
|
77
|
-
*
|
|
78
|
-
* @deprecated use call.camera.state.selectedDevice
|
|
79
|
-
*/
|
|
80
|
-
videoDeviceId?: string;
|
|
81
|
-
/**
|
|
82
|
-
* The device ID of the currently selected audio output device of the local participant (returned by the [MediaDevices API](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia))
|
|
83
|
-
*
|
|
84
|
-
* If the value is not defined, the user hasn't selected any device (in these cases the default system audio output could be used)
|
|
85
|
-
*
|
|
86
|
-
* @deprecated use call.speaker.state.selectedDevice
|
|
87
|
-
*/
|
|
88
|
-
audioOutputDeviceId?: string;
|
|
89
|
-
}
|
|
90
68
|
export type VideoTrackType = 'videoTrack' | 'screenShareTrack';
|
|
91
69
|
export type AudioTrackType = 'audioTrack' | 'screenShareAudioTrack';
|
|
92
70
|
export type TrackMuteType = 'audio' | 'video' | 'screenshare' | 'screenshare_audio';
|
|
@@ -104,11 +82,10 @@ export type ParticipantPin = {
|
|
|
104
82
|
*/
|
|
105
83
|
pinnedAt: number;
|
|
106
84
|
};
|
|
107
|
-
export declare const isStreamVideoLocalParticipant: (p: StreamVideoParticipant | StreamVideoLocalParticipant) => p is StreamVideoLocalParticipant;
|
|
108
85
|
/**
|
|
109
86
|
* A partial representation of the StreamVideoParticipant.
|
|
110
87
|
*/
|
|
111
|
-
export type StreamVideoParticipantPatch = Partial<StreamVideoParticipant
|
|
88
|
+
export type StreamVideoParticipantPatch = Partial<StreamVideoParticipant>;
|
|
112
89
|
/**
|
|
113
90
|
* A collection of {@link StreamVideoParticipantPatch} organized by sessionId.
|
|
114
91
|
*/
|
package/package.json
CHANGED
package/src/Call.ts
CHANGED
|
@@ -9,11 +9,7 @@ import {
|
|
|
9
9
|
Subscriber,
|
|
10
10
|
} from './rtc';
|
|
11
11
|
import { muteTypeToTrackType } from './rtc/helpers/tracks';
|
|
12
|
-
import {
|
|
13
|
-
GoAwayReason,
|
|
14
|
-
SdkType,
|
|
15
|
-
TrackType,
|
|
16
|
-
} from './gen/video/sfu/models/models';
|
|
12
|
+
import { GoAwayReason, TrackType } from './gen/video/sfu/models/models';
|
|
17
13
|
import {
|
|
18
14
|
registerEventHandlers,
|
|
19
15
|
registerRingingCallEventHandlers,
|
|
@@ -120,7 +116,7 @@ import {
|
|
|
120
116
|
Logger,
|
|
121
117
|
StreamCallEvent,
|
|
122
118
|
} from './coordinator/connection/types';
|
|
123
|
-
import { getClientDetails
|
|
119
|
+
import { getClientDetails } from './client-details';
|
|
124
120
|
import { getLogger } from './logger';
|
|
125
121
|
import {
|
|
126
122
|
CameraDirection,
|
|
@@ -1004,14 +1000,11 @@ export class Call {
|
|
|
1004
1000
|
this.reconnectAttempts = 0; // reset the reconnect attempts counter
|
|
1005
1001
|
this.state.setCallingState(CallingState.JOINED);
|
|
1006
1002
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
} catch (error) {
|
|
1013
|
-
this.logger('warn', 'Camera and/or mic init failed during join call');
|
|
1014
|
-
}
|
|
1003
|
+
try {
|
|
1004
|
+
await this.initCamera();
|
|
1005
|
+
await this.initMic();
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
this.logger('warn', 'Camera and/or mic init failed during join call');
|
|
1015
1008
|
}
|
|
1016
1009
|
|
|
1017
1010
|
// 3. once we have the "joinResponse", and possibly reconciled the local state
|
|
@@ -1322,56 +1315,6 @@ export class Call {
|
|
|
1322
1315
|
return this.statsReporter?.stopReportingStatsFor(sessionId);
|
|
1323
1316
|
};
|
|
1324
1317
|
|
|
1325
|
-
/**
|
|
1326
|
-
* Sets the used audio output device (`audioOutputDeviceId` of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore).
|
|
1327
|
-
*
|
|
1328
|
-
* This method only stores the selection, if you're using custom UI components, you'll have to implement the audio switching, for more information see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/sinkId.
|
|
1329
|
-
*
|
|
1330
|
-
*
|
|
1331
|
-
* @param deviceId the selected device, `undefined` means the user wants to use the system's default audio output
|
|
1332
|
-
*
|
|
1333
|
-
* @deprecated use `call.speaker` instead
|
|
1334
|
-
*/
|
|
1335
|
-
setAudioOutputDevice = (deviceId?: string) => {
|
|
1336
|
-
if (!this.sfuClient) return;
|
|
1337
|
-
this.state.updateParticipant(this.sfuClient.sessionId, {
|
|
1338
|
-
audioOutputDeviceId: deviceId,
|
|
1339
|
-
});
|
|
1340
|
-
};
|
|
1341
|
-
|
|
1342
|
-
/**
|
|
1343
|
-
* Sets the `audioDeviceId` property of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore)).
|
|
1344
|
-
*
|
|
1345
|
-
* This method only stores the selection, if you want to start publishing a media stream call the [`publishAudioStream` method](#publishaudiostream) that will set `audioDeviceId` as well.
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
* @param deviceId the selected device, pass `undefined` to clear the device selection
|
|
1349
|
-
*
|
|
1350
|
-
* @deprecated use call.microphone.select
|
|
1351
|
-
*/
|
|
1352
|
-
setAudioDevice = (deviceId?: string) => {
|
|
1353
|
-
if (!this.sfuClient) return;
|
|
1354
|
-
this.state.updateParticipant(this.sfuClient.sessionId, {
|
|
1355
|
-
audioDeviceId: deviceId,
|
|
1356
|
-
});
|
|
1357
|
-
};
|
|
1358
|
-
|
|
1359
|
-
/**
|
|
1360
|
-
* Sets the `videoDeviceId` property of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore).
|
|
1361
|
-
*
|
|
1362
|
-
* This method only stores the selection, if you want to start publishing a media stream call the [`publishVideoStream` method](#publishvideostream) that will set `videoDeviceId` as well.
|
|
1363
|
-
*
|
|
1364
|
-
* @param deviceId the selected device, pass `undefined` to clear the device selection
|
|
1365
|
-
*
|
|
1366
|
-
* @deprecated use call.camera.select
|
|
1367
|
-
*/
|
|
1368
|
-
setVideoDevice = (deviceId?: string) => {
|
|
1369
|
-
if (!this.sfuClient) return;
|
|
1370
|
-
this.state.updateParticipant(this.sfuClient.sessionId, {
|
|
1371
|
-
videoDeviceId: deviceId,
|
|
1372
|
-
});
|
|
1373
|
-
};
|
|
1374
|
-
|
|
1375
1318
|
/**
|
|
1376
1319
|
* Resets the last sent reaction for the user holding the given `sessionId`. This is a local action, it won't reset the reaction on the backend.
|
|
1377
1320
|
*
|
|
@@ -136,7 +136,7 @@ export abstract class InputMediaDeviceManager<
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
protected abstract getDevices(): Observable<MediaDeviceInfo[]>;
|
|
139
|
+
protected abstract getDevices(): Observable<MediaDeviceInfo[] | undefined>;
|
|
140
140
|
|
|
141
141
|
protected abstract getStream(constraints: C): Promise<MediaStream>;
|
|
142
142
|
|
|
@@ -2,18 +2,12 @@ import { Call } from '../Call';
|
|
|
2
2
|
import {
|
|
3
3
|
AudioTrackType,
|
|
4
4
|
DebounceType,
|
|
5
|
-
StreamVideoLocalParticipant,
|
|
6
5
|
StreamVideoParticipant,
|
|
7
6
|
VideoTrackType,
|
|
8
7
|
VisibilityState,
|
|
9
8
|
} from '../types';
|
|
9
|
+
import { TrackType, VideoDimension } from '../gen/video/sfu/models/models';
|
|
10
10
|
import {
|
|
11
|
-
SdkType,
|
|
12
|
-
TrackType,
|
|
13
|
-
VideoDimension,
|
|
14
|
-
} from '../gen/video/sfu/models/models';
|
|
15
|
-
import {
|
|
16
|
-
combineLatest,
|
|
17
11
|
distinctUntilChanged,
|
|
18
12
|
distinctUntilKeyChanged,
|
|
19
13
|
map,
|
|
@@ -22,7 +16,6 @@ import {
|
|
|
22
16
|
} from 'rxjs';
|
|
23
17
|
import { ViewportTracker } from './ViewportTracker';
|
|
24
18
|
import { getLogger } from '../logger';
|
|
25
|
-
import { getSdkInfo } from '../client-details';
|
|
26
19
|
import { isFirefox, isSafari } from './browsers';
|
|
27
20
|
|
|
28
21
|
const DEFAULT_VIEWPORT_VISIBILITY_STATE: Record<
|
|
@@ -174,7 +167,7 @@ export class DynascaleManager {
|
|
|
174
167
|
(participants) =>
|
|
175
168
|
participants.find(
|
|
176
169
|
(participant) => participant.sessionId === sessionId,
|
|
177
|
-
) as
|
|
170
|
+
) as StreamVideoParticipant,
|
|
178
171
|
),
|
|
179
172
|
takeWhile((participant) => !!participant),
|
|
180
173
|
distinctUntilChanged(),
|
|
@@ -339,9 +332,9 @@ export class DynascaleManager {
|
|
|
339
332
|
const participant$ = this.call.state.participants$.pipe(
|
|
340
333
|
map(
|
|
341
334
|
(participants) =>
|
|
342
|
-
participants.find(
|
|
343
|
-
|
|
344
|
-
|
|
335
|
+
participants.find(
|
|
336
|
+
(p) => p.sessionId === sessionId,
|
|
337
|
+
) as StreamVideoParticipant,
|
|
345
338
|
),
|
|
346
339
|
takeWhile((p) => !!p),
|
|
347
340
|
distinctUntilChanged(),
|
|
@@ -373,20 +366,14 @@ export class DynascaleManager {
|
|
|
373
366
|
});
|
|
374
367
|
});
|
|
375
368
|
|
|
376
|
-
const sinkIdSubscription =
|
|
377
|
-
|
|
378
|
-
this.call.speaker.state.selectedDevice
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if ('setSinkId' in audioElement && typeof deviceId === 'string') {
|
|
386
|
-
// @ts-expect-error setSinkId is not yet in the lib
|
|
387
|
-
audioElement.setSinkId(deviceId);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
369
|
+
const sinkIdSubscription = !('setSinkId' in audioElement)
|
|
370
|
+
? null
|
|
371
|
+
: this.call.speaker.state.selectedDevice$.subscribe((deviceId) => {
|
|
372
|
+
if (deviceId) {
|
|
373
|
+
// @ts-expect-error setSinkId is not yet in the lib
|
|
374
|
+
audioElement.setSinkId(deviceId);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
390
377
|
|
|
391
378
|
const volumeSubscription = this.call.speaker.state.volume$.subscribe(
|
|
392
379
|
(volume) => {
|
|
@@ -397,7 +384,7 @@ export class DynascaleManager {
|
|
|
397
384
|
audioElement.autoplay = true;
|
|
398
385
|
|
|
399
386
|
return () => {
|
|
400
|
-
sinkIdSubscription
|
|
387
|
+
sinkIdSubscription?.unsubscribe();
|
|
401
388
|
volumeSubscription.unsubscribe();
|
|
402
389
|
updateMediaStreamSubscription.unsubscribe();
|
|
403
390
|
};
|
|
@@ -4,21 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import '../../rtc/__tests__/mocks/webrtc.mocks';
|
|
6
6
|
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, it,
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
8
|
import { DynascaleManager } from '../DynascaleManager';
|
|
9
9
|
import { Call } from '../../Call';
|
|
10
10
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
11
11
|
import { StreamVideoWriteableStateStore } from '../../store';
|
|
12
12
|
import { DebounceType, VisibilityState } from '../../types';
|
|
13
13
|
import { noopComparator } from '../../sorting';
|
|
14
|
-
import {
|
|
15
|
-
import { getSdkInfo } from '../../client-details';
|
|
16
|
-
|
|
17
|
-
vi.mock('../../client-details.ts', () => {
|
|
18
|
-
return {
|
|
19
|
-
getSdkInfo: vi.fn(),
|
|
20
|
-
};
|
|
21
|
-
});
|
|
14
|
+
import { TrackType } from '../../gen/video/sfu/models/models';
|
|
22
15
|
|
|
23
16
|
describe('DynascaleManager', () => {
|
|
24
17
|
let dynascaleManager: DynascaleManager;
|
|
@@ -145,7 +138,7 @@ describe('DynascaleManager', () => {
|
|
|
145
138
|
expect(audioElement.volume).toBe(1);
|
|
146
139
|
|
|
147
140
|
// @ts-expect-error setSinkId is not defined in types
|
|
148
|
-
expect(audioElement.setSinkId).
|
|
141
|
+
expect(audioElement.setSinkId).not.toHaveBeenCalled();
|
|
149
142
|
|
|
150
143
|
call.speaker.select('different-device-id');
|
|
151
144
|
|
|
@@ -154,18 +147,6 @@ describe('DynascaleManager', () => {
|
|
|
154
147
|
'different-device-id',
|
|
155
148
|
);
|
|
156
149
|
|
|
157
|
-
const mock = getSdkInfo as Mock;
|
|
158
|
-
mock.mockImplementation(() => ({
|
|
159
|
-
type: SdkType.REACT,
|
|
160
|
-
}));
|
|
161
|
-
|
|
162
|
-
call.state.updateParticipant('session-id-local', {
|
|
163
|
-
audioOutputDeviceId: 'new-device-id',
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// @ts-expect-error setSinkId is not defined in types
|
|
167
|
-
expect(audioElement.setSinkId).toHaveBeenCalledWith('new-device-id');
|
|
168
|
-
|
|
169
150
|
call.speaker.setVolume(0.5);
|
|
170
151
|
|
|
171
152
|
expect(audioElement.volume).toBe(0.5);
|
|
@@ -44,11 +44,15 @@ export class PermissionsContext {
|
|
|
44
44
|
* within the call.
|
|
45
45
|
*
|
|
46
46
|
* @param permission the permission to check for.
|
|
47
|
+
* @param settings the call settings to check against (optional).
|
|
47
48
|
*/
|
|
48
|
-
canRequest = (
|
|
49
|
-
|
|
49
|
+
canRequest = (
|
|
50
|
+
permission: OwnCapability,
|
|
51
|
+
settings: CallSettingsResponse | undefined = this.settings,
|
|
52
|
+
) => {
|
|
53
|
+
if (!settings) return false;
|
|
50
54
|
|
|
51
|
-
const { audio, video, screensharing } =
|
|
55
|
+
const { audio, video, screensharing } = settings;
|
|
52
56
|
switch (permission) {
|
|
53
57
|
case OwnCapability.SEND_AUDIO:
|
|
54
58
|
return audio.access_request_enabled;
|
package/src/rtc/Publisher.ts
CHANGED
|
@@ -14,10 +14,7 @@ import {
|
|
|
14
14
|
OptimalVideoLayer,
|
|
15
15
|
} from './videoLayers';
|
|
16
16
|
import { getPreferredCodecs } from './codecs';
|
|
17
|
-
import {
|
|
18
|
-
trackTypeToDeviceIdKey,
|
|
19
|
-
trackTypeToParticipantStreamKey,
|
|
20
|
-
} from './helpers/tracks';
|
|
17
|
+
import { trackTypeToParticipantStreamKey } from './helpers/tracks';
|
|
21
18
|
import { CallState } from '../store';
|
|
22
19
|
import { PublishOptions } from '../types';
|
|
23
20
|
import { isReactNative } from '../helpers/platforms';
|
|
@@ -388,14 +385,11 @@ export class Publisher {
|
|
|
388
385
|
[audioOrVideoOrScreenShareStream]: undefined,
|
|
389
386
|
}));
|
|
390
387
|
} else {
|
|
391
|
-
const deviceId = track.getSettings().deviceId;
|
|
392
|
-
const audioOrVideoDeviceKey = trackTypeToDeviceIdKey(trackType);
|
|
393
388
|
this.state.updateParticipant(this.sfuClient.sessionId, (p) => {
|
|
394
389
|
return {
|
|
395
390
|
publishedTracks: p.publishedTracks.includes(trackType)
|
|
396
391
|
? p.publishedTracks
|
|
397
392
|
: [...p.publishedTracks, trackType],
|
|
398
|
-
...(audioOrVideoDeviceKey && { [audioOrVideoDeviceKey]: deviceId }),
|
|
399
393
|
[audioOrVideoOrScreenShareStream]: mediaStream,
|
|
400
394
|
};
|
|
401
395
|
});
|
|
@@ -99,7 +99,6 @@ describe('Publisher', () => {
|
|
|
99
99
|
// initial publish
|
|
100
100
|
await publisher.publishStream(mediaStream, track, TrackType.VIDEO);
|
|
101
101
|
|
|
102
|
-
expect(state.localParticipant?.videoDeviceId).toEqual('test-device-id');
|
|
103
102
|
expect(state.localParticipant?.publishedTracks).toContain(TrackType.VIDEO);
|
|
104
103
|
expect(state.localParticipant?.videoStream).toEqual(mediaStream);
|
|
105
104
|
expect(transceiver.setCodecPreferences).toHaveBeenCalled();
|
|
@@ -136,7 +135,6 @@ describe('Publisher', () => {
|
|
|
136
135
|
expect.any(Function),
|
|
137
136
|
);
|
|
138
137
|
expect(transceiver.sender.replaceTrack).toHaveBeenCalledWith(newTrack);
|
|
139
|
-
expect(state.localParticipant?.videoDeviceId).toEqual('test-device-id-2');
|
|
140
138
|
|
|
141
139
|
// stop publishing
|
|
142
140
|
await publisher.unpublishStream(TrackType.VIDEO, true);
|
|
@@ -144,7 +142,6 @@ describe('Publisher', () => {
|
|
|
144
142
|
expect(state.localParticipant?.publishedTracks).not.toContain(
|
|
145
143
|
TrackType.VIDEO,
|
|
146
144
|
);
|
|
147
|
-
expect(state.localParticipant?.videoDeviceId).toEqual('test-device-id-2');
|
|
148
145
|
});
|
|
149
146
|
|
|
150
147
|
it('can publish and un-pubish with just enabling and disabling tracks', async () => {
|
|
@@ -178,7 +175,6 @@ describe('Publisher', () => {
|
|
|
178
175
|
// initial publish
|
|
179
176
|
await publisher.publishStream(mediaStream, track, TrackType.VIDEO);
|
|
180
177
|
|
|
181
|
-
expect(state.localParticipant?.videoDeviceId).toEqual('test-device-id');
|
|
182
178
|
expect(state.localParticipant?.publishedTracks).toContain(TrackType.VIDEO);
|
|
183
179
|
expect(track.enabled).toBe(true);
|
|
184
180
|
expect(state.localParticipant?.videoStream).toEqual(mediaStream);
|
package/src/rtc/flows/join.ts
CHANGED
|
@@ -3,12 +3,7 @@ import {
|
|
|
3
3
|
JoinCallRequest,
|
|
4
4
|
JoinCallResponse,
|
|
5
5
|
} from '../../gen/coordinator';
|
|
6
|
-
import {
|
|
7
|
-
isStreamVideoLocalParticipant,
|
|
8
|
-
JoinCallData,
|
|
9
|
-
StreamVideoLocalParticipant,
|
|
10
|
-
StreamVideoParticipant,
|
|
11
|
-
} from '../../types';
|
|
6
|
+
import { JoinCallData, StreamVideoParticipant } from '../../types';
|
|
12
7
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
13
8
|
|
|
14
9
|
/**
|
|
@@ -107,21 +102,11 @@ const getCascadingModeParams = () => {
|
|
|
107
102
|
* @param source the participant to reconcile from.
|
|
108
103
|
*/
|
|
109
104
|
export const reconcileParticipantLocalState = (
|
|
110
|
-
target: StreamVideoParticipant
|
|
111
|
-
source?: StreamVideoParticipant
|
|
105
|
+
target: StreamVideoParticipant,
|
|
106
|
+
source?: StreamVideoParticipant,
|
|
112
107
|
) => {
|
|
113
108
|
if (!source) return target;
|
|
114
109
|
|
|
115
110
|
// copy everything from source to target
|
|
116
|
-
Object.assign(target, source);
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
isStreamVideoLocalParticipant(source) &&
|
|
120
|
-
isStreamVideoLocalParticipant(target)
|
|
121
|
-
) {
|
|
122
|
-
target.audioDeviceId = source.audioDeviceId;
|
|
123
|
-
target.videoDeviceId = source.videoDeviceId;
|
|
124
|
-
target.audioOutputDeviceId = source.audioOutputDeviceId;
|
|
125
|
-
}
|
|
126
|
-
return target;
|
|
111
|
+
return Object.assign(target, source);
|
|
127
112
|
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { TrackType } from '../../gen/video/sfu/models/models';
|
|
2
|
-
import type {
|
|
3
|
-
StreamVideoLocalParticipant,
|
|
4
|
-
StreamVideoParticipant,
|
|
5
|
-
} from '../../types';
|
|
2
|
+
import type { StreamVideoParticipant } from '../../types';
|
|
6
3
|
import { TrackMuteType } from '../../types';
|
|
7
4
|
|
|
8
5
|
export const trackTypeToParticipantStreamKey = (
|
|
@@ -25,24 +22,6 @@ export const trackTypeToParticipantStreamKey = (
|
|
|
25
22
|
}
|
|
26
23
|
};
|
|
27
24
|
|
|
28
|
-
export const trackTypeToDeviceIdKey = (
|
|
29
|
-
trackType: TrackType,
|
|
30
|
-
): keyof StreamVideoLocalParticipant | undefined => {
|
|
31
|
-
switch (trackType) {
|
|
32
|
-
case TrackType.AUDIO:
|
|
33
|
-
return 'audioDeviceId';
|
|
34
|
-
case TrackType.VIDEO:
|
|
35
|
-
return 'videoDeviceId';
|
|
36
|
-
case TrackType.SCREEN_SHARE:
|
|
37
|
-
case TrackType.SCREEN_SHARE_AUDIO:
|
|
38
|
-
case TrackType.UNSPECIFIED:
|
|
39
|
-
return undefined;
|
|
40
|
-
default:
|
|
41
|
-
const exhaustiveTrackTypeCheck: never = trackType;
|
|
42
|
-
throw new Error(`Unknown track type: ${exhaustiveTrackTypeCheck}`);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
25
|
export const muteTypeToTrackType = (muteType: TrackMuteType): TrackType => {
|
|
47
26
|
switch (muteType) {
|
|
48
27
|
case 'audio':
|