@livedigital/client 2.1.1 → 2.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.
@@ -15,11 +15,18 @@ export declare type ProduceParams = {
15
15
  rtpParameters: RtpParameters;
16
16
  appData: Record<string, unknown>;
17
17
  };
18
+ export declare type TrackTransformParams = {
19
+ width?: number;
20
+ height?: number;
21
+ };
18
22
  export declare type ProducerData = {
19
23
  id: string;
20
24
  kind: MediaKind;
21
25
  peerId: string;
22
26
  label: TrackLabel;
27
+ encodings: RtpEncodingParameters[];
28
+ trackTransformParams: TrackTransformParams;
29
+ maxSpatialLayer: number;
23
30
  };
24
31
  export declare type ConsumerData = {
25
32
  producerData: ProducerData;
@@ -191,4 +198,17 @@ export declare type CreateVideoTrackParams = {
191
198
  };
192
199
  };
193
200
  encoderConfig: VideoEncoderConfig;
201
+ transformParams: TransformParams;
202
+ };
203
+ export declare type SpatialLayerParams = RtpEncodingParameters & TransformParams;
204
+ export declare type ProducerRequestMaxSpatialLayer = {
205
+ producerId: string;
206
+ spatialLayer: number;
207
+ };
208
+ export declare type ProducerSetMaxSpatialLayer = ProducerRequestMaxSpatialLayer & {
209
+ peerId: string;
210
+ };
211
+ export declare type TransformParams = {
212
+ width?: number;
213
+ height?: number;
194
214
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.1.1",
5
+ "version": "2.2.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -36,6 +36,8 @@ export const MEDIASOUP_EVENTS = {
36
36
  pauseProducer: 'producer.pause',
37
37
  resumeProducer: 'producer.resume',
38
38
  producerScoreChanged: 'producer.scoreChanged',
39
+ producerSetMaxSpatialLayer: 'producer.setMaxSpatialLayer',
40
+ producerRequestMaxSpatialLayer: 'producer.requestMaxSpatialLayer',
39
41
  createConsumer: 'consumer.create',
40
42
  closeConsumer: 'consumer.close',
41
43
  pauseConsumer: 'consumer.pause',
@@ -1,10 +1,24 @@
1
1
  export const WEBCAM_SIMULCAST_ENCODINGS = [
2
- { scaleResolutionDownBy: 5, maxBitrate: 200000 },
3
- { scaleResolutionDownBy: 4, maxBitrate: 400000 },
4
- { scaleResolutionDownBy: 2, maxBitrate: 1500000 },
2
+ {
3
+ scaleResolutionDownBy: 4, // 320x180 0.15mbps 12fps by default
4
+ maxBitrate: 150000,
5
+ maxFramerate: 12,
6
+ },
7
+ {
8
+ scaleResolutionDownBy: 2, // 640x360 0.35mbps 18fps by default
9
+ maxBitrate: 350000,
10
+ maxFramerate: 18,
11
+ },
12
+ {
13
+ scaleResolutionDownBy: 1, // 1280x720 0.9mbps 24fps by default
14
+ maxBitrate: 900000,
15
+ maxFramerate: 24,
16
+ },
5
17
  ];
6
18
 
7
19
  export const SCREEN_SHARING_SIMULCAST_ENCODINGS = [
8
- { dtx: true, maxBitrate: 1500000 },
9
- { dtx: true, maxBitrate: 6000000 },
20
+ {
21
+ maxBitrate: 1000000, // 1920x1080 1mbps 30fps by default
22
+ dtx: true,
23
+ },
10
24
  ];
@@ -1,13 +1,9 @@
1
1
  import { ConsumerOptions } from 'mediasoup-client/lib/Consumer';
2
2
  import {
3
3
  ConsumerScoreChangedPayload,
4
- PreferredLayersParams,
5
4
  ProducerData,
6
5
  ProducerScoreChangedPayload,
7
- SetConsumerPriorityParams,
8
- SocketResponse,
9
6
  ConnectionQuality,
10
- StartTrackPayload,
11
7
  EndTrackPayload,
12
8
  PayloadOfPublishedMedia,
13
9
  PayloadOfUnpublishedMedia,
@@ -20,6 +16,7 @@ import { MEDIASOUP_EVENTS, PEER_EVENTS } from '../constants/events';
20
16
  import Logger from './Logger';
21
17
  import PeerProducer from './PeerProducer';
22
18
  import PeerConsumer from './PeerConsumer';
19
+ import PeerTrack from './media/tracks/PeerTrack';
23
20
 
24
21
  interface PeerConstructor {
25
22
  id: string;
@@ -59,7 +56,7 @@ class Peer {
59
56
 
60
57
  private producers: Map<string, PeerProducer> = new Map();
61
58
 
62
- private consumers: Map<string, PeerConsumer> = new Map();
59
+ public readonly tracks: Map<string, PeerTrack> = new Map();
63
60
 
64
61
  private readonly engine: Engine;
65
62
 
@@ -127,105 +124,6 @@ class Peer {
127
124
  await this.createConsumer(producer);
128
125
  }
129
126
 
130
- public async setMinResolution(consumerId: string): Promise<void> {
131
- const consumer = this.consumers.get(consumerId);
132
- if (!consumer?.isVideo || consumer.spatialLayers === 0) {
133
- return;
134
- }
135
-
136
- await this.changeConsumerPreferredLayers(consumer, { spatialLayer: 0 });
137
- }
138
-
139
- public async setMediumResolution(consumerId: string): Promise<void> {
140
- const consumer = this.consumers.get(consumerId);
141
- if (!consumer?.isVideo || consumer.spatialLayers === 0) {
142
- return;
143
- }
144
-
145
- const mediumLayer = consumer.spatialLayers > 2
146
- ? consumer.spatialLayers - 2
147
- : consumer.spatialLayers;
148
- await this.changeConsumerPreferredLayers(consumer, { spatialLayer: mediumLayer });
149
- }
150
-
151
- public async setMaxResolution(consumerId: string): Promise<void> {
152
- const consumer = this.consumers.get(consumerId);
153
- if (!consumer?.isVideo || consumer.spatialLayers === 0) {
154
- return;
155
- }
156
-
157
- await this.changeConsumerPreferredLayers(consumer, {
158
- spatialLayer: consumer.spatialLayers - 1,
159
- });
160
- }
161
-
162
- public async pause(consumerId: string): Promise<void> {
163
- const consumer = this.consumers.get(consumerId);
164
- if (!consumer) {
165
- this.logger.warn('pause()', { message: 'This peer has no consumer to pause', peer: this, consumerId });
166
- return;
167
- }
168
-
169
- if (consumer.paused) {
170
- this.logger.warn('pause()', { message: 'Already paused', peer: this, consumerId });
171
- return;
172
- }
173
-
174
- try {
175
- await this.pauseRemoteConsumer(consumer.id);
176
- consumer.pause();
177
- this.logger.debug('pause()', { peer: this, consumerId });
178
- } catch (err) {
179
- this.logger.error('pause()', err);
180
- }
181
- }
182
-
183
- public async resume(consumerId: string): Promise<void> {
184
- const consumer = this.consumers.get(consumerId);
185
- if (!consumer) {
186
- this.logger.warn('resume()', { message: 'This peer has no consumer to resume', peer: this, consumerId });
187
- return;
188
- }
189
-
190
- if (!consumer.paused) {
191
- this.logger.warn('resume()', { message: 'Already playing', peer: this, consumerId });
192
- return;
193
- }
194
-
195
- try {
196
- await this.resumeRemoteConsumer(consumer.id);
197
- consumer.resume();
198
- this.logger.debug('resume()', { peer: this });
199
- } catch (err) {
200
- this.logger.error('resume()', err);
201
- }
202
- }
203
-
204
- public async setPriority(consumerId: string, priority: number): Promise<void> {
205
- const consumer = this.consumers.get(consumerId);
206
- if (!consumer) {
207
- this.logger.warn('setPriority()', {
208
- message: 'This peer has no consumer to change priority',
209
- peer: this,
210
- consumerId,
211
- });
212
-
213
- return;
214
- }
215
-
216
- await this.setConsumerPriority({ consumerId, priority });
217
- }
218
-
219
- private async setConsumerPriority(params: SetConsumerPriorityParams): Promise<void> {
220
- try {
221
- await this.engine.network.socket.request(MEDIASOUP_EVENTS.setConsumerPriority, params);
222
- this.logger.debug('setConsumerPriority()', { peer: this, params });
223
- } catch (err) {
224
- this.logger.error('setConsumerPriority()', { peer: this, params });
225
- throw new Error('Can`t change stream priority');
226
- }
227
- }
228
-
229
127
  private async createConsumer(producer: PeerProducer): Promise<void> {
230
128
  const transport = this.engine.network.receiveTransport;
231
129
  if (!transport) {
@@ -249,56 +147,21 @@ class Peer {
249
147
  const consumer = await transport.consume(remoteConsumer);
250
148
  await this.engine.network.socket.request(MEDIASOUP_EVENTS.resumeConsumer, { consumerId: consumer.id });
251
149
 
252
- this.consumers.set(consumer.id, new PeerConsumer(consumer));
253
-
254
150
  this.logger.debug(`Subscribed for ${producer.kind}`, { peer: this });
255
- this.observer.safeEmit(PEER_EVENTS.trackStart, {
256
- producerId: producer.id,
257
- consumerId: consumer.id,
258
- track: consumer.track,
151
+ const track = new PeerTrack({
152
+ mediaStreamTrack: consumer.track,
259
153
  label: producer.label,
260
- } as StartTrackPayload);
154
+ consumer: new PeerConsumer(consumer),
155
+ engine: this.engine,
156
+ });
157
+ this.tracks.set(track.label, track);
158
+ this.observer.safeEmit(PEER_EVENTS.trackStart, track);
261
159
  } catch (error) {
262
160
  this.logger.error('createConsumer()', producer, error);
263
161
  throw new Error('Error subscribe media');
264
162
  }
265
163
  }
266
164
 
267
- private async changeConsumerPreferredLayers(
268
- consumer: PeerConsumer,
269
- { spatialLayer, temporalLayer }: PreferredLayersParams,
270
- ): Promise<void> {
271
- try {
272
- await this.engine.network.socket.request(MEDIASOUP_EVENTS.consumerChangePreferredLayers, {
273
- consumerId: consumer.id,
274
- spatialLayer,
275
- temporalLayer,
276
- });
277
-
278
- this.logger.debug('changeConsumerPreferredLayers()', {
279
- peer: this,
280
- consumer,
281
- spatialLayer,
282
- temporalLayer,
283
- });
284
- } catch (err) {
285
- this.logger.error('changeConsumerPreferredLayers()', {
286
- peer: this,
287
- consumer,
288
- error: err,
289
- });
290
- throw new Error('Error change preferred layer');
291
- }
292
- }
293
-
294
- private async pauseRemoteConsumer(consumerId: string): Promise<SocketResponse> {
295
- return this.engine.network.socket.request(MEDIASOUP_EVENTS.pauseConsumer, { consumerId });
296
- }
297
-
298
- private async resumeRemoteConsumer(consumerId: string): Promise<SocketResponse> {
299
- return this.engine.network.socket.request(MEDIASOUP_EVENTS.resumeConsumer, { consumerId });
300
- }
301
-
302
165
  private handleNewProducer(producerData: ProducerData): void {
303
166
  if (this.producers.get(producerData.id)) {
304
167
  return;
@@ -328,13 +191,13 @@ class Peer {
328
191
  });
329
192
 
330
193
  this.observer.on(MEDIASOUP_EVENTS.closeConsumer, (consumerId) => {
331
- const consumer = this.consumers.get(consumerId);
194
+ const consumer = this.getConsumerById(consumerId);
332
195
  if (!consumer) {
333
196
  return;
334
197
  }
335
198
 
336
199
  consumer.close();
337
- this.consumers.delete(consumerId);
200
+ this.tracks.delete(consumer.appData.producerData.label);
338
201
  this.observer.safeEmit(PEER_EVENTS.trackEnd, {
339
202
  producerId: consumer.producerId,
340
203
  kind: consumer.kind,
@@ -344,7 +207,7 @@ class Peer {
344
207
  });
345
208
 
346
209
  this.observer.on(MEDIASOUP_EVENTS.pauseConsumer, (consumerId) => {
347
- const consumer = this.consumers.get(consumerId);
210
+ const consumer = this.getConsumerById(consumerId);
348
211
  if (!consumer) {
349
212
  return;
350
213
  }
@@ -354,7 +217,7 @@ class Peer {
354
217
  });
355
218
 
356
219
  this.observer.on(MEDIASOUP_EVENTS.resumeConsumer, (consumerId) => {
357
- const consumer = this.consumers.get(consumerId);
220
+ const consumer = this.getConsumerById(consumerId);
358
221
  if (!consumer) {
359
222
  return;
360
223
  }
@@ -369,7 +232,7 @@ class Peer {
369
232
  return;
370
233
  }
371
234
 
372
- const consumer = this.consumers.get(payload.consumerId);
235
+ const consumer = this.getConsumerById(payload.consumerId);
373
236
  if (!consumer) {
374
237
  return;
375
238
  }
@@ -419,6 +282,12 @@ class Peer {
419
282
  return;
420
283
  }
421
284
 
285
+ const track = this.engine.media.getAllTracks().find((t) => t.getProducer()?.id === payload.producerId);
286
+ if (!track) {
287
+ return;
288
+ }
289
+
290
+ const maxSpatialLayer = track.getProducer()?.maxSpatialLayer;
422
291
  const { scores } = payload;
423
292
  if (scores.length === 0) {
424
293
  return;
@@ -426,13 +295,18 @@ class Peer {
426
295
 
427
296
  let { score } = scores[0];
428
297
 
429
- if (scores.length > 1) {
298
+ if (scores.length > 1 && maxSpatialLayer !== 0) {
299
+ // don't count disabled layers if they were disabled recently
300
+ if (maxSpatialLayer) {
301
+ scores.splice(maxSpatialLayer + 1);
302
+ }
303
+
430
304
  // video with multiple encodings has personal score for each encoding
431
305
  const scoresSum = scores
432
306
  .map(({ score: currentScore }) => currentScore)
433
307
  .reduce((acc, curr) => acc + curr, 0);
434
308
 
435
- score = Math.floor(scoresSum / payload.scores.length);
309
+ score = Math.floor(scoresSum / scores.length);
436
310
  }
437
311
 
438
312
  this.outgoingConnectionQuality = Peer.getConnectionQualityByScore(score);
@@ -440,21 +314,30 @@ class Peer {
440
314
  });
441
315
 
442
316
  this.observer.on(MEDIASOUP_EVENTS.consumerChangePreferredLayers, (payload: ChangePreferredLayersPayload) => {
443
- const consumer = this.consumers.get(payload.consumerId);
317
+ const consumer = this.getConsumerById(payload.consumerId);
444
318
  if (consumer?.isVideo) {
445
319
  consumer.setCurrentTemporalLayer(payload.temporalLayer);
446
320
  consumer.setCurrentSpatialLayer(payload.spatialLayer);
321
+ if (payload.spatialLayer === consumer.requestedSpatialLayer) {
322
+ consumer.setRequestedSpatialLayer(undefined);
323
+ }
324
+
325
+ this.logger.debug('consumerChangePreferredLayers()', { consumer, ...payload });
447
326
  }
448
327
  });
449
328
  }
450
329
 
451
330
  private emitConnectionQuality(): void {
452
- // pick connection (incoming or outgoing) with lower quality
453
- const connectionQuality = Math.min(this.outgoingConnectionQuality, this.incomingConnectionQuality);
331
+ // after the initialization a new layer, or new producer, scores of producers and consumers drop for a short time
332
+ setTimeout(() => {
333
+ // pick connection (incoming or outgoing) with lower quality
334
+ const connectionQuality = Math.min(this.outgoingConnectionQuality, this.incomingConnectionQuality);
335
+ this.observer.safeEmit(PEER_EVENTS.connectionQualityChanged, {
336
+ connectionQuality,
337
+ });
454
338
 
455
- this.observer.safeEmit(PEER_EVENTS.connectionQualityChanged, {
456
- connectionQuality,
457
- });
339
+ this.logger.debug('emitConnectionQuality()', { peer: this, connectionQuality });
340
+ }, 1000);
458
341
  }
459
342
 
460
343
  static getConnectionQualityByScore(score: number): ConnectionQuality {
@@ -469,13 +352,23 @@ class Peer {
469
352
  return ConnectionQuality.GOOD;
470
353
  }
471
354
 
472
- public getAllConsumers(): PeerConsumer[] {
473
- return Array.from(this.consumers.values());
355
+ private getAllConsumers(): PeerConsumer[] {
356
+ return Array.from(this.tracks.values())
357
+ .map((track) => track.consumer)
358
+ .filter((item): item is PeerConsumer => !!item);
474
359
  }
475
360
 
476
361
  public getAllProducers(): PeerProducer[] {
477
362
  return Array.from(this.producers.values());
478
363
  }
364
+
365
+ public getConsumerByProducerId(producerId: string): PeerConsumer | undefined {
366
+ return this.getAllConsumers().find((consumer) => consumer.producerId === producerId);
367
+ }
368
+
369
+ public getConsumerById(id: string): PeerConsumer | undefined {
370
+ return this.getAllConsumers().find((consumer) => consumer.id === id);
371
+ }
479
372
  }
480
373
 
481
374
  export default Peer;
@@ -1,7 +1,7 @@
1
1
  import { parseScalabilityMode } from 'mediasoup-client';
2
- import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
2
+ import { MediaKind, RtpEncodingParameters } from 'mediasoup-client/lib/RtpParameters';
3
3
  import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
4
- import { ConsumerData } from '../types/common';
4
+ import { ConsumerData, SpatialLayerParams } from '../types/common';
5
5
  import Logger from './Logger';
6
6
 
7
7
  class PeerConsumer {
@@ -17,6 +17,14 @@ class PeerConsumer {
17
17
 
18
18
  public currentTemporalLayer = 0;
19
19
 
20
+ public requestedSpatialLayer?: number;
21
+
22
+ public currentMaxSpatialLayer: number;
23
+
24
+ public availableSpatialLayers: SpatialLayerParams[] = [];
25
+
26
+ public currentSpatialLayerParams?: SpatialLayerParams;
27
+
20
28
  readonly id: string;
21
29
 
22
30
  readonly kind: MediaKind;
@@ -36,6 +44,10 @@ class PeerConsumer {
36
44
  this.appData = consumer.appData as ConsumerData;
37
45
  this.consumer = consumer;
38
46
  this.logger = new Logger('PeerConsumer');
47
+ this.currentMaxSpatialLayer = this.appData.producerData.maxSpatialLayer;
48
+ this.parseScalabilityMode();
49
+ this.setSpatialLayersParams();
50
+ this.setCurrentSpatialLayerParams();
39
51
  }
40
52
 
41
53
  get paused() {
@@ -76,23 +88,57 @@ class PeerConsumer {
76
88
  }
77
89
  }
78
90
 
91
+ setSpatialLayersParams(): void {
92
+ const { encodings } = this.appData.producerData;
93
+ this.availableSpatialLayers = encodings.map((encoding) => this.parseSpatialLayerParams(encoding));
94
+ }
95
+
96
+ setCurrentSpatialLayerParams(): void {
97
+ const { encodings } = this.appData.producerData;
98
+ this.currentSpatialLayerParams = this.parseSpatialLayerParams(encodings[this.currentSpatialLayer]);
99
+ this.logger.debug('setCurrentSpatialLayerParams()', { currentSpatialLayerParams: this.currentSpatialLayerParams });
100
+ }
101
+
102
+ parseSpatialLayerParams(encoding: RtpEncodingParameters): SpatialLayerParams {
103
+ const { trackTransformParams: { width, height } } = this.appData.producerData;
104
+ const coefficient = encoding.scaleResolutionDownBy || 1;
105
+ if (width && height) {
106
+ return {
107
+ width: width / coefficient,
108
+ height: height / coefficient,
109
+ ...encoding,
110
+ };
111
+ }
112
+
113
+ return encoding;
114
+ }
115
+
79
116
  setCurrentSpatialLayer(currentSpatialLayer: number): void {
80
- if (this.kind !== 'video') {
117
+ if (!this.isVideo) {
81
118
  throw new Error('setCurrentSpatialLayer applies to video consumers only');
82
119
  }
83
120
 
84
121
  this.currentSpatialLayer = currentSpatialLayer;
85
122
  this.logger.debug('setCurrentSpatialLayer()', { currentSpatialLayer });
123
+ this.setCurrentSpatialLayerParams();
86
124
  }
87
125
 
88
126
  setCurrentTemporalLayer(currentTemporalLayer: number): void {
89
- if (this.kind !== 'video') {
127
+ if (!this.isVideo) {
90
128
  throw new Error('setCurrentTemporalLayer applies to video consumers only');
91
129
  }
92
130
 
93
131
  this.currentTemporalLayer = currentTemporalLayer;
94
132
  this.logger.debug('setCurrentTemporalLayer()', { currentTemporalLayer });
95
133
  }
134
+
135
+ setRequestedSpatialLayer(spatialLayer: number | undefined): void {
136
+ this.requestedSpatialLayer = spatialLayer;
137
+ }
138
+
139
+ setCurrentMaxSpatialLayer(spatialLayer: number): void {
140
+ this.currentMaxSpatialLayer = spatialLayer;
141
+ }
96
142
  }
97
143
 
98
144
  export default PeerConsumer;
@@ -1,5 +1,6 @@
1
1
  import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
2
- import { ProducerData, TrackLabel } from '../types/common';
2
+ import { RtpEncodingParameters } from 'mediasoup-client/lib/types';
3
+ import { ProducerData, TrackLabel, TrackTransformParams } from '../types/common';
3
4
 
4
5
  class PeerProducer {
5
6
  readonly id: string;
@@ -10,6 +11,12 @@ class PeerProducer {
10
11
 
11
12
  readonly label: TrackLabel;
12
13
 
14
+ readonly encodings: RtpEncodingParameters[];
15
+
16
+ readonly trackTransformParams: TrackTransformParams;
17
+
18
+ readonly maxSpatialLayer: number;
19
+
13
20
  public score = 10;
14
21
 
15
22
  constructor(params: ProducerData) {
@@ -21,6 +28,9 @@ class PeerProducer {
21
28
  this.kind = kind;
22
29
  this.peerId = peerId;
23
30
  this.label = label;
31
+ this.encodings = params.encodings;
32
+ this.trackTransformParams = params.trackTransformParams;
33
+ this.maxSpatialLayer = params.maxSpatialLayer;
24
34
  }
25
35
  }
26
36
 
@@ -2,11 +2,13 @@ import {
2
2
  ChangePreferredLayersParams,
3
3
  ConsumerScoreChangedPayload,
4
4
  ProducerData,
5
- ProducerScoreChangedPayload,
5
+ ProducerRequestMaxSpatialLayer,
6
+ ProducerScoreChangedPayload, ProducerSetMaxSpatialLayer,
6
7
  } from '../../types/common';
7
8
  import Engine from '../index';
8
9
  import { MEDIASOUP_EVENTS } from '../../constants/events';
9
10
  import Logger from '../Logger';
11
+ import VideoTrack from '../media/tracks/VideoTrack';
10
12
 
11
13
  class MediaSoupEventHandler {
12
14
  private readonly engine: Engine;
@@ -108,6 +110,83 @@ class MediaSoupEventHandler {
108
110
 
109
111
  peer.observer.safeEmit(MEDIASOUP_EVENTS.producerScoreChanged, payload);
110
112
  });
113
+
114
+ connection.on(MEDIASOUP_EVENTS.producerRequestMaxSpatialLayer, async ({
115
+ producerId,
116
+ spatialLayer,
117
+ }: ProducerRequestMaxSpatialLayer) => {
118
+ const track = this.engine.media.getAllTracks().find((t) => t.getProducer()?.id === producerId);
119
+ if (!track) {
120
+ this.logger.warn('producerRequestMaxSpatialLayer()', { message: 'Producer not found' });
121
+ return;
122
+ }
123
+
124
+ if (!(track instanceof VideoTrack)) {
125
+ this.logger.warn('producerRequestMaxSpatialLayer()', { message: 'Wrong producer kind' });
126
+ return;
127
+ }
128
+
129
+ const currentMaxSpatialLayer = track.getMaxSpatialLayer();
130
+ if (currentMaxSpatialLayer === spatialLayer) {
131
+ this.logger.debug('producerRequestMaxSpatialLayer()', {
132
+ message: 'Skip set max spatial layer',
133
+ requestedSpatialLayer: spatialLayer,
134
+ currentMaxSpatialLayer,
135
+ });
136
+ return;
137
+ }
138
+
139
+ await track.setMaxSpatialLayer(spatialLayer);
140
+ await this.engine.network.socket.request(MEDIASOUP_EVENTS.producerSetMaxSpatialLayer, {
141
+ producerId,
142
+ maxSpatialLayer: spatialLayer,
143
+ });
144
+ });
145
+
146
+ connection.on(MEDIASOUP_EVENTS.producerSetMaxSpatialLayer, async ({
147
+ peerId,
148
+ producerId,
149
+ spatialLayer,
150
+ }: ProducerSetMaxSpatialLayer) => {
151
+ const peer = this.engine.peers.find((item) => item.id === peerId);
152
+ if (!peer || peer.isMe) {
153
+ return;
154
+ }
155
+
156
+ const consumer = peer.getConsumerByProducerId(producerId);
157
+ if (!consumer) {
158
+ this.logger.warn('producerSetMaxSpatialLayer()', { message: 'Consumer not found' });
159
+ return;
160
+ }
161
+
162
+ consumer.setCurrentMaxSpatialLayer(spatialLayer);
163
+ this.logger.debug('producerSetMaxSpatialLayer()', {
164
+ producerId,
165
+ consumerId: consumer.id,
166
+ spatialLayer,
167
+ });
168
+
169
+ if (consumer.requestedSpatialLayer !== spatialLayer) {
170
+ this.logger.debug('producerSetMaxSpatialLayer()', {
171
+ message: 'No need to request new preferred layer',
172
+ currentMaxSpatialLayer: spatialLayer,
173
+ requestedMaxSpatialLayer: consumer.requestedSpatialLayer,
174
+ });
175
+
176
+ return;
177
+ }
178
+
179
+ await this.engine.network.socket.request(MEDIASOUP_EVENTS.consumerChangePreferredLayers, {
180
+ consumerId: consumer.id,
181
+ spatialLayer,
182
+ });
183
+
184
+ this.logger.debug('requestVideoPreferredLayers()', {
185
+ producerId,
186
+ consumerId: consumer.id,
187
+ spatialLayer,
188
+ });
189
+ });
111
190
  }
112
191
  }
113
192