livekit-client 2.18.10 → 2.19.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/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 +749 -438
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.pt.worker.js.map +1 -1
- package/dist/livekit-client.pt.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/room/RTCEngine.d.ts +0 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +4 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +5 -13
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +5 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/rpc/client/RpcClientManager.d.ts +39 -0
- package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -0
- package/dist/src/room/rpc/client/events.d.ts +8 -0
- package/dist/src/room/rpc/client/events.d.ts.map +1 -0
- package/dist/src/room/rpc/index.d.ts +6 -0
- package/dist/src/room/rpc/index.d.ts.map +1 -0
- package/dist/src/room/rpc/server/RpcServerManager.d.ts +44 -0
- package/dist/src/room/rpc/server/RpcServerManager.d.ts.map +1 -0
- package/dist/src/room/rpc/server/events.d.ts +8 -0
- package/dist/src/room/rpc/server/events.d.ts.map +1 -0
- package/dist/src/room/{rpc.d.ts → rpc/utils.d.ts} +34 -4
- package/dist/src/room/rpc/utils.d.ts.map +1 -0
- package/dist/src/room/utils.d.ts +1 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/version.d.ts +8 -0
- package/dist/src/version.d.ts.map +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +0 -3
- package/dist/ts4.2/room/Room.d.ts +4 -2
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +5 -13
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +5 -1
- package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -0
- package/dist/ts4.2/room/rpc/client/events.d.ts +8 -0
- package/dist/ts4.2/room/rpc/index.d.ts +7 -0
- package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -0
- package/dist/ts4.2/room/rpc/server/events.d.ts +8 -0
- package/dist/ts4.2/room/{rpc.d.ts → rpc/utils.d.ts} +34 -4
- package/dist/ts4.2/room/utils.d.ts +1 -0
- package/dist/ts4.2/version.d.ts +8 -0
- package/package.json +4 -2
- package/src/api/SignalClient.ts +1 -0
- package/src/room/RTCEngine.ts +4 -30
- package/src/room/Room.test.ts +99 -1
- package/src/room/Room.ts +107 -88
- package/src/room/participant/LocalParticipant.ts +16 -180
- package/src/room/participant/RemoteParticipant.ts +9 -0
- package/src/room/rpc/client/RpcClientManager.test.ts +430 -0
- package/src/room/rpc/client/RpcClientManager.ts +269 -0
- package/src/room/rpc/client/events.ts +9 -0
- package/src/room/rpc/index.ts +14 -0
- package/src/room/rpc/server/RpcServerManager.test.ts +471 -0
- package/src/room/rpc/server/RpcServerManager.ts +293 -0
- package/src/room/rpc/server/events.ts +9 -0
- package/src/room/{rpc.ts → rpc/utils.ts} +49 -8
- package/src/room/utils.ts +7 -1
- package/src/version.ts +10 -0
- package/dist/src/room/rpc.d.ts.map +0 -1
- package/src/room/rpc.test.ts +0 -301
package/src/room/Room.ts
CHANGED
|
@@ -48,6 +48,7 @@ import { PacketTrailerManager } from '../packetTrailer/PacketTrailerManager';
|
|
|
48
48
|
import { isPacketTrailerSupported } from '../packetTrailer/utils';
|
|
49
49
|
import TypedPromise from '../utils/TypedPromise';
|
|
50
50
|
import { getBrowser } from '../utils/browserParser';
|
|
51
|
+
import { CLIENT_PROTOCOL_DEFAULT } from '../version';
|
|
51
52
|
import { BackOffStrategy } from './BackOffStrategy';
|
|
52
53
|
import DeviceManager from './DeviceManager';
|
|
53
54
|
import RTCEngine, { DataChannelKind, type RegionStrategy } from './RTCEngine';
|
|
@@ -81,7 +82,14 @@ import LocalParticipant from './participant/LocalParticipant';
|
|
|
81
82
|
import Participant from './participant/Participant';
|
|
82
83
|
import { type ConnectionQuality, ParticipantKind } from './participant/Participant';
|
|
83
84
|
import RemoteParticipant from './participant/RemoteParticipant';
|
|
84
|
-
import {
|
|
85
|
+
import {
|
|
86
|
+
RPC_REQUEST_DATA_STREAM_TOPIC,
|
|
87
|
+
RPC_RESPONSE_DATA_STREAM_TOPIC,
|
|
88
|
+
RpcClientManager,
|
|
89
|
+
RpcError,
|
|
90
|
+
type RpcInvocationData,
|
|
91
|
+
RpcServerManager,
|
|
92
|
+
} from './rpc';
|
|
85
93
|
import CriticalTimers from './timers';
|
|
86
94
|
import LocalAudioTrack from './track/LocalAudioTrack';
|
|
87
95
|
import type LocalTrack from './track/LocalTrack';
|
|
@@ -221,7 +229,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
221
229
|
|
|
222
230
|
private outgoingDataTrackManager: OutgoingDataTrackManager;
|
|
223
231
|
|
|
224
|
-
private
|
|
232
|
+
private rpcClientManager: RpcClientManager;
|
|
233
|
+
|
|
234
|
+
private rpcServerManager: RpcServerManager;
|
|
225
235
|
|
|
226
236
|
get hasE2EESetup(): boolean {
|
|
227
237
|
return this.e2eeManager !== undefined;
|
|
@@ -301,15 +311,36 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
301
311
|
.finally(() => this.outgoingDataTrackManager.handlePacketSendComplete(handle));
|
|
302
312
|
});
|
|
303
313
|
|
|
314
|
+
this.registerRpcDataStreamHandler();
|
|
315
|
+
|
|
316
|
+
this.rpcClientManager = new RpcClientManager(
|
|
317
|
+
this.log,
|
|
318
|
+
this.outgoingDataStreamManager,
|
|
319
|
+
this.getRemoteParticipantClientProtocol,
|
|
320
|
+
() => this.engine.latestJoinResponse?.serverInfo?.version,
|
|
321
|
+
);
|
|
322
|
+
this.rpcClientManager.on('sendDataPacket', ({ packet }) => {
|
|
323
|
+
this.engine?.sendDataPacket(packet, DataChannelKind.RELIABLE);
|
|
324
|
+
});
|
|
325
|
+
this.rpcServerManager = new RpcServerManager(
|
|
326
|
+
this.log,
|
|
327
|
+
this.outgoingDataStreamManager,
|
|
328
|
+
this.getRemoteParticipantClientProtocol,
|
|
329
|
+
);
|
|
330
|
+
this.rpcServerManager.on('sendDataPacket', ({ packet }) => {
|
|
331
|
+
this.engine?.sendDataPacket(packet, DataChannelKind.RELIABLE);
|
|
332
|
+
});
|
|
333
|
+
|
|
304
334
|
this.disconnectLock = new Mutex();
|
|
305
335
|
this.localParticipant = new LocalParticipant(
|
|
306
336
|
'',
|
|
307
337
|
'',
|
|
308
338
|
this.engine,
|
|
309
339
|
this.options,
|
|
310
|
-
this.rpcHandlers,
|
|
311
340
|
this.outgoingDataStreamManager,
|
|
312
341
|
this.outgoingDataTrackManager,
|
|
342
|
+
this.rpcClientManager,
|
|
343
|
+
this.rpcServerManager,
|
|
313
344
|
);
|
|
314
345
|
|
|
315
346
|
this.setupPacketTrailer();
|
|
@@ -342,18 +373,34 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
342
373
|
}
|
|
343
374
|
|
|
344
375
|
if (isWeb()) {
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
// in order to catch device changes prior to room connection we need to register the event in the constructor
|
|
348
|
-
navigator.mediaDevices?.addEventListener?.('devicechange', this.handleDeviceChange, {
|
|
349
|
-
signal: abortController.signal,
|
|
350
|
-
});
|
|
376
|
+
const cleanupController = new AbortController();
|
|
377
|
+
let onDeviceChange: () => void;
|
|
351
378
|
|
|
352
379
|
if (Room.cleanupRegistry) {
|
|
380
|
+
// Wrap the listener in a WeakRef closure so navigator.mediaDevices does not
|
|
381
|
+
// strongly retain the Room. When the user drops their Room ref, the
|
|
382
|
+
// FinalizationRegistry callback aborts the controller and removes the listener.
|
|
383
|
+
const roomRef = new WeakRef(this);
|
|
384
|
+
onDeviceChange = () => {
|
|
385
|
+
const self = roomRef.deref();
|
|
386
|
+
if (!self) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
self.handleDeviceChange();
|
|
390
|
+
};
|
|
353
391
|
Room.cleanupRegistry.register(this, () => {
|
|
354
|
-
|
|
392
|
+
cleanupController.abort();
|
|
355
393
|
});
|
|
394
|
+
} else {
|
|
395
|
+
// Legacy browsers without WeakRef/FinalizationRegistry: fall back to a
|
|
396
|
+
// direct listener (matches pre-#1944 behavior).
|
|
397
|
+
onDeviceChange = this.handleDeviceChange;
|
|
356
398
|
}
|
|
399
|
+
|
|
400
|
+
// in order to catch device changes prior to room connection we need to register the event in the constructor
|
|
401
|
+
navigator.mediaDevices?.addEventListener?.('devicechange', onDeviceChange, {
|
|
402
|
+
signal: cleanupController.signal,
|
|
403
|
+
});
|
|
357
404
|
}
|
|
358
405
|
}
|
|
359
406
|
|
|
@@ -400,12 +447,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
400
447
|
* Other errors thrown in your handler will not be transmitted as-is, and will instead arrive to the caller as `1500` ("Application Error").
|
|
401
448
|
*/
|
|
402
449
|
registerRpcMethod(method: string, handler: (data: RpcInvocationData) => Promise<string>) {
|
|
403
|
-
|
|
404
|
-
throw Error(
|
|
405
|
-
`RPC handler already registered for method ${method}, unregisterRpcMethod before trying to register again`,
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
this.rpcHandlers.set(method, handler);
|
|
450
|
+
this.rpcServerManager.registerRpcMethod(method, handler);
|
|
409
451
|
}
|
|
410
452
|
|
|
411
453
|
/**
|
|
@@ -414,7 +456,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
414
456
|
* @param method - The name of the RPC method to unregister
|
|
415
457
|
*/
|
|
416
458
|
unregisterRpcMethod(method: string) {
|
|
417
|
-
this.
|
|
459
|
+
this.rpcServerManager.unregisterRpcMethod(method);
|
|
418
460
|
}
|
|
419
461
|
|
|
420
462
|
/**
|
|
@@ -729,6 +771,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
729
771
|
|
|
730
772
|
static cleanupRegistry =
|
|
731
773
|
typeof FinalizationRegistry !== 'undefined' &&
|
|
774
|
+
typeof WeakRef !== 'undefined' &&
|
|
732
775
|
new FinalizationRegistry((cleanup: () => void) => {
|
|
733
776
|
cleanup();
|
|
734
777
|
});
|
|
@@ -1873,7 +1916,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1873
1916
|
});
|
|
1874
1917
|
this.emit(RoomEvent.ParticipantDisconnected, participant);
|
|
1875
1918
|
participant.setDisconnected();
|
|
1876
|
-
this.
|
|
1919
|
+
this.rpcClientManager.handleParticipantDisconnected(participant.identity);
|
|
1877
1920
|
}
|
|
1878
1921
|
|
|
1879
1922
|
// updates are sent only when there's a change to speaker ordering
|
|
@@ -2017,14 +2060,31 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2017
2060
|
this.handleDataStream(packet, encryptionType);
|
|
2018
2061
|
} else if (packet.value.case === 'rpcRequest') {
|
|
2019
2062
|
const rpc = packet.value.value;
|
|
2020
|
-
this.handleIncomingRpcRequest(
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2063
|
+
this.rpcServerManager.handleIncomingRpcRequest(packet.participantIdentity, rpc);
|
|
2064
|
+
} else if (packet.value.case === 'rpcResponse') {
|
|
2065
|
+
const rpcResponse = packet.value.value;
|
|
2066
|
+
switch (rpcResponse.value.case) {
|
|
2067
|
+
case 'payload':
|
|
2068
|
+
this.rpcClientManager.handleIncomingRpcResponseSuccess(
|
|
2069
|
+
rpcResponse.requestId,
|
|
2070
|
+
rpcResponse.value.value,
|
|
2071
|
+
);
|
|
2072
|
+
break;
|
|
2073
|
+
case 'error':
|
|
2074
|
+
this.rpcClientManager.handleIncomingRpcResponseFailure(
|
|
2075
|
+
rpcResponse.requestId,
|
|
2076
|
+
RpcError.fromProto(rpcResponse.value.value),
|
|
2077
|
+
);
|
|
2078
|
+
break;
|
|
2079
|
+
default:
|
|
2080
|
+
this.log.warn(
|
|
2081
|
+
`Unknown rpcResponse.value.case: ${rpcResponse.value.case}`,
|
|
2082
|
+
this.logContext,
|
|
2083
|
+
);
|
|
2084
|
+
break;
|
|
2085
|
+
}
|
|
2086
|
+
} else if (packet.value.case === 'rpcAck') {
|
|
2087
|
+
this.rpcClientManager.handleIncomingRpcAck(packet.value.value.requestId);
|
|
2028
2088
|
}
|
|
2029
2089
|
};
|
|
2030
2090
|
|
|
@@ -2093,68 +2153,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2093
2153
|
this.incomingDataStreamManager.handleDataStreamPacket(packet, encryptionType);
|
|
2094
2154
|
};
|
|
2095
2155
|
|
|
2096
|
-
private async handleIncomingRpcRequest(
|
|
2097
|
-
callerIdentity: string,
|
|
2098
|
-
requestId: string,
|
|
2099
|
-
method: string,
|
|
2100
|
-
payload: string,
|
|
2101
|
-
responseTimeout: number,
|
|
2102
|
-
version: number,
|
|
2103
|
-
) {
|
|
2104
|
-
await this.engine.publishRpcAck(callerIdentity, requestId);
|
|
2105
|
-
|
|
2106
|
-
if (version !== 1) {
|
|
2107
|
-
await this.engine.publishRpcResponse(
|
|
2108
|
-
callerIdentity,
|
|
2109
|
-
requestId,
|
|
2110
|
-
null,
|
|
2111
|
-
RpcError.builtIn('UNSUPPORTED_VERSION'),
|
|
2112
|
-
);
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
const handler = this.rpcHandlers.get(method);
|
|
2117
|
-
|
|
2118
|
-
if (!handler) {
|
|
2119
|
-
await this.engine.publishRpcResponse(
|
|
2120
|
-
callerIdentity,
|
|
2121
|
-
requestId,
|
|
2122
|
-
null,
|
|
2123
|
-
RpcError.builtIn('UNSUPPORTED_METHOD'),
|
|
2124
|
-
);
|
|
2125
|
-
return;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
let responseError: RpcError | null = null;
|
|
2129
|
-
let responsePayload: string | null = null;
|
|
2130
|
-
|
|
2131
|
-
try {
|
|
2132
|
-
const response = await handler({
|
|
2133
|
-
requestId,
|
|
2134
|
-
callerIdentity,
|
|
2135
|
-
payload,
|
|
2136
|
-
responseTimeout,
|
|
2137
|
-
});
|
|
2138
|
-
if (byteLength(response) > MAX_PAYLOAD_BYTES) {
|
|
2139
|
-
responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE');
|
|
2140
|
-
this.log.warn(`RPC Response payload too large for ${method}`);
|
|
2141
|
-
} else {
|
|
2142
|
-
responsePayload = response;
|
|
2143
|
-
}
|
|
2144
|
-
} catch (error) {
|
|
2145
|
-
if (error instanceof RpcError) {
|
|
2146
|
-
responseError = error;
|
|
2147
|
-
} else {
|
|
2148
|
-
this.log.warn(
|
|
2149
|
-
`Uncaught error returned by RPC handler for ${method}. Returning APPLICATION_ERROR instead.`,
|
|
2150
|
-
error,
|
|
2151
|
-
);
|
|
2152
|
-
responseError = RpcError.builtIn('APPLICATION_ERROR');
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
await this.engine.publishRpcResponse(callerIdentity, requestId, responsePayload, responseError);
|
|
2156
|
-
}
|
|
2157
|
-
|
|
2158
2156
|
bufferedSegments: Map<string, TranscriptionSegmentModel> = new Map();
|
|
2159
2157
|
|
|
2160
2158
|
private handleAudioPlaybackStarted = () => {
|
|
@@ -2500,6 +2498,27 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2500
2498
|
}
|
|
2501
2499
|
}
|
|
2502
2500
|
|
|
2501
|
+
private getRemoteParticipantClientProtocol = (identity: Participant['identity']) => {
|
|
2502
|
+
return this.remoteParticipants.get(identity)?.clientProtocol ?? CLIENT_PROTOCOL_DEFAULT;
|
|
2503
|
+
};
|
|
2504
|
+
|
|
2505
|
+
private registerRpcDataStreamHandler() {
|
|
2506
|
+
this.incomingDataStreamManager.registerTextStreamHandler(
|
|
2507
|
+
RPC_REQUEST_DATA_STREAM_TOPIC,
|
|
2508
|
+
async (reader, { identity }) => {
|
|
2509
|
+
const attributes = reader.info.attributes ?? {};
|
|
2510
|
+
await this.rpcServerManager.handleIncomingDataStream(reader, identity, attributes);
|
|
2511
|
+
},
|
|
2512
|
+
);
|
|
2513
|
+
this.incomingDataStreamManager.registerTextStreamHandler(
|
|
2514
|
+
RPC_RESPONSE_DATA_STREAM_TOPIC,
|
|
2515
|
+
async (reader, { identity }) => {
|
|
2516
|
+
const attributes = reader.info.attributes ?? {};
|
|
2517
|
+
await this.rpcClientManager.handleIncomingDataStream(reader, identity, attributes);
|
|
2518
|
+
},
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2503
2522
|
private registerConnectionReconcile() {
|
|
2504
2523
|
this.clearConnectionReconcile();
|
|
2505
2524
|
let consecutiveFailures = 0;
|
|
@@ -13,9 +13,6 @@ import {
|
|
|
13
13
|
ParticipantInfo,
|
|
14
14
|
RequestResponse,
|
|
15
15
|
RequestResponse_Reason,
|
|
16
|
-
RpcAck,
|
|
17
|
-
RpcRequest,
|
|
18
|
-
RpcResponse,
|
|
19
16
|
SimulcastCodec,
|
|
20
17
|
SipDTMF,
|
|
21
18
|
SubscribedQualityUpdate,
|
|
@@ -54,11 +51,11 @@ import {
|
|
|
54
51
|
} from '../errors';
|
|
55
52
|
import { EngineEvent, ParticipantEvent, TrackEvent } from '../events';
|
|
56
53
|
import {
|
|
57
|
-
MAX_PAYLOAD_BYTES,
|
|
58
54
|
type PerformRpcParams,
|
|
55
|
+
RpcClientManager,
|
|
59
56
|
RpcError,
|
|
60
57
|
type RpcInvocationData,
|
|
61
|
-
|
|
58
|
+
RpcServerManager,
|
|
62
59
|
} from '../rpc';
|
|
63
60
|
import LocalAudioTrack from '../track/LocalAudioTrack';
|
|
64
61
|
import LocalTrack from '../track/LocalTrack';
|
|
@@ -94,7 +91,6 @@ import {
|
|
|
94
91
|
} from '../types';
|
|
95
92
|
import {
|
|
96
93
|
Future,
|
|
97
|
-
compareVersions,
|
|
98
94
|
isAudioTrack,
|
|
99
95
|
isE2EESimulcastSupported,
|
|
100
96
|
isFireFox,
|
|
@@ -162,12 +158,14 @@ export default class LocalParticipant extends Participant {
|
|
|
162
158
|
|
|
163
159
|
private firstActiveAgent?: RemoteParticipant;
|
|
164
160
|
|
|
165
|
-
private rpcHandlers: Map<string, (data: RpcInvocationData) => Promise<string>>;
|
|
166
|
-
|
|
167
161
|
private roomOutgoingDataStreamManager: OutgoingDataStreamManager;
|
|
168
162
|
|
|
169
163
|
private roomOutgoingDataTrackManager: OutgoingDataTrackManager;
|
|
170
164
|
|
|
165
|
+
private rpcClientManager: RpcClientManager;
|
|
166
|
+
|
|
167
|
+
private rpcServerManager: RpcServerManager;
|
|
168
|
+
|
|
171
169
|
private pendingSignalRequests: Map<
|
|
172
170
|
number,
|
|
173
171
|
{
|
|
@@ -179,25 +177,16 @@ export default class LocalParticipant extends Participant {
|
|
|
179
177
|
|
|
180
178
|
private enabledPublishVideoCodecs: Codec[] = [];
|
|
181
179
|
|
|
182
|
-
private pendingAcks = new Map<string, { resolve: () => void; participantIdentity: string }>();
|
|
183
|
-
|
|
184
|
-
private pendingResponses = new Map<
|
|
185
|
-
string,
|
|
186
|
-
{
|
|
187
|
-
resolve: (payload: string | null, error: RpcError | null) => void;
|
|
188
|
-
participantIdentity: string;
|
|
189
|
-
}
|
|
190
|
-
>();
|
|
191
|
-
|
|
192
180
|
/** @internal */
|
|
193
181
|
constructor(
|
|
194
182
|
sid: string,
|
|
195
183
|
identity: string,
|
|
196
184
|
engine: RTCEngine,
|
|
197
185
|
options: InternalRoomOptions,
|
|
198
|
-
roomRpcHandlers: Map<string, (data: RpcInvocationData) => Promise<string>>,
|
|
199
186
|
roomOutgoingDataStreamManager: OutgoingDataStreamManager,
|
|
200
187
|
roomOutgoingDataTrackManager: OutgoingDataTrackManager,
|
|
188
|
+
rpcClientManager: RpcClientManager,
|
|
189
|
+
rpcServerManager: RpcServerManager,
|
|
201
190
|
) {
|
|
202
191
|
super(sid, identity, undefined, undefined, undefined, {
|
|
203
192
|
loggerName: options.loggerName,
|
|
@@ -215,9 +204,10 @@ export default class LocalParticipant extends Participant {
|
|
|
215
204
|
['audiooutput', 'default'],
|
|
216
205
|
]);
|
|
217
206
|
this.pendingSignalRequests = new Map();
|
|
218
|
-
this.rpcHandlers = roomRpcHandlers;
|
|
219
207
|
this.roomOutgoingDataStreamManager = roomOutgoingDataStreamManager;
|
|
220
208
|
this.roomOutgoingDataTrackManager = roomOutgoingDataTrackManager;
|
|
209
|
+
this.rpcClientManager = rpcClientManager;
|
|
210
|
+
this.rpcServerManager = rpcServerManager;
|
|
221
211
|
}
|
|
222
212
|
|
|
223
213
|
get lastCameraError(): Error | undefined {
|
|
@@ -277,8 +267,7 @@ export default class LocalParticipant extends Participant {
|
|
|
277
267
|
.on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished)
|
|
278
268
|
.on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate)
|
|
279
269
|
.on(EngineEvent.Closing, this.handleClosing)
|
|
280
|
-
.on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse)
|
|
281
|
-
.on(EngineEvent.DataPacketReceived, this.handleDataPacket);
|
|
270
|
+
.on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse);
|
|
282
271
|
}
|
|
283
272
|
|
|
284
273
|
private handleReconnecting = () => {
|
|
@@ -362,27 +351,6 @@ export default class LocalParticipant extends Participant {
|
|
|
362
351
|
}
|
|
363
352
|
};
|
|
364
353
|
|
|
365
|
-
private handleDataPacket = (packet: DataPacket) => {
|
|
366
|
-
switch (packet.value.case) {
|
|
367
|
-
case 'rpcResponse':
|
|
368
|
-
let rpcResponse = packet.value.value as RpcResponse;
|
|
369
|
-
let payload: string | null = null;
|
|
370
|
-
let error: RpcError | null = null;
|
|
371
|
-
|
|
372
|
-
if (rpcResponse.value.case === 'payload') {
|
|
373
|
-
payload = rpcResponse.value.value;
|
|
374
|
-
} else if (rpcResponse.value.case === 'error') {
|
|
375
|
-
error = RpcError.fromProto(rpcResponse.value.value);
|
|
376
|
-
}
|
|
377
|
-
this.handleIncomingRpcResponse(rpcResponse.requestId, payload, error);
|
|
378
|
-
break;
|
|
379
|
-
case 'rpcAck':
|
|
380
|
-
let rpcAck = packet.value.value as RpcAck;
|
|
381
|
-
this.handleIncomingRpcAck(rpcAck.requestId);
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
|
|
386
354
|
/**
|
|
387
355
|
* Sets and updates the metadata of the local participant.
|
|
388
356
|
* Note: this requires `canUpdateOwnMetadata` permission.
|
|
@@ -1883,69 +1851,9 @@ export default class LocalParticipant extends Participant {
|
|
|
1883
1851
|
* @returns A promise that resolves with the response payload or rejects with an error.
|
|
1884
1852
|
* @throws Error on failure. Details in `message`.
|
|
1885
1853
|
*/
|
|
1886
|
-
performRpc({
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
payload,
|
|
1890
|
-
responseTimeout = 15000,
|
|
1891
|
-
}: PerformRpcParams): TypedPromise<string, RpcError> {
|
|
1892
|
-
const maxRoundTripLatency = 7000;
|
|
1893
|
-
const minEffectiveTimeout = maxRoundTripLatency + 1000;
|
|
1894
|
-
|
|
1895
|
-
return new TypedPromise<string, RpcError>(async (resolve, reject) => {
|
|
1896
|
-
if (byteLength(payload) > MAX_PAYLOAD_BYTES) {
|
|
1897
|
-
reject(RpcError.builtIn('REQUEST_PAYLOAD_TOO_LARGE'));
|
|
1898
|
-
return;
|
|
1899
|
-
}
|
|
1900
|
-
|
|
1901
|
-
if (
|
|
1902
|
-
this.engine.latestJoinResponse?.serverInfo?.version &&
|
|
1903
|
-
compareVersions(this.engine.latestJoinResponse?.serverInfo?.version, '1.8.0') < 0
|
|
1904
|
-
) {
|
|
1905
|
-
reject(RpcError.builtIn('UNSUPPORTED_SERVER'));
|
|
1906
|
-
return;
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
const effectiveTimeout = Math.max(responseTimeout, minEffectiveTimeout);
|
|
1910
|
-
const id = crypto.randomUUID();
|
|
1911
|
-
await this.publishRpcRequest(destinationIdentity, id, method, payload, effectiveTimeout);
|
|
1912
|
-
|
|
1913
|
-
const ackTimeoutId = setTimeout(() => {
|
|
1914
|
-
this.pendingAcks.delete(id);
|
|
1915
|
-
reject(RpcError.builtIn('CONNECTION_TIMEOUT'));
|
|
1916
|
-
this.pendingResponses.delete(id);
|
|
1917
|
-
clearTimeout(responseTimeoutId);
|
|
1918
|
-
}, maxRoundTripLatency);
|
|
1919
|
-
|
|
1920
|
-
this.pendingAcks.set(id, {
|
|
1921
|
-
resolve: () => {
|
|
1922
|
-
clearTimeout(ackTimeoutId);
|
|
1923
|
-
},
|
|
1924
|
-
participantIdentity: destinationIdentity,
|
|
1925
|
-
});
|
|
1926
|
-
|
|
1927
|
-
const responseTimeoutId = setTimeout(() => {
|
|
1928
|
-
this.pendingResponses.delete(id);
|
|
1929
|
-
reject(RpcError.builtIn('RESPONSE_TIMEOUT'));
|
|
1930
|
-
}, responseTimeout);
|
|
1931
|
-
|
|
1932
|
-
this.pendingResponses.set(id, {
|
|
1933
|
-
resolve: (responsePayload: string | null, responseError: RpcError | null) => {
|
|
1934
|
-
clearTimeout(responseTimeoutId);
|
|
1935
|
-
if (this.pendingAcks.has(id)) {
|
|
1936
|
-
this.log.warn('RPC response received before ack', id);
|
|
1937
|
-
this.pendingAcks.delete(id);
|
|
1938
|
-
clearTimeout(ackTimeoutId);
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
if (responseError) {
|
|
1942
|
-
reject(responseError);
|
|
1943
|
-
} else {
|
|
1944
|
-
resolve(responsePayload ?? '');
|
|
1945
|
-
}
|
|
1946
|
-
},
|
|
1947
|
-
participantIdentity: destinationIdentity,
|
|
1948
|
-
});
|
|
1854
|
+
performRpc(params: PerformRpcParams): TypedPromise<string, RpcError> {
|
|
1855
|
+
return this.rpcClientManager.performRpc(params).then(([_id, completionPromise]) => {
|
|
1856
|
+
return completionPromise;
|
|
1949
1857
|
});
|
|
1950
1858
|
}
|
|
1951
1859
|
|
|
@@ -1953,20 +1861,14 @@ export default class LocalParticipant extends Participant {
|
|
|
1953
1861
|
* @deprecated use `room.registerRpcMethod` instead
|
|
1954
1862
|
*/
|
|
1955
1863
|
registerRpcMethod(method: string, handler: (data: RpcInvocationData) => Promise<string>) {
|
|
1956
|
-
|
|
1957
|
-
this.log.warn(
|
|
1958
|
-
`you're overriding the RPC handler for method ${method}, in the future this will throw an error`,
|
|
1959
|
-
);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
this.rpcHandlers.set(method, handler);
|
|
1864
|
+
this.rpcServerManager.registerRpcMethod(method, handler);
|
|
1963
1865
|
}
|
|
1964
1866
|
|
|
1965
1867
|
/**
|
|
1966
1868
|
* @deprecated use `room.unregisterRpcMethod` instead
|
|
1967
1869
|
*/
|
|
1968
1870
|
unregisterRpcMethod(method: string) {
|
|
1969
|
-
this.
|
|
1871
|
+
this.rpcServerManager.unregisterRpcMethod(method);
|
|
1970
1872
|
}
|
|
1971
1873
|
|
|
1972
1874
|
/**
|
|
@@ -1997,72 +1899,6 @@ export default class LocalParticipant extends Participant {
|
|
|
1997
1899
|
}
|
|
1998
1900
|
}
|
|
1999
1901
|
|
|
2000
|
-
private handleIncomingRpcAck(requestId: string) {
|
|
2001
|
-
const handler = this.pendingAcks.get(requestId);
|
|
2002
|
-
if (handler) {
|
|
2003
|
-
handler.resolve();
|
|
2004
|
-
this.pendingAcks.delete(requestId);
|
|
2005
|
-
} else {
|
|
2006
|
-
console.error('Ack received for unexpected RPC request', requestId);
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
private handleIncomingRpcResponse(
|
|
2011
|
-
requestId: string,
|
|
2012
|
-
payload: string | null,
|
|
2013
|
-
error: RpcError | null,
|
|
2014
|
-
) {
|
|
2015
|
-
const handler = this.pendingResponses.get(requestId);
|
|
2016
|
-
if (handler) {
|
|
2017
|
-
handler.resolve(payload, error);
|
|
2018
|
-
this.pendingResponses.delete(requestId);
|
|
2019
|
-
} else {
|
|
2020
|
-
console.error('Response received for unexpected RPC request', requestId);
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
|
|
2024
|
-
/** @internal */
|
|
2025
|
-
private async publishRpcRequest(
|
|
2026
|
-
destinationIdentity: string,
|
|
2027
|
-
requestId: string,
|
|
2028
|
-
method: string,
|
|
2029
|
-
payload: string,
|
|
2030
|
-
responseTimeout: number,
|
|
2031
|
-
) {
|
|
2032
|
-
const packet = new DataPacket({
|
|
2033
|
-
destinationIdentities: [destinationIdentity],
|
|
2034
|
-
kind: DataPacket_Kind.RELIABLE,
|
|
2035
|
-
value: {
|
|
2036
|
-
case: 'rpcRequest',
|
|
2037
|
-
value: new RpcRequest({
|
|
2038
|
-
id: requestId,
|
|
2039
|
-
method,
|
|
2040
|
-
payload,
|
|
2041
|
-
responseTimeoutMs: responseTimeout,
|
|
2042
|
-
version: 1,
|
|
2043
|
-
}),
|
|
2044
|
-
},
|
|
2045
|
-
});
|
|
2046
|
-
|
|
2047
|
-
await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
|
-
/** @internal */
|
|
2051
|
-
handleParticipantDisconnected(participantIdentity: string) {
|
|
2052
|
-
for (const [id, { participantIdentity: pendingIdentity }] of this.pendingAcks) {
|
|
2053
|
-
if (pendingIdentity === participantIdentity) {
|
|
2054
|
-
this.pendingAcks.delete(id);
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
for (const [id, { participantIdentity: pendingIdentity, resolve }] of this.pendingResponses) {
|
|
2059
|
-
if (pendingIdentity === participantIdentity) {
|
|
2060
|
-
resolve(null, RpcError.builtIn('RECIPIENT_DISCONNECTED'));
|
|
2061
|
-
this.pendingResponses.delete(id);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
1902
|
/** @internal */
|
|
2067
1903
|
setEnabledPublishCodecs(codecs: Codec[]) {
|
|
2068
1904
|
this.enabledPublishVideoCodecs = codecs.filter(
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
} from '@livekit/protocol';
|
|
7
7
|
import type { SignalClient } from '../../api/SignalClient';
|
|
8
8
|
import { DeferrableMap } from '../../utils/deferrable-map';
|
|
9
|
+
import { CLIENT_PROTOCOL_DEFAULT } from '../../version';
|
|
9
10
|
import RemoteDataTrack from '../data-track/RemoteDataTrack';
|
|
10
11
|
import type IncomingDataTrackManager from '../data-track/incoming/IncomingDataTrackManager';
|
|
11
12
|
import { DataTrackInfo } from '../data-track/types';
|
|
@@ -41,6 +42,11 @@ export default class RemoteParticipant extends Participant {
|
|
|
41
42
|
|
|
42
43
|
signalClient: SignalClient;
|
|
43
44
|
|
|
45
|
+
/** A version number indicating the set of features that the report participant's client supports.
|
|
46
|
+
* @internal
|
|
47
|
+
**/
|
|
48
|
+
clientProtocol: number;
|
|
49
|
+
|
|
44
50
|
private volumeMap: Map<Track.Source, number>;
|
|
45
51
|
|
|
46
52
|
private audioOutput?: AudioOutputOptions;
|
|
@@ -65,6 +71,7 @@ export default class RemoteParticipant extends Participant {
|
|
|
65
71
|
const info = DataTrackInfo.from(dti);
|
|
66
72
|
return new RemoteDataTrack(info, manager, { publisherIdentity: pi.identity });
|
|
67
73
|
}),
|
|
74
|
+
pi.clientProtocol,
|
|
68
75
|
);
|
|
69
76
|
}
|
|
70
77
|
|
|
@@ -87,6 +94,7 @@ export default class RemoteParticipant extends Participant {
|
|
|
87
94
|
loggerOptions?: LoggerOptions,
|
|
88
95
|
kind: ParticipantKind = ParticipantKind.STANDARD,
|
|
89
96
|
remoteDataTracks: Array<RemoteDataTrack> = [],
|
|
97
|
+
clientProtocol: number = CLIENT_PROTOCOL_DEFAULT,
|
|
90
98
|
) {
|
|
91
99
|
super(sid, identity || '', name, metadata, attributes, loggerOptions, kind);
|
|
92
100
|
this.signalClient = signalClient;
|
|
@@ -99,6 +107,7 @@ export default class RemoteParticipant extends Participant {
|
|
|
99
107
|
}),
|
|
100
108
|
);
|
|
101
109
|
this.volumeMap = new Map();
|
|
110
|
+
this.clientProtocol = clientProtocol;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
protected addTrackPublication(publication: RemoteTrackPublication) {
|