@livedigital/client 2.26.0 → 2.27.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.
@@ -304,6 +304,10 @@ export declare type UpdatePeerAppDataPayload = {
304
304
  peerId: string;
305
305
  appData: Record<string, unknown>;
306
306
  };
307
+ export declare type CreateTracksPayload = {
308
+ mediaStreamTracks: MediaStreamTrack[];
309
+ constraints: MediaStreamConstraints;
310
+ };
307
311
  export declare type TrackPublishParams = {
308
312
  keyFrameRequestDelay?: number;
309
313
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.26.0",
5
+ "version": "2.27.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -48,7 +48,7 @@
48
48
  "qs": "^6.9.6",
49
49
  "serialize-error": "^7.0.1",
50
50
  "socket.io-client": "^4.3.0",
51
- "webrtc-issue-detector": "^1.3.0"
51
+ "webrtc-issue-detector": "^1.3.1"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@rollup/plugin-commonjs": "^19.0.0",
@@ -457,20 +457,7 @@ class Engine {
457
457
 
458
458
  private watchClientEvents(): void {
459
459
  this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackProduced, (track: Track) => {
460
- const myPeer = this.peersRepository.get(<string> this.mySocketId);
461
- if (!myPeer) {
462
- return;
463
- }
464
-
465
- const peerTrack = new PeerTrack({
466
- mediaStreamTrack: track.mediaStreamTrack,
467
- label: track.getLabel(),
468
- engine: this,
469
- isPaused: track.isPaused,
470
- peerEventEmitter: myPeer.observer,
471
- });
472
-
473
- myPeer.tracks.set(peerTrack.label, peerTrack);
460
+ this.createSelfPeerTrack(track);
474
461
  });
475
462
 
476
463
  this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackUnproduced, (track: Track) => {
@@ -481,14 +468,30 @@ class Engine {
481
468
 
482
469
  this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackPaused, (track: Track) => {
483
470
  const peerTrack = this.myPeer?.tracks.get(track.getLabel());
484
- peerTrack?.pause();
471
+ peerTrack?.close();
485
472
  });
486
473
 
487
474
  this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackResumed, (track: Track) => {
488
- const peerTrack = this.myPeer?.tracks.get(track.getLabel());
489
- peerTrack?.resume();
475
+ this.createSelfPeerTrack(track);
490
476
  });
491
477
  }
478
+
479
+ private createSelfPeerTrack(track: Track): void {
480
+ const myPeer = this.peersRepository.get(<string> this.mySocketId);
481
+ if (!myPeer) {
482
+ return;
483
+ }
484
+
485
+ const peerTrack = new PeerTrack({
486
+ mediaStreamTrack: track.mediaStreamTrack,
487
+ label: track.getLabel(),
488
+ engine: this,
489
+ isPaused: track.isPaused,
490
+ peerEventEmitter: myPeer.observer,
491
+ });
492
+
493
+ myPeer.tracks.set(peerTrack.label, peerTrack);
494
+ }
492
495
  }
493
496
 
494
497
  export default Engine;
@@ -3,7 +3,11 @@ import { RtpCapabilities, RtpCodecCapability } from 'mediasoup-client/lib/RtpPar
3
3
  import VideoTrack from './tracks/VideoTrack';
4
4
  import AudioTrack from './tracks/AudioTrack';
5
5
  import {
6
- CreateScreenVideoTrackOptions, CreateVideoTrackParams, LogLevel, Track,
6
+ CreateScreenVideoTrackOptions,
7
+ CreateTracksPayload,
8
+ CreateVideoTrackParams,
9
+ LogLevel,
10
+ Track,
7
11
  } from '../../types/common';
8
12
  import Logger from '../Logger';
9
13
  import { VIDEO_CONSTRAINS } from '../../constants/videoConstrains';
@@ -11,6 +15,7 @@ import { SCREEN_SHARING_SIMULCAST_ENCODINGS, WEBCAM_SIMULCAST_ENCODINGS } from '
11
15
  import { CreateMediaParams } from '../../types/engine';
12
16
  import Engine from '../index';
13
17
  import EnhancedEventEmitter from '../../EnhancedEventEmitter';
18
+ import MediaStreamTrackManager from './tracks/MediaStreamTrackManager';
14
19
 
