@livedigital/client 2.17.0 → 2.18.0-webrtc-issue-events.2

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 (36) hide show
  1. package/dist/constants/common.d.ts +2 -0
  2. package/dist/constants/events.d.ts +8 -0
  3. package/dist/engine/Peer.d.ts +0 -1
  4. package/dist/engine/PeerConsumer.d.ts +2 -1
  5. package/dist/engine/index.d.ts +2 -6
  6. package/dist/engine/media/tracks/AudioTrack.d.ts +1 -0
  7. package/dist/engine/media/tracks/BaseTrack.d.ts +21 -3
  8. package/dist/engine/media/tracks/PeerTrack.d.ts +8 -5
  9. package/dist/engine/media/tracks/VideoTrack.d.ts +1 -0
  10. package/dist/engine/network/Socket.d.ts +1 -1
  11. package/dist/engine/network/index.d.ts +9 -1
  12. package/dist/engine/wid/types.d.ts +5 -0
  13. package/dist/index.d.ts +0 -4
  14. package/dist/index.es.js +1 -1
  15. package/dist/index.js +2 -2
  16. package/dist/types/common.d.ts +18 -0
  17. package/dist/types/engine.d.ts +3 -0
  18. package/package.json +1 -1
  19. package/src/constants/common.ts +3 -0
  20. package/src/constants/events.ts +9 -0
  21. package/src/engine/DefaultEngineDependenciesFactory.ts +1 -0
  22. package/src/engine/Peer.ts +24 -42
  23. package/src/engine/PeerConsumer.ts +33 -1
  24. package/src/engine/index.ts +46 -159
  25. package/src/engine/media/index.ts +18 -6
  26. package/src/engine/media/tracks/AudioTrack.ts +10 -0
  27. package/src/engine/media/tracks/BaseTrack.ts +287 -5
  28. package/src/engine/media/tracks/PeerTrack.ts +137 -24
  29. package/src/engine/media/tracks/VideoTrack.ts +12 -0
  30. package/src/engine/network/Socket.ts +4 -4
  31. package/src/engine/network/index.ts +51 -1
  32. package/src/engine/wid/WebRTCIssueDetector.ts +17 -7
  33. package/src/engine/wid/types.ts +6 -0
  34. package/src/index.ts +0 -16
  35. package/src/types/common.ts +21 -0
  36. package/src/types/engine.ts +3 -0
@@ -1,5 +1,7 @@
1
1
  import { MediaKind, RtpEncodingParameters, RtpParameters } from 'mediasoup-client/lib/types';
2
2
  import { ConsumerOptions } from 'mediasoup-client/lib/Consumer';
3
+ import { RtpCapabilities } from 'mediasoup-client/src/RtpParameters';
4
+ import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
3
5
  import AudioTrack from '../engine/media/tracks/AudioTrack';
4
6
  import VideoTrack from '../engine/media/tracks/VideoTrack';
