videomail-client 16.0.3 → 16.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/umd/index.js CHANGED
@@ -5,13 +5,13 @@
5
5
  else root["VideomailClient"] = factory();
6
6
  })(globalThis, ()=>(()=>{
7
7
  var __webpack_modules__ = {
8
- eq (module1, __webpack_exports__, __webpack_require__) {
8
+ pJ (module1, __webpack_exports__, __webpack_require__) {
9
9
  "use strict";
10
- var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("erX");
11
- var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1_default = /*#__PURE__*/ __webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1);
12
- var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("zm");
13
- var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0_default = /*#__PURE__*/ __webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0);
14
- var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0_default()(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1_default());
10
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("K7");
11
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1_default = /*#__PURE__*/ __webpack_require__.n(_node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1);
12
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("Ly");
13
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0_default = /*#__PURE__*/ __webpack_require__.n(_node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0);
14
+ var ___CSS_LOADER_EXPORT___ = _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0_default()(_node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1_default());
15
15
  ___CSS_LOADER_EXPORT___.push([
16
16
  module1.id,
17
17
  `@keyframes blink {
@@ -159,7 +159,7 @@
159
159
  });
160
160
  },
161
161
  b6 (module1, exports1, __webpack_require__) {
162
- var EventEmitter = __webpack_require__("q").EventEmitter, inherits = __webpack_require__("c5"), raf = __webpack_require__("jY"), methods;
162
+ var EventEmitter = __webpack_require__("qU").EventEmitter, inherits = __webpack_require__("c5"), raf = __webpack_require__("jY"), methods;
163
163
  var defaultRAFObject = {
164
164
  requestAnimationFrame: raf,
165
165
  cancelAnimationFrame: raf.cancel
@@ -1837,7 +1837,7 @@
1837
1837
  },
1838
1838
  xF (module1, __unused_rspack_exports, __webpack_require__) {
1839
1839
  "use strict";
1840
- var document1 = __webpack_require__("g");
1840
+ var document1 = __webpack_require__("gM");
1841
1841
  var Event1 = __webpack_require__("cE");
1842
1842
  var Keys = __webpack_require__("Dy");
1843
1843
  module1.exports = Visibility;
@@ -2192,7 +2192,7 @@
2192
2192
  var isArray = __webpack_require__("ab");
2193
2193
  var Duplex;
2194
2194
  Readable.ReadableState = ReadableState;
2195
- __webpack_require__("q").EventEmitter;
2195
+ __webpack_require__("qU").EventEmitter;
2196
2196
  var EElistenerCount = function(emitter, type) {
2197
2197
  return emitter.listeners(type).length;
2198
2198
  };
@@ -3456,7 +3456,7 @@
3456
3456
  };
3457
3457
  },
3458
3458
  tb (module1, __unused_rspack_exports, __webpack_require__) {
3459
- module1.exports = __webpack_require__("q").EventEmitter;
3459
+ module1.exports = __webpack_require__("qU").EventEmitter;
3460
3460
  },
3461
3461
  GB (module1, exports1, __webpack_require__) {
3462
3462
  exports1 = module1.exports = __webpack_require__("rp");
@@ -3853,7 +3853,7 @@
3853
3853
  "use strict";
3854
3854
  module1.exports = Object;
3855
3855
  },
3856
- q (module1) {
3856
+ qU (module1) {
3857
3857
  "use strict";
3858
3858
  var R = 'object' == typeof Reflect ? Reflect : null;
3859
3859
  var ReflectApply = R && 'function' == typeof R.apply ? R.apply : function(target, receiver, args) {
@@ -4903,7 +4903,7 @@
4903
4903
  return tuple.listen;
4904
4904
  }
4905
4905
  },
4906
- g (module1, __unused_rspack_exports, __webpack_require__) {
4906
+ gM (module1, __unused_rspack_exports, __webpack_require__) {
4907
4907
  var topLevel = void 0 !== __webpack_require__.g ? __webpack_require__.g : "u" > typeof window ? window : {};
4908
4908
  var minDoc = __webpack_require__("MM");
4909
4909
  var doccy;
@@ -8806,7 +8806,7 @@
8806
8806
  var isArray = __webpack_require__("N3");
8807
8807
  var Duplex;
8808
8808
  Readable.ReadableState = ReadableState;
8809
- __webpack_require__("q").EventEmitter;
8809
+ __webpack_require__("qU").EventEmitter;
8810
8810
  var EElistenerCount = function(emitter, type) {
8811
8811
  return emitter.listeners(type).length;
8812
8812
  };
@@ -8825,7 +8825,7 @@
8825
8825
  var debug = void 0;
8826
8826
  debug = debugUtil && debugUtil.debuglog ? debugUtil.debuglog('stream') : function() {};
8827
8827
  var BufferList = __webpack_require__("aI");
8828
- var destroyImpl = __webpack_require__("R5");
8828
+ var destroyImpl = __webpack_require__("R");
8829
8829
  var StringDecoder;
8830
8830
  util.inherits(Readable, Stream);
8831
8831
  var kProxyEvents = [
@@ -9560,7 +9560,7 @@
9560
9560
  function _isUint8Array(obj) {
9561
9561
  return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
9562
9562
  }
9563
- var destroyImpl = __webpack_require__("R5");
9563
+ var destroyImpl = __webpack_require__("R");
9564
9564
  util.inherits(Writable, Stream);
9565
9565
  function nop() {}
9566
9566
  function WritableState(options, stream) {
@@ -10014,7 +10014,7 @@
10014
10014
  return this.constructor.name + ' ' + obj;
10015
10015
  };
10016
10016
  },
10017
- R5 (module1, __unused_rspack_exports, __webpack_require__) {
10017
+ R (module1, __unused_rspack_exports, __webpack_require__) {
10018
10018
  "use strict";
10019
10019
  var pna = __webpack_require__("sj");
10020
10020
  function destroy(err, cb) {
@@ -10070,7 +10070,7 @@
10070
10070
  };
10071
10071
  },
10072
10072
  O (module1, __unused_rspack_exports, __webpack_require__) {
10073
- module1.exports = __webpack_require__("q").EventEmitter;
10073
+ module1.exports = __webpack_require__("qU").EventEmitter;
10074
10074
  },
10075
10075
  Fo (module1, exports1, __webpack_require__) {
10076
10076
  exports1 = module1.exports = __webpack_require__("w");
@@ -10560,7 +10560,7 @@
10560
10560
  a () {},
10561
10561
  Av () {},
10562
10562
  eD () {},
10563
- zm (module1) {
10563
+ Ly (module1) {
10564
10564
  "use strict";
10565
10565
  module1.exports = function(cssWithMappingToString) {
10566
10566
  var list = [];
@@ -10614,13 +10614,13 @@
10614
10614
  return list;
10615
10615
  };
10616
10616
  },
10617
- erX (module1) {
10617
+ K7 (module1) {
10618
10618
  "use strict";
10619
10619
  module1.exports = function(i) {
10620
10620
  return i[1];
10621
10621
  };
10622
10622
  },
10623
- R (module1) {
10623
+ q (module1) {
10624
10624
  "use strict";
10625
10625
  var stylesInDOM = [];
10626
10626
  function getIndexByIdentifier(identifier) {
@@ -10699,7 +10699,7 @@
10699
10699
  };
10700
10700
  };
10701
10701
  },
10702
- uZ (module1) {
10702
+ f (module1) {
10703
10703
  "use strict";
10704
10704
  var memo = {};
10705
10705
  function getTarget(target) {
@@ -10721,7 +10721,7 @@
10721
10721
  }
10722
10722
  module1.exports = insertBySelector;
10723
10723
  },
10724
- mK (module1) {
10724
+ z7 (module1) {
10725
10725
  "use strict";
10726
10726
  function insertStyleElement(options) {
10727
10727
  var element = document.createElement("style");
@@ -10731,7 +10731,7 @@
10731
10731
  }
10732
10732
  module1.exports = insertStyleElement;
10733
10733
  },
10734
- kU (module1, __unused_rspack_exports, __webpack_require__) {
10734
+ g (module1, __unused_rspack_exports, __webpack_require__) {
10735
10735
  "use strict";
10736
10736
  function setAttributesWithoutAttributes(styleElement) {
10737
10737
  var nonce = __webpack_require__.nc;
@@ -10739,7 +10739,7 @@
10739
10739
  }
10740
10740
  module1.exports = setAttributesWithoutAttributes;
10741
10741
  },
10742
- Bd (module1) {
10742
+ V (module1) {
10743
10743
  "use strict";
10744
10744
  function apply(styleElement, options, obj) {
10745
10745
  var css = "";
@@ -10776,7 +10776,7 @@
10776
10776
  }
10777
10777
  module1.exports = domAPI;
10778
10778
  },
10779
- snK (module1) {
10779
+ AD (module1) {
10780
10780
  "use strict";
10781
10781
  function styleTagTransform(css, styleElement) {
10782
10782
  if (styleElement.styleSheet) styleElement.styleSheet.cssText = css;
@@ -11121,7 +11121,7 @@
11121
11121
  var client = __webpack_require__("Q");
11122
11122
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11123
11123
  var package_namespaceObject = {
11124
- rE: "16.0.3"
11124
+ rE: "16.1.1"
11125
11125
  };
11126
11126
  function isAudioEnabled(options) {
11127
11127
  return Boolean(options.audio.enabled);
@@ -15689,19 +15689,19 @@
15689
15689
  return newOptions;
15690
15690
  }
15691
15691
  const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
15692
- var injectStylesIntoStyleTag = __webpack_require__("R");
15692
+ var injectStylesIntoStyleTag = __webpack_require__("q");
15693
15693
  var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
15694
- var styleDomAPI = __webpack_require__("Bd");
15694
+ var styleDomAPI = __webpack_require__("V");
15695
15695
  var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
15696
- var insertBySelector = __webpack_require__("uZ");
15696
+ var insertBySelector = __webpack_require__("f");
15697
15697
  var insertBySelector_default = /*#__PURE__*/ __webpack_require__.n(insertBySelector);
15698
- var setAttributesWithoutAttributes = __webpack_require__("kU");
15698
+ var setAttributesWithoutAttributes = __webpack_require__("g");
15699
15699
  var setAttributesWithoutAttributes_default = /*#__PURE__*/ __webpack_require__.n(setAttributesWithoutAttributes);
15700
- var insertStyleElement = __webpack_require__("mK");
15700
+ var insertStyleElement = __webpack_require__("z7");
15701
15701
  var insertStyleElement_default = /*#__PURE__*/ __webpack_require__.n(insertStyleElement);
15702
- var styleTagTransform = __webpack_require__("snK");
15702
+ var styleTagTransform = __webpack_require__("AD");
15703
15703
  var styleTagTransform_default = /*#__PURE__*/ __webpack_require__.n(styleTagTransform);
15704
- var main = __webpack_require__("eq");
15704
+ var main = __webpack_require__("pJ");
15705
15705
  var main_options = {};
15706
15706
  main_options.styleTagTransform = styleTagTransform_default();
15707
15707
  main_options.setAttributes = setAttributesWithoutAttributes_default();
@@ -18636,10 +18636,14 @@
18636
18636
  this.on("USER_MEDIA_READY", ()=>{
18637
18637
  this.facingMode?.show();
18638
18638
  });
18639
+ this.on("CAMERA_RECOVERING", ()=>{
18640
+ this.facingMode?.hide();
18641
+ });
18639
18642
  this.on("RECORDING", ()=>{
18640
18643
  this.startRecording();
18641
18644
  });
18642
18645
  this.on("RESUMING", ()=>{
18646
+ this.hidePause();
18643
18647
  this.resumeRecording();
18644
18648
  });
18645
18649
  this.on("STOPPING", ()=>{
@@ -19254,6 +19258,7 @@
19254
19258
  class AudioRecorder {
19255
19259
  scriptProcessor;
19256
19260
  audioInput;
19261
+ volume;
19257
19262
  vcAudioContext;
19258
19263
  userMedia;
19259
19264
  options;
@@ -19276,9 +19281,19 @@
19276
19281
  const float32Array = e.inputBuffer.getChannelData(0);
19277
19282
  cb(new esm(float32Array));
19278
19283
  }
19284
+ disconnect() {
19285
+ if (this.scriptProcessor) {
19286
+ this.scriptProcessor.onaudioprocess = null;
19287
+ this.scriptProcessor.disconnect();
19288
+ }
19289
+ this.audioInput?.disconnect();
19290
+ this.volume?.disconnect();
19291
+ }
19279
19292
  init(localMediaStream) {
19280
19293
  this.options.logger.debug("AudioRecorder: init()");
19294
+ this.disconnect();
19281
19295
  const volume = this.getAudioContext().createGain();
19296
+ this.volume = volume;
19282
19297
  try {
19283
19298
  this.audioInput = this.getAudioContext().createMediaStreamSource(localMediaStream);
19284
19299
  } catch (exc) {
@@ -19310,10 +19325,13 @@
19310
19325
  this.onAudioProcess(e, cb);
19311
19326
  };
19312
19327
  }
19328
+ async resume() {
19329
+ const context = this.getAudioContext();
19330
+ if ("running" !== context.state) await context.resume();
19331
+ }
19313
19332
  stop() {
19314
19333
  this.options.logger.debug("AudioRecorder: stop()");
19315
- if (this.scriptProcessor) this.scriptProcessor.onaudioprocess = null;
19316
- if (this.audioInput) this.audioInput.disconnect();
19334
+ this.disconnect();
19317
19335
  if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
19318
19336
  this.options.logger.debug("AudioRecorder: audio context is closed");
19319
19337
  this.vcAudioContext = void 0;
@@ -19358,6 +19376,8 @@
19358
19376
  onLoadedMetaDataReached = false;
19359
19377
  playingPromiseReached = false;
19360
19378
  videoTrackLabel;
19379
+ clearInitializationListeners;
19380
+ initializationId = 0;
19361
19381
  outputEvent = (e)=>{
19362
19382
  this.logEvent(e.type, {
19363
19383
  readyState: this.rawVisualUserMedia?.readyState
@@ -19399,6 +19419,16 @@
19399
19419
  this.currentVisualStream = void 0;
19400
19420
  }
19401
19421
  }
19422
+ async restorePlayback() {
19423
+ if (!this.rawVisualUserMedia) throw new Error("No camera preview is available.");
19424
+ await this.rawVisualUserMedia.play();
19425
+ await this.audioRecorder?.resume();
19426
+ }
19427
+ hasLiveStream() {
19428
+ const videoReady = this.currentVisualStream?.getVideoTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
19429
+ const audioReady = !isAudioEnabled(this.options) || this.currentVisualStream?.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
19430
+ return Boolean(this.currentVisualStream?.active && videoReady && audioReady);
19431
+ }
19402
19432
  hasEnded() {
19403
19433
  if (this.rawVisualUserMedia?.ended) return this.rawVisualUserMedia.ended;
19404
19434
  return !this.currentVisualStream?.active;
@@ -19413,6 +19443,7 @@
19413
19443
  }
19414
19444
  unloadRemainingEventListeners() {
19415
19445
  this.options.logger.debug("UserMedia: unloadRemainingEventListeners()");
19446
+ this.clearInitializationListeners?.();
19416
19447
  mediaEvents.forEach((eventName)=>{
19417
19448
  this.rawVisualUserMedia?.removeEventListener(eventName, this.outputEvent);
19418
19449
  });
@@ -19422,11 +19453,13 @@
19422
19453
  this.removeListener("SENDING_FIRST_FRAME");
19423
19454
  this.audioRecorder?.record(audioCallback);
19424
19455
  }
19425
- init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, switchingFacingMode) {
19456
+ init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, params) {
19457
+ this.clearInitializationListeners?.();
19426
19458
  this.stop(localMediaStream, {
19427
19459
  aboutToInitialize: true,
19428
- switchingFacingMode
19460
+ preserveRecordingState: Boolean(params?.switchingFacingMode || params?.recovering)
19429
19461
  });
19462
+ const initializationId = ++this.initializationId;
19430
19463
  this.onPlayReached = false;
19431
19464
  this.onLoadedMetaDataReached = false;
19432
19465
  this.playingPromiseReached = false;
@@ -19439,18 +19472,25 @@
19439
19472
  this.rawVisualUserMedia?.removeEventListener("play", onPlay);
19440
19473
  this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
19441
19474
  };
19475
+ this.clearInitializationListeners = ()=>{
19476
+ this.rawVisualUserMedia?.removeEventListener("play", onPlay);
19477
+ this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
19478
+ this.clearInitializationListeners = void 0;
19479
+ };
19442
19480
  const play = ()=>{
19443
19481
  try {
19444
19482
  this.rawVisualUserMedia?.load();
19445
19483
  if (this.rawVisualUserMedia?.paused) {
19446
19484
  this.options.logger.debug(`UserMedia: play(): media.readyState=${this.rawVisualUserMedia.readyState}, media.paused=${this.rawVisualUserMedia.paused}, media.ended=${this.rawVisualUserMedia.ended}, media.played=${string_pretty(this.rawVisualUserMedia.played)}`);
19447
19485
  this.rawVisualUserMedia.play().then(()=>{
19486
+ if (initializationId !== this.initializationId) return;
19448
19487
  if (!this.playingPromiseReached) {
19449
19488
  this.options.logger.debug("UserMedia: play promise successful. Playing now.");
19450
19489
  this.playingPromiseReached = true;
19451
19490
  }
19452
19491
  }).catch((exc)=>{
19453
- if (exc instanceof Error) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
19492
+ if (initializationId !== this.initializationId) return;
19493
+ if (exc instanceof Error && !params?.recovering) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
19454
19494
  else {
19455
19495
  unloadAllEventListeners();
19456
19496
  endedEarlyCallback(error_createError({
@@ -19471,16 +19511,28 @@
19471
19511
  this.options.logger.debug(`UserMedia: fireCallbacks(readyState=${readyState}, onPlayReached=${this.onPlayReached}, onLoadedMetaDataReached=${this.onLoadedMetaDataReached})`);
19472
19512
  if (this.onPlayReached && this.onLoadedMetaDataReached) if (this.audioRecorder) try {
19473
19513
  this.audioRecorder.init(localMediaStream);
19474
- this.on("SENDING_FIRST_FRAME", ()=>{
19475
- this.audioRecord(audioCallback);
19476
- });
19477
- videoCallback();
19514
+ const onAudioReady = ()=>{
19515
+ if (initializationId !== this.initializationId || this.currentVisualStream !== localMediaStream) return;
19516
+ if (this.recording) this.audioRecord(audioCallback);
19517
+ else this.on("SENDING_FIRST_FRAME", ()=>{
19518
+ this.audioRecord(audioCallback);
19519
+ });
19520
+ this.clearInitializationListeners?.();
19521
+ videoCallback();
19522
+ };
19523
+ if (params?.recovering) this.audioRecorder.resume().then(onAudioReady).catch(endedEarlyCallback);
19524
+ else onAudioReady();
19478
19525
  } catch (exc) {
19479
19526
  unloadAllEventListeners();
19480
- this.stop();
19527
+ this.stop(void 0, {
19528
+ preserveRecordingState: Boolean(params?.recovering)
19529
+ });
19481
19530
  endedEarlyCallback(exc);
19482
19531
  }
19483
- else videoCallback();
19532
+ else {
19533
+ this.clearInitializationListeners?.();
19534
+ videoCallback();
19535
+ }
19484
19536
  };
19485
19537
  const onPlay = ()=>{
19486
19538
  try {
@@ -19550,9 +19602,8 @@
19550
19602
  this.setVisualStream(localMediaStream);
19551
19603
  play();
19552
19604
  } catch (exc) {
19553
- this.emit("ERROR", {
19554
- exc
19555
- });
19605
+ unloadAllEventListeners();
19606
+ endedEarlyCallback(exc);
19556
19607
  }
19557
19608
  }
19558
19609
  getVideoTrackLabel() {
@@ -19565,6 +19616,8 @@
19565
19616
  try {
19566
19617
  let chosenStream = visualStream;
19567
19618
  if (!params?.aboutToInitialize) {
19619
+ this.initializationId++;
19620
+ this.clearInitializationListeners?.();
19568
19621
  chosenStream ??= this.currentVisualStream;
19569
19622
  const tracks = chosenStream?.getTracks();
19570
19623
  if (tracks) tracks.forEach((track)=>{
@@ -19574,7 +19627,7 @@
19574
19627
  this.audioRecorder?.stop();
19575
19628
  this.audioRecorder = void 0;
19576
19629
  }
19577
- if (!params?.switchingFacingMode) this.paused = this.recording = false;
19630
+ if (!params?.preserveRecordingState) this.paused = this.recording = false;
19578
19631
  } catch (exc) {
19579
19632
  this.emit("ERROR", {
19580
19633
  exc
@@ -19672,6 +19725,7 @@
19672
19725
  originalAnimationFrameObject;
19673
19726
  samplesCount = 0;
19674
19727
  framesCount = 0;
19728
+ recordingAudioSampleRate;
19675
19729
  recordingStats;
19676
19730
  confirmedFrameNumber = 0;
19677
19731
  confirmedSampleNumber = 0;
@@ -19690,6 +19744,10 @@
19690
19744
  ctx;
19691
19745
  userMediaLoaded;
19692
19746
  userMediaLoading = false;
19747
+ mediaRequestId = 0;
19748
+ cameraRecovery;
19749
+ resolveCameraRecovery;
19750
+ resumeAfterCameraRecovery = false;
19693
19751
  submitting = false;
19694
19752
  unloaded;
19695
19753
  stopTime;
@@ -19953,7 +20011,9 @@
19953
20011
  this.pingInterval = void 0;
19954
20012
  }
19955
20013
  onAudioSample(audioSample) {
19956
- if (this.recordingTransportFailed) return;
20014
+ if (this.recordingTransportFailed || this.cameraRecovery) return;
20015
+ const sampleRate = this.userMedia?.getAudioSampleRate();
20016
+ if (sampleRate && sampleRate > 0) this.recordingAudioSampleRate ??= sampleRate;
19957
20017
  this.samplesCount++;
19958
20018
  this.mediaTransmissionStarted = true;
19959
20019
  const audioBuffer = audioSample.toBuffer();
@@ -19966,19 +20026,26 @@
19966
20026
  try {
19967
20027
  this.options.logger.debug(`Recorder: onUserMediaReady(${params ? string_pretty(params) : ""})`);
19968
20028
  const switchingFacingMode = params?.switchingFacingMode;
20029
+ if (params?.recovering) {
20030
+ if (!this.userMedia?.hasLiveStream()) return void this.failCameraRecovery(new Error("The restored camera has no live tracks."));
20031
+ if (void 0 !== this.recordingAudioSampleRate && this.userMedia.getAudioSampleRate() !== this.recordingAudioSampleRate) return void this.failCameraRecovery(new Error("The audio sample rate changed during recovery."));
20032
+ }
19969
20033
  this.userMediaLoading = this.blocking = this.unloaded = this.submitting = false;
19970
20034
  this.userMediaLoaded = true;
19971
20035
  this.clearUserMediaTimeout();
19972
- if (!switchingFacingMode) this.loop = this.createLoop();
20036
+ if (!switchingFacingMode && !params?.recovering) this.loop = this.createLoop();
19973
20037
  this.show();
19974
20038
  if (params?.recordWhenReady) this.record();
19975
20039
  this.emit("USER_MEDIA_READY", {
19976
20040
  switchingFacingMode: params?.switchingFacingMode,
20041
+ recovering: params?.recovering,
19977
20042
  paused: this.isPaused(),
19978
20043
  recordWhenReady: params?.recordWhenReady
19979
20044
  });
20045
+ if (params?.recovering) this.finishCameraRecovery(true);
19980
20046
  } catch (exc) {
19981
- this.emit("ERROR", {
20047
+ if (params?.recovering) this.failCameraRecovery(exc);
20048
+ else this.emit("ERROR", {
19982
20049
  exc
19983
20050
  });
19984
20051
  }
@@ -20349,6 +20416,7 @@
20349
20416
  return this.blocking;
20350
20417
  }
20351
20418
  userMediaErrorCallback(err, usedConstraints) {
20419
+ if (this.cameraRecovery) return void this.failCameraRecovery(err);
20352
20420
  if (this.unloaded) return void this.options.logger.debug(`Recorder: already unloaded. Not going to throw error ${string_pretty(err)}`);
20353
20421
  this.userMediaLoading = false;
20354
20422
  this.clearUserMediaTimeout();
@@ -20380,7 +20448,7 @@
20380
20448
  });
20381
20449
  }
20382
20450
  }
20383
- getUserMediaCallback(localStream, params) {
20451
+ getUserMediaCallback(localStream, params, requestId = this.mediaRequestId) {
20384
20452
  if (!this.userMedia) {
20385
20453
  localStream.getTracks().forEach((track)=>{
20386
20454
  track.stop();
@@ -20393,20 +20461,20 @@
20393
20461
  this.options.logger.debug(`Recorder: getUserMediaCallback(${params ? string_pretty(params) : ""})`);
20394
20462
  if (this.showUserMedia()) try {
20395
20463
  this.userMedia.init(localStream, ()=>{
20396
- if (!this.unloaded) this.onUserMediaReady(params);
20464
+ if (!this.unloaded && requestId === this.mediaRequestId) this.onUserMediaReady(params);
20397
20465
  }, this.onAudioSample.bind(this), (err)=>{
20398
- if (!this.unloaded) this.userMediaErrorCallback(err, {
20466
+ if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(err, {
20399
20467
  audio: isAudioEnabled(this.options)
20400
20468
  });
20401
- }, params?.switchingFacingMode);
20469
+ }, params);
20402
20470
  } catch (exc) {
20403
20471
  localStream.getTracks().forEach((track)=>{
20404
20472
  track.stop();
20405
20473
  });
20406
20474
  this.userMediaLoading = false;
20407
20475
  this.clearUserMediaTimeout();
20408
- this.emit("ERROR", {
20409
- exc
20476
+ this.userMediaErrorCallback(exc, {
20477
+ audio: isAudioEnabled(this.options)
20410
20478
  });
20411
20479
  }
20412
20480
  else {
@@ -20415,12 +20483,14 @@
20415
20483
  });
20416
20484
  this.userMediaLoading = false;
20417
20485
  this.clearUserMediaTimeout();
20486
+ this.cancelCameraRecovery();
20418
20487
  }
20419
20488
  }
20420
20489
  loadGenuineUserMedia(params) {
20421
20490
  this.options.logger.debug(`Recorder: loadGenuineUserMedia(${params ? string_pretty(params) : ""})`);
20422
20491
  this.emit("ASKING_WEBCAM_PERMISSION");
20423
20492
  this.userMediaLoading = true;
20493
+ const requestId = ++this.mediaRequestId;
20424
20494
  const constraints = {
20425
20495
  video: {
20426
20496
  frameRate: {
@@ -20434,18 +20504,18 @@
20434
20504
  this.options.logger.debug(string_summarize("Recorder: available webcam constraints are:", navigator.mediaDevices.getSupportedConstraints()));
20435
20505
  const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
20436
20506
  streamPromise.then((localStream)=>{
20437
- if (this.unloaded) return void localStream.getTracks().forEach((track)=>{
20507
+ if (this.unloaded || requestId !== this.mediaRequestId) return void localStream.getTracks().forEach((track)=>{
20438
20508
  track.stop();
20439
20509
  });
20440
- this.getUserMediaCallback(localStream, params);
20510
+ this.getUserMediaCallback(localStream, params, requestId);
20441
20511
  }).catch((reason)=>{
20442
- if (!this.unloaded) this.userMediaErrorCallback(reason, constraints);
20512
+ if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(reason, constraints);
20443
20513
  });
20444
20514
  }
20445
20515
  configureVideoConstraints(constraints, params) {
20446
20516
  const videoConstraints = "object" == typeof constraints.video ? constraints.video : void 0;
20447
20517
  if (!videoConstraints) return;
20448
- if (params?.switchingFacingMode) videoConstraints.facingMode = params.switchingFacingMode;
20518
+ if (params?.switchingFacingMode || params?.recovering) videoConstraints.facingMode = params.switchingFacingMode || this.facingMode;
20449
20519
  if (this.options.video.width) videoConstraints.width = {
20450
20520
  ideal: this.options.video.width
20451
20521
  };
@@ -20473,8 +20543,8 @@
20473
20543
  if (!this.userMediaLoaded) {
20474
20544
  this.userMediaLoading = false;
20475
20545
  const err = util_getBrowser(this.options).getNoAccessIssue();
20476
- this.emit("ERROR", {
20477
- err
20546
+ this.userMediaErrorCallback(err, {
20547
+ audio: isAudioEnabled(this.options)
20478
20548
  });
20479
20549
  }
20480
20550
  }, this.options.timeouts.userMedia);
@@ -20484,6 +20554,7 @@
20484
20554
  this.clearUserMediaTimeout();
20485
20555
  this.options.logger.debug("Recorder: failed to load genuine user media");
20486
20556
  this.userMediaLoading = false;
20557
+ if (this.cameraRecovery) return void this.failCameraRecovery(exc);
20487
20558
  const errorListeners = this.getListeners("ERROR");
20488
20559
  if (errorListeners?.length) this.emit("ERROR", {
20489
20560
  exc
@@ -20607,9 +20678,10 @@
20607
20678
  return this.recordingStats;
20608
20679
  }
20609
20680
  getAudioSampleRate() {
20610
- return this.userMedia?.getAudioSampleRate();
20681
+ return this.recordingAudioSampleRate ?? this.userMedia?.getAudioSampleRate();
20611
20682
  }
20612
20683
  stop(params) {
20684
+ this.cancelCameraRecovery();
20613
20685
  if (!this.userMedia) throw new Error("No user media defined, unable to stop");
20614
20686
  this.options.logger.debug(`Recorder: stop(${params ? string_pretty(params) : ""})`);
20615
20687
  this.recordingActive = false;
@@ -20635,7 +20707,7 @@
20635
20707
  if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
20636
20708
  if (isAudioEnabled(this.options) && this.userMedia) {
20637
20709
  this.recordingStats.samplesCount = this.samplesCount;
20638
- this.recordingStats.sampleRate = this.userMedia.getAudioSampleRate();
20710
+ this.recordingStats.sampleRate = this.recordingAudioSampleRate ?? this.userMedia.getAudioSampleRate();
20639
20711
  }
20640
20712
  this.writeCommand("stop", this.recordingStats, ()=>{
20641
20713
  this.emit("STOPPED", {
@@ -20690,6 +20762,7 @@
20690
20762
  this.built = this.connecting = this.connected = false;
20691
20763
  }
20692
20764
  reset() {
20765
+ this.cancelCameraRecovery();
20693
20766
  if (!this.unloaded) {
20694
20767
  this.options.logger.debug("Recorder: reset()");
20695
20768
  this.emit("RESETTING");
@@ -20717,7 +20790,70 @@
20717
20790
  isReady() {
20718
20791
  return this.userMedia?.isReady();
20719
20792
  }
20793
+ finishCameraRecovery(restored) {
20794
+ this.clearUserMediaTimeout();
20795
+ const resolve = this.resolveCameraRecovery;
20796
+ const resumeRequested = restored && this.resumeAfterCameraRecovery;
20797
+ this.cameraRecovery = void 0;
20798
+ this.resolveCameraRecovery = void 0;
20799
+ this.resumeAfterCameraRecovery = false;
20800
+ resolve?.(restored);
20801
+ if (resumeRequested && this.isPaused()) this.resume();
20802
+ }
20803
+ cancelCameraRecovery() {
20804
+ if (!this.cameraRecovery) return;
20805
+ this.mediaRequestId++;
20806
+ this.userMediaLoading = false;
20807
+ this.userMediaLoaded = false;
20808
+ this.clearUserMediaTimeout();
20809
+ this.userMedia?.stop(void 0, {
20810
+ preserveRecordingState: true
20811
+ });
20812
+ this.finishCameraRecovery(false);
20813
+ }
20814
+ failCameraRecovery(exc) {
20815
+ this.cancelCameraRecovery();
20816
+ const err = error_createError({
20817
+ message: "Camera recovery failed",
20818
+ explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
20819
+ exc,
20820
+ options: this.options
20821
+ });
20822
+ this.emit("CAMERA_RECOVERY_FAILED", {
20823
+ err
20824
+ });
20825
+ }
20826
+ recoverCamera() {
20827
+ if (this.cameraRecovery) return this.cameraRecovery;
20828
+ if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
20829
+ const liveStream = this.userMedia.hasLiveStream();
20830
+ if (liveStream && this.isRecording()) return Promise.resolve(true);
20831
+ if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
20832
+ const recovery = new Promise((resolve)=>{
20833
+ this.resolveCameraRecovery = resolve;
20834
+ });
20835
+ this.cameraRecovery = recovery;
20836
+ this.emit("CAMERA_RECOVERING");
20837
+ if (liveStream) {
20838
+ this.userMediaTimeout = window.setTimeout(()=>{
20839
+ this.failCameraRecovery(new Error("Camera playback recovery timed out."));
20840
+ }, this.options.timeouts.userMedia);
20841
+ this.userMedia.restorePlayback().then(()=>{
20842
+ if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
20843
+ }).catch((exc)=>{
20844
+ if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
20845
+ });
20846
+ } else {
20847
+ this.userMediaLoaded = false;
20848
+ this.loadUserMedia({
20849
+ recovering: true
20850
+ });
20851
+ }
20852
+ return recovery;
20853
+ }
20720
20854
  pause(params) {
20855
+ this.cancelCameraRecovery();
20856
+ this.resumeAfterDrain = false;
20721
20857
  if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
20722
20858
  else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
20723
20859
  this.userMedia?.pause();
@@ -20726,10 +20862,15 @@
20726
20862
  this.sendPings();
20727
20863
  }
20728
20864
  resume() {
20865
+ if (this.cameraRecovery || !this.userMedia?.hasLiveStream()) {
20866
+ this.recoverCamera();
20867
+ this.resumeAfterCameraRecovery = Boolean(this.cameraRecovery);
20868
+ return;
20869
+ }
20729
20870
  this.options.logger.debug(`Recorder: resume() with frame ${this.framesCount}`);
20730
20871
  this.stopPings();
20731
20872
  this.emit("RESUMING");
20732
- this.userMedia?.resume();
20873
+ this.userMedia.resume();
20733
20874
  if (this.streamBackpressured) this.resumeAfterDrain = true;
20734
20875
  else this.loop.start();
20735
20876
  }
@@ -20784,7 +20925,7 @@
20784
20925
  return newLoop;
20785
20926
  }
20786
20927
  record() {
20787
- if (this.unloaded) return;
20928
+ if (this.unloaded || this.cameraRecovery) return;
20788
20929
  if (!this.connected) {
20789
20930
  this.options.logger.debug("Recorder: reconnecting before recording …");
20790
20931
  this.initSocket(()=>{
@@ -20857,6 +20998,7 @@
20857
20998
  this.recordingActive = true;
20858
20999
  this.recordingTransportFailed = false;
20859
21000
  this.mediaTransmissionStarted = false;
21001
+ this.recordingAudioSampleRate = void 0;
20860
21002
  this.userMedia.record();
20861
21003
  this.emit("RECORDING", {
20862
21004
  framesCount: this.framesCount
@@ -20918,7 +21060,7 @@
20918
21060
  }
20919
21061
  }
20920
21062
  switchFacingMode() {
20921
- if (!util_getBrowser(this.options).isMobile()) return;
21063
+ if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
20922
21064
  if ("user" === this.facingMode) this.facingMode = "environment";
20923
21065
  else if ("environment" === this.facingMode) this.facingMode = "user";
20924
21066
  else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
@@ -20929,12 +21071,14 @@
20929
21071
  initEvents() {
20930
21072
  this.options.logger.debug("Recorder: initEvents()");
20931
21073
  this.on("SUBMITTING", ()=>{
21074
+ this.cancelCameraRecovery();
20932
21075
  this.submitting = true;
20933
21076
  });
20934
21077
  this.on("SUBMITTED", ()=>{
20935
21078
  this.submitting = false;
20936
21079
  });
20937
21080
  this.on("BLOCKING", ()=>{
21081
+ this.cancelCameraRecovery();
20938
21082
  this.blocking = true;
20939
21083
  this.clearUserMediaTimeout();
20940
21084
  });
@@ -21362,6 +21506,7 @@
21362
21506
  notifier;
21363
21507
  visualsElement;
21364
21508
  built = false;
21509
+ cameraRecoveryFailed = false;
21365
21510
  constructor(container, options){
21366
21511
  super("Visuals", options);
21367
21512
  this.container = container;
@@ -21396,6 +21541,17 @@
21396
21541
  this.endWaiting();
21397
21542
  this.container.enableForm(false);
21398
21543
  });
21544
+ this.on("CAMERA_RECOVERING", ()=>{
21545
+ this.clearCameraRecoveryNotice();
21546
+ });
21547
+ this.on("CAMERA_RECOVERY_FAILED", ({ err })=>{
21548
+ this.cameraRecoveryFailed = true;
21549
+ this.options.logger.warn(err);
21550
+ if (this.options.displayErrors) this.notifier.notify(err.message, err.explanation, {
21551
+ problem: true
21552
+ });
21553
+ this.endWaiting();
21554
+ });
21399
21555
  this.on("PREVIEW", ()=>{
21400
21556
  this.endWaiting();
21401
21557
  });
@@ -21474,6 +21630,7 @@
21474
21630
  child.remove();
21475
21631
  }
21476
21632
  reset() {
21633
+ this.clearCameraRecoveryNotice();
21477
21634
  this.endWaiting();
21478
21635
  this.recorder.reset();
21479
21636
  }
@@ -21484,11 +21641,13 @@
21484
21641
  this.container.endWaiting();
21485
21642
  }
21486
21643
  stop(params) {
21644
+ this.clearCameraRecoveryNotice();
21487
21645
  this.recorder.stop(params);
21488
21646
  this.recorderInsides.hidePause();
21489
21647
  }
21490
21648
  back(keepHidden = false, cb) {
21491
21649
  this.options.logger.debug(`Visuals: back(keepHidden = ${keepHidden})`);
21650
+ this.cameraRecoveryFailed = false;
21492
21651
  this.replay.hide();
21493
21652
  this.notifier.hide();
21494
21653
  if (keepHidden) {
@@ -21510,6 +21669,7 @@
21510
21669
  if (!this.built) return;
21511
21670
  const e = params?.e;
21512
21671
  this.options.logger.debug(`Visuals: unload(${e ? string_pretty(e) : ""})`);
21672
+ this.cameraRecoveryFailed = false;
21513
21673
  this.recorder.unload(params);
21514
21674
  this.recorderInsides.unload();
21515
21675
  this.replay.unload(params);
@@ -21523,13 +21683,31 @@
21523
21683
  this.recorder.pause(params);
21524
21684
  this.recorderInsides.showPause();
21525
21685
  }
21686
+ clearCameraRecoveryNotice() {
21687
+ if (this.cameraRecoveryFailed) {
21688
+ this.notifier.hide();
21689
+ this.recorder.show();
21690
+ this.cameraRecoveryFailed = false;
21691
+ }
21692
+ }
21693
+ recoverCamera() {
21694
+ if (this.replay.isShown() || this.isNotifying() && !this.cameraRecoveryFailed) return Promise.resolve(false);
21695
+ this.clearCameraRecoveryNotice();
21696
+ return this.recorder.recoverCamera();
21697
+ }
21526
21698
  resume() {
21527
- if (this.recorderInsides.isCountingDown()) this.recorderInsides.resumeCountdown();
21528
- else this.recorder.resume();
21529
- this.recorderInsides.hidePause();
21699
+ this.clearCameraRecoveryNotice();
21700
+ if (this.recorderInsides.isCountingDown()) {
21701
+ this.recorderInsides.resumeCountdown();
21702
+ this.recorderInsides.hidePause();
21703
+ } else {
21704
+ this.recorder.resume();
21705
+ if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
21706
+ }
21530
21707
  }
21531
21708
  pauseOrResume() {
21532
- if (this.isRecordable()) {
21709
+ if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
21710
+ else if (this.isRecordable()) {
21533
21711
  if (this.isRecording()) this.pause();
21534
21712
  else if (this.recorder.isPaused()) this.resume();
21535
21713
  else if (this.recorder.isReady()) this.recorder.record();
@@ -21772,10 +21950,10 @@
21772
21950
  });
21773
21951
  if (!playerOnly) this.stopListeningToVisibility = this.visibility.onChange((visible)=>{
21774
21952
  if (this.built) if (visible) {
21775
- if (isAutoPauseEnabled(this.options) && this.isCountingDown()) this.resume();
21953
+ if (isAutoPauseEnabled(this.options)) this.recoverCamera();
21776
21954
  this.emit("VISIBLE");
21777
21955
  } else {
21778
- if (isAutoPauseEnabled(this.options) && (this.isCountingDown() || this.isRecording())) this.pause();
21956
+ if (isAutoPauseEnabled(this.options)) this.suspend();
21779
21957
  this.emit("INVISIBLE");
21780
21958
  }
21781
21959
  });
@@ -21963,6 +22141,13 @@
21963
22141
  pause(params) {
21964
22142
  this.visuals.pause(params);
21965
22143
  }
22144
+ suspend() {
22145
+ if (this.built && (this.isCountingDown() || this.isRecording() || this.isPaused())) this.pause();
22146
+ }
22147
+ recoverCamera() {
22148
+ if (!this.built || this.hasError || this.submitted) return Promise.resolve(false);
22149
+ return this.visuals.recoverCamera();
22150
+ }
21966
22151
  validate(event, force = false) {
21967
22152
  let runValidation = true;
21968
22153
  let valid = true;
@@ -22281,6 +22466,12 @@
22281
22466
  record() {
22282
22467
  this.container.record();
22283
22468
  }
22469
+ pause() {
22470
+ this.container.suspend();
22471
+ }
22472
+ recoverCamera() {
22473
+ return this.container.recoverCamera();
22474
+ }
22284
22475
  getLogLines() {
22285
22476
  if (this.options.logger.getLines) return this.options.logger.getLines();
22286
22477
  }