livekit-client 2.18.0 → 2.18.2
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 +8 -7
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +8026 -5883
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +12 -4
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +6 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +2 -1
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +5 -0
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts +1 -1
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +28 -11
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +14 -3
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-track/LocalDataTrack.d.ts +28 -5
- package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -1
- package/dist/src/room/data-track/RemoteDataTrack.d.ts +5 -5
- package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -1
- package/dist/src/room/data-track/depacketizer.d.ts +4 -4
- package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
- package/dist/src/room/data-track/frame.d.ts +14 -0
- package/dist/src/room/data-track/frame.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +19 -11
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/pipeline.d.ts +6 -5
- package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/errors.d.ts +16 -6
- package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/pipeline.d.ts +7 -6
- package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/types.d.ts +14 -4
- package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
- package/dist/src/room/data-track/packetizer.d.ts +4 -4
- package/dist/src/room/data-track/packetizer.d.ts.map +1 -1
- package/dist/src/room/data-track/track-interfaces.d.ts +1 -1
- package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -1
- package/dist/src/room/data-track/types.d.ts +6 -1
- package/dist/src/room/data-track/types.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +24 -3
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +12 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +13 -0
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +1 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/deferrable-map.d.ts +32 -0
- package/dist/src/utils/deferrable-map.d.ts.map +1 -0
- package/dist/src/utils/serializer.d.ts +48 -0
- package/dist/src/utils/serializer.d.ts.map +1 -0
- package/dist/ts4.2/api/SignalClient.d.ts +12 -4
- package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +2 -1
- package/dist/ts4.2/e2ee/types.d.ts +6 -0
- package/dist/ts4.2/e2ee/utils.d.ts +2 -1
- package/dist/ts4.2/index.d.ts +7 -4
- package/dist/ts4.2/room/PCTransport.d.ts +5 -0
- package/dist/ts4.2/room/PCTransportManager.d.ts +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +28 -11
- package/dist/ts4.2/room/Room.d.ts +14 -3
- package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +27 -4
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +4 -4
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +4 -4
- package/dist/ts4.2/room/data-track/frame.d.ts +14 -0
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +21 -10
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +6 -5
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
- package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +16 -6
- package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +7 -6
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +14 -4
- package/dist/ts4.2/room/data-track/packetizer.d.ts +4 -4
- package/dist/ts4.2/room/data-track/track-interfaces.d.ts +1 -1
- package/dist/ts4.2/room/data-track/types.d.ts +6 -1
- package/dist/ts4.2/room/events.d.ts +24 -3
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +12 -1
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +13 -0
- package/dist/ts4.2/room/utils.d.ts +1 -0
- package/dist/ts4.2/utils/deferrable-map.d.ts +32 -0
- package/dist/ts4.2/utils/serializer.d.ts +48 -0
- package/package.json +1 -1
- package/src/api/SignalClient.test.ts +9 -4
- package/src/api/SignalClient.ts +116 -9
- package/src/connectionHelper/ConnectionCheck.ts +1 -1
- package/src/e2ee/constants.ts +1 -0
- package/src/e2ee/types.ts +6 -0
- package/src/e2ee/utils.ts +4 -3
- package/src/e2ee/worker/DataCryptor.ts +1 -4
- package/src/e2ee/worker/FrameCryptor.ts +1 -4
- package/src/e2ee/worker/ParticipantKeyHandler.ts +1 -1
- package/src/index.ts +13 -4
- package/src/room/PCTransport.ts +41 -1
- package/src/room/PCTransportManager.ts +1 -1
- package/src/room/RTCEngine.ts +312 -125
- package/src/room/Room.ts +168 -35
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +26 -2
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -7
- package/src/room/data-track/LocalDataTrack.ts +83 -10
- package/src/room/data-track/RemoteDataTrack.ts +7 -9
- package/src/room/data-track/depacketizer.ts +21 -12
- package/src/room/data-track/frame.ts +28 -2
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +58 -73
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +139 -80
- package/src/room/data-track/incoming/pipeline.ts +29 -24
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +225 -32
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +150 -75
- package/src/room/data-track/outgoing/errors.ts +36 -7
- package/src/room/data-track/outgoing/pipeline.ts +23 -17
- package/src/room/data-track/outgoing/types.ts +12 -3
- package/src/room/data-track/packet/extensions.ts +17 -22
- package/src/room/data-track/packet/index.test.ts +22 -33
- package/src/room/data-track/packetizer.test.ts +2 -2
- package/src/room/data-track/packetizer.ts +4 -4
- package/src/room/data-track/track-interfaces.ts +1 -1
- package/src/room/data-track/types.ts +21 -1
- package/src/room/events.ts +26 -1
- package/src/room/participant/LocalParticipant.ts +74 -8
- package/src/room/participant/RemoteParticipant.ts +25 -0
- package/src/room/utils.ts +4 -0
- package/src/utils/deferrable-map.ts +109 -0
- package/src/utils/serializer.ts +72 -0
- package/dist/src/room/data-track/e2ee.d.ts +0 -12
- package/dist/src/room/data-track/e2ee.d.ts.map +0 -1
- package/dist/ts4.2/room/data-track/e2ee.d.ts +0 -12
- package/src/room/data-track/e2ee.ts +0 -15
package/src/room/Room.ts
CHANGED
|
@@ -47,7 +47,7 @@ import TypedPromise from '../utils/TypedPromise';
|
|
|
47
47
|
import { getBrowser } from '../utils/browserParser';
|
|
48
48
|
import { BackOffStrategy } from './BackOffStrategy';
|
|
49
49
|
import DeviceManager from './DeviceManager';
|
|
50
|
-
import RTCEngine from './RTCEngine';
|
|
50
|
+
import RTCEngine, { DataChannelKind } from './RTCEngine';
|
|
51
51
|
import { RegionUrlProvider } from './RegionUrlProvider';
|
|
52
52
|
import IncomingDataStreamManager from './data-stream/incoming/IncomingDataStreamManager';
|
|
53
53
|
import {
|
|
@@ -55,6 +55,11 @@ import {
|
|
|
55
55
|
type TextStreamHandler,
|
|
56
56
|
} from './data-stream/incoming/StreamReader';
|
|
57
57
|
import OutgoingDataStreamManager from './data-stream/outgoing/OutgoingDataStreamManager';
|
|
58
|
+
import type LocalDataTrack from './data-track/LocalDataTrack';
|
|
59
|
+
import type RemoteDataTrack from './data-track/RemoteDataTrack';
|
|
60
|
+
import IncomingDataTrackManager from './data-track/incoming/IncomingDataTrackManager';
|
|
61
|
+
import OutgoingDataTrackManager from './data-track/outgoing/OutgoingDataTrackManager';
|
|
62
|
+
import { DataTrackInfo, type DataTrackSid } from './data-track/types';
|
|
58
63
|
import {
|
|
59
64
|
audioDefaults,
|
|
60
65
|
publishDefaults,
|
|
@@ -70,7 +75,7 @@ import {
|
|
|
70
75
|
} from './errors';
|
|
71
76
|
import { EngineEvent, ParticipantEvent, RoomEvent, TrackEvent } from './events';
|
|
72
77
|
import LocalParticipant from './participant/LocalParticipant';
|
|
73
|
-
import
|
|
78
|
+
import Participant from './participant/Participant';
|
|
74
79
|
import { type ConnectionQuality, ParticipantKind } from './participant/Participant';
|
|
75
80
|
import RemoteParticipant from './participant/RemoteParticipant';
|
|
76
81
|
import { MAX_PAYLOAD_BYTES, RpcError, type RpcInvocationData, byteLength } from './rpc';
|
|
@@ -182,6 +187,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
182
187
|
|
|
183
188
|
private e2eeManager: BaseE2EEManager | undefined;
|
|
184
189
|
|
|
190
|
+
private e2eeStateMutex: Mutex = new Mutex();
|
|
191
|
+
|
|
185
192
|
private connectionReconcileInterval?: ReturnType<typeof setInterval>;
|
|
186
193
|
|
|
187
194
|
private regionUrlProvider?: RegionUrlProvider;
|
|
@@ -205,6 +212,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
205
212
|
|
|
206
213
|
private outgoingDataStreamManager: OutgoingDataStreamManager;
|
|
207
214
|
|
|
215
|
+
private incomingDataTrackManager: IncomingDataTrackManager;
|
|
216
|
+
|
|
217
|
+
private outgoingDataTrackManager: OutgoingDataTrackManager;
|
|
218
|
+
|
|
208
219
|
private rpcHandlers: Map<string, (data: RpcInvocationData) => Promise<string>> = new Map();
|
|
209
220
|
|
|
210
221
|
get hasE2EESetup(): boolean {
|
|
@@ -243,8 +254,47 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
243
254
|
this.incomingDataStreamManager = new IncomingDataStreamManager();
|
|
244
255
|
this.outgoingDataStreamManager = new OutgoingDataStreamManager(this.engine, this.log);
|
|
245
256
|
|
|
246
|
-
this.
|
|
257
|
+
this.incomingDataTrackManager = new IncomingDataTrackManager({ e2eeManager: this.e2eeManager });
|
|
258
|
+
this.incomingDataTrackManager
|
|
259
|
+
.on('sfuUpdateSubscription', (event) => {
|
|
260
|
+
this.engine.client.sendUpdateDataSubscription(event.sid, event.subscribe);
|
|
261
|
+
})
|
|
262
|
+
.on('trackPublished', (event) => {
|
|
263
|
+
if (event.track.publisherIdentity === this.localParticipant.identity) {
|
|
264
|
+
// Only advertize tracks from other participants
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.emit(RoomEvent.DataTrackPublished, event.track);
|
|
268
|
+
this.remoteParticipants.get(event.track.publisherIdentity)?.addRemoteDataTrack(event.track);
|
|
269
|
+
})
|
|
270
|
+
.on('trackUnpublished', (event) => {
|
|
271
|
+
if (event.publisherIdentity === this.localParticipant.identity) {
|
|
272
|
+
// Only advertize tracks from other participants
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.emit(RoomEvent.DataTrackUnpublished, event.sid);
|
|
276
|
+
this.remoteParticipants.get(event.publisherIdentity)?.removeRemoteDataTrack(event.sid);
|
|
277
|
+
});
|
|
247
278
|
|
|
279
|
+
this.outgoingDataTrackManager = new OutgoingDataTrackManager({ e2eeManager: this.e2eeManager });
|
|
280
|
+
this.outgoingDataTrackManager
|
|
281
|
+
.on('sfuPublishRequest', (event) => {
|
|
282
|
+
this.engine.client.sendPublishDataTrackRequest(event.handle, event.name, event.usesE2ee);
|
|
283
|
+
})
|
|
284
|
+
.on('sfuUnpublishRequest', (event) => {
|
|
285
|
+
this.engine.client.sendUnPublishDataTrackRequest(event.handle);
|
|
286
|
+
})
|
|
287
|
+
.on('trackPublished', (event) => {
|
|
288
|
+
this.emit(RoomEvent.LocalDataTrackPublished, event.track);
|
|
289
|
+
})
|
|
290
|
+
.on('trackUnpublished', (event) => {
|
|
291
|
+
this.emit(RoomEvent.LocalDataTrackUnpublished, event.sid);
|
|
292
|
+
})
|
|
293
|
+
.on('packetAvailable', ({ bytes }) => {
|
|
294
|
+
this.engine.sendLossyBytes(bytes, DataChannelKind.DATA_TRACK_LOSSY, 'wait');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
this.disconnectLock = new Mutex();
|
|
248
298
|
this.localParticipant = new LocalParticipant(
|
|
249
299
|
'',
|
|
250
300
|
'',
|
|
@@ -252,6 +302,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
252
302
|
this.options,
|
|
253
303
|
this.rpcHandlers,
|
|
254
304
|
this.outgoingDataStreamManager,
|
|
305
|
+
this.outgoingDataTrackManager,
|
|
255
306
|
);
|
|
256
307
|
|
|
257
308
|
if (this.options.e2ee || this.options.encryption) {
|
|
@@ -259,6 +310,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
259
310
|
}
|
|
260
311
|
|
|
261
312
|
this.engine.e2eeManager = this.e2eeManager;
|
|
313
|
+
this.incomingDataTrackManager.updateE2eeManager(this.e2eeManager ?? null);
|
|
314
|
+
this.outgoingDataTrackManager.updateE2eeManager(this.e2eeManager ?? null);
|
|
262
315
|
|
|
263
316
|
if (this.options.videoCaptureDefaults.deviceId) {
|
|
264
317
|
this.localParticipant.activeDeviceMap.set(
|
|
@@ -359,13 +412,21 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
359
412
|
* @experimental
|
|
360
413
|
*/
|
|
361
414
|
async setE2EEEnabled(enabled: boolean) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if (this.
|
|
365
|
-
this.
|
|
415
|
+
const unlock = await this.e2eeStateMutex.lock();
|
|
416
|
+
try {
|
|
417
|
+
if (this.e2eeManager) {
|
|
418
|
+
if (this.isE2EEEnabled !== enabled) {
|
|
419
|
+
await this.localParticipant.setE2EEEnabled(enabled);
|
|
420
|
+
|
|
421
|
+
if (this.localParticipant.identity !== '') {
|
|
422
|
+
this.e2eeManager.setParticipantCryptorEnabled(enabled, this.localParticipant.identity);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
throw Error('e2ee not configured, please set e2ee settings within the room options');
|
|
366
427
|
}
|
|
367
|
-
}
|
|
368
|
-
|
|
428
|
+
} finally {
|
|
429
|
+
unlock();
|
|
369
430
|
}
|
|
370
431
|
}
|
|
371
432
|
|
|
@@ -398,6 +459,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
398
459
|
this.emit(RoomEvent.EncryptionError, error, participant);
|
|
399
460
|
});
|
|
400
461
|
this.e2eeManager?.setup(this);
|
|
462
|
+
this.e2eeManager?.setupEngine(this.engine);
|
|
401
463
|
}
|
|
402
464
|
}
|
|
403
465
|
|
|
@@ -515,6 +577,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
515
577
|
}
|
|
516
578
|
})
|
|
517
579
|
.on(EngineEvent.Restarting, this.handleRestarting)
|
|
580
|
+
.on(EngineEvent.Restarted, this.handleRestarted)
|
|
518
581
|
.on(EngineEvent.SignalRestarted, this.handleSignalRestarted)
|
|
519
582
|
.on(EngineEvent.Offline, () => {
|
|
520
583
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
|
@@ -560,6 +623,66 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
560
623
|
} else {
|
|
561
624
|
this.handleParticipantUpdates(roomMoved.otherParticipants);
|
|
562
625
|
}
|
|
626
|
+
})
|
|
627
|
+
.on(EngineEvent.PublishDataTrackResponse, (event) => {
|
|
628
|
+
if (!event.info) {
|
|
629
|
+
this.log.warn(
|
|
630
|
+
`received PublishDataTrackResponse, but event.info was ${event.info}, so skipping.`,
|
|
631
|
+
this.logContext,
|
|
632
|
+
);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
this.outgoingDataTrackManager.receivedSfuPublishResponse(event.info.pubHandle, {
|
|
637
|
+
type: 'ok',
|
|
638
|
+
data: {
|
|
639
|
+
sid: event.info.sid,
|
|
640
|
+
pubHandle: event.info.pubHandle,
|
|
641
|
+
name: event.info.name,
|
|
642
|
+
usesE2ee: event.info.encryption !== Encryption_Type.NONE,
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
})
|
|
646
|
+
.on(EngineEvent.UnPublishDataTrackResponse, (event) => {
|
|
647
|
+
if (!event.info) {
|
|
648
|
+
this.log.warn(
|
|
649
|
+
`received UnPublishDataTrackResponse, but event.info was ${event.info}, so skipping.`,
|
|
650
|
+
this.logContext,
|
|
651
|
+
);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
this.outgoingDataTrackManager.receivedSfuUnpublishResponse(event.info.pubHandle);
|
|
656
|
+
})
|
|
657
|
+
.on(EngineEvent.DataTrackSubscriberHandles, (event) => {
|
|
658
|
+
const handleToSidMapping = new Map(
|
|
659
|
+
Object.entries(event.subHandles).map(([key, value]) => {
|
|
660
|
+
return [parseInt(key, 10), value.trackSid];
|
|
661
|
+
}),
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
this.incomingDataTrackManager.receivedSfuSubscriberHandles(handleToSidMapping);
|
|
665
|
+
})
|
|
666
|
+
.on(EngineEvent.DataTrackPacketReceived, (packetBytes) => {
|
|
667
|
+
try {
|
|
668
|
+
this.incomingDataTrackManager.packetReceived(packetBytes);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
// NOTE: wrapping in the bare try/catch like this means that the Throws<...> type doesn't
|
|
671
|
+
// propagate upwards into the public interface.
|
|
672
|
+
throw err;
|
|
673
|
+
}
|
|
674
|
+
})
|
|
675
|
+
.on(EngineEvent.Joined, (joinResponse) => {
|
|
676
|
+
// Ingest data track publication updates into data tracks infrastructure
|
|
677
|
+
const mapped = new Map(
|
|
678
|
+
joinResponse.otherParticipants.map((participant) => {
|
|
679
|
+
return [
|
|
680
|
+
participant.identity,
|
|
681
|
+
participant.dataTracks.map((info) => DataTrackInfo.from(info)),
|
|
682
|
+
];
|
|
683
|
+
}),
|
|
684
|
+
);
|
|
685
|
+
this.incomingDataTrackManager.receiveSfuPublicationUpdates(mapped);
|
|
563
686
|
});
|
|
564
687
|
|
|
565
688
|
if (this.localParticipant) {
|
|
@@ -782,7 +905,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
782
905
|
roomOptions: InternalRoomOptions,
|
|
783
906
|
abortController: AbortController,
|
|
784
907
|
): Promise<JoinResponse> => {
|
|
785
|
-
const joinResponse = await engine.join(
|
|
908
|
+
const { joinResponse, serverInfo } = await engine.join(
|
|
786
909
|
url,
|
|
787
910
|
token,
|
|
788
911
|
{
|
|
@@ -797,23 +920,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
797
920
|
!roomOptions.singlePeerConnection,
|
|
798
921
|
);
|
|
799
922
|
|
|
800
|
-
let serverInfo: Partial<ServerInfo> | undefined = joinResponse.serverInfo;
|
|
801
|
-
if (!serverInfo) {
|
|
802
|
-
serverInfo = { version: joinResponse.serverVersion, region: joinResponse.serverRegion };
|
|
803
|
-
}
|
|
804
923
|
this.serverInfo = serverInfo;
|
|
805
924
|
|
|
806
|
-
this.log.debug(
|
|
807
|
-
`connected to Livekit Server ${Object.entries(serverInfo)
|
|
808
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
809
|
-
.join(', ')}`,
|
|
810
|
-
{
|
|
811
|
-
room: joinResponse.room?.name,
|
|
812
|
-
roomSid: joinResponse.room?.sid,
|
|
813
|
-
identity: joinResponse.participant?.identity,
|
|
814
|
-
},
|
|
815
|
-
);
|
|
816
|
-
|
|
817
925
|
if (!serverInfo.version) {
|
|
818
926
|
throw new UnsupportedServer('unknown server version');
|
|
819
927
|
}
|
|
@@ -1456,10 +1564,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1456
1564
|
) as RemoteParticipant | undefined;
|
|
1457
1565
|
|
|
1458
1566
|
if (!participant) {
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
this.
|
|
1462
|
-
|
|
1567
|
+
// server could require extra media sections to accelerate subscription.
|
|
1568
|
+
if (participantSid.startsWith('PA')) {
|
|
1569
|
+
this.log.error(
|
|
1570
|
+
`Tried to add a track for a participant, that's not present. Sid: ${participantSid}`,
|
|
1571
|
+
this.logContext,
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1463
1574
|
return;
|
|
1464
1575
|
}
|
|
1465
1576
|
|
|
@@ -1527,6 +1638,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1527
1638
|
}
|
|
1528
1639
|
};
|
|
1529
1640
|
|
|
1641
|
+
private handleRestarted = () => {
|
|
1642
|
+
this.outgoingDataTrackManager.sfuWillRepublishTracks();
|
|
1643
|
+
this.incomingDataTrackManager.resendSubscriptionUpdates();
|
|
1644
|
+
};
|
|
1645
|
+
|
|
1530
1646
|
private handleSignalRestarted = async (joinResponse: JoinResponse) => {
|
|
1531
1647
|
this.log.debug(`signal reconnected to server, region ${joinResponse.serverRegion}`, {
|
|
1532
1648
|
...this.logContext,
|
|
@@ -1640,10 +1756,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1640
1756
|
|
|
1641
1757
|
private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
|
|
1642
1758
|
// handle changes to participant state, and send events
|
|
1643
|
-
|
|
1759
|
+
for (const info of participantInfos) {
|
|
1644
1760
|
if (info.identity === this.localParticipant.identity) {
|
|
1645
1761
|
this.localParticipant.updateInfo(info);
|
|
1646
|
-
|
|
1762
|
+
continue;
|
|
1647
1763
|
}
|
|
1648
1764
|
|
|
1649
1765
|
// LiveKit server doesn't send identity info prior to version 1.5.2 in disconnect updates
|
|
@@ -1661,7 +1777,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1661
1777
|
// create participant if doesn't exist
|
|
1662
1778
|
remoteParticipant = this.getOrCreateParticipant(info.identity, info);
|
|
1663
1779
|
}
|
|
1664
|
-
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
// Ingest data track publication updates into data tracks infrastructure
|
|
1783
|
+
const mapped = new Map(
|
|
1784
|
+
participantInfos
|
|
1785
|
+
.filter((p) => p.identity !== this.localParticipant.identity)
|
|
1786
|
+
.map((info) => {
|
|
1787
|
+
return [info.identity, info.dataTracks.map((dataTrack) => DataTrackInfo.from(dataTrack))];
|
|
1788
|
+
}),
|
|
1789
|
+
);
|
|
1790
|
+
this.incomingDataTrackManager.receiveSfuPublicationUpdates(mapped);
|
|
1665
1791
|
};
|
|
1666
1792
|
|
|
1667
1793
|
private handleParticipantDisconnected(identity: string, participant?: RemoteParticipant) {
|
|
@@ -1672,6 +1798,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1672
1798
|
}
|
|
1673
1799
|
|
|
1674
1800
|
this.incomingDataStreamManager.validateParticipantHasNoActiveDataStreams(identity);
|
|
1801
|
+
this.incomingDataTrackManager.handleRemoteParticipantDisconnected(identity);
|
|
1675
1802
|
|
|
1676
1803
|
participant.trackPublications.forEach((publication) => {
|
|
1677
1804
|
participant.unpublishTrack(publication.trackSid, true);
|
|
@@ -2192,7 +2319,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2192
2319
|
track.on(TrackEvent.VideoPlaybackFailed, this.handleVideoPlaybackFailed);
|
|
2193
2320
|
track.on(TrackEvent.VideoPlaybackStarted, this.handleVideoPlaybackStarted);
|
|
2194
2321
|
}
|
|
2195
|
-
this.
|
|
2322
|
+
this.emitWhenConnected(RoomEvent.TrackSubscribed, track, publication, participant);
|
|
2196
2323
|
},
|
|
2197
2324
|
)
|
|
2198
2325
|
.on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
|
|
@@ -2270,7 +2397,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2270
2397
|
return acc;
|
|
2271
2398
|
}, [] as RemoteTrackPublication[]);
|
|
2272
2399
|
const localTracks = this.localParticipant.getTrackPublications() as LocalTrackPublication[]; // FIXME would be nice to have this return LocalTrackPublications directly instead of the type cast
|
|
2273
|
-
this.
|
|
2400
|
+
const localDataTrackInfos = this.outgoingDataTrackManager.queryPublished();
|
|
2401
|
+
this.engine.sendSyncState(remoteTracks, localTracks, localDataTrackInfos);
|
|
2274
2402
|
}
|
|
2275
2403
|
|
|
2276
2404
|
/**
|
|
@@ -2342,6 +2470,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2342
2470
|
return false;
|
|
2343
2471
|
}
|
|
2344
2472
|
this.state = state;
|
|
2473
|
+
this.incomingDataStreamManager.setConnected(state === ConnectionState.Connected);
|
|
2345
2474
|
this.emit(RoomEvent.ConnectionStateChanged, this.state);
|
|
2346
2475
|
return true;
|
|
2347
2476
|
}
|
|
@@ -2732,10 +2861,14 @@ export type RoomEventCallbacks = {
|
|
|
2732
2861
|
recordingStatusChanged: (recording: boolean) => void;
|
|
2733
2862
|
participantEncryptionStatusChanged: (encrypted: boolean, participant?: Participant) => void;
|
|
2734
2863
|
encryptionError: (error: Error, participant?: Participant) => void;
|
|
2735
|
-
dcBufferStatusChanged: (isLow: boolean, kind:
|
|
2864
|
+
dcBufferStatusChanged: (isLow: boolean, kind: DataChannelKind) => void;
|
|
2736
2865
|
activeDeviceChanged: (kind: MediaDeviceKind, deviceId: string) => void;
|
|
2737
2866
|
chatMessage: (message: ChatMessage, participant?: RemoteParticipant | LocalParticipant) => void;
|
|
2738
2867
|
localTrackSubscribed: (publication: LocalTrackPublication, participant: LocalParticipant) => void;
|
|
2739
2868
|
metricsReceived: (metrics: MetricsBatch, participant?: Participant) => void;
|
|
2740
2869
|
participantActive: (participant: Participant) => void;
|
|
2870
|
+
dataTrackPublished: (track: RemoteDataTrack) => void;
|
|
2871
|
+
dataTrackUnpublished: (sid: DataTrackSid) => void;
|
|
2872
|
+
localDataTrackPublished: (track: LocalDataTrack) => void;
|
|
2873
|
+
localDataTrackUnpublished: (sid: DataTrackSid) => void;
|
|
2741
2874
|
};
|
|
@@ -27,6 +27,25 @@ export default class IncomingDataStreamManager {
|
|
|
27
27
|
|
|
28
28
|
private textStreamHandlers = new Map<string, TextStreamHandler>();
|
|
29
29
|
|
|
30
|
+
private isConnected = false;
|
|
31
|
+
|
|
32
|
+
private bufferedPackets: Array<{ packet: DataPacket; encryptionType: Encryption_Type }> = [];
|
|
33
|
+
|
|
34
|
+
setConnected(connected: boolean) {
|
|
35
|
+
this.isConnected = connected;
|
|
36
|
+
if (connected) {
|
|
37
|
+
this.flushBufferedPackets();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private flushBufferedPackets() {
|
|
42
|
+
const packets = this.bufferedPackets;
|
|
43
|
+
this.bufferedPackets = [];
|
|
44
|
+
for (const { packet, encryptionType } of packets) {
|
|
45
|
+
this.handleDataStreamPacket(packet, encryptionType);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
registerTextStreamHandler(topic: string, callback: TextStreamHandler) {
|
|
31
50
|
if (this.textStreamHandlers.has(topic)) {
|
|
32
51
|
throw new DataStreamError(
|
|
@@ -58,6 +77,7 @@ export default class IncomingDataStreamManager {
|
|
|
58
77
|
clearControllers() {
|
|
59
78
|
this.byteStreamControllers.clear();
|
|
60
79
|
this.textStreamControllers.clear();
|
|
80
|
+
this.bufferedPackets = [];
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
validateParticipantHasNoActiveDataStreams(participantIdentity: string) {
|
|
@@ -88,7 +108,11 @@ export default class IncomingDataStreamManager {
|
|
|
88
108
|
}
|
|
89
109
|
}
|
|
90
110
|
|
|
91
|
-
|
|
111
|
+
handleDataStreamPacket(packet: DataPacket, encryptionType: Encryption_Type) {
|
|
112
|
+
if (!this.isConnected) {
|
|
113
|
+
this.bufferedPackets.push({ packet, encryptionType });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
92
116
|
switch (packet.value.case) {
|
|
93
117
|
case 'streamHeader':
|
|
94
118
|
return this.handleStreamHeader(
|
|
@@ -105,7 +129,7 @@ export default class IncomingDataStreamManager {
|
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
108
|
-
private
|
|
132
|
+
private handleStreamHeader(
|
|
109
133
|
streamHeader: DataStream_Header,
|
|
110
134
|
participantIdentity: string,
|
|
111
135
|
encryptionType: Encryption_Type,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Mutex } from '@livekit/mutex';
|
|
2
2
|
import {
|
|
3
3
|
DataPacket,
|
|
4
|
-
DataPacket_Kind,
|
|
5
4
|
DataStream_ByteHeader,
|
|
6
5
|
DataStream_Chunk,
|
|
7
6
|
DataStream_Header,
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
} from '@livekit/protocol';
|
|
13
12
|
import { type StructuredLogger } from '../../../logger';
|
|
14
13
|
import type RTCEngine from '../../RTCEngine';
|
|
14
|
+
import { DataChannelKind } from '../../RTCEngine';
|
|
15
15
|
import { EngineEvent } from '../../events';
|
|
16
16
|
import type {
|
|
17
17
|
ByteStreamInfo,
|
|
@@ -137,7 +137,7 @@ export default class OutgoingDataStreamManager {
|
|
|
137
137
|
value: header,
|
|
138
138
|
},
|
|
139
139
|
});
|
|
140
|
-
await this.engine.sendDataPacket(packet,
|
|
140
|
+
await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
|
|
141
141
|
|
|
142
142
|
let chunkId = 0;
|
|
143
143
|
const engine = this.engine;
|
|
@@ -158,7 +158,7 @@ export default class OutgoingDataStreamManager {
|
|
|
158
158
|
value: chunk,
|
|
159
159
|
},
|
|
160
160
|
});
|
|
161
|
-
await engine.sendDataPacket(chunkPacket,
|
|
161
|
+
await engine.sendDataPacket(chunkPacket, DataChannelKind.RELIABLE);
|
|
162
162
|
|
|
163
163
|
chunkId += 1;
|
|
164
164
|
}
|
|
@@ -174,7 +174,7 @@ export default class OutgoingDataStreamManager {
|
|
|
174
174
|
value: trailer,
|
|
175
175
|
},
|
|
176
176
|
});
|
|
177
|
-
await engine.sendDataPacket(trailerPacket,
|
|
177
|
+
await engine.sendDataPacket(trailerPacket, DataChannelKind.RELIABLE);
|
|
178
178
|
},
|
|
179
179
|
abort(err) {
|
|
180
180
|
console.log('Sink error:', err);
|
|
@@ -262,7 +262,7 @@ export default class OutgoingDataStreamManager {
|
|
|
262
262
|
},
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
-
await this.engine.sendDataPacket(packet,
|
|
265
|
+
await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
|
|
266
266
|
|
|
267
267
|
let chunkId = 0;
|
|
268
268
|
const writeMutex = new Mutex();
|
|
@@ -288,7 +288,7 @@ export default class OutgoingDataStreamManager {
|
|
|
288
288
|
}),
|
|
289
289
|
},
|
|
290
290
|
});
|
|
291
|
-
await engine.sendDataPacket(chunkPacket,
|
|
291
|
+
await engine.sendDataPacket(chunkPacket, DataChannelKind.RELIABLE);
|
|
292
292
|
chunkId += 1;
|
|
293
293
|
byteOffset += subChunk.byteLength;
|
|
294
294
|
}
|
|
@@ -307,7 +307,7 @@ export default class OutgoingDataStreamManager {
|
|
|
307
307
|
value: trailer,
|
|
308
308
|
},
|
|
309
309
|
});
|
|
310
|
-
await engine.sendDataPacket(trailerPacket,
|
|
310
|
+
await engine.sendDataPacket(trailerPacket, DataChannelKind.RELIABLE);
|
|
311
311
|
},
|
|
312
312
|
abort(err) {
|
|
313
313
|
logLocal.error('Sink error:', err);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import log, { LoggerNames, type StructuredLogger, getLogger } from '../../logger';
|
|
2
|
+
import { type DataTrackFrame, DataTrackFrameInternal } from './frame';
|
|
3
|
+
import type { DataTrackHandle } from './handle';
|
|
2
4
|
import type OutgoingDataTrackManager from './outgoing/OutgoingDataTrackManager';
|
|
5
|
+
import { DataTrackPushFrameError } from './outgoing/errors';
|
|
6
|
+
import type { DataTrackOptions } from './outgoing/types';
|
|
3
7
|
import {
|
|
4
8
|
DataTrackSymbol,
|
|
5
9
|
type IDataTrack,
|
|
@@ -15,23 +19,66 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
|
15
19
|
|
|
16
20
|
readonly typeSymbol = DataTrackSymbol;
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
protected options: DataTrackOptions;
|
|
23
|
+
|
|
24
|
+
/** Represents the currently active {@link DataTrackHandle} for the publication. */
|
|
25
|
+
protected handle: DataTrackHandle | null = null;
|
|
19
26
|
|
|
20
27
|
protected manager: OutgoingDataTrackManager;
|
|
21
28
|
|
|
29
|
+
protected log: StructuredLogger = log;
|
|
30
|
+
|
|
22
31
|
/** @internal */
|
|
23
|
-
constructor(
|
|
24
|
-
this.
|
|
32
|
+
constructor(options: DataTrackOptions, manager: OutgoingDataTrackManager) {
|
|
33
|
+
this.options = options;
|
|
25
34
|
this.manager = manager;
|
|
35
|
+
|
|
36
|
+
this.log = getLogger(LoggerNames.DataTracks);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @internal */
|
|
40
|
+
static withExplicitHandle(
|
|
41
|
+
options: DataTrackOptions,
|
|
42
|
+
manager: OutgoingDataTrackManager,
|
|
43
|
+
handle: DataTrackHandle,
|
|
44
|
+
) {
|
|
45
|
+
const track = new LocalDataTrack(options, manager);
|
|
46
|
+
track.handle = handle;
|
|
47
|
+
return track;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Metrics about the data track publication. */
|
|
51
|
+
get info() {
|
|
52
|
+
const descriptor = this.descriptor;
|
|
53
|
+
if (descriptor?.type === 'active') {
|
|
54
|
+
return descriptor.info;
|
|
55
|
+
} else {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
26
58
|
}
|
|
27
59
|
|
|
28
60
|
/** The raw descriptor from the manager containing the internal state for this local track. */
|
|
29
61
|
protected get descriptor() {
|
|
30
|
-
return this.manager.getDescriptor(this.
|
|
62
|
+
return this.handle ? this.manager.getDescriptor(this.handle) : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Publish the track to the SFU. This must be done before calling {@link tryPush} for the first time.
|
|
67
|
+
* @internal
|
|
68
|
+
* */
|
|
69
|
+
async publish(signal?: AbortSignal) {
|
|
70
|
+
try {
|
|
71
|
+
this.handle = await this.manager.publishRequest(this.options, signal);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
// NOTE: Rethrow errors to break Throws<...> type boundary
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
31
76
|
}
|
|
32
77
|
|
|
33
|
-
isPublished() {
|
|
34
|
-
|
|
78
|
+
isPublished(): this is { info: DataTrackInfo } {
|
|
79
|
+
// NOTE: a track which is internally in the "resubscribing" state is still considered
|
|
80
|
+
// published from the public API perspective.
|
|
81
|
+
return this.descriptor?.type === 'active' && this.descriptor.publishState !== 'unpublished';
|
|
35
82
|
}
|
|
36
83
|
|
|
37
84
|
/** Try pushing a frame to subscribers of the track.
|
|
@@ -41,12 +88,38 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
|
41
88
|
* - The track has been unpublished by the local participant or SFU
|
|
42
89
|
* - The room is no longer connected
|
|
43
90
|
*/
|
|
44
|
-
tryPush(
|
|
91
|
+
tryPush(frame: DataTrackFrame) {
|
|
92
|
+
if (!this.handle) {
|
|
93
|
+
throw DataTrackPushFrameError.trackUnpublished();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const internalFrame = DataTrackFrameInternal.from(frame);
|
|
97
|
+
|
|
45
98
|
try {
|
|
46
|
-
return this.manager.tryProcessAndSend(this.
|
|
99
|
+
return this.manager.tryProcessAndSend(this.handle, internalFrame);
|
|
47
100
|
} catch (err) {
|
|
48
101
|
// NOTE: wrapping in the bare try/catch like this means that the Throws<...> type doesn't
|
|
49
|
-
//
|
|
102
|
+
// propagate upwards into the public interface.
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Unpublish the track from the SFU. Once this is called, any further calls to {@link tryPush}
|
|
109
|
+
* will fail.
|
|
110
|
+
* */
|
|
111
|
+
async unpublish() {
|
|
112
|
+
if (!this.handle) {
|
|
113
|
+
log.warn(
|
|
114
|
+
`Data track "${this.options.name}" is not published, so unpublishing has no effect.`,
|
|
115
|
+
);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
await this.manager.unpublishRequest(this.handle);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
// NOTE: Rethrow errors to break Throws<...> type boundary
|
|
50
123
|
throw err;
|
|
51
124
|
}
|
|
52
125
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type Participant from '../participant/Participant';
|
|
2
|
-
import type
|
|
2
|
+
import { type DataTrackFrame } from './frame';
|
|
3
3
|
import type IncomingDataTrackManager from './incoming/IncomingDataTrackManager';
|
|
4
4
|
import {
|
|
5
5
|
DataTrackSymbol,
|
|
@@ -13,12 +13,12 @@ type RemoteDataTrackOptions = {
|
|
|
13
13
|
publisherIdentity: Participant['identity'];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export type
|
|
16
|
+
export type DataTrackSubscribeOptions = {
|
|
17
17
|
signal?: AbortSignal;
|
|
18
18
|
|
|
19
19
|
/** The number of {@link DataTrackFrame}s to hold in the ReadableStream before disgarding extra
|
|
20
|
-
* frames. Defaults to
|
|
21
|
-
|
|
20
|
+
* frames. Defaults to 16, but this may not be good enough for especially high frequency data. */
|
|
21
|
+
bufferSize?: number;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export default class RemoteDataTrack implements IRemoteTrack, IDataTrack {
|
|
@@ -64,14 +64,12 @@ export default class RemoteDataTrack implements IRemoteTrack, IDataTrack {
|
|
|
64
64
|
* Note that newly created subscriptions only receive frames published after
|
|
65
65
|
* the initial subscription is established.
|
|
66
66
|
*/
|
|
67
|
-
|
|
68
|
-
options?: RemoteDataTrackSubscribeOptions,
|
|
69
|
-
): Promise<ReadableStream<DataTrackFrame>> {
|
|
67
|
+
subscribe(options?: DataTrackSubscribeOptions): ReadableStream<DataTrackFrame> {
|
|
70
68
|
try {
|
|
71
|
-
const stream =
|
|
69
|
+
const [stream] = this.manager.openSubscriptionStream(
|
|
72
70
|
this.info.sid,
|
|
73
71
|
options?.signal,
|
|
74
|
-
options?.
|
|
72
|
+
options?.bufferSize,
|
|
75
73
|
);
|
|
76
74
|
return stream;
|
|
77
75
|
} catch (err) {
|