livekit-client 0.18.5 → 1.0.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.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/dist/api/SignalClient.d.ts +2 -2
  3. package/dist/api/SignalClient.d.ts.map +1 -1
  4. package/dist/index.d.ts +3 -4
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/livekit-client.esm.mjs +257 -254
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/options.d.ts +1 -68
  11. package/dist/options.d.ts.map +1 -1
  12. package/dist/room/DeviceManager.d.ts.map +1 -1
  13. package/dist/room/RTCEngine.d.ts +3 -2
  14. package/dist/room/RTCEngine.d.ts.map +1 -1
  15. package/dist/room/Room.d.ts +11 -7
  16. package/dist/room/Room.d.ts.map +1 -1
  17. package/dist/room/events.d.ts +6 -12
  18. package/dist/room/events.d.ts.map +1 -1
  19. package/dist/room/participant/LocalParticipant.d.ts.map +1 -1
  20. package/dist/room/participant/Participant.d.ts +0 -4
  21. package/dist/room/participant/Participant.d.ts.map +1 -1
  22. package/dist/room/participant/RemoteParticipant.d.ts +3 -2
  23. package/dist/room/participant/RemoteParticipant.d.ts.map +1 -1
  24. package/dist/room/track/RemoteVideoTrack.d.ts +24 -1
  25. package/dist/room/track/RemoteVideoTrack.d.ts.map +1 -1
  26. package/dist/room/track/Track.d.ts +2 -1
  27. package/dist/room/track/Track.d.ts.map +1 -1
  28. package/dist/room/track/options.d.ts +0 -30
  29. package/dist/room/track/options.d.ts.map +1 -1
  30. package/dist/room/track/types.d.ts +5 -0
  31. package/dist/room/track/types.d.ts.map +1 -1
  32. package/dist/test/MockMediaStreamTrack.d.ts +26 -0
  33. package/dist/test/MockMediaStreamTrack.d.ts.map +1 -0
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.d.ts.map +1 -1
  36. package/package.json +4 -3
  37. package/src/api/SignalClient.ts +32 -7
  38. package/src/index.ts +4 -3
  39. package/src/options.ts +0 -82
  40. package/src/room/DeviceManager.ts +4 -1
  41. package/src/room/RTCEngine.ts +16 -7
  42. package/src/room/Room.ts +66 -42
  43. package/src/room/events.ts +7 -14
  44. package/src/room/participant/LocalParticipant.ts +4 -0
  45. package/src/room/participant/Participant.ts +0 -5
  46. package/src/room/participant/RemoteParticipant.ts +16 -5
  47. package/src/room/participant/publishUtils.test.ts +2 -2
  48. package/src/room/track/LocalVideoTrack.ts +1 -1
  49. package/src/room/track/RemoteVideoTrack.test.ts +149 -0
  50. package/src/room/track/RemoteVideoTrack.ts +118 -39
  51. package/src/room/track/Track.ts +18 -2
  52. package/src/room/track/create.ts +1 -1
  53. package/src/room/track/options.ts +1 -31
  54. package/src/room/track/types.ts +5 -0
  55. package/src/room/track/utils.test.ts +6 -6
  56. package/src/test/MockMediaStreamTrack.ts +83 -0
  57. package/src/version.ts +1 -1
  58. package/dist/connect.d.ts +0 -24
  59. package/dist/connect.d.ts.map +0 -1
  60. package/src/connect.ts +0 -98
