@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
package/src/engine/Peer.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { ConsumerOptions } from 'mediasoup-client/lib/Consumer';
|
|
2
2
|
import {
|
|
3
|
-
ChangePreferredLayersParams,
|
|
4
3
|
ConsumerScoreChangedPayload,
|
|
5
4
|
PreferredLayersParams,
|
|
6
5
|
ProducerData,
|
|
7
|
-
ProducerKind,
|
|
8
6
|
ProducerScoreChangedPayload,
|
|
9
7
|
SetConsumerPriorityParams,
|
|
10
8
|
SocketResponse,
|
|
9
|
+
ConnectionQuality,
|
|
10
|
+
StartTrackPayload,
|
|
11
|
+
EndTrackPayload,
|
|
12
|
+
PayloadOfPublishedMedia, PayloadOfUnpublishedMedia,
|
|
11
13
|
} from '../types/common';
|
|
12
14
|
import Consumer from './media/Consumer';
|
|
13
15
|
import VideoConsumer from './media/VideoConsumer';
|
|
@@ -16,7 +18,6 @@ import Engine from './index';
|
|
|
16
18
|
import { MEDIASOUP_EVENTS, PEER_EVENTS } from '../constants/events';
|
|
17
19
|
import Logger from './Logger';
|
|
18
20
|
import PeerProducer from './PeerProducer';
|
|
19
|
-
import { ConnectionQuality } from '../types/common';
|
|
20
21
|
|
|
21
22
|
interface PeerConstructor {
|
|
22
23
|
id: string;
|
|
@@ -51,13 +52,9 @@ class Peer {
|
|
|
51
52
|
|
|
52
53
|
public appData: Record<string, unknown>;
|
|
53
54
|
|
|
54
|
-
private
|
|
55
|
+
private producers: Map<string, PeerProducer> = new Map();
|
|
55
56
|
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
private videoConsumer?: VideoConsumer;
|
|
59
|
-
|
|
60
|
-
private audioConsumer?: Consumer;
|
|
57
|
+
private consumers: Map<string, Consumer | VideoConsumer> = new Map();
|
|
61
58
|
|
|
62
59
|
private readonly engine: Engine;
|
|
63
60
|
|
|
@@ -75,8 +72,6 @@ class Peer {
|
|
|
75
72
|
appId,
|
|
76
73
|
loginDate = new Date(),
|
|
77
74
|
producers = [],
|
|
78
|
-
videoConsumer,
|
|
79
|
-
audioConsumer,
|
|
80
75
|
engine,
|
|
81
76
|
appData,
|
|
82
77
|
uid,
|
|
@@ -88,8 +83,6 @@ class Peer {
|
|
|
88
83
|
this.appData = appData || {};
|
|
89
84
|
this.uid = uid;
|
|
90
85
|
this.engine = engine;
|
|
91
|
-
this.videoConsumer = videoConsumer;
|
|
92
|
-
this.audioConsumer = audioConsumer;
|
|
93
86
|
this.logger = new Logger('Peer');
|
|
94
87
|
producers.forEach(this.handleNewProducer.bind(this));
|
|
95
88
|
this.handlePeerEvents();
|
|
@@ -99,245 +92,121 @@ class Peer {
|
|
|
99
92
|
return this._observer;
|
|
100
93
|
}
|
|
101
94
|
|
|
102
|
-
public get isPublishedVideo(): boolean {
|
|
103
|
-
return !!this.videoProducer;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
public get isPublishedAudio(): boolean {
|
|
107
|
-
return !!this.audioProducer;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
public get hasVideo(): boolean {
|
|
111
|
-
return !!this.videoConsumer;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public get hasAudio(): boolean {
|
|
115
|
-
return !!this.audioConsumer;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public get videoTrack(): MediaStreamTrack | undefined {
|
|
119
|
-
if (this.isMe) {
|
|
120
|
-
return this.engine.system.videoStream?.getVideoTracks()[0];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return this.videoConsumer?.track;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
public get audioTrack(): MediaStreamTrack | undefined {
|
|
127
|
-
if (this.isMe) {
|
|
128
|
-
return this.engine.system.audioStream?.getVideoTracks()[0];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return this.audioConsumer?.track;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public get isVideoPaused(): boolean {
|
|
135
|
-
return this.videoConsumer?.paused || false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
public get isAudioPaused(): boolean {
|
|
139
|
-
return this.audioConsumer?.paused || false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
public get videoCodec(): string | undefined {
|
|
143
|
-
if (!this.videoConsumer) {
|
|
144
|
-
return undefined;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return this.videoConsumer.rtpParameters.codecs[0].mimeType.split('/')[1];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public get audioCodec(): string | undefined {
|
|
151
|
-
if (!this.audioConsumer) {
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return this.audioConsumer.rtpParameters.codecs[0].mimeType.split('/')[1];
|
|
156
|
-
}
|
|
157
|
-
|
|
158
95
|
public get isMe(): boolean {
|
|
159
96
|
return this.id === this.engine.mySocketId;
|
|
160
97
|
}
|
|
161
98
|
|
|
162
|
-
public
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (this.videoConsumer) {
|
|
169
|
-
this.logger.warn('subscribeVideo()', { message: 'Already subscribed this peer\'s video', peer: this });
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
await this.createConsumer(this.videoProducer);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
public async subscribeAudio(): Promise<void> {
|
|
177
|
-
if (!this.audioProducer) {
|
|
178
|
-
this.logger.warn('subscribeAudio()', { message: 'This peer has no published audio', peer: this });
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (this.audioConsumer) {
|
|
183
|
-
this.logger.warn('subscribeAudio()', { message: 'Already subscribed this peer\'s audio', peer: this });
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
await this.createConsumer(this.audioProducer);
|
|
99
|
+
public get publishedMedia(): PayloadOfPublishedMedia[] {
|
|
100
|
+
return Array.from(this.producers.values()).map((producer) => ({
|
|
101
|
+
producerId: producer.id,
|
|
102
|
+
kind: producer.kind,
|
|
103
|
+
label: producer.label,
|
|
104
|
+
}));
|
|
188
105
|
}
|
|
189
106
|
|
|
190
|
-
public async
|
|
191
|
-
|
|
192
|
-
|
|
107
|
+
public async subscribe(producerId: string): Promise<void> {
|
|
108
|
+
const producer = this.producers.get(producerId);
|
|
109
|
+
if (!producer) {
|
|
110
|
+
this.logger.warn('subscribe()', { message: 'Peer does not have a producer', peer: this, producerId });
|
|
193
111
|
return;
|
|
194
112
|
}
|
|
195
113
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
public async unSubscribeAudio(): Promise<void> {
|
|
202
|
-
if (!this.audioConsumer) {
|
|
203
|
-
this.logger.warn('unSubscribeAudio()', { message: 'This peer has no subscribed audio', peer: this });
|
|
114
|
+
const consumer = this.getAllConsumers().find((item) => item.producerId === producerId);
|
|
115
|
+
if (consumer) {
|
|
116
|
+
this.logger.warn('subscribe()', { message: 'Already subscribed to producer', peer: this, producerId });
|
|
204
117
|
return;
|
|
205
118
|
}
|
|
206
119
|
|
|
207
|
-
await this.
|
|
208
|
-
this.audioConsumer = undefined;
|
|
209
|
-
this.observer.safeEmit('track-end', { kind: 'audio' });
|
|
120
|
+
await this.createConsumer(producer);
|
|
210
121
|
}
|
|
211
122
|
|
|
212
|
-
public async setMinResolution(): Promise<void> {
|
|
213
|
-
|
|
123
|
+
public async setMinResolution(consumerId: string): Promise<void> {
|
|
124
|
+
const consumer = this.consumers.get(consumerId);
|
|
125
|
+
if (!(consumer instanceof VideoConsumer) || consumer.spatialLayers === 0) {
|
|
214
126
|
return;
|
|
215
127
|
}
|
|
216
128
|
|
|
217
|
-
await this.changeConsumerPreferredLayers({
|
|
218
|
-
spatialLayer: 0,
|
|
219
|
-
});
|
|
129
|
+
await this.changeConsumerPreferredLayers(consumer, { spatialLayer: 0 });
|
|
220
130
|
}
|
|
221
131
|
|
|
222
|
-
public async setMediumResolution(): Promise<void> {
|
|
223
|
-
|
|
132
|
+
public async setMediumResolution(consumerId: string): Promise<void> {
|
|
133
|
+
const consumer = this.consumers.get(consumerId);
|
|
134
|
+
if (!(consumer instanceof VideoConsumer) || consumer.spatialLayers === 0) {
|
|
224
135
|
return;
|
|
225
136
|
}
|
|
226
137
|
|
|
227
|
-
const mediumLayer =
|
|
228
|
-
?
|
|
229
|
-
:
|
|
230
|
-
|
|
231
|
-
await this.changeConsumerPreferredLayers({
|
|
232
|
-
spatialLayer: mediumLayer,
|
|
233
|
-
});
|
|
138
|
+
const mediumLayer = consumer.spatialLayers > 2
|
|
139
|
+
? consumer.spatialLayers - 2
|
|
140
|
+
: consumer.spatialLayers;
|
|
141
|
+
await this.changeConsumerPreferredLayers(consumer, { spatialLayer: mediumLayer });
|
|
234
142
|
}
|
|
235
143
|
|
|
236
|
-
public async setMaxResolution(): Promise<void> {
|
|
237
|
-
|
|
144
|
+
public async setMaxResolution(consumerId: string): Promise<void> {
|
|
145
|
+
const consumer = this.consumers.get(consumerId);
|
|
146
|
+
if (!(consumer instanceof VideoConsumer) || consumer.spatialLayers === 0) {
|
|
238
147
|
return;
|
|
239
148
|
}
|
|
240
149
|
|
|
241
|
-
await this.changeConsumerPreferredLayers({
|
|
242
|
-
spatialLayer:
|
|
150
|
+
await this.changeConsumerPreferredLayers(consumer, {
|
|
151
|
+
spatialLayer: consumer.spatialLayers - 1,
|
|
243
152
|
});
|
|
244
153
|
}
|
|
245
154
|
|
|
246
|
-
public async
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (this.videoConsumer.paused) {
|
|
253
|
-
this.logger.warn('pauseVideo()', { message: 'Already paused', peer: this });
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
await this.pauseRemoteConsumer(this.videoConsumer?.id);
|
|
259
|
-
this.videoConsumer.pause();
|
|
260
|
-
this.logger.debug('pauseVideo()', { peer: this });
|
|
261
|
-
} catch (err) {
|
|
262
|
-
this.logger.error('pauseVideo()', err);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
public async pauseAudio(): Promise<void> {
|
|
267
|
-
if (!this.audioConsumer) {
|
|
268
|
-
this.logger.warn('pauseAudio()', { message: 'This peer has no subscribed audio', peer: this });
|
|
155
|
+
public async pause(consumerId: string): Promise<void> {
|
|
156
|
+
const consumer = this.consumers.get(consumerId);
|
|
157
|
+
if (!consumer) {
|
|
158
|
+
this.logger.warn('pause()', { message: 'This peer has no consumer to pause', peer: this, consumerId });
|
|
269
159
|
return;
|
|
270
160
|
}
|
|
271
161
|
|
|
272
|
-
if (
|
|
273
|
-
this.logger.warn('
|
|
162
|
+
if (consumer.paused) {
|
|
163
|
+
this.logger.warn('pause()', { message: 'Already paused', peer: this, consumerId });
|
|
274
164
|
return;
|
|
275
165
|
}
|
|
276
166
|
|
|
277
167
|
try {
|
|
278
|
-
await this.pauseRemoteConsumer(
|
|
279
|
-
|
|
280
|
-
this.logger.debug('
|
|
168
|
+
await this.pauseRemoteConsumer(consumer.id);
|
|
169
|
+
consumer.pause();
|
|
170
|
+
this.logger.debug('pause()', { peer: this, consumerId });
|
|
281
171
|
} catch (err) {
|
|
282
|
-
this.logger.error('
|
|
172
|
+
this.logger.error('pause()', err);
|
|
283
173
|
}
|
|
284
174
|
}
|
|
285
175
|
|
|
286
|
-
public async
|
|
287
|
-
|
|
288
|
-
|
|
176
|
+
public async resume(consumerId: string): Promise<void> {
|
|
177
|
+
const consumer = this.consumers.get(consumerId);
|
|
178
|
+
if (!consumer) {
|
|
179
|
+
this.logger.warn('resume()', { message: 'This peer has no consumer to resume', peer: this, consumerId });
|
|
289
180
|
return;
|
|
290
181
|
}
|
|
291
182
|
|
|
292
|
-
if (!
|
|
293
|
-
this.logger.warn('
|
|
183
|
+
if (!consumer.paused) {
|
|
184
|
+
this.logger.warn('resume()', { message: 'Already playing', peer: this, consumerId });
|
|
294
185
|
return;
|
|
295
186
|
}
|
|
296
187
|
|
|
297
188
|
try {
|
|
298
|
-
await this.resumeRemoteConsumer(
|
|
299
|
-
|
|
300
|
-
this.logger.debug('
|
|
189
|
+
await this.resumeRemoteConsumer(consumer.id);
|
|
190
|
+
consumer.resume();
|
|
191
|
+
this.logger.debug('resume()', { peer: this });
|
|
301
192
|
} catch (err) {
|
|
302
|
-
this.logger.error('
|
|
193
|
+
this.logger.error('resume()', err);
|
|
303
194
|
}
|
|
304
195
|
}
|
|
305
196
|
|
|
306
|
-
public async
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
try {
|
|
317
|
-
await this.resumeRemoteConsumer(this.audioConsumer?.id);
|
|
318
|
-
this.audioConsumer.resume();
|
|
319
|
-
this.logger.debug('resumeAudio()', { peer: this });
|
|
320
|
-
} catch (err) {
|
|
321
|
-
this.logger.error('resumeAudio()', err);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
public async setVideoStreamPriority(priority: number): Promise<void> {
|
|
326
|
-
if (!this.videoConsumer) {
|
|
327
|
-
this.logger.warn('setVideoStreamPriority()', { message: 'This peer has no subscribed audio', peer: this });
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
await this.setConsumerPriority({ consumerId: this.videoConsumer.id, priority });
|
|
332
|
-
}
|
|
197
|
+
public async setPriority(consumerId: string, priority: number): Promise<void> {
|
|
198
|
+
const consumer = this.consumers.get(consumerId);
|
|
199
|
+
if (!consumer) {
|
|
200
|
+
this.logger.warn('setPriority()', {
|
|
201
|
+
message: 'This peer has no consumer to change priority',
|
|
202
|
+
peer: this,
|
|
203
|
+
consumerId,
|
|
204
|
+
});
|
|
333
205
|
|
|
334
|
-
public async setAudioStreamPriority(priority: number): Promise<void> {
|
|
335
|
-
if (!this.audioConsumer) {
|
|
336
|
-
this.logger.warn('setAudioStreamPriority()', { message: 'This peer has no subscribed audio', peer: this });
|
|
337
206
|
return;
|
|
338
207
|
}
|
|
339
208
|
|
|
340
|
-
await this.setConsumerPriority({ consumerId
|
|
209
|
+
await this.setConsumerPriority({ consumerId, priority });
|
|
341
210
|
}
|
|
342
211
|
|
|
343
212
|
private async setConsumerPriority(params: SetConsumerPriorityParams): Promise<void> {
|
|
@@ -372,91 +241,46 @@ class Peer {
|
|
|
372
241
|
await this.engine.network.socket.request(MEDIASOUP_EVENTS.resumeConsumer, { consumerId: consumer.id });
|
|
373
242
|
|
|
374
243
|
if (producer.kind === 'audio') {
|
|
375
|
-
this.
|
|
244
|
+
this.consumers.set(consumer.id, new Consumer(consumer));
|
|
376
245
|
}
|
|
377
246
|
|
|
378
247
|
if (producer.kind === 'video') {
|
|
379
|
-
this.
|
|
248
|
+
this.consumers.set(consumer.id, new VideoConsumer(consumer));
|
|
380
249
|
}
|
|
381
250
|
|
|
382
251
|
this.logger.debug(`Subscribed for ${producer.kind}`, { peer: this });
|
|
383
|
-
this.observer.safeEmit(PEER_EVENTS.trackStart, {
|
|
252
|
+
this.observer.safeEmit(PEER_EVENTS.trackStart, {
|
|
253
|
+
producerId: producer.id,
|
|
254
|
+
consumerId: consumer.id,
|
|
255
|
+
track: consumer.track,
|
|
256
|
+
label: producer.label,
|
|
257
|
+
} as StartTrackPayload);
|
|
384
258
|
} catch (error) {
|
|
385
259
|
this.logger.error('createConsumer()', producer, error);
|
|
386
260
|
throw new Error('Error subscribe media');
|
|
387
261
|
}
|
|
388
262
|
}
|
|
389
263
|
|
|
390
|
-
private async
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
consumerId: consumer.id,
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
consumer.close();
|
|
398
|
-
this.logger.warn('Unsubscribed media', { peer: this, kind: consumer.kind });
|
|
399
|
-
} catch (err) {
|
|
400
|
-
this.logger.debug('closeConsumer()', { peer: this, consumer, error: err });
|
|
401
|
-
throw new Error('Error unsubscribe media');
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
private async changeConsumerPreferredLayers({ spatialLayer, temporalLayer }: PreferredLayersParams): Promise<void> {
|
|
406
|
-
if (!this.videoConsumer) {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const {
|
|
411
|
-
spatialLayer: oldSpatialLayer,
|
|
412
|
-
temporalLayer: oldTemporalLayer,
|
|
413
|
-
} = this.videoConsumer.currentPreferredLayers;
|
|
414
|
-
|
|
415
|
-
if (oldSpatialLayer === spatialLayer || oldTemporalLayer === temporalLayer) {
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
264
|
+
private async changeConsumerPreferredLayers(
|
|
265
|
+
consumer: VideoConsumer,
|
|
266
|
+
{ spatialLayer, temporalLayer }: PreferredLayersParams,
|
|
267
|
+
): Promise<void> {
|
|
419
268
|
try {
|
|
420
|
-
this.videoConsumer.currentPreferredLayers = { spatialLayer, temporalLayer };
|
|
421
269
|
await this.engine.network.socket.request(MEDIASOUP_EVENTS.consumerChangePreferredLayers, {
|
|
422
|
-
consumerId:
|
|
270
|
+
consumerId: consumer.id,
|
|
423
271
|
spatialLayer,
|
|
424
272
|
temporalLayer,
|
|
425
273
|
});
|
|
426
274
|
} catch (err) {
|
|
427
275
|
this.logger.error('changeConsumerPreferredLayers()', {
|
|
428
276
|
peer: this,
|
|
429
|
-
consumer
|
|
277
|
+
consumer,
|
|
430
278
|
error: err,
|
|
431
279
|
});
|
|
432
280
|
throw new Error('Error change preferred layer');
|
|
433
281
|
}
|
|
434
282
|
}
|
|
435
283
|
|
|
436
|
-
private setVideoProducer(producer: PeerProducer): void {
|
|
437
|
-
if (producer.kind !== 'video') {
|
|
438
|
-
throw new Error('Error kind of producer');
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
this.videoProducer = producer;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
private setAudioProducer(producer: PeerProducer): void {
|
|
445
|
-
if (producer.kind !== 'audio') {
|
|
446
|
-
throw new Error('Error kind of producer');
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
this.audioProducer = producer;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
private removeVideoProducer(): void {
|
|
453
|
-
this.videoProducer = undefined;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
private removeAudioProducer(): void {
|
|
457
|
-
this.audioProducer = undefined;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
284
|
private async pauseRemoteConsumer(consumerId: string): Promise<SocketResponse> {
|
|
461
285
|
return this.engine.network.socket.request(MEDIASOUP_EVENTS.pauseConsumer, { consumerId });
|
|
462
286
|
}
|
|
@@ -467,87 +291,62 @@ class Peer {
|
|
|
467
291
|
|
|
468
292
|
private handleNewProducer(producerData: ProducerData): void {
|
|
469
293
|
const producer = new PeerProducer(producerData);
|
|
470
|
-
|
|
471
|
-
this.setVideoProducer(producer);
|
|
472
|
-
} else {
|
|
473
|
-
this.setAudioProducer(producer);
|
|
474
|
-
}
|
|
475
|
-
|
|
294
|
+
this.producers.set(producer.id, producer);
|
|
476
295
|
this.logger.debug('Peer published media:', { peer: this, producer });
|
|
477
|
-
this.observer.safeEmit(PEER_EVENTS.mediaPublished, {
|
|
296
|
+
this.observer.safeEmit(PEER_EVENTS.mediaPublished, {
|
|
297
|
+
producerId: producer.id,
|
|
298
|
+
kind: producer.kind,
|
|
299
|
+
label: producer.label,
|
|
300
|
+
} as PayloadOfPublishedMedia);
|
|
478
301
|
}
|
|
479
302
|
|
|
480
303
|
private handlePeerEvents(): void {
|
|
481
304
|
this.observer.on(MEDIASOUP_EVENTS.newProducer, this.handleNewProducer.bind(this));
|
|
482
305
|
|
|
483
306
|
this.observer.on(MEDIASOUP_EVENTS.producerClose, (producer: ProducerData) => {
|
|
484
|
-
|
|
485
|
-
this.removeVideoProducer();
|
|
486
|
-
} else {
|
|
487
|
-
this.removeAudioProducer();
|
|
488
|
-
}
|
|
489
|
-
|
|
307
|
+
this.producers.delete(producer.id);
|
|
490
308
|
this.logger.debug('Peer unpublished media:', { peer: this, producer });
|
|
491
|
-
this.observer.safeEmit(PEER_EVENTS.mediaUnPublished, {
|
|
309
|
+
this.observer.safeEmit(PEER_EVENTS.mediaUnPublished, {
|
|
310
|
+
producerId: producer.id,
|
|
311
|
+
kind: producer.kind,
|
|
312
|
+
label: producer.label,
|
|
313
|
+
} as PayloadOfUnpublishedMedia);
|
|
492
314
|
});
|
|
493
315
|
|
|
494
316
|
this.observer.on(MEDIASOUP_EVENTS.closeConsumer, (consumerId) => {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
this.videoConsumer = undefined;
|
|
498
|
-
this.observer.safeEmit(PEER_EVENTS.trackEnd, { kind: 'video' });
|
|
499
|
-
this.logger.debug('Peer video track was ended:', { peer: this });
|
|
317
|
+
const consumer = this.consumers.get(consumerId);
|
|
318
|
+
if (!consumer) {
|
|
500
319
|
return;
|
|
501
320
|
}
|
|
502
321
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
322
|
+
consumer.close();
|
|
323
|
+
this.consumers.delete(consumerId);
|
|
324
|
+
this.observer.safeEmit(PEER_EVENTS.trackEnd, {
|
|
325
|
+
producerId: consumer.producerId,
|
|
326
|
+
kind: consumer.kind,
|
|
327
|
+
label: consumer.appData.producerData.label,
|
|
328
|
+
} as EndTrackPayload);
|
|
329
|
+
this.logger.debug('Peer video track was ended:', { peer: this });
|
|
509
330
|
});
|
|
510
331
|
|
|
511
332
|
this.observer.on(MEDIASOUP_EVENTS.pauseConsumer, (consumerId) => {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
this.logger.debug('Peer video track was paused:', { peer: this });
|
|
333
|
+
const consumer = this.consumers.get(consumerId);
|
|
334
|
+
if (!consumer) {
|
|
515
335
|
return;
|
|
516
336
|
}
|
|
517
337
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
this.logger.debug('Peer audio track was paused:', { peer: this });
|
|
521
|
-
}
|
|
338
|
+
consumer.pause();
|
|
339
|
+
this.logger.debug('Peer video track was paused:', { peer: this });
|
|
522
340
|
});
|
|
523
341
|
|
|
524
342
|
this.observer.on(MEDIASOUP_EVENTS.resumeConsumer, (consumerId) => {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
this.logger.debug('Peer video track was resumed:', { peer: this });
|
|
343
|
+
const consumer = this.consumers.get(consumerId);
|
|
344
|
+
if (!consumer) {
|
|
528
345
|
return;
|
|
529
346
|
}
|
|
530
347
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
this.logger.debug('Peer video track was resumed:', { peer: this });
|
|
534
|
-
}
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
this.observer.on(MEDIASOUP_EVENTS.consumerChangePreferredLayers, ({
|
|
538
|
-
spatialLayer, temporalLayer,
|
|
539
|
-
}: ChangePreferredLayersParams) => {
|
|
540
|
-
if (!this.videoConsumer) {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
this.videoConsumer.setCurrentSpatialLayer(spatialLayer);
|
|
545
|
-
this.videoConsumer.setCurrentTemporalLayer(temporalLayer);
|
|
546
|
-
this.logger.debug('Peer video track was changed preferred layers:', {
|
|
547
|
-
peer: this,
|
|
548
|
-
spatialLayer,
|
|
549
|
-
temporalLayer,
|
|
550
|
-
});
|
|
348
|
+
consumer.resume();
|
|
349
|
+
this.logger.debug('Peer video track was paused:', { peer: this });
|
|
551
350
|
});
|
|
552
351
|
|
|
553
352
|
// set outgoing connection quality for remote peer
|
|
@@ -556,8 +355,8 @@ class Peer {
|
|
|
556
355
|
return;
|
|
557
356
|
}
|
|
558
357
|
|
|
559
|
-
const consumer =
|
|
560
|
-
if (consumer
|
|
358
|
+
const consumer = this.consumers.get(payload.consumerId);
|
|
359
|
+
if (!consumer) {
|
|
561
360
|
return;
|
|
562
361
|
}
|
|
563
362
|
|
|
@@ -565,22 +364,14 @@ class Peer {
|
|
|
565
364
|
consumer.score = score;
|
|
566
365
|
consumer.producerScore = producerScore;
|
|
567
366
|
|
|
568
|
-
const peerProducerScores =
|
|
569
|
-
if (this.audioConsumer) {
|
|
570
|
-
peerProducerScores.push(this.audioConsumer.producerScore);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (this.videoConsumer) {
|
|
574
|
-
peerProducerScores.push(this.videoConsumer.producerScore);
|
|
575
|
-
}
|
|
576
|
-
|
|
367
|
+
const peerProducerScores: number[] = this.getAllConsumers().map((item) => item.producerScore);
|
|
577
368
|
const minScore = Math.min(...peerProducerScores);
|
|
578
|
-
this.outgoingConnectionQuality =
|
|
369
|
+
this.outgoingConnectionQuality = Peer.getConnectionQualityByScore(minScore);
|
|
579
370
|
this.emitConnectionQuality();
|
|
580
371
|
});
|
|
581
372
|
|
|
582
373
|
// set incoming connection quality for current peer
|
|
583
|
-
this.observer.on(MEDIASOUP_EVENTS.consumerScoreChanged, (
|
|
374
|
+
this.observer.on(MEDIASOUP_EVENTS.consumerScoreChanged, () => {
|
|
584
375
|
if (!this.isMe) {
|
|
585
376
|
return;
|
|
586
377
|
}
|
|
@@ -588,14 +379,11 @@ class Peer {
|
|
|
588
379
|
const allPeersLowQualityConsumersScores = [] as number[];
|
|
589
380
|
// pick scores of low quality consumers (consumerScore < producerScore)
|
|
590
381
|
this.engine.peers.forEach((peer) => {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (audioConsumer && audioConsumer?.score < audioConsumer?.producerScore) {
|
|
597
|
-
allPeersLowQualityConsumersScores.push(audioConsumer.score);
|
|
598
|
-
}
|
|
382
|
+
peer.getAllConsumers().forEach((item) => {
|
|
383
|
+
if (item.score < item.producerScore) {
|
|
384
|
+
allPeersLowQualityConsumersScores.push(item.score);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
599
387
|
}, []);
|
|
600
388
|
|
|
601
389
|
if (allPeersLowQualityConsumersScores.length === 0) {
|
|
@@ -605,7 +393,7 @@ class Peer {
|
|
|
605
393
|
// calculate average score of low quality consumers
|
|
606
394
|
const scoresSum = allPeersLowQualityConsumersScores.map((score) => score).reduce((acc, curr) => acc + curr, 0);
|
|
607
395
|
const averageConsumersScore = Math.floor(scoresSum / allPeersLowQualityConsumersScores.length);
|
|
608
|
-
this.incomingConnectionQuality =
|
|
396
|
+
this.incomingConnectionQuality = Peer.getConnectionQualityByScore(averageConsumersScore);
|
|
609
397
|
}
|
|
610
398
|
|
|
611
399
|
this.emitConnectionQuality();
|
|
@@ -622,18 +410,18 @@ class Peer {
|
|
|
622
410
|
return;
|
|
623
411
|
}
|
|
624
412
|
|
|
625
|
-
let score = scores[0]
|
|
413
|
+
let { score } = scores[0];
|
|
626
414
|
|
|
627
415
|
if (scores.length > 1) {
|
|
628
416
|
// video with multiple encodings has personal score for each encoding
|
|
629
417
|
const scoresSum = scores
|
|
630
|
-
.map(({ score }) =>
|
|
418
|
+
.map(({ score: currentScore }) => currentScore)
|
|
631
419
|
.reduce((acc, curr) => acc + curr, 0);
|
|
632
420
|
|
|
633
421
|
score = Math.floor(scoresSum / payload.scores.length);
|
|
634
422
|
}
|
|
635
423
|
|
|
636
|
-
this.outgoingConnectionQuality =
|
|
424
|
+
this.outgoingConnectionQuality = Peer.getConnectionQualityByScore(score);
|
|
637
425
|
this.emitConnectionQuality();
|
|
638
426
|
});
|
|
639
427
|
}
|
|
@@ -647,18 +435,25 @@ class Peer {
|
|
|
647
435
|
});
|
|
648
436
|
}
|
|
649
437
|
|
|
650
|
-
|
|
438
|
+
static getConnectionQualityByScore(score: number): ConnectionQuality {
|
|
651
439
|
if (score < ScoreThreshold.BAD) {
|
|
652
440
|
return ConnectionQuality.BAD;
|
|
653
441
|
}
|
|
654
442
|
|
|
655
|
-
|
|
656
443
|
if (score < ScoreThreshold.MEDIUM) {
|
|
657
444
|
return ConnectionQuality.MEDIUM;
|
|
658
445
|
}
|
|
659
446
|
|
|
660
447
|
return ConnectionQuality.GOOD;
|
|
661
448
|
}
|
|
449
|
+
|
|
450
|
+
public getAllConsumers(): Consumer[] {
|
|
451
|
+
return Array.from(this.consumers.values());
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
public getAllProducers(): PeerProducer[] {
|
|
455
|
+
return Array.from(this.producers.values());
|
|
456
|
+
}
|
|
662
457
|
}
|
|
663
458
|
|
|
664
459
|
export default Peer;
|