@livedigital/client 3.8.0 → 3.9.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.
@@ -102,7 +102,7 @@ export declare enum ConnectionQuality {
102
102
  GOOD = 3
103
103
  }
104
104
  export declare type EncoderConfig = {};
105
- export declare type VideoCodec = 'h264' | 'vp8';
105
+ export declare type VideoCodec = 'h264' | 'vp8' | 'vp9';
106
106
  export declare type AudioCodec = 'opus';
107
107
  export declare type VideoEncoderConfig = EncoderConfig & {
108
108
  preferredCodec?: VideoCodec;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "3.8.0",
5
+ "version": "3.9.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -62,7 +62,7 @@ class PeerConsumer {
62
62
  });
63
63
  this.currentMaxSpatialLayer = this.appData.producerData.maxSpatialLayer;
64
64
  this.parseScalabilityMode();
65
- this.setSpatialLayersParams();
65
+ this.initAvailableSpatialLayers();
66
66
  this.setCurrentSpatialLayerParams();
67
67
  }
68
68
 
@@ -104,26 +104,46 @@ class PeerConsumer {
104
104
  }
105
105
  }
106
106
 
107
- setSpatialLayersParams(): void {
108
- const { encodings } = this.appData.producerData;
107
+ initAvailableSpatialLayers(): void {
108
+ const { encodings, trackTransformParams } = this.appData.producerData;
109
109
  if (!encodings) {
110
110
  return;
111
111
  }
112
112
 
113
- this.availableSpatialLayers = encodings.map((encoding) => this.parseSpatialLayerParams(encoding));
113
+ this.availableSpatialLayers = encodings.map((encoding) => this.tryPrepareSpatialLayerParams(encoding));
114
+
115
+ if (!trackTransformParams || this.availableSpatialLayers.length > 1 || this.spatialLayers < 2) {
116
+ this.logger.debug('Using available spatial layers', { case: 'initAvailableSpatialLayers' });
117
+ return;
118
+ }
119
+
120
+ this.logger.debug('Pre-generating encodings with spatial layer params', { case: 'initAvailableSpatialLayers' });
121
+
122
+ const preGeneratedEncodings: SpatialLayerParams[] = [];
123
+ const baseEncoding = encodings[encodings.length - 1];
124
+
125
+ // imitate encodings with spatial layers params for automatic quality switching on client side
126
+ for (let i = this.spatialLayers - 1; i >= 0; i -= 1) {
127
+ preGeneratedEncodings.push(this.tryPrepareSpatialLayerParams({
128
+ ...baseEncoding,
129
+ scaleResolutionDownBy: 2 ** i,
130
+ }));
131
+ }
132
+
133
+ this.availableSpatialLayers = preGeneratedEncodings;
114
134
  }
115
135
 
116
136
  setCurrentSpatialLayerParams(): void {
117
137
  const { encodings } = this.appData.producerData;
118
- if (!encodings) {
138
+ if (!encodings || !encodings[this.currentSpatialLayer]) {
119
139
  return;
120
140
  }
121
141
 
122
- this.currentSpatialLayerParams = this.parseSpatialLayerParams(encodings[this.currentSpatialLayer]);
142
+ this.currentSpatialLayerParams = this.tryPrepareSpatialLayerParams(encodings[this.currentSpatialLayer]);
123
143
  this.logger.debug('setCurrentSpatialLayerParams()', { currentSpatialLayerParams: this.currentSpatialLayerParams });
124
144
  }
125
145
 
126
- parseSpatialLayerParams(encoding: RtpEncodingParameters): SpatialLayerParams {
146
+ tryPrepareSpatialLayerParams(encoding: RtpEncodingParameters): SpatialLayerParams {
127
147
  const { trackTransformParams } = this.appData.producerData;
128
148
  if (!trackTransformParams) {
129
149
  return encoding;
@@ -131,6 +151,7 @@ class PeerConsumer {
131
151
 
132
152
  const { width, height } = trackTransformParams;
133
153
  const coefficient = encoding.scaleResolutionDownBy || 1;
154
+
134
155
  if (width && height) {
135
156
  return {
136
157
  width: width / coefficient,
@@ -53,6 +53,7 @@ import {
53
53
  MediaTracksFactory, AudioTrack, Track, BaseTrack,
54
54
  } from '../types/media';
55
55
  import ChannelStateSyncEventHandler from './handlers/ChannelStateSyncEventHandler';
56
+ import { getSupportedCodecsMimeTypes } from '../helpers/media';
56
57
 
57
58
  type EngineParams = {
58
59
  clientEventEmitter: EnhancedEventEmitter;
@@ -205,6 +206,11 @@ class Engine {
205
206
  .request('router.getRtpCapabilities') as { rtpCapabilities: RtpCapabilities };
206
207
  await this.media.createNoiseSuppressor();
207
208
  await this.media.loadDevice(rtpCapabilities);
209
+
210
+ this.logger.info('Supported capabilities', {
211
+ codecs: getSupportedCodecsMimeTypes(this.media.mediasoupDevice),
212
+ });
213
+
208
214
  this.webRtcIssueDetector?.watchNewPeerConnections();
209
215
  await this.network.createSendTransport(this.media.mediasoupDevice);
210
216
  await this.network.createRecvTransport(this.media.mediasoupDevice);
@@ -233,7 +233,7 @@ class PeerTrack {
233
233
 
234
234
  try {
235
235
  if (this.consumer.isAudio) {
236
- this.#logger.warn('requestVideoPreferredLayers()', { message: 'Only video consumers support' });
236
+ this.#logger.warn('Only video consumers support layers', { case: 'requestVideoPreferredLayers' });
237
237
  return;
238
238
  }
239
239
 
@@ -242,7 +242,11 @@ class PeerTrack {
242
242
  }
243
243
 
244
244
  if (spatialLayer > this.consumer.spatialLayers) {
245
- this.#logger.warn('requestVideoPreferredLayers()', { message: 'Invalid spatial layer' });
245
+ this.#logger.warn('Invalid spatial layer', {
246
+ spatialLayer,
247
+ consumerSpatialLayers: this.consumer.spatialLayers,
248
+ case: 'requestVideoPreferredLayers',
249
+ });
246
250
  return;
247
251
  }
248
252
 
@@ -264,9 +268,16 @@ class PeerTrack {
264
268
  temporalLayer,
265
269
  });
266
270
  } catch (error) {
267
- this.#logger.error('requestVideoPreferredLayers()', {
268
- peer: this,
271
+ this.#logger.error('Failed to request preferred layers', {
272
+ case: 'requestVideoPreferredLayers',
273
+ track: {
274
+ id: this.mediaStreamTrack.id,
275
+ kind: this.mediaStreamTrack.kind,
276
+ label: this.mediaStreamTrack.label,
277
+ },
269
278
  consumerId: this.consumer.id,
279
+ spatialLayers: this.consumer.spatialLayers,
280
+ temporalLayers: this.consumer.temporalLayers,
270
281
  error,
271
282
  });
272
283
  throw new Error('Error request preferred layers');
@@ -301,7 +312,7 @@ class PeerTrack {
301
312
  }
302
313
 
303
314
  if (this.consumer.isAudio) {
304
- this.#logger.warn('getVideoAvailableLayers()', { message: 'Only video consumers support' });
315
+ this.#logger.warn('Only video consumers support layers', { case: 'getVideoAvailableLayers' });
305
316
  return undefined;
306
317
  }
307
318
 
@@ -353,12 +364,11 @@ class PeerTrack {
353
364
  || spatialLayer < this.consumer.currentMaxSpatialLayer;
354
365
 
355
366
  if (cantRequest) {
356
- this.#logger.warn('requestMaxSpatialLayer()', {
357
- message: 'Can`t request this spatial layer',
367
+ this.#logger.warn('Can`t request this spatial layer', {
368
+ case: 'requestMaxSpatialLayer',
358
369
  consumerId,
359
370
  spatialLayer,
360
371
  });
361
-
362
372
  return;
363
373
  }
364
374
 
@@ -373,8 +383,8 @@ class PeerTrack {
373
383
  spatialLayer,
374
384
  });
375
385
  } catch (error) {
376
- this.#logger.warn('requestMaxSpatialLayer()', {
377
- message: 'Error request spatial layer',
386
+ this.#logger.warn('Failed to request max spatial layer', {
387
+ case: 'requestMaxSpatialLayer',
378
388
  consumerId,
379
389
  spatialLayer,
380
390
  error,
@@ -1,5 +1,6 @@
1
- import { types } from 'mediasoup-client';
1
+ import { Device, types } from 'mediasoup-client';
2
2
  import Bowser from 'bowser';
3
+ import { RtpCodecCapability } from 'mediasoup-client/lib/RtpParameters';
3
4
 
4
5
  // eslint-disable-next-line import/prefer-default-export
5
6
  export const detectHandlerName = (userAgent?: string): types.BuiltinHandlerName | undefined => {
@@ -24,7 +25,7 @@ export const detectHandlerName = (userAgent?: string): types.BuiltinHandlerName
24
25
 
25
26
  export const getNewAudioContext = (contextOptions?: AudioContextOptions): AudioContext | null => {
26
27
  const Context = window.AudioContext // Default
27
- || (window as unknown as { webkitAudioContext : any }).webkitAudioContext // Safari and old versions of Chrome
28
+ || (window as unknown as { webkitAudioContext: any }).webkitAudioContext // Safari and old versions of Chrome
28
29
  || false;
29
30
 
30
31
  if (Context) {
@@ -33,3 +34,16 @@ export const getNewAudioContext = (contextOptions?: AudioContextOptions): AudioC
33
34
 
34
35
  return null;
35
36
  };
37
+
38
+ export const getSupportedCodecsMimeTypes = (device: Device): string[] => device
39
+ .rtpCapabilities
40
+ .codecs
41
+ ?.reduce((acc: string[], codec: RtpCodecCapability) => {
42
+ const preparedCodec = codec.mimeType?.toLowerCase();
43
+
44
+ if (!acc.includes(preparedCodec)) {
45
+ acc.push(preparedCodec);
46
+ }
47
+
48
+ return acc;
49
+ }, []) || [];
@@ -128,7 +128,7 @@ export enum ConnectionQuality {
128
128
 
129
129
  export type EncoderConfig = {};
130
130
 
131
- export type VideoCodec = 'h264' | 'vp8';
131
+ export type VideoCodec = 'h264' | 'vp8' | 'vp9';
132
132
 
133
133
  export type AudioCodec = 'opus';
134
134