@stream-io/video-client 1.43.0 → 1.44.1-beta.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 +15 -0
- package/dist/index.browser.es.js +220 -64
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +219 -63
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +220 -64
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +1 -1
- package/dist/src/StreamVideoClient.d.ts +2 -8
- package/dist/src/coordinator/connection/types.d.ts +5 -0
- package/dist/src/devices/CameraManager.d.ts +7 -2
- package/dist/src/devices/DeviceManager.d.ts +7 -15
- package/dist/src/devices/MicrophoneManager.d.ts +2 -1
- package/dist/src/devices/SpeakerManager.d.ts +6 -1
- package/dist/src/devices/devicePersistence.d.ts +27 -0
- package/dist/src/helpers/clientUtils.d.ts +1 -1
- package/dist/src/permissions/PermissionsContext.d.ts +1 -1
- package/dist/src/types.d.ts +38 -2
- package/package.json +1 -1
- package/src/Call.ts +28 -8
- package/src/StreamVideoClient.ts +1 -9
- package/src/coordinator/connection/types.ts +6 -0
- package/src/devices/CameraManager.ts +31 -11
- package/src/devices/DeviceManager.ts +113 -31
- package/src/devices/MicrophoneManager.ts +26 -8
- package/src/devices/ScreenShareManager.ts +7 -1
- package/src/devices/SpeakerManager.ts +62 -18
- package/src/devices/__tests__/CameraManager.test.ts +184 -21
- package/src/devices/__tests__/DeviceManager.test.ts +184 -2
- package/src/devices/__tests__/DeviceManagerFilters.test.ts +2 -0
- package/src/devices/__tests__/MicrophoneManager.test.ts +146 -2
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +2 -0
- package/src/devices/__tests__/ScreenShareManager.test.ts +2 -0
- package/src/devices/__tests__/SpeakerManager.test.ts +90 -0
- package/src/devices/__tests__/devicePersistence.test.ts +142 -0
- package/src/devices/__tests__/devices.test.ts +390 -0
- package/src/devices/__tests__/mediaStreamTestHelpers.ts +58 -0
- package/src/devices/__tests__/mocks.ts +35 -0
- package/src/devices/devicePersistence.ts +106 -0
- package/src/devices/devices.ts +3 -3
- package/src/helpers/__tests__/DynascaleManager.test.ts +3 -1
- package/src/helpers/clientUtils.ts +1 -1
- package/src/permissions/PermissionsContext.ts +1 -0
- package/src/sorting/presets.ts +1 -1
- package/src/store/CallState.ts +1 -1
- package/src/types.ts +49 -2
package/dist/src/Call.d.ts
CHANGED
|
@@ -761,7 +761,7 @@ export declare class Call {
|
|
|
761
761
|
*
|
|
762
762
|
* @internal
|
|
763
763
|
*/
|
|
764
|
-
applyDeviceConfig: (settings: CallSettingsResponse, publish: boolean) => Promise<void>;
|
|
764
|
+
applyDeviceConfig: (settings: CallSettingsResponse, publish: boolean, skipSpeakerApply?: boolean) => Promise<void>;
|
|
765
765
|
/**
|
|
766
766
|
* Will begin tracking the given element for visibility changes within the
|
|
767
767
|
* configured viewport element (`call.setViewport`).
|
|
@@ -2,15 +2,9 @@ import { Call } from './Call';
|
|
|
2
2
|
import { StreamClient } from './coordinator/connection/client';
|
|
3
3
|
import { StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore } from './store';
|
|
4
4
|
import type { ConnectedEvent, CreateDeviceRequest, CreateGuestRequest, CreateGuestResponse, GetEdgesResponse, ListDevicesResponse, QueryAggregateCallStatsRequest, QueryAggregateCallStatsResponse, QueryCallsRequest, QueryCallStatsRequest, QueryCallStatsResponse } from './gen/coordinator';
|
|
5
|
-
import { AllClientEvents, ClientEventListener, StreamClientOptions, TokenOrProvider,
|
|
5
|
+
import { AllClientEvents, ClientEventListener, StreamClientOptions, TokenOrProvider, User } from './coordinator/connection/types';
|
|
6
|
+
import type { StreamVideoClientOptions } from './types';
|
|
6
7
|
import { ScopedLogger } from './logger';
|
|
7
|
-
export type StreamVideoClientOptions = {
|
|
8
|
-
apiKey: string;
|
|
9
|
-
options?: StreamClientOptions;
|
|
10
|
-
user?: User;
|
|
11
|
-
token?: string;
|
|
12
|
-
tokenProvider?: TokenProvider;
|
|
13
|
-
};
|
|
14
8
|
/**
|
|
15
9
|
* A `StreamVideoClient` instance lets you communicate with our API, and authenticate users.
|
|
16
10
|
*/
|
|
@@ -2,6 +2,7 @@ import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
|
2
2
|
import { ConnectedEvent, UserRequest, VideoEvent } from '../../gen/coordinator';
|
|
3
3
|
import { AllSfuEvents } from '../../rtc';
|
|
4
4
|
import type { ConfigureLoggersOptions, LogLevel } from '@stream-io/logger';
|
|
5
|
+
import type { DevicePersistenceOptions } from '../../devices/devicePersistence';
|
|
5
6
|
export type UR = Record<string, unknown>;
|
|
6
7
|
export type User = (Omit<UserRequest, 'role'> & {
|
|
7
8
|
type?: 'authenticated';
|
|
@@ -185,6 +186,10 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
|
|
|
185
186
|
* When set to true, the incoming calls are rejected when the user is busy in an another call.
|
|
186
187
|
*/
|
|
187
188
|
rejectCallWhenBusy?: boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Device persistence preference options (web only).
|
|
191
|
+
*/
|
|
192
|
+
devicePersistence?: DevicePersistenceOptions;
|
|
188
193
|
};
|
|
189
194
|
export type ClientAppIdentifier = {
|
|
190
195
|
sdkName?: 'react' | 'react-native' | 'plain-javascript' | (string & {});
|
|
@@ -3,21 +3,26 @@ import { Call } from '../Call';
|
|
|
3
3
|
import { CameraDirection, CameraManagerState } from './CameraManagerState';
|
|
4
4
|
import { DeviceManager } from './DeviceManager';
|
|
5
5
|
import { VideoSettingsResponse } from '../gen/coordinator';
|
|
6
|
+
import { DevicePersistenceOptions } from './devicePersistence';
|
|
6
7
|
export declare class CameraManager extends DeviceManager<CameraManagerState> {
|
|
7
8
|
private targetResolution;
|
|
8
9
|
/**
|
|
9
10
|
* Constructs a new CameraManager.
|
|
10
11
|
*
|
|
11
12
|
* @param call the call instance.
|
|
13
|
+
* @param devicePersistence the device persistence preferences to use.
|
|
12
14
|
*/
|
|
13
|
-
constructor(call: Call);
|
|
15
|
+
constructor(call: Call, devicePersistence: Required<DevicePersistenceOptions>);
|
|
14
16
|
private isDirectionSupportedByDevice;
|
|
15
17
|
/**
|
|
16
18
|
* Select the camera direction.
|
|
17
19
|
*
|
|
18
20
|
* @param direction the direction of the camera to select.
|
|
21
|
+
* @param options additional direction selection options.
|
|
19
22
|
*/
|
|
20
|
-
selectDirection(direction: Exclude<CameraDirection, undefined
|
|
23
|
+
selectDirection(direction: Exclude<CameraDirection, undefined>, options?: {
|
|
24
|
+
enableCamera?: boolean;
|
|
25
|
+
}): Promise<void>;
|
|
21
26
|
/**
|
|
22
27
|
* Flips the camera direction: if it's front it will change to back, if it's back, it will change to front.
|
|
23
28
|
*
|
|
@@ -5,6 +5,7 @@ import { DeviceManagerState } from './DeviceManagerState';
|
|
|
5
5
|
import { ScopedLogger } from '../logger';
|
|
6
6
|
import { TrackType } from '../gen/video/sfu/models/models';
|
|
7
7
|
import { MediaStreamFilter, MediaStreamFilterRegistrationResult } from './filters';
|
|
8
|
+
import { DevicePersistenceOptions } from './devicePersistence';
|
|
8
9
|
export declare abstract class DeviceManager<S extends DeviceManagerState<C>, C = MediaTrackConstraints> {
|
|
9
10
|
/**
|
|
10
11
|
* if true, stops the media stream when call is left
|
|
@@ -12,28 +13,16 @@ export declare abstract class DeviceManager<S extends DeviceManagerState<C>, C =
|
|
|
12
13
|
stopOnLeave: boolean;
|
|
13
14
|
logger: ScopedLogger;
|
|
14
15
|
state: S;
|
|
15
|
-
/**
|
|
16
|
-
* When `true`, the `apply()` method will skip automatically enabling/disabling
|
|
17
|
-
* the device based on server defaults (`mic_default_on`, `camera_default_on`).
|
|
18
|
-
*
|
|
19
|
-
* This is useful when application code wants to handle device preferences
|
|
20
|
-
* (e.g., persisted user preferences) and prevent server defaults from
|
|
21
|
-
* overriding them.
|
|
22
|
-
*
|
|
23
|
-
* @default false
|
|
24
|
-
*
|
|
25
|
-
* @internal
|
|
26
|
-
*/
|
|
27
|
-
deferServerDefaults: boolean;
|
|
28
16
|
protected readonly call: Call;
|
|
29
17
|
protected readonly trackType: TrackType;
|
|
30
|
-
protected subscriptions:
|
|
18
|
+
protected subscriptions: (() => void)[];
|
|
19
|
+
protected devicePersistence: Required<DevicePersistenceOptions>;
|
|
31
20
|
protected areSubscriptionsSetUp: boolean;
|
|
32
21
|
private isTrackStoppedDueToTrackEnd;
|
|
33
22
|
private filters;
|
|
34
23
|
private statusChangeConcurrencyTag;
|
|
35
24
|
private filterRegistrationConcurrencyTag;
|
|
36
|
-
protected constructor(call: Call, state: S, trackType: TrackType);
|
|
25
|
+
protected constructor(call: Call, state: S, trackType: TrackType, devicePersistence: Required<DevicePersistenceOptions>);
|
|
37
26
|
setup(): void;
|
|
38
27
|
/**
|
|
39
28
|
* Lists the available audio/video devices
|
|
@@ -115,4 +104,7 @@ export declare abstract class DeviceManager<S extends DeviceManagerState<C>, C =
|
|
|
115
104
|
private get mediaDeviceKind();
|
|
116
105
|
private handleDisconnectedOrReplacedDevices;
|
|
117
106
|
protected findDevice(devices: MediaDeviceInfo[], deviceId: string): MediaDeviceInfo | undefined;
|
|
107
|
+
private persistPreference;
|
|
108
|
+
protected applyPersistedPreferences(enabledInCallType: boolean): Promise<boolean>;
|
|
109
|
+
private applyMutedState;
|
|
118
110
|
}
|
|
@@ -6,6 +6,7 @@ import { MicrophoneManagerState } from './MicrophoneManagerState';
|
|
|
6
6
|
import { TrackDisableMode } from './DeviceManagerState';
|
|
7
7
|
import { AudioBitrateProfile } from '../gen/video/sfu/models/models';
|
|
8
8
|
import { AudioSettingsResponse } from '../gen/coordinator';
|
|
9
|
+
import { DevicePersistenceOptions } from './devicePersistence';
|
|
9
10
|
export declare class MicrophoneManager extends AudioDeviceManager<MicrophoneManagerState> {
|
|
10
11
|
private speakingWhileMutedNotificationEnabled;
|
|
11
12
|
private soundDetectorConcurrencyTag;
|
|
@@ -18,7 +19,7 @@ export declare class MicrophoneManager extends AudioDeviceManager<MicrophoneMana
|
|
|
18
19
|
private noiseCancellationRegistration?;
|
|
19
20
|
private unregisterNoiseCancellation?;
|
|
20
21
|
private silenceThresholdMs;
|
|
21
|
-
constructor(call: Call, disableMode?: TrackDisableMode);
|
|
22
|
+
constructor(call: Call, devicePersistence: Required<DevicePersistenceOptions>, disableMode?: TrackDisableMode);
|
|
22
23
|
setup(): void;
|
|
23
24
|
/**
|
|
24
25
|
* Enables noise cancellation for the microphone.
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { Call } from '../Call';
|
|
2
2
|
import { SpeakerState } from './SpeakerState';
|
|
3
3
|
import { CallSettingsResponse } from '../gen/coordinator';
|
|
4
|
+
import { DevicePersistenceOptions } from './devicePersistence';
|
|
4
5
|
export declare class SpeakerManager {
|
|
5
6
|
readonly state: SpeakerState;
|
|
6
7
|
private subscriptions;
|
|
7
8
|
private areSubscriptionsSetUp;
|
|
8
9
|
private readonly call;
|
|
9
10
|
private defaultDevice?;
|
|
10
|
-
|
|
11
|
+
private readonly devicePersistence;
|
|
12
|
+
constructor(call: Call, devicePreferences: Required<DevicePersistenceOptions>);
|
|
11
13
|
apply(settings: CallSettingsResponse): void;
|
|
14
|
+
private applyWeb;
|
|
15
|
+
private applyRN;
|
|
12
16
|
setup(): void;
|
|
13
17
|
/**
|
|
14
18
|
* Lists the available audio output devices
|
|
@@ -47,4 +51,5 @@ export declare class SpeakerManager {
|
|
|
47
51
|
* @param volume a number between 0 and 1. Set it to `undefined` to use the default volume.
|
|
48
52
|
*/
|
|
49
53
|
setParticipantVolume(sessionId: string, volume: number | undefined): void;
|
|
54
|
+
private persistSpeakerDevicePreference;
|
|
50
55
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type DevicePersistenceOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Enables device preference persistence on web.
|
|
4
|
+
* @default true
|
|
5
|
+
*/
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Storage key for persisted preferences.
|
|
9
|
+
* @default '@stream-io/device-preferences'
|
|
10
|
+
*/
|
|
11
|
+
storageKey?: string;
|
|
12
|
+
};
|
|
13
|
+
export type DevicePreferenceKey = 'microphone' | 'camera' | 'speaker';
|
|
14
|
+
export type LocalDevicePreference = {
|
|
15
|
+
selectedDeviceId: string;
|
|
16
|
+
selectedDeviceLabel: string;
|
|
17
|
+
muted?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type LocalDevicePreferences = {
|
|
20
|
+
[type in DevicePreferenceKey]?: LocalDevicePreference | LocalDevicePreference[];
|
|
21
|
+
};
|
|
22
|
+
export declare const defaultDeviceId = "default";
|
|
23
|
+
export declare const normalize: (options: DevicePersistenceOptions | undefined) => Required<DevicePersistenceOptions>;
|
|
24
|
+
export declare const createSyntheticDevice: (deviceId: string, kind: MediaDeviceKind) => MediaDeviceInfo;
|
|
25
|
+
export declare const readPreferences: (storageKey: string) => LocalDevicePreferences;
|
|
26
|
+
export declare const writePreferences: (currentDevice: MediaDeviceInfo | undefined, deviceKey: DevicePreferenceKey, muted: boolean | undefined, storageKey: string) => void;
|
|
27
|
+
export declare const toPreferenceList: (preference?: LocalDevicePreference | LocalDevicePreference[]) => LocalDevicePreference[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StreamClientOptions, TokenOrProvider, User } from '../coordinator/connection/types';
|
|
2
2
|
import { StreamClient } from '../coordinator/connection/client';
|
|
3
|
-
import type { StreamVideoClientOptions } from '../
|
|
3
|
+
import type { StreamVideoClientOptions } from '../types';
|
|
4
4
|
/**
|
|
5
5
|
* Utility function to get the instance key.
|
|
6
6
|
*/
|
|
@@ -33,7 +33,7 @@ export declare class PermissionsContext {
|
|
|
33
33
|
* Helper method that checks whether the current user has the permission
|
|
34
34
|
* to publish the given track type.
|
|
35
35
|
*/
|
|
36
|
-
canPublish: (trackType: TrackType) => boolean
|
|
36
|
+
canPublish: (trackType: TrackType) => boolean;
|
|
37
37
|
/**
|
|
38
38
|
* Checks if the current user can request a specific permission
|
|
39
39
|
* within the call.
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Participant, TrackType, VideoDimension } from './gen/video/sfu/models/models';
|
|
2
|
-
import type { CallRecordingStartedEventRecordingTypeEnum, JoinCallRequest, MemberResponse, OwnCapability, ReactionResponse,
|
|
2
|
+
import type { AudioSettingsRequestDefaultDeviceEnum, CallRecordingStartedEventRecordingTypeEnum, JoinCallRequest, MemberResponse, OwnCapability, ReactionResponse, StartRecordingRequest, StartRecordingResponse } from './gen/coordinator';
|
|
3
3
|
import type { StreamClient } from './coordinator/connection/client';
|
|
4
|
+
import type { RejectReason, StreamClientOptions, TokenProvider, User } from './coordinator/connection/types';
|
|
4
5
|
import type { Comparator } from './sorting';
|
|
5
6
|
import type { StreamVideoWriteableStateStore } from './store';
|
|
6
7
|
import { AxiosError } from 'axios';
|
|
7
|
-
import { RejectReason } from './coordinator/connection/types';
|
|
8
8
|
export type StreamReaction = Pick<ReactionResponse, 'type' | 'emoji_code' | 'custom'>;
|
|
9
9
|
export declare enum VisibilityState {
|
|
10
10
|
UNKNOWN = "UNKNOWN",
|
|
@@ -273,6 +273,42 @@ export type CallConstructor = {
|
|
|
273
273
|
*/
|
|
274
274
|
clientStore: StreamVideoWriteableStateStore;
|
|
275
275
|
};
|
|
276
|
+
type StreamVideoClientBaseOptions = {
|
|
277
|
+
apiKey: string;
|
|
278
|
+
options?: StreamClientOptions;
|
|
279
|
+
};
|
|
280
|
+
type StreamVideoClientOptionsWithoutUser = StreamVideoClientBaseOptions & {
|
|
281
|
+
user?: undefined;
|
|
282
|
+
token?: never;
|
|
283
|
+
tokenProvider?: never;
|
|
284
|
+
};
|
|
285
|
+
type GuestUser = Extract<User, {
|
|
286
|
+
type: 'guest';
|
|
287
|
+
}>;
|
|
288
|
+
type AnonymousUser = Extract<User, {
|
|
289
|
+
type: 'anonymous';
|
|
290
|
+
}>;
|
|
291
|
+
type AuthenticatedUser = Exclude<User, GuestUser | AnonymousUser>;
|
|
292
|
+
type StreamVideoClientOptionsWithGuestUser = StreamVideoClientBaseOptions & {
|
|
293
|
+
user: GuestUser;
|
|
294
|
+
token?: never;
|
|
295
|
+
tokenProvider?: never;
|
|
296
|
+
};
|
|
297
|
+
type StreamVideoClientOptionsWithAnonymousUser = StreamVideoClientBaseOptions & {
|
|
298
|
+
user: AnonymousUser;
|
|
299
|
+
token?: string;
|
|
300
|
+
tokenProvider?: TokenProvider;
|
|
301
|
+
};
|
|
302
|
+
type StreamVideoClientOptionsWithAuthenticatedUser = StreamVideoClientBaseOptions & {
|
|
303
|
+
user: AuthenticatedUser;
|
|
304
|
+
} & ({
|
|
305
|
+
token: string;
|
|
306
|
+
tokenProvider?: TokenProvider;
|
|
307
|
+
} | {
|
|
308
|
+
token?: string;
|
|
309
|
+
tokenProvider: TokenProvider;
|
|
310
|
+
});
|
|
311
|
+
export type StreamVideoClientOptions = StreamVideoClientOptionsWithoutUser | StreamVideoClientOptionsWithGuestUser | StreamVideoClientOptionsWithAnonymousUser | StreamVideoClientOptionsWithAuthenticatedUser;
|
|
276
312
|
export type CallRecordingType = CallRecordingStartedEventRecordingTypeEnum;
|
|
277
313
|
export type StartCallRecordingFnType = {
|
|
278
314
|
(): Promise<StartRecordingResponse>;
|
package/package.json
CHANGED
package/src/Call.ts
CHANGED
|
@@ -162,6 +162,7 @@ import {
|
|
|
162
162
|
ScreenShareManager,
|
|
163
163
|
SpeakerManager,
|
|
164
164
|
} from './devices';
|
|
165
|
+
import { normalize } from './devices/devicePersistence';
|
|
165
166
|
import { hasPending, withoutConcurrency } from './helpers/concurrency';
|
|
166
167
|
import { ensureExhausted } from './helpers/ensureExhausted';
|
|
167
168
|
import { pushToIfMissing } from './helpers/array';
|
|
@@ -171,6 +172,7 @@ import {
|
|
|
171
172
|
promiseWithResolvers,
|
|
172
173
|
} from './helpers/promise';
|
|
173
174
|
import { GetCallStatsResponse } from './gen/shims';
|
|
175
|
+
import { isReactNative } from './helpers/platforms';
|
|
174
176
|
|
|
175
177
|
/**
|
|
176
178
|
* An object representation of a `Call`.
|
|
@@ -341,9 +343,10 @@ export class Call {
|
|
|
341
343
|
ringing ? CallingState.RINGING : CallingState.IDLE,
|
|
342
344
|
);
|
|
343
345
|
|
|
344
|
-
|
|
345
|
-
this.
|
|
346
|
-
this.
|
|
346
|
+
const preferences = normalize(streamClient.options.devicePersistence);
|
|
347
|
+
this.camera = new CameraManager(this, preferences);
|
|
348
|
+
this.microphone = new MicrophoneManager(this, preferences);
|
|
349
|
+
this.speaker = new SpeakerManager(this, preferences);
|
|
347
350
|
this.screenShare = new ScreenShareManager(this);
|
|
348
351
|
this.dynascaleManager = new DynascaleManager(
|
|
349
352
|
this.state,
|
|
@@ -744,7 +747,8 @@ export class Call {
|
|
|
744
747
|
// const calls = useCalls().filter((c) => c.ringing);
|
|
745
748
|
const calls = this.clientStore.calls.filter((c) => c.cid !== this.cid);
|
|
746
749
|
this.clientStore.setCalls([this, ...calls]);
|
|
747
|
-
|
|
750
|
+
const skipSpeakerApply = isReactNative() && true;
|
|
751
|
+
await this.applyDeviceConfig(settings, false, skipSpeakerApply);
|
|
748
752
|
};
|
|
749
753
|
|
|
750
754
|
/**
|
|
@@ -779,8 +783,14 @@ export class Call {
|
|
|
779
783
|
this.watching = true;
|
|
780
784
|
this.clientStore.registerOrUpdateCall(this);
|
|
781
785
|
}
|
|
782
|
-
|
|
783
|
-
|
|
786
|
+
const skipSpeakerApply = isReactNative()
|
|
787
|
+
? (params?.ring ?? this.ringing)
|
|
788
|
+
: false;
|
|
789
|
+
await this.applyDeviceConfig(
|
|
790
|
+
response.call.settings,
|
|
791
|
+
false,
|
|
792
|
+
skipSpeakerApply,
|
|
793
|
+
);
|
|
784
794
|
|
|
785
795
|
return response;
|
|
786
796
|
};
|
|
@@ -810,7 +820,14 @@ export class Call {
|
|
|
810
820
|
this.clientStore.registerOrUpdateCall(this);
|
|
811
821
|
}
|
|
812
822
|
|
|
813
|
-
|
|
823
|
+
const skipSpeakerApply = isReactNative()
|
|
824
|
+
? (data?.ring ?? this.ringing)
|
|
825
|
+
: false;
|
|
826
|
+
await this.applyDeviceConfig(
|
|
827
|
+
response.call.settings,
|
|
828
|
+
false,
|
|
829
|
+
skipSpeakerApply,
|
|
830
|
+
);
|
|
814
831
|
|
|
815
832
|
return response;
|
|
816
833
|
};
|
|
@@ -2794,8 +2811,11 @@ export class Call {
|
|
|
2794
2811
|
applyDeviceConfig = async (
|
|
2795
2812
|
settings: CallSettingsResponse,
|
|
2796
2813
|
publish: boolean,
|
|
2814
|
+
skipSpeakerApply: boolean = false,
|
|
2797
2815
|
) => {
|
|
2798
|
-
|
|
2816
|
+
if (!skipSpeakerApply) {
|
|
2817
|
+
this.speaker.apply(settings);
|
|
2818
|
+
}
|
|
2799
2819
|
await this.camera.apply(settings.video, publish).catch((err) => {
|
|
2800
2820
|
this.logger.warn('Camera init failed', err);
|
|
2801
2821
|
});
|
package/src/StreamVideoClient.ts
CHANGED
|
@@ -27,10 +27,10 @@ import {
|
|
|
27
27
|
ErrorFromResponse,
|
|
28
28
|
StreamClientOptions,
|
|
29
29
|
TokenOrProvider,
|
|
30
|
-
TokenProvider,
|
|
31
30
|
User,
|
|
32
31
|
UserWithId,
|
|
33
32
|
} from './coordinator/connection/types';
|
|
33
|
+
import type { StreamVideoClientOptions } from './types';
|
|
34
34
|
import { retryInterval, sleep } from './coordinator/connection/utils';
|
|
35
35
|
import {
|
|
36
36
|
createCoordinatorClient,
|
|
@@ -42,14 +42,6 @@ import { logToConsole, ScopedLogger, videoLoggerSystem } from './logger';
|
|
|
42
42
|
import { withoutConcurrency } from './helpers/concurrency';
|
|
43
43
|
import { enableTimerWorker } from './timers';
|
|
44
44
|
|
|
45
|
-
export type StreamVideoClientOptions = {
|
|
46
|
-
apiKey: string;
|
|
47
|
-
options?: StreamClientOptions;
|
|
48
|
-
user?: User;
|
|
49
|
-
token?: string;
|
|
50
|
-
tokenProvider?: TokenProvider;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
45
|
/**
|
|
54
46
|
* A `StreamVideoClient` instance lets you communicate with our API, and authenticate users.
|
|
55
47
|
*/
|
|
@@ -2,6 +2,7 @@ import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
|
2
2
|
import { ConnectedEvent, UserRequest, VideoEvent } from '../../gen/coordinator';
|
|
3
3
|
import { AllSfuEvents } from '../../rtc';
|
|
4
4
|
import type { ConfigureLoggersOptions, LogLevel } from '@stream-io/logger';
|
|
5
|
+
import type { DevicePersistenceOptions } from '../../devices/devicePersistence';
|
|
5
6
|
|
|
6
7
|
export type UR = Record<string, unknown>;
|
|
7
8
|
|
|
@@ -258,6 +259,11 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
|
|
|
258
259
|
* When set to true, the incoming calls are rejected when the user is busy in an another call.
|
|
259
260
|
*/
|
|
260
261
|
rejectCallWhenBusy?: boolean;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Device persistence preference options (web only).
|
|
265
|
+
*/
|
|
266
|
+
devicePersistence?: DevicePersistenceOptions;
|
|
261
267
|
};
|
|
262
268
|
|
|
263
269
|
export type ClientAppIdentifier = {
|
|
@@ -7,6 +7,7 @@ import { VideoSettingsResponse } from '../gen/coordinator';
|
|
|
7
7
|
import { TrackType } from '../gen/video/sfu/models/models';
|
|
8
8
|
import { isMobile } from '../helpers/compatibility';
|
|
9
9
|
import { isReactNative } from '../helpers/platforms';
|
|
10
|
+
import { DevicePersistenceOptions } from './devicePersistence';
|
|
10
11
|
|
|
11
12
|
export class CameraManager extends DeviceManager<CameraManagerState> {
|
|
12
13
|
private targetResolution = {
|
|
@@ -18,9 +19,13 @@ export class CameraManager extends DeviceManager<CameraManagerState> {
|
|
|
18
19
|
* Constructs a new CameraManager.
|
|
19
20
|
*
|
|
20
21
|
* @param call the call instance.
|
|
22
|
+
* @param devicePersistence the device persistence preferences to use.
|
|
21
23
|
*/
|
|
22
|
-
constructor(
|
|
23
|
-
|
|
24
|
+
constructor(
|
|
25
|
+
call: Call,
|
|
26
|
+
devicePersistence: Required<DevicePersistenceOptions>,
|
|
27
|
+
) {
|
|
28
|
+
super(call, new CameraManagerState(), TrackType.VIDEO, devicePersistence);
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
private isDirectionSupportedByDevice() {
|
|
@@ -31,8 +36,12 @@ export class CameraManager extends DeviceManager<CameraManagerState> {
|
|
|
31
36
|
* Select the camera direction.
|
|
32
37
|
*
|
|
33
38
|
* @param direction the direction of the camera to select.
|
|
39
|
+
* @param options additional direction selection options.
|
|
34
40
|
*/
|
|
35
|
-
async selectDirection(
|
|
41
|
+
async selectDirection(
|
|
42
|
+
direction: Exclude<CameraDirection, undefined>,
|
|
43
|
+
options: { enableCamera?: boolean } = {},
|
|
44
|
+
) {
|
|
36
45
|
if (!this.isDirectionSupportedByDevice()) {
|
|
37
46
|
this.logger.warn('Setting direction is not supported on this device');
|
|
38
47
|
return;
|
|
@@ -47,9 +56,10 @@ export class CameraManager extends DeviceManager<CameraManagerState> {
|
|
|
47
56
|
// providing both device id and direction doesn't work, so we deselect the device
|
|
48
57
|
this.state.setDirection(direction);
|
|
49
58
|
this.state.setDevice(undefined);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
|
|
60
|
+
const { enableCamera = true } = options;
|
|
61
|
+
if (isReactNative() || !enableCamera) return;
|
|
62
|
+
|
|
53
63
|
this.getTracks().forEach((track) => track.stop());
|
|
54
64
|
try {
|
|
55
65
|
await this.unmuteStream();
|
|
@@ -120,16 +130,26 @@ export class CameraManager extends DeviceManager<CameraManagerState> {
|
|
|
120
130
|
await this.statusChangeSettled();
|
|
121
131
|
await this.selectTargetResolution(settings.target_resolution);
|
|
122
132
|
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
const enabledInCallType = settings.enabled ?? true;
|
|
134
|
+
const shouldApplyDefaults =
|
|
135
|
+
this.state.status === undefined &&
|
|
136
|
+
this.state.optimisticStatus === undefined;
|
|
137
|
+
let persistedPreferencesApplied = false;
|
|
138
|
+
if (shouldApplyDefaults && this.devicePersistence.enabled) {
|
|
139
|
+
persistedPreferencesApplied =
|
|
140
|
+
await this.applyPersistedPreferences(enabledInCallType);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// apply a direction and enable the camera only if in "pristine" state,
|
|
144
|
+
// and there are no persisted preferences
|
|
125
145
|
const canPublish = this.call.permissionsContext.canPublish(this.trackType);
|
|
126
|
-
if (
|
|
146
|
+
if (shouldApplyDefaults && !persistedPreferencesApplied) {
|
|
127
147
|
if (!this.state.direction && !this.state.selectedDevice) {
|
|
128
148
|
const direction = settings.camera_facing === 'front' ? 'front' : 'back';
|
|
129
|
-
await this.selectDirection(direction);
|
|
149
|
+
await this.selectDirection(direction, { enableCamera: false });
|
|
130
150
|
}
|
|
131
151
|
|
|
132
|
-
if (canPublish && settings.camera_default_on &&
|
|
152
|
+
if (canPublish && settings.camera_default_on && enabledInCallType) {
|
|
133
153
|
await this.enable();
|
|
134
154
|
}
|
|
135
155
|
}
|