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/esm/index.js CHANGED
@@ -69,12 +69,12 @@ __webpack_require__.m = __webpack_modules__;
69
69
  __webpack_require__.nc = void 0;
70
70
  })();
71
71
  __webpack_require__.add({
72
- eq (module, __webpack_exports__, __webpack_require__) {
73
- var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("er");
74
- 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);
75
- var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("zm");
76
- 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);
77
- 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());
72
+ pJ (module, __webpack_exports__, __webpack_require__) {
73
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("K7");
74
+ 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);
75
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("L");
76
+ 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);
77
+ 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());
78
78
  ___CSS_LOADER_EXPORT___.push([
79
79
  module.id,
80
80
  `@keyframes blink {
@@ -3378,7 +3378,7 @@ __webpack_require__.add({
3378
3378
  }
3379
3379
  module.exports = whichTypedArray;
3380
3380
  },
3381
- zm (module) {
3381
+ L (module) {
3382
3382
  module.exports = function(cssWithMappingToString) {
3383
3383
  var list = [];
3384
3384
  list.toString = function() {
@@ -3431,12 +3431,12 @@ __webpack_require__.add({
3431
3431
  return list;
3432
3432
  };
3433
3433
  },
3434
- er (module) {
3434
+ K7 (module) {
3435
3435
  module.exports = function(i) {
3436
3436
  return i[1];
3437
3437
  };
3438
3438
  },
3439
- RR (module) {
3439
+ qB (module) {
3440
3440
  var stylesInDOM = [];
3441
3441
  function getIndexByIdentifier(identifier) {
3442
3442
  var result = -1;
@@ -3514,7 +3514,7 @@ __webpack_require__.add({
3514
3514
  };
3515
3515
  };
3516
3516
  },
3517
- uZ (module) {
3517
+ fR (module) {
3518
3518
  var memo = {};
3519
3519
  function getTarget(target) {
3520
3520
  if (void 0 === memo[target]) {
@@ -3535,7 +3535,7 @@ __webpack_require__.add({
3535
3535
  }
3536
3536
  module.exports = insertBySelector;
3537
3537
  },
3538
- mK (module) {
3538
+ z7 (module) {
3539
3539
  function insertStyleElement(options) {
3540
3540
  var element = document.createElement("style");
3541
3541
  options.setAttributes(element, options.attributes);
@@ -3544,14 +3544,14 @@ __webpack_require__.add({
3544
3544
  }
3545
3545
  module.exports = insertStyleElement;
3546
3546
  },
3547
- kU (module, __unused_rspack_exports, __webpack_require__) {
3547
+ g2 (module, __unused_rspack_exports, __webpack_require__) {
3548
3548
  function setAttributesWithoutAttributes(styleElement) {
3549
3549
  var nonce = __webpack_require__.nc;
3550
3550
  if (nonce) styleElement.setAttribute("nonce", nonce);
3551
3551
  }
3552
3552
  module.exports = setAttributesWithoutAttributes;
3553
3553
  },
3554
- Bd (module) {
3554
+ Vq (module) {
3555
3555
  function apply(styleElement, options, obj) {
3556
3556
  var css = "";
3557
3557
  if (obj.supports) css += "@supports (".concat(obj.supports, ") {");
@@ -3587,7 +3587,7 @@ __webpack_require__.add({
3587
3587
  }
3588
3588
  module.exports = domAPI;
3589
3589
  },
3590
- snK (module) {
3590
+ AD (module) {
3591
3591
  function styleTagTransform(css, styleElement) {
3592
3592
  if (styleElement.styleSheet) styleElement.styleSheet.cssText = css;
3593
3593
  else {
@@ -3620,7 +3620,7 @@ const constants = {
3620
3620
  }
3621
3621
  };
3622
3622
  var package_namespaceObject = {
3623
- rE: "16.0.3"
3623
+ rE: "16.1.1"
3624
3624
  };
3625
3625
  function isAudioEnabled(options) {
3626
3626
  return Boolean(options.audio.enabled);
@@ -4855,18 +4855,18 @@ function mergeWithDefaultOptions(options = {}) {
4855
4855
  return newOptions;
4856
4856
  }
4857
4857
  const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
4858
- const main_0 = __webpack_require__("eq");
4859
- const styleTagTransform = __webpack_require__("snK");
4858
+ const main_0 = __webpack_require__("pJ");
4859
+ const styleTagTransform = __webpack_require__("AD");
4860
4860
  var styleTagTransform_default = /*#__PURE__*/ __webpack_require__.n(styleTagTransform);
4861
- const setAttributesWithoutAttributes = __webpack_require__("kU");
4861
+ const setAttributesWithoutAttributes = __webpack_require__("g2");
4862
4862
  var setAttributesWithoutAttributes_default = /*#__PURE__*/ __webpack_require__.n(setAttributesWithoutAttributes);
4863
- const insertBySelector = __webpack_require__("uZ");
4863
+ const insertBySelector = __webpack_require__("fR");
4864
4864
  var insertBySelector_default = /*#__PURE__*/ __webpack_require__.n(insertBySelector);
4865
- const styleDomAPI = __webpack_require__("Bd");
4865
+ const styleDomAPI = __webpack_require__("Vq");
4866
4866
  var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
4867
- const insertStyleElement = __webpack_require__("mK");
4867
+ const insertStyleElement = __webpack_require__("z7");
4868
4868
  var insertStyleElement_default = /*#__PURE__*/ __webpack_require__.n(insertStyleElement);
4869
- const injectStylesIntoStyleTag = __webpack_require__("RR");
4869
+ const injectStylesIntoStyleTag = __webpack_require__("qB");
4870
4870
  var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
4871
4871
  var main_options = {};
4872
4872
  main_options.styleTagTransform = styleTagTransform_default();
@@ -5760,10 +5760,14 @@ class RecorderInsides extends util_Despot {
5760
5760
  this.on("USER_MEDIA_READY", ()=>{
5761
5761
  this.facingMode?.show();
5762
5762
  });
5763
+ this.on("CAMERA_RECOVERING", ()=>{
5764
+ this.facingMode?.hide();
5765
+ });
5763
5766
  this.on("RECORDING", ()=>{
5764
5767
  this.startRecording();
5765
5768
  });
5766
5769
  this.on("RESUMING", ()=>{
5770
+ this.hidePause();
5767
5771
  this.resumeRecording();
5768
5772
  });
5769
5773
  this.on("STOPPING", ()=>{
@@ -6333,9 +6337,19 @@ class AudioRecorder {
6333
6337
  const float32Array = e.inputBuffer.getChannelData(0);
6334
6338
  cb(new audio_sample(float32Array));
6335
6339
  }
6340
+ disconnect() {
6341
+ if (this.scriptProcessor) {
6342
+ this.scriptProcessor.onaudioprocess = null;
6343
+ this.scriptProcessor.disconnect();
6344
+ }
6345
+ this.audioInput?.disconnect();
6346
+ this.volume?.disconnect();
6347
+ }
6336
6348
  init(localMediaStream) {
6337
6349
  this.options.logger.debug("AudioRecorder: init()");
6350
+ this.disconnect();
6338
6351
  const volume = this.getAudioContext().createGain();
6352
+ this.volume = volume;
6339
6353
  try {
6340
6354
  this.audioInput = this.getAudioContext().createMediaStreamSource(localMediaStream);
6341
6355
  } catch (exc) {
@@ -6367,10 +6381,13 @@ class AudioRecorder {
6367
6381
  this.onAudioProcess(e, cb);
6368
6382
  };
6369
6383
  }
6384
+ async resume() {
6385
+ const context = this.getAudioContext();
6386
+ if ("running" !== context.state) await context.resume();
6387
+ }
6370
6388
  stop() {
6371
6389
  this.options.logger.debug("AudioRecorder: stop()");
6372
- if (this.scriptProcessor) this.scriptProcessor.onaudioprocess = null;
6373
- if (this.audioInput) this.audioInput.disconnect();
6390
+ this.disconnect();
6374
6391
  if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
6375
6392
  this.options.logger.debug("AudioRecorder: audio context is closed");
6376
6393
  this.vcAudioContext = void 0;
@@ -6389,6 +6406,7 @@ class AudioRecorder {
6389
6406
  constructor(userMedia, options){
6390
6407
  AudioRecorder_define_property(this, "scriptProcessor", void 0);
6391
6408
  AudioRecorder_define_property(this, "audioInput", void 0);
6409
+ AudioRecorder_define_property(this, "volume", void 0);
6392
6410
  AudioRecorder_define_property(this, "vcAudioContext", void 0);
6393
6411
  AudioRecorder_define_property(this, "userMedia", void 0);
6394
6412
  AudioRecorder_define_property(this, "options", void 0);
@@ -6448,6 +6466,16 @@ class UserMedia extends util_Despot {
6448
6466
  this.currentVisualStream = void 0;
6449
6467
  }
6450
6468
  }
6469
+ async restorePlayback() {
6470
+ if (!this.rawVisualUserMedia) throw new Error("No camera preview is available.");
6471
+ await this.rawVisualUserMedia.play();
6472
+ await this.audioRecorder?.resume();
6473
+ }
6474
+ hasLiveStream() {
6475
+ const videoReady = this.currentVisualStream?.getVideoTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
6476
+ const audioReady = !isAudioEnabled(this.options) || this.currentVisualStream?.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
6477
+ return Boolean(this.currentVisualStream?.active && videoReady && audioReady);
6478
+ }
6451
6479
  hasEnded() {
6452
6480
  if (this.rawVisualUserMedia?.ended) return this.rawVisualUserMedia.ended;
6453
6481
  return !this.currentVisualStream?.active;
@@ -6462,6 +6490,7 @@ class UserMedia extends util_Despot {
6462
6490
  }
6463
6491
  unloadRemainingEventListeners() {
6464
6492
  this.options.logger.debug("UserMedia: unloadRemainingEventListeners()");
6493
+ this.clearInitializationListeners?.();
6465
6494
  mediaEvents.forEach((eventName)=>{
6466
6495
  this.rawVisualUserMedia?.removeEventListener(eventName, this.outputEvent);
6467
6496
  });
@@ -6471,11 +6500,13 @@ class UserMedia extends util_Despot {
6471
6500
  this.removeListener("SENDING_FIRST_FRAME");
6472
6501
  this.audioRecorder?.record(audioCallback);
6473
6502
  }
6474
- init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, switchingFacingMode) {
6503
+ init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, params) {
6504
+ this.clearInitializationListeners?.();
6475
6505
  this.stop(localMediaStream, {
6476
6506
  aboutToInitialize: true,
6477
- switchingFacingMode
6507
+ preserveRecordingState: Boolean(params?.switchingFacingMode || params?.recovering)
6478
6508
  });
6509
+ const initializationId = ++this.initializationId;
6479
6510
  this.onPlayReached = false;
6480
6511
  this.onLoadedMetaDataReached = false;
6481
6512
  this.playingPromiseReached = false;
@@ -6488,18 +6519,25 @@ class UserMedia extends util_Despot {
6488
6519
  this.rawVisualUserMedia?.removeEventListener("play", onPlay);
6489
6520
  this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
6490
6521
  };
6522
+ this.clearInitializationListeners = ()=>{
6523
+ this.rawVisualUserMedia?.removeEventListener("play", onPlay);
6524
+ this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
6525
+ this.clearInitializationListeners = void 0;
6526
+ };
6491
6527
  const play = ()=>{
6492
6528
  try {
6493
6529
  this.rawVisualUserMedia?.load();
6494
6530
  if (this.rawVisualUserMedia?.paused) {
6495
6531
  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)}`);
6496
6532
  this.rawVisualUserMedia.play().then(()=>{
6533
+ if (initializationId !== this.initializationId) return;
6497
6534
  if (!this.playingPromiseReached) {
6498
6535
  this.options.logger.debug("UserMedia: play promise successful. Playing now.");
6499
6536
  this.playingPromiseReached = true;
6500
6537
  }
6501
6538
  }).catch((exc)=>{
6502
- if (exc instanceof Error) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
6539
+ if (initializationId !== this.initializationId) return;
6540
+ if (exc instanceof Error && !params?.recovering) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
6503
6541
  else {
6504
6542
  unloadAllEventListeners();
6505
6543
  endedEarlyCallback(error_createError({
@@ -6520,16 +6558,28 @@ class UserMedia extends util_Despot {
6520
6558
  this.options.logger.debug(`UserMedia: fireCallbacks(readyState=${readyState}, onPlayReached=${this.onPlayReached}, onLoadedMetaDataReached=${this.onLoadedMetaDataReached})`);
6521
6559
  if (this.onPlayReached && this.onLoadedMetaDataReached) if (this.audioRecorder) try {
6522
6560
  this.audioRecorder.init(localMediaStream);
6523
- this.on("SENDING_FIRST_FRAME", ()=>{
6524
- this.audioRecord(audioCallback);
6525
- });
6526
- videoCallback();
6561
+ const onAudioReady = ()=>{
6562
+ if (initializationId !== this.initializationId || this.currentVisualStream !== localMediaStream) return;
6563
+ if (this.recording) this.audioRecord(audioCallback);
6564
+ else this.on("SENDING_FIRST_FRAME", ()=>{
6565
+ this.audioRecord(audioCallback);
6566
+ });
6567
+ this.clearInitializationListeners?.();
6568
+ videoCallback();
6569
+ };
6570
+ if (params?.recovering) this.audioRecorder.resume().then(onAudioReady).catch(endedEarlyCallback);
6571
+ else onAudioReady();
6527
6572
  } catch (exc) {
6528
6573
  unloadAllEventListeners();
6529
- this.stop();
6574
+ this.stop(void 0, {
6575
+ preserveRecordingState: Boolean(params?.recovering)
6576
+ });
6530
6577
  endedEarlyCallback(exc);
6531
6578
  }
6532
- else videoCallback();
6579
+ else {
6580
+ this.clearInitializationListeners?.();
6581
+ videoCallback();
6582
+ }
6533
6583
  };
6534
6584
  const onPlay = ()=>{
6535
6585
  try {
@@ -6599,9 +6649,8 @@ class UserMedia extends util_Despot {
6599
6649
  this.setVisualStream(localMediaStream);
6600
6650
  play();
6601
6651
  } catch (exc) {
6602
- this.emit("ERROR", {
6603
- exc
6604
- });
6652
+ unloadAllEventListeners();
6653
+ endedEarlyCallback(exc);
6605
6654
  }
6606
6655
  }
6607
6656
  getVideoTrackLabel() {
@@ -6614,6 +6663,8 @@ class UserMedia extends util_Despot {
6614
6663
  try {
6615
6664
  let chosenStream = visualStream;
6616
6665
  if (!params?.aboutToInitialize) {
6666
+ this.initializationId++;
6667
+ this.clearInitializationListeners?.();
6617
6668
  chosenStream ??= this.currentVisualStream;
6618
6669
  const tracks = chosenStream?.getTracks();
6619
6670
  if (tracks) tracks.forEach((track)=>{
@@ -6623,7 +6674,7 @@ class UserMedia extends util_Despot {
6623
6674
  this.audioRecorder?.stop();
6624
6675
  this.audioRecorder = void 0;
6625
6676
  }
6626
- if (!params?.switchingFacingMode) this.paused = this.recording = false;
6677
+ if (!params?.preserveRecordingState) this.paused = this.recording = false;
6627
6678
  } catch (exc) {
6628
6679
  this.emit("ERROR", {
6629
6680
  exc
@@ -6710,7 +6761,7 @@ class UserMedia extends util_Despot {
6710
6761
  };
6711
6762
  }
6712
6763
  constructor(recorder, options){
6713
- super("UserMedia", options), userMedia_define_property(this, "recorder", void 0), userMedia_define_property(this, "rawVisualUserMedia", void 0), userMedia_define_property(this, "paused", false), userMedia_define_property(this, "recording", false), userMedia_define_property(this, "audioRecorder", void 0), userMedia_define_property(this, "currentVisualStream", void 0), userMedia_define_property(this, "onPlayReached", false), userMedia_define_property(this, "onLoadedMetaDataReached", false), userMedia_define_property(this, "playingPromiseReached", false), userMedia_define_property(this, "videoTrackLabel", void 0), userMedia_define_property(this, "outputEvent", (e)=>{
6764
+ super("UserMedia", options), userMedia_define_property(this, "recorder", void 0), userMedia_define_property(this, "rawVisualUserMedia", void 0), userMedia_define_property(this, "paused", false), userMedia_define_property(this, "recording", false), userMedia_define_property(this, "audioRecorder", void 0), userMedia_define_property(this, "currentVisualStream", void 0), userMedia_define_property(this, "onPlayReached", false), userMedia_define_property(this, "onLoadedMetaDataReached", false), userMedia_define_property(this, "playingPromiseReached", false), userMedia_define_property(this, "videoTrackLabel", void 0), userMedia_define_property(this, "clearInitializationListeners", void 0), userMedia_define_property(this, "initializationId", 0), userMedia_define_property(this, "outputEvent", (e)=>{
6714
6765
  this.logEvent(e.type, {
6715
6766
  readyState: this.rawVisualUserMedia?.readyState
6716
6767
  });
@@ -6971,7 +7022,9 @@ class Recorder extends util_Despot {
6971
7022
  this.pingInterval = void 0;
6972
7023
  }
6973
7024
  onAudioSample(audioSample) {
6974
- if (this.recordingTransportFailed) return;
7025
+ if (this.recordingTransportFailed || this.cameraRecovery) return;
7026
+ const sampleRate = this.userMedia?.getAudioSampleRate();
7027
+ if (sampleRate && sampleRate > 0) this.recordingAudioSampleRate ??= sampleRate;
6975
7028
  this.samplesCount++;
6976
7029
  this.mediaTransmissionStarted = true;
6977
7030
  const audioBuffer = audioSample.toBuffer();
@@ -6984,19 +7037,26 @@ class Recorder extends util_Despot {
6984
7037
  try {
6985
7038
  this.options.logger.debug(`Recorder: onUserMediaReady(${params ? string_pretty(params) : ""})`);
6986
7039
  const switchingFacingMode = params?.switchingFacingMode;
7040
+ if (params?.recovering) {
7041
+ if (!this.userMedia?.hasLiveStream()) return void this.failCameraRecovery(new Error("The restored camera has no live tracks."));
7042
+ if (void 0 !== this.recordingAudioSampleRate && this.userMedia.getAudioSampleRate() !== this.recordingAudioSampleRate) return void this.failCameraRecovery(new Error("The audio sample rate changed during recovery."));
7043
+ }
6987
7044
  this.userMediaLoading = this.blocking = this.unloaded = this.submitting = false;
6988
7045
  this.userMediaLoaded = true;
6989
7046
  this.clearUserMediaTimeout();
6990
- if (!switchingFacingMode) this.loop = this.createLoop();
7047
+ if (!switchingFacingMode && !params?.recovering) this.loop = this.createLoop();
6991
7048
  this.show();
6992
7049
  if (params?.recordWhenReady) this.record();
6993
7050
  this.emit("USER_MEDIA_READY", {
6994
7051
  switchingFacingMode: params?.switchingFacingMode,
7052
+ recovering: params?.recovering,
6995
7053
  paused: this.isPaused(),
6996
7054
  recordWhenReady: params?.recordWhenReady
6997
7055
  });
7056
+ if (params?.recovering) this.finishCameraRecovery(true);
6998
7057
  } catch (exc) {
6999
- this.emit("ERROR", {
7058
+ if (params?.recovering) this.failCameraRecovery(exc);
7059
+ else this.emit("ERROR", {
7000
7060
  exc
7001
7061
  });
7002
7062
  }
@@ -7367,6 +7427,7 @@ class Recorder extends util_Despot {
7367
7427
  return this.blocking;
7368
7428
  }
7369
7429
  userMediaErrorCallback(err, usedConstraints) {
7430
+ if (this.cameraRecovery) return void this.failCameraRecovery(err);
7370
7431
  if (this.unloaded) return void this.options.logger.debug(`Recorder: already unloaded. Not going to throw error ${string_pretty(err)}`);
7371
7432
  this.userMediaLoading = false;
7372
7433
  this.clearUserMediaTimeout();
@@ -7398,7 +7459,7 @@ class Recorder extends util_Despot {
7398
7459
  });
7399
7460
  }
7400
7461
  }
7401
- getUserMediaCallback(localStream, params) {
7462
+ getUserMediaCallback(localStream, params, requestId = this.mediaRequestId) {
7402
7463
  if (!this.userMedia) {
7403
7464
  localStream.getTracks().forEach((track)=>{
7404
7465
  track.stop();
@@ -7411,20 +7472,20 @@ class Recorder extends util_Despot {
7411
7472
  this.options.logger.debug(`Recorder: getUserMediaCallback(${params ? string_pretty(params) : ""})`);
7412
7473
  if (this.showUserMedia()) try {
7413
7474
  this.userMedia.init(localStream, ()=>{
7414
- if (!this.unloaded) this.onUserMediaReady(params);
7475
+ if (!this.unloaded && requestId === this.mediaRequestId) this.onUserMediaReady(params);
7415
7476
  }, this.onAudioSample.bind(this), (err)=>{
7416
- if (!this.unloaded) this.userMediaErrorCallback(err, {
7477
+ if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(err, {
7417
7478
  audio: isAudioEnabled(this.options)
7418
7479
  });
7419
- }, params?.switchingFacingMode);
7480
+ }, params);
7420
7481
  } catch (exc) {
7421
7482
  localStream.getTracks().forEach((track)=>{
7422
7483
  track.stop();
7423
7484
  });
7424
7485
  this.userMediaLoading = false;
7425
7486
  this.clearUserMediaTimeout();
7426
- this.emit("ERROR", {
7427
- exc
7487
+ this.userMediaErrorCallback(exc, {
7488
+ audio: isAudioEnabled(this.options)
7428
7489
  });
7429
7490
  }
7430
7491
  else {
@@ -7433,12 +7494,14 @@ class Recorder extends util_Despot {
7433
7494
  });
7434
7495
  this.userMediaLoading = false;
7435
7496
  this.clearUserMediaTimeout();
7497
+ this.cancelCameraRecovery();
7436
7498
  }
7437
7499
  }
7438
7500
  loadGenuineUserMedia(params) {
7439
7501
  this.options.logger.debug(`Recorder: loadGenuineUserMedia(${params ? string_pretty(params) : ""})`);
7440
7502
  this.emit("ASKING_WEBCAM_PERMISSION");
7441
7503
  this.userMediaLoading = true;
7504
+ const requestId = ++this.mediaRequestId;
7442
7505
  const constraints = {
7443
7506
  video: {
7444
7507
  frameRate: {
@@ -7452,18 +7515,18 @@ class Recorder extends util_Despot {
7452
7515
  this.options.logger.debug(string_summarize("Recorder: available webcam constraints are:", navigator.mediaDevices.getSupportedConstraints()));
7453
7516
  const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
7454
7517
  streamPromise.then((localStream)=>{
7455
- if (this.unloaded) return void localStream.getTracks().forEach((track)=>{
7518
+ if (this.unloaded || requestId !== this.mediaRequestId) return void localStream.getTracks().forEach((track)=>{
7456
7519
  track.stop();
7457
7520
  });
7458
- this.getUserMediaCallback(localStream, params);
7521
+ this.getUserMediaCallback(localStream, params, requestId);
7459
7522
  }).catch((reason)=>{
7460
- if (!this.unloaded) this.userMediaErrorCallback(reason, constraints);
7523
+ if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(reason, constraints);
7461
7524
  });
7462
7525
  }
7463
7526
  configureVideoConstraints(constraints, params) {
7464
7527
  const videoConstraints = "object" == typeof constraints.video ? constraints.video : void 0;
7465
7528
  if (!videoConstraints) return;
7466
- if (params?.switchingFacingMode) videoConstraints.facingMode = params.switchingFacingMode;
7529
+ if (params?.switchingFacingMode || params?.recovering) videoConstraints.facingMode = params.switchingFacingMode || this.facingMode;
7467
7530
  if (this.options.video.width) videoConstraints.width = {
7468
7531
  ideal: this.options.video.width
7469
7532
  };
@@ -7491,8 +7554,8 @@ class Recorder extends util_Despot {
7491
7554
  if (!this.userMediaLoaded) {
7492
7555
  this.userMediaLoading = false;
7493
7556
  const err = util_getBrowser(this.options).getNoAccessIssue();
7494
- this.emit("ERROR", {
7495
- err
7557
+ this.userMediaErrorCallback(err, {
7558
+ audio: isAudioEnabled(this.options)
7496
7559
  });
7497
7560
  }
7498
7561
  }, this.options.timeouts.userMedia);
@@ -7502,6 +7565,7 @@ class Recorder extends util_Despot {
7502
7565
  this.clearUserMediaTimeout();
7503
7566
  this.options.logger.debug("Recorder: failed to load genuine user media");
7504
7567
  this.userMediaLoading = false;
7568
+ if (this.cameraRecovery) return void this.failCameraRecovery(exc);
7505
7569
  const errorListeners = this.getListeners("ERROR");
7506
7570
  if (errorListeners?.length) this.emit("ERROR", {
7507
7571
  exc
@@ -7625,9 +7689,10 @@ class Recorder extends util_Despot {
7625
7689
  return this.recordingStats;
7626
7690
  }
7627
7691
  getAudioSampleRate() {
7628
- return this.userMedia?.getAudioSampleRate();
7692
+ return this.recordingAudioSampleRate ?? this.userMedia?.getAudioSampleRate();
7629
7693
  }
7630
7694
  stop(params) {
7695
+ this.cancelCameraRecovery();
7631
7696
  if (!this.userMedia) throw new Error("No user media defined, unable to stop");
7632
7697
  this.options.logger.debug(`Recorder: stop(${params ? string_pretty(params) : ""})`);
7633
7698
  this.recordingActive = false;
@@ -7653,7 +7718,7 @@ class Recorder extends util_Despot {
7653
7718
  if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
7654
7719
  if (isAudioEnabled(this.options) && this.userMedia) {
7655
7720
  this.recordingStats.samplesCount = this.samplesCount;
7656
- this.recordingStats.sampleRate = this.userMedia.getAudioSampleRate();
7721
+ this.recordingStats.sampleRate = this.recordingAudioSampleRate ?? this.userMedia.getAudioSampleRate();
7657
7722
  }
7658
7723
  this.writeCommand("stop", this.recordingStats, ()=>{
7659
7724
  this.emit("STOPPED", {
@@ -7708,6 +7773,7 @@ class Recorder extends util_Despot {
7708
7773
  this.built = this.connecting = this.connected = false;
7709
7774
  }
7710
7775
  reset() {
7776
+ this.cancelCameraRecovery();
7711
7777
  if (!this.unloaded) {
7712
7778
  this.options.logger.debug("Recorder: reset()");
7713
7779
  this.emit("RESETTING");
@@ -7735,7 +7801,70 @@ class Recorder extends util_Despot {
7735
7801
  isReady() {
7736
7802
  return this.userMedia?.isReady();
7737
7803
  }
7804
+ finishCameraRecovery(restored) {
7805
+ this.clearUserMediaTimeout();
7806
+ const resolve = this.resolveCameraRecovery;
7807
+ const resumeRequested = restored && this.resumeAfterCameraRecovery;
7808
+ this.cameraRecovery = void 0;
7809
+ this.resolveCameraRecovery = void 0;
7810
+ this.resumeAfterCameraRecovery = false;
7811
+ resolve?.(restored);
7812
+ if (resumeRequested && this.isPaused()) this.resume();
7813
+ }
7814
+ cancelCameraRecovery() {
7815
+ if (!this.cameraRecovery) return;
7816
+ this.mediaRequestId++;
7817
+ this.userMediaLoading = false;
7818
+ this.userMediaLoaded = false;
7819
+ this.clearUserMediaTimeout();
7820
+ this.userMedia?.stop(void 0, {
7821
+ preserveRecordingState: true
7822
+ });
7823
+ this.finishCameraRecovery(false);
7824
+ }
7825
+ failCameraRecovery(exc) {
7826
+ this.cancelCameraRecovery();
7827
+ const err = error_createError({
7828
+ message: "Camera recovery failed",
7829
+ explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
7830
+ exc,
7831
+ options: this.options
7832
+ });
7833
+ this.emit("CAMERA_RECOVERY_FAILED", {
7834
+ err
7835
+ });
7836
+ }
7837
+ recoverCamera() {
7838
+ if (this.cameraRecovery) return this.cameraRecovery;
7839
+ if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
7840
+ const liveStream = this.userMedia.hasLiveStream();
7841
+ if (liveStream && this.isRecording()) return Promise.resolve(true);
7842
+ if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
7843
+ const recovery = new Promise((resolve)=>{
7844
+ this.resolveCameraRecovery = resolve;
7845
+ });
7846
+ this.cameraRecovery = recovery;
7847
+ this.emit("CAMERA_RECOVERING");
7848
+ if (liveStream) {
7849
+ this.userMediaTimeout = window.setTimeout(()=>{
7850
+ this.failCameraRecovery(new Error("Camera playback recovery timed out."));
7851
+ }, this.options.timeouts.userMedia);
7852
+ this.userMedia.restorePlayback().then(()=>{
7853
+ if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
7854
+ }).catch((exc)=>{
7855
+ if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
7856
+ });
7857
+ } else {
7858
+ this.userMediaLoaded = false;
7859
+ this.loadUserMedia({
7860
+ recovering: true
7861
+ });
7862
+ }
7863
+ return recovery;
7864
+ }
7738
7865
  pause(params) {
7866
+ this.cancelCameraRecovery();
7867
+ this.resumeAfterDrain = false;
7739
7868
  if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
7740
7869
  else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
7741
7870
  this.userMedia?.pause();
@@ -7744,10 +7873,15 @@ class Recorder extends util_Despot {
7744
7873
  this.sendPings();
7745
7874
  }
7746
7875
  resume() {
7876
+ if (this.cameraRecovery || !this.userMedia?.hasLiveStream()) {
7877
+ this.recoverCamera();
7878
+ this.resumeAfterCameraRecovery = Boolean(this.cameraRecovery);
7879
+ return;
7880
+ }
7747
7881
  this.options.logger.debug(`Recorder: resume() with frame ${this.framesCount}`);
7748
7882
  this.stopPings();
7749
7883
  this.emit("RESUMING");
7750
- this.userMedia?.resume();
7884
+ this.userMedia.resume();
7751
7885
  if (this.streamBackpressured) this.resumeAfterDrain = true;
7752
7886
  else this.loop.start();
7753
7887
  }
@@ -7802,7 +7936,7 @@ class Recorder extends util_Despot {
7802
7936
  return newLoop;
7803
7937
  }
7804
7938
  record() {
7805
- if (this.unloaded) return;
7939
+ if (this.unloaded || this.cameraRecovery) return;
7806
7940
  if (!this.connected) {
7807
7941
  this.options.logger.debug("Recorder: reconnecting before recording …");
7808
7942
  this.initSocket(()=>{
@@ -7875,6 +8009,7 @@ class Recorder extends util_Despot {
7875
8009
  this.recordingActive = true;
7876
8010
  this.recordingTransportFailed = false;
7877
8011
  this.mediaTransmissionStarted = false;
8012
+ this.recordingAudioSampleRate = void 0;
7878
8013
  this.userMedia.record();
7879
8014
  this.emit("RECORDING", {
7880
8015
  framesCount: this.framesCount
@@ -7936,7 +8071,7 @@ class Recorder extends util_Despot {
7936
8071
  }
7937
8072
  }
7938
8073
  switchFacingMode() {
7939
- if (!util_getBrowser(this.options).isMobile()) return;
8074
+ if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
7940
8075
  if ("user" === this.facingMode) this.facingMode = "environment";
7941
8076
  else if ("environment" === this.facingMode) this.facingMode = "user";
7942
8077
  else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
@@ -7947,12 +8082,14 @@ class Recorder extends util_Despot {
7947
8082
  initEvents() {
7948
8083
  this.options.logger.debug("Recorder: initEvents()");
7949
8084
  this.on("SUBMITTING", ()=>{
8085
+ this.cancelCameraRecovery();
7950
8086
  this.submitting = true;
7951
8087
  });
7952
8088
  this.on("SUBMITTED", ()=>{
7953
8089
  this.submitting = false;
7954
8090
  });
7955
8091
  this.on("BLOCKING", ()=>{
8092
+ this.cancelCameraRecovery();
7956
8093
  this.blocking = true;
7957
8094
  this.clearUserMediaTimeout();
7958
8095
  });
@@ -8126,7 +8263,7 @@ class Recorder extends util_Despot {
8126
8263
  return this.userMediaLoaded;
8127
8264
  }
8128
8265
  constructor(visuals, replay, options){
8129
- super("Recorder", options), recorder_define_property(this, "visuals", void 0), recorder_define_property(this, "replay", void 0), recorder_define_property(this, "pendingWrites", new boundedWriteQueue()), recorder_define_property(this, "loop", void 0), recorder_define_property(this, "originalAnimationFrameObject", void 0), recorder_define_property(this, "samplesCount", 0), recorder_define_property(this, "framesCount", 0), recorder_define_property(this, "recordingStats", void 0), recorder_define_property(this, "confirmedFrameNumber", 0), recorder_define_property(this, "confirmedSampleNumber", 0), recorder_define_property(this, "recorderElement", void 0), recorder_define_property(this, "userMedia", void 0), recorder_define_property(this, "userMediaTimeout", void 0), recorder_define_property(this, "retryTimeout", void 0), recorder_define_property(this, "reconnectTimeout", void 0), recorder_define_property(this, "connectionTimeout", void 0), recorder_define_property(this, "backpressureTimeout", void 0), recorder_define_property(this, "stopTimeout", void 0), recorder_define_property(this, "frameProgress", void 0), recorder_define_property(this, "sampleProgress", void 0), recorder_define_property(this, "canvas", void 0), recorder_define_property(this, "recordingDimensions", void 0), recorder_define_property(this, "ctx", void 0), recorder_define_property(this, "userMediaLoaded", void 0), recorder_define_property(this, "userMediaLoading", false), recorder_define_property(this, "submitting", false), recorder_define_property(this, "unloaded", void 0), recorder_define_property(this, "stopTime", void 0), recorder_define_property(this, "stream", void 0), recorder_define_property(this, "streamBackpressured", false), recorder_define_property(this, "resumeAfterDrain", false), recorder_define_property(this, "mediaTransmissionStarted", false), recorder_define_property(this, "recordingActive", false), recorder_define_property(this, "recordingTransportFailed", false), recorder_define_property(this, "connecting", false), recorder_define_property(this, "connected", false), recorder_define_property(this, "connectionFailed", false), recorder_define_property(this, "reconnecting", false), recorder_define_property(this, "reconnectAttempts", 0), recorder_define_property(this, "blocking", false), recorder_define_property(this, "built", false), recorder_define_property(this, "key", void 0), recorder_define_property(this, "waitingTime", void 0), recorder_define_property(this, "connectingStartedAt", void 0), recorder_define_property(this, "lastCloseEvent", void 0), recorder_define_property(this, "lastSocketError", void 0), recorder_define_property(this, "pingInterval", void 0), recorder_define_property(this, "frame", void 0), recorder_define_property(this, "recordingBuffer", void 0), recorder_define_property(this, "facingMode", void 0);
8266
+ super("Recorder", options), recorder_define_property(this, "visuals", void 0), recorder_define_property(this, "replay", void 0), recorder_define_property(this, "pendingWrites", new boundedWriteQueue()), recorder_define_property(this, "loop", void 0), recorder_define_property(this, "originalAnimationFrameObject", void 0), recorder_define_property(this, "samplesCount", 0), recorder_define_property(this, "framesCount", 0), recorder_define_property(this, "recordingAudioSampleRate", void 0), recorder_define_property(this, "recordingStats", void 0), recorder_define_property(this, "confirmedFrameNumber", 0), recorder_define_property(this, "confirmedSampleNumber", 0), recorder_define_property(this, "recorderElement", void 0), recorder_define_property(this, "userMedia", void 0), recorder_define_property(this, "userMediaTimeout", void 0), recorder_define_property(this, "retryTimeout", void 0), recorder_define_property(this, "reconnectTimeout", void 0), recorder_define_property(this, "connectionTimeout", void 0), recorder_define_property(this, "backpressureTimeout", void 0), recorder_define_property(this, "stopTimeout", void 0), recorder_define_property(this, "frameProgress", void 0), recorder_define_property(this, "sampleProgress", void 0), recorder_define_property(this, "canvas", void 0), recorder_define_property(this, "recordingDimensions", void 0), recorder_define_property(this, "ctx", void 0), recorder_define_property(this, "userMediaLoaded", void 0), recorder_define_property(this, "userMediaLoading", false), recorder_define_property(this, "mediaRequestId", 0), recorder_define_property(this, "cameraRecovery", void 0), recorder_define_property(this, "resolveCameraRecovery", void 0), recorder_define_property(this, "resumeAfterCameraRecovery", false), recorder_define_property(this, "submitting", false), recorder_define_property(this, "unloaded", void 0), recorder_define_property(this, "stopTime", void 0), recorder_define_property(this, "stream", void 0), recorder_define_property(this, "streamBackpressured", false), recorder_define_property(this, "resumeAfterDrain", false), recorder_define_property(this, "mediaTransmissionStarted", false), recorder_define_property(this, "recordingActive", false), recorder_define_property(this, "recordingTransportFailed", false), recorder_define_property(this, "connecting", false), recorder_define_property(this, "connected", false), recorder_define_property(this, "connectionFailed", false), recorder_define_property(this, "reconnecting", false), recorder_define_property(this, "reconnectAttempts", 0), recorder_define_property(this, "blocking", false), recorder_define_property(this, "built", false), recorder_define_property(this, "key", void 0), recorder_define_property(this, "waitingTime", void 0), recorder_define_property(this, "connectingStartedAt", void 0), recorder_define_property(this, "lastCloseEvent", void 0), recorder_define_property(this, "lastSocketError", void 0), recorder_define_property(this, "pingInterval", void 0), recorder_define_property(this, "frame", void 0), recorder_define_property(this, "recordingBuffer", void 0), recorder_define_property(this, "facingMode", void 0);
8130
8267
  this.visuals = visuals;
8131
8268
  this.replay = replay;
8132
8269
  this.facingMode = options.video.facingMode;
@@ -8419,6 +8556,17 @@ class Visuals extends util_Despot {
8419
8556
  this.endWaiting();
8420
8557
  this.container.enableForm(false);
8421
8558
  });
8559
+ this.on("CAMERA_RECOVERING", ()=>{
8560
+ this.clearCameraRecoveryNotice();
8561
+ });
8562
+ this.on("CAMERA_RECOVERY_FAILED", ({ err })=>{
8563
+ this.cameraRecoveryFailed = true;
8564
+ this.options.logger.warn(err);
8565
+ if (this.options.displayErrors) this.notifier.notify(err.message, err.explanation, {
8566
+ problem: true
8567
+ });
8568
+ this.endWaiting();
8569
+ });
8422
8570
  this.on("PREVIEW", ()=>{
8423
8571
  this.endWaiting();
8424
8572
  });
@@ -8497,6 +8645,7 @@ class Visuals extends util_Despot {
8497
8645
  child.remove();
8498
8646
  }
8499
8647
  reset() {
8648
+ this.clearCameraRecoveryNotice();
8500
8649
  this.endWaiting();
8501
8650
  this.recorder.reset();
8502
8651
  }
@@ -8507,11 +8656,13 @@ class Visuals extends util_Despot {
8507
8656
  this.container.endWaiting();
8508
8657
  }
8509
8658
  stop(params) {
8659
+ this.clearCameraRecoveryNotice();
8510
8660
  this.recorder.stop(params);
8511
8661
  this.recorderInsides.hidePause();
8512
8662
  }
8513
8663
  back(keepHidden = false, cb) {
8514
8664
  this.options.logger.debug(`Visuals: back(keepHidden = ${keepHidden})`);
8665
+ this.cameraRecoveryFailed = false;
8515
8666
  this.replay.hide();
8516
8667
  this.notifier.hide();
8517
8668
  if (keepHidden) {
@@ -8533,6 +8684,7 @@ class Visuals extends util_Despot {
8533
8684
  if (!this.built) return;
8534
8685
  const e = params?.e;
8535
8686
  this.options.logger.debug(`Visuals: unload(${e ? string_pretty(e) : ""})`);
8687
+ this.cameraRecoveryFailed = false;
8536
8688
  this.recorder.unload(params);
8537
8689
  this.recorderInsides.unload();
8538
8690
  this.replay.unload(params);
@@ -8546,13 +8698,31 @@ class Visuals extends util_Despot {
8546
8698
  this.recorder.pause(params);
8547
8699
  this.recorderInsides.showPause();
8548
8700
  }
8701
+ clearCameraRecoveryNotice() {
8702
+ if (this.cameraRecoveryFailed) {
8703
+ this.notifier.hide();
8704
+ this.recorder.show();
8705
+ this.cameraRecoveryFailed = false;
8706
+ }
8707
+ }
8708
+ recoverCamera() {
8709
+ if (this.replay.isShown() || this.isNotifying() && !this.cameraRecoveryFailed) return Promise.resolve(false);
8710
+ this.clearCameraRecoveryNotice();
8711
+ return this.recorder.recoverCamera();
8712
+ }
8549
8713
  resume() {
8550
- if (this.recorderInsides.isCountingDown()) this.recorderInsides.resumeCountdown();
8551
- else this.recorder.resume();
8552
- this.recorderInsides.hidePause();
8714
+ this.clearCameraRecoveryNotice();
8715
+ if (this.recorderInsides.isCountingDown()) {
8716
+ this.recorderInsides.resumeCountdown();
8717
+ this.recorderInsides.hidePause();
8718
+ } else {
8719
+ this.recorder.resume();
8720
+ if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
8721
+ }
8553
8722
  }
8554
8723
  pauseOrResume() {
8555
- if (this.isRecordable()) {
8724
+ if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
8725
+ else if (this.isRecordable()) {
8556
8726
  if (this.isRecording()) this.pause();
8557
8727
  else if (this.recorder.isPaused()) this.resume();
8558
8728
  else if (this.recorder.isReady()) this.recorder.record();
@@ -8674,7 +8844,7 @@ class Visuals extends util_Despot {
8674
8844
  return this.visualsElement;
8675
8845
  }
8676
8846
  constructor(container, options){
8677
- super("Visuals", options), visuals_define_property(this, "container", void 0), visuals_define_property(this, "replay", void 0), visuals_define_property(this, "recorder", void 0), visuals_define_property(this, "recorderInsides", void 0), visuals_define_property(this, "notifier", void 0), visuals_define_property(this, "visualsElement", void 0), visuals_define_property(this, "built", false);
8847
+ super("Visuals", options), visuals_define_property(this, "container", void 0), visuals_define_property(this, "replay", void 0), visuals_define_property(this, "recorder", void 0), visuals_define_property(this, "recorderInsides", void 0), visuals_define_property(this, "notifier", void 0), visuals_define_property(this, "visualsElement", void 0), visuals_define_property(this, "built", false), visuals_define_property(this, "cameraRecoveryFailed", false);
8678
8848
  this.container = container;
8679
8849
  this.replay = new visuals_replay(this, options);
8680
8850
  this.recorder = new visuals_recorder(this, this.replay, options);
@@ -8776,10 +8946,10 @@ class Container extends util_Despot {
8776
8946
  });
8777
8947
  if (!playerOnly) this.stopListeningToVisibility = this.visibility.onChange((visible)=>{
8778
8948
  if (this.built) if (visible) {
8779
- if (isAutoPauseEnabled(this.options) && this.isCountingDown()) this.resume();
8949
+ if (isAutoPauseEnabled(this.options)) this.recoverCamera();
8780
8950
  this.emit("VISIBLE");
8781
8951
  } else {
8782
- if (isAutoPauseEnabled(this.options) && (this.isCountingDown() || this.isRecording())) this.pause();
8952
+ if (isAutoPauseEnabled(this.options)) this.suspend();
8783
8953
  this.emit("INVISIBLE");
8784
8954
  }
8785
8955
  });
@@ -8967,6 +9137,13 @@ class Container extends util_Despot {
8967
9137
  pause(params) {
8968
9138
  this.visuals.pause(params);
8969
9139
  }
9140
+ suspend() {
9141
+ if (this.built && (this.isCountingDown() || this.isRecording() || this.isPaused())) this.pause();
9142
+ }
9143
+ recoverCamera() {
9144
+ if (!this.built || this.hasError || this.submitted) return Promise.resolve(false);
9145
+ return this.visuals.recoverCamera();
9146
+ }
8970
9147
  validate(event, force = false) {
8971
9148
  let runValidation = true;
8972
9149
  let valid = true;
@@ -9308,6 +9485,12 @@ class VideomailClient extends util_Despot {
9308
9485
  record() {
9309
9486
  this.container.record();
9310
9487
  }
9488
+ pause() {
9489
+ this.container.suspend();
9490
+ }
9491
+ recoverCamera() {
9492
+ return this.container.recoverCamera();
9493
+ }
9311
9494
  getLogLines() {
9312
9495
  if (this.options.logger.getLines) return this.options.logger.getLines();
9313
9496
  }