livekit-client 2.15.4 → 2.15.5

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 (62) 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 +329 -124
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +834 -541
  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/worker/FrameCryptor.d.ts +0 -46
  10. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  11. package/dist/src/e2ee/worker/naluUtils.d.ts +27 -0
  12. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -0
  13. package/dist/src/index.d.ts +2 -2
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/room/Room.d.ts +6 -10
  16. package/dist/src/room/Room.d.ts.map +1 -1
  17. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +20 -0
  18. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -0
  19. package/dist/src/room/{StreamReader.d.ts → data-stream/incoming/StreamReader.d.ts} +32 -6
  20. package/dist/src/room/data-stream/incoming/StreamReader.d.ts.map +1 -0
  21. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +27 -0
  22. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -0
  23. package/dist/src/room/{StreamWriter.d.ts → data-stream/outgoing/StreamWriter.d.ts} +1 -1
  24. package/dist/src/room/data-stream/outgoing/StreamWriter.d.ts.map +1 -0
  25. package/dist/src/room/errors.d.ts +13 -0
  26. package/dist/src/room/errors.d.ts.map +1 -1
  27. package/dist/src/room/participant/LocalParticipant.d.ts +32 -19
  28. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  29. package/dist/src/room/track/LocalTrack.d.ts +7 -2
  30. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  31. package/dist/src/room/types.d.ts +17 -1
  32. package/dist/src/room/types.d.ts.map +1 -1
  33. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +0 -46
  34. package/dist/ts4.2/src/e2ee/worker/naluUtils.d.ts +27 -0
  35. package/dist/ts4.2/src/index.d.ts +2 -2
  36. package/dist/ts4.2/src/room/Room.d.ts +6 -10
  37. package/dist/ts4.2/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +20 -0
  38. package/dist/ts4.2/src/room/{StreamReader.d.ts → data-stream/incoming/StreamReader.d.ts} +32 -6
  39. package/dist/ts4.2/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +27 -0
  40. package/dist/ts4.2/src/room/{StreamWriter.d.ts → data-stream/outgoing/StreamWriter.d.ts} +1 -1
  41. package/dist/ts4.2/src/room/errors.d.ts +13 -0
  42. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +32 -19
  43. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +7 -2
  44. package/dist/ts4.2/src/room/types.d.ts +17 -1
  45. package/package.json +1 -1
  46. package/src/e2ee/worker/FrameCryptor.ts +48 -139
  47. package/src/e2ee/worker/naluUtils.ts +328 -0
  48. package/src/index.ts +2 -2
  49. package/src/room/Room.ts +93 -206
  50. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +247 -0
  51. package/src/room/data-stream/incoming/StreamReader.ts +317 -0
  52. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +316 -0
  53. package/src/room/{StreamWriter.ts → data-stream/outgoing/StreamWriter.ts} +1 -1
  54. package/src/room/errors.ts +34 -0
  55. package/src/room/participant/LocalParticipant.ts +39 -295
  56. package/src/room/track/LocalAudioTrack.ts +2 -2
  57. package/src/room/track/LocalTrack.ts +65 -48
  58. package/src/room/types.ts +22 -1
  59. package/src/room/utils.ts +2 -2
  60. package/dist/src/room/StreamReader.d.ts.map +0 -1
  61. package/dist/src/room/StreamWriter.d.ts.map +0 -1
  62. 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.5";
11368
11392
 
11369
11393
  const version = version$1;
11370
11394
  const protocolVersion = 16;
