@livedigital/client 2.45.0-test-speaker.1 → 3.0.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.
Files changed (35) hide show
  1. package/dist/constants/common.d.ts +1 -0
  2. package/dist/engine/handlers/ChannelAudioObserverEventHandler.d.ts +0 -3
  3. package/dist/engine/index.d.ts +9 -4
  4. package/dist/engine/media/DefaultMediaTracksFactory.d.ts +6 -0
  5. package/dist/engine/media/index.d.ts +9 -8
  6. package/dist/engine/media/tracks/{AudioTrack.d.ts → DefaultAudioTrack.d.ts} +5 -7
  7. package/dist/engine/media/tracks/{BaseTrack.d.ts → DefaultBaseTrack.d.ts} +6 -14
  8. package/dist/engine/media/tracks/{VideoTrack.d.ts → DefaultVideoTrack.d.ts} +7 -6
  9. package/dist/engine/network/Socket.d.ts +2 -2
  10. package/dist/helpers/retry.d.ts +1 -0
  11. package/dist/index.d.ts +4 -3
  12. package/dist/index.es.js +11 -11
  13. package/dist/index.js +11 -11
  14. package/dist/types/common.d.ts +2 -14
  15. package/dist/types/engine.d.ts +8 -1
  16. package/dist/types/media.d.ts +73 -0
  17. package/dist/types/network.d.ts +20 -0
  18. package/package.json +3 -1
  19. package/src/constants/common.ts +2 -0
  20. package/src/engine/handlers/ChannelAudioObserverEventHandler.ts +0 -23
  21. package/src/engine/handlers/MediaSoupEventHandler.ts +1 -1
  22. package/src/engine/index.ts +52 -33
  23. package/src/engine/media/DefaultMediaTracksFactory.ts +17 -0
  24. package/src/engine/media/index.ts +19 -16
  25. package/src/engine/media/tracks/{AudioTrack.ts → DefaultAudioTrack.ts} +5 -8
  26. package/src/engine/media/tracks/{BaseTrack.ts → DefaultBaseTrack.ts} +9 -13
  27. package/src/engine/media/tracks/{VideoTrack.ts → DefaultVideoTrack.ts} +7 -6
  28. package/src/engine/network/Socket.ts +6 -3
  29. package/src/helpers/media.ts +1 -1
  30. package/src/helpers/retry.ts +5 -0
  31. package/src/index.ts +8 -3
  32. package/src/types/common.ts +2 -17
  33. package/src/types/engine.ts +11 -1
  34. package/src/types/media.ts +96 -0
  35. package/src/types/network.ts +24 -0
@@ -5,11 +5,9 @@ import { RtpCapabilities } from 'mediasoup-client/src/RtpParameters';
5
5
  import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
6
6
  import { ConnectionState } from 'mediasoup-client/src/Transport';
7
7
  import { DtlsRole } from 'mediasoup-client/lib/Transport';
8
- import AudioTrack from '../engine/media/tracks/AudioTrack';
9
- import VideoTrack from '../engine/media/tracks/VideoTrack';
10
8
  import NodeJSTimeout = NodeJS.Timeout;
11
9
  export declare type Timeout = NodeJSTimeout | number;
