@livedigital/client 2.0.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.
- 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/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/dist/types/common.d.ts +26 -0
- package/package.json +2 -2
- 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 +47 -12
- 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/types/common.ts +32 -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,9 @@ 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 VideoTrack from './media/tracks/VideoTrack';
|
|
26
|
+
import AudioTrack from './media/tracks/AudioTrack';
|
|
27
|
+
import PeerTrack from './media/tracks/PeerTrack';
|
|
26
28
|
|
|
27
29
|
type EngineParams = {
|
|
28
30
|
clientEventEmitter: EnhancedEventEmitter,
|
|
@@ -219,8 +221,11 @@ class Engine {
|
|
|
219
221
|
video: trackParams.videoTrackOptions,
|
|
220
222
|
});
|
|
221
223
|
|
|
222
|
-
track
|
|
223
|
-
|
|
224
|
+
if (track instanceof VideoTrack) {
|
|
225
|
+
track.setLabel(TrackLabel.Camera);
|
|
226
|
+
track.setEncoderConfig(trackParams.encoderConfig);
|
|
227
|
+
track.setTransformParams(trackParams.transformParams);
|
|
228
|
+
}
|
|
224
229
|
|
|
225
230
|
this.logger.debug('createCameraVideoTrack()', { trackParams, track });
|
|
226
231
|
return track;
|
|
@@ -265,9 +270,10 @@ class Engine {
|
|
|
265
270
|
this.unpublish(track);
|
|
266
271
|
});
|
|
267
272
|
|
|
268
|
-
if (track
|
|
273
|
+
if (track instanceof VideoTrack) {
|
|
269
274
|
track.setLabel(TrackLabel.ScreenVideo);
|
|
270
275
|
track.setEncoderConfig(videoTrackParams.encoderConfig);
|
|
276
|
+
track.setTransformParams(videoTrackParams.transformParams);
|
|
271
277
|
this.logger.debug('createScreenMediaTrack()', { trackParams: videoTrackParams, track });
|
|
272
278
|
return;
|
|
273
279
|
}
|
|
@@ -303,6 +309,19 @@ class Engine {
|
|
|
303
309
|
? track.getCodecOptions()
|
|
304
310
|
: undefined;
|
|
305
311
|
|
|
312
|
+
const maxSpatialLayer = encodings && encodings.length >= 1 ? 1 : 0;
|
|
313
|
+
const appData = {
|
|
314
|
+
peerId: this.mySocketId,
|
|
315
|
+
label: track.getLabel(),
|
|
316
|
+
trackTransformParams: {},
|
|
317
|
+
maxSpatialLayer: 0,
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
if (track instanceof VideoTrack) {
|
|
321
|
+
appData.maxSpatialLayer = maxSpatialLayer;
|
|
322
|
+
appData.trackTransformParams = track.transformParams;
|
|
323
|
+
}
|
|
324
|
+
|
|
306
325
|
const producer = await this.network.sendTransport?.produce({
|
|
307
326
|
track: track.mediaStreamTrack,
|
|
308
327
|
encodings,
|
|
@@ -311,10 +330,7 @@ class Engine {
|
|
|
311
330
|
stopTracks: true,
|
|
312
331
|
disableTrackOnPause: true,
|
|
313
332
|
zeroRtpOnPause: true,
|
|
314
|
-
appData
|
|
315
|
-
peerId: this.mySocketId,
|
|
316
|
-
label: track.getLabel(),
|
|
317
|
-
},
|
|
333
|
+
appData,
|
|
318
334
|
});
|
|
319
335
|
|
|
320
336
|
if (!producer) {
|
|
@@ -322,12 +338,31 @@ class Engine {
|
|
|
322
338
|
}
|
|
323
339
|
|
|
324
340
|
track.setProducer(producer);
|
|
341
|
+
|
|
342
|
+
if (track instanceof VideoTrack) {
|
|
343
|
+
await track.setMaxSpatialLayer(maxSpatialLayer);
|
|
344
|
+
await this.network.socket.request(MEDIASOUP_EVENTS.producerSetMaxSpatialLayer, {
|
|
345
|
+
producerId: track.getProducer()?.id,
|
|
346
|
+
maxSpatialLayer,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (track instanceof AudioTrack) {
|
|
351
|
+
await track.setPriority('high');
|
|
352
|
+
}
|
|
353
|
+
|
|
325
354
|
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
326
|
-
myPeer
|
|
327
|
-
|
|
328
|
-
|
|
355
|
+
if (!myPeer) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const peerTrack = new PeerTrack({
|
|
360
|
+
mediaStreamTrack: track.mediaStreamTrack,
|
|
329
361
|
label: track.getLabel(),
|
|
330
|
-
|
|
362
|
+
engine: this,
|
|
363
|
+
});
|
|
364
|
+
myPeer.tracks.set(peerTrack.label, peerTrack);
|
|
365
|
+
myPeer.observer.safeEmit(PEER_EVENTS.trackStart, peerTrack);
|
|
331
366
|
|
|
332
367
|
this.logger.debug('publish()', { track, encodings, codecOptions });
|
|
333
368
|
});
|
|
@@ -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;
|
package/src/types/common.ts
CHANGED
|
@@ -23,11 +23,26 @@ export type ProduceParams = {
|
|
|
23
23
|
appData: Record<string, unknown>,
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
export type TrackTransformParams = {
|
|
27
|
+
width?: number,
|
|
28
|
+
height?: number,
|
|
29
|
+
};
|
|
30
|
+
|
|
26
31
|
export type ProducerData = {
|
|
27
32
|
id: string,
|
|
28
33
|
kind: MediaKind,
|
|
29
34
|
peerId: string,
|
|
30
35
|
label: TrackLabel,
|
|
36
|
+
encodings: RtpEncodingParameters[],
|
|
37
|
+
trackTransformParams: TrackTransformParams,
|
|
38
|
+
maxSpatialLayer: number,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ConsumerData = {
|
|
42
|
+
producerData: ProducerData,
|
|
43
|
+
peerId: string,
|
|
44
|
+
appId: string,
|
|
45
|
+
channelId: string,
|
|
31
46
|
};
|
|
32
47
|
|
|
33
48
|
export type Role = 'audience' | 'host';
|
|
@@ -210,4 +225,21 @@ export type CreateVideoTrackParams = {
|
|
|
210
225
|
frameRate?: { max?: number, min?: number, ideal?: number };
|
|
211
226
|
},
|
|
212
227
|
encoderConfig: VideoEncoderConfig,
|
|
228
|
+
transformParams: TransformParams,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export type SpatialLayerParams = RtpEncodingParameters & TransformParams;
|
|
232
|
+
|
|
233
|
+
export type ProducerRequestMaxSpatialLayer = {
|
|
234
|
+
producerId: string,
|
|
235
|
+
spatialLayer: number,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export type ProducerSetMaxSpatialLayer = ProducerRequestMaxSpatialLayer & {
|
|
239
|
+
peerId: string,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export type TransformParams = {
|
|
243
|
+
width?: number,
|
|
244
|
+
height?: number,
|
|
213
245
|
};
|
|
@@ -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;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
|
|
2
|
-
import { parseScalabilityMode } from 'mediasoup-client';
|
|
3
|
-
import Consumer from './Consumer';
|
|
4
|
-
import Logger from '../Logger';
|
|
5
|
-
|
|
6
|
-
class VideoConsumer extends Consumer {
|
|
7
|
-
public spatialLayers = 0;
|
|
8
|
-
|
|
9
|
-
public temporalLayers = 0;
|
|
10
|
-
|
|
11
|
-
public currentSpatialLayer = 0;
|
|
12
|
-
|
|
13
|
-
public currentTemporalLayer = 0;
|
|
14
|
-
|
|
15
|
-
private readonly logger: Logger;
|
|
16
|
-
|
|
17
|
-
constructor(consumer: MediasoupConsumer) {
|
|
18
|
-
super(consumer);
|
|
19
|
-
this.logger = new Logger('VideoConsumer');
|
|
20
|
-
this.parseScalabilityMode();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
parseScalabilityMode(): void {
|
|
24
|
-
if (this.rtpParameters.encodings) {
|
|
25
|
-
const { scalabilityMode } = this.rtpParameters.encodings[0];
|
|
26
|
-
const { spatialLayers, temporalLayers } = parseScalabilityMode(scalabilityMode);
|
|
27
|
-
this.spatialLayers = spatialLayers;
|
|
28
|
-
this.temporalLayers = temporalLayers;
|
|
29
|
-
this.logger.debug('parseScalabilityMode()', {
|
|
30
|
-
scalabilityMode,
|
|
31
|
-
spatialLayers,
|
|
32
|
-
temporalLayers,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
setCurrentSpatialLayer(currentSpatialLayer: number): void {
|
|
38
|
-
this.currentSpatialLayer = currentSpatialLayer;
|
|
39
|
-
this.logger.debug('setCurrentSpatialLayer()', { currentSpatialLayer });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
setCurrentTemporalLayer(currentTemporalLayer: number): void {
|
|
43
|
-
this.currentTemporalLayer = currentTemporalLayer;
|
|
44
|
-
this.logger.debug('setCurrentTemporalLayer()', { currentTemporalLayer });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default VideoConsumer;
|