@livedigital/client 1.9.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/engine/Peer.d.ts +16 -35
- package/dist/engine/PeerProducer.d.ts +2 -1
- package/dist/engine/index.d.ts +9 -9
- 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 +8 -10
- 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/system/index.d.ts +1 -18
- package/dist/index.d.ts +9 -23
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/dist/types/common.d.ts +57 -7
- package/package.json +11 -7
- package/src/engine/Peer.ts +132 -337
- package/src/engine/PeerProducer.ts +8 -2
- package/src/engine/index.ts +163 -194
- package/src/engine/media/Consumer.ts +0 -4
- package/src/engine/media/VideoConsumer.ts +1 -18
- package/src/engine/media/index.ts +32 -32
- 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/system/index.ts +5 -167
- package/src/index.ts +21 -74
- package/src/types/common.ts +71 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
|
|
2
|
-
import { ProducerData } from '../types/common';
|
|
2
|
+
import { ProducerData, TrackLabel } from '../types/common';
|
|
3
3
|
|
|
4
4
|
class PeerProducer {
|
|
5
5
|
readonly id: string;
|
|
@@ -8,13 +8,19 @@ class PeerProducer {
|
|
|
8
8
|
|
|
9
9
|
readonly peerId: string;
|
|
10
10
|
|
|
11
|
+
readonly label: TrackLabel;
|
|
12
|
+
|
|
11
13
|
public score = 10;
|
|
12
14
|
|
|
13
15
|
constructor(params: ProducerData) {
|
|
14
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
id, kind, peerId, label,
|
|
18
|
+
} = params;
|
|
19
|
+
|
|
15
20
|
this.id = id;
|
|
16
21
|
this.kind = kind;
|
|
17
22
|
this.peerId = peerId;
|
|
23
|
+
this.label = label;
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
26
|
|
package/src/engine/index.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import { RtpCapabilities
|
|
1
|
+
import { RtpCapabilities } from 'mediasoup-client/lib/RtpParameters';
|
|
2
|
+
import { VIDEO_CONSTRAINS } from 'constants/videoConstrains';
|
|
2
3
|
import {
|
|
3
4
|
PeerResponse,
|
|
4
5
|
JoinChannelParams,
|
|
5
6
|
SocketResponse,
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
CreateCameraVideoTrackOptions,
|
|
8
|
+
CreateMicrophoneAudioTrackOptions,
|
|
9
|
+
CreateScreenVideoTrackOptions,
|
|
10
|
+
CreateScreenAudioTrackOptions,
|
|
11
|
+
Track,
|
|
12
|
+
TrackLabel,
|
|
13
|
+
EndTrackPayload,
|
|
14
|
+
StartTrackPayload,
|
|
8
15
|
} from '../types/common';
|
|
9
|
-
import { WEBCAM_SIMULCAST_ENCODINGS } from '../constants/simulcastEncodings';
|
|
10
16
|
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
11
17
|
import System from './system';
|
|
12
18
|
import Peer from './Peer';
|
|
@@ -84,8 +90,7 @@ class Engine {
|
|
|
84
90
|
|
|
85
91
|
this.network.socket.disconnect();
|
|
86
92
|
this.peersRepository.clear();
|
|
87
|
-
await this.
|
|
88
|
-
await this.unpublishVideo();
|
|
93
|
+
await this.unpublish();
|
|
89
94
|
await this.network.closeSendTransport();
|
|
90
95
|
await this.network.closeReceiveTransport();
|
|
91
96
|
|
|
@@ -154,253 +159,217 @@ class Engine {
|
|
|
154
159
|
this.peersRepository.delete(peerId);
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
public async
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (!this.initialized) {
|
|
165
|
-
throw new Error('First you need to connect');
|
|
166
|
-
}
|
|
162
|
+
public async pause(track: Track): Promise<void> {
|
|
163
|
+
const producer = track.getProducer();
|
|
164
|
+
if (!producer) {
|
|
165
|
+
this.logger.warn('pause()', { message: 'Track is not published', kind: track.kind });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
try {
|
|
170
|
+
await this.pauseRemoteProducer(producer.id);
|
|
171
|
+
producer.pause();
|
|
172
|
+
this.logger.debug('pause()', { kind: track.kind });
|
|
173
|
+
} catch (error) {
|
|
174
|
+
this.logger.error('pause()', { error, kind: track.kind });
|
|
175
|
+
throw new Error('Can`t pause track');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
171
178
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
public async resume(track: Track): Promise<void> {
|
|
180
|
+
const producer = track.getProducer();
|
|
181
|
+
if (!producer) {
|
|
182
|
+
this.logger.warn('resume()', { message: 'Track is not published' });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
175
185
|
|
|
176
|
-
|
|
177
|
-
this.
|
|
186
|
+
try {
|
|
187
|
+
await this.resumeRemoteProducer(producer.id);
|
|
188
|
+
producer.resume();
|
|
189
|
+
this.logger.debug('resume()', { kind: track.kind });
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.logger.error('resume()', { error, kind: track.kind });
|
|
192
|
+
throw new Error('Can`t resume track');
|
|
178
193
|
}
|
|
194
|
+
}
|
|
179
195
|
|
|
196
|
+
async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track | null> {
|
|
180
197
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
198
|
+
const track = await this.media.createUserMediaTrack({
|
|
199
|
+
audio: false,
|
|
200
|
+
video: {
|
|
201
|
+
deviceId: options?.deviceId,
|
|
202
|
+
frameRate: options?.frameRate || 15,
|
|
203
|
+
width: options?.width || VIDEO_CONSTRAINS.hd.width,
|
|
204
|
+
height: options?.height || VIDEO_CONSTRAINS.hd.height,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
185
207
|
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
return;
|
|
208
|
+
track.setLabel(TrackLabel.Camera);
|
|
209
|
+
if (options?.encoderConfig) {
|
|
210
|
+
track.setEncoderConfig(options.encoderConfig);
|
|
190
211
|
}
|
|
191
212
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
213
|
+
this.logger.debug('createCameraVideoTrack()', { options, track });
|
|
214
|
+
return track;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.logger.error('createCameraVideoTrack()', { error, options });
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
197
220
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
disableTrackOnPause: false,
|
|
205
|
-
zeroRtpOnPause: true,
|
|
206
|
-
appData: {
|
|
207
|
-
peerId: this.mySocketId,
|
|
221
|
+
async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track | null> {
|
|
222
|
+
try {
|
|
223
|
+
const track = await this.media.createUserMediaTrack({
|
|
224
|
+
video: false,
|
|
225
|
+
audio: {
|
|
226
|
+
deviceId: options?.deviceId,
|
|
208
227
|
},
|
|
209
228
|
});
|
|
210
229
|
|
|
211
|
-
|
|
212
|
-
|
|
230
|
+
track.setLabel(TrackLabel.Microphone);
|
|
231
|
+
if (options?.encoderConfig) {
|
|
232
|
+
track.setEncoderConfig(options.encoderConfig);
|
|
213
233
|
}
|
|
214
234
|
|
|
215
|
-
this.
|
|
216
|
-
|
|
217
|
-
myPeer?.observer.safeEmit(PEER_EVENTS.trackStart, { kind: videoProducer.kind, track: trackToProduce });
|
|
218
|
-
this.logger.debug('publishVideo()', { codec });
|
|
235
|
+
this.logger.debug('createMicrophoneAudioTrack()', { options, track });
|
|
236
|
+
return track;
|
|
219
237
|
} catch (error) {
|
|
220
|
-
this.logger.error('
|
|
221
|
-
|
|
222
|
-
} finally {
|
|
223
|
-
this.system.setIsEnableVideoDevicesLock(false);
|
|
238
|
+
this.logger.error('createMicrophoneAudioTrack()', { error, options });
|
|
239
|
+
return null;
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
242
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
243
|
+
async createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track | null> {
|
|
244
|
+
try {
|
|
245
|
+
const track = await this.media.createDisplayMediaTrack({
|
|
246
|
+
audio: false,
|
|
247
|
+
video: {
|
|
248
|
+
frameRate: options?.frameRate || 30,
|
|
249
|
+
width: options?.width || VIDEO_CONSTRAINS.fullhd.width,
|
|
250
|
+
height: options?.height || VIDEO_CONSTRAINS.fullhd.height,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
237
253
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
254
|
+
track.mediaStreamTrack.addEventListener('ended', () => {
|
|
255
|
+
this.unpublish(track);
|
|
256
|
+
});
|
|
241
257
|
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
track.setLabel(TrackLabel.ScreenVideo);
|
|
259
|
+
if (options?.encoderConfig) {
|
|
260
|
+
track.setEncoderConfig(options.encoderConfig);
|
|
244
261
|
}
|
|
245
262
|
|
|
246
|
-
|
|
247
|
-
|
|
263
|
+
this.logger.debug('createScreenVideoTrack()', { options, track });
|
|
264
|
+
return track;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
this.logger.error('createScreenVideoTrack()', { error, options });
|
|
267
|
+
return null;
|
|
248
268
|
}
|
|
269
|
+
}
|
|
249
270
|
|
|
250
|
-
|
|
251
|
-
opusFec: true,
|
|
252
|
-
};
|
|
253
|
-
|
|
271
|
+
async createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track | null> {
|
|
254
272
|
try {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
273
|
+
const track = await this.media.createDisplayMediaTrack({
|
|
274
|
+
video: false,
|
|
275
|
+
audio: true,
|
|
276
|
+
});
|
|
259
277
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
278
|
+
track.mediaStreamTrack.addEventListener('ended', () => {
|
|
279
|
+
this.unpublish(track);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
track.setLabel(TrackLabel.ScreenAudio);
|
|
283
|
+
if (options?.encoderConfig) {
|
|
284
|
+
track.setEncoderConfig(options.encoderConfig);
|
|
264
285
|
}
|
|
265
286
|
|
|
266
|
-
|
|
267
|
-
|
|
287
|
+
this.logger.debug('createScreenAudioTrack()', { options, track });
|
|
288
|
+
return track;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
this.logger.error('createScreenAudioTrack()', { error, options });
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public async publish(tracks: Track | Track[]): Promise<void> {
|
|
296
|
+
const tracksToPublish = Array.isArray(tracks) ? tracks : [tracks];
|
|
297
|
+
const actions = tracksToPublish.map(async (track) => {
|
|
298
|
+
const encodings = 'getEncodings' in track
|
|
299
|
+
? track.getEncodings()
|
|
300
|
+
: undefined;
|
|
301
|
+
|
|
302
|
+
const codecOptions = 'getCodecOptions' in track
|
|
303
|
+
? track.getCodecOptions()
|
|
304
|
+
: undefined;
|
|
305
|
+
|
|
306
|
+
const producer = await this.network.sendTransport?.produce({
|
|
307
|
+
track: track.mediaStreamTrack,
|
|
308
|
+
encodings,
|
|
268
309
|
codecOptions,
|
|
310
|
+
codec: this.media.getTrackCodec(track),
|
|
269
311
|
stopTracks: true,
|
|
270
|
-
disableTrackOnPause:
|
|
312
|
+
disableTrackOnPause: true,
|
|
271
313
|
zeroRtpOnPause: true,
|
|
272
314
|
appData: {
|
|
273
315
|
peerId: this.mySocketId,
|
|
316
|
+
label: track.getLabel(),
|
|
274
317
|
},
|
|
275
318
|
});
|
|
276
319
|
|
|
277
|
-
if (!
|
|
320
|
+
if (!producer) {
|
|
278
321
|
return;
|
|
279
322
|
}
|
|
280
323
|
|
|
281
|
-
|
|
324
|
+
track.setProducer(producer);
|
|
282
325
|
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
283
|
-
myPeer?.observer.safeEmit(PEER_EVENTS.trackStart, {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
} finally {
|
|
289
|
-
this.system.setIsEnableAudioDevicesLock(false);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
326
|
+
myPeer?.observer.safeEmit(PEER_EVENTS.trackStart, {
|
|
327
|
+
producerId: producer.id,
|
|
328
|
+
track: track.mediaStreamTrack,
|
|
329
|
+
label: track.getLabel(),
|
|
330
|
+
} as StartTrackPayload);
|
|
292
331
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.logger.warn('unpublishVideo()', { message: 'No published video' });
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
332
|
+
this.logger.debug('publish()', { track });
|
|
333
|
+
});
|
|
298
334
|
|
|
299
335
|
try {
|
|
300
|
-
|
|
301
|
-
await this.network.closeRemoteProducer(this.media.videoProducer.id);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
this.media.closeVideoProducer();
|
|
305
|
-
this.system.stopVideoStream();
|
|
306
|
-
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
307
|
-
if (myPeer) {
|
|
308
|
-
myPeer.observer.safeEmit(PEER_EVENTS.trackEnd, { kind: 'video' });
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
this.logger.debug('unpublishVideo()');
|
|
336
|
+
await Promise.all(actions);
|
|
312
337
|
} catch (error) {
|
|
313
|
-
this.logger.error('
|
|
314
|
-
throw new Error('Error unpublish video');
|
|
338
|
+
this.logger.error('publish()', { error });
|
|
315
339
|
}
|
|
316
340
|
}
|
|
317
341
|
|
|
318
|
-
public async
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
342
|
+
public async unpublish(tracks?: Track | Track[]): Promise<void> {
|
|
343
|
+
let tracksToUnpublish = [];
|
|
344
|
+
if (!tracks) {
|
|
345
|
+
tracksToUnpublish = this.media.getAllTracks();
|
|
346
|
+
} else {
|
|
347
|
+
tracksToUnpublish = Array.isArray(tracks) ? tracks : [tracks];
|
|
322
348
|
}
|
|
323
349
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
350
|
+
const actions = tracksToUnpublish.map(async (track) => {
|
|
351
|
+
const producer = track.getProducer();
|
|
352
|
+
if (producer && this.network.socket.connection?.connected) {
|
|
353
|
+
await this.network.closeRemoteProducer(producer.id);
|
|
327
354
|
}
|
|
328
355
|
|
|
329
|
-
this.media.closeAudioProducer();
|
|
330
|
-
this.system.stopAudioStream();
|
|
331
356
|
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
332
357
|
if (myPeer) {
|
|
333
|
-
myPeer.observer.safeEmit(PEER_EVENTS.trackEnd, {
|
|
358
|
+
myPeer.observer.safeEmit(PEER_EVENTS.trackEnd, {
|
|
359
|
+
producerId: track.getProducer()?.id,
|
|
360
|
+
kind: track.getProducer()?.kind,
|
|
361
|
+
label: track.getLabel(),
|
|
362
|
+
} as EndTrackPayload);
|
|
334
363
|
}
|
|
335
364
|
|
|
336
|
-
this.
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
throw new Error('Error unpublish audio');
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
public async pauseVideo(): Promise<void> {
|
|
344
|
-
if (!this.media.videoProducer) {
|
|
345
|
-
this.logger.warn('pauseVideo()', { message: 'No published video' });
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
await this.pauseRemoteProducer(this.media.videoProducer.id);
|
|
351
|
-
this.media.videoProducer.pause();
|
|
352
|
-
this.logger.debug('pauseVideo()');
|
|
353
|
-
} catch (error) {
|
|
354
|
-
this.logger.error('pauseVideo()', { error });
|
|
355
|
-
throw new Error('Can`t pause video');
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
public async resumeVideo(): Promise<void> {
|
|
360
|
-
if (!this.media.videoProducer) {
|
|
361
|
-
this.logger.warn('resumeVideo()', { message: 'No published video' });
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
try {
|
|
366
|
-
await this.resumeRemoteProducer(this.media.videoProducer.id);
|
|
367
|
-
this.media.videoProducer.resume();
|
|
368
|
-
this.logger.debug('resumeVideo()');
|
|
369
|
-
} catch (error) {
|
|
370
|
-
this.logger.error('resumeVideo()', { error });
|
|
371
|
-
throw new Error('Can`t resume video');
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
public async pauseAudio(): Promise<void> {
|
|
376
|
-
if (!this.media.audioProducer) {
|
|
377
|
-
this.logger.warn('pauseAudio()', { message: 'No published audio' });
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
try {
|
|
382
|
-
await this.pauseRemoteProducer(this.media.audioProducer.id);
|
|
383
|
-
this.media.audioProducer.pause();
|
|
384
|
-
this.logger.debug('pauseAudio()');
|
|
385
|
-
} catch (error) {
|
|
386
|
-
this.logger.error('pauseAudio()', { error });
|
|
387
|
-
throw new Error('Can`t pause audio');
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
public async resumeAudio(): Promise<void> {
|
|
392
|
-
if (!this.media.audioProducer) {
|
|
393
|
-
this.logger.warn('resumeAudio()', { message: 'No published audio' });
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
365
|
+
await this.media.deleteTrack(track);
|
|
366
|
+
this.logger.debug('unpublish()', { track });
|
|
367
|
+
});
|
|
396
368
|
|
|
397
369
|
try {
|
|
398
|
-
await
|
|
399
|
-
this.media.audioProducer.resume();
|
|
400
|
-
this.logger.debug('resumeAudio()');
|
|
370
|
+
await Promise.all(actions);
|
|
401
371
|
} catch (error) {
|
|
402
|
-
this.logger.error('
|
|
403
|
-
throw new Error('Can`t resume audio');
|
|
372
|
+
this.logger.error('unpublish()', { error });
|
|
404
373
|
}
|
|
405
374
|
}
|
|
406
375
|
}
|
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import Consumer from './Consumer';
|
|
2
1
|
import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
|
|
3
2
|
import { parseScalabilityMode } from 'mediasoup-client';
|
|
3
|
+
import Consumer from './Consumer';
|
|
4
4
|
|
|
5
5
|
class VideoConsumer extends Consumer {
|
|
6
6
|
public spatialLayers = 0;
|
|
7
7
|
|
|
8
8
|
public temporalLayers = 0;
|
|
9
9
|
|
|
10
|
-
private currentSpatialLayer = 0;
|
|
11
|
-
|
|
12
|
-
private currentTemporalLayer = 0;
|
|
13
|
-
|
|
14
|
-
currentPreferredLayers: { spatialLayer?: number, temporalLayer?: number } = {
|
|
15
|
-
spatialLayer: undefined,
|
|
16
|
-
temporalLayer: undefined,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
10
|
constructor(consumer: MediasoupConsumer) {
|
|
20
11
|
super(consumer);
|
|
21
12
|
this.parseScalabilityMode();
|
|
@@ -29,14 +20,6 @@ class VideoConsumer extends Consumer {
|
|
|
29
20
|
this.temporalLayers = temporalLayers;
|
|
30
21
|
}
|
|
31
22
|
}
|
|
32
|
-
|
|
33
|
-
setCurrentSpatialLayer(currentSpatialLayer: number): void {
|
|
34
|
-
this.currentSpatialLayer = currentSpatialLayer;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
setCurrentTemporalLayer(currentTemporalLayer: number): void {
|
|
38
|
-
this.currentTemporalLayer = currentTemporalLayer;
|
|
39
|
-
}
|
|
40
23
|
}
|
|
41
24
|
|
|
42
25
|
export default VideoConsumer;
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import { Device } from 'mediasoup-client';
|
|
2
|
-
import { Producer } from 'mediasoup-client/lib/Producer';
|
|
3
2
|
import { RtpCapabilities, RtpCodecCapability } from 'mediasoup-client/lib/RtpParameters';
|
|
3
|
+
import VideoTrack from './tracks/VideoTrack';
|
|
4
|
+
import AudioTrack from './tracks/AudioTrack';
|
|
5
|
+
import { Track } from '../../types/common';
|
|
4
6
|
|
|
5
7
|
class Media {
|
|
6
8
|
public mediasoupDevice: Device;
|
|
7
9
|
|
|
8
|
-
public audioProducer?: Producer;
|
|
9
|
-
|
|
10
|
-
public videoProducer?: Producer;
|
|
11
|
-
|
|
12
10
|
public isDeviceLoaded = false;
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
public isAudioDisabled = false;
|
|
12
|
+
private tracks: Map<string, Track> = new Map();
|
|
17
13
|
|
|
18
14
|
constructor() {
|
|
19
15
|
this.mediasoupDevice = new Device();
|
|
@@ -26,46 +22,50 @@ class Media {
|
|
|
26
22
|
}
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
getTrackCodec(track: Track): RtpCodecCapability | undefined {
|
|
26
|
+
if (!(track instanceof VideoTrack)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
30
|
if (!this.mediasoupDevice.rtpCapabilities?.codecs) {
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const preferredCodec = 'h264';
|
|
35
34
|
return this.mediasoupDevice
|
|
36
35
|
.rtpCapabilities
|
|
37
36
|
.codecs
|
|
38
|
-
.find((c) => c.mimeType.toLowerCase() === `video/${
|
|
37
|
+
.find((c) => c.mimeType.toLowerCase() === `video/${track.getPreferredCodec()}`);
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
private createTrack(stream: MediaStream): Track {
|
|
41
|
+
const mediaStreamTrack = stream.getTracks()[0];
|
|
42
|
+
|
|
43
|
+
const track = mediaStreamTrack.kind === 'audio'
|
|
44
|
+
? new AudioTrack(mediaStreamTrack)
|
|
45
|
+
: new VideoTrack(mediaStreamTrack);
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.isAudioDisabled = false;
|
|
47
|
+
this.tracks.set(track.id, track);
|
|
48
|
+
return track;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
async createUserMediaTrack(constraints: MediaStreamConstraints): Promise<Track> {
|
|
52
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
53
|
+
return this.createTrack(stream);
|
|
54
|
+
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.
|
|
56
|
+
async createDisplayMediaTrack(constraints: MediaStreamConstraints): Promise<Track> {
|
|
57
|
+
const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
|
|
58
|
+
return this.createTrack(stream);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
async deleteTrack(track: Track) {
|
|
62
|
+
track.closeProducer();
|
|
63
|
+
track.mediaStreamTrack.stop();
|
|
64
|
+
this.tracks.delete(track.id);
|
|
65
|
+
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
this.
|
|
68
|
-
this.isAudioDisabled = true;
|
|
67
|
+
getAllTracks(): Track[] {
|
|
68
|
+
return Array.from(this.tracks.values());
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
2
|
+
import { AudioEncoderConfig } from 'types/common';
|
|
3
|
+
import BaseTrack from './BaseTrack';
|
|
4
|
+
import TrackWithCodecOptions from './TrackWithCodecOptions';
|
|
5
|
+
|
|
6
|
+
class AudioTrack extends BaseTrack implements TrackWithCodecOptions {
|
|
7
|
+
getEncoderConfig(): AudioEncoderConfig {
|
|
8
|
+
return this.encoderConfig;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getCodecOptions(): ProducerCodecOptions {
|
|
12
|
+
return {
|
|
13
|
+
opusFec: this.getEncoderConfig().enableFec || true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default AudioTrack;
|