livekit-client 1.6.6 → 1.6.8
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 +508 -317
- 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/proto/google/protobuf/timestamp.d.ts +9 -2
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +958 -58
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +8394 -3725
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +10 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +1 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +18 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +2 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +10 -2
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +7 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +11 -2
- package/dist/ts4.2/src/proto/livekit_models.d.ts +1199 -177
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +9260 -4023
- package/dist/ts4.2/src/room/RTCEngine.d.ts +10 -3
- package/dist/ts4.2/src/room/Room.d.ts +1 -1
- package/dist/ts4.2/src/room/events.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +18 -0
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/Track.d.ts +2 -1
- package/dist/ts4.2/src/room/track/options.d.ts +10 -2
- package/dist/ts4.2/src/room/types.d.ts +7 -0
- package/dist/ts4.2/src/version.d.ts +1 -1
- package/package.json +14 -14
- package/src/proto/google/protobuf/timestamp.ts +4 -0
- package/src/proto/livekit_models.ts +128 -31
- package/src/proto/livekit_rtc.ts +262 -161
- package/src/room/PCTransport.ts +2 -0
- package/src/room/RTCEngine.ts +50 -55
- package/src/room/Room.ts +60 -43
- package/src/room/events.ts +1 -1
- package/src/room/participant/LocalParticipant.ts +118 -28
- package/src/room/participant/RemoteParticipant.ts +2 -3
- package/src/room/track/RemoteTrackPublication.ts +3 -2
- package/src/room/track/RemoteVideoTrack.ts +0 -3
- package/src/room/track/Track.ts +2 -1
- package/src/room/track/options.ts +14 -2
- package/src/room/types.ts +9 -0
- package/src/version.ts +1 -1
@@ -29,7 +29,8 @@ import {
|
|
29
29
|
} from '../track/options';
|
30
30
|
import { Track } from '../track/Track';
|
31
31
|
import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
|
32
|
-
import {
|
32
|
+
import type { DataPublishOptions } from '../types';
|
33
|
+
import { Future, isFireFox, isSafari, isWeb, supportsAV1 } from '../utils';
|
33
34
|
import Participant from './Participant';
|
34
35
|
import { ParticipantTrackPermission, trackPermissionToProto } from './ParticipantTrackPermission';
|
35
36
|
import {
|
@@ -52,6 +53,8 @@ export default class LocalParticipant extends Participant {
|
|
52
53
|
|
53
54
|
private pendingPublishing = new Set<Track.Source>();
|
54
55
|
|
56
|
+
private pendingPublishPromises = new Map<LocalTrack, Promise<LocalTrackPublication>>();
|
57
|
+
|
55
58
|
private cameraError: Error | undefined;
|
56
59
|
|
57
60
|
private microphoneError: Error | undefined;
|
@@ -63,6 +66,8 @@ export default class LocalParticipant extends Participant {
|
|
63
66
|
// keep a pointer to room options
|
64
67
|
private roomOptions: InternalRoomOptions;
|
65
68
|
|
69
|
+
private reconnectFuture?: Future<void>;
|
70
|
+
|
66
71
|
/** @internal */
|
67
72
|
constructor(sid: string, identity: string, engine: RTCEngine, options: InternalRoomOptions) {
|
68
73
|
super(sid, identity);
|
@@ -118,11 +123,25 @@ export default class LocalParticipant extends Participant {
|
|
118
123
|
this.engine.client.onLocalTrackUnpublished = this.handleLocalTrackUnpublished;
|
119
124
|
|
120
125
|
this.engine
|
121
|
-
.on(EngineEvent.Connected, this.
|
122
|
-
.on(EngineEvent.Restarted, this.
|
123
|
-
.on(EngineEvent.Resumed, this.
|
126
|
+
.on(EngineEvent.Connected, this.handleReconnected)
|
127
|
+
.on(EngineEvent.Restarted, this.handleReconnected)
|
128
|
+
.on(EngineEvent.Resumed, this.handleReconnected)
|
129
|
+
.on(EngineEvent.Restarting, this.handleReconnecting)
|
130
|
+
.on(EngineEvent.Resuming, this.handleReconnecting);
|
124
131
|
}
|
125
132
|
|
133
|
+
private handleReconnecting = () => {
|
134
|
+
if (!this.reconnectFuture) {
|
135
|
+
this.reconnectFuture = new Future<void>();
|
136
|
+
}
|
137
|
+
};
|
138
|
+
|
139
|
+
private handleReconnected = () => {
|
140
|
+
this.reconnectFuture?.resolve?.();
|
141
|
+
this.reconnectFuture = undefined;
|
142
|
+
this.updateTrackSubscriptionPermissions();
|
143
|
+
};
|
144
|
+
|
126
145
|
/**
|
127
146
|
* Enable or disable a participant's camera track.
|
128
147
|
*
|
@@ -382,6 +401,11 @@ export default class LocalParticipant extends Participant {
|
|
382
401
|
const stream: MediaStream = await navigator.mediaDevices.getDisplayMedia({
|
383
402
|
audio: options.audio ?? false,
|
384
403
|
video: videoConstraints,
|
404
|
+
// @ts-expect-error support for experimental display media features
|
405
|
+
controller: options.controller,
|
406
|
+
selfBrowserSurface: options.selfBrowserSurface,
|
407
|
+
surfaceSwitching: options.surfaceSwitching,
|
408
|
+
systemAudio: options.systemAudio,
|
385
409
|
});
|
386
410
|
|
387
411
|
const tracks = stream.getVideoTracks();
|
@@ -408,6 +432,10 @@ export default class LocalParticipant extends Participant {
|
|
408
432
|
track: LocalTrack | MediaStreamTrack,
|
409
433
|
options?: TrackPublishOptions,
|
410
434
|
): Promise<LocalTrackPublication> {
|
435
|
+
await this.reconnectFuture?.promise;
|
436
|
+
if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
|
437
|
+
await this.pendingPublishPromises.get(track);
|
438
|
+
}
|
411
439
|
// convert raw media track into audio or video track
|
412
440
|
if (track instanceof MediaStreamTrack) {
|
413
441
|
switch (track.kind) {
|
@@ -422,6 +450,22 @@ export default class LocalParticipant extends Participant {
|
|
422
450
|
}
|
423
451
|
}
|
424
452
|
|
453
|
+
// is it already published? if so skip
|
454
|
+
let existingPublication: LocalTrackPublication | undefined;
|
455
|
+
this.tracks.forEach((publication) => {
|
456
|
+
if (!publication.track) {
|
457
|
+
return;
|
458
|
+
}
|
459
|
+
if (publication.track === track) {
|
460
|
+
existingPublication = <LocalTrackPublication>publication;
|
461
|
+
}
|
462
|
+
});
|
463
|
+
|
464
|
+
if (existingPublication) {
|
465
|
+
log.warn('track has already been published, skipping');
|
466
|
+
return existingPublication;
|
467
|
+
}
|
468
|
+
|
425
469
|
const isStereo =
|
426
470
|
options?.forceStereo ||
|
427
471
|
('channelCount' in track.mediaStreamTrack.getSettings() &&
|
@@ -439,29 +483,40 @@ export default class LocalParticipant extends Participant {
|
|
439
483
|
`Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.`,
|
440
484
|
);
|
441
485
|
}
|
486
|
+
if (options.red === undefined) {
|
487
|
+
log.info(
|
488
|
+
`Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work.`,
|
489
|
+
);
|
490
|
+
}
|
442
491
|
options.dtx ??= false;
|
492
|
+
options.red ??= false;
|
443
493
|
}
|
444
494
|
const opts: TrackPublishOptions = {
|
445
495
|
...this.roomOptions.publishDefaults,
|
446
496
|
...options,
|
447
497
|
};
|
448
498
|
|
449
|
-
// is it already published? if so skip
|
450
|
-
let existingPublication: LocalTrackPublication | undefined;
|
451
|
-
this.tracks.forEach((publication) => {
|
452
|
-
if (!publication.track) {
|
453
|
-
return;
|
454
|
-
}
|
455
|
-
if (publication.track === track) {
|
456
|
-
existingPublication = <LocalTrackPublication>publication;
|
457
|
-
}
|
458
|
-
});
|
459
|
-
|
460
|
-
if (existingPublication) return existingPublication;
|
461
|
-
|
462
499
|
if (opts.source) {
|
463
500
|
track.source = opts.source;
|
464
501
|
}
|
502
|
+
const publishPromise = this.publish(track, opts, options, isStereo);
|
503
|
+
this.pendingPublishPromises.set(track, publishPromise);
|
504
|
+
try {
|
505
|
+
const publication = await publishPromise;
|
506
|
+
return publication;
|
507
|
+
} catch (e) {
|
508
|
+
throw e;
|
509
|
+
} finally {
|
510
|
+
this.pendingPublishPromises.delete(track);
|
511
|
+
}
|
512
|
+
}
|
513
|
+
|
514
|
+
private async publish(
|
515
|
+
track: LocalTrack,
|
516
|
+
opts: TrackPublishOptions,
|
517
|
+
options: TrackPublishOptions | undefined,
|
518
|
+
isStereo: boolean,
|
519
|
+
) {
|
465
520
|
const existingTrackOfSource = Array.from(this.tracks.values()).find(
|
466
521
|
(publishedTrack) => track instanceof LocalTrack && publishedTrack.source === track.source,
|
467
522
|
);
|
@@ -721,17 +776,24 @@ export default class LocalParticipant extends Participant {
|
|
721
776
|
track.stop();
|
722
777
|
}
|
723
778
|
|
779
|
+
let negotiationNeeded = false;
|
780
|
+
const trackSender = track.sender;
|
781
|
+
track.sender = undefined;
|
724
782
|
if (
|
725
783
|
this.engine.publisher &&
|
726
784
|
this.engine.publisher.pc.connectionState !== 'closed' &&
|
727
|
-
|
785
|
+
trackSender
|
728
786
|
) {
|
729
787
|
try {
|
730
|
-
this.engine.removeTrack(
|
788
|
+
if (this.engine.removeTrack(trackSender)) {
|
789
|
+
negotiationNeeded = true;
|
790
|
+
}
|
731
791
|
if (track instanceof LocalVideoTrack) {
|
732
792
|
for (const [, trackInfo] of track.simulcastCodecs) {
|
733
793
|
if (trackInfo.sender) {
|
734
|
-
this.engine.removeTrack(trackInfo.sender)
|
794
|
+
if (this.engine.removeTrack(trackInfo.sender)) {
|
795
|
+
negotiationNeeded = true;
|
796
|
+
}
|
735
797
|
trackInfo.sender = undefined;
|
736
798
|
}
|
737
799
|
}
|
@@ -739,13 +801,9 @@ export default class LocalParticipant extends Participant {
|
|
739
801
|
}
|
740
802
|
} catch (e) {
|
741
803
|
log.warn('failed to unpublish track', { error: e, method: 'unpublishTrack' });
|
742
|
-
} finally {
|
743
|
-
await this.engine.negotiate();
|
744
804
|
}
|
745
805
|
}
|
746
806
|
|
747
|
-
track.sender = undefined;
|
748
|
-
|
749
807
|
// remove from our maps
|
750
808
|
this.tracks.delete(publication.trackSid);
|
751
809
|
switch (publication.kind) {
|
@@ -762,6 +820,9 @@ export default class LocalParticipant extends Participant {
|
|
762
820
|
this.emit(ParticipantEvent.LocalTrackUnpublished, publication);
|
763
821
|
publication.setTrack(undefined);
|
764
822
|
|
823
|
+
if (negotiationNeeded) {
|
824
|
+
await this.engine.negotiate();
|
825
|
+
}
|
765
826
|
return publication;
|
766
827
|
}
|
767
828
|
|
@@ -794,6 +855,22 @@ export default class LocalParticipant extends Participant {
|
|
794
855
|
);
|
795
856
|
}
|
796
857
|
|
858
|
+
/**
|
859
|
+
* Publish a new data payload to the room. Data will be forwarded to each
|
860
|
+
* participant in the room if the destination field in publishOptions is empty
|
861
|
+
*
|
862
|
+
* @param data Uint8Array of the payload. To send string data, use TextEncoder.encode
|
863
|
+
* @param kind whether to send this as reliable or lossy.
|
864
|
+
* For data that you need delivery guarantee (such as chat messages), use Reliable.
|
865
|
+
* For data that should arrive as quickly as possible, but you are ok with dropped
|
866
|
+
* packets, use Lossy.
|
867
|
+
* @param publishOptions optionally specify a `topic` and `destination`
|
868
|
+
*/
|
869
|
+
async publishData(
|
870
|
+
data: Uint8Array,
|
871
|
+
kind: DataPacket_Kind,
|
872
|
+
publishOptions?: DataPublishOptions,
|
873
|
+
): Promise<void>;
|
797
874
|
/**
|
798
875
|
* Publish a new data payload to the room. Data will be forwarded to each
|
799
876
|
* participant in the room if the destination argument is empty
|
@@ -809,14 +886,26 @@ export default class LocalParticipant extends Participant {
|
|
809
886
|
data: Uint8Array,
|
810
887
|
kind: DataPacket_Kind,
|
811
888
|
destination?: RemoteParticipant[] | string[],
|
889
|
+
): Promise<void>;
|
890
|
+
|
891
|
+
async publishData(
|
892
|
+
data: Uint8Array,
|
893
|
+
kind: DataPacket_Kind,
|
894
|
+
publishOptions: DataPublishOptions | RemoteParticipant[] | string[] = {},
|
812
895
|
) {
|
813
|
-
const
|
896
|
+
const destination = Array.isArray(publishOptions)
|
897
|
+
? publishOptions
|
898
|
+
: publishOptions?.destination;
|
899
|
+
const destinationSids: string[] = [];
|
900
|
+
|
901
|
+
const topic = !Array.isArray(publishOptions) ? publishOptions.topic : undefined;
|
902
|
+
|
814
903
|
if (destination !== undefined) {
|
815
904
|
destination.forEach((val: any) => {
|
816
905
|
if (val instanceof RemoteParticipant) {
|
817
|
-
|
906
|
+
destinationSids.push(val.sid);
|
818
907
|
} else {
|
819
|
-
|
908
|
+
destinationSids.push(val);
|
820
909
|
}
|
821
910
|
});
|
822
911
|
}
|
@@ -828,7 +917,8 @@ export default class LocalParticipant extends Participant {
|
|
828
917
|
user: {
|
829
918
|
participantSid: this.sid,
|
830
919
|
payload: data,
|
831
|
-
destinationSids:
|
920
|
+
destinationSids: destinationSids,
|
921
|
+
topic,
|
832
922
|
},
|
833
923
|
},
|
834
924
|
};
|
@@ -236,8 +236,7 @@ export default class RemoteParticipant extends Participant {
|
|
236
236
|
}
|
237
237
|
publication = new RemoteTrackPublication(
|
238
238
|
kind,
|
239
|
-
ti
|
240
|
-
ti.name,
|
239
|
+
ti,
|
241
240
|
this.signalClient.connectOptions?.autoSubscribe,
|
242
241
|
);
|
243
242
|
publication.updateInfo(ti);
|
@@ -246,7 +245,7 @@ export default class RemoteParticipant extends Participant {
|
|
246
245
|
(publishedTrack) => publishedTrack.source === publication?.source,
|
247
246
|
);
|
248
247
|
if (existingTrackOfSource && publication.source !== Track.Source.Unknown) {
|
249
|
-
log.
|
248
|
+
log.debug(
|
250
249
|
`received a second track publication for ${this.identity} with the same source: ${publication.source}`,
|
251
250
|
{
|
252
251
|
oldTrack: existingTrackOfSource,
|
@@ -24,9 +24,10 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
24
24
|
|
25
25
|
protected fps?: number;
|
26
26
|
|
27
|
-
constructor(kind: Track.Kind,
|
28
|
-
super(kind,
|
27
|
+
constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined) {
|
28
|
+
super(kind, ti.sid, ti.name);
|
29
29
|
this.subscribed = autoSubscribe;
|
30
|
+
this.updateInfo(ti);
|
30
31
|
}
|
31
32
|
|
32
33
|
/**
|
@@ -31,9 +31,6 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
31
31
|
) {
|
32
32
|
super(mediaTrack, sid, Track.Kind.Video, receiver);
|
33
33
|
this.adaptiveStreamSettings = adaptiveStreamSettings;
|
34
|
-
if (this.isAdaptiveStream) {
|
35
|
-
this.streamState = Track.StreamState.Paused;
|
36
|
-
}
|
37
34
|
}
|
38
35
|
|
39
36
|
get isAdaptiveStream(): boolean {
|
package/src/room/track/Track.ts
CHANGED
@@ -32,7 +32,8 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
32
32
|
mediaStream?: MediaStream;
|
33
33
|
|
34
34
|
/**
|
35
|
-
* indicates current state of stream
|
35
|
+
* indicates current state of stream, it'll indicate `paused` if the track
|
36
|
+
* has been paused by congestion controller
|
36
37
|
*/
|
37
38
|
streamState: Track.StreamState = Track.StreamState.Active;
|
38
39
|
|
@@ -28,12 +28,12 @@ export interface TrackPublishDefaults {
|
|
28
28
|
audioBitrate?: number;
|
29
29
|
|
30
30
|
/**
|
31
|
-
* dtx (Discontinuous Transmission of audio),
|
31
|
+
* dtx (Discontinuous Transmission of audio), enabled by default for mono tracks.
|
32
32
|
*/
|
33
33
|
dtx?: boolean;
|
34
34
|
|
35
35
|
/**
|
36
|
-
* red (Redundant Audio Data),
|
36
|
+
* red (Redundant Audio Data), enabled by default for mono tracks.
|
37
37
|
*/
|
38
38
|
red?: boolean;
|
39
39
|
|
@@ -133,6 +133,18 @@ export interface ScreenShareCaptureOptions {
|
|
133
133
|
|
134
134
|
/** capture resolution, defaults to full HD */
|
135
135
|
resolution?: VideoResolution;
|
136
|
+
|
137
|
+
/** a CaptureController object instance containing methods that can be used to further manipulate the capture session if included. */
|
138
|
+
controller?: unknown; // TODO replace type with CaptureController once it lands in TypeScript
|
139
|
+
|
140
|
+
/** specifies whether the browser should allow the user to select the current tab for capture */
|
141
|
+
selfBrowserSurface?: 'include' | 'exclude';
|
142
|
+
|
143
|
+
/** specifies whether the browser should display a control to allow the user to dynamically switch the shared tab during screen-sharing. */
|
144
|
+
surfaceSwitching?: 'include' | 'exclude';
|
145
|
+
|
146
|
+
/** specifies whether the browser should include the system audio among the possible audio sources offered to the user */
|
147
|
+
systemAudio?: 'include' | 'exclude';
|
136
148
|
}
|
137
149
|
|
138
150
|
export interface AudioCaptureOptions {
|
package/src/room/types.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import type RemoteParticipant from './participant/RemoteParticipant';
|
2
|
+
|
1
3
|
export type SimulationOptions = {
|
2
4
|
publish?: {
|
3
5
|
audio?: boolean;
|
@@ -11,3 +13,10 @@ export type SimulationOptions = {
|
|
11
13
|
video?: boolean;
|
12
14
|
};
|
13
15
|
};
|
16
|
+
|
17
|
+
export type DataPublishOptions = {
|
18
|
+
/** the participants who will receive the message, will be sent to every one if empty */
|
19
|
+
destination?: RemoteParticipant[] | string[];
|
20
|
+
/** the topic under which the message gets published */
|
21
|
+
topic?: string;
|
22
|
+
};
|
package/src/version.ts
CHANGED