livekit-client 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|