12
- export declare type SocketResponse = {
10
+ export declare type SocketResponse<T = Record<string, any>> = T & {
13
11
  success?: boolean;
14
12
  error?: string;
15
13
  errorCode?: string;
@@ -63,10 +61,8 @@ export declare type AvailableMediaDevices = {
63
61
  };
64
62
  export declare type JoinChannelParams = {
65
63
  channelId: string;
66
- appId: string;
67
- sdkSecret: string;
64
+ token: string;
68
65
  role: Role;
69
- uid?: string;
70
66
  appData?: Record<string, unknown>;
71
67
  };
72
68
  export declare type ChannelEvent = {
@@ -143,7 +139,6 @@ export declare type CreateScreenMediaOptions = BaseVideoTrackOptions & BaseAudio
143
139
  videoEncoderConfig?: VideoEncoderConfig;
144
140
  audioEncoderConfig?: AudioEncoderConfig;
145
141
  };
146
- export declare type Track = VideoTrack | AudioTrack;
147
142
  export declare enum TrackLabel {
148
143
  Camera = "camera",
149
144
  Microphone = "microphone",
@@ -312,9 +307,6 @@ export declare type CreateTracksPayload = {
312
307
  mediaStreamTracks: MediaStreamTrack[];
313
308
  constraints: MediaStreamConstraints;
314
309
  };
315
- export declare type TrackPublishParams = {
316
- keyFrameRequestDelay?: number;
317
- };
318
310
  export declare type ChangeProducerStatePayload = {
319
311
  peerId: string;
320
312
  producerId: string;
@@ -323,10 +315,6 @@ export declare type ForceCloseProducerPayload = {
323
315
  peerId: string;
324
316
  label: TrackLabel;
325
317
  };
326
- export interface TrackWithEncodings {
327
- getCodecOptions(): ProducerCodecOptions;
328
- getEncodings(): RtpEncodingParameters[];
329
- }
330
318
  export declare type ActivityConfirmationRequiredPayload = {
331
319
  channelId: string;
332
320
  time: number;
@@ -7,8 +7,9 @@ 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, LogMessageHandler } from './common';
10
+ import { Logger, LogLevel, LogMessageHandler, Role } from './common';
11
11
  import ChannelAudioObserverEventHandler from '../engine/handlers/ChannelAudioObserverEventHandler';
12
+ import { MediaTracksFactory } from './media';
12
13
  export declare type IssuesHandler = (issues: IssueDetectorResult) => void;
13
14
  export declare type NetworkScoresUpdatedHandler = (networks: NetworkScores) => void;
14
15
  export interface CreateIssueDetectorParams {
@@ -21,6 +22,7 @@ export interface CreateMediaParams {
21
22
  logLevel: LogLevel;
22
23
  engine: Engine;
23
24
  clientEventEmitter: EnhancedEventEmitter;
25
+ mediaTracksFactory: MediaTracksFactory;
24
26
  onLogMessage?: LogMessageHandler;
25
27
  }
26
28
  export interface CreateMediaStreamTrackManagerParams {
@@ -49,6 +51,11 @@ export interface CreateAudioObserverEventHandlerParams {
49
51
  engine: Engine;
50
52
  onLogMessage?: LogMessageHandler;
51
53
  }
54
+ export interface ConnectParams {
55
+ channelId: string;
56
+ role: Role;
57
+ token: string;
58
+ }
52
59
  export interface EngineDependenciesFactory {
53
60
  createSystem: (params: CreateSystemParams) => System;
54
61
  createMedia: (params: CreateMediaParams) => Media;
@@ -0,0 +1,73 @@
1
+ import { Producer, ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
2
+ import { RtpEncodingParameters } from 'mediasoup-client/lib/types';
3
+ import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
4
+ import Engine from '../engine';
5
+ import { BaseTrackInfo, EncoderConfig, LogLevel, TrackLabel, TrackOutboundStats, TrackProduceParams } from './common';
6
+ import EnhancedEventEmitter from '../EnhancedEventEmitter';
7
+ import MediaStreamTrackManager from '../engine/media/tracks/MediaStreamTrackManager';
8
+ export interface BaseTrackParams {
9
+ mediaStreamTrack: MediaStreamTrack;
10
+ logLevel: LogLevel;
11
+ engine: Engine;
12
+ clientEventEmitter: EnhancedEventEmitter;
13
+ constraints: MediaStreamConstraints;
14
+ mediaStreamTrackManager: MediaStreamTrackManager;
15
+ }
16
+ export interface BaseTrack {
17
+ mediaStreamTrack: MediaStreamTrack;
18
+ id: string;
19
+ kind: MediaKind;
20
+ isPublished: boolean;
21
+ isPaused: boolean;
22
+ clientEventEmitter: EnhancedEventEmitter;
23
+ producerId?: string;
24
+ getLabel(): TrackLabel;
25
+ setLabel(label: TrackLabel): void;
26
+ setEncoderConfig(encoderConfig: EncoderConfig): void;
27
+ getEncoderConfig(): EncoderConfig;
28
+ setProducer(producer: Producer): void;
29
+ setStopTrackOnPause(value: boolean): void;
30
+ getProducer(): Producer | undefined;
31
+ stopMediaStreamTrack(): void;
32
+ closeProducer(stopTrack?: boolean): Promise<void>;
33
+ setPriority(priority: RTCPriorityType): Promise<void>;
34
+ produce({ encodings, codecOptions, preferredCodec, transformParams, keyFrameRequestDelay, }: TrackProduceParams): Promise<void>;
35
+ publish(): Promise<void>;
36
+ unpublish(): Promise<void>;
37
+ pause(): Promise<void>;
38
+ resume(): Promise<void>;
39
+ getInfo(): Promise<BaseTrackInfo>;
40
+ getStats(): Promise<TrackOutboundStats | undefined>;
41
+ replaceTrack(track: MediaStreamTrack): Promise<void>;
42
+ }
43
+ export interface TrackWithEncodings extends BaseTrack {
44
+ getCodecOptions(): ProducerCodecOptions;
45
+ getEncodings(): RtpEncodingParameters[];
46
+ getPreferredCodec(): string;
47
+ }
48
+ export interface TrackWithEffects extends BaseTrack {
49
+ isEffectsProcessing: boolean;
50
+ }
51
+ export interface AudioTrack extends BaseTrack, TrackWithEncodings, TrackWithEffects {
52
+ enableNoiseSuppression(): Promise<void>;
53
+ disableNoiseSuppression(): Promise<void>;
54
+ }
55
+ export declare type VideoTrackPublishParams = {
56
+ keyFrameRequestDelay?: number;
57
+ };
58
+ export interface VideoTrack extends BaseTrack, TrackWithEncodings, TrackWithEffects {
59
+ publish(params?: VideoTrackPublishParams): Promise<void>;
60
+ setTransformParams(transformParams: {}): void;
61
+ setMaxSpatialLayer(spatialLayer: number): Promise<void>;
62
+ getMaxSpatialLayer(): number | undefined;
63
+ enableBlur(): Promise<void>;
64
+ disableBlur(): Promise<void>;
65
+ }
66
+ export declare type Track = AudioTrack | VideoTrack;
67
+ export interface AudioTrackParams extends BaseTrackParams {
68
+ noiseSuppressor?: WebAssembly.WebAssemblyInstantiatedSource;
69
+ }
70
+ export interface MediaTracksFactory {
71
+ createAudioTrack: (params: AudioTrackParams) => BaseTrack;
72
+ createVideoTrack: (params: BaseTrackParams) => BaseTrack;
73
+ }
@@ -1,3 +1,4 @@
1
+ import { SocketIOEvents } from '../constants/events';
1
2
  import { Role } from './common';
2
3
  export declare type GetNodeRequest = {
3
4
  channelId: string;
@@ -6,3 +7,22 @@ export declare type GetNodeRequest = {
6
7
  export declare type GetNodeResponse = {
7
8
  webSocketUrl: string;
8
9
  };
10
+ export declare type JoinChannelRequest = {
11
+ appData?: Record<string, unknown>;
12
+ };
13
+ export declare type JoinChannelSuccessResponse = {
14
+ channelId: string;
15
+ appId: string;
16
+ role: string;
17
+ };
18
+ export declare type SocketConnectionError = {
19
+ message: string;
20
+ data?: {
21
+ errorCode: string;
22
+ };
23
+ };
24
+ export declare type SocketObserverEvent = {
25
+ state: SocketIOEvents;
26
+ code?: string;
27
+ error?: SocketConnectionError;
28
+ };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.45.0-test-speaker.1",
5
+ "version": "3.0.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -68,6 +68,7 @@
68
68
  "@types/chai-subset": "^1.3.3",
69
69
  "@types/emscripten": "^1.39.6",
70
70
  "@types/faker": "^5.5.9",
71
+ "@types/jsonwebtoken": "^9.0.2",
71
72
  "@types/mocha": "^10.0.1",
72
73
  "@types/node": "^14.18.36",
73
74
  "@types/qs": "^6.9.5",
@@ -88,6 +89,7 @@
88
89
  "eslint-plugin-react": "^7.26.1",
89
90
  "event-target-shim": "^6.0.2",
90
91
  "faker": "^5.5.3",
92
+ "jsonwebtoken": "^9.0.1",
91
93
  "mocha": "^10.2.0",
92
94
  "nyc": "^15.1.0",
93
95
  "rollup": "^2.57.0",
@@ -16,3 +16,5 @@ export const CONSUMER_CHECK_STATE_TIMEOUT = 4500;
16
16
  export const PRODUCER_CHECK_STATE_TIMEOUT = 3000;
17
17
 
18
18
  export const PEER_APP_DATA_MAX_SIZE_BYTES = 1024;
19
+
20
+ export const SOCKET_ERROR_CODE_UNAUTHORIZED = 'unauthorized';
@@ -8,17 +8,12 @@ import {
8
8
  ChannelAudioObserverEvents,
9
9
  DominantSpeakerEvent, PeersVolumesEvent,
10
10
  } from '../../types/channelAudioObserver';
11
- import Peer from '../Peer';
12
11
 
13
12
  class ChannelAudioObserverEventHandler {
14
13
  private readonly engine: Engine;
15
14
 
16
15
  private readonly logger: Logger;
17
16
 
18
- private lastDominantSpeaker?: Peer;
19
-
20
- private isSilence = true;
21
-
22
17
  constructor(params: { engine: Engine, onLogMessage?: LogMessageHandler }) {
23
18
  const { engine, onLogMessage } = params;
24
19
  this.engine = engine;
@@ -61,12 +56,9 @@ class ChannelAudioObserverEventHandler {
61
56
  const { peerId } = payload.data;
62
57
  const peer = this.engine.getPeerById(peerId);
63
58
  this.engine.setActiveSpeakerPeer(peer);
64
- this.lastDominantSpeaker = peer;
65
59
  }
66
60
 
67
61
  private handlePeerVolumes(payload: PeersVolumesEvent): void {
68
- this.checkLastDominantSpeakerContinueSpeak(payload);
69
- this.isSilence = false;
70
62
  payload.volumes.forEach((item) => {
71
63
  const { peerId, trackLabel, value } = item;
72
64
  const track = this.engine.getPeerById(peerId)?.tracks.get(trackLabel);
@@ -79,7 +71,6 @@ class ChannelAudioObserverEventHandler {
79
71
  }
80
72
 
81
73
  private handleSilence(): void {
82
- this.isSilence = true;
83
74
  const tracks = this.engine.peers.flatMap((peer) => Array.from(peer.tracks.values()));
84
75
  tracks.forEach((track) => track.setVolume(0));
85
76
  this.engine.setActiveSpeakerPeer(undefined);
@@ -130,20 +121,6 @@ class ChannelAudioObserverEventHandler {
130
121
  }
131
122
  }
132
123
 
133
- private checkLastDominantSpeakerContinueSpeak(payload: PeersVolumesEvent) {
134
- if (!this.isSilence || !this.lastDominantSpeaker) {
135
- return;
136
- }
137
-
138
- const lastDominantSpeakerVolume = payload.volumes.find((item) => item.peerId === this.lastDominantSpeaker?.id);
139
- if (!lastDominantSpeakerVolume) {
140
- return;
141
- }
142
-
143
- const lastDominantSpeakerPeer = this.engine.getPeerById(lastDominantSpeakerVolume.peerId);
144
- this.engine.setActiveSpeakerPeer(lastDominantSpeakerPeer);
145
- }
146
-
147
124
  static throwInvalidPayload(): void {
148
125
  throw new InvalidPayloadError('Invalid channel audio observer event handler payload');
149
126
  }
@@ -14,7 +14,7 @@ import {
14
14
  CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, MEDIASOUP_EVENTS, PEER_EVENTS,
15
15
  } from '../../constants/events';
16
16
  import Logger from '../Logger';
17
- import VideoTrack from '../media/tracks/VideoTrack';
17
+ import VideoTrack from '../media/tracks/DefaultVideoTrack';
18
18
 
19
19
  class MediaSoupEventHandler {
20
20
  private readonly engine: Engine;
@@ -10,7 +10,6 @@ import {
10
10
  PeerResponse,
11
11
  Role,
12
12
  SocketResponse,
13
- Track,
14
13
  TrackLabel,
15
14
  LogMessageHandler,
16
15
  LogLevel,
@@ -27,23 +26,32 @@ import Logger from './Logger';
27
26
  import {
28
27
  CHANNEL_EVENTS, CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, SocketIOEvents,
29
28
  } from '../constants/events';
30
- import { GetNodeRequest } from '../types/network';
31
- import VideoTrack from './media/tracks/VideoTrack';
29
+ import {
30
+ GetNodeRequest, JoinChannelRequest, JoinChannelSuccessResponse, SocketConnectionError, SocketObserverEvent,
31
+ } from '../types/network';
32
+ import VideoTrack from './media/tracks/DefaultVideoTrack';
32
33
  import PeerTrack from './media/tracks/PeerTrack';
33
34
  import { retryAsync } from '../helpers/retry';
34
- import { EngineDependenciesFactory, IssuesHandler, NetworkScoresUpdatedHandler } from '../types/engine';
35
- import { LogLevels } from '../constants/common';
35
+ import {
36
+ ConnectParams, EngineDependenciesFactory, IssuesHandler, NetworkScoresUpdatedHandler,
37
+ } from '../types/engine';
38
+ import { LogLevels, SOCKET_ERROR_CODE_UNAUTHORIZED } from '../constants/common';
36
39
  import { LoadBalancerApiClientParams } from './network/LoadBalancerClient';
37
40
  import validateAppData from '../helpers/appDataValidator';
38
- import AudioTrack from './media/tracks/AudioTrack';
39
41
  import NeedJoinFirstError from '../errors/NeedJoinFirstError';
40
42
  import ChannelAudioObserverEventHandler from './handlers/ChannelAudioObserverEventHandler';
43
+ import {
44
+ MediaTracksFactory, AudioTrack, Track, BaseTrack,
45
+ } from '../types/media';
41
46
 
42
47
  type EngineParams = {
43
- clientEventEmitter: EnhancedEventEmitter,
48
+ clientEventEmitter: EnhancedEventEmitter;
44
49
  network: {
45
50
  loadbalancer: LoadBalancerApiClientParams;
46
- },
51
+ };
52
+ media: {
53
+ mediaTracksFactory: MediaTracksFactory;
54
+ };
47
55
  dependenciesFactory: EngineDependenciesFactory;
48
56
  logLevel?: LogLevel;
49
57
  onLogMessage?: LogMessageHandler;
@@ -93,6 +101,7 @@ class Engine {
93
101
  const {
94
102
  clientEventEmitter,
95
103
  network,
104
+ media,
96
105
  dependenciesFactory,
97
106
  logLevel,
98
107
  } = params;
@@ -106,6 +115,7 @@ class Engine {
106
115
  logLevel: this.logLevel,
107
116
  engine: this,
108
117
  clientEventEmitter,
118
+ mediaTracksFactory: media.mediaTracksFactory,
109
119
  onLogMessage: params.onLogMessage,
110
120
  });
111
121
  this.network = dependenciesFactory.createNetwork({
@@ -201,9 +211,16 @@ class Engine {
201
211
  }
202
212
 
203
213
  private watchSocketState(): void {
204
- this.network.socket.observer.on('state', (evt: { state: SocketIOEvents, code?: string }) => {
205
- const { state, code } = evt;
206
- this.clientEventEmitter.emit(state, { code });
214
+ this.network.socket.observer.on('state', (evt: SocketObserverEvent) => {
215
+ const { state, code, error } = evt;
216
+ this.clientEventEmitter.emit(state, { code, error });
217
+
218
+ const isUnauthorizedError = error?.data?.errorCode === SOCKET_ERROR_CODE_UNAUTHORIZED;
219
+ if (state === SocketIOEvents.Error && this.isJoined && isUnauthorizedError) {
220
+ // reconnection failed due to expired token
221
+ this.clientEventEmitter.emit(CLIENT_EVENTS.channelRejoinRequired);
222
+ return;
223
+ }
207
224
 
208
225
  if (state === SocketIOEvents.Reconnected) {
209
226
  this.clientEventEmitter.emit(CLIENT_EVENTS.channelRejoinRequired);
@@ -252,7 +269,7 @@ class Engine {
252
269
  this.logger.debug('join()', { params });
253
270
  this.isChannelJoining = true;
254
271
  await this.connectToSocketServerWithRetry(params);
255
- await this.performJoin(params);
272
+ await this.performJoin({ appData: params.appData });
256
273
  } catch (error) {
257
274
  this.logger.error('join()', { error });
258
275
  throw error;
@@ -310,7 +327,7 @@ class Engine {
310
327
  }
311
328
  }
312
329
 
313
- async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track> {
330
+ async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<VideoTrack> {
314
331
  const trackParams = Media.getCameraVideoTrackParams(options);
315
332
  try {
316
333
  const [track] = await this.media.createUserMediaTracks({
@@ -334,14 +351,14 @@ class Engine {
334
351
 
335
352
  this.media.setTrack(track);
336
353
  this.logger.debug('createCameraVideoTrack()', { trackParams, track });
337
- return track;
354
+ return track as VideoTrack;
338
355
  } catch (error) {
339
356
  this.logger.error('createCameraVideoTrack()', { error, trackParams });
340
357
  throw new Error(`Can not create camera track: ${error.message}`);
341
358
  }
342
359
  }
343
360
 
344
- async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track> {
361
+ async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<AudioTrack> {
345
362
  try {
346
363
  const [track] = await this.media.createUserMediaTracks({
347
364
  video: false,
@@ -354,9 +371,9 @@ class Engine {
354
371
  track.setEncoderConfig(options.encoderConfig);
355
372
  }
356
373
 
357
- if (track instanceof AudioTrack && options?.noiseSuppression) {
374
+ if (track.kind === 'audio' && options?.noiseSuppression) {
358
375
  try {
359
- await track.enableNoiseSuppression();
376
+ await (track as AudioTrack).enableNoiseSuppression();
360
377
  } catch (error) {
361
378
  this.logger.warn('Failed to start noise suppression, featured original track', { track: this });
362
379
  }
@@ -364,7 +381,7 @@ class Engine {
364
381
 
365
382
  this.media.setTrack(track);
366
383
  this.logger.debug('createMicrophoneAudioTrack()', { options, track });
367
- return track;
384
+ return track as AudioTrack;
368
385
  } catch (error) {
369
386
  this.logger.error('createMicrophoneAudioTrack()', { error, options });
370
387
  throw new Error(`Can not create microphone track: ${error.message}`);
@@ -413,14 +430,14 @@ class Engine {
413
430
  });
414
431
  });
415
432
 
416
- return tracks;
433
+ return tracks as Track[];
417
434
  } catch (error) {
418
435
  this.logger.error('createScreenMediaTrack()', { error, trackParams: videoTrackParams });
419
436
  throw new Error(`Can not create screen media tracks: ${error.message}`);
420
437
  }
421
438
  }
422
439
 
423
- deleteTrack(tracks: Track): Promise<void> {
440
+ deleteTrack(tracks: BaseTrack): Promise<void> {
424
441
  return this.media.deleteTrack(tracks);
425
442
  }
426
443
 
@@ -445,7 +462,7 @@ class Engine {
445
462
  this.clientEventEmitter.safeEmit(CLIENT_EVENTS.activeSpeakerChanged, { peer });
446
463
  }
447
464
 
448
- private async connectToSocketServerWithRetry(params: { channelId: string, role: Role }): Promise<void> {
465
+ private async connectToSocketServerWithRetry(params: ConnectParams): Promise<void> {
449
466
  const connectToSocketServerAction = async () => this.connectToSocketServer(params);
450
467
  return retryAsync(connectToSocketServerAction, {
451
468
  maxRetries: 15,
@@ -456,15 +473,17 @@ class Engine {
456
473
  namespace: 'RetrySocketConnect',
457
474
  logLevel: this.logLevel,
458
475
  }),
476
+ abortOnError: (error: SocketConnectionError) => error?.data?.errorCode === SOCKET_ERROR_CODE_UNAUTHORIZED,
459
477
  });
460
478
  }
461
479
 
462
- private async connectToSocketServer(params: { channelId: string, role: Role }): Promise<void> {
480
+ private async connectToSocketServer(params: ConnectParams): Promise<void> {
463
481
  const { webSocketUrl } = await this.getAvailableNode({
464
482
  channelId: params.channelId,
465
483
  role: params.role,
466
484
  });
467
- this.network.socket.connect(webSocketUrl);
485
+
486
+ this.network.socket.connect(webSocketUrl, params.token);
468
487
 
469
488
  try {
470
489
  await this.waitForSocketConnection();
@@ -493,7 +512,7 @@ class Engine {
493
512
 
494
513
  private waitForSocketConnection(): Promise<void> {
495
514
  return new Promise((resolve, reject) => {
496
- const onSocketStateChange = async (data: { state: SocketIOEvents, error?: string }) => {
515
+ const onSocketStateChange = async (data: SocketObserverEvent) => {
497
516
  const { error, state } = data;
498
517
  const stopListening = () => this.network.socket.observer.removeListener('state', onSocketStateChange);
499
518
  const isStateNotExpected = [SocketIOEvents.Disconnected, SocketIOEvents.Reconnecting].includes(state);
@@ -520,16 +539,16 @@ class Engine {
520
539
  });
521
540
  }
522
541
 
523
- private async performJoin(params: JoinChannelParams): Promise<void> {
542
+ private async performJoin(params: JoinChannelRequest): Promise<void> {
524
543
  try {
525
- await this.sendJoinChannelRequestWithRetry(params);
526
- this.app = params.appId;
527
- this.channel = params.channelId;
544
+ const { appId, channelId } = await this.sendJoinChannelRequestWithRetry<JoinChannelSuccessResponse>(params);
545
+ this.app = appId;
546
+ this.channel = channelId;
528
547
  await this.initialize();
529
548
  this.channelEventsHandler.subscribeToEvents();
530
549
  this.mediaSoupEventsHandler.subscribeToEvents();
531
550
  this.isJoined = true;
532
- this.logger.debug('Successfully joined channel', { channelId: params.channelId });
551
+ this.logger.debug('Successfully joined channel', { channelId });
533
552
  await this.createAudioObserver();
534
553
  } catch (error) {
535
554
  this.logger.error('performJoin()', { error });
@@ -537,8 +556,8 @@ class Engine {
537
556
  }
538
557
  }
539
558
 
540
- private async sendJoinChannelRequestWithRetry(params: JoinChannelParams): Promise<SocketResponse> {
541
- const joinChannelAction = async () => this.sendJoinChannelRequest(params);
559
+ private async sendJoinChannelRequestWithRetry<T>(params: JoinChannelRequest): Promise<SocketResponse<T>> {
560
+ const joinChannelAction = async () => this.sendJoinChannelRequest<T>(params);
542
561
  return retryAsync(joinChannelAction, {
543
562
  maxRetries: 3,
544
563
  minBackoffDelayMs: 300,
@@ -550,8 +569,8 @@ class Engine {
550
569
  });
551
570
  }
552
571
 
553
- private async sendJoinChannelRequest(params: JoinChannelParams): Promise<SocketResponse> {
554
- return this.network.socket.request(CHANNEL_EVENTS.channelJoin, params);
572
+ private async sendJoinChannelRequest<T>(params: JoinChannelRequest): Promise<SocketResponse<T>> {
573
+ return this.network.socket.request<T>(CHANNEL_EVENTS.channelJoin, params);
555
574
  }
556
575
 
557
576
  get cahPublish(): boolean {
@@ -0,0 +1,17 @@
1
+ import {
2
+ AudioTrack, AudioTrackParams, BaseTrackParams, MediaTracksFactory, VideoTrack,
3
+ } from '../../types/media';
4
+ import DefaultVideoTrack from './tracks/DefaultVideoTrack';
5
+ import DefaultAudioTrack from './tracks/DefaultAudioTrack';
6
+
7
+ class DefaultMediaTracksFactory implements MediaTracksFactory {
8
+ createAudioTrack(params: AudioTrackParams): AudioTrack {
9
+ return new DefaultAudioTrack(params);
10
+ }
11
+
12
+ createVideoTrack(params: BaseTrackParams): VideoTrack {
13
+ return new DefaultVideoTrack(params);
14
+ }
15
+ }
16
+
17
+ export default DefaultMediaTracksFactory;
@@ -1,14 +1,11 @@
1
1
  import { Device } from 'mediasoup-client';
2
2
  import { RtpCapabilities, RtpCodecCapability } from 'mediasoup-client/lib/RtpParameters';
3
- import VideoTrack from './tracks/VideoTrack';
4
- import AudioTrack from './tracks/AudioTrack';
5
3
  import {
6
4
  CreateCameraVideoTrackOptions,
7
5
  CreateScreenMediaOptions,
8
6
  CreateTracksPayload,
9
7
  CreateVideoTrackParams,
10
8
  LogLevel,
11
- Track,
12
9
  TrackLabel,
13
10
  TransformParams,
14
11
  } from '../../types/common';
@@ -21,13 +18,16 @@ import EnhancedEventEmitter from '../../EnhancedEventEmitter';
21
18
  import MediaStreamTrackManager from './tracks/MediaStreamTrackManager';
22
19
  import { detectHandlerName } from '../../helpers/media';
23
20
  import WasmModuleCompiler from './streamEffects/audio/noiseSuppression/WasmModuleCompiler';
21
+ import {
22
+ AudioTrack, BaseTrack, MediaTracksFactory, TrackWithEncodings, VideoTrack,
23
+ } from '../../types/media';
24
24
 
25
25
  class Media {
26
26
  public isDeviceLoaded = false;
27
27
 
28
28
  private device?: Device;
29
29
 
30
- private tracks: Map<TrackLabel, Track> = new Map();
30
+ private tracks: Map<TrackLabel, BaseTrack> = new Map();
31
31
 
32
32
  readonly #logger: Logger;
33
33
 
@@ -39,6 +39,8 @@ class Media {
39
39
 
40
40
  readonly #mediaStreamTrackManager: MediaStreamTrackManager;
41
41
 
42
+ readonly #mediaTracksFactory: MediaTracksFactory;
43
+
42
44
  #noiseSuppressor?: WebAssembly.WebAssemblyInstantiatedSource;
43
45
 
44
46
  constructor(params: CreateMediaParams) {
@@ -50,6 +52,7 @@ class Media {
50
52
  });
51
53
  this.#engine = params.engine;
52
54
  this.#clientEventEmitter = params.clientEventEmitter;
55
+ this.#mediaTracksFactory = params.mediaTracksFactory;
53
56
  this.#mediaStreamTrackManager = new MediaStreamTrackManager({
54
57
  logLevel: params.logLevel,
55
58
  onLogMessage: params.onLogMessage,
@@ -68,7 +71,7 @@ class Media {
68
71
  return !!this.#noiseSuppressor;
69
72
  }
70
73
 
71
- getTrack(label: TrackLabel): Track | undefined {
74
+ getTrack(label: TrackLabel): BaseTrack | undefined {
72
75
  return this.tracks.get(label);
73
76
  }
74
77
 
@@ -92,7 +95,7 @@ class Media {
92
95
  }
93
96
  }
94
97
 
95
- getTrackCodec(track: Track): RtpCodecCapability | undefined {
98
+ getTrackCodec(track: TrackWithEncodings): RtpCodecCapability | undefined {
96
99
  const { rtpCapabilities, rtpCapabilities: { codecs } } = this.mediasoupDevice;
97
100
  if (!codecs) {
98
101
  this.#logger.error('getTrackCodec()', { track, rtpCapabilities });
@@ -102,7 +105,7 @@ class Media {
102
105
  return codecs.find((c) => c.mimeType.toLowerCase() === `${track.kind}/${track.getPreferredCodec()}`);
103
106
  }
104
107
 
105
- private createTracks({ constraints, mediaStreamTracks }: CreateTracksPayload): Track[] {
108
+ private createTracks({ constraints, mediaStreamTracks }: CreateTracksPayload): BaseTrack[] {
106
109
  return mediaStreamTracks.map((mediaStreamTrack) => {
107
110
  const params = {
108
111
  mediaStreamTrack,
@@ -114,8 +117,8 @@ class Media {
114
117
  };
115
118
 
116
119
  const track = mediaStreamTrack.kind === 'audio'
117
- ? new AudioTrack({ ...params, noiseSuppressor: this.#noiseSuppressor })
118
- : new VideoTrack(params);
120
+ ? this.#mediaTracksFactory.createAudioTrack({ ...params, noiseSuppressor: this.#noiseSuppressor })
121
+ : this.#mediaTracksFactory.createVideoTrack(params);
119
122
 
120
123
  this.#logger.debug('createTrack() track created', {
121
124
  trackId: track.id,
@@ -126,24 +129,24 @@ class Media {
126
129
  });
127
130
  }
128
131
 
129
- async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<Track[]> {
132
+ async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<BaseTrack[]> {
130
133
  const mediaStreamTracks = await this.#mediaStreamTrackManager.createUserMediaTracks(constraints);
131
134
  return this.createTracks({ constraints, mediaStreamTracks });
132
135
  }
133
136
 
134
- async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<Track[]> {
137
+ async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<BaseTrack[]> {
135
138
  const mediaStreamTracks = await this.#mediaStreamTrackManager.createDisplayMediaTracks(constraints);
136
139
  return this.createTracks({ constraints, mediaStreamTracks });
137
140
  }
138
141
 
139
- async deleteTrack(track: Track) {
142
+ async deleteTrack(track: BaseTrack) {
140
143
  await track.closeProducer();
141
144
  if ('disableNoiseSuppression' in track) {
142
- await track.disableNoiseSuppression();
145
+ await (track as AudioTrack).disableNoiseSuppression();
143
146
  }
144
147
 
145
148
  if ('disableBlur' in track) {
146
- await track.disableBlur();
149
+ await (track as VideoTrack).disableBlur();
147
150
  }
148
151
 
149
152
  track.stopMediaStreamTrack();
@@ -151,7 +154,7 @@ class Media {
151
154
  this.#logger.debug('deleteTrack() tack deleted', { trackId: track.id, kind: track.kind });
152
155
  }
153
156
 
154
- getAllTracks(): Track[] {
157
+ getAllTracks(): BaseTrack[] {
155
158
  return Array.from(this.tracks.values());
156
159
  }
157
160
 
@@ -165,7 +168,7 @@ class Media {
165
168
  }
166
169
  }
167
170
 
168
- setTrack(track: Track): void {
171
+ setTrack(track: BaseTrack): void {
169
172
  this.tracks.set(track.getLabel(), track);
170
173
  }
171
174