livekit-client 1.7.0 → 1.8.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.
- package/README.md +20 -1
- package/dist/livekit-client.esm.mjs +2240 -1067
- 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/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/options.d.ts +5 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +32 -0
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +315 -75
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +9 -1
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/ReconnectPolicy.d.ts +1 -0
- package/dist/src/room/ReconnectPolicy.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts +14 -0
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -0
- package/dist/src/room/Room.d.ts +6 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +2 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +15 -2
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +3 -2
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts +2 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +3 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +4 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +4 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/index.d.ts +3 -1
- package/dist/ts4.2/src/options.d.ts +5 -0
- package/dist/ts4.2/src/proto/livekit_models.d.ts +32 -0
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +348 -84
- package/dist/ts4.2/src/room/RTCEngine.d.ts +9 -1
- package/dist/ts4.2/src/room/ReconnectPolicy.d.ts +1 -0
- package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +14 -0
- package/dist/ts4.2/src/room/Room.d.ts +6 -1
- package/dist/ts4.2/src/room/errors.d.ts +2 -1
- package/dist/ts4.2/src/room/events.d.ts +15 -2
- package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -2
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +2 -1
- package/dist/ts4.2/src/room/track/Track.d.ts +3 -1
- package/dist/ts4.2/src/room/types.d.ts +4 -0
- package/dist/ts4.2/src/room/utils.d.ts +4 -0
- package/package.json +19 -19
- package/src/api/SignalClient.ts +4 -4
- package/src/index.ts +3 -0
- package/src/options.ts +6 -0
- package/src/proto/google/protobuf/timestamp.ts +15 -6
- package/src/proto/livekit_models.ts +903 -222
- package/src/proto/livekit_rtc.ts +1053 -279
- package/src/room/RTCEngine.ts +168 -56
- package/src/room/ReconnectPolicy.ts +2 -0
- package/src/room/RegionUrlProvider.ts +73 -0
- package/src/room/Room.ts +212 -133
- package/src/room/defaults.ts +1 -0
- package/src/room/errors.ts +1 -0
- package/src/room/events.ts +15 -0
- package/src/room/track/LocalAudioTrack.ts +14 -6
- package/src/room/track/LocalTrack.ts +22 -8
- package/src/room/track/LocalVideoTrack.ts +12 -6
- package/src/room/track/RemoteTrackPublication.ts +10 -4
- package/src/room/track/RemoteVideoTrack.test.ts +2 -0
- package/src/room/track/RemoteVideoTrack.ts +53 -9
- package/src/room/track/Track.ts +46 -31
- package/src/room/track/utils.ts +3 -2
- package/src/room/types.ts +6 -0
- package/src/room/utils.ts +53 -0
@@ -4,7 +4,7 @@ import { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc
|
|
4
4
|
import { TrackEvent } from '../events';
|
5
5
|
import type RemoteTrack from './RemoteTrack';
|
6
6
|
import RemoteVideoTrack from './RemoteVideoTrack';
|
7
|
-
import
|
7
|
+
import { Track } from './Track';
|
8
8
|
import { TrackPublication } from './TrackPublication';
|
9
9
|
|
10
10
|
export default class RemoteTrackPublication extends TrackPublication {
|
@@ -181,6 +181,7 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
181
181
|
prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
182
182
|
prevTrack.off(TrackEvent.Ended, this.handleEnded);
|
183
183
|
prevTrack.detach();
|
184
|
+
prevTrack.stopMonitor();
|
184
185
|
this.emit(TrackEvent.Unsubscribed, prevTrack);
|
185
186
|
}
|
186
187
|
super.setTrack(track);
|
@@ -207,8 +208,13 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
207
208
|
/** @internal */
|
208
209
|
updateInfo(info: TrackInfo) {
|
209
210
|
super.updateInfo(info);
|
211
|
+
const prevMetadataMuted = this.metadataMuted;
|
210
212
|
this.metadataMuted = info.muted;
|
211
|
-
this.track
|
213
|
+
if (this.track) {
|
214
|
+
this.track.setMuted(info.muted);
|
215
|
+
} else if (prevMetadataMuted !== info.muted) {
|
216
|
+
this.emit(info.muted ? TrackEvent.Muted : TrackEvent.Unmuted);
|
217
|
+
}
|
212
218
|
}
|
213
219
|
|
214
220
|
private emitSubscriptionUpdateIfChanged(previousStatus: TrackPublication.SubscriptionStatus) {
|
@@ -233,8 +239,8 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
233
239
|
}
|
234
240
|
|
235
241
|
private isManualOperationAllowed(): boolean {
|
236
|
-
if (this.isAdaptiveStream) {
|
237
|
-
log.warn('adaptive stream is enabled, cannot change track settings', {
|
242
|
+
if (this.kind === Track.Kind.Video && this.isAdaptiveStream) {
|
243
|
+
log.warn('adaptive stream is enabled, cannot change video track settings', {
|
238
244
|
trackSid: this.trackSid,
|
239
245
|
});
|
240
246
|
return false;
|
@@ -3,7 +3,13 @@ import log from '../../logger';
|
|
3
3
|
import { TrackEvent } from '../events';
|
4
4
|
import { computeBitrate, VideoReceiverStats } from '../stats';
|
5
5
|
import CriticalTimers from '../timers';
|
6
|
-
import {
|
6
|
+
import {
|
7
|
+
getDevicePixelRatio,
|
8
|
+
getIntersectionObserver,
|
9
|
+
getResizeObserver,
|
10
|
+
isWeb,
|
11
|
+
ObservableMediaElement,
|
12
|
+
} from '../utils';
|
7
13
|
import RemoteTrack from './RemoteTrack';
|
8
14
|
import { attachToElement, detachTrack, Track } from './Track';
|
9
15
|
import type { AdaptiveStreamSettings } from './types';
|
@@ -21,7 +27,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
21
27
|
|
22
28
|
private lastDimensions?: Track.Dimensions;
|
23
29
|
|
24
|
-
private
|
30
|
+
private isObserved: boolean = false;
|
25
31
|
|
26
32
|
constructor(
|
27
33
|
mediaTrack: MediaStreamTrack,
|
@@ -38,7 +44,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
38
44
|
}
|
39
45
|
|
40
46
|
get mediaStreamTrack() {
|
41
|
-
if (this.isAdaptiveStream && !this.
|
47
|
+
if (this.isAdaptiveStream && !this.isObserved) {
|
42
48
|
log.warn(
|
43
49
|
'When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start',
|
44
50
|
);
|
@@ -78,7 +84,6 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
78
84
|
const elementInfo = new HTMLElementInfo(element);
|
79
85
|
this.observeElementInfo(elementInfo);
|
80
86
|
}
|
81
|
-
this.hasUsedAttach = true;
|
82
87
|
return element;
|
83
88
|
}
|
84
89
|
|
@@ -105,6 +110,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
105
110
|
// the tab comes into focus for the first time.
|
106
111
|
this.debouncedHandleResize();
|
107
112
|
this.updateVisibility();
|
113
|
+
this.isObserved = true;
|
108
114
|
} else {
|
109
115
|
log.warn('visibility resize observer not triggered');
|
110
116
|
}
|
@@ -223,7 +229,9 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
223
229
|
this.adaptiveStreamSettings?.pauseVideoInBackground ?? true // default to true
|
224
230
|
? this.isInBackground
|
225
231
|
: false;
|
226
|
-
const
|
232
|
+
const isPiPMode = this.elementInfos.some((info) => info.pictureInPicture);
|
233
|
+
const isVisible =
|
234
|
+
(this.elementInfos.some((info) => info.visible) && !backgroundPause) || isPiPMode;
|
227
235
|
|
228
236
|
if (this.lastVisible === isVisible) {
|
229
237
|
return;
|
@@ -246,7 +254,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
246
254
|
let maxHeight = 0;
|
247
255
|
for (const info of this.elementInfos) {
|
248
256
|
const pixelDensity = this.adaptiveStreamSettings?.pixelDensity ?? 1;
|
249
|
-
const pixelDensityValue = pixelDensity === 'screen' ?
|
257
|
+
const pixelDensityValue = pixelDensity === 'screen' ? getDevicePixelRatio() : pixelDensity;
|
250
258
|
const currentElementWidth = info.width() * pixelDensityValue;
|
251
259
|
const currentElementHeight = info.height() * pixelDensityValue;
|
252
260
|
if (currentElementWidth + currentElementHeight > maxWidth + maxHeight) {
|
@@ -273,6 +281,7 @@ export interface ElementInfo {
|
|
273
281
|
width(): number;
|
274
282
|
height(): number;
|
275
283
|
visible: boolean;
|
284
|
+
pictureInPicture: boolean;
|
276
285
|
visibilityChangedAt: number | undefined;
|
277
286
|
|
278
287
|
handleResize?: () => void;
|
@@ -284,7 +293,13 @@ export interface ElementInfo {
|
|
284
293
|
class HTMLElementInfo implements ElementInfo {
|
285
294
|
element: HTMLMediaElement;
|
286
295
|
|
287
|
-
visible: boolean
|
296
|
+
get visible(): boolean {
|
297
|
+
return this.isPiP || this.isIntersecting;
|
298
|
+
}
|
299
|
+
|
300
|
+
get pictureInPicture(): boolean {
|
301
|
+
return this.isPiP;
|
302
|
+
}
|
288
303
|
|
289
304
|
visibilityChangedAt: number | undefined;
|
290
305
|
|
@@ -292,9 +307,14 @@ class HTMLElementInfo implements ElementInfo {
|
|
292
307
|
|
293
308
|
handleVisibilityChanged?: () => void;
|
294
309
|
|
310
|
+
private isPiP: boolean;
|
311
|
+
|
312
|
+
private isIntersecting: boolean;
|
313
|
+
|
295
314
|
constructor(element: HTMLMediaElement, visible?: boolean) {
|
296
315
|
this.element = element;
|
297
|
-
this.
|
316
|
+
this.isIntersecting = visible ?? isElementInViewport(element);
|
317
|
+
this.isPiP = isWeb() && document.pictureInPictureElement === element;
|
298
318
|
this.visibilityChangedAt = 0;
|
299
319
|
}
|
300
320
|
|
@@ -307,6 +327,10 @@ class HTMLElementInfo implements ElementInfo {
|
|
307
327
|
}
|
308
328
|
|
309
329
|
observe() {
|
330
|
+
// make sure we update the current visible state once we start to observe
|
331
|
+
this.isIntersecting = isElementInViewport(this.element);
|
332
|
+
this.isPiP = document.pictureInPictureElement === this.element;
|
333
|
+
|
310
334
|
(this.element as ObservableMediaElement).handleResize = () => {
|
311
335
|
this.handleResize?.();
|
312
336
|
};
|
@@ -314,20 +338,40 @@ class HTMLElementInfo implements ElementInfo {
|
|
314
338
|
|
315
339
|
getIntersectionObserver().observe(this.element);
|
316
340
|
getResizeObserver().observe(this.element);
|
341
|
+
(this.element as HTMLVideoElement).addEventListener('enterpictureinpicture', this.onEnterPiP);
|
342
|
+
(this.element as HTMLVideoElement).addEventListener('leavepictureinpicture', this.onLeavePiP);
|
317
343
|
}
|
318
344
|
|
319
345
|
private onVisibilityChanged = (entry: IntersectionObserverEntry) => {
|
320
346
|
const { target, isIntersecting } = entry;
|
321
347
|
if (target === this.element) {
|
322
|
-
this.
|
348
|
+
this.isIntersecting = isIntersecting;
|
323
349
|
this.visibilityChangedAt = Date.now();
|
324
350
|
this.handleVisibilityChanged?.();
|
325
351
|
}
|
326
352
|
};
|
327
353
|
|
354
|
+
private onEnterPiP = () => {
|
355
|
+
this.isPiP = true;
|
356
|
+
this.handleVisibilityChanged?.();
|
357
|
+
};
|
358
|
+
|
359
|
+
private onLeavePiP = () => {
|
360
|
+
this.isPiP = false;
|
361
|
+
this.handleVisibilityChanged?.();
|
362
|
+
};
|
363
|
+
|
328
364
|
stopObserving() {
|
329
365
|
getIntersectionObserver()?.unobserve(this.element);
|
330
366
|
getResizeObserver()?.unobserve(this.element);
|
367
|
+
(this.element as HTMLVideoElement).removeEventListener(
|
368
|
+
'enterpictureinpicture',
|
369
|
+
this.onEnterPiP,
|
370
|
+
);
|
371
|
+
(this.element as HTMLVideoElement).removeEventListener(
|
372
|
+
'leavepictureinpicture',
|
373
|
+
this.onLeavePiP,
|
374
|
+
);
|
331
375
|
}
|
332
376
|
}
|
333
377
|
|
package/src/room/track/Track.ts
CHANGED
@@ -42,7 +42,7 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
42
42
|
|
43
43
|
protected _mediaStreamID: string;
|
44
44
|
|
45
|
-
protected isInBackground: boolean;
|
45
|
+
protected isInBackground: boolean = false;
|
46
46
|
|
47
47
|
private backgroundTimeout: ReturnType<typeof setTimeout> | undefined;
|
48
48
|
|
@@ -57,12 +57,6 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
57
57
|
this._mediaStreamTrack = mediaTrack;
|
58
58
|
this._mediaStreamID = mediaTrack.id;
|
59
59
|
this.source = Track.Source.Unknown;
|
60
|
-
if (isWeb()) {
|
61
|
-
this.isInBackground = document.visibilityState === 'hidden';
|
62
|
-
document.addEventListener('visibilitychange', this.appVisibilityChangedListener);
|
63
|
-
} else {
|
64
|
-
this.isInBackground = false;
|
65
|
-
}
|
66
60
|
}
|
67
61
|
|
68
62
|
/** current receive bits per second */
|
@@ -97,6 +91,9 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
97
91
|
if (this.kind === Track.Kind.Video) {
|
98
92
|
elementType = 'video';
|
99
93
|
}
|
94
|
+
if (this.attachedElements.length === 0 && Track.Kind.Video) {
|
95
|
+
this.addAppVisibilityListener();
|
96
|
+
}
|
100
97
|
if (!element) {
|
101
98
|
if (elementType === 'audio') {
|
102
99
|
recycledElements.forEach((e) => {
|
@@ -167,37 +164,40 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
167
164
|
*/
|
168
165
|
detach(element: HTMLMediaElement): HTMLMediaElement;
|
169
166
|
detach(element?: HTMLMediaElement): HTMLMediaElement | HTMLMediaElement[] {
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
167
|
+
try {
|
168
|
+
// detach from a single element
|
169
|
+
if (element) {
|
170
|
+
detachTrack(this._mediaStreamTrack, element);
|
171
|
+
const idx = this.attachedElements.indexOf(element);
|
172
|
+
if (idx >= 0) {
|
173
|
+
this.attachedElements.splice(idx, 1);
|
174
|
+
this.recycleElement(element);
|
175
|
+
this.emit(TrackEvent.ElementDetached, element);
|
176
|
+
}
|
177
|
+
return element;
|
178
178
|
}
|
179
|
-
return element;
|
180
|
-
}
|
181
179
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
180
|
+
const detached: HTMLMediaElement[] = [];
|
181
|
+
this.attachedElements.forEach((elm) => {
|
182
|
+
detachTrack(this._mediaStreamTrack, elm);
|
183
|
+
detached.push(elm);
|
184
|
+
this.recycleElement(elm);
|
185
|
+
this.emit(TrackEvent.ElementDetached, elm);
|
186
|
+
});
|
189
187
|
|
190
|
-
|
191
|
-
|
192
|
-
|
188
|
+
// remove all tracks
|
189
|
+
this.attachedElements = [];
|
190
|
+
return detached;
|
191
|
+
} finally {
|
192
|
+
if (this.attachedElements.length === 0) {
|
193
|
+
this.removeAppVisibilityListener();
|
194
|
+
}
|
195
|
+
}
|
193
196
|
}
|
194
197
|
|
195
198
|
stop() {
|
196
199
|
this.stopMonitor();
|
197
200
|
this._mediaStreamTrack.stop();
|
198
|
-
if (isWeb()) {
|
199
|
-
document.removeEventListener('visibilitychange', this.appVisibilityChangedListener);
|
200
|
-
}
|
201
201
|
}
|
202
202
|
|
203
203
|
protected enable() {
|
@@ -212,7 +212,7 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
212
212
|
abstract startMonitor(signalClient?: SignalClient): void;
|
213
213
|
|
214
214
|
/* @internal */
|
215
|
-
|
215
|
+
stopMonitor() {
|
216
216
|
if (this.monitorInterval) {
|
217
217
|
clearInterval(this.monitorInterval);
|
218
218
|
}
|
@@ -253,6 +253,21 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
253
253
|
protected async handleAppVisibilityChanged() {
|
254
254
|
this.isInBackground = document.visibilityState === 'hidden';
|
255
255
|
}
|
256
|
+
|
257
|
+
protected addAppVisibilityListener() {
|
258
|
+
if (isWeb()) {
|
259
|
+
this.isInBackground = document.visibilityState === 'hidden';
|
260
|
+
document.addEventListener('visibilitychange', this.appVisibilityChangedListener);
|
261
|
+
} else {
|
262
|
+
this.isInBackground = false;
|
263
|
+
}
|
264
|
+
}
|
265
|
+
|
266
|
+
protected removeAppVisibilityListener() {
|
267
|
+
if (isWeb()) {
|
268
|
+
document.removeEventListener('visibilitychange', this.appVisibilityChangedListener);
|
269
|
+
}
|
270
|
+
}
|
256
271
|
}
|
257
272
|
|
258
273
|
/** @internal */
|
package/src/room/track/utils.ts
CHANGED
@@ -105,8 +105,9 @@ export async function detectSilence(track: AudioTrack, timeOffset = 200): Promis
|
|
105
105
|
* @internal
|
106
106
|
*/
|
107
107
|
export function getNewAudioContext(): AudioContext | void {
|
108
|
-
|
109
|
-
|
108
|
+
const AudioContext =
|
109
|
+
// @ts-ignore
|
110
|
+
typeof window !== 'undefined' && (window.AudioContext || window.webkitAudioContext);
|
110
111
|
if (AudioContext) {
|
111
112
|
return new AudioContext({ latencyHint: 'interactive' });
|
112
113
|
}
|
package/src/room/types.ts
CHANGED
@@ -20,3 +20,9 @@ export type DataPublishOptions = {
|
|
20
20
|
/** the topic under which the message gets published */
|
21
21
|
topic?: string;
|
22
22
|
};
|
23
|
+
|
24
|
+
export type LiveKitReactNativeInfo = {
|
25
|
+
// Corresponds to RN's PlatformOSType
|
26
|
+
platform: 'ios' | 'android' | 'windows' | 'macos' | 'web' | 'native';
|
27
|
+
devicePixelRatio: number;
|
28
|
+
};
|
package/src/room/utils.ts
CHANGED
@@ -4,6 +4,7 @@ import { protocolVersion, version } from '../version';
|
|
4
4
|
import type LocalAudioTrack from './track/LocalAudioTrack';
|
5
5
|
import type RemoteAudioTrack from './track/RemoteAudioTrack';
|
6
6
|
import { getNewAudioContext } from './track/utils';
|
7
|
+
import type { LiveKitReactNativeInfo } from './types';
|
7
8
|
|
8
9
|
const separator = '|';
|
9
10
|
|
@@ -122,6 +123,54 @@ export function isWeb(): boolean {
|
|
122
123
|
return typeof document !== 'undefined';
|
123
124
|
}
|
124
125
|
|
126
|
+
export function isReactNative(): boolean {
|
127
|
+
// navigator.product is deprecated on browsers, but will be set appropriately for react-native.
|
128
|
+
return navigator.product == 'ReactNative';
|
129
|
+
}
|
130
|
+
|
131
|
+
export function isCloud(serverUrl: URL) {
|
132
|
+
return serverUrl.hostname.endsWith('.livekit.cloud');
|
133
|
+
}
|
134
|
+
|
135
|
+
function getLKReactNativeInfo(): LiveKitReactNativeInfo | undefined {
|
136
|
+
// global defined only for ReactNative.
|
137
|
+
// @ts-ignore
|
138
|
+
if (global && global.LiveKitReactNativeGlobal) {
|
139
|
+
// @ts-ignore
|
140
|
+
return global.LiveKitReactNativeGlobal as LiveKitReactNativeInfo;
|
141
|
+
}
|
142
|
+
|
143
|
+
return undefined;
|
144
|
+
}
|
145
|
+
|
146
|
+
export function getReactNativeOs(): string | undefined {
|
147
|
+
if (!isReactNative()) {
|
148
|
+
return undefined;
|
149
|
+
}
|
150
|
+
|
151
|
+
let info = getLKReactNativeInfo();
|
152
|
+
if (info) {
|
153
|
+
return info.platform;
|
154
|
+
}
|
155
|
+
|
156
|
+
return undefined;
|
157
|
+
}
|
158
|
+
|
159
|
+
export function getDevicePixelRatio(): number {
|
160
|
+
if (isWeb()) {
|
161
|
+
return window.devicePixelRatio;
|
162
|
+
}
|
163
|
+
|
164
|
+
if (isReactNative()) {
|
165
|
+
let info = getLKReactNativeInfo();
|
166
|
+
if (info) {
|
167
|
+
return info.devicePixelRatio;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
return 1;
|
172
|
+
}
|
173
|
+
|
125
174
|
export function compareVersions(v1: string, v2: string): number {
|
126
175
|
const parts1 = v1.split('.');
|
127
176
|
const parts2 = v2.split('.');
|
@@ -174,6 +223,10 @@ export function getClientInfo(): ClientInfo {
|
|
174
223
|
protocol: protocolVersion,
|
175
224
|
version,
|
176
225
|
});
|
226
|
+
|
227
|
+
if (isReactNative()) {
|
228
|
+
info.os = getReactNativeOs() ?? '';
|
229
|
+
}
|
177
230
|
return info;
|
178
231
|
}
|
179
232
|
|