videomail-client 16.1.0 → 16.2.0

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.
@@ -1,11 +1,11 @@
1
1
  var __webpack_modules__ = {
2
- eq (module, __webpack_exports__, __webpack_require__) {
2
+ pJ (module, __webpack_exports__, __webpack_require__) {
3
3
  "use strict";
4
- var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("erX");
5
- 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);
6
- var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("zm");
7
- 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);
8
- 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());
4
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("K7");
5
+ 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);
6
+ var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("Ly");
7
+ 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);
8
+ 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());
9
9
  ___CSS_LOADER_EXPORT___.push([
10
10
  module.id,
11
11
  `@keyframes blink {
@@ -153,7 +153,7 @@ var __webpack_modules__ = {
153
153
  });
154
154
  },
155
155
  b6 (module, exports1, __webpack_require__) {
156
- var EventEmitter = __webpack_require__("q").EventEmitter, inherits = __webpack_require__("c5"), raf = __webpack_require__("jY"), methods;
156
+ var EventEmitter = __webpack_require__("qU").EventEmitter, inherits = __webpack_require__("c5"), raf = __webpack_require__("jY"), methods;
157
157
  var defaultRAFObject = {
158
158
  requestAnimationFrame: raf,
159
159
  cancelAnimationFrame: raf.cancel
@@ -1648,7 +1648,7 @@ var __webpack_modules__ = {
1648
1648
  return 0 === comparison || comparison & DOCUMENT_POSITION_CONTAINED_BY;
1649
1649
  }
1650
1650
  },
1651
- T (__unused_rspack_module, exports1, __webpack_require__) {
1651
+ TU (__unused_rspack_module, exports1, __webpack_require__) {
1652
1652
  function isArray(arg) {
1653
1653
  if (Array.isArray) return Array.isArray(arg);
1654
1654
  return '[object Array]' === objectToString(arg);
@@ -1831,7 +1831,7 @@ var __webpack_modules__ = {
1831
1831
  },
1832
1832
  xF (module, __unused_rspack_exports, __webpack_require__) {
1833
1833
  "use strict";
1834
- var document1 = __webpack_require__("g");
1834
+ var document1 = __webpack_require__("gM");
1835
1835
  var Event1 = __webpack_require__("cE");
1836
1836
  var Keys = __webpack_require__("Dy");
1837
1837
  module.exports = Visibility;
@@ -2113,7 +2113,7 @@ var __webpack_modules__ = {
2113
2113
  return keys;
2114
2114
  };
2115
2115
  module.exports = Duplex;
2116
- var util = Object.create(__webpack_require__("T"));
2116
+ var util = Object.create(__webpack_require__("TU"));
2117
2117
  util.inherits = __webpack_require__("c5");
2118
2118
  var Readable = __webpack_require__("rp");
2119
2119
  var Writable = __webpack_require__("pv");
@@ -2167,7 +2167,7 @@ var __webpack_modules__ = {
2167
2167
  "use strict";
2168
2168
  module.exports = PassThrough;
2169
2169
  var Transform = __webpack_require__("It");
2170
- var util = Object.create(__webpack_require__("T"));
2170
+ var util = Object.create(__webpack_require__("TU"));
2171
2171
  util.inherits = __webpack_require__("c5");
2172
2172
  util.inherits(PassThrough, Transform);
2173
2173
  function PassThrough(options) {
@@ -2186,7 +2186,7 @@ var __webpack_modules__ = {
2186
2186
  var isArray = __webpack_require__("ab");
2187
2187
  var Duplex;
2188
2188
  Readable.ReadableState = ReadableState;
2189
- __webpack_require__("q").EventEmitter;
2189
+ __webpack_require__("qU").EventEmitter;
2190
2190
  var EElistenerCount = function(emitter, type) {
2191
2191
  return emitter.listeners(type).length;
2192
2192
  };
@@ -2199,7 +2199,7 @@ var __webpack_modules__ = {
2199
2199
  function _isUint8Array(obj) {
2200
2200
  return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
2201
2201
  }
2202
- var util = Object.create(__webpack_require__("T"));
2202
+ var util = Object.create(__webpack_require__("TU"));
2203
2203
  util.inherits = __webpack_require__("c5");
2204
2204
  var debugUtil = __webpack_require__("AN");
2205
2205
  var debug = void 0;
@@ -2826,7 +2826,7 @@ var __webpack_modules__ = {
2826
2826
  "use strict";
2827
2827
  module.exports = Transform;
2828
2828
  var Duplex = __webpack_require__("OO");
2829
- var util = Object.create(__webpack_require__("T"));
2829
+ var util = Object.create(__webpack_require__("TU"));
2830
2830
  util.inherits = __webpack_require__("c5");
2831
2831
  util.inherits(Transform, Duplex);
2832
2832
  function afterTransform(er, data) {
@@ -2926,7 +2926,7 @@ var __webpack_modules__ = {
2926
2926
  ].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;
2927
2927
  var Duplex;
2928
2928
  Writable.WritableState = WritableState;
2929
- var util = Object.create(__webpack_require__("T"));
2929
+ var util = Object.create(__webpack_require__("TU"));
2930
2930
  util.inherits = __webpack_require__("c5");
2931
2931
  var internalUtil = {
2932
2932
  deprecate: __webpack_require__("NQ")
@@ -3450,7 +3450,7 @@ var __webpack_modules__ = {
3450
3450
  };
3451
3451
  },
3452
3452
  tb (module, __unused_rspack_exports, __webpack_require__) {
3453
- module.exports = __webpack_require__("q").EventEmitter;
3453
+ module.exports = __webpack_require__("qU").EventEmitter;
3454
3454
  },
3455
3455
  GB (module, exports1, __webpack_require__) {
3456
3456
  exports1 = module.exports = __webpack_require__("rp");
@@ -3847,7 +3847,7 @@ var __webpack_modules__ = {
3847
3847
  "use strict";
3848
3848
  module.exports = Object;
3849
3849
  },
3850
- q (module) {
3850
+ qU (module) {
3851
3851
  "use strict";
3852
3852
  var R = 'object' == typeof Reflect ? Reflect : null;
3853
3853
  var ReflectApply = R && 'function' == typeof R.apply ? R.apply : function(target, receiver, args) {
@@ -4897,7 +4897,7 @@ var __webpack_modules__ = {
4897
4897
  return tuple.listen;
4898
4898
  }
4899
4899
  },
4900
- g (module, __unused_rspack_exports, __webpack_require__) {
4900
+ gM (module, __unused_rspack_exports, __webpack_require__) {
4901
4901
  var topLevel = void 0 !== __webpack_require__.g ? __webpack_require__.g : "u" > typeof window ? window : {};
4902
4902
  var minDoc = __webpack_require__("MM");
4903
4903
  var doccy;
@@ -8248,7 +8248,7 @@ var __webpack_modules__ = {
8248
8248
  });
8249
8249
  });
8250
8250
  },
8251
- Sy (__unused_rspack_module, exports1, __webpack_require__) {
8251
+ S (__unused_rspack_module, exports1, __webpack_require__) {
8252
8252
  var process = __webpack_require__("zT");
8253
8253
  var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || function(obj) {
8254
8254
  var keys = Object.keys(obj);
@@ -8727,7 +8727,7 @@ var __webpack_modules__ = {
8727
8727
  return keys;
8728
8728
  };
8729
8729
  module.exports = Duplex;
8730
- var util = Object.create(__webpack_require__("T"));
8730
+ var util = Object.create(__webpack_require__("TU"));
8731
8731
  util.inherits = __webpack_require__("c5");
8732
8732
  var Readable = __webpack_require__("w");
8733
8733
  var Writable = __webpack_require__("S5");
@@ -8781,7 +8781,7 @@ var __webpack_modules__ = {
8781
8781
  "use strict";
8782
8782
  module.exports = PassThrough;
8783
8783
  var Transform = __webpack_require__("Ge");
8784
- var util = Object.create(__webpack_require__("T"));
8784
+ var util = Object.create(__webpack_require__("TU"));
8785
8785
  util.inherits = __webpack_require__("c5");
8786
8786
  util.inherits(PassThrough, Transform);
8787
8787
  function PassThrough(options) {
@@ -8800,7 +8800,7 @@ var __webpack_modules__ = {
8800
8800
  var isArray = __webpack_require__("N3");
8801
8801
  var Duplex;
8802
8802
  Readable.ReadableState = ReadableState;
8803
- __webpack_require__("q").EventEmitter;
8803
+ __webpack_require__("qU").EventEmitter;
8804
8804
  var EElistenerCount = function(emitter, type) {
8805
8805
  return emitter.listeners(type).length;
8806
8806
  };
@@ -8813,13 +8813,13 @@ var __webpack_modules__ = {
8813
8813
  function _isUint8Array(obj) {
8814
8814
  return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
8815
8815
  }
8816
- var util = Object.create(__webpack_require__("T"));
8816
+ var util = Object.create(__webpack_require__("TU"));
8817
8817
  util.inherits = __webpack_require__("c5");
8818
8818
  var debugUtil = __webpack_require__("eD");
8819
8819
  var debug = void 0;
8820
8820
  debug = debugUtil && debugUtil.debuglog ? debugUtil.debuglog('stream') : function() {};
8821
8821
  var BufferList = __webpack_require__("aI");
8822
- var destroyImpl = __webpack_require__("R5");
8822
+ var destroyImpl = __webpack_require__("R");
8823
8823
  var StringDecoder;
8824
8824
  util.inherits(Readable, Stream);
8825
8825
  var kProxyEvents = [
@@ -9440,7 +9440,7 @@ var __webpack_modules__ = {
9440
9440
  "use strict";
9441
9441
  module.exports = Transform;
9442
9442
  var Duplex = __webpack_require__("S7");
9443
- var util = Object.create(__webpack_require__("T"));
9443
+ var util = Object.create(__webpack_require__("TU"));
9444
9444
  util.inherits = __webpack_require__("c5");
9445
9445
  util.inherits(Transform, Duplex);
9446
9446
  function afterTransform(er, data) {
@@ -9540,7 +9540,7 @@ var __webpack_modules__ = {
9540
9540
  ].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;
9541
9541
  var Duplex;
9542
9542
  Writable.WritableState = WritableState;
9543
- var util = Object.create(__webpack_require__("T"));
9543
+ var util = Object.create(__webpack_require__("TU"));
9544
9544
  util.inherits = __webpack_require__("c5");
9545
9545
  var internalUtil = {
9546
9546
  deprecate: __webpack_require__("NQ")
@@ -9554,7 +9554,7 @@ var __webpack_modules__ = {
9554
9554
  function _isUint8Array(obj) {
9555
9555
  return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
9556
9556
  }
9557
- var destroyImpl = __webpack_require__("R5");
9557
+ var destroyImpl = __webpack_require__("R");
9558
9558
  util.inherits(Writable, Stream);
9559
9559
  function nop() {}
9560
9560
  function WritableState(options, stream) {
@@ -10008,7 +10008,7 @@ var __webpack_modules__ = {
10008
10008
  return this.constructor.name + ' ' + obj;
10009
10009
  };
10010
10010
  },
10011
- R5 (module, __unused_rspack_exports, __webpack_require__) {
10011
+ R (module, __unused_rspack_exports, __webpack_require__) {
10012
10012
  "use strict";
10013
10013
  var pna = __webpack_require__("sj");
10014
10014
  function destroy(err, cb) {
@@ -10064,7 +10064,7 @@ var __webpack_modules__ = {
10064
10064
  };
10065
10065
  },
10066
10066
  O (module, __unused_rspack_exports, __webpack_require__) {
10067
- module.exports = __webpack_require__("q").EventEmitter;
10067
+ module.exports = __webpack_require__("qU").EventEmitter;
10068
10068
  },
10069
10069
  Fo (module, exports1, __webpack_require__) {
10070
10070
  exports1 = module.exports = __webpack_require__("w");
@@ -10554,7 +10554,7 @@ var __webpack_modules__ = {
10554
10554
  a () {},
10555
10555
  Av () {},
10556
10556
  eD () {},
10557
- zm (module) {
10557
+ Ly (module) {
10558
10558
  "use strict";
10559
10559
  module.exports = function(cssWithMappingToString) {
10560
10560
  var list = [];
@@ -10608,13 +10608,13 @@ var __webpack_modules__ = {
10608
10608
  return list;
10609
10609
  };
10610
10610
  },
10611
- erX (module) {
10611
+ K7 (module) {
10612
10612
  "use strict";
10613
10613
  module.exports = function(i) {
10614
10614
  return i[1];
10615
10615
  };
10616
10616
  },
10617
- R (module) {
10617
+ q (module) {
10618
10618
  "use strict";
10619
10619
  var stylesInDOM = [];
10620
10620
  function getIndexByIdentifier(identifier) {
@@ -10693,7 +10693,7 @@ var __webpack_modules__ = {
10693
10693
  };
10694
10694
  };
10695
10695
  },
10696
- uZ (module) {
10696
+ f (module) {
10697
10697
  "use strict";
10698
10698
  var memo = {};
10699
10699
  function getTarget(target) {
@@ -10715,7 +10715,7 @@ var __webpack_modules__ = {
10715
10715
  }
10716
10716
  module.exports = insertBySelector;
10717
10717
  },
10718
- mK (module) {
10718
+ z7 (module) {
10719
10719
  "use strict";
10720
10720
  function insertStyleElement(options) {
10721
10721
  var element = document.createElement("style");
@@ -10725,7 +10725,7 @@ var __webpack_modules__ = {
10725
10725
  }
10726
10726
  module.exports = insertStyleElement;
10727
10727
  },
10728
- kU (module, __unused_rspack_exports, __webpack_require__) {
10728
+ g (module, __unused_rspack_exports, __webpack_require__) {
10729
10729
  "use strict";
10730
10730
  function setAttributesWithoutAttributes(styleElement) {
10731
10731
  var nonce = __webpack_require__.nc;
@@ -10733,7 +10733,7 @@ var __webpack_modules__ = {
10733
10733
  }
10734
10734
  module.exports = setAttributesWithoutAttributes;
10735
10735
  },
10736
- Bd (module) {
10736
+ V (module) {
10737
10737
  "use strict";
10738
10738
  function apply(styleElement, options, obj) {
10739
10739
  var css = "";
@@ -10770,7 +10770,7 @@ var __webpack_modules__ = {
10770
10770
  }
10771
10771
  module.exports = domAPI;
10772
10772
  },
10773
- snK (module) {
10773
+ AD (module) {
10774
10774
  "use strict";
10775
10775
  function styleTagTransform(css, styleElement) {
10776
10776
  if (styleElement.styleSheet) styleElement.styleSheet.cssText = css;
@@ -11115,7 +11115,7 @@ var __webpack_exports__ = {};
11115
11115
  var client = __webpack_require__("Q");
11116
11116
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11117
11117
  var package_namespaceObject = {
11118
- rE: "16.1.0"
11118
+ rE: "16.2.0"
11119
11119
  };
11120
11120
  function isAudioEnabled(options) {
11121
11121
  return Boolean(options.audio.enabled);
@@ -11127,7 +11127,7 @@ var __webpack_exports__ = {};
11127
11127
  function isAutoPauseEnabled(options) {
11128
11128
  return options.enableAutoPause && options.enablePause;
11129
11129
  }
11130
- var util = __webpack_require__("Sy");
11130
+ var util = __webpack_require__("S");
11131
11131
  var util_default = /*#__PURE__*/ __webpack_require__.n(util);
11132
11132
  function getEventDetails(event) {
11133
11133
  const eventWithErrorDetails = event;
@@ -15017,6 +15017,14 @@ var __webpack_exports__ = {};
15017
15017
  }
15018
15018
  Despot_define_property(Despot, "emitters", new WeakMap());
15019
15019
  const util_Despot = Despot;
15020
+ const EMAIL_SEPARATOR_REGEX = /[ ,]+/gu;
15021
+ function trimEmail(email) {
15022
+ return email.replace(EMAIL_SEPARATOR_REGEX, "");
15023
+ }
15024
+ function trimEmails(emails) {
15025
+ const trimmedEmails = emails.split(EMAIL_SEPARATOR_REGEX).map((item)=>item.trim()).filter(Boolean);
15026
+ return trimmedEmails;
15027
+ }
15020
15028
  function getValidity(element) {
15021
15029
  const validity = "validity" in element ? element.validity : void 0;
15022
15030
  return validity;
@@ -15036,14 +15044,6 @@ var __webpack_exports__ = {};
15036
15044
  element.style.removeProperty("display");
15037
15045
  }
15038
15046
  const html_showElement = showElement;
15039
- const EMAIL_SEPARATOR_REGEX = /[ ,]+/gu;
15040
- function trimEmail(email) {
15041
- return email.replace(EMAIL_SEPARATOR_REGEX, "");
15042
- }
15043
- function trimEmails(emails) {
15044
- const trimmedEmails = emails.split(EMAIL_SEPARATOR_REGEX).map((item)=>item.trim()).filter(Boolean);
15045
- return trimmedEmails;
15046
- }
15047
15047
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
15048
15048
  try {
15049
15049
  var info = gen[key](arg);
@@ -15821,19 +15821,19 @@ var __webpack_exports__ = {};
15821
15821
  return newOptions;
15822
15822
  }
15823
15823
  const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
15824
- var injectStylesIntoStyleTag = __webpack_require__("R");
15824
+ var injectStylesIntoStyleTag = __webpack_require__("q");
15825
15825
  var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
15826
- var styleDomAPI = __webpack_require__("Bd");
15826
+ var styleDomAPI = __webpack_require__("V");
15827
15827
  var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
15828
- var insertBySelector = __webpack_require__("uZ");
15828
+ var insertBySelector = __webpack_require__("f");
15829
15829
  var insertBySelector_default = /*#__PURE__*/ __webpack_require__.n(insertBySelector);
15830
- var setAttributesWithoutAttributes = __webpack_require__("kU");
15830
+ var setAttributesWithoutAttributes = __webpack_require__("g");
15831
15831
  var setAttributesWithoutAttributes_default = /*#__PURE__*/ __webpack_require__.n(setAttributesWithoutAttributes);
15832
- var insertStyleElement = __webpack_require__("mK");
15832
+ var insertStyleElement = __webpack_require__("z7");
15833
15833
  var insertStyleElement_default = /*#__PURE__*/ __webpack_require__.n(insertStyleElement);
15834
- var styleTagTransform = __webpack_require__("snK");
15834
+ var styleTagTransform = __webpack_require__("AD");
15835
15835
  var styleTagTransform_default = /*#__PURE__*/ __webpack_require__.n(styleTagTransform);
15836
- var main = __webpack_require__("eq");
15836
+ var main = __webpack_require__("pJ");
15837
15837
  var main_options = {};
15838
15838
  main_options.styleTagTransform = styleTagTransform_default();
15839
15839
  main_options.setAttributes = setAttributesWithoutAttributes_default();
@@ -16837,11 +16837,11 @@ var __webpack_exports__ = {};
16837
16837
  const originalDocument = document1;
16838
16838
  const currentScript = originalDocument.currentScript;
16839
16839
  window1.DocumentFragment;
16840
- const HTMLTemplateElement = window1.HTMLTemplateElement, Node = window1.Node, Element = window1.Element, NodeFilter = window1.NodeFilter;
16840
+ const HTMLTemplateElement = window1.HTMLTemplateElement, Node = window1.Node, Element1 = window1.Element, NodeFilter = window1.NodeFilter;
16841
16841
  void 0 === window1.NamedNodeMap && (window1.NamedNodeMap || window1.MozNamedAttrMap);
16842
16842
  window1.HTMLFormElement;
16843
16843
  const DOMParser = window1.DOMParser, trustedTypes = window1.trustedTypes;
16844
- const ElementPrototype = Element.prototype;
16844
+ const ElementPrototype = Element1.prototype;
16845
16845
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
16846
16846
  const remove = lookupGetter(ElementPrototype, "remove");
16847
16847
  const removeAttributeNode = lookupGetter(ElementPrototype, "removeAttributeNode");
@@ -17924,6 +17924,20 @@ var __webpack_exports__ = {};
17924
17924
  return DOMPurify;
17925
17925
  }
17926
17926
  var purify_default = createDOMPurify();
17927
+ function isRecipientSelectionValid(recipients) {
17928
+ var _recipients_to, _recipients_cc, _recipients_bcc;
17929
+ const hasTo = Boolean(null == (_recipients_to = recipients.to) ? void 0 : _recipients_to.length);
17930
+ const hasCc = Boolean(null == (_recipients_cc = recipients.cc) ? void 0 : _recipients_cc.length);
17931
+ const hasBcc = Boolean(null == (_recipients_bcc = recipients.bcc) ? void 0 : _recipients_bcc.length);
17932
+ const hasConfiguredTo = "to" in recipients;
17933
+ const hasConfiguredCc = "cc" in recipients;
17934
+ const hasConfiguredBcc = "bcc" in recipients;
17935
+ let isValid = true;
17936
+ if (hasConfiguredTo) isValid = hasTo || hasConfiguredCc && hasCc || hasConfiguredBcc && hasBcc;
17937
+ else if (hasConfiguredCc) isValid = hasCc || !hasConfiguredBcc || hasBcc;
17938
+ return isValid;
17939
+ }
17940
+ const email_isRecipientSelectionValid = isRecipientSelectionValid;
17927
17941
  function limitHeight(height, options, calledFrom) {
17928
17942
  const dimension = {
17929
17943
  unit: "px"
@@ -18557,7 +18571,7 @@ var __webpack_exports__ = {};
18557
18571
  this.visuals = visuals;
18558
18572
  }
18559
18573
  }
18560
- const facingMode = FacingMode;
18574
+ const recorder_facingMode = FacingMode;
18561
18575
  function pausedNote_define_property(obj, key, value) {
18562
18576
  if (key in obj) Object.defineProperty(obj, key, {
18563
18577
  value: value,
@@ -18924,7 +18938,7 @@ var __webpack_exports__ = {};
18924
18938
  this.recordTimer = new recordTimer(visuals, this.recordNote, options);
18925
18939
  const browser = util_getBrowser(options);
18926
18940
  if (options.video.countdown) this.countdown = new countdown(visuals, options);
18927
- if (options.video.facingModeButton && browser.isMobile()) this.facingMode = new facingMode(visuals, options);
18941
+ if (options.video.facingModeButton && browser.isMobile()) this.facingMode = new recorder_facingMode(visuals, options);
18928
18942
  if (options.enablePause) this.pausedNote = new pausedNote(visuals, options);
18929
18943
  }
18930
18944
  }
@@ -19175,6 +19189,18 @@ var __webpack_exports__ = {};
19175
19189
  }
19176
19190
  }
19177
19191
  const notifier = Notifier;
19192
+ function summarize(title, contents) {
19193
+ const lines = Object.entries(contents).sort(([keyA], [keyB])=>keyA.localeCompare(keyB)).map(([key, value])=>{
19194
+ const formattedValue = void 0 === value ? "undefined" : "object" == typeof value ? string_pretty(value) : value;
19195
+ return ` • ${key}: ${formattedValue}`;
19196
+ });
19197
+ const line = [
19198
+ `🔎 ${title}`,
19199
+ ...lines
19200
+ ].join("\n");
19201
+ return line;
19202
+ }
19203
+ const string_summarize = summarize;
19178
19204
  var animitter = __webpack_require__("b6");
19179
19205
  var animitter_default = /*#__PURE__*/ __webpack_require__.n(animitter);
19180
19206
  var typedarray_to_buffer = __webpack_require__("E");
@@ -19238,46 +19264,6 @@ var __webpack_exports__ = {};
19238
19264
  canvas_to_buffer_modern_r.atob = void 0;
19239
19265
  var websocket_stream_stream = __webpack_require__("U");
19240
19266
  var stream_default = /*#__PURE__*/ __webpack_require__.n(websocket_stream_stream);
19241
- function boundedWriteQueue_define_property(obj, key, value) {
19242
- if (key in obj) Object.defineProperty(obj, key, {
19243
- value: value,
19244
- enumerable: true,
19245
- configurable: true,
19246
- writable: true
19247
- });
19248
- else obj[key] = value;
19249
- return obj;
19250
- }
19251
- const DEFAULT_MAX_QUEUED_BYTES = 1048576;
19252
- class BoundedWriteQueue {
19253
- enqueue(buffer, params) {
19254
- if (this.queuedBytes + buffer.length > this.maxBytes) return false;
19255
- this.writes.push({
19256
- buffer,
19257
- params
19258
- });
19259
- this.queuedBytes += buffer.length;
19260
- return true;
19261
- }
19262
- dequeue() {
19263
- const write = this.writes.shift();
19264
- if (write) this.queuedBytes -= write.buffer.length;
19265
- return write;
19266
- }
19267
- clear() {
19268
- this.writes.length = 0;
19269
- this.queuedBytes = 0;
19270
- }
19271
- constructor(maxBytes = DEFAULT_MAX_QUEUED_BYTES){
19272
- boundedWriteQueue_define_property(this, "maxBytes", void 0);
19273
- boundedWriteQueue_define_property(this, "writes", void 0);
19274
- boundedWriteQueue_define_property(this, "queuedBytes", void 0);
19275
- this.maxBytes = maxBytes;
19276
- this.writes = [];
19277
- this.queuedBytes = 0;
19278
- }
19279
- }
19280
- const boundedWriteQueue = BoundedWriteQueue;
19281
19267
  function getEdgeCodes(value, amount, fromEnd) {
19282
19268
  const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
19283
19269
  const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
@@ -19357,117 +19343,11 @@ var __webpack_exports__ = {};
19357
19343
  };
19358
19344
  }
19359
19345
  const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
19360
- function figureMinHeight(height, options) {
19361
- let minHeight;
19362
- if (options.video.height) {
19363
- minHeight = height ? Math.min(options.video.height, height) : options.video.height;
19364
- if (minHeight < 1) throw error_createError({
19365
- message: `Got a min height less than 1 (${minHeight})!`,
19366
- options
19367
- });
19368
- } else minHeight = height;
19369
- return minHeight;
19370
- }
19371
- const dimensions_figureMinHeight = figureMinHeight;
19372
- function getRatio(options, videoHeight, videoWidth) {
19373
- let ratio = 1;
19374
- const hasVideoDimensions = videoHeight && videoWidth;
19375
- const desiredHeight = options.video.height;
19376
- const desiredWidth = options.video.width;
19377
- const hasDesiredDimensions = desiredHeight && desiredWidth;
19378
- if (hasDesiredDimensions) ratio = hasVideoDimensions ? videoHeight < desiredHeight || videoWidth < desiredWidth ? videoHeight / videoWidth : desiredHeight / desiredWidth : desiredHeight / desiredWidth;
19379
- else if (hasVideoDimensions) ratio = videoHeight / videoWidth;
19380
- return ratio;
19381
- }
19382
- const dimensions_getRatio = getRatio;
19383
- function calculateHeight(responsive, videoWidth, options, ratio, element) {
19384
- const dimension = {
19385
- unit: "px"
19386
- };
19387
- let width = videoWidth;
19388
- if (responsive && element) {
19389
- const limitedDimension = dimensions_limitWidth(element, options, width);
19390
- width = limitedDimension.value;
19391
- }
19392
- const chosenRatio = null != ratio ? ratio : dimensions_getRatio(options, void 0, videoWidth);
19393
- const height = width ? Math.round(width * chosenRatio) : void 0;
19394
- if (height && Number.isInteger(height) && height < 1) throw error_createError({
19395
- message: "Just calculated a height less than 1 which is wrong.",
19396
- options
19397
- });
19398
- const minHeight = dimensions_figureMinHeight(height, options);
19399
- dimension.value = minHeight;
19400
- return dimension;
19401
- }
19402
- const dimensions_calculateHeight = calculateHeight;
19403
- function calculateWidth(responsive, options, videoHeight, ratio) {
19404
- const dimension = {
19405
- unit: "px"
19406
- };
19407
- let height = dimensions_figureMinHeight(videoHeight, options);
19408
- if (responsive) {
19409
- const limitedDimension = dimensions_limitHeight(height, options, "calculateWidth");
19410
- height = limitedDimension.value;
19411
- }
19412
- if (!height || height < 1) throw error_createError({
19413
- message: `Height ${height} cannot be smaller than 1 when calculating width.`,
19414
- options
19415
- });
19416
- const chosenRatio = null != ratio ? ratio : dimensions_getRatio(options, videoHeight);
19417
- const calculatedWidth = Math.round(height / chosenRatio);
19418
- if (calculatedWidth < 1) throw error_createError({
19419
- message: "Calculated width cannot be smaller than 1!",
19420
- options
19421
- });
19422
- dimension.value = calculatedWidth;
19423
- return dimension;
19424
- }
19425
- const dimensions_calculateWidth = calculateWidth;
19426
19346
  function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19427
19347
  return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19428
19348
  }
19429
19349
  const util_isAutomatedUserAgent = isAutomatedUserAgent;
19430
- function summarize(title, contents) {
19431
- const lines = Object.entries(contents).sort(([keyA], [keyB])=>keyA.localeCompare(keyB)).map(([key, value])=>{
19432
- const formattedValue = void 0 === value ? "undefined" : "object" == typeof value ? string_pretty(value) : value;
19433
- return ` • ${key}: ${formattedValue}`;
19434
- });
19435
- const line = [
19436
- `🔎 ${title}`,
19437
- ...lines
19438
- ].join("\n");
19439
- return line;
19440
- }
19441
- const string_summarize = summarize;
19442
- function getFirstVideoTrack(localMediaStream) {
19443
- const videoTracks = localMediaStream.getVideoTracks();
19444
- let videoTrack;
19445
- if (videoTracks[0]) videoTrack = videoTracks[0];
19446
- return videoTrack;
19447
- }
19448
- const media_getFirstVideoTrack = getFirstVideoTrack;
19449
- const MEDIA_EVENTS = [
19450
- "loadstart",
19451
- "suspend",
19452
- "progress",
19453
- "abort",
19454
- "emptied",
19455
- "stalled",
19456
- "pause",
19457
- "loadeddata",
19458
- "waiting",
19459
- "playing",
19460
- "canplay",
19461
- "canplaythrough",
19462
- "seeking",
19463
- "seeked",
19464
- "ended",
19465
- "ratechange",
19466
- "durationchange",
19467
- "volumechange"
19468
- ];
19469
- const mediaEvents = MEDIA_EVENTS;
19470
- function esm_define_property(obj, key, value) {
19350
+ function boundedWriteQueue_define_property(obj, key, value) {
19471
19351
  if (key in obj) Object.defineProperty(obj, key, {
19472
19352
  value: value,
19473
19353
  enumerable: true,
@@ -19477,52 +19357,37 @@ var __webpack_exports__ = {};
19477
19357
  else obj[key] = value;
19478
19358
  return obj;
19479
19359
  }
19480
- class AudioSample {
19481
- toBuffer() {
19482
- const l = this.float32Array.length;
19483
- const arr = new Int16Array(l);
19484
- this.float32Array.forEach((sample, i)=>{
19485
- const clampedSample = Number.isFinite(sample) ? Math.max(-1, sample) : sample;
19486
- arr[i] = 0x7fff * Math.min(1, clampedSample);
19360
+ const DEFAULT_MAX_QUEUED_BYTES = 1048576;
19361
+ class BoundedWriteQueue {
19362
+ enqueue(buffer, params) {
19363
+ if (this.queuedBytes + buffer.length > this.maxBytes) return false;
19364
+ this.writes.push({
19365
+ buffer,
19366
+ params
19487
19367
  });
19488
- return typedarray_to_buffer(arr);
19368
+ this.queuedBytes += buffer.length;
19369
+ return true;
19489
19370
  }
19490
- constructor(float32Array){
19491
- esm_define_property(this, "float32Array", void 0);
19492
- this.float32Array = float32Array;
19371
+ dequeue() {
19372
+ const write = this.writes.shift();
19373
+ if (write) this.queuedBytes -= write.buffer.length;
19374
+ return write;
19493
19375
  }
19494
- }
19495
- const esm_src = AudioSample;
19496
- const esm = esm_src;
19497
- var is_power_of_two = __webpack_require__("dZ");
19498
- var is_power_of_two_default = /*#__PURE__*/ __webpack_require__.n(is_power_of_two);
19499
- function AudioRecorder_asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
19500
- try {
19501
- var info = gen[key](arg);
19502
- var value = info.value;
19503
- } catch (error) {
19504
- reject(error);
19505
- return;
19376
+ clear() {
19377
+ this.writes.length = 0;
19378
+ this.queuedBytes = 0;
19379
+ }
19380
+ constructor(maxBytes = DEFAULT_MAX_QUEUED_BYTES){
19381
+ boundedWriteQueue_define_property(this, "maxBytes", void 0);
19382
+ boundedWriteQueue_define_property(this, "writes", void 0);
19383
+ boundedWriteQueue_define_property(this, "queuedBytes", void 0);
19384
+ this.maxBytes = maxBytes;
19385
+ this.writes = [];
19386
+ this.queuedBytes = 0;
19506
19387
  }
19507
- if (info.done) resolve(value);
19508
- else Promise.resolve(value).then(_next, _throw);
19509
- }
19510
- function AudioRecorder_async_to_generator(fn) {
19511
- return function() {
19512
- var self1 = this, args = arguments;
19513
- return new Promise(function(resolve, reject) {
19514
- var gen = fn.apply(self1, args);
19515
- function _next(value) {
19516
- AudioRecorder_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
19517
- }
19518
- function _throw(err) {
19519
- AudioRecorder_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
19520
- }
19521
- _next(void 0);
19522
- });
19523
- };
19524
19388
  }
19525
- function AudioRecorder_define_property(obj, key, value) {
19389
+ const boundedWriteQueue = BoundedWriteQueue;
19390
+ function streamWriter_define_property(obj, key, value) {
19526
19391
  if (key in obj) Object.defineProperty(obj, key, {
19527
19392
  value: value,
19528
19393
  enumerable: true,
@@ -19532,149 +19397,183 @@ var __webpack_exports__ = {};
19532
19397
  else obj[key] = value;
19533
19398
  return obj;
19534
19399
  }
19535
- const CHANNELS = 1;
19536
- function getAudioContextClass() {
19537
- return window.AudioContext;
19538
- }
19539
- class AudioRecorder {
19540
- hasAudioContext() {
19541
- return Boolean(getAudioContextClass()) && Boolean(this.getAudioContext());
19542
- }
19543
- getAudioContext() {
19544
- if (!this.vcAudioContext) {
19545
- const AudioContext = getAudioContextClass();
19546
- this.vcAudioContext = new AudioContext();
19547
- }
19548
- return this.vcAudioContext;
19549
- }
19550
- onAudioProcess(e, cb) {
19551
- if (!this.userMedia.isRecording() || this.userMedia.isPaused()) return;
19552
- const float32Array = e.inputBuffer.getChannelData(0);
19553
- cb(new esm(float32Array));
19400
+ const PIPE_SYMBOL = "°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ ";
19401
+ class RecorderStreamWriter extends util_Despot {
19402
+ hasExceededWebSocketSendBuffer(buffer, opts) {
19403
+ return buffer.length > 0 && !this.pendingWrites.enqueue(buffer, opts);
19554
19404
  }
19555
- disconnect() {
19556
- var _this_audioInput, _this_volume;
19557
- if (this.scriptProcessor) {
19558
- this.scriptProcessor.onaudioprocess = null;
19559
- this.scriptProcessor.disconnect();
19560
- }
19561
- null == (_this_audioInput = this.audioInput) || _this_audioInput.disconnect();
19562
- null == (_this_volume = this.volume) || _this_volume.disconnect();
19405
+ writeStream(buffer, opts) {
19406
+ if (this.stream) if (this.stream.destroyed) {
19407
+ this.stopPings();
19408
+ const err = error_createError({
19409
+ message: "Already disconnected",
19410
+ explanation: "Sorry, connection to the server has been destroyed. Please reload.",
19411
+ options: this.options
19412
+ });
19413
+ this.handleWriteFailure(err);
19414
+ } else if (this.streamBackpressured) {
19415
+ if (this.hasExceededWebSocketSendBuffer(buffer, opts)) this.failMediaTransmission(error_createError({
19416
+ message: "WebSocket send buffer limit exceeded",
19417
+ explanation: "The connection to the server is too slow to keep up with this recording. The recording has been stopped; please start a new one.",
19418
+ options: this.options
19419
+ }));
19420
+ } else this.writeToStream(this.stream, buffer, opts);
19563
19421
  }
19564
- init(localMediaStream) {
19565
- this.options.logger.debug("AudioRecorder: init()");
19566
- this.disconnect();
19567
- const volume = this.getAudioContext().createGain();
19568
- this.volume = volume;
19422
+ writeToStream(stream, buffer, opts) {
19423
+ const onFlushedCallback = null == opts ? void 0 : opts.onFlushedCallback;
19569
19424
  try {
19570
- this.audioInput = this.getAudioContext().createMediaStreamSource(localMediaStream);
19425
+ const canContinue = stream.write(buffer, ()=>{
19426
+ if (!onFlushedCallback) return;
19427
+ try {
19428
+ onFlushedCallback(opts);
19429
+ } catch (exc) {
19430
+ const err = error_createError({
19431
+ message: "Failed to write stream buffer",
19432
+ explanation: `stream.write() failed because of ${string_pretty(exc)}`,
19433
+ options: this.options,
19434
+ exc
19435
+ });
19436
+ this.emit("ERROR", {
19437
+ err
19438
+ });
19439
+ }
19440
+ });
19441
+ if (!canContinue) {
19442
+ this.streamBackpressured = true;
19443
+ this.startBackpressureTimeout();
19444
+ this.pauseForBackpressure();
19445
+ return false;
19446
+ }
19447
+ return true;
19571
19448
  } catch (exc) {
19572
- throw error_createError({
19573
- message: "Webcam has no audio",
19574
- exc,
19575
- options: this.options
19449
+ const err = error_createError({
19450
+ message: "Failed writing to server",
19451
+ explanation: `stream.write() failed because of ${string_pretty(exc)}`,
19452
+ options: this.options,
19453
+ exc
19576
19454
  });
19455
+ this.handleWriteFailure(err);
19456
+ return false;
19577
19457
  }
19578
- let { bufferSize } = this.options.audio;
19579
- if ("auto" === bufferSize) bufferSize = util_getBrowser(this.options).isFirefox() ? 512 : 2048;
19580
- if (void 0 === bufferSize || !is_power_of_two_default()(bufferSize)) throw error_createError({
19581
- message: `Audio buffer size must be a power of two. The current buffer size is ${bufferSize}.`,
19582
- options: this.options
19458
+ }
19459
+ flushPendingWrites(stream) {
19460
+ if (this.isStaleStream(stream)) return;
19461
+ this.streamBackpressured = false;
19462
+ this.clearBackpressureTimeout();
19463
+ let pendingWrite = this.pendingWrites.dequeue();
19464
+ while(pendingWrite){
19465
+ if (!this.writeToStream(stream, pendingWrite.buffer, pendingWrite.params)) return;
19466
+ pendingWrite = this.pendingWrites.dequeue();
19467
+ }
19468
+ this.resumeRecordingAfterDrain();
19469
+ }
19470
+ isStaleStream(stream) {
19471
+ return this.stream !== stream || stream.destroyed;
19472
+ }
19473
+ canPauseForBackpressure() {
19474
+ return this.recordingActive && !this.isUserMediaPaused() && this.isCaptureLoopRunning();
19475
+ }
19476
+ pauseForBackpressure() {
19477
+ if (!this.canPauseForBackpressure()) return;
19478
+ this.resumeAfterDrain = true;
19479
+ this.stopCaptureLoop();
19480
+ }
19481
+ resumeRecordingAfterDrain() {
19482
+ if (!this.resumeAfterDrain) return;
19483
+ this.resumeAfterDrain = false;
19484
+ if (this.canResumeAfterDrain()) this.startCaptureLoop();
19485
+ }
19486
+ canResumeAfterDrain() {
19487
+ return this.recordingActive && !this.isUserMediaPaused();
19488
+ }
19489
+ handleWriteFailure(err) {
19490
+ if (this.mediaTransmissionStarted) return void this.failMediaTransmission(err);
19491
+ this.emit("ERROR", {
19492
+ err
19583
19493
  });
19584
- if (!Number.isFinite(this.options.audio.volume) || this.options.audio.volume < 0 || this.options.audio.volume > 1) throw error_createError({
19585
- message: `Audio volume must be between zero and one. The current volume is ${this.options.audio.volume}.`,
19586
- options: this.options
19494
+ }
19495
+ failMediaTransmission(err) {
19496
+ if (this.recordingTransportFailed) return;
19497
+ this.recordingTransportFailed = true;
19498
+ this.recordingActive = false;
19499
+ this.mediaTransmissionStarted = false;
19500
+ this.streamBackpressured = false;
19501
+ this.resumeAfterDrain = false;
19502
+ this.pendingWrites.clear();
19503
+ this.connected = false;
19504
+ this.connecting = false;
19505
+ this.reconnecting = false;
19506
+ this.clearReconnectTimeout();
19507
+ this.clearBackpressureTimeout();
19508
+ this.discardSocket();
19509
+ this.reset();
19510
+ this.framesCount = this.samplesCount = 0;
19511
+ this.confirmedFrameNumber = this.confirmedSampleNumber = 0;
19512
+ delete this.recordingStats;
19513
+ this.recordingDimensions = void 0;
19514
+ this.emit("ERROR", {
19515
+ err
19587
19516
  });
19588
- volume.gain.value = this.options.audio.volume;
19589
- this.scriptProcessor = this.getAudioContext().createScriptProcessor(bufferSize, CHANNELS, CHANNELS);
19590
- this.audioInput.connect(volume);
19591
- volume.connect(this.scriptProcessor);
19592
- this.scriptProcessor.connect(this.getAudioContext().destination);
19593
19517
  }
19594
- record(cb) {
19595
- this.options.logger.debug("AudioRecorder: record()");
19596
- if (this.scriptProcessor) this.scriptProcessor.onaudioprocess = (e)=>{
19597
- this.onAudioProcess(e, cb);
19598
- };
19518
+ startBackpressureTimeout() {
19519
+ if (!this.shouldStartBackpressureTimeout()) return;
19520
+ this.backpressureTimeout = window.setTimeout(()=>{
19521
+ this.backpressureTimeout = void 0;
19522
+ if (this.shouldIgnoreBackpressureTimeout()) return;
19523
+ const err = error_createError({
19524
+ message: "WebSocket write timed out",
19525
+ explanation: "The server stopped accepting recording data. The recording has been stopped; please check your connection and start a new one.",
19526
+ options: this.options
19527
+ });
19528
+ this.failMediaTransmission(err);
19529
+ }, this.options.timeouts.connection);
19599
19530
  }
19600
- resume() {
19601
- return AudioRecorder_async_to_generator(function*() {
19602
- const context = this.getAudioContext();
19603
- if ("running" !== context.state) yield context.resume();
19604
- }).call(this);
19531
+ shouldStartBackpressureTimeout() {
19532
+ return void 0 === this.backpressureTimeout && this.mediaTransmissionStarted;
19605
19533
  }
19606
- stop() {
19607
- this.options.logger.debug("AudioRecorder: stop()");
19608
- this.disconnect();
19609
- if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
19610
- this.options.logger.debug("AudioRecorder: audio context is closed");
19611
- this.vcAudioContext = void 0;
19612
- }).catch((err)=>{
19613
- if (err instanceof Error) return void this.options.logger.error(error_createError({
19614
- err,
19615
- options: this.options
19616
- }));
19617
- this.options.logger.error(err);
19534
+ shouldIgnoreBackpressureTimeout() {
19535
+ return !this.streamBackpressured || this.recordingTransportFailed;
19536
+ }
19537
+ failInterruptedRecording() {
19538
+ const unconfirmedFrames = Math.max(0, this.framesCount - this.confirmedFrameNumber);
19539
+ const unconfirmedSamples = Math.max(0, this.samplesCount - this.confirmedSampleNumber);
19540
+ const err = error_createError({
19541
+ message: "Recording interrupted",
19542
+ explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
19543
+ options: this.options,
19544
+ cause: {
19545
+ unconfirmedFrames,
19546
+ unconfirmedSamples
19547
+ }
19618
19548
  });
19549
+ this.failMediaTransmission(err);
19619
19550
  }
19620
- getSampleRate() {
19621
- if (this.hasAudioContext()) return this.getAudioContext().sampleRate;
19622
- return -1;
19551
+ handleStreamDrain(stream) {
19552
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *drain* event emitted`);
19553
+ this.flushPendingWrites(stream);
19623
19554
  }