15
20
  class Media {
16
21
  public isDeviceLoaded = false;
@@ -27,6 +32,8 @@ class Media {
27
32
 
28
33
  readonly #clientEventEmitter: EnhancedEventEmitter;
29
34
 
35
+ readonly #mediaStreamTrackManager: MediaStreamTrackManager;
36
+
30
37
  constructor(params: CreateMediaParams) {
31
38
  this.#logLevel = params.logLevel;
32
39
  this.#logger = new Logger({
@@ -35,6 +42,7 @@ class Media {
35
42
  });
36
43
  this.#engine = params.engine;
37
44
  this.#clientEventEmitter = params.clientEventEmitter;
45
+ this.#mediaStreamTrackManager = new MediaStreamTrackManager(params.logLevel);
38
46
  }
39
47
 
40
48
  get mediasoupDevice(): Device {
@@ -62,20 +70,20 @@ class Media {
62
70
  return codecs.find((c) => c.mimeType.toLowerCase() === `${track.kind}/${track.getPreferredCodec()}`);
63
71
  }
64
72
 
65
- private createTracks(stream: MediaStream): Track[] {
66
- const mediaStreamTracks = stream.getTracks();
73
+ private createTracks({ constraints, mediaStreamTracks }: CreateTracksPayload): Track[] {
67
74
  return mediaStreamTracks.map((mediaStreamTrack) => {
68
75
  const params = {
69
76
  mediaStreamTrack,
70
77
  logLevel: this.#logLevel,
71
78
  engine: this.#engine,
72
79
  clientEventEmitter: this.#clientEventEmitter,
80
+ constraints,
81
+ mediaStreamTrackManager: this.#mediaStreamTrackManager,
73
82
  };
74
83
 
75
84
  const track = mediaStreamTrack.kind === 'audio' ? new AudioTrack(params) : new VideoTrack(params);
76
85
  this.tracks.set(track.id, track);
77
86
  this.#logger.debug('createTrack() track created', {
78
- streamId: stream.id,
79
87
  trackId: track.id,
80
88
  kind: track.kind,
81
89
  });
@@ -85,14 +93,13 @@ class Media {
85
93
  }
86
94
 
87
95
  async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<Track[]> {
88
- const stream = await navigator.mediaDevices.getUserMedia(constraints);
89
- this.#logger.debug('createUserMediaTrack() stream created', { streamId: stream.id });
90
- return this.createTracks(stream);
96
+ const mediaStreamTracks = await this.#mediaStreamTrackManager.createUserMediaTracks(constraints);
97
+ return this.createTracks({ constraints, mediaStreamTracks });
91
98
  }
92
99
 
93
100
  async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<Track[]> {
94
- const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
95
- return this.createTracks(stream);
101
+ const mediaStreamTracks = await this.#mediaStreamTrackManager.createDisplayMediaTracks(constraints);
102
+ return this.createTracks({ constraints, mediaStreamTracks });
96
103
  }
97
104
 
98
105
  deleteTrack(track: Track) {
@@ -5,27 +5,30 @@ import {
5
5
  EncoderConfig,
6
6
  LogLevel,
7
7
  SocketResponse,
8
- TrackLabel, TrackOutboundStats,
8
+ TrackLabel,
9
+ TrackOutboundStats,
9
10
  TrackProduceParams,
10
11
  } from '../../../types/common';
11
12
  import Logger from '../../Logger';
12
13
  import Engine from '../../index';
13
14
  import PeerConsumer from '../../PeerConsumer';
14
15
  import { PRODUCER_CHECK_STATE_TIMEOUT } from '../../../constants/common';
15
- import Timeout = NodeJS.Timeout;
16
- import { MEDIASOUP_EVENTS, INTERNAL_CLIENT_EVENTS, CLIENT_EVENTS } from '../../../constants/events';
16
+ import { CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, MEDIASOUP_EVENTS } from '../../../constants/events';
17
17
  import EnhancedEventEmitter from '../../../EnhancedEventEmitter';
18
18
  import filterStatsCodecs from '../../../helpers/filterStatsCodecs';
19
+ import MediaStreamTrackManager from './MediaStreamTrackManager';
19
20
 
20
21
  export type BaseTrackConstructorParams = {
21
22
  mediaStreamTrack: MediaStreamTrack,
22
23
  logLevel: LogLevel,
23
24
  engine: Engine,
24
25
  clientEventEmitter: EnhancedEventEmitter,
26
+ constraints: MediaStreamConstraints;
27
+ mediaStreamTrackManager: MediaStreamTrackManager;
25
28
  };
26
29
 
27
30
  class BaseTrack {
28
- readonly #mediaStreamTrack: MediaStreamTrack;
31
+ #mediaStreamTrack: MediaStreamTrack;
29
32
 
30
33
  protected encoderConfig: EncoderConfig = {};
31
34
 
@@ -39,7 +42,7 @@ class BaseTrack {
39
42
 
40
43
  #selfConsumer?: PeerConsumer;
41
44
 
42
- #checkStateTimeout?: Timeout;
45
+ #checkStateTimeout?: NodeJS.Timeout;
43
46
 
44
47
  #producerRestarted = false;
45
48
 
@@ -47,13 +50,19 @@ class BaseTrack {
47
50
 
48
51
  #closed = false;
49
52
 
53
+ readonly #constraints: MediaStreamConstraints;
54
+
55
+ readonly #mediaStreamTrackManager: MediaStreamTrackManager;
56
+
50
57
  constructor(params: BaseTrackConstructorParams) {
51
58
  const {
52
- mediaStreamTrack, logLevel, engine, clientEventEmitter,
59
+ mediaStreamTrack, logLevel, engine, clientEventEmitter, constraints, mediaStreamTrackManager,
53
60
  } = params;
54
61
  this.#mediaStreamTrack = mediaStreamTrack;
55
62
  this.#engine = engine;
56
63
  this.#clientEventEmitter = clientEventEmitter;
64
+ this.#constraints = constraints;
65
+ this.#mediaStreamTrackManager = mediaStreamTrackManager;
57
66
  this.logger = new Logger({
58
67
  namespace: 'Track',
59
68
  logLevel,
@@ -366,6 +375,7 @@ class BaseTrack {
366
375
  await this.cancelProducerCheckState();
367
376
  await this.pauseRemoteProducer(this.producer.id);
368
377
  this.producer.pause();
378
+ this.#mediaStreamTrack.stop();
369
379
  this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackPaused, this);
370
380
  this.logger.debug('pause()', { track: this });
371
381
  } catch (error) {
@@ -381,6 +391,12 @@ class BaseTrack {
381
391
  }
382
392
 
383
393
  try {
394
+ const track = await this.#mediaStreamTrackManager.createMediaStreamTrack({
395
+ label: this.label,
396
+ constraints: this.#constraints,
397
+ });
398
+ this.#mediaStreamTrack = track;
399
+ await this.producer.replaceTrack({ track });
384
400
  await this.resumeRemoteProducer(this.producer.id);
385
401
  this.producer.resume();
386
402
  this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackResumed, this);
@@ -0,0 +1,46 @@
1
+ import { LogLevel, TrackLabel } from '../../../types/common';
2
+ import Logger from '../../Logger';
3
+
4
+ export type GetMediaStreamTrackParams = {
5
+ label: TrackLabel;
6
+ constraints: MediaStreamConstraints;
7
+ };
8
+
9
+ class MediaStreamTrackManager {
10
+ readonly #logger: Logger;
11
+
12
+ constructor(logLevel: LogLevel) {
13
+ this.#logger = new Logger({
14
+ namespace: 'MediaStreamTrackManager',
15
+ logLevel,
16
+ });
17
+ }
18
+
19
+ async createMediaStreamTrack({ label, constraints }: GetMediaStreamTrackParams): Promise<MediaStreamTrack> {
20
+ if (MediaStreamTrackManager.isDisplayMedia(label)) {
21
+ const [track] = await this.createDisplayMediaTracks(constraints);
22
+ return track;
23
+ }
24
+
25
+ const [track] = await this.createUserMediaTracks(constraints);
26
+ return track;
27
+ }
28
+
29
+ private static isDisplayMedia(label: TrackLabel): boolean {
30
+ return [TrackLabel.ScreenVideo, TrackLabel.ScreenAudio].includes(label);
31
+ }
32
+
33
+ async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<MediaStreamTrack[]> {
34
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
35
+ this.#logger.debug('createUserMediaTracks() stream created', { streamId: stream.id });
36
+ return stream.getTracks();
37
+ }
38
+
39
+ async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<MediaStreamTrack[]> {
40
+ const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
41
+ this.#logger.debug('getDisplayMediaTracks() stream created', { streamId: stream.id });
42
+ return stream.getTracks();
43
+ }
44
+ }
45
+
46
+ export default MediaStreamTrackManager;
@@ -350,6 +350,11 @@ export type UpdatePeerAppDataPayload = {
350
350
  appData: Record<string, unknown>,
351
351
  };
352
352
 
353
+ export type CreateTracksPayload = {
354
+ mediaStreamTracks: MediaStreamTrack[],
355
+ constraints: MediaStreamConstraints,
356
+ };
357
+
353
358
  export type TrackPublishParams = {
354
359
  keyFrameRequestDelay?: number;
355
360
  };