@livedigital/client 1.13.0 → 1.13.1

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.
@@ -153,3 +153,29 @@ export declare type PayloadOfPublishedMedia = {
153
153
  label: TrackLabel;
154
154
  };
155
155
  export declare type PayloadOfUnpublishedMedia = PayloadOfPublishedMedia;
156
+ export declare type ChangePreferredLayersPayload = PreferredLayersParams & {
157
+ consumerId: string;
158
+ spatialLayer: number;
159
+ temporalLayer: number;
160
+ };
161
+ export declare type CreateVideoTrackParams = {
162
+ videoTrackOptions: {
163
+ deviceId?: string;
164
+ width?: {
165
+ max?: number;
166
+ min?: number;
167
+ ideal?: number;
168
+ };
169
+ height?: {
170
+ max?: number;
171
+ min?: number;
172
+ ideal?: number;
173
+ };
174
+ frameRate?: {
175
+ max?: number;
176
+ min?: number;
177
+ ideal?: number;
178
+ };
179
+ };
180
+ encoderConfig: VideoEncoderConfig;
181
+ };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "1.13.0",
5
+ "version": "1.13.1",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -1,10 +1,10 @@
1
1
  // eslint-disable-next-line import/prefer-default-export
2
2
  export const VIDEO_CONSTRAINS = {
3
- qvga: { width: { ideal: 320 }, height: { ideal: 240 } },
4
- vga: { width: { ideal: 640 }, height: { ideal: 480 } },
3
+ qvga: { width: 320, height: 240 },
4
+ vga: { width: 640, height: 480 },
5
5
  hd: {
6
- width: { ideal: 1280 },
7
- height: { ideal: 720 },
6
+ width: 1280,
7
+ height: 720,
8
8
  },
9
- fullhd: { width: { ideal: 1920 }, height: { ideal: 1080 } },
9
+ fullhd: { width: 1920, height: 1080 },
10
10
  };
@@ -9,7 +9,9 @@ import {
9
9
  ConnectionQuality,
10
10
  StartTrackPayload,
11
11
  EndTrackPayload,
12
- PayloadOfPublishedMedia, PayloadOfUnpublishedMedia,
12
+ PayloadOfPublishedMedia,
13
+ PayloadOfUnpublishedMedia,
14
+ ChangePreferredLayersPayload,
13
15
  } from '../types/common';
14
16
  import Consumer from './media/Consumer';
15
17
  import VideoConsumer from './media/VideoConsumer';
@@ -271,6 +273,13 @@ class Peer {
271
273
  spatialLayer,
272
274
  temporalLayer,
273
275
  });