@@ -28,11 +28,11 @@ describe('presetsForResolution', () => {
28
28
 
29
29
  describe('determineAppropriateEncoding', () => {
30
30
  it('uses higher encoding', () => {
31
- expect(determineAppropriateEncoding(false, 600, 300)).toEqual(VideoPresets.vga.encoding);
31
+ expect(determineAppropriateEncoding(false, 600, 300)).toEqual(VideoPresets.h360.encoding);
32
32
  });
33
33
 
34
34
  it('handles portrait', () => {
35
- expect(determineAppropriateEncoding(false, 300, 600)).toEqual(VideoPresets.vga.encoding);
35
+ expect(determineAppropriateEncoding(false, 300, 600)).toEqual(VideoPresets.h360.encoding);
36
36
  });
37
37
  });
38
38
 
@@ -89,7 +89,7 @@ export default class LocalVideoTrack extends LocalTrack {
89
89
  bytesSent: v.bytesSent,
90
90
  framesSent: v.framesSent,
91
91
  timestamp: v.timestamp,
92
- rid: v.rid ?? '',
92
+ rid: v.rid ?? v.id,
93
93
  retransmittedPacketsSent: v.retransmittedPacketsSent,
94
94
  qualityLimitationReason: v.qualityLimitationReason,
95
95
  qualityLimitationResolutionChanges: v.qualityLimitationResolutionChanges,
@@ -0,0 +1,149 @@
1
+ import { TrackEvent } from '../events';
2
+ import RemoteVideoTrack, { ElementInfo } from './RemoteVideoTrack';
3
+ import MockMediaStreamTrack from '../../test/MockMediaStreamTrack';
4
+ import { Track } from './Track';
5
+
6
+ jest.useFakeTimers();
7
+
8
+ describe('RemoteVideoTrack', () => {
9
+ let track: RemoteVideoTrack;
10
+
11
+ beforeEach(() => {
12
+ track = new RemoteVideoTrack(new MockMediaStreamTrack(), 'sid', undefined, {});
13
+ });
14
+ describe('element visibility', () => {
15
+ let events: boolean[] = [];
16
+
17
+ beforeEach(() => {
18
+ track.on(TrackEvent.VisibilityChanged, (visible) => {
19
+ events.push(visible);
20
+ });
21
+ });
22
+ afterEach(() => {
23
+ events = [];
24
+ });
25
+
26
+ it('emits a visibility event upon observing visible element', () => {
27
+ const elementInfo = new MockElementInfo();
28
+ elementInfo.visible = true;
29
+
30
+ track.observeElementInfo(elementInfo);
31
+
32
+ expect(events).toHaveLength(1);
33
+ expect(events[0]).toBeTruthy();
34
+ });
35
+
36
+ it('emits a visibility event upon element becoming visible', () => {
37
+ const elementInfo = new MockElementInfo();
38
+ track.observeElementInfo(elementInfo);
39
+
40
+ elementInfo.setVisible(true);
41
+
42
+ expect(events).toHaveLength(2);
43
+ expect(events[1]).toBeTruthy();
44
+ });
45
+
46
+ it('emits a visibility event upon removing only visible element', () => {
47
+ const elementInfo = new MockElementInfo();
48
+ elementInfo.visible = true;
49
+
50
+ track.observeElementInfo(elementInfo);
51
+ track.stopObservingElementInfo(elementInfo);
52
+
53
+ expect(events).toHaveLength(2);
54
+ expect(events[1]).toBeFalsy();
55
+ });
56
+ });
57
+
58
+ describe('element dimensions', () => {
59
+ let events: Track.Dimensions[] = [];
60
+
61
+ beforeEach(() => {
62
+ track.on(TrackEvent.VideoDimensionsChanged, (dimensions) => {
63
+ events.push(dimensions);
64
+ });
65
+ });
66
+
67
+ afterEach(() => {
68
+ events = [];
69
+ });
70
+
71
+ it('emits a dimensions event upon observing element', () => {
72
+ const elementInfo = new MockElementInfo();
73
+ elementInfo.setDimensions(100, 100);
74
+
75
+ track.observeElementInfo(elementInfo);
76
+ jest.runAllTimers();
77
+
78
+ expect(events).toHaveLength(1);
79
+ expect(events[0].width).toBe(100);
80
+ expect(events[0].height).toBe(100);
81
+ });
82
+
83
+ it('emits a dimensions event upon element resize', () => {
84
+ const elementInfo = new MockElementInfo();
85
+ elementInfo.setDimensions(100, 100);
86
+
87
+ track.observeElementInfo(elementInfo);
88
+ jest.runAllTimers();
89
+
90
+ elementInfo.setDimensions(200, 200);
91
+ jest.runAllTimers();
92
+
93
+ expect(events).toHaveLength(2);
94
+ expect(events[1].width).toBe(200);
95
+ expect(events[1].height).toBe(200);
96
+ });
97
+ });
98
+ });
99
+
100
+ class MockElementInfo implements ElementInfo {
101
+ element: object = {};
102
+
103
+ private _width = 0;
104
+
105
+ private _height = 0;
106
+
107
+ setDimensions(width: number, height: number) {
108
+ let shouldEmit = false;
109
+ if (this._width !== width) {
110
+ this._width = width;
111
+ shouldEmit = true;
112
+ }
113
+ if (this._height !== height) {
114
+ this._height = height;
115
+ shouldEmit = true;
116
+ }
117
+
118
+ if (shouldEmit) {
119
+ this.handleResize?.();
120
+ }
121
+ }
122
+
123
+ width(): number {
124
+ return this._width;
125
+ }
126
+
127
+ height(): number {
128
+ return this._height;
129
+ }
130
+
131
+ visible = false;
132
+
133
+ setVisible = (visible: boolean) => {
134
+ if (this.visible !== visible) {
135
+ this.visible = visible;
136
+ this.handleVisibilityChanged?.();
137
+ }
138
+ };
139
+
140
+ visibilityChangedAt = 0;
141
+
142
+ handleResize?: () => void;
143
+
144
+ handleVisibilityChanged?: () => void;
145
+
146
+ observe(): void {}
147
+
148
+ stopObserving(): void {}
149
+ }
@@ -1,15 +1,11 @@
1
1
  import { debounce } from 'ts-debounce';
2
2
  import { TrackEvent } from '../events';
3
3
  import { computeBitrate, monitorFrequency, VideoReceiverStats } from '../stats';
4
- import {
5
- getIntersectionObserver,
6
- getResizeObserver,
7
- isMobile,
8
- ObservableMediaElement,
9
- } from '../utils';
4
+ import { getIntersectionObserver, getResizeObserver, ObservableMediaElement } from '../utils';
10
5
  import RemoteTrack from './RemoteTrack';
11
6
  import { attachToElement, detachTrack, Track } from './Track';
12
7
  import { AdaptiveStreamSettings } from './types';
8
+ import log from '../../logger';
13
9
 
14
10
  const REACTION_DELAY = 100;
15
11
 
@@ -27,6 +23,8 @@ export default class RemoteVideoTrack extends RemoteTrack {
27
23
 
28
24
  private lastDimensions?: Track.Dimensions;
29
25
 
26
+ private hasUsedAttach: boolean = false;
27
+
30
28
  constructor(
31
29
  mediaTrack: MediaStreamTrack,
32
30
  sid: string,
@@ -45,9 +43,9 @@ export default class RemoteVideoTrack extends RemoteTrack {
45
43
  }
46
44
 
47
45
  get mediaStreamTrack() {
48
- if (this.isAdaptiveStream && this.attachedElements.length === 0) {
49
- throw Error(
50
- 'When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, direct usage of mediaStreamTrack is unsupported in this case',
46
+ if (this.isAdaptiveStream && !this.hasUsedAttach) {
47
+ log.warn(
48
+ 'When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start',
51
49
  );
52
50
  }
53
51
  return this._mediaStreamTrack;
@@ -82,23 +80,51 @@ export default class RemoteVideoTrack extends RemoteTrack {
82
80
  this.adaptiveStreamSettings &&
83
81
  this.elementInfos.find((info) => info.element === element) === undefined
84
82
  ) {
85
- this.elementInfos.push({
86
- element,
87
- visible: true, // default visible
88
- });
89
-
90
- (element as ObservableMediaElement).handleResize = this.debouncedHandleResize;
91
- (element as ObservableMediaElement).handleVisibilityChanged = this.handleVisibilityChanged;
92
-
93
- getIntersectionObserver().observe(element);
94
- getResizeObserver().observe(element);
83
+ const elementInfo = new HTMLElementInfo(element);
84
+ this.observeElementInfo(elementInfo);
85
+ }
86
+ this.hasUsedAttach = true;
87
+ return element;
88
+ }
95
89
 
90
+ /**
91
+ * Observe an ElementInfo for changes when adaptive streaming.
92
+ * @param elementInfo
93
+ * @internal
94
+ */
95
+ observeElementInfo(elementInfo: ElementInfo) {
96
+ if (
97
+ this.adaptiveStreamSettings &&
98
+ this.elementInfos.find((info) => info === elementInfo) === undefined
99
+ ) {
100
+ elementInfo.handleResize = () => {
101
+ this.debouncedHandleResize();
102
+ };
103
+ elementInfo.handleVisibilityChanged = () => {
104
+ this.updateVisibility();
105
+ };
106
+ this.elementInfos.push(elementInfo);
107
+ elementInfo.observe();
96
108
  // trigger the first resize update cycle
97
109
  // if the tab is backgrounded, the initial resize event does not fire until
98
110
  // the tab comes into focus for the first time.
99
111
  this.debouncedHandleResize();
112
+ this.updateVisibility();
100
113
  }
101
- return element;
114
+ }
115
+
116
+ /**
117
+ * Stop observing an ElementInfo for changes.
118
+ * @param elementInfo
119
+ * @internal
120
+ */
121
+ stopObservingElementInfo(elementInfo: ElementInfo) {
122
+ const stopElementInfos = this.elementInfos.filter((info) => info === elementInfo);
123
+ for (const info of stopElementInfos) {
124
+ info.stopObserving();
125
+ }
126
+ this.elementInfos = this.elementInfos.filter((info) => info !== elementInfo);
127
+ this.updateVisibility();
102
128
  }
103
129
 
104
130
  detach(): HTMLMediaElement[];
@@ -166,26 +192,16 @@ export default class RemoteVideoTrack extends RemoteTrack {
166
192
  }
167
193
 
168
194
  private stopObservingElement(element: HTMLMediaElement) {
169
- getIntersectionObserver()?.unobserve(element);
170
- getResizeObserver()?.unobserve(element);
195
+ const stopElementInfos = this.elementInfos.filter((info) => info.element === element);
196
+ for (const info of stopElementInfos) {
197
+ info.stopObserving();
198
+ }
171
199
  this.elementInfos = this.elementInfos.filter((info) => info.element !== element);
172
200
  }
173
201
 
174
- private handleVisibilityChanged = (entry: IntersectionObserverEntry) => {
175
- const { target, isIntersecting } = entry;
176
- const elementInfo = this.elementInfos.find((info) => info.element === target);
177
- if (elementInfo) {
178
- elementInfo.visible = isIntersecting;
179
- elementInfo.visibilityChangedAt = Date.now();
180
- }
181
- this.updateVisibility();
182
- };
183
-
184
202
  protected async handleAppVisibilityChanged() {
185
203
  await super.handleAppVisibilityChanged();
186
204
  if (!this.isAdaptiveStream) return;
187
- // on desktop don't pause when tab is backgrounded
188
- if (!isMobile()) return;
189
205
  this.updateVisibility();
190
206
  }
191
207
 
@@ -198,7 +214,12 @@ export default class RemoteVideoTrack extends RemoteTrack {
198
214
  (prev, info) => Math.max(prev, info.visibilityChangedAt || 0),
199
215
  0,
200
216
  );
201
- const isVisible = this.elementInfos.some((info) => info.visible) && !this.isInBackground;
217
+
218
+ const backgroundPause =
219
+ this.adaptiveStreamSettings?.pauseVideoInBackground ?? true // default to true
220
+ ? this.isInBackground
221
+ : false;
222
+ const isVisible = this.elementInfos.some((info) => info.visible) && !backgroundPause;
202
223
 
203
224
  if (this.lastVisible === isVisible) {
204
225
  return;
@@ -222,8 +243,8 @@ export default class RemoteVideoTrack extends RemoteTrack {
222
243
  for (const info of this.elementInfos) {
223
244
  const pixelDensity = this.adaptiveStreamSettings?.pixelDensity ?? 1;
224
245
  const pixelDensityValue = pixelDensity === 'screen' ? window.devicePixelRatio : pixelDensity;
225
- const currentElementWidth = info.element.clientWidth * pixelDensityValue;
226
- const currentElementHeight = info.element.clientHeight * pixelDensityValue;
246
+ const currentElementWidth = info.width() * pixelDensityValue;
247
+ const currentElementHeight = info.height() * pixelDensityValue;
227
248
  if (currentElementWidth + currentElementHeight > maxWidth + maxHeight) {
228
249
  maxWidth = currentElementWidth;
229
250
  maxHeight = currentElementHeight;
@@ -238,12 +259,70 @@ export default class RemoteVideoTrack extends RemoteTrack {
238
259
  width: maxWidth,
239
260
  height: maxHeight,
240
261
  };
262
+
241
263
  this.emit(TrackEvent.VideoDimensionsChanged, this.lastDimensions, this);
242
264
  }
243
265
  }
244
266
 
245
- interface ElementInfo {
267
+ export interface ElementInfo {
268
+ element: object;
269
+ width(): number;
270
+ height(): number;
271
+ visible: boolean;
272
+ visibilityChangedAt: number | undefined;
273
+
274
+ handleResize?: () => void;
275
+ handleVisibilityChanged?: () => void;
276
+ observe(): void;
277
+ stopObserving(): void;
278
+ }
279
+
280
+ class HTMLElementInfo implements ElementInfo {
246
281
  element: HTMLMediaElement;
282
+
247
283
  visible: boolean;
248
- visibilityChangedAt?: number;
284
+
285
+ visibilityChangedAt: number | undefined;
286
+
287
+ handleResize?: () => void;
288
+
289
+ handleVisibilityChanged?: () => void;
290
+
291
+ constructor(element: HTMLMediaElement, visible: boolean = false) {
292
+ this.element = element;
293
+ this.visible = visible;
294
+ this.visibilityChangedAt = 0;
295
+ }
296
+
297
+ width(): number {
298
+ return this.element.clientWidth;
299
+ }
300
+
301
+ height(): number {
302
+ return this.element.clientWidth;
303
+ }
304
+
305
+ observe() {
306
+ (this.element as ObservableMediaElement).handleResize = () => {
307
+ this.handleResize?.();
308
+ };
309
+ (this.element as ObservableMediaElement).handleVisibilityChanged = this.onVisibilityChanged;
310
+
311
+ getIntersectionObserver().observe(this.element);
312
+ getResizeObserver().observe(this.element);
313
+ }
314
+
315
+ private onVisibilityChanged = (entry: IntersectionObserverEntry) => {
316
+ const { target, isIntersecting } = entry;
317
+ if (target === this.element) {
318
+ this.visible = isIntersecting;
319
+ this.visibilityChangedAt = Date.now();
320
+ this.handleVisibilityChanged?.();
321
+ }
322
+ };
323
+
324
+ stopObserving() {
325
+ getIntersectionObserver()?.unobserve(this.element);
326
+ getResizeObserver()?.unobserve(this.element);
327
+ }
249
328
  }
@@ -5,6 +5,8 @@ import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc';
5
5
  import { TrackEvent } from '../events';
6
6
  import { isFireFox, isSafari, isWeb } from '../utils';
7
7
 
8
+ const BACKGROUND_REACTION_DELAY = 5000;
9
+
8
10
  // keep old audio elements when detached, we would re-use them since on iOS
9
11
  // Safari tracks which audio elements have been "blessed" by the user.
10
12
  const recycledElements: Array<HTMLAudioElement> = [];
@@ -32,6 +34,8 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
32
34
 
33
35
  protected isInBackground: boolean;
34
36
 
37
+ private backgroundTimeout: ReturnType<typeof setTimeout> | undefined;
38
+
35
39
  protected _currentBitrate: number = 0;
36
40
 
37
41
  protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind) {
@@ -179,8 +183,20 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
179
183
  }
180
184
  }
181
185
 
182
- appVisibilityChangedListener = () => {
183
- this.handleAppVisibilityChanged();
186
+ protected appVisibilityChangedListener = () => {
187
+ if (this.backgroundTimeout) {
188
+ clearTimeout(this.backgroundTimeout);
189
+ }
190
+ // delay app visibility update if it goes to hidden
191
+ // update immediately if it comes back to focus
192
+ if (document.visibilityState === 'hidden') {
193
+ this.backgroundTimeout = setTimeout(
194
+ () => this.handleAppVisibilityChanged(),
195
+ BACKGROUND_REACTION_DELAY,
196
+ );
197
+ } else {
198
+ this.handleAppVisibilityChanged();
199
+ }
184
200
  };
185
201
 
186
202
  protected async handleAppVisibilityChanged() {
@@ -89,7 +89,7 @@ export async function createLocalScreenTracks(
89
89
  options = {};
90
90
  }
91
91
  if (options.resolution === undefined) {
92
- options.resolution = VideoPresets.fhd.resolution;
92
+ options.resolution = VideoPresets.h1080.resolution;
93
93
  }
94
94
 
95
95
  let videoConstraints: MediaTrackConstraints | boolean = true;
@@ -232,20 +232,10 @@ export const VideoPresets = {
232
232
  h216: new VideoPreset(384, 216, 180_000, 15),
233
233
  h360: new VideoPreset(640, 360, 300_000, 20),
234
234
  h540: new VideoPreset(960, 540, 600_000, 25),
235
- h720: new VideoPreset(1280, 720, 2_000_000, 30),
235
+ h720: new VideoPreset(1280, 720, 1_700_000, 30),
236
236
  h1080: new VideoPreset(1920, 1080, 3_000_000, 30),
237
237
  h1440: new VideoPreset(2560, 1440, 5_000_000, 30),
238
238
  h2160: new VideoPreset(3840, 2160, 8_000_000, 30),
239
- /** @deprecated */
240
- qvga: new VideoPreset(320, 180, 120_000, 10),
241
- /** @deprecated */
242
- vga: new VideoPreset(640, 360, 300_000, 20),
243
- /** @deprecated */
244
- qhd: new VideoPreset(960, 540, 600_000, 25),
245
- /** @deprecated */
246
- hd: new VideoPreset(1280, 720, 2_000_000, 30),
247
- /** @deprecated */
248
- fhd: new VideoPreset(1920, 1080, 3_000_000, 30),
249
239
  } as const;
250
240
 
251
241
  /**
@@ -261,16 +251,6 @@ export const VideoPresets43 = {
261
251
  h720: new VideoPreset(960, 720, 1_500_000, 30),
262
252
  h1080: new VideoPreset(1440, 1080, 2_500_000, 30),
263
253
  h1440: new VideoPreset(1920, 1440, 3_500_000, 30),
264
- /** @deprecated */
265
- qvga: new VideoPreset(240, 180, 90_000, 10),
266
- /** @deprecated */
267
- vga: new VideoPreset(480, 360, 225_000, 20),
268
- /** @deprecated */
269
- qhd: new VideoPreset(720, 540, 450_000, 25),
270
- /** @deprecated */
271
- hd: new VideoPreset(960, 720, 1_500_000, 30),
272
- /** @deprecated */
273
- fhd: new VideoPreset(1440, 1080, 2_800_000, 30),
274
254
  } as const;
275
255
 
276
256
  export const ScreenSharePresets = {
@@ -279,14 +259,4 @@ export const ScreenSharePresets = {
279
259
  h720fps15: new VideoPreset(1280, 720, 1_000_000, 15),
280
260
  h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15),
281
261
  h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30),
282
- /** @deprecated */
283
- vga: new VideoPreset(640, 360, 200_000, 3),
284
- /** @deprecated */
285
- hd_8: new VideoPreset(1280, 720, 400_000, 5),
286
- /** @deprecated */
287
- hd_15: new VideoPreset(1280, 720, 1_000_000, 15),
288
- /** @deprecated */
289
- fhd_15: new VideoPreset(1920, 1080, 1_500_000, 15),
290
- /** @deprecated */
291
- fhd_30: new VideoPreset(1920, 1080, 3_000_000, 30),
292
262
  } as const;
@@ -17,4 +17,9 @@ export type AdaptiveStreamSettings = {
17
17
  * streaming on high definition screens.
18
18
  */
19
19
  pixelDensity?: number | 'screen';
20
+ /**
21
+ * If true, video gets paused when switching to another tab.
22
+ * Defaults to true.
23
+ */
24
+ pauseVideoInBackground?: boolean;
20
25
  };
@@ -8,7 +8,7 @@ describe('mergeDefaultOptions', () => {
8
8
  };
9
9
  const videoDefaults: VideoCaptureOptions = {
10
10
  deviceId: 'video123',
11
- resolution: VideoPresets.fhd.resolution,
11
+ resolution: VideoPresets.h1080.resolution,
12
12
  };
13
13
 
14
14
  it('does not enable undefined options', () => {
@@ -88,7 +88,7 @@ describe('constraintsForOptions', () => {
88
88
  it('converts video options correctly', () => {
89
89
  const constraints = constraintsForOptions({
90
90
  video: {
91
- resolution: VideoPresets.hd.resolution,
91
+ resolution: VideoPresets.h720.resolution,
92
92
  facingMode: 'user',
93
93
  deviceId: 'video123',
94
94
  },
@@ -102,9 +102,9 @@ describe('constraintsForOptions', () => {
102
102
  'facingMode',
103
103
  'deviceId',
104
104
  ]);
105
- expect(videoOpts.width).toEqual(VideoPresets.hd.resolution.width);
106
- expect(videoOpts.height).toEqual(VideoPresets.hd.resolution.height);
107
- expect(videoOpts.frameRate).toEqual(VideoPresets.hd.resolution.frameRate);
108
- expect(videoOpts.aspectRatio).toEqual(VideoPresets.hd.resolution.aspectRatio);
105
+ expect(videoOpts.width).toEqual(VideoPresets.h720.resolution.width);
106
+ expect(videoOpts.height).toEqual(VideoPresets.h720.resolution.height);
107
+ expect(videoOpts.frameRate).toEqual(VideoPresets.h720.resolution.frameRate);
108
+ expect(videoOpts.aspectRatio).toEqual(VideoPresets.h720.resolution.aspectRatio);
109
109
  });
110
110
  });
@@ -0,0 +1,83 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ export default class MockMediaStreamTrack implements MediaStreamTrack {
3
+ contentHint: string = '';
4
+
5
+ enabled: boolean = true;
6
+
7
+ id: string = 'id';
8
+
9
+ kind: string = 'video';
10
+
11
+ label: string = 'label';
12
+
13
+ muted: boolean = false;
14
+
15
+ onended: ((this: MediaStreamTrack, ev: Event) => any) | null = null;
16
+
17
+ onmute: ((this: MediaStreamTrack, ev: Event) => any) | null = null;
18
+
19
+ onunmute: ((this: MediaStreamTrack, ev: Event) => any) | null = null;
20
+
21
+ readyState: MediaStreamTrackState = 'live';
22
+
23
+ isolated: boolean = false;
24
+
25
+ onisolationchange: ((this: MediaStreamTrack, ev: Event) => any) | null = null;
26
+
27
+ // @ts-ignore
28
+ applyConstraints(constraints?: MediaTrackConstraints): Promise<void> {
29
+ throw new Error('Method not implemented.');
30
+ }
31
+
32
+ clone(): MediaStreamTrack {
33
+ throw new Error('Method not implemented.');
34
+ }
35
+
36
+ getCapabilities(): MediaTrackCapabilities {
37
+ throw new Error('Method not implemented.');
38
+ }
39
+
40
+ getConstraints(): MediaTrackConstraints {
41
+ throw new Error('Method not implemented.');
42
+ }
43
+
44
+ getSettings(): MediaTrackSettings {
45
+ throw new Error('Method not implemented.');
46
+ }
47
+
48
+ stop(): void {
49
+ throw new Error('Method not implemented.');
50
+ }
51
+
52
+ addEventListener<K extends keyof MediaStreamTrackEventMap>(
53
+ type: K,
54
+ listener: (this: MediaStreamTrack, ev: MediaStreamTrackEventMap[K]) => any,
55
+ options?: boolean | AddEventListenerOptions,
56
+ ): void;
57
+ addEventListener(
58
+ type: string,
59
+ listener: EventListenerOrEventListenerObject,
60
+ options?: boolean | AddEventListenerOptions,
61
+ ): void;
62
+ addEventListener(type: any, listener: any, options?: any): void {
63
+ throw new Error('Method not implemented.');
64
+ }
65
+
66
+ removeEventListener<K extends keyof MediaStreamTrackEventMap>(
67
+ type: K,
68
+ listener: (this: MediaStreamTrack, ev: MediaStreamTrackEventMap[K]) => any,
69
+ options?: boolean | EventListenerOptions,
70
+ ): void;
71
+ removeEventListener(
72
+ type: string,
73
+ listener: EventListenerOrEventListenerObject,
74
+ options?: boolean | EventListenerOptions,
75
+ ): void;
76
+ removeEventListener(type: any, listener: any, options?: any): void {
77
+ throw new Error('Method not implemented.');
78
+ }
79
+
80
+ dispatchEvent(event: Event): boolean {
81
+ throw new Error('Method not implemented.');
82
+ }
83
+ }
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '0.18.5';
1
+ export const version = '1.0.1';
2
2
  export const protocolVersion = 7;
package/dist/connect.d.ts DELETED
@@ -1,24 +0,0 @@
1
- import { ConnectOptions } from './options';
2
- import Room from './room/Room';
3
- export { version } from './version';
4
- /**
5
- * @deprecated Use room.connect() instead
6
- *
7
- * Connects to a LiveKit room, shorthand for `new Room()` and [[Room.connect]]
8
- *
9
- * ```typescript
10
- * connect('wss://myhost.livekit.io', token, {
11
- * // publish audio and video tracks on joining
12
- * audio: true,
13
- * video: true,
14
- * captureDefaults: {
15
- * facingMode: 'user',
16
- * },
17
- * })
18
- * ```
19
- * @param url URL to LiveKit server
20
- * @param token AccessToken, a JWT token that includes authentication and room details
21
- * @param options
22
- */
23
- export declare function connect(url: string, token: string, options?: ConnectOptions): Promise<Room>;
24
- //# sourceMappingURL=connect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,IAAI,MAAM,aAAa,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEjG"}