livekit-client 0.16.6 → 0.17.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 (94) hide show
  1. package/dist/api/RequestQueue.js +6 -6
  2. package/dist/api/RequestQueue.js.map +1 -1
  3. package/dist/api/SignalClient.d.ts +3 -1
  4. package/dist/api/SignalClient.js +47 -5
  5. package/dist/api/SignalClient.js.map +1 -1
  6. package/dist/connect.js +1 -1
  7. package/dist/connect.js.map +1 -1
  8. package/dist/options.d.ts +7 -2
  9. package/dist/proto/livekit_models.d.ts +33 -0
  10. package/dist/proto/livekit_models.js +213 -3
  11. package/dist/proto/livekit_models.js.map +1 -1
  12. package/dist/proto/livekit_rtc.d.ts +15 -1
  13. package/dist/proto/livekit_rtc.js +128 -2
  14. package/dist/proto/livekit_rtc.js.map +1 -1
  15. package/dist/room/RTCEngine.d.ts +4 -3
  16. package/dist/room/RTCEngine.js +34 -13
  17. package/dist/room/RTCEngine.js.map +1 -1
  18. package/dist/room/Room.js +27 -12
  19. package/dist/room/Room.js.map +1 -1
  20. package/dist/room/events.d.ts +6 -1
  21. package/dist/room/events.js +6 -1
  22. package/dist/room/events.js.map +1 -1
  23. package/dist/room/participant/LocalParticipant.d.ts +3 -1
  24. package/dist/room/participant/LocalParticipant.js +24 -1
  25. package/dist/room/participant/LocalParticipant.js.map +1 -1
  26. package/dist/room/participant/RemoteParticipant.d.ts +2 -1
  27. package/dist/room/participant/RemoteParticipant.js +3 -3
  28. package/dist/room/participant/RemoteParticipant.js.map +1 -1
  29. package/dist/room/participant/publishUtils.d.ts +6 -0
  30. package/dist/room/participant/publishUtils.js +65 -24
  31. package/dist/room/participant/publishUtils.js.map +1 -1
  32. package/dist/room/participant/publishUtils.test.js +35 -5
  33. package/dist/room/participant/publishUtils.test.js.map +1 -1
  34. package/dist/room/track/LocalAudioTrack.d.ts +2 -0
  35. package/dist/room/track/LocalAudioTrack.js +23 -0
  36. package/dist/room/track/LocalAudioTrack.js.map +1 -1
  37. package/dist/room/track/LocalTrack.d.ts +4 -0
  38. package/dist/room/track/LocalTrack.js +35 -0
  39. package/dist/room/track/LocalTrack.js.map +1 -1
  40. package/dist/room/track/LocalVideoTrack.d.ts +1 -0
  41. package/dist/room/track/LocalVideoTrack.js +13 -0
  42. package/dist/room/track/LocalVideoTrack.js.map +1 -1
  43. package/dist/room/track/RemoteTrack.d.ts +1 -0
  44. package/dist/room/track/RemoteTrack.js +2 -0
  45. package/dist/room/track/RemoteTrack.js.map +1 -1
  46. package/dist/room/track/RemoteVideoTrack.d.ts +4 -2
  47. package/dist/room/track/RemoteVideoTrack.js +28 -11
  48. package/dist/room/track/RemoteVideoTrack.js.map +1 -1
  49. package/dist/room/track/Track.d.ts +5 -1
  50. package/dist/room/track/Track.js +20 -1
  51. package/dist/room/track/Track.js.map +1 -1
  52. package/dist/room/track/create.js +1 -0
  53. package/dist/room/track/create.js.map +1 -1
  54. package/dist/room/track/defaults.js +2 -2
  55. package/dist/room/track/defaults.js.map +1 -1
  56. package/dist/room/track/options.d.ts +65 -15
  57. package/dist/room/track/options.js +38 -0
  58. package/dist/room/track/options.js.map +1 -1
  59. package/dist/room/track/types.d.ts +11 -0
  60. package/dist/room/track/utils.d.ts +10 -0
  61. package/dist/room/track/utils.js +46 -1
  62. package/dist/room/track/utils.js.map +1 -1
  63. package/dist/room/utils.d.ts +2 -0
  64. package/dist/room/utils.js +9 -1
  65. package/dist/room/utils.js.map +1 -1
  66. package/dist/version.d.ts +1 -1
  67. package/dist/version.js +1 -1
  68. package/package.json +2 -2
  69. package/src/api/RequestQueue.ts +7 -7
  70. package/src/api/SignalClient.ts +36 -6
  71. package/src/connect.ts +1 -1
  72. package/src/options.ts +12 -3
  73. package/src/proto/livekit_models.ts +249 -0
  74. package/src/proto/livekit_rtc.ts +155 -0
  75. package/src/room/RTCEngine.ts +39 -16
  76. package/src/room/Room.ts +27 -13
  77. package/src/room/events.ts +6 -1
  78. package/src/room/participant/LocalParticipant.ts +31 -4
  79. package/src/room/participant/RemoteParticipant.ts +4 -4
  80. package/src/room/participant/publishUtils.test.ts +46 -6
  81. package/src/room/participant/publishUtils.ts +72 -27
  82. package/src/room/track/LocalAudioTrack.ts +19 -1
  83. package/src/room/track/LocalTrack.ts +37 -0
  84. package/src/room/track/LocalVideoTrack.ts +9 -1
  85. package/src/room/track/RemoteTrack.ts +3 -0
  86. package/src/room/track/RemoteVideoTrack.ts +25 -10
  87. package/src/room/track/Track.ts +16 -2
  88. package/src/room/track/create.ts +1 -0
  89. package/src/room/track/defaults.ts +2 -2
  90. package/src/room/track/options.ts +55 -3
  91. package/src/room/track/types.ts +12 -0
  92. package/src/room/track/utils.ts +39 -0
  93. package/src/room/utils.ts +8 -0
  94. package/src/version.ts +1 -1
