livekit-client 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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();
|