livekit-client 2.15.4 → 2.15.6

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.
Files changed (84) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +373 -164
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +982 -643
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  10. package/dist/src/e2ee/worker/FrameCryptor.d.ts +0 -47
  11. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  12. package/dist/src/e2ee/worker/naluUtils.d.ts +27 -0
  13. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -0
  14. package/dist/src/e2ee/worker/sifPayload.d.ts +22 -0
  15. package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -0
  16. package/dist/src/index.d.ts +2 -2
  17. package/dist/src/index.d.ts.map +1 -1
  18. package/dist/src/room/Room.d.ts +6 -10
  19. package/dist/src/room/Room.d.ts.map +1 -1
  20. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +20 -0
  21. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -0
  22. package/dist/{ts4.2/src/room → src/room/data-stream/incoming}/StreamReader.d.ts +82 -56
  23. package/dist/src/room/data-stream/incoming/StreamReader.d.ts.map +1 -0
  24. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +27 -0
  25. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -0
  26. package/dist/src/room/{StreamWriter.d.ts → data-stream/outgoing/StreamWriter.d.ts} +1 -1
  27. package/dist/src/room/data-stream/outgoing/StreamWriter.d.ts.map +1 -0
  28. package/dist/src/room/errors.d.ts +13 -0
  29. package/dist/src/room/errors.d.ts.map +1 -1
  30. package/dist/src/room/participant/LocalParticipant.d.ts +32 -19
  31. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  32. package/dist/src/room/track/LocalTrack.d.ts +7 -2
  33. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  34. package/dist/src/room/track/RemoteVideoTrack.d.ts +1 -0
  35. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  36. package/dist/src/room/track/Track.d.ts +4 -1
  37. package/dist/src/room/track/Track.d.ts.map +1 -1
  38. package/dist/src/room/types.d.ts +17 -1
  39. package/dist/src/room/types.d.ts.map +1 -1
  40. package/dist/src/room/utils.d.ts +8 -0
  41. package/dist/src/room/utils.d.ts.map +1 -1
  42. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +0 -47
  43. package/dist/ts4.2/src/e2ee/worker/naluUtils.d.ts +27 -0
  44. package/dist/ts4.2/src/e2ee/worker/sifPayload.d.ts +22 -0
  45. package/dist/ts4.2/src/index.d.ts +2 -2
  46. package/dist/ts4.2/src/room/Room.d.ts +6 -10
  47. package/dist/ts4.2/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +20 -0
  48. package/dist/{src/room → ts4.2/src/room/data-stream/incoming}/StreamReader.d.ts +82 -56
  49. package/dist/ts4.2/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +27 -0
  50. package/dist/ts4.2/src/room/{StreamWriter.d.ts → data-stream/outgoing/StreamWriter.d.ts} +1 -1
  51. package/dist/ts4.2/src/room/errors.d.ts +13 -0
  52. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +32 -19
  53. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +7 -2
  54. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -0
  55. package/dist/ts4.2/src/room/track/Track.d.ts +4 -1
  56. package/dist/ts4.2/src/room/types.d.ts +17 -1
  57. package/dist/ts4.2/src/room/utils.d.ts +8 -0
  58. package/package.json +7 -7
  59. package/src/e2ee/E2eeManager.ts +18 -1
  60. package/src/e2ee/worker/FrameCryptor.ts +56 -157
  61. package/src/e2ee/worker/e2ee.worker.ts +6 -1
  62. package/src/e2ee/worker/naluUtils.ts +328 -0
  63. package/src/e2ee/worker/sifPayload.ts +75 -0
  64. package/src/index.ts +2 -2
  65. package/src/room/Room.ts +104 -208
  66. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +247 -0
  67. package/src/room/data-stream/incoming/StreamReader.ts +317 -0
  68. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +316 -0
  69. package/src/room/{StreamWriter.ts → data-stream/outgoing/StreamWriter.ts} +1 -1
  70. package/src/room/errors.ts +34 -0
  71. package/src/room/participant/LocalParticipant.ts +39 -295
  72. package/src/room/track/LocalAudioTrack.ts +2 -2
  73. package/src/room/track/LocalTrack.ts +70 -50
  74. package/src/room/track/RemoteVideoTrack.ts +12 -2
  75. package/src/room/track/Track.ts +10 -1
  76. package/src/room/types.ts +22 -1
  77. package/src/room/utils.ts +14 -5
  78. package/dist/src/e2ee/worker/SifGuard.d.ts +0 -11
  79. package/dist/src/e2ee/worker/SifGuard.d.ts.map +0 -1
  80. package/dist/src/room/StreamReader.d.ts.map +0 -1
  81. package/dist/src/room/StreamWriter.d.ts.map +0 -1
  82. package/dist/ts4.2/src/e2ee/worker/SifGuard.d.ts +0 -11
  83. package/src/e2ee/worker/SifGuard.ts +0 -47
  84. package/src/room/StreamReader.ts +0 -170
@@ -10617,6 +10617,30 @@ class SignalRequestError extends LivekitError {
10617
10617
  this.reasonName = typeof reason === 'string' ? reason : RequestResponse_Reason[reason];
10618
10618
  }
10619
10619
  }
10620
+ // NOTE: matches with https://github.com/livekit/client-sdk-swift/blob/f37bbd260d61e165084962db822c79f995f1a113/Sources/LiveKit/DataStream/StreamError.swift#L17
10621
+ var DataStreamErrorReason;
10622
+ (function (DataStreamErrorReason) {
10623
+ // Unable to open a stream with the same ID more than once.
10624
+ DataStreamErrorReason[DataStreamErrorReason["AlreadyOpened"] = 0] = "AlreadyOpened";
10625
+ // Stream closed abnormally by remote participant.
10626
+ DataStreamErrorReason[DataStreamErrorReason["AbnormalEnd"] = 1] = "AbnormalEnd";
10627
+ // Incoming chunk data could not be decoded.
10628
+ DataStreamErrorReason[DataStreamErrorReason["DecodeFailed"] = 2] = "DecodeFailed";
10629
+ // Read length exceeded total length specified in stream header.
10630
+ DataStreamErrorReason[DataStreamErrorReason["LengthExceeded"] = 3] = "LengthExceeded";
10631
+ // Read length less than total length specified in stream header.
10632
+ DataStreamErrorReason[DataStreamErrorReason["Incomplete"] = 4] = "Incomplete";
10633
+ // Unable to register a stream handler more than once.
10634
+ DataStreamErrorReason[DataStreamErrorReason["HandlerAlreadyRegistered"] = 7] = "HandlerAlreadyRegistered";
10635
+ })(DataStreamErrorReason || (DataStreamErrorReason = {}));
10636
+ class DataStreamError extends LivekitError {
10637
+ constructor(message, reason) {
10638
+ super(16, message);
10639
+ this.name = 'DataStreamError';
10640
+ this.reason = reason;
10641
+ this.reasonName = DataStreamErrorReason[reason];
10642
+ }
10643
+ }
10620
10644
  var MediaDeviceFailure;
10621
10645
  (function (MediaDeviceFailure) {
10622
10646
  // user rejected permissions
@@ -11364,7 +11388,7 @@ function getOSVersion(ua) {
11364
11388
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11365
11389
  }
11366
11390
 
11367
- var version$1 = "2.15.4";
11391
+ var version$1 = "2.15.6";
11368
11392
 
11369
11393
  const version = version$1;
11370
11394
  const protocolVersion = 16;
@@ -11401,17 +11425,24 @@ var VideoQuality;
11401
11425
  VideoQuality[VideoQuality["HIGH"] = 2] = "HIGH";
11402
11426
  })(VideoQuality || (VideoQuality = {}));
