@stream-io/video-client 1.4.1 → 1.4.3
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 +219 -144
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +219 -142
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +219 -144
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +7 -7
- package/dist/src/StreamSfuClient.d.ts +7 -7
- package/dist/src/StreamVideoClient.d.ts +5 -5
- package/dist/src/coordinator/connection/client.d.ts +13 -14
- package/dist/src/coordinator/connection/connection.d.ts +3 -5
- package/dist/src/coordinator/connection/insights.d.ts +0 -1
- package/dist/src/devices/BrowserPermission.d.ts +24 -0
- package/dist/src/devices/InputMediaDeviceManagerState.d.ts +3 -3
- package/dist/src/devices/devices.d.ts +30 -11
- package/dist/src/gen/coordinator/index.d.ts +5 -0
- package/dist/src/helpers/ViewportTracker.d.ts +1 -1
- package/dist/src/helpers/lazy.d.ts +4 -0
- package/dist/src/helpers/sdp-munging.d.ts +2 -2
- package/dist/src/rtc/Dispatcher.d.ts +2 -2
- package/dist/src/rtc/codecs.d.ts +1 -1
- package/dist/src/rtc/signal.d.ts +0 -1
- package/dist/src/stats/utils.d.ts +4 -4
- package/dist/src/store/CallState.d.ts +1 -1
- package/package.json +4 -4
- package/src/devices/BrowserPermission.ts +152 -0
- package/src/devices/CameraManagerState.ts +2 -6
- package/src/devices/InputMediaDeviceManagerState.ts +10 -44
- package/src/devices/MicrophoneManagerState.ts +2 -6
- package/src/devices/__tests__/CameraManager.test.ts +3 -0
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +5 -3
- package/src/devices/__tests__/InputMediaDeviceManagerFilters.test.ts +6 -2
- package/src/devices/__tests__/InputMediaDeviceManagerState.test.ts +41 -51
- package/src/devices/__tests__/MicrophoneManager.test.ts +4 -1
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +8 -1
- package/src/devices/__tests__/SpeakerManager.test.ts +8 -1
- package/src/devices/__tests__/mocks.ts +6 -1
- package/src/devices/devices.ts +113 -112
- package/src/gen/coordinator/index.ts +5 -0
- package/src/helpers/RNSpeechDetector.ts +1 -1
- package/src/helpers/lazy.ts +15 -0
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
BehaviorSubject,
|
|
3
3
|
distinctUntilChanged,
|
|
4
4
|
Observable,
|
|
5
|
+
of,
|
|
5
6
|
shareReplay,
|
|
6
7
|
} from 'rxjs';
|
|
7
|
-
import { isReactNative } from '../helpers/platforms';
|
|
8
8
|
import { RxUtils } from '../store';
|
|
9
|
-
import {
|
|
9
|
+
import { BrowserPermission } from './BrowserPermission';
|
|
10
10
|
|
|
11
11
|
export type InputDeviceStatus = 'enabled' | 'disabled' | undefined;
|
|
12
12
|
export type TrackDisableMode = 'stop-tracks' | 'disable-tracks';
|
|
@@ -65,57 +65,23 @@ export abstract class InputMediaDeviceManagerState<C = MediaTrackConstraints> {
|
|
|
65
65
|
* An observable that will emit `true` if browser/system permission
|
|
66
66
|
* is granted, `false` otherwise.
|
|
67
67
|
*/
|
|
68
|
-
hasBrowserPermission
|
|
69
|
-
const notifyGranted = () => subscriber.next(true);
|
|
70
|
-
const permissionsAPIAvailable = !!navigator?.permissions?.query;
|
|
71
|
-
if (isReactNative() || !this.permissionName || !permissionsAPIAvailable) {
|
|
72
|
-
getLogger(['devices'])(
|
|
73
|
-
'warn',
|
|
74
|
-
`Permissions can't be queried. Assuming granted.`,
|
|
75
|
-
);
|
|
76
|
-
return notifyGranted();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let permissionState: PermissionStatus;
|
|
80
|
-
const notify = () => {
|
|
81
|
-
subscriber.next(
|
|
82
|
-
// In some browsers, the 'change' event doesn't reliably emit and hence,
|
|
83
|
-
// permissionState stays in 'prompt' state forever.
|
|
84
|
-
// Typically, this happens when a user grants one-time permission.
|
|
85
|
-
// Instead of checking if a permission is granted, we check if it isn't denied
|
|
86
|
-
permissionState.state !== 'denied',
|
|
87
|
-
);
|
|
88
|
-
};
|
|
89
|
-
navigator.permissions
|
|
90
|
-
.query({ name: this.permissionName })
|
|
91
|
-
.then((permissionStatus) => {
|
|
92
|
-
permissionState = permissionStatus;
|
|
93
|
-
permissionState.addEventListener('change', notify);
|
|
94
|
-
notify();
|
|
95
|
-
})
|
|
96
|
-
.catch(() => {
|
|
97
|
-
// permission doesn't exist or can't be queried -> assume it's granted
|
|
98
|
-
// an example would be Firefox,
|
|
99
|
-
// where neither camera microphone permission can be queried
|
|
100
|
-
notifyGranted();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
return () => {
|
|
104
|
-
permissionState?.removeEventListener('change', notify);
|
|
105
|
-
};
|
|
106
|
-
}).pipe(shareReplay(1));
|
|
68
|
+
hasBrowserPermission$: Observable<boolean>;
|
|
107
69
|
|
|
108
70
|
/**
|
|
109
71
|
* Constructs new InputMediaDeviceManagerState instance.
|
|
110
72
|
*
|
|
111
73
|
* @param disableMode the disable mode to use.
|
|
112
|
-
* @param
|
|
74
|
+
* @param permission the BrowserPermission to use for querying.
|
|
113
75
|
* `undefined` means no permission is required.
|
|
114
76
|
*/
|
|
115
77
|
constructor(
|
|
116
78
|
public readonly disableMode: TrackDisableMode = 'stop-tracks',
|
|
117
|
-
|
|
118
|
-
) {
|
|
79
|
+
permission?: BrowserPermission,
|
|
80
|
+
) {
|
|
81
|
+
this.hasBrowserPermission$ = permission
|
|
82
|
+
? permission.asObservable().pipe(shareReplay(1))
|
|
83
|
+
: of(true);
|
|
84
|
+
}
|
|
119
85
|
|
|
120
86
|
/**
|
|
121
87
|
* The device status
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
InputMediaDeviceManagerState,
|
|
4
4
|
TrackDisableMode,
|
|
5
5
|
} from './InputMediaDeviceManagerState';
|
|
6
|
+
import { getAudioBrowserPermission } from './devices';
|
|
6
7
|
|
|
7
8
|
export class MicrophoneManagerState extends InputMediaDeviceManagerState {
|
|
8
9
|
private speakingWhileMutedSubject = new BehaviorSubject<boolean>(false);
|
|
@@ -15,12 +16,7 @@ export class MicrophoneManagerState extends InputMediaDeviceManagerState {
|
|
|
15
16
|
speakingWhileMuted$: Observable<boolean>;
|
|
16
17
|
|
|
17
18
|
constructor(disableMode: TrackDisableMode) {
|
|
18
|
-
super(
|
|
19
|
-
disableMode,
|
|
20
|
-
// `microphone` is not in the W3C standard yet,
|
|
21
|
-
// but it's supported by Chrome and Safari.
|
|
22
|
-
'microphone' as PermissionName,
|
|
23
|
-
);
|
|
19
|
+
super(disableMode, getAudioBrowserPermission());
|
|
24
20
|
|
|
25
21
|
this.speakingWhileMuted$ = this.speakingWhileMutedSubject
|
|
26
22
|
.asObservable()
|
|
@@ -4,6 +4,7 @@ import { CallingState, StreamVideoWriteableStateStore } from '../../store';
|
|
|
4
4
|
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
|
6
6
|
import {
|
|
7
|
+
mockBrowserPermission,
|
|
7
8
|
mockCall,
|
|
8
9
|
mockDeviceIds$,
|
|
9
10
|
mockVideoDevices,
|
|
@@ -22,6 +23,8 @@ vi.mock('../devices.ts', () => {
|
|
|
22
23
|
return of(mockVideoDevices);
|
|
23
24
|
}),
|
|
24
25
|
getVideoStream: vi.fn(() => Promise.resolve(mockVideoStream())),
|
|
26
|
+
getAudioBrowserPermission: () => mockBrowserPermission,
|
|
27
|
+
getVideoBrowserPermission: () => mockBrowserPermission,
|
|
25
28
|
deviceIds$: mockDeviceIds$(),
|
|
26
29
|
};
|
|
27
30
|
});
|
|
@@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
6
6
|
import {
|
|
7
7
|
MockTrack,
|
|
8
8
|
emitDeviceIds,
|
|
9
|
+
mockBrowserPermission,
|
|
9
10
|
mockCall,
|
|
10
11
|
mockDeviceIds$,
|
|
11
12
|
mockVideoDevices,
|
|
@@ -26,6 +27,8 @@ vi.mock('../../Call.ts', () => {
|
|
|
26
27
|
vi.mock('../devices.ts', () => {
|
|
27
28
|
console.log('MOCKING devices API');
|
|
28
29
|
return {
|
|
30
|
+
getAudioBrowserPermission: () => mockBrowserPermission,
|
|
31
|
+
getVideoBrowserPermission: () => mockBrowserPermission,
|
|
29
32
|
deviceIds$: mockDeviceIds$(),
|
|
30
33
|
};
|
|
31
34
|
});
|
|
@@ -48,7 +51,7 @@ class TestInputMediaDeviceManager extends InputMediaDeviceManager<TestInputMedia
|
|
|
48
51
|
call,
|
|
49
52
|
new TestInputMediaDeviceManagerState(
|
|
50
53
|
'stop-tracks',
|
|
51
|
-
|
|
54
|
+
mockBrowserPermission,
|
|
52
55
|
),
|
|
53
56
|
TrackType.VIDEO,
|
|
54
57
|
);
|
|
@@ -315,8 +318,7 @@ describe('InputMediaDeviceManager.test', () => {
|
|
|
315
318
|
await vi.runAllTimersAsync();
|
|
316
319
|
|
|
317
320
|
expect(manager.state.status).toBe('disabled');
|
|
318
|
-
expect(manager.
|
|
319
|
-
expect(manager.enablePromise).toBeUndefined();
|
|
321
|
+
expect(manager.state.optimisticStatus).toBe('disabled');
|
|
320
322
|
expect(manager.state.selectedDevice).toBe(device.deviceId);
|
|
321
323
|
|
|
322
324
|
vi.useRealTimers();
|
|
@@ -5,7 +5,11 @@ import { StreamClient } from '../../coordinator/connection/client';
|
|
|
5
5
|
import { StreamVideoWriteableStateStore } from '../../store';
|
|
6
6
|
import { InputMediaDeviceManagerState } from '../InputMediaDeviceManagerState';
|
|
7
7
|
import { InputMediaDeviceManager } from '../InputMediaDeviceManager';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
mockBrowserPermission,
|
|
10
|
+
mockVideoDevices,
|
|
11
|
+
mockVideoStream,
|
|
12
|
+
} from './mocks';
|
|
9
13
|
import { TrackType } from '../../gen/video/sfu/models/models';
|
|
10
14
|
|
|
11
15
|
import '../../rtc/__tests__/mocks/webrtc.mocks';
|
|
@@ -28,7 +32,7 @@ class TestInputMediaDeviceManager extends InputMediaDeviceManager<TestInputMedia
|
|
|
28
32
|
call,
|
|
29
33
|
new TestInputMediaDeviceManagerState(
|
|
30
34
|
'stop-tracks',
|
|
31
|
-
|
|
35
|
+
mockBrowserPermission,
|
|
32
36
|
),
|
|
33
37
|
TrackType.VIDEO,
|
|
34
38
|
);
|
|
@@ -1,72 +1,66 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { InputMediaDeviceManagerState } from '../InputMediaDeviceManagerState';
|
|
3
|
+
import { firstValueFrom } from 'rxjs';
|
|
4
|
+
import { BrowserPermission } from '../BrowserPermission';
|
|
3
5
|
|
|
4
6
|
class TestInputMediaDeviceManagerState extends InputMediaDeviceManagerState {
|
|
5
7
|
constructor() {
|
|
6
|
-
super(
|
|
8
|
+
super(
|
|
9
|
+
'stop-tracks',
|
|
10
|
+
new BrowserPermission({
|
|
11
|
+
queryName: 'camera' as PermissionName,
|
|
12
|
+
constraints: {},
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
7
15
|
}
|
|
8
16
|
|
|
9
17
|
getDeviceIdFromStream = vi.fn();
|
|
10
18
|
}
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
20
|
+
function mockPermissionStatus(state: PermissionState): PermissionStatus {
|
|
21
|
+
return {
|
|
22
|
+
state,
|
|
23
|
+
addEventListener: vi.fn(),
|
|
24
|
+
removeEventListener: vi.fn(),
|
|
25
|
+
} as any;
|
|
26
|
+
}
|
|
18
27
|
|
|
28
|
+
describe('InputMediaDeviceManagerState', () => {
|
|
19
29
|
describe('hasBrowserPermission', () => {
|
|
20
30
|
it('should emit true when permission is granted', async () => {
|
|
21
|
-
const permissionStatus
|
|
22
|
-
state: 'granted',
|
|
23
|
-
addEventListener: vi.fn(),
|
|
24
|
-
};
|
|
31
|
+
const permissionStatus = mockPermissionStatus('granted');
|
|
25
32
|
const query = vi.fn(() => Promise.resolve(permissionStatus));
|
|
26
|
-
globalThis.navigator
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
globalThis.navigator = { permissions: { query } } as any;
|
|
34
|
+
const state = new TestInputMediaDeviceManagerState();
|
|
35
|
+
|
|
36
|
+
const hasPermission = await firstValueFrom(state.hasBrowserPermission$);
|
|
29
37
|
|
|
30
|
-
const hasPermission = await new Promise((resolve) => {
|
|
31
|
-
state.hasBrowserPermission$.subscribe((v) => resolve(v));
|
|
32
|
-
});
|
|
33
38
|
expect(hasPermission).toBe(true);
|
|
34
39
|
expect(query).toHaveBeenCalledWith({ name: 'camera' });
|
|
35
40
|
expect(permissionStatus.addEventListener).toHaveBeenCalled();
|
|
36
41
|
});
|
|
37
42
|
|
|
38
43
|
it('should emit false when permission is denied', async () => {
|
|
39
|
-
const permissionStatus
|
|
40
|
-
state: 'denied',
|
|
41
|
-
addEventListener: vi.fn(),
|
|
42
|
-
removeEventListener: vi.fn(),
|
|
43
|
-
};
|
|
44
|
+
const permissionStatus = mockPermissionStatus('denied');
|
|
44
45
|
const query = vi.fn(() => Promise.resolve(permissionStatus));
|
|
45
|
-
globalThis.navigator
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
globalThis.navigator = { permissions: { query } } as any;
|
|
47
|
+
const state = new TestInputMediaDeviceManagerState();
|
|
48
|
+
|
|
49
|
+
const hasPermission = await firstValueFrom(state.hasBrowserPermission$);
|
|
48
50
|
|
|
49
|
-
const hasPermission = await new Promise((resolve) => {
|
|
50
|
-
state.hasBrowserPermission$.subscribe((v) => resolve(v));
|
|
51
|
-
});
|
|
52
51
|
expect(hasPermission).toBe(false);
|
|
53
52
|
expect(query).toHaveBeenCalledWith({ name: 'camera' });
|
|
54
53
|
expect(permissionStatus.addEventListener).toHaveBeenCalled();
|
|
55
54
|
});
|
|
56
55
|
|
|
57
56
|
it('should emit true when prompt is needed', async () => {
|
|
58
|
-
const permissionStatus
|
|
59
|
-
state: 'prompt',
|
|
60
|
-
addEventListener: vi.fn(),
|
|
61
|
-
};
|
|
57
|
+
const permissionStatus = mockPermissionStatus('prompt');
|
|
62
58
|
const query = vi.fn(() => Promise.resolve(permissionStatus));
|
|
63
|
-
globalThis.navigator
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
globalThis.navigator = { permissions: { query } } as any;
|
|
60
|
+
const state = new TestInputMediaDeviceManagerState();
|
|
61
|
+
|
|
62
|
+
const hasPermission = await firstValueFrom(state.hasBrowserPermission$);
|
|
66
63
|
|
|
67
|
-
const hasPermission = await new Promise((resolve) => {
|
|
68
|
-
state.hasBrowserPermission$.subscribe((v) => resolve(v));
|
|
69
|
-
});
|
|
70
64
|
expect(hasPermission).toBe(true);
|
|
71
65
|
expect(query).toHaveBeenCalledWith({ name: 'camera' });
|
|
72
66
|
expect(permissionStatus.addEventListener).toHaveBeenCalled();
|
|
@@ -74,25 +68,21 @@ describe('InputMediaDeviceManagerState', () => {
|
|
|
74
68
|
|
|
75
69
|
it('should emit true when permissions cannot be queried', async () => {
|
|
76
70
|
const query = vi.fn(() => Promise.reject());
|
|
77
|
-
globalThis.navigator
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
globalThis.navigator = { permissions: { query } } as any;
|
|
72
|
+
const state = new TestInputMediaDeviceManagerState();
|
|
73
|
+
|
|
74
|
+
const hasPermission = await firstValueFrom(state.hasBrowserPermission$);
|
|
80
75
|
|
|
81
|
-
const hasPermission = await new Promise((resolve) => {
|
|
82
|
-
state.hasBrowserPermission$.subscribe((v) => resolve(v));
|
|
83
|
-
});
|
|
84
76
|
expect(hasPermission).toBe(true);
|
|
85
77
|
expect(query).toHaveBeenCalledWith({ name: 'camera' });
|
|
86
78
|
});
|
|
87
79
|
|
|
88
80
|
it('should emit true when permissions API is unavailable', async () => {
|
|
89
|
-
globalThis.navigator
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
globalThis.navigator = {} as any;
|
|
82
|
+
const state = new TestInputMediaDeviceManagerState();
|
|
83
|
+
|
|
84
|
+
const hasPermission = await firstValueFrom(state.hasBrowserPermission$);
|
|
92
85
|
|
|
93
|
-
const hasPermission = await new Promise((resolve) => {
|
|
94
|
-
state.hasBrowserPermission$.subscribe((v) => resolve(v));
|
|
95
|
-
});
|
|
96
86
|
expect(hasPermission).toBe(true);
|
|
97
87
|
});
|
|
98
88
|
});
|
|
@@ -12,6 +12,7 @@ import { CallingState, StreamVideoWriteableStateStore } from '../../store';
|
|
|
12
12
|
import {
|
|
13
13
|
mockAudioDevices,
|
|
14
14
|
mockAudioStream,
|
|
15
|
+
mockBrowserPermission,
|
|
15
16
|
mockCall,
|
|
16
17
|
mockDeviceIds$,
|
|
17
18
|
} from './mocks';
|
|
@@ -31,6 +32,8 @@ vi.mock('../devices.ts', () => {
|
|
|
31
32
|
return of(mockAudioDevices);
|
|
32
33
|
}),
|
|
33
34
|
getAudioStream: vi.fn(() => Promise.resolve(mockAudioStream())),
|
|
35
|
+
getAudioBrowserPermission: () => mockBrowserPermission,
|
|
36
|
+
getVideoBrowserPermission: () => mockBrowserPermission,
|
|
34
37
|
deviceIds$: mockDeviceIds$(),
|
|
35
38
|
};
|
|
36
39
|
});
|
|
@@ -50,7 +53,7 @@ vi.mock('../../Call.ts', () => {
|
|
|
50
53
|
});
|
|
51
54
|
|
|
52
55
|
class NoiseCancellationStub implements INoiseCancellation {
|
|
53
|
-
private listeners: { [event: string]: Array<() => void> } = {};
|
|
56
|
+
private listeners: { [event: string]: Array<(arg: boolean) => void> } = {};
|
|
54
57
|
|
|
55
58
|
isSupported = () => true;
|
|
56
59
|
init = () => Promise.resolve(undefined);
|
|
@@ -4,7 +4,12 @@ import { MicrophoneManager } from '../MicrophoneManager';
|
|
|
4
4
|
import { Call } from '../../Call';
|
|
5
5
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
6
6
|
import { StreamVideoWriteableStateStore } from '../../store';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
mockAudioDevices,
|
|
9
|
+
mockAudioStream,
|
|
10
|
+
mockBrowserPermission,
|
|
11
|
+
mockCall,
|
|
12
|
+
} from './mocks';
|
|
8
13
|
import { of } from 'rxjs';
|
|
9
14
|
import '../../rtc/__tests__/mocks/webrtc.mocks';
|
|
10
15
|
import { OwnCapability } from '../../gen/coordinator';
|
|
@@ -25,6 +30,8 @@ vi.mock('../devices.ts', () => {
|
|
|
25
30
|
return of(mockAudioDevices);
|
|
26
31
|
}),
|
|
27
32
|
getAudioStream: vi.fn(() => Promise.resolve(mockAudioStream())),
|
|
33
|
+
getAudioBrowserPermission: () => mockBrowserPermission,
|
|
34
|
+
getVideoBrowserPermission: () => mockBrowserPermission,
|
|
28
35
|
deviceIds$: {},
|
|
29
36
|
};
|
|
30
37
|
});
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
emitDeviceIds,
|
|
4
|
+
mockAudioDevices,
|
|
5
|
+
mockBrowserPermission,
|
|
6
|
+
mockDeviceIds$,
|
|
7
|
+
} from './mocks';
|
|
3
8
|
import { of } from 'rxjs';
|
|
4
9
|
import { SpeakerManager } from '../SpeakerManager';
|
|
5
10
|
import { checkIfAudioOutputChangeSupported } from '../devices';
|
|
@@ -12,6 +17,8 @@ vi.mock('../devices.ts', () => {
|
|
|
12
17
|
return {
|
|
13
18
|
getAudioOutputDevices: vi.fn(() => of(mockAudioDevices)),
|
|
14
19
|
checkIfAudioOutputChangeSupported: vi.fn(() => true),
|
|
20
|
+
getAudioBrowserPermission: () => mockBrowserPermission,
|
|
21
|
+
getVideoBrowserPermission: () => mockBrowserPermission,
|
|
15
22
|
deviceIds$: mockDeviceIds$(),
|
|
16
23
|
};
|
|
17
24
|
});
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
OwnCapability,
|
|
6
6
|
} from '../../gen/coordinator';
|
|
7
7
|
import { Call } from '../../Call';
|
|
8
|
-
import { Subject } from 'rxjs';
|
|
8
|
+
import { of, Subject } from 'rxjs';
|
|
9
|
+
import { BrowserPermission } from '../BrowserPermission';
|
|
9
10
|
|
|
10
11
|
export const mockVideoDevices = [
|
|
11
12
|
{
|
|
@@ -217,3 +218,7 @@ export const mockDeviceIds$ = () => {
|
|
|
217
218
|
export const emitDeviceIds = (values: MediaDeviceInfo[]) => {
|
|
218
219
|
deviceIds.next(values);
|
|
219
220
|
};
|
|
221
|
+
|
|
222
|
+
export const mockBrowserPermission = {
|
|
223
|
+
asObservable: () => of(true),
|
|
224
|
+
} as BrowserPermission;
|