@@ -12246,13 +12270,13 @@ function unwrapConstraint(constraint) {
12246
12270
  if (Array.isArray(constraint)) {
12247
12271
  return constraint[0];
12248
12272
  }
12249
- if (constraint.exact) {
12273
+ if (constraint.exact !== undefined) {
12250
12274
  if (Array.isArray(constraint.exact)) {
12251
12275
  return constraint.exact[0];
12252
12276
  }
12253
12277
  return constraint.exact;
12254
12278
  }
12255
- if (constraint.ideal) {
12279
+ if (constraint.ideal !== undefined) {
12256
12280
  if (Array.isArray(constraint.ideal)) {
12257
12281
  return constraint.ideal[0];
12258
12282
  }
@@ -15931,9 +15955,14 @@ class LocalTrack extends Track {
15931
15955
  this.providedByUser = userProvidedTrack;
15932
15956
  this.muteLock = new _();
15933
15957
  this.pauseUpstreamLock = new _();
15934
- this.processorLock = new _();
15935
- this.restartLock = new _();
15936
- this.setMediaStreamTrack(mediaTrack, true);
15958
+ this.trackChangeLock = new _();
15959
+ this.trackChangeLock.lock().then(unlock => __awaiter(this, void 0, void 0, function* () {
15960
+ try {
15961
+ yield this.setMediaStreamTrack(mediaTrack, true);
15962
+ } finally {
15963
+ unlock();
15964
+ }
15965
+ }));
15937
15966
  // added to satisfy TS compiler, constraints are synced with MediaStreamTrack
15938
15967
  this._constraints = mediaTrack.getConstraints();
15939
15968
  if (constraints) {
@@ -16009,26 +16038,21 @@ class LocalTrack extends Track {
16009
16038
  }
16010
16039
  let processedTrack;
16011
16040
  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();
16041
+ this.log.debug('restarting processor', this.logContext);
16042
+ if (this.kind === 'unknown') {
16043
+ throw TypeError('cannot set processor on track of unknown kind');
16031
16044
  }
16045
+ if (this.processorElement) {
16046
+ attachToElement(newTrack, this.processorElement);
16047
+ // ensure the processorElement itself stays muted
16048
+ this.processorElement.muted = true;
16049
+ }
16050
+ yield this.processor.restart({
16051
+ track: newTrack,
16052
+ kind: this.kind,
16053
+ element: this.processorElement
16054
+ });
16055
+ processedTrack = this.processor.processedTrack;
16032
16056
  }
16033
16057
  if (this.sender && ((_a = this.sender.transport) === null || _a === void 0 ? void 0 : _a.state) !== 'closed') {
16034
16058
  yield this.sender.replaceTrack(processedTrack !== null && processedTrack !== void 0 ? processedTrack : newTrack);
@@ -16126,32 +16150,37 @@ class LocalTrack extends Track {
16126
16150
  }
16127
16151
  replaceTrack(track, userProvidedOrOptions) {
16128
16152
  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();
16153
+ const unlock = yield this.trackChangeLock.lock();
16154
+ try {
16155
+ if (!this.sender) {
16156
+ throw new TrackInvalidError('unable to replace an unpublished track');
16157
+ }
16158
+ let userProvidedTrack;
16159
+ let stopProcessor;
16160
+ if (typeof userProvidedOrOptions === 'boolean') {
16161
+ userProvidedTrack = userProvidedOrOptions;
16162
+ } else if (userProvidedOrOptions !== undefined) {
16163
+ userProvidedTrack = userProvidedOrOptions.userProvidedTrack;
16164
+ stopProcessor = userProvidedOrOptions.stopProcessor;
16165
+ }
16166
+ this.providedByUser = userProvidedTrack !== null && userProvidedTrack !== void 0 ? userProvidedTrack : true;
16167
+ this.log.debug('replace MediaStreamTrack', this.logContext);
16168
+ yield this.setMediaStreamTrack(track);
16169
+ // this must be synced *after* setting mediaStreamTrack above, since it relies
16170
+ // on the previous state in order to cleanup
16171
+ if (stopProcessor && this.processor) {
16172
+ yield this.internalStopProcessor();
16173
+ }
16174
+ return this;
16175
+ } finally {
16176
+ unlock();
16147
16177
  }
16148
- return this;
16149
16178
  });
16150
16179
  }
16151
16180
  restart(constraints) {
16152
16181
  return __awaiter(this, void 0, void 0, function* () {
16153
16182
  this.manuallyStopped = false;
16154
- const unlock = yield this.restartLock.lock();
16183
+ const unlock = yield this.trackChangeLock.lock();
16155
16184
  try {
16156
16185
  if (!constraints) {
16157
16186
  constraints = this._constraints;
@@ -16334,7 +16363,7 @@ class LocalTrack extends Track {
16334
16363
  let showProcessedStreamLocally = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
16335
16364
  return function* () {
16336
16365
  var _a;
16337
- const unlock = yield _this3.processorLock.lock();
16366
+ const unlock = yield _this3.trackChangeLock.lock();
16338
16367
  try {
16339
16368
  _this3.log.debug('setting up processor', _this3.logContext);
16340
16369
  const processorElement = document.createElement(_this3.kind);
@@ -16347,7 +16376,7 @@ class LocalTrack extends Track {
16347
16376
  yield processor.init(processorOptions);
16348
16377
  _this3.log.debug('processor initialized', _this3.logContext);
16349
16378
  if (_this3.processor) {
16350
- yield _this3.stopProcessor();
16379
+ yield _this3.internalStopProcessor();
16351
16380
  }
16352
16381
  if (_this3.kind === 'unknown') {
16353
16382
  throw TypeError('cannot set processor on track of unknown kind');
@@ -16405,22 +16434,41 @@ class LocalTrack extends Track {
16405
16434
  return __awaiter(this, arguments, void 0, function () {
16406
16435
  var _this4 = this;
16407
16436
  let keepElement = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
16437
+ return function* () {
16438
+ const unlock = yield _this4.trackChangeLock.lock();
16439
+ try {
16440
+ yield _this4.internalStopProcessor(keepElement);
16441
+ } finally {
16442
+ unlock();
16443
+ }
16444
+ }();
16445
+ });
16446
+ }
16447
+ /**
16448
+ * @internal
16449
+ * This method assumes the caller has acquired a trackChangeLock already.
16450
+ * The public facing method for stopping the processor is `stopProcessor` and it wraps this method in the trackChangeLock.
16451
+ */
16452
+ internalStopProcessor() {
16453
+ return __awaiter(this, arguments, void 0, function () {
16454
+ var _this5 = this;
16455
+ let keepElement = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
16408
16456
  return function* () {
16409
16457
  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;
16458
+ if (!_this5.processor) return;
16459
+ _this5.log.debug('stopping processor', _this5.logContext);
16460
+ (_a = _this5.processor.processedTrack) === null || _a === void 0 ? void 0 : _a.stop();
16461
+ yield _this5.processor.destroy();
16462
+ _this5.processor = undefined;
16415
16463
  if (!keepElement) {
16416
- (_b = _this4.processorElement) === null || _b === void 0 ? void 0 : _b.remove();
16417
- _this4.processorElement = undefined;
16464
+ (_b = _this5.processorElement) === null || _b === void 0 ? void 0 : _b.remove();
16465
+ _this5.processorElement = undefined;
16418
16466
  }
16419
16467
  // apply original track constraints in case the processor changed them
16420
- yield _this4._mediaStreamTrack.applyConstraints(_this4._constraints);
16468
+ yield _this5._mediaStreamTrack.applyConstraints(_this5._constraints);
16421
16469
  // force re-setting of the mediaStreamTrack on the sender
16422
- yield _this4.setMediaStreamTrack(_this4._mediaStreamTrack, true);
16423
- _this4.emit(TrackEvent.TrackProcessorUpdate);
16470
+ yield _this5.setMediaStreamTrack(_this5._mediaStreamTrack, true);
16471
+ _this5.emit(TrackEvent.TrackProcessorUpdate);
16424
16472
  }();
16425
16473
  });
16426
16474
  }
@@ -16614,13 +16662,13 @@ class LocalAudioTrack extends LocalTrack {
16614
16662
  setProcessor(processor) {
16615
16663
  return __awaiter(this, void 0, void 0, function* () {
16616
16664
  var _a;
16617
- const unlock = yield this.processorLock.lock();
16665
+ const unlock = yield this.trackChangeLock.lock();
16618
16666
  try {
16619
16667
  if (!isReactNative() && !this.audioContext) {
16620
16668
  throw Error('Audio context needs to be set on LocalAudioTrack in order to enable processors');
16621
16669
  }
16622
16670
  if (this.processor) {
16623
- yield this.stopProcessor();
16671
+ yield this.internalStopProcessor();
16624
16672
  }
16625
16673
  const processorOptions = {
16626
16674
  kind: this.kind,
@@ -19004,30 +19052,71 @@ class BaseStreamReader {
19004
19052
  get info() {
19005
19053
  return this._info;
19006
19054
  }
19007
- constructor(info, stream, totalByteSize) {
19055
+ /** @internal */
19056
+ validateBytesReceived() {
19057
+ let doneReceiving = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
19058
+ if (typeof this.totalByteSize !== 'number' || this.totalByteSize === 0) {
19059
+ return;
19060
+ }
19061
+ if (doneReceiving && this.bytesReceived < this.totalByteSize) {
19062
+ throw new DataStreamError("Not enough chunk(s) received - expected ".concat(this.totalByteSize, " bytes of data total, only received ").concat(this.bytesReceived, " bytes"), DataStreamErrorReason.Incomplete);
19063
+ } else if (this.bytesReceived > this.totalByteSize) {
19064
+ throw new DataStreamError("Extra chunk(s) received - expected ".concat(this.totalByteSize, " bytes of data total, received ").concat(this.bytesReceived, " bytes"), DataStreamErrorReason.LengthExceeded);
19065
+ }
19066
+ }
19067
+ constructor(info, stream, totalByteSize, outOfBandFailureRejectingFuture) {
19008
19068
  this.reader = stream;
19009
19069
  this.totalByteSize = totalByteSize;
19010
19070
  this._info = info;
19011
19071
  this.bytesReceived = 0;
19072
+ this.outOfBandFailureRejectingFuture = outOfBandFailureRejectingFuture;
19012
19073
  }
19013
19074
  }
19014
19075
  class ByteStreamReader extends BaseStreamReader {
19015
19076
  handleChunkReceived(chunk) {
19016
19077
  var _a;
19017
19078
  this.bytesReceived += chunk.content.byteLength;
19079
+ this.validateBytesReceived();
19018
19080
  const currentProgress = this.totalByteSize ? this.bytesReceived / this.totalByteSize : undefined;
19019
19081
  (_a = this.onProgress) === null || _a === void 0 ? void 0 : _a.call(this, currentProgress);
19020
19082
  }
19021
19083
  [Symbol.asyncIterator]() {
19022
19084
  const reader = this.reader.getReader();
19085
+ let rejectingSignalFuture = new Future();
19086
+ let activeSignal = null;
19087
+ let onAbort = null;
19088
+ if (this.signal) {
19089
+ const signal = this.signal;
19090
+ onAbort = () => {
19091
+ var _a;
19092
+ (_a = rejectingSignalFuture.reject) === null || _a === void 0 ? void 0 : _a.call(rejectingSignalFuture, signal.reason);
19093
+ };
19094
+ signal.addEventListener('abort', onAbort);
19095
+ activeSignal = signal;
19096
+ }
19097
+ const cleanup = () => {
19098
+ reader.releaseLock();
19099
+ if (activeSignal && onAbort) {
19100
+ activeSignal.removeEventListener('abort', onAbort);
19101
+ }
19102
+ this.signal = undefined;
19103
+ };
19023
19104
  return {
19024
19105
  next: () => __awaiter(this, void 0, void 0, function* () {
19106
+ var _a, _b;
19025
19107
  try {
19026
19108
  const {
19027
19109
  done,
19028
19110
  value
19029
- } = yield reader.read();
19111
+ } = yield Promise.race([reader.read(),
19112
+ // Rejects if this.signal is aborted
19113
+ rejectingSignalFuture.promise,
19114
+ // Rejects if something external says it should, like a participant disconnecting, etc
19115
+ (_b = (_a = this.outOfBandFailureRejectingFuture) === null || _a === void 0 ? void 0 : _a.promise) !== null && _b !== void 0 ? _b : new Promise(() => {
19116
+ /* never resolves */
19117
+ })]);
19030
19118
  if (done) {
19119
+ this.validateBytesReceived(true);
19031
19120
  return {
19032
19121
  done: true,
19033
19122
  value: undefined
@@ -19039,17 +19128,16 @@ class ByteStreamReader extends BaseStreamReader {
19039
19128
  value: value.content
19040
19129
  };
19041
19130
  }
19042
- } catch (error) {
19043
- // TODO handle errors
19044
- return {
19045
- done: true,
19046
- value: undefined
19047
- };
19131
+ } catch (err) {
19132
+ cleanup();
19133
+ throw err;
19048
19134
  }
19049
19135
  }),
19136
+ // note: `return` runs only for premature exits, see:
19137
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#errors_during_iteration
19050
19138
  return() {
19051
19139
  return __awaiter(this, void 0, void 0, function* () {
19052
- reader.releaseLock();
19140
+ cleanup();
19053
19141
  return {
19054
19142
  done: true,
19055
19143
  value: undefined
@@ -19058,29 +19146,45 @@ class ByteStreamReader extends BaseStreamReader {
19058
19146
  }
19059
19147
  };
19060
19148
  }
19149
+ /**
19150
+ * Injects an AbortSignal, which if aborted, will terminate the currently active
19151
+ * stream iteration operation.
19152
+ *
19153
+ * Note that when using AbortSignal.timeout(...), the timeout applies across
19154
+ * the whole iteration operation, not just one individual chunk read.
19155
+ */
19156
+ withAbortSignal(signal) {
19157
+ this.signal = signal;
19158
+ return this;
19159
+ }
19061
19160
  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 {
19161
+ return __awaiter(this, arguments, void 0, function () {
19162
+ var _this = this;
19163
+ let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
19164
+ return function* () {
19165
+ var _a, e_1, _b, _c;
19166
+ let chunks = new Set();
19167
+ const iterator = opts.signal ? _this.withAbortSignal(opts.signal) : _this;
19077
19168
  try {
19078
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
19169
+ 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) {
19170
+ _c = iterator_1_1.value;
19171
+ _d = false;
19172
+ const chunk = _c;
19173
+ chunks.add(chunk);
19174
+ }
19175
+ } catch (e_1_1) {
19176
+ e_1 = {
19177
+ error: e_1_1
19178
+ };
19079
19179
  } finally {
19080
- if (e_1) throw e_1.error;
19180
+ try {
19181
+ if (!_d && !_a && (_b = iterator_1.return)) yield _b.call(iterator_1);
19182
+ } finally {
19183
+ if (e_1) throw e_1.error;
19184
+ }
19081
19185
  }
19082
- }
19083
- return Array.from(chunks);
19186
+ return Array.from(chunks);
19187
+ }();
19084
19188
  });
19085
19189
  }
19086
19190
  }
@@ -19092,8 +19196,8 @@ class TextStreamReader extends BaseStreamReader {
19092
19196
  * A TextStreamReader instance can be used as an AsyncIterator that returns the entire string
19093
19197
  * that has been received up to the current point in time.
19094
19198
  */
19095
- constructor(info, stream, totalChunkCount) {
19096
- super(info, stream, totalChunkCount);
19199
+ constructor(info, stream, totalChunkCount, outOfBandFailureRejectingFuture) {
19200
+ super(info, stream, totalChunkCount, outOfBandFailureRejectingFuture);
19097
19201
  this.receivedChunks = new Map();
19098
19202
  }
19099
19203
  handleChunkReceived(chunk) {
@@ -19106,6 +19210,7 @@ class TextStreamReader extends BaseStreamReader {
19106
19210
  }
19107
19211
  this.receivedChunks.set(index, chunk);
19108
19212
  this.bytesReceived += chunk.content.byteLength;
19213
+ this.validateBytesReceived();
19109
19214
  const currentProgress = this.totalByteSize ? this.bytesReceived / this.totalByteSize : undefined;
19110
19215
  (_a = this.onProgress) === null || _a === void 0 ? void 0 : _a.call(this, currentProgress);
19111
19216
  }
@@ -19116,37 +19221,71 @@ class TextStreamReader extends BaseStreamReader {
19116
19221
  */
19117
19222
  [Symbol.asyncIterator]() {
19118
19223
  const reader = this.reader.getReader();
19119
- const decoder = new TextDecoder();
19224
+ const decoder = new TextDecoder('utf-8', {
19225
+ fatal: true
19226
+ });
19227
+ let rejectingSignalFuture = new Future();
19228
+ let activeSignal = null;
19229
+ let onAbort = null;
19230
+ if (this.signal) {
19231
+ const signal = this.signal;
19232
+ onAbort = () => {
19233
+ var _a;
19234
+ (_a = rejectingSignalFuture.reject) === null || _a === void 0 ? void 0 : _a.call(rejectingSignalFuture, signal.reason);
19235
+ };
19236
+ signal.addEventListener('abort', onAbort);
19237
+ activeSignal = signal;
19238
+ }
19239
+ const cleanup = () => {
19240
+ reader.releaseLock();
19241
+ if (activeSignal && onAbort) {
19242
+ activeSignal.removeEventListener('abort', onAbort);
19243
+ }
19244
+ this.signal = undefined;
19245
+ };
19120
19246
  return {
19121
19247
  next: () => __awaiter(this, void 0, void 0, function* () {
19248
+ var _a, _b;
19122
19249
  try {
19123
19250
  const {
19124
19251
  done,
19125
19252
  value
19126
- } = yield reader.read();
19253
+ } = yield Promise.race([reader.read(),
19254
+ // Rejects if this.signal is aborted
19255
+ rejectingSignalFuture.promise,
19256
+ // Rejects if something external says it should, like a participant disconnecting, etc
19257
+ (_b = (_a = this.outOfBandFailureRejectingFuture) === null || _a === void 0 ? void 0 : _a.promise) !== null && _b !== void 0 ? _b : new Promise(() => {
19258
+ /* never resolves */
19259
+ })]);
19127
19260
  if (done) {
19261
+ this.validateBytesReceived(true);
19128
19262
  return {
19129
19263
  done: true,
19130
19264
  value: undefined
19131
19265
  };
19132
19266
  } else {
19133
19267
  this.handleChunkReceived(value);
19268
+ let decodedResult;
19269
+ try {
19270
+ decodedResult = decoder.decode(value.content);
19271
+ } catch (err) {
19272
+ throw new DataStreamError("Cannot decode datastream chunk ".concat(value.chunkIndex, " as text: ").concat(err), DataStreamErrorReason.DecodeFailed);
19273
+ }
19134
19274
  return {
19135
19275
  done: false,
19136
- value: decoder.decode(value.content)
19276
+ value: decodedResult
19137
19277
  };
19138
19278
  }
19139
- } catch (error) {
19140
- // TODO handle errors
19141
- return {
19142
- done: true,
19143
- value: undefined
19144
- };
19279
+ } catch (err) {
19280
+ cleanup();
19281
+ throw err;
19145
19282
  }
19146
19283
  }),
19284
+ // note: `return` runs only for premature exits, see:
19285
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#errors_during_iteration
19147
19286
  return() {
19148
19287
  return __awaiter(this, void 0, void 0, function* () {
19149
- reader.releaseLock();
19288
+ cleanup();
19150
19289
  return {
19151
19290
  done: true,
19152
19291
  value: undefined
@@ -19155,31 +19294,217 @@ class TextStreamReader extends BaseStreamReader {
19155
19294
  }
19156
19295
  };
19157
19296
  }
19297
+ /**
19298
+ * Injects an AbortSignal, which if aborted, will terminate the currently active
19299
+ * stream iteration operation.
19300
+ *
19301
+ * Note that when using AbortSignal.timeout(...), the timeout applies across
19302
+ * the whole iteration operation, not just one individual chunk read.
19303
+ */
19304
+ withAbortSignal(signal) {
19305
+ this.signal = signal;
19306
+ return this;
19307
+ }
19158
19308
  readAll() {
19309
+ return __awaiter(this, arguments, void 0, function () {
19310
+ var _this2 = this;
19311
+ let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
19312
+ return function* () {
19313
+ var _a, e_2, _b, _c;
19314
+ let finalString = '';
19315
+ const iterator = opts.signal ? _this2.withAbortSignal(opts.signal) : _this2;
19316
+ try {
19317
+ 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) {
19318
+ _c = iterator_2_1.value;
19319
+ _d = false;
19320
+ const chunk = _c;
19321
+ finalString += chunk;
19322
+ }
19323
+ } catch (e_2_1) {
19324
+ e_2 = {
19325
+ error: e_2_1
19326
+ };
19327
+ } finally {
19328
+ try {
19329
+ if (!_d && !_a && (_b = iterator_2.return)) yield _b.call(iterator_2);
19330
+ } finally {
19331
+ if (e_2) throw e_2.error;
19332
+ }
19333
+ }
19334
+ return finalString;
19335
+ }();
19336
+ });
19337
+ }
19338
+ }
19339
+
19340
+ class IncomingDataStreamManager {
19341
+ constructor() {
19342
+ this.log = livekitLogger;
19343
+ this.byteStreamControllers = new Map();
19344
+ this.textStreamControllers = new Map();
19345
+ this.byteStreamHandlers = new Map();
19346
+ this.textStreamHandlers = new Map();
19347
+ }
19348
+ registerTextStreamHandler(topic, callback) {
19349
+ if (this.textStreamHandlers.has(topic)) {
19350
+ throw new DataStreamError("A text stream handler for topic \"".concat(topic, "\" has already been set."), DataStreamErrorReason.HandlerAlreadyRegistered);
19351
+ }
19352
+ this.textStreamHandlers.set(topic, callback);
19353
+ }
19354
+ unregisterTextStreamHandler(topic) {
19355
+ this.textStreamHandlers.delete(topic);
19356
+ }
19357
+ registerByteStreamHandler(topic, callback) {
19358
+ if (this.byteStreamHandlers.has(topic)) {
19359
+ throw new DataStreamError("A byte stream handler for topic \"".concat(topic, "\" has already been set."), DataStreamErrorReason.HandlerAlreadyRegistered);
19360
+ }
19361
+ this.byteStreamHandlers.set(topic, callback);
19362
+ }
19363
+ unregisterByteStreamHandler(topic) {
19364
+ this.byteStreamHandlers.delete(topic);
19365
+ }
19366
+ clearHandlersAndControllers() {
19367
+ this.byteStreamControllers.clear();
19368
+ this.textStreamControllers.clear();
19369
+ this.byteStreamHandlers.clear();
19370
+ this.textStreamHandlers.clear();
19371
+ }
19372
+ validateParticipantHasNoActiveDataStreams(participantIdentity) {
19373
+ var _a, _b, _c, _d;
19374
+ // Terminate any in flight data stream receives from the given participant
19375
+ const textStreamsBeingSentByDisconnectingParticipant = Array.from(this.textStreamControllers.entries()).filter(entry => entry[1].sendingParticipantIdentity === participantIdentity);
19376
+ const byteStreamsBeingSentByDisconnectingParticipant = Array.from(this.byteStreamControllers.entries()).filter(entry => entry[1].sendingParticipantIdentity === participantIdentity);
19377
+ if (textStreamsBeingSentByDisconnectingParticipant.length > 0 || byteStreamsBeingSentByDisconnectingParticipant.length > 0) {
19378
+ const abnormalEndError = new DataStreamError("Participant ".concat(participantIdentity, " unexpectedly disconnected in the middle of sending data"), DataStreamErrorReason.AbnormalEnd);
19379
+ for (const [id, controller] of byteStreamsBeingSentByDisconnectingParticipant) {
19380
+ (_b = (_a = controller.outOfBandFailureRejectingFuture).reject) === null || _b === void 0 ? void 0 : _b.call(_a, abnormalEndError);
19381
+ this.byteStreamControllers.delete(id);
19382
+ }
19383
+ for (const [id, controller] of textStreamsBeingSentByDisconnectingParticipant) {
19384
+ (_d = (_c = controller.outOfBandFailureRejectingFuture).reject) === null || _d === void 0 ? void 0 : _d.call(_c, abnormalEndError);
19385
+ this.textStreamControllers.delete(id);
19386
+ }
19387
+ }
19388
+ }
19389
+ handleDataStreamPacket(packet) {
19159
19390
  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;
19391
+ switch (packet.value.case) {
19392
+ case 'streamHeader':
19393
+ return this.handleStreamHeader(packet.value.value, packet.participantIdentity);
19394
+ case 'streamChunk':
19395
+ return this.handleStreamChunk(packet.value.value);
19396
+ case 'streamTrailer':
19397
+ return this.handleStreamTrailer(packet.value.value);
19398
+ default:
19399
+ throw new Error("DataPacket of value \"".concat(packet.value.case, "\" is not data stream related!"));
19400
+ }
19401
+ });
19402
+ }
19403
+ handleStreamHeader(streamHeader, participantIdentity) {
19404
+ return __awaiter(this, void 0, void 0, function* () {
19405
+ var _a;
19406
+ if (streamHeader.contentHeader.case === 'byteHeader') {
19407
+ const streamHandlerCallback = this.byteStreamHandlers.get(streamHeader.topic);
19408
+ if (!streamHandlerCallback) {
19409
+ this.log.debug('ignoring incoming byte stream due to no handler for topic', streamHeader.topic);
19410
+ return;
19168
19411
  }
19169
- } catch (e_2_1) {
19170
- e_2 = {
19171
- error: e_2_1
19412
+ let streamController;
19413
+ const outOfBandFailureRejectingFuture = new Future();
19414
+ const info = {
19415
+ id: streamHeader.streamId,
19416
+ name: (_a = streamHeader.contentHeader.value.name) !== null && _a !== void 0 ? _a : 'unknown',
19417
+ mimeType: streamHeader.mimeType,
19418
+ size: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,
19419
+ topic: streamHeader.topic,
19420
+ timestamp: bigIntToNumber(streamHeader.timestamp),
19421
+ attributes: streamHeader.attributes
19172
19422
  };
19173
- } finally {
19174
- try {
19175
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
19176
- } finally {
19177
- if (e_2) throw e_2.error;
19423
+ const stream = new ReadableStream({
19424
+ start: controller => {
19425
+ streamController = controller;
19426
+ if (this.textStreamControllers.has(streamHeader.streamId)) {
19427
+ throw new DataStreamError("A data stream read is already in progress for a stream with id ".concat(streamHeader.streamId, "."), DataStreamErrorReason.AlreadyOpened);
19428
+ }
19429
+ this.byteStreamControllers.set(streamHeader.streamId, {
19430
+ info,
19431
+ controller: streamController,
19432
+ startTime: Date.now(),
19433
+ sendingParticipantIdentity: participantIdentity,
19434
+ outOfBandFailureRejectingFuture
19435
+ });
19436
+ }
19437
+ });
19438
+ streamHandlerCallback(new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength), outOfBandFailureRejectingFuture), {
19439
+ identity: participantIdentity
19440
+ });
19441
+ } else if (streamHeader.contentHeader.case === 'textHeader') {
19442
+ const streamHandlerCallback = this.textStreamHandlers.get(streamHeader.topic);
19443
+ if (!streamHandlerCallback) {
19444
+ this.log.debug('ignoring incoming text stream due to no handler for topic', streamHeader.topic);
19445
+ return;
19178
19446
  }
19447
+ let streamController;
19448
+ const outOfBandFailureRejectingFuture = new Future();
19449
+ const info = {
19450
+ id: streamHeader.streamId,
19451
+ mimeType: streamHeader.mimeType,
19452
+ size: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,
19453
+ topic: streamHeader.topic,
19454
+ timestamp: Number(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.textStreamControllers.set(streamHeader.streamId, {
19464
+ info,
19465
+ controller: streamController,
19466
+ startTime: Date.now(),
19467
+ sendingParticipantIdentity: participantIdentity,
19468
+ outOfBandFailureRejectingFuture
19469
+ });
19470
+ }
19471
+ });
19472
+ streamHandlerCallback(new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength), outOfBandFailureRejectingFuture), {
19473
+ identity: participantIdentity
19474
+ });
19179
19475
  }
19180
- return finalString;
19181
19476
  });
19182
19477
  }
19478
+ handleStreamChunk(chunk) {
19479
+ const fileBuffer = this.byteStreamControllers.get(chunk.streamId);
19480
+ if (fileBuffer) {
19481
+ if (chunk.content.length > 0) {
19482
+ fileBuffer.controller.enqueue(chunk);
19483
+ }
19484
+ }
19485
+ const textBuffer = this.textStreamControllers.get(chunk.streamId);
19486
+ if (textBuffer) {
19487
+ if (chunk.content.length > 0) {
19488
+ textBuffer.controller.enqueue(chunk);
19489
+ }
19490
+ }
19491
+ }
19492
+ handleStreamTrailer(trailer) {
19493
+ const textBuffer = this.textStreamControllers.get(trailer.streamId);
19494
+ if (textBuffer) {
19495
+ textBuffer.info.attributes = Object.assign(Object.assign({}, textBuffer.info.attributes), trailer.attributes);
19496
+ textBuffer.controller.close();
19497
+ this.textStreamControllers.delete(trailer.streamId);
19498
+ }
19499
+ const fileBuffer = this.byteStreamControllers.get(trailer.streamId);
19500
+ if (fileBuffer) {
19501
+ {
19502
+ fileBuffer.info.attributes = Object.assign(Object.assign({}, fileBuffer.info.attributes), trailer.attributes);
19503
+ fileBuffer.controller.close();
19504
+ this.byteStreamControllers.delete(trailer.streamId);
19505
+ }
19506
+ }
19507
+ }
19183
19508
  }
19184
19509
 
19185
19510
  class BaseStreamWriter {
@@ -19204,14 +19529,292 @@ class BaseStreamWriter {
19204
19529
  class TextStreamWriter extends BaseStreamWriter {}
19205
19530
  class ByteStreamWriter extends BaseStreamWriter {}
19206
19531
 
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;
19532
+ const STREAM_CHUNK_SIZE = 15000;
19533
+ /**
19534
+ * Manages sending custom user data via data channels.
19535
+ * @internal
19536
+ */
19537
+ class OutgoingDataStreamManager {
19538
+ constructor(engine, log) {
19539
+ this.engine = engine;
19540
+ this.log = log;
19212
19541
  }
19213
- get isLocal() {
19214
- return false;
19542
+ setupEngine(engine) {
19543
+ this.engine = engine;
19544
+ }
19545
+ /** {@inheritDoc LocalParticipant.sendText} */
19546
+ sendText(text, options) {
19547
+ return __awaiter(this, void 0, void 0, function* () {
19548
+ var _a;
19549
+ const streamId = crypto.randomUUID();
19550
+ const textInBytes = new TextEncoder().encode(text);
19551
+ const totalTextLength = textInBytes.byteLength;
19552
+ const fileIds = (_a = options === null || options === void 0 ? void 0 : options.attachments) === null || _a === void 0 ? void 0 : _a.map(() => crypto.randomUUID());
19553
+ const progresses = new Array(fileIds ? fileIds.length + 1 : 1).fill(0);
19554
+ const handleProgress = (progress, idx) => {
19555
+ var _a;
19556
+ progresses[idx] = progress;
19557
+ const totalProgress = progresses.reduce((acc, val) => acc + val, 0);
19558
+ (_a = options === null || options === void 0 ? void 0 : options.onProgress) === null || _a === void 0 ? void 0 : _a.call(options, totalProgress);
19559
+ };
19560
+ const writer = yield this.streamText({
19561
+ streamId,
19562
+ totalSize: totalTextLength,
19563
+ destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities,
19564
+ topic: options === null || options === void 0 ? void 0 : options.topic,
19565
+ attachedStreamIds: fileIds,
19566
+ attributes: options === null || options === void 0 ? void 0 : options.attributes
19567
+ });
19568
+ yield writer.write(text);
19569
+ // set text part of progress to 1
19570
+ handleProgress(1, 0);
19571
+ yield writer.close();
19572
+ if ((options === null || options === void 0 ? void 0 : options.attachments) && fileIds) {
19573
+ yield Promise.all(options.attachments.map((file, idx) => __awaiter(this, void 0, void 0, function* () {
19574
+ return this._sendFile(fileIds[idx], file, {
19575
+ topic: options.topic,
19576
+ mimeType: file.type,
19577
+ onProgress: progress => {
19578
+ handleProgress(progress, idx + 1);
19579
+ }
19580
+ });
19581
+ })));
19582
+ }
19583
+ return writer.info;
19584
+ });
19585
+ }
19586
+ /**
19587
+ * @internal
19588
+ * @experimental CAUTION, might get removed in a minor release
19589
+ */
19590
+ streamText(options) {
19591
+ return __awaiter(this, void 0, void 0, function* () {
19592
+ var _a, _b;
19593
+ const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
19594
+ const info = {
19595
+ id: streamId,
19596
+ mimeType: 'text/plain',
19597
+ timestamp: Date.now(),
19598
+ topic: (_b = options === null || options === void 0 ? void 0 : options.topic) !== null && _b !== void 0 ? _b : '',
19599
+ size: options === null || options === void 0 ? void 0 : options.totalSize,
19600
+ attributes: options === null || options === void 0 ? void 0 : options.attributes
19601
+ };
19602
+ const header = new DataStream_Header({
19603
+ streamId,
19604
+ mimeType: info.mimeType,
19605
+ topic: info.topic,
19606
+ timestamp: numberToBigInt(info.timestamp),
19607
+ totalLength: numberToBigInt(options === null || options === void 0 ? void 0 : options.totalSize),
19608
+ attributes: info.attributes,
19609
+ contentHeader: {
19610
+ case: 'textHeader',
19611
+ value: new DataStream_TextHeader({
19612
+ version: options === null || options === void 0 ? void 0 : options.version,
19613
+ attachedStreamIds: options === null || options === void 0 ? void 0 : options.attachedStreamIds,
19614
+ replyToStreamId: options === null || options === void 0 ? void 0 : options.replyToStreamId,
19615
+ operationType: (options === null || options === void 0 ? void 0 : options.type) === 'update' ? DataStream_OperationType.UPDATE : DataStream_OperationType.CREATE
19616
+ })
19617
+ }
19618
+ });
19619
+ const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
19620
+ const packet = new DataPacket({
19621
+ destinationIdentities,
19622
+ value: {
19623
+ case: 'streamHeader',
19624
+ value: header
19625
+ }
19626
+ });
19627
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
19628
+ let chunkId = 0;
19629
+ const engine = this.engine;
19630
+ const writableStream = new WritableStream({
19631
+ // Implement the sink
19632
+ write(text) {
19633
+ return __awaiter(this, void 0, void 0, function* () {
19634
+ for (const textByteChunk of splitUtf8(text, STREAM_CHUNK_SIZE)) {
19635
+ yield engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
19636
+ const chunk = new DataStream_Chunk({
19637
+ content: textByteChunk,
19638
+ streamId,
19639
+ chunkIndex: numberToBigInt(chunkId)
19640
+ });
19641
+ const chunkPacket = new DataPacket({
19642
+ destinationIdentities,
19643
+ value: {
19644
+ case: 'streamChunk',
19645
+ value: chunk
19646
+ }
19647
+ });
19648
+ yield engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
19649
+ chunkId += 1;
19650
+ }
19651
+ });
19652
+ },
19653
+ close() {
19654
+ return __awaiter(this, void 0, void 0, function* () {
19655
+ const trailer = new DataStream_Trailer({
19656
+ streamId
19657
+ });
19658
+ const trailerPacket = new DataPacket({
19659
+ destinationIdentities,
19660
+ value: {
19661
+ case: 'streamTrailer',
19662
+ value: trailer
19663
+ }
19664
+ });
19665
+ yield engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
19666
+ });
19667
+ },
19668
+ abort(err) {
19669
+ console.log('Sink error:', err);
19670
+ // TODO handle aborts to signal something to receiver side
19671
+ }
19672
+ });
19673
+ let onEngineClose = () => __awaiter(this, void 0, void 0, function* () {
19674
+ yield writer.close();
19675
+ });
19676
+ engine.once(EngineEvent.Closing, onEngineClose);
19677
+ const writer = new TextStreamWriter(writableStream, info, () => this.engine.off(EngineEvent.Closing, onEngineClose));
19678
+ return writer;
19679
+ });
19680
+ }
19681
+ sendFile(file, options) {
19682
+ return __awaiter(this, void 0, void 0, function* () {
19683
+ const streamId = crypto.randomUUID();
19684
+ yield this._sendFile(streamId, file, options);
19685
+ return {
19686
+ id: streamId
19687
+ };
19688
+ });
19689
+ }
19690
+ _sendFile(streamId, file, options) {
19691
+ return __awaiter(this, void 0, void 0, function* () {
19692
+ var _a;
19693
+ const writer = yield this.streamBytes({
19694
+ streamId,
19695
+ totalSize: file.size,
19696
+ name: file.name,
19697
+ mimeType: (_a = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _a !== void 0 ? _a : file.type,
19698
+ topic: options === null || options === void 0 ? void 0 : options.topic,
19699
+ destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities
19700
+ });
19701
+ const reader = file.stream().getReader();
19702
+ while (true) {
19703
+ const {
19704
+ done,
19705
+ value
19706
+ } = yield reader.read();
19707
+ if (done) {
19708
+ break;
19709
+ }
19710
+ yield writer.write(value);
19711
+ }
19712
+ yield writer.close();
19713
+ return writer.info;
19714
+ });
19715
+ }
19716
+ streamBytes(options) {
19717
+ return __awaiter(this, void 0, void 0, function* () {
19718
+ var _a, _b, _c, _d, _e;
19719
+ const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
19720
+ const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
19721
+ const info = {
19722
+ id: streamId,
19723
+ mimeType: (_b = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _b !== void 0 ? _b : 'application/octet-stream',
19724
+ topic: (_c = options === null || options === void 0 ? void 0 : options.topic) !== null && _c !== void 0 ? _c : '',
19725
+ timestamp: Date.now(),
19726
+ attributes: options === null || options === void 0 ? void 0 : options.attributes,
19727
+ size: options === null || options === void 0 ? void 0 : options.totalSize,
19728
+ name: (_d = options === null || options === void 0 ? void 0 : options.name) !== null && _d !== void 0 ? _d : 'unknown'
19729
+ };
19730
+ const header = new DataStream_Header({
19731
+ totalLength: numberToBigInt((_e = info.size) !== null && _e !== void 0 ? _e : 0),
19732
+ mimeType: info.mimeType,
19733
+ streamId,
19734
+ topic: info.topic,
19735
+ timestamp: numberToBigInt(Date.now()),
19736
+ attributes: info.attributes,
19737
+ contentHeader: {
19738
+ case: 'byteHeader',
19739
+ value: new DataStream_ByteHeader({
19740
+ name: info.name
19741
+ })
19742
+ }
19743
+ });
19744
+ const packet = new DataPacket({
19745
+ destinationIdentities,
19746
+ value: {
19747
+ case: 'streamHeader',
19748
+ value: header
19749
+ }
19750
+ });
19751
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
19752
+ let chunkId = 0;
19753
+ const writeMutex = new _();
19754
+ const engine = this.engine;
19755
+ const logLocal = this.log;
19756
+ const writableStream = new WritableStream({
19757
+ write(chunk) {
19758
+ return __awaiter(this, void 0, void 0, function* () {
19759
+ const unlock = yield writeMutex.lock();
19760
+ let byteOffset = 0;
19761
+ try {
19762
+ while (byteOffset < chunk.byteLength) {
19763
+ const subChunk = chunk.slice(byteOffset, byteOffset + STREAM_CHUNK_SIZE);
19764
+ yield engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
19765
+ const chunkPacket = new DataPacket({
19766
+ destinationIdentities,
19767
+ value: {
19768
+ case: 'streamChunk',
19769
+ value: new DataStream_Chunk({
19770
+ content: subChunk,
19771
+ streamId,
19772
+ chunkIndex: numberToBigInt(chunkId)
19773
+ })
19774
+ }
19775
+ });
19776
+ yield engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
19777
+ chunkId += 1;
19778
+ byteOffset += subChunk.byteLength;
19779
+ }
19780
+ } finally {
19781
+ unlock();
19782
+ }
19783
+ });
19784
+ },
19785
+ close() {
19786
+ return __awaiter(this, void 0, void 0, function* () {
19787
+ const trailer = new DataStream_Trailer({
19788
+ streamId
19789
+ });
19790
+ const trailerPacket = new DataPacket({
19791
+ destinationIdentities,
19792
+ value: {
19793
+ case: 'streamTrailer',
19794
+ value: trailer
19795
+ }
19796
+ });
19797
+ yield engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
19798
+ });
19799
+ },
19800
+ abort(err) {
19801
+ logLocal.error('Sink error:', err);
19802
+ }
19803
+ });
19804
+ const byteWriter = new ByteStreamWriter(writableStream, info);
19805
+ return byteWriter;
19806
+ });
19807
+ }
19808
+ }
19809
+
19810
+ class RemoteTrack extends Track {
19811
+ constructor(mediaTrack, sid, kind, receiver, loggerOptions) {
19812
+ super(mediaTrack, kind, loggerOptions);
19813
+ this.sid = sid;
19814
+ this.receiver = receiver;
19815
+ }
19816
+ get isLocal() {
19817
+ return false;
19215
19818
  }
19216
19819
  /** @internal */
19217
19820
  setMuted(muted) {
@@ -20533,10 +21136,9 @@ function trackPermissionToProto(perms) {
20533
21136
  });
20534
21137
  }
20535
21138
 
20536
- const STREAM_CHUNK_SIZE = 15000;
20537
21139
  class LocalParticipant extends Participant {
20538
21140
  /** @internal */
20539
- constructor(sid, identity, engine, options, roomRpcHandlers) {
21141
+ constructor(sid, identity, engine, options, roomRpcHandlers, roomOutgoingDataStreamManager) {
20540
21142
  super(sid, identity, undefined, undefined, undefined, {
20541
21143
  loggerName: options.loggerName,
20542
21144
  loggerContextCb: () => this.engine.logContext
@@ -20768,6 +21370,7 @@ class LocalParticipant extends Participant {
20768
21370
  this.activeDeviceMap = new Map([['audioinput', 'default'], ['videoinput', 'default'], ['audiooutput', 'default']]);
20769
21371
  this.pendingSignalRequests = new Map();
20770
21372
  this.rpcHandlers = roomRpcHandlers;
21373
+ this.roomOutgoingDataStreamManager = roomOutgoingDataStreamManager;
20771
21374
  }
20772
21375
  get lastCameraError() {
20773
21376
  return this.cameraError;
@@ -21919,6 +22522,7 @@ class LocalParticipant extends Participant {
21919
22522
  yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
21920
22523
  });
21921
22524
  }
22525
+ /** @deprecated Consider migrating to {@link sendText} */
21922
22526
  sendChatMessage(text, options) {
21923
22527
  return __awaiter(this, void 0, void 0, function* () {
21924
22528
  const msg = {
@@ -21940,6 +22544,7 @@ class LocalParticipant extends Participant {
21940
22544
  return msg;
21941
22545
  });
21942
22546
  }
22547
+ /** @deprecated Consider migrating to {@link sendText} */
21943
22548
  editChatMessage(editText, originalMessage) {
21944
22549
  return __awaiter(this, void 0, void 0, function* () {
21945
22550
  const msg = Object.assign(Object.assign({}, originalMessage), {
@@ -21949,277 +22554,62 @@ class LocalParticipant extends Participant {
21949
22554
  const packet = new DataPacket({
21950
22555
  value: {
21951
22556
  case: 'chatMessage',
21952
- 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
22557
+ value: new ChatMessage(Object.assign(Object.assign({}, msg), {
22558
+ timestamp: protoInt64.parse(msg.timestamp),
22559
+ editTimestamp: protoInt64.parse(msg.editTimestamp)
22560
+ }))
22166
22561
  }
22167
22562
  });
22168
22563
  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;
22564
+ this.emit(ParticipantEvent.ChatMessage, msg);
22565
+ return msg;
22566
+ });
22567
+ }
22568
+ /**
22569
+ * Sends the given string to participants in the room via the data channel.
22570
+ * For longer messages, consider using {@link streamText} instead.
22571
+ *
22572
+ * @param text The text payload
22573
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22574
+ */
22575
+ sendText(text, options) {
22576
+ return __awaiter(this, void 0, void 0, function* () {
22577
+ return this.roomOutgoingDataStreamManager.sendText(text, options);
22578
+ });
22579
+ }
22580
+ /**
22581
+ * Creates a new TextStreamWriter which can be used to stream text incrementally
22582
+ * to participants in the room via the data channel.
22583
+ *
22584
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22585
+ *
22586
+ * @internal
22587
+ * @experimental CAUTION, might get removed in a minor release
22588
+ */
22589
+ streamText(options) {
22590
+ return __awaiter(this, void 0, void 0, function* () {
22591
+ return this.roomOutgoingDataStreamManager.streamText(options);
22592
+ });
22593
+ }
22594
+ /** Send a File to all participants in the room via the data channel.
22595
+ * @param file The File object payload
22596
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22597
+ * @param options.onProgress A callback function used to monitor the upload progress percentage.
22598
+ */
22599
+ sendFile(file, options) {
22600
+ return __awaiter(this, void 0, void 0, function* () {
22601
+ return this.roomOutgoingDataStreamManager.sendFile(file, options);
22602
+ });
22603
+ }
22604
+ /**
22605
+ * Stream bytes incrementally to participants in the room via the data channel.
22606
+ * For sending files, consider using {@link sendFile} instead.
22607
+ *
22608
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
22609
+ */
22610
+ streamBytes(options) {
22611
+ return __awaiter(this, void 0, void 0, function* () {
22612
+ return this.roomOutgoingDataStreamManager.streamBytes(options);
22223
22613
  });
22224
22614
  }
22225
22615
  /**
@@ -23051,10 +23441,6 @@ class Room extends eventsExports.EventEmitter {
23051
23441
  this.log = livekitLogger;
23052
23442
  this.bufferedEvents = [];
23053
23443
  this.isResuming = false;
23054
- this.byteStreamControllers = new Map();
23055
- this.textStreamControllers = new Map();
23056
- this.byteStreamHandlers = new Map();
23057
- this.textStreamHandlers = new Map();
23058
23444
  this.rpcHandlers = new Map();
23059
23445
  this.connect = (url, token, opts) => __awaiter(this, void 0, void 0, function* () {
23060
23446
  var _a;
@@ -23594,12 +23980,8 @@ class Room extends eventsExports.EventEmitter {
23594
23980
  this.handleChatMessage(participant, packet.value.value);
23595
23981
  } else if (packet.value.case === 'metrics') {
23596
23982
  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);
23983
+ } else if (packet.value.case === 'streamHeader' || packet.value.case === 'streamChunk' || packet.value.case === 'streamTrailer') {
23984
+ this.handleDataStream(packet);
23603
23985
  } else if (packet.value.case === 'rpcRequest') {
23604
23986
  const rpc = packet.value.value;
23605
23987
  this.handleIncomingRpcRequest(packet.participantIdentity, rpc.id, rpc.method, rpc.payload, rpc.responseTimeoutMs, rpc.version);
@@ -23615,7 +23997,6 @@ class Room extends eventsExports.EventEmitter {
23615
23997
  // also emit on the participant
23616
23998
  participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.SipDTMFReceived, dtmf);
23617
23999
  };
23618
- this.bufferedSegments = new Map();
23619
24000
  this.handleTranscription = (_remoteParticipant, transcription) => {
23620
24001
  // find the participant
23621
24002
  const participant = transcription.transcribedParticipantIdentity === this.localParticipant.identity ? this.localParticipant : this.getParticipantByIdentity(transcription.transcribedParticipantIdentity);
@@ -23632,6 +24013,10 @@ class Room extends eventsExports.EventEmitter {
23632
24013
  this.handleMetrics = (metrics, participant) => {
23633
24014
  this.emit(RoomEvent.MetricsReceived, metrics, participant);
23634
24015
  };
24016
+ this.handleDataStream = packet => {
24017
+ this.incomingDataStreamManager.handleDataStreamPacket(packet);
24018
+ };
24019
+ this.bufferedSegments = new Map();
23635
24020
  this.handleAudioPlaybackStarted = () => {
23636
24021
  if (this.canPlaybackAudio) {
23637
24022
  return;
@@ -23766,8 +24151,10 @@ class Room extends eventsExports.EventEmitter {
23766
24151
  this.options.videoCaptureDefaults = Object.assign(Object.assign({}, videoDefaults), options === null || options === void 0 ? void 0 : options.videoCaptureDefaults);
23767
24152
  this.options.publishDefaults = Object.assign(Object.assign({}, publishDefaults), options === null || options === void 0 ? void 0 : options.publishDefaults);
23768
24153
  this.maybeCreateEngine();
24154
+ this.incomingDataStreamManager = new IncomingDataStreamManager();
24155
+ this.outgoingDataStreamManager = new OutgoingDataStreamManager(this.engine, this.log);
23769
24156
  this.disconnectLock = new _();
23770
- this.localParticipant = new LocalParticipant('', '', this.engine, this.options, this.rpcHandlers);
24157
+ this.localParticipant = new LocalParticipant('', '', this.engine, this.options, this.rpcHandlers, this.outgoingDataStreamManager);
23771
24158
  if (this.options.videoCaptureDefaults.deviceId) {
23772
24159
  this.localParticipant.activeDeviceMap.set('videoinput', unwrapConstraint(this.options.videoCaptureDefaults.deviceId));
23773
24160
  }
@@ -23794,22 +24181,16 @@ class Room extends eventsExports.EventEmitter {
23794
24181
  }
23795
24182
  }
23796
24183
  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);
24184
+ return this.incomingDataStreamManager.registerTextStreamHandler(topic, callback);
23801
24185
  }
23802
24186
  unregisterTextStreamHandler(topic) {
23803
- this.textStreamHandlers.delete(topic);
24187
+ return this.incomingDataStreamManager.unregisterTextStreamHandler(topic);
23804
24188
  }
23805
24189
  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);
24190
+ return this.incomingDataStreamManager.registerByteStreamHandler(topic, callback);
23810
24191
  }
23811
24192
  unregisterByteStreamHandler(topic) {
23812
- this.byteStreamHandlers.delete(topic);
24193
+ return this.incomingDataStreamManager.unregisterByteStreamHandler(topic);
23813
24194
  }
23814
24195
  /**
23815
24196
  * Establishes the participant as a receiver for calls of the specified RPC method.
@@ -23851,44 +24232,6 @@ class Room extends eventsExports.EventEmitter {
23851
24232
  unregisterRpcMethod(method) {
23852
24233
  this.rpcHandlers.delete(method);
23853
24234
  }
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
24235
  /**
23893
24236
  * @experimental
23894
24237
  */
@@ -24053,6 +24396,9 @@ class Room extends eventsExports.EventEmitter {
24053
24396
  if (this.e2eeManager) {
24054
24397
  this.e2eeManager.setupEngine(this.engine);
24055
24398
  }
24399
+ if (this.outgoingDataStreamManager) {
24400
+ this.outgoingDataStreamManager.setupEngine(this.engine);
24401
+ }
24056
24402
  }
24057
24403
  /**
24058
24404
  * getLocalDevices abstracts navigator.mediaDevices.enumerateDevices.
@@ -24429,6 +24775,7 @@ class Room extends eventsExports.EventEmitter {
24429
24775
  this.isResuming = false;
24430
24776
  this.bufferedEvents = [];
24431
24777
  this.transcriptionReceivedTimes.clear();
24778
+ this.incomingDataStreamManager.clearHandlersAndControllers();
24432
24779
  if (this.state === ConnectionState.Disconnected) {
24433
24780
  return;
24434
24781
  }
@@ -24480,6 +24827,7 @@ class Room extends eventsExports.EventEmitter {
24480
24827
  if (!participant) {
24481
24828
  return;
24482
24829
  }
24830
+ this.incomingDataStreamManager.validateParticipantHasNoActiveDataStreams(identity);
24483
24831
  participant.trackPublications.forEach(publication => {
24484
24832
  participant.unpublishTrack(publication.trackSid, true);
24485
24833
  });
@@ -24487,99 +24835,44 @@ class Room extends eventsExports.EventEmitter {
24487
24835
  participant.setDisconnected();
24488
24836
  (_a = this.localParticipant) === null || _a === void 0 ? void 0 : _a.handleParticipantDisconnected(participant.identity);
24489
24837
  }
24490
- handleStreamHeader(streamHeader, participantIdentity) {
24838
+ handleIncomingRpcRequest(callerIdentity, requestId, method, payload, responseTimeout, version) {
24491
24839
  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
24840
+ yield this.engine.publishRpcAck(callerIdentity, requestId);
24841
+ if (version !== 1) {
24842
+ yield this.engine.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_VERSION'));
24843
+ return;
24844
+ }
24845
+ const handler = this.rpcHandlers.get(method);
24846
+ if (!handler) {
24847
+ yield this.engine.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_METHOD'));
24848
+ return;
24849
+ }
24850
+ let responseError = null;
24851
+ let responsePayload = null;
24852
+ try {
24853
+ const response = yield handler({
24854
+ requestId,
24855
+ callerIdentity,
24856
+ payload,
24857
+ responseTimeout
24521
24858
  });
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;
24859
+ if (byteLength(response) > MAX_PAYLOAD_BYTES) {
24860
+ responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE');
24861
+ console.warn("RPC Response payload too large for ".concat(method));
24862
+ } else {
24863
+ responsePayload = response;
24864
+ }
24865
+ } catch (error) {
24866
+ if (error instanceof RpcError) {
24867
+ responseError = error;
24868
+ } else {
24869
+ console.warn("Uncaught error returned by RPC handler for ".concat(method, ". Returning APPLICATION_ERROR instead."), error);
24870
+ responseError = RpcError.builtIn('APPLICATION_ERROR');
24527
24871
  }
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
24872
  }
24873
+ yield this.engine.publishRpcResponse(callerIdentity, requestId, responsePayload, responseError);
24551
24874
  });
24552
24875
  }
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
24876
  /**
24584
24877
  * attempt to select the default devices if the previously selected devices are no longer available after a device change event
24585
24878
  */
@@ -25835,5 +26128,5 @@ function isFacingModeValue(item) {
25835
26128
  return item === undefined || allowedValues.includes(item);
25836
26129
  }
25837
26130
 
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 };
26131
+ 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
26132
  //# sourceMappingURL=livekit-client.esm.mjs.map