livekit-client 2.4.2 → 2.5.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +8 -4
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +374 -102
- 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 +3 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/publishAudio.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +4 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +5 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +4 -3
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +12 -3
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +5 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -0
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +4 -4
- package/dist/src/room/timers.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/RemoteTrack.d.ts +12 -2
- package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +1 -1
- package/dist/src/room/types.d.ts +2 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +1 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +3 -2
- package/dist/ts4.2/src/room/PCTransport.d.ts +1 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +4 -3
- package/dist/ts4.2/src/room/Room.d.ts +5 -0
- package/dist/ts4.2/src/room/errors.d.ts +4 -3
- package/dist/ts4.2/src/room/events.d.ts +12 -3
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +5 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -0
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +1 -1
- package/dist/ts4.2/src/room/timers.d.ts +4 -4
- package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +12 -2
- package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/options.d.ts +1 -1
- package/dist/ts4.2/src/room/types.d.ts +2 -0
- package/dist/ts4.2/src/room/utils.d.ts +1 -1
- package/dist/ts4.2/src/version.d.ts +1 -1
- package/package.json +10 -10
- package/src/api/SignalClient.ts +12 -6
- package/src/connectionHelper/checks/publishAudio.ts +4 -1
- package/src/connectionHelper/checks/publishVideo.ts +6 -3
- package/src/room/PCTransport.ts +4 -1
- package/src/room/RTCEngine.ts +11 -5
- package/src/room/Room.ts +42 -5
- package/src/room/errors.ts +7 -3
- package/src/room/events.ts +12 -1
- package/src/room/participant/LocalParticipant.ts +125 -84
- package/src/room/participant/Participant.ts +1 -0
- package/src/room/participant/RemoteParticipant.ts +1 -1
- package/src/room/timers.ts +15 -6
- package/src/room/track/LocalVideoTrack.test.ts +60 -0
- package/src/room/track/LocalVideoTrack.ts +1 -1
- package/src/room/track/RemoteAudioTrack.ts +1 -1
- package/src/room/track/RemoteTrack.ts +38 -2
- package/src/room/track/RemoteVideoTrack.ts +2 -2
- package/src/room/track/options.ts +1 -1
- package/src/room/types.ts +2 -0
- package/src/room/utils.ts +10 -0
- package/src/version.ts +1 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { AddTrackRequest, ConnectionQualityUpdate, JoinResponse, StreamStateUpdate, SubscriptionPermissionUpdate, SubscriptionResponse } from '@livekit/protocol';
|
2
|
-
import { DataPacket, DataPacket_Kind, DisconnectReason,
|
2
|
+
import { DataPacket, DataPacket_Kind, DisconnectReason, ParticipantInfo, RequestResponse, Room as RoomModel, SpeakerInfo, SubscribedQualityUpdate, TrackInfo, TrackUnpublishedResponse, Transcription } 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';
|
@@ -136,7 +136,7 @@ export type EngineEventCallbacks = {
|
|
136
136
|
signalResumed: () => void;
|
137
137
|
signalRestarted: (joinResp: JoinResponse) => void;
|
138
138
|
closing: () => void;
|
139
|
-
mediaTrackAdded: (track: MediaStreamTrack, streams: MediaStream, receiver
|
139
|
+
mediaTrackAdded: (track: MediaStreamTrack, streams: MediaStream, receiver: RTCRtpReceiver) => void;
|
140
140
|
activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void;
|
141
141
|
dataPacketReceived: (packet: DataPacket) => void;
|
142
142
|
transcriptionReceived: (transcription: Transcription) => void;
|
@@ -154,9 +154,10 @@ export type EngineEventCallbacks = {
|
|
154
154
|
subscriptionPermissionUpdate: (update: SubscriptionPermissionUpdate) => void;
|
155
155
|
subscribedQualityUpdate: (update: SubscribedQualityUpdate) => void;
|
156
156
|
localTrackUnpublished: (unpublishedResponse: TrackUnpublishedResponse) => void;
|
157
|
+
localTrackSubscribed: (trackSid: string) => void;
|
157
158
|
remoteMute: (trackSid: string, muted: boolean) => void;
|
158
159
|
offline: () => void;
|
159
|
-
|
160
|
+
signalRequestResponse: (response: RequestResponse) => void;
|
160
161
|
};
|
161
162
|
export {};
|
162
163
|
//# sourceMappingURL=RTCEngine.d.ts.map
|
@@ -67,6 +67,10 @@ declare class Room extends Room_base {
|
|
67
67
|
private log;
|
68
68
|
private bufferedEvents;
|
69
69
|
private isResuming;
|
70
|
+
/**
|
71
|
+
* map to store first point in time when a particular transcription segment was received
|
72
|
+
*/
|
73
|
+
private transcriptionReceivedTimes;
|
70
74
|
/**
|
71
75
|
* Creates a new Room, the primary construct for a LiveKit session.
|
72
76
|
* @param options
|
@@ -264,5 +268,6 @@ export type RoomEventCallbacks = {
|
|
264
268
|
encryptionError: (error: Error) => void;
|
265
269
|
dcBufferStatusChanged: (isLow: boolean, kind: DataPacket_Kind) => void;
|
266
270
|
activeDeviceChanged: (kind: MediaDeviceKind, deviceId: string) => void;
|
271
|
+
localTrackSubscribed: (publication: LocalTrackPublication, participant: LocalParticipant) => void;
|
267
272
|
};
|
268
273
|
//# sourceMappingURL=Room.d.ts.map
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { RequestResponse_Reason } from '@livekit/protocol';
|
2
2
|
export declare class LivekitError extends Error {
|
3
3
|
code: number;
|
4
4
|
constructor(code: number, message?: string);
|
@@ -33,9 +33,10 @@ export declare class NegotiationError extends LivekitError {
|
|
33
33
|
export declare class PublishDataError extends LivekitError {
|
34
34
|
constructor(message?: string);
|
35
35
|
}
|
36
|
+
export type RequestErrorReason = Exclude<RequestResponse_Reason, RequestResponse_Reason.OK> | 'TimeoutError';
|
36
37
|
export declare class SignalRequestError extends LivekitError {
|
37
|
-
reason:
|
38
|
-
constructor(message: string, reason
|
38
|
+
reason: RequestErrorReason;
|
39
|
+
constructor(message: string, reason: RequestErrorReason);
|
39
40
|
}
|
40
41
|
export declare enum MediaDeviceFailure {
|
41
42
|
PermissionDenied = "PermissionDenied",
|
@@ -282,7 +282,11 @@ export declare enum RoomEvent {
|
|
282
282
|
* Triggered by a call to room.switchActiveDevice
|
283
283
|
* args: (kind: MediaDeviceKind, deviceId: string)
|
284
284
|
*/
|
285
|
-
ActiveDeviceChanged = "activeDeviceChanged"
|
285
|
+
ActiveDeviceChanged = "activeDeviceChanged",
|
286
|
+
/**
|
287
|
+
* fired when the first remote participant has subscribed to the localParticipant's track
|
288
|
+
*/
|
289
|
+
LocalTrackSubscribed = "localTrackSubscribed"
|
286
290
|
}
|
287
291
|
export declare enum ParticipantEvent {
|
288
292
|
/**
|
@@ -442,7 +446,11 @@ export declare enum ParticipantEvent {
|
|
442
446
|
* all users.
|
443
447
|
* When a participant's attributes changed, this event will be emitted with the changed attributes
|
444
448
|
*/
|
445
|
-
AttributesChanged = "attributesChanged"
|
449
|
+
AttributesChanged = "attributesChanged",
|
450
|
+
/**
|
451
|
+
* fired on local participant only, when the first remote participant has subscribed to the track specified in the payload
|
452
|
+
*/
|
453
|
+
LocalTrackSubscribed = "localTrackSubscribed"
|
446
454
|
}
|
447
455
|
/** @internal */
|
448
456
|
export declare enum EngineEvent {
|
@@ -471,8 +479,9 @@ export declare enum EngineEvent {
|
|
471
479
|
RemoteMute = "remoteMute",
|
472
480
|
SubscribedQualityUpdate = "subscribedQualityUpdate",
|
473
481
|
LocalTrackUnpublished = "localTrackUnpublished",
|
482
|
+
LocalTrackSubscribed = "localTrackSubscribed",
|
474
483
|
Offline = "offline",
|
475
|
-
|
484
|
+
SignalRequestResponse = "signalRequestResponse"
|
476
485
|
}
|
477
486
|
export declare enum TrackEvent {
|
478
487
|
Message = "message",
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ParticipantInfo, ParticipantPermission } from '@livekit/protocol';
|
1
|
+
import { Codec, ParticipantInfo, ParticipantPermission } from '@livekit/protocol';
|
2
2
|
import type { InternalRoomOptions } from '../../options';
|
3
3
|
import type RTCEngine from '../RTCEngine';
|
4
4
|
import LocalTrack from '../track/LocalTrack';
|
@@ -27,6 +27,7 @@ export default class LocalParticipant extends Participant {
|
|
27
27
|
private encryptionType;
|
28
28
|
private reconnectFuture?;
|
29
29
|
private pendingSignalRequests;
|
30
|
+
private enabledPublishVideoCodecs;
|
30
31
|
/** @internal */
|
31
32
|
constructor(sid: string, identity: string, engine: RTCEngine, options: InternalRoomOptions);
|
32
33
|
get lastCameraError(): Error | undefined;
|
@@ -41,7 +42,7 @@ export default class LocalParticipant extends Participant {
|
|
41
42
|
private handleReconnecting;
|
42
43
|
private handleReconnected;
|
43
44
|
private handleDisconnected;
|
44
|
-
private
|
45
|
+
private handleSignalRequestResponse;
|
45
46
|
/**
|
46
47
|
* Sets and updates the metadata of the local participant.
|
47
48
|
* Note: this requires `canUpdateOwnMetadata` permission.
|
@@ -152,6 +153,8 @@ export default class LocalParticipant extends Participant {
|
|
152
153
|
*/
|
153
154
|
setTrackSubscriptionPermissions(allParticipantsAllowed: boolean, participantTrackPermissions?: ParticipantTrackPermission[]): void;
|
154
155
|
/** @internal */
|
156
|
+
setEnabledPublishCodecs(codecs: Codec[]): void;
|
157
|
+
/** @internal */
|
155
158
|
updateInfo(info: ParticipantInfo): boolean;
|
156
159
|
private updateTrackSubscriptionPermissions;
|
157
160
|
/** @internal */
|
@@ -121,5 +121,6 @@ export type ParticipantEventCallbacks = {
|
|
121
121
|
participantPermissionsChanged: (prevPermissions?: ParticipantPermission) => void;
|
122
122
|
trackSubscriptionStatusChanged: (publication: RemoteTrackPublication, status: TrackPublication.SubscriptionStatus) => void;
|
123
123
|
attributesChanged: (changedAttributes: Record<string, string>) => void;
|
124
|
+
localTrackSubscribed: (trackPublication: LocalTrackPublication) => void;
|
124
125
|
};
|
125
126
|
//# sourceMappingURL=Participant.d.ts.map
|
@@ -37,7 +37,7 @@ export default class RemoteParticipant extends Participant {
|
|
37
37
|
*/
|
38
38
|
getVolume(source?: Track.Source.Microphone | Track.Source.ScreenShareAudio): number | undefined;
|
39
39
|
/** @internal */
|
40
|
-
addSubscribedMediaTrack(mediaTrack: MediaStreamTrack, sid: Track.SID, mediaStream: MediaStream, receiver
|
40
|
+
addSubscribedMediaTrack(mediaTrack: MediaStreamTrack, sid: Track.SID, mediaStream: MediaStream, receiver: RTCRtpReceiver, adaptiveStreamSettings?: AdaptiveStreamSettings, triesLeft?: number): RemoteTrackPublication | undefined;
|
41
41
|
/** @internal */
|
42
42
|
get hasMetadata(): boolean;
|
43
43
|
/**
|
@@ -4,9 +4,9 @@
|
|
4
4
|
* that the timer fires on time.
|
5
5
|
*/
|
6
6
|
export default class CriticalTimers {
|
7
|
-
static setTimeout: (
|
8
|
-
static setInterval: (
|
9
|
-
static clearTimeout: (
|
10
|
-
static clearInterval: (
|
7
|
+
static setTimeout: (...args: Parameters<typeof setTimeout>) => ReturnType<typeof setTimeout>;
|
8
|
+
static setInterval: (...args: Parameters<typeof setInterval>) => ReturnType<typeof setInterval>;
|
9
|
+
static clearTimeout: (...args: Parameters<typeof clearTimeout>) => ReturnType<typeof clearTimeout>;
|
10
|
+
static clearInterval: (...args: Parameters<typeof clearInterval>) => ReturnType<typeof clearInterval>;
|
11
11
|
}
|
12
12
|
//# sourceMappingURL=timers.d.ts.map
|
@@ -11,7 +11,7 @@ export default class RemoteAudioTrack extends RemoteTrack<Track.Kind.Audio> {
|
|
11
11
|
private sourceNode?;
|
12
12
|
private webAudioPluginNodes;
|
13
13
|
private sinkId?;
|
14
|
-
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver
|
14
|
+
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver: RTCRtpReceiver, audioContext?: AudioContext, audioOutput?: AudioOutputOptions, loggerOptions?: LoggerOptions);
|
15
15
|
/**
|
16
16
|
* sets the volume for all attached audio elements
|
17
17
|
*/
|
@@ -2,8 +2,8 @@ import type { LoggerOptions } from '../types';
|
|
2
2
|
import { Track } from './Track';
|
3
3
|
export default abstract class RemoteTrack<TrackKind extends Track.Kind = Track.Kind> extends Track<TrackKind> {
|
4
4
|
/** @internal */
|
5
|
-
receiver
|
6
|
-
constructor(mediaTrack: MediaStreamTrack, sid: string, kind: TrackKind, receiver
|
5
|
+
receiver: RTCRtpReceiver | undefined;
|
6
|
+
constructor(mediaTrack: MediaStreamTrack, sid: string, kind: TrackKind, receiver: RTCRtpReceiver, loggerOptions?: LoggerOptions);
|
7
7
|
/** @internal */
|
8
8
|
setMuted(muted: boolean): void;
|
9
9
|
/** @internal */
|
@@ -17,6 +17,16 @@ export default abstract class RemoteTrack<TrackKind extends Track.Kind = Track.K
|
|
17
17
|
* @returns Promise<RTCStatsReport> | undefined
|
18
18
|
*/
|
19
19
|
getRTCStatsReport(): Promise<RTCStatsReport | undefined>;
|
20
|
+
/**
|
21
|
+
* Allows to set a playout delay (in seconds) for this track.
|
22
|
+
* A higher value allows for more buffering of the track in the browser
|
23
|
+
* and will result in a delay of media being played back of `delayInSeconds`
|
24
|
+
*/
|
25
|
+
setPlayoutDelay(delayInSeconds: number): void;
|
26
|
+
/**
|
27
|
+
* Returns the current playout delay (in seconds) of this track.
|
28
|
+
*/
|
29
|
+
getPlayoutDelay(): number;
|
20
30
|
startMonitor(): void;
|
21
31
|
protected abstract monitorReceiver(): void;
|
22
32
|
registerTimeSyncUpdate(): void;
|
@@ -8,7 +8,7 @@ export default class RemoteVideoTrack extends RemoteTrack<Track.Kind.Video> {
|
|
8
8
|
private adaptiveStreamSettings?;
|
9
9
|
private lastVisible?;
|
10
10
|
private lastDimensions?;
|
11
|
-
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver
|
11
|
+
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver: RTCRtpReceiver, adaptiveStreamSettings?: AdaptiveStreamSettings, loggerOptions?: LoggerOptions);
|
12
12
|
get isAdaptiveStream(): boolean;
|
13
13
|
/**
|
14
14
|
* Note: When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start
|
@@ -53,7 +53,7 @@ export interface TrackPublishDefaults {
|
|
53
53
|
*/
|
54
54
|
simulcast?: boolean;
|
55
55
|
/**
|
56
|
-
* scalability mode for svc codecs, defaults to '
|
56
|
+
* scalability mode for svc codecs, defaults to 'L3T3_KEY'.
|
57
57
|
* for svc codecs, simulcast is disabled.
|
58
58
|
*/
|
59
59
|
scalabilityMode?: ScalabilityMode;
|
@@ -95,5 +95,5 @@ export declare function unwrapConstraint(constraint: ConstrainDOMString): string
|
|
95
95
|
export declare function unwrapConstraint(constraint: ConstrainULong): number;
|
96
96
|
export declare function toWebsocketUrl(url: string): string;
|
97
97
|
export declare function toHttpUrl(url: string): string;
|
98
|
-
export declare function extractTranscriptionSegments(transcription: TranscriptionModel): TranscriptionSegment[];
|
98
|
+
export declare function extractTranscriptionSegments(transcription: TranscriptionModel, firstReceivedTimesMap: Map<string, number>): TranscriptionSegment[];
|
99
99
|
//# sourceMappingURL=utils.d.ts.map
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "livekit-client",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.5.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",
|
@@ -36,7 +36,7 @@
|
|
36
36
|
"author": "David Zhao <david@davidzhao.com>",
|
37
37
|
"license": "Apache-2.0",
|
38
38
|
"dependencies": {
|
39
|
-
"@livekit/protocol": "1.
|
39
|
+
"@livekit/protocol": "1.20.1",
|
40
40
|
"events": "^3.3.0",
|
41
41
|
"loglevel": "^1.8.0",
|
42
42
|
"sdp-transform": "^2.14.1",
|
@@ -46,8 +46,8 @@
|
|
46
46
|
"webrtc-adapter": "^9.0.0"
|
47
47
|
},
|
48
48
|
"devDependencies": {
|
49
|
-
"@babel/core": "7.
|
50
|
-
"@babel/preset-env": "7.
|
49
|
+
"@babel/core": "7.25.2",
|
50
|
+
"@babel/preset-env": "7.25.3",
|
51
51
|
"@bufbuild/protoc-gen-es": "^1.3.0",
|
52
52
|
"@changesets/cli": "2.27.7",
|
53
53
|
"@livekit/changesets-changelog-github": "^0.0.4",
|
@@ -62,8 +62,8 @@
|
|
62
62
|
"@types/events": "^3.0.0",
|
63
63
|
"@types/sdp-transform": "2.4.9",
|
64
64
|
"@types/ua-parser-js": "0.7.39",
|
65
|
-
"@typescript-eslint/eslint-plugin": "7.
|
66
|
-
"@typescript-eslint/parser": "7.
|
65
|
+
"@typescript-eslint/eslint-plugin": "7.18.0",
|
66
|
+
"@typescript-eslint/parser": "7.18.0",
|
67
67
|
"downlevel-dts": "^0.11.0",
|
68
68
|
"eslint": "8.57.0",
|
69
69
|
"eslint-config-airbnb-typescript": "18.0.0",
|
@@ -73,15 +73,15 @@
|
|
73
73
|
"gh-pages": "6.1.1",
|
74
74
|
"jsdom": "^24.0.0",
|
75
75
|
"prettier": "^3.0.0",
|
76
|
-
"rollup": "4.
|
76
|
+
"rollup": "4.19.2",
|
77
77
|
"rollup-plugin-delete": "^2.0.0",
|
78
78
|
"rollup-plugin-re": "1.0.7",
|
79
79
|
"rollup-plugin-typescript2": "0.36.0",
|
80
80
|
"size-limit": "^8.2.4",
|
81
|
-
"typedoc": "0.26.
|
81
|
+
"typedoc": "0.26.5",
|
82
82
|
"typedoc-plugin-no-inherit": "1.4.0",
|
83
|
-
"typescript": "5.5.
|
84
|
-
"vite": "5.3.
|
83
|
+
"typescript": "5.5.4",
|
84
|
+
"vite": "5.3.5",
|
85
85
|
"vitest": "^1.0.0"
|
86
86
|
},
|
87
87
|
"scripts": {
|
package/src/api/SignalClient.ts
CHANGED
@@ -4,7 +4,6 @@ import {
|
|
4
4
|
ClientInfo,
|
5
5
|
ConnectionQualityUpdate,
|
6
6
|
DisconnectReason,
|
7
|
-
ErrorResponse,
|
8
7
|
JoinResponse,
|
9
8
|
LeaveRequest,
|
10
9
|
LeaveRequest_Action,
|
@@ -13,6 +12,7 @@ import {
|
|
13
12
|
Ping,
|
14
13
|
ReconnectReason,
|
15
14
|
ReconnectResponse,
|
15
|
+
RequestResponse,
|
16
16
|
Room,
|
17
17
|
SessionDescription,
|
18
18
|
SignalRequest,
|
@@ -142,7 +142,9 @@ export class SignalClient {
|
|
142
142
|
|
143
143
|
onLeave?: (leave: LeaveRequest) => void;
|
144
144
|
|
145
|
-
|
145
|
+
onRequestResponse?: (response: RequestResponse) => void;
|
146
|
+
|
147
|
+
onLocalTrackSubscribed?: (trackSid: string) => void;
|
146
148
|
|
147
149
|
connectOptions?: ConnectOpts;
|
148
150
|
|
@@ -440,6 +442,7 @@ export class SignalClient {
|
|
440
442
|
async close(updateState: boolean = true) {
|
441
443
|
const unlock = await this.closingLock.lock();
|
442
444
|
try {
|
445
|
+
this.clearPingInterval();
|
443
446
|
if (updateState) {
|
444
447
|
this.state = SignalConnectionState.DISCONNECTING;
|
445
448
|
}
|
@@ -470,7 +473,6 @@ export class SignalClient {
|
|
470
473
|
if (updateState) {
|
471
474
|
this.state = SignalConnectionState.DISCONNECTED;
|
472
475
|
}
|
473
|
-
this.clearPingInterval();
|
474
476
|
unlock();
|
475
477
|
}
|
476
478
|
}
|
@@ -739,9 +741,13 @@ export class SignalClient {
|
|
739
741
|
this.rtt = Date.now() - Number.parseInt(msg.value.lastPingTimestamp.toString());
|
740
742
|
this.resetPingTimeout();
|
741
743
|
pingHandled = true;
|
742
|
-
} else if (msg.case === '
|
743
|
-
if (this.
|
744
|
-
this.
|
744
|
+
} else if (msg.case === 'requestResponse') {
|
745
|
+
if (this.onRequestResponse) {
|
746
|
+
this.onRequestResponse(msg.value);
|
747
|
+
}
|
748
|
+
} else if (msg.case === 'trackSubscribed') {
|
749
|
+
if (this.onLocalTrackSubscribed) {
|
750
|
+
this.onLocalTrackSubscribed(msg.value.trackSid);
|
745
751
|
}
|
746
752
|
} else {
|
747
753
|
this.log.debug('unsupported message', { ...this.logContext, msgCase: msg.case });
|
@@ -21,7 +21,10 @@ export class PublishAudioCheck extends Checker {
|
|
21
21
|
}
|
22
22
|
let numPackets = 0;
|
23
23
|
stats.forEach((stat) => {
|
24
|
-
if (
|
24
|
+
if (
|
25
|
+
stat.type === 'outbound-rtp' &&
|
26
|
+
(stat.kind === 'audio' || (!stat.kind && stat.mediaType === 'audio'))
|
27
|
+
) {
|
25
28
|
numPackets = stat.packetsSent;
|
26
29
|
}
|
27
30
|
});
|
@@ -12,7 +12,7 @@ export class PublishVideoCheck extends Checker {
|
|
12
12
|
const track = await createLocalVideoTrack();
|
13
13
|
room.localParticipant.publishTrack(track);
|
14
14
|
// wait for a few seconds to publish
|
15
|
-
await new Promise((resolve) => setTimeout(resolve,
|
15
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
16
16
|
|
17
17
|
// verify RTC stats that it's publishing
|
18
18
|
const stats = await track.sender?.getStats();
|
@@ -21,8 +21,11 @@ export class PublishVideoCheck extends Checker {
|
|
21
21
|
}
|
22
22
|
let numPackets = 0;
|
23
23
|
stats.forEach((stat) => {
|
24
|
-
if (
|
25
|
-
|
24
|
+
if (
|
25
|
+
stat.type === 'outbound-rtp' &&
|
26
|
+
(stat.kind === 'video' || (!stat.kind && stat.mediaType === 'video'))
|
27
|
+
) {
|
28
|
+
numPackets += stat.packetsSent;
|
26
29
|
}
|
27
30
|
});
|
28
31
|
if (numPackets === 0) {
|
package/src/room/PCTransport.ts
CHANGED
@@ -23,6 +23,8 @@ eliminate this issue.
|
|
23
23
|
*/
|
24
24
|
const startBitrateForSVC = 0.7;
|
25
25
|
|
26
|
+
const debounceInterval = 20;
|
27
|
+
|
26
28
|
export const PCEvents = {
|
27
29
|
NegotiationStarted: 'negotiationStarted',
|
28
30
|
NegotiationComplete: 'negotiationComplete',
|
@@ -228,7 +230,7 @@ export default class PCTransport extends EventEmitter {
|
|
228
230
|
throw e;
|
229
231
|
}
|
230
232
|
}
|
231
|
-
},
|
233
|
+
}, debounceInterval);
|
232
234
|
|
233
235
|
async createAndSendOffer(options?: RTCOfferOptions) {
|
234
236
|
if (this.onOffer === undefined) {
|
@@ -260,6 +262,7 @@ export default class PCTransport extends EventEmitter {
|
|
260
262
|
// actually negotiate
|
261
263
|
this.log.debug('starting to negotiate', this.logContext);
|
262
264
|
const offer = await this.pc.createOffer(options);
|
265
|
+
this.log.debug('original offer', { sdp: offer.sdp, ...this.logContext });
|
263
266
|
|
264
267
|
const sdpParsed = parse(offer.sdp ?? '');
|
265
268
|
sdpParsed.media.forEach((media) => {
|
package/src/room/RTCEngine.ts
CHANGED
@@ -7,13 +7,13 @@ import {
|
|
7
7
|
DataPacket,
|
8
8
|
DataPacket_Kind,
|
9
9
|
DisconnectReason,
|
10
|
-
ErrorResponse,
|
11
10
|
type JoinResponse,
|
12
11
|
type LeaveRequest,
|
13
12
|
LeaveRequest_Action,
|
14
13
|
ParticipantInfo,
|
15
14
|
ReconnectReason,
|
16
15
|
type ReconnectResponse,
|
16
|
+
RequestResponse,
|
17
17
|
Room as RoomModel,
|
18
18
|
SignalTarget,
|
19
19
|
SpeakerInfo,
|
@@ -194,7 +194,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
194
194
|
this.emit(EngineEvent.SubscriptionPermissionUpdate, update);
|
195
195
|
this.client.onSpeakersChanged = (update) => this.emit(EngineEvent.SpeakersChanged, update);
|
196
196
|
this.client.onStreamStateUpdate = (update) => this.emit(EngineEvent.StreamStateChanged, update);
|
197
|
-
this.client.
|
197
|
+
this.client.onRequestResponse = (response) =>
|
198
|
+
this.emit(EngineEvent.SignalRequestResponse, response);
|
198
199
|
}
|
199
200
|
|
200
201
|
/** @internal */
|
@@ -231,7 +232,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
231
232
|
}
|
232
233
|
|
233
234
|
// create offer
|
234
|
-
if (!this.subscriberPrimary) {
|
235
|
+
if (!this.subscriberPrimary || joinResponse.fastPublish) {
|
235
236
|
this.negotiate();
|
236
237
|
}
|
237
238
|
|
@@ -493,6 +494,10 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
493
494
|
this.emit(EngineEvent.LocalTrackUnpublished, response);
|
494
495
|
};
|
495
496
|
|
497
|
+
this.client.onLocalTrackSubscribed = (trackSid: string) => {
|
498
|
+
this.emit(EngineEvent.LocalTrackSubscribed, trackSid);
|
499
|
+
};
|
500
|
+
|
496
501
|
this.client.onTokenRefresh = (token: string) => {
|
497
502
|
this.token = token;
|
498
503
|
};
|
@@ -1393,7 +1398,7 @@ export type EngineEventCallbacks = {
|
|
1393
1398
|
mediaTrackAdded: (
|
1394
1399
|
track: MediaStreamTrack,
|
1395
1400
|
streams: MediaStream,
|
1396
|
-
receiver
|
1401
|
+
receiver: RTCRtpReceiver,
|
1397
1402
|
) => void;
|
1398
1403
|
activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void;
|
1399
1404
|
dataPacketReceived: (packet: DataPacket) => void;
|
@@ -1412,9 +1417,10 @@ export type EngineEventCallbacks = {
|
|
1412
1417
|
subscriptionPermissionUpdate: (update: SubscriptionPermissionUpdate) => void;
|
1413
1418
|
subscribedQualityUpdate: (update: SubscribedQualityUpdate) => void;
|
1414
1419
|
localTrackUnpublished: (unpublishedResponse: TrackUnpublishedResponse) => void;
|
1420
|
+
localTrackSubscribed: (trackSid: string) => void;
|
1415
1421
|
remoteMute: (trackSid: string, muted: boolean) => void;
|
1416
1422
|
offline: () => void;
|
1417
|
-
|
1423
|
+
signalRequestResponse: (response: RequestResponse) => void;
|
1418
1424
|
};
|
1419
1425
|
|
1420
1426
|
function supportOptionalDatachannel(protocol: number | undefined): boolean {
|
package/src/room/Room.ts
CHANGED
@@ -162,6 +162,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
162
162
|
|
163
163
|
private isResuming: boolean = false;
|
164
164
|
|
165
|
+
/**
|
166
|
+
* map to store first point in time when a particular transcription segment was received
|
167
|
+
*/
|
168
|
+
private transcriptionReceivedTimes: Map<string, number>;
|
169
|
+
|
165
170
|
/**
|
166
171
|
* Creates a new Room, the primary construct for a LiveKit session.
|
167
172
|
* @param options
|
@@ -174,6 +179,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
174
179
|
this.options = { ...roomOptionDefaults, ...options };
|
175
180
|
|
176
181
|
this.log = getLogger(this.options.loggerName ?? LoggerNames.Room);
|
182
|
+
this.transcriptionReceivedTimes = new Map();
|
177
183
|
|
178
184
|
this.options.audioCaptureDefaults = {
|
179
185
|
...audioDefaults,
|
@@ -328,7 +334,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
328
334
|
.on(EngineEvent.SubscriptionPermissionUpdate, this.handleSubscriptionPermissionUpdate)
|
329
335
|
.on(
|
330
336
|
EngineEvent.MediaTrackAdded,
|
331
|
-
(mediaTrack: MediaStreamTrack, stream: MediaStream, receiver
|
337
|
+
(mediaTrack: MediaStreamTrack, stream: MediaStream, receiver: RTCRtpReceiver) => {
|
332
338
|
this.onTrackAdded(mediaTrack, stream, receiver);
|
333
339
|
},
|
334
340
|
)
|
@@ -370,6 +376,24 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
370
376
|
})
|
371
377
|
.on(EngineEvent.DCBufferStatusChanged, (status, kind) => {
|
372
378
|
this.emit(RoomEvent.DCBufferStatusChanged, status, kind);
|
379
|
+
})
|
380
|
+
.on(EngineEvent.LocalTrackSubscribed, (subscribedSid) => {
|
381
|
+
const trackPublication = this.localParticipant
|
382
|
+
.getTrackPublications()
|
383
|
+
.find(({ trackSid }) => trackSid === subscribedSid) as LocalTrackPublication | undefined;
|
384
|
+
if (!trackPublication) {
|
385
|
+
this.log.warn(
|
386
|
+
'could not find local track subscription for subscribed event',
|
387
|
+
this.logContext,
|
388
|
+
);
|
389
|
+
return;
|
390
|
+
}
|
391
|
+
this.localParticipant.emit(ParticipantEvent.LocalTrackSubscribed, trackPublication);
|
392
|
+
this.emitWhenConnected(
|
393
|
+
RoomEvent.LocalTrackSubscribed,
|
394
|
+
trackPublication,
|
395
|
+
this.localParticipant,
|
396
|
+
);
|
373
397
|
});
|
374
398
|
|
375
399
|
if (this.localParticipant) {
|
@@ -608,6 +632,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
608
632
|
|
609
633
|
this.localParticipant.sid = pi.sid;
|
610
634
|
this.localParticipant.identity = pi.identity;
|
635
|
+
this.localParticipant.setEnabledPublishCodecs(joinResponse.enabledPublishCodecs);
|
611
636
|
|
612
637
|
if (this.options.e2ee && this.e2eeManager) {
|
613
638
|
try {
|
@@ -1152,7 +1177,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1152
1177
|
private onTrackAdded(
|
1153
1178
|
mediaTrack: MediaStreamTrack,
|
1154
1179
|
stream: MediaStream,
|
1155
|
-
receiver
|
1180
|
+
receiver: RTCRtpReceiver,
|
1156
1181
|
) {
|
1157
1182
|
// don't fire onSubscribed when connecting
|
1158
1183
|
// WebRTC fires onTrack as soon as setRemoteDescription is called on the offer
|
@@ -1274,6 +1299,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1274
1299
|
this.clearConnectionReconcile();
|
1275
1300
|
this.isResuming = false;
|
1276
1301
|
this.bufferedEvents = [];
|
1302
|
+
this.transcriptionReceivedTimes.clear();
|
1277
1303
|
if (this.state === ConnectionState.Disconnected) {
|
1278
1304
|
return;
|
1279
1305
|
}
|
@@ -1535,7 +1561,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1535
1561
|
: this.getParticipantByIdentity(transcription.transcribedParticipantIdentity);
|
1536
1562
|
const publication = participant?.trackPublications.get(transcription.trackId);
|
1537
1563
|
|
1538
|
-
const segments = extractTranscriptionSegments(transcription);
|
1564
|
+
const segments = extractTranscriptionSegments(transcription, this.transcriptionReceivedTimes);
|
1539
1565
|
|
1540
1566
|
publication?.emit(TrackEvent.TranscriptionReceived, segments);
|
1541
1567
|
participant?.emit(ParticipantEvent.TranscriptionReceived, segments, publication);
|
@@ -2055,7 +2081,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
2055
2081
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
2056
2082
|
type: TrackType.AUDIO,
|
2057
2083
|
});
|
2058
|
-
p.addSubscribedMediaTrack(
|
2084
|
+
p.addSubscribedMediaTrack(
|
2085
|
+
dummyVideo,
|
2086
|
+
videoTrack.sid,
|
2087
|
+
new MediaStream([dummyVideo]),
|
2088
|
+
new RTCRtpReceiver(),
|
2089
|
+
);
|
2059
2090
|
info.tracks = [...info.tracks, videoTrack];
|
2060
2091
|
}
|
2061
2092
|
if (participantOptions.audio) {
|
@@ -2065,7 +2096,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
2065
2096
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
2066
2097
|
type: TrackType.AUDIO,
|
2067
2098
|
});
|
2068
|
-
p.addSubscribedMediaTrack(
|
2099
|
+
p.addSubscribedMediaTrack(
|
2100
|
+
dummyTrack,
|
2101
|
+
audioTrack.sid,
|
2102
|
+
new MediaStream([dummyTrack]),
|
2103
|
+
new RTCRtpReceiver(),
|
2104
|
+
);
|
2069
2105
|
info.tracks = [...info.tracks, audioTrack];
|
2070
2106
|
}
|
2071
2107
|
|
@@ -2192,4 +2228,5 @@ export type RoomEventCallbacks = {
|
|
2192
2228
|
encryptionError: (error: Error) => void;
|
2193
2229
|
dcBufferStatusChanged: (isLow: boolean, kind: DataPacket_Kind) => void;
|
2194
2230
|
activeDeviceChanged: (kind: MediaDeviceKind, deviceId: string) => void;
|
2231
|
+
localTrackSubscribed: (publication: LocalTrackPublication, participant: LocalParticipant) => void;
|
2195
2232
|
};
|
package/src/room/errors.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { RequestResponse_Reason } from '@livekit/protocol';
|
2
2
|
|
3
3
|
export class LivekitError extends Error {
|
4
4
|
code: number;
|
@@ -65,10 +65,14 @@ export class PublishDataError extends LivekitError {
|
|
65
65
|
}
|
66
66
|
}
|
67
67
|
|
68
|
+
export type RequestErrorReason =
|
69
|
+
| Exclude<RequestResponse_Reason, RequestResponse_Reason.OK>
|
70
|
+
| 'TimeoutError';
|
71
|
+
|
68
72
|
export class SignalRequestError extends LivekitError {
|
69
|
-
reason:
|
73
|
+
reason: RequestErrorReason;
|
70
74
|
|
71
|
-
constructor(message: string, reason:
|
75
|
+
constructor(message: string, reason: RequestErrorReason) {
|
72
76
|
super(15, message);
|
73
77
|
this.reason = reason;
|
74
78
|
}
|