@livedigital/client 2.27.0 → 2.28.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.
@@ -133,6 +133,7 @@ export declare type AudioEncoderConfig = EncoderConfig & {
133
133
  enableFec?: boolean;
134
134
  };
135
135
  export declare type CreateTrackOptions = {
136
+ stopTrackOnPause?: boolean;
136
137
  encoderConfig?: EncoderConfig;
137
138
  deviceId?: string;
138
139
  };
@@ -154,6 +155,7 @@ export declare type CreateScreenAudioTrackOptions = CreateMicrophoneAudioTrackOp
154
155
  encoderConfig?: AudioEncoderConfig;
155
156
  };
156
157
  export declare type CreateScreenMediaOptions = {
158
+ stopTrackOnPause?: boolean;
157
159
  video: CreateScreenVideoTrackOptions;
158
160
  audio: CreateScreenAudioTrackOptions;
159
161
  };
@@ -7,7 +7,7 @@ import Engine from '../engine';
7
7
  import ChannelEventHandler from '../engine/handlers/ChannelEventHandler';
8
8
  import MediaSoupEventHandler from '../engine/handlers/MediaSoupEventHandler';
9
9
  import { LoadBalancerApiClientParams } from '../engine/network/LoadBalancerClient';
10
- import { Logger, LogLevel } from './common';
10
+ import { Logger, LogLevel, LogMessageHandler } from './common';
11
11
  export declare type IssuesHandler = (issues: IssueDetectorResult) => void;
12
12
  export declare type NetworkScoresUpdatedHandler = (networks: NetworkScores) => void;