11403
11427
  class Track extends eventsExports.EventEmitter {
11428
+ /**
11429
+ * indicates current state of stream, it'll indicate `paused` if the track
11430
+ * has been paused by congestion controller
11431
+ */
11432
+ get streamState() {
11433
+ return this._streamState;
11434
+ }
11435
+ /** @internal */
11436
+ setStreamState(value) {
11437
+ this._streamState = value;
11438
+ }
11404
11439
  constructor(mediaTrack, kind) {
11405
11440
  let loggerOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
11406
11441
  var _a;
11407
11442
  super();
11408
11443
  this.attachedElements = [];
11409
11444
  this.isMuted = false;
11410
- /**
11411
- * indicates current state of stream, it'll indicate `paused` if the track
11412
- * has been paused by congestion controller
11413
- */
11414
- this.streamState = Track.StreamState.Active;
11445
+ this._streamState = Track.StreamState.Active;
11415
11446
  this.isInBackground = false;
11416
11447
  this._currentBitrate = 0;
11417
11448
  this.log = livekitLogger;
@@ -11904,8 +11935,9 @@ function supportsAV1() {
11904
11935
  if (!('getCapabilities' in RTCRtpSender)) {
11905
11936
  return false;
11906
11937
  }
11907
- if (isSafari()) {
11938
+ if (isSafari() || isFireFox()) {
11908
11939
  // Safari 17 on iPhone14 reports AV1 capability, but does not actually support it
11940
+ // Firefox does support AV1, but SVC publishing is not supported
11909
11941
  return false;
11910
11942
  }
11911
11943
  const capabilities = RTCRtpSender.getCapabilities('video');
@@ -12007,9 +12039,9 @@ function isE2EESimulcastSupported() {
12007
12039
  if (browser) {
12008
12040
  if (browser.name !== 'Safari' && browser.os !== 'iOS') {
12009
12041
  return true;
12010
- } else if (browser.os === 'iOS' && browser.osVersion && compareVersions(supportedSafariVersion, browser.osVersion) >= 0) {
12042
+ } else if (browser.os === 'iOS' && browser.osVersion && compareVersions(browser.osVersion, supportedSafariVersion) >= 0) {
12011
12043
  return true;
12012
- } else if (browser.name === 'Safari' && compareVersions(supportedSafariVersion, browser.version) >= 0) {
12044
+ } else if (browser.name === 'Safari' && compareVersions(browser.version, supportedSafariVersion) >= 0) {
12013
12045
  return true;
12014
12046
  } else {
12015
12047
  return false;
@@ -12057,6 +12089,14 @@ function getDevicePixelRatio() {
12057
12089
  }
12058
12090
  return 1;
12059
12091
  }
12092
+ /**
12093
+ * @param v1 - The first version string to compare.
12094
+ * @param v2 - The second version string to compare.
12095
+ * @returns A number indicating the order of the versions:
12096
+ * - 1 if v1 is greater than v2
12097
+ * - -1 if v1 is less than v2
12098
+ * - 0 if v1 and v2 are equal
12099
+ */
12060
12100
  function compareVersions(v1, v2) {
12061
12101
  const parts1 = v1.split('.');
12062
12102
  const parts2 = v2.split('.');
@@ -12246,13 +12286,13 @@ function unwrapConstraint(constraint) {
12246
12286
  if (Array.isArray(constraint)) {
12247
12287
  return constraint[0];
12248
12288
  }
12249
- if (constraint.exact) {
12289
+ if (constraint.exact !== undefined) {
12250
12290
  if (Array.isArray(constraint.exact)) {
12251
12291
  return constraint.exact[0];
12252
12292
  }
12253
12293
  return constraint.exact;
12254
12294
  }
12255
- if (constraint.ideal) {
12295
+ if (constraint.ideal !== undefined) {
12256
12296
  if (Array.isArray(constraint.ideal)) {
12257
12297
  return constraint.ideal[0];
12258
12298
  }
@@ -12851,6 +12891,21 @@ class E2EEManager extends eventsExports.EventEmitter {
12851
12891
  room.localParticipant.on(ParticipantEvent.LocalSenderCreated, (sender, track) => __awaiter(this, void 0, void 0, function* () {
12852
12892
  this.setupE2EESender(track, sender);
12853
12893
  }));
12894
+ room.localParticipant.on(ParticipantEvent.LocalTrackPublished, publication => {
12895
+ // Safari doesn't support retrieving payload information on RTCEncodedVideoFrame, so we need to update the codec manually once we have the trackInfo from the server
12896
+ if (!isVideoTrack(publication.track) || !isSafariBased()) {
12897
+ return;
12898
+ }
12899
+ const msg = {
12900
+ kind: 'updateCodec',
12901
+ data: {
12902
+ trackId: publication.track.mediaStreamID,
12903
+ codec: mimeTypeToVideoCodecString(publication.trackInfo.codecs[0].mimeType),
12904
+ participantIdentity: this.room.localParticipant.identity
12905
+ }
12906
+ };
12907
+ this.worker.postMessage(msg);
12908
+ });
12854
12909
  keyProvider.on(KeyProviderEvent.SetKey, keyInfo => this.postKey(keyInfo)).on(KeyProviderEvent.RatchetRequest, (participantId, keyIndex) => this.postRatchetRequest(participantId, keyIndex));
12855
12910
  }
12856
12911
  postRatchetRequest(participantIdentity, keyIndex) {
@@ -15931,9 +15986,14 @@ class LocalTrack extends Track {
15931
15986
  this.providedByUser = userProvidedTrack;
15932
15987
  this.muteLock = new _();
15933
15988
  this.pauseUpstreamLock = new _();
15934
- this.processorLock = new _();
15935
- this.restartLock = new _();
15936
- this.setMediaStreamTrack(mediaTrack, true);
15989
+ this.trackChangeLock = new _();
15990
+ this.trackChangeLock.lock().then(unlock => __awaiter(this, void 0, void 0, function* () {
15991
+ try {
15992
+ yield this.setMediaStreamTrack(mediaTrack, true);
15993
+ } finally {
15994
+ unlock();
15995
+ }
15996
+ }));
15937
15997
  // added to satisfy TS compiler, constraints are synced with MediaStreamTrack
15938
15998
  this._constraints = mediaTrack.getConstraints();
15939
15999
  if (constraints) {
@@ -16009,26 +16069,21 @@ class LocalTrack extends Track {
16009
16069
  }
16010
16070
  let processedTrack;
16011
16071
  if (this.processor && newTrack) {
16012
- const unlock = yield this.processorLock.lock();
16013
- try {
16014
- this.log.debug('restarting processor', this.logContext);
16015
- if (this.kind === 'unknown') {
16016
- throw TypeError('cannot set processor on track of unknown kind');
16017
- }
16018
- if (this.processorElement) {
16019
- attachToElement(newTrack, this.processorElement);
16020
- // ensure the processorElement itself stays muted
16021
- this.processorElement.muted = true;
16022
- }
16023
- yield this.processor.restart({
16024
- track: newTrack,
16025
- kind: this.kind,
16026
- element: this.processorElement
16027
- });
16028
- processedTrack = this.processor.processedTrack;
16029
- } finally {
16030
- unlock();
16072
+ this.log.debug('restarting processor', this.logContext);
16073
+ if (this.kind === 'unknown') {
16074
+ throw TypeError('cannot set processor on track of unknown kind');
16075
+ }
16076
+ if (this.processorElement) {
16077
+ attachToElement(newTrack, this.processorElement);
16078
+ // ensure the processorElement itself stays muted
16079
+ this.processorElement.muted = true;
16031
16080
  }
16081
+ yield this.processor.restart({
16082
+ track: newTrack,
16083
+ kind: this.kind,
16084
+ element: this.processorElement
16085
+ });
16086
+ processedTrack = this.processor.processedTrack;
16032
16087
  }
16033
16088
  if (this.sender && ((_a = this.sender.transport) === null || _a === void 0 ? void 0 : _a.state) !== 'closed') {
16034
16089
  yield this.sender.replaceTrack(processedTrack !== null && processedTrack !== void 0 ? processedTrack : newTrack);
@@ -16126,32 +16181,37 @@ class LocalTrack extends Track {
16126
16181
  }
16127
16182
  replaceTrack(track, userProvidedOrOptions) {
16128
16183
  return __awaiter(this, void 0, void 0, function* () {
16129
- if (!this.sender) {
16130
- throw new TrackInvalidError('unable to replace an unpublished track');
16131
- }
16132
- let userProvidedTrack;
16133
- let stopProcessor;
16134
- if (typeof userProvidedOrOptions === 'boolean') {
16135
- userProvidedTrack = userProvidedOrOptions;
16136
- } else if (userProvidedOrOptions !== undefined) {
16137
- userProvidedTrack = userProvidedOrOptions.userProvidedTrack;
16138
- stopProcessor = userProvidedOrOptions.stopProcessor;
16139
- }
16140
- this.providedByUser = userProvidedTrack !== null && userProvidedTrack !== void 0 ? userProvidedTrack : true;
16141
- this.log.debug('replace MediaStreamTrack', this.logContext);
16142
- yield this.setMediaStreamTrack(track);
16143
- // this must be synced *after* setting mediaStreamTrack above, since it relies
16144
- // on the previous state in order to cleanup
16145
- if (stopProcessor && this.processor) {
16146
- yield this.stopProcessor();
16184
+ const unlock = yield this.trackChangeLock.lock();
16185
+ try {
16186
+ if (!this.sender) {
16187
+ throw new TrackInvalidError('unable to replace an unpublished track');
16188
+ }
16189
+ let userProvidedTrack;
16190
+ let stopProcessor;
16191
+ if (typeof userProvidedOrOptions === 'boolean') {
16192
+ userProvidedTrack = userProvidedOrOptions;
16193
+ } else if (userProvidedOrOptions !== undefined) {
16194
+ userProvidedTrack = userProvidedOrOptions.userProvidedTrack;
16195
+ stopProcessor = userProvidedOrOptions.stopProcessor;
16196
+ }
16197
+ this.providedByUser = userProvidedTrack !== null && userProvidedTrack !== void 0 ? userProvidedTrack : true;
16198
+ this.log.debug('replace MediaStreamTrack', this.logContext);
16199
+ yield this.setMediaStreamTrack(track);
16200
+ // this must be synced *after* setting mediaStreamTrack above, since it relies
16201
+ // on the previous state in order to cleanup
16202
+ if (stopProcessor && this.processor) {
16203
+ yield this.internalStopProcessor();
16204
+ }
16205
+ return this;
16206
+ } finally {
16207
+ unlock();
16147
16208
  }
16148
- return this;
16149
16209
  });
16150
16210
  }
16151
16211
  restart(constraints) {
16152
16212
  return __awaiter(this, void 0, void 0, function* () {
16153
16213
  this.manuallyStopped = false;
16154
- const unlock = yield this.restartLock.lock();
16214
+ const unlock = yield this.trackChangeLock.lock();
16155
16215
  try {
16156
16216
  if (!constraints) {
16157
16217
  constraints = this._constraints;
@@ -16174,9 +16234,9 @@ class LocalTrack extends Track {
16174
16234
  facingMode
16175
16235
  } : true;
16176
16236
  } else {
16177
- streamConstraints.audio = deviceId ? {
16237
+ streamConstraints.audio = deviceId ? Object.assign({
16178
16238
  deviceId
16179
- } : true;
16239
+ }, otherConstraints) : true;
16180
16240
  }
16181
16241
  // these steps are duplicated from setMediaStreamTrack because we must stop
16182
16242
  // the previous tracks before new tracks can be acquired
@@ -16191,7 +16251,10 @@ class LocalTrack extends Track {
16191
16251
  // create new track and attach
16192
16252
  const mediaStream = yield navigator.mediaDevices.getUserMedia(streamConstraints);
16193
16253
  const newTrack = mediaStream.getTracks()[0];
16194
- yield newTrack.applyConstraints(otherConstraints);
16254
+ if (this.kind === Track.Kind.Video) {
16255
+ // we already captured the audio track with the constraints, so we only need to apply the video constraints
16256
+ yield newTrack.applyConstraints(otherConstraints);
16257
+ }
16195
16258
  newTrack.addEventListener('ended', this.handleEnded);
16196
16259
  this.log.debug('re-acquired MediaStreamTrack', this.logContext);
16197
16260
  yield this.setMediaStreamTrack(newTrack);
@@ -16334,7 +16397,7 @@ class LocalTrack extends Track {
16334
16397
  let showProcessedStreamLocally = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
16335
16398
  return function* () {
16336
16399
  var _a;
16337
- const unlock = yield _this3.processorLock.lock();
16400
+ const unlock = yield _this3.trackChangeLock.lock();
16338
16401
  try {
16339
16402
  _this3.log.debug('setting up processor', _this3.logContext);
16340
16403
  const processorElement = document.createElement(_this3.kind);
@@ -16347,7 +16410,7 @@ class LocalTrack extends Track {
16347
16410
  yield processor.init(processorOptions);
16348
16411
  _this3.log.debug('processor initialized', _this3.logContext);
16349
16412
  if (_this3.processor) {
16350
- yield _this3.stopProcessor();
16413
+ yield _this3.internalStopProcessor();
16351
16414
  }
16352
16415
  if (_this3.kind === 'unknown') {
16353
16416
  throw TypeError('cannot set processor on track of unknown kind');
@@ -16405,22 +16468,41 @@ class LocalTrack extends Track {
16405
16468
  return __awaiter(this, arguments, void 0, function () {
16406
16469
  var _this4 = this;
16407
16470
  let keepElement = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
16471
+ return function* () {
16472
+ const unlock = yield _this4.trackChangeLock.lock();
16473
+ try {
16474
+ yield _this4.internalStopProcessor(keepElement);
16475
+ } finally {
16476
+ unlock();
16477
+ }
16478
+ }();
16479
+ });
16480
+ }
16481
+ /**
16482
+ * @internal
16483
+ * This method assumes the caller has acquired a trackChangeLock already.
16484
+ * The public facing method for stopping the processor is `stopProcessor` and it wraps this method in the trackChangeLock.
16485
+ */
16486
+ internalStopProcessor() {
16487
+ return __awaiter(this, arguments, void 0, function () {
16488
+ var _this5 = this;
16489
+ let keepElement = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
16408
16490
  return function* () {
16409
16491
  var _a, _b;
16410
- if (!_this4.processor) return;
16411
- _this4.log.debug('stopping processor', _this4.logContext);
16412
- (_a = _this4.processor.processedTrack) === null || _a === void 0 ? void 0 : _a.stop();
16413
- yield _this4.processor.destroy();
16414
- _this4.processor = undefined;
16492
+ if (!_this5.processor) return;
16493
+ _this5.log.debug('stopping processor', _this5.logContext);
16494
+ (_a = _this5.processor.processedTrack) === null || _a === void 0 ? void 0 : _a.stop();
16495
+ yield _this5.processor.destroy();
16496
+ _this5.processor = undefined;
16415
16497
  if (!keepElement) {
16416
- (_b = _this4.processorElement) === null || _b === void 0 ? void 0 : _b.remove();
16417
- _this4.processorElement = undefined;
16498
+ (_b = _this5.processorElement) === null || _b === void 0 ? void 0 : _b.remove();
16499
+ _this5.processorElement = undefined;
16418
16500
  }
16419
16501
  // apply original track constraints in case the processor changed them
16420
- yield _this4._mediaStreamTrack.applyConstraints(_this4._constraints);
16502
+ yield _this5._mediaStreamTrack.applyConstraints(_this5._constraints);
16421
16503
  // force re-setting of the mediaStreamTrack on the sender
16422
- yield _this4.setMediaStreamTrack(_this4._mediaStreamTrack, true);
16423
- _this4.emit(TrackEvent.TrackProcessorUpdate);
16504
+ yield _this5.setMediaStreamTrack(_this5._mediaStreamTrack, true);
16505
+ _this5.emit(TrackEvent.TrackProcessorUpdate);
16424
16506
  }();
16425
16507
  });
16426
16508
  }
@@ -16614,13 +16696,13 @@ class LocalAudioTrack extends LocalTrack {
16614
16696
  setProcessor(processor) {
16615
16697
  return __awaiter(this, void 0, void 0, function* () {
16616
16698
  var _a;
16617
- const unlock = yield this.processorLock.lock();
16699
+ const unlock = yield this.trackChangeLock.lock();
16618
16700
  try {
16619
16701
  if (!isReactNative() && !this.audioContext) {
16620
16702
  throw Error('Audio context needs to be set on LocalAudioTrack in order to enable processors');
16621
16703
  }
16622
16704
  if (this.processor) {
16623
- yield this.stopProcessor();
16705
+ yield this.internalStopProcessor();
16624
16706
  }
16625
16707
  const processorOptions = {
16626
16708
  kind: this.kind,
@@ -19004,30 +19086,71 @@ class BaseStreamReader {
19004
19086
  get info() {
19005
19087
  return this._info;
19006
19088
  }
19007
- constructor(info, stream, totalByteSize) {
19089
+ /** @internal */
19090
+ validateBytesReceived() {
19091
+ let doneReceiving = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
19092
+ if (typeof this.totalByteSize !== 'number' || this.totalByteSize === 0) {
19093
+ return;
19094
+ }
19095
+ if (doneReceiving && this.bytesReceived < this.totalByteSize) {
19096
+ throw new DataStreamError("Not enough chunk(s) received - expected ".concat(this.totalByteSize, " bytes of data total, only received ").concat(this.bytesReceived, " bytes"), DataStreamErrorReason.Incomplete);
19097
+ } else if (this.bytesReceived > this.totalByteSize) {
19098
+ throw new DataStreamError("Extra chunk(s) received - expected ".concat(this.totalByteSize, " bytes of data total, received ").concat(this.bytesReceived, " bytes"), DataStreamErrorReason.LengthExceeded);
19099
+ }
19100
+ }
19101
+ constructor(info, stream, totalByteSize, outOfBandFailureRejectingFuture) {
19008
19102
  this.reader = stream;
19009
19103
  this.totalByteSize = totalByteSize;
19010
19104
  this._info = info;
19011
19105
  this.bytesReceived = 0;
19106
+ this.outOfBandFailureRejectingFuture = outOfBandFailureRejectingFuture;
19012
19107
  }
19013
19108
  }
19014
19109
  class ByteStreamReader extends BaseStreamReader {
19015
19110
  handleChunkReceived(chunk) {
19016
19111
  var _a;
19017
19112
  this.bytesReceived += chunk.content.byteLength;
19113
+ this.validateBytesReceived();
19018
19114
  const currentProgress = this.totalByteSize ? this.bytesReceived / this.totalByteSize : undefined;
19019
19115
  (_a = this.onProgress) === null || _a === void 0 ? void 0 : _a.call(this, currentProgress);
19020
19116
  }
19021
19117
  [Symbol.asyncIterator]() {
19022
19118
  const reader = this.reader.getReader();
19119
+ let rejectingSignalFuture = new Future();
19120
+ let activeSignal = null;
19121
+ let onAbort = null;
19122
+ if (this.signal) {
19123
+ const signal = this.signal;
19124
+ onAbort = () => {
19125
+ var _a;
19126
+ (_a = rejectingSignalFuture.reject) === null || _a === void 0 ? void 0 : _a.call(rejectingSignalFuture, signal.reason);
19127
+ };
19128
+ signal.addEventListener('abort', onAbort);
19129
+ activeSignal = signal;
19130
+ }
19131
+ const cleanup = () => {
19132
+ reader.releaseLock();
19133
+ if (activeSignal && onAbort) {
19134
+ activeSignal.removeEventListener('abort', onAbort);
19135
+ }
19136
+ this.signal = undefined;
19137
+ };
19023
19138
  return {
19024
19139
  next: () => __awaiter(this, void 0, void 0, function* () {
19140
+ var _a, _b;
19025
19141
  try {
19026
19142
  const {
19027
19143
  done,
19028
19144
  value
19029
- } = yield reader.read();
19145
+ } = yield Promise.race([reader.read(),
19146
+ // Rejects if this.signal is aborted
19147
+ rejectingSignalFuture.promise,
19148
+ // Rejects if something external says it should, like a participant disconnecting, etc
19149
+ (_b = (_a = this.outOfBandFailureRejectingFuture) === null || _a === void 0 ? void 0 : _a.promise) !== null && _b !== void 0 ? _b : new Promise(() => {
19150
+ /* never resolves */
19151
+ })]);
19030
19152
  if (done) {
19153
+ this.validateBytesReceived(true);
19031
19154
  return {
19032
19155
  done: true,
19033
19156
  value: undefined
@@ -19039,17 +19162,16 @@ class ByteStreamReader extends BaseStreamReader {
19039
19162
  value: value.content
19040
19163
  };
19041
19164
  }
19042
- } catch (error) {
19043
- // TODO handle errors
19044
- return {
19045
- done: true,
19046
- value: undefined
19047
- };
19165
+ } catch (err) {
19166
+ cleanup();
19167
+ throw err;
19048
19168
  }
19049
19169
  }),
19170
+ // note: `return` runs only for premature exits, see:
19171
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#errors_during_iteration
19050
19172
  return() {
19051
19173
  return __awaiter(this, void 0, void 0, function* () {
19052
- reader.releaseLock();
19174
+ cleanup();
19053
19175
  return {
19054
19176
  done: true,
19055
19177
  value: undefined
@@ -19058,29 +19180,45 @@ class ByteStreamReader extends BaseStreamReader {
19058
19180
  }
19059
19181
  };
19060
19182
  }
19183
+ /**
19184
+ * Injects an AbortSignal, which if aborted, will terminate the currently active
19185
+ * stream iteration operation.
19186
+ *
19187
+ * Note that when using AbortSignal.timeout(...), the timeout applies across
19188
+ * the whole iteration operation, not just one individual chunk read.
19189
+ */
19190
+ withAbortSignal(signal) {
19191
+ this.signal = signal;
19192
+ return this;
19193
+ }
19061
19194
  readAll() {
19062
- return __awaiter(this, void 0, void 0, function* () {
19063
- var _a, e_1, _b, _c;
19064
- let chunks = new Set();
19065
- try {
19066
- for (var _d = true, _e = __asyncValues(this), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
19067
- _c = _f.value;
19068
- _d = false;
19069
- const chunk = _c;
19070
- chunks.add(chunk);
19071
- }
19072
- } catch (e_1_1) {
19073
- e_1 = {
19074
- error: e_1_1
19075
- };
19076
- } finally {
19195
+ return __awaiter(this, arguments, void 0, function () {
19196
+ var _this = this;
19197
+ let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
19198
+ return function* () {
19199
+ var _a, e_1, _b, _c;
19200
+ let chunks = new Set();
19201
+ const iterator = opts.signal ? _this.withAbortSignal(opts.signal) : _this;
19077
19202
  try {
19078
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
19203
+ for (var _d = true, iterator_1 = __asyncValues(iterator), iterator_1_1; iterator_1_1 = yield iterator_1.next(), _a = iterator_1_1.done, !_a; _d = true) {
19204
+ _c = iterator_1_1.value;
19205
+ _d = false;
19206
+ const chunk = _c;
19207
+ chunks.add(chunk);
19208
+ }
19209
+ } catch (e_1_1) {
19210
+ e_1 = {
19211
+ error: e_1_1
19212
+ };
19079
19213
  } finally {
19080
- if (e_1) throw e_1.error;
19214
+ try {
19215
+ if (!_d && !_a && (_b = iterator_1.return)) yield _b.call(iterator_1);
19216
+ } finally {
19217
+ if (e_1) throw e_1.error;
19218
+ }
19081
19219
  }
19082
- }
19083
- return Array.from(chunks);
19220
+ return Array.from(chunks);
19221
+ }();
19084
19222
  });
19085
19223
  }
19086
19224
  }
@@ -19092,8 +19230,8 @@ class TextStreamReader extends BaseStreamReader {
19092
19230
  * A TextStreamReader instance can be used as an AsyncIterator that returns the entire string
19093
19231
  * that has been received up to the current point in time.
19094
19232
  */
19095
- constructor(info, stream, totalChunkCount) {
19096
- super(info, stream, totalChunkCount);
19233
+ constructor(info, stream, totalChunkCount, outOfBandFailureRejectingFuture) {
19234
+ super(info, stream, totalChunkCount, outOfBandFailureRejectingFuture);
19097
19235
  this.receivedChunks = new Map();
19098
19236
  }
19099
19237
  handleChunkReceived(chunk) {
@@ -19106,6 +19244,7 @@ class TextStreamReader extends BaseStreamReader {
19106
19244
  }
19107
19245
  this.receivedChunks.set(index, chunk);
19108
19246
  this.bytesReceived += chunk.content.byteLength;
19247
+ this.validateBytesReceived();
19109
19248
  const currentProgress = this.totalByteSize ? this.bytesReceived / this.totalByteSize : undefined;
19110
19249
  (_a = this.onProgress) === null || _a === void 0 ? void 0 : _a.call(this, currentProgress);
19111
19250
  }
@@ -19116,37 +19255,71 @@ class TextStreamReader extends BaseStreamReader {
19116
19255
  */
19117
19256
  [Symbol.asyncIterator]() {
19118
19257
  const reader = this.reader.getReader();
19119
- const decoder = new TextDecoder();
19258
+ const decoder = new TextDecoder('utf-8', {
19259
+ fatal: true
19260
+ });
19261
+ let rejectingSignalFuture = new Future();
19262
+ let activeSignal = null;
19263
+ let onAbort = null;
19264
+ if (this.signal) {
19265
+ const signal = this.signal;
19266
+ onAbort = () => {
19267
+ var _a;
19268
+ (_a = rejectingSignalFuture.reject) === null || _a === void 0 ? void 0 : _a.call(rejectingSignalFuture, signal.reason);
19269
+ };
19270
+ signal.addEventListener('abort', onAbort);
19271
+ activeSignal = signal;
19272
+ }
19273
+ const cleanup = () => {
19274
+ reader.releaseLock();
19275
+ if (activeSignal && onAbort) {
19276
+ activeSignal.removeEventListener('abort', onAbort);
19277
+ }
19278
+ this.signal = undefined;
19279
+ };
19120
19280
  return {
19121
19281
  next: () => __awaiter(this, void 0, void 0, function* () {
19282
+ var _a, _b;
19122
19283
  try {
19123
19284
  const {
19124
19285
  done,
19125
19286
  value
19126
- } = yield reader.read();
19287
+ } = yield Promise.race([reader.read(),
19288
+ // Rejects if this.signal is aborted
19289
+ rejectingSignalFuture.promise,
19290
+ // Rejects if something external says it should, like a participant disconnecting, etc
19291
+ (_b = (_a = this.outOfBandFailureRejectingFuture) === null || _a === void 0 ? void 0 : _a.promise) !== null && _b !== void 0 ? _b : new Promise(() => {
19292
+ /* never resolves */
19293
+ })]);
19127
19294
  if (done) {
19295
+ this.validateBytesReceived(true);
19128
19296
  return {
19129
19297
  done: true,
19130
19298
  value: undefined
19131
19299
  };
19132
19300
  } else {
19133
19301
  this.handleChunkReceived(value);
19302
+ let decodedResult;
19303
+ try {
19304
+ decodedResult = decoder.decode(value.content);
19305
+ } catch (err) {
19306
+ throw new DataStreamError("Cannot decode datastream chunk ".concat(value.chunkIndex, " as text: ").concat(err), DataStreamErrorReason.DecodeFailed);
19307
+ }
19134
19308
  return {
19135
19309
  done: false,
19136
- value: decoder.decode(value.content)
19310
+ value: decodedResult
19137
19311
  };
19138
19312
  }
19139
- } catch (error) {
19140
- // TODO handle errors
19141
- return {
19142
- done: true,
19143
- value: undefined
19144
- };
19313
+ } catch (err) {
19314
+ cleanup();
19315
+ throw err;
19145
19316
  }
19146
19317
  }),
19318
+ // note: `return` runs only for premature exits, see:
19319
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#errors_during_iteration
19147
19320
  return() {
19148
19321
  return __awaiter(this, void 0, void 0, function* () {
19149
- reader.releaseLock();
19322
+ cleanup();
19150
19323
  return {
19151
19324
  done: true,
19152
19325
  value: undefined
@@ -19155,138 +19328,602 @@ class TextStreamReader extends BaseStreamReader {
19155
19328
  }
19156
19329
  };
19157
19330
  }
19331
+ /**
19332
+ * Injects an AbortSignal, which if aborted, will terminate the currently active
19333
+ * stream iteration operation.
19334
+ *
19335
+ * Note that when using AbortSignal.timeout(...), the timeout applies across
19336
+ * the whole iteration operation, not just one individual chunk read.
19337
+ */
19338
+ withAbortSignal(signal) {
19339
+ this.signal = signal;
19340
+ return this;
19341
+ }
19158
19342
  readAll() {
19159
- return __awaiter(this, void 0, void 0, function* () {
19160
- var _a, e_2, _b, _c;
19161
- let finalString = '';
19162
- try {
19163
- for (var _d = true, _e = __asyncValues(this), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
19164
- _c = _f.value;
19165
- _d = false;
19166
- const chunk = _c;
19167
- finalString += chunk;
19168
- }
19169
- } catch (e_2_1) {
19170
- e_2 = {
19171
- error: e_2_1
19172
- };
19173
- } finally {
19343
+ return __awaiter(this, arguments, void 0, function () {
19344
+ var _this2 = this;
19345
+ let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
19346
+ return function* () {
19347
+ var _a, e_2, _b, _c;
19348
+ let finalString = '';
19349
+ const iterator = opts.signal ? _this2.withAbortSignal(opts.signal) : _this2;
19174
19350
  try {
19175
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
19351
+ for (var _d = true, iterator_2 = __asyncValues(iterator), iterator_2_1; iterator_2_1 = yield iterator_2.next(), _a = iterator_2_1.done, !_a; _d = true) {
19352
+ _c = iterator_2_1.value;
19353
+ _d = false;
19354
+ const chunk = _c;
19355
+ finalString += chunk;
19356
+ }
19357
+ } catch (e_2_1) {
19358
+ e_2 = {
19359
+ error: e_2_1
19360
+ };
19176
19361
  } finally {
19177
- if (e_2) throw e_2.error;
19362
+ try {
19363
+ if (!_d && !_a && (_b = iterator_2.return)) yield _b.call(iterator_2);
19364
+ } finally {
19365
+ if (e_2) throw e_2.error;
19366
+ }
19178
19367
  }
19179
- }
19180
- return finalString;
19368
+ return finalString;
19369
+ }();
19181
19370
  });
19182
19371
  }
19183
19372
  }
19184
19373
 
19185
- class BaseStreamWriter {
19186
- constructor(writableStream, info, onClose) {
19187
- this.writableStream = writableStream;
19188
- this.defaultWriter = writableStream.getWriter();
19189
- this.onClose = onClose;
19190
- this.info = info;
19191
- }
19192
- write(chunk) {
19193
- return this.defaultWriter.write(chunk);
19194
- }
19195
- close() {
19196
- return __awaiter(this, void 0, void 0, function* () {
19197
- var _a;
19198
- yield this.defaultWriter.close();
19199
- this.defaultWriter.releaseLock();
19200
- (_a = this.onClose) === null || _a === void 0 ? void 0 : _a.call(this);
19201
- });
19374
+ class IncomingDataStreamManager {
19375
+ constructor() {
19376
+ this.log = livekitLogger;
19377
+ this.byteStreamControllers = new Map();
19378
+ this.textStreamControllers = new Map();
19379
+ this.byteStreamHandlers = new Map();
19380
+ this.textStreamHandlers = new Map();
19202
19381
  }
19203
- }
19204
- class TextStreamWriter extends BaseStreamWriter {}
19205
- class ByteStreamWriter extends BaseStreamWriter {}
19206
-
19207
- class RemoteTrack extends Track {
19208
- constructor(mediaTrack, sid, kind, receiver, loggerOptions) {
19209
- super(mediaTrack, kind, loggerOptions);
19210
- this.sid = sid;
19211
- this.receiver = receiver;
19382
+ registerTextStreamHandler(topic, callback) {
19383
+ if (this.textStreamHandlers.has(topic)) {
19384
+ throw new DataStreamError("A text stream handler for topic \"".concat(topic, "\" has already been set."), DataStreamErrorReason.HandlerAlreadyRegistered);
19385
+ }
19386
+ this.textStreamHandlers.set(topic, callback);
19212
19387
  }
19213
- get isLocal() {
19214
- return false;
19388
+ unregisterTextStreamHandler(topic) {
19389
+ this.textStreamHandlers.delete(topic);
19215
19390
  }
19216
- /** @internal */
19217
- setMuted(muted) {
19218
- if (this.isMuted !== muted) {
19219
- this.isMuted = muted;
19220
- this._mediaStreamTrack.enabled = !muted;
19221
- this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
19391
+ registerByteStreamHandler(topic, callback) {
19392
+ if (this.byteStreamHandlers.has(topic)) {
19393
+ throw new DataStreamError("A byte stream handler for topic \"".concat(topic, "\" has already been set."), DataStreamErrorReason.HandlerAlreadyRegistered);
19222
19394
  }
19395
+ this.byteStreamHandlers.set(topic, callback);
19223
19396
  }
19224
- /** @internal */
19225
- setMediaStream(stream) {
19226
- // this is needed to determine when the track is finished
19227
- this.mediaStream = stream;
19228
- const onRemoveTrack = event => {
19229
- if (event.track === this._mediaStreamTrack) {
19230
- stream.removeEventListener('removetrack', onRemoveTrack);
19231
- if (this.receiver && 'playoutDelayHint' in this.receiver) {
19232
- this.receiver.playoutDelayHint = undefined;
19233
- }
19234
- this.receiver = undefined;
19235
- this._currentBitrate = 0;
19236
- this.emit(TrackEvent.Ended, this);
19237
- }
19238
- };
19239
- stream.addEventListener('removetrack', onRemoveTrack);
19397
+ unregisterByteStreamHandler(topic) {
19398
+ this.byteStreamHandlers.delete(topic);
19240
19399
  }
19241
- start() {
19242
- this.startMonitor();
19243
- // use `enabled` of track to enable re-use of transceiver
19244
- super.enable();
19400
+ clearHandlersAndControllers() {
19401
+ this.byteStreamControllers.clear();
19402
+ this.textStreamControllers.clear();
19403
+ this.byteStreamHandlers.clear();
19404
+ this.textStreamHandlers.clear();
19245
19405
  }
19246
- stop() {
19247
- this.stopMonitor();
19248
- // use `enabled` of track to enable re-use of transceiver
19249
- super.disable();
19406
+ validateParticipantHasNoActiveDataStreams(participantIdentity) {
19407
+ var _a, _b, _c, _d;
19408
+ // Terminate any in flight data stream receives from the given participant
19409
+ const textStreamsBeingSentByDisconnectingParticipant = Array.from(this.textStreamControllers.entries()).filter(entry => entry[1].sendingParticipantIdentity === participantIdentity);
19410
+ const byteStreamsBeingSentByDisconnectingParticipant = Array.from(this.byteStreamControllers.entries()).filter(entry => entry[1].sendingParticipantIdentity === participantIdentity);
19411
+ if (textStreamsBeingSentByDisconnectingParticipant.length > 0 || byteStreamsBeingSentByDisconnectingParticipant.length > 0) {
19412
+ const abnormalEndError = new DataStreamError("Participant ".concat(participantIdentity, " unexpectedly disconnected in the middle of sending data"), DataStreamErrorReason.AbnormalEnd);
19413
+ for (const [id, controller] of byteStreamsBeingSentByDisconnectingParticipant) {
19414
+ (_b = (_a = controller.outOfBandFailureRejectingFuture).reject) === null || _b === void 0 ? void 0 : _b.call(_a, abnormalEndError);
19415
+ this.byteStreamControllers.delete(id);
19416
+ }
19417
+ for (const [id, controller] of textStreamsBeingSentByDisconnectingParticipant) {
19418
+ (_d = (_c = controller.outOfBandFailureRejectingFuture).reject) === null || _d === void 0 ? void 0 : _d.call(_c, abnormalEndError);
19419
+ this.textStreamControllers.delete(id);
19420
+ }
19421
+ }
19250
19422
  }
19251
- /**
19252
- * Gets the RTCStatsReport for the RemoteTrack's underlying RTCRtpReceiver
19253
- * See https://developer.mozilla.org/en-US/docs/Web/API/RTCStatsReport
19254
- *
19255
- * @returns Promise<RTCStatsReport> | undefined
19256
- */
19257
- getRTCStatsReport() {
19423
+ handleDataStreamPacket(packet) {
19258
19424
  return __awaiter(this, void 0, void 0, function* () {
19259
- var _a;
19260
- if (!((_a = this.receiver) === null || _a === void 0 ? void 0 : _a.getStats)) {
19261
- return;
19425
+ switch (packet.value.case) {
19426
+ case 'streamHeader':
19427
+ return this.handleStreamHeader(packet.value.value, packet.participantIdentity);
19428
+ case 'streamChunk':
19429
+ return this.handleStreamChunk(packet.value.value);
19430
+ case 'streamTrailer':
19431
+ return this.handleStreamTrailer(packet.value.value);
19432
+ default:
19433
+ throw new Error("DataPacket of value \"".concat(packet.value.case, "\" is not data stream related!"));
19262
19434
  }
19263
- const statsReport = yield this.receiver.getStats();
19264
- return statsReport;
19265
19435
  });
19266
19436
  }
19267
- /**
19268
- * Allows to set a playout delay (in seconds) for this track.
19269
- * A higher value allows for more buffering of the track in the browser
19270
- * and will result in a delay of media being played back of `delayInSeconds`
19271
- */
19272
- setPlayoutDelay(delayInSeconds) {
19273
- if (this.receiver) {
19274
- if ('playoutDelayHint' in this.receiver) {
19275
- this.receiver.playoutDelayHint = delayInSeconds;
19276
- } else {
19277
- this.log.warn('Playout delay not supported in this browser');
19278
- }
19279
- } else {
19280
- this.log.warn('Cannot set playout delay, track already ended');
19281
- }
19282
- }
19283
- /**
19284
- * Returns the current playout delay (in seconds) of this track.
19285
- */
19286
- getPlayoutDelay() {
19287
- if (this.receiver) {
19288
- if ('playoutDelayHint' in this.receiver) {
19289
- return this.receiver.playoutDelayHint;
19437
+ handleStreamHeader(streamHeader, participantIdentity) {
19438
+ return __awaiter(this, void 0, void 0, function* () {
19439
+ var _a;
19440
+ if (streamHeader.contentHeader.case === 'byteHeader') {
19441
+ const streamHandlerCallback = this.byteStreamHandlers.get(streamHeader.topic);
19442
+ if (!streamHandlerCallback) {
19443
+ this.log.debug('ignoring incoming byte stream due to no handler for topic', streamHeader.topic);
19444
+ return;
19445
+ }
19446
+ let streamController;
19447
+ const outOfBandFailureRejectingFuture = new Future();
19448
+ const info = {
19449
+ id: streamHeader.streamId,
19450
+ name: (_a = streamHeader.contentHeader.value.name) !== null && _a !== void 0 ? _a : 'unknown',
19451
+ mimeType: streamHeader.mimeType,
19452
+ size: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,
19453
+ topic: streamHeader.topic,
19454
+ timestamp: bigIntToNumber(streamHeader.timestamp),
19455
+ attributes: streamHeader.attributes
19456
+ };
19457
+ const stream = new ReadableStream({
19458
+ start: controller => {
19459
+ streamController = controller;
19460
+ if (this.textStreamControllers.has(streamHeader.streamId)) {
19461
+ throw new DataStreamError("A data stream read is already in progress for a stream with id ".concat(streamHeader.streamId, "."), DataStreamErrorReason.AlreadyOpened);
19462
+ }
19463
+ this.byteStreamControllers.set(streamHeader.streamId, {
19464
+ info,
19465
+ controller: streamController,
19466
+ startTime: Date.now(),
19467
+ sendingParticipantIdentity: participantIdentity,
19468
+ outOfBandFailureRejectingFuture
19469
+ });
19470
+ }
19471
+ });
19472
+ streamHandlerCallback(new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength), outOfBandFailureRejectingFuture), {
19473
+ identity: participantIdentity
19474
+ });
19475
+ } else if (streamHeader.contentHeader.case === 'textHeader') {
19476
+ const streamHandlerCallback = this.textStreamHandlers.get(streamHeader.topic);
19477
+ if (!streamHandlerCallback) {
19478
+ this.log.debug('ignoring incoming text stream due to no handler for topic', streamHeader.topic);
19479
+ return;
19480
+ }
19481
+ let streamController;
19482
+ const outOfBandFailureRejectingFuture = new Future();
19483
+ const info = {
19484
+ id: streamHeader.streamId,
19485
+ mimeType: streamHeader.mimeType,
19486
+ size: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,
19487
+ topic: streamHeader.topic,
19488
+ timestamp: Number(streamHeader.timestamp),
19489
+ attributes: streamHeader.attributes
19490
+ };
19491
+ const stream = new ReadableStream({
19492
+ start: controller => {
19493
+ streamController = controller;
19494
+ if (this.textStreamControllers.has(streamHeader.streamId)) {
19495
+ throw new DataStreamError("A data stream read is already in progress for a stream with id ".concat(streamHeader.streamId, "."), DataStreamErrorReason.AlreadyOpened);
19496
+ }
19497
+ this.textStreamControllers.set(streamHeader.streamId, {
19498
+ info,
19499
+ controller: streamController,
19500
+ startTime: Date.now(),
19501
+ sendingParticipantIdentity: participantIdentity,
19502
+ outOfBandFailureRejectingFuture
19503
+ });
19504
+ }
19505
+ });
19506
+ streamHandlerCallback(new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength), outOfBandFailureRejectingFuture), {
19507
+ identity: participantIdentity
19508
+ });
19509
+ }
19510
+ });
19511
+ }
19512
+ handleStreamChunk(chunk) {
19513
+ const fileBuffer = this.byteStreamControllers.get(chunk.streamId);
19514
+ if (fileBuffer) {
19515
+ if (chunk.content.length > 0) {
19516
+ fileBuffer.controller.enqueue(chunk);
19517
+ }
19518
+ }
19519
+ const textBuffer = this.textStreamControllers.get(chunk.streamId);
19520
+ if (textBuffer) {
19521
+ if (chunk.content.length > 0) {
19522
+ textBuffer.controller.enqueue(chunk);
19523
+ }
19524
+ }
19525
+ }
19526
+ handleStreamTrailer(trailer) {
19527
+ const textBuffer = this.textStreamControllers.get(trailer.streamId);
19528
+ if (textBuffer) {
19529
+ textBuffer.info.attributes = Object.assign(Object.assign({}, textBuffer.info.attributes), trailer.attributes);
19530
+ textBuffer.controller.close();
19531
+ this.textStreamControllers.delete(trailer.streamId);
19532
+ }
19533
+ const fileBuffer = this.byteStreamControllers.get(trailer.streamId);
19534
+ if (fileBuffer) {
19535
+ {
19536
+ fileBuffer.info.attributes = Object.assign(Object.assign({}, fileBuffer.info.attributes), trailer.attributes);
19537
+ fileBuffer.controller.close();
19538
+ this.byteStreamControllers.delete(trailer.streamId);
19539
+ }
19540
+ }
19541
+ }
19542
+ }
19543
+
19544
+ class BaseStreamWriter {
19545
+ constructor(writableStream, info, onClose) {
19546
+ this.writableStream = writableStream;
19547
+ this.defaultWriter = writableStream.getWriter();
19548
+ this.onClose = onClose;
19549
+ this.info = info;
19550
+ }
19551
+ write(chunk) {
19552
+ return this.defaultWriter.write(chunk);
19553
+ }
19554
+ close() {
19555
+ return __awaiter(this, void 0, void 0, function* () {
19556
+ var _a;
19557
+ yield this.defaultWriter.close();
19558
+ this.defaultWriter.releaseLock();
19559
+ (_a = this.onClose) === null || _a === void 0 ? void 0 : _a.call(this);
19560
+ });
19561
+ }
19562
+ }
19563
+ class TextStreamWriter extends BaseStreamWriter {}
19564
+ class ByteStreamWriter extends BaseStreamWriter {}
19565
+
19566
+ const STREAM_CHUNK_SIZE = 15000;
19567
+ /**
19568
+ * Manages sending custom user data via data channels.
19569
+ * @internal
19570
+ */
19571
+ class OutgoingDataStreamManager {
19572
+ constructor(engine, log) {
19573
+ this.engine = engine;
19574
+ this.log = log;
19575
+ }
19576
+ setupEngine(engine) {
19577
+ this.engine = engine;
19578
+ }
19579
+ /** {@inheritDoc LocalParticipant.sendText} */
19580
+ sendText(text, options) {
19581
+ return __awaiter(this, void 0, void 0, function* () {
19582
+ var _a;
19583
+ const streamId = crypto.randomUUID();
19584
+ const textInBytes = new TextEncoder().encode(text);
19585
+ const totalTextLength = textInBytes.byteLength;
19586
+ const fileIds = (_a = options === null || options === void 0 ? void 0 : options.attachments) === null || _a === void 0 ? void 0 : _a.map(() => crypto.randomUUID());
19587
+ const progresses = new Array(fileIds ? fileIds.length + 1 : 1).fill(0);
19588
+ const handleProgress = (progress, idx) => {
19589
+ var _a;
19590
+ progresses[idx] = progress;
19591
+ const totalProgress = progresses.reduce((acc, val) => acc + val, 0);
19592
+ (_a = options === null || options === void 0 ? void 0 : options.onProgress) === null || _a === void 0 ? void 0 : _a.call(options, totalProgress);
19593
+ };
19594
+ const writer = yield this.streamText({
19595
+ streamId,
19596
+ totalSize: totalTextLength,
19597
+ destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities,
19598
+ topic: options === null || options === void 0 ? void 0 : options.topic,
19599
+ attachedStreamIds: fileIds,
19600
+ attributes: options === null || options === void 0 ? void 0 : options.attributes
19601
+ });
19602
+ yield writer.write(text);
19603
+ // set text part of progress to 1
19604
+ handleProgress(1, 0);
19605
+ yield writer.close();
19606
+ if ((options === null || options === void 0 ? void 0 : options.attachments) && fileIds) {
19607
+ yield Promise.all(options.attachments.map((file, idx) => __awaiter(this, void 0, void 0, function* () {
19608
+ return this._sendFile(fileIds[idx], file, {
19609
+ topic: options.topic,
19610
+ mimeType: file.type,
19611
+ onProgress: progress => {
19612
+ handleProgress(progress, idx + 1);
19613
+ }
19614
+ });
19615
+ })));
19616
+ }
19617
+ return writer.info;
19618
+ });
19619
+ }
19620
+ /**
19621
+ * @internal
19622
+ * @experimental CAUTION, might get removed in a minor release
19623
+ */
19624
+ streamText(options) {
19625
+ return __awaiter(this, void 0, void 0, function* () {
19626
+ var _a, _b;
19627
+ const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
19628
+ const info = {
19629
+ id: streamId,
19630
+ mimeType: 'text/plain',
19631
+ timestamp: Date.now(),
19632
+ topic: (_b = options === null || options === void 0 ? void 0 : options.topic) !== null && _b !== void 0 ? _b : '',
19633
+ size: options === null || options === void 0 ? void 0 : options.totalSize,
19634
+ attributes: options === null || options === void 0 ? void 0 : options.attributes
19635
+ };
19636
+ const header = new DataStream_Header({
19637
+ streamId,
19638
+ mimeType: info.mimeType,
19639
+ topic: info.topic,
19640
+ timestamp: numberToBigInt(info.timestamp),
19641
+ totalLength: numberToBigInt(options === null || options === void 0 ? void 0 : options.totalSize),
19642
+ attributes: info.attributes,
19643
+ contentHeader: {
19644
+ case: 'textHeader',
19645
+ value: new DataStream_TextHeader({
19646
+ version: options === null || options === void 0 ? void 0 : options.version,
19647
+ attachedStreamIds: options === null || options === void 0 ? void 0 : options.attachedStreamIds,
19648
+ replyToStreamId: options === null || options === void 0 ? void 0 : options.replyToStreamId,
19649
+ operationType: (options === null || options === void 0 ? void 0 : options.type) === 'update' ? DataStream_OperationType.UPDATE : DataStream_OperationType.CREATE
19650
+ })
19651
+ }
19652
+ });
19653
+ const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
19654
+ const packet = new DataPacket({
19655
+ destinationIdentities,
19656
+ value: {
19657
+ case: 'streamHeader',
19658
+ value: header
19659
+ }
19660
+ });
19661
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
19662
+ let chunkId = 0;
19663
+ const engine = this.engine;
19664
+ const writableStream = new WritableStream({
19665
+ // Implement the sink
19666
+ write(text) {
19667
+ return __awaiter(this, void 0, void 0, function* () {
19668
+ for (const textByteChunk of splitUtf8(text, STREAM_CHUNK_SIZE)) {
19669
+ yield engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
19670
+ const chunk = new DataStream_Chunk({
19671
+ content: textByteChunk,
19672
+ streamId,
19673
+ chunkIndex: numberToBigInt(chunkId)
19674
+ });
19675
+ const chunkPacket = new DataPacket({
19676
+ destinationIdentities,
19677
+ value: {
19678
+ case: 'streamChunk',
19679
+ value: chunk
19680
+ }
19681
+ });
19682
+ yield engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
19683
+ chunkId += 1;
19684
+ }
19685
+ });
19686
+ },
19687
+ close() {
19688
+ return __awaiter(this, void 0, void 0, function* () {
19689
+ const trailer = new DataStream_Trailer({
19690
+ streamId
19691
+ });
19692
+ const trailerPacket = new DataPacket({
19693
+ destinationIdentities,
19694
+ value: {
19695
+ case: 'streamTrailer',
19696
+ value: trailer
19697
+ }
19698
+ });
19699
+ yield engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
19700
+ });
19701
+ },
19702
+ abort(err) {
19703
+ console.log('Sink error:', err);
19704
+ // TODO handle aborts to signal something to receiver side
19705
+ }
19706
+ });
19707
+ let onEngineClose = () => __awaiter(this, void 0, void 0, function* () {
19708
+ yield writer.close();
19709
+ });
19710
+ engine.once(EngineEvent.Closing, onEngineClose);
19711
+ const writer = new TextStreamWriter(writableStream, info, () => this.engine.off(EngineEvent.Closing, onEngineClose));
19712
+ return writer;
19713
+ });
19714
+ }
19715
+ sendFile(file, options) {
19716
+ return __awaiter(this, void 0, void 0, function* () {
19717
+ const streamId = crypto.randomUUID();
19718
+ yield this._sendFile(streamId, file, options);
19719
+ return {
19720
+ id: streamId
19721
+ };
19722
+ });
19723
+ }
19724
+ _sendFile(streamId, file, options) {
19725
+ return __awaiter(this, void 0, void 0, function* () {
19726
+ var _a;
19727
+ const writer = yield this.streamBytes({
19728
+ streamId,
19729
+ totalSize: file.size,
19730
+ name: file.name,
19731
+ mimeType: (_a = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _a !== void 0 ? _a : file.type,
19732
+ topic: options === null || options === void 0 ? void 0 : options.topic,
19733
+ destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities
19734
+ });
19735
+ const reader = file.stream().getReader();
19736
+ while (true) {
19737
+ const {
19738
+ done,
19739
+ value
19740
+ } = yield reader.read();
19741
+ if (done) {
19742
+ break;
19743
+ }
19744
+ yield writer.write(value);
19745
+ }
19746
+ yield writer.close();
19747
+ return writer.info;
19748
+ });
19749
+ }
19750
+ streamBytes(options) {
19751
+ return __awaiter(this, void 0, void 0, function* () {
19752
+ var _a, _b, _c, _d, _e;
19753
+ const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
19754
+ const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
19755
+ const info = {
19756
+ id: streamId,
19757
+ mimeType: (_b = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _b !== void 0 ? _b : 'application/octet-stream',
19758
+ topic: (_c = options === null || options === void 0 ? void 0 : options.topic) !== null && _c !== void 0 ? _c : '',
19759
+ timestamp: Date.now(),
19760
+ attributes: options === null || options === void 0 ? void 0 : options.attributes,
19761
+ size: options === null || options === void 0 ? void 0 : options.totalSize,
19762
+ name: (_d = options === null || options === void 0 ? void 0 : options.name) !== null && _d !== void 0 ? _d : 'unknown'
19763
+ };
19764
+ const header = new DataStream_Header({
19765
+ totalLength: numberToBigInt((_e = info.size) !== null && _e !== void 0 ? _e : 0),
19766
+ mimeType: info.mimeType,
19767
+ streamId,
19768
+ topic: info.topic,
19769
+ timestamp: numberToBigInt(Date.now()),
19770
+ attributes: info.attributes,
19771
+ contentHeader: {
19772
+ case: 'byteHeader',
19773
+ value: new DataStream_ByteHeader({
19774
+ name: info.name
19775
+ })
19776
+ }
19777
+ });
19778
+ const packet = new DataPacket({
19779
+ destinationIdentities,
19780
+ value: {
19781
+ case: 'streamHeader',
19782
+ value: header
19783
+ }
19784
+ });
19785
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
19786
+ let chunkId = 0;
19787
+ const writeMutex = new _();
19788
+ const engine = this.engine;
19789
+ const logLocal = this.log;
19790
+ const writableStream = new WritableStream({
19791
+ write(chunk) {
19792
+ return __awaiter(this, void 0, void 0, function* () {
19793
+ const unlock = yield writeMutex.lock();
19794
+ let byteOffset = 0;
19795
+ try {
19796
+ while (byteOffset < chunk.byteLength) {
19797
+ const subChunk = chunk.slice(byteOffset, byteOffset + STREAM_CHUNK_SIZE);
19798
+ yield engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
19799
+ const chunkPacket = new DataPacket({
19800
+ destinationIdentities,
19801
+ value: {
19802
+ case: 'streamChunk',
19803
+ value: new DataStream_Chunk({
19804
+ content: subChunk,
19805
+ streamId,
19806
+ chunkIndex: numberToBigInt(chunkId)
19807
+ })
19808
+ }
19809
+ });
19810
+ yield engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
19811
+ chunkId += 1;
19812
+ byteOffset += subChunk.byteLength;
19813
+ }
19814
+ } finally {
19815
+ unlock();
19816
+ }
19817
+ });
19818
+ },
19819
+ close() {
19820
+ return __awaiter(this, void 0, void 0, function* () {
19821
+ const trailer = new DataStream_Trailer({
19822
+ streamId
19823
+ });
19824
+ const trailerPacket = new DataPacket({
19825
+ destinationIdentities,
19826
+ value: {
19827
+ case: 'streamTrailer',
19828
+ value: trailer
19829
+ }
19830
+ });
19831
+ yield engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
19832
+ });
19833
+ },
19834
+ abort(err) {
19835
+ logLocal.error('Sink error:', err);
19836
+ }
19837
+ });
19838
+ const byteWriter = new ByteStreamWriter(writableStream, info);
19839
+ return byteWriter;
19840
+ });
19841
+ }
19842
+ }
19843
+
19844
+ class RemoteTrack extends Track {
19845
+ constructor(mediaTrack, sid, kind, receiver, loggerOptions) {
19846
+ super(mediaTrack, kind, loggerOptions);
19847
+ this.sid = sid;
19848
+ this.receiver = receiver;
19849
+ }
19850
+ get isLocal() {
19851
+ return false;
19852
+ }
19853
+ /** @internal */
19854
+ setMuted(muted) {
19855
+ if (this.isMuted !== muted) {
19856
+ this.isMuted = muted;
19857
+ this._mediaStreamTrack.enabled = !muted;
19858
+ this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
19859
+ }
19860
+ }
19861
+ /** @internal */
19862
+ setMediaStream(stream) {
19863
+ // this is needed to determine when the track is finished
19864
+ this.mediaStream = stream;
19865
+ const onRemoveTrack = event => {
19866
+ if (event.track === this._mediaStreamTrack) {
19867
+ stream.removeEventListener('removetrack', onRemoveTrack);
19868
+ if (this.receiver && 'playoutDelayHint' in this.receiver) {
19869
+ this.receiver.playoutDelayHint = undefined;
19870
+ }
19871
+ this.receiver = undefined;
19872
+ this._currentBitrate = 0;
19873
+ this.emit(TrackEvent.Ended, this);
19874
+ }
19875
+ };
19876
+ stream.addEventListener('removetrack', onRemoveTrack);
19877
+ }
19878
+ start() {
19879
+ this.startMonitor();
19880
+ // use `enabled` of track to enable re-use of transceiver
19881
+ super.enable();
19882
+ }
19883
+ stop() {
19884
+ this.stopMonitor();
19885
+ // use `enabled` of track to enable re-use of transceiver
19886
+ super.disable();
19887
+ }
19888
+ /**
19889
+ * Gets the RTCStatsReport for the RemoteTrack's underlying RTCRtpReceiver
19890
+ * See https://developer.mozilla.org/en-US/docs/Web/API/RTCStatsReport
19891
+ *
19892
+ * @returns Promise<RTCStatsReport> | undefined
19893
+ */
19894
+ getRTCStatsReport() {
19895
+ return __awaiter(this, void 0, void 0, function* () {
19896
+ var _a;
19897
+ if (!((_a = this.receiver) === null || _a === void 0 ? void 0 : _a.getStats)) {
19898
+ return;
19899
+ }
19900
+ const statsReport = yield this.receiver.getStats();
19901
+ return statsReport;
19902
+ });
19903
+ }
19904
+ /**
19905
+ * Allows to set a playout delay (in seconds) for this track.
19906
+ * A higher value allows for more buffering of the track in the browser
19907
+ * and will result in a delay of media being played back of `delayInSeconds`
19908
+ */
19909
+ setPlayoutDelay(delayInSeconds) {
19910
+ if (this.receiver) {
19911
+ if ('playoutDelayHint' in this.receiver) {
19912
+ this.receiver.playoutDelayHint = delayInSeconds;
19913
+ } else {
19914
+ this.log.warn('Playout delay not supported in this browser');
19915
+ }
19916
+ } else {
19917
+ this.log.warn('Cannot set playout delay, track already ended');
19918
+ }
19919
+ }
19920
+ /**
19921
+ * Returns the current playout delay (in seconds) of this track.
19922
+ */
19923
+ getPlayoutDelay() {
19924
+ if (this.receiver) {
19925
+ if ('playoutDelayHint' in this.receiver) {
19926
+ return this.receiver.playoutDelayHint;
19290
19927
  } else {
19291
19928
  this.log.warn('Playout delay not supported in this browser');
19292
19929
  }
@@ -19552,6 +20189,15 @@ class RemoteVideoTrack extends RemoteTrack {
19552
20189
  get isAdaptiveStream() {
19553
20190
  return this.adaptiveStreamSettings !== undefined;
19554
20191
  }
20192
+ setStreamState(value) {
20193
+ super.setStreamState(value);
20194
+ console.log('setStreamState', value);
20195
+ if (value === Track.StreamState.Active) {
20196
+ // update visibility for adaptive stream tracks when stream state received from server is active
20197
+ // this is needed to ensure the track is stopped when there's no element attached to it at all
20198
+ this.updateVisibility();
20199
+ }
20200
+ }
19555
20201
  /**
19556
20202
  * Note: When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start
19557
20203
  */
@@ -19701,14 +20347,14 @@ class RemoteVideoTrack extends RemoteTrack {
19701
20347
  this.updateVisibility();
19702
20348
  });
19703
20349
  }
19704
- updateVisibility() {
20350
+ updateVisibility(forceEmit) {
19705
20351
  var _a, _b;
19706
20352
  const lastVisibilityChange = this.elementInfos.reduce((prev, info) => Math.max(prev, info.visibilityChangedAt || 0), 0);
19707
20353
  const backgroundPause = ((_b = (_a = this.adaptiveStreamSettings) === null || _a === void 0 ? void 0 : _a.pauseVideoInBackground) !== null && _b !== void 0 ? _b : true // default to true
19708
20354
  ) ? this.isInBackground : false;
19709
20355
  const isPiPMode = this.elementInfos.some(info => info.pictureInPicture);
19710
20356
  const isVisible = this.elementInfos.some(info => info.visible) && !backgroundPause || isPiPMode;
19711
- if (this.lastVisible === isVisible) {
20357
+ if (this.lastVisible === isVisible && !forceEmit) {
19712
20358
  return;
19713
20359
  }
19714
20360
  if (!isVisible && Date.now() - lastVisibilityChange < REACTION_DELAY) {
@@ -20533,10 +21179,9 @@ function trackPermissionToProto(perms) {
20533
21179
  });
20534
21180
  }
20535
21181
 
20536
- const STREAM_CHUNK_SIZE = 15000;
20537
21182
  class LocalParticipant extends Participant {
20538
21183
  /** @internal */
20539
- constructor(sid, identity, engine, options, roomRpcHandlers) {
21184
+ constructor(sid, identity, engine, options, roomRpcHandlers, roomOutgoingDataStreamManager) {
20540
21185
  super(sid, identity, undefined, undefined, undefined, {
20541
21186
  loggerName: options.loggerName,
20542
21187
  loggerContextCb: () => this.engine.logContext
@@ -20768,6 +21413,7 @@ class LocalParticipant extends Participant {
20768
21413
  this.activeDeviceMap = new Map([['audioinput', 'default'], ['videoinput', 'default'], ['audiooutput', 'default']]);
20769
21414
  this.pendingSignalRequests = new Map();
20770
21415
  this.rpcHandlers = roomRpcHandlers;
21416
+ this.roomOutgoingDataStreamManager = roomOutgoingDataStreamManager;
20771
21417
  }
20772
21418
  get lastCameraError() {
20773
21419
  return this.cameraError;
@@ -21919,6 +22565,7 @@ class LocalParticipant extends Participant {
21919
22565
  yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
21920
22566
  });
21921
22567
  }
22568
+ /** @deprecated Consider migrating to {@link sendText} */
21922
22569
  sendChatMessage(text, options) {
21923
22570
  return __awaiter(this, void 0, void 0, function* () {
21924
22571
  const msg = {
@@ -21940,6 +22587,7 @@ class LocalParticipant extends Participant {
21940
22587
  return msg;
21941
22588
  });
21942
22589
  }
22590
+ /** @deprecated Consider migrating to {@link sendText} */
21943
22591
  editChatMessage(editText, originalMessage) {
21944
22592
  return __awaiter(this, void 0, void 0, function* () {
21945
22593
  const msg = Object.assign(Object.assign({}, originalMessage), {
@@ -21950,276 +22598,61 @@ class LocalParticipant extends Participant {
21950
22598
  value: {
21951
22599
  case: 'chatMessage',
21952
22600
  value: new ChatMessage(Object.assign(Object.assign({}, msg), {
21953
- timestamp: protoInt64.parse(msg.timestamp),
21954
- editTimestamp: protoInt64.parse(msg.editTimestamp)
21955
- }))
21956
- }
21957
- });
21958
- yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
21959
- this.emit(ParticipantEvent.ChatMessage, msg);
21960
- return msg;
21961
- });
21962
- }
21963
- sendText(text, options) {
21964
- return __awaiter(this, void 0, void 0, function* () {
21965
- var _a;
21966
- const streamId = crypto.randomUUID();
21967
- const textInBytes = new TextEncoder().encode(text);
21968
- const totalTextLength = textInBytes.byteLength;
21969
- const fileIds = (_a = options === null || options === void 0 ? void 0 : options.attachments) === null || _a === void 0 ? void 0 : _a.map(() => crypto.randomUUID());
21970
- const progresses = new Array(fileIds ? fileIds.length + 1 : 1).fill(0);
21971
- const handleProgress = (progress, idx) => {
21972
- var _a;
21973
- progresses[idx] = progress;
21974
- const totalProgress = progresses.reduce((acc, val) => acc + val, 0);
21975
- (_a = options === null || options === void 0 ? void 0 : options.onProgress) === null || _a === void 0 ? void 0 : _a.call(options, totalProgress);
21976
- };
21977
- const writer = yield this.streamText({
21978
- streamId,
21979
- totalSize: totalTextLength,
21980
- destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities,
21981
- topic: options === null || options === void 0 ? void 0 : options.topic,
21982
- attachedStreamIds: fileIds,
21983
- attributes: options === null || options === void 0 ? void 0 : options.attributes
21984
- });
21985
- yield writer.write(text);
21986
- // set text part of progress to 1
21987
- handleProgress(1, 0);
21988
- yield writer.close();
21989
- if ((options === null || options === void 0 ? void 0 : options.attachments) && fileIds) {
21990
- yield Promise.all(options.attachments.map((file, idx) => __awaiter(this, void 0, void 0, function* () {
21991
- return this._sendFile(fileIds[idx], file, {
21992
- topic: options.topic,
21993
- mimeType: file.type,
21994
- onProgress: progress => {
21995
- handleProgress(progress, idx + 1);
21996
- }
21997
- });
21998
- })));
21999
- }
22000
- return writer.info;
22001
- });
22002
- }
22003
- /**
22004
- * @internal
22005
- * @experimental CAUTION, might get removed in a minor release
22006
- */
22007
- streamText(options) {
22008
- return __awaiter(this, void 0, void 0, function* () {
22009
- var _a, _b;
22010
- const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
22011
- const info = {
22012
- id: streamId,
22013
- mimeType: 'text/plain',
22014
- timestamp: Date.now(),
22015
- topic: (_b = options === null || options === void 0 ? void 0 : options.topic) !== null && _b !== void 0 ? _b : '',
22016
- size: options === null || options === void 0 ? void 0 : options.totalSize,
22017
- attributes: options === null || options === void 0 ? void 0 : options.attributes
22018
- };
22019
- const header = new DataStream_Header({
22020
- streamId,
22021
- mimeType: info.mimeType,
22022
- topic: info.topic,
22023
- timestamp: numberToBigInt(info.timestamp),
22024
- totalLength: numberToBigInt(options === null || options === void 0 ? void 0 : options.totalSize),
22025
- attributes: info.attributes,
22026
- contentHeader: {
22027
- case: 'textHeader',
22028
- value: new DataStream_TextHeader({
22029
- version: options === null || options === void 0 ? void 0 : options.version,
22030
- attachedStreamIds: options === null || options === void 0 ? void 0 : options.attachedStreamIds,
22031
- replyToStreamId: options === null || options === void 0 ? void 0 : options.replyToStreamId,
22032
- operationType: (options === null || options === void 0 ? void 0 : options.type) === 'update' ? DataStream_OperationType.UPDATE : DataStream_OperationType.CREATE
22033
- })
22034
- }
22035
- });
22036
- const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
22037
- const packet = new DataPacket({
22038
- destinationIdentities,
22039
- value: {
22040
- case: 'streamHeader',
22041
- value: header
22042
- }
22043
- });
22044
- yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
22045
- let chunkId = 0;
22046
- const localP = this;
22047
- const writableStream = new WritableStream({
22048
- // Implement the sink
22049
- write(text) {
22050
- return __awaiter(this, void 0, void 0, function* () {
22051
- for (const textByteChunk of splitUtf8(text, STREAM_CHUNK_SIZE)) {
22052
- yield localP.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
22053
- const chunk = new DataStream_Chunk({
22054
- content: textByteChunk,
22055
- streamId,
22056
- chunkIndex: numberToBigInt(chunkId)
22057
- });
22058
- const chunkPacket = new DataPacket({
22059
- destinationIdentities,
22060
- value: {
22061
- case: 'streamChunk',
22062
- value: chunk
22063
- }
22064
- });
22065
- yield localP.engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
22066
- chunkId += 1;
22067
- }
22068
- });
22069
- },
22070
- close() {
22071
- return __awaiter(this, void 0, void 0, function* () {
22072
- const trailer = new DataStream_Trailer({
22073
- streamId
22074
- });
22075
- const trailerPacket = new DataPacket({
22076
- destinationIdentities,
22077
- value: {
22078
- case: 'streamTrailer',
22079
- value: trailer
22080
- }
22081
- });
22082
- yield localP.engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
22083
- });
22084
- },
22085
- abort(err) {
22086
- console.log('Sink error:', err);
22087
- // TODO handle aborts to signal something to receiver side
22088
- }
22089
- });
22090
- let onEngineClose = () => __awaiter(this, void 0, void 0, function* () {
22091
- yield writer.close();
22092
- });
22093
- localP.engine.once(EngineEvent.Closing, onEngineClose);
22094
- const writer = new TextStreamWriter(writableStream, info, () => this.engine.off(EngineEvent.Closing, onEngineClose));
22095
- return writer;
22096
- });
22097
- }
22098
- sendFile(file, options) {
22099
- return __awaiter(this, void 0, void 0, function* () {
22100
- const streamId = crypto.randomUUID();
22101
- yield this._sendFile(streamId, file, options);
22102
- return {
22103
- id: streamId
22104
- };
22105
- });
22106
- }
22107
- _sendFile(streamId, file, options) {
22108
- return __awaiter(this, void 0, void 0, function* () {
22109
- var _a;
22110
- const writer = yield this.streamBytes({
22111
- streamId,
22112
- totalSize: file.size,
22113
- name: file.name,
22114
- mimeType: (_a = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _a !== void 0 ? _a : file.type,
22115
- topic: options === null || options === void 0 ? void 0 : options.topic,
22116
- destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities
22117
- });
22118
- const reader = file.stream().getReader();
22119
- while (true) {
22120
- const {
22121
- done,
22122
- value
22123
- } = yield reader.read();
22124
- if (done) {
22125
- break;
22126
- }
22127
- yield writer.write(value);
22128
- }
22129
- yield writer.close();
22130
- return writer.info;
22131
- });
22132
- }
22133
- streamBytes(options) {
22134
- return __awaiter(this, void 0, void 0, function* () {
22135
- var _a, _b, _c, _d, _e;
22136
- const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
22137
- const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
22138
- const info = {
22139
- id: streamId,
22140
- mimeType: (_b = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _b !== void 0 ? _b : 'application/octet-stream',
22141
- topic: (_c = options === null || options === void 0 ? void 0 : options.topic) !== null && _c !== void 0 ? _c : '',
22142
- timestamp: Date.now(),
22143
- attributes: options === null || options === void 0 ? void 0 : options.attributes,
22144
- size: options === null || options === void 0 ? void 0 : options.totalSize,
22145
- name: (_d = options === null || options === void 0 ? void 0 : options.name) !== null && _d !== void 0 ? _d : 'unknown'
22146
- };
22147
- const header = new DataStream_Header({
22148
- totalLength: numberToBigInt((_e = info.size) !== null && _e !== void 0 ? _e : 0),
22149
- mimeType: info.mimeType,
22150
- streamId,
22151
- topic: info.topic,
22152
- timestamp: numberToBigInt(Date.now()),
22153
- attributes: info.attributes,
22154
- contentHeader: {
22155
- case: 'byteHeader',
22156
- value: new DataStream_ByteHeader({
22157
- name: info.name
22158
- })
22159
- }
22160
- });
22161
- const packet = new DataPacket({
22162
- destinationIdentities,
22163
- value: {
22164
- case: 'streamHeader',
22165
- value: header
22601
+ timestamp: protoInt64.parse(msg.timestamp),
22602
+ editTimestamp: protoInt64.parse(msg.editTimestamp)
22603
+ }))
22166
22604
  }
22167
22605
  });
22168
22606
  yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
22169
- let chunkId = 0;
22170
- const writeMutex = new _();
22171
- const engine = this.engine;
22172
- const log = this.log;
22173
- const writableStream = new WritableStream({
22174
- write(chunk) {
22175
- return __awaiter(this, void 0, void 0, function* () {
22176
- const unlock = yield writeMutex.lock();
22177
- let byteOffset = 0;
22178
- try {
22179
- while (byteOffset < chunk.byteLength) {
22180
- const subChunk = chunk.slice(byteOffset, byteOffset + STREAM_CHUNK_SIZE);
22181
- yield engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
22182
- const chunkPacket = new DataPacket({
22183
- destinationIdentities,
22184
- value: {
22185
- case: 'streamChunk',
22186
- value: new DataStream_Chunk({
22187
- content: subChunk,
22188
- streamId,
22189
- chunkIndex: numberToBigInt(chunkId)
22190
- })
22191
- }
22192
- });
22193
- yield engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
22194
- chunkId += 1;
22195
- byteOffset += subChunk.byteLength;
22196
- }
22197
- } finally {
22198
- unlock();
22199
- }
22200
- });
22201
- },
22202
- close() {
22203
- return __awaiter(this, void 0, void 0, function* () {
22204
- const trailer = new DataStream_Trailer({
22205
- streamId
22206
- });
22207
- const trailerPacket = new DataPacket({
22208
- destinationIdentities,
22209
- value: {
22210
- case: 'streamTrailer',
22211
- value: trailer
22212
- }
22213
- });
22214
- yield engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
22215
- });
22216
- },
22217
- abort(err) {
22218
- log.error('Sink error:', err);
22219
- }
22220
- });
22221
- const byteWriter = new ByteStreamWriter(writableStream, info);
22222
- return byteWriter;
22607
+ this.emit(ParticipantEvent.ChatMessage, msg);
22608
+ return msg;
22609
+ });
22610
+ }
22611
+ /**
22612
+ * Sends the given string to participants in the room via the data channel.
22613
+ * For longer messages, consider using {@link streamText} instead.
22614
+ *
22615
+ * @param text The text payload
22616
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22617
+ */
22618
+ sendText(text, options) {
22619
+ return __awaiter(this, void 0, void 0, function* () {
22620
+ return this.roomOutgoingDataStreamManager.sendText(text, options);
22621
+ });
22622
+ }
22623
+ /**
22624
+ * Creates a new TextStreamWriter which can be used to stream text incrementally
22625
+ * to participants in the room via the data channel.
22626
+ *
22627
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22628
+ *
22629
+ * @internal
22630
+ * @experimental CAUTION, might get removed in a minor release
22631
+ */
22632
+ streamText(options) {
22633
+ return __awaiter(this, void 0, void 0, function* () {
22634
+ return this.roomOutgoingDataStreamManager.streamText(options);
22635
+ });
22636
+ }
22637
+ /** Send a File to all participants in the room via the data channel.
22638
+ * @param file The File object payload
22639
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22640
+ * @param options.onProgress A callback function used to monitor the upload progress percentage.
22641
+ */
22642
+ sendFile(file, options) {
22643
+ return __awaiter(this, void 0, void 0, function* () {
22644
+ return this.roomOutgoingDataStreamManager.sendFile(file, options);
22645
+ });
22646
+ }
22647
+ /**
22648
+ * Stream bytes incrementally to participants in the room via the data channel.
22649
+ * For sending files, consider using {@link sendFile} instead.
22650
+ *
22651
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22652
+ */
22653
+ streamBytes(options) {
22654
+ return __awaiter(this, void 0, void 0, function* () {
22655
+ return this.roomOutgoingDataStreamManager.streamBytes(options);
22223
22656
  });
22224
22657
  }
22225
22658
  /**
@@ -23051,10 +23484,6 @@ class Room extends eventsExports.EventEmitter {
23051
23484
  this.log = livekitLogger;
23052
23485
  this.bufferedEvents = [];
23053
23486
  this.isResuming = false;
23054
- this.byteStreamControllers = new Map();
23055
- this.textStreamControllers = new Map();
23056
- this.byteStreamHandlers = new Map();
23057
- this.textStreamHandlers = new Map();
23058
23487
  this.rpcHandlers = new Map();
23059
23488
  this.connect = (url, token, opts) => __awaiter(this, void 0, void 0, function* () {
23060
23489
  var _a;
@@ -23552,8 +23981,8 @@ class Room extends eventsExports.EventEmitter {
23552
23981
  return;
23553
23982
  }
23554
23983
  const newStreamState = Track.streamStateFromProto(streamState.state);
23984
+ pub.track.setStreamState(newStreamState);
23555
23985
  if (newStreamState !== pub.track.streamState) {
23556
- pub.track.streamState = newStreamState;
23557
23986
  participant.emit(ParticipantEvent.TrackStreamStateChanged, pub, pub.track.streamState);
23558
23987
  this.emitWhenConnected(RoomEvent.TrackStreamStateChanged, pub, pub.track.streamState, participant);
23559
23988
  }
@@ -23594,12 +24023,8 @@ class Room extends eventsExports.EventEmitter {
23594
24023
  this.handleChatMessage(participant, packet.value.value);
23595
24024
  } else if (packet.value.case === 'metrics') {
23596
24025
  this.handleMetrics(packet.value.value, participant);
23597
- } else if (packet.value.case === 'streamHeader') {
23598
- this.handleStreamHeader(packet.value.value, packet.participantIdentity);
23599
- } else if (packet.value.case === 'streamChunk') {
23600
- this.handleStreamChunk(packet.value.value);
23601
- } else if (packet.value.case === 'streamTrailer') {
23602
- this.handleStreamTrailer(packet.value.value);
24026
+ } else if (packet.value.case === 'streamHeader' || packet.value.case === 'streamChunk' || packet.value.case === 'streamTrailer') {
24027
+ this.handleDataStream(packet);
23603
24028
  } else if (packet.value.case === 'rpcRequest') {
23604
24029
  const rpc = packet.value.value;
23605
24030
  this.handleIncomingRpcRequest(packet.participantIdentity, rpc.id, rpc.method, rpc.payload, rpc.responseTimeoutMs, rpc.version);
@@ -23615,7 +24040,6 @@ class Room extends eventsExports.EventEmitter {
23615
24040
  // also emit on the participant
23616
24041
  participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.SipDTMFReceived, dtmf);
23617
24042
  };
23618
- this.bufferedSegments = new Map();
23619
24043
  this.handleTranscription = (_remoteParticipant, transcription) => {
23620
24044
  // find the participant
23621
24045
  const participant = transcription.transcribedParticipantIdentity === this.localParticipant.identity ? this.localParticipant : this.getParticipantByIdentity(transcription.transcribedParticipantIdentity);
@@ -23632,6 +24056,10 @@ class Room extends eventsExports.EventEmitter {
23632
24056
  this.handleMetrics = (metrics, participant) => {
23633
24057
  this.emit(RoomEvent.MetricsReceived, metrics, participant);
23634
24058
  };
24059
+ this.handleDataStream = packet => {
24060
+ this.incomingDataStreamManager.handleDataStreamPacket(packet);
24061
+ };
24062
+ this.bufferedSegments = new Map();
23635
24063
  this.handleAudioPlaybackStarted = () => {
23636
24064
  if (this.canPlaybackAudio) {
23637
24065
  return;
@@ -23766,8 +24194,10 @@ class Room extends eventsExports.EventEmitter {
23766
24194
  this.options.videoCaptureDefaults = Object.assign(Object.assign({}, videoDefaults), options === null || options === void 0 ? void 0 : options.videoCaptureDefaults);
23767
24195
  this.options.publishDefaults = Object.assign(Object.assign({}, publishDefaults), options === null || options === void 0 ? void 0 : options.publishDefaults);
23768
24196
  this.maybeCreateEngine();
24197
+ this.incomingDataStreamManager = new IncomingDataStreamManager();
24198
+ this.outgoingDataStreamManager = new OutgoingDataStreamManager(this.engine, this.log);
23769
24199
  this.disconnectLock = new _();
23770
- this.localParticipant = new LocalParticipant('', '', this.engine, this.options, this.rpcHandlers);
24200
+ this.localParticipant = new LocalParticipant('', '', this.engine, this.options, this.rpcHandlers, this.outgoingDataStreamManager);
23771
24201
  if (this.options.videoCaptureDefaults.deviceId) {
23772
24202
  this.localParticipant.activeDeviceMap.set('videoinput', unwrapConstraint(this.options.videoCaptureDefaults.deviceId));
23773
24203
  }
@@ -23794,22 +24224,16 @@ class Room extends eventsExports.EventEmitter {
23794
24224
  }
23795
24225
  }
23796
24226
  registerTextStreamHandler(topic, callback) {
23797
- if (this.textStreamHandlers.has(topic)) {
23798
- throw new TypeError("A text stream handler for topic \"".concat(topic, "\" has already been set."));
23799
- }
23800
- this.textStreamHandlers.set(topic, callback);
24227
+ return this.incomingDataStreamManager.registerTextStreamHandler(topic, callback);
23801
24228
  }
23802
24229
  unregisterTextStreamHandler(topic) {
23803
- this.textStreamHandlers.delete(topic);
24230
+ return this.incomingDataStreamManager.unregisterTextStreamHandler(topic);
23804
24231
  }
23805
24232
  registerByteStreamHandler(topic, callback) {
23806
- if (this.byteStreamHandlers.has(topic)) {
23807
- throw new TypeError("A byte stream handler for topic \"".concat(topic, "\" has already been set."));
23808
- }
23809
- this.byteStreamHandlers.set(topic, callback);
24233
+ return this.incomingDataStreamManager.registerByteStreamHandler(topic, callback);
23810
24234
  }
23811
24235
  unregisterByteStreamHandler(topic) {
23812
- this.byteStreamHandlers.delete(topic);
24236
+ return this.incomingDataStreamManager.unregisterByteStreamHandler(topic);
23813
24237
  }
23814
24238
  /**
23815
24239
  * Establishes the participant as a receiver for calls of the specified RPC method.
@@ -23851,44 +24275,6 @@ class Room extends eventsExports.EventEmitter {
23851
24275
  unregisterRpcMethod(method) {
23852
24276
  this.rpcHandlers.delete(method);
23853
24277
  }
23854
- handleIncomingRpcRequest(callerIdentity, requestId, method, payload, responseTimeout, version) {
23855
- return __awaiter(this, void 0, void 0, function* () {
23856
- yield this.engine.publishRpcAck(callerIdentity, requestId);
23857
- if (version !== 1) {
23858
- yield this.engine.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_VERSION'));
23859
- return;
23860
- }
23861
- const handler = this.rpcHandlers.get(method);
23862
- if (!handler) {
23863
- yield this.engine.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_METHOD'));
23864
- return;
23865
- }
23866
- let responseError = null;
23867
- let responsePayload = null;
23868
- try {
23869
- const response = yield handler({
23870
- requestId,
23871
- callerIdentity,
23872
- payload,
23873
- responseTimeout
23874
- });
23875
- if (byteLength(response) > MAX_PAYLOAD_BYTES) {
23876
- responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE');
23877
- console.warn("RPC Response payload too large for ".concat(method));
23878
- } else {
23879
- responsePayload = response;
23880
- }
23881
- } catch (error) {
23882
- if (error instanceof RpcError) {
23883
- responseError = error;
23884
- } else {
23885
- console.warn("Uncaught error returned by RPC handler for ".concat(method, ". Returning APPLICATION_ERROR instead."), error);
23886
- responseError = RpcError.builtIn('APPLICATION_ERROR');
23887
- }
23888
- }
23889
- yield this.engine.publishRpcResponse(callerIdentity, requestId, responsePayload, responseError);
23890
- });
23891
- }
23892
24278
  /**
23893
24279
  * @experimental
23894
24280
  */
@@ -24053,6 +24439,9 @@ class Room extends eventsExports.EventEmitter {
24053
24439
  if (this.e2eeManager) {
24054
24440
  this.e2eeManager.setupEngine(this.engine);
24055
24441
  }
24442
+ if (this.outgoingDataStreamManager) {
24443
+ this.outgoingDataStreamManager.setupEngine(this.engine);
24444
+ }
24056
24445
  }
24057
24446
  /**
24058
24447
  * getLocalDevices abstracts navigator.mediaDevices.enumerateDevices.
@@ -24419,7 +24808,10 @@ class Room extends eventsExports.EventEmitter {
24419
24808
  adaptiveStreamSettings = {};
24420
24809
  }
24421
24810
  }
24422
- participant.addSubscribedMediaTrack(mediaTrack, trackId, stream, receiver, adaptiveStreamSettings);
24811
+ const publication = participant.addSubscribedMediaTrack(mediaTrack, trackId, stream, receiver, adaptiveStreamSettings);
24812
+ if ((publication === null || publication === void 0 ? void 0 : publication.isEncrypted) && !this.e2eeManager) {
24813
+ this.emit(RoomEvent.EncryptionError, new Error("Encrypted ".concat(publication.source, " track received from participant ").concat(participant.sid, ", but room does not have encryption enabled!")));
24814
+ }
24423
24815
  }
24424
24816
  handleDisconnect() {
24425
24817
  let shouldStopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
@@ -24429,6 +24821,7 @@ class Room extends eventsExports.EventEmitter {
24429
24821
  this.isResuming = false;
24430
24822
  this.bufferedEvents = [];
24431
24823
  this.transcriptionReceivedTimes.clear();
24824
+ this.incomingDataStreamManager.clearHandlersAndControllers();
24432
24825
  if (this.state === ConnectionState.Disconnected) {
24433
24826
  return;
24434
24827
  }
@@ -24480,6 +24873,7 @@ class Room extends eventsExports.EventEmitter {
24480
24873
  if (!participant) {
24481
24874
  return;
24482
24875
  }
24876
+ this.incomingDataStreamManager.validateParticipantHasNoActiveDataStreams(identity);
24483
24877
  participant.trackPublications.forEach(publication => {
24484
24878
  participant.unpublishTrack(publication.trackSid, true);
24485
24879
  });
@@ -24487,99 +24881,44 @@ class Room extends eventsExports.EventEmitter {
24487
24881
  participant.setDisconnected();
24488
24882
  (_a = this.localParticipant) === null || _a === void 0 ? void 0 : _a.handleParticipantDisconnected(participant.identity);
24489
24883
  }
24490
- handleStreamHeader(streamHeader, participantIdentity) {
24884
+ handleIncomingRpcRequest(callerIdentity, requestId, method, payload, responseTimeout, version) {
24491
24885
  return __awaiter(this, void 0, void 0, function* () {
24492
- var _a;
24493
- if (streamHeader.contentHeader.case === 'byteHeader') {
24494
- const streamHandlerCallback = this.byteStreamHandlers.get(streamHeader.topic);
24495
- if (!streamHandlerCallback) {
24496
- this.log.debug('ignoring incoming byte stream due to no handler for topic', streamHeader.topic);
24497
- return;
24498
- }
24499
- let streamController;
24500
- const info = {
24501
- id: streamHeader.streamId,
24502
- name: (_a = streamHeader.contentHeader.value.name) !== null && _a !== void 0 ? _a : 'unknown',
24503
- mimeType: streamHeader.mimeType,
24504
- size: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,
24505
- topic: streamHeader.topic,
24506
- timestamp: bigIntToNumber(streamHeader.timestamp),
24507
- attributes: streamHeader.attributes
24508
- };
24509
- const stream = new ReadableStream({
24510
- start: controller => {
24511
- streamController = controller;
24512
- this.byteStreamControllers.set(streamHeader.streamId, {
24513
- info,
24514
- controller: streamController,
24515
- startTime: Date.now()
24516
- });
24517
- }
24518
- });
24519
- streamHandlerCallback(new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)), {
24520
- identity: participantIdentity
24886
+ yield this.engine.publishRpcAck(callerIdentity, requestId);
24887
+ if (version !== 1) {
24888
+ yield this.engine.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_VERSION'));
24889
+ return;
24890
+ }
24891
+ const handler = this.rpcHandlers.get(method);
24892
+ if (!handler) {
24893
+ yield this.engine.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_METHOD'));
24894
+ return;
24895
+ }
24896
+ let responseError = null;
24897
+ let responsePayload = null;
24898
+ try {
24899
+ const response = yield handler({
24900
+ requestId,
24901
+ callerIdentity,
24902
+ payload,
24903
+ responseTimeout
24521
24904
  });
24522
- } else if (streamHeader.contentHeader.case === 'textHeader') {
24523
- const streamHandlerCallback = this.textStreamHandlers.get(streamHeader.topic);
24524
- if (!streamHandlerCallback) {
24525
- this.log.debug('ignoring incoming text stream due to no handler for topic', streamHeader.topic);
24526
- return;
24905
+ if (byteLength(response) > MAX_PAYLOAD_BYTES) {
24906
+ responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE');
24907
+ console.warn("RPC Response payload too large for ".concat(method));
24908
+ } else {
24909
+ responsePayload = response;
24910
+ }
24911
+ } catch (error) {
24912
+ if (error instanceof RpcError) {
24913
+ responseError = error;
24914
+ } else {
24915
+ console.warn("Uncaught error returned by RPC handler for ".concat(method, ". Returning APPLICATION_ERROR instead."), error);
24916
+ responseError = RpcError.builtIn('APPLICATION_ERROR');
24527
24917
  }
24528
- let streamController;
24529
- const info = {
24530
- id: streamHeader.streamId,
24531
- mimeType: streamHeader.mimeType,
24532
- size: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,
24533
- topic: streamHeader.topic,
24534
- timestamp: Number(streamHeader.timestamp),
24535
- attributes: streamHeader.attributes
24536
- };
24537
- const stream = new ReadableStream({
24538
- start: controller => {
24539
- streamController = controller;
24540
- this.textStreamControllers.set(streamHeader.streamId, {
24541
- info,
24542
- controller: streamController,
24543
- startTime: Date.now()
24544
- });
24545
- }
24546
- });
24547
- streamHandlerCallback(new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)), {
24548
- identity: participantIdentity
24549
- });
24550
24918
  }
24919
+ yield this.engine.publishRpcResponse(callerIdentity, requestId, responsePayload, responseError);
24551
24920
  });
24552
24921
  }
24553
- handleStreamChunk(chunk) {
24554
- const fileBuffer = this.byteStreamControllers.get(chunk.streamId);
24555
- if (fileBuffer) {
24556
- if (chunk.content.length > 0) {
24557
- fileBuffer.controller.enqueue(chunk);
24558
- }
24559
- }
24560
- const textBuffer = this.textStreamControllers.get(chunk.streamId);
24561
- if (textBuffer) {
24562
- if (chunk.content.length > 0) {
24563
- textBuffer.controller.enqueue(chunk);
24564
- }
24565
- }
24566
- }
24567
- handleStreamTrailer(trailer) {
24568
- const textBuffer = this.textStreamControllers.get(trailer.streamId);
24569
- if (textBuffer) {
24570
- textBuffer.info.attributes = Object.assign(Object.assign({}, textBuffer.info.attributes), trailer.attributes);
24571
- textBuffer.controller.close();
24572
- this.textStreamControllers.delete(trailer.streamId);
24573
- }
24574
- const fileBuffer = this.byteStreamControllers.get(trailer.streamId);
24575
- if (fileBuffer) {
24576
- {
24577
- fileBuffer.info.attributes = Object.assign(Object.assign({}, fileBuffer.info.attributes), trailer.attributes);
24578
- fileBuffer.controller.close();
24579
- this.byteStreamControllers.delete(trailer.streamId);
24580
- }
24581
- }
24582
- }
24583
24922
  /**
24584
24923
  * attempt to select the default devices if the previously selected devices are no longer available after a device change event
24585
24924
  */
@@ -25835,5 +26174,5 @@ function isFacingModeValue(item) {
25835
26174
  return item === undefined || allowedValues.includes(item);
25836
26175
  }
25837
26176
 
25838
- export { AudioPresets, BackupCodecPolicy, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalTrackRecorder, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, _ as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, PublishTrackError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, TrackType, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, attributeTypings as attributes, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isAudioTrack, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isScriptTransformSupported, isVideoFrame, isVideoTrack, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
26177
+ export { AudioPresets, BackupCodecPolicy, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DataStreamError, DataStreamErrorReason, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalTrackRecorder, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, _ as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, PublishTrackError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, TrackType, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, attributeTypings as attributes, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isAudioTrack, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isScriptTransformSupported, isVideoFrame, isVideoTrack, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
25839
26178
  //# sourceMappingURL=livekit-client.esm.mjs.map