@livedigital/client 1.7.0 → 1.10.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/dist/constants/events.d.ts +1 -0
- package/dist/engine/Peer.d.ts +16 -35
- package/dist/engine/PeerProducer.d.ts +2 -1
- package/dist/engine/index.d.ts +15 -11
- package/dist/engine/media/Consumer.d.ts +0 -1
- package/dist/engine/media/VideoConsumer.d.ts +1 -9
- package/dist/engine/media/index.d.ts +9 -11
- package/dist/engine/media/tracks/AudioTrack.d.ts +9 -0
- package/dist/engine/media/tracks/BaseTrack.d.ts +23 -0
- package/dist/engine/media/tracks/TrackWithCodecOptions.d.ts +4 -0
- package/dist/engine/media/tracks/TrackWithEncodings.d.ts +4 -0
- package/dist/engine/media/tracks/VideoTrack.d.ts +13 -0
- package/dist/engine/network/LoadBalancerClient.d.ts +7 -1
- package/dist/engine/network/index.d.ts +5 -2
- package/dist/engine/system/index.d.ts +10 -19
- package/dist/index.d.ts +16 -22
- package/dist/index.es.js +15 -1
- package/dist/index.js +15 -1
- package/dist/types/common.d.ts +57 -7
- package/package.json +11 -7
- package/src/constants/events.ts +1 -0
- package/src/engine/Peer.ts +132 -337
- package/src/engine/PeerProducer.ts +8 -2
- package/src/engine/index.ts +172 -197
- package/src/engine/media/Consumer.ts +0 -4
- package/src/engine/media/VideoConsumer.ts +1 -18
- package/src/engine/media/index.ts +35 -35
- package/src/engine/media/tracks/AudioTrack.ts +18 -0
- package/src/engine/media/tracks/BaseTrack.ts +70 -0
- package/src/engine/media/tracks/TrackWithCodecOptions.ts +5 -0
- package/src/engine/media/tracks/TrackWithEncodings.ts +5 -0
- package/src/engine/media/tracks/VideoTrack.ts +29 -0
- package/src/engine/network/LoadBalancerClient.ts +18 -4
- package/src/engine/network/index.ts +8 -3
- package/src/engine/system/index.ts +64 -198
- package/src/index.ts +39 -68
- package/src/types/common.ts +71 -8
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Producer } from 'mediasoup-client/lib/Producer';
|
|
2
|
+
import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
|
|
3
|
+
import { EncoderConfig, TrackLabel } from 'types/common';
|
|
4
|
+
|
|
5
|
+
class BaseTrack {
|
|
6
|
+
readonly #mediaStreamTrack: MediaStreamTrack;
|
|
7
|
+
|
|
8
|
+
protected encoderConfig: EncoderConfig = {};
|
|
9
|
+
|
|
10
|
+
private producer?: Producer;
|
|
11
|
+
|
|
12
|
+
private label: TrackLabel = TrackLabel.Unknown;
|
|
13
|
+
|
|
14
|
+
constructor(mediaStreamTrack: MediaStreamTrack) {
|
|
15
|
+
this.#mediaStreamTrack = mediaStreamTrack;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get mediaStreamTrack(): MediaStreamTrack {
|
|
19
|
+
return this.#mediaStreamTrack;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get id(): string {
|
|
23
|
+
return this.mediaStreamTrack.id;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get kind(): MediaKind {
|
|
27
|
+
return this.mediaStreamTrack.kind as MediaKind;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get producerId(): string | undefined {
|
|
31
|
+
return this.producer?.id;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get isPublished(): boolean {
|
|
35
|
+
return this.producer
|
|
36
|
+
? !this.producer.closed
|
|
37
|
+
: false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getLabel(): TrackLabel {
|
|
41
|
+
return this.label;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setLabel(label: TrackLabel): void {
|
|
45
|
+
this.label = label;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setEncoderConfig(encoderConfig: EncoderConfig): void {
|
|
49
|
+
this.encoderConfig = encoderConfig;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getEncoderConfig(): EncoderConfig {
|
|
53
|
+
return this.encoderConfig;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setProducer(producer: Producer): void {
|
|
57
|
+
this.producer = producer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getProducer(): Producer | undefined {
|
|
61
|
+
return this.producer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
closeProducer(): void {
|
|
65
|
+
this.producer?.close();
|
|
66
|
+
this.producer = undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default BaseTrack;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { WEBCAM_SIMULCAST_ENCODINGS } from 'constants/simulcastEncodings';
|
|
2
|
+
import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
3
|
+
import { RtpEncodingParameters } from 'mediasoup-client/lib/RtpParameters';
|
|
4
|
+
import { VideoCodec, VideoEncoderConfig } from 'types/common';
|
|
5
|
+
import BaseTrack from './BaseTrack';
|
|
6
|
+
import TrackWithCodecOptions from './TrackWithCodecOptions';
|
|
7
|
+
import TrackWithEncodings from './TrackWithEncodings';
|
|
8
|
+
|
|
9
|
+
class VideoTrack extends BaseTrack implements TrackWithCodecOptions, TrackWithEncodings {
|
|
10
|
+
getEncoderConfig(): VideoEncoderConfig {
|
|
11
|
+
return this.encoderConfig;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getCodecOptions(): ProducerCodecOptions {
|
|
15
|
+
return {
|
|
16
|
+
videoGoogleStartBitrate: this.getEncoderConfig().videoGoogleStartBitrate || 1000,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getPreferredCodec(): VideoCodec {
|
|
21
|
+
return this.getEncoderConfig().preferredCodec || 'h264';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getEncodings(): RtpEncodingParameters[] {
|
|
25
|
+
return this.getEncoderConfig().encodings || WEBCAM_SIMULCAST_ENCODINGS;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default VideoTrack;
|
|
@@ -2,19 +2,33 @@ import axios, { AxiosInstance } from 'axios';
|
|
|
2
2
|
import qs from 'qs';
|
|
3
3
|
import { GetNodeResponse } from '../../types/common';
|
|
4
4
|
|
|
5
|
+
export type LoadBalancerApiClientParams = {
|
|
6
|
+
baseURL?: string;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
customNode?: GetNodeResponse;
|
|
9
|
+
};
|
|
10
|
+
|
|
5
11
|
class LoadBalancerApiClient {
|
|
6
12
|
private readonly api: AxiosInstance;
|
|
7
13
|
|
|
8
|
-
|
|
14
|
+
private customNode?: GetNodeResponse;
|
|
15
|
+
|
|
16
|
+
constructor(params: LoadBalancerApiClientParams) {
|
|
17
|
+
const { baseURL, timeout, customNode } = params;
|
|
9
18
|
this.api = axios.create({
|
|
10
|
-
baseURL: process.env.LIVEDIGITAL_APP_LOAD_BALANCER_BASE_URL,
|
|
11
|
-
timeout: 5000,
|
|
19
|
+
baseURL: baseURL || process.env.LIVEDIGITAL_APP_LOAD_BALANCER_BASE_URL,
|
|
20
|
+
timeout: timeout || 5000,
|
|
12
21
|
withCredentials: false,
|
|
13
|
-
paramsSerializer: (
|
|
22
|
+
paramsSerializer: (data) => qs.stringify(data),
|
|
14
23
|
});
|
|
24
|
+
this.customNode = customNode;
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
async getNode({ channelId }: { channelId: string }): Promise<GetNodeResponse> {
|
|
28
|
+
if (this.customNode) {
|
|
29
|
+
return this.customNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
const { data } = await this.api.get<GetNodeResponse>('/nodes/best', {
|
|
19
33
|
params: { channelId },
|
|
20
34
|
});
|
|
@@ -7,10 +7,14 @@ import {
|
|
|
7
7
|
import { Device } from 'mediasoup-client';
|
|
8
8
|
import SocketIO from './Socket';
|
|
9
9
|
import { ProduceParams } from '../../types/common';
|
|
10
|
-
import LoadBalancerApiClient from './LoadBalancerClient';
|
|
10
|
+
import LoadBalancerApiClient, { LoadBalancerApiClientParams } from './LoadBalancerClient';
|
|
11
11
|
import { MEDIASOUP_EVENTS, MEDIASOUP_TRANSPORT_EVENTS } from '../../constants/events';
|
|
12
12
|
import Logger from '../Logger';
|
|
13
13
|
|
|
14
|
+
export type NetworkParams = {
|
|
15
|
+
loadbalancer: LoadBalancerApiClientParams,
|
|
16
|
+
};
|
|
17
|
+
|
|
14
18
|
class Network {
|
|
15
19
|
public readonly socket: SocketIO;
|
|
16
20
|
|
|
@@ -22,9 +26,10 @@ class Network {
|
|
|
22
26
|
|
|
23
27
|
private readonly logger: Logger;
|
|
24
28
|
|
|
25
|
-
constructor() {
|
|
29
|
+
constructor(params: NetworkParams) {
|
|
30
|
+
const { loadbalancer } = params;
|
|
26
31
|
this.socket = new SocketIO();
|
|
27
|
-
this.loadBalancerClient = new LoadBalancerApiClient();
|
|
32
|
+
this.loadBalancerClient = new LoadBalancerApiClient(loadbalancer);
|
|
28
33
|
this.logger = new Logger('Network');
|
|
29
34
|
}
|
|
30
35
|
|
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { CLIENT_EVENTS } from 'constants/events';
|
|
2
|
+
import EnhancedEventEmitter from 'EnhancedEventEmitter';
|
|
3
|
+
import { AvailableMediaDevices } from 'types/common';
|
|
3
4
|
import Logger from '../Logger';
|
|
4
5
|
|
|
6
|
+
type SystemParams = {
|
|
7
|
+
clientEventEmitter: EnhancedEventEmitter,
|
|
8
|
+
};
|
|
9
|
+
|
|
5
10
|
class System {
|
|
6
11
|
public isEnableVideoDevicesLock = false;
|
|
7
12
|
|
|
8
13
|
public isEnableAudioDevicesLock = false;
|
|
9
14
|
|
|
10
|
-
public
|
|
11
|
-
|
|
12
|
-
public videoStream?: MediaStream;
|
|
13
|
-
|
|
14
|
-
public audioStream?: MediaStream;
|
|
15
|
+
public isDevicesDetected = false;
|
|
15
16
|
|
|
16
17
|
public availableVideoDevices: MediaDeviceInfo[] = [];
|
|
17
18
|
|
|
18
19
|
public availableAudioDevices: MediaDeviceInfo[] = [];
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
private isMediaDevicesAccessGranted = false;
|
|
21
22
|
|
|
22
|
-
public
|
|
23
|
+
public clientEventEmitter: EnhancedEventEmitter;
|
|
23
24
|
|
|
24
25
|
private readonly logger: Logger;
|
|
25
26
|
|
|
26
|
-
constructor() {
|
|
27
|
+
constructor(params: SystemParams) {
|
|
28
|
+
const { clientEventEmitter } = params;
|
|
29
|
+
this.clientEventEmitter = clientEventEmitter;
|
|
27
30
|
this.logger = new Logger('System');
|
|
28
31
|
this.listenDevices();
|
|
29
32
|
}
|
|
@@ -36,24 +39,6 @@ class System {
|
|
|
36
39
|
this.isEnableAudioDevicesLock = value;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
setCurrentVideoDevice(deviceId: string): void {
|
|
40
|
-
const isExistsDevice = this.availableVideoDevices.find((device) => device.deviceId === deviceId);
|
|
41
|
-
if (!isExistsDevice) {
|
|
42
|
-
throw new Error('Unknown video device id');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
this.currentVideoDeviceId = deviceId;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
setCurrentAudioDevice(deviceId: string): void {
|
|
49
|
-
const isExistsDevice = this.availableAudioDevices.find((device) => device.deviceId === deviceId);
|
|
50
|
-
if (!isExistsDevice) {
|
|
51
|
-
throw new Error('Unknown audio device id');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.currentAudioDeviceId = deviceId;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
42
|
setAvailableVideoDevices(availableVideoDevices: MediaDeviceInfo[]): void {
|
|
58
43
|
this.availableVideoDevices = availableVideoDevices;
|
|
59
44
|
}
|
|
@@ -62,199 +47,80 @@ class System {
|
|
|
62
47
|
this.availableAudioDevices = availableAudioDevices;
|
|
63
48
|
}
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
...constraints,
|
|
78
|
-
...videoTrackConstraints,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
getAudioConstraints(audioTrackConstraints?: AudioTrackConstraints): MediaTrackConstraints {
|
|
83
|
-
const constraints = {
|
|
84
|
-
autoGainControl: true,
|
|
85
|
-
deviceId: this.currentAudioDeviceId || undefined,
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
if (!audioTrackConstraints) {
|
|
89
|
-
return constraints;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
...constraints,
|
|
94
|
-
...audioTrackConstraints,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async getVideoTrack(): Promise<MediaStreamTrack> {
|
|
99
|
-
const videoStream = this.videoStream || await this.startVideoStream();
|
|
100
|
-
const [track] = videoStream.getVideoTracks();
|
|
101
|
-
return track;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async getAudioTrack(): Promise<MediaStreamTrack> {
|
|
105
|
-
const videoStream = this.videoStream || await this.startAudioStream();
|
|
106
|
-
const [track] = videoStream.getAudioTracks();
|
|
107
|
-
return track;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async startVideoStream(videoTrackConstraints?: VideoTrackConstraints): Promise<MediaStream> {
|
|
111
|
-
if (!this.videoStream || !this.videoStream.active) {
|
|
112
|
-
try {
|
|
113
|
-
this.videoStream = await navigator.mediaDevices.getUserMedia({
|
|
114
|
-
video: this.getVideoConstraints(videoTrackConstraints),
|
|
115
|
-
});
|
|
116
|
-
this.logger.debug('startVideoStream()');
|
|
117
|
-
} catch (error) {
|
|
118
|
-
this.logger.error('startVideoStream()', { error });
|
|
119
|
-
|
|
120
|
-
if (error.name === 'NotReadableError') {
|
|
121
|
-
this.videoStream = undefined;
|
|
122
|
-
throw new Error('DeviceIsBusy');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (error.name === 'This action is not allowed') {
|
|
126
|
-
throw new Error('NotAllowedError');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
throw new Error('Can`t start video stream');
|
|
50
|
+
async requestMediaDevicesAccess(): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
53
|
+
video: true,
|
|
54
|
+
audio: true,
|
|
55
|
+
});
|
|
56
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
57
|
+
this.isMediaDevicesAccessGranted = true;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
this.logger.error('getUserMedia()', { error });
|
|
60
|
+
if (error.name === 'NotReadableError') {
|
|
61
|
+
throw new Error('DeviceIsBusy');
|
|
130
62
|
}
|
|
131
63
|
}
|
|
132
|
-
|
|
133
|
-
return this.videoStream;
|
|
134
64
|
}
|
|
135
65
|
|
|
136
|
-
async
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
} catch (error) {
|
|
144
|
-
this.logger.error('startAudioStream()', { error });
|
|
145
|
-
|
|
146
|
-
if (error.name === 'NotReadableError') {
|
|
147
|
-
this.audioStream = undefined;
|
|
148
|
-
throw new Error('DeviceIsBusy');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (error.name === 'NotAllowedError') {
|
|
152
|
-
throw new Error('Device access denied');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
throw new Error('Can`t start audio stream');
|
|
66
|
+
async refreshAvailableMediaDevicesList(): Promise<void> {
|
|
67
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
68
|
+
const videoDevices = [] as MediaDeviceInfo[];
|
|
69
|
+
const audioDevices = [] as MediaDeviceInfo[];
|
|
70
|
+
devices.forEach((device) => {
|
|
71
|
+
if (device.deviceId === '') {
|
|
72
|
+
return;
|
|
156
73
|
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return this.audioStream;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async detectDevices(): Promise<void> {
|
|
163
|
-
this.isDevicesLoaded = false;
|
|
164
74
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const availableDevicesTypes = {
|
|
169
|
-
video: !!mediaDevices.find((device) => device.kind === 'videoinput'),
|
|
170
|
-
audio: !!mediaDevices.find((device) => device.kind === 'audioinput'),
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
await navigator.mediaDevices.getUserMedia({
|
|
175
|
-
video: availableDevicesTypes.video && this.getVideoConstraints(),
|
|
176
|
-
audio: availableDevicesTypes.audio && this.getAudioConstraints(),
|
|
177
|
-
});
|
|
178
|
-
} catch (error) {
|
|
179
|
-
this.logger.error('getUserMedia()', { error });
|
|
180
|
-
if (error.name === 'NotReadableError') {
|
|
181
|
-
throw new Error('DeviceIsBusy');
|
|
182
|
-
}
|
|
75
|
+
if (device.deviceId === 'default') {
|
|
76
|
+
return;
|
|
183
77
|
}
|
|
184
78
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const audioDevices = [] as MediaDeviceInfo[];
|
|
189
|
-
devices.forEach((device) => {
|
|
190
|
-
if (device.deviceId === 'default') {
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
79
|
+
if (device.kind === 'videoinput') {
|
|
80
|
+
videoDevices.push(device);
|
|
81
|
+
}
|
|
193
82
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
83
|
+
if (device.kind === 'audioinput') {
|
|
84
|
+
audioDevices.push(device);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
197
87
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
});
|
|
88
|
+
this.setAvailableVideoDevices(videoDevices);
|
|
89
|
+
this.setAvailableAudioDevices(audioDevices);
|
|
202
90
|
|
|
203
|
-
|
|
204
|
-
this.setAvailableAudioDevices(audioDevices);
|
|
205
|
-
this.isDevicesLoaded = true;
|
|
206
|
-
this.logger.debug('detectDevices()', { videoDevices, audioDevices });
|
|
207
|
-
} catch (error) {
|
|
208
|
-
this.logger.error('detectDevices()', { error });
|
|
209
|
-
throw new Error('Can`t detect devices');
|
|
210
|
-
}
|
|
91
|
+
this.logger.debug('Devices list updated');
|
|
211
92
|
}
|
|
212
93
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (this.isDevicesLoaded) {
|
|
216
|
-
this.logger.debug('Available devices list was changed');
|
|
217
|
-
this.detectDevices();
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
stopVideoStream(): void {
|
|
223
|
-
if (!this.videoStream) {
|
|
224
|
-
this.logger.warn('stopVideoStream()', { message: 'No available video stream' });
|
|
94
|
+
async detectDevices(): Promise<void> {
|
|
95
|
+
if (this.isDevicesDetected) {
|
|
225
96
|
return;
|
|
226
97
|
}
|
|
227
98
|
|
|
228
|
-
this.
|
|
229
|
-
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
this.videoStream = undefined;
|
|
233
|
-
this.logger.debug('stopVideoStream()');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
stopAudioStream(): void {
|
|
237
|
-
if (!this.audioStream) {
|
|
238
|
-
this.logger.warn('stopVideoStream()', { message: 'No available audio stream' });
|
|
239
|
-
return;
|
|
99
|
+
if (!this.isMediaDevicesAccessGranted) {
|
|
100
|
+
await this.requestMediaDevicesAccess();
|
|
240
101
|
}
|
|
241
102
|
|
|
242
|
-
|
|
243
|
-
|
|
103
|
+
navigator.mediaDevices.addEventListener('devicechange', () => {
|
|
104
|
+
this.refreshAvailableMediaDevicesList();
|
|
244
105
|
});
|
|
245
106
|
|
|
246
|
-
this.
|
|
247
|
-
this.logger.debug('stopAudioStream()');
|
|
248
|
-
}
|
|
107
|
+
await this.refreshAvailableMediaDevicesList();
|
|
249
108
|
|
|
250
|
-
|
|
251
|
-
this.stopVideoStream();
|
|
252
|
-
await this.startVideoStream();
|
|
109
|
+
this.isDevicesDetected = true;
|
|
253
110
|
}
|
|
254
111
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
112
|
+
listenDevices(): void {
|
|
113
|
+
navigator.mediaDevices.ondevicechange = async () => {
|
|
114
|
+
if (!this.isDevicesDetected) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await this.refreshAvailableMediaDevicesList();
|
|
119
|
+
this.clientEventEmitter.safeEmit(CLIENT_EVENTS.devicesListUpdated, {
|
|
120
|
+
audio: this.availableAudioDevices,
|
|
121
|
+
video: this.availableVideoDevices,
|
|
122
|
+
} as AvailableMediaDevices);
|
|
123
|
+
};
|
|
258
124
|
}
|
|
259
125
|
}
|
|
260
126
|
|
package/src/index.ts
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AvailableMediaDevices,
|
|
3
|
+
CreateCameraVideoTrackOptions,
|
|
4
|
+
CreateMicrophoneAudioTrackOptions,
|
|
5
|
+
CreateScreenAudioTrackOptions,
|
|
6
|
+
CreateScreenVideoTrackOptions,
|
|
3
7
|
JoinChannelParams,
|
|
4
|
-
|
|
5
|
-
PublishVideoOptions,
|
|
8
|
+
Track,
|
|
6
9
|
} from './types/common';
|
|
7
10
|
import EnhancedEventEmitter from './EnhancedEventEmitter';
|
|
8
11
|
import Engine from './engine';
|
|
9
12
|
import Peer from './engine/Peer';
|
|
13
|
+
import { LoadBalancerApiClientParams } from './engine/network/LoadBalancerClient';
|
|
14
|
+
|
|
15
|
+
type ClientParams = {
|
|
16
|
+
network?: {
|
|
17
|
+
loadbalancer?: LoadBalancerApiClientParams,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
10
20
|
|
|
11
21
|
class Client {
|
|
12
22
|
private readonly engine: Engine;
|
|
13
23
|
|
|
14
24
|
private readonly _observer = new EnhancedEventEmitter();
|
|
15
25
|
|
|
16
|
-
constructor() {
|
|
17
|
-
|
|
26
|
+
constructor(params: ClientParams) {
|
|
27
|
+
const { network } = params;
|
|
28
|
+
this.engine = new Engine({
|
|
29
|
+
clientEventEmitter: this.observer,
|
|
30
|
+
network: {
|
|
31
|
+
loadbalancer: {
|
|
32
|
+
baseURL: network?.loadbalancer?.baseURL,
|
|
33
|
+
customNode: network?.loadbalancer?.customNode,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
18
37
|
}
|
|
19
38
|
|
|
20
39
|
get observer(): EnhancedEventEmitter {
|
|
@@ -29,22 +48,6 @@ class Client {
|
|
|
29
48
|
return this.engine.peers;
|
|
30
49
|
}
|
|
31
50
|
|
|
32
|
-
get isVideoEnabled(): boolean {
|
|
33
|
-
return !!this.engine.media.videoProducer;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get isVideoPaused(): boolean {
|
|
37
|
-
return !!this.engine.media.videoProducer?.paused;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get isAudioEnabled(): boolean {
|
|
41
|
-
return !!this.engine.media.audioProducer;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get isAudioPaused(): boolean {
|
|
45
|
-
return !!this.engine.media.audioProducer?.paused;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
51
|
get availableVideoDevices(): MediaDeviceInfo[] {
|
|
49
52
|
return this.engine.system.availableVideoDevices;
|
|
50
53
|
}
|
|
@@ -53,14 +56,6 @@ class Client {
|
|
|
53
56
|
return this.engine.system.availableAudioDevices;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
get currentAudioDeviceId(): string | undefined {
|
|
57
|
-
return this.engine.system.currentAudioDeviceId;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get currentVideoDeviceId(): string | undefined {
|
|
61
|
-
return this.engine.system.currentVideoDeviceId;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
59
|
async detectDevices(): Promise<AvailableMediaDevices> {
|
|
65
60
|
await this.engine.system.detectDevices();
|
|
66
61
|
return {
|
|
@@ -69,30 +64,6 @@ class Client {
|
|
|
69
64
|
};
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
async getVideoStream(): Promise<MediaStream> {
|
|
73
|
-
return this.engine.system.videoStream || this.engine.system.startVideoStream();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async getAudioStream(): Promise<MediaStream> {
|
|
77
|
-
return this.engine.system.audioStream || this.engine.system.startAudioStream();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async getVideoTrack(): Promise<MediaStreamTrack> {
|
|
81
|
-
return this.engine.system.getVideoTrack();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async getAudioTrack(): Promise<MediaStreamTrack> {
|
|
85
|
-
return this.engine.system.getAudioTrack();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
setVideoDevice(deviceId: string): void {
|
|
89
|
-
this.engine.system.setCurrentVideoDevice(deviceId);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
setAudioDevice(deviceId: string): void {
|
|
93
|
-
this.engine.system.setCurrentAudioDevice(deviceId);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
67
|
join(params: JoinChannelParams): Promise<void> {
|
|
97
68
|
return this.engine.join(params);
|
|
98
69
|
}
|
|
@@ -101,36 +72,36 @@ class Client {
|
|
|
101
72
|
return this.engine.release();
|
|
102
73
|
}
|
|
103
74
|
|
|
104
|
-
|
|
105
|
-
return this.engine.
|
|
75
|
+
publish(tracks: Track | Track[]): Promise<void> {
|
|
76
|
+
return this.engine.publish(tracks);
|
|
106
77
|
}
|
|
107
78
|
|
|
108
|
-
|
|
109
|
-
return this.engine.
|
|
79
|
+
unpublish(tracks?: Track | Track[]): Promise<void> {
|
|
80
|
+
return this.engine.unpublish(tracks);
|
|
110
81
|
}
|
|
111
82
|
|
|
112
|
-
|
|
113
|
-
return this.engine.
|
|
83
|
+
pause(track: Track): Promise<void> {
|
|
84
|
+
return this.engine.pause(track);
|
|
114
85
|
}
|
|
115
86
|
|
|
116
|
-
|
|
117
|
-
return this.engine.
|
|
87
|
+
resume(track: Track): Promise<void> {
|
|
88
|
+
return this.engine.resume(track);
|
|
118
89
|
}
|
|
119
90
|
|
|
120
|
-
|
|
121
|
-
return this.engine.
|
|
91
|
+
createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track | null> {
|
|
92
|
+
return this.engine.createCameraVideoTrack(options);
|
|
122
93
|
}
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
return this.engine.
|
|
95
|
+
createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track | null> {
|
|
96
|
+
return this.engine.createMicrophoneAudioTrack(options);
|
|
126
97
|
}
|
|
127
98
|
|
|
128
|
-
|
|
129
|
-
return this.engine.
|
|
99
|
+
createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track | null> {
|
|
100
|
+
return this.engine.createScreenVideoTrack(options);
|
|
130
101
|
}
|
|
131
102
|
|
|
132
|
-
|
|
133
|
-
return this.engine.
|
|
103
|
+
createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track | null> {
|
|
104
|
+
return this.engine.createScreenAudioTrack(options);
|
|
134
105
|
}
|
|
135
106
|
}
|
|
136
107
|
|