@@ -1,9 +1,10 @@
1
1
  import log from '../../logger';
2
+ import { TrackEvent } from '../events';
2
3
  import { AudioSenderStats, computeBitrate, monitorFrequency } from '../stats';
3
4
  import LocalTrack from './LocalTrack';
4
5
  import { AudioCaptureOptions } from './options';
5
6
  import { Track } from './Track';
6
- import { constraintsForOptions } from './utils';
7
+ import { constraintsForOptions, detectSilence } from './utils';
7
8
 
8
9
  export default class LocalAudioTrack extends LocalTrack {
9
10
  sender?: RTCRtpSender;
@@ -18,6 +19,7 @@ export default class LocalAudioTrack extends LocalTrack {
18
19
  constraints?: MediaTrackConstraints,
19
20
  ) {
20
21
  super(mediaTrack, Track.Kind.Audio, constraints);
22
+ this.checkForSilence();
21
23
  }
22
24
 
23
25
  async setDeviceId(deviceId: string) {
@@ -61,6 +63,12 @@ export default class LocalAudioTrack extends LocalTrack {
61
63
  await this.restart(constraints);
62
64
  }
63
65
 
66
+ protected async restart(constraints?: MediaTrackConstraints): Promise<LocalTrack> {
67
+ const track = await super.restart(constraints);
68
+ this.checkForSilence();
69
+ return track;
70
+ }
71
+
64
72
  /* @internal */
65
73
  startMonitor() {
66
74
  setTimeout(() => {
@@ -116,4 +124,14 @@ export default class LocalAudioTrack extends LocalTrack {
116
124
 
117
125
  return audioStats;
118
126
  }
127
+
128
+ async checkForSilence() {
129
+ const trackIsSilent = await detectSilence(this);
130
+ if (trackIsSilent) {
131
+ if (!this.isMuted) {
132
+ log.warn('silence detected on local audio track');
133
+ }
134
+ this.emit(TrackEvent.AudioSilenceDetected);
135
+ }
136
+ }
119
137
  }
@@ -2,6 +2,7 @@ import log from '../../logger';
2
2
  import DeviceManager from '../DeviceManager';
3
3
  import { TrackInvalidError } from '../errors';
4
4
  import { TrackEvent } from '../events';
5
+ import { isMobile } from '../utils';
5
6
  import { attachToElement, detachTrack, Track } from './Track';
6
7
 
7
8
  export default class LocalTrack extends Track {
@@ -10,12 +11,18 @@ export default class LocalTrack extends Track {
10
11
 
11
12
  protected constraints: MediaTrackConstraints;
12
13
 
14
+ protected wasMuted: boolean;
15
+
16
+ protected reacquireTrack: boolean;
17
+
13
18
  protected constructor(
14
19
  mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints,
15
20
  ) {
16
21
  super(mediaTrack, kind);
17
22
  this.mediaStreamTrack.addEventListener('ended', this.handleEnded);
18
23
  this.constraints = constraints ?? mediaTrack.getConstraints();
24
+ this.reacquireTrack = false;
25
+ this.wasMuted = false;
19
26
  }
20
27
 
21
28
  get id(): string {
@@ -104,6 +111,7 @@ export default class LocalTrack extends Track {
104
111
  attachToElement(newTrack, el);
105
112
  });
106
113
 
114
+ this.mediaStream = mediaStream;
107
115
  this.constraints = constraints;
108
116
  return this;
109
117
  }
@@ -118,7 +126,36 @@ export default class LocalTrack extends Track {
118
126
  this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
119
127
  }
120
128
 
129
+ protected get needsReAcquisition(): boolean {
130
+ return this.mediaStreamTrack.readyState !== 'live'
131
+ || this.mediaStreamTrack.muted
132
+ || !this.mediaStreamTrack.enabled
133
+ || this.reacquireTrack;
134
+ }
135
+
136
+ protected async handleAppVisibilityChanged() {
137
+ await super.handleAppVisibilityChanged();
138
+ if (!isMobile()) return;
139
+ log.debug('visibility changed, is in Background: ', this.isInBackground);
140
+
141
+ if (!this.isInBackground && this.needsReAcquisition) {
142
+ log.debug('track needs to be reaquired, restarting', this.source);
143
+ await this.restart();
144
+ this.reacquireTrack = false;
145
+ // Restore muted state if had to be restarted
146
+ this.setTrackMuted(this.wasMuted);
147
+ }
148
+
149
+ // store muted state each time app goes to background
150
+ if (this.isInBackground) {
151
+ this.wasMuted = this.isMuted;
152
+ }
153
+ }
154
+
121
155
  private handleEnded = () => {
156
+ if (this.isInBackground) {
157
+ this.reacquireTrack = true;
158
+ }
122
159
  this.emit(TrackEvent.Ended, this);
123
160
  };
124
161
  }
@@ -3,7 +3,7 @@ import log from '../../logger';
3
3
  import { VideoLayer, VideoQuality } from '../../proto/livekit_models';
4
4
  import { SubscribedQuality } from '../../proto/livekit_rtc';
5
5
  import { computeBitrate, monitorFrequency, VideoSenderStats } from '../stats';
6
- import { isFireFox } from '../utils';
6
+ import { isFireFox, isMobile } from '../utils';
7
7
  import LocalTrack from './LocalTrack';
8
8
  import { VideoCaptureOptions } from './options';
9
9
  import { Track } from './Track';
@@ -238,6 +238,14 @@ export default class LocalVideoTrack extends LocalTrack {
238
238
  this.monitorSender();
239
239
  }, monitorFrequency);
240
240
  };
241
+
242
+ protected async handleAppVisibilityChanged() {
243
+ await super.handleAppVisibilityChanged();
244
+ if (!isMobile()) return;
245
+ if (this.isInBackground && this.source === Track.Source.Camera) {
246
+ this.mediaStreamTrack.enabled = false;
247
+ }
248
+ }
241
249
  }
242
250
 
243
251
  export function videoQualityForRid(rid: string): VideoQuality {
@@ -6,6 +6,8 @@ export default abstract class RemoteTrack extends Track {
6
6
  /** @internal */
7
7
  receiver?: RTCRtpReceiver;
8
8
 
9
+ streamState: Track.StreamState = Track.StreamState.Active;
10
+
9
11
  constructor(
10
12
  mediaTrack: MediaStreamTrack,
11
13
  sid: string,
@@ -30,6 +32,7 @@ export default abstract class RemoteTrack extends Track {
30
32
  // this is needed to determine when the track is finished
31
33
  // we send each track down in its own MediaStream, so we can assume the
32
34
  // current track is the only one that can be removed.
35
+ this.mediaStream = stream;
33
36
  stream.onremovetrack = () => {
34
37
  this.receiver = undefined;
35
38
  this._currentBitrate = 0;
@@ -1,9 +1,12 @@
1
1
  import { debounce } from 'ts-debounce';
2
2
  import { TrackEvent } from '../events';
3
3
  import { computeBitrate, monitorFrequency, VideoReceiverStats } from '../stats';
4
- import { getIntersectionObserver, getResizeObserver, ObservableMediaElement } from '../utils';
4
+ import {
5
+ getIntersectionObserver, getResizeObserver, isMobile, ObservableMediaElement,
6
+ } from '../utils';
5
7
  import RemoteTrack from './RemoteTrack';
6
8
  import { attachToElement, detachTrack, Track } from './Track';
9
+ import { AdaptiveStreamSettings } from './types';
7
10
 
8
11
  const REACTION_DELAY = 100;
9
12
 
@@ -15,7 +18,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
15
18
 
16
19
  private elementInfos: ElementInfo[] = [];
17
20
 
18
- private adaptiveStream?: boolean;
21
+ private adaptiveStreamSettings?: AdaptiveStreamSettings;
19
22
 
20
23
  private lastVisible?: boolean;
21
24
 
@@ -25,14 +28,14 @@ export default class RemoteVideoTrack extends RemoteTrack {
25
28
  mediaTrack: MediaStreamTrack,
26
29
  sid: string,
27
30
  receiver?: RTCRtpReceiver,
28
- adaptiveStream?: boolean,
31
+ adaptiveStreamSettings?: AdaptiveStreamSettings,
29
32
  ) {
30
33
  super(mediaTrack, sid, Track.Kind.Video, receiver);
31
- this.adaptiveStream = adaptiveStream;
34
+ this.adaptiveStreamSettings = adaptiveStreamSettings;
32
35
  }
33
36
 
34
37
  get isAdaptiveStream(): boolean {
35
- return this.adaptiveStream ?? false;
38
+ return this.adaptiveStreamSettings !== undefined;
36
39
  }
37
40
 
38
41
  /** @internal */
@@ -60,7 +63,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
60
63
 
61
64
  // It's possible attach is called multiple times on an element. When that's
62
65
  // the case, we'd want to avoid adding duplicate elementInfos
63
- if (this.adaptiveStream
66
+ if (this.adaptiveStreamSettings
64
67
  && this.elementInfos.find((info) => info.element === element) === undefined
65
68
  ) {
66
69
  this.elementInfos.push({
@@ -164,6 +167,14 @@ export default class RemoteVideoTrack extends RemoteTrack {
164
167
  this.updateVisibility();
165
168
  };
166
169
 
170
+ protected async handleAppVisibilityChanged() {
171
+ await super.handleAppVisibilityChanged();
172
+ if (!this.isAdaptiveStream) return;
173
+ // on desktop don't pause when tab is backgrounded
174
+ if (!isMobile()) return;
175
+ this.updateVisibility();
176
+ }
177
+
167
178
  private readonly debouncedHandleResize = debounce(() => {
168
179
  this.updateDimensions();
169
180
  }, REACTION_DELAY);
@@ -173,7 +184,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
173
184
  (prev, info) => Math.max(prev, info.visibilityChangedAt || 0),
174
185
  0,
175
186
  );
176
- const isVisible = this.elementInfos.some((info) => info.visible);
187
+ const isVisible = this.elementInfos.some((info) => info.visible) && !this.isInBackground;
177
188
 
178
189
  if (this.lastVisible === isVisible) {
179
190
  return;
@@ -195,9 +206,13 @@ export default class RemoteVideoTrack extends RemoteTrack {
195
206
  let maxWidth = 0;
196
207
  let maxHeight = 0;
197
208
  for (const info of this.elementInfos) {
198
- if (info.element.clientWidth + info.element.clientHeight > maxWidth + maxHeight) {
199
- maxWidth = info.element.clientWidth;
200
- maxHeight = info.element.clientHeight;
209
+ const pixelDensity = this.adaptiveStreamSettings?.pixelDensity ?? 1;
210
+ const pixelDensityValue = pixelDensity === 'screen' ? window.devicePixelRatio : pixelDensity;
211
+ const currentElementWidth = info.element.clientWidth * pixelDensityValue;
212
+ const currentElementHeight = info.element.clientHeight * pixelDensityValue;
213
+ if (currentElementWidth + currentElementHeight > maxWidth + maxHeight) {
214
+ maxWidth = currentElementWidth;
215
+ maxHeight = currentElementHeight;
201
216
  }
202
217
  }
203
218
 
@@ -12,16 +12,18 @@ const recycledElements: Array<HTMLAudioElement> = [];
12
12
  export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
13
13
  kind: Track.Kind;
14
14
 
15
+ mediaStream?: MediaStream;
16
+
15
17
  mediaStreamTrack: MediaStreamTrack;
16
18
 
17
19
  attachedElements: HTMLMediaElement[] = [];
18
20
 
19
21
  isMuted: boolean = false;
20
22
 
21
- streamState: Track.StreamState = Track.StreamState.Active;
22
-
23
23
  source: Track.Source;
24
24
 
25
+ protected isInBackground: boolean;
26
+
25
27
  /**
26
28
  * sid is set after track is published to server, or if it's a remote track
27
29
  */
@@ -34,6 +36,8 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
34
36
  this.kind = kind;
35
37
  this.mediaStreamTrack = mediaTrack;
36
38
  this.source = Track.Source.Unknown;
39
+ this.isInBackground = document.visibilityState === 'hidden';
40
+ document.addEventListener('visibilitychange', this.appVisibilityChangedListener);
37
41
  }
38
42
 
39
43
  /** current receive bits per second */
@@ -131,6 +135,7 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
131
135
 
132
136
  stop() {
133
137
  this.mediaStreamTrack.stop();
138
+ document.removeEventListener('visibilitychange', this.appVisibilityChangedListener);
134
139
  }
135
140
 
136
141
  protected enable() {
@@ -156,6 +161,14 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
156
161
  }
157
162
  }
158
163
  }
164
+
165
+ appVisibilityChangedListener = () => {
166
+ this.handleAppVisibilityChanged();
167
+ };
168
+
169
+ protected async handleAppVisibilityChanged() {
170
+ this.isInBackground = document.visibilityState === 'hidden';
171
+ }
159
172
  }
160
173
 
161
174
  /** @internal */
@@ -318,6 +331,7 @@ export type TrackEventCallbacks = {
318
331
  updateSubscription: () => void,
319
332
  audioPlaybackStarted: () => void,
320
333
  audioPlaybackFailed: (error: Error) => void,
334
+ audioSilenceDetected: () => void,
321
335
  visibilityChanged: (visible: boolean, track?: any) => void,
322
336
  videoDimensionsChanged: (dimensions: Track.Dimensions, track?: any) => void,
323
337
  };
@@ -47,6 +47,7 @@ export async function createLocalTracks(
47
47
  } else if (track.kind === Track.Kind.Audio) {
48
48
  track.source = Track.Source.Microphone;
49
49
  }
50
+ track.mediaStream = stream;
50
51
  return track;
51
52
  });
52
53
  }
@@ -7,7 +7,7 @@ export const publishDefaults: TrackPublishDefaults = {
7
7
  audioBitrate: AudioPresets.speech.maxBitrate,
8
8
  dtx: true,
9
9
  simulcast: true,
10
- screenShareEncoding: ScreenSharePresets.hd_15.encoding,
10
+ screenShareEncoding: ScreenSharePresets.h1080fps15.encoding,
11
11
  stopMicTrackOnMute: false,
12
12
  };
13
13
 
@@ -19,5 +19,5 @@ export const audioDefaults: AudioCaptureOptions = {
19
19
  };
20
20
 
21
21
  export const videoDefaults: VideoCaptureOptions = {
22
- resolution: VideoPresets.qhd.resolution,
22
+ resolution: VideoPresets.h540.resolution,
23
23
  };
@@ -33,6 +33,20 @@ export interface TrackPublishDefaults {
33
33
  */
34
34
  simulcast?: boolean;
35
35
 
36
+ /**
37
+ * custom video simulcast layers for camera tracks, defaults to h180, h360, h540
38
+ * You can specify up to two custom layers that will be used instead of
39
+ * the LiveKit default layers.
40
+ * Note: the layers need to be ordered from lowest to highest quality
41
+ */
42
+ videoSimulcastLayers?: Array<VideoPreset>;
43
+
44
+ /**
45
+ * custom video simulcast layers for screen tracks
46
+ * Note: the layers need to be ordered from lowest to highest quality
47
+ */
48
+ screenShareSimulcastLayers?: Array<VideoPreset>;
49
+
36
50
  /**
37
51
  * For local tracks, stop the underlying MediaStreamTrack when the track is muted (or paused)
38
52
  * on some platforms, this option is necessary to disable the microphone recording indicator.
@@ -202,28 +216,66 @@ export namespace AudioPresets {
202
216
  * Sane presets for video resolution/encoding
203
217
  */
204
218
  export const VideoPresets = {
219
+ h90: new VideoPreset(160, 90, 60_000, 15),
220
+ h180: new VideoPreset(320, 180, 120_000, 15),
221
+ h216: new VideoPreset(384, 216, 180_000, 15),
222
+ h360: new VideoPreset(640, 360, 300_000, 20),
223
+ h540: new VideoPreset(960, 540, 600_000, 25),
224
+ h720: new VideoPreset(1280, 720, 2_000_000, 30),
225
+ h1080: new VideoPreset(1920, 1080, 3_000_000, 30),
226
+ h1440: new VideoPreset(2560, 1440, 5_000_000, 30),
227
+ h2160: new VideoPreset(3840, 2160, 8_000_000, 30),
228
+ /** @deprecated */
205
229
  qvga: new VideoPreset(320, 180, 120_000, 10),
230
+ /** @deprecated */
206
231
  vga: new VideoPreset(640, 360, 300_000, 20),
232
+ /** @deprecated */
207
233
  qhd: new VideoPreset(960, 540, 600_000, 25),
234
+ /** @deprecated */
208
235
  hd: new VideoPreset(1280, 720, 2_000_000, 30),
236
+ /** @deprecated */
209
237
  fhd: new VideoPreset(1920, 1080, 3_000_000, 30),
210
- };
238
+ } as const;
211
239
 
212
240
  /**
213
241
  * Four by three presets
214
242
  */
215
243
  export const VideoPresets43 = {
244
+ h120: new VideoPreset(160, 120, 80_000, 15),
245
+ h180: new VideoPreset(240, 180, 100_000, 15),
246
+ h240: new VideoPreset(320, 240, 150_000, 15),
247
+ h360: new VideoPreset(480, 360, 225_000, 20),
248
+ h480: new VideoPreset(640, 480, 300_000, 20),
249
+ h540: new VideoPreset(720, 540, 450_000, 25),
250
+ h720: new VideoPreset(960, 720, 1_500_000, 30),
251
+ h1080: new VideoPreset(1440, 1080, 2_500_000, 30),
252
+ h1440: new VideoPreset(1920, 1440, 3_500_000, 30),
253
+ /** @deprecated */
216
254
  qvga: new VideoPreset(240, 180, 90_000, 10),
255
+ /** @deprecated */
217
256
  vga: new VideoPreset(480, 360, 225_000, 20),
257
+ /** @deprecated */
218
258
  qhd: new VideoPreset(720, 540, 450_000, 25),
259
+ /** @deprecated */
219
260
  hd: new VideoPreset(960, 720, 1_500_000, 30),
261
+ /** @deprecated */
220
262
  fhd: new VideoPreset(1440, 1080, 2_800_000, 30),
221
- };
263
+ } as const;
222
264
 
223
265
  export const ScreenSharePresets = {
266
+ h360fps3: new VideoPreset(640, 360, 200_000, 3),
267
+ h720fps5: new VideoPreset(1280, 720, 400_000, 5),
268
+ h720fps15: new VideoPreset(1280, 720, 1_000_000, 15),
269
+ h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15),
270
+ h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30),
271
+ /** @deprecated */
224
272
  vga: new VideoPreset(640, 360, 200_000, 3),
273
+ /** @deprecated */
225
274
  hd_8: new VideoPreset(1280, 720, 400_000, 5),
275
+ /** @deprecated */
226
276
  hd_15: new VideoPreset(1280, 720, 1_000_000, 15),
277
+ /** @deprecated */
227
278
  fhd_15: new VideoPreset(1920, 1080, 1_500_000, 15),
279
+ /** @deprecated */
228
280
  fhd_30: new VideoPreset(1920, 1080, 3_000_000, 30),
229
- };
281
+ } as const;
@@ -6,3 +6,15 @@ import type RemoteVideoTrack from './RemoteVideoTrack';
6
6
  export type RemoteTrack = RemoteAudioTrack | RemoteVideoTrack;
7
7
  export type AudioTrack = RemoteAudioTrack | LocalAudioTrack;
8
8
  export type VideoTrack = RemoteVideoTrack | LocalVideoTrack;
9
+
10
+ export type AdaptiveStreamSettings = {
11
+ /**
12
+ * Set a custom pixel density, defaults to 1
13
+ * When streaming videos on a ultra high definition screen this setting
14
+ * let's you account for the devicePixelRatio of those screens.
15
+ * Set it to `screen` to use the actual pixel density of the screen
16
+ * Note: this might significantly increase the bandwidth consumed by people
17
+ * streaming on high definition screens.
18
+ */
19
+ pixelDensity?: number | 'screen'
20
+ };
@@ -1,7 +1,9 @@
1
+ import { sleep } from '../utils';
1
2
  import {
2
3
  AudioCaptureOptions, CreateLocalTracksOptions,
3
4
  VideoCaptureOptions,
4
5
  } from './options';
6
+ import { AudioTrack } from './types';
5
7
 
6
8
  export function mergeDefaultOptions(
7
9
  options?: CreateLocalTracksOptions,
@@ -74,3 +76,40 @@ export function constraintsForOptions(options: CreateLocalTracksOptions): MediaS
74
76
  }
75
77
  return constraints;
76
78
  }
