livekit-client 1.7.1 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +21 -1
- package/dist/livekit-client.esm.mjs +14241 -12994
- 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/api/SignalClient.d.ts +11 -10
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/index.d.ts +7 -7
- package/dist/src/index.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 +37 -0
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +347 -75
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +12 -3
- 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 +23 -15
- package/dist/src/room/Room.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 +23 -2
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +14 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +4 -2
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +2 -2
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +4 -3
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteAudioTrack.d.ts +1 -1
- package/dist/src/room/track/RemoteAudioTrack.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 +1 -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/create.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +5 -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/api/SignalClient.d.ts +14 -10
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +1 -1
- package/dist/ts4.2/src/index.d.ts +7 -6
- package/dist/ts4.2/src/proto/livekit_models.d.ts +37 -0
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +380 -84
- package/dist/ts4.2/src/room/RTCEngine.d.ts +12 -3
- 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 +23 -15
- package/dist/ts4.2/src/room/errors.d.ts +2 -1
- package/dist/ts4.2/src/room/events.d.ts +23 -2
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +14 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +4 -2
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -2
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +4 -3
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/Track.d.ts +3 -1
- package/dist/ts4.2/src/room/types.d.ts +5 -0
- package/dist/ts4.2/src/room/utils.d.ts +4 -0
- package/package.json +21 -20
- package/src/api/SignalClient.ts +41 -29
- package/src/connectionHelper/ConnectionCheck.ts +1 -2
- package/src/connectionHelper/checks/Checker.ts +1 -1
- package/src/connectionHelper/checks/reconnect.ts +1 -1
- package/src/index.ts +9 -8
- package/src/proto/google/protobuf/timestamp.ts +15 -6
- package/src/proto/livekit_models.ts +917 -221
- package/src/proto/livekit_rtc.ts +1053 -279
- package/src/room/RTCEngine.ts +171 -47
- package/src/room/ReconnectPolicy.ts +2 -0
- package/src/room/RegionUrlProvider.ts +73 -0
- package/src/room/Room.ts +278 -177
- package/src/room/errors.ts +1 -0
- package/src/room/events.ts +24 -0
- package/src/room/participant/LocalParticipant.ts +30 -7
- package/src/room/participant/Participant.ts +27 -3
- package/src/room/participant/RemoteParticipant.ts +6 -3
- package/src/room/participant/publishUtils.test.ts +1 -1
- package/src/room/participant/publishUtils.ts +1 -1
- package/src/room/track/LocalAudioTrack.ts +14 -7
- package/src/room/track/LocalTrack.ts +23 -9
- package/src/room/track/LocalTrackPublication.ts +1 -1
- package/src/room/track/LocalVideoTrack.ts +15 -9
- package/src/room/track/RemoteAudioTrack.ts +1 -1
- package/src/room/track/RemoteTrackPublication.ts +4 -3
- package/src/room/track/RemoteVideoTrack.test.ts +1 -1
- package/src/room/track/RemoteVideoTrack.ts +8 -7
- package/src/room/track/Track.ts +46 -31
- package/src/room/track/create.ts +2 -2
- package/src/room/types.ts +17 -0
- package/src/room/utils.ts +53 -0
package/src/room/events.ts
CHANGED
@@ -168,6 +168,14 @@ export enum RoomEvent {
|
|
168
168
|
*/
|
169
169
|
ParticipantMetadataChanged = 'participantMetadataChanged',
|
170
170
|
|
171
|
+
/**
|
172
|
+
* Participant's display name changed
|
173
|
+
*
|
174
|
+
* args: (name: string, [[Participant]])
|
175
|
+
*
|
176
|
+
*/
|
177
|
+
ParticipantNameChanged = 'participantNameChanged',
|
178
|
+
|
171
179
|
/**
|
172
180
|
* Room metadata is a simple way for app-specific state to be pushed to
|
173
181
|
* all users.
|
@@ -264,6 +272,12 @@ export enum RoomEvent {
|
|
264
272
|
* args: (isRecording: boolean)
|
265
273
|
*/
|
266
274
|
RecordingStatusChanged = 'recordingStatusChanged',
|
275
|
+
|
276
|
+
/**
|
277
|
+
* Emits whenever the current buffer status of a data channel changes
|
278
|
+
* args: (isLow: boolean, kind: [[DataPacket_Kind]])
|
279
|
+
*/
|
280
|
+
DCBufferStatusChanged = 'dcBufferStatusChanged',
|
267
281
|
}
|
268
282
|
|
269
283
|
export enum ParticipantEvent {
|
@@ -353,6 +367,14 @@ export enum ParticipantEvent {
|
|
353
367
|
*/
|
354
368
|
ParticipantMetadataChanged = 'participantMetadataChanged',
|
355
369
|
|
370
|
+
/**
|
371
|
+
* Participant's display name changed
|
372
|
+
*
|
373
|
+
* args: (name: string, [[Participant]])
|
374
|
+
*
|
375
|
+
*/
|
376
|
+
ParticipantNameChanged = 'participantNameChanged',
|
377
|
+
|
356
378
|
/**
|
357
379
|
* Data received from this participant as sender.
|
358
380
|
* Data packets provides the ability to use LiveKit to send/receive arbitrary payloads.
|
@@ -427,10 +449,12 @@ export enum EngineEvent {
|
|
427
449
|
Restarting = 'restarting',
|
428
450
|
Restarted = 'restarted',
|
429
451
|
SignalResumed = 'signalResumed',
|
452
|
+
SignalRestarted = 'signalRestarted',
|
430
453
|
Closing = 'closing',
|
431
454
|
MediaTrackAdded = 'mediaTrackAdded',
|
432
455
|
ActiveSpeakersUpdate = 'activeSpeakersUpdate',
|
433
456
|
DataPacketReceived = 'dataPacketReceived',
|
457
|
+
DCBufferStatusChanged = 'dcBufferStatusChanged',
|
434
458
|
}
|
435
459
|
|
436
460
|
export enum TrackEvent {
|
@@ -10,35 +10,35 @@ import {
|
|
10
10
|
TrackPublishedResponse,
|
11
11
|
TrackUnpublishedResponse,
|
12
12
|
} from '../../proto/livekit_rtc';
|
13
|
+
import type RTCEngine from '../RTCEngine';
|
13
14
|
import { DeviceUnsupportedError, TrackInvalidError, UnexpectedConnectionState } from '../errors';
|
14
15
|
import { EngineEvent, ParticipantEvent, TrackEvent } from '../events';
|
15
|
-
import type RTCEngine from '../RTCEngine';
|
16
16
|
import LocalAudioTrack from '../track/LocalAudioTrack';
|
17
17
|
import LocalTrack from '../track/LocalTrack';
|
18
18
|
import LocalTrackPublication from '../track/LocalTrackPublication';
|
19
19
|
import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTrack';
|
20
|
+
import { Track } from '../track/Track';
|
20
21
|
import {
|
21
22
|
AudioCaptureOptions,
|
22
23
|
BackupVideoCodec,
|
23
24
|
CreateLocalTracksOptions,
|
24
|
-
isBackupCodec,
|
25
25
|
ScreenShareCaptureOptions,
|
26
26
|
ScreenSharePresets,
|
27
27
|
TrackPublishOptions,
|
28
28
|
VideoCaptureOptions,
|
29
|
+
isBackupCodec,
|
29
30
|
} from '../track/options';
|
30
|
-
import { Track } from '../track/Track';
|
31
31
|
import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
|
32
32
|
import type { DataPublishOptions } from '../types';
|
33
33
|
import { Future, isFireFox, isSafari, isWeb, supportsAV1 } from '../utils';
|
34
34
|
import Participant from './Participant';
|
35
35
|
import { ParticipantTrackPermission, trackPermissionToProto } from './ParticipantTrackPermission';
|
36
|
+
import RemoteParticipant from './RemoteParticipant';
|
36
37
|
import {
|
37
38
|
computeTrackBackupEncodings,
|
38
39
|
computeVideoEncodings,
|
39
40
|
mediaTrackToLocalTrack,
|
40
41
|
} from './publishUtils';
|
41
|
-
import RemoteParticipant from './RemoteParticipant';
|
42
42
|
|
43
43
|
export default class LocalParticipant extends Participant {
|
44
44
|
audioTracks: Map<string, LocalTrackPublication>;
|
@@ -148,6 +148,26 @@ export default class LocalParticipant extends Participant {
|
|
148
148
|
this.reconnectFuture = undefined;
|
149
149
|
};
|
150
150
|
|
151
|
+
/**
|
152
|
+
* Sets and updates the metadata of the local participant.
|
153
|
+
* Note: this requires `CanUpdateOwnMetadata` permission encoded in the token.
|
154
|
+
* @param metadata
|
155
|
+
*/
|
156
|
+
setMetadata(metadata: string): void {
|
157
|
+
super.setMetadata(metadata);
|
158
|
+
this.engine.client.sendUpdateLocalMetadata(metadata, this.name ?? '');
|
159
|
+
}
|
160
|
+
|
161
|
+
/**
|
162
|
+
* Sets and updates the name of the local participant.
|
163
|
+
* Note: this requires `CanUpdateOwnMetadata` permission encoded in the token.
|
164
|
+
* @param metadata
|
165
|
+
*/
|
166
|
+
setName(name: string): void {
|
167
|
+
super.setName(name);
|
168
|
+
this.engine.client.sendUpdateLocalMetadata(this.metadata ?? '', name);
|
169
|
+
}
|
170
|
+
|
151
171
|
/**
|
152
172
|
* Enable or disable a participant's camera track.
|
153
173
|
*
|
@@ -961,13 +981,15 @@ export default class LocalParticipant extends Participant {
|
|
961
981
|
}
|
962
982
|
|
963
983
|
/** @internal */
|
964
|
-
updateInfo(info: ParticipantInfo) {
|
984
|
+
updateInfo(info: ParticipantInfo): boolean {
|
965
985
|
if (info.sid !== this.sid) {
|
966
986
|
// drop updates that specify a wrong sid.
|
967
987
|
// the sid for local participant is only explicitly set on join and full reconnect
|
968
|
-
return;
|
988
|
+
return false;
|
989
|
+
}
|
990
|
+
if (!super.updateInfo(info)) {
|
991
|
+
return false;
|
969
992
|
}
|
970
|
-
super.updateInfo(info);
|
971
993
|
|
972
994
|
// reconcile track mute status.
|
973
995
|
// if server's track mute status doesn't match actual, we'll have to update
|
@@ -986,6 +1008,7 @@ export default class LocalParticipant extends Participant {
|
|
986
1008
|
}
|
987
1009
|
}
|
988
1010
|
});
|
1011
|
+
return true;
|
989
1012
|
}
|
990
1013
|
|
991
1014
|
private updateTrackSubscriptionPermissions = () => {
|
@@ -2,10 +2,10 @@ import { EventEmitter } from 'events';
|
|
2
2
|
import type TypedEmitter from 'typed-emitter';
|
3
3
|
import log from '../../logger';
|
4
4
|
import {
|
5
|
-
ConnectionQuality as ProtoQuality,
|
6
5
|
DataPacket_Kind,
|
7
6
|
ParticipantInfo,
|
8
7
|
ParticipantPermission,
|
8
|
+
ConnectionQuality as ProtoQuality,
|
9
9
|
} from '../../proto/livekit_models';
|
10
10
|
import { ParticipantEvent, TrackEvent } from '../events';
|
11
11
|
import type LocalTrackPublication from '../track/LocalTrackPublication';
|
@@ -144,10 +144,23 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
144
144
|
}
|
145
145
|
|
146
146
|
/** @internal */
|
147
|
-
updateInfo(info: ParticipantInfo) {
|
147
|
+
updateInfo(info: ParticipantInfo): boolean {
|
148
|
+
// it's possible the update could be applied out of order due to await
|
149
|
+
// during reconnect sequences. when that happens, it's possible for server
|
150
|
+
// to have sent more recent version of participant info while JS is waiting
|
151
|
+
// to process the existing payload.
|
152
|
+
// when the participant sid remains the same, and we already have a later version
|
153
|
+
// of the payload, they can be safely skipped
|
154
|
+
if (
|
155
|
+
this.participantInfo &&
|
156
|
+
this.participantInfo.sid === info.sid &&
|
157
|
+
this.participantInfo.version > info.version
|
158
|
+
) {
|
159
|
+
return false;
|
160
|
+
}
|
148
161
|
this.identity = info.identity;
|
149
162
|
this.sid = info.sid;
|
150
|
-
this.
|
163
|
+
this.setName(info.name);
|
151
164
|
this.setMetadata(info.metadata);
|
152
165
|
if (info.permission) {
|
153
166
|
this.setPermissions(info.permission);
|
@@ -155,6 +168,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
155
168
|
// set this last so setMetadata can detect changes
|
156
169
|
this.participantInfo = info;
|
157
170
|
log.trace('update participant info', { info });
|
171
|
+
return true;
|
158
172
|
}
|
159
173
|
|
160
174
|
/** @internal */
|
@@ -168,6 +182,15 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
168
182
|
}
|
169
183
|
}
|
170
184
|
|
185
|
+
protected setName(name: string) {
|
186
|
+
const changed = this.name !== name;
|
187
|
+
this.name = name;
|
188
|
+
|
189
|
+
if (changed) {
|
190
|
+
this.emit(ParticipantEvent.ParticipantNameChanged, name);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
171
194
|
/** @internal */
|
172
195
|
setPermissions(permissions: ParticipantPermission): boolean {
|
173
196
|
const prevPermissions = this.permissions;
|
@@ -250,6 +273,7 @@ export type ParticipantEventCallbacks = {
|
|
250
273
|
localTrackPublished: (publication: LocalTrackPublication) => void;
|
251
274
|
localTrackUnpublished: (publication: LocalTrackPublication) => void;
|
252
275
|
participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
|
276
|
+
participantNameChanged: (name: string) => void;
|
253
277
|
dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void;
|
254
278
|
isSpeakingChanged: (speaking: boolean) => void;
|
255
279
|
connectionQualityChanged: (connectionQuality: ConnectionQuality) => void;
|
@@ -3,13 +3,13 @@ import log from '../../logger';
|
|
3
3
|
import type { ParticipantInfo } from '../../proto/livekit_models';
|
4
4
|
import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc';
|
5
5
|
import { ParticipantEvent, TrackEvent } from '../events';
|
6
|
-
import type { AudioOutputOptions } from '../track/options';
|
7
6
|
import RemoteAudioTrack from '../track/RemoteAudioTrack';
|
8
7
|
import type RemoteTrack from '../track/RemoteTrack';
|
9
8
|
import RemoteTrackPublication from '../track/RemoteTrackPublication';
|
10
9
|
import RemoteVideoTrack from '../track/RemoteVideoTrack';
|
11
10
|
import { Track } from '../track/Track';
|
12
11
|
import type { TrackPublication } from '../track/TrackPublication';
|
12
|
+
import type { AudioOutputOptions } from '../track/options';
|
13
13
|
import type { AdaptiveStreamSettings } from '../track/types';
|
14
14
|
import Participant, { ParticipantEventCallbacks } from './Participant';
|
15
15
|
|
@@ -215,8 +215,10 @@ export default class RemoteParticipant extends Participant {
|
|
215
215
|
}
|
216
216
|
|
217
217
|
/** @internal */
|
218
|
-
updateInfo(info: ParticipantInfo) {
|
219
|
-
super.updateInfo(info)
|
218
|
+
updateInfo(info: ParticipantInfo): boolean {
|
219
|
+
if (!super.updateInfo(info)) {
|
220
|
+
return false;
|
221
|
+
}
|
220
222
|
|
221
223
|
// we are getting a list of all available tracks, reconcile in here
|
222
224
|
// and send out events for changes
|
@@ -277,6 +279,7 @@ export default class RemoteParticipant extends Participant {
|
|
277
279
|
newTracks.forEach((publication) => {
|
278
280
|
this.emit(ParticipantEvent.TrackPublished, publication);
|
279
281
|
});
|
282
|
+
return true;
|
280
283
|
}
|
281
284
|
|
282
285
|
/** @internal */
|
@@ -2,6 +2,7 @@ import log from '../../logger';
|
|
2
2
|
import { TrackInvalidError } from '../errors';
|
3
3
|
import LocalAudioTrack from '../track/LocalAudioTrack';
|
4
4
|
import LocalVideoTrack from '../track/LocalVideoTrack';
|
5
|
+
import { Track } from '../track/Track';
|
5
6
|
import {
|
6
7
|
BackupVideoCodec,
|
7
8
|
ScreenSharePresets,
|
@@ -12,7 +13,6 @@ import {
|
|
12
13
|
VideoPresets,
|
13
14
|
VideoPresets43,
|
14
15
|
} from '../track/options';
|
15
|
-
import { Track } from '../track/Track';
|
16
16
|
|
17
17
|
/** @internal */
|
18
18
|
export function mediaTrackToLocalTrack(
|
@@ -3,8 +3,8 @@ import { TrackEvent } from '../events';
|
|
3
3
|
import { AudioSenderStats, computeBitrate, monitorFrequency } from '../stats';
|
4
4
|
import { isWeb } from '../utils';
|
5
5
|
import LocalTrack from './LocalTrack';
|
6
|
-
import type { AudioCaptureOptions } from './options';
|
7
6
|
import { Track } from './Track';
|
7
|
+
import type { AudioCaptureOptions } from './options';
|
8
8
|
import { constraintsForOptions, detectSilence } from './utils';
|
9
9
|
|
10
10
|
export default class LocalAudioTrack extends LocalTrack {
|
@@ -39,7 +39,8 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
39
39
|
}
|
40
40
|
|
41
41
|
async mute(): Promise<LocalAudioTrack> {
|
42
|
-
await this.
|
42
|
+
const unlock = await this.muteLock.lock();
|
43
|
+
try {
|
43
44
|
// disabled special handling as it will cause BT headsets to switch communication modes
|
44
45
|
if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
|
45
46
|
log.debug('stopping mic track');
|
@@ -47,12 +48,15 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
47
48
|
this._mediaStreamTrack.stop();
|
48
49
|
}
|
49
50
|
await super.mute();
|
50
|
-
|
51
|
-
|
51
|
+
return this;
|
52
|
+
} finally {
|
53
|
+
unlock();
|
54
|
+
}
|
52
55
|
}
|
53
56
|
|
54
57
|
async unmute(): Promise<LocalAudioTrack> {
|
55
|
-
await this.
|
58
|
+
const unlock = await this.muteLock.lock();
|
59
|
+
try {
|
56
60
|
if (
|
57
61
|
this.source === Track.Source.Microphone &&
|
58
62
|
(this.stopOnMute || this._mediaStreamTrack.readyState === 'ended') &&
|
@@ -62,8 +66,11 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
62
66
|
await this.restartTrack();
|
63
67
|
}
|
64
68
|
await super.unmute();
|
65
|
-
|
66
|
-
|
69
|
+
|
70
|
+
return this;
|
71
|
+
} finally {
|
72
|
+
unlock();
|
73
|
+
}
|
67
74
|
}
|
68
75
|
|
69
76
|
async restartTrack(options?: AudioCaptureOptions) {
|
@@ -1,11 +1,16 @@
|
|
1
|
-
import Queue from 'async-await-queue';
|
2
1
|
import log from '../../logger';
|
3
2
|
import DeviceManager from '../DeviceManager';
|
4
3
|
import { TrackInvalidError } from '../errors';
|
5
4
|
import { TrackEvent } from '../events';
|
6
|
-
import {
|
5
|
+
import {
|
6
|
+
Mutex,
|
7
|
+
getEmptyAudioStreamTrack,
|
8
|
+
getEmptyVideoStreamTrack,
|
9
|
+
isMobile,
|
10
|
+
sleep,
|
11
|
+
} from '../utils';
|
12
|
+
import { Track, attachToElement, detachTrack } from './Track';
|
7
13
|
import type { VideoCodec } from './options';
|
8
|
-
import { attachToElement, detachTrack, Track } from './Track';
|
9
14
|
|
10
15
|
const defaultDimensionsTimeout = 2 * 1000;
|
11
16
|
|
@@ -22,7 +27,9 @@ export default abstract class LocalTrack extends Track {
|
|
22
27
|
|
23
28
|
protected providedByUser: boolean;
|
24
29
|
|
25
|
-
protected
|
30
|
+
protected muteLock: Mutex;
|
31
|
+
|
32
|
+
protected pauseUpstreamLock: Mutex;
|
26
33
|
|
27
34
|
/**
|
28
35
|
*
|
@@ -42,7 +49,8 @@ export default abstract class LocalTrack extends Track {
|
|
42
49
|
this.constraints = constraints ?? mediaTrack.getConstraints();
|
43
50
|
this.reacquireTrack = false;
|
44
51
|
this.providedByUser = userProvidedTrack;
|
45
|
-
this.
|
52
|
+
this.muteLock = new Mutex();
|
53
|
+
this.pauseUpstreamLock = new Mutex();
|
46
54
|
}
|
47
55
|
|
48
56
|
get id(): string {
|
@@ -246,7 +254,8 @@ export default abstract class LocalTrack extends Track {
|
|
246
254
|
};
|
247
255
|
|
248
256
|
async pauseUpstream() {
|
249
|
-
this.
|
257
|
+
const unlock = await this.pauseUpstreamLock.lock();
|
258
|
+
try {
|
250
259
|
if (this._isUpstreamPaused === true) {
|
251
260
|
return;
|
252
261
|
}
|
@@ -260,11 +269,14 @@ export default abstract class LocalTrack extends Track {
|
|
260
269
|
const emptyTrack =
|
261
270
|
this.kind === Track.Kind.Audio ? getEmptyAudioStreamTrack() : getEmptyVideoStreamTrack();
|
262
271
|
await this.sender.replaceTrack(emptyTrack);
|
263
|
-
}
|
272
|
+
} finally {
|
273
|
+
unlock();
|
274
|
+
}
|
264
275
|
}
|
265
276
|
|
266
277
|
async resumeUpstream() {
|
267
|
-
this.
|
278
|
+
const unlock = await this.pauseUpstreamLock.lock();
|
279
|
+
try {
|
268
280
|
if (this._isUpstreamPaused === false) {
|
269
281
|
return;
|
270
282
|
}
|
@@ -276,7 +288,9 @@ export default abstract class LocalTrack extends Track {
|
|
276
288
|
this.emit(TrackEvent.UpstreamResumed, this);
|
277
289
|
|
278
290
|
await this.sender.replaceTrack(this._mediaStreamTrack);
|
279
|
-
}
|
291
|
+
} finally {
|
292
|
+
unlock();
|
293
|
+
}
|
280
294
|
}
|
281
295
|
|
282
296
|
protected abstract monitorSender(): void;
|
@@ -3,9 +3,9 @@ import { TrackEvent } from '../events';
|
|
3
3
|
import type LocalAudioTrack from './LocalAudioTrack';
|
4
4
|
import type LocalTrack from './LocalTrack';
|
5
5
|
import type LocalVideoTrack from './LocalVideoTrack';
|
6
|
-
import type { TrackPublishOptions } from './options';
|
7
6
|
import type { Track } from './Track';
|
8
7
|
import { TrackPublication } from './TrackPublication';
|
8
|
+
import type { TrackPublishOptions } from './options';
|
9
9
|
|
10
10
|
export default class LocalTrackPublication extends TrackPublication {
|
11
11
|
track?: LocalTrack = undefined;
|
@@ -2,11 +2,11 @@ import type { SignalClient } from '../../api/SignalClient';
|
|
2
2
|
import log from '../../logger';
|
3
3
|
import { VideoLayer, VideoQuality } from '../../proto/livekit_models';
|
4
4
|
import type { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc';
|
5
|
-
import { computeBitrate, monitorFrequency
|
6
|
-
import { isFireFox, isMobile, isWeb
|
5
|
+
import { VideoSenderStats, computeBitrate, monitorFrequency } from '../stats';
|
6
|
+
import { Mutex, isFireFox, isMobile, isWeb } from '../utils';
|
7
7
|
import LocalTrack from './LocalTrack';
|
8
|
-
import type { VideoCaptureOptions, VideoCodec } from './options';
|
9
8
|
import { Track } from './Track';
|
9
|
+
import type { VideoCaptureOptions, VideoCodec } from './options';
|
10
10
|
import { constraintsForOptions } from './utils';
|
11
11
|
|
12
12
|
export class SimulcastTrackInfo {
|
@@ -97,26 +97,32 @@ export default class LocalVideoTrack extends LocalTrack {
|
|
97
97
|
}
|
98
98
|
|
99
99
|
async mute(): Promise<LocalVideoTrack> {
|
100
|
-
await this.
|
100
|
+
const unlock = await this.muteLock.lock();
|
101
|
+
try {
|
101
102
|
if (this.source === Track.Source.Camera && !this.isUserProvided) {
|
102
103
|
log.debug('stopping camera track');
|
103
104
|
// also stop the track, so that camera indicator is turned off
|
104
105
|
this._mediaStreamTrack.stop();
|
105
106
|
}
|
106
107
|
await super.mute();
|
107
|
-
|
108
|
-
|
108
|
+
return this;
|
109
|
+
} finally {
|
110
|
+
unlock();
|
111
|
+
}
|
109
112
|
}
|
110
113
|
|
111
114
|
async unmute(): Promise<LocalVideoTrack> {
|
112
|
-
await this.
|
115
|
+
const unlock = await this.muteLock.lock();
|
116
|
+
try {
|
113
117
|
if (this.source === Track.Source.Camera && !this.isUserProvided) {
|
114
118
|
log.debug('reacquiring camera track');
|
115
119
|
await this.restartTrack();
|
116
120
|
}
|
117
121
|
await super.unmute();
|
118
|
-
|
119
|
-
|
122
|
+
return this;
|
123
|
+
} finally {
|
124
|
+
unlock();
|
125
|
+
}
|
120
126
|
}
|
121
127
|
|
122
128
|
async getSenderStats(): Promise<VideoSenderStats[]> {
|
@@ -2,9 +2,9 @@ import log from '../../logger';
|
|
2
2
|
import { TrackEvent } from '../events';
|
3
3
|
import { AudioReceiverStats, computeBitrate } from '../stats';
|
4
4
|
import { supportsSetSinkId } from '../utils';
|
5
|
-
import type { AudioOutputOptions } from './options';
|
6
5
|
import RemoteTrack from './RemoteTrack';
|
7
6
|
import { Track } from './Track';
|
7
|
+
import type { AudioOutputOptions } from './options';
|
8
8
|
|
9
9
|
export default class RemoteAudioTrack extends RemoteTrack {
|
10
10
|
private prevStats?: AudioReceiverStats;
|
@@ -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);
|
@@ -238,8 +239,8 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
238
239
|
}
|
239
240
|
|
240
241
|
private isManualOperationAllowed(): boolean {
|
241
|
-
if (this.isAdaptiveStream) {
|
242
|
-
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', {
|
243
244
|
trackSid: this.trackSid,
|
244
245
|
});
|
245
246
|
return false;
|
@@ -1,6 +1,6 @@
|
|
1
|
+
import MockMediaStreamTrack from '../../test/MockMediaStreamTrack';
|
1
2
|
import { TrackEvent } from '../events';
|
2
3
|
import RemoteVideoTrack, { ElementInfo } from './RemoteVideoTrack';
|
3
|
-
import MockMediaStreamTrack from '../../test/MockMediaStreamTrack';
|
4
4
|
import type { Track } from './Track';
|
5
5
|
|
6
6
|
jest.useFakeTimers();
|
@@ -1,16 +1,17 @@
|
|
1
1
|
import { debounce } from 'ts-debounce';
|
2
2
|
import log from '../../logger';
|
3
3
|
import { TrackEvent } from '../events';
|
4
|
-
import {
|
4
|
+
import { VideoReceiverStats, computeBitrate } from '../stats';
|
5
5
|
import CriticalTimers from '../timers';
|
6
6
|
import {
|
7
|
+
ObservableMediaElement,
|
8
|
+
getDevicePixelRatio,
|
7
9
|
getIntersectionObserver,
|
8
10
|
getResizeObserver,
|
9
11
|
isWeb,
|
10
|
-
ObservableMediaElement,
|
11
12
|
} from '../utils';
|
12
13
|
import RemoteTrack from './RemoteTrack';
|
13
|
-
import { attachToElement, detachTrack
|
14
|
+
import { Track, attachToElement, detachTrack } from './Track';
|
14
15
|
import type { AdaptiveStreamSettings } from './types';
|
15
16
|
|
16
17
|
const REACTION_DELAY = 100;
|
@@ -26,7 +27,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
26
27
|
|
27
28
|
private lastDimensions?: Track.Dimensions;
|
28
29
|
|
29
|
-
private
|
30
|
+
private isObserved: boolean = false;
|
30
31
|
|
31
32
|
constructor(
|
32
33
|
mediaTrack: MediaStreamTrack,
|
@@ -43,7 +44,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
43
44
|
}
|
44
45
|
|
45
46
|
get mediaStreamTrack() {
|
46
|
-
if (this.isAdaptiveStream && !this.
|
47
|
+
if (this.isAdaptiveStream && !this.isObserved) {
|
47
48
|
log.warn(
|
48
49
|
'When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start',
|
49
50
|
);
|
@@ -83,7 +84,6 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
83
84
|
const elementInfo = new HTMLElementInfo(element);
|
84
85
|
this.observeElementInfo(elementInfo);
|
85
86
|
}
|
86
|
-
this.hasUsedAttach = true;
|
87
87
|
return element;
|
88
88
|
}
|
89
89
|
|
@@ -110,6 +110,7 @@ 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
|
+
this.isObserved = true;
|
113
114
|
} else {
|
114
115
|
log.warn('visibility resize observer not triggered');
|
115
116
|
}
|
@@ -253,7 +254,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
253
254
|
let maxHeight = 0;
|
254
255
|
for (const info of this.elementInfos) {
|
255
256
|
const pixelDensity = this.adaptiveStreamSettings?.pixelDensity ?? 1;
|
256
|
-
const pixelDensityValue = pixelDensity === 'screen' ?
|
257
|
+
const pixelDensityValue = pixelDensity === 'screen' ? getDevicePixelRatio() : pixelDensity;
|
257
258
|
const currentElementWidth = info.width() * pixelDensityValue;
|
258
259
|
const currentElementHeight = info.height() * pixelDensityValue;
|
259
260
|
if (currentElementWidth + currentElementHeight > maxWidth + maxHeight) {
|