276
+
277
+ this.logger.debug('changeConsumerPreferredLayers()', {
278
+ peer: this,
279
+ consumer,
280
+ spatialLayer,
281
+ temporalLayer,
282
+ });
274
283
  } catch (err) {
275
284
  this.logger.error('changeConsumerPreferredLayers()', {
276
285
  peer: this,
@@ -424,6 +433,14 @@ class Peer {
424
433
  this.outgoingConnectionQuality = Peer.getConnectionQualityByScore(score);
425
434
  this.emitConnectionQuality();
426
435
  });
436
+
437
+ this.observer.on(MEDIASOUP_EVENTS.consumerChangePreferredLayers, (payload: ChangePreferredLayersPayload) => {
438
+ const consumer = this.consumers.get(payload.consumerId);
439
+ if (consumer instanceof VideoConsumer) {
440
+ consumer.setCurrentTemporalLayer(payload.temporalLayer);
441
+ consumer.setCurrentSpatialLayer(payload.spatialLayer);
442
+ }
443
+ });
427
444
  }
428
445
 
429
446
  private emitConnectionQuality(): void {
@@ -71,7 +71,7 @@ class MediaSoupEventHandler {
71
71
  });
72
72
 
73
73
  connection.on(MEDIASOUP_EVENTS.consumerChangePreferredLayers, ({
74
- peerId, spatialLayer, temporalLayer,
74
+ peerId, spatialLayer, temporalLayer, consumerId,
75
75
  }: ChangePreferredLayersParams) => {
76
76
  const peer = this.engine.peers.find((item) => item.id === peerId);
77
77
  if (!peer || peer.isMe) {
@@ -79,6 +79,7 @@ class MediaSoupEventHandler {
79
79
  }
80
80
 
81
81
  peer.observer.safeEmit(MEDIASOUP_EVENTS.consumerChangePreferredLayers, {
82
+ consumerId,
82
83
  spatialLayer,
83
84
  temporalLayer,
84
85
  });
@@ -1,5 +1,4 @@
1
1
  import { RtpCapabilities } from 'mediasoup-client/lib/RtpParameters';
2
- import { VIDEO_CONSTRAINS } from '../constants/videoConstrains';
3
2
  import {
4
3
  PeerResponse,
5
4
  JoinChannelParams,
@@ -193,32 +192,26 @@ class Engine {
193
192
  }
194
193
  }
195
194
 
196
- async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track | null> {
195
+ async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track> {
196
+ const trackParams = Media.getCameraVideoTrackParams(options);
197
197
  try {
198
198
  const track = await this.media.createUserMediaTrack({
199
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
- },
200
+ video: trackParams.videoTrackOptions,
206
201
  });
207
202
 
208
203
  track.setLabel(TrackLabel.Camera);
209
- if (options?.encoderConfig) {
210
- track.setEncoderConfig(options.encoderConfig);
211
- }
204
+ track.setEncoderConfig(trackParams.encoderConfig);
212
205
 
213
- this.logger.debug('createCameraVideoTrack()', { options, track });
206
+ this.logger.debug('createCameraVideoTrack()', { trackParams, track });
214
207
  return track;
215
208
  } catch (error) {
216
- this.logger.error('createCameraVideoTrack()', { error, options });
217
- return null;
209
+ this.logger.error('createCameraVideoTrack()', { error, trackParams });
210
+ throw new Error(`Can not create camera track: ${error.message}`);
218
211
  }
219
212
  }
220
213
 
221
- async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track | null> {
214
+ async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track> {
222
215
  try {
223
216
  const track = await this.media.createUserMediaTrack({
224
217
  video: false,
@@ -236,19 +229,16 @@ class Engine {
236
229
  return track;
237
230
  } catch (error) {
238
231
  this.logger.error('createMicrophoneAudioTrack()', { error, options });
239
- return null;
232
+ throw new Error(`Can not create microphone track: ${error.message}`);
240
233
  }
241
234
  }
242
235
 
243
- async createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track | null> {
236
+ async createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track> {
237
+ const trackParams = Media.getScreenVideoTrackParams(options);
244
238
  try {
245
239
  const track = await this.media.createDisplayMediaTrack({
246
240
  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
- },
241
+ video: trackParams.videoTrackOptions,
252
242
  });
253
243
 
254
244
  track.mediaStreamTrack.addEventListener('ended', () => {
@@ -256,19 +246,17 @@ class Engine {
256
246
  });
257
247
 
258
248
  track.setLabel(TrackLabel.ScreenVideo);
259
- if (options?.encoderConfig) {
260
- track.setEncoderConfig(options.encoderConfig);
261
- }
249
+ track.setEncoderConfig(trackParams.encoderConfig);
262
250
 
263
- this.logger.debug('createScreenVideoTrack()', { options, track });
251
+ this.logger.debug('createScreenVideoTrack()', { trackParams, track });
264
252
  return track;
265
253
  } catch (error) {
266
- this.logger.error('createScreenVideoTrack()', { error, options });
267
- return null;
254
+ this.logger.error('createScreenVideoTrack()', { error, trackParams });
255
+ throw new Error(`Can not create screen video track: ${error.message}`);
268
256
  }
269
257
  }
270
258
 
271
- async createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track | null> {
259
+ async createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track> {
272
260
  try {
273
261
  const track = await this.media.createDisplayMediaTrack({
274
262
  video: false,
@@ -288,7 +276,7 @@ class Engine {
288
276
  return track;
289
277
  } catch (error) {
290
278
  this.logger.error('createScreenAudioTrack()', { error, options });
291
- return null;
279
+ throw new Error(`Can not create screen audio track: ${error.message}`);
292
280
  }
293
281
  }
294
282
 
@@ -329,7 +317,7 @@ class Engine {
329
317
  label: track.getLabel(),
330
318
  } as StartTrackPayload);
331
319
 
332
- this.logger.debug('publish()', { track });
320
+ this.logger.debug('publish()', { track, encodings, codecOptions });
333
321
  });
334
322
 
335
323
  try {
@@ -1,14 +1,22 @@
1
1
  import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
2
2
  import { parseScalabilityMode } from 'mediasoup-client';
3
3
  import Consumer from './Consumer';
4
+ import Logger from '../Logger';
4
5
 
5
6
  class VideoConsumer extends Consumer {
6
7
  public spatialLayers = 0;
7
8
 
8
9
  public temporalLayers = 0;
9
10
 
11
+ public currentSpatialLayer = 0;
12
+
13
+ public currentTemporalLayer = 0;
14
+
15
+ private readonly logger: Logger;
16
+
10
17
  constructor(consumer: MediasoupConsumer) {
11
18
  super(consumer);
19
+ this.logger = new Logger('VideoConsumer');
12
20
  this.parseScalabilityMode();
13
21
  }
14
22
 
@@ -18,8 +26,23 @@ class VideoConsumer extends Consumer {
18
26
  const { spatialLayers, temporalLayers } = parseScalabilityMode(scalabilityMode);
19
27
  this.spatialLayers = spatialLayers;
20
28
  this.temporalLayers = temporalLayers;
29
+ this.logger.debug('parseScalabilityMode()', {
30
+ scalabilityMode,
31
+ spatialLayers,
32
+ temporalLayers,
33
+ });
21
34
  }
22
35
  }
36
+
37
+ setCurrentSpatialLayer(currentSpatialLayer: number): void {
38
+ this.currentSpatialLayer = currentSpatialLayer;
39
+ this.logger.debug('setCurrentSpatialLayer()', { currentSpatialLayer });
40
+ }
41
+
42
+ setCurrentTemporalLayer(currentTemporalLayer: number): void {
43
+ this.currentTemporalLayer = currentTemporalLayer;
44
+ this.logger.debug('setCurrentTemporalLayer()', { currentTemporalLayer });
45
+ }
23
46
  }
24
47
 
25
48
  export default VideoConsumer;
@@ -2,8 +2,10 @@ import { Device } from 'mediasoup-client';
2
2
  import { RtpCapabilities, RtpCodecCapability } from 'mediasoup-client/lib/RtpParameters';
3
3
  import VideoTrack from './tracks/VideoTrack';
4
4
  import AudioTrack from './tracks/AudioTrack';
5
- import { Track } from '../../types/common';
5
+ import { CreateScreenVideoTrackOptions, CreateVideoTrackParams, Track } from '../../types/common';
6
6
  import Logger from '../Logger';
7
+ import { VIDEO_CONSTRAINS } from '../../constants/videoConstrains';
8
+ import { SCREEN_SHARING_SIMULCAST_ENCODINGS, WEBCAM_SIMULCAST_ENCODINGS } from '../../constants/simulcastEncodings';
7
9
 
8
10
  class Media {
9
11
  public isDeviceLoaded = false;
@@ -78,6 +80,33 @@ class Media {
78
80
  getAllTracks(): Track[] {
79
81
  return Array.from(this.tracks.values());
80
82
  }
83
+
84
+ static getScreenVideoTrackParams(options?: CreateScreenVideoTrackOptions): CreateVideoTrackParams {
85
+ return {
86
+ videoTrackOptions: {
87
+ frameRate: { ideal: options?.frameRate || 30 },
88
+ width: { ideal: options?.width || VIDEO_CONSTRAINS.fullhd.width },
89
+ height: { ideal: options?.height || VIDEO_CONSTRAINS.fullhd.height },
90
+ },
91
+ encoderConfig: options?.encoderConfig || {
92
+ encodings: SCREEN_SHARING_SIMULCAST_ENCODINGS,
93
+ },
94
+ };
95
+ }
96
+
97
+ static getCameraVideoTrackParams(options?: CreateScreenVideoTrackOptions): CreateVideoTrackParams {
98
+ return {
99
+ videoTrackOptions: {
100
+ deviceId: options?.deviceId,
101
+ frameRate: { ideal: options?.frameRate || 30 },
102
+ width: { ideal: options?.width || VIDEO_CONSTRAINS.hd.width },
103
+ height: { ideal: options?.height || VIDEO_CONSTRAINS.hd.height },
104
+ },
105
+ encoderConfig: options?.encoderConfig || {
106
+ encodings: WEBCAM_SIMULCAST_ENCODINGS,
107
+ },
108
+ };
109
+ }
81
110
  }
82
111
 
83
112
  export default Media;
package/src/index.ts CHANGED
@@ -88,19 +88,19 @@ class Client {
88
88
  return this.engine.resume(track);
89
89
  }
90
90
 
91
- createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track | null> {
91
+ createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track> {
92
92
  return this.engine.createCameraVideoTrack(options);
93
93
  }
94
94
 
95
- createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track | null> {
95
+ createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track> {
96
96
  return this.engine.createMicrophoneAudioTrack(options);
97
97
  }
98
98
 
99
- createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track | null> {
99
+ createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track> {
100
100
  return this.engine.createScreenVideoTrack(options);
101
101
  }
102
102
 
103
- createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track | null> {
103
+ createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track> {
104
104
  return this.engine.createScreenAudioTrack(options);
105
105
  }
106
106
 
@@ -186,3 +186,19 @@ export type PayloadOfPublishedMedia = {
186
186
  };
187
187
 
188
188
  export type PayloadOfUnpublishedMedia = PayloadOfPublishedMedia;
189
+
190
+ export type ChangePreferredLayersPayload = PreferredLayersParams & {
191
+ consumerId: string,
192
+ spatialLayer: number,
193
+ temporalLayer: number,
194
+ };
195
+
196
+ export type CreateVideoTrackParams = {
197
+ videoTrackOptions: {
198
+ deviceId?: string,
199
+ width?: { max?: number, min?: number, ideal?: number };
200
+ height?: { max?: number, min?: number, ideal?: number };
201
+ frameRate?: { max?: number, min?: number, ideal?: number };
202
+ },
203
+ encoderConfig: VideoEncoderConfig,
204
+ };