19624
- constructor(userMedia, options){
19625
- AudioRecorder_define_property(this, "scriptProcessor", void 0);
19626
- AudioRecorder_define_property(this, "audioInput", void 0);
19627
- AudioRecorder_define_property(this, "volume", void 0);
19628
- AudioRecorder_define_property(this, "vcAudioContext", void 0);
19629
- AudioRecorder_define_property(this, "userMedia", void 0);
19630
- AudioRecorder_define_property(this, "options", void 0);
19631
- this.options = options;
19632
- this.userMedia = userMedia;
19555
+ clearBackpressureTimeout() {
19556
+ if (void 0 === this.backpressureTimeout) return;
19557
+ window.clearTimeout(this.backpressureTimeout);
19558
+ this.backpressureTimeout = void 0;
19633
19559
  }
19634
- }
19635
- const media_AudioRecorder = AudioRecorder;
19636
- const VIRTUAL_KEYWORDS = [
19637
- "obs",
19638
- "virtual",
19639
- "manycam",
19640
- "vcam",
19641
- "splitcam",
19642
- "droidcam"
19643
- ];
19644
- function isVirtualCamera(videoTrack) {
19645
- const capabilities = videoTrack.getCapabilities();
19646
- if (!capabilities.frameRate) return true;
19647
- const isVirtual = VIRTUAL_KEYWORDS.some((keyword)=>videoTrack.label.toLowerCase().includes(keyword));
19648
- return isVirtual;
19649
- }
19650
- const util_isVirtualCamera = isVirtualCamera;
19651
- function userMedia_asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
19652
- try {
19653
- var info = gen[key](arg);
19654
- var value = info.value;
19655
- } catch (error) {
19656
- reject(error);
19657
- return;
19560
+ discardSocket() {
19561
+ this.options.logger.debug("Recorder: discarding socket stream ...");
19562
+ this.clearBackpressureTimeout();
19563
+ this.pendingWrites.clear();
19564
+ this.streamBackpressured = false;
19565
+ this.resumeAfterDrain = false;
19566
+ const stream = this.stream;
19567
+ this.stream = void 0;
19568
+ if (stream && !stream.destroyed) stream.destroy();
19569
+ }
19570
+ constructor(options){
19571
+ super("Recorder", options), streamWriter_define_property(this, "pendingWrites", new boundedWriteQueue()), streamWriter_define_property(this, "samplesCount", 0), streamWriter_define_property(this, "framesCount", 0), streamWriter_define_property(this, "recordingStats", void 0), streamWriter_define_property(this, "confirmedFrameNumber", 0), streamWriter_define_property(this, "confirmedSampleNumber", 0), streamWriter_define_property(this, "backpressureTimeout", void 0), streamWriter_define_property(this, "recordingDimensions", void 0), streamWriter_define_property(this, "stream", void 0), streamWriter_define_property(this, "streamBackpressured", false), streamWriter_define_property(this, "resumeAfterDrain", false), streamWriter_define_property(this, "mediaTransmissionStarted", false), streamWriter_define_property(this, "recordingActive", false), streamWriter_define_property(this, "recordingTransportFailed", false), streamWriter_define_property(this, "connecting", false), streamWriter_define_property(this, "connected", false), streamWriter_define_property(this, "reconnecting", false);
19658
19572
  }
19659
- if (info.done) resolve(value);
19660
- else Promise.resolve(value).then(_next, _throw);
19661
19573
  }