79
+ /**
80
+ * This function detects silence on a given [[Track]] instance.
81
+ * Returns true if the track seems to be entirely silent.
82
+ */
83
+ export async function detectSilence(
84
+ track: AudioTrack,
85
+ timeOffset = 200,
86
+ ): Promise<boolean> {
87
+ const ctx = getNewAudioContext();
88
+ if (ctx) {
89
+ const analyser = ctx.createAnalyser();
90
+ analyser.fftSize = 2048;
91
+
92
+ const bufferLength = analyser.frequencyBinCount;
93
+ const dataArray = new Uint8Array(bufferLength);
94
+ const source = ctx.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));
95
+
96
+ source.connect(analyser);
97
+ await sleep(timeOffset);
98
+ analyser.getByteTimeDomainData(dataArray);
99
+ const someNoise = dataArray.some((sample) => sample !== 128 && sample !== 0);
100
+ ctx.close();
101
+ return !someNoise;
102
+ }
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * @internal
108
+ */
109
+ export function getNewAudioContext(): AudioContext | void {
110
+ // @ts-ignore
111
+ const AudioContext = window.AudioContext || window.webkitAudioContext;
112
+ if (AudioContext) {
113
+ return new AudioContext();
114
+ }
115
+ }
package/src/room/utils.ts CHANGED
@@ -23,6 +23,14 @@ export function isSafari(): boolean {
23
23
  return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
24
24
  }
25
25
 
26
+ export function isMobile(): boolean {
27
+ return /Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent);
28
+ }
29
+
30
+ export function isWeb(): boolean {
31
+ return typeof document !== 'undefined';
32
+ }
33
+
26
34
  function roDispatchCallback(entries: ResizeObserverEntry[]) {
27
35
  for (const entry of entries) {
28
36
  (entry.target as ObservableMediaElement).handleResize(entry);
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '0.16.6';
1
+ export const version = '0.17.2';
2
2
  export const protocolVersion = 6;