livekit-client 2.20.1 → 2.21.0
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 +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +37 -7
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +2279 -651
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.fm.worker.js +1 -1
- package/dist/livekit-client.fm.worker.js.map +1 -1
- package/dist/livekit-client.fm.worker.mjs +37 -7
- package/dist/livekit-client.fm.worker.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +3 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +2 -0
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts +1 -0
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +31 -26
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +4 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-channel/DataChannelManager.d.ts +62 -0
- package/dist/src/room/data-channel/DataChannelManager.d.ts.map +1 -0
- package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts +95 -0
- package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts.map +1 -0
- package/dist/src/room/data-channel/LossyDataChannel.d.ts +40 -0
- package/dist/src/room/data-channel/LossyDataChannel.d.ts.map +1 -0
- package/dist/src/room/data-channel/ReliableDataChannel.d.ts +56 -0
- package/dist/src/room/data-channel/ReliableDataChannel.d.ts.map +1 -0
- package/dist/src/room/data-channel/types.d.ts +12 -0
- package/dist/src/room/data-channel/types.d.ts.map +1 -0
- package/dist/src/room/data-stream/compression.d.ts +49 -0
- package/dist/src/room/data-stream/compression.d.ts.map +1 -0
- package/dist/src/room/data-stream/constants.d.ts +18 -0
- package/dist/src/room/data-stream/constants.d.ts.map +1 -0
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +4 -0
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/StreamReader.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +49 -2
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/header-utils.d.ts +18 -0
- package/dist/src/room/data-stream/outgoing/header-utils.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +7 -0
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +3 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +9 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +11 -2
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts +7 -0
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
- package/dist/src/room/rpc/server/RpcServerManager.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +1 -0
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +11 -1
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +17 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +13 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/signalToken.d.ts +26 -0
- package/dist/src/test/signalToken.d.ts.map +1 -0
- package/dist/src/utils/dataPacketBuffer.d.ts +18 -0
- package/dist/src/utils/dataPacketBuffer.d.ts.map +1 -1
- package/dist/src/version.d.ts +5 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/ts4.2/index.d.ts +1 -1
- package/dist/ts4.2/logger.d.ts +3 -1
- package/dist/ts4.2/room/PCTransport.d.ts +2 -0
- package/dist/ts4.2/room/PCTransportManager.d.ts +1 -0
- package/dist/ts4.2/room/RTCEngine.d.ts +31 -26
- package/dist/ts4.2/room/Room.d.ts +4 -0
- package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +62 -0
- package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +95 -0
- package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -0
- package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -0
- package/dist/ts4.2/room/data-channel/types.d.ts +12 -0
- package/dist/ts4.2/room/data-stream/compression.d.ts +49 -0
- package/dist/ts4.2/room/data-stream/constants.d.ts +18 -0
- package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +4 -0
- package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +49 -2
- package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -0
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +7 -0
- package/dist/ts4.2/room/errors.d.ts +3 -1
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +9 -1
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +11 -1
- package/dist/ts4.2/room/participant/publishUtils.d.ts +7 -0
- package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +1 -0
- package/dist/ts4.2/room/track/options.d.ts +11 -1
- package/dist/ts4.2/room/types.d.ts +17 -0
- package/dist/ts4.2/room/utils.d.ts +13 -0
- package/dist/ts4.2/test/signalToken.d.ts +26 -0
- package/dist/ts4.2/utils/dataPacketBuffer.d.ts +18 -0
- package/dist/ts4.2/version.d.ts +5 -1
- package/package.json +8 -4
- package/src/api/SignalClient.e2e.test.ts +264 -0
- package/src/api/SignalClient.ts +34 -2
- package/src/index.ts +2 -0
- package/src/logger.ts +2 -0
- package/src/room/PCTransport.ts +66 -31
- package/src/room/PCTransportManager.ts +10 -6
- package/src/room/RTCEngine.test.ts +238 -6
- package/src/room/RTCEngine.ts +101 -338
- package/src/room/RegionUrlProvider.ts +4 -2
- package/src/room/Room.test.ts +4 -1
- package/src/room/Room.ts +47 -9
- package/src/room/data-channel/DataChannelManager.test.ts +152 -0
- package/src/room/data-channel/DataChannelManager.ts +237 -0
- package/src/room/data-channel/FlowControlledDataChannel.test.ts +154 -0
- package/src/room/data-channel/FlowControlledDataChannel.ts +216 -0
- package/src/room/data-channel/LossyDataChannel.test.ts +118 -0
- package/src/room/data-channel/LossyDataChannel.ts +125 -0
- package/src/room/data-channel/ReliableDataChannel.test.ts +158 -0
- package/src/room/data-channel/ReliableDataChannel.ts +153 -0
- package/src/room/data-channel/types.ts +30 -0
- package/src/room/data-stream/compression.ts +117 -0
- package/src/room/data-stream/constants.ts +18 -0
- package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +1601 -0
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +438 -86
- package/src/room/data-stream/incoming/StreamReader.ts +8 -2
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +1292 -0
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +489 -116
- package/src/room/data-stream/outgoing/header-utils.ts +87 -0
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +303 -0
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +70 -8
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +2 -0
- package/src/room/errors.ts +10 -4
- package/src/room/participant/LocalParticipant.ts +29 -6
- package/src/room/participant/Participant.ts +5 -0
- package/src/room/participant/RemoteParticipant.ts +19 -5
- package/src/room/participant/publishUtils.ts +14 -11
- package/src/room/rpc/client/RpcClientManager.test.ts +20 -27
- package/src/room/rpc/client/RpcClientManager.ts +1 -4
- package/src/room/rpc/server/RpcServerManager.test.ts +24 -23
- package/src/room/rpc/server/RpcServerManager.ts +1 -3
- package/src/room/track/LocalAudioTrack.ts +16 -0
- package/src/room/track/Track.ts +10 -6
- package/src/room/track/options.ts +11 -1
- package/src/room/types.ts +18 -0
- package/src/room/utils.ts +67 -0
- package/src/test/signalServerSetup.ts +151 -0
- package/src/test/signalToken.ts +59 -0
- package/src/test/vitest-context.d.ts +10 -0
- package/src/utils/dataPacketBuffer.test.ts +100 -0
- package/src/utils/dataPacketBuffer.ts +46 -1
- package/src/version.ts +5 -1
package/src/room/RTCEngine.ts
CHANGED
|
@@ -63,11 +63,15 @@ import log, { LoggerNames, getLogger } from '../logger';
|
|
|
63
63
|
import type { InternalRoomOptions } from '../options';
|
|
64
64
|
import type { NonSharedUint8Array } from '../type-polyfills/non-shared-typed-arrays';
|
|
65
65
|
import TypedPromise from '../utils/TypedPromise';
|
|
66
|
-
import { DataPacketBuffer } from '../utils/dataPacketBuffer';
|
|
67
66
|
import { TTLMap } from '../utils/ttlmap';
|
|
68
67
|
import PCTransport, { PCEvents } from './PCTransport';
|
|
69
68
|
import { PCTransportManager, PCTransportState } from './PCTransportManager';
|
|
70
69
|
import type { ReconnectContext, ReconnectPolicy } from './ReconnectPolicy';
|
|
70
|
+
import { DataChannelManager } from './data-channel/DataChannelManager';
|
|
71
|
+
import type { FlowControlledDataChannel } from './data-channel/FlowControlledDataChannel';
|
|
72
|
+
import type { LossyDataChannel } from './data-channel/LossyDataChannel';
|
|
73
|
+
import type { ReliableDataChannel } from './data-channel/ReliableDataChannel';
|
|
74
|
+
import { DataChannelKind } from './data-channel/types';
|
|
71
75
|
import { DataTrackInfo } from './data-track/types';
|
|
72
76
|
import { roomConnectOptionDefaults } from './defaults';
|
|
73
77
|
import {
|
|
@@ -91,7 +95,6 @@ import type { TrackPublishOptions, VideoCodec } from './track/options';
|
|
|
91
95
|
import { getTrackPublicationInfo } from './track/utils';
|
|
92
96
|
import type { LoggerOptions } from './types';
|
|
93
97
|
import {
|
|
94
|
-
Future,
|
|
95
98
|
isPublisherOfferWithJoinSupported,
|
|
96
99
|
isReactNative,
|
|
97
100
|
isVideoCodec,
|
|
@@ -103,14 +106,9 @@ import {
|
|
|
103
106
|
toHttpUrl,
|
|
104
107
|
} from './utils';
|
|
105
108
|
|
|
106
|
-
const lossyDataChannel = '_lossy';
|
|
107
|
-
const reliableDataChannel = '_reliable';
|
|
108
|
-
const dataTrackDataChannel = '_data_track';
|
|
109
109
|
const minReconnectWait = 2 * 1000;
|
|
110
110
|
const leaveReconnect = 'leave-reconnect';
|
|
111
111
|
const reliabeReceiveStateTTL = 30_000;
|
|
112
|
-
const lossyDataChannelBufferThresholdMin = 8 * 1024;
|
|
113
|
-
const lossyDataChannelBufferThresholdMax = 256 * 1024;
|
|
114
112
|
|
|
115
113
|
const initialMediaSectionsAudio = 3;
|
|
116
114
|
const initialMediaSectionsVideo = 3;
|
|
@@ -123,11 +121,7 @@ enum PCState {
|
|
|
123
121
|
Closed,
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
export
|
|
127
|
-
RELIABLE = DataPacket_Kind.RELIABLE,
|
|
128
|
-
LOSSY = DataPacket_Kind.LOSSY,
|
|
129
|
-
DATA_TRACK_LOSSY = 2,
|
|
130
|
-
}
|
|
124
|
+
export { DataChannelKind };
|
|
131
125
|
|
|
132
126
|
// Default data-channel max message size (bytes), used when the remote SDP
|
|
133
127
|
// answer does not advertise an `a=max-message-size` attribute (RFC 8841).
|
|
@@ -171,22 +165,24 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
171
165
|
return !!this.reconnectTimeout;
|
|
172
166
|
}
|
|
173
167
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
private
|
|
180
|
-
|
|
181
|
-
// @ts-ignore noUnusedLocals
|
|
182
|
-
private reliableDCSub?: RTCDataChannel;
|
|
168
|
+
/**
|
|
169
|
+
* Owns the data channels: the three flow-controlled publisher wrappers (engine-lifetime; the
|
|
170
|
+
* RTCDataChannel handles underneath are attached/detached as peer connections come and go, with
|
|
171
|
+
* waiter invalidation built into the turnover) plus the subscriber-side receive handles.
|
|
172
|
+
*/
|
|
173
|
+
private dataChannels: DataChannelManager;
|
|
183
174
|
|
|
184
|
-
private
|
|
175
|
+
private get reliableChannel(): ReliableDataChannel {
|
|
176
|
+
return this.dataChannels.reliable;
|
|
177
|
+
}
|
|
185
178
|
|
|
186
|
-
|
|
187
|
-
|
|
179
|
+
private get lossyChannel(): LossyDataChannel {
|
|
180
|
+
return this.dataChannels.lossy;
|
|
181
|
+
}
|
|
188
182
|
|
|
189
|
-
private
|
|
183
|
+
private get dataTrackChannel(): LossyDataChannel {
|
|
184
|
+
return this.dataChannels.dataTrack;
|
|
185
|
+
}
|
|
190
186
|
|
|
191
187
|
private subscriberPrimary: boolean = false;
|
|
192
188
|
|
|
@@ -243,27 +239,13 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
243
239
|
|
|
244
240
|
private publisherConnectionPromise: Promise<void> | undefined;
|
|
245
241
|
|
|
246
|
-
private reliableDataSequence: number = 1;
|
|
247
|
-
|
|
248
|
-
private reliableMessageBuffer = new DataPacketBuffer();
|
|
249
|
-
|
|
250
242
|
private reliableReceivedState: TTLMap<string, number> = new TTLMap(reliabeReceiveStateTTL);
|
|
251
243
|
|
|
252
|
-
private lossyDataStatCurrentBytes: number = 0;
|
|
253
|
-
|
|
254
|
-
private lossyDataStatByterate: number = 0;
|
|
255
|
-
|
|
256
|
-
private lossyDataStatInterval: ReturnType<typeof setInterval> | undefined;
|
|
257
|
-
|
|
258
|
-
private lossyDataDropCount: number = 0;
|
|
259
|
-
|
|
260
244
|
private midToTrackId: { [key: string]: string } = {};
|
|
261
245
|
|
|
262
246
|
/** used to indicate whether the browser is currently waiting to reconnect */
|
|
263
247
|
private isWaitingForNetworkReconnect: boolean = false;
|
|
264
248
|
|
|
265
|
-
private bufferStatusLowClosingFuture = new Future<never, UnexpectedConnectionState>();
|
|
266
|
-
|
|
267
249
|
constructor(private options: InternalRoomOptions) {
|
|
268
250
|
super();
|
|
269
251
|
this.log = getLogger(options.loggerName ?? LoggerNames.Engine, () => this.logContext);
|
|
@@ -276,11 +258,16 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
276
258
|
this.reconnectPolicy = this.options.reconnectPolicy;
|
|
277
259
|
this.closingLock = new Mutex();
|
|
278
260
|
this.dataProcessLock = new Mutex();
|
|
279
|
-
this.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
261
|
+
this.dataChannels = new DataChannelManager({
|
|
262
|
+
isEngineClosed: () => this.isClosed,
|
|
263
|
+
isReconnecting: () => this.attemptingReconnect,
|
|
264
|
+
onDataMessage: (message) => this.handleDataMessage(message),
|
|
265
|
+
onDataTrackMessage: (message) => this.handleDataTrackMessage(message),
|
|
266
|
+
onDataError: (event) => this.handleDataError(event),
|
|
267
|
+
onChannelClose: (kind) => this.handleDataChannelClose(kind)(),
|
|
268
|
+
onBufferStatusChanged: (kind, isLow) =>
|
|
269
|
+
this.emit(EngineEvent.DCBufferStatusChanged, isLow, kind),
|
|
270
|
+
});
|
|
284
271
|
|
|
285
272
|
this.client.onParticipantUpdate = (updates) =>
|
|
286
273
|
this.emit(EngineEvent.ParticipantUpdate, updates);
|
|
@@ -297,13 +284,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
297
284
|
this.client.onParticipantUpdate = (updates) =>
|
|
298
285
|
this.emit(EngineEvent.ParticipantUpdate, updates);
|
|
299
286
|
this.client.onJoined = (joinResponse) => this.emit(EngineEvent.Joined, joinResponse);
|
|
300
|
-
|
|
301
|
-
this.on(EngineEvent.Closing, () => {
|
|
302
|
-
this.bufferStatusLowClosingFuture.reject?.(new UnexpectedConnectionState('engine closed'));
|
|
303
|
-
});
|
|
304
|
-
// Swallow the rejection at the source so it doesn't surface as an unhandled promise rejection
|
|
305
|
-
// when no waitForBufferStatusLow callers are attached.
|
|
306
|
-
this.bufferStatusLowClosingFuture.promise.catch(() => {});
|
|
307
287
|
}
|
|
308
288
|
|
|
309
289
|
/** @internal */
|
|
@@ -458,55 +438,16 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
458
438
|
}
|
|
459
439
|
|
|
460
440
|
async cleanupPeerConnections() {
|
|
461
|
-
|
|
462
|
-
if (!dc) {
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Detach the data channel handlers before closing anything. Closing a peer connection tears
|
|
467
|
-
// down the SCTP transport, which can dispatch `error`/`close` events on the still-open data
|
|
468
|
-
// channels; if our handlers are still attached at that point, handleDataError logs a spurious
|
|
469
|
-
// "Unknown DataChannel error" during an otherwise graceful disconnect. Removing the handlers
|
|
470
|
-
// before dc.close()/pcManager.close() makes this deterministic regardless of how/when the
|
|
471
|
-
// browser dispatches those teardown events. See livekit/client-sdk-js#1953.
|
|
472
|
-
dc.onbufferedamountlow = null;
|
|
473
|
-
dc.onclose = null;
|
|
474
|
-
dc.onclosing = null;
|
|
475
|
-
dc.onerror = null;
|
|
476
|
-
dc.onmessage = null;
|
|
477
|
-
dc.onopen = null;
|
|
478
|
-
|
|
479
|
-
dc.close();
|
|
480
|
-
};
|
|
481
|
-
dcCleanup(this.lossyDC);
|
|
482
|
-
dcCleanup(this.lossyDCSub);
|
|
483
|
-
dcCleanup(this.reliableDC);
|
|
484
|
-
dcCleanup(this.reliableDCSub);
|
|
485
|
-
dcCleanup(this.dataTrackDC);
|
|
486
|
-
dcCleanup(this.dataTrackDCSub);
|
|
441
|
+
this.dataChannels.teardown();
|
|
487
442
|
|
|
488
443
|
await this.pcManager?.close();
|
|
489
444
|
this.pcManager = undefined;
|
|
490
445
|
|
|
491
|
-
this.lossyDC = undefined;
|
|
492
|
-
this.lossyDCSub = undefined;
|
|
493
|
-
this.reliableDC = undefined;
|
|
494
|
-
this.reliableDCSub = undefined;
|
|
495
|
-
this.dataTrackDC = undefined;
|
|
496
|
-
this.dataTrackDCSub = undefined;
|
|
497
|
-
this.reliableMessageBuffer = new DataPacketBuffer();
|
|
498
|
-
this.reliableDataSequence = 1;
|
|
499
446
|
this.reliableReceivedState.clear();
|
|
500
447
|
}
|
|
501
448
|
|
|
502
449
|
cleanupLossyDataStats() {
|
|
503
|
-
this.
|
|
504
|
-
this.lossyDataStatCurrentBytes = 0;
|
|
505
|
-
if (this.lossyDataStatInterval) {
|
|
506
|
-
clearInterval(this.lossyDataStatInterval);
|
|
507
|
-
this.lossyDataStatInterval = undefined;
|
|
508
|
-
}
|
|
509
|
-
this.lossyDataDropCount = 0;
|
|
450
|
+
this.lossyChannel.stopThresholdTuning();
|
|
510
451
|
}
|
|
511
452
|
|
|
512
453
|
async cleanupClient() {
|
|
@@ -575,7 +516,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
575
516
|
}
|
|
576
517
|
|
|
577
518
|
get dataSubscriberReadyState(): string | undefined {
|
|
578
|
-
return this.
|
|
519
|
+
return this.dataChannelForKind(DataChannelKind.RELIABLE, true)?.readyState;
|
|
579
520
|
}
|
|
580
521
|
|
|
581
522
|
async getConnectedServerAddress(): Promise<string | undefined> {
|
|
@@ -885,116 +826,39 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
885
826
|
return;
|
|
886
827
|
}
|
|
887
828
|
|
|
888
|
-
|
|
889
|
-
if (this.lossyDC) {
|
|
890
|
-
this.lossyDC.onmessage = null;
|
|
891
|
-
this.lossyDC.onerror = null;
|
|
892
|
-
this.lossyDC.onclose = null;
|
|
893
|
-
}
|
|
894
|
-
if (this.reliableDC) {
|
|
895
|
-
this.reliableDC.onmessage = null;
|
|
896
|
-
this.reliableDC.onerror = null;
|
|
897
|
-
this.reliableDC.onclose = null;
|
|
898
|
-
}
|
|
899
|
-
if (this.dataTrackDC) {
|
|
900
|
-
this.dataTrackDC.onmessage = null;
|
|
901
|
-
this.dataTrackDC.onerror = null;
|
|
902
|
-
this.dataTrackDC.onclose = null;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
// create data channels
|
|
906
|
-
this.lossyDC = this.pcManager.createPublisherDataChannel(lossyDataChannel, {
|
|
907
|
-
ordered: false,
|
|
908
|
-
maxRetransmits: 0,
|
|
909
|
-
});
|
|
910
|
-
this.reliableDC = this.pcManager.createPublisherDataChannel(reliableDataChannel, {
|
|
911
|
-
ordered: true,
|
|
912
|
-
});
|
|
913
|
-
this.dataTrackDC = this.pcManager.createPublisherDataChannel(dataTrackDataChannel, {
|
|
914
|
-
ordered: false,
|
|
915
|
-
maxRetransmits: 0,
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
// also handle messages over the pub channel, for backwards compatibility
|
|
919
|
-
this.lossyDC.onmessage = this.handleDataMessage;
|
|
920
|
-
this.reliableDC.onmessage = this.handleDataMessage;
|
|
921
|
-
this.dataTrackDC.onmessage = this.handleDataTrackMessage;
|
|
922
|
-
|
|
923
|
-
// handle datachannel errors
|
|
924
|
-
this.lossyDC.onerror = this.handleDataError;
|
|
925
|
-
this.reliableDC.onerror = this.handleDataError;
|
|
926
|
-
this.dataTrackDC.onerror = this.handleDataError;
|
|
927
|
-
|
|
928
|
-
// detect unexpected publisher data channel closes
|
|
929
|
-
this.lossyDC.onclose = this.handleDataChannelClose(DataChannelKind.LOSSY);
|
|
930
|
-
this.reliableDC.onclose = this.handleDataChannelClose(DataChannelKind.RELIABLE);
|
|
931
|
-
this.dataTrackDC.onclose = this.handleDataChannelClose(DataChannelKind.DATA_TRACK_LOSSY);
|
|
932
|
-
|
|
933
|
-
// set up dc buffer threshold, set to 64kB (otherwise 0 by default)
|
|
934
|
-
this.lossyDC.bufferedAmountLowThreshold = 65535;
|
|
935
|
-
this.reliableDC.bufferedAmountLowThreshold = 65535;
|
|
936
|
-
this.dataTrackDC.bufferedAmountLowThreshold = 65535;
|
|
937
|
-
|
|
938
|
-
// handle buffer amount low events
|
|
939
|
-
this.lossyDC.onbufferedamountlow = () => this.handleBufferedAmountLow(DataChannelKind.LOSSY);
|
|
940
|
-
this.reliableDC.onbufferedamountlow = () =>
|
|
941
|
-
this.handleBufferedAmountLow(DataChannelKind.RELIABLE);
|
|
942
|
-
this.dataTrackDC.onbufferedamountlow = () =>
|
|
943
|
-
this.handleBufferedAmountLow(DataChannelKind.DATA_TRACK_LOSSY);
|
|
944
|
-
|
|
945
|
-
this.cleanupLossyDataStats();
|
|
946
|
-
this.lossyDataStatInterval = setInterval(() => {
|
|
947
|
-
this.lossyDataStatByterate = this.lossyDataStatCurrentBytes;
|
|
948
|
-
this.lossyDataStatCurrentBytes = 0;
|
|
949
|
-
|
|
950
|
-
const dc = this.dataChannelForKind(DataChannelKind.LOSSY);
|
|
951
|
-
if (dc) {
|
|
952
|
-
// control buffered latency to ~100ms
|
|
953
|
-
const threshold = this.lossyDataStatByterate / 10;
|
|
954
|
-
dc.bufferedAmountLowThreshold = Math.min(
|
|
955
|
-
Math.max(threshold, lossyDataChannelBufferThresholdMin),
|
|
956
|
-
lossyDataChannelBufferThresholdMax,
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
|
-
}, 1000);
|
|
829
|
+
this.dataChannels.createPublisherChannels(this.pcManager);
|
|
960
830
|
}
|
|
961
831
|
|
|
962
832
|
private handleDataChannel = async ({ channel }: RTCDataChannelEvent) => {
|
|
963
833
|
if (!channel) {
|
|
964
834
|
return;
|
|
965
835
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
this.reliableDCSub = channel;
|
|
969
|
-
handler = this.handleDataMessage;
|
|
970
|
-
} else if (channel.label === lossyDataChannel) {
|
|
971
|
-
this.lossyDCSub = channel;
|
|
972
|
-
handler = this.handleDataMessage;
|
|
973
|
-
} else if (channel.label === dataTrackDataChannel) {
|
|
974
|
-
this.dataTrackDCSub = channel;
|
|
975
|
-
handler = this.handleDataTrackMessage;
|
|
976
|
-
} else {
|
|
977
|
-
return;
|
|
836
|
+
if (this.dataChannels.adoptSubscriberChannel(channel)) {
|
|
837
|
+
this.log.debug(`on data channel ${channel.id}, ${channel.label}`);
|
|
978
838
|
}
|
|
979
|
-
this.log.debug(`on data channel ${channel.id}, ${channel.label}`);
|
|
980
|
-
channel.onmessage = handler;
|
|
981
839
|
};
|
|
982
840
|
|
|
841
|
+
/** Normalizes an incoming data-channel message into bytes, or logs and returns undefined. */
|
|
842
|
+
private async decodeDataMessage(message: MessageEvent): Promise<Uint8Array | undefined> {
|
|
843
|
+
if (message.data instanceof ArrayBuffer) {
|
|
844
|
+
return new Uint8Array(message.data);
|
|
845
|
+
}
|
|
846
|
+
if (message.data instanceof Blob) {
|
|
847
|
+
return new Uint8Array(await message.data.arrayBuffer());
|
|
848
|
+
}
|
|
849
|
+
this.log.error('unsupported data type', { data: message.data });
|
|
850
|
+
return undefined;
|
|
851
|
+
}
|
|
852
|
+
|
|
983
853
|
private handleDataMessage = async (message: MessageEvent) => {
|
|
984
854
|
// make sure to respect incoming data message order by processing message events one after the other
|
|
985
855
|
const unlock = await this.dataProcessLock.lock();
|
|
986
856
|
try {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if (message.data instanceof ArrayBuffer) {
|
|
990
|
-
buffer = message.data;
|
|
991
|
-
} else if (message.data instanceof Blob) {
|
|
992
|
-
buffer = await message.data.arrayBuffer();
|
|
993
|
-
} else {
|
|
994
|
-
this.log.error('unsupported data type', { data: message.data });
|
|
857
|
+
const bytes = await this.decodeDataMessage(message);
|
|
858
|
+
if (!bytes) {
|
|
995
859
|
return;
|
|
996
860
|
}
|
|
997
|
-
const dp = DataPacket.fromBinary(
|
|
861
|
+
const dp = DataPacket.fromBinary(bytes);
|
|
998
862
|
|
|
999
863
|
if (dp.sequence > 0 && dp.participantSid !== '') {
|
|
1000
864
|
const lastSeq = this.reliableReceivedState.get(dp.participantSid);
|
|
@@ -1043,18 +907,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1043
907
|
};
|
|
1044
908
|
|
|
1045
909
|
private handleDataTrackMessage = async (message: MessageEvent) => {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
if (message.data instanceof ArrayBuffer) {
|
|
1049
|
-
buffer = message.data;
|
|
1050
|
-
} else if (message.data instanceof Blob) {
|
|
1051
|
-
buffer = await message.data.arrayBuffer();
|
|
1052
|
-
} else {
|
|
1053
|
-
this.log.error('unsupported data type', { data: message.data });
|
|
910
|
+
const bytes = await this.decodeDataMessage(message);
|
|
911
|
+
if (!bytes) {
|
|
1054
912
|
return;
|
|
1055
913
|
}
|
|
1056
|
-
|
|
1057
|
-
this.emit('dataTrackPacketReceived', new Uint8Array(buffer));
|
|
914
|
+
this.emit('dataTrackPacketReceived', bytes);
|
|
1058
915
|
};
|
|
1059
916
|
|
|
1060
917
|
private handleDataError = (event: Event) => {
|
|
@@ -1093,10 +950,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1093
950
|
}
|
|
1094
951
|
};
|
|
1095
952
|
|
|
1096
|
-
private handleBufferedAmountLow = (channelKind: DataChannelKind) => {
|
|
1097
|
-
this.updateAndEmitDCBufferStatus(channelKind);
|
|
1098
|
-
};
|
|
1099
|
-
|
|
1100
953
|
async createSender(
|
|
1101
954
|
track: LocalTrack,
|
|
1102
955
|
opts: TrackPublishOptions,
|
|
@@ -1500,12 +1353,18 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1500
1353
|
|
|
1501
1354
|
// recreate publish datachannel if it's id is null
|
|
1502
1355
|
// (for safari https://bugs.webkit.org/show_bug.cgi?id=184688)
|
|
1503
|
-
|
|
1356
|
+
const reliableDC = this.dataChannelForKind(DataChannelKind.RELIABLE);
|
|
1357
|
+
if (reliableDC?.readyState === 'open' && reliableDC.id === null) {
|
|
1504
1358
|
this.createDataChannels();
|
|
1505
1359
|
}
|
|
1506
1360
|
|
|
1507
1361
|
if (res?.lastMessageSeq) {
|
|
1508
|
-
this.resendReliableMessagesForResume(res.lastMessageSeq)
|
|
1362
|
+
this.resendReliableMessagesForResume(res.lastMessageSeq).catch((error) => {
|
|
1363
|
+
this.log.warn('failed to resend reliable messages after resume', {
|
|
1364
|
+
...this.logContext,
|
|
1365
|
+
error,
|
|
1366
|
+
});
|
|
1367
|
+
});
|
|
1509
1368
|
}
|
|
1510
1369
|
|
|
1511
1370
|
// resume success
|
|
@@ -1571,7 +1430,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1571
1430
|
}
|
|
1572
1431
|
|
|
1573
1432
|
/* @internal */
|
|
1574
|
-
async sendDataPacket(
|
|
1433
|
+
async sendDataPacket(
|
|
1434
|
+
packet: DataPacket,
|
|
1435
|
+
/** Data-track frames don't come through here — they're sent pre-serialized via {@link sendDataTrackFrame }. */
|
|
1436
|
+
kind: Exclude<DataChannelKind, DataChannelKind.DATA_TRACK_LOSSY>,
|
|
1437
|
+
) {
|
|
1575
1438
|
// make sure we do have a data connection
|
|
1576
1439
|
await this.ensurePublisherConnected(kind);
|
|
1577
1440
|
|
|
@@ -1593,8 +1456,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1593
1456
|
}
|
|
1594
1457
|
|
|
1595
1458
|
if (kind === DataChannelKind.RELIABLE) {
|
|
1596
|
-
packet.sequence = this.
|
|
1597
|
-
this.reliableDataSequence += 1;
|
|
1459
|
+
packet.sequence = this.reliableChannel.nextSequence();
|
|
1598
1460
|
}
|
|
1599
1461
|
|
|
1600
1462
|
const msg = packet.toBinary() as Uint8Array<ArrayBuffer>;
|
|
@@ -1616,124 +1478,46 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1616
1478
|
);
|
|
1617
1479
|
}
|
|
1618
1480
|
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
const dc = this.dataChannelForKind(kind);
|
|
1626
|
-
if (dc) {
|
|
1627
|
-
await this.waitForBufferStatusLow(kind);
|
|
1628
|
-
this.reliableMessageBuffer.push({ data: msg, sequence: packet.sequence });
|
|
1629
|
-
|
|
1630
|
-
if (this.attemptingReconnect) {
|
|
1631
|
-
return;
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
dc.send(msg);
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
this.updateAndEmitDCBufferStatus(kind);
|
|
1638
|
-
break;
|
|
1481
|
+
// The full-buffer policy (drop for lossy, wait/replay for reliable) is the channel's own, as
|
|
1482
|
+
// is emitting the buffer-status change once the send settles.
|
|
1483
|
+
if (kind === DataChannelKind.RELIABLE) {
|
|
1484
|
+
await this.reliableChannel.send(msg, packet.sequence);
|
|
1485
|
+
} else {
|
|
1486
|
+
await this.lossyChannel.send(msg);
|
|
1639
1487
|
}
|
|
1640
1488
|
}
|
|
1641
1489
|
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
// buffer status to not be low before continuing.
|
|
1656
|
-
switch (bufferStatusLowBehavior) {
|
|
1657
|
-
case 'wait':
|
|
1658
|
-
await this.waitForBufferStatusLow(kind);
|
|
1659
|
-
break;
|
|
1660
|
-
case 'drop':
|
|
1661
|
-
// this.log.warn(`dropping lossy data channel message`, this.logContext);
|
|
1662
|
-
// Drop messages to reduce latency
|
|
1663
|
-
this.lossyDataDropCount += 1;
|
|
1664
|
-
if (this.lossyDataDropCount % 100 === 0) {
|
|
1665
|
-
this.log.warn(
|
|
1666
|
-
`dropping lossy data channel messages, total dropped: ${this.lossyDataDropCount}`,
|
|
1667
|
-
);
|
|
1668
|
-
}
|
|
1669
|
-
return;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
this.lossyDataStatCurrentBytes += bytes.byteLength;
|
|
1673
|
-
|
|
1674
|
-
if (this.attemptingReconnect) {
|
|
1675
|
-
return;
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
dc.send(bytes);
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
this.updateAndEmitDCBufferStatus(kind);
|
|
1490
|
+
/**
|
|
1491
|
+
* Sends pre-serialized bytes on the data-track channel. This is the one send path that doesn't
|
|
1492
|
+
* go through {@link sendDataPacket} — Room's `packetAvailable` handler calls it directly with
|
|
1493
|
+
* bytes the data-track pipeline already serialized.
|
|
1494
|
+
*
|
|
1495
|
+
* @internal
|
|
1496
|
+
*/
|
|
1497
|
+
async sendDataTrackFrame(bytes: NonSharedUint8Array) {
|
|
1498
|
+
// Make sure we do have a data connection: on lazily negotiated publisher connections this is
|
|
1499
|
+
// what kicks negotiation off, and it waits for the channel to open. Memoized, so the
|
|
1500
|
+
// steady-state cost is one await on an already-resolved promise.
|
|
1501
|
+
await this.ensurePublisherConnected(DataChannelKind.DATA_TRACK_LOSSY);
|
|
1502
|
+
await this.dataTrackChannel.send(bytes);
|
|
1682
1503
|
}
|
|
1683
1504
|
|
|
1684
1505
|
private async resendReliableMessagesForResume(lastMessageSeq: number) {
|
|
1685
1506
|
await this.ensurePublisherConnected(DataChannelKind.RELIABLE);
|
|
1686
|
-
|
|
1687
|
-
if (dc) {
|
|
1688
|
-
this.reliableMessageBuffer.popToSequence(lastMessageSeq);
|
|
1689
|
-
this.reliableMessageBuffer.getAll().forEach((msg) => {
|
|
1690
|
-
dc.send(msg.data);
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
this.updateAndEmitDCBufferStatus(DataChannelKind.RELIABLE);
|
|
1507
|
+
await this.reliableChannel.replay(lastMessageSeq);
|
|
1694
1508
|
}
|
|
1695
1509
|
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
this.reliableMessageBuffer.alignBufferedAmount(dc.bufferedAmount);
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
const status = this.isBufferStatusLow(kind);
|
|
1705
|
-
if (typeof status !== 'undefined' && status !== this.dcBufferStatus.get(kind)) {
|
|
1706
|
-
this.dcBufferStatus.set(kind, status);
|
|
1707
|
-
this.emit(EngineEvent.DCBufferStatusChanged, status, kind);
|
|
1708
|
-
}
|
|
1709
|
-
};
|
|
1710
|
-
|
|
1711
|
-
private isBufferStatusLow = (kind: DataChannelKind): boolean | undefined => {
|
|
1712
|
-
const dc = this.dataChannelForKind(kind);
|
|
1713
|
-
if (dc) {
|
|
1714
|
-
return dc.bufferedAmount <= dc.bufferedAmountLowThreshold;
|
|
1715
|
-
}
|
|
1716
|
-
};
|
|
1510
|
+
/** The flow-control gate for `kind` — see {@link FlowControlledDataChannel}. */
|
|
1511
|
+
private flowControlFor(kind: DataChannelKind): FlowControlledDataChannel {
|
|
1512
|
+
return this.dataChannels.channelFor(kind);
|
|
1513
|
+
}
|
|
1717
1514
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
resolve();
|
|
1725
|
-
} else {
|
|
1726
|
-
const dc = this.dataChannelForKind(kind);
|
|
1727
|
-
if (!dc) {
|
|
1728
|
-
reject(new UnexpectedConnectionState(`DataChannel not found, kind: ${kind}`));
|
|
1729
|
-
return;
|
|
1730
|
-
}
|
|
1731
|
-
this.bufferStatusLowClosingFuture.promise.catch((e) => reject(e));
|
|
1732
|
-
dc.addEventListener('bufferedamountlow', () => resolve(), {
|
|
1733
|
-
once: true,
|
|
1734
|
-
});
|
|
1735
|
-
}
|
|
1736
|
-
});
|
|
1515
|
+
/**
|
|
1516
|
+
* Resolves once the caller may send on the `kind` channel — see
|
|
1517
|
+
* {@link FlowControlledDataChannel.waitForHeadroomWithLock}.
|
|
1518
|
+
*/
|
|
1519
|
+
async waitForBufferHeadroom(kind: DataChannelKind) {
|
|
1520
|
+
return this.flowControlFor(kind).waitForHeadroomWithLock();
|
|
1737
1521
|
}
|
|
1738
1522
|
|
|
1739
1523
|
/**
|
|
@@ -1835,9 +1619,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1835
1619
|
// don't negotiate without any transceivers or data channel, it will generate sdp without ice frag then negotiate failed
|
|
1836
1620
|
if (
|
|
1837
1621
|
this.pcManager.publisher.getTransceivers().length == 0 &&
|
|
1838
|
-
!this.
|
|
1839
|
-
!this.reliableDC &&
|
|
1840
|
-
!this.dataTrackDC
|
|
1622
|
+
!this.dataChannels.hasPublisherChannels
|
|
1841
1623
|
) {
|
|
1842
1624
|
this.createDataChannels();
|
|
1843
1625
|
}
|
|
@@ -1886,26 +1668,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1886
1668
|
}
|
|
1887
1669
|
|
|
1888
1670
|
dataChannelForKind(kind: DataChannelKind, sub?: boolean): RTCDataChannel | undefined {
|
|
1889
|
-
|
|
1890
|
-
case DataChannelKind.RELIABLE:
|
|
1891
|
-
if (!sub) {
|
|
1892
|
-
return this.reliableDC;
|
|
1893
|
-
} else {
|
|
1894
|
-
return this.reliableDCSub;
|
|
1895
|
-
}
|
|
1896
|
-
case DataChannelKind.LOSSY:
|
|
1897
|
-
if (!sub) {
|
|
1898
|
-
return this.lossyDC;
|
|
1899
|
-
} else {
|
|
1900
|
-
return this.lossyDCSub;
|
|
1901
|
-
}
|
|
1902
|
-
case DataChannelKind.DATA_TRACK_LOSSY:
|
|
1903
|
-
if (!sub) {
|
|
1904
|
-
return this.dataTrackDC;
|
|
1905
|
-
} else {
|
|
1906
|
-
return this.dataTrackDCSub;
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1671
|
+
return this.dataChannels.getHandle(kind, sub);
|
|
1909
1672
|
}
|
|
1910
1673
|
|
|
1911
1674
|
/** @internal */
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Mutex } from '@livekit/mutex';
|
|
2
2
|
import type { RegionInfo, RegionSettings } from '@livekit/protocol';
|
|
3
|
-
import
|
|
3
|
+
import { LoggerNames, getLogger } from '../logger';
|
|
4
4
|
import { ConnectionError, ConnectionErrorReason } from './errors';
|
|
5
5
|
import { extractMaxAgeFromRequestHeaders, isCloud } from './utils';
|
|
6
6
|
|
|
7
|
+
const log = getLogger(LoggerNames.Region);
|
|
8
|
+
|
|
7
9
|
export const DEFAULT_MAX_AGE_MS = 5_000;
|
|
8
10
|
export const STOP_REFETCH_DELAY_MS = 30_000;
|
|
9
11
|
|
|
@@ -229,7 +231,7 @@ export class RegionUrlProvider {
|
|
|
229
231
|
if (regionsLeft.length > 0) {
|
|
230
232
|
const nextRegion = regionsLeft[0];
|
|
231
233
|
this.attemptedRegions.push(nextRegion);
|
|
232
|
-
log.
|
|
234
|
+
log.info(`switching to region: ${nextRegion.region}`, { region: nextRegion.region });
|
|
233
235
|
return nextRegion.url;
|
|
234
236
|
} else {
|
|
235
237
|
return null;
|
package/src/room/Room.test.ts
CHANGED
|
@@ -81,7 +81,10 @@ describe('Room signaling options', () => {
|
|
|
81
81
|
'wss://test.livekit.io',
|
|
82
82
|
'test-token',
|
|
83
83
|
expect.objectContaining({
|
|
84
|
-
clientInfoCapabilities: [
|
|
84
|
+
clientInfoCapabilities: [
|
|
85
|
+
ClientInfo_Capability.CAP_PACKET_TRAILER,
|
|
86
|
+
ClientInfo_Capability.CAP_COMPRESSION_DEFLATE_RAW,
|
|
87
|
+
],
|
|
85
88
|
e2eeEnabled: true,
|
|
86
89
|
}),
|
|
87
90
|
expect.any(AbortSignal),
|