5
7
  export declare type SocketResponse = {
@@ -13,6 +15,12 @@ export declare type ProduceParams = {
13
15
  rtpParameters: RtpParameters;
14
16
  appData: Record<string, unknown>;
15
17
  };
18
+ export declare type TrackProduceParams = {
19
+ encodings?: RtpEncodingParameters[];
20
+ codecOptions?: ProducerCodecOptions;
21
+ preferredCodec?: string;
22
+ transformParams?: TrackTransformParams;
23
+ };
16
24
  export declare type TrackTransformParams = {
17
25
  width?: number;
18
26
  height?: number;
@@ -212,3 +220,13 @@ export declare type TransportConnectionTimeoutPayload = {
212
220
  direction: 'receive' | 'send';
213
221
  timeout: number;
214
222
  };
223
+ export declare type ExtendedRTCInboundRtpStreamStats = RTCInboundRtpStreamStats & {
224
+ decoderImplementation?: string;
225
+ };
226
+ export declare type CreateConsumerPayload = {
227
+ producerId: string;
228
+ rtpCapabilities: RtpCapabilities;
229
+ appId?: string;
230
+ channelId?: string;
231
+ producerPeerId: string;
232
+ };
@@ -12,9 +12,12 @@ import { LogLevel } from './common';
12
12
  export declare type IssuesHandler = (issues: IssueDetectorResult) => void;
13
13
  export interface CreateIssueDetectorParams {
14
14
  onIssues?: IssuesHandler;
15
+ ignoreSSRCList?: number[];
15
16
  }
16
17
  export interface CreateMediaParams {
17
18
  logLevel: LogLevel;
19
+ engine: Engine;
20
+ clientEventEmitter: EnhancedEventEmitter;
18
21
  }
19
22
  export interface CreateNetworkParams {
20
23
  logLevel: LogLevel;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.17.0",
5
+ "version": "2.18.0-webrtc-issue-events.2",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -11,3 +11,6 @@ export const LogLevels = {
11
11
  Info: LOG_LEVEL_INFO,
12
12
  Debug: LOG_LEVEL_DEBUG,
13
13
  };
14
+
15
+ export const CONSUMER_CHECK_STATE_TIMEOUT = 5000;
16
+ export const PRODUCER_CHECK_STATE_TIMEOUT = 3000;
@@ -12,6 +12,14 @@ export const CLIENT_EVENTS = {
12
12
  channelRejoinRequired: 'channel-rejoin-required',
13
13
  devicesListUpdated: 'devices-list-updated',
14
14
  transportConnectionTimeout: 'transport-connection-timeout',
15
+ trackPublishingFailed: 'track-publishing-failed',
16
+ };
17
+
18
+ export const INTERNAL_CLIENT_EVENTS = {
19
+ trackProduced: 'track-produced',
20
+ trackUnproduced: 'track-unproduced',
21
+ trackPaused: 'track-paused',
22
+ trackResumed: 'track-resumed',
15
23
  };
16
24
 
17
25
  export const PEER_EVENTS = {
@@ -22,6 +30,7 @@ export const PEER_EVENTS = {
22
30
  trackEnd: 'track-end',
23
31
  trackPaused: 'track-paused',
24
32
  trackResumed: 'track-resumed',
33
+ trackFailed: 'track-failed',
25
34
  };
26
35
 
27
36
  export enum SocketIOEvents {
@@ -50,6 +50,7 @@ class DefaultEngineDependenciesFactory implements EngineDependenciesFactory {
50
50
  createIssueDetector(params: CreateIssueDetectorParams): WebRTCIssueDetector {
51
51
  return new WebRTCIssueDetector({
52
52
  onIssues: params.onIssues,
53
+ ignoreSSRCList: params.ignoreSSRCList,
53
54
  });
54
55
  }
55
56
  }
@@ -8,7 +8,6 @@ import {
8
8
  ChangePreferredLayersPayload,
9
9
  Role,
10
10
  ProducerSetMaxSpatialLayer,
11
- RemoteConsumerOptions,
12
11
  } from '../types/common';
13
12
  import EnhancedEventEmitter from '../EnhancedEventEmitter';
14
13
  import Engine from './index';
@@ -114,61 +113,44 @@ class Peer {
114
113
  }
115
114
 
116
115
  public async subscribe(producerId: string): Promise<void> {
117
- const producer = this.producers.get(producerId);
118
- if (!producer) {
119
- this.logger.warn('subscribe()', { message: 'Peer does not have a producer', peer: this, producerId });
120
- return;
121
- }
122
-
123
- const consumer = this.getAllConsumers().find((item) => item.producerId === producerId);
124
- if (consumer) {
125
- this.logger.warn('subscribe()', { message: 'Already subscribed to producer', peer: this, producerId });
126
- return;
127
- }
128
-
129
- await this.createConsumer(producer);
130
- }
116
+ try {
117
+ const producer = this.producers.get(producerId);
118
+ if (!producer) {
119
+ this.logger.warn('subscribe()', { message: 'Peer does not have a producer', peer: this, producerId });
120
+ return;
121
+ }
131
122
 
132
- private async createConsumer(producer: PeerProducer): Promise<void> {
133
- const transport = this.engine.network.receiveTransport;
134
- if (!transport) {
135
- this.logger.error('createConsumer()', 'missing receiveTransport');
136
- throw new Error('Client not initialized');
137
- }
123
+ const peerConsumer = this.getAllConsumers().find((item) => item.producerId === producerId);
124
+ if (peerConsumer) {
125
+ this.logger.debug('subscribe()', { message: 'Already subscribed to producer', peer: this, producerId });
126
+ return;
127
+ }
138
128
 
139
- try {
140
- const remoteConsumer = await this.engine.network.socket.request(MEDIASOUP_EVENTS.createConsumer, {
129
+ const { isProducerPaused, consumer } = await this.engine.network.createConsumer({
141
130
  producerId: producer.id,
142
- transportId: transport.id,
143
131
  rtpCapabilities: this.engine.media.mediasoupDevice.rtpCapabilities,
144
- appData: {
145
- peerId: this.engine.mySocketId,
146
- appId: this.engine.appId,
147
- channelId: this.engine.channelId,
148
- },
149
132
  producerPeerId: this.id,
150
- }) as RemoteConsumerOptions;
151
-
152
- const consumer = await transport.consume(remoteConsumer);
153
- if (!remoteConsumer.producerPaused) {
154
- await this.engine.network.socket.request(MEDIASOUP_EVENTS.resumeConsumer, { consumerId: consumer.id });
155
- }
133
+ appId: this.engine.appId,
134
+ channelId: this.engine.channelId,
135
+ });
156
136
 
157
- this.logger.debug(`Subscribed for ${producer.kind}`, { peer: this });
158
137
  const track = new PeerTrack({
159
138
  mediaStreamTrack: consumer.track,
160
139
  label: producer.label,
161
- consumer: new PeerConsumer({
162
- consumer,
163
- logLevel: this.engine.logLevel,
164
- }),
140
+ consumer: new PeerConsumer({ consumer, logLevel: this.engine.logLevel }),
165
141
  engine: this.engine,
166
- isPaused: remoteConsumer.producerPaused,
142
+ isPaused: true,
167
143
  peerEventEmitter: this.observer,
168
144
  });
145
+
146
+ if (!isProducerPaused) {
147
+ await track.resume();
148
+ }
149
+
169
150
  this.tracks.set(track.label, track);
151
+ this.logger.debug(`Subscribed for ${producer.kind}`, { peer: this });
170
152
  } catch (error) {
171
- this.logger.error('createConsumer()', producer, error);
153
+ this.logger.error('subscribe()', { producerId, error });
172
154
  throw new Error('Error subscribe media');
173
155
  }
174
156
  }
@@ -1,7 +1,12 @@
1
1
  import { parseScalabilityMode } from 'mediasoup-client';
2
2
  import { MediaKind, RtpEncodingParameters } from 'mediasoup-client/lib/RtpParameters';
3
3
  import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
4
- import { ConsumerData, LogLevel, SpatialLayerParams } from '../types/common';
4
+ import {
5
+ ConsumerData,
6
+ ExtendedRTCInboundRtpStreamStats,
7
+ LogLevel,
8
+ SpatialLayerParams,
9
+ } from '../types/common';
5
10
  import Logger from './Logger';
6
11
 
7
12
  export type PeerConsumerConstructorParams = {
@@ -161,6 +166,33 @@ class PeerConsumer {
161
166
  setCurrentMaxSpatialLayer(spatialLayer: number): void {
162
167
  this.currentMaxSpatialLayer = spatialLayer;
163
168
  }
169
+
170
+ async getInboundRTPStreamStats(): Promise<ExtendedRTCInboundRtpStreamStats> {
171
+ return new Promise((resolve, reject) => {
172
+ const getRTCStatsReport = async (attempt = 0) => {
173
+ try {
174
+ const rtcStatsReport = await this.consumer.getStats();
175
+ const inboundRTPStreamStats: ExtendedRTCInboundRtpStreamStats = Array.from(rtcStatsReport.values())
176
+ .find((stat) => stat.type === 'inbound-rtp');
177
+ if (!inboundRTPStreamStats && attempt < 5) {
178
+ setTimeout(() => getRTCStatsReport(attempt + 1), 150);
179
+ return;
180
+ }
181
+
182
+ if (attempt >= 5 && !inboundRTPStreamStats) {
183
+ reject(new Error('InboundRTPStreamStat not exist'));
184
+ }
185
+
186
+ resolve(inboundRTPStreamStats);
187
+ } catch (error) {
188
+ reject(new Error('Can not get RTCStatsReport'));
189
+ this.logger.warn('getInboundRTPStreamStats()', { error });
190
+ }
191
+ };
192
+
193
+ getRTCStatsReport();
194
+ });
195
+ }
164
196
  }
165
197
 
166
198
  export default PeerConsumer;
@@ -21,11 +21,10 @@ import ChannelEventHandler from './handlers/ChannelEventHandler';
21
21
  import MediaSoupEventHandler from './handlers/MediaSoupEventHandler';
22
22
  import Logger from './Logger';
23
23
  import {
24
- CHANNEL_EVENTS, CLIENT_EVENTS, MEDIASOUP_EVENTS, SocketIOEvents,
24
+ CHANNEL_EVENTS, CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, SocketIOEvents,
25
25
  } from '../constants/events';
26
26
  import { GetNodeRequest } from '../types/network';
27
27
  import VideoTrack from './media/tracks/VideoTrack';
28
- import AudioTrack from './media/tracks/AudioTrack';
29
28
  import PeerTrack from './media/tracks/PeerTrack';
30
29
  import WebRTCIssueDetector from './wid/WebRTCIssueDetector';
31
30
  import { retryAsync } from '../helpers/retry';
@@ -89,6 +88,8 @@ class Engine {
89
88
  });
90
89
  this.media = dependenciesFactory.createMedia({
91
90
  logLevel: this.logLevel,
91
+ engine: this,
92
+ clientEventEmitter,
92
93
  });
93
94
  this.network = dependenciesFactory.createNetwork({
94
95
  logLevel: this.logLevel,
@@ -106,9 +107,11 @@ class Engine {
106
107
 
107
108
  this.webRtcIssueDetector = dependenciesFactory.createIssueDetector({
108
109
  onIssues: params.onIssues,
110
+ ignoreSSRCList: [1234], // mediasoup-client probator
109
111
  });
110
112
 
111
113
  this.watchSocketState();
114
+ this.watchClientEvents();
112
115
  }
113
116
 
114
117
  private async initialize(): Promise<void> {
@@ -137,7 +140,7 @@ class Engine {
137
140
  this.webRtcIssueDetector.resetWatchedPeerConnectionsList();
138
141
  this.network.socket.disconnect();
139
142
  this.peersRepository.clear();
140
- await this.unpublish();
143
+ await this.unpublishAllTracks();
141
144
  await this.network.closeSendTransport();
142
145
  await this.network.closeReceiveTransport();
143
146
 
@@ -148,14 +151,6 @@ class Engine {
148
151
  }
149
152
  }
150
153
 
151
- private async pauseRemoteProducer(producerId: string): Promise<SocketResponse> {
152
- return this.network.socket.request(MEDIASOUP_EVENTS.pauseProducer, { producerId });
153
- }
154
-
155
- private async resumeRemoteProducer(producerId: string): Promise<SocketResponse> {
156
- return this.network.socket.request(MEDIASOUP_EVENTS.resumeProducer, { producerId });
157
- }
158
-
159
154
  private watchSocketState(): void {
160
155
  this.network.socket.observer.on('state', (evt: { state: SocketIOEvents, code?: string }) => {
161
156
  const { state, code } = evt;
@@ -221,44 +216,6 @@ class Engine {
221
216
  this.peersRepository.delete(peerId);
222
217
  }
223
218
 
224
- public async pause(track: Track): Promise<void> {
225
- const producer = track.getProducer();
226
- if (!producer) {
227
- this.logger.warn('pause()', { message: 'Track is not published', kind: track.kind });
228
- return;
229
- }
230
-
231
- try {
232
- await this.pauseRemoteProducer(producer.id);
233
- producer.pause();
234
- const peerTrack = this.myPeer?.tracks.get(track.getLabel());
235
- peerTrack?.pause();
236
- this.logger.debug('pause()', { kind: track.kind });
237
- } catch (error) {
238
- this.logger.error('pause()', { error, kind: track.kind });
239
- throw new Error('Can`t pause track');
240
- }
241
- }
242
-
243
- public async resume(track: Track): Promise<void> {
244
- const producer = track.getProducer();
245
- if (!producer) {
246
- this.logger.warn('resume()', { message: 'Track is not published' });
247
- return;
248
- }
249
-
250
- try {
251
- await this.resumeRemoteProducer(producer.id);
252
- producer.resume();
253
- this.logger.debug('resume()', { kind: track.kind });
254
- const peerTrack = this.myPeer?.tracks.get(track.getLabel());
255
- peerTrack?.resume();
256
- } catch (error) {
257
- this.logger.error('resume()', { error, kind: track.kind });
258
- throw new Error('Can`t resume track');
259
- }
260
- }
261
-
262
219
  async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track> {
263
220
  const trackParams = Media.getCameraVideoTrackParams(options);
264
221
  try {
@@ -313,7 +270,7 @@ class Engine {
313
270
 
314
271
  tracks.forEach((track) => {
315
272
  track.mediaStreamTrack.addEventListener('ended', () => {
316
- this.unpublish(track);
273
+ track.unpublish();
317
274
  });
318
275
 
319
276
  if (track instanceof VideoTrack) {
@@ -339,115 +296,6 @@ class Engine {
339
296
  }
340
297
  }
341
298
 
342
- public async publish(tracks: Track | Track[]): Promise<void> {
343
- if (!this.cahPublish) {
344
- this.logger.error('publish()', { message: 'Not enough access to publish' });
345
- throw new Error('Not enough access to publish');
346
- }
347
-
348
- const tracksToPublish = Array.isArray(tracks) ? tracks : [tracks];
349
- const actions = tracksToPublish.map(async (track) => {
350
- const encodings = 'getEncodings' in track
351
- ? track.getEncodings()
352
- : undefined;
353
-
354
- const codecOptions = 'getCodecOptions' in track
355
- ? track.getCodecOptions()
356
- : undefined;
357
-
358
- const maxSpatialLayer = 0;
359
- const appData = {
360
- peerId: this.mySocketId,
361
- label: track.getLabel(),
362
- trackTransformParams: {},
363
- maxSpatialLayer: 0,
364
- };
365
-
366
- if (track instanceof VideoTrack) {
367
- appData.maxSpatialLayer = maxSpatialLayer;
368
- appData.trackTransformParams = track.transformParams;
369
- }
370
-
371
- const producer = await this.network.sendTransport?.produce({
372
- track: track.mediaStreamTrack,
373
- encodings,
374
- codecOptions,
375
- codec: this.media.getTrackCodec(track),
376
- stopTracks: true,
377
- disableTrackOnPause: true,
378
- zeroRtpOnPause: true,
379
- appData,
380
- });
381
-
382
- if (!producer) {
383
- return;
384
- }
385
-
386
- track.setProducer(producer);
387
-
388
- if (track instanceof VideoTrack) {
389
- await track.setMaxSpatialLayer(maxSpatialLayer);
390
- await this.network.socket.request(MEDIASOUP_EVENTS.producerSetMaxSpatialLayer, {
391
- producerId: track.getProducer()?.id,
392
- maxSpatialLayer,
393
- });
394
- }
395
-
396
- if (track instanceof AudioTrack) {
397
- await track.setPriority('high');
398
- }
399
-
400
- const myPeer = this.peersRepository.get(<string> this.mySocketId);
401
- if (!myPeer) {
402
- return;
403
- }
404
-
405
- const peerTrack = new PeerTrack({
406
- mediaStreamTrack: track.mediaStreamTrack,
407
- label: track.getLabel(),
408
- engine: this,
409
- isPaused: producer.paused,
410
- peerEventEmitter: myPeer.observer,
411
- });
412
- myPeer.tracks.set(peerTrack.label, peerTrack);
413
- this.logger.debug('publish()', { track, encodings, codecOptions });
414
- });
415
-
416
- try {
417
- await Promise.all(actions);
418
- } catch (error) {
419
- this.logger.error('publish()', { error });
420
- }
421
- }
422
-
423
- public async unpublish(tracks?: Track | Track[]): Promise<void> {
424
- let tracksToUnpublish: Track[];
425
- if (!tracks) {
426
- tracksToUnpublish = this.media.getAllTracks();
427
- } else {
428
- tracksToUnpublish = Array.isArray(tracks) ? tracks : [tracks];
429
- }
430
-
431
- const actions = tracksToUnpublish.map(async (track) => {
432
- const producer = track.getProducer();
433
- if (producer && this.network.socket.connection?.connected) {
434
- await this.network.closeRemoteProducer(producer.id);
435
- }
436
-
437
- const peerTrack = this.myPeer?.tracks.get(track.getLabel());
438
- peerTrack?.close();
439
-
440
- await this.media.deleteTrack(track);
441
- this.logger.debug('unpublish()', { track });
442
- });
443
-
444
- try {
445
- await Promise.all(actions);
446
- } catch (error) {
447
- this.logger.error('unpublish()', { error });
448
- }
449
- }
450
-
451
299
  deleteTrack(tracks: Track): void {
452
300
  return this.media.deleteTrack(tracks);
453
301
  }
@@ -568,6 +416,45 @@ class Engine {
568
416
  private get myPeer(): Peer | undefined {
569
417
  return this.peers.find((peer) => peer.isMe);
570
418
  }
419
+
420
+ private async unpublishAllTracks(): Promise<void> {
421
+ await Promise.all(this.media.getAllTracks().map((track) => track.unpublish()));
422
+ }
423
+
424
+ private watchClientEvents(): void {
425
+ this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackProduced, (track: Track) => {
426
+ const myPeer = this.peersRepository.get(<string> this.mySocketId);
427
+ if (!myPeer) {
428
+ return;
429
+ }
430
+
431
+ const peerTrack = new PeerTrack({
432
+ mediaStreamTrack: track.mediaStreamTrack,
433
+ label: track.getLabel(),
434
+ engine: this,
435
+ isPaused: track.isPaused,
436
+ peerEventEmitter: myPeer.observer,
437
+ });
438
+
439
+ myPeer.tracks.set(peerTrack.label, peerTrack);
440
+ });
441
+
442
+ this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackUnproduced, (track: Track) => {
443
+ const peerTrack = this.myPeer?.tracks.get(track.getLabel());
444
+ peerTrack?.close();
445
+ this.media.deleteTrack(track);
446
+ });
447
+
448
+ this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackPaused, (track: Track) => {
449
+ const peerTrack = this.myPeer?.tracks.get(track.getLabel());
450
+ peerTrack?.pause();
451
+ });
452
+
453
+ this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackResumed, (track: Track) => {
454
+ const peerTrack = this.myPeer?.tracks.get(track.getLabel());
455
+ peerTrack?.resume();
456
+ });
457
+ }
571
458
  }
572
459
 
573
460
  export default Engine;
@@ -9,6 +9,8 @@ import Logger from '../Logger';
9
9
  import { VIDEO_CONSTRAINS } from '../../constants/videoConstrains';
10
10
  import { SCREEN_SHARING_SIMULCAST_ENCODINGS, WEBCAM_SIMULCAST_ENCODINGS } from '../../constants/simulcastEncodings';
11
11
  import { CreateMediaParams } from '../../types/engine';
12
+ import Engine from '../index';
13
+ import EnhancedEventEmitter from '../../EnhancedEventEmitter';
12
14
 
13
15
  class Media {
14
16
  public isDeviceLoaded = false;
@@ -17,9 +19,13 @@ class Media {
17
19
 
18
20
  private tracks: Map<string, Track> = new Map();
19
21
 
20
- #logger: Logger;
22
+ readonly #logger: Logger;
21
23
 
22
- #logLevel: LogLevel;
24
+ readonly #logLevel: LogLevel;
25
+
26
+ readonly #engine: Engine;
27
+
28
+ readonly #clientEventEmitter: EnhancedEventEmitter;
23
29
 
24
30
  constructor(params: CreateMediaParams) {
25
31
  this.#logLevel = params.logLevel;
@@ -27,6 +33,8 @@ class Media {
27
33
  namespace: 'Media',
28
34
  logLevel: params.logLevel,
29
35
  });
36
+ this.#engine = params.engine;
37
+ this.#clientEventEmitter = params.clientEventEmitter;
30
38
  }
31
39
 
32
40
  get mediasoupDevice(): Device {
@@ -57,10 +65,14 @@ class Media {
57
65
  private createTracks(stream: MediaStream): Track[] {
58
66
  const mediaStreamTracks = stream.getTracks();
59
67
  return mediaStreamTracks.map((mediaStreamTrack) => {
60
- const track = mediaStreamTrack.kind === 'audio'
61
- ? new AudioTrack({ mediaStreamTrack, logLevel: this.#logLevel })
62
- : new VideoTrack({ mediaStreamTrack, logLevel: this.#logLevel });
63
-
68
+ const params = {
69
+ mediaStreamTrack,
70
+ logLevel: this.#logLevel,
71
+ engine: this.#engine,
72
+ clientEventEmitter: this.#clientEventEmitter,
73
+ };
74
+
75
+ const track = mediaStreamTrack.kind === 'audio' ? new AudioTrack(params) : new VideoTrack(params);
64
76
  this.tracks.set(track.id, track);
65
77
  this.#logger.debug('createTrack() track created', {
66
78
  streamId: stream.id,
@@ -2,6 +2,7 @@ import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
2
2
  import { AudioCodec, AudioEncoderConfig } from '../../../types/common';
3
3
  import BaseTrack from './BaseTrack';
4
4
  import TrackWithCodecOptions from './TrackWithCodecOptions';
5
+ import { INTERNAL_CLIENT_EVENTS } from '../../../constants/events';
5
6
 
6
7
  class AudioTrack extends BaseTrack implements TrackWithCodecOptions {
7
8
  getEncoderConfig(): AudioEncoderConfig {
@@ -22,6 +23,15 @@ class AudioTrack extends BaseTrack implements TrackWithCodecOptions {
22
23
 
23
24
  return 'opus';
24
25
  }
26
+
27
+ async publish(): Promise<void> {
28
+ await this.produce({
29
+ preferredCodec: this.getPreferredCodec(),
30
+ codecOptions: this.getCodecOptions(),
31
+ });
32
+
33
+ this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackProduced, this);
34
+ }
25
35
  }
26
36
 
27
37
  export default AudioTrack;