livekit-client 2.21.0 → 2.22.1
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 +10 -10
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +189 -2
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +2243 -270
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.fm.worker.js +1 -1
- package/dist/livekit-client.fm.worker.js.map +1 -1
- package/dist/livekit-client.fm.worker.mjs +189 -2
- package/dist/livekit-client.fm.worker.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 +25 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/api/SignalClientStateMachine.d.ts +85 -0
- package/dist/src/api/SignalClientStateMachine.d.ts.map +1 -0
- package/dist/src/api/WebSocketStream.d.ts.map +1 -1
- package/dist/src/api/utils.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +2 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/options.d.ts +22 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +51 -3
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +27 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +10 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -1
- package/dist/src/room/statsSummary.d.ts +13 -0
- package/dist/src/room/statsSummary.d.ts.map +1 -0
- package/dist/src/room/token-source/TokenSource.d.ts +19 -10
- package/dist/src/room/token-source/TokenSource.d.ts.map +1 -1
- package/dist/src/room/token-source/utils.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +12 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrack.d.ts +3 -0
- package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +10 -0
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +15 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/promiseState.d.ts +12 -0
- package/dist/src/test/promiseState.d.ts.map +1 -0
- package/dist/src/test/signalToken.d.ts.map +1 -1
- package/dist/src/utils/AsyncQueue.d.ts +3 -3
- package/dist/src/utils/AsyncQueue.d.ts.map +1 -1
- package/dist/src/utils/machineInspector.d.ts +54 -0
- package/dist/src/utils/machineInspector.d.ts.map +1 -0
- package/dist/ts4.2/api/SignalClient.d.ts +25 -1
- package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -0
- package/dist/ts4.2/index.d.ts +3 -2
- package/dist/ts4.2/logger.d.ts +2 -1
- package/dist/ts4.2/options.d.ts +22 -0
- package/dist/ts4.2/room/PCTransport.d.ts +51 -3
- package/dist/ts4.2/room/RTCEngine.d.ts +27 -3
- package/dist/ts4.2/room/Room.d.ts +10 -1
- package/dist/ts4.2/room/events.d.ts +1 -1
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +1 -1
- package/dist/ts4.2/room/statsSummary.d.ts +13 -0
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +17 -8
- package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +12 -1
- package/dist/ts4.2/room/track/RemoteTrack.d.ts +3 -0
- package/dist/ts4.2/room/track/Track.d.ts +10 -0
- package/dist/ts4.2/room/utils.d.ts +15 -0
- package/dist/ts4.2/test/promiseState.d.ts +12 -0
- package/dist/ts4.2/utils/AsyncQueue.d.ts +3 -3
- package/dist/ts4.2/utils/machineInspector.d.ts +54 -0
- package/package.json +19 -12
- package/src/api/SignalClient.e2e.test.ts +16 -9
- package/src/api/SignalClient.test.ts +320 -8
- package/src/api/SignalClient.ts +260 -82
- package/src/api/SignalClientStateMachine.test.ts +472 -0
- package/src/api/SignalClientStateMachine.ts +180 -0
- package/src/api/WebSocketStream.ts +19 -3
- package/src/api/utils.test.ts +20 -1
- package/src/api/utils.ts +5 -0
- package/src/e2ee/utils.ts +1 -1
- package/src/index.ts +5 -0
- package/src/logger.ts +1 -0
- package/src/options.ts +24 -0
- package/src/room/PCTransport.test.ts +243 -1
- package/src/room/PCTransport.ts +183 -81
- package/src/room/RTCEngine.test.ts +339 -2
- package/src/room/RTCEngine.ts +168 -18
- package/src/room/Room.test.ts +134 -3
- package/src/room/Room.ts +122 -22
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +1 -1
- package/src/room/data-stream/incoming/StreamReader.ts +1 -1
- package/src/room/errors.ts +1 -2
- package/src/room/events.ts +1 -1
- package/src/room/statsSummary.ts +187 -0
- package/src/room/token-source/TokenSource.ts +25 -12
- package/src/room/token-source/test-tokens.ts +20 -0
- package/src/room/token-source/utils.test.ts +27 -0
- package/src/room/token-source/utils.ts +12 -5
- package/src/room/track/LocalAudioTrack.ts +9 -3
- package/src/room/track/LocalVideoTrack.test.ts +105 -2
- package/src/room/track/LocalVideoTrack.ts +36 -10
- package/src/room/track/RemoteTrack.test.ts +144 -0
- package/src/room/track/RemoteTrack.ts +39 -12
- package/src/room/track/Track.ts +29 -1
- package/src/room/utils.test.ts +94 -2
- package/src/room/utils.ts +53 -1
- package/src/test/promiseState.ts +23 -0
- package/src/test/signalServerSetup.ts +2 -1
- package/src/test/signalToken.ts +17 -13
- package/src/type-polyfills/header-extensions.d.ts +13 -0
- package/src/utils/AsyncQueue.ts +3 -3
- package/src/utils/machineInspector.ts +90 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MediaAttributes, MediaDescription } from 'sdp-transform';
|
|
1
|
+
import type { MediaAttributes, MediaDescription, SessionDescription } from 'sdp-transform';
|
|
2
2
|
import type TypedEmitter from 'typed-emitter';
|
|
3
3
|
import type { LoggerOptions } from './types';
|
|
4
4
|
/** @internal */
|
|
@@ -9,6 +9,18 @@ interface TrackBitrateInfo {
|
|
|
9
9
|
maxbr: number;
|
|
10
10
|
isScreenShare?: boolean;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Applies the configured start bitrate when this media section belongs to `cid`.
|
|
14
|
+
* This SDP munging is used for a bitrate setting that cannot be applied through
|
|
15
|
+
* `RTCRtpEncodingParameters`.
|
|
16
|
+
*
|
|
17
|
+
* Returns `undefined` when the section does not belong to the track, `0` when
|
|
18
|
+
* it does but does not offer the requested codec, and the codec payload when the
|
|
19
|
+
* requested codec is present (whether the bitrate was added or already set).
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyVideoStartBitrate(media: MediaDescription, cid: string, codec: string, maxbr: number, isScreenShare?: boolean): number | undefined;
|
|
12
24
|
export declare const PCEvents: {
|
|
13
25
|
readonly NegotiationStarted: "negotiationStarted";
|
|
14
26
|
readonly NegotiationComplete: "negotiationComplete";
|
|
@@ -79,13 +91,44 @@ export default class PCTransport extends PCTransport_base {
|
|
|
79
91
|
getSenders(): RTCRtpSender[];
|
|
80
92
|
getLocalDescription(): RTCSessionDescription | null | undefined;
|
|
81
93
|
getRemoteDescription(): RTCSessionDescription | null;
|
|
82
|
-
|
|
94
|
+
/** stats of the underlying connection, `undefined` when there is none */
|
|
95
|
+
getStats(): Promise<RTCStatsReport> | undefined;
|
|
83
96
|
getMaxMessageSize(): number | undefined;
|
|
84
97
|
getConnectedAddress(): Promise<string | undefined>;
|
|
85
98
|
close: () => void;
|
|
86
99
|
private setMungedSDP;
|
|
87
|
-
private ensureVideoDDExtensionForSVC;
|
|
88
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Adds the AV1 dependency descriptor extension to `media` unless it is already there, and
|
|
103
|
+
* returns the id it is mapped to so callers can pass it back in as `ddExtID` (0 when no id has
|
|
104
|
+
* been chosen yet).
|
|
105
|
+
*
|
|
106
|
+
* A bundle has to map one URI to one id, so an id already in use for the extension anywhere in
|
|
107
|
+
* `sdp` wins over both the cached one and a fresh one: Chrome advertises the extension itself on
|
|
108
|
+
* sections it can send on, and an earlier offer may have munged it into others.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
export declare function ensureVideoDDExtension(media: {
|
|
112
|
+
type: string;
|
|
113
|
+
port: number;
|
|
114
|
+
protocol: string;
|
|
115
|
+
payloads?: string | undefined;
|
|
116
|
+
} & MediaDescription, sdp: SessionDescription, ddExtID: number): number;
|
|
117
|
+
/**
|
|
118
|
+
* Checks whether an fmtp config declares `param` as an exact, `;`-delimited
|
|
119
|
+
* token. A plain substring check conflates distinct opus parameters — e.g.
|
|
120
|
+
* `stereo=1` is a substring of `sprop-stereo=1` — so `param` must match a whole
|
|
121
|
+
* parameter, not appear anywhere within the config string.
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
export declare function fmtpConfigHasParam(config: string, param: string): boolean;
|
|
125
|
+
/** @internal */
|
|
126
|
+
export declare function ensureAudioNackAndStereo(media: {
|
|
127
|
+
type: string;
|
|
128
|
+
port: number;
|
|
129
|
+
protocol: string;
|
|
130
|
+
payloads?: string | undefined;
|
|
131
|
+
} & MediaDescription, stereoMids: string[], nackMids: string[]): void;
|
|
89
132
|
/**
|
|
90
133
|
* Returns the mids of transceivers that carry no outgoing track: the
|
|
91
134
|
* pre-populated placeholders added by `RTCEngine.applyInitialPublisherLayout`,
|
|
@@ -117,6 +160,11 @@ export declare function placeholderMidsFromTransceivers(transceivers: readonly R
|
|
|
117
160
|
* @internal
|
|
118
161
|
*/
|
|
119
162
|
export declare function conformBundledCodecFmtp(media: MediaDescription[], isPlaceholder: (media: MediaDescription) => boolean): void;
|
|
163
|
+
/** @internal */
|
|
164
|
+
export declare function extractStereoAndNackAudioFromOffer(offer: RTCSessionDescriptionInit): {
|
|
165
|
+
stereoMids: string[];
|
|
166
|
+
nackMids: string[];
|
|
167
|
+
};
|
|
120
168
|
type PCTransportEventCallbacks = {
|
|
121
169
|
negotiationStarted: () => void;
|
|
122
170
|
negotiationComplete: () => void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AddTrackRequest, ConnectionQualityUpdate, JoinResponse, RegionSettings, StreamStateUpdate, SubscriptionPermissionUpdate, SubscriptionResponse } from '@livekit/protocol';
|
|
2
|
-
import { DataPacket, DataTrackSubscriberHandles, DisconnectReason, Encryption_Type, ParticipantInfo, PublishDataTrackResponse, RequestResponse, Room as RoomModel, RoomMovedResponse, ServerInfo, SpeakerInfo, SubscribedQualityUpdate, TrackInfo, TrackUnpublishedResponse, Transcription, UnpublishDataTrackResponse } from '@livekit/protocol';
|
|
2
|
+
import { DataPacket, DataTrackSubscriberHandles, DisconnectReason, Encryption_Type, ParticipantInfo, PublishDataTrackResponse, ReconnectReason, RequestResponse, Room as RoomModel, RoomMovedResponse, ServerInfo, SpeakerInfo, SubscribedQualityUpdate, TrackInfo, TrackUnpublishedResponse, Transcription, UnpublishDataTrackResponse } from '@livekit/protocol';
|
|
3
3
|
import type TypedEventEmitter from 'typed-emitter';
|
|
4
4
|
import type { SignalOptions } from '../api/SignalClient';
|
|
5
5
|
import { SignalClient } from '../api/SignalClient';
|
|
@@ -80,6 +80,10 @@ export default class RTCEngine extends RTCEngine_base {
|
|
|
80
80
|
private midToTrackId;
|
|
81
81
|
/** used to indicate whether the browser is currently waiting to reconnect */
|
|
82
82
|
private isWaitingForNetworkReconnect;
|
|
83
|
+
/** set while the local participant's connection quality is `LOST`; forces a full reconnect on timeout */
|
|
84
|
+
private lostQualityTimeout?;
|
|
85
|
+
/** timestamp (ms) the primary transport entered `CONNECTING`, used to bound how long we tolerate it */
|
|
86
|
+
private transportConnectingSince?;
|
|
83
87
|
constructor(options: InternalRoomOptions);
|
|
84
88
|
/** @internal */
|
|
85
89
|
get logContext(): {
|
|
@@ -94,10 +98,15 @@ export default class RTCEngine extends RTCEngine_base {
|
|
|
94
98
|
joinResponse: JoinResponse;
|
|
95
99
|
serverInfo: Partial<ServerInfo>;
|
|
96
100
|
}>;
|
|
97
|
-
|
|
101
|
+
/**
|
|
102
|
+
* @param reason why the session is ending, recorded by the signal lifecycle. Worth passing
|
|
103
|
+
* wherever the caller knows more than "someone called close" — the server's leave reason, or
|
|
104
|
+
* having given up on reconnecting.
|
|
105
|
+
*/
|
|
106
|
+
close(reason?: string): Promise<void>;
|
|
98
107
|
cleanupPeerConnections(): Promise<void>;
|
|
99
108
|
cleanupLossyDataStats(): void;
|
|
100
|
-
cleanupClient(): Promise<void>;
|
|
109
|
+
cleanupClient(reason?: string): Promise<void>;
|
|
101
110
|
addTrack(req: AddTrackRequest): Promise<TrackInfo>;
|
|
102
111
|
/**
|
|
103
112
|
* Removes sender from PeerConnection, returning true if it was removed successfully
|
|
@@ -137,6 +146,21 @@ export default class RTCEngine extends RTCEngine_base {
|
|
|
137
146
|
private createSimulcastTransceiverSender;
|
|
138
147
|
private createRTCRtpSender;
|
|
139
148
|
private handleDisconnect;
|
|
149
|
+
/**
|
|
150
|
+
* A sustained local `LOST` while connected and publishing means the server isn't receiving
|
|
151
|
+
* our media, so force a full reconnect; any non-`LOST` value cancels a pending trigger.
|
|
152
|
+
*/
|
|
153
|
+
private handleLocalConnectionQuality;
|
|
154
|
+
private scheduleLostQualityReconnect;
|
|
155
|
+
private clearLostQualityTimeout;
|
|
156
|
+
/** Whether the publisher currently has any sender with a live track. */
|
|
157
|
+
private hasActivePublisherSenders;
|
|
158
|
+
/**
|
|
159
|
+
* Forces a full reconnect while keeping the engine (and its saved credentials) alive. Used by
|
|
160
|
+
* Room's connection-reconcile safety net when the transport silently died but we looked connected.
|
|
161
|
+
* @internal
|
|
162
|
+
*/
|
|
163
|
+
reconnect(reason?: ReconnectReason): void;
|
|
140
164
|
private attemptReconnect;
|
|
141
165
|
private getNextRetryDelay;
|
|
142
166
|
private restartConnection;
|
|
@@ -72,12 +72,15 @@ declare class Room extends Room_base {
|
|
|
72
72
|
private frameMetadataManager;
|
|
73
73
|
private e2eeStateMutex;
|
|
74
74
|
private connectionReconcileInterval?;
|
|
75
|
+
private statsLogInterval?;
|
|
75
76
|
private regionUrlProvider?;
|
|
76
77
|
private regionUrl?;
|
|
77
78
|
private isVideoPlaybackBlocked;
|
|
78
79
|
private log;
|
|
80
|
+
private statsLog;
|
|
79
81
|
private bufferedEvents;
|
|
80
82
|
private isResuming;
|
|
83
|
+
private pendingTrackAddedCallbacks;
|
|
81
84
|
/**
|
|
82
85
|
* map to store first point in time when a particular transcription segment was received
|
|
83
86
|
*/
|
|
@@ -226,6 +229,7 @@ declare class Room extends Room_base {
|
|
|
226
229
|
private setupLocalParticipantEvents;
|
|
227
230
|
private recreateEngine;
|
|
228
231
|
private onTrackAdded;
|
|
232
|
+
private cancelPendingTrackAdded;
|
|
229
233
|
private handleLocalTrackSubscribed;
|
|
230
234
|
private emitLocalTrackSubscribed;
|
|
231
235
|
private handleRestarting;
|
|
@@ -274,6 +278,11 @@ declare class Room extends Room_base {
|
|
|
274
278
|
private getRemoteParticipantCapabilities;
|
|
275
279
|
private getAllRemoteParticipantIdentities;
|
|
276
280
|
private registerRpcDataStreamHandler;
|
|
281
|
+
private setStatsLogging;
|
|
282
|
+
/**
|
|
283
|
+
* Dumps stats of both peer connections.
|
|
284
|
+
*/
|
|
285
|
+
private logWebRTCStats;
|
|
277
286
|
private registerConnectionReconcile;
|
|
278
287
|
private clearConnectionReconcile;
|
|
279
288
|
private setAndEmitConnectionState;
|
|
@@ -311,7 +320,7 @@ export type RoomEventCallbacks = {
|
|
|
311
320
|
moved: (name: string) => void;
|
|
312
321
|
mediaDevicesChanged: () => void;
|
|
313
322
|
participantConnected: (participant: RemoteParticipant) => void;
|
|
314
|
-
participantDisconnected: (participant: RemoteParticipant) => void;
|
|
323
|
+
participantDisconnected: (participant: RemoteParticipant, disconnectReason?: DisconnectReason) => void;
|
|
315
324
|
trackPublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void;
|
|
316
325
|
trackSubscribed: (track: RemoteTrack, publication: RemoteTrackPublication, participant: RemoteParticipant) => void;
|
|
317
326
|
trackSubscriptionFailed: (trackSid: string, participant: RemoteParticipant, reason?: SubscriptionError) => void;
|
|
@@ -70,7 +70,7 @@ export declare enum RoomEvent {
|
|
|
70
70
|
* When a [[RemoteParticipant]] leaves *after* the local
|
|
71
71
|
* participant has joined.
|
|
72
72
|
*
|
|
73
|
-
* args: ([[RemoteParticipant]])
|
|
73
|
+
* args: ([[RemoteParticipant]], [[DisconnectReason]] | undefined)
|
|
74
74
|
*/
|
|
75
75
|
ParticipantDisconnected = "participantDisconnected",
|
|
76
76
|
/**
|
|
@@ -222,7 +222,7 @@ export default class LocalParticipant extends Participant {
|
|
|
222
222
|
*
|
|
223
223
|
* @param options.topic Topic identifier used to route the stream to appropriate handlers.
|
|
224
224
|
*/
|
|
225
|
-
streamBytes(options?: StreamBytesOptions): Promise<import("
|
|
225
|
+
streamBytes(options?: StreamBytesOptions): Promise<import("../..").ByteStreamWriter>;
|
|
226
226
|
/**
|
|
227
227
|
* Initiate an RPC call to a remote participant
|
|
228
228
|
* @param params - Parameters for initiating the RPC call, see {@link PerformRpcParams}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** one summarised stats entry; keys without a value are dropped */
|
|
2
|
+
type Summary = Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* Picks the interesting fields out of a `getStats()` report and groups them by
|
|
5
|
+
* RTP stream, so a stats dump can be read without unfolding the raw report.
|
|
6
|
+
*/
|
|
7
|
+
export declare function summarizeStatsReport(report: RTCStatsReport): {
|
|
8
|
+
connection: Summary | undefined;
|
|
9
|
+
outbound: Summary[] | undefined;
|
|
10
|
+
inbound: Summary[] | undefined;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=statsSummary.d.ts.map
|
|
@@ -34,13 +34,18 @@ declare class TokenSourceEndpoint extends TokenSourceCached {
|
|
|
34
34
|
private createRequestFromOptions;
|
|
35
35
|
protected update(options: TokenSourceFetchOptions): Promise<TokenSourceResponse>;
|
|
36
36
|
}
|
|
37
|
-
export type
|
|
37
|
+
export type DevelopmentTokenServerOptions = {
|
|
38
38
|
baseUrl?: string;
|
|
39
39
|
};
|
|
40
|
-
declare class
|
|
41
|
-
constructor(
|
|
40
|
+
declare class TokenSourceDevelopmentTokenServer extends TokenSourceEndpoint {
|
|
41
|
+
constructor(tokenServerId: string, options: DevelopmentTokenServerOptions);
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
/** @deprecated use {@link DevelopmentTokenServerOptions} instead */
|
|
44
|
+
export type SandboxTokenServerOptions = DevelopmentTokenServerOptions;
|
|
45
|
+
/** @deprecated Use {@link TokenSourceDevelopmentTokenServer} instead */
|
|
46
|
+
declare class TokenSourceSandboxTokenServer extends TokenSourceDevelopmentTokenServer {
|
|
47
|
+
}
|
|
48
|
+
export type { TokenSourceLiteral, TokenSourceCustom, TokenSourceEndpoint, TokenSourceSandboxTokenServer, TokenSourceDevelopmentTokenServer };
|
|
44
49
|
export { decodeTokenPayload, areTokenSourceFetchOptionsEqual };
|
|
45
50
|
export declare const TokenSource: {
|
|
46
51
|
/** TokenSource.literal contains a single, literal set of {@link TokenSourceResponseObject}
|
|
@@ -56,18 +61,22 @@ export declare const TokenSource: {
|
|
|
56
61
|
/**
|
|
57
62
|
* TokenSource.endpoint creates a token source that fetches credentials from a given URL using
|
|
58
63
|
* the standard endpoint format:
|
|
59
|
-
* @see https://
|
|
64
|
+
* @see https://docs.livekit.io/frontends/build/authentication/endpoint/
|
|
60
65
|
*/
|
|
61
66
|
endpoint(url: string, options?: EndpointOptions): TokenSourceEndpoint;
|
|
62
67
|
/**
|
|
63
|
-
*
|
|
68
|
+
* @deprecated Use {@link TokenSource.developmentTokenServer} instead
|
|
69
|
+
*/
|
|
70
|
+
sandboxTokenServer(sandboxId: string, options?: SandboxTokenServerOptions): TokenSourceSandboxTokenServer;
|
|
71
|
+
/**
|
|
72
|
+
* TokenSource.developmentTokenServer queries a development token server for credentials,
|
|
64
73
|
* which supports quick prototyping / getting started types of use cases.
|
|
65
74
|
*
|
|
66
75
|
* This token provider is INSECURE and should NOT be used in production.
|
|
67
76
|
*
|
|
68
77
|
* For more info:
|
|
69
|
-
* @see https://
|
|
78
|
+
* @see https://docs.livekit.io/frontends/build/authentication/sandbox-token-server/
|
|
70
79
|
*/
|
|
71
|
-
|
|
80
|
+
developmentTokenServer(tokenServerId: string, options?: DevelopmentTokenServerOptions): TokenSourceDevelopmentTokenServer;
|
|
72
81
|
};
|
|
73
82
|
//# sourceMappingURL=TokenSource.d.ts.map
|
|
@@ -56,8 +56,19 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
|
56
56
|
private applyEncodingsToSender;
|
|
57
57
|
setProcessor(processor: TrackProcessor<Track.Kind.Video>, showProcessedStreamLocally?: boolean): Promise<void>;
|
|
58
58
|
setDegradationPreference(preference: RTCDegradationPreference): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Degradation preference is a property of the sender, not of the track, so every sender
|
|
61
|
+
* publishing this track needs it applied separately. A backup codec publishes over its
|
|
62
|
+
* own sender, which would otherwise let the browser resolve a preference implicitly and
|
|
63
|
+
* diverge from the primary encoder.
|
|
64
|
+
*
|
|
65
|
+
* Callers apply this sequentially rather than concurrently: `setParameters` is only valid
|
|
66
|
+
* against the parameters most recently returned by `getParameters`, which is why this file
|
|
67
|
+
* serializes other sender parameter updates through `senderLock`.
|
|
68
|
+
*/
|
|
69
|
+
private applyDegradationPreference;
|
|
59
70
|
addSimulcastTrack(codec: VideoCodec, encodings?: RTCRtpEncodingParameters[]): SimulcastTrackInfo | undefined;
|
|
60
|
-
setSimulcastTrackSender(codec: VideoCodec, sender: RTCRtpSender): void
|
|
71
|
+
setSimulcastTrackSender(codec: VideoCodec, sender: RTCRtpSender): Promise<void>;
|
|
61
72
|
/**
|
|
62
73
|
* @internal
|
|
63
74
|
* Sets codecs that should be publishing, returns new codecs that have not yet
|
|
@@ -29,7 +29,10 @@ export default abstract class RemoteTrack<TrackKind extends Track.Kind = Track.K
|
|
|
29
29
|
*/
|
|
30
30
|
getPlayoutDelay(): number;
|
|
31
31
|
startMonitor(): void;
|
|
32
|
+
stopMonitor(): void;
|
|
32
33
|
protected abstract monitorReceiver(): void;
|
|
34
|
+
private timeSyncLoop;
|
|
35
|
+
private onTimeSyncListenerAdded;
|
|
33
36
|
registerTimeSyncUpdate(): void;
|
|
34
37
|
}
|
|
35
38
|
//# sourceMappingURL=RemoteTrack.d.ts.map
|
|
@@ -42,6 +42,7 @@ export declare abstract class Track<TrackKind extends Track.Kind = Track.Kind> e
|
|
|
42
42
|
protected timeSyncHandle: number | undefined;
|
|
43
43
|
protected _currentBitrate: number;
|
|
44
44
|
protected monitorInterval?: ReturnType<typeof setInterval>;
|
|
45
|
+
private finalStatsLogged;
|
|
45
46
|
protected log: StructuredLogger;
|
|
46
47
|
protected constructor(mediaTrack: MediaStreamTrack, kind: TrackKind, loggerOptions?: LoggerOptions);
|
|
47
48
|
protected get logContext(): {
|
|
@@ -78,7 +79,16 @@ export declare abstract class Track<TrackKind extends Track.Kind = Track.Kind> e
|
|
|
78
79
|
protected enable(): void;
|
|
79
80
|
protected disable(): void;
|
|
80
81
|
abstract startMonitor(signalClient?: SignalClient): void;
|
|
82
|
+
/**
|
|
83
|
+
* Raw stats of the sender or receiver this track is attached to.
|
|
84
|
+
*/
|
|
85
|
+
abstract getRTCStatsReport(): Promise<RTCStatsReport | undefined>;
|
|
81
86
|
stopMonitor(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Dumps the raw stats of the track as it ends, once: a track that stops
|
|
89
|
+
* between two of the room's stats dumps is gone by the time the next one runs.
|
|
90
|
+
*/
|
|
91
|
+
private logFinalStats;
|
|
82
92
|
/** @internal */
|
|
83
93
|
updateLoggerOptions(loggerOptions: LoggerOptions): void;
|
|
84
94
|
private recycleElement;
|
|
@@ -32,6 +32,20 @@ export declare function supportsAV1(): boolean;
|
|
|
32
32
|
export declare function supportsVP9(): boolean;
|
|
33
33
|
export declare function supportsH265(): boolean;
|
|
34
34
|
export declare function isSVCCodec(codec?: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Opts `transceiver` into negotiating the AV1 dependency descriptor, reporting whether it will be.
|
|
37
|
+
*
|
|
38
|
+
* Chrome only offers the extension on transceivers that can send, so one we create to receive on
|
|
39
|
+
* never negotiates it — and Chrome 152 stopped decoding AV1 that arrives without it: frames get
|
|
40
|
+
* assembled, none ever decode, and the receiver asks for a keyframe forever. Asking through the
|
|
41
|
+
* transceiver rather than munging the extension into the SDP leaves the browser owning the
|
|
42
|
+
* extension id, which is what keeps that id consistent across the bundle and across
|
|
43
|
+
* renegotiations.
|
|
44
|
+
*
|
|
45
|
+
* A no-op where the browser offers no such control, or does not know the extension at all.
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
export declare function negotiateDependencyDescriptor(transceiver: RTCRtpTransceiver): boolean;
|
|
35
49
|
export declare function supportsSetSinkId(elm?: HTMLMediaElement): boolean;
|
|
36
50
|
/**
|
|
37
51
|
* Checks whether or not setting an audio output via {@link Room#setActiveDevice}
|
|
@@ -162,4 +176,5 @@ export declare function decodeBase64(base64: string): Uint8Array;
|
|
|
162
176
|
export declare function extractMaxAgeFromRequestHeaders(headers: Headers): number | undefined;
|
|
163
177
|
export declare function isCompressionStreamSupported(): boolean;
|
|
164
178
|
export declare function isPublisherOfferWithJoinSupported(): boolean;
|
|
179
|
+
export declare function extractTrackSid(mediaTrack: MediaStreamTrack, stream: MediaStream): Track.SID | undefined;
|
|
165
180
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helper: resolves `true` if `promise` is still pending after `ms`
|
|
3
|
+
* (default: one macrotask), `false` if it has already settled — regardless of
|
|
4
|
+
* whether it resolved or rejected.
|
|
5
|
+
*
|
|
6
|
+
* Use to assert that something has *not* happened yet (e.g. an onClose callback
|
|
7
|
+
* that must stay silent), optionally within a timeout window. Preferable to
|
|
8
|
+
* `Promise.race([p, Promise.resolve(sentinel)])` when the "not yet" needs to
|
|
9
|
+
* hold for a duration rather than just the current microtask.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isPending(promise: Promise<unknown>, ms?: number): Promise<boolean>;
|
|
12
|
+
//# sourceMappingURL=promiseState.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in registry that lets a development tool observe the state machines driving the connection
|
|
3
|
+
* layer.
|
|
4
|
+
*
|
|
5
|
+
* The machines are private to the objects that own them and, more importantly, do not outlive them:
|
|
6
|
+
* a full reconnect replaces the whole engine along with its `SignalClient`, so anything watching a
|
|
7
|
+
* single machine reference goes blind exactly when the interesting part starts. Machines therefore
|
|
8
|
+
* announce themselves here as they are constructed, and a subscriber sees the whole succession.
|
|
9
|
+
*
|
|
10
|
+
* Nothing is recorded until {@link enableMachineInspector} is called, which no shipping code does —
|
|
11
|
+
* with the registry off, announcing is a comparison and a return.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The slice of machina's `Fsm` surface an inspector needs. Structural rather than machina's own
|
|
15
|
+
* type so that any machine satisfies it regardless of its state and input unions.
|
|
16
|
+
*/
|
|
17
|
+
export interface InspectableMachine {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly initialState: string;
|
|
20
|
+
readonly states: Record<string, Record<string, unknown>>;
|
|
21
|
+
readonly context?: unknown;
|
|
22
|
+
currentState(): string;
|
|
23
|
+
canHandle(input: string): boolean;
|
|
24
|
+
on(eventName: string, callback: (data: any) => void): {
|
|
25
|
+
off(): void;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface MachineAnnouncement {
|
|
29
|
+
/** What the machine drives, e.g. `signal`. Not unique: each new `SignalClient` announces one. */
|
|
30
|
+
label: string;
|
|
31
|
+
/** How many machines have been announced before this one, so successive instances are tellable apart. */
|
|
32
|
+
seq: number;
|
|
33
|
+
/**
|
|
34
|
+
* When the machine was constructed. Carried on the announcement because subscribers receive the
|
|
35
|
+
* recorded ones on subscribe, and stamping those at replay time would date them all to whenever
|
|
36
|
+
* the tool happened to open.
|
|
37
|
+
*/
|
|
38
|
+
at: number;
|
|
39
|
+
machine: InspectableMachine;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Starts recording machine announcements. Call before the machines of interest are constructed —
|
|
43
|
+
* for the signal machine that means before the `Room` is created.
|
|
44
|
+
*/
|
|
45
|
+
export declare function enableMachineInspector(): void;
|
|
46
|
+
export declare function isMachineInspectorEnabled(): boolean;
|
|
47
|
+
/** Announces a machine to whatever is watching. A no-op unless the inspector was enabled. */
|
|
48
|
+
export declare function announceMachine(label: string, machine: InspectableMachine): void;
|
|
49
|
+
/**
|
|
50
|
+
* Subscribes to machine announcements, replaying the ones already recorded so that a panel opened
|
|
51
|
+
* after connecting still sees the machine currently in charge. Returns an unsubscribe function.
|
|
52
|
+
*/
|
|
53
|
+
export declare function onMachineAnnounced(callback: (announcement: MachineAnnouncement) => void): () => void;
|
|
54
|
+
//# sourceMappingURL=machineInspector.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livekit-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.22.1",
|
|
4
4
|
"description": "JavaScript/TypeScript client SDK for LiveKit",
|
|
5
5
|
"main": "./dist/livekit-client.umd.js",
|
|
6
6
|
"unpkg": "./dist/livekit-client.umd.js",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"events": "^3.3.0",
|
|
55
55
|
"jose": "^6.1.0",
|
|
56
56
|
"loglevel": "^1.9.2",
|
|
57
|
+
"machina": "^7.0.0",
|
|
57
58
|
"sdp-transform": "^2.15.0",
|
|
58
59
|
"tslib": "2.8.1",
|
|
59
60
|
"typed-emitter": "^2.1.0",
|
|
@@ -66,12 +67,12 @@
|
|
|
66
67
|
"@babel/core": "7.29.7",
|
|
67
68
|
"@babel/preset-env": "7.29.7",
|
|
68
69
|
"@bufbuild/protoc-gen-es": "^1.10.0",
|
|
69
|
-
"@changesets/cli": "2.31.
|
|
70
|
+
"@changesets/cli": "2.31.1",
|
|
70
71
|
"@eslint/js": "10.0.1",
|
|
71
|
-
"@livekit/changesets-changelog-github": "^0.0
|
|
72
|
+
"@livekit/changesets-changelog-github": "^0.2.0",
|
|
72
73
|
"@livekit/throws-transformer": "^0.1.3",
|
|
73
|
-
"@rollup/plugin-babel": "7.
|
|
74
|
-
"@rollup/plugin-commonjs": "29.0.
|
|
74
|
+
"@rollup/plugin-babel": "7.1.0",
|
|
75
|
+
"@rollup/plugin-commonjs": "29.0.3",
|
|
75
76
|
"@rollup/plugin-json": "6.1.0",
|
|
76
77
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
77
78
|
"@rollup/plugin-terser": "^1.0.0",
|
|
@@ -82,34 +83,39 @@
|
|
|
82
83
|
"@types/events": "^3.0.3",
|
|
83
84
|
"@types/sdp-transform": "2.15.0",
|
|
84
85
|
"@types/ua-parser-js": "0.7.39",
|
|
85
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
86
|
-
"@typescript-eslint/parser": "8.
|
|
86
|
+
"@typescript-eslint/eslint-plugin": "8.64.0",
|
|
87
|
+
"@typescript-eslint/parser": "8.64.0",
|
|
87
88
|
"@vitest/browser": "^4.1.10",
|
|
88
89
|
"@vitest/browser-playwright": "^4.1.10",
|
|
89
90
|
"downlevel-dts": "^0.11.0",
|
|
90
|
-
"eslint": "10.
|
|
91
|
+
"eslint": "10.7.0",
|
|
91
92
|
"eslint-config-airbnb-extended": "^2.3.2",
|
|
92
93
|
"eslint-config-prettier": "10.1.8",
|
|
93
94
|
"eslint-plugin-compat": "^6.0.2",
|
|
94
95
|
"eslint-plugin-import-x": "^4.16.1",
|
|
96
|
+
"eslint-plugin-machina": "^0.1.4",
|
|
95
97
|
"eslint-plugin-prettier": "^5.5.4",
|
|
96
98
|
"gh-pages": "6.3.0",
|
|
97
99
|
"glob": "^13.0.6",
|
|
98
100
|
"happy-dom": "^20.0.0",
|
|
99
101
|
"jsdom": "^26.1.0",
|
|
102
|
+
"livekit-server-sdk": "^2.17.0",
|
|
103
|
+
"machina-inspect": "^4.0.0",
|
|
104
|
+
"machina-test": "^3.0.0",
|
|
105
|
+
"mermaid": "^11.16.1",
|
|
100
106
|
"playwright": "^1.61.1",
|
|
101
107
|
"prettier": "^3.4.2",
|
|
102
108
|
"publint": "^0.3.21",
|
|
103
|
-
"rollup": "4.
|
|
109
|
+
"rollup": "4.62.2",
|
|
104
110
|
"rollup-plugin-delete": "^2.1.0",
|
|
105
111
|
"rollup-plugin-typescript2": "0.37.0",
|
|
106
|
-
"size-limit": "^
|
|
112
|
+
"size-limit": "^12.0.0",
|
|
107
113
|
"tsx": "^4.21.0",
|
|
108
|
-
"typedoc": "0.28.
|
|
114
|
+
"typedoc": "0.28.20",
|
|
109
115
|
"typedoc-plugin-no-inherit": "1.6.1",
|
|
110
116
|
"typescript": "5.9.3",
|
|
111
117
|
"typescript-eslint": "^8.47.0",
|
|
112
|
-
"vite": "
|
|
118
|
+
"vite": "8.1.5",
|
|
113
119
|
"vitest": "^4.1.10"
|
|
114
120
|
},
|
|
115
121
|
"scripts": {
|
|
@@ -120,6 +126,7 @@
|
|
|
120
126
|
"build-docs": "typedoc && mkdir -p docs/assets/github && cp .github/*.png docs/assets/github/ && find docs -name '*.html' -type f -exec sed -i.bak 's|=\"/.github/|=\"assets/github/|g' {} + && find docs -name '*.bak' -delete",
|
|
121
127
|
"proto": "protoc --es_out src/proto --es_opt target=ts -I./protocol ./protocol/livekit_rtc.proto ./protocol/livekit_models.proto",
|
|
122
128
|
"examples:demo": "vite examples/demo -c vite.config.mjs",
|
|
129
|
+
"examples:machine": "vite examples/state-machine -c vite.config.mjs",
|
|
123
130
|
"dev": "pnpm examples:demo",
|
|
124
131
|
"lint": "eslint src",
|
|
125
132
|
"test": "vitest run src",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type LeaveRequest, LeaveRequest_Action } from '@livekit/protocol';
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, inject, it
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, inject, it } from 'vitest';
|
|
3
3
|
import { ConnectionErrorReason } from '../room/errors';
|
|
4
|
+
import { sleep } from '../room/utils';
|
|
5
|
+
import { isPending } from '../test/promiseState';
|
|
4
6
|
import { createInvalidToken, createToken } from '../test/signalToken';
|
|
5
7
|
import { SignalClient, SignalConnectionState, type SignalOptions } from './SignalClient';
|
|
6
8
|
|
|
@@ -15,8 +17,6 @@ const defaultOpts = (): SignalOptions => ({
|
|
|
15
17
|
websocketTimeout: 5_000,
|
|
16
18
|
});
|
|
17
19
|
|
|
18
|
-
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
19
|
-
|
|
20
20
|
function withTimeout<T>(p: Promise<T>, ms: number, label: string): Promise<T> {
|
|
21
21
|
return Promise.race([
|
|
22
22
|
p,
|
|
@@ -70,12 +70,11 @@ describe.skipIf(!!unavailable)('SignalClient e2e', () => {
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('stays connected past the ping timeout while the server pongs', async () => {
|
|
73
|
-
const onClose = vi.fn();
|
|
74
73
|
await join('happy');
|
|
75
|
-
|
|
74
|
+
const closed = captureClose(client);
|
|
76
75
|
// Server ping timeout is 3s; a healthy pong loop must keep us alive.
|
|
77
76
|
await sleep(4_000);
|
|
78
|
-
expect(
|
|
77
|
+
expect(await isPending(closed)).toBe(true);
|
|
79
78
|
expect(client.currentState).toBe(SignalConnectionState.CONNECTED);
|
|
80
79
|
});
|
|
81
80
|
|
|
@@ -151,15 +150,19 @@ describe.skipIf(!!unavailable)('SignalClient e2e', () => {
|
|
|
151
150
|
(e) => e as Error,
|
|
152
151
|
);
|
|
153
152
|
expect(err).toBeInstanceOf(Error);
|
|
153
|
+
expect((err as { reason?: ConnectionErrorReason }).reason).toBe(
|
|
154
|
+
ConnectionErrorReason.LeaveRequest,
|
|
155
|
+
);
|
|
156
|
+
expect((err as Error).message).toContain('Received leave request');
|
|
154
157
|
});
|
|
155
158
|
|
|
156
159
|
it('closes gracefully via close() without firing onClose', async () => {
|
|
157
|
-
const onClose = vi.fn();
|
|
158
160
|
await join('happy');
|
|
159
|
-
|
|
161
|
+
const closed = captureClose(client);
|
|
160
162
|
await client.close();
|
|
161
163
|
expect(client.isDisconnected).toBe(true);
|
|
162
|
-
|
|
164
|
+
// Give any stray onClose a macrotask to land before asserting it stayed silent.
|
|
165
|
+
expect(await isPending(closed, 100)).toBe(true);
|
|
163
166
|
});
|
|
164
167
|
|
|
165
168
|
it('reconnects (resume) back to CONNECTED', async () => {
|
|
@@ -181,6 +184,10 @@ describe.skipIf(!!unavailable)('SignalClient e2e', () => {
|
|
|
181
184
|
(e) => e as Error,
|
|
182
185
|
);
|
|
183
186
|
expect(err).toBeInstanceOf(Error);
|
|
187
|
+
expect((err as { reason?: ConnectionErrorReason }).reason).toBe(
|
|
188
|
+
ConnectionErrorReason.LeaveRequest,
|
|
189
|
+
);
|
|
190
|
+
expect((err as Error).message).toContain('Received leave request');
|
|
184
191
|
});
|
|
185
192
|
|
|
186
193
|
// --- validate-endpoint classification -------------------------------
|