@livedigital/client 2.1.0 → 2.3.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 +2 -0
- package/dist/constants/simulcastEncodings.d.ts +2 -1
- package/dist/engine/Peer.d.ts +8 -16
- package/dist/engine/PeerConsumer.d.ts +37 -0
- package/dist/engine/PeerProducer.d.ts +5 -1
- package/dist/engine/media/tracks/BaseTrack.d.ts +3 -0
- package/dist/engine/media/tracks/PeerTrack.d.ts +29 -0
- package/dist/engine/media/tracks/VideoTrack.d.ts +4 -0
- package/dist/engine/network/LoadBalancerClient.d.ts +2 -4
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/dist/types/common.d.ts +27 -4
- package/dist/types/network.d.ts +8 -0
- package/package.json +1 -1
- package/src/constants/events.ts +2 -0
- package/src/constants/simulcastEncodings.ts +19 -5
- package/src/engine/Peer.ts +58 -172
- package/src/engine/PeerConsumer.ts +144 -0
- package/src/engine/PeerProducer.ts +11 -1
- package/src/engine/handlers/MediaSoupEventHandler.ts +80 -1
- package/src/engine/index.ts +54 -15
- package/src/engine/media/index.ts +8 -0
- package/src/engine/media/tracks/BaseTrack.ts +16 -0
- package/src/engine/media/tracks/PeerTrack.ts +214 -0
- package/src/engine/media/tracks/VideoTrack.ts +30 -0
- package/src/engine/network/LoadBalancerClient.ts +3 -3
- package/src/types/common.ts +33 -5
- package/src/types/network.ts +10 -0
- package/dist/engine/media/Consumer.d.ts +0 -6
- package/dist/engine/media/VideoConsumer.d.ts +0 -14
- package/src/engine/media/Consumer.ts +0 -9
- package/src/engine/media/VideoConsumer.ts +0 -48
|
@@ -2,11 +2,13 @@ import {
|
|
|
2
2
|
ChangePreferredLayersParams,
|
|
3
3
|
ConsumerScoreChangedPayload,
|
|
4
4
|
ProducerData,
|
|
5
|
-
|
|
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
|
|
package/src/engine/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
PeerResponse,
|
|
9
9
|
Role,
|
|
10
10
|
SocketResponse,
|
|
11
|
-
StartTrackPayload,
|
|
12
11
|
Track,
|
|
13
12
|
TrackLabel,
|
|
14
13
|
} from '../types/common';
|
|
@@ -23,6 +22,10 @@ import Logger from './Logger';
|
|
|
23
22
|
import {
|
|
24
23
|
CHANNEL_EVENTS, CLIENT_EVENTS, MEDIASOUP_EVENTS, PEER_EVENTS, SocketIOEvents,
|
|
25
24
|
} from '../constants/events';
|
|
25
|
+
import { GetNodeRequest } from '../types/network';
|
|
26
|
+
import VideoTrack from './media/tracks/VideoTrack';
|
|
27
|
+
import AudioTrack from './media/tracks/AudioTrack';
|
|
28
|
+
import PeerTrack from './media/tracks/PeerTrack';
|
|
26
29
|
|
|
27
30
|
type EngineParams = {
|
|
28
31
|
clientEventEmitter: EnhancedEventEmitter,
|
|
@@ -142,7 +145,10 @@ class Engine {
|
|
|
142
145
|
try {
|
|
143
146
|
this.logger.debug('join()', { params });
|
|
144
147
|
this.isRoomJoining = true;
|
|
145
|
-
const { webSocketUrl } = await this.getAvailableNode({
|
|
148
|
+
const { webSocketUrl } = await this.getAvailableNode({
|
|
149
|
+
channelId: params.channelId,
|
|
150
|
+
role: params.role,
|
|
151
|
+
});
|
|
146
152
|
this.network.socket.connect(webSocketUrl);
|
|
147
153
|
await this.waitForSocketConnection();
|
|
148
154
|
await this.performJoin(params);
|
|
@@ -219,8 +225,11 @@ class Engine {
|
|
|
219
225
|
video: trackParams.videoTrackOptions,
|
|
220
226
|
});
|
|
221
227
|
|
|
222
|
-
track
|
|
223
|
-
|
|
228
|
+
if (track instanceof VideoTrack) {
|
|
229
|
+
track.setLabel(TrackLabel.Camera);
|
|
230
|
+
track.setEncoderConfig(trackParams.encoderConfig);
|
|
231
|
+
track.setTransformParams(trackParams.transformParams);
|
|
232
|
+
}
|
|
224
233
|
|
|
225
234
|
this.logger.debug('createCameraVideoTrack()', { trackParams, track });
|
|
226
235
|
return track;
|
|
@@ -265,9 +274,10 @@ class Engine {
|
|
|
265
274
|
this.unpublish(track);
|
|
266
275
|
});
|
|
267
276
|
|
|
268
|
-
if (track
|
|
277
|
+
if (track instanceof VideoTrack) {
|
|
269
278
|
track.setLabel(TrackLabel.ScreenVideo);
|
|
270
279
|
track.setEncoderConfig(videoTrackParams.encoderConfig);
|
|
280
|
+
track.setTransformParams(videoTrackParams.transformParams);
|
|
271
281
|
this.logger.debug('createScreenMediaTrack()', { trackParams: videoTrackParams, track });
|
|
272
282
|
return;
|
|
273
283
|
}
|
|
@@ -303,6 +313,19 @@ class Engine {
|
|
|
303
313
|
? track.getCodecOptions()
|
|
304
314
|
: undefined;
|
|
305
315
|
|
|
316
|
+
const maxSpatialLayer = encodings && encodings.length >= 1 ? 1 : 0;
|
|
317
|
+
const appData = {
|
|
318
|
+
peerId: this.mySocketId,
|
|
319
|
+
label: track.getLabel(),
|
|
320
|
+
trackTransformParams: {},
|
|
321
|
+
maxSpatialLayer: 0,
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
if (track instanceof VideoTrack) {
|
|
325
|
+
appData.maxSpatialLayer = maxSpatialLayer;
|
|
326
|
+
appData.trackTransformParams = track.transformParams;
|
|
327
|
+
}
|
|
328
|
+
|
|
306
329
|
const producer = await this.network.sendTransport?.produce({
|
|
307
330
|
track: track.mediaStreamTrack,
|
|
308
331
|
encodings,
|
|
@@ -311,10 +334,7 @@ class Engine {
|
|
|
311
334
|
stopTracks: true,
|
|
312
335
|
disableTrackOnPause: true,
|
|
313
336
|
zeroRtpOnPause: true,
|
|
314
|
-
appData
|
|
315
|
-
peerId: this.mySocketId,
|
|
316
|
-
label: track.getLabel(),
|
|
317
|
-
},
|
|
337
|
+
appData,
|
|
318
338
|
});
|
|
319
339
|
|
|
320
340
|
if (!producer) {
|
|
@@ -322,12 +342,31 @@ class Engine {
|
|
|
322
342
|
}
|
|
323
343
|
|
|
324
344
|
track.setProducer(producer);
|
|
345
|
+
|
|
346
|
+
if (track instanceof VideoTrack) {
|
|
347
|
+
await track.setMaxSpatialLayer(maxSpatialLayer);
|
|
348
|
+
await this.network.socket.request(MEDIASOUP_EVENTS.producerSetMaxSpatialLayer, {
|
|
349
|
+
producerId: track.getProducer()?.id,
|
|
350
|
+
maxSpatialLayer,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (track instanceof AudioTrack) {
|
|
355
|
+
await track.setPriority('high');
|
|
356
|
+
}
|
|
357
|
+
|
|
325
358
|
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
326
|
-
myPeer
|
|
327
|
-
|
|
328
|
-
|
|
359
|
+
if (!myPeer) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const peerTrack = new PeerTrack({
|
|
364
|
+
mediaStreamTrack: track.mediaStreamTrack,
|
|
329
365
|
label: track.getLabel(),
|
|
330
|
-
|
|
366
|
+
engine: this,
|
|
367
|
+
});
|
|
368
|
+
myPeer.tracks.set(peerTrack.label, peerTrack);
|
|
369
|
+
myPeer.observer.safeEmit(PEER_EVENTS.trackStart, peerTrack);
|
|
331
370
|
|
|
332
371
|
this.logger.debug('publish()', { track, encodings, codecOptions });
|
|
333
372
|
});
|
|
@@ -385,9 +424,9 @@ class Engine {
|
|
|
385
424
|
return this.app;
|
|
386
425
|
}
|
|
387
426
|
|
|
388
|
-
private async getAvailableNode(
|
|
427
|
+
private async getAvailableNode(params: GetNodeRequest): Promise<{ webSocketUrl: string }> {
|
|
389
428
|
try {
|
|
390
|
-
const response = await this.network.loadBalancerClient.getNode(
|
|
429
|
+
const response = await this.network.loadBalancerClient.getNode(params);
|
|
391
430
|
return { webSocketUrl: response.webSocketUrl };
|
|
392
431
|
} catch (error) {
|
|
393
432
|
this.logger.error('getAvailableNode()', { error: 'No available nodes' });
|
|
@@ -93,6 +93,10 @@ class Media {
|
|
|
93
93
|
width: { ideal: options?.width || VIDEO_CONSTRAINS.fullhd.width },
|
|
94
94
|
height: { ideal: options?.height || VIDEO_CONSTRAINS.fullhd.height },
|
|
95
95
|
},
|
|
96
|
+
transformParams: {
|
|
97
|
+
width: options?.width || VIDEO_CONSTRAINS.hd.width,
|
|
98
|
+
height: options?.height || VIDEO_CONSTRAINS.hd.height,
|
|
99
|
+
},
|
|
96
100
|
encoderConfig: options?.encoderConfig || {
|
|
97
101
|
encodings: SCREEN_SHARING_SIMULCAST_ENCODINGS,
|
|
98
102
|
},
|
|
@@ -107,6 +111,10 @@ class Media {
|
|
|
107
111
|
width: { ideal: options?.width || VIDEO_CONSTRAINS.hd.width },
|
|
108
112
|
height: { ideal: options?.height || VIDEO_CONSTRAINS.hd.height },
|
|
109
113
|
},
|
|
114
|
+
transformParams: {
|
|
115
|
+
width: options?.width || VIDEO_CONSTRAINS.hd.width,
|
|
116
|
+
height: options?.height || VIDEO_CONSTRAINS.hd.height,
|
|
117
|
+
},
|
|
110
118
|
encoderConfig: options?.encoderConfig || {
|
|
111
119
|
encodings: WEBCAM_SIMULCAST_ENCODINGS,
|
|
112
120
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Producer } from 'mediasoup-client/lib/Producer';
|
|
2
2
|
import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
|
|
3
3
|
import { EncoderConfig, TrackLabel } from '../../../types/common';
|
|
4
|
+
import Logger from '../../Logger';
|
|
4
5
|
|
|
5
6
|
class BaseTrack {
|
|
6
7
|
readonly #mediaStreamTrack: MediaStreamTrack;
|
|
@@ -11,6 +12,8 @@ class BaseTrack {
|
|
|
11
12
|
|
|
12
13
|
private label: TrackLabel = TrackLabel.Unknown;
|
|
13
14
|
|
|
15
|
+
protected logger = new Logger('Track');
|
|
16
|
+
|
|
14
17
|
constructor(mediaStreamTrack: MediaStreamTrack) {
|
|
15
18
|
this.#mediaStreamTrack = mediaStreamTrack;
|
|
16
19
|
}
|
|
@@ -65,6 +68,19 @@ class BaseTrack {
|
|
|
65
68
|
this.producer?.close();
|
|
66
69
|
this.producer = undefined;
|
|
67
70
|
}
|
|
71
|
+
|
|
72
|
+
async setPriority(priority: RTCPriorityType): Promise<void> {
|
|
73
|
+
if (!this.producer) {
|
|
74
|
+
this.logger.warn('setPriority()', 'Not published yet');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await this.producer.setRtpEncodingParameters({ priority });
|
|
79
|
+
this.logger.debug('setPriority()', {
|
|
80
|
+
label: this.getLabel(),
|
|
81
|
+
priority,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
export default BaseTrack;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PreferredLayersParams, SetConsumerPriorityParams, SocketResponse,
|
|
3
|
+
SpatialLayerParams,
|
|
4
|
+
TrackLabel,
|
|
5
|
+
} from '../../../types/common';
|
|
6
|
+
import Logger from '../../Logger';
|
|
7
|
+
import { MEDIASOUP_EVENTS } from '../../../constants/events';
|
|
8
|
+
import PeerConsumer from '../../PeerConsumer';
|
|
9
|
+
import Engine from '../../index';
|
|
10
|
+
|
|
11
|
+
interface PeerTrackConstructor {
|
|
12
|
+
mediaStreamTrack: MediaStreamTrack,
|
|
13
|
+
label: TrackLabel,
|
|
14
|
+
consumer?: PeerConsumer,
|
|
15
|
+
engine: Engine,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class PeerTrack {
|
|
19
|
+
public readonly mediaStreamTrack: MediaStreamTrack;
|
|
20
|
+
|
|
21
|
+
public readonly label: TrackLabel = TrackLabel.Unknown;
|
|
22
|
+
|
|
23
|
+
readonly consumer?: PeerConsumer;
|
|
24
|
+
|
|
25
|
+
readonly #engine: Engine;
|
|
26
|
+
|
|
27
|
+
readonly #logger = new Logger('PeerTrack');
|
|
28
|
+
|
|
29
|
+
constructor(payload: PeerTrackConstructor) {
|
|
30
|
+
this.mediaStreamTrack = payload.mediaStreamTrack;
|
|
31
|
+
this.label = payload.label;
|
|
32
|
+
this.consumer = payload.consumer;
|
|
33
|
+
this.#engine = payload.engine;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get consumerId(): string | undefined {
|
|
37
|
+
return this.consumer?.id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get currentSpatialLayer(): number {
|
|
41
|
+
return this.consumer?.currentSpatialLayer || 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public close(): void {
|
|
45
|
+
this.consumer?.close();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public async pause(): Promise<void> {
|
|
49
|
+
if (!this.consumer) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (this.consumer.paused) {
|
|
54
|
+
this.#logger.warn('pause()', { message: 'Already paused', peer: this, consumer: this.consumer });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await this.pauseRemoteConsumer(this.consumer.id);
|
|
60
|
+
this.consumer.pause();
|
|
61
|
+
this.#logger.debug('pause()', { peer: this, consumer: this.consumer });
|
|
62
|
+
} catch (err) {
|
|
63
|
+
this.#logger.error('pause()', err);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public async resume(): Promise<void> {
|
|
68
|
+
if (!this.consumer) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!this.consumer.paused) {
|
|
73
|
+
this.#logger.warn('resume()', { message: 'Already playing', peer: this, consumer: this.consumer });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await this.resumeRemoteConsumer(this.consumer.id);
|
|
79
|
+
this.consumer.resume();
|
|
80
|
+
this.#logger.debug('resume()', { peer: this });
|
|
81
|
+
} catch (err) {
|
|
82
|
+
this.#logger.error('resume()', err);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public async setPriority(priority: number): Promise<void> {
|
|
87
|
+
if (!this.consumer) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await this.setConsumerPriority({ consumerId: this.consumer.id, priority });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private async setConsumerPriority(params: SetConsumerPriorityParams): Promise<void> {
|
|
95
|
+
try {
|
|
96
|
+
await this.#engine.network.socket.request(MEDIASOUP_EVENTS.setConsumerPriority, params);
|
|
97
|
+
this.#logger.debug('setConsumerPriority()', { peer: this, params });
|
|
98
|
+
} catch (err) {
|
|
99
|
+
this.#logger.error('setConsumerPriority()', { peer: this, params });
|
|
100
|
+
throw new Error('Can`t change stream priority');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private async pauseRemoteConsumer(consumerId: string): Promise<SocketResponse> {
|
|
105
|
+
return this.#engine.network.socket.request(MEDIASOUP_EVENTS.pauseConsumer, { consumerId });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async resumeRemoteConsumer(consumerId: string): Promise<SocketResponse> {
|
|
109
|
+
return this.#engine.network.socket.request(MEDIASOUP_EVENTS.resumeConsumer, { consumerId });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public async requestVideoPreferredLayers({ spatialLayer, temporalLayer }: PreferredLayersParams): Promise<void> {
|
|
113
|
+
if (!this.consumer) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
if (this.consumer.isAudio) {
|
|
119
|
+
this.#logger.warn('requestVideoPreferredLayers()', { message: 'Only video consumers support' });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (spatialLayer === this.consumer.currentSpatialLayer) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (spatialLayer > this.consumer.spatialLayers) {
|
|
128
|
+
this.#logger.warn('requestVideoPreferredLayers()', { message: 'Invalid spatial layer' });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (spatialLayer > this.consumer.currentMaxSpatialLayer) {
|
|
133
|
+
await this.requestMaxSpatialLayer(this.consumer.id, spatialLayer);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await this.#engine.network.socket.request(MEDIASOUP_EVENTS.consumerChangePreferredLayers, {
|
|
138
|
+
consumerId: this.consumer.id,
|
|
139
|
+
spatialLayer,
|
|
140
|
+
temporalLayer,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
this.#logger.debug('requestVideoPreferredLayers()', {
|
|
144
|
+
peer: this,
|
|
145
|
+
consumer: this.consumer,
|
|
146
|
+
spatialLayer,
|
|
147
|
+
temporalLayer,
|
|
148
|
+
});
|
|
149
|
+
} catch (err) {
|
|
150
|
+
this.#logger.error('requestVideoPreferredLayers()', {
|
|
151
|
+
peer: this,
|
|
152
|
+
consumerId: this.consumer.id,
|
|
153
|
+
error: err,
|
|
154
|
+
});
|
|
155
|
+
throw new Error('Error request preferred layers');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public getVideoAvailableLayers(): SpatialLayerParams[] | undefined {
|
|
160
|
+
if (!this.consumer) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (this.consumer.isAudio) {
|
|
165
|
+
this.#logger.warn('getVideoAvailableLayers()', { message: 'Only video consumers support' });
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return this.consumer.availableSpatialLayers;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async requestMaxSpatialLayer(consumerId: string, spatialLayer: number): Promise<void> {
|
|
173
|
+
try {
|
|
174
|
+
if (!this.consumer) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const cantRequest = this.consumer.isAudio
|
|
179
|
+
|| this.consumer.spatialLayers === 0
|
|
180
|
+
|| this.consumer.currentMaxSpatialLayer === spatialLayer
|
|
181
|
+
|| spatialLayer < this.consumer.currentMaxSpatialLayer;
|
|
182
|
+
|
|
183
|
+
if (cantRequest) {
|
|
184
|
+
this.#logger.warn('requestMaxSpatialLayer()', {
|
|
185
|
+
message: 'Can`t request this spatial layer',
|
|
186
|
+
consumerId,
|
|
187
|
+
spatialLayer,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.consumer.setRequestedSpatialLayer(spatialLayer);
|
|
194
|
+
await this.#engine.network.socket.request(MEDIASOUP_EVENTS.producerRequestMaxSpatialLayer, {
|
|
195
|
+
producerId: this.consumer.producerId,
|
|
196
|
+
spatialLayer,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
this.#logger.debug('requestMaxSpatialLayer()', {
|
|
200
|
+
consumerId,
|
|
201
|
+
spatialLayer,
|
|
202
|
+
});
|
|
203
|
+
} catch (error) {
|
|
204
|
+
this.#logger.warn('requestMaxSpatialLayer()', {
|
|
205
|
+
message: 'Error request spatial layer',
|
|
206
|
+
consumerId,
|
|
207
|
+
spatialLayer,
|
|
208
|
+
error,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export default PeerTrack;
|
|
@@ -7,6 +7,8 @@ import TrackWithCodecOptions from './TrackWithCodecOptions';
|
|
|
7
7
|
import TrackWithEncodings from './TrackWithEncodings';
|
|
8
8
|
|
|
9
9
|
class VideoTrack extends BaseTrack implements TrackWithCodecOptions, TrackWithEncodings {
|
|
10
|
+
public transformParams = {};
|
|
11
|
+
|
|
10
12
|
getEncoderConfig(): VideoEncoderConfig {
|
|
11
13
|
return this.encoderConfig;
|
|
12
14
|
}
|
|
@@ -24,6 +26,34 @@ class VideoTrack extends BaseTrack implements TrackWithCodecOptions, TrackWithEn
|
|
|
24
26
|
getEncodings(): RtpEncodingParameters[] {
|
|
25
27
|
return this.getEncoderConfig().encodings || WEBCAM_SIMULCAST_ENCODINGS;
|
|
26
28
|
}
|
|
29
|
+
|
|
30
|
+
setTransformParams(transformParams: { }) {
|
|
31
|
+
this.transformParams = transformParams;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async setMaxSpatialLayer(spatialLayer: number): Promise<void> {
|
|
35
|
+
const producer = this.getProducer();
|
|
36
|
+
if (!producer) {
|
|
37
|
+
this.logger.warn('setMaxSpatialLayer()', { message: 'Not produced yet' });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await producer.setMaxSpatialLayer(spatialLayer);
|
|
42
|
+
this.logger.debug('setMaxSpatialLayer()', {
|
|
43
|
+
label: this.getLabel(),
|
|
44
|
+
spatialLayer,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getMaxSpatialLayer(): number | undefined {
|
|
49
|
+
const producer = this.getProducer();
|
|
50
|
+
if (!producer) {
|
|
51
|
+
this.logger.warn('getMaxSpatialLayer()', { message: 'Not produced yet' });
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return producer.maxSpatialLayer;
|
|
56
|
+
}
|
|
27
57
|
}
|
|
28
58
|
|
|
29
59
|
export default VideoTrack;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import axios, { AxiosInstance } from 'axios';
|
|
2
2
|
import qs from 'qs';
|
|
3
|
-
import { GetNodeResponse } from '../../types/
|
|
3
|
+
import { GetNodeRequest, GetNodeResponse } from '../../types/network';
|
|
4
4
|
|
|
5
5
|
export type LoadBalancerApiClientParams = {
|
|
6
6
|
baseURL?: string;
|
|
@@ -24,13 +24,13 @@ class LoadBalancerApiClient {
|
|
|
24
24
|
this.customNode = customNode;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
async getNode(
|
|
27
|
+
async getNode(params: GetNodeRequest): Promise<GetNodeResponse> {
|
|
28
28
|
if (this.customNode) {
|
|
29
29
|
return this.customNode;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const { data } = await this.api.get<GetNodeResponse>('/nodes/best', {
|
|
33
|
-
params
|
|
33
|
+
params,
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
return data;
|
package/src/types/common.ts
CHANGED
|
@@ -13,21 +13,32 @@ export type SocketResponse = {
|
|
|
13
13
|
[key: string]: unknown;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export type GetNodeResponse = {
|
|
17
|
-
webSocketUrl: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
16
|
export type ProduceParams = {
|
|
21
17
|
kind: MediaKind,
|
|
22
18
|
rtpParameters: RtpParameters,
|
|
23
19
|
appData: Record<string, unknown>,
|
|
24
20
|
};
|
|
25
21
|
|
|
22
|
+
export type TrackTransformParams = {
|
|
23
|
+
width?: number,
|
|
24
|
+
height?: number,
|
|
25
|
+
};
|
|
26
|
+
|
|
26
27
|
export type ProducerData = {
|
|
27
28
|
id: string,
|
|
28
29
|
kind: MediaKind,
|
|
29
30
|
peerId: string,
|
|
30
31
|
label: TrackLabel,
|
|
32
|
+
encodings: RtpEncodingParameters[],
|
|
33
|
+
trackTransformParams: TrackTransformParams,
|
|
34
|
+
maxSpatialLayer: number,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type ConsumerData = {
|
|
38
|
+
producerData: ProducerData,
|
|
39
|
+
peerId: string,
|
|
40
|
+
appId: string,
|
|
41
|
+
channelId: string,
|
|
31
42
|
};
|
|
32
43
|
|
|
33
44
|
export type Role = 'audience' | 'host';
|
|
@@ -51,9 +62,9 @@ export type JoinChannelParams = {
|
|
|
51
62
|
channelId: string,
|
|
52
63
|
appId: string,
|
|
53
64
|
sdkSecret: string,
|
|
65
|
+
role: Role,
|
|
54
66
|
uid?: string,
|
|
55
67
|
appData?: Record<string, unknown>,
|
|
56
|
-
role: Role,
|
|
57
68
|
};
|
|
58
69
|
|
|
59
70
|
export type ChannelEvent = {
|
|
@@ -210,4 +221,21 @@ export type CreateVideoTrackParams = {
|
|
|
210
221
|
frameRate?: { max?: number, min?: number, ideal?: number };
|
|
211
222
|
},
|
|
212
223
|
encoderConfig: VideoEncoderConfig,
|
|
224
|
+
transformParams: TransformParams,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export type SpatialLayerParams = RtpEncodingParameters & TransformParams;
|
|
228
|
+
|
|
229
|
+
export type ProducerRequestMaxSpatialLayer = {
|
|
230
|
+
producerId: string,
|
|
231
|
+
spatialLayer: number,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export type ProducerSetMaxSpatialLayer = ProducerRequestMaxSpatialLayer & {
|
|
235
|
+
peerId: string,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export type TransformParams = {
|
|
239
|
+
width?: number,
|
|
240
|
+
height?: number,
|
|
213
241
|
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
|
|
2
|
-
import Consumer from './Consumer';
|
|
3
|
-
declare class VideoConsumer extends Consumer {
|
|
4
|
-
spatialLayers: number;
|
|
5
|
-
temporalLayers: number;
|
|
6
|
-
currentSpatialLayer: number;
|
|
7
|
-
currentTemporalLayer: number;
|
|
8
|
-
private readonly logger;
|
|
9
|
-
constructor(consumer: MediasoupConsumer);
|
|
10
|
-
parseScalabilityMode(): void;
|
|
11
|
-
setCurrentSpatialLayer(currentSpatialLayer: number): void;
|
|
12
|
-
setCurrentTemporalLayer(currentTemporalLayer: number): void;
|
|
13
|
-
}
|
|
14
|
-
export default VideoConsumer;
|