@livedigital/client 1.1.2 → 1.2.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/engine/Peer.d.ts +2 -2
- package/dist/engine/system/index.d.ts +5 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/engine/Peer.ts +27 -10
- package/src/engine/handlers/MediaSoupEventHandler.ts +9 -0
- package/src/engine/index.ts +10 -3
- package/src/engine/network/LoadBalancerClient.ts +1 -1
- package/src/engine/system/index.ts +29 -23
- package/src/index.ts +33 -1
package/package.json
CHANGED
package/src/engine/Peer.ts
CHANGED
|
@@ -47,7 +47,7 @@ class Peer {
|
|
|
47
47
|
|
|
48
48
|
public uid?: string;
|
|
49
49
|
|
|
50
|
-
public appData
|
|
50
|
+
public appData: Record<string, unknown>;
|
|
51
51
|
|
|
52
52
|
private videoProducer?: ProducerData;
|
|
53
53
|
|
|
@@ -75,7 +75,8 @@ class Peer {
|
|
|
75
75
|
videoConsumer,
|
|
76
76
|
audioConsumer,
|
|
77
77
|
engine,
|
|
78
|
-
appData
|
|
78
|
+
appData,
|
|
79
|
+
uid,
|
|
79
80
|
}: PeerConstructor) {
|
|
80
81
|
this.id = id;
|
|
81
82
|
this.channelId = channelId;
|
|
@@ -84,7 +85,8 @@ class Peer {
|
|
|
84
85
|
this.isVirtual = isVirtual;
|
|
85
86
|
this.originChannelId = originChannelId;
|
|
86
87
|
this.originPeerId = originPeerId;
|
|
87
|
-
this.appData = appData;
|
|
88
|
+
this.appData = appData || {};
|
|
89
|
+
this.uid = uid;
|
|
88
90
|
this.engine = engine;
|
|
89
91
|
this.videoConsumer = videoConsumer;
|
|
90
92
|
this.audioConsumer = audioConsumer;
|
|
@@ -163,7 +165,7 @@ class Peer {
|
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
if (this.videoConsumer) {
|
|
166
|
-
|
|
168
|
+
return;
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
await this.createConsumer(this.videoProducer);
|
|
@@ -175,7 +177,7 @@ class Peer {
|
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
if (this.audioConsumer) {
|
|
178
|
-
|
|
180
|
+
return;
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
await this.createConsumer(this.audioProducer);
|
|
@@ -357,7 +359,7 @@ class Peer {
|
|
|
357
359
|
consumer.close();
|
|
358
360
|
} catch (err) {
|
|
359
361
|
this.logger.error('closeConsumer()', consumer, err);
|
|
360
|
-
throw new Error('Error
|
|
362
|
+
throw new Error('Error unsubscribe media');
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
|
|
@@ -384,7 +386,7 @@ class Peer {
|
|
|
384
386
|
});
|
|
385
387
|
} catch (err) {
|
|
386
388
|
this.logger.error('changeConsumerPreferredLayers()', { consumerId: this.videoConsumer.id });
|
|
387
|
-
throw new Error('Error
|
|
389
|
+
throw new Error('Error change preferred layer');
|
|
388
390
|
}
|
|
389
391
|
}
|
|
390
392
|
|
|
@@ -440,10 +442,10 @@ class Peer {
|
|
|
440
442
|
if (producer.kind === ProducerKind.VIDEO) {
|
|
441
443
|
this.setVideoProducer(producer);
|
|
442
444
|
} else {
|
|
443
|
-
this.
|
|
445
|
+
this.setAudioProducer(producer);
|
|
444
446
|
}
|
|
445
447
|
|
|
446
|
-
this.
|
|
448
|
+
this.observer.safeEmit(PEER_EVENTS.mediaPublished, { kind: producer.kind });
|
|
447
449
|
});
|
|
448
450
|
|
|
449
451
|
this.observer.on(MEDIASOUP_EVENTS.producerClose, (producer: ProducerData) => {
|
|
@@ -453,7 +455,22 @@ class Peer {
|
|
|
453
455
|
this.removeAudioProducer();
|
|
454
456
|
}
|
|
455
457
|
|
|
456
|
-
this.
|
|
458
|
+
this.observer.safeEmit(PEER_EVENTS.mediaUnPublished, { kind: producer.kind });
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
this.observer.on(MEDIASOUP_EVENTS.closeConsumer, (consumerId) => {
|
|
462
|
+
if (this.videoConsumer?.id === consumerId) {
|
|
463
|
+
this.videoConsumer?.close();
|
|
464
|
+
this.videoConsumer = undefined;
|
|
465
|
+
this.observer.safeEmit(PEER_EVENTS.trackEnd, { kind: 'video' });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (this.audioConsumer?.id === consumerId) {
|
|
470
|
+
this.audioConsumer?.close();
|
|
471
|
+
this.audioConsumer = undefined;
|
|
472
|
+
this.observer.safeEmit(PEER_EVENTS.trackEnd, { kind: 'audio' });
|
|
473
|
+
}
|
|
457
474
|
});
|
|
458
475
|
|
|
459
476
|
this.observer.on(MEDIASOUP_EVENTS.pauseConsumer, (consumerId) => {
|
|
@@ -33,6 +33,15 @@ class MediaSoupEventHandler {
|
|
|
33
33
|
peer.observer.safeEmit(MEDIASOUP_EVENTS.newProducer, producer);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
connection.on(MEDIASOUP_EVENTS.closeConsumer, (consumerId: string, peerId: string) => {
|
|
37
|
+
const peer = this.engine.peers.find((item) => item.id === peerId);
|
|
38
|
+
if (!peer) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
peer.observer.safeEmit(MEDIASOUP_EVENTS.closeConsumer, consumerId);
|
|
43
|
+
});
|
|
44
|
+
|
|
36
45
|
connection.on(MEDIASOUP_EVENTS.resumeConsumer, (consumerId: string, peerId: string) => {
|
|
37
46
|
const peer = this.engine.peers.find((item) => item.id === peerId);
|
|
38
47
|
if (!peer || peer.isMe) {
|
package/src/engine/index.ts
CHANGED
|
@@ -50,7 +50,8 @@ class Engine {
|
|
|
50
50
|
|
|
51
51
|
private async initialize(): Promise<void> {
|
|
52
52
|
try {
|
|
53
|
-
const rtpCapabilities = await this.network.socket
|
|
53
|
+
const { rtpCapabilities } = await this.network.socket
|
|
54
|
+
.request('router.getRtpCapabilities') as { rtpCapabilities: RtpCapabilities };
|
|
54
55
|
await this.media.loadDevice(rtpCapabilities);
|
|
55
56
|
await this.network.createSendTransport(this.media.mediasoupDevice);
|
|
56
57
|
await this.network.createRecvTransport(this.media.mediasoupDevice);
|
|
@@ -58,6 +59,7 @@ class Engine {
|
|
|
58
59
|
this.logger.debug('initialize()');
|
|
59
60
|
} catch (error) {
|
|
60
61
|
this.logger.error('initialize()', error);
|
|
62
|
+
throw new Error('Error initialize engine');
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -85,6 +87,7 @@ class Engine {
|
|
|
85
87
|
this.logger.debug('release()');
|
|
86
88
|
} catch (err) {
|
|
87
89
|
this.logger.error('release()', err);
|
|
90
|
+
throw new Error('Error release engine');
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -121,6 +124,7 @@ class Engine {
|
|
|
121
124
|
this.isJoined = true;
|
|
122
125
|
} catch (error) {
|
|
123
126
|
this.logger.error('join()', error);
|
|
127
|
+
throw new Error(error);
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
|
|
@@ -146,7 +150,7 @@ class Engine {
|
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
if (!this.media.mediasoupDevice.canProduce('video')) {
|
|
149
|
-
|
|
153
|
+
throw new Error('Cant produce video');
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
if (!this.system.availableVideoDevices.length) {
|
|
@@ -210,6 +214,10 @@ class Engine {
|
|
|
210
214
|
return;
|
|
211
215
|
}
|
|
212
216
|
|
|
217
|
+
if (!this.media.mediasoupDevice.canProduce('audio')) {
|
|
218
|
+
throw new Error('Cant produce audio');
|
|
219
|
+
}
|
|
220
|
+
|
|
213
221
|
const audioStream = await this.system.startAudioStream();
|
|
214
222
|
this.system.setIsEnableAudioDevicesLock(true);
|
|
215
223
|
|
|
@@ -247,7 +255,6 @@ class Engine {
|
|
|
247
255
|
this.logger.debug('publishAudio()');
|
|
248
256
|
} catch (error) {
|
|
249
257
|
this.logger.error('publishAudio()');
|
|
250
|
-
|
|
251
258
|
throw new Error('Enable audio error');
|
|
252
259
|
} finally {
|
|
253
260
|
this.system.setIsEnableAudioDevicesLock(false);
|
|
@@ -7,7 +7,7 @@ class LoadBalancerApiClient {
|
|
|
7
7
|
|
|
8
8
|
constructor() {
|
|
9
9
|
this.api = axios.create({
|
|
10
|
-
baseURL:
|
|
10
|
+
baseURL: process.env.LIVEDIGITAL_APP_LOAD_BALANCER_BASE_URL,
|
|
11
11
|
timeout: 5000,
|
|
12
12
|
withCredentials: false,
|
|
13
13
|
paramsSerializer: (params) => qs.stringify(params),
|
|
@@ -21,14 +21,11 @@ class System {
|
|
|
21
21
|
|
|
22
22
|
public currentVideoDeviceId?: string;
|
|
23
23
|
|
|
24
|
-
public getUserMediaError?: string = undefined;
|
|
25
|
-
|
|
26
|
-
public deviceIsBusy?: boolean = false;
|
|
27
|
-
|
|
28
24
|
private readonly logger: Logger;
|
|
29
25
|
|
|
30
26
|
constructor() {
|
|
31
27
|
this.logger = new Logger('System');
|
|
28
|
+
this.listenDevices();
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
setIsEnableVideoDevicesLock(value: boolean): void {
|
|
@@ -98,7 +95,19 @@ class System {
|
|
|
98
95
|
};
|
|
99
96
|
}
|
|
100
97
|
|
|
101
|
-
async
|
|
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(params?: VideoConstraintParams): Promise<MediaStream> {
|
|
102
111
|
if (!this.videoStream || !this.videoStream.active) {
|
|
103
112
|
try {
|
|
104
113
|
this.videoStream = await navigator.mediaDevices.getUserMedia({
|
|
@@ -108,16 +117,12 @@ class System {
|
|
|
108
117
|
this.logger.error('startVideoStream()');
|
|
109
118
|
|
|
110
119
|
if (error.name === 'NotReadableError') {
|
|
111
|
-
this.deviceIsBusy = true;
|
|
112
120
|
this.videoStream = undefined;
|
|
113
|
-
|
|
114
|
-
return undefined;
|
|
121
|
+
throw new Error('DeviceIsBusy');
|
|
115
122
|
}
|
|
116
123
|
|
|
117
|
-
if (error.name === '
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return undefined;
|
|
124
|
+
if (error.name === 'This action is not allowed') {
|
|
125
|
+
throw new Error('NotAllowedError');
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
throw new Error('Can`t start video stream');
|
|
@@ -127,7 +132,7 @@ class System {
|
|
|
127
132
|
return this.videoStream;
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
async startAudioStream(params?: AudioConstraintParams): Promise<MediaStream
|
|
135
|
+
async startAudioStream(params?: AudioConstraintParams): Promise<MediaStream> {
|
|
131
136
|
if (!this.audioStream || !this.audioStream.active) {
|
|
132
137
|
try {
|
|
133
138
|
this.audioStream = await navigator.mediaDevices.getUserMedia({
|
|
@@ -137,16 +142,12 @@ class System {
|
|
|
137
142
|
this.logger.error('startAudioStream()', error);
|
|
138
143
|
|
|
139
144
|
if (error.name === 'NotReadableError') {
|
|
140
|
-
this.deviceIsBusy = true;
|
|
141
145
|
this.audioStream = undefined;
|
|
142
|
-
|
|
143
|
-
return undefined;
|
|
146
|
+
throw new Error('DeviceIsBusy');
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
if (error.name === 'NotAllowedError') {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return undefined;
|
|
150
|
+
throw new Error('Device access denied');
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
throw new Error('Can`t start audio stream');
|
|
@@ -158,8 +159,6 @@ class System {
|
|
|
158
159
|
|
|
159
160
|
async detectDevices(): Promise<void> {
|
|
160
161
|
this.isDevicesLoaded = false;
|
|
161
|
-
this.deviceIsBusy = false;
|
|
162
|
-
this.getUserMediaError = undefined;
|
|
163
162
|
|
|
164
163
|
try {
|
|
165
164
|
const mediaDevices = await navigator.mediaDevices.enumerateDevices();
|
|
@@ -176,7 +175,7 @@ class System {
|
|
|
176
175
|
});
|
|
177
176
|
} catch (error) {
|
|
178
177
|
if (error.name === 'NotReadableError') {
|
|
179
|
-
|
|
178
|
+
throw new Error('DeviceIsBusy');
|
|
180
179
|
}
|
|
181
180
|
}
|
|
182
181
|
|
|
@@ -202,12 +201,19 @@ class System {
|
|
|
202
201
|
this.setAvailableAudioDevices(audioDevices);
|
|
203
202
|
this.isDevicesLoaded = true;
|
|
204
203
|
} catch (error) {
|
|
205
|
-
this.getUserMediaError = error.name || error;
|
|
206
204
|
this.logger.error('detectDevices()');
|
|
207
205
|
throw new Error('Can`t detect devices');
|
|
208
206
|
}
|
|
209
207
|
}
|
|
210
208
|
|
|
209
|
+
listenDevices(): void {
|
|
210
|
+
navigator.mediaDevices.ondevicechange = () => {
|
|
211
|
+
if (this.isDevicesLoaded) {
|
|
212
|
+
this.detectDevices();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
211
217
|
stopVideoStream(): void {
|
|
212
218
|
if (!this.videoStream) {
|
|
213
219
|
return;
|
package/src/index.ts
CHANGED
|
@@ -43,7 +43,23 @@ class Client {
|
|
|
43
43
|
return !!this.engine.media.audioProducer?.paused;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
get availableVideoDevices(): MediaDeviceInfo[] {
|
|
47
|
+
return this.engine.system.availableVideoDevices;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get availableAudioDevices(): MediaDeviceInfo[] {
|
|
51
|
+
return this.engine.system.availableAudioDevices;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get currentAudioDeviceId(): string | undefined {
|
|
55
|
+
return this.engine.system.currentAudioDeviceId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get currentVideoDeviceId(): string | undefined {
|
|
59
|
+
return this.engine.system.currentVideoDeviceId;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async detectDevices(): Promise<AvailableMediaDevices> {
|
|
47
63
|
await this.engine.system.detectDevices();
|
|
48
64
|
return {
|
|
49
65
|
video: this.engine.system.availableVideoDevices,
|
|
@@ -51,6 +67,22 @@ class Client {
|
|
|
51
67
|
};
|
|
52
68
|
}
|
|
53
69
|
|
|
70
|
+
async getVideoStream(): Promise<MediaStream> {
|
|
71
|
+
return this.engine.system.videoStream || this.engine.system.startVideoStream();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getAudioStream(): Promise<MediaStream> {
|
|
75
|
+
return this.engine.system.audioStream || this.engine.system.startAudioStream();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async getVideoTrack(): Promise<MediaStreamTrack> {
|
|
79
|
+
return this.engine.system.getVideoTrack();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getAudioTrack(): Promise<MediaStreamTrack> {
|
|
83
|
+
return this.engine.system.getAudioTrack();
|
|
84
|
+
}
|
|
85
|
+
|
|
54
86
|
setVideoDevice(deviceId: string): void {
|
|
55
87
|
this.engine.system.setCurrentVideoDevice(deviceId);
|
|
56
88
|
}
|