13
13
  export interface CreateIssueDetectorParams {
@@ -20,20 +20,31 @@ export interface CreateMediaParams {
20
20
  logLevel: LogLevel;
21
21
  engine: Engine;
22
22
  clientEventEmitter: EnhancedEventEmitter;
23
+ onLogMessage?: LogMessageHandler;
23
24
  }
24
25
  export interface CreateNetworkParams {
25
26
  logLevel: LogLevel;
26
27
  loadbalancer: LoadBalancerApiClientParams;
28
+ onLogMessage?: LogMessageHandler;
27
29
  }
28
30
  export interface CreateSystemParams {
29
31
  logLevel: LogLevel;
30
32
  clientEventEmitter: EnhancedEventEmitter;
33
+ onLogMessage?: LogMessageHandler;
34
+ }
35
+ export interface CreateMediaSoupEventHandlerParams {
36
+ engine: Engine;
37
+ onLogMessage?: LogMessageHandler;
38
+ }
39
+ export interface CreateChannelEventHandlerParams {
40
+ engine: Engine;
41
+ onLogMessage?: LogMessageHandler;
31
42
  }
32
43
  export interface EngineDependenciesFactory {
33
44
  createSystem: (params: CreateSystemParams) => System;
34
45
  createMedia: (params: CreateMediaParams) => Media;
35
46
  createNetwork: (params: CreateNetworkParams) => Network;
36
- createChannelEventHandler: (engine: Engine) => ChannelEventHandler;
37
- createMediaSoupEventHandler: (engine: Engine) => MediaSoupEventHandler;
47
+ createChannelEventHandler: (params: CreateChannelEventHandlerParams) => ChannelEventHandler;
48
+ createMediaSoupEventHandler: (params: CreateMediaSoupEventHandlerParams) => MediaSoupEventHandler;
38
49
  createIssueDetector: (params: CreateIssueDetectorParams) => WebRTCIssueDetector;
39
50
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.27.0",
5
+ "version": "2.28.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -21,6 +21,8 @@ export const INTERNAL_CLIENT_EVENTS = {
21
21
  trackUnproduced: 'track-unproduced',
22
22
  trackPaused: 'track-paused',
23
23
  trackResumed: 'track-resumed',
24
+ trackClosed: 'track-closed',
25
+ trackReopened: 'track-reopened',
24
26
  };
25
27
 
26
28
  export const PEER_EVENTS = {
@@ -17,11 +17,11 @@ import Network from './network';
17
17
  import System from './system';
18
18
  import Media from './media';
19
19
  import ChannelEventHandler from './handlers/ChannelEventHandler';
20
- import Engine from './index';
21
20
  import MediaSoupEventHandler from './handlers/MediaSoupEventHandler';
22
21
  import {
23
22
  CreateIssueDetectorParams,
24
23
  CreateMediaParams,
24
+ CreateMediaSoupEventHandlerParams,
25
25
  CreateNetworkParams,
26
26
  CreateSystemParams,
27
27
  EngineDependenciesFactory,
@@ -40,9 +40,10 @@ class DefaultEngineDependenciesFactory implements EngineDependenciesFactory {
40
40
  }
41
41
 
42
42
  createNetwork(params: CreateNetworkParams): Network {
43
- const { loadbalancer, logLevel } = params;
43
+ const { loadbalancer, logLevel, onLogMessage } = params;
44
44
  const socketClient = new SocketIO({
45
45
  logLevel,
46
+ onLogMessage,
46
47
  });
47
48
 
48
49
  const loadBalancerApiClient = new LoadBalancerApiClient(loadbalancer);
@@ -50,15 +51,16 @@ class DefaultEngineDependenciesFactory implements EngineDependenciesFactory {
50
51
  logLevel,
51
52
  socketClient,
52
53
  loadBalancerApiClient,
54
+ onLogMessage,
53
55
  });
54
56
  }
55
57
 
56
- createChannelEventHandler(engine: Engine): ChannelEventHandler {
57
- return new ChannelEventHandler(engine);
58
+ createChannelEventHandler(params: CreateMediaSoupEventHandlerParams): ChannelEventHandler {
59
+ return new ChannelEventHandler(params);
58
60
  }
59
61
 
60
- createMediaSoupEventHandler(engine: Engine): MediaSoupEventHandler {
61
- return new MediaSoupEventHandler(engine);
62
+ createMediaSoupEventHandler(params: CreateMediaSoupEventHandlerParams): MediaSoupEventHandler {
63
+ return new MediaSoupEventHandler(params);
62
64
  }
63
65
 
64
66
  createIssueDetector(params: CreateIssueDetectorParams): WebRTCIssueDetector {
@@ -1,4 +1,9 @@
1
- import { ChannelEvent, PeerResponse, UpdatePeerAppDataPayload } from '../../types/common';
1
+ import {
2
+ ChannelEvent,
3
+ LogMessageHandler,
4
+ PeerResponse,
5
+ UpdatePeerAppDataPayload,
6
+ } from '../../types/common';
2
7
  import Engine from '../index';
3
8
  import { CHANNEL_EVENTS, CLIENT_EVENTS } from '../../constants/events';
4
9
  import Logger from '../Logger';
@@ -8,11 +13,13 @@ class ChannelEventHandler {
8
13
 
9
14
  private readonly logger: Logger;
10
15
 
11
- constructor(engine: Engine) {
16
+ constructor(params: { engine: Engine, onLogMessage?: LogMessageHandler }) {
17
+ const { engine, onLogMessage } = params;
12
18
  this.engine = engine;
13
19
  this.logger = new Logger({
14
20
  logLevel: engine.logLevel,
15
21
  namespace: 'ChannelEvents',
22
+ onLogMessage,
16
23
  });
17
24
  }
18
25
 
@@ -2,6 +2,7 @@ import { AwaitQueue } from 'awaitqueue';
2
2
  import {
3
3
  ChangePreferredLayersParams,
4
4
  ConsumerScoreChangedPayload,
5
+ LogMessageHandler,
5
6
  ProducerData,
6
7
  ProducerRequestMaxSpatialLayer,
7
8
  ProducerScoreChangedPayload,
@@ -20,11 +21,13 @@ class MediaSoupEventHandler {
20
21
 
21
22
  private readonly queue = new AwaitQueue();
22
23
 
23
- constructor(engine: Engine) {
24
+ constructor(params: { engine: Engine, onLogMessage?: LogMessageHandler }) {
25
+ const { engine, onLogMessage } = params;
24
26
  this.engine = engine;
25
27
  this.logger = new Logger({
26
28
  logLevel: engine.logLevel,
27
29
  namespace: 'MediasoupEvents',
30
+ onLogMessage,
28
31
  });
29
32
  }
30
33
 
@@ -87,20 +87,30 @@ class Engine {
87
87
  this.system = dependenciesFactory.createSystem({
88
88
  logLevel: this.logLevel,
89
89
  clientEventEmitter,
90
+ onLogMessage: params.onLogMessage,
90
91
  });
91
92
  this.media = dependenciesFactory.createMedia({
92
93
  logLevel: this.logLevel,
93
94
  engine: this,
94
95
  clientEventEmitter,
96
+ onLogMessage: params.onLogMessage,
95
97
  });
96
98
  this.network = dependenciesFactory.createNetwork({
97
99
  logLevel: this.logLevel,
98
100
  loadbalancer: network.loadbalancer,
101
+ onLogMessage: params.onLogMessage,
99
102
  });
100
103
  this.peersRepository = new Map<string, Peer>();
101
104
  this.clientEventEmitter = clientEventEmitter;
102
- this.channelEventsHandler = dependenciesFactory.createChannelEventHandler(this);
103
- this.mediaSoupEventsHandler = dependenciesFactory.createMediaSoupEventHandler(this);
105
+ this.channelEventsHandler = dependenciesFactory.createChannelEventHandler({
106
+ engine: this,
107
+ onLogMessage: params.onLogMessage,
108
+ });
109
+ this.mediaSoupEventsHandler = dependenciesFactory.createMediaSoupEventHandler({
110
+ engine: this,
111
+ onLogMessage: params.onLogMessage,
112
+ });
113
+
104
114
  this.logger = new Logger({
105
115
  logLevel: this.logLevel,
106
116
  namespace: 'Engine',
@@ -162,7 +172,6 @@ class Engine {
162
172
  this.clientEventEmitter.emit(state, { code });
163
173
 
164
174
  if (state === SocketIOEvents.Reconnected) {
165
- this.network.socket.disconnect();
166
175
  this.clientEventEmitter.emit(CLIENT_EVENTS.channelRejoinRequired);
167
176
  }
168
177
  });
@@ -255,6 +264,7 @@ class Engine {
255
264
  video: trackParams.videoTrackOptions,
256
265
  });
257
266
 
267
+ track.setStopTrackOnPause(Boolean(options?.stopTrackOnPause));
258
268
  if (track instanceof VideoTrack) {
259
269
  track.setLabel(TrackLabel.Camera);
260
270
  track.setEncoderConfig(trackParams.encoderConfig);
@@ -279,6 +289,7 @@ class Engine {
279
289
  });
280
290
 
281
291
  track.setLabel(TrackLabel.Microphone);
292
+ track.setStopTrackOnPause(Boolean(options?.stopTrackOnPause));
282
293
  if (options?.encoderConfig) {
283
294
  track.setEncoderConfig(options.encoderConfig);
284
295
  }
@@ -304,6 +315,7 @@ class Engine {
304
315
  track.unpublish();
305
316
  });
306
317
 
318
+ track.setStopTrackOnPause(Boolean(options?.stopTrackOnPause));
307
319
  if (track instanceof VideoTrack) {
308
320
  track.setLabel(TrackLabel.ScreenVideo);
309
321
  track.setEncoderConfig(videoTrackParams.encoderConfig);
@@ -468,10 +480,20 @@ class Engine {
468
480
 
469
481
  this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackPaused, (track: Track) => {
470
482
  const peerTrack = this.myPeer?.tracks.get(track.getLabel());
471
- peerTrack?.close();
483
+ peerTrack?.pause();
472
484
  });
473
485
 
474
486
  this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackResumed, (track: Track) => {
487
+ const peerTrack = this.myPeer?.tracks.get(track.getLabel());
488
+ peerTrack?.resume();
489
+ });
490
+
491
+ this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackClosed, (track: Track) => {
492
+ const peerTrack = this.myPeer?.tracks.get(track.getLabel());
493
+ peerTrack?.close();
494
+ });
495
+
496
+ this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackReopened, (track: Track) => {
475
497
  this.createSelfPeerTrack(track);
476
498
  });
477
499
  }
@@ -39,6 +39,7 @@ class Media {
39
39
  this.#logger = new Logger({
40
40
  namespace: 'Media',
41
41
  logLevel: params.logLevel,
42
+ onLogMessage: params.onLogMessage,
42
43
  });
43
44
  this.#engine = params.engine;
44
45
  this.#clientEventEmitter = params.clientEventEmitter;
@@ -54,6 +54,8 @@ class BaseTrack {
54
54
 
55
55
  readonly #mediaStreamTrackManager: MediaStreamTrackManager;
56
56
 
57
+ #stopTrackOnPause = true;
58
+
57
59
  constructor(params: BaseTrackConstructorParams) {
58
60
  const {
59
61
  mediaStreamTrack, logLevel, engine, clientEventEmitter, constraints, mediaStreamTrackManager,
@@ -119,6 +121,14 @@ class BaseTrack {
119
121
  this.producer = producer;
120
122
  }
121
123
 
124
+ setStopTrackOnPause(value: boolean): void {
125
+ if (this.isPublished) {
126
+ return;
127
+ }
128
+
129
+ this.#stopTrackOnPause = value;
130
+ }
131
+
122
132
  getProducer(): Producer | undefined {
123
133
  return this.producer;
124
134
  }
@@ -375,9 +385,15 @@ class BaseTrack {
375
385
  await this.cancelProducerCheckState();
376
386
  await this.pauseRemoteProducer(this.producer.id);
377
387
  this.producer.pause();
378
- this.#mediaStreamTrack.stop();
379
- this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackPaused, this);
380
388
  this.logger.debug('pause()', { track: this });
389
+
390
+ if (this.#stopTrackOnPause) {
391
+ this.#mediaStreamTrack.stop();
392
+ this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackClosed, this);
393
+ return;
394
+ }
395
+
396
+ this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackPaused, this);
381
397
  } catch (error) {
382
398
  this.logger.error('pause()', { error, track: this });
383
399
  throw new Error('Can`t pause track');
@@ -391,16 +407,16 @@ class BaseTrack {
391
407
  }
392
408
 
393
409
  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 });
400
- await this.resumeRemoteProducer(this.producer.id);
401
- this.producer.resume();
402
- this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackResumed, this);
403
410
  this.logger.debug('resume()', { track: this });
411
+ if (this.#stopTrackOnPause) {
412
+ await this.reopenMediaStreamTrack();
413
+ await this.resumeProducer();
414
+ this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackReopened, this);
415
+ return;
416
+ }
417
+
418
+ await this.resumeProducer();
419
+ this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackResumed, this);
404
420
  } catch (error) {
405
421
  this.logger.error('resume()', { error, track: this });
406
422
  throw new Error('Can`t resume track');
@@ -439,6 +455,26 @@ class BaseTrack {
439
455
  }
440
456
  }
441
457
 
458
+ private async resumeProducer(): Promise<void> {
459
+ if (!this.producer) {
460
+ this.logger.debug('resumeProducer()', { message: 'Track not produced yet', track: this });
461
+ return;
462
+ }
463
+
464
+ await this.resumeRemoteProducer(this.producer.id);
465
+ this.producer.resume();
466
+ }
467
+
468
+ private async reopenMediaStreamTrack(): Promise<void> {
469
+ const track = await this.#mediaStreamTrackManager.createMediaStreamTrack({
470
+ label: this.label,
471
+ constraints: this.#constraints,
472
+ });
473
+
474
+ this.#mediaStreamTrack = track;
475
+ await this.producer?.replaceTrack({ track });
476
+ }
477
+
442
478
  private getOutboundRTPStreamStats(): Promise<RTCOutboundRtpStreamStats> {
443
479
  return new Promise((resolve, reject) => {
444
480
  const getRTCStatsReport = async (attempt = 0) => {
@@ -1,11 +1,12 @@
1
1
  import { io, Socket } from 'socket.io-client';
2
2
  import EnhancedEventEmitter from '../../EnhancedEventEmitter';
3
3
  import { SocketIOEvents } from '../../constants/events';
4
- import { LogLevel, SocketResponse } from '../../types/common';
4
+ import { LogLevel, LogMessageHandler, SocketResponse } from '../../types/common';
5
5
  import Logger from '../Logger';
6
6
 
7
7
  export type SocketIOConstructorParams = {
8
8
  logLevel: LogLevel,
9
+ onLogMessage?: LogMessageHandler,
9
10
  };
10
11
 
11
12
  class SocketIO {
@@ -37,6 +38,7 @@ class SocketIO {
37
38
  this.logger = new Logger({
38
39
  namespace: 'Socket',
39
40
  logLevel: params.logLevel,
41
+ onLogMessage: params.onLogMessage,
40
42
  });
41
43
  }
42
44
 
@@ -8,7 +8,13 @@ import { Device } from 'mediasoup-client';
8
8
  import { Consumer } from 'mediasoup-client/lib/Consumer';
9
9
  import SocketIO from './Socket';
10
10
  import {
11
- CreateConsumerPayload, LogLevel, ProduceParams, RemoteConsumerOptions, SocketResponse, UpdatePeerAppDataPayload,
11
+ CreateConsumerPayload,
12
+ LogLevel,
13
+ LogMessageHandler,
14
+ ProduceParams,
15
+ RemoteConsumerOptions,
16
+ SocketResponse,
17
+ UpdatePeerAppDataPayload,
12
18
  } from '../../types/common';
13
19
  import LoadBalancerApiClient from './LoadBalancerClient';
14
20
  import { CHANNEL_EVENTS, MEDIASOUP_EVENTS, MEDIASOUP_TRANSPORT_EVENTS } from '../../constants/events';
@@ -18,6 +24,7 @@ export type NetworkParams = {
18
24
  socketClient: SocketIO;
19
25
  logLevel: LogLevel,
20
26
  loadBalancerApiClient: LoadBalancerApiClient;
27
+ onLogMessage?: LogMessageHandler;
21
28
  };
22
29
 
23
30
  class Network {
@@ -32,12 +39,13 @@ class Network {
32
39
  private readonly logger: Logger;
33
40
 
34
41
  constructor(params: NetworkParams) {
35
- const { socketClient, loadBalancerApiClient } = params;
42
+ const { socketClient, loadBalancerApiClient, onLogMessage } = params;
36
43
  this.socket = socketClient;
37
44
  this.loadBalancerClient = loadBalancerApiClient;
38
45
  this.logger = new Logger({
39
46
  namespace: 'Network',
40
47
  logLevel: params.logLevel,
48
+ onLogMessage,
41
49
  });
42
50
  }
43
51
 
@@ -20,11 +20,12 @@ class System {
20
20
  private readonly logger: Logger;
21
21
 
22
22
  constructor(params: CreateSystemParams) {
23
- const { clientEventEmitter, logLevel } = params;
23
+ const { clientEventEmitter, logLevel, onLogMessage } = params;
24
24
  this.clientEventEmitter = clientEventEmitter;
25
25
  this.logger = new Logger({
26
26
  namespace: 'System',
27
27
  logLevel,
28
+ onLogMessage,
28
29
  });
29
30
 
30
31
  this.deviceChangeHandler = this.deviceChangeHandler.bind(this);
@@ -159,6 +159,7 @@ export type AudioEncoderConfig = EncoderConfig & {
159
159
  };
160
160
 
161
161
  export type CreateTrackOptions = {
162
+ stopTrackOnPause?: boolean,
162
163
  encoderConfig?: EncoderConfig,
163
164
  deviceId?: string,
164
165
  };
@@ -186,6 +187,7 @@ export type CreateScreenAudioTrackOptions = CreateMicrophoneAudioTrackOptions &
186
187
  };
187
188
 
188
189
  export type CreateScreenMediaOptions = {
190
+ stopTrackOnPause?: boolean,
189
191
  video: CreateScreenVideoTrackOptions,
190
192
  audio: CreateScreenAudioTrackOptions,
191
193
  };
@@ -7,7 +7,7 @@ import Engine from '../engine';
7
7
  import ChannelEventHandler from '../engine/handlers/ChannelEventHandler';
8
8
  import MediaSoupEventHandler from '../engine/handlers/MediaSoupEventHandler';
9
9
  import { LoadBalancerApiClientParams } from '../engine/network/LoadBalancerClient';
10
- import { Logger, LogLevel } from './common';
10
+ import { Logger, LogLevel, LogMessageHandler } from './common';
11
11
 
12
12
  export type IssuesHandler = (issues: IssueDetectorResult) => void;
13
13
 
@@ -24,23 +24,36 @@ export interface CreateMediaParams {
24
24
  logLevel: LogLevel;
25
25
  engine: Engine;
26
26
  clientEventEmitter: EnhancedEventEmitter;
27
+ onLogMessage?: LogMessageHandler;
27
28
  }
28
29
 
29
30
  export interface CreateNetworkParams {
30
31
  logLevel: LogLevel;
31
32
  loadbalancer: LoadBalancerApiClientParams;
33
+ onLogMessage?: LogMessageHandler;
32
34
  }
33
35
 
34
36
  export interface CreateSystemParams {
35
37
  logLevel: LogLevel;
36
- clientEventEmitter: EnhancedEventEmitter
38
+ clientEventEmitter: EnhancedEventEmitter;
39
+ onLogMessage?: LogMessageHandler;
40
+ }
41
+
42
+ export interface CreateMediaSoupEventHandlerParams {
43
+ engine: Engine;
44
+ onLogMessage?: LogMessageHandler;
45
+ }
46
+
47
+ export interface CreateChannelEventHandlerParams {
48
+ engine: Engine;
49
+ onLogMessage?: LogMessageHandler;
37
50
  }
38
51
 
39
52
  export interface EngineDependenciesFactory {
40
53
  createSystem: (params: CreateSystemParams) => System;
41
54
  createMedia: (params: CreateMediaParams) => Media;
42
55
  createNetwork: (params: CreateNetworkParams) => Network;
43
- createChannelEventHandler: (engine: Engine) => ChannelEventHandler;
44
- createMediaSoupEventHandler: (engine: Engine) => MediaSoupEventHandler;
56
+ createChannelEventHandler: (params: CreateChannelEventHandlerParams) => ChannelEventHandler;
57
+ createMediaSoupEventHandler: (params: CreateMediaSoupEventHandlerParams) => MediaSoupEventHandler;
45
58
  createIssueDetector: (params: CreateIssueDetectorParams) => WebRTCIssueDetector;
46
59
  }