livekit-client 1.8.0 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +3 -2
- package/dist/livekit-client.esm.mjs +13470 -13341
- 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 +5 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +5 -0
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +32 -0
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +5 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +19 -15
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +15 -0
- 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 +1 -1
- 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/create.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -0
- package/dist/src/room/types.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 +6 -7
- package/dist/ts4.2/src/proto/livekit_models.d.ts +5 -0
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +32 -0
- package/dist/ts4.2/src/room/RTCEngine.d.ts +5 -3
- package/dist/ts4.2/src/room/Room.d.ts +19 -15
- package/dist/ts4.2/src/room/events.d.ts +15 -0
- 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 +1 -1
- 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/types.d.ts +1 -0
- package/package.json +4 -3
- package/src/api/SignalClient.ts +38 -26
- 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 +8 -10
- package/src/proto/livekit_models.ts +15 -0
- package/src/room/RTCEngine.ts +32 -11
- package/src/room/RegionUrlProvider.ts +1 -1
- package/src/room/Room.ts +102 -70
- package/src/room/events.ts +17 -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 +1 -1
- package/src/room/track/LocalTrack.ts +2 -2
- package/src/room/track/LocalTrackPublication.ts +1 -1
- package/src/room/track/LocalVideoTrack.ts +3 -3
- package/src/room/track/RemoteAudioTrack.ts +1 -1
- package/src/room/track/RemoteVideoTrack.test.ts +1 -1
- package/src/room/track/RemoteVideoTrack.ts +3 -3
- package/src/room/track/create.ts +2 -2
- package/src/room/types.ts +11 -0
package/src/room/Room.ts
CHANGED
@@ -29,6 +29,9 @@ import {
|
|
29
29
|
StreamStateUpdate,
|
30
30
|
SubscriptionPermissionUpdate,
|
31
31
|
} from '../proto/livekit_rtc';
|
32
|
+
import DeviceManager from './DeviceManager';
|
33
|
+
import RTCEngine from './RTCEngine';
|
34
|
+
import { RegionUrlProvider } from './RegionUrlProvider';
|
32
35
|
import {
|
33
36
|
audioDefaults,
|
34
37
|
publishDefaults,
|
@@ -36,14 +39,12 @@ import {
|
|
36
39
|
roomOptionDefaults,
|
37
40
|
videoDefaults,
|
38
41
|
} from './defaults';
|
39
|
-
import DeviceManager from './DeviceManager';
|
40
42
|
import { ConnectionError, ConnectionErrorReason, UnsupportedServer } from './errors';
|
41
43
|
import { EngineEvent, ParticipantEvent, RoomEvent, TrackEvent } from './events';
|
42
44
|
import LocalParticipant from './participant/LocalParticipant';
|
43
45
|
import type Participant from './participant/Participant';
|
44
46
|
import type { ConnectionQuality } from './participant/Participant';
|
45
47
|
import RemoteParticipant from './participant/RemoteParticipant';
|
46
|
-
import RTCEngine from './RTCEngine';
|
47
48
|
import LocalAudioTrack from './track/LocalAudioTrack';
|
48
49
|
import LocalTrackPublication from './track/LocalTrackPublication';
|
49
50
|
import LocalVideoTrack from './track/LocalVideoTrack';
|
@@ -53,18 +54,17 @@ import { Track } from './track/Track';
|
|
53
54
|
import type { TrackPublication } from './track/TrackPublication';
|
54
55
|
import type { AdaptiveStreamSettings } from './track/types';
|
55
56
|
import { getNewAudioContext } from './track/utils';
|
56
|
-
import type { SimulationOptions } from './types';
|
57
|
+
import type { SimulationOptions, SimulationScenario } from './types';
|
57
58
|
import {
|
58
|
-
createDummyVideoStreamTrack,
|
59
59
|
Future,
|
60
|
+
Mutex,
|
61
|
+
createDummyVideoStreamTrack,
|
60
62
|
getEmptyAudioStreamTrack,
|
61
63
|
isCloud,
|
62
64
|
isWeb,
|
63
|
-
Mutex,
|
64
65
|
supportsSetSinkId,
|
65
66
|
unpackStreamId,
|
66
67
|
} from './utils';
|
67
|
-
import { RegionUrlProvider } from './RegionUrlProvider';
|
68
68
|
|
69
69
|
export enum ConnectionState {
|
70
70
|
Disconnected = 'disconnected',
|
@@ -99,23 +99,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
99
99
|
/** @internal */
|
100
100
|
engine!: RTCEngine;
|
101
101
|
|
102
|
-
// available after connected
|
103
|
-
/** server assigned unique room id */
|
104
|
-
sid: string = '';
|
105
|
-
|
106
|
-
/** user assigned name, derived from JWT token */
|
107
|
-
name: string = '';
|
108
|
-
|
109
102
|
/** the current participant */
|
110
103
|
localParticipant: LocalParticipant;
|
111
104
|
|
112
|
-
/** room metadata */
|
113
|
-
metadata: string | undefined = undefined;
|
114
|
-
|
115
105
|
/** options of room */
|
116
106
|
options: InternalRoomOptions;
|
117
107
|
|
118
|
-
private
|
108
|
+
private roomInfo?: RoomModel;
|
119
109
|
|
120
110
|
private identityToSid: Map<string, string>;
|
121
111
|
|
@@ -165,6 +155,36 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
165
155
|
this.localParticipant = new LocalParticipant('', '', this.engine, this.options);
|
166
156
|
}
|
167
157
|
|
158
|
+
/**
|
159
|
+
* if the current room has a participant with `recorder: true` in its JWT grant
|
160
|
+
**/
|
161
|
+
get isRecording(): boolean {
|
162
|
+
return this.roomInfo?.activeRecording ?? false;
|
163
|
+
}
|
164
|
+
|
165
|
+
/** server assigned unique room id */
|
166
|
+
get sid(): string {
|
167
|
+
return this.roomInfo?.sid ?? '';
|
168
|
+
}
|
169
|
+
|
170
|
+
/** user assigned name, derived from JWT token */
|
171
|
+
get name(): string {
|
172
|
+
return this.roomInfo?.name ?? '';
|
173
|
+
}
|
174
|
+
|
175
|
+
/** room metadata */
|
176
|
+
get metadata(): string | undefined {
|
177
|
+
return this.roomInfo?.metadata;
|
178
|
+
}
|
179
|
+
|
180
|
+
get numParticipants(): number {
|
181
|
+
return this.roomInfo?.numParticipants ?? 0;
|
182
|
+
}
|
183
|
+
|
184
|
+
get numPublishers(): number {
|
185
|
+
return this.roomInfo?.numPublishers ?? 0;
|
186
|
+
}
|
187
|
+
|
168
188
|
private maybeCreateEngine() {
|
169
189
|
if (this.engine) {
|
170
190
|
return;
|
@@ -207,7 +227,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
207
227
|
}
|
208
228
|
})
|
209
229
|
.on(EngineEvent.Restarting, this.handleRestarting)
|
210
|
-
.on(EngineEvent.
|
230
|
+
.on(EngineEvent.SignalRestarted, this.handleSignalRestarted)
|
211
231
|
.on(EngineEvent.DCBufferStatusChanged, (status, kind) => {
|
212
232
|
this.emit(RoomEvent.DCBufferStatusChanged, status, kind);
|
213
233
|
});
|
@@ -368,31 +388,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
368
388
|
this.localParticipant.sid = pi.sid;
|
369
389
|
this.localParticipant.identity = pi.identity;
|
370
390
|
|
371
|
-
this.localParticipant.updateInfo(pi);
|
372
|
-
// forward metadata changed for the local participant
|
373
|
-
this.setupLocalParticipantEvents();
|
374
|
-
|
375
391
|
// populate remote participants, these should not trigger new events
|
376
|
-
joinResponse.otherParticipants
|
377
|
-
if (
|
378
|
-
info.sid !== this.localParticipant.sid &&
|
379
|
-
info.identity !== this.localParticipant.identity
|
380
|
-
) {
|
381
|
-
this.getOrCreateParticipant(info.sid, info);
|
382
|
-
} else {
|
383
|
-
log.warn('received info to create local participant as remote participant', {
|
384
|
-
info,
|
385
|
-
localParticipant: this.localParticipant,
|
386
|
-
});
|
387
|
-
}
|
388
|
-
});
|
392
|
+
this.handleParticipantUpdates([pi, ...joinResponse.otherParticipants]);
|
389
393
|
|
390
|
-
|
391
|
-
|
392
|
-
this.metadata = joinResponse.room!.metadata;
|
393
|
-
if (this._isRecording !== joinResponse.room!.activeRecording) {
|
394
|
-
this._isRecording = joinResponse.room!.activeRecording;
|
395
|
-
this.emit(RoomEvent.RecordingStatusChanged, joinResponse.room!.activeRecording);
|
394
|
+
if (joinResponse.room) {
|
395
|
+
this.handleRoomUpdate(joinResponse.room);
|
396
396
|
}
|
397
397
|
};
|
398
398
|
|
@@ -433,6 +433,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
433
433
|
);
|
434
434
|
|
435
435
|
this.applyJoinResponse(joinResponse);
|
436
|
+
// forward metadata changed for the local participant
|
437
|
+
this.setupLocalParticipantEvents();
|
436
438
|
this.emit(RoomEvent.SignalConnected);
|
437
439
|
} catch (err) {
|
438
440
|
this.recreateEngine();
|
@@ -534,17 +536,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
534
536
|
this.connectFuture = undefined;
|
535
537
|
}
|
536
538
|
|
537
|
-
/**
|
538
|
-
* if the current room has a participant with `recorder: true` in its JWT grant
|
539
|
-
**/
|
540
|
-
get isRecording() {
|
541
|
-
return this._isRecording;
|
542
|
-
}
|
543
|
-
|
544
539
|
/**
|
545
540
|
* @internal for testing
|
546
541
|
*/
|
547
|
-
async simulateScenario(scenario:
|
542
|
+
async simulateScenario(scenario: SimulationScenario) {
|
548
543
|
let postAction = () => {};
|
549
544
|
let req: SimulateScenario | undefined;
|
550
545
|
switch (scenario) {
|
@@ -593,6 +588,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
593
588
|
this.engine.client.onClose('simulate resume-reconnect');
|
594
589
|
}
|
595
590
|
break;
|
591
|
+
case 'full-reconnect':
|
592
|
+
this.engine.fullReconnectOnNext = true;
|
593
|
+
await this.engine.client.close();
|
594
|
+
if (this.engine.client.onClose) {
|
595
|
+
this.engine.client.onClose('simulate full-reconnect');
|
596
|
+
}
|
597
|
+
break;
|
596
598
|
case 'force-tcp':
|
597
599
|
case 'force-tls':
|
598
600
|
req = SimulateScenario.fromPartial({
|
@@ -734,6 +736,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
734
736
|
private setupLocalParticipantEvents() {
|
735
737
|
this.localParticipant
|
736
738
|
.on(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged)
|
739
|
+
.on(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged)
|
737
740
|
.on(ParticipantEvent.TrackMuted, this.onLocalTrackMuted)
|
738
741
|
.on(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted)
|
739
742
|
.on(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished)
|
@@ -825,20 +828,14 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
825
828
|
}
|
826
829
|
};
|
827
830
|
|
828
|
-
private
|
829
|
-
log.debug(`reconnected to server`, {
|
831
|
+
private handleSignalRestarted = async (joinResponse: JoinResponse) => {
|
832
|
+
log.debug(`signal reconnected to server`, {
|
830
833
|
region: joinResponse.serverRegion,
|
831
834
|
});
|
832
835
|
|
833
|
-
|
834
|
-
// rehydrate participants
|
835
|
-
if (joinResponse.participant) {
|
836
|
-
// with a restart, the sid will have changed, we'll map our understanding to it
|
837
|
-
this.localParticipant.sid = joinResponse.participant.sid;
|
838
|
-
this.handleParticipantUpdates([joinResponse.participant]);
|
839
|
-
}
|
840
|
-
this.handleParticipantUpdates(joinResponse.otherParticipants);
|
836
|
+
this.applyJoinResponse(joinResponse);
|
841
837
|
|
838
|
+
try {
|
842
839
|
// unpublish & republish tracks
|
843
840
|
const localPubs: LocalTrackPublication[] = [];
|
844
841
|
this.localParticipant.tracks.forEach((pub) => {
|
@@ -872,10 +869,24 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
872
869
|
);
|
873
870
|
} catch (error) {
|
874
871
|
log.error('error trying to re-publish tracks after reconnection', { error });
|
875
|
-
} finally {
|
876
|
-
this.setAndEmitConnectionState(ConnectionState.Connected);
|
877
|
-
this.emit(RoomEvent.Reconnected);
|
878
872
|
}
|
873
|
+
|
874
|
+
try {
|
875
|
+
await this.engine.waitForRestarted();
|
876
|
+
log.debug(`fully reconnected to server`, {
|
877
|
+
region: joinResponse.serverRegion,
|
878
|
+
});
|
879
|
+
} catch {
|
880
|
+
// reconnection failed, handleDisconnect is being invoked already, just return here
|
881
|
+
return;
|
882
|
+
}
|
883
|
+
this.setAndEmitConnectionState(ConnectionState.Connected);
|
884
|
+
this.emit(RoomEvent.Reconnected);
|
885
|
+
|
886
|
+
// emit participant connected events after connection has been re-established
|
887
|
+
this.participants.forEach((participant) => {
|
888
|
+
this.emit(RoomEvent.ParticipantConnected, participant);
|
889
|
+
});
|
879
890
|
};
|
880
891
|
|
881
892
|
private handleDisconnect(shouldStopTracks = true, reason?: DisconnectReason) {
|
@@ -901,6 +912,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
901
912
|
|
902
913
|
this.localParticipant
|
903
914
|
.off(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged)
|
915
|
+
.off(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged)
|
904
916
|
.off(ParticipantEvent.TrackMuted, this.onLocalTrackMuted)
|
905
917
|
.off(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted)
|
906
918
|
.off(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished)
|
@@ -1106,14 +1118,14 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1106
1118
|
this.emit(RoomEvent.MediaDevicesChanged);
|
1107
1119
|
};
|
1108
1120
|
|
1109
|
-
private handleRoomUpdate = (
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1121
|
+
private handleRoomUpdate = (room: RoomModel) => {
|
1122
|
+
const oldRoom = this.roomInfo;
|
1123
|
+
this.roomInfo = room;
|
1124
|
+
if (oldRoom && oldRoom.metadata !== room.metadata) {
|
1125
|
+
this.emitWhenConnected(RoomEvent.RoomMetadataChanged, room.metadata);
|
1113
1126
|
}
|
1114
|
-
if (
|
1115
|
-
this.
|
1116
|
-
this.emitWhenConnected(RoomEvent.RoomMetadataChanged, r.metadata);
|
1127
|
+
if (oldRoom?.activeRecording !== room.activeRecording) {
|
1128
|
+
this.emitWhenConnected(RoomEvent.RecordingStatusChanged, room.activeRecording);
|
1117
1129
|
}
|
1118
1130
|
};
|
1119
1131
|
|
@@ -1222,6 +1234,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1222
1234
|
.on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
|
1223
1235
|
this.emitWhenConnected(RoomEvent.ParticipantMetadataChanged, metadata, participant);
|
1224
1236
|
})
|
1237
|
+
.on(ParticipantEvent.ParticipantNameChanged, (name) => {
|
1238
|
+
this.emitWhenConnected(RoomEvent.ParticipantNameChanged, name, participant);
|
1239
|
+
})
|
1225
1240
|
.on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
|
1226
1241
|
this.emitWhenConnected(RoomEvent.ConnectionQualityChanged, quality, participant);
|
1227
1242
|
})
|
@@ -1338,6 +1353,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1338
1353
|
this.emit(RoomEvent.ParticipantMetadataChanged, metadata, this.localParticipant);
|
1339
1354
|
};
|
1340
1355
|
|
1356
|
+
private onLocalParticipantNameChanged = (name: string) => {
|
1357
|
+
this.emit(RoomEvent.ParticipantNameChanged, name, this.localParticipant);
|
1358
|
+
};
|
1359
|
+
|
1341
1360
|
private onLocalTrackMuted = (pub: TrackPublication) => {
|
1342
1361
|
this.emit(RoomEvent.TrackMuted, pub, this.localParticipant);
|
1343
1362
|
};
|
@@ -1392,7 +1411,19 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1392
1411
|
...options.participants,
|
1393
1412
|
};
|
1394
1413
|
this.handleDisconnect();
|
1395
|
-
this.
|
1414
|
+
this.roomInfo = {
|
1415
|
+
sid: 'RM_SIMULATED',
|
1416
|
+
name: 'simulated-room',
|
1417
|
+
emptyTimeout: 0,
|
1418
|
+
maxParticipants: 0,
|
1419
|
+
creationTime: new Date().getTime(),
|
1420
|
+
metadata: '',
|
1421
|
+
numParticipants: 1,
|
1422
|
+
numPublishers: 1,
|
1423
|
+
turnPassword: '',
|
1424
|
+
enabledCodecs: [],
|
1425
|
+
activeRecording: false,
|
1426
|
+
};
|
1396
1427
|
|
1397
1428
|
this.localParticipant.updateInfo(
|
1398
1429
|
ParticipantInfo.fromPartial({
|
@@ -1537,6 +1568,7 @@ export type RoomEventCallbacks = {
|
|
1537
1568
|
metadata: string | undefined,
|
1538
1569
|
participant: RemoteParticipant | LocalParticipant,
|
1539
1570
|
) => void;
|
1571
|
+
participantNameChanged: (name: string, participant: RemoteParticipant | LocalParticipant) => void;
|
1540
1572
|
participantPermissionsChanged: (
|
1541
1573
|
prevPermissions: ParticipantPermission | undefined,
|
1542
1574
|
participant: RemoteParticipant | LocalParticipant,
|
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.
|
@@ -359,6 +367,14 @@ export enum ParticipantEvent {
|
|
359
367
|
*/
|
360
368
|
ParticipantMetadataChanged = 'participantMetadataChanged',
|
361
369
|
|
370
|
+
/**
|
371
|
+
* Participant's display name changed
|
372
|
+
*
|
373
|
+
* args: (name: string, [[Participant]])
|
374
|
+
*
|
375
|
+
*/
|
376
|
+
ParticipantNameChanged = 'participantNameChanged',
|
377
|
+
|
362
378
|
/**
|
363
379
|
* Data received from this participant as sender.
|
364
380
|
* Data packets provides the ability to use LiveKit to send/receive arbitrary payloads.
|
@@ -433,6 +449,7 @@ export enum EngineEvent {
|
|
433
449
|
Restarting = 'restarting',
|
434
450
|
Restarted = 'restarted',
|
435
451
|
SignalResumed = 'signalResumed',
|
452
|
+
SignalRestarted = 'signalRestarted',
|
436
453
|
Closing = 'closing',
|
437
454
|
MediaTrackAdded = 'mediaTrackAdded',
|
438
455
|
ActiveSpeakersUpdate = 'activeSpeakersUpdate',
|
@@ -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 {
|
@@ -3,14 +3,14 @@ import DeviceManager from '../DeviceManager';
|
|
3
3
|
import { TrackInvalidError } from '../errors';
|
4
4
|
import { TrackEvent } from '../events';
|
5
5
|
import {
|
6
|
+
Mutex,
|
6
7
|
getEmptyAudioStreamTrack,
|
7
8
|
getEmptyVideoStreamTrack,
|
8
9
|
isMobile,
|
9
|
-
Mutex,
|
10
10
|
sleep,
|
11
11
|
} from '../utils';
|
12
|
+
import { Track, attachToElement, detachTrack } from './Track';
|
12
13
|
import type { VideoCodec } from './options';
|
13
|
-
import { attachToElement, detachTrack, Track } from './Track';
|
14
14
|
|
15
15
|
const defaultDimensionsTimeout = 2 * 1000;
|
16
16
|
|
@@ -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 {
|
@@ -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;
|
@@ -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();
|