19662
- function userMedia_async_to_generator(fn) {
19663
- return function() {
19664
- var self1 = this, args = arguments;
19665
- return new Promise(function(resolve, reject) {
19666
- var gen = fn.apply(self1, args);
19667
- function _next(value) {
19668
- userMedia_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
19669
- }
19670
- function _throw(err) {
19671
- userMedia_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
19672
- }
19673
- _next(void 0);
19674
- });
19675
- };
19676
- }
19677
- function userMedia_define_property(obj, key, value) {
19574
+ const streamWriter = RecorderStreamWriter;
19575
+ var Buffer = __webpack_require__("dY").Buffer;
19576
+ function connection_define_property(obj, key, value) {
19678
19577
  if (key in obj) Object.defineProperty(obj, key, {
19679
19578
  value: value,
19680
19579
  enumerable: true,
@@ -19684,423 +19583,310 @@ var __webpack_exports__ = {};
19684
19583
  else obj[key] = value;
19685
19584
  return obj;
19686
19585
  }
19687
- const EVENT_ASCII = "|—O—|";
19688
- class UserMedia extends util_Despot {
19689
- attachMediaStream(stream) {
19690
- if (this.rawVisualUserMedia) {
19691
- this.rawVisualUserMedia.srcObject = stream;
19692
- const previousStream = this.currentVisualStream;
19693
- this.currentVisualStream = stream;
19694
- if (previousStream && previousStream !== stream) previousStream.getTracks().forEach((track)=>{
19695
- track.stop();
19696
- });
19697
- } else throw error_createError({
19698
- message: "Error attaching stream to element.",
19699
- explanation: "Contact the developer about this",
19700
- options: this.options
19586
+ class RecorderConnection extends streamWriter {
19587
+ canReconnectAfterStreamClose() {
19588
+ return Boolean(this.connected && this.userMediaLoaded);
19589
+ }
19590
+ shouldReportStreamClose() {
19591
+ return !this.connecting && !this.blocking;
19592
+ }
19593
+ handleStreamClose(url2Connect) {
19594
+ const debugLine = string_summarize(`${PIPE_SYMBOL}Stream has closed:`, {
19595
+ connecting: this.connecting,
19596
+ connected: this.connected,
19597
+ userMediaLoaded: this.userMediaLoaded,
19598
+ blocking: this.blocking
19701
19599
  });
19600
+ this.options.logger.debug(debugLine);
19601
+ const canReconnect = this.canReconnectAfterStreamClose();
19602
+ this.connected = false;
19603
+ this.stopPings();
19604
+ if (this.shouldIgnoreStreamClose()) return;
19605
+ if (this.mediaTransmissionStarted) this.failInterruptedRecording();
19606
+ else if (canReconnect) {
19607
+ this.pendingWrites.clear();
19608
+ this.streamBackpressured = false;
19609
+ this.resumeAfterDrain = false;
19610
+ this.reconnecting = true;
19611
+ this.initSocket();
19612
+ } else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
19613
+ this.handleConnectionFailure({
19614
+ url2Connect,
19615
+ cause: "closed"
19616
+ });
19617
+ }, 0);
19702
19618
  }
19703
- setVisualStream(localMediaStream) {
19704
- if (localMediaStream) this.attachMediaStream(localMediaStream);
19705
- else {
19706
- if (this.rawVisualUserMedia) {
19707
- this.rawVisualUserMedia.srcObject = null;
19708
- this.rawVisualUserMedia.removeAttribute("src");
19709
- }
19710
- this.currentVisualStream = void 0;
19619
+ shouldIgnoreStreamClose() {
19620
+ return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
19621
+ }
19622
+ handleStreamError(url2Connect, err) {
19623
+ var _this_lastSocketError;
19624
+ if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
19625
+ else this.lastSocketError = serializeError(err);
19626
+ const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
19627
+ error: serializeError(err)
19628
+ });
19629
+ this.options.logger.debug(debugLine);
19630
+ if (this.validate()) {
19631
+ this.connected = false;
19632
+ this.stopPings();
19633
+ this.discardSocket();
19634
+ return;
19711
19635
  }
19636
+ if (!this.connected) return void this.handleConnectionFailure({
19637
+ url2Connect,
19638
+ cause: "error"
19639
+ });
19640
+ const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
19641
+ cause: null != (_this_lastSocketError = this.lastSocketError) ? _this_lastSocketError : err
19642
+ });
19643
+ if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19644
+ this.emit("ERROR", {
19645
+ err: error_createError({
19646
+ message: "WebSocket stream error",
19647
+ explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
19648
+ options: this.options,
19649
+ exc: streamError
19650
+ })
19651
+ });
19712
19652
  }
19713
- restorePlayback() {
19714
- return userMedia_async_to_generator(function*() {
19715
- var _this_audioRecorder;
19716
- if (!this.rawVisualUserMedia) throw new Error("No camera preview is available.");
19717
- yield this.rawVisualUserMedia.play();
19718
- yield null == (_this_audioRecorder = this.audioRecorder) ? void 0 : _this_audioRecorder.resume();
19719
- }).call(this);
19653
+ sendPings() {
19654
+ this.stopPings();
19655
+ this.pingInterval = window.setInterval(()=>{
19656
+ this.options.logger.debug("Recorder: pinging...");
19657
+ this.writeStream(Buffer.from(""));
19658
+ }, this.options.timeouts.pingInterval);
19720
19659
  }
19721
- hasLiveStream() {
19722
- var _this_currentVisualStream, _this_currentVisualStream1, _this_currentVisualStream2;
19723
- const videoReady = null == (_this_currentVisualStream = this.currentVisualStream) ? void 0 : _this_currentVisualStream.getVideoTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
19724
- const audioReady = !isAudioEnabled(this.options) || (null == (_this_currentVisualStream1 = this.currentVisualStream) ? void 0 : _this_currentVisualStream1.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted));
19725
- return Boolean((null == (_this_currentVisualStream2 = this.currentVisualStream) ? void 0 : _this_currentVisualStream2.active) && videoReady && audioReady);
19660
+ stopPings() {
19661
+ if (void 0 === this.pingInterval) return;
19662
+ this.options.logger.debug("Recorder: stopping pings...");
19663
+ window.clearInterval(this.pingInterval);
19664
+ this.pingInterval = void 0;
19726
19665
  }
19727
- hasEnded() {
19728
- var _this_rawVisualUserMedia, _this_currentVisualStream;
19729
- if (null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.ended) return this.rawVisualUserMedia.ended;
19730
- return !(null == (_this_currentVisualStream = this.currentVisualStream) ? void 0 : _this_currentVisualStream.active);
19666
+ clearConnectionTimeout() {
19667
+ if (void 0 === this.connectionTimeout) return;
19668
+ this.options.logger.debug("Recorder: clearConnectionTimeout()");
19669
+ window.clearTimeout(this.connectionTimeout);
19670
+ this.connectionTimeout = void 0;
19731
19671
  }
19732
- hasInvalidDimensions() {
19733
- if (!this.rawVisualUserMedia) return false;
19734
- const { videoWidth, videoHeight } = this.rawVisualUserMedia;
19735
- return videoWidth > 0 && videoWidth < 3 || videoHeight > 0 && videoHeight < 3;
19672
+ clearReconnectTimeout() {
19673
+ if (void 0 === this.reconnectTimeout) return;
19674
+ this.options.logger.debug("Recorder: clearReconnectTimeout()");
19675
+ window.clearTimeout(this.reconnectTimeout);
19676
+ this.reconnectTimeout = void 0;
19736
19677
  }
19737
- logEvent(eventType, params) {
19738
- this.options.logger.debug(`UserMedia: ... ${EVENT_ASCII} event ${eventType}: ${string_pretty(params)}`);
19678
+ handleConnectionFailure(params) {
19679
+ if (this.retryConnection(params)) return;
19680
+ this.failConnection(params);
19739
19681
  }
19740
- unloadRemainingEventListeners() {
19741
- var _this_clearInitializationListeners, _this, _this_rawVisualUserMedia;
19742
- this.options.logger.debug("UserMedia: unloadRemainingEventListeners()");
19743
- null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
19744
- mediaEvents.forEach((eventName)=>{
19745
- var _this_rawVisualUserMedia;
19746
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener(eventName, this.outputEvent);
19747
- });
19748
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener("error", this.handleMediaError);
19682
+ retryConnection(params) {
19683
+ if (!this.canRetryConnection()) return false;
19684
+ if (void 0 !== this.reconnectTimeout) return true;
19685
+ const delayMs = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 10000);
19686
+ this.reconnectAttempts++;
19687
+ this.connecting = false;
19688
+ this.clearConnectionTimeout();
19689
+ this.discardSocket();
19690
+ this.options.logger.debug(`Recorder: retrying WebSocket connection in ${delayMs}ms (cause ${params.cause}).`);
19691
+ this.reconnectTimeout = window.setTimeout(()=>{
19692
+ this.reconnectTimeout = void 0;
19693
+ if (this.shouldContinueReconnect()) this.initSocket();
19694
+ }, delayMs);
19695
+ return true;
19749
19696
  }
19750
- audioRecord(audioCallback) {
19751
- var _this_audioRecorder;
19752
- this.removeListener("SENDING_FIRST_FRAME");
19753
- null == (_this_audioRecorder = this.audioRecorder) || _this_audioRecorder.record(audioCallback);
19697
+ canRetryConnection() {
19698
+ return Boolean(this.reconnecting && this.userMediaLoaded && this.isOnline() && !this.unloaded && !this.blocking);
19754
19699
  }
19755
- init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, params) {
19756
- var _this_clearInitializationListeners, _this;
19757
- null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
19758
- this.stop(localMediaStream, {
19759
- aboutToInitialize: true,
19760
- preserveRecordingState: Boolean((null == params ? void 0 : params.switchingFacingMode) || (null == params ? void 0 : params.recovering))
19761
- });
19762
- const initializationId = ++this.initializationId;
19763
- this.onPlayReached = false;
19764
- this.onLoadedMetaDataReached = false;
19765
- this.playingPromiseReached = false;
19766
- this.videoTrackLabel = void 0;
19767
- if (isAudioEnabled(this.options)) null != this.audioRecorder || (this.audioRecorder = new media_AudioRecorder(this, this.options));
19768
- const unloadAllEventListeners = ()=>{
19769
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
19770
- this.options.logger.debug("UserMedia: unloadAllEventListeners()");
19771
- this.unloadRemainingEventListeners();
19772
- this.removeListener("SENDING_FIRST_FRAME");
19773
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener("play", onPlay);
19774
- null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.removeEventListener("loadedmetadata", onLoadedMetaData);
19775
- };
19776
- this.clearInitializationListeners = ()=>{
19777
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
19778
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener("play", onPlay);
19779
- null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.removeEventListener("loadedmetadata", onLoadedMetaData);
19780
- this.clearInitializationListeners = void 0;
19700
+ shouldContinueReconnect() {
19701
+ return this.reconnecting && !this.unloaded && !this.blocking;
19702
+ }
19703
+ isOnline() {
19704
+ return navigator.onLine;
19705
+ }
19706
+ failConnection(params) {
19707
+ var _this_stream;
19708
+ if (this.shouldIgnoreConnectionFailure()) return;
19709
+ this.connectionFailed = true;
19710
+ this.connecting = false;
19711
+ this.reconnecting = false;
19712
+ this.reconnectAttempts = 0;
19713
+ this.clearReconnectTimeout();
19714
+ this.clearConnectionTimeout();
19715
+ const { url2Connect, cause } = params;
19716
+ const online = this.isOnline();
19717
+ const elapsedMs = this.connectingStartedAt ? Date.now() - this.connectingStartedAt : void 0;
19718
+ const closeEvent = this.lastCloseEvent;
19719
+ const socketError = this.lastSocketError;
19720
+ const contents = {
19721
+ cause,
19722
+ closeCode: null == closeEvent ? void 0 : closeEvent.code,
19723
+ closeReason: null == closeEvent ? void 0 : closeEvent.reason,
19724
+ elapsedMs,
19725
+ online,
19726
+ secureContext: globalThis.isSecureContext,
19727
+ socketReadyState: null == (_this_stream = this.stream) ? void 0 : _this_stream.socket.readyState,
19728
+ timeoutMs: this.options.timeouts.connection,
19729
+ url: url2Connect,
19730
+ unloaded: this.unloaded,
19731
+ userMediaLoaded: this.userMediaLoaded,
19732
+ userMediaLoading: this.userMediaLoading,
19733
+ wasClean: null == closeEvent ? void 0 : closeEvent.wasClean,
19734
+ socketError,
19735
+ blocking: this.blocking,
19736
+ visibilityState: document.visibilityState
19781
19737
  };
19782
- const play = ()=>{
19783
- try {
19784
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
19785
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.load();
19786
- if (null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.paused) {
19787
- 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)}`);
19788
- this.rawVisualUserMedia.play().then(()=>{
19789
- if (initializationId !== this.initializationId) return;
19790
- if (!this.playingPromiseReached) {
19791
- this.options.logger.debug("UserMedia: play promise successful. Playing now.");
19792
- this.playingPromiseReached = true;
19793
- }
19794
- }).catch((exc)=>{
19795
- if (initializationId !== this.initializationId) return;
19796
- if (exc instanceof Error && !(null == params ? void 0 : params.recovering)) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
19797
- else {
19798
- unloadAllEventListeners();
19799
- endedEarlyCallback(error_createError({
19800
- message: "Failed to play user media upon play event.",
19801
- exc,
19802
- options: this.options
19803
- }));
19804
- }
19805
- });
19806
- }
19807
- } catch (exc) {
19808
- unloadAllEventListeners();
19809
- endedEarlyCallback(exc);
19810
- }
19811
- };
19812
- const fireCallbacks = ()=>{
19813
- var _this_rawVisualUserMedia;
19814
- const readyState = null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState;
19815
- this.options.logger.debug(`UserMedia: fireCallbacks(readyState=${readyState}, onPlayReached=${this.onPlayReached}, onLoadedMetaDataReached=${this.onLoadedMetaDataReached})`);
19816
- if (this.onPlayReached && this.onLoadedMetaDataReached) if (this.audioRecorder) try {
19817
- this.audioRecorder.init(localMediaStream);
19818
- const onAudioReady = ()=>{
19819
- var _this_clearInitializationListeners, _this;
19820
- if (initializationId !== this.initializationId || this.currentVisualStream !== localMediaStream) return;
19821
- if (this.recording) this.audioRecord(audioCallback);
19822
- else this.on("SENDING_FIRST_FRAME", ()=>{
19823
- this.audioRecord(audioCallback);
19824
- });
19825
- null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
19826
- videoCallback();
19827
- };
19828
- if (null == params ? void 0 : params.recovering) this.audioRecorder.resume().then(onAudioReady).catch(endedEarlyCallback);
19829
- else onAudioReady();
19830
- } catch (exc) {
19831
- unloadAllEventListeners();
19832
- this.stop(void 0, {
19833
- preserveRecordingState: Boolean(null == params ? void 0 : params.recovering)
19834
- });
19835
- endedEarlyCallback(exc);
19836
- }
19837
- else {
19838
- var _this_clearInitializationListeners, _this;
19839
- null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
19840
- videoCallback();
19841
- }
19842
- };
19843
- const onPlay = ()=>{
19844
- try {
19845
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3, _this_rawVisualUserMedia4, _this_rawVisualUserMedia5;
19846
- this.logEvent("play", {
19847
- readyState: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState,
19848
- audio: isAudioEnabled(this.options),
19849
- width: null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.width,
19850
- height: null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia2.height,
19851
- videoWidth: null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia3.videoWidth,
19852
- videoHeight: null == (_this_rawVisualUserMedia4 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia4.videoHeight
19853
- });
19854
- null == (_this_rawVisualUserMedia5 = this.rawVisualUserMedia) || _this_rawVisualUserMedia5.removeEventListener("play", onPlay);
19855
- if (this.hasEnded() || this.hasInvalidDimensions()) endedEarlyCallback(error_createError({
19856
- message: "Already busy",
19857
- explanation: "Probably another browser window is using your webcam?",
19858
- options: this.options
19859
- }));
19860
- else {
19861
- this.onPlayReached = true;
19862
- fireCallbacks();
19863
- }
19864
- } catch (exc) {
19865
- unloadAllEventListeners();
19866
- endedEarlyCallback(exc);
19867
- }
19868
- };
19869
- const onLoadedMetaData = ()=>{
19870
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3, _this_rawVisualUserMedia4, _this_rawVisualUserMedia5, _this_rawVisualUserMedia6;
19871
- this.logEvent("loadedmetadata", {
19872
- readyState: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState,
19873
- paused: null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.paused,
19874
- width: null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia2.width,
19875
- height: null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia3.height,
19876
- videoWidth: null == (_this_rawVisualUserMedia4 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia4.videoWidth,
19877
- videoHeight: null == (_this_rawVisualUserMedia5 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia5.videoHeight
19738
+ const debugLine = string_summarize("Recorder: failConnection() diagnostic", contents);
19739
+ this.options.logger.debug(debugLine);
19740
+ let explanation;
19741
+ if (online) if ("timeout" === cause) explanation = `The server at ${url2Connect} did not respond within ${this.options.timeouts.connection}ms, even though your device is online. This usually points to a firewall or proxy silently dropping the connection. Please try a different network. If the problem persists, contact us.`;
19742
+ else if ("error" === cause) explanation = `An error occurred while connecting to ${url2Connect}. Please check your internet connection and try again. If the problem persists, contact us.`;
19743
+ else {
19744
+ const closeSuffix = closeEvent ? ` (code ${closeEvent.code})` : "";
19745
+ explanation = `Connection to ${url2Connect} is closed${closeSuffix}. Please check your internet connection and try again. If the problem persists, contact us.`;
19746
+ }
19747
+ else explanation = "Your device appears to be offline. Please check your internet connection and try again.";
19748
+ this.discardSocket();
19749
+ const err = error_createError({
19750
+ message: "Unable to connect to the server",
19751
+ explanation,
19752
+ options: this.options,
19753
+ cause: contents,
19754
+ exc: socketError ? new Error("WebSocket connection error", {
19755
+ cause: socketError
19756
+ }) : void 0
19757
+ });
19758
+ this.emit("ERROR", {
19759
+ err
19760
+ });
19761
+ }
19762
+ shouldIgnoreConnectionFailure() {
19763
+ return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
19764
+ }
19765
+ initSocket(cb) {
19766
+ if (this.connected) {
19767
+ this.options.logger.debug("Recorder: already **connected**. Not going to initialize a new WebSocket connection.");
19768
+ null == cb || cb();
19769
+ return;
19770
+ }
19771
+ if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
19772
+ this.clearReconnectTimeout();
19773
+ if (util_isAutomatedUserAgent()) {
19774
+ this.connecting = false;
19775
+ this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
19776
+ return;
19777
+ }
19778
+ this.connecting = true;
19779
+ this.connectionFailed = false;
19780
+ this.connectingStartedAt = Date.now();
19781
+ this.lastCloseEvent = void 0;
19782
+ this.lastSocketError = void 0;
19783
+ this.emit("CONNECTING");
19784
+ let url2Connect;
19785
+ try {
19786
+ const socketUrlObj = new URL(this.options.socketUrl);
19787
+ socketUrlObj.searchParams.set(constants.WHITELIST_KEY_LABEL, this.options.whitelistKey);
19788
+ url2Connect = socketUrlObj.toString();
19789
+ } catch (exc) {
19790
+ this.connecting = this.connected = false;
19791
+ const err = error_createError({
19792
+ message: "Invalid WebSocket URL",
19793
+ explanation: `The configured socketUrl "${this.options.socketUrl}" is not a valid URL. Please check your videomail-client configuration.`,
19794
+ options: this.options,
19795
+ exc
19878
19796
  });
19879
- null == (_this_rawVisualUserMedia6 = this.rawVisualUserMedia) || _this_rawVisualUserMedia6.removeEventListener("loadedmetadata", onLoadedMetaData);
19880
- if (!this.hasEnded() && !this.hasInvalidDimensions()) {
19881
- this.emit("LOADED_META_DATA");
19882
- this.onLoadedMetaDataReached = true;
19883
- fireCallbacks();
19884
- }
19885
- };
19797
+ this.emit("ERROR", {
19798
+ err
19799
+ });
19800
+ return;
19801
+ }
19802
+ this.options.logger.debug(`Recorder: initializing web socket stream to ${url2Connect}`);
19803
+ let nativeSocket;
19886
19804
  try {
19887
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3;
19888
- const videoTrack = media_getFirstVideoTrack(localMediaStream);
19889
- this.videoTrackLabel = null == videoTrack ? void 0 : videoTrack.label;
19890
- if (videoTrack) if (videoTrack.enabled) if (util_isVirtualCamera(videoTrack)) throw error_createError({
19891
- message: "Virtual cameras are not allowed.",
19892
- explanation: `Please use a real camera, not "${videoTrack.label}".`,
19893
- options: this.options
19805
+ nativeSocket = new WebSocket(url2Connect);
19806
+ nativeSocket.addEventListener("close", (event)=>{
19807
+ this.lastCloseEvent = {
19808
+ code: event.code,
19809
+ reason: event.reason,
19810
+ wasClean: event.wasClean
19811
+ };
19894
19812
  });
19895
- else {
19896
- let description = "";
19897
- if (videoTrack.label.length > 0) description = description.concat(videoTrack.label);
19898
- description = description.concat(` with enabled=${videoTrack.enabled}, muted=${videoTrack.muted}, readyState=${videoTrack.readyState}`);
19899
- this.options.logger.debug(`UserMedia: ${videoTrack.kind} detected. ${description}`);
19900
- }
19901
- else throw error_createError({
19902
- message: "Webcam is disabled",
19903
- explanation: "The video track seems to be disabled. Enable it in your system.",
19904
- options: this.options
19813
+ nativeSocket.addEventListener("error", (event)=>{
19814
+ this.lastSocketError = error_getEventDetails(event);
19905
19815
  });
19906
- else this.options.logger.debug("UserMedia: detected (but no video tracks exist");
19907
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.addEventListener("loadedmetadata", onLoadedMetaData);
19908
- null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.addEventListener("play", onPlay);
19909
- null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) || _this_rawVisualUserMedia2.removeEventListener("error", this.handleMediaError);
19910
- null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) || _this_rawVisualUserMedia3.addEventListener("error", this.handleMediaError);
19911
- this.setVisualStream(localMediaStream);
19912
- play();
19913
19816
  } catch (exc) {
19914
- unloadAllEventListeners();
19915
- endedEarlyCallback(exc);
19817
+ this.connecting = this.connected = false;
19818
+ const diagnostic = error_getWebSocketDiagnostic(url2Connect);
19819
+ const message = diagnostic.looksAutomated ? "Automated crawler: WebSocket not supported in this environment" : `Failed to construct WebSocket to ${url2Connect}`;
19820
+ const explanation = diagnostic.looksAutomated ? "Headless crawlers cannot use Videomail's WebSocket. No action needed." : `Please check your connection and try again. If the problem persists, contact us. Diagnostic: ${diagnostic.text}`;
19821
+ const err = error_createError({
19822
+ message,
19823
+ explanation,
19824
+ options: this.options,
19825
+ exc
19826
+ });
19827
+ this.emit("ERROR", {
19828
+ err
19829
+ });
19830
+ return;
19916
19831
  }
19917
- }
19918
- getVideoTrackLabel() {
19919
- return this.videoTrackLabel;
19920
- }
19921
- isReady() {
19922
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
19923
- return Boolean((null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.srcObject) || (null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.currentSrc));
19924
- }
19925
- stop(visualStream, params) {
19926
19832
  try {
19927
- let chosenStream = visualStream;
19928
- if (!(null == params ? void 0 : params.aboutToInitialize)) {
19929
- var _this_clearInitializationListeners, _this, _this_audioRecorder;
19930
- this.initializationId++;
19931
- null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
19932
- null != chosenStream || (chosenStream = this.currentVisualStream);
19933
- const tracks = null == chosenStream ? void 0 : chosenStream.getTracks();
19934
- if (tracks) tracks.forEach((track)=>{
19935
- track.stop();
19936
- });
19937
- this.setVisualStream();
19938
- null == (_this_audioRecorder = this.audioRecorder) || _this_audioRecorder.stop();
19939
- this.audioRecorder = void 0;
19940
- }
19941
- if (!(null == params ? void 0 : params.preserveRecordingState)) this.paused = this.recording = false;
19833
+ this.stream = stream_default()(nativeSocket);
19942
19834
  } catch (exc) {
19943
- this.emit("ERROR", {
19835
+ this.connecting = this.connected = false;
19836
+ const err = error_createError({
19837
+ message: `Failed to create a stream to ${url2Connect}`,
19838
+ explanation: "Please check your connection and try again. If the problem persists, contact us.",
19839
+ options: this.options,
19944
19840
  exc
19945
19841
  });
19842
+ this.emit("ERROR", {
19843
+ err
19844
+ });
19946
19845
  }
19947
- }
19948
- createCanvas() {
19949
- const canvas = document.createElement("canvas");
19950
- const rawWidthDimension = this.getRawWidth(true);
19951
- if (rawWidthDimension.value) canvas.width = rawWidthDimension.value;
19952
- const rawHeight = this.getRawHeight(true);
19953
- if (rawHeight) canvas.height = rawHeight;
19954
- return canvas;
19955
- }
19956
- getVideoHeight() {
19957
- if (!this.rawVisualUserMedia) return;
19958
- return this.rawVisualUserMedia.videoHeight || this.rawVisualUserMedia.height;
19959
- }
19960
- getVideoWidth() {
19961
- if (!this.rawVisualUserMedia) return;
19962
- return this.rawVisualUserMedia.videoWidth || this.rawVisualUserMedia.width;
19963
- }
19964
- hasVideoWidth() {
19965
- const videoWidth = this.getVideoWidth();
19966
- return videoWidth && videoWidth > 0;
19967
- }
19968
- getRawWidth(responsive) {
19969
- let rawWidth;
19970
- const widthDimension = {
19971
- unit: "px"
19972
- };
19973
- rawWidth = responsive ? this.getVideoWidth() : this.options.video.width ? this.options.video.width : this.getVideoWidth();
19974
- if (responsive) {
19975
- const widthDimension = this.recorder.limitWidth(rawWidth);
19976
- rawWidth = null == widthDimension ? void 0 : widthDimension.value;
19977
- }
19978
- widthDimension.value = rawWidth;
19979
- return widthDimension;
19980
- }
19981
- getRawHeight(responsive) {
19982
- let rawHeight;
19983
- if (responsive) {
19984
- rawHeight = this.options.video.height || this.getVideoHeight();
19985
- const ratio = this.recorder.getRatio();
19986
- const rawWidth = this.getRawWidth(responsive);
19987
- if (void 0 !== ratio && rawWidth.value) rawHeight = rawWidth.value * ratio;
19988
- } else rawHeight = this.options.video.height ? this.options.video.height : this.getVideoHeight();
19989
- if (responsive) {
19990
- const heightDimension = this.recorder.limitHeight(rawHeight);
19991
- rawHeight = heightDimension.value;
19992
- }
19993
- return rawHeight;
19994
- }
19995
- getRawVisuals() {
19996
- return this.rawVisualUserMedia;
19997
- }
19998
- pause() {
19999
- this.paused = true;
20000
- }
20001
- isPaused() {
20002
- return this.paused;
20003
- }
20004
- resume() {
20005
- this.paused = false;
20006
- }
20007
- record() {
20008
- this.recording = true;
20009
- }
20010
- isRecording() {
20011
- return this.recording;
20012
- }
20013
- getAudioSampleRate() {
20014
- if (this.audioRecorder) return this.audioRecorder.getSampleRate();
20015
- return -1;
20016
- }
20017
- getCharacteristics() {
20018
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3, _this_rawVisualUserMedia4;
20019
- return {
20020
- audioSampleRate: this.getAudioSampleRate(),
20021
- muted: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.muted,
20022
- width: null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.width,
20023
- height: null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia2.height,
20024
- videoWidth: null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia3.videoWidth,
20025
- videoHeight: null == (_this_rawVisualUserMedia4 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia4.videoHeight
20026
- };
20027
- }
20028
- constructor(recorder, options){
20029
- 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)=>{
20030
- var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
20031
- this.logEvent(e.type, {
20032
- readyState: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState
19846
+ const stream = this.stream;
19847
+ if (stream) {
19848
+ const connectionTimeoutMs = this.options.timeouts.connection;
19849
+ this.connectionTimeout = window.setTimeout(()=>{
19850
+ if (this.isOnline() && this.isUserMediaLoaded()) {
19851
+ this.options.logger.debug(`${PIPE_SYMBOL}Reconnecting due to connection timeout.`);
19852
+ this.reconnecting = true;
19853
+ this.handleConnectionFailure({
19854
+ url2Connect,
19855
+ cause: "timeout"
19856
+ });
19857
+ } else this.handleConnectionFailure({
19858
+ url2Connect,
19859
+ cause: "timeout"
19860
+ });
19861
+ }, connectionTimeoutMs);
19862
+ stream.on("close", ()=>{
19863
+ this.handleStreamClose(url2Connect);
20033
19864
  });
20034
- null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.removeEventListener(e.type, this.outputEvent);
20035
- }), userMedia_define_property(this, "handleMediaError", (event)=>{
20036
- this.options.logger.warn(`Caught video element error event: ${string_pretty(event)}`);
20037
- });
20038
- this.recorder = recorder;
20039
- this.rawVisualUserMedia = recorder.getRawVisualUserMedia();
20040
- mediaEvents.forEach((eventName)=>{
20041
- var _this_rawVisualUserMedia;
20042
- null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.addEventListener(eventName, this.outputEvent, false);
20043
- });
20044
- }
20045
- }
20046
- const visuals_userMedia = UserMedia;
20047
- var Buffer = __webpack_require__("dY").Buffer;
20048
- function recorder_define_property(obj, key, value) {
20049
- if (key in obj) Object.defineProperty(obj, key, {
20050
- value: value,
20051
- enumerable: true,
20052
- configurable: true,
20053
- writable: true
20054
- });
20055
- else obj[key] = value;
20056
- return obj;
20057
- }
20058
- function recorder_object_spread(target) {
20059
- for(var i = 1; i < arguments.length; i++){
20060
- var source = null != arguments[i] ? arguments[i] : {};
20061
- var ownKeys = Object.keys(source);
20062
- if ("function" == typeof Object.getOwnPropertySymbols) ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20063
- return Object.getOwnPropertyDescriptor(source, sym).enumerable;
20064
- }));
20065
- ownKeys.forEach(function(key) {
20066
- recorder_define_property(target, key, source[key]);
20067
- });
20068
- }
20069
- return target;
20070
- }
20071
- const PIPE_SYMBOL = "°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ ";
20072
- class Recorder extends util_Despot {
20073
- hasExceededWebSocketSendBuffer(buffer, opts) {
20074
- return buffer.length > 0 && !this.pendingWrites.enqueue(buffer, opts);
20075
- }
20076
- writeStream(buffer, opts) {
20077
- if (this.stream) if (this.stream.destroyed) {
20078
- this.stopPings();
20079
- const err = error_createError({
20080
- message: "Already disconnected",
20081
- explanation: "Sorry, connection to the server has been destroyed. Please reload.",
20082
- options: this.options
19865
+ stream.on("connect", (args)=>{
19866
+ var _this_stream;
19867
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
19868
+ this.clearConnectionTimeout();
19869
+ const isClosing = (null == (_this_stream = this.stream) ? void 0 : _this_stream.socket.readyState) === WebSocket.CLOSING;
19870
+ if (!this.connected && !isClosing && !this.unloaded) {
19871
+ this.connected = true;
19872
+ this.connecting = this.unloaded = false;
19873
+ this.reconnecting = false;
19874
+ this.reconnectAttempts = 0;
19875
+ this.clearReconnectTimeout();
19876
+ this.emit("CONNECTED");
19877
+ null == cb || cb();
19878
+ }
20083
19879
  });
20084
- this.handleWriteFailure(err);
20085
- } else if (this.streamBackpressured) {
20086
- if (this.hasExceededWebSocketSendBuffer(buffer, opts)) this.failMediaTransmission(error_createError({
20087
- message: "WebSocket send buffer limit exceeded",
20088
- explanation: "The connection to the server is too slow to keep up with this recording. The recording has been stopped; please start a new one.",
20089
- options: this.options
20090
- }));
20091
- } else this.writeToStream(this.stream, buffer, opts);
20092
- }
20093
- writeToStream(stream, buffer, opts) {
20094
- const onFlushedCallback = null == opts ? void 0 : opts.onFlushedCallback;
20095
- try {
20096
- const canContinue = stream.write(buffer, ()=>{
20097
- if (!onFlushedCallback) return;
19880
+ stream.on("data", (data)=>{
19881
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
20098
19882
  try {
20099
- onFlushedCallback(opts);
19883
+ const command = JSON.parse(data.toString());
19884
+ this.executeCommand(command);
20100
19885
  } catch (exc) {
19886
+ this.options.logger.error(`Failed to parse command: ${exc}`);
20101
19887
  const err = error_createError({
20102
- message: "Failed to write stream buffer",
20103
- explanation: `stream.write() failed because of ${string_pretty(exc)}`,
19888
+ message: "Invalid server command",
19889
+ explanation: `Contact us. The invalid command was: ${data.toString()}.`,
20104
19890
  options: this.options,
20105
19891
  exc
20106
19892
  });
@@ -20109,204 +19895,404 @@ var __webpack_exports__ = {};
20109
19895
  });
20110
19896
  }
20111
19897
  });
20112
- if (!canContinue) {
20113
- this.streamBackpressured = true;
20114
- this.startBackpressureTimeout();
20115
- this.pauseForBackpressure();
20116
- return false;
20117
- }
20118
- return true;
20119
- } catch (exc) {
20120
- const err = error_createError({
20121
- message: "Failed writing to server",
20122
- explanation: `stream.write() failed because of ${string_pretty(exc)}`,
20123
- options: this.options,
20124
- exc
19898
+ stream.on("error", (err)=>{
19899
+ this.handleStreamError(url2Connect, err);
19900
+ });
19901
+ stream.on("drain", ()=>{
19902
+ this.handleStreamDrain(stream);
19903
+ });
19904
+ stream.on("preend", (args)=>{
19905
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
19906
+ });
19907
+ stream.on("end", ()=>{
19908
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *end* event emitted`);
19909
+ });
19910
+ stream.on("pipe", (src)=>{
19911
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *pipe* event emitted with src: ${string_pretty(src)}`);
19912
+ });
19913
+ stream.on("unpipe", (src)=>{
19914
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *unpipe* event emitted with src: ${string_pretty(src)}`);
19915
+ });
19916
+ stream.on("resume", (args)=>{
19917
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *resume* event emitted with args: ${string_pretty(args)}`);
19918
+ });
19919
+ stream.on("uncork", (args)=>{
19920
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *uncork* event emitted with args: ${string_pretty(args)}`);
19921
+ });
19922
+ stream.on("readable", (args)=>{
19923
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *readable* event emitted with args: ${string_pretty(args)}`);
19924
+ });
19925
+ stream.on("prefinish", (args)=>{
19926
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *prefinish* event emitted with args: ${string_pretty(args)}`);
19927
+ });
19928
+ stream.on("finish", (args)=>{
19929
+ this.options.logger.debug(`${PIPE_SYMBOL}Stream *finish* event emitted with args: ${string_pretty(args)}`);
20125
19930
  });
20126
- this.handleWriteFailure(err);
20127
- return false;
20128
19931
  }
20129
19932
  }
20130
- flushPendingWrites(stream) {
20131
- if (this.isStaleStream(stream)) return;
20132
- this.streamBackpressured = false;
20133
- this.clearBackpressureTimeout();
20134
- let pendingWrite = this.pendingWrites.dequeue();
20135
- while(pendingWrite){
20136
- if (!this.writeToStream(stream, pendingWrite.buffer, pendingWrite.params)) return;
20137
- pendingWrite = this.pendingWrites.dequeue();
19933
+ writeCommand(command, args, cb) {
19934
+ if (this.connected) {
19935
+ if (this.stream) {
19936
+ if (args) this.options.logger.debug(`$ ${command} with ${string_pretty(args)}`);
19937
+ else this.options.logger.debug(`$ ${command}`);
19938
+ const commandObj = {
19939
+ command,
19940
+ args
19941
+ };
19942
+ this.writeStream(Buffer.from(JSON.stringify(commandObj)), cb ? {
19943
+ onFlushedCallback: ()=>{
19944
+ queueMicrotask(cb);
19945
+ }
19946
+ } : void 0);
19947
+ }
19948
+ } else {
19949
+ this.options.logger.debug(`Reconnecting for the command ${command} …`);
19950
+ this.initSocket(()=>{
19951
+ this.writeCommand(command, args, cb);
19952
+ });
20138
19953
  }
20139
- this.resumeRecordingAfterDrain();
20140
- }
20141
- isStaleStream(stream) {
20142
- return this.stream !== stream || stream.destroyed;
20143
- }
20144
- canPauseForBackpressure() {
20145
- var _this_userMedia, _this_loop;
20146
- return Boolean(this.recordingActive && !(null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isPaused()) && (null == (_this_loop = this.loop) ? void 0 : _this_loop.isRunning()));
20147
19954
  }
20148
- pauseForBackpressure() {
20149
- if (!this.canPauseForBackpressure()) return;
20150
- this.resumeAfterDrain = true;
20151
- this.loop.stop();
19955
+ constructor(options){
19956
+ super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0);
20152
19957
  }
20153
- resumeRecordingAfterDrain() {
20154
- if (!this.resumeAfterDrain) return;
20155
- this.resumeAfterDrain = false;
20156
- if (this.canResumeAfterDrain()) {
20157
- var _this_loop;
20158
- null == (_this_loop = this.loop) || _this_loop.start();
19958
+ }
19959
+ const connection = RecorderConnection;
19960
+ function createUserMediaConstraints({ options, facingMode, limitedWidth, userMediaParams }) {
19961
+ const video = {
19962
+ frameRate: {
19963
+ ideal: options.video.fps
20159
19964
  }
19965
+ };
19966
+ if ((null == userMediaParams ? void 0 : userMediaParams.switchingFacingMode) || (null == userMediaParams ? void 0 : userMediaParams.recovering)) video.facingMode = userMediaParams.switchingFacingMode || facingMode;
19967
+ if (options.video.width) video.width = {
19968
+ ideal: options.video.width
19969
+ };
19970
+ else if (null == limitedWidth ? void 0 : limitedWidth.value) video.width = {
19971
+ ideal: limitedWidth.value
19972
+ };
19973
+ if (options.video.height) video.height = {
19974
+ ideal: options.video.height
19975
+ };
19976
+ return {
19977
+ audio: isAudioEnabled(options),
19978
+ video
19979
+ };
19980
+ }
19981
+ const mediaConstraints = createUserMediaConstraints;
19982
+ function media_define_property(obj, key, value) {
19983
+ if (key in obj) Object.defineProperty(obj, key, {
19984
+ value: value,
19985
+ enumerable: true,
19986
+ configurable: true,
19987
+ writable: true
19988
+ });
19989
+ else obj[key] = value;
19990
+ return obj;
19991
+ }
19992
+ class RecorderMedia extends connection {
19993
+ show() {
19994
+ html_showElement(this.recorderElement);
20160
19995
  }
20161
- canResumeAfterDrain() {
20162
- var _this_userMedia;
20163
- return this.recordingActive && !(null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isPaused());
20164
- }
20165
- handleWriteFailure(err) {
20166
- if (this.mediaTransmissionStarted) return void this.failMediaTransmission(err);
20167
- this.emit("ERROR", {
20168
- err
20169
- });
20170
- }
20171
- failMediaTransmission(err) {
20172
- if (this.recordingTransportFailed) return;
20173
- this.recordingTransportFailed = true;
20174
- this.recordingActive = false;
20175
- this.mediaTransmissionStarted = false;
20176
- this.streamBackpressured = false;
20177
- this.resumeAfterDrain = false;
20178
- this.pendingWrites.clear();
20179
- this.connected = false;
20180
- this.connecting = false;
20181
- this.reconnecting = false;
20182
- this.clearReconnectTimeout();
20183
- this.clearBackpressureTimeout();
20184
- this.discardSocket();
20185
- this.reset();
20186
- this.framesCount = this.samplesCount = 0;
20187
- this.confirmedFrameNumber = this.confirmedSampleNumber = 0;
20188
- delete this.recordingStats;
20189
- this.recordingDimensions = void 0;
20190
- this.emit("ERROR", {
20191
- err
20192
- });
19996
+ clearRetryTimeout() {
19997
+ if (void 0 === this.retryTimeout) return;
19998
+ this.options.logger.debug("Recorder: clearRetryTimeout()");
19999
+ window.clearTimeout(this.retryTimeout);
20000
+ this.retryTimeout = void 0;
20193
20001
  }
20194
- startBackpressureTimeout() {
20195
- if (!this.shouldStartBackpressureTimeout()) return;
20196
- this.backpressureTimeout = window.setTimeout(()=>{
20197
- this.backpressureTimeout = void 0;
20198
- if (this.shouldIgnoreBackpressureTimeout()) return;
20199
- const err = error_createError({
20200
- message: "WebSocket write timed out",
20201
- explanation: "The server stopped accepting recording data. The recording has been stopped; please check your connection and start a new one.",
20002
+ showUserMedia() {
20003
+ if (!this.connected) return false;
20004
+ const hidden = this.isHidden();
20005
+ if (!hidden) return true;
20006
+ const notifying = this.isNotifying();
20007
+ if (notifying) return true;
20008
+ return this.blocking;
20009
+ }
20010
+ userMediaErrorCallback(err, usedConstraints) {
20011
+ var _this_userMedia;
20012
+ if (this.cameraRecovery) return void this.failCameraRecovery(err);
20013
+ if (this.unloaded) return void this.options.logger.debug(`Recorder: already unloaded. Not going to throw error ${string_pretty(err)}`);
20014
+ this.userMediaLoading = false;
20015
+ this.clearUserMediaTimeout();
20016
+ const characteristics = null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.getCharacteristics();
20017
+ this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
20018
+ const errorListeners = this.getListeners("ERROR");
20019
+ if (null == errorListeners ? void 0 : errorListeners.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
20020
+ const videomailError = error_createError({
20021
+ err,
20022
+ options: this.options,
20023
+ usedConstraints
20024
+ });
20025
+ this.emit("ERROR", {
20026
+ err: videomailError
20027
+ });
20028
+ } else {
20029
+ this.options.logger.debug(`Recorder: ignore user media error ${string_pretty(err)}`);
20030
+ this.retryTimeout = window.setTimeout(()=>{
20031
+ this.retryTimeout = void 0;
20032
+ if (!this.unloaded) this.loadUserMedia();
20033
+ }, this.options.timeouts.userMedia);
20034
+ }
20035
+ else {
20036
+ this.options.logger.debug(`Recorder: no error listeners attached but throwing error ${string_pretty(err)}`);
20037
+ throw error_createError({
20038
+ err,
20039
+ message: "Unable to process this error since there are no error listeners anymore.",
20202
20040
  options: this.options
20203
20041
  });
20204
- this.failMediaTransmission(err);
20205
- }, this.options.timeouts.connection);
20206
- }
20207
- shouldStartBackpressureTimeout() {
20208
- return void 0 === this.backpressureTimeout && this.mediaTransmissionStarted;
20042
+ }
20209
20043
  }
20210
- shouldIgnoreBackpressureTimeout() {
20211
- return !this.streamBackpressured || this.recordingTransportFailed;
20044
+ getUserMediaCallback(localStream, params, requestId = this.mediaRequestId) {
20045
+ if (!this.userMedia) {
20046
+ localStream.getTracks().forEach((track)=>{
20047
+ track.stop();
20048
+ });
20049
+ throw error_createError({
20050
+ message: "No user media is defined",
20051
+ options: this.options
20052
+ });
20053
+ }
20054
+ this.options.logger.debug(`Recorder: getUserMediaCallback(${params ? string_pretty(params) : ""})`);
20055
+ if (this.showUserMedia()) try {
20056
+ this.userMedia.init(localStream, ()=>{
20057
+ if (!this.unloaded && requestId === this.mediaRequestId) this.onUserMediaReady(params);
20058
+ }, this.onAudioSample.bind(this), (err)=>{
20059
+ if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(err, {
20060
+ audio: isAudioEnabled(this.options)
20061
+ });
20062
+ }, params);
20063
+ } catch (exc) {
20064
+ localStream.getTracks().forEach((track)=>{
20065
+ track.stop();
20066
+ });
20067
+ this.userMediaLoading = false;
20068
+ this.clearUserMediaTimeout();
20069
+ this.userMediaErrorCallback(exc, {
20070
+ audio: isAudioEnabled(this.options)
20071
+ });
20072
+ }
20073
+ else {
20074
+ localStream.getTracks().forEach((track)=>{
20075
+ track.stop();
20076
+ });
20077
+ this.userMediaLoading = false;
20078
+ this.clearUserMediaTimeout();
20079
+ this.cancelCameraRecovery();
20080
+ }
20212
20081
  }
20213
- failInterruptedRecording() {
20214
- const unconfirmedFrames = Math.max(0, this.framesCount - this.confirmedFrameNumber);
20215
- const unconfirmedSamples = Math.max(0, this.samplesCount - this.confirmedSampleNumber);
20216
- const err = error_createError({
20217
- message: "Recording interrupted",
20218
- explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
20082
+ loadGenuineUserMedia(params) {
20083
+ this.options.logger.debug(`Recorder: loadGenuineUserMedia(${params ? string_pretty(params) : ""})`);
20084
+ this.emit("ASKING_WEBCAM_PERMISSION");
20085
+ this.userMediaLoading = true;
20086
+ const requestId = ++this.mediaRequestId;
20087
+ const limitedWidth = this.options.video.width ? void 0 : this.visuals.limitWidth();
20088
+ const constraints = mediaConstraints({
20089
+ facingMode: this.facingMode,
20090
+ limitedWidth,
20219
20091
  options: this.options,
20220
- cause: {
20221
- unconfirmedFrames,
20222
- unconfirmedSamples
20223
- }
20092
+ userMediaParams: params
20093
+ });
20094
+ this.options.logger.debug(string_summarize("Recorder: our webcam constraints are:", constraints));
20095
+ this.options.logger.debug(string_summarize("Recorder: available webcam constraints are:", navigator.mediaDevices.getSupportedConstraints()));
20096
+ const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
20097
+ streamPromise.then((localStream)=>{
20098
+ if (this.unloaded || requestId !== this.mediaRequestId) return void localStream.getTracks().forEach((track)=>{
20099
+ track.stop();
20100
+ });
20101
+ this.getUserMediaCallback(localStream, params, requestId);
20102
+ }).catch((reason)=>{
20103
+ if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(reason, constraints);
20224
20104
  });
20225
- this.failMediaTransmission(err);
20226
20105
  }
20227
- handleStreamDrain(stream) {
20228
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *drain* event emitted`);
20229
- this.flushPendingWrites(stream);
20106
+ loadUserMedia(params) {
20107
+ if (this.userMediaLoaded) {
20108
+ this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already loaded");
20109
+ this.onUserMediaReady(params);
20110
+ return;
20111
+ } else if (this.userMediaLoading) return void this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already asking for permission");
20112
+ this.options.logger.debug(`Recorder: loadUserMedia(${params ? string_pretty(params) : ""})`);
20113
+ this.emit("LOADING_USER_MEDIA");
20114
+ try {
20115
+ if (!navigator.mediaDevices) throw new Error("No media devices are available.");
20116
+ this.userMediaTimeout = window.setTimeout(()=>{
20117
+ if (!this.userMediaLoaded) {
20118
+ this.userMediaLoading = false;
20119
+ const err = util_getBrowser(this.options).getNoAccessIssue();
20120
+ this.userMediaErrorCallback(err, {
20121
+ audio: isAudioEnabled(this.options)
20122
+ });
20123
+ }
20124
+ }, this.options.timeouts.userMedia);
20125
+ this.userMediaLoading = true;
20126
+ this.loadGenuineUserMedia(params);
20127
+ } catch (exc) {
20128
+ this.clearUserMediaTimeout();
20129
+ this.options.logger.debug("Recorder: failed to load genuine user media");
20130
+ this.userMediaLoading = false;
20131
+ if (this.cameraRecovery) return void this.failCameraRecovery(exc);
20132
+ const errorListeners = this.getListeners("ERROR");
20133
+ if (null == errorListeners ? void 0 : errorListeners.length) this.emit("ERROR", {
20134
+ exc
20135
+ });
20136
+ else {
20137
+ this.options.logger.debug("Recorder: no error listeners attached but throwing exception further");
20138
+ throw exc;
20139
+ }
20140
+ }
20230
20141
  }
20231
- canReconnectAfterStreamClose() {
20232
- return Boolean(this.connected && this.userMediaLoaded);
20142
+ isNotifying() {
20143
+ return this.visuals.isNotifying();
20233
20144
  }
20234
- shouldReportStreamClose() {
20235
- return !this.connecting && !this.blocking;
20145
+ isHidden() {
20146
+ return !this.recorderElement || html_isHidden(this.recorderElement);
20236
20147
  }
20237
- handleStreamClose(url2Connect) {
20238
- const debugLine = string_summarize(`${PIPE_SYMBOL}Stream has closed:`, {
20239
- connecting: this.connecting,
20240
- connected: this.connected,
20241
- userMediaLoaded: this.userMediaLoaded,
20242
- blocking: this.blocking
20243
- });
20244
- this.options.logger.debug(debugLine);
20245
- const canReconnect = this.canReconnectAfterStreamClose();
20246
- this.connected = false;
20247
- this.stopPings();
20248
- if (this.shouldIgnoreStreamClose()) return;
20249
- if (this.mediaTransmissionStarted) this.failInterruptedRecording();
20250
- else if (canReconnect) {
20251
- this.pendingWrites.clear();
20252
- this.streamBackpressured = false;
20253
- this.resumeAfterDrain = false;
20254
- this.reconnecting = true;
20255
- this.initSocket();
20256
- } else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
20257
- this.handleConnectionFailure({
20258
- url2Connect,
20259
- cause: "closed"
20260
- });
20261
- }, 0);
20148
+ clearUserMediaTimeout() {
20149
+ if (void 0 !== this.userMediaTimeout) {
20150
+ this.options.logger.debug("Recorder: clearUserMediaTimeout()");
20151
+ window.clearTimeout(this.userMediaTimeout);
20152
+ this.userMediaTimeout = void 0;
20153
+ }
20262
20154
  }
20263
- shouldIgnoreStreamClose() {
20264
- return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
20155
+ clearStopTimeout() {
20156
+ if (void 0 === this.stopTimeout) return;
20157
+ window.clearTimeout(this.stopTimeout);
20158
+ this.stopTimeout = void 0;
20265
20159
  }
20266
- handleStreamError(url2Connect, err) {
20267
- var _this_lastSocketError;
20268
- if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
20269
- else this.lastSocketError = serializeError(err);
20270
- const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
20271
- error: serializeError(err)
20160
+ isReady() {
20161
+ var _this_userMedia;
20162
+ return null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isReady();
20163
+ }
20164
+ finishCameraRecovery(restored) {
20165
+ this.clearUserMediaTimeout();
20166
+ const resolve = this.resolveCameraRecovery;
20167
+ const resumeRequested = restored && this.resumeAfterCameraRecovery;
20168
+ this.cameraRecovery = void 0;
20169
+ this.resolveCameraRecovery = void 0;
20170
+ this.resumeAfterCameraRecovery = false;
20171
+ null == resolve || resolve(restored);
20172
+ if (resumeRequested && this.isPaused()) this.resume();
20173
+ }
20174
+ cancelCameraRecovery() {
20175
+ var _this_userMedia;
20176
+ if (!this.cameraRecovery) return;
20177
+ this.mediaRequestId++;
20178
+ this.userMediaLoading = false;
20179
+ this.userMediaLoaded = false;
20180
+ this.clearUserMediaTimeout();
20181
+ null == (_this_userMedia = this.userMedia) || _this_userMedia.stop(void 0, {
20182
+ preserveRecordingState: true
20272
20183
  });
20273
- this.options.logger.debug(debugLine);
20274
- if (this.validate()) {
20275
- this.connected = false;
20276
- this.stopPings();
20277
- this.discardSocket();
20278
- return;
20279
- }
20280
- if (!this.connected) return void this.handleConnectionFailure({
20281
- url2Connect,
20282
- cause: "error"
20184
+ this.finishCameraRecovery(false);
20185
+ }
20186
+ failCameraRecovery(exc) {
20187
+ this.cancelCameraRecovery();
20188
+ const err = error_createError({
20189
+ message: "Camera recovery failed",
20190
+ explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
20191
+ exc,
20192
+ options: this.options
20283
20193
  });
20284
- const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
20285
- cause: null != (_this_lastSocketError = this.lastSocketError) ? _this_lastSocketError : err
20194
+ this.emit("CAMERA_RECOVERY_FAILED", {
20195
+ err
20286
20196
  });
20287
- if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
20288
- this.emit("ERROR", {
20289
- err: error_createError({
20290
- message: "WebSocket stream error",
20291
- explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
20292
- options: this.options,
20293
- exc: streamError
20294
- })
20197
+ }
20198
+ recoverCamera() {
20199
+ if (this.cameraRecovery) return this.cameraRecovery;
20200
+ if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
20201
+ const liveStream = this.userMedia.hasLiveStream();
20202
+ if (liveStream && this.isRecording()) return Promise.resolve(true);
20203
+ if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
20204
+ const recovery = new Promise((resolve)=>{
20205
+ this.resolveCameraRecovery = resolve;
20295
20206
  });
20207
+ this.cameraRecovery = recovery;
20208
+ this.emit("CAMERA_RECOVERING");
20209
+ if (liveStream) {
20210
+ this.userMediaTimeout = window.setTimeout(()=>{
20211
+ this.failCameraRecovery(new Error("Camera playback recovery timed out."));
20212
+ }, this.options.timeouts.userMedia);
20213
+ this.userMedia.restorePlayback().then(()=>{
20214
+ if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
20215
+ }).catch((exc)=>{
20216
+ if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
20217
+ });
20218
+ } else {
20219
+ this.userMediaLoaded = false;
20220
+ this.loadUserMedia({
20221
+ recovering: true
20222
+ });
20223
+ }
20224
+ return recovery;
20296
20225
  }
20297
- sendPings() {
20298
- this.stopPings();
20299
- this.pingInterval = window.setInterval(()=>{
20300
- this.options.logger.debug("Recorder: pinging...");
20301
- this.writeStream(Buffer.from(""));
20302
- }, this.options.timeouts.pingInterval);
20226
+ switchFacingMode() {
20227
+ if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
20228
+ if ("user" === this.facingMode) this.facingMode = "environment";
20229
+ else if ("environment" === this.facingMode) this.facingMode = "user";
20230
+ else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
20231
+ this.loadGenuineUserMedia({
20232
+ switchingFacingMode: this.facingMode
20233
+ });
20303
20234
  }
20304
- stopPings() {
20305
- if (void 0 === this.pingInterval) return;
20306
- this.options.logger.debug("Recorder: stopping pings...");
20307
- window.clearInterval(this.pingInterval);
20308
- this.pingInterval = void 0;
20235
+ hide() {
20236
+ if (!this.isHidden()) {
20237
+ if (this.recorderElement) html_hideElement(this.recorderElement);
20238
+ this.clearUserMediaTimeout();
20239
+ this.clearRetryTimeout();
20240
+ }
20241
+ }
20242
+ isUnloaded() {
20243
+ return this.unloaded;
20244
+ }
20245
+ isUserMediaLoaded() {
20246
+ return this.userMediaLoaded;
20247
+ }
20248
+ constructor(visuals, options){
20249
+ super(options), media_define_property(this, "visuals", void 0), media_define_property(this, "recorderElement", void 0), media_define_property(this, "userMedia", void 0), media_define_property(this, "userMediaTimeout", void 0), media_define_property(this, "retryTimeout", void 0), media_define_property(this, "stopTimeout", void 0), media_define_property(this, "mediaRequestId", 0), media_define_property(this, "cameraRecovery", void 0), media_define_property(this, "resolveCameraRecovery", void 0), media_define_property(this, "resumeAfterCameraRecovery", false), media_define_property(this, "submitting", false), media_define_property(this, "built", false), media_define_property(this, "key", void 0), media_define_property(this, "facingMode", void 0);
20250
+ this.visuals = visuals;
20251
+ this.facingMode = options.video.facingMode;
20252
+ }
20253
+ }
20254
+ const media = RecorderMedia;
20255
+ function calculateProgress(confirmedCount, totalCount) {
20256
+ return `${(confirmedCount / (totalCount || 1) * 100).toFixed(2)}%`;
20257
+ }
20258
+ function getConfirmedProgressNumber({ args, kind, options }) {
20259
+ const label = "frame" === kind ? "frame" : "audio sample";
20260
+ if (!args) throw error_createError({
20261
+ message: `Arguments are missing for updating the ${label} progress`,
20262
+ options
20263
+ });
20264
+ const confirmedNumber = args[kind];
20265
+ if (!confirmedNumber) throw error_createError({
20266
+ message: `The ${label} number is missing`,
20267
+ options
20268
+ });
20269
+ return confirmedNumber;
20270
+ }
20271
+ var capture_Buffer = __webpack_require__("dY").Buffer;
20272
+ function capture_define_property(obj, key, value) {
20273
+ if (key in obj) Object.defineProperty(obj, key, {
20274
+ value: value,
20275
+ enumerable: true,
20276
+ configurable: true,
20277
+ writable: true
20278
+ });
20279
+ else obj[key] = value;
20280
+ return obj;
20281
+ }
20282
+ function capture_object_spread(target) {
20283
+ for(var i = 1; i < arguments.length; i++){
20284
+ var source = null != arguments[i] ? arguments[i] : {};
20285
+ var ownKeys = Object.keys(source);
20286
+ if ("function" == typeof Object.getOwnPropertySymbols) ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20287
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
20288
+ }));
20289
+ ownKeys.forEach(function(key) {
20290
+ capture_define_property(target, key, source[key]);
20291
+ });
20309
20292
  }
20293
+ return target;
20294
+ }
20295
+ class RecorderCapture extends media {
20310
20296
  onAudioSample(audioSample) {
20311
20297
  var _this_userMedia;
20312
20298
  if (this.recordingTransportFailed || this.cameraRecovery) return;
@@ -20317,9 +20303,6 @@ var __webpack_exports__ = {};
20317
20303
  const audioBuffer = audioSample.toBuffer();
20318
20304
  this.writeStream(audioBuffer);
20319
20305
  }
20320
- show() {
20321
- html_showElement(this.recorderElement);
20322
- }
20323
20306
  onUserMediaReady(params) {
20324
20307
  try {
20325
20308
  this.options.logger.debug(`Recorder: onUserMediaReady(${params ? string_pretty(params) : ""})`);
@@ -20349,885 +20332,1258 @@ var __webpack_exports__ = {};
20349
20332
  });
20350
20333
  }
20351
20334
  }
20352
- clearRetryTimeout() {
20353
- if (void 0 === this.retryTimeout) return;
20354
- this.options.logger.debug("Recorder: clearRetryTimeout()");
20355
- window.clearTimeout(this.retryTimeout);
20356
- this.retryTimeout = void 0;
20335
+ calculateFrameProgress() {
20336
+ return calculateProgress(this.confirmedFrameNumber, this.framesCount);
20357
20337
  }
20358
- clearConnectionTimeout() {
20359
- if (void 0 === this.connectionTimeout) return;
20360
- this.options.logger.debug("Recorder: clearConnectionTimeout()");
20361
- window.clearTimeout(this.connectionTimeout);
20362
- this.connectionTimeout = void 0;
20338
+ calculateSampleProgress() {
20339
+ return calculateProgress(this.confirmedSampleNumber, this.samplesCount);
20363
20340
  }
20364
- clearBackpressureTimeout() {
20365
- if (void 0 === this.backpressureTimeout) return;
20366
- window.clearTimeout(this.backpressureTimeout);
20367
- this.backpressureTimeout = void 0;
20341
+ updateOverallProgress() {
20342
+ this.frameProgress = this.calculateFrameProgress();
20343
+ if (isAudioEnabled(this.options)) this.sampleProgress = this.calculateSampleProgress();
20344
+ this.emit("PROGRESS", {
20345
+ frameProgress: this.frameProgress,
20346
+ sampleProgress: this.sampleProgress
20347
+ });
20368
20348
  }
20369
- clearReconnectTimeout() {
20370
- if (void 0 === this.reconnectTimeout) return;
20371
- this.options.logger.debug("Recorder: clearReconnectTimeout()");
20372
- window.clearTimeout(this.reconnectTimeout);
20373
- this.reconnectTimeout = void 0;
20349
+ updateFrameProgress(args) {
20350
+ this.confirmedFrameNumber = getConfirmedProgressNumber({
20351
+ args,
20352
+ kind: "frame",
20353
+ options: this.options
20354
+ });
20355
+ this.frameProgress = this.calculateFrameProgress();
20356
+ this.updateOverallProgress();
20374
20357
  }
20375
- handleConnectionFailure(params) {
20376
- if (this.retryConnection(params)) return;
20377
- this.failConnection(params);
20358
+ updateSampleProgress(args) {
20359
+ this.confirmedSampleNumber = getConfirmedProgressNumber({
20360
+ args,
20361
+ kind: "sample",
20362
+ options: this.options
20363
+ });
20364
+ this.sampleProgress = this.calculateSampleProgress();
20365
+ this.updateOverallProgress();
20378
20366
  }
20379
- retryConnection(params) {
20380
- if (!this.canRetryConnection()) return false;
20381
- if (void 0 !== this.reconnectTimeout) return true;
20382
- const delayMs = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 10000);
20383
- this.reconnectAttempts++;
20384
- this.connecting = false;
20385
- this.clearConnectionTimeout();
20386
- this.discardSocket();
20387
- this.options.logger.debug(`Recorder: retrying WebSocket connection in ${delayMs}ms (cause ${params.cause}).`);
20388
- this.reconnectTimeout = window.setTimeout(()=>{
20389
- this.reconnectTimeout = void 0;
20390
- if (this.shouldContinueReconnect()) this.initSocket();
20391
- }, delayMs);
20392
- return true;
20367
+ preview(args) {
20368
+ var _args_duration;
20369
+ if (!args) throw error_createError({
20370
+ message: "Preview arguments are missing.",
20371
+ options: this.options
20372
+ });
20373
+ const hasAudio = this.samplesCount > 0;
20374
+ this.mediaTransmissionStarted = false;
20375
+ this.recordingActive = false;
20376
+ this.recordingTransportFailed = false;
20377
+ this.confirmedFrameNumber = this.confirmedSampleNumber = this.samplesCount = this.framesCount = 0;
20378
+ this.sampleProgress = this.frameProgress = void 0;
20379
+ this.key = args.key;
20380
+ if (args.mp4) this.replay.setMp4Source(`${args.mp4 + constants.WHITELIST_KEY_LABEL}/${this.options.whitelistKey}/videomail.mp4`, true);
20381
+ if (args.webm) this.replay.setWebMSource(`${args.webm + constants.WHITELIST_KEY_LABEL}/${this.options.whitelistKey}/videomail.webm`, true);
20382
+ this.hide();
20383
+ const widthDimension = this.getRecorderWidth(true);
20384
+ const heightDimension = this.getRecorderHeight(true);
20385
+ const duration = null != (_args_duration = args.duration) ? _args_duration : -1;
20386
+ this.emit("PREVIEW", {
20387
+ key: this.key,
20388
+ width: null == widthDimension ? void 0 : widthDimension.value,
20389
+ height: null == heightDimension ? void 0 : heightDimension.value,
20390
+ hasAudio,
20391
+ duration
20392
+ });
20393
+ if (this.stopTime) this.waitingTime = Date.now() - this.stopTime;
20394
+ null != this.recordingStats || (this.recordingStats = {});
20395
+ this.recordingStats.waitingTime = this.waitingTime;
20393
20396
  }
20394
- canRetryConnection() {
20395
- return Boolean(this.reconnecting && this.userMediaLoaded && this.isOnline() && !this.unloaded && !this.blocking);
20397
+ cancelAnimationFrame() {
20398
+ var _this_loop;
20399
+ null == (_this_loop = this.loop) || _this_loop.dispose();
20396
20400
  }
20397
- shouldContinueReconnect() {
20398
- return this.reconnecting && !this.unloaded && !this.blocking;
20401
+ getIntervalSum() {
20402
+ if (!this.loop) return;
20403
+ return this.loop.getElapsedTime();
20399
20404
  }
20400
- discardSocket() {
20401
- this.options.logger.debug("Recorder: discarding socket stream ...");
20402
- this.clearBackpressureTimeout();
20403
- this.pendingWrites.clear();
20404
- this.streamBackpressured = false;
20405
- this.resumeAfterDrain = false;
20406
- const stream = this.stream;
20407
- this.stream = void 0;
20408
- if (stream && !stream.destroyed) stream.destroy();
20405
+ getAvgInterval() {
20406
+ var _this_getIntervalSum;
20407
+ const intervalSum = null != (_this_getIntervalSum = this.getIntervalSum()) ? _this_getIntervalSum : 0;
20408
+ if (!this.hasValidInterval(intervalSum)) return;
20409
+ return intervalSum / this.framesCount;
20409
20410
  }
20410
- isOnline() {
20411
- return navigator.onLine;
20412
- }
20413
- failConnection(params) {
20414
- var _this_stream;
20415
- if (this.shouldIgnoreConnectionFailure()) return;
20416
- this.connectionFailed = true;
20417
- this.connecting = false;
20418
- this.reconnecting = false;
20419
- this.reconnectAttempts = 0;
20420
- this.clearReconnectTimeout();
20421
- this.clearConnectionTimeout();
20422
- const { url2Connect, cause } = params;
20423
- const online = this.isOnline();
20424
- const elapsedMs = this.connectingStartedAt ? Date.now() - this.connectingStartedAt : void 0;
20425
- const closeEvent = this.lastCloseEvent;
20426
- const socketError = this.lastSocketError;
20427
- const contents = {
20428
- cause,
20429
- closeCode: null == closeEvent ? void 0 : closeEvent.code,
20430
- closeReason: null == closeEvent ? void 0 : closeEvent.reason,
20431
- elapsedMs,
20432
- online,
20433
- secureContext: globalThis.isSecureContext,
20434
- socketReadyState: null == (_this_stream = this.stream) ? void 0 : _this_stream.socket.readyState,
20435
- timeoutMs: this.options.timeouts.connection,
20436
- url: url2Connect,
20437
- unloaded: this.unloaded,
20438
- userMediaLoaded: this.userMediaLoaded,
20439
- userMediaLoading: this.userMediaLoading,
20440
- wasClean: null == closeEvent ? void 0 : closeEvent.wasClean,
20441
- socketError,
20442
- blocking: this.blocking,
20443
- visibilityState: document.visibilityState
20444
- };
20445
- const debugLine = string_summarize("Recorder: failConnection() diagnostic", contents);
20446
- this.options.logger.debug(debugLine);
20447
- let explanation;
20448
- if (online) if ("timeout" === cause) explanation = `The server at ${url2Connect} did not respond within ${this.options.timeouts.connection}ms, even though your device is online. This usually points to a firewall or proxy silently dropping the connection. Please try a different network. If the problem persists, contact us.`;
20449
- else if ("error" === cause) explanation = `An error occurred while connecting to ${url2Connect}. Please check your internet connection and try again. If the problem persists, contact us.`;
20450
- else {
20451
- const closeSuffix = closeEvent ? ` (code ${closeEvent.code})` : "";
20452
- explanation = `Connection to ${url2Connect} is closed${closeSuffix}. Please check your internet connection and try again. If the problem persists, contact us.`;
20453
- }
20454
- else explanation = "Your device appears to be offline. Please check your internet connection and try again.";
20455
- this.discardSocket();
20456
- const err = error_createError({
20457
- message: "Unable to connect to the server",
20458
- explanation,
20459
- options: this.options,
20460
- cause: contents,
20461
- exc: socketError ? new Error("WebSocket connection error", {
20462
- cause: socketError
20463
- }) : void 0
20464
- });
20465
- this.emit("ERROR", {
20466
- err
20467
- });
20411
+ getAvgFps() {
20412
+ var _this_getIntervalSum;
20413
+ const intervalSum = null != (_this_getIntervalSum = this.getIntervalSum()) ? _this_getIntervalSum : 0;
20414
+ if (!this.hasValidInterval(intervalSum)) return;
20415
+ return this.framesCount / intervalSum * 1000;
20468
20416
  }
20469
- shouldIgnoreConnectionFailure() {
20470
- return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
20417
+ hasValidInterval(intervalSum) {
20418
+ return intervalSum > 0;
20471
20419
  }
20472
- calculateFrameProgress() {
20473
- return `${(this.confirmedFrameNumber / (this.framesCount || 1) * 100).toFixed(2)}%`;
20420
+ getRecordingStats() {
20421
+ return this.recordingStats;
20474
20422
  }
20475
- calculateSampleProgress() {
20476
- return `${(this.confirmedSampleNumber / (this.samplesCount || 1) * 100).toFixed(2)}%`;
20423
+ getAudioSampleRate() {
20424
+ var _this_recordingAudioSampleRate;
20425
+ var _this_userMedia;
20426
+ return null != (_this_recordingAudioSampleRate = this.recordingAudioSampleRate) ? _this_recordingAudioSampleRate : null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.getAudioSampleRate();
20477
20427
  }
20478
- updateOverallProgress() {
20479
- this.frameProgress = this.calculateFrameProgress();
20480
- if (isAudioEnabled(this.options)) this.sampleProgress = this.calculateSampleProgress();
20481
- this.emit("PROGRESS", {
20482
- frameProgress: this.frameProgress,
20483
- sampleProgress: this.sampleProgress
20428
+ stop(params) {
20429
+ var _this_loop_complete, _this_loop;
20430
+ this.cancelCameraRecovery();
20431
+ if (!this.userMedia) throw new Error("No user media defined, unable to stop");
20432
+ this.options.logger.debug(`Recorder: stop(${params ? string_pretty(params) : ""})`);
20433
+ this.recordingActive = false;
20434
+ const limitReached = null == params ? void 0 : params.limitReached;
20435
+ this.emit("STOPPING", {
20436
+ limitReached
20484
20437
  });
20485
- }
20486
- updateFrameProgress(args) {
20487
- if (args) {
20488
- if (!args.frame) throw error_createError({
20489
- message: "The frame number is missing",
20490
- options: this.options
20438
+ null == (_this_loop = this.loop) || null == (_this_loop_complete = _this_loop.complete) || _this_loop_complete.call(_this_loop);
20439
+ this.stopTimeout = window.setTimeout(()=>{
20440
+ this.stopTimeout = void 0;
20441
+ this.stopTime = Date.now();
20442
+ const videoType = this.replay.getVideoType();
20443
+ if (!videoType) throw new Error("Unable to video record when no video type is defined.");
20444
+ this.recordingStats = {
20445
+ avgFps: this.getAvgFps(),
20446
+ wantedFps: this.options.video.fps,
20447
+ avgInterval: this.getAvgInterval(),
20448
+ wantedInterval: 1e3 / this.options.video.fps,
20449
+ intervalSum: this.getIntervalSum(),
20450
+ framesCount: this.framesCount,
20451
+ videoType
20452
+ };
20453
+ if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
20454
+ if (isAudioEnabled(this.options) && this.userMedia) {
20455
+ var _this_recordingAudioSampleRate;
20456
+ this.recordingStats.samplesCount = this.samplesCount;
20457
+ this.recordingStats.sampleRate = null != (_this_recordingAudioSampleRate = this.recordingAudioSampleRate) ? _this_recordingAudioSampleRate : this.userMedia.getAudioSampleRate();
20458
+ }
20459
+ this.writeCommand("stop", this.recordingStats, ()=>{
20460
+ this.emit("STOPPED", {
20461
+ recordingStats: this.recordingStats
20462
+ });
20491
20463
  });
20492
- } else throw error_createError({
20493
- message: "Arguments are missing for updating the frame progress",
20494
- options: this.options
20495
- });
20496
- this.confirmedFrameNumber = args.frame;
20497
- this.frameProgress = this.calculateFrameProgress();
20498
- this.updateOverallProgress();
20464
+ this.reset();
20465
+ }, 60);
20499
20466
  }
20500
- updateSampleProgress(args) {
20501
- if (args) {
20502
- if (!args.sample) throw error_createError({
20503
- message: "The audio sample number is missing",
20504
- options: this.options
20505
- });
20506
- } else throw error_createError({
20507
- message: "Arguments are missing for updating the audio sample progress",
20508
- options: this.options
20509
- });
20510
- this.confirmedSampleNumber = args.sample;
20511
- this.sampleProgress = this.calculateSampleProgress();
20512
- this.updateOverallProgress();
20467
+ back(cb) {
20468
+ this.emit("GOING_BACK");
20469
+ this.key = void 0;
20470
+ this.recordingDimensions = void 0;
20471
+ this.unloaded = false;
20472
+ this.show();
20473
+ this.writeCommand("back", void 0, cb);
20513
20474
  }
20514
- preview(args) {
20515
- var _args_duration;
20516
- if (!args) throw error_createError({
20517
- message: "Preview arguments are missing.",
20518
- options: this.options
20519
- });
20520
- const hasAudio = this.samplesCount > 0;
20521
- this.mediaTransmissionStarted = false;
20522
- this.recordingActive = false;
20523
- this.recordingTransportFailed = false;
20524
- this.confirmedFrameNumber = this.confirmedSampleNumber = this.samplesCount = this.framesCount = 0;
20525
- this.sampleProgress = this.frameProgress = void 0;
20526
- this.key = args.key;
20527
- if (args.mp4) this.replay.setMp4Source(`${args.mp4 + constants.WHITELIST_KEY_LABEL}/${this.options.whitelistKey}/videomail.mp4`, true);
20528
- if (args.webm) this.replay.setWebMSource(`${args.webm + constants.WHITELIST_KEY_LABEL}/${this.options.whitelistKey}/videomail.webm`, true);
20529
- this.hide();
20530
- const widthDimension = this.getRecorderWidth(true);
20531
- const heightDimension = this.getRecorderHeight(true);
20532
- const duration = null != (_args_duration = args.duration) ? _args_duration : -1;
20533
- this.emit("PREVIEW", {
20534
- key: this.key,
20535
- width: null == widthDimension ? void 0 : widthDimension.value,
20536
- height: null == heightDimension ? void 0 : heightDimension.value,
20537
- hasAudio,
20538
- duration
20539
- });
20540
- if (this.stopTime) this.waitingTime = Date.now() - this.stopTime;
20541
- null != this.recordingStats || (this.recordingStats = {});
20542
- this.recordingStats.waitingTime = this.waitingTime;
20475
+ reInitializeAudio() {
20476
+ var _this_userMedia;
20477
+ this.options.logger.debug("Recorder: reInitializeAudio()");
20478
+ this.clearUserMediaTimeout();
20479
+ this.userMediaLoading = false;
20480
+ null == (_this_userMedia = this.userMedia) || _this_userMedia.stop();
20481
+ this.recordingDimensions = void 0;
20482
+ this.userMediaLoaded = this.key = this.canvas = this.ctx = void 0;
20483
+ this.loadUserMedia();
20543
20484
  }
20544
- initSocket(cb) {
20545
- if (this.connected) {
20546
- this.options.logger.debug("Recorder: already **connected**. Not going to initialize a new WebSocket connection.");
20547
- null == cb || cb();
20548
- return;
20549
- }
20550
- if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
20551
- this.clearReconnectTimeout();
20552
- if (util_isAutomatedUserAgent()) {
20553
- this.connecting = false;
20554
- this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
20555
- return;
20485
+ reset() {
20486
+ this.cancelCameraRecovery();
20487
+ if (!this.unloaded) {
20488
+ var _this_userMedia;
20489
+ this.options.logger.debug("Recorder: reset()");
20490
+ this.emit("RESETTING");
20491
+ this.cancelAnimationFrame();
20492
+ null == (_this_userMedia = this.userMedia) || _this_userMedia.stop();
20493
+ this.replay.reset();
20494
+ this.userMediaLoaded = this.key = this.canvas = this.ctx = this.recordingBuffer = void 0;
20556
20495
  }
20557
- this.connecting = true;
20558
- this.connectionFailed = false;
20559
- this.connectingStartedAt = Date.now();
20560
- this.lastCloseEvent = void 0;
20561
- this.lastSocketError = void 0;
20562
- this.emit("CONNECTING");
20563
- let url2Connect;
20564
- try {
20565
- const socketUrlObj = new URL(this.options.socketUrl);
20566
- socketUrlObj.searchParams.set(constants.WHITELIST_KEY_LABEL, this.options.whitelistKey);
20567
- url2Connect = socketUrlObj.toString();
20568
- } catch (exc) {
20569
- this.connecting = this.connected = false;
20570
- const err = error_createError({
20571
- message: "Invalid WebSocket URL",
20572
- explanation: `The configured socketUrl "${this.options.socketUrl}" is not a valid URL. Please check your videomail-client configuration.`,
20573
- options: this.options,
20574
- exc
20575
- });
20576
- this.emit("ERROR", {
20577
- err
20578
- });
20496
+ }
20497
+ validate() {
20498
+ return Boolean(this.key) && void 0 === this.canvas;
20499
+ }
20500
+ pause(params) {
20501
+ var _this_userMedia;
20502
+ this.cancelCameraRecovery();
20503
+ this.resumeAfterDrain = false;
20504
+ const loop = this.loop;
20505
+ if (!loop) throw new Error("No recording loop defined");
20506
+ if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
20507
+ else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
20508
+ null == (_this_userMedia = this.userMedia) || _this_userMedia.pause();
20509
+ loop.stop();
20510
+ this.emit("PAUSED");
20511
+ this.sendPings();
20512
+ }
20513
+ resume() {
20514
+ var _this_userMedia;
20515
+ if (this.cameraRecovery || !(null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.hasLiveStream())) {
20516
+ this.recoverCamera();
20517
+ this.resumeAfterCameraRecovery = Boolean(this.cameraRecovery);
20579
20518
  return;
20580
20519
  }
20581
- this.options.logger.debug(`Recorder: initializing web socket stream to ${url2Connect}`);
20582
- let nativeSocket;
20520
+ const loop = this.loop;
20521
+ if (!loop) throw new Error("No recording loop defined");
20522
+ this.options.logger.debug(`Recorder: resume() with frame ${this.framesCount}`);
20523
+ this.stopPings();
20524
+ this.emit("RESUMING");
20525
+ this.userMedia.resume();
20526
+ if (this.streamBackpressured) this.resumeAfterDrain = true;
20527
+ else loop.start();
20528
+ }
20529
+ onFlushed(opts) {
20530
+ const frameNumber = opts.frameNumber;
20531
+ if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
20532
+ }
20533
+ draw(_deltaTime, elapsedTime) {
20534
+ if (this.recordingTransportFailed) return;
20535
+ if (!this.userMedia) throw new Error("No user media defined, unable to draw on canvas");
20583
20536
  try {
20584
- nativeSocket = new WebSocket(url2Connect);
20585
- nativeSocket.addEventListener("close", (event)=>{
20586
- this.lastCloseEvent = {
20587
- code: event.code,
20588
- reason: event.reason,
20589
- wasClean: event.wasClean
20590
- };
20591
- });
20592
- nativeSocket.addEventListener("error", (event)=>{
20593
- this.lastSocketError = error_getEventDetails(event);
20594
- });
20537
+ if (!this.isPaused() && this.stream && this.ctx) {
20538
+ var _this_frame, _this_recordingBuffer;
20539
+ if (0 === this.framesCount) this.emit("SENDING_FIRST_FRAME");
20540
+ this.framesCount++;
20541
+ const imageSource = this.userMedia.getRawVisuals();
20542
+ if (this.canvas && imageSource) this.ctx.drawImage(imageSource, 0, 0, this.canvas.width, this.canvas.height);
20543
+ else throw new Error("Unable to draw an image without a defined canvas");
20544
+ this.recordingBuffer = null == (_this_frame = this.frame) ? void 0 : _this_frame.toBuffer();
20545
+ const recordingBufferLength = null == (_this_recordingBuffer = this.recordingBuffer) ? void 0 : _this_recordingBuffer.length;
20546
+ if (recordingBufferLength) {
20547
+ if (this.recordingBuffer) {
20548
+ const frameControlBuffer = capture_Buffer.from(JSON.stringify({
20549
+ frameNumber: this.framesCount
20550
+ }));
20551
+ const frameBuffer = capture_Buffer.concat([
20552
+ this.recordingBuffer,
20553
+ frameControlBuffer
20554
+ ]);
20555
+ this.mediaTransmissionStarted = true;
20556
+ this.writeStream(frameBuffer, {
20557
+ frameNumber: this.framesCount,
20558
+ onFlushedCallback: this.onFlushed.bind(this)
20559
+ });
20560
+ this.visuals.checkTimer(elapsedTime);
20561
+ }
20562
+ } else throw error_createError({
20563
+ message: "Failed to extract webcam data.",
20564
+ options: this.options
20565
+ });
20566
+ }
20595
20567
  } catch (exc) {
20596
- this.connecting = this.connected = false;
20597
- const diagnostic = error_getWebSocketDiagnostic(url2Connect);
20598
- const message = diagnostic.looksAutomated ? "Automated crawler: WebSocket not supported in this environment" : `Failed to construct WebSocket to ${url2Connect}`;
20599
- const explanation = diagnostic.looksAutomated ? "Headless crawlers cannot use Videomail's WebSocket. No action needed." : `Please check your connection and try again. If the problem persists, contact us. Diagnostic: ${diagnostic.text}`;
20600
- const err = error_createError({
20601
- message,
20602
- explanation,
20603
- options: this.options,
20568
+ this.emit("ERROR", {
20604
20569
  exc
20605
20570
  });
20606
- this.emit("ERROR", {
20607
- err
20571
+ }
20572
+ }
20573
+ createLoop() {
20574
+ const newLoop = animitter_default()({
20575
+ fps: this.options.video.fps
20576
+ }, this.draw.bind(this));
20577
+ this.originalAnimationFrameObject = newLoop.getRequestAnimationFrameObject();
20578
+ return newLoop;
20579
+ }
20580
+ record() {
20581
+ if (this.unloaded || this.cameraRecovery) return;
20582
+ if (!this.connected) {
20583
+ this.options.logger.debug("Recorder: reconnecting before recording …");
20584
+ this.initSocket(()=>{
20585
+ this.once("SERVER_READY", ()=>{
20586
+ if (this.unloaded) return;
20587
+ if (this.userMediaLoaded || this.options.loadUserMediaOnRecord) this.record();
20588
+ else this.once("USER_MEDIA_READY", ()=>{
20589
+ if (!this.unloaded) this.record();
20590
+ });
20591
+ });
20592
+ });
20593
+ return;
20594
+ }
20595
+ if (!this.userMediaLoaded) {
20596
+ if (this.options.loadUserMediaOnRecord) this.loadUserMedia({
20597
+ recordWhenReady: true
20608
20598
  });
20599
+ else {
20600
+ const err = error_createError({
20601
+ message: "Load and enable your camera first",
20602
+ options: this.options
20603
+ });
20604
+ this.emit("ERROR", {
20605
+ err
20606
+ });
20607
+ }
20609
20608
  return;
20610
20609
  }
20610
+ const loop = this.loop;
20611
+ if (!loop) throw new Error("No recording loop defined");
20612
+ this.recordingDimensions = void 0;
20611
20613
  try {
20612
- this.stream = stream_default()(nativeSocket);
20614
+ if (!this.userMedia) throw new Error("No user media defined, unable to create canvas");
20615
+ this.canvas = this.userMedia.createCanvas();
20613
20616
  } catch (exc) {
20614
- this.connecting = this.connected = false;
20615
20617
  const err = error_createError({
20616
- message: `Failed to create a stream to ${url2Connect}`,
20617
- explanation: "Please check your connection and try again. If the problem persists, contact us.",
20618
- options: this.options,
20619
- exc
20618
+ exc,
20619
+ options: this.options
20620
20620
  });
20621
20621
  this.emit("ERROR", {
20622
20622
  err
20623
20623
  });
20624
+ return;
20624
20625
  }
20625
- const stream = this.stream;
20626
- if (stream) {
20627
- const connectionTimeoutMs = this.options.timeouts.connection;
20628
- this.connectionTimeout = window.setTimeout(()=>{
20629
- if (this.isOnline() && this.isUserMediaLoaded()) {
20630
- this.options.logger.debug(`${PIPE_SYMBOL}Reconnecting due to connection timeout.`);
20631
- this.reconnecting = true;
20632
- this.handleConnectionFailure({
20633
- url2Connect,
20634
- cause: "timeout"
20635
- });
20636
- } else this.handleConnectionFailure({
20637
- url2Connect,
20638
- cause: "timeout"
20639
- });
20640
- }, connectionTimeoutMs);
20641
- stream.on("close", ()=>{
20642
- this.handleStreamClose(url2Connect);
20643
- });
20644
- stream.on("connect", (args)=>{
20645
- var _this_stream;
20646
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
20647
- this.clearConnectionTimeout();
20648
- const isClosing = (null == (_this_stream = this.stream) ? void 0 : _this_stream.socket.readyState) === WebSocket.CLOSING;
20649
- if (!this.connected && !isClosing && !this.unloaded) {
20650
- this.connected = true;
20651
- this.connecting = this.unloaded = false;
20652
- this.reconnecting = false;
20653
- this.reconnectAttempts = 0;
20654
- this.clearReconnectTimeout();
20655
- this.emit("CONNECTED");
20656
- null == cb || cb();
20657
- }
20658
- });
20659
- stream.on("data", (data)=>{
20660
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
20661
- try {
20662
- const command = JSON.parse(data.toString());
20663
- this.executeCommand(command);
20664
- } catch (exc) {
20665
- this.options.logger.error(`Failed to parse command: ${exc}`);
20666
- const err = error_createError({
20667
- message: "Invalid server command",
20668
- explanation: `Contact us. The invalid command was: ${data.toString()}.`,
20669
- options: this.options,
20670
- exc
20671
- });
20672
- this.emit("ERROR", {
20673
- err
20674
- });
20675
- }
20676
- });
20677
- stream.on("error", (err)=>{
20678
- this.handleStreamError(url2Connect, err);
20679
- });
20680
- stream.on("drain", ()=>{
20681
- this.handleStreamDrain(stream);
20682
- });
20683
- stream.on("preend", (args)=>{
20684
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
20685
- });
20686
- stream.on("end", ()=>{
20687
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *end* event emitted`);
20688
- });
20689
- stream.on("pipe", (src)=>{
20690
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *pipe* event emitted with src: ${string_pretty(src)}`);
20691
- });
20692
- stream.on("unpipe", (src)=>{
20693
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *unpipe* event emitted with src: ${string_pretty(src)}`);
20694
- });
20695
- stream.on("resume", (args)=>{
20696
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *resume* event emitted with args: ${string_pretty(args)}`);
20697
- });
20698
- stream.on("uncork", (args)=>{
20699
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *uncork* event emitted with args: ${string_pretty(args)}`);
20700
- });
20701
- stream.on("readable", (args)=>{
20702
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *readable* event emitted with args: ${string_pretty(args)}`);
20703
- });
20704
- stream.on("prefinish", (args)=>{
20705
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *prefinish* event emitted with args: ${string_pretty(args)}`);
20626
+ this.ctx = this.canvas.getContext("2d");
20627
+ if (!this.canvas.width) {
20628
+ const err = error_createError({
20629
+ message: "Canvas has an invalid width.",
20630
+ options: this.options
20706
20631
  });
20707
- stream.on("finish", (args)=>{
20708
- this.options.logger.debug(`${PIPE_SYMBOL}Stream *finish* event emitted with args: ${string_pretty(args)}`);
20632
+ this.emit("ERROR", {
20633
+ err
20709
20634
  });
20635
+ return;
20710
20636
  }
20711
- }
20712
- showUserMedia() {
20713
- if (!this.connected) return false;
20714
- const hidden = this.isHidden();
20715
- if (!hidden) return true;
20716
- const notifying = this.isNotifying();
20717
- if (notifying) return true;
20718
- return this.blocking;
20719
- }
20720
- userMediaErrorCallback(err, usedConstraints) {
20721
- var _this_userMedia;
20722
- if (this.cameraRecovery) return void this.failCameraRecovery(err);
20723
- if (this.unloaded) return void this.options.logger.debug(`Recorder: already unloaded. Not going to throw error ${string_pretty(err)}`);
20724
- this.userMediaLoading = false;
20725
- this.clearUserMediaTimeout();
20726
- const characteristics = null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.getCharacteristics();
20727
- this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
20728
- const errorListeners = this.getListeners("ERROR");
20729
- if (null == errorListeners ? void 0 : errorListeners.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
20730
- const videomailError = error_createError({
20731
- err,
20732
- options: this.options,
20733
- usedConstraints
20637
+ if (!this.canvas.height) {
20638
+ const err = error_createError({
20639
+ message: "Canvas has an invalid height.",
20640
+ options: this.options
20734
20641
  });
20735
20642
  this.emit("ERROR", {
20736
- err: videomailError
20643
+ err
20737
20644
  });
20738
- } else {
20739
- this.options.logger.debug(`Recorder: ignore user media error ${string_pretty(err)}`);
20740
- this.retryTimeout = window.setTimeout(()=>{
20741
- this.retryTimeout = void 0;
20742
- if (!this.unloaded) this.loadUserMedia();
20743
- }, this.options.timeouts.userMedia);
20645
+ return;
20744
20646
  }
20745
- else {
20746
- this.options.logger.debug(`Recorder: no error listeners attached but throwing error ${string_pretty(err)}`);
20747
- throw error_createError({
20748
- err,
20749
- message: "Unable to process this error since there are no error listeners anymore.",
20750
- options: this.options
20751
- });
20647
+ this.recordingDimensions = {
20648
+ width: this.canvas.width,
20649
+ height: this.canvas.height
20650
+ };
20651
+ this.frame = new canvas_to_buffer_modern_r(this.canvas, this.options.image.types, this.options.image.quality);
20652
+ this.options.logger.debug("Recorder: record()");
20653
+ this.recordingActive = true;
20654
+ this.recordingTransportFailed = false;
20655
+ this.mediaTransmissionStarted = false;
20656
+ this.recordingAudioSampleRate = void 0;
20657
+ this.userMedia.record();
20658
+ this.emit("RECORDING", {
20659
+ framesCount: this.framesCount
20660
+ });
20661
+ loop.on("update", (_deltaTime, elapsedTime)=>{
20662
+ let avgFPS;
20663
+ avgFPS = 0 !== elapsedTime ? Math.round(this.framesCount / elapsedTime * 1000) : void 0;
20664
+ this.options.logger.debug(`Recorder updates avgFps = ${avgFPS} at frame ${this.framesCount}`);
20665
+ });
20666
+ loop.start();
20667
+ }
20668
+ setAnimationFrameObject(newObj) {
20669
+ if (this.loop) {
20670
+ const isRecording = this.isRecording();
20671
+ this.loop.stop();
20672
+ this.loop.setRequestAnimationFrameObject(newObj);
20673
+ if (isRecording) this.loop.start();
20752
20674
  }
20753
20675
  }
20754
- getUserMediaCallback(localStream, params, requestId = this.mediaRequestId) {
20755
- if (!this.userMedia) {
20756
- localStream.getTracks().forEach((track)=>{
20757
- track.stop();
20758
- });
20676
+ restoreAnimationFrameObject() {
20677
+ this.options.logger.debug("Recorder: restoreAnimationFrameObject()");
20678
+ if (this.originalAnimationFrameObject) this.setAnimationFrameObject(this.originalAnimationFrameObject);
20679
+ }
20680
+ loopWithTimeouts() {
20681
+ this.options.logger.debug("Recorder: loopWithTimeouts()");
20682
+ const wantedInterval = 1e3 / this.options.video.fps;
20683
+ let processingTime = 0;
20684
+ const raf = (fn)=>window.setTimeout(()=>{
20685
+ const start = Date.now();
20686
+ fn();
20687
+ processingTime = Date.now() - start;
20688
+ }, wantedInterval - processingTime);
20689
+ const cancel = (id)=>{
20690
+ window.clearTimeout(id);
20691
+ };
20692
+ this.setAnimationFrameObject({
20693
+ requestAnimationFrame: raf,
20694
+ cancelAnimationFrame: cancel
20695
+ });
20696
+ }
20697
+ isPaused() {
20698
+ var _this_userMedia, _this_loop;
20699
+ return (null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isPaused()) && !(null == (_this_loop = this.loop) ? void 0 : _this_loop.isRunning());
20700
+ }
20701
+ isUserMediaPaused() {
20702
+ var _ref;
20703
+ var _this_userMedia;
20704
+ return null != (_ref = null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isPaused()) ? _ref : false;
20705
+ }
20706
+ isCaptureLoopRunning() {
20707
+ var _ref;
20708
+ var _this_loop;
20709
+ return null != (_ref = null == (_this_loop = this.loop) ? void 0 : _this_loop.isRunning()) ? _ref : false;
20710
+ }
20711
+ stopCaptureLoop() {
20712
+ var _this_loop;
20713
+ null == (_this_loop = this.loop) || _this_loop.stop();
20714
+ }
20715
+ startCaptureLoop() {
20716
+ var _this_loop;
20717
+ null == (_this_loop = this.loop) || _this_loop.start();
20718
+ }
20719
+ isRecording() {
20720
+ var _this_loop;
20721
+ return Boolean((null == (_this_loop = this.loop) ? void 0 : _this_loop.isRunning()) && !this.isPaused() && !this.isNotifying() && this.stream && !this.stream.destroyed);
20722
+ }
20723
+ getRecordingDimensions() {
20724
+ return this.recordingDimensions && capture_object_spread({}, this.recordingDimensions);
20725
+ }
20726
+ constructor(visuals, replay, options){
20727
+ super(visuals, options), capture_define_property(this, "replay", void 0), capture_define_property(this, "loop", void 0), capture_define_property(this, "originalAnimationFrameObject", void 0), capture_define_property(this, "recordingAudioSampleRate", void 0), capture_define_property(this, "frameProgress", void 0), capture_define_property(this, "sampleProgress", void 0), capture_define_property(this, "canvas", void 0), capture_define_property(this, "ctx", void 0), capture_define_property(this, "stopTime", void 0), capture_define_property(this, "waitingTime", void 0), capture_define_property(this, "frame", void 0), capture_define_property(this, "recordingBuffer", void 0);
20728
+ this.replay = replay;
20729
+ }
20730
+ }
20731
+ const capture = RecorderCapture;
20732
+ function figureMinHeight(height, options) {
20733
+ let minHeight;
20734
+ if (options.video.height) {
20735
+ minHeight = height ? Math.min(options.video.height, height) : options.video.height;
20736
+ if (minHeight < 1) throw error_createError({
20737
+ message: `Got a min height less than 1 (${minHeight})!`,
20738
+ options
20739
+ });
20740
+ } else minHeight = height;
20741
+ return minHeight;
20742
+ }
20743
+ const dimensions_figureMinHeight = figureMinHeight;
20744
+ function getRatio(options, videoHeight, videoWidth) {
20745
+ let ratio = 1;
20746
+ const hasVideoDimensions = videoHeight && videoWidth;
20747
+ const desiredHeight = options.video.height;
20748
+ const desiredWidth = options.video.width;
20749
+ const hasDesiredDimensions = desiredHeight && desiredWidth;
20750
+ if (hasDesiredDimensions) ratio = hasVideoDimensions ? videoHeight < desiredHeight || videoWidth < desiredWidth ? videoHeight / videoWidth : desiredHeight / desiredWidth : desiredHeight / desiredWidth;
20751
+ else if (hasVideoDimensions) ratio = videoHeight / videoWidth;
20752
+ return ratio;
20753
+ }
20754
+ const dimensions_getRatio = getRatio;
20755
+ function calculateHeight(responsive, videoWidth, options, ratio, element) {
20756
+ const dimension = {
20757
+ unit: "px"
20758
+ };
20759
+ let width = videoWidth;
20760
+ if (responsive && element) {
20761
+ const limitedDimension = dimensions_limitWidth(element, options, width);
20762
+ width = limitedDimension.value;
20763
+ }
20764
+ const chosenRatio = null != ratio ? ratio : dimensions_getRatio(options, void 0, videoWidth);
20765
+ const height = width ? Math.round(width * chosenRatio) : void 0;
20766
+ if (height && Number.isInteger(height) && height < 1) throw error_createError({
20767
+ message: "Just calculated a height less than 1 which is wrong.",
20768
+ options
20769
+ });
20770
+ const minHeight = dimensions_figureMinHeight(height, options);
20771
+ dimension.value = minHeight;
20772
+ return dimension;
20773
+ }
20774
+ const dimensions_calculateHeight = calculateHeight;
20775
+ function calculateWidth(responsive, options, videoHeight, ratio) {
20776
+ const dimension = {
20777
+ unit: "px"
20778
+ };
20779
+ let height = dimensions_figureMinHeight(videoHeight, options);
20780
+ if (responsive) {
20781
+ const limitedDimension = dimensions_limitHeight(height, options, "calculateWidth");
20782
+ height = limitedDimension.value;
20783
+ }
20784
+ if (!height || height < 1) throw error_createError({
20785
+ message: `Height ${height} cannot be smaller than 1 when calculating width.`,
20786
+ options
20787
+ });
20788
+ const chosenRatio = null != ratio ? ratio : dimensions_getRatio(options, videoHeight);
20789
+ const calculatedWidth = Math.round(height / chosenRatio);
20790
+ if (calculatedWidth < 1) throw error_createError({
20791
+ message: "Calculated width cannot be smaller than 1!",
20792
+ options
20793
+ });
20794
+ dimension.value = calculatedWidth;
20795
+ return dimension;
20796
+ }
20797
+ const dimensions_calculateWidth = calculateWidth;
20798
+ function dimensions_define_property(obj, key, value) {
20799
+ if (key in obj) Object.defineProperty(obj, key, {
20800
+ value: value,
20801
+ enumerable: true,
20802
+ configurable: true,
20803
+ writable: true
20804
+ });
20805
+ else obj[key] = value;
20806
+ return obj;
20807
+ }
20808
+ class RecorderDimensions {
20809
+ correctDimensions() {
20810
+ const recorderElement = this.context.getRecorderElement();
20811
+ if (!recorderElement) return;
20812
+ const widthDimension = dimensions_useFullWidth(this.context.options.video.mobileBreakPoint);
20813
+ if (this.context.options.video.stretch || widthDimension) {
20814
+ recorderElement.style.width = "100%";
20815
+ recorderElement.style.removeProperty("height");
20816
+ } else {
20817
+ if (this.context.options.video.width) {
20818
+ const recorderWidth = this.getRecorderWidth(true);
20819
+ if (null == recorderWidth ? void 0 : recorderWidth.value) recorderElement.width = recorderWidth.value;
20820
+ else recorderElement.style.removeProperty("width");
20821
+ }
20822
+ if (this.context.options.video.height) {
20823
+ const recorderHeight = this.getRecorderHeight(true);
20824
+ if (null == recorderHeight ? void 0 : recorderHeight.value) recorderElement.height = recorderHeight.value;
20825
+ else recorderElement.style.removeProperty("height");
20826
+ }
20827
+ }
20828
+ }
20829
+ getRecorderWidth(responsive) {
20830
+ const userMedia = this.context.getUserMedia();
20831
+ if (null == userMedia ? void 0 : userMedia.hasVideoWidth()) return userMedia.getRawWidth(responsive);
20832
+ else if (responsive && this.context.options.video.width) return this.context.visuals.limitWidth(this.context.options.video.width);
20833
+ const dimension = {
20834
+ unit: "px",
20835
+ value: this.context.options.video.width
20836
+ };
20837
+ return dimension;
20838
+ }
20839
+ getRecorderHeight(responsive, useBoundingClientRect) {
20840
+ const recorderElement = this.context.getRecorderElement();
20841
+ const userMedia = this.context.getUserMedia();
20842
+ let recorderHeight;
20843
+ if (recorderElement && useBoundingClientRect) {
20844
+ const height = recorderElement.getBoundingClientRect().height;
20845
+ recorderHeight = {
20846
+ unit: "px",
20847
+ value: height
20848
+ };
20849
+ } else if (userMedia) {
20850
+ let height = userMedia.getRawHeight(responsive);
20851
+ if (void 0 !== height && this.context.options.video.width) {
20852
+ const ratio = this.getRatio();
20853
+ if (void 0 !== ratio) {
20854
+ const idealHeight = this.context.options.video.width * ratio;
20855
+ height = Math.min(idealHeight, height);
20856
+ }
20857
+ }
20858
+ recorderHeight = {
20859
+ unit: "px",
20860
+ value: height
20861
+ };
20862
+ } else if (responsive && this.context.options.video.height) recorderHeight = this.calculateHeight(responsive);
20863
+ else if (this.context.options.video.height) {
20864
+ const height = this.context.options.video.height;
20865
+ recorderHeight = {
20866
+ unit: "px",
20867
+ value: height
20868
+ };
20869
+ }
20870
+ return recorderHeight;
20871
+ }
20872
+ getRatio() {
20873
+ const userMedia = this.context.getUserMedia();
20874
+ let ratio;
20875
+ if (userMedia) {
20876
+ const userMediaVideoWidth = userMedia.getVideoWidth();
20877
+ const userMediaVideoHeight = userMedia.getVideoHeight();
20878
+ if (!userMediaVideoWidth || userMediaVideoWidth < 1) ratio = this.context.visuals.getRatio();
20879
+ else if (userMediaVideoHeight) ratio = userMediaVideoHeight / userMediaVideoWidth;
20880
+ } else ratio = dimensions_getRatio(this.context.options);
20881
+ return ratio;
20882
+ }
20883
+ calculateWidth(responsive) {
20884
+ const userMedia = this.context.getUserMedia();
20885
+ const recorderElement = this.context.getRecorderElement();
20886
+ let videoHeight;
20887
+ if (userMedia) videoHeight = userMedia.getVideoHeight();
20888
+ else if (recorderElement) videoHeight = recorderElement.videoHeight || recorderElement.height;
20889
+ return dimensions_calculateWidth(responsive, this.context.options, videoHeight, this.getRatio());
20890
+ }
20891
+ calculateHeight(responsive) {
20892
+ const userMedia = this.context.getUserMedia();
20893
+ const recorderElement = this.context.getRecorderElement();
20894
+ let videoDimension;
20895
+ if (userMedia) {
20896
+ const videoHeight = userMedia.getVideoHeight();
20897
+ videoDimension = {
20898
+ value: videoHeight,
20899
+ unit: "px"
20900
+ };
20901
+ } else if (recorderElement) {
20902
+ const videoHeight = recorderElement.videoHeight || recorderElement.height;
20903
+ videoDimension = {
20904
+ value: videoHeight,
20905
+ unit: "px"
20906
+ };
20907
+ } else videoDimension = dimensions_calculateHeight(responsive, void 0, this.context.options, this.getRatio(), recorderElement);
20908
+ return videoDimension;
20909
+ }
20910
+ constructor(context){
20911
+ dimensions_define_property(this, "context", void 0);
20912
+ this.context = context;
20913
+ }
20914
+ }
20915
+ const recorder_dimensions = RecorderDimensions;
20916
+ function getFirstVideoTrack(localMediaStream) {
20917
+ const videoTracks = localMediaStream.getVideoTracks();
20918
+ let videoTrack;
20919
+ if (videoTracks[0]) videoTrack = videoTracks[0];
20920
+ return videoTrack;
20921
+ }
20922
+ const media_getFirstVideoTrack = getFirstVideoTrack;
20923
+ const MEDIA_EVENTS = [
20924
+ "loadstart",
20925
+ "suspend",
20926
+ "progress",
20927
+ "abort",
20928
+ "emptied",
20929
+ "stalled",
20930
+ "pause",
20931
+ "loadeddata",
20932
+ "waiting",
20933
+ "playing",
20934
+ "canplay",
20935
+ "canplaythrough",
20936
+ "seeking",
20937
+ "seeked",
20938
+ "ended",
20939
+ "ratechange",
20940
+ "durationchange",
20941
+ "volumechange"
20942
+ ];
20943
+ const mediaEvents = MEDIA_EVENTS;
20944
+ function esm_define_property(obj, key, value) {
20945
+ if (key in obj) Object.defineProperty(obj, key, {
20946
+ value: value,
20947
+ enumerable: true,
20948
+ configurable: true,
20949
+ writable: true
20950
+ });
20951
+ else obj[key] = value;
20952
+ return obj;
20953
+ }
20954
+ class AudioSample {
20955
+ toBuffer() {
20956
+ const l = this.float32Array.length;
20957
+ const arr = new Int16Array(l);
20958
+ this.float32Array.forEach((sample, i)=>{
20959
+ const clampedSample = Number.isFinite(sample) ? Math.max(-1, sample) : sample;
20960
+ arr[i] = 0x7fff * Math.min(1, clampedSample);
20961
+ });
20962
+ return typedarray_to_buffer(arr);
20963
+ }
20964
+ constructor(float32Array){
20965
+ esm_define_property(this, "float32Array", void 0);
20966
+ this.float32Array = float32Array;
20967
+ }
20968
+ }
20969
+ const esm_src = AudioSample;
20970
+ const esm = esm_src;
20971
+ var is_power_of_two = __webpack_require__("dZ");
20972
+ var is_power_of_two_default = /*#__PURE__*/ __webpack_require__.n(is_power_of_two);
20973
+ function AudioRecorder_asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
20974
+ try {
20975
+ var info = gen[key](arg);
20976
+ var value = info.value;
20977
+ } catch (error) {
20978
+ reject(error);
20979
+ return;
20980
+ }
20981
+ if (info.done) resolve(value);
20982
+ else Promise.resolve(value).then(_next, _throw);
20983
+ }
20984
+ function AudioRecorder_async_to_generator(fn) {
20985
+ return function() {
20986
+ var self1 = this, args = arguments;
20987
+ return new Promise(function(resolve, reject) {
20988
+ var gen = fn.apply(self1, args);
20989
+ function _next(value) {
20990
+ AudioRecorder_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
20991
+ }
20992
+ function _throw(err) {
20993
+ AudioRecorder_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
20994
+ }
20995
+ _next(void 0);
20996
+ });
20997
+ };
20998
+ }
20999
+ function AudioRecorder_define_property(obj, key, value) {
21000
+ if (key in obj) Object.defineProperty(obj, key, {
21001
+ value: value,
21002
+ enumerable: true,
21003
+ configurable: true,
21004
+ writable: true
21005
+ });
21006
+ else obj[key] = value;
21007
+ return obj;
21008
+ }
21009
+ const CHANNELS = 1;
21010
+ function getAudioContextClass() {
21011
+ return window.AudioContext;
21012
+ }
21013
+ class AudioRecorder {
21014
+ hasAudioContext() {
21015
+ return Boolean(getAudioContextClass()) && Boolean(this.getAudioContext());
21016
+ }
21017
+ getAudioContext() {
21018
+ if (!this.vcAudioContext) {
21019
+ const AudioContext = getAudioContextClass();
21020
+ this.vcAudioContext = new AudioContext();
21021
+ }
21022
+ return this.vcAudioContext;
21023
+ }
21024
+ onAudioProcess(e, cb) {
21025
+ if (!this.userMedia.isRecording() || this.userMedia.isPaused()) return;
21026
+ const float32Array = e.inputBuffer.getChannelData(0);
21027
+ cb(new esm(float32Array));
21028
+ }
21029
+ disconnect() {
21030
+ var _this_audioInput, _this_volume;
21031
+ if (this.scriptProcessor) {
21032
+ this.scriptProcessor.onaudioprocess = null;
21033
+ this.scriptProcessor.disconnect();
21034
+ }
21035
+ null == (_this_audioInput = this.audioInput) || _this_audioInput.disconnect();
21036
+ null == (_this_volume = this.volume) || _this_volume.disconnect();
21037
+ }
21038
+ init(localMediaStream) {
21039
+ this.options.logger.debug("AudioRecorder: init()");
21040
+ this.disconnect();
21041
+ const volume = this.getAudioContext().createGain();
21042
+ this.volume = volume;
21043
+ try {
21044
+ this.audioInput = this.getAudioContext().createMediaStreamSource(localMediaStream);
21045
+ } catch (exc) {
20759
21046
  throw error_createError({
20760
- message: "No user media is defined",
21047
+ message: "Webcam has no audio",
21048
+ exc,
20761
21049
  options: this.options
20762
21050
  });
20763
21051
  }
20764
- this.options.logger.debug(`Recorder: getUserMediaCallback(${params ? string_pretty(params) : ""})`);
20765
- if (this.showUserMedia()) try {
20766
- this.userMedia.init(localStream, ()=>{
20767
- if (!this.unloaded && requestId === this.mediaRequestId) this.onUserMediaReady(params);
20768
- }, this.onAudioSample.bind(this), (err)=>{
20769
- if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(err, {
20770
- audio: isAudioEnabled(this.options)
20771
- });
20772
- }, params);
20773
- } catch (exc) {
20774
- localStream.getTracks().forEach((track)=>{
21052
+ let { bufferSize } = this.options.audio;
21053
+ if ("auto" === bufferSize) bufferSize = util_getBrowser(this.options).isFirefox() ? 512 : 2048;
21054
+ if (void 0 === bufferSize || !is_power_of_two_default()(bufferSize)) throw error_createError({
21055
+ message: `Audio buffer size must be a power of two. The current buffer size is ${bufferSize}.`,
21056
+ options: this.options
21057
+ });
21058
+ if (!Number.isFinite(this.options.audio.volume) || this.options.audio.volume < 0 || this.options.audio.volume > 1) throw error_createError({
21059
+ message: `Audio volume must be between zero and one. The current volume is ${this.options.audio.volume}.`,
21060
+ options: this.options
21061
+ });
21062
+ volume.gain.value = this.options.audio.volume;
21063
+ this.scriptProcessor = this.getAudioContext().createScriptProcessor(bufferSize, CHANNELS, CHANNELS);
21064
+ this.audioInput.connect(volume);
21065
+ volume.connect(this.scriptProcessor);
21066
+ this.scriptProcessor.connect(this.getAudioContext().destination);
21067
+ }
21068
+ record(cb) {
21069
+ this.options.logger.debug("AudioRecorder: record()");
21070
+ if (this.scriptProcessor) this.scriptProcessor.onaudioprocess = (e)=>{
21071
+ this.onAudioProcess(e, cb);
21072
+ };
21073
+ }
21074
+ resume() {
21075
+ return AudioRecorder_async_to_generator(function*() {
21076
+ const context = this.getAudioContext();
21077
+ if ("running" !== context.state) yield context.resume();
21078
+ }).call(this);
21079
+ }
21080
+ stop() {
21081
+ this.options.logger.debug("AudioRecorder: stop()");
21082
+ this.disconnect();
21083
+ if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
21084
+ this.options.logger.debug("AudioRecorder: audio context is closed");
21085
+ this.vcAudioContext = void 0;
21086
+ }).catch((err)=>{
21087
+ if (err instanceof Error) return void this.options.logger.error(error_createError({
21088
+ err,
21089
+ options: this.options
21090
+ }));
21091
+ this.options.logger.error(err);
21092
+ });
21093
+ }
21094
+ getSampleRate() {
21095
+ if (this.hasAudioContext()) return this.getAudioContext().sampleRate;
21096
+ return -1;
21097
+ }
21098
+ constructor(userMedia, options){
21099
+ AudioRecorder_define_property(this, "scriptProcessor", void 0);
21100
+ AudioRecorder_define_property(this, "audioInput", void 0);
21101
+ AudioRecorder_define_property(this, "volume", void 0);
21102
+ AudioRecorder_define_property(this, "vcAudioContext", void 0);
21103
+ AudioRecorder_define_property(this, "userMedia", void 0);
21104
+ AudioRecorder_define_property(this, "options", void 0);
21105
+ this.options = options;
21106
+ this.userMedia = userMedia;
21107
+ }
21108
+ }
21109
+ const media_AudioRecorder = AudioRecorder;
21110
+ const VIRTUAL_KEYWORDS = [
21111
+ "obs",
21112
+ "virtual",
21113
+ "manycam",
21114
+ "vcam",
21115
+ "splitcam",
21116
+ "droidcam"
21117
+ ];
21118
+ function isVirtualCamera(videoTrack) {
21119
+ const capabilities = videoTrack.getCapabilities();
21120
+ if (!capabilities.frameRate) return true;
21121
+ const isVirtual = VIRTUAL_KEYWORDS.some((keyword)=>videoTrack.label.toLowerCase().includes(keyword));
21122
+ return isVirtual;
21123
+ }
21124
+ const util_isVirtualCamera = isVirtualCamera;
21125
+ function userMedia_asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
21126
+ try {
21127
+ var info = gen[key](arg);
21128
+ var value = info.value;
21129
+ } catch (error) {
21130
+ reject(error);
21131
+ return;
21132
+ }
21133
+ if (info.done) resolve(value);
21134
+ else Promise.resolve(value).then(_next, _throw);
21135
+ }
21136
+ function userMedia_async_to_generator(fn) {
21137
+ return function() {
21138
+ var self1 = this, args = arguments;
21139
+ return new Promise(function(resolve, reject) {
21140
+ var gen = fn.apply(self1, args);
21141
+ function _next(value) {
21142
+ userMedia_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
21143
+ }
21144
+ function _throw(err) {
21145
+ userMedia_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
21146
+ }
21147
+ _next(void 0);
21148
+ });
21149
+ };
21150
+ }
21151
+ function userMedia_define_property(obj, key, value) {
21152
+ if (key in obj) Object.defineProperty(obj, key, {
21153
+ value: value,
21154
+ enumerable: true,
21155
+ configurable: true,
21156
+ writable: true
21157
+ });
21158
+ else obj[key] = value;
21159
+ return obj;
21160
+ }
21161
+ const EVENT_ASCII = "|—O—|";
21162
+ class UserMedia extends util_Despot {
21163
+ attachMediaStream(stream) {
21164
+ if (this.rawVisualUserMedia) {
21165
+ this.rawVisualUserMedia.srcObject = stream;
21166
+ const previousStream = this.currentVisualStream;
21167
+ this.currentVisualStream = stream;
21168
+ if (previousStream && previousStream !== stream) previousStream.getTracks().forEach((track)=>{
20775
21169
  track.stop();
20776
21170
  });
20777
- this.userMediaLoading = false;
20778
- this.clearUserMediaTimeout();
20779
- this.userMediaErrorCallback(exc, {
20780
- audio: isAudioEnabled(this.options)
20781
- });
20782
- }
21171
+ } else throw error_createError({
21172
+ message: "Error attaching stream to element.",
21173
+ explanation: "Contact the developer about this",
21174
+ options: this.options
21175
+ });
21176
+ }
21177
+ setVisualStream(localMediaStream) {
21178
+ if (localMediaStream) this.attachMediaStream(localMediaStream);
20783
21179
  else {
20784
- localStream.getTracks().forEach((track)=>{
20785
- track.stop();
20786
- });
20787
- this.userMediaLoading = false;
20788
- this.clearUserMediaTimeout();
20789
- this.cancelCameraRecovery();
21180
+ if (this.rawVisualUserMedia) {
21181
+ this.rawVisualUserMedia.srcObject = null;
21182
+ this.rawVisualUserMedia.removeAttribute("src");
21183
+ }
21184
+ this.currentVisualStream = void 0;
20790
21185
  }
20791
21186
  }
20792
- loadGenuineUserMedia(params) {
20793
- this.options.logger.debug(`Recorder: loadGenuineUserMedia(${params ? string_pretty(params) : ""})`);
20794
- this.emit("ASKING_WEBCAM_PERMISSION");
20795
- this.userMediaLoading = true;
20796
- const requestId = ++this.mediaRequestId;
20797
- const constraints = {
20798
- video: {
20799
- frameRate: {
20800
- ideal: this.options.video.fps
20801
- }
20802
- },
20803
- audio: isAudioEnabled(this.options)
20804
- };
20805
- this.configureVideoConstraints(constraints, params);
20806
- this.options.logger.debug(string_summarize("Recorder: our webcam constraints are:", constraints));
20807
- this.options.logger.debug(string_summarize("Recorder: available webcam constraints are:", navigator.mediaDevices.getSupportedConstraints()));
20808
- const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
20809
- streamPromise.then((localStream)=>{
20810
- if (this.unloaded || requestId !== this.mediaRequestId) return void localStream.getTracks().forEach((track)=>{
20811
- track.stop();
20812
- });
20813
- this.getUserMediaCallback(localStream, params, requestId);
20814
- }).catch((reason)=>{
20815
- if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(reason, constraints);
21187
+ restorePlayback() {
21188
+ return userMedia_async_to_generator(function*() {
21189
+ var _this_audioRecorder;
21190
+ if (!this.rawVisualUserMedia) throw new Error("No camera preview is available.");
21191
+ yield this.rawVisualUserMedia.play();
21192
+ yield null == (_this_audioRecorder = this.audioRecorder) ? void 0 : _this_audioRecorder.resume();
21193
+ }).call(this);
21194
+ }
21195
+ hasLiveStream() {
21196
+ var _this_currentVisualStream, _this_currentVisualStream1, _this_currentVisualStream2;
21197
+ const videoReady = null == (_this_currentVisualStream = this.currentVisualStream) ? void 0 : _this_currentVisualStream.getVideoTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
21198
+ const audioReady = !isAudioEnabled(this.options) || (null == (_this_currentVisualStream1 = this.currentVisualStream) ? void 0 : _this_currentVisualStream1.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted));
21199
+ return Boolean((null == (_this_currentVisualStream2 = this.currentVisualStream) ? void 0 : _this_currentVisualStream2.active) && videoReady && audioReady);
21200
+ }
21201
+ hasEnded() {
21202
+ var _this_rawVisualUserMedia, _this_currentVisualStream;
21203
+ if (null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.ended) return this.rawVisualUserMedia.ended;
21204
+ return !(null == (_this_currentVisualStream = this.currentVisualStream) ? void 0 : _this_currentVisualStream.active);
21205
+ }
21206
+ hasInvalidDimensions() {
21207
+ if (!this.rawVisualUserMedia) return false;
21208
+ const { videoWidth, videoHeight } = this.rawVisualUserMedia;
21209
+ return videoWidth > 0 && videoWidth < 3 || videoHeight > 0 && videoHeight < 3;
21210
+ }
21211
+ logEvent(eventType, params) {
21212
+ this.options.logger.debug(`UserMedia: ... ${EVENT_ASCII} event ${eventType}: ${string_pretty(params)}`);
21213
+ }
21214
+ unloadRemainingEventListeners() {
21215
+ var _this_clearInitializationListeners, _this, _this_rawVisualUserMedia;
21216
+ this.options.logger.debug("UserMedia: unloadRemainingEventListeners()");
21217
+ null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
21218
+ mediaEvents.forEach((eventName)=>{
21219
+ var _this_rawVisualUserMedia;
21220
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener(eventName, this.outputEvent);
20816
21221
  });
21222
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener("error", this.handleMediaError);
21223
+ }
21224
+ audioRecord(audioCallback) {
21225
+ var _this_audioRecorder;
21226
+ this.removeListener("SENDING_FIRST_FRAME");
21227
+ null == (_this_audioRecorder = this.audioRecorder) || _this_audioRecorder.record(audioCallback);
20817
21228
  }
20818
- configureVideoConstraints(constraints, params) {
20819
- const videoConstraints = "object" == typeof constraints.video ? constraints.video : void 0;
20820
- if (!videoConstraints) return;
20821
- if ((null == params ? void 0 : params.switchingFacingMode) || (null == params ? void 0 : params.recovering)) videoConstraints.facingMode = params.switchingFacingMode || this.facingMode;
20822
- if (this.options.video.width) videoConstraints.width = {
20823
- ideal: this.options.video.width
21229
+ init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, params) {
21230
+ var _this_clearInitializationListeners, _this;
21231
+ null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
21232
+ this.stop(localMediaStream, {
21233
+ aboutToInitialize: true,
21234
+ preserveRecordingState: Boolean((null == params ? void 0 : params.switchingFacingMode) || (null == params ? void 0 : params.recovering))
21235
+ });
21236
+ const initializationId = ++this.initializationId;
21237
+ this.onPlayReached = false;
21238
+ this.onLoadedMetaDataReached = false;
21239
+ this.playingPromiseReached = false;
21240
+ this.videoTrackLabel = void 0;
21241
+ if (isAudioEnabled(this.options)) null != this.audioRecorder || (this.audioRecorder = new media_AudioRecorder(this, this.options));
21242
+ const unloadAllEventListeners = ()=>{
21243
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
21244
+ this.options.logger.debug("UserMedia: unloadAllEventListeners()");
21245
+ this.unloadRemainingEventListeners();
21246
+ this.removeListener("SENDING_FIRST_FRAME");
21247
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener("play", onPlay);
21248
+ null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.removeEventListener("loadedmetadata", onLoadedMetaData);
20824
21249
  };
20825
- else {
20826
- const limitedWidth = this.limitWidth();
20827
- if (null == limitedWidth ? void 0 : limitedWidth.value) videoConstraints.width = {
20828
- ideal: limitedWidth.value
20829
- };
20830
- }
20831
- if (this.options.video.height) videoConstraints.height = {
20832
- ideal: this.options.video.height
21250
+ this.clearInitializationListeners = ()=>{
21251
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
21252
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.removeEventListener("play", onPlay);
21253
+ null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.removeEventListener("loadedmetadata", onLoadedMetaData);
21254
+ this.clearInitializationListeners = void 0;
21255
+ };
21256
+ const play = ()=>{
21257
+ try {
21258
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
21259
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.load();
21260
+ if (null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.paused) {
21261
+ 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)}`);
21262
+ this.rawVisualUserMedia.play().then(()=>{
21263
+ if (initializationId !== this.initializationId) return;
21264
+ if (!this.playingPromiseReached) {
21265
+ this.options.logger.debug("UserMedia: play promise successful. Playing now.");
21266
+ this.playingPromiseReached = true;
21267
+ }
21268
+ }).catch((exc)=>{
21269
+ if (initializationId !== this.initializationId) return;
21270
+ if (exc instanceof Error && !(null == params ? void 0 : params.recovering)) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
21271
+ else {
21272
+ unloadAllEventListeners();
21273
+ endedEarlyCallback(error_createError({
21274
+ message: "Failed to play user media upon play event.",
21275
+ exc,
21276
+ options: this.options
21277
+ }));
21278
+ }
21279
+ });
21280
+ }
21281
+ } catch (exc) {
21282
+ unloadAllEventListeners();
21283
+ endedEarlyCallback(exc);
21284
+ }
21285
+ };
21286
+ const fireCallbacks = ()=>{
21287
+ var _this_rawVisualUserMedia;
21288
+ const readyState = null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState;
21289
+ this.options.logger.debug(`UserMedia: fireCallbacks(readyState=${readyState}, onPlayReached=${this.onPlayReached}, onLoadedMetaDataReached=${this.onLoadedMetaDataReached})`);
21290
+ if (this.onPlayReached && this.onLoadedMetaDataReached) if (this.audioRecorder) try {
21291
+ this.audioRecorder.init(localMediaStream);
21292
+ const onAudioReady = ()=>{
21293
+ var _this_clearInitializationListeners, _this;
21294
+ if (initializationId !== this.initializationId || this.currentVisualStream !== localMediaStream) return;
21295
+ if (this.recording) this.audioRecord(audioCallback);
21296
+ else this.on("SENDING_FIRST_FRAME", ()=>{
21297
+ this.audioRecord(audioCallback);
21298
+ });
21299
+ null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
21300
+ videoCallback();
21301
+ };
21302
+ if (null == params ? void 0 : params.recovering) this.audioRecorder.resume().then(onAudioReady).catch(endedEarlyCallback);
21303
+ else onAudioReady();
21304
+ } catch (exc) {
21305
+ unloadAllEventListeners();
21306
+ this.stop(void 0, {
21307
+ preserveRecordingState: Boolean(null == params ? void 0 : params.recovering)
21308
+ });
21309
+ endedEarlyCallback(exc);
21310
+ }
21311
+ else {
21312
+ var _this_clearInitializationListeners, _this;
21313
+ null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
21314
+ videoCallback();
21315
+ }
21316
+ };
21317
+ const onPlay = ()=>{
21318
+ try {
21319
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3, _this_rawVisualUserMedia4, _this_rawVisualUserMedia5;
21320
+ this.logEvent("play", {
21321
+ readyState: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState,
21322
+ audio: isAudioEnabled(this.options),
21323
+ width: null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.width,
21324
+ height: null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia2.height,
21325
+ videoWidth: null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia3.videoWidth,
21326
+ videoHeight: null == (_this_rawVisualUserMedia4 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia4.videoHeight
21327
+ });
21328
+ null == (_this_rawVisualUserMedia5 = this.rawVisualUserMedia) || _this_rawVisualUserMedia5.removeEventListener("play", onPlay);
21329
+ if (this.hasEnded() || this.hasInvalidDimensions()) endedEarlyCallback(error_createError({
21330
+ message: "Already busy",
21331
+ explanation: "Probably another browser window is using your webcam?",
21332
+ options: this.options
21333
+ }));
21334
+ else {
21335
+ this.onPlayReached = true;
21336
+ fireCallbacks();
21337
+ }
21338
+ } catch (exc) {
21339
+ unloadAllEventListeners();
21340
+ endedEarlyCallback(exc);
21341
+ }
21342
+ };
21343
+ const onLoadedMetaData = ()=>{
21344
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3, _this_rawVisualUserMedia4, _this_rawVisualUserMedia5, _this_rawVisualUserMedia6;
21345
+ this.logEvent("loadedmetadata", {
21346
+ readyState: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState,
21347
+ paused: null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.paused,
21348
+ width: null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia2.width,
21349
+ height: null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia3.height,
21350
+ videoWidth: null == (_this_rawVisualUserMedia4 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia4.videoWidth,
21351
+ videoHeight: null == (_this_rawVisualUserMedia5 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia5.videoHeight
21352
+ });
21353
+ null == (_this_rawVisualUserMedia6 = this.rawVisualUserMedia) || _this_rawVisualUserMedia6.removeEventListener("loadedmetadata", onLoadedMetaData);
21354
+ if (!this.hasEnded() && !this.hasInvalidDimensions()) {
21355
+ this.emit("LOADED_META_DATA");
21356
+ this.onLoadedMetaDataReached = true;
21357
+ fireCallbacks();
21358
+ }
20833
21359
  };
20834
- }
20835
- loadUserMedia(params) {
20836
- if (this.userMediaLoaded) {
20837
- this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already loaded");
20838
- this.onUserMediaReady(params);
20839
- return;
20840
- } else if (this.userMediaLoading) return void this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already asking for permission");
20841
- this.options.logger.debug(`Recorder: loadUserMedia(${params ? string_pretty(params) : ""})`);
20842
- this.emit("LOADING_USER_MEDIA");
20843
21360
  try {
20844
- if (!navigator.mediaDevices) throw new Error("No media devices are available.");
20845
- this.userMediaTimeout = window.setTimeout(()=>{
20846
- if (!this.userMediaLoaded) {
20847
- this.userMediaLoading = false;
20848
- const err = util_getBrowser(this.options).getNoAccessIssue();
20849
- this.userMediaErrorCallback(err, {
20850
- audio: isAudioEnabled(this.options)
20851
- });
20852
- }
20853
- }, this.options.timeouts.userMedia);
20854
- this.userMediaLoading = true;
20855
- this.loadGenuineUserMedia(params);
20856
- } catch (exc) {
20857
- this.clearUserMediaTimeout();
20858
- this.options.logger.debug("Recorder: failed to load genuine user media");
20859
- this.userMediaLoading = false;
20860
- if (this.cameraRecovery) return void this.failCameraRecovery(exc);
20861
- const errorListeners = this.getListeners("ERROR");
20862
- if (null == errorListeners ? void 0 : errorListeners.length) this.emit("ERROR", {
20863
- exc
21361
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3;
21362
+ const videoTrack = media_getFirstVideoTrack(localMediaStream);
21363
+ this.videoTrackLabel = null == videoTrack ? void 0 : videoTrack.label;
21364
+ if (videoTrack) if (videoTrack.enabled) if (util_isVirtualCamera(videoTrack)) throw error_createError({
21365
+ message: "Virtual cameras are not allowed.",
21366
+ explanation: `Please use a real camera, not "${videoTrack.label}".`,
21367
+ options: this.options
20864
21368
  });
20865
21369
  else {
20866
- this.options.logger.debug("Recorder: no error listeners attached but throwing exception further");
20867
- throw exc;
21370
+ let description = "";
21371
+ if (videoTrack.label.length > 0) description = description.concat(videoTrack.label);
21372
+ description = description.concat(` with enabled=${videoTrack.enabled}, muted=${videoTrack.muted}, readyState=${videoTrack.readyState}`);
21373
+ this.options.logger.debug(`UserMedia: ${videoTrack.kind} detected. ${description}`);
20868
21374
  }
21375
+ else throw error_createError({
21376
+ message: "Webcam is disabled",
21377
+ explanation: "The video track seems to be disabled. Enable it in your system.",
21378
+ options: this.options
21379
+ });
21380
+ else this.options.logger.debug("UserMedia: detected (but no video tracks exist");
21381
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.addEventListener("loadedmetadata", onLoadedMetaData);
21382
+ null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.addEventListener("play", onPlay);
21383
+ null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) || _this_rawVisualUserMedia2.removeEventListener("error", this.handleMediaError);
21384
+ null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) || _this_rawVisualUserMedia3.addEventListener("error", this.handleMediaError);
21385
+ this.setVisualStream(localMediaStream);
21386
+ play();
21387
+ } catch (exc) {
21388
+ unloadAllEventListeners();
21389
+ endedEarlyCallback(exc);
20869
21390
  }
20870
21391
  }
20871
- executeCommand(command) {
20872
- if (this.unloaded) return;
21392
+ getVideoTrackLabel() {
21393
+ return this.videoTrackLabel;
21394
+ }
21395
+ isReady() {
21396
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
21397
+ return Boolean((null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.srcObject) || (null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.currentSrc));
21398
+ }
21399
+ stop(visualStream, params) {
20873
21400
  try {
20874
- if (command.args) {
20875
- const debugLine = string_summarize(`Server commanded: ${command.command} with:`, command.args);
20876
- this.options.logger.debug(debugLine);
20877
- } else this.options.logger.debug(`Server commanded: ${command.command}`);
20878
- switch(command.command){
20879
- case "ready":
20880
- this.emit("SERVER_READY");
20881
- if (!this.userMediaTimeout) if (this.options.loadUserMediaOnRecord) this.show();
20882
- else this.loadUserMedia();
20883
- break;
20884
- case "preview":
20885
- this.preview(command.args);
20886
- break;
20887
- case "error":
20888
- {
20889
- var _command_args_err, _command_args, _command_args1;
20890
- let explanation = "(No explanation given)";
20891
- if (null == (_command_args = command.args) ? void 0 : null == (_command_args_err = _command_args.err) ? void 0 : _command_args_err.message) explanation = command.args.err.message;
20892
- const err = error_createError({
20893
- message: "Websocket error from Videomail server",
20894
- explanation,
20895
- err: deserializeError(null == (_command_args1 = command.args) ? void 0 : _command_args1.err),
20896
- options: this.options
20897
- });
20898
- this.emit("ERROR", {
20899
- err
20900
- });
20901
- break;
20902
- }
20903
- case "confirmFrame":
20904
- this.updateFrameProgress(command.args);
20905
- break;
20906
- case "confirmSample":
20907
- this.updateSampleProgress(command.args);
20908
- break;
20909
- case "beginAudioEncoding":
20910
- this.emit("BEGIN_AUDIO_ENCODING");
20911
- break;
20912
- case "beginVideoEncoding":
20913
- this.emit("BEGIN_VIDEO_ENCODING");
20914
- break;
20915
- default:
20916
- {
20917
- const err = error_createError({
20918
- message: `Unknown server command: ${command.command}`,
20919
- options: this.options
20920
- });
20921
- this.emit("ERROR", {
20922
- err
20923
- });
20924
- break;
20925
- }
21401
+ let chosenStream = visualStream;
21402
+ if (!(null == params ? void 0 : params.aboutToInitialize)) {
21403
+ var _this_clearInitializationListeners, _this, _this_audioRecorder;
21404
+ this.initializationId++;
21405
+ null == (_this_clearInitializationListeners = (_this = this).clearInitializationListeners) || _this_clearInitializationListeners.call(_this);
21406
+ null != chosenStream || (chosenStream = this.currentVisualStream);
21407
+ const tracks = null == chosenStream ? void 0 : chosenStream.getTracks();
21408
+ if (tracks) tracks.forEach((track)=>{
21409
+ track.stop();
21410
+ });
21411
+ this.setVisualStream();
21412
+ null == (_this_audioRecorder = this.audioRecorder) || _this_audioRecorder.stop();
21413
+ this.audioRecorder = void 0;
20926
21414
  }
21415
+ if (!(null == params ? void 0 : params.preserveRecordingState)) this.paused = this.recording = false;
20927
21416
  } catch (exc) {
20928
21417
  this.emit("ERROR", {
20929
21418
  exc
20930
21419
  });
20931
21420
  }
20932
21421
  }
20933
- isNotifying() {
20934
- return this.visuals.isNotifying();
20935
- }
20936
- isHidden() {
20937
- return !this.recorderElement || html_isHidden(this.recorderElement);
20938
- }
20939
- writeCommand(command, args, cb) {
20940
- if (this.connected) {
20941
- if (this.stream) {
20942
- if (args) this.options.logger.debug(`$ ${command} with ${string_pretty(args)}`);
20943
- else this.options.logger.debug(`$ ${command}`);
20944
- const commandObj = {
20945
- command,
20946
- args
20947
- };
20948
- this.writeStream(Buffer.from(JSON.stringify(commandObj)), cb ? {
20949
- onFlushedCallback: ()=>{
20950
- queueMicrotask(cb);
20951
- }
20952
- } : void 0);
20953
- }
20954
- } else {
20955
- this.options.logger.debug(`Reconnecting for the command ${command} …`);
20956
- this.initSocket(()=>{
20957
- this.writeCommand(command, args, cb);
20958
- });
20959
- }
20960
- }
20961
- cancelAnimationFrame() {
20962
- var _this_loop;
20963
- null == (_this_loop = this.loop) || _this_loop.dispose();
20964
- }
20965
- getIntervalSum() {
20966
- if (!this.loop) return;
20967
- return this.loop.getElapsedTime();
20968
- }
20969
- getAvgInterval() {
20970
- var _this_getIntervalSum;
20971
- const intervalSum = null != (_this_getIntervalSum = this.getIntervalSum()) ? _this_getIntervalSum : 0;
20972
- if (!this.hasValidInterval(intervalSum)) return;
20973
- return intervalSum / this.framesCount;
20974
- }
20975
- getAvgFps() {
20976
- var _this_getIntervalSum;
20977
- const intervalSum = null != (_this_getIntervalSum = this.getIntervalSum()) ? _this_getIntervalSum : 0;
20978
- if (!this.hasValidInterval(intervalSum)) return;
20979
- return this.framesCount / intervalSum * 1000;
20980
- }
20981
- hasValidInterval(intervalSum) {
20982
- return intervalSum > 0;
20983
- }
20984
- getRecordingStats() {
20985
- return this.recordingStats;
20986
- }
20987
- getAudioSampleRate() {
20988
- var _this_recordingAudioSampleRate;
20989
- var _this_userMedia;
20990
- return null != (_this_recordingAudioSampleRate = this.recordingAudioSampleRate) ? _this_recordingAudioSampleRate : null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.getAudioSampleRate();
20991
- }
20992
- stop(params) {
20993
- var _this_loop;
20994
- this.cancelCameraRecovery();
20995
- if (!this.userMedia) throw new Error("No user media defined, unable to stop");
20996
- this.options.logger.debug(`Recorder: stop(${params ? string_pretty(params) : ""})`);
20997
- this.recordingActive = false;
20998
- const limitReached = null == params ? void 0 : params.limitReached;
20999
- this.emit("STOPPING", {
21000
- limitReached
21001
- });
21002
- null == (_this_loop = this.loop) || _this_loop.complete();
21003
- this.stopTimeout = window.setTimeout(()=>{
21004
- this.stopTimeout = void 0;
21005
- this.stopTime = Date.now();
21006
- const videoType = this.replay.getVideoType();
21007
- if (!videoType) throw new Error("Unable to video record when no video type is defined.");
21008
- this.recordingStats = {
21009
- avgFps: this.getAvgFps(),
21010
- wantedFps: this.options.video.fps,
21011
- avgInterval: this.getAvgInterval(),
21012
- wantedInterval: 1e3 / this.options.video.fps,
21013
- intervalSum: this.getIntervalSum(),
21014
- framesCount: this.framesCount,
21015
- videoType
21016
- };
21017
- if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
21018
- if (isAudioEnabled(this.options) && this.userMedia) {
21019
- var _this_recordingAudioSampleRate;
21020
- this.recordingStats.samplesCount = this.samplesCount;
21021
- this.recordingStats.sampleRate = null != (_this_recordingAudioSampleRate = this.recordingAudioSampleRate) ? _this_recordingAudioSampleRate : this.userMedia.getAudioSampleRate();
21022
- }
21023
- this.writeCommand("stop", this.recordingStats, ()=>{
21024
- this.emit("STOPPED", {
21025
- recordingStats: this.recordingStats
21026
- });
21027
- });
21028
- this.reset();
21029
- }, 60);
21422
+ createCanvas() {
21423
+ const canvas = document.createElement("canvas");
21424
+ const rawWidthDimension = this.getRawWidth(true);
21425
+ if (rawWidthDimension.value) canvas.width = rawWidthDimension.value;
21426
+ const rawHeight = this.getRawHeight(true);
21427
+ if (rawHeight) canvas.height = rawHeight;
21428
+ return canvas;
21030
21429
  }
21031
- back(cb) {
21032
- this.emit("GOING_BACK");
21033
- this.key = void 0;
21034
- this.recordingDimensions = void 0;
21035
- this.unloaded = false;
21036
- this.show();
21037
- this.writeCommand("back", void 0, cb);
21430
+ getVideoHeight() {
21431
+ if (!this.rawVisualUserMedia) return;
21432
+ return this.rawVisualUserMedia.videoHeight || this.rawVisualUserMedia.height;
21038
21433
  }
21039
- reInitializeAudio() {
21040
- var _this_userMedia;
21041
- this.options.logger.debug("Recorder: reInitializeAudio()");
21042
- this.clearUserMediaTimeout();
21043
- this.userMediaLoading = false;
21044
- null == (_this_userMedia = this.userMedia) || _this_userMedia.stop();
21045
- this.recordingDimensions = void 0;
21046
- this.userMediaLoaded = this.key = this.canvas = this.ctx = void 0;
21047
- this.loadUserMedia();
21434
+ getVideoWidth() {
21435
+ if (!this.rawVisualUserMedia) return;
21436
+ return this.rawVisualUserMedia.videoWidth || this.rawVisualUserMedia.width;
21048
21437
  }
21049
- unload(params) {
21050
- if (this.unloaded || !this.built) return;
21051
- const e = null == params ? void 0 : params.e;
21052
- let cause;
21053
- if (e) cause = e.type;
21054
- const prettyCause = cause ? `, cause: ${cause}` : "";
21055
- this.options.logger.debug(`Recorder: unload()${prettyCause}`);
21056
- this.recordingActive = false;
21057
- this.mediaTransmissionStarted = false;
21058
- this.reset();
21059
- this.recordingDimensions = void 0;
21060
- this.clearUserMediaTimeout();
21061
- this.clearConnectionTimeout();
21062
- this.clearReconnectTimeout();
21063
- this.clearRetryTimeout();
21064
- this.clearStopTimeout();
21065
- this.stopPings();
21066
- this.userMediaLoading = false;
21067
- this.connecting = false;
21068
- this.reconnecting = false;
21069
- this.reconnectAttempts = 0;
21070
- if (this.userMedia) this.userMedia.unloadRemainingEventListeners();
21071
- if (this.submitting) ;
21072
- else if (this.stream) this.discardSocket();
21073
- this.unloaded = true;
21074
- this.built = this.connecting = this.connected = false;
21438
+ hasVideoWidth() {
21439
+ const videoWidth = this.getVideoWidth();
21440
+ return videoWidth && videoWidth > 0;
21075
21441
  }
21076
- reset() {
21077
- this.cancelCameraRecovery();
21078
- if (!this.unloaded) {
21079
- var _this_userMedia;
21080
- this.options.logger.debug("Recorder: reset()");
21081
- this.emit("RESETTING");
21082
- this.cancelAnimationFrame();
21083
- null == (_this_userMedia = this.userMedia) || _this_userMedia.stop();
21084
- this.replay.reset();
21085
- this.userMediaLoaded = this.key = this.canvas = this.ctx = this.recordingBuffer = void 0;
21442
+ getRawWidth(responsive) {
21443
+ let rawWidth;
21444
+ const widthDimension = {
21445
+ unit: "px"
21446
+ };
21447
+ rawWidth = responsive ? this.getVideoWidth() : this.options.video.width ? this.options.video.width : this.getVideoWidth();
21448
+ if (responsive) {
21449
+ const widthDimension = this.recorder.limitWidth(rawWidth);
21450
+ rawWidth = null == widthDimension ? void 0 : widthDimension.value;
21086
21451
  }
21452
+ widthDimension.value = rawWidth;
21453
+ return widthDimension;
21087
21454
  }
21088
- clearUserMediaTimeout() {
21089
- if (void 0 !== this.userMediaTimeout) {
21090
- this.options.logger.debug("Recorder: clearUserMediaTimeout()");
21091
- window.clearTimeout(this.userMediaTimeout);
21092
- this.userMediaTimeout = void 0;
21455
+ getRawHeight(responsive) {
21456
+ let rawHeight;
21457
+ if (responsive) {
21458
+ rawHeight = this.options.video.height || this.getVideoHeight();
21459
+ const ratio = this.recorder.getRatio();
21460
+ const rawWidth = this.getRawWidth(responsive);
21461
+ if (void 0 !== ratio && rawWidth.value) rawHeight = rawWidth.value * ratio;
21462
+ } else rawHeight = this.options.video.height ? this.options.video.height : this.getVideoHeight();
21463
+ if (responsive) {
21464
+ const heightDimension = this.recorder.limitHeight(rawHeight);
21465
+ rawHeight = heightDimension.value;
21093
21466
  }
21467
+ return rawHeight;
21094
21468
  }
21095
- clearStopTimeout() {
21096
- if (void 0 === this.stopTimeout) return;
21097
- window.clearTimeout(this.stopTimeout);
21098
- this.stopTimeout = void 0;
21099
- }
21100
- validate() {
21101
- return Boolean(this.key) && void 0 === this.canvas;
21469
+ getRawVisuals() {
21470
+ return this.rawVisualUserMedia;
21102
21471
  }
21103
- isReady() {
21104
- var _this_userMedia;
21105
- return null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isReady();
21472
+ pause() {
21473
+ this.paused = true;
21106
21474
  }
21107
- finishCameraRecovery(restored) {
21108
- this.clearUserMediaTimeout();
21109
- const resolve = this.resolveCameraRecovery;
21110
- const resumeRequested = restored && this.resumeAfterCameraRecovery;
21111
- this.cameraRecovery = void 0;
21112
- this.resolveCameraRecovery = void 0;
21113
- this.resumeAfterCameraRecovery = false;
21114
- null == resolve || resolve(restored);
21115
- if (resumeRequested && this.isPaused()) this.resume();
21475
+ isPaused() {
21476
+ return this.paused;
21116
21477
  }
21117
- cancelCameraRecovery() {
21118
- var _this_userMedia;
21119
- if (!this.cameraRecovery) return;
21120
- this.mediaRequestId++;
21121
- this.userMediaLoading = false;
21122
- this.userMediaLoaded = false;
21123
- this.clearUserMediaTimeout();
21124
- null == (_this_userMedia = this.userMedia) || _this_userMedia.stop(void 0, {
21125
- preserveRecordingState: true
21126
- });
21127
- this.finishCameraRecovery(false);
21478
+ resume() {
21479
+ this.paused = false;
21128
21480
  }
21129
- failCameraRecovery(exc) {
21130
- this.cancelCameraRecovery();
21131
- const err = error_createError({
21132
- message: "Camera recovery failed",
21133
- explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
21134
- exc,
21135
- options: this.options
21136
- });
21137
- this.emit("CAMERA_RECOVERY_FAILED", {
21138
- err
21139
- });
21481
+ record() {
21482
+ this.recording = true;
21140
21483
  }
21141
- recoverCamera() {
21142
- if (this.cameraRecovery) return this.cameraRecovery;
21143
- if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
21144
- const liveStream = this.userMedia.hasLiveStream();
21145
- if (liveStream && this.isRecording()) return Promise.resolve(true);
21146
- if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
21147
- const recovery = new Promise((resolve)=>{
21148
- this.resolveCameraRecovery = resolve;
21149
- });
21150
- this.cameraRecovery = recovery;
21151
- this.emit("CAMERA_RECOVERING");
21152
- if (liveStream) {
21153
- this.userMediaTimeout = window.setTimeout(()=>{
21154
- this.failCameraRecovery(new Error("Camera playback recovery timed out."));
21155
- }, this.options.timeouts.userMedia);
21156
- this.userMedia.restorePlayback().then(()=>{
21157
- if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
21158
- }).catch((exc)=>{
21159
- if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
21160
- });
21161
- } else {
21162
- this.userMediaLoaded = false;
21163
- this.loadUserMedia({
21164
- recovering: true
21165
- });
21166
- }
21167
- return recovery;
21484
+ isRecording() {
21485
+ return this.recording;
21168
21486
  }
21169
- pause(params) {
21170
- var _this_userMedia;
21171
- this.cancelCameraRecovery();
21172
- this.resumeAfterDrain = false;
21173
- if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
21174
- else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
21175
- null == (_this_userMedia = this.userMedia) || _this_userMedia.pause();
21176
- this.loop.stop();
21177
- this.emit("PAUSED");
21178
- this.sendPings();
21487
+ getAudioSampleRate() {
21488
+ if (this.audioRecorder) return this.audioRecorder.getSampleRate();
21489
+ return -1;
21179
21490
  }
21180
- resume() {
21181
- var _this_userMedia;
21182
- if (this.cameraRecovery || !(null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.hasLiveStream())) {
21183
- this.recoverCamera();
21184
- this.resumeAfterCameraRecovery = Boolean(this.cameraRecovery);
21185
- return;
21186
- }
21187
- this.options.logger.debug(`Recorder: resume() with frame ${this.framesCount}`);
21188
- this.stopPings();
21189
- this.emit("RESUMING");
21190
- this.userMedia.resume();
21191
- if (this.streamBackpressured) this.resumeAfterDrain = true;
21192
- else this.loop.start();
21491
+ getCharacteristics() {
21492
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1, _this_rawVisualUserMedia2, _this_rawVisualUserMedia3, _this_rawVisualUserMedia4;
21493
+ return {
21494
+ audioSampleRate: this.getAudioSampleRate(),
21495
+ muted: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.muted,
21496
+ width: null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia1.width,
21497
+ height: null == (_this_rawVisualUserMedia2 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia2.height,
21498
+ videoWidth: null == (_this_rawVisualUserMedia3 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia3.videoWidth,
21499
+ videoHeight: null == (_this_rawVisualUserMedia4 = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia4.videoHeight
21500
+ };
21193
21501
  }
21194
- onFlushed(opts) {
21195
- const frameNumber = opts.frameNumber;
21196
- if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
21502
+ constructor(recorder, options){
21503
+ 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)=>{
21504
+ var _this_rawVisualUserMedia, _this_rawVisualUserMedia1;
21505
+ this.logEvent(e.type, {
21506
+ readyState: null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) ? void 0 : _this_rawVisualUserMedia.readyState
21507
+ });
21508
+ null == (_this_rawVisualUserMedia1 = this.rawVisualUserMedia) || _this_rawVisualUserMedia1.removeEventListener(e.type, this.outputEvent);
21509
+ }), userMedia_define_property(this, "handleMediaError", (event)=>{
21510
+ this.options.logger.warn(`Caught video element error event: ${string_pretty(event)}`);
21511
+ });
21512
+ this.recorder = recorder;
21513
+ this.rawVisualUserMedia = recorder.getRawVisualUserMedia();
21514
+ mediaEvents.forEach((eventName)=>{
21515
+ var _this_rawVisualUserMedia;
21516
+ null == (_this_rawVisualUserMedia = this.rawVisualUserMedia) || _this_rawVisualUserMedia.addEventListener(eventName, this.outputEvent, false);
21517
+ });
21197
21518
  }
21198
- draw(_deltaTime, elapsedTime) {
21199
- if (this.recordingTransportFailed) return;
21200
- if (!this.userMedia) throw new Error("No user media defined, unable to draw on canvas");
21519
+ }
21520
+ const visuals_userMedia = UserMedia;
21521
+ function recorder_define_property(obj, key, value) {
21522
+ if (key in obj) Object.defineProperty(obj, key, {
21523
+ value: value,
21524
+ enumerable: true,
21525
+ configurable: true,
21526
+ writable: true
21527
+ });
21528
+ else obj[key] = value;
21529
+ return obj;
21530
+ }
21531
+ class Recorder extends capture {
21532
+ executeCommand(command) {
21533
+ if (this.unloaded) return;
21201
21534
  try {
21202
- if (!this.isPaused() && this.stream && this.ctx) {
21203
- var _this_frame, _this_recordingBuffer;
21204
- if (0 === this.framesCount) this.emit("SENDING_FIRST_FRAME");
21205
- this.framesCount++;
21206
- const imageSource = this.userMedia.getRawVisuals();
21207
- if (this.canvas && imageSource) this.ctx.drawImage(imageSource, 0, 0, this.canvas.width, this.canvas.height);
21208
- else throw new Error("Unable to draw an image without a defined canvas");
21209
- this.recordingBuffer = null == (_this_frame = this.frame) ? void 0 : _this_frame.toBuffer();
21210
- const recordingBufferLength = null == (_this_recordingBuffer = this.recordingBuffer) ? void 0 : _this_recordingBuffer.length;
21211
- if (recordingBufferLength) {
21212
- if (this.recordingBuffer) {
21213
- const frameControlBuffer = Buffer.from(JSON.stringify({
21214
- frameNumber: this.framesCount
21215
- }));
21216
- const frameBuffer = Buffer.concat([
21217
- this.recordingBuffer,
21218
- frameControlBuffer
21219
- ]);
21220
- this.mediaTransmissionStarted = true;
21221
- this.writeStream(frameBuffer, {
21222
- frameNumber: this.framesCount,
21223
- onFlushedCallback: this.onFlushed.bind(this)
21535
+ if (command.args) {
21536
+ const debugLine = string_summarize(`Server commanded: ${command.command} with:`, command.args);
21537
+ this.options.logger.debug(debugLine);
21538
+ } else this.options.logger.debug(`Server commanded: ${command.command}`);
21539
+ switch(command.command){
21540
+ case "ready":
21541
+ this.emit("SERVER_READY");
21542
+ if (!this.userMediaTimeout) if (this.options.loadUserMediaOnRecord) this.show();
21543
+ else this.loadUserMedia();
21544
+ break;
21545
+ case "preview":
21546
+ this.preview(command.args);
21547
+ break;
21548
+ case "error":
21549
+ {
21550
+ var _command_args_err, _command_args, _command_args1;
21551
+ let explanation = "(No explanation given)";
21552
+ if (null == (_command_args = command.args) ? void 0 : null == (_command_args_err = _command_args.err) ? void 0 : _command_args_err.message) explanation = command.args.err.message;
21553
+ const err = error_createError({
21554
+ message: "Websocket error from Videomail server",
21555
+ explanation,
21556
+ err: deserializeError(null == (_command_args1 = command.args) ? void 0 : _command_args1.err),
21557
+ options: this.options
21224
21558
  });
21225
- this.visuals.checkTimer(elapsedTime);
21559
+ this.emit("ERROR", {
21560
+ err
21561
+ });
21562
+ break;
21563
+ }
21564
+ case "confirmFrame":
21565
+ this.updateFrameProgress(command.args);
21566
+ break;
21567
+ case "confirmSample":
21568
+ this.updateSampleProgress(command.args);
21569
+ break;
21570
+ case "beginAudioEncoding":
21571
+ this.emit("BEGIN_AUDIO_ENCODING");
21572
+ break;
21573
+ case "beginVideoEncoding":
21574
+ this.emit("BEGIN_VIDEO_ENCODING");
21575
+ break;
21576
+ default:
21577
+ {
21578
+ const err = error_createError({
21579
+ message: `Unknown server command: ${command.command}`,
21580
+ options: this.options
21581
+ });
21582
+ this.emit("ERROR", {
21583
+ err
21584
+ });
21585
+ break;
21226
21586
  }
21227
- } else throw error_createError({
21228
- message: "Failed to extract webcam data.",
21229
- options: this.options
21230
- });
21231
21587
  }
21232
21588
  } catch (exc) {
21233
21589
  this.emit("ERROR", {
@@ -21235,156 +21591,34 @@ var __webpack_exports__ = {};
21235
21591
  });
21236
21592
  }
21237
21593
  }
21238
- createLoop() {
21239
- const newLoop = animitter_default()({
21240
- fps: this.options.video.fps
21241
- }, this.draw.bind(this));
21242
- this.originalAnimationFrameObject = newLoop.getRequestAnimationFrameObject();
21243
- return newLoop;
21244
- }
21245
- record() {
21246
- if (this.unloaded || this.cameraRecovery) return;
21247
- if (!this.connected) {
21248
- this.options.logger.debug("Recorder: reconnecting before recording …");
21249
- this.initSocket(()=>{
21250
- this.once("SERVER_READY", ()=>{
21251
- if (this.unloaded) return;
21252
- if (this.userMediaLoaded || this.options.loadUserMediaOnRecord) this.record();
21253
- else this.once("USER_MEDIA_READY", ()=>{
21254
- if (!this.unloaded) this.record();
21255
- });
21256
- });
21257
- });
21258
- return;
21259
- }
21260
- if (!this.userMediaLoaded) {
21261
- if (this.options.loadUserMediaOnRecord) this.loadUserMedia({
21262
- recordWhenReady: true
21263
- });
21264
- else {
21265
- const err = error_createError({
21266
- message: "Load and enable your camera first",
21267
- options: this.options
21268
- });
21269
- this.emit("ERROR", {
21270
- err
21271
- });
21272
- }
21273
- return;
21274
- }
21275
- this.recordingDimensions = void 0;
21276
- try {
21277
- if (!this.userMedia) throw new Error("No user media defined, unable to create canvas");
21278
- this.canvas = this.userMedia.createCanvas();
21279
- } catch (exc) {
21280
- const err = error_createError({
21281
- exc,
21282
- options: this.options
21283
- });
21284
- this.emit("ERROR", {
21285
- err
21286
- });
21287
- return;
21288
- }
21289
- this.ctx = this.canvas.getContext("2d");
21290
- if (!this.canvas.width) {
21291
- const err = error_createError({
21292
- message: "Canvas has an invalid width.",
21293
- options: this.options
21294
- });
21295
- this.emit("ERROR", {
21296
- err
21297
- });
21298
- return;
21299
- }
21300
- if (!this.canvas.height) {
21301
- const err = error_createError({
21302
- message: "Canvas has an invalid height.",
21303
- options: this.options
21304
- });
21305
- this.emit("ERROR", {
21306
- err
21307
- });
21308
- return;
21309
- }
21310
- this.recordingDimensions = {
21311
- width: this.canvas.width,
21312
- height: this.canvas.height
21313
- };
21314
- this.frame = new canvas_to_buffer_modern_r(this.canvas, this.options.image.types, this.options.image.quality);
21315
- this.options.logger.debug("Recorder: record()");
21316
- this.recordingActive = true;
21317
- this.recordingTransportFailed = false;
21594
+ unload(params) {
21595
+ if (this.unloaded || !this.built) return;
21596
+ const e = null == params ? void 0 : params.e;
21597
+ let cause;
21598
+ if (e) cause = e.type;
21599
+ const prettyCause = cause ? `, cause: ${cause}` : "";
21600
+ this.options.logger.debug(`Recorder: unload()${prettyCause}`);
21601
+ this.recordingActive = false;
21318
21602
  this.mediaTransmissionStarted = false;
21319
- this.recordingAudioSampleRate = void 0;
21320
- this.userMedia.record();
21321
- this.emit("RECORDING", {
21322
- framesCount: this.framesCount
21323
- });
21324
- this.loop.on("update", (_deltaTime, elapsedTime)=>{
21325
- let avgFPS;
21326
- avgFPS = 0 !== elapsedTime ? Math.round(this.framesCount / elapsedTime * 1000) : void 0;
21327
- this.options.logger.debug(`Recorder updates avgFps = ${avgFPS} at frame ${this.framesCount}`);
21328
- });
21329
- this.loop.start();
21330
- }
21331
- setAnimationFrameObject(newObj) {
21332
- if (this.loop) {
21333
- const isRecording = this.isRecording();
21334
- this.loop.stop();
21335
- this.loop.setRequestAnimationFrameObject(newObj);
21336
- if (isRecording) this.loop.start();
21337
- }
21338
- }
21339
- restoreAnimationFrameObject() {
21340
- this.options.logger.debug("Recorder: restoreAnimationFrameObject()");
21341
- this.setAnimationFrameObject(this.originalAnimationFrameObject);
21342
- }
21343
- loopWithTimeouts() {
21344
- this.options.logger.debug("Recorder: loopWithTimeouts()");
21345
- const wantedInterval = 1e3 / this.options.video.fps;
21346
- let processingTime = 0;
21347
- let start;
21348
- const raf = (fn)=>setTimeout(()=>{
21349
- start = Date.now();
21350
- fn();
21351
- processingTime = Date.now() - start;
21352
- }, wantedInterval - processingTime);
21353
- const cancel = (id)=>{
21354
- window.clearTimeout(id);
21355
- };
21356
- this.setAnimationFrameObject({
21357
- requestAnimationFrame: raf,
21358
- cancelAnimationFrame: cancel
21359
- });
21603
+ this.reset();
21604
+ this.recordingDimensions = void 0;
21605
+ this.clearUserMediaTimeout();
21606
+ this.clearConnectionTimeout();
21607
+ this.clearReconnectTimeout();
21608
+ this.clearRetryTimeout();
21609
+ this.clearStopTimeout();
21610
+ this.stopPings();
21611
+ this.userMediaLoading = false;
21612
+ this.connecting = false;
21613
+ this.reconnecting = false;
21614
+ this.reconnectAttempts = 0;
21615
+ if (this.userMedia) this.userMedia.unloadRemainingEventListeners();
21616
+ this.unloaded = true;
21617
+ this.discardSocket();
21618
+ this.built = this.connecting = this.connected = false;
21360
21619
  }
21361
21620
  correctDimensions() {
21362
- if (!this.recorderElement) return;
21363
- const widthDimension = dimensions_useFullWidth(this.options.video.mobileBreakPoint);
21364
- if (this.options.video.stretch || widthDimension) {
21365
- this.recorderElement.style.width = "100%";
21366
- this.recorderElement.style.removeProperty("height");
21367
- } else {
21368
- if (this.options.video.width) {
21369
- const recorderWidth = this.getRecorderWidth(true);
21370
- if (null == recorderWidth ? void 0 : recorderWidth.value) this.recorderElement.width = recorderWidth.value;
21371
- else this.recorderElement.style.removeProperty("width");
21372
- }
21373
- if (this.options.video.height) {
21374
- const recorderHeight = this.getRecorderHeight(true);
21375
- if (null == recorderHeight ? void 0 : recorderHeight.value) this.recorderElement.height = recorderHeight.value;
21376
- else this.recorderElement.style.removeProperty("height");
21377
- }
21378
- }
21379
- }
21380
- switchFacingMode() {
21381
- if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
21382
- if ("user" === this.facingMode) this.facingMode = "environment";
21383
- else if ("environment" === this.facingMode) this.facingMode = "user";
21384
- else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
21385
- this.loadGenuineUserMedia({
21386
- switchingFacingMode: this.facingMode
21387
- });
21621
+ this.dimensions.correctDimensions();
21388
21622
  }
21389
21623
  initEvents() {
21390
21624
  this.options.logger.debug("Recorder: initEvents()");
@@ -21457,101 +21691,20 @@ var __webpack_exports__ = {};
21457
21691
  }
21458
21692
  this.built = true;
21459
21693
  }
21460
- isPaused() {
21461
- var _this_userMedia;
21462
- return (null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.isPaused()) && !this.loop.isRunning();
21463
- }
21464
- isRecording() {
21465
- var _this_loop;
21466
- return Boolean((null == (_this_loop = this.loop) ? void 0 : _this_loop.isRunning()) && !this.isPaused() && !this.isNotifying() && this.stream && !this.stream.destroyed);
21467
- }
21468
- hide() {
21469
- if (!this.isHidden()) {
21470
- if (this.recorderElement) html_hideElement(this.recorderElement);
21471
- this.clearUserMediaTimeout();
21472
- this.clearRetryTimeout();
21473
- }
21474
- }
21475
- isUnloaded() {
21476
- return this.unloaded;
21477
- }
21478
21694
  getRecorderWidth(responsive) {
21479
- var _this_userMedia;
21480
- if (null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.hasVideoWidth()) return this.userMedia.getRawWidth(responsive);
21481
- else if (responsive && this.options.video.width) return this.limitWidth(this.options.video.width);
21482
- const dimension = {
21483
- unit: "px",
21484
- value: this.options.video.width
21485
- };
21486
- return dimension;
21487
- }
21488
- getRecordingDimensions() {
21489
- return this.recordingDimensions && recorder_object_spread({}, this.recordingDimensions);
21695
+ return this.dimensions.getRecorderWidth(responsive);
21490
21696
  }
21491
21697
  getRecorderHeight(responsive, useBoundingClientRect) {
21492
- let recorderHeight;
21493
- if (this.recorderElement && useBoundingClientRect) {
21494
- const height = this.recorderElement.getBoundingClientRect().height;
21495
- recorderHeight = {
21496
- unit: "px",
21497
- value: height
21498
- };
21499
- } else if (this.userMedia) {
21500
- let height = this.userMedia.getRawHeight(responsive);
21501
- if (void 0 !== height && this.options.video.width) {
21502
- const ratio = this.getRatio();
21503
- if (void 0 !== ratio) {
21504
- const idealHeight = this.options.video.width * ratio;
21505
- height = Math.min(idealHeight, height);
21506
- }
21507
- }
21508
- recorderHeight = {
21509
- unit: "px",
21510
- value: height
21511
- };
21512
- } else if (responsive && this.options.video.height) recorderHeight = this.calculateHeight(responsive);
21513
- else if (this.options.video.height) {
21514
- const height = this.options.video.height;
21515
- recorderHeight = {
21516
- unit: "px",
21517
- value: height
21518
- };
21519
- }
21520
- return recorderHeight;
21698
+ return this.dimensions.getRecorderHeight(responsive, useBoundingClientRect);
21521
21699
  }
21522
21700
  getRatio() {
21523
- let ratio;
21524
- if (this.userMedia) {
21525
- const userMediaVideoWidth = this.userMedia.getVideoWidth();
21526
- const userMediaVideoHeight = this.userMedia.getVideoHeight();
21527
- if (!userMediaVideoWidth || userMediaVideoWidth < 1) ratio = this.visuals.getRatio();
21528
- else if (userMediaVideoHeight) ratio = userMediaVideoHeight / userMediaVideoWidth;
21529
- } else ratio = dimensions_getRatio(this.options);
21530
- return ratio;
21701
+ return this.dimensions.getRatio();
21531
21702
  }
21532
21703
  calculateWidth(responsive) {
21533
- let videoHeight;
21534
- if (this.userMedia) videoHeight = this.userMedia.getVideoHeight();
21535
- else if (this.recorderElement) videoHeight = this.recorderElement.videoHeight || this.recorderElement.height;
21536
- const ratio = this.getRatio();
21537
- return dimensions_calculateWidth(responsive, this.options, videoHeight, ratio);
21704
+ return this.dimensions.calculateWidth(responsive);
21538
21705
  }
21539
21706
  calculateHeight(responsive) {
21540
- let videoDimension;
21541
- if (this.userMedia) {
21542
- const videoHeight = this.userMedia.getVideoHeight();
21543
- videoDimension = {
21544
- value: videoHeight,
21545
- unit: "px"
21546
- };
21547
- } else if (this.recorderElement) {
21548
- const videoHeight = this.recorderElement.videoHeight || this.recorderElement.height;
21549
- videoDimension = {
21550
- value: videoHeight,
21551
- unit: "px"
21552
- };
21553
- } else videoDimension = dimensions_calculateHeight(responsive, void 0, this.options, this.getRatio(), this.recorderElement);
21554
- return videoDimension;
21707
+ return this.dimensions.calculateHeight(responsive);
21555
21708
  }
21556
21709
  getRawVisualUserMedia() {
21557
21710
  return this.recorderElement;
@@ -21568,14 +21721,14 @@ var __webpack_exports__ = {};
21568
21721
  limitHeight(height) {
21569
21722
  return this.visuals.limitHeight(height);
21570
21723
  }
21571
- isUserMediaLoaded() {
21572
- return this.userMediaLoaded;
21573
- }
21574
21724
  constructor(visuals, replay, options){
21575
- 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);
21576
- this.visuals = visuals;
21577
- this.replay = replay;
21578
- this.facingMode = options.video.facingMode;
21725
+ super(visuals, replay, options), recorder_define_property(this, "dimensions", void 0);
21726
+ this.dimensions = new recorder_dimensions({
21727
+ options,
21728
+ visuals,
21729
+ getRecorderElement: ()=>this.recorderElement,
21730
+ getUserMedia: ()=>this.userMedia
21731
+ });
21579
21732
  }
21580
21733
  }
21581
21734
  const visuals_recorder = Recorder;
@@ -22539,8 +22692,8 @@ var __webpack_exports__ = {};
22539
22692
  }
22540
22693
  else runValidation = false;
22541
22694
  if (runValidation) {
22542
- var _event_target;
22543
- const targetName = null == event ? void 0 : null == (_event_target = event.target) ? void 0 : _event_target.name;
22695
+ const target = null == event ? void 0 : event.target;
22696
+ const targetName = target instanceof Element ? target.getAttribute("name") : void 0;
22544
22697
  if (targetName) this.emit("VALIDATING", {
22545
22698
  targetName
22546
22699
  });
@@ -22575,27 +22728,10 @@ var __webpack_exports__ = {};
22575
22728
  }
22576
22729
  }
22577
22730
  if (valid) {
22578
- const recipients = this.form.getRecipients();
22579
- const toIsConfigured = "to" in recipients;
22580
- const ccIsConfigured = "cc" in recipients;
22581
- const bccIsConfigured = "bcc" in recipients;
22582
- const hasTo = recipients.to && recipients.to.length > 0;
22583
- const hasCc = recipients.cc && recipients.cc.length > 0;
22584
- const hasBcc = recipients.bcc && recipients.bcc.length > 0;
22585
- if (toIsConfigured) {
22586
- if (!hasTo) if (ccIsConfigured && bccIsConfigured) {
22587
- if (!hasCc && !hasBcc) valid = false;
22588
- } else if (ccIsConfigured) {
22589
- if (!hasCc) valid = false;
22590
- } else if (bccIsConfigured) {
22591
- if (!hasBcc) valid = false;
22592
- } else valid = false;
22593
- } else if (ccIsConfigured) {
22594
- if (!hasCc) {
22595
- if (bccIsConfigured && !hasBcc) valid = false;
22596
- }
22731
+ if (!email_isRecipientSelectionValid(this.form.getRecipients())) {
22732
+ valid = false;
22733
+ whyInvalid = "At least one recipient is required";
22597
22734
  }
22598
- if (!valid) whyInvalid = "At least one recipient is required";
22599
22735
  }
22600
22736
  } else {
22601
22737
  valid = visualsValid;