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.
- package/README.md +1 -1
- package/dist/api/SignalClient.d.ts +2 -2
- package/dist/api/SignalClient.d.ts.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/livekit-client.esm.mjs +257 -254
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/options.d.ts +1 -68
- package/dist/options.d.ts.map +1 -1
- package/dist/room/DeviceManager.d.ts.map +1 -1
- package/dist/room/RTCEngine.d.ts +3 -2
- package/dist/room/RTCEngine.d.ts.map +1 -1
- package/dist/room/Room.d.ts +11 -7
- package/dist/room/Room.d.ts.map +1 -1
- package/dist/room/events.d.ts +6 -12
- package/dist/room/events.d.ts.map +1 -1
- package/dist/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/room/participant/Participant.d.ts +0 -4
- package/dist/room/participant/Participant.d.ts.map +1 -1
- package/dist/room/participant/RemoteParticipant.d.ts +3 -2
- package/dist/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/room/track/RemoteVideoTrack.d.ts +24 -1
- package/dist/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/room/track/Track.d.ts +2 -1
- package/dist/room/track/Track.d.ts.map +1 -1
- package/dist/room/track/options.d.ts +0 -30
- package/dist/room/track/options.d.ts.map +1 -1
- package/dist/room/track/types.d.ts +5 -0
- package/dist/room/track/types.d.ts.map +1 -1
- package/dist/test/MockMediaStreamTrack.d.ts +26 -0
- package/dist/test/MockMediaStreamTrack.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/api/SignalClient.ts +32 -7
- package/src/index.ts +4 -3
- package/src/options.ts +0 -82
- package/src/room/DeviceManager.ts +4 -1
- package/src/room/RTCEngine.ts +16 -7
- package/src/room/Room.ts +66 -42
- package/src/room/events.ts +7 -14
- package/src/room/participant/LocalParticipant.ts +4 -0
- package/src/room/participant/Participant.ts +0 -5
- package/src/room/participant/RemoteParticipant.ts +16 -5
- package/src/room/participant/publishUtils.test.ts +2 -2
- package/src/room/track/LocalVideoTrack.ts +1 -1
- package/src/room/track/RemoteVideoTrack.test.ts +149 -0
- package/src/room/track/RemoteVideoTrack.ts +118 -39
- package/src/room/track/Track.ts +18 -2
- package/src/room/track/create.ts +1 -1
- package/src/room/track/options.ts +1 -31
- package/src/room/track/types.ts +5 -0
- package/src/room/track/utils.test.ts +6 -6
- package/src/test/MockMediaStreamTrack.ts +83 -0
- package/src/version.ts +1 -1
- package/dist/connect.d.ts +0 -24
- package/dist/connect.d.ts.map +0 -1
- 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.
|
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.
|
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.
|
49
|
-
|
50
|
-
'When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement,
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
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
|
-
|
170
|
-
|
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
|
-
|
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.
|
226
|
-
const currentElementHeight = info.
|
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
|
-
|
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
|
}
|
package/src/room/track/Track.ts
CHANGED
@@ -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.
|
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() {
|
package/src/room/track/create.ts
CHANGED
@@ -89,7 +89,7 @@ export async function createLocalScreenTracks(
|
|
89
89
|
options = {};
|
90
90
|
}
|
91
91
|
if (options.resolution === undefined) {
|
92
|
-
options.resolution = VideoPresets.
|
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,
|
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;
|
package/src/room/track/types.ts
CHANGED
@@ -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.
|
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.
|
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.
|
106
|
-
expect(videoOpts.height).toEqual(VideoPresets.
|
107
|
-
expect(videoOpts.frameRate).toEqual(VideoPresets.
|
108
|
-
expect(videoOpts.aspectRatio).toEqual(VideoPresets.
|
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.
|
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
|
package/dist/connect.d.ts.map
DELETED
@@ -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"}
|