@livekit/rtc-node 0.10.4 → 0.11.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 +54 -2
- package/build.rs +1 -1
- package/dist/audio_frame.d.ts +1 -1
- package/dist/audio_frame.d.ts.map +1 -1
- package/dist/audio_frame.js +4 -4
- package/dist/audio_frame.js.map +1 -1
- package/dist/audio_resampler.d.ts.map +1 -1
- package/dist/audio_resampler.js +8 -7
- package/dist/audio_resampler.js.map +1 -1
- package/dist/audio_source.d.ts.map +1 -1
- package/dist/audio_source.js +8 -4
- package/dist/audio_source.js.map +1 -1
- package/dist/audio_stream.d.ts +1 -1
- package/dist/audio_stream.d.ts.map +1 -1
- package/dist/audio_stream.js +4 -3
- package/dist/audio_stream.js.map +1 -1
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/e2ee.js +25 -25
- package/dist/e2ee.js.map +1 -1
- package/dist/ffi_client.d.ts +3 -3
- package/dist/ffi_client.d.ts.map +1 -1
- package/dist/ffi_client.js +9 -7
- package/dist/ffi_client.js.map +1 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -4
- package/dist/index.js.map +1 -1
- package/dist/napi/native.d.ts +1 -0
- package/dist/participant.d.ts +48 -2
- package/dist/participant.d.ts.map +1 -1
- package/dist/participant.js +143 -24
- package/dist/participant.js.map +1 -1
- package/dist/proto/audio_frame_pb.d.ts +451 -526
- package/dist/proto/audio_frame_pb.d.ts.map +1 -1
- package/dist/proto/audio_frame_pb.js +200 -1179
- package/dist/proto/audio_frame_pb.js.map +1 -1
- package/dist/proto/e2ee_pb.d.ts +264 -333
- package/dist/proto/e2ee_pb.d.ts.map +1 -1
- package/dist/proto/e2ee_pb.js +136 -810
- package/dist/proto/e2ee_pb.js.map +1 -1
- package/dist/proto/ffi_pb.d.ts +175 -129
- package/dist/proto/ffi_pb.d.ts.map +1 -1
- package/dist/proto/ffi_pb.js +58 -395
- package/dist/proto/ffi_pb.js.map +1 -1
- package/dist/proto/handle_pb.d.ts +14 -13
- package/dist/proto/handle_pb.d.ts.map +1 -1
- package/dist/proto/handle_pb.js +8 -39
- package/dist/proto/handle_pb.js.map +1 -1
- package/dist/proto/participant_pb.d.ts +54 -52
- package/dist/proto/participant_pb.d.ts.map +1 -1
- package/dist/proto/participant_pb.js +18 -90
- package/dist/proto/participant_pb.js.map +1 -1
- package/dist/proto/room_pb.d.ts +1042 -1187
- package/dist/proto/room_pb.d.ts.map +1 -1
- package/dist/proto/room_pb.js +470 -2938
- package/dist/proto/room_pb.js.map +1 -1
- package/dist/proto/rpc_pb.d.ts +235 -0
- package/dist/proto/rpc_pb.d.ts.map +1 -0
- package/dist/proto/rpc_pb.js +74 -0
- package/dist/proto/rpc_pb.js.map +1 -0
- package/dist/proto/stats_pb.d.ts +738 -800
- package/dist/proto/stats_pb.d.ts.map +1 -1
- package/dist/proto/stats_pb.js +218 -1960
- package/dist/proto/stats_pb.js.map +1 -1
- package/dist/proto/track_pb.d.ts +225 -258
- package/dist/proto/track_pb.d.ts.map +1 -1
- package/dist/proto/track_pb.js +98 -591
- package/dist/proto/track_pb.js.map +1 -1
- package/dist/proto/video_frame_pb.d.ts +357 -390
- package/dist/proto/video_frame_pb.d.ts.map +1 -1
- package/dist/proto/video_frame_pb.js +137 -790
- package/dist/proto/video_frame_pb.js.map +1 -1
- package/dist/room.d.ts +21 -7
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +134 -54
- package/dist/room.js.map +1 -1
- package/dist/rpc.d.ts +78 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +69 -0
- package/dist/rpc.js.map +1 -0
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +7 -3
- package/dist/track.js.map +1 -1
- package/dist/track_publication.d.ts.map +1 -1
- package/dist/track_publication.js +3 -2
- package/dist/track_publication.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/video_frame.d.ts +2 -2
- package/dist/video_frame.d.ts.map +1 -1
- package/dist/video_frame.js +47 -25
- package/dist/video_frame.js.map +1 -1
- package/dist/video_source.d.ts.map +1 -1
- package/dist/video_source.js +4 -3
- package/dist/video_source.js.map +1 -1
- package/dist/video_stream.d.ts +1 -1
- package/dist/video_stream.d.ts.map +1 -1
- package/dist/video_stream.js +7 -3
- package/dist/video_stream.js.map +1 -1
- package/package.json +13 -10
- package/src/audio_frame.ts +6 -6
- package/src/audio_resampler.ts +10 -9
- package/src/audio_source.ts +9 -8
- package/src/audio_stream.ts +7 -6
- package/src/e2ee.ts +34 -34
- package/src/ffi_client.ts +23 -10
- package/src/index.ts +61 -2
- package/src/napi/native.d.ts +1 -0
- package/src/nodejs.rs +6 -3
- package/src/participant.ts +207 -47
- package/src/proto/audio_frame_pb.ts +562 -1279
- package/src/proto/e2ee_pb.ts +365 -886
- package/src/proto/ffi_pb.ts +206 -405
- package/src/proto/handle_pb.ts +21 -35
- package/src/proto/participant_pb.ts +71 -112
- package/src/proto/room_pb.ts +1284 -2995
- package/src/proto/rpc_pb.ts +310 -0
- package/src/proto/stats_pb.ts +1028 -1947
- package/src/proto/track_pb.ts +287 -621
- package/src/proto/video_frame_pb.ts +445 -905
- package/src/room.ts +181 -75
- package/src/rpc.ts +121 -0
- package/src/track.ts +8 -7
- package/src/track_publication.ts +5 -4
- package/src/version.ts +1 -0
- package/src/video_frame.ts +54 -27
- package/src/video_source.ts +7 -6
- package/src/video_stream.ts +7 -6
- package/dist/utils.d.ts +0 -7
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -48
- package/dist/utils.js.map +0 -1
- package/src/utils.test.ts +0 -47
- package/src/utils.ts +0 -44
package/src/room.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { create } from '@bufbuild/protobuf';
|
|
4
5
|
import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
|
|
5
6
|
import EventEmitter from 'events';
|
|
6
7
|
import type { E2EEOptions } from './e2ee.js';
|
|
@@ -18,11 +19,12 @@ import type {
|
|
|
18
19
|
DataPacketKind,
|
|
19
20
|
DisconnectReason,
|
|
20
21
|
DisconnectResponse,
|
|
22
|
+
RoomOptions as FfiRoomOptions,
|
|
21
23
|
IceServer,
|
|
22
24
|
RoomInfo,
|
|
23
25
|
} from './proto/room_pb.js';
|
|
24
26
|
import {
|
|
25
|
-
|
|
27
|
+
ConnectRequestSchema,
|
|
26
28
|
ConnectionState,
|
|
27
29
|
ContinualGatheringPolicy,
|
|
28
30
|
IceTransportType,
|
|
@@ -53,18 +55,20 @@ export interface RoomOptions {
|
|
|
53
55
|
rtcConfig?: RtcConfiguration;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
export const defaultRoomOptions
|
|
58
|
+
export const defaultRoomOptions = {
|
|
57
59
|
autoSubscribe: true,
|
|
58
60
|
dynacast: false,
|
|
59
61
|
e2ee: undefined,
|
|
60
62
|
rtcConfig: undefined,
|
|
61
|
-
|
|
63
|
+
adaptiveStream: false,
|
|
64
|
+
joinRetries: 1,
|
|
65
|
+
} satisfies Omit<FfiRoomOptions, '$typeName'>;
|
|
62
66
|
|
|
63
67
|
export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>) {
|
|
64
|
-
private info
|
|
68
|
+
private info?: RoomInfo;
|
|
65
69
|
private ffiHandle?: FfiHandle;
|
|
66
70
|
|
|
67
|
-
e2eeManager
|
|
71
|
+
e2eeManager?: E2EEManager;
|
|
68
72
|
connectionState: ConnectionState = ConnectionState.CONN_DISCONNECTED;
|
|
69
73
|
|
|
70
74
|
remoteParticipants: Map<string, RemoteParticipant> = new Map();
|
|
@@ -74,12 +78,12 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
74
78
|
super();
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
get name(): string {
|
|
78
|
-
return this.info
|
|
81
|
+
get name(): string | undefined {
|
|
82
|
+
return this.info?.name;
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
get metadata(): string {
|
|
82
|
-
return this.info
|
|
85
|
+
get metadata(): string | undefined {
|
|
86
|
+
return this.info?.metadata;
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
get isConnected(): boolean {
|
|
@@ -87,28 +91,34 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
async getSid(): Promise<string> {
|
|
90
|
-
|
|
94
|
+
if (!this.isConnected) {
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
if (this.info && this.info.sid !== '') {
|
|
98
|
+
return this.info.sid;
|
|
99
|
+
}
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const handleRoomUpdate = (sid: string) => {
|
|
102
|
+
if (sid !== '') {
|
|
103
|
+
this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);
|
|
104
|
+
resolve(sid);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
this.on(RoomEvent.RoomSidChanged, handleRoomUpdate);
|
|
108
|
+
this.once(RoomEvent.Disconnected, () => {
|
|
109
|
+
this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);
|
|
110
|
+
reject('Room disconnected before room server id was available');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
91
113
|
}
|
|
92
114
|
|
|
93
115
|
async connect(url: string, token: string, opts?: RoomOptions) {
|
|
94
116
|
const options = { ...defaultRoomOptions, ...opts };
|
|
95
117
|
|
|
96
|
-
const req =
|
|
118
|
+
const req = create(ConnectRequestSchema, {
|
|
97
119
|
url: url,
|
|
98
120
|
token: token,
|
|
99
|
-
options
|
|
100
|
-
autoSubscribe: options.autoSubscribe,
|
|
101
|
-
dynacast: options.dynacast,
|
|
102
|
-
e2ee: {
|
|
103
|
-
encryptionType: options.e2ee?.encryptionType,
|
|
104
|
-
keyProviderOptions: {
|
|
105
|
-
failureTolerance: options.e2ee?.keyProviderOptions?.failureTolerance,
|
|
106
|
-
ratchetSalt: options.e2ee?.keyProviderOptions?.ratchetSalt,
|
|
107
|
-
ratchetWindowSize: options.e2ee?.keyProviderOptions?.ratchetWindowSize,
|
|
108
|
-
sharedKey: options.e2ee?.keyProviderOptions?.sharedKey,
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
},
|
|
121
|
+
options,
|
|
112
122
|
});
|
|
113
123
|
|
|
114
124
|
const res = FfiClient.instance.request<ConnectResponse>({
|
|
@@ -122,19 +132,23 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
122
132
|
return ev.message.case == 'connect' && ev.message.value.asyncId == res.asyncId;
|
|
123
133
|
});
|
|
124
134
|
|
|
125
|
-
if (cb.
|
|
126
|
-
throw new ConnectError(cb.error);
|
|
135
|
+
if (cb.message.case !== 'result') {
|
|
136
|
+
throw new ConnectError(cb.message.value ?? 'Unknown error');
|
|
127
137
|
}
|
|
128
138
|
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
const { room, localParticipant, participants } = cb.message.value;
|
|
140
|
+
|
|
141
|
+
this.ffiHandle = new FfiHandle(room!.handle!.id);
|
|
142
|
+
if (options.e2ee) {
|
|
143
|
+
this.e2eeManager = new E2EEManager(this.ffiHandle.handle, options.e2ee);
|
|
144
|
+
}
|
|
131
145
|
|
|
132
|
-
this.info =
|
|
146
|
+
this.info = room!.info;
|
|
133
147
|
this.connectionState = ConnectionState.CONN_CONNECTED;
|
|
134
|
-
this.localParticipant = new LocalParticipant(
|
|
148
|
+
this.localParticipant = new LocalParticipant(localParticipant!);
|
|
135
149
|
|
|
136
|
-
for (const pt of
|
|
137
|
-
const rp = this.createRemoteParticipant(pt.participant);
|
|
150
|
+
for (const pt of participants) {
|
|
151
|
+
const rp = this.createRemoteParticipant(pt.participant!);
|
|
138
152
|
|
|
139
153
|
for (const pub of pt.publications) {
|
|
140
154
|
const publication = new RemoteTrackPublication(pub);
|
|
@@ -146,7 +160,7 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
async disconnect() {
|
|
149
|
-
if (!this.isConnected) {
|
|
163
|
+
if (!this.isConnected || !this.ffiHandle) {
|
|
150
164
|
return;
|
|
151
165
|
}
|
|
152
166
|
|
|
@@ -164,7 +178,25 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
164
178
|
}
|
|
165
179
|
|
|
166
180
|
private onFfiEvent = (ffiEvent: FfiEvent) => {
|
|
167
|
-
if (
|
|
181
|
+
if (!this.localParticipant || !this.ffiHandle || !this.info) {
|
|
182
|
+
throw TypeError('cannot handle ffi events before connectCallback');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (ffiEvent.message.case == 'rpcMethodInvocation') {
|
|
186
|
+
if (
|
|
187
|
+
ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle
|
|
188
|
+
) {
|
|
189
|
+
this.localParticipant.handleRpcMethodInvocation(
|
|
190
|
+
ffiEvent.message.value.invocationId,
|
|
191
|
+
ffiEvent.message.value.method,
|
|
192
|
+
ffiEvent.message.value.requestId,
|
|
193
|
+
ffiEvent.message.value.callerIdentity,
|
|
194
|
+
ffiEvent.message.value.payload,
|
|
195
|
+
ffiEvent.message.value.responseTimeoutMs,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
} else if (
|
|
168
200
|
ffiEvent.message.case != 'roomEvent' ||
|
|
169
201
|
ffiEvent.message.value.roomHandle != this.ffiHandle.handle
|
|
170
202
|
) {
|
|
@@ -173,66 +205,88 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
173
205
|
|
|
174
206
|
const ev = ffiEvent.message.value.message;
|
|
175
207
|
if (ev.case == 'participantConnected') {
|
|
176
|
-
const participant = this.createRemoteParticipant(ev.value.info);
|
|
208
|
+
const participant = this.createRemoteParticipant(ev.value.info!);
|
|
177
209
|
this.remoteParticipants.set(participant.identity, participant);
|
|
178
210
|
this.emit(RoomEvent.ParticipantConnected, participant);
|
|
179
211
|
} else if (ev.case == 'participantDisconnected') {
|
|
180
212
|
const participant = this.remoteParticipants.get(ev.value.participantIdentity);
|
|
181
|
-
|
|
182
|
-
|
|
213
|
+
if (participant) {
|
|
214
|
+
this.remoteParticipants.delete(participant.identity);
|
|
215
|
+
this.emit(RoomEvent.ParticipantDisconnected, participant);
|
|
216
|
+
}
|
|
183
217
|
} else if (ev.case == 'localTrackPublished') {
|
|
184
218
|
const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
|
|
219
|
+
if (!publication) {
|
|
220
|
+
throw new TypeError('local track publication not found');
|
|
221
|
+
}
|
|
185
222
|
this.emit(RoomEvent.LocalTrackPublished, publication, this.localParticipant);
|
|
186
223
|
} else if (ev.case == 'localTrackUnpublished') {
|
|
187
224
|
const publication = this.localParticipant.trackPublications.get(ev.value.publicationSid);
|
|
188
225
|
this.localParticipant.trackPublications.delete(ev.value.publicationSid);
|
|
189
|
-
|
|
226
|
+
if (publication) {
|
|
227
|
+
this.emit(RoomEvent.LocalTrackUnpublished, publication, this.localParticipant);
|
|
228
|
+
}
|
|
190
229
|
} else if (ev.case == 'localTrackSubscribed') {
|
|
191
230
|
const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
|
|
231
|
+
if (!publication) {
|
|
232
|
+
throw new TypeError('local track publication not found');
|
|
233
|
+
}
|
|
192
234
|
publication.resolveFirstSubscription();
|
|
193
|
-
this.emit(RoomEvent.LocalTrackSubscribed, publication.track);
|
|
235
|
+
this.emit(RoomEvent.LocalTrackSubscribed, publication.track!);
|
|
194
236
|
} else if (ev.case == 'trackPublished') {
|
|
195
|
-
const participant = this.
|
|
196
|
-
const publication = new RemoteTrackPublication(ev.value.publication);
|
|
237
|
+
const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
|
|
238
|
+
const publication = new RemoteTrackPublication(ev.value.publication!);
|
|
197
239
|
participant.trackPublications.set(publication.sid, publication);
|
|
198
240
|
this.emit(RoomEvent.TrackPublished, publication, participant);
|
|
199
241
|
} else if (ev.case == 'trackUnpublished') {
|
|
200
|
-
const participant = this.
|
|
242
|
+
const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
|
|
201
243
|
const publication = participant.trackPublications.get(ev.value.publicationSid);
|
|
202
244
|
participant.trackPublications.delete(ev.value.publicationSid);
|
|
203
|
-
|
|
245
|
+
if (publication) {
|
|
246
|
+
this.emit(RoomEvent.TrackUnpublished, publication, participant);
|
|
247
|
+
}
|
|
204
248
|
} else if (ev.case == 'trackSubscribed') {
|
|
205
|
-
const ownedTrack = ev.value.track
|
|
206
|
-
const
|
|
207
|
-
const publication =
|
|
249
|
+
const ownedTrack = ev.value.track!;
|
|
250
|
+
const trackInfo = ownedTrack.info!;
|
|
251
|
+
const { participant, publication } = this.requirePublicationOfRemoteParticipant(
|
|
252
|
+
ev.value.participantIdentity,
|
|
253
|
+
trackInfo.sid,
|
|
254
|
+
);
|
|
208
255
|
publication.subscribed = true;
|
|
209
|
-
if (
|
|
256
|
+
if (trackInfo.kind == TrackKind.KIND_VIDEO) {
|
|
210
257
|
publication.track = new RemoteVideoTrack(ownedTrack);
|
|
211
|
-
} else if (
|
|
258
|
+
} else if (trackInfo.kind == TrackKind.KIND_AUDIO) {
|
|
212
259
|
publication.track = new RemoteAudioTrack(ownedTrack);
|
|
213
260
|
}
|
|
214
261
|
|
|
215
|
-
this.emit(RoomEvent.TrackSubscribed, publication.track
|
|
262
|
+
this.emit(RoomEvent.TrackSubscribed, publication.track!, publication, participant);
|
|
216
263
|
} else if (ev.case == 'trackUnsubscribed') {
|
|
217
|
-
const participant = this.
|
|
218
|
-
|
|
264
|
+
const { participant, publication } = this.requirePublicationOfRemoteParticipant(
|
|
265
|
+
ev.value.participantIdentity,
|
|
266
|
+
ev.value.trackSid,
|
|
267
|
+
);
|
|
268
|
+
const track = publication.track!;
|
|
219
269
|
publication.track = undefined;
|
|
220
270
|
publication.subscribed = false;
|
|
221
|
-
this.emit(RoomEvent.TrackUnsubscribed,
|
|
271
|
+
this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
|
|
222
272
|
} else if (ev.case == 'trackSubscriptionFailed') {
|
|
223
|
-
const participant = this.
|
|
273
|
+
const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
|
|
224
274
|
this.emit(RoomEvent.TrackSubscriptionFailed, ev.value.trackSid, participant, ev.value.error);
|
|
225
275
|
} else if (ev.case == 'trackMuted') {
|
|
226
|
-
const participant = this.
|
|
227
|
-
|
|
276
|
+
const { participant, publication } = this.requirePublicationOfParticipant(
|
|
277
|
+
ev.value.participantIdentity,
|
|
278
|
+
ev.value.trackSid,
|
|
279
|
+
);
|
|
228
280
|
publication.info.muted = true;
|
|
229
281
|
if (publication.track) {
|
|
230
282
|
publication.track.info.muted = true;
|
|
231
283
|
}
|
|
232
284
|
this.emit(RoomEvent.TrackMuted, publication, participant);
|
|
233
285
|
} else if (ev.case == 'trackUnmuted') {
|
|
234
|
-
const participant = this.
|
|
235
|
-
|
|
286
|
+
const { participant, publication } = this.requirePublicationOfParticipant(
|
|
287
|
+
ev.value.participantIdentity,
|
|
288
|
+
ev.value.trackSid,
|
|
289
|
+
);
|
|
236
290
|
publication.info.muted = false;
|
|
237
291
|
if (publication.track) {
|
|
238
292
|
publication.track.info.muted = false;
|
|
@@ -240,32 +294,45 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
240
294
|
this.emit(RoomEvent.TrackUnmuted, publication, participant);
|
|
241
295
|
} else if (ev.case == 'activeSpeakersChanged') {
|
|
242
296
|
const activeSpeakers = ev.value.participantIdentities.map((identity) =>
|
|
243
|
-
this.
|
|
297
|
+
this.requireParticipantByIdentity(identity),
|
|
244
298
|
);
|
|
245
299
|
this.emit(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
|
|
246
300
|
} else if (ev.case == 'roomMetadataChanged') {
|
|
247
301
|
this.info.metadata = ev.value.metadata;
|
|
248
302
|
this.emit(RoomEvent.RoomMetadataChanged, this.info.metadata);
|
|
249
303
|
} else if (ev.case == 'participantMetadataChanged') {
|
|
250
|
-
const participant = this.
|
|
304
|
+
const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
|
|
251
305
|
participant.info.metadata = ev.value.metadata;
|
|
252
306
|
this.emit(RoomEvent.ParticipantMetadataChanged, participant.metadata, participant);
|
|
253
307
|
} else if (ev.case == 'participantNameChanged') {
|
|
254
|
-
const participant = this.
|
|
308
|
+
const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
|
|
255
309
|
participant.info.name = ev.value.name;
|
|
256
310
|
this.emit(RoomEvent.ParticipantNameChanged, participant.name, participant);
|
|
257
311
|
} else if (ev.case == 'participantAttributesChanged') {
|
|
258
|
-
const participant = this.
|
|
259
|
-
participant.info.attributes = ev.value.attributes
|
|
312
|
+
const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
|
|
313
|
+
participant.info.attributes = ev.value.attributes.reduce(
|
|
314
|
+
(acc, value) => {
|
|
315
|
+
acc[value.key] = value.value;
|
|
316
|
+
return acc;
|
|
317
|
+
},
|
|
318
|
+
{} as Record<string, string>,
|
|
319
|
+
);
|
|
260
320
|
if (Object.keys(ev.value.changedAttributes).length > 0) {
|
|
261
|
-
|
|
321
|
+
const changedAttributes = ev.value.changedAttributes.reduce(
|
|
322
|
+
(acc, value) => {
|
|
323
|
+
acc[value.key] = value.value;
|
|
324
|
+
return acc;
|
|
325
|
+
},
|
|
326
|
+
{} as Record<string, string>,
|
|
327
|
+
);
|
|
328
|
+
this.emit(RoomEvent.ParticipantAttributesChanged, changedAttributes, participant);
|
|
262
329
|
}
|
|
263
330
|
} else if (ev.case == 'connectionQualityChanged') {
|
|
264
|
-
const participant = this.
|
|
331
|
+
const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
|
|
265
332
|
this.emit(RoomEvent.ConnectionQualityChanged, ev.value.quality, participant);
|
|
266
333
|
} else if (ev.case == 'chatMessage') {
|
|
267
334
|
const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity);
|
|
268
|
-
const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message
|
|
335
|
+
const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message!;
|
|
269
336
|
const message: ChatMessage = {
|
|
270
337
|
id,
|
|
271
338
|
message: messageText,
|
|
@@ -280,11 +347,9 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
280
347
|
const dataPacket = ev.value.value;
|
|
281
348
|
switch (dataPacket.case) {
|
|
282
349
|
case 'user':
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
);
|
|
287
|
-
new FfiHandle(dataPacket.value.data.handle.id).dispose();
|
|
350
|
+
const data = dataPacket.value.data!.data!;
|
|
351
|
+
const buffer = FfiClient.instance.copyBuffer(data.dataPtr, Number(data.dataLen));
|
|
352
|
+
new FfiHandle(dataPacket.value.data!.handle!.id).dispose();
|
|
288
353
|
this.emit(
|
|
289
354
|
RoomEvent.DataReceived,
|
|
290
355
|
buffer,
|
|
@@ -316,24 +381,62 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
316
381
|
this.emit(RoomEvent.Reconnecting);
|
|
317
382
|
} else if (ev.case == 'reconnected') {
|
|
318
383
|
this.emit(RoomEvent.Reconnected);
|
|
384
|
+
} else if (ev.case == 'roomSidChanged') {
|
|
385
|
+
this.emit(RoomEvent.RoomSidChanged, ev.value.sid);
|
|
319
386
|
}
|
|
320
387
|
};
|
|
321
388
|
|
|
322
|
-
private retrieveParticipantByIdentity(identity: string): Participant {
|
|
323
|
-
if (this.localParticipant
|
|
389
|
+
private retrieveParticipantByIdentity(identity: string): Participant | undefined {
|
|
390
|
+
if (this.localParticipant?.identity === identity) {
|
|
324
391
|
return this.localParticipant;
|
|
325
392
|
} else {
|
|
326
393
|
return this.remoteParticipants.get(identity);
|
|
327
394
|
}
|
|
328
395
|
}
|
|
329
396
|
|
|
397
|
+
private requireParticipantByIdentity(identity: string): Participant {
|
|
398
|
+
if (this.localParticipant?.identity === identity) {
|
|
399
|
+
return this.localParticipant;
|
|
400
|
+
} else if (this.remoteParticipants.has(identity)) {
|
|
401
|
+
return this.remoteParticipants.get(identity)!;
|
|
402
|
+
} else {
|
|
403
|
+
throw new TypeError(`participant ${identity} not found`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private requireRemoteParticipant(identity: string) {
|
|
408
|
+
const participant = this.remoteParticipants.get(identity);
|
|
409
|
+
if (!participant) {
|
|
410
|
+
throw new TypeError(`participant ${identity} not found`);
|
|
411
|
+
}
|
|
412
|
+
return participant;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private requirePublicationOfParticipant(identity: string, trackSid: string) {
|
|
416
|
+
const participant = this.requireParticipantByIdentity(identity);
|
|
417
|
+
const publication = participant.trackPublications.get(trackSid);
|
|
418
|
+
if (!publication) {
|
|
419
|
+
throw new TypeError(`publication ${trackSid} not found`);
|
|
420
|
+
}
|
|
421
|
+
return { participant, publication };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private requirePublicationOfRemoteParticipant(identity: string, trackSid: string) {
|
|
425
|
+
const participant = this.requireRemoteParticipant(identity);
|
|
426
|
+
const publication = participant.trackPublications.get(trackSid);
|
|
427
|
+
if (!publication) {
|
|
428
|
+
throw new TypeError(`publication ${trackSid} not found`);
|
|
429
|
+
}
|
|
430
|
+
return { participant, publication };
|
|
431
|
+
}
|
|
432
|
+
|
|
330
433
|
private createRemoteParticipant(ownedInfo: OwnedParticipant) {
|
|
331
|
-
if (this.remoteParticipants.has(ownedInfo.info
|
|
434
|
+
if (this.remoteParticipants.has(ownedInfo.info!.identity)) {
|
|
332
435
|
throw new Error('Participant already exists');
|
|
333
436
|
}
|
|
334
437
|
|
|
335
438
|
const participant = new RemoteParticipant(ownedInfo);
|
|
336
|
-
this.remoteParticipants.set(ownedInfo.info
|
|
439
|
+
this.remoteParticipants.set(ownedInfo.info!.identity, participant);
|
|
337
440
|
return participant;
|
|
338
441
|
}
|
|
339
442
|
}
|
|
@@ -374,6 +477,7 @@ export type RoomCallbacks = {
|
|
|
374
477
|
trackUnmuted: (publication: TrackPublication, participant: Participant) => void;
|
|
375
478
|
activeSpeakersChanged: (speakers: Participant[]) => void;
|
|
376
479
|
roomMetadataChanged: (metadata: string) => void;
|
|
480
|
+
roomInfoUpdated: (info: RoomInfo) => void;
|
|
377
481
|
participantMetadataChanged: (metadata: string | undefined, participant: Participant) => void;
|
|
378
482
|
participantNameChanged: (name: string, participant: Participant) => void;
|
|
379
483
|
participantAttributesChanged: (
|
|
@@ -388,13 +492,14 @@ export type RoomCallbacks = {
|
|
|
388
492
|
topic?: string,
|
|
389
493
|
) => void;
|
|
390
494
|
chatMessage: (message: ChatMessage, participant?: Participant) => void;
|
|
391
|
-
dtmfReceived: (code: number, digit: string, participant
|
|
495
|
+
dtmfReceived: (code: number, digit: string, participant?: RemoteParticipant) => void;
|
|
392
496
|
encryptionError: (error: Error) => void;
|
|
393
497
|
connectionStateChanged: (state: ConnectionState) => void;
|
|
394
498
|
connected: () => void;
|
|
395
499
|
disconnected: (reason: DisconnectReason) => void;
|
|
396
500
|
reconnecting: () => void;
|
|
397
501
|
reconnected: () => void;
|
|
502
|
+
roomSidChanged: (sid: string) => void;
|
|
398
503
|
};
|
|
399
504
|
|
|
400
505
|
export enum RoomEvent {
|
|
@@ -412,6 +517,7 @@ export enum RoomEvent {
|
|
|
412
517
|
TrackUnmuted = 'trackUnmuted',
|
|
413
518
|
ActiveSpeakersChanged = 'activeSpeakersChanged',
|
|
414
519
|
RoomMetadataChanged = 'roomMetadataChanged',
|
|
520
|
+
RoomSidChanged = 'roomSidChanged',
|
|
415
521
|
ParticipantMetadataChanged = 'participantMetadataChanged',
|
|
416
522
|
ParticipantNameChanged = 'participantNameChanged',
|
|
417
523
|
ParticipantAttributesChanged = 'participantAttributesChanged',
|
package/src/rpc.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import type { RpcError as RpcError_Proto } from './proto/rpc_pb.js';
|
|
5
|
+
|
|
6
|
+
/** Parameters for initiating an RPC call */
|
|
7
|
+
export interface PerformRpcParams {
|
|
8
|
+
/** The `identity` of the destination participant */
|
|
9
|
+
destinationIdentity: string;
|
|
10
|
+
/** The method name to call */
|
|
11
|
+
method: string;
|
|
12
|
+
/** The method payload */
|
|
13
|
+
payload: string;
|
|
14
|
+
/** Timeout for receiving a response after initial connection (milliseconds). Default: 10000 */
|
|
15
|
+
responseTimeout?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Data passed to method handler for incoming RPC invocations
|
|
20
|
+
*/
|
|
21
|
+
export interface RpcInvocationData {
|
|
22
|
+
/**
|
|
23
|
+
* The unique request ID. Will match at both sides of the call, useful for debugging or logging.
|
|
24
|
+
*/
|
|
25
|
+
requestId: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The unique participant identity of the caller.
|
|
29
|
+
*/
|
|
30
|
+
callerIdentity: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The payload of the request. User-definable format, typically JSON.
|
|
34
|
+
*/
|
|
35
|
+
payload: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The maximum time the caller will wait for a response.
|
|
39
|
+
*/
|
|
40
|
+
responseTimeout: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Specialized error handling for RPC methods.
|
|
45
|
+
*
|
|
46
|
+
* Instances of this type, when thrown in a method handler, will have their `message`
|
|
47
|
+
* serialized and sent across the wire. The caller will receive an equivalent error on the other side.
|
|
48
|
+
*
|
|
49
|
+
* Built-in types are included but developers may use any string, with a max length of 256 bytes.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
export class RpcError extends Error {
|
|
53
|
+
code: typeof RpcError.ErrorCode | number;
|
|
54
|
+
data?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates an error object with the given code and message, plus an optional data payload.
|
|
58
|
+
*
|
|
59
|
+
* If thrown in an RPC method handler, the error will be sent back to the caller.
|
|
60
|
+
*
|
|
61
|
+
* Error codes 1001-1999 are reserved for built-in errors (see RpcError.ErrorCode for their meanings).
|
|
62
|
+
*/
|
|
63
|
+
constructor(code: number, message: string, data?: string) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.code = code;
|
|
66
|
+
this.message = message;
|
|
67
|
+
this.data = data;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static fromProto(proto: RpcError_Proto) {
|
|
71
|
+
return new RpcError(proto.code, proto.message, proto.data);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
toProto() {
|
|
75
|
+
return {
|
|
76
|
+
code: this.code as number,
|
|
77
|
+
message: this.message,
|
|
78
|
+
data: this.data,
|
|
79
|
+
} as RpcError_Proto;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static ErrorCode = {
|
|
83
|
+
APPLICATION_ERROR: 1500,
|
|
84
|
+
CONNECTION_TIMEOUT: 1501,
|
|
85
|
+
RESPONSE_TIMEOUT: 1502,
|
|
86
|
+
RECIPIENT_DISCONNECTED: 1503,
|
|
87
|
+
RESPONSE_PAYLOAD_TOO_LARGE: 1504,
|
|
88
|
+
SEND_FAILED: 1505,
|
|
89
|
+
|
|
90
|
+
UNSUPPORTED_METHOD: 1400,
|
|
91
|
+
RECIPIENT_NOT_FOUND: 1401,
|
|
92
|
+
REQUEST_PAYLOAD_TOO_LARGE: 1402,
|
|
93
|
+
UNSUPPORTED_SERVER: 1403,
|
|
94
|
+
} as const;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
static ErrorMessage: Record<keyof typeof RpcError.ErrorCode, string> = {
|
|
100
|
+
APPLICATION_ERROR: 'Application error in method handler',
|
|
101
|
+
CONNECTION_TIMEOUT: 'Connection timeout',
|
|
102
|
+
RESPONSE_TIMEOUT: 'Response timeout',
|
|
103
|
+
RECIPIENT_DISCONNECTED: 'Recipient disconnected',
|
|
104
|
+
RESPONSE_PAYLOAD_TOO_LARGE: 'Response payload too large',
|
|
105
|
+
SEND_FAILED: 'Failed to send',
|
|
106
|
+
|
|
107
|
+
UNSUPPORTED_METHOD: 'Method not supported at destination',
|
|
108
|
+
RECIPIENT_NOT_FOUND: 'Recipient not found',
|
|
109
|
+
REQUEST_PAYLOAD_TOO_LARGE: 'Request payload too large',
|
|
110
|
+
UNSUPPORTED_SERVER: 'RPC not supported by server',
|
|
111
|
+
} as const;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates an error object from the code, with an auto-populated message.
|
|
115
|
+
*
|
|
116
|
+
* @internal
|
|
117
|
+
*/
|
|
118
|
+
static builtIn(key: keyof typeof RpcError.ErrorCode, data?: string): RpcError {
|
|
119
|
+
return new RpcError(RpcError.ErrorCode[key], RpcError.ErrorMessage[key], data);
|
|
120
|
+
}
|
|
121
|
+
}
|
package/src/track.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { create } from '@bufbuild/protobuf';
|
|
4
5
|
import type { AudioSource } from './audio_source.js';
|
|
5
6
|
import { FfiClient, FfiHandle } from './ffi_client.js';
|
|
6
7
|
import type {
|
|
@@ -11,7 +12,7 @@ import type {
|
|
|
11
12
|
TrackInfo,
|
|
12
13
|
TrackKind,
|
|
13
14
|
} from './proto/track_pb.js';
|
|
14
|
-
import {
|
|
15
|
+
import { CreateAudioTrackRequestSchema, CreateVideoTrackRequestSchema } from './proto/track_pb.js';
|
|
15
16
|
import type { VideoSource } from './video_source.js';
|
|
16
17
|
|
|
17
18
|
export abstract class Track {
|
|
@@ -22,8 +23,8 @@ export abstract class Track {
|
|
|
22
23
|
ffi_handle: FfiHandle;
|
|
23
24
|
|
|
24
25
|
constructor(owned: OwnedTrack) {
|
|
25
|
-
this.info = owned.info
|
|
26
|
-
this.ffi_handle = new FfiHandle(owned.handle
|
|
26
|
+
this.info = owned.info!;
|
|
27
|
+
this.ffi_handle = new FfiHandle(owned.handle!.id);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
get sid(): string {
|
|
@@ -53,7 +54,7 @@ export class LocalAudioTrack extends Track {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
static createAudioTrack(name: string, source: AudioSource): LocalAudioTrack {
|
|
56
|
-
const req =
|
|
57
|
+
const req = create(CreateAudioTrackRequestSchema, {
|
|
57
58
|
name: name,
|
|
58
59
|
sourceHandle: source.ffiHandle.handle,
|
|
59
60
|
});
|
|
@@ -62,7 +63,7 @@ export class LocalAudioTrack extends Track {
|
|
|
62
63
|
message: { case: 'createAudioTrack', value: req },
|
|
63
64
|
});
|
|
64
65
|
|
|
65
|
-
return new LocalAudioTrack(res.track);
|
|
66
|
+
return new LocalAudioTrack(res.track!);
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -72,7 +73,7 @@ export class LocalVideoTrack extends Track {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
static createVideoTrack(name: string, source: VideoSource): LocalVideoTrack {
|
|
75
|
-
const req =
|
|
76
|
+
const req = create(CreateVideoTrackRequestSchema, {
|
|
76
77
|
name: name,
|
|
77
78
|
sourceHandle: source.ffiHandle.handle,
|
|
78
79
|
});
|
|
@@ -81,7 +82,7 @@ export class LocalVideoTrack extends Track {
|
|
|
81
82
|
message: { case: 'createVideoTrack', value: req },
|
|
82
83
|
});
|
|
83
84
|
|
|
84
|
-
return new LocalVideoTrack(res.track);
|
|
85
|
+
return new LocalVideoTrack(res.track!);
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
|
package/src/track_publication.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { create } from '@bufbuild/protobuf';
|
|
4
5
|
import { FfiClient } from './ffi_client.js';
|
|
5
6
|
import { FfiHandle } from './napi/native.js';
|
|
6
7
|
import type { EncryptionType } from './proto/e2ee_pb.js';
|
|
7
8
|
import type { SetSubscribedResponse } from './proto/room_pb.js';
|
|
8
|
-
import {
|
|
9
|
+
import { SetSubscribedRequestSchema } from './proto/room_pb.js';
|
|
9
10
|
import type {
|
|
10
11
|
OwnedTrackPublication,
|
|
11
12
|
TrackKind,
|
|
@@ -23,8 +24,8 @@ export abstract class TrackPublication {
|
|
|
23
24
|
track?: Track;
|
|
24
25
|
|
|
25
26
|
constructor(ownedInfo: OwnedTrackPublication) {
|
|
26
|
-
this.info = ownedInfo.info
|
|
27
|
-
this.ffiHandle = new FfiHandle(ownedInfo
|
|
27
|
+
this.info = ownedInfo.info!;
|
|
28
|
+
this.ffiHandle = new FfiHandle(ownedInfo!.handle!.id);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
get sid(): string {
|
|
@@ -100,7 +101,7 @@ export class RemoteTrackPublication extends TrackPublication {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
setSubscribed(subscribed: boolean) {
|
|
103
|
-
const req =
|
|
104
|
+
const req = create(SetSubscribedRequestSchema, {
|
|
104
105
|
subscribe: subscribed,
|
|
105
106
|
publicationHandle: this.ffiHandle.handle,
|
|
106
107
|
});
|
package/src/version.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SDK_VERSION = "0.11.1";
|