livekit-client 1.1.2 → 1.1.5
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/dist/livekit-client.esm.mjs +205 -89
- 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/room/Room.d.ts +4 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +2 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.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 +4 -2
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +6 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/room/Room.ts +150 -55
- package/src/room/participant/LocalParticipant.ts +7 -6
- package/src/room/participant/RemoteParticipant.ts +5 -11
- package/src/room/participant/publishUtils.ts +2 -2
- package/src/room/track/LocalAudioTrack.ts +7 -3
- package/src/room/track/LocalTrack.ts +11 -2
- package/src/room/track/LocalVideoTrack.ts +7 -3
- package/src/room/track/RemoteVideoTrack.ts +30 -2
- package/src/room/track/create.ts +2 -2
- package/src/room/utils.ts +15 -0
@@ -23,9 +23,7 @@ export default class RemoteParticipant extends Participant {
|
|
23
23
|
|
24
24
|
/** @internal */
|
25
25
|
static fromParticipantInfo(signalClient: SignalClient, pi: ParticipantInfo): RemoteParticipant {
|
26
|
-
|
27
|
-
rp.updateInfo(pi);
|
28
|
-
return rp;
|
26
|
+
return new RemoteParticipant(signalClient, pi.sid, pi.identity);
|
29
27
|
}
|
30
28
|
|
31
29
|
/** @internal */
|
@@ -182,8 +180,6 @@ export default class RemoteParticipant extends Participant {
|
|
182
180
|
|
183
181
|
/** @internal */
|
184
182
|
updateInfo(info: ParticipantInfo) {
|
185
|
-
const alreadyHasMetadata = this.hasMetadata;
|
186
|
-
|
187
183
|
super.updateInfo(info);
|
188
184
|
|
189
185
|
// we are getting a list of all available tracks, reconcile in here
|
@@ -212,12 +208,10 @@ export default class RemoteParticipant extends Participant {
|
|
212
208
|
validTracks.set(ti.sid, publication);
|
213
209
|
});
|
214
210
|
|
215
|
-
//
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
});
|
220
|
-
}
|
211
|
+
// always emit events for new publications, Room will not forward them unless it's ready
|
212
|
+
newTracks.forEach((publication) => {
|
213
|
+
this.emit(ParticipantEvent.TrackPublished, publication);
|
214
|
+
});
|
221
215
|
|
222
216
|
// detect removed tracks
|
223
217
|
this.tracks.forEach((publication) => {
|
@@ -18,9 +18,9 @@ export function mediaTrackToLocalTrack(
|
|
18
18
|
): LocalVideoTrack | LocalAudioTrack {
|
19
19
|
switch (mediaStreamTrack.kind) {
|
20
20
|
case 'audio':
|
21
|
-
return new LocalAudioTrack(mediaStreamTrack, constraints);
|
21
|
+
return new LocalAudioTrack(mediaStreamTrack, constraints, false);
|
22
22
|
case 'video':
|
23
|
-
return new LocalVideoTrack(mediaStreamTrack, constraints);
|
23
|
+
return new LocalVideoTrack(mediaStreamTrack, constraints, false);
|
24
24
|
default:
|
25
25
|
throw new TrackInvalidError(`unsupported track type: ${mediaStreamTrack.kind}`);
|
26
26
|
}
|
@@ -15,8 +15,12 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
15
15
|
|
16
16
|
private prevStats?: AudioSenderStats;
|
17
17
|
|
18
|
-
constructor(
|
19
|
-
|
18
|
+
constructor(
|
19
|
+
mediaTrack: MediaStreamTrack,
|
20
|
+
constraints?: MediaTrackConstraints,
|
21
|
+
userProvidedTrack = true,
|
22
|
+
) {
|
23
|
+
super(mediaTrack, Track.Kind.Audio, constraints, userProvidedTrack);
|
20
24
|
this.checkForSilence();
|
21
25
|
}
|
22
26
|
|
@@ -42,7 +46,7 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
42
46
|
}
|
43
47
|
|
44
48
|
async unmute(): Promise<LocalAudioTrack> {
|
45
|
-
if (this.source === Track.Source.Microphone && this.stopOnMute) {
|
49
|
+
if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
|
46
50
|
log.debug('reacquiring mic track');
|
47
51
|
await this.restartTrack();
|
48
52
|
}
|
@@ -19,16 +19,20 @@ export default class LocalTrack extends Track {
|
|
19
19
|
|
20
20
|
protected reacquireTrack: boolean;
|
21
21
|
|
22
|
+
protected providedByUser: boolean;
|
23
|
+
|
22
24
|
protected constructor(
|
23
25
|
mediaTrack: MediaStreamTrack,
|
24
26
|
kind: Track.Kind,
|
25
27
|
constraints?: MediaTrackConstraints,
|
28
|
+
userProvidedTrack = false,
|
26
29
|
) {
|
27
30
|
super(mediaTrack, kind);
|
28
31
|
this._mediaStreamTrack.addEventListener('ended', this.handleEnded);
|
29
32
|
this.constraints = constraints ?? mediaTrack.getConstraints();
|
30
33
|
this.reacquireTrack = false;
|
31
34
|
this.wasMuted = false;
|
35
|
+
this.providedByUser = userProvidedTrack;
|
32
36
|
}
|
33
37
|
|
34
38
|
get id(): string {
|
@@ -56,6 +60,10 @@ export default class LocalTrack extends Track {
|
|
56
60
|
return this._isUpstreamPaused;
|
57
61
|
}
|
58
62
|
|
63
|
+
get isUserProvided() {
|
64
|
+
return this.providedByUser;
|
65
|
+
}
|
66
|
+
|
59
67
|
/**
|
60
68
|
* @returns DeviceID of the device that is currently being used for this track
|
61
69
|
*/
|
@@ -80,7 +88,7 @@ export default class LocalTrack extends Track {
|
|
80
88
|
return this;
|
81
89
|
}
|
82
90
|
|
83
|
-
async replaceTrack(track: MediaStreamTrack): Promise<LocalTrack> {
|
91
|
+
async replaceTrack(track: MediaStreamTrack, userProvidedTrack = true): Promise<LocalTrack> {
|
84
92
|
if (!this.sender) {
|
85
93
|
throw new TrackInvalidError('unable to replace an unpublished track');
|
86
94
|
}
|
@@ -108,6 +116,7 @@ export default class LocalTrack extends Track {
|
|
108
116
|
});
|
109
117
|
|
110
118
|
this.mediaStream = new MediaStream([track]);
|
119
|
+
this.providedByUser = userProvidedTrack;
|
111
120
|
return this;
|
112
121
|
}
|
113
122
|
|
@@ -184,7 +193,7 @@ export default class LocalTrack extends Track {
|
|
184
193
|
if (!isMobile()) return;
|
185
194
|
log.debug(`visibility changed, is in Background: ${this.isInBackground}`);
|
186
195
|
|
187
|
-
if (!this.isInBackground && this.needsReAcquisition) {
|
196
|
+
if (!this.isInBackground && this.needsReAcquisition && !this.isUserProvided) {
|
188
197
|
log.debug(`track needs to be reaquired, restarting ${this.source}`);
|
189
198
|
await this.restart();
|
190
199
|
this.reacquireTrack = false;
|
@@ -41,8 +41,12 @@ export default class LocalVideoTrack extends LocalTrack {
|
|
41
41
|
|
42
42
|
private subscribedCodecs?: SubscribedCodec[];
|
43
43
|
|
44
|
-
constructor(
|
45
|
-
|
44
|
+
constructor(
|
45
|
+
mediaTrack: MediaStreamTrack,
|
46
|
+
constraints?: MediaTrackConstraints,
|
47
|
+
userProvidedTrack = true,
|
48
|
+
) {
|
49
|
+
super(mediaTrack, Track.Kind.Video, constraints, userProvidedTrack);
|
46
50
|
}
|
47
51
|
|
48
52
|
get isSimulcast(): boolean {
|
@@ -92,7 +96,7 @@ export default class LocalVideoTrack extends LocalTrack {
|
|
92
96
|
}
|
93
97
|
|
94
98
|
async unmute(): Promise<LocalVideoTrack> {
|
95
|
-
if (this.source === Track.Source.Camera) {
|
99
|
+
if (this.source === Track.Source.Camera && !this.isUserProvided) {
|
96
100
|
log.debug('reacquiring camera track');
|
97
101
|
await this.restartTrack();
|
98
102
|
}
|
@@ -110,6 +110,8 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
110
110
|
// the tab comes into focus for the first time.
|
111
111
|
this.debouncedHandleResize();
|
112
112
|
this.updateVisibility();
|
113
|
+
} else {
|
114
|
+
log.warn('visibility resize observer not triggered');
|
113
115
|
}
|
114
116
|
}
|
115
117
|
|
@@ -294,9 +296,9 @@ class HTMLElementInfo implements ElementInfo {
|
|
294
296
|
|
295
297
|
handleVisibilityChanged?: () => void;
|
296
298
|
|
297
|
-
constructor(element: HTMLMediaElement, visible
|
299
|
+
constructor(element: HTMLMediaElement, visible?: boolean) {
|
298
300
|
this.element = element;
|
299
|
-
this.visible = visible;
|
301
|
+
this.visible = visible ?? isElementInViewport(element);
|
300
302
|
this.visibilityChangedAt = 0;
|
301
303
|
}
|
302
304
|
|
@@ -332,3 +334,29 @@ class HTMLElementInfo implements ElementInfo {
|
|
332
334
|
getResizeObserver()?.unobserve(this.element);
|
333
335
|
}
|
334
336
|
}
|
337
|
+
|
338
|
+
// does not account for occlusion by other elements
|
339
|
+
function isElementInViewport(el: HTMLElement) {
|
340
|
+
let top = el.offsetTop;
|
341
|
+
let left = el.offsetLeft;
|
342
|
+
const width = el.offsetWidth;
|
343
|
+
const height = el.offsetHeight;
|
344
|
+
const { hidden } = el;
|
345
|
+
const { opacity, display } = getComputedStyle(el);
|
346
|
+
|
347
|
+
while (el.offsetParent) {
|
348
|
+
el = el.offsetParent as HTMLElement;
|
349
|
+
top += el.offsetTop;
|
350
|
+
left += el.offsetLeft;
|
351
|
+
}
|
352
|
+
|
353
|
+
return (
|
354
|
+
top < window.pageYOffset + window.innerHeight &&
|
355
|
+
left < window.pageXOffset + window.innerWidth &&
|
356
|
+
top + height > window.pageYOffset &&
|
357
|
+
left + width > window.pageXOffset &&
|
358
|
+
!hidden &&
|
359
|
+
(opacity !== '' ? parseFloat(opacity) > 0 : true) &&
|
360
|
+
display !== 'none'
|
361
|
+
);
|
362
|
+
}
|
package/src/room/track/create.ts
CHANGED
@@ -110,11 +110,11 @@ export async function createLocalScreenTracks(
|
|
110
110
|
if (tracks.length === 0) {
|
111
111
|
throw new TrackInvalidError('no video track found');
|
112
112
|
}
|
113
|
-
const screenVideo = new LocalVideoTrack(tracks[0]);
|
113
|
+
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false);
|
114
114
|
screenVideo.source = Track.Source.ScreenShare;
|
115
115
|
const localTracks: Array<LocalTrack> = [screenVideo];
|
116
116
|
if (stream.getAudioTracks().length > 0) {
|
117
|
-
const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0]);
|
117
|
+
const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false);
|
118
118
|
screenAudio.source = Track.Source.ScreenShareAudio;
|
119
119
|
localTracks.push(screenAudio);
|
120
120
|
}
|
package/src/room/utils.ts
CHANGED
@@ -114,3 +114,18 @@ export function getEmptyAudioStreamTrack() {
|
|
114
114
|
}
|
115
115
|
return emptyAudioStreamTrack;
|
116
116
|
}
|
117
|
+
|
118
|
+
export class Future<T> {
|
119
|
+
promise: Promise<T>;
|
120
|
+
|
121
|
+
resolve!: (arg: T) => void;
|
122
|
+
|
123
|
+
reject!: (e: any) => void;
|
124
|
+
|
125
|
+
constructor() {
|
126
|
+
this.promise = new Promise<T>((resolve, reject) => {
|
127
|
+
this.resolve = resolve;
|
128
|
+
this.reject = reject;
|
129
|
+
});
|
130
|
+
}
|
131
|
+
}
|