livekit-client 2.5.2 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/livekit-client.esm.mjs +545 -154
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +4 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +4 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +11 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +2 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +6 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -2
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/index.d.ts +1 -1
- package/dist/ts4.2/src/room/Room.d.ts +4 -1
- package/dist/ts4.2/src/room/events.d.ts +4 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +11 -1
- package/dist/ts4.2/src/room/participant/Participant.d.ts +2 -1
- package/dist/ts4.2/src/room/types.d.ts +6 -0
- package/dist/ts4.2/src/room/utils.d.ts +3 -2
- package/package.json +4 -4
- package/src/index.ts +6 -1
- package/src/room/Room.ts +27 -2
- package/src/room/events.ts +4 -0
- package/src/room/participant/LocalParticipant.ts +132 -44
- package/src/room/participant/Participant.ts +2 -1
- package/src/room/types.ts +7 -0
- package/src/room/utils.ts +17 -2
@@ -1,5 +1,6 @@
|
|
1
1
|
import {
|
2
2
|
AddTrackRequest,
|
3
|
+
ChatMessage as ChatMessageModel,
|
3
4
|
Codec,
|
4
5
|
DataPacket,
|
5
6
|
DataPacket_Kind,
|
@@ -13,6 +14,7 @@ import {
|
|
13
14
|
TrackInfo,
|
14
15
|
TrackUnpublishedResponse,
|
15
16
|
UserPacket,
|
17
|
+
protoInt64,
|
16
18
|
} from '@livekit/protocol';
|
17
19
|
import type { InternalRoomOptions } from '../../options';
|
18
20
|
import { PCTransportState } from '../PCTransportManager';
|
@@ -48,7 +50,7 @@ import {
|
|
48
50
|
mimeTypeToVideoCodecString,
|
49
51
|
screenCaptureToDisplayMediaStreamOptions,
|
50
52
|
} from '../track/utils';
|
51
|
-
import type { DataPublishOptions } from '../types';
|
53
|
+
import type { ChatMessage, DataPublishOptions } from '../types';
|
52
54
|
import {
|
53
55
|
Future,
|
54
56
|
isE2EESimulcastSupported,
|
@@ -88,6 +90,8 @@ export default class LocalParticipant extends Participant {
|
|
88
90
|
|
89
91
|
private pendingPublishPromises = new Map<LocalTrack, Promise<LocalTrackPublication>>();
|
90
92
|
|
93
|
+
private republishPromise: Promise<void> | undefined;
|
94
|
+
|
91
95
|
private cameraError: Error | undefined;
|
92
96
|
|
93
97
|
private microphoneError: Error | undefined;
|
@@ -380,6 +384,9 @@ export default class LocalParticipant extends Participant {
|
|
380
384
|
publishOptions?: TrackPublishOptions,
|
381
385
|
) {
|
382
386
|
this.log.debug('setTrackEnabled', { ...this.logContext, source, enabled });
|
387
|
+
if (this.republishPromise) {
|
388
|
+
await this.republishPromise;
|
389
|
+
}
|
383
390
|
let track = this.getTrackPublication(source);
|
384
391
|
if (enabled) {
|
385
392
|
if (track) {
|
@@ -387,9 +394,12 @@ export default class LocalParticipant extends Participant {
|
|
387
394
|
} else {
|
388
395
|
let localTracks: Array<LocalTrack> | undefined;
|
389
396
|
if (this.pendingPublishing.has(source)) {
|
390
|
-
|
391
|
-
|
392
|
-
|
397
|
+
const pendingTrack = await this.waitForPendingPublicationOfSource(source);
|
398
|
+
if (!pendingTrack) {
|
399
|
+
this.log.info('skipping duplicate published source', { ...this.logContext, source });
|
400
|
+
}
|
401
|
+
await pendingTrack?.unmute();
|
402
|
+
return pendingTrack;
|
393
403
|
}
|
394
404
|
this.pendingPublishing.add(source);
|
395
405
|
try {
|
@@ -437,16 +447,22 @@ export default class LocalParticipant extends Participant {
|
|
437
447
|
this.pendingPublishing.delete(source);
|
438
448
|
}
|
439
449
|
}
|
440
|
-
} else
|
441
|
-
|
442
|
-
|
443
|
-
track = await this.
|
444
|
-
|
445
|
-
|
446
|
-
|
450
|
+
} else {
|
451
|
+
if (!track?.track) {
|
452
|
+
// if there's no track available yet first wait for pending publishing promises of that source to see if it becomes available
|
453
|
+
track = await this.waitForPendingPublicationOfSource(source);
|
454
|
+
}
|
455
|
+
if (track && track.track) {
|
456
|
+
// screenshare cannot be muted, unpublish instead
|
457
|
+
if (source === Track.Source.ScreenShare) {
|
458
|
+
track = await this.unpublishTrack(track.track);
|
459
|
+
const screenAudioTrack = this.getTrackPublication(Track.Source.ScreenShareAudio);
|
460
|
+
if (screenAudioTrack && screenAudioTrack.track) {
|
461
|
+
this.unpublishTrack(screenAudioTrack.track);
|
462
|
+
}
|
463
|
+
} else {
|
464
|
+
await track.mute();
|
447
465
|
}
|
448
|
-
} else {
|
449
|
-
await track.mute();
|
450
466
|
}
|
451
467
|
}
|
452
468
|
return track;
|
@@ -611,15 +627,23 @@ export default class LocalParticipant extends Participant {
|
|
611
627
|
* @param track
|
612
628
|
* @param options
|
613
629
|
*/
|
614
|
-
async publishTrack(
|
630
|
+
async publishTrack(track: LocalTrack | MediaStreamTrack, options?: TrackPublishOptions) {
|
631
|
+
return this.publishOrRepublishTrack(track, options);
|
632
|
+
}
|
633
|
+
|
634
|
+
private async publishOrRepublishTrack(
|
615
635
|
track: LocalTrack | MediaStreamTrack,
|
616
636
|
options?: TrackPublishOptions,
|
637
|
+
isRepublish = false,
|
617
638
|
): Promise<LocalTrackPublication> {
|
618
639
|
if (track instanceof LocalAudioTrack) {
|
619
640
|
track.setAudioContext(this.audioContext);
|
620
641
|
}
|
621
642
|
|
622
643
|
await this.reconnectFuture?.promise;
|
644
|
+
if (this.republishPromise && !isRepublish) {
|
645
|
+
await this.republishPromise;
|
646
|
+
}
|
623
647
|
if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
|
624
648
|
await this.pendingPublishPromises.get(track);
|
625
649
|
}
|
@@ -1248,39 +1272,53 @@ export default class LocalParticipant extends Participant {
|
|
1248
1272
|
}
|
1249
1273
|
|
1250
1274
|
async republishAllTracks(options?: TrackPublishOptions, restartTracks: boolean = true) {
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1275
|
+
if (this.republishPromise) {
|
1276
|
+
await this.republishPromise;
|
1277
|
+
}
|
1278
|
+
this.republishPromise = new Promise(async (resolve, reject) => {
|
1279
|
+
try {
|
1280
|
+
const localPubs: LocalTrackPublication[] = [];
|
1281
|
+
this.trackPublications.forEach((pub) => {
|
1282
|
+
if (pub.track) {
|
1283
|
+
if (options) {
|
1284
|
+
pub.options = { ...pub.options, ...options };
|
1285
|
+
}
|
1286
|
+
localPubs.push(pub);
|
1287
|
+
}
|
1288
|
+
});
|
1289
|
+
|
1290
|
+
await Promise.all(
|
1291
|
+
localPubs.map(async (pub) => {
|
1292
|
+
const track = pub.track!;
|
1293
|
+
await this.unpublishTrack(track, false);
|
1294
|
+
if (
|
1295
|
+
restartTracks &&
|
1296
|
+
!track.isMuted &&
|
1297
|
+
track.source !== Track.Source.ScreenShare &&
|
1298
|
+
track.source !== Track.Source.ScreenShareAudio &&
|
1299
|
+
(track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) &&
|
1300
|
+
!track.isUserProvided
|
1301
|
+
) {
|
1302
|
+
// generally we need to restart the track before publishing, often a full reconnect
|
1303
|
+
// is necessary because computer had gone to sleep.
|
1304
|
+
this.log.debug('restarting existing track', {
|
1305
|
+
...this.logContext,
|
1306
|
+
track: pub.trackSid,
|
1307
|
+
});
|
1308
|
+
await track.restartTrack();
|
1309
|
+
}
|
1310
|
+
await this.publishOrRepublishTrack(track, pub.options, true);
|
1311
|
+
}),
|
1312
|
+
);
|
1313
|
+
resolve();
|
1314
|
+
} catch (error: any) {
|
1315
|
+
reject(error);
|
1316
|
+
} finally {
|
1317
|
+
this.republishPromise = undefined;
|
1258
1318
|
}
|
1259
1319
|
});
|
1260
1320
|
|
1261
|
-
await
|
1262
|
-
localPubs.map(async (pub) => {
|
1263
|
-
const track = pub.track!;
|
1264
|
-
await this.unpublishTrack(track, false);
|
1265
|
-
if (
|
1266
|
-
restartTracks &&
|
1267
|
-
!track.isMuted &&
|
1268
|
-
track.source !== Track.Source.ScreenShare &&
|
1269
|
-
track.source !== Track.Source.ScreenShareAudio &&
|
1270
|
-
(track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) &&
|
1271
|
-
!track.isUserProvided
|
1272
|
-
) {
|
1273
|
-
// generally we need to restart the track before publishing, often a full reconnect
|
1274
|
-
// is necessary because computer had gone to sleep.
|
1275
|
-
this.log.debug('restarting existing track', {
|
1276
|
-
...this.logContext,
|
1277
|
-
track: pub.trackSid,
|
1278
|
-
});
|
1279
|
-
await track.restartTrack();
|
1280
|
-
}
|
1281
|
-
await this.publishTrack(track, pub.options);
|
1282
|
-
}),
|
1283
|
-
);
|
1321
|
+
await this.republishPromise;
|
1284
1322
|
}
|
1285
1323
|
|
1286
1324
|
/**
|
@@ -1311,6 +1349,47 @@ export default class LocalParticipant extends Participant {
|
|
1311
1349
|
await this.engine.sendDataPacket(packet, kind);
|
1312
1350
|
}
|
1313
1351
|
|
1352
|
+
async sendChatMessage(text: string): Promise<ChatMessage> {
|
1353
|
+
const msg = {
|
1354
|
+
id: crypto.randomUUID(),
|
1355
|
+
message: text,
|
1356
|
+
timestamp: Date.now(),
|
1357
|
+
} as const satisfies ChatMessage;
|
1358
|
+
const packet = new DataPacket({
|
1359
|
+
value: {
|
1360
|
+
case: 'chatMessage',
|
1361
|
+
value: new ChatMessageModel({
|
1362
|
+
...msg,
|
1363
|
+
timestamp: protoInt64.parse(msg.timestamp),
|
1364
|
+
}),
|
1365
|
+
},
|
1366
|
+
});
|
1367
|
+
await this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
|
1368
|
+
this.emit(ParticipantEvent.ChatMessage, msg);
|
1369
|
+
return msg;
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
async editChatMessage(editText: string, originalMessage: ChatMessage) {
|
1373
|
+
const msg = {
|
1374
|
+
...originalMessage,
|
1375
|
+
message: editText,
|
1376
|
+
editTimestamp: Date.now(),
|
1377
|
+
} as const satisfies ChatMessage;
|
1378
|
+
const packet = new DataPacket({
|
1379
|
+
value: {
|
1380
|
+
case: 'chatMessage',
|
1381
|
+
value: new ChatMessageModel({
|
1382
|
+
...msg,
|
1383
|
+
timestamp: protoInt64.parse(msg.timestamp),
|
1384
|
+
editTimestamp: protoInt64.parse(msg.editTimestamp),
|
1385
|
+
}),
|
1386
|
+
},
|
1387
|
+
});
|
1388
|
+
await this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
|
1389
|
+
this.emit(ParticipantEvent.ChatMessage, msg);
|
1390
|
+
return msg;
|
1391
|
+
}
|
1392
|
+
|
1314
1393
|
/**
|
1315
1394
|
* Control who can subscribe to LocalParticipant's published tracks.
|
1316
1395
|
*
|
@@ -1571,4 +1650,13 @@ export default class LocalParticipant extends Participant {
|
|
1571
1650
|
});
|
1572
1651
|
return publication;
|
1573
1652
|
}
|
1653
|
+
|
1654
|
+
private async waitForPendingPublicationOfSource(source: Track.Source) {
|
1655
|
+
const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(
|
1656
|
+
([pendingTrack]) => pendingTrack.source === source,
|
1657
|
+
);
|
1658
|
+
if (publishPromiseEntry) {
|
1659
|
+
return publishPromiseEntry[1];
|
1660
|
+
}
|
1661
|
+
}
|
1574
1662
|
}
|
@@ -19,7 +19,7 @@ import type RemoteTrackPublication from '../track/RemoteTrackPublication';
|
|
19
19
|
import { Track } from '../track/Track';
|
20
20
|
import type { TrackPublication } from '../track/TrackPublication';
|
21
21
|
import { diffAttributes } from '../track/utils';
|
22
|
-
import type { LoggerOptions, TranscriptionSegment } from '../types';
|
22
|
+
import type { ChatMessage, LoggerOptions, TranscriptionSegment } from '../types';
|
23
23
|
|
24
24
|
export enum ConnectionQuality {
|
25
25
|
Excellent = 'excellent',
|
@@ -387,4 +387,5 @@ export type ParticipantEventCallbacks = {
|
|
387
387
|
) => void;
|
388
388
|
attributesChanged: (changedAttributes: Record<string, string>) => void;
|
389
389
|
localTrackSubscribed: (trackPublication: LocalTrackPublication) => void;
|
390
|
+
chatMessage: (msg: ChatMessage) => void;
|
390
391
|
};
|
package/src/room/types.ts
CHANGED
package/src/room/utils.ts
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
ChatMessage as ChatMessageModel,
|
3
|
+
ClientInfo,
|
4
|
+
ClientInfo_SDK,
|
5
|
+
Transcription as TranscriptionModel,
|
6
|
+
} from '@livekit/protocol';
|
2
7
|
import { getBrowser } from '../utils/browserParser';
|
3
8
|
import { protocolVersion, version } from '../version';
|
4
9
|
import CriticalTimers from './timers';
|
@@ -6,7 +11,7 @@ import type LocalAudioTrack from './track/LocalAudioTrack';
|
|
6
11
|
import type RemoteAudioTrack from './track/RemoteAudioTrack';
|
7
12
|
import { VideoCodec, videoCodecs } from './track/options';
|
8
13
|
import { getNewAudioContext } from './track/utils';
|
9
|
-
import type { LiveKitReactNativeInfo, TranscriptionSegment } from './types';
|
14
|
+
import type { ChatMessage, LiveKitReactNativeInfo, TranscriptionSegment } from './types';
|
10
15
|
|
11
16
|
const separator = '|';
|
12
17
|
export const ddExtensionURI =
|
@@ -554,3 +559,13 @@ export function extractTranscriptionSegments(
|
|
554
559
|
};
|
555
560
|
});
|
556
561
|
}
|
562
|
+
|
563
|
+
export function extractChatMessage(msg: ChatMessageModel): ChatMessage {
|
564
|
+
const { id, timestamp, message, editTimestamp } = msg;
|
565
|
+
return {
|
566
|
+
id,
|
567
|
+
timestamp: Number.parseInt(timestamp.toString()),
|
568
|
+
editTimestamp: editTimestamp ? Number.parseInt(editTimestamp.toString()) : undefined,
|
569
|
+
message,
|
570
|
+
};
|
571
|
+
}
|