@stream-io/video-client 0.3.2 → 0.3.4
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 +14 -0
- package/dist/index.browser.es.js +77 -56
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +77 -56
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +77 -56
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +2 -1
- package/dist/src/coordinator/connection/client.d.ts +9 -4
- package/dist/src/devices/CameraManager.d.ts +3 -9
- package/dist/src/devices/InputMediaDeviceManager.d.ts +11 -5
- package/dist/src/devices/InputMediaDeviceManagerState.d.ts +6 -1
- package/dist/src/devices/MicrophoneManager.d.ts +3 -9
- package/dist/src/devices/MicrophoneManagerState.d.ts +1 -0
- package/dist/src/rtc/Publisher.d.ts +2 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Call.ts +3 -2
- package/src/StreamVideoClient.ts +1 -10
- package/src/__tests__/StreamVideoClient.test.ts +13 -0
- package/src/coordinator/connection/client.ts +28 -0
- package/src/devices/CameraManager.ts +8 -17
- package/src/devices/CameraManagerState.ts +1 -1
- package/src/devices/InputMediaDeviceManager.ts +39 -17
- package/src/devices/InputMediaDeviceManagerState.ts +12 -2
- package/src/devices/MicrophoneManager.ts +8 -17
- package/src/devices/MicrophoneManagerState.ts +4 -0
- package/src/devices/__tests__/CameraManager.test.ts +4 -14
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +48 -5
- package/src/devices/__tests__/MicrophoneManager.test.ts +8 -7
- package/src/rtc/Publisher.ts +8 -2
- package/src/rtc/__tests__/Publisher.test.ts +67 -1
package/dist/src/Call.d.ts
CHANGED
|
@@ -217,8 +217,9 @@ export declare class Call {
|
|
|
217
217
|
*
|
|
218
218
|
*
|
|
219
219
|
* @param trackType the track type to stop publishing.
|
|
220
|
+
* @param stopTrack if `true` the track will be stopped, else it will be just disabled
|
|
220
221
|
*/
|
|
221
|
-
stopPublish: (trackType: TrackType) => Promise<void>;
|
|
222
|
+
stopPublish: (trackType: TrackType, stopTrack?: boolean) => Promise<void>;
|
|
222
223
|
/**
|
|
223
224
|
* Update track subscription configuration for one or more participants.
|
|
224
225
|
* You have to create a subscription for each participant for all the different kinds of tracks you want to receive.
|
|
@@ -4,8 +4,9 @@ import WebSocket from 'isomorphic-ws';
|
|
|
4
4
|
import { StableWSConnection } from './connection';
|
|
5
5
|
import { TokenManager } from './token_manager';
|
|
6
6
|
import { WSConnectionFallback } from './connection_fallback';
|
|
7
|
-
import { APIErrorResponse, ConnectAPIResponse, ErrorFromResponse, EventHandler, Logger, StreamClientOptions, StreamVideoEvent, TokenOrProvider, UserWithId } from './types';
|
|
7
|
+
import { APIErrorResponse, ConnectAPIResponse, ErrorFromResponse, EventHandler, Logger, StreamClientOptions, StreamVideoEvent, TokenOrProvider, User, UserWithId } from './types';
|
|
8
8
|
import { InsightMetrics } from './insights';
|
|
9
|
+
import { CreateGuestResponse } from '../../gen/coordinator';
|
|
9
10
|
export declare class StreamClient {
|
|
10
11
|
_user?: UserWithId;
|
|
11
12
|
anonymous: boolean;
|
|
@@ -38,6 +39,7 @@ export declare class StreamClient {
|
|
|
38
39
|
resolveConnectionId?: Function;
|
|
39
40
|
rejectConnectionId?: Function;
|
|
40
41
|
connectionIdPromise?: Promise<string | undefined>;
|
|
42
|
+
guestUserCreatePromise?: Promise<CreateGuestResponse>;
|
|
41
43
|
private nextRequestAbortController;
|
|
42
44
|
/**
|
|
43
45
|
* Initialize a client.
|
|
@@ -66,7 +68,7 @@ export declare class StreamClient {
|
|
|
66
68
|
*
|
|
67
69
|
* @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup
|
|
68
70
|
*/
|
|
69
|
-
connectUser: (user: UserWithId, userTokenOrProvider: TokenOrProvider) => Promise<void | import("
|
|
71
|
+
connectUser: (user: UserWithId, userTokenOrProvider: TokenOrProvider) => Promise<void | import("../../gen/coordinator").ConnectedEvent>;
|
|
70
72
|
_setToken: (user: UserWithId, userTokenOrProvider: TokenOrProvider, isAnonymous: boolean) => Promise<void>;
|
|
71
73
|
_setUser: (user: UserWithId) => void;
|
|
72
74
|
/**
|
|
@@ -86,7 +88,7 @@ export declare class StreamClient {
|
|
|
86
88
|
/**
|
|
87
89
|
* Creates a new WebSocket connection with the current user. Returns empty promise, if there is an active connection
|
|
88
90
|
*/
|
|
89
|
-
openConnection: () => Promise<void | import("
|
|
91
|
+
openConnection: () => Promise<void | import("../../gen/coordinator").ConnectedEvent>;
|
|
90
92
|
_normalizeDate: (before: Date | string | null) => string | null;
|
|
91
93
|
/**
|
|
92
94
|
* Disconnects the websocket and removes the user from client.
|
|
@@ -95,6 +97,9 @@ export declare class StreamClient {
|
|
|
95
97
|
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
96
98
|
*/
|
|
97
99
|
disconnectUser: (timeout?: number) => Promise<void>;
|
|
100
|
+
connectGuestUser: (user: User & {
|
|
101
|
+
type: 'guest';
|
|
102
|
+
}) => Promise<void | import("../../gen/coordinator").ConnectedEvent>;
|
|
98
103
|
/**
|
|
99
104
|
* connectAnonymousUser - Set an anonymous user and open a WebSocket connection
|
|
100
105
|
*/
|
|
@@ -144,7 +149,7 @@ export declare class StreamClient {
|
|
|
144
149
|
/**
|
|
145
150
|
* @private
|
|
146
151
|
*/
|
|
147
|
-
connect: () => Promise<void | import("
|
|
152
|
+
connect: () => Promise<void | import("../../gen/coordinator").ConnectedEvent>;
|
|
148
153
|
/**
|
|
149
154
|
* Check the connectivity with server for warmup purpose.
|
|
150
155
|
*
|
|
@@ -19,13 +19,7 @@ export declare class CameraManager extends InputMediaDeviceManager<CameraManager
|
|
|
19
19
|
protected getDevices(): Observable<MediaDeviceInfo[]>;
|
|
20
20
|
protected getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
21
21
|
protected publishStream(stream: MediaStream): Promise<void>;
|
|
22
|
-
protected stopPublishStream(): Promise<void>;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*/
|
|
26
|
-
pause(): void;
|
|
27
|
-
/**
|
|
28
|
-
* (Re)enables the video tracks of the camera
|
|
29
|
-
*/
|
|
30
|
-
resume(): void;
|
|
22
|
+
protected stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
23
|
+
protected muteTracks(): void;
|
|
24
|
+
protected unmuteTracks(): void;
|
|
31
25
|
}
|
|
@@ -19,11 +19,17 @@ export declare abstract class InputMediaDeviceManager<T extends InputMediaDevice
|
|
|
19
19
|
enable(): Promise<void>;
|
|
20
20
|
/**
|
|
21
21
|
* Stops camera/microphone
|
|
22
|
+
*
|
|
22
23
|
* @returns
|
|
23
24
|
*/
|
|
24
25
|
disable(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* If status was previously enabled, it will reenable the device.
|
|
28
|
+
*/
|
|
29
|
+
resume(): Promise<void>;
|
|
25
30
|
/**
|
|
26
31
|
* If current device statis is disabled, it will enable the device, else it will disable it.
|
|
32
|
+
*
|
|
27
33
|
* @returns
|
|
28
34
|
*/
|
|
29
35
|
toggle(): Promise<void>;
|
|
@@ -36,12 +42,12 @@ export declare abstract class InputMediaDeviceManager<T extends InputMediaDevice
|
|
|
36
42
|
*/
|
|
37
43
|
select(deviceId: string | undefined): Promise<void>;
|
|
38
44
|
protected applySettingsToStream(): Promise<void>;
|
|
39
|
-
abstract pause(): void;
|
|
40
|
-
abstract resume(): void;
|
|
41
45
|
protected abstract getDevices(): Observable<MediaDeviceInfo[]>;
|
|
42
46
|
protected abstract getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
43
47
|
protected abstract publishStream(stream: MediaStream): Promise<void>;
|
|
44
|
-
protected abstract stopPublishStream(): Promise<void>;
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
protected abstract stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
49
|
+
protected abstract muteTracks(): void;
|
|
50
|
+
protected abstract unmuteTracks(): void;
|
|
51
|
+
private muteStream;
|
|
52
|
+
private unmuteStream;
|
|
47
53
|
}
|
|
@@ -2,9 +2,14 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
|
|
2
2
|
import { RxUtils } from '../store';
|
|
3
3
|
export type InputDeviceStatus = 'enabled' | 'disabled' | undefined;
|
|
4
4
|
export declare abstract class InputMediaDeviceManagerState {
|
|
5
|
+
readonly disableMode: 'stop-tracks' | 'disable-tracks';
|
|
5
6
|
protected statusSubject: BehaviorSubject<InputDeviceStatus>;
|
|
6
7
|
protected mediaStreamSubject: BehaviorSubject<MediaStream | undefined>;
|
|
7
8
|
protected selectedDeviceSubject: BehaviorSubject<string | undefined>;
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
prevStatus: InputDeviceStatus;
|
|
8
13
|
/**
|
|
9
14
|
* An Observable that emits the current media stream, or `undefined` if the device is currently disabled.
|
|
10
15
|
*
|
|
@@ -18,7 +23,7 @@ export declare abstract class InputMediaDeviceManagerState {
|
|
|
18
23
|
* An Observable that emits the device status
|
|
19
24
|
*/
|
|
20
25
|
status$: Observable<InputDeviceStatus>;
|
|
21
|
-
constructor();
|
|
26
|
+
constructor(disableMode?: 'stop-tracks' | 'disable-tracks');
|
|
22
27
|
/**
|
|
23
28
|
* The device status
|
|
24
29
|
*/
|
|
@@ -7,13 +7,7 @@ export declare class MicrophoneManager extends InputMediaDeviceManager<Microphon
|
|
|
7
7
|
protected getDevices(): Observable<MediaDeviceInfo[]>;
|
|
8
8
|
protected getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
9
9
|
protected publishStream(stream: MediaStream): Promise<void>;
|
|
10
|
-
protected stopPublishStream(): Promise<void>;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
pause(): void;
|
|
15
|
-
/**
|
|
16
|
-
* (Re)enables the audio tracks of the microphone
|
|
17
|
-
*/
|
|
18
|
-
resume(): void;
|
|
10
|
+
protected stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
11
|
+
protected muteTracks(): void;
|
|
12
|
+
protected unmuteTracks(): void;
|
|
19
13
|
}
|
|
@@ -76,8 +76,9 @@ export declare class Publisher {
|
|
|
76
76
|
* Stops publishing the given track type to the SFU, if it is currently being published.
|
|
77
77
|
* Underlying track will be stopped and removed from the publisher.
|
|
78
78
|
* @param trackType the track type to unpublish.
|
|
79
|
+
* @param stopTrack specifies whether track should be stopped or just disabled
|
|
79
80
|
*/
|
|
80
|
-
unpublishStream: (trackType: TrackType) => Promise<void>;
|
|
81
|
+
unpublishStream: (trackType: TrackType, stopTrack: boolean) => Promise<void>;
|
|
81
82
|
/**
|
|
82
83
|
* Returns true if the given track type is currently being published to the SFU.
|
|
83
84
|
*
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.3.
|
|
1
|
+
export declare const version = "0.3.4";
|
package/package.json
CHANGED
package/src/Call.ts
CHANGED
|
@@ -1092,10 +1092,11 @@ export class Call {
|
|
|
1092
1092
|
*
|
|
1093
1093
|
*
|
|
1094
1094
|
* @param trackType the track type to stop publishing.
|
|
1095
|
+
* @param stopTrack if `true` the track will be stopped, else it will be just disabled
|
|
1095
1096
|
*/
|
|
1096
|
-
stopPublish = async (trackType: TrackType) => {
|
|
1097
|
+
stopPublish = async (trackType: TrackType, stopTrack: boolean = true) => {
|
|
1097
1098
|
this.logger('info', `stopPublish ${TrackType[trackType]}`);
|
|
1098
|
-
await this.publisher?.unpublishStream(trackType);
|
|
1099
|
+
await this.publisher?.unpublishStream(trackType, stopTrack);
|
|
1099
1100
|
};
|
|
1100
1101
|
|
|
1101
1102
|
/**
|
package/src/StreamVideoClient.ts
CHANGED
|
@@ -152,16 +152,7 @@ export class StreamVideoClient {
|
|
|
152
152
|
};
|
|
153
153
|
if (user.type === 'guest') {
|
|
154
154
|
connectUser = async () => {
|
|
155
|
-
|
|
156
|
-
user: {
|
|
157
|
-
...user,
|
|
158
|
-
role: 'guest',
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
return this.streamClient.connectUser(
|
|
162
|
-
response.user,
|
|
163
|
-
response.access_token,
|
|
164
|
-
);
|
|
155
|
+
return this.streamClient.connectGuestUser(user);
|
|
165
156
|
};
|
|
166
157
|
}
|
|
167
158
|
this.connectionPromise = this.disconnectionPromise
|
|
@@ -3,6 +3,7 @@ import { StreamVideoClient } from '../StreamVideoClient';
|
|
|
3
3
|
import 'dotenv/config';
|
|
4
4
|
import { generateUUIDv4 } from '../coordinator/connection/utils';
|
|
5
5
|
import { StreamVideoServerClient } from '../StreamVideoServerClient';
|
|
6
|
+
import { User } from '../coordinator/connection/types';
|
|
6
7
|
|
|
7
8
|
const apiKey = process.env.STREAM_API_KEY!;
|
|
8
9
|
const secret = process.env.STREAM_SECRET!;
|
|
@@ -87,6 +88,18 @@ describe('StreamVideoClient', () => {
|
|
|
87
88
|
);
|
|
88
89
|
});
|
|
89
90
|
|
|
91
|
+
it('API calls should be hold until auth is done - guest user', async () => {
|
|
92
|
+
const user: User = {
|
|
93
|
+
id: 'jane-guest',
|
|
94
|
+
type: 'guest',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
client.connectUser(user);
|
|
98
|
+
const response = await client.queryCalls({});
|
|
99
|
+
|
|
100
|
+
expect(response.calls).toBeDefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
90
103
|
afterEach(() => {
|
|
91
104
|
client.disconnectUser();
|
|
92
105
|
});
|
|
@@ -30,11 +30,13 @@ import {
|
|
|
30
30
|
StreamClientOptions,
|
|
31
31
|
StreamVideoEvent,
|
|
32
32
|
TokenOrProvider,
|
|
33
|
+
User,
|
|
33
34
|
UserWithId,
|
|
34
35
|
} from './types';
|
|
35
36
|
import { InsightMetrics, postInsights } from './insights';
|
|
36
37
|
import { version } from '../../../version';
|
|
37
38
|
import { getLocationHint } from './location';
|
|
39
|
+
import { CreateGuestRequest, CreateGuestResponse } from '../../gen/coordinator';
|
|
38
40
|
|
|
39
41
|
export class StreamClient {
|
|
40
42
|
_user?: UserWithId;
|
|
@@ -70,6 +72,7 @@ export class StreamClient {
|
|
|
70
72
|
resolveConnectionId?: Function;
|
|
71
73
|
rejectConnectionId?: Function;
|
|
72
74
|
connectionIdPromise?: Promise<string | undefined>;
|
|
75
|
+
guestUserCreatePromise?: Promise<CreateGuestResponse>;
|
|
73
76
|
private nextRequestAbortController: AbortController | null = null;
|
|
74
77
|
|
|
75
78
|
/**
|
|
@@ -402,6 +405,30 @@ export class StreamClient {
|
|
|
402
405
|
this.resolveConnectionId = undefined;
|
|
403
406
|
};
|
|
404
407
|
|
|
408
|
+
connectGuestUser = async (user: User & { type: 'guest' }) => {
|
|
409
|
+
this.guestUserCreatePromise = this.doAxiosRequest<
|
|
410
|
+
CreateGuestResponse,
|
|
411
|
+
CreateGuestRequest
|
|
412
|
+
>(
|
|
413
|
+
'post',
|
|
414
|
+
'/guest',
|
|
415
|
+
{
|
|
416
|
+
user: {
|
|
417
|
+
...user,
|
|
418
|
+
role: 'guest',
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{ publicEndpoint: true },
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const response = await this.guestUserCreatePromise;
|
|
425
|
+
this.guestUserCreatePromise.finally(
|
|
426
|
+
() => (this.guestUserCreatePromise = undefined),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
return this.connectUser(response.user, response.access_token);
|
|
430
|
+
};
|
|
431
|
+
|
|
405
432
|
/**
|
|
406
433
|
* connectAnonymousUser - Set an anonymous user and open a WebSocket connection
|
|
407
434
|
*/
|
|
@@ -524,6 +551,7 @@ export class StreamClient {
|
|
|
524
551
|
if (!options.publicEndpoint) {
|
|
525
552
|
await Promise.all([
|
|
526
553
|
this.tokenManager.tokenReady(),
|
|
554
|
+
this.guestUserCreatePromise,
|
|
527
555
|
this.connectionIdPromise,
|
|
528
556
|
]);
|
|
529
557
|
}
|
|
@@ -49,25 +49,16 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
|
|
|
49
49
|
protected publishStream(stream: MediaStream): Promise<void> {
|
|
50
50
|
return this.call.publishVideoStream(stream);
|
|
51
51
|
}
|
|
52
|
-
protected stopPublishStream(): Promise<void> {
|
|
53
|
-
return this.call.stopPublish(TrackType.VIDEO);
|
|
52
|
+
protected stopPublishStream(stopTracks: boolean): Promise<void> {
|
|
53
|
+
return this.call.stopPublish(TrackType.VIDEO, stopTracks);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.state.mediaStream?.getVideoTracks().forEach((track) => {
|
|
61
|
-
track.enabled = false;
|
|
62
|
-
});
|
|
56
|
+
protected muteTracks(): void {
|
|
57
|
+
this.state.mediaStream
|
|
58
|
+
?.getVideoTracks()
|
|
59
|
+
.forEach((t) => (t.enabled = false));
|
|
63
60
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* (Re)enables the video tracks of the camera
|
|
67
|
-
*/
|
|
68
|
-
resume() {
|
|
69
|
-
this.state.mediaStream?.getVideoTracks().forEach((track) => {
|
|
70
|
-
track.enabled = true;
|
|
71
|
-
});
|
|
61
|
+
protected unmuteTracks(): void {
|
|
62
|
+
this.state.mediaStream?.getVideoTracks().forEach((t) => (t.enabled = true));
|
|
72
63
|
}
|
|
73
64
|
}
|
|
@@ -15,7 +15,7 @@ export class CameraManagerState extends InputMediaDeviceManagerState {
|
|
|
15
15
|
direction$: Observable<CameraDirection>;
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
|
-
super();
|
|
18
|
+
super('stop-tracks');
|
|
19
19
|
this.direction$ = this.directionSubject
|
|
20
20
|
.asObservable()
|
|
21
21
|
.pipe(distinctUntilChanged());
|
|
@@ -28,24 +28,39 @@ export abstract class InputMediaDeviceManager<
|
|
|
28
28
|
if (this.state.status === 'enabled') {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
await this.
|
|
31
|
+
await this.unmuteStream();
|
|
32
32
|
this.state.setStatus('enabled');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Stops camera/microphone
|
|
37
|
+
*
|
|
37
38
|
* @returns
|
|
38
39
|
*/
|
|
39
40
|
async disable() {
|
|
40
41
|
if (this.state.status === 'disabled') {
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
this.state.prevStatus = this.state.status;
|
|
45
|
+
await this.muteStream(this.state.disableMode === 'stop-tracks');
|
|
44
46
|
this.state.setStatus('disabled');
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
/**
|
|
50
|
+
* If status was previously enabled, it will reenable the device.
|
|
51
|
+
*/
|
|
52
|
+
async resume() {
|
|
53
|
+
if (
|
|
54
|
+
this.state.prevStatus === 'enabled' &&
|
|
55
|
+
this.state.status === 'disabled'
|
|
56
|
+
) {
|
|
57
|
+
this.enable();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
47
61
|
/**
|
|
48
62
|
* If current device statis is disabled, it will enable the device, else it will disable it.
|
|
63
|
+
*
|
|
49
64
|
* @returns
|
|
50
65
|
*/
|
|
51
66
|
async toggle() {
|
|
@@ -76,15 +91,11 @@ export abstract class InputMediaDeviceManager<
|
|
|
76
91
|
|
|
77
92
|
protected async applySettingsToStream() {
|
|
78
93
|
if (this.state.status === 'enabled') {
|
|
79
|
-
await this.
|
|
80
|
-
await this.
|
|
94
|
+
await this.muteStream();
|
|
95
|
+
await this.unmuteStream();
|
|
81
96
|
}
|
|
82
97
|
}
|
|
83
98
|
|
|
84
|
-
abstract pause(): void;
|
|
85
|
-
|
|
86
|
-
abstract resume(): void;
|
|
87
|
-
|
|
88
99
|
protected abstract getDevices(): Observable<MediaDeviceInfo[]>;
|
|
89
100
|
|
|
90
101
|
protected abstract getStream(
|
|
@@ -93,26 +104,37 @@ export abstract class InputMediaDeviceManager<
|
|
|
93
104
|
|
|
94
105
|
protected abstract publishStream(stream: MediaStream): Promise<void>;
|
|
95
106
|
|
|
96
|
-
protected abstract stopPublishStream(): Promise<void>;
|
|
107
|
+
protected abstract stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
108
|
+
|
|
109
|
+
protected abstract muteTracks(): void;
|
|
110
|
+
|
|
111
|
+
protected abstract unmuteTracks(): void;
|
|
97
112
|
|
|
98
|
-
private async
|
|
113
|
+
private async muteStream(stopTracks: boolean = true) {
|
|
99
114
|
if (!this.state.mediaStream) {
|
|
100
115
|
return;
|
|
101
116
|
}
|
|
102
117
|
if (this.call.state.callingState === CallingState.JOINED) {
|
|
103
|
-
await this.stopPublishStream();
|
|
118
|
+
await this.stopPublishStream(stopTracks);
|
|
104
119
|
} else if (this.state.mediaStream) {
|
|
105
|
-
|
|
120
|
+
stopTracks
|
|
121
|
+
? disposeOfMediaStream(this.state.mediaStream)
|
|
122
|
+
: this.muteTracks();
|
|
123
|
+
}
|
|
124
|
+
if (stopTracks) {
|
|
125
|
+
this.state.setMediaStream(undefined);
|
|
106
126
|
}
|
|
107
|
-
this.state.setMediaStream(undefined);
|
|
108
127
|
}
|
|
109
128
|
|
|
110
|
-
private async
|
|
129
|
+
private async unmuteStream() {
|
|
130
|
+
let stream: MediaStream;
|
|
111
131
|
if (this.state.mediaStream) {
|
|
112
|
-
|
|
132
|
+
stream = this.state.mediaStream;
|
|
133
|
+
this.unmuteTracks();
|
|
134
|
+
} else {
|
|
135
|
+
const constraints = { deviceId: this.state.selectedDevice };
|
|
136
|
+
stream = await this.getStream(constraints);
|
|
113
137
|
}
|
|
114
|
-
const constraints = { deviceId: this.state.selectedDevice };
|
|
115
|
-
const stream = await this.getStream(constraints);
|
|
116
138
|
if (this.call.state.callingState === CallingState.JOINED) {
|
|
117
139
|
await this.publishStream(stream);
|
|
118
140
|
}
|
|
@@ -11,6 +11,10 @@ export abstract class InputMediaDeviceManagerState {
|
|
|
11
11
|
protected selectedDeviceSubject = new BehaviorSubject<string | undefined>(
|
|
12
12
|
undefined,
|
|
13
13
|
);
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
prevStatus: InputDeviceStatus;
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* An Observable that emits the current media stream, or `undefined` if the device is currently disabled.
|
|
@@ -28,12 +32,18 @@ export abstract class InputMediaDeviceManagerState {
|
|
|
28
32
|
*/
|
|
29
33
|
status$: Observable<InputDeviceStatus>;
|
|
30
34
|
|
|
31
|
-
constructor(
|
|
35
|
+
constructor(
|
|
36
|
+
public readonly disableMode:
|
|
37
|
+
| 'stop-tracks'
|
|
38
|
+
| 'disable-tracks' = 'stop-tracks',
|
|
39
|
+
) {
|
|
32
40
|
this.mediaStream$ = this.mediaStreamSubject.asObservable();
|
|
33
41
|
this.selectedDevice$ = this.selectedDeviceSubject
|
|
34
42
|
.asObservable()
|
|
35
43
|
.pipe(distinctUntilChanged());
|
|
36
|
-
this.status$ = this.statusSubject
|
|
44
|
+
this.status$ = this.statusSubject
|
|
45
|
+
.asObservable()
|
|
46
|
+
.pipe(distinctUntilChanged());
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
/**
|
|
@@ -21,25 +21,16 @@ export class MicrophoneManager extends InputMediaDeviceManager<MicrophoneManager
|
|
|
21
21
|
protected publishStream(stream: MediaStream): Promise<void> {
|
|
22
22
|
return this.call.publishAudioStream(stream);
|
|
23
23
|
}
|
|
24
|
-
protected stopPublishStream(): Promise<void> {
|
|
25
|
-
return this.call.stopPublish(TrackType.AUDIO);
|
|
24
|
+
protected stopPublishStream(stopTracks: boolean): Promise<void> {
|
|
25
|
+
return this.call.stopPublish(TrackType.AUDIO, stopTracks);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.state.mediaStream?.getAudioTracks().forEach((track) => {
|
|
33
|
-
track.enabled = false;
|
|
34
|
-
});
|
|
28
|
+
protected muteTracks(): void {
|
|
29
|
+
this.state.mediaStream
|
|
30
|
+
?.getAudioTracks()
|
|
31
|
+
.forEach((t) => (t.enabled = false));
|
|
35
32
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* (Re)enables the audio tracks of the microphone
|
|
39
|
-
*/
|
|
40
|
-
resume() {
|
|
41
|
-
this.state.mediaStream?.getAudioTracks().forEach((track) => {
|
|
42
|
-
track.enabled = true;
|
|
43
|
-
});
|
|
33
|
+
protected unmuteTracks(): void {
|
|
34
|
+
this.state.mediaStream?.getAudioTracks().forEach((t) => (t.enabled = true));
|
|
44
35
|
}
|
|
45
36
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { InputMediaDeviceManagerState } from './InputMediaDeviceManagerState';
|
|
2
2
|
|
|
3
3
|
export class MicrophoneManagerState extends InputMediaDeviceManagerState {
|
|
4
|
+
constructor() {
|
|
5
|
+
super('disable-tracks');
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
protected getDeviceIdFromStream(stream: MediaStream): string | undefined {
|
|
5
9
|
return stream.getAudioTracks()[0]?.getSettings().deviceId as
|
|
6
10
|
| string
|
|
@@ -8,7 +8,6 @@ import { getVideoStream } from '../devices';
|
|
|
8
8
|
import { TrackType } from '../../gen/video/sfu/models/models';
|
|
9
9
|
import { CameraManager } from '../CameraManager';
|
|
10
10
|
import { of } from 'rxjs';
|
|
11
|
-
import { CallSettingsResponse } from '../../gen/coordinator';
|
|
12
11
|
|
|
13
12
|
vi.mock('../devices.ts', () => {
|
|
14
13
|
console.log('MOCKING devices API');
|
|
@@ -83,7 +82,10 @@ describe('CameraManager', () => {
|
|
|
83
82
|
|
|
84
83
|
await manager.disable();
|
|
85
84
|
|
|
86
|
-
expect(manager['call'].stopPublish).toHaveBeenCalledWith(
|
|
85
|
+
expect(manager['call'].stopPublish).toHaveBeenCalledWith(
|
|
86
|
+
TrackType.VIDEO,
|
|
87
|
+
true,
|
|
88
|
+
);
|
|
87
89
|
});
|
|
88
90
|
|
|
89
91
|
it('flip', async () => {
|
|
@@ -131,18 +133,6 @@ describe('CameraManager', () => {
|
|
|
131
133
|
});
|
|
132
134
|
});
|
|
133
135
|
|
|
134
|
-
it('should pause and resume tracks', async () => {
|
|
135
|
-
await manager.enable();
|
|
136
|
-
|
|
137
|
-
manager.pause();
|
|
138
|
-
|
|
139
|
-
expect(manager.state.mediaStream?.getVideoTracks()[0].enabled).toBe(false);
|
|
140
|
-
|
|
141
|
-
manager.resume();
|
|
142
|
-
|
|
143
|
-
expect(manager.state.mediaStream?.getVideoTracks()[0].enabled).toBe(true);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
136
|
afterEach(() => {
|
|
147
137
|
vi.clearAllMocks();
|
|
148
138
|
vi.resetModules();
|