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.
- package/README.md +1 -1
- package/dist/cjs/index.cjs +2119 -1983
- package/dist/esm/index.js +2010 -1874
- package/dist/esm/types/events/params.d.ts +4 -3
- package/dist/esm/util/email/isRecipientSelectionValid.d.ts +4 -0
- package/dist/esm/wrappers/container.d.ts +1 -1
- package/dist/esm/wrappers/visuals/recorder/capture.d.ts +87 -0
- package/dist/esm/wrappers/visuals/recorder/connection.d.ts +46 -0
- package/dist/esm/wrappers/visuals/recorder/dimensions.d.ts +21 -0
- package/dist/esm/wrappers/visuals/recorder/media.d.ts +51 -0
- package/dist/esm/wrappers/visuals/recorder/mediaConstraints.d.ts +14 -0
- package/dist/esm/wrappers/visuals/recorder/progress.d.ts +11 -0
- package/dist/esm/wrappers/visuals/recorder/streamWriter.d.ts +56 -0
- package/dist/esm/wrappers/visuals/recorder.d.ts +13 -165
- package/dist/umd/index.js +2040 -1953
- package/package.json +13 -13
- /package/dist/esm/util/{trimEmail.d.ts → email/trimEmail.d.ts} +0 -0
package/dist/umd/index.js
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
else root["VideomailClient"] = factory();
|
|
6
6
|
})(globalThis, ()=>(()=>{
|
|
7
7
|
var __webpack_modules__ = {
|
|
8
|
-
|
|
8
|
+
pJ (module1, __webpack_exports__, __webpack_require__) {
|
|
9
9
|
"use strict";
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var ___CSS_LOADER_EXPORT___ =
|
|
10
|
+
var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1 = __webpack_require__("K7");
|
|
11
|
+
var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1_default = /*#__PURE__*/ __webpack_require__.n(_node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1);
|
|
12
|
+
var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0 = __webpack_require__("Ly");
|
|
13
|
+
var _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0_default = /*#__PURE__*/ __webpack_require__.n(_node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0);
|
|
14
|
+
var ___CSS_LOADER_EXPORT___ = _node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_0_default()(_node_modules_rslib_core_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_1_default());
|
|
15
15
|
___CSS_LOADER_EXPORT___.push([
|
|
16
16
|
module1.id,
|
|
17
17
|
`@keyframes blink {
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
});
|
|
160
160
|
},
|
|
161
161
|
b6 (module1, exports1, __webpack_require__) {
|
|
162
|
-
var EventEmitter = __webpack_require__("
|
|
162
|
+
var EventEmitter = __webpack_require__("qU").EventEmitter, inherits = __webpack_require__("c5"), raf = __webpack_require__("jY"), methods;
|
|
163
163
|
var defaultRAFObject = {
|
|
164
164
|
requestAnimationFrame: raf,
|
|
165
165
|
cancelAnimationFrame: raf.cancel
|
|
@@ -1654,7 +1654,7 @@
|
|
|
1654
1654
|
return 0 === comparison || comparison & DOCUMENT_POSITION_CONTAINED_BY;
|
|
1655
1655
|
}
|
|
1656
1656
|
},
|
|
1657
|
-
|
|
1657
|
+
TU (__unused_rspack_module, exports1, __webpack_require__) {
|
|
1658
1658
|
function isArray(arg) {
|
|
1659
1659
|
if (Array.isArray) return Array.isArray(arg);
|
|
1660
1660
|
return '[object Array]' === objectToString(arg);
|
|
@@ -1837,7 +1837,7 @@
|
|
|
1837
1837
|
},
|
|
1838
1838
|
xF (module1, __unused_rspack_exports, __webpack_require__) {
|
|
1839
1839
|
"use strict";
|
|
1840
|
-
var document1 = __webpack_require__("
|
|
1840
|
+
var document1 = __webpack_require__("gM");
|
|
1841
1841
|
var Event1 = __webpack_require__("cE");
|
|
1842
1842
|
var Keys = __webpack_require__("Dy");
|
|
1843
1843
|
module1.exports = Visibility;
|
|
@@ -2119,7 +2119,7 @@
|
|
|
2119
2119
|
return keys;
|
|
2120
2120
|
};
|
|
2121
2121
|
module1.exports = Duplex;
|
|
2122
|
-
var util = Object.create(__webpack_require__("
|
|
2122
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
2123
2123
|
util.inherits = __webpack_require__("c5");
|
|
2124
2124
|
var Readable = __webpack_require__("rp");
|
|
2125
2125
|
var Writable = __webpack_require__("pv");
|
|
@@ -2173,7 +2173,7 @@
|
|
|
2173
2173
|
"use strict";
|
|
2174
2174
|
module1.exports = PassThrough;
|
|
2175
2175
|
var Transform = __webpack_require__("It");
|
|
2176
|
-
var util = Object.create(__webpack_require__("
|
|
2176
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
2177
2177
|
util.inherits = __webpack_require__("c5");
|
|
2178
2178
|
util.inherits(PassThrough, Transform);
|
|
2179
2179
|
function PassThrough(options) {
|
|
@@ -2192,7 +2192,7 @@
|
|
|
2192
2192
|
var isArray = __webpack_require__("ab");
|
|
2193
2193
|
var Duplex;
|
|
2194
2194
|
Readable.ReadableState = ReadableState;
|
|
2195
|
-
__webpack_require__("
|
|
2195
|
+
__webpack_require__("qU").EventEmitter;
|
|
2196
2196
|
var EElistenerCount = function(emitter, type) {
|
|
2197
2197
|
return emitter.listeners(type).length;
|
|
2198
2198
|
};
|
|
@@ -2205,7 +2205,7 @@
|
|
|
2205
2205
|
function _isUint8Array(obj) {
|
|
2206
2206
|
return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
|
|
2207
2207
|
}
|
|
2208
|
-
var util = Object.create(__webpack_require__("
|
|
2208
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
2209
2209
|
util.inherits = __webpack_require__("c5");
|
|
2210
2210
|
var debugUtil = __webpack_require__("AN");
|
|
2211
2211
|
var debug = void 0;
|
|
@@ -2832,7 +2832,7 @@
|
|
|
2832
2832
|
"use strict";
|
|
2833
2833
|
module1.exports = Transform;
|
|
2834
2834
|
var Duplex = __webpack_require__("OO");
|
|
2835
|
-
var util = Object.create(__webpack_require__("
|
|
2835
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
2836
2836
|
util.inherits = __webpack_require__("c5");
|
|
2837
2837
|
util.inherits(Transform, Duplex);
|
|
2838
2838
|
function afterTransform(er, data) {
|
|
@@ -2932,7 +2932,7 @@
|
|
|
2932
2932
|
].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;
|
|
2933
2933
|
var Duplex;
|
|
2934
2934
|
Writable.WritableState = WritableState;
|
|
2935
|
-
var util = Object.create(__webpack_require__("
|
|
2935
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
2936
2936
|
util.inherits = __webpack_require__("c5");
|
|
2937
2937
|
var internalUtil = {
|
|
2938
2938
|
deprecate: __webpack_require__("NQ")
|
|
@@ -3456,7 +3456,7 @@
|
|
|
3456
3456
|
};
|
|
3457
3457
|
},
|
|
3458
3458
|
tb (module1, __unused_rspack_exports, __webpack_require__) {
|
|
3459
|
-
module1.exports = __webpack_require__("
|
|
3459
|
+
module1.exports = __webpack_require__("qU").EventEmitter;
|
|
3460
3460
|
},
|
|
3461
3461
|
GB (module1, exports1, __webpack_require__) {
|
|
3462
3462
|
exports1 = module1.exports = __webpack_require__("rp");
|
|
@@ -3853,7 +3853,7 @@
|
|
|
3853
3853
|
"use strict";
|
|
3854
3854
|
module1.exports = Object;
|
|
3855
3855
|
},
|
|
3856
|
-
|
|
3856
|
+
qU (module1) {
|
|
3857
3857
|
"use strict";
|
|
3858
3858
|
var R = 'object' == typeof Reflect ? Reflect : null;
|
|
3859
3859
|
var ReflectApply = R && 'function' == typeof R.apply ? R.apply : function(target, receiver, args) {
|
|
@@ -4903,7 +4903,7 @@
|
|
|
4903
4903
|
return tuple.listen;
|
|
4904
4904
|
}
|
|
4905
4905
|
},
|
|
4906
|
-
|
|
4906
|
+
gM (module1, __unused_rspack_exports, __webpack_require__) {
|
|
4907
4907
|
var topLevel = void 0 !== __webpack_require__.g ? __webpack_require__.g : "u" > typeof window ? window : {};
|
|
4908
4908
|
var minDoc = __webpack_require__("MM");
|
|
4909
4909
|
var doccy;
|
|
@@ -8254,7 +8254,7 @@
|
|
|
8254
8254
|
});
|
|
8255
8255
|
});
|
|
8256
8256
|
},
|
|
8257
|
-
|
|
8257
|
+
S (__unused_rspack_module, exports1, __webpack_require__) {
|
|
8258
8258
|
var process = __webpack_require__("zT");
|
|
8259
8259
|
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || function(obj) {
|
|
8260
8260
|
var keys = Object.keys(obj);
|
|
@@ -8733,7 +8733,7 @@
|
|
|
8733
8733
|
return keys;
|
|
8734
8734
|
};
|
|
8735
8735
|
module1.exports = Duplex;
|
|
8736
|
-
var util = Object.create(__webpack_require__("
|
|
8736
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
8737
8737
|
util.inherits = __webpack_require__("c5");
|
|
8738
8738
|
var Readable = __webpack_require__("w");
|
|
8739
8739
|
var Writable = __webpack_require__("S5");
|
|
@@ -8787,7 +8787,7 @@
|
|
|
8787
8787
|
"use strict";
|
|
8788
8788
|
module1.exports = PassThrough;
|
|
8789
8789
|
var Transform = __webpack_require__("Ge");
|
|
8790
|
-
var util = Object.create(__webpack_require__("
|
|
8790
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
8791
8791
|
util.inherits = __webpack_require__("c5");
|
|
8792
8792
|
util.inherits(PassThrough, Transform);
|
|
8793
8793
|
function PassThrough(options) {
|
|
@@ -8806,7 +8806,7 @@
|
|
|
8806
8806
|
var isArray = __webpack_require__("N3");
|
|
8807
8807
|
var Duplex;
|
|
8808
8808
|
Readable.ReadableState = ReadableState;
|
|
8809
|
-
__webpack_require__("
|
|
8809
|
+
__webpack_require__("qU").EventEmitter;
|
|
8810
8810
|
var EElistenerCount = function(emitter, type) {
|
|
8811
8811
|
return emitter.listeners(type).length;
|
|
8812
8812
|
};
|
|
@@ -8819,13 +8819,13 @@
|
|
|
8819
8819
|
function _isUint8Array(obj) {
|
|
8820
8820
|
return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
|
|
8821
8821
|
}
|
|
8822
|
-
var util = Object.create(__webpack_require__("
|
|
8822
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
8823
8823
|
util.inherits = __webpack_require__("c5");
|
|
8824
8824
|
var debugUtil = __webpack_require__("eD");
|
|
8825
8825
|
var debug = void 0;
|
|
8826
8826
|
debug = debugUtil && debugUtil.debuglog ? debugUtil.debuglog('stream') : function() {};
|
|
8827
8827
|
var BufferList = __webpack_require__("aI");
|
|
8828
|
-
var destroyImpl = __webpack_require__("
|
|
8828
|
+
var destroyImpl = __webpack_require__("R");
|
|
8829
8829
|
var StringDecoder;
|
|
8830
8830
|
util.inherits(Readable, Stream);
|
|
8831
8831
|
var kProxyEvents = [
|
|
@@ -9446,7 +9446,7 @@
|
|
|
9446
9446
|
"use strict";
|
|
9447
9447
|
module1.exports = Transform;
|
|
9448
9448
|
var Duplex = __webpack_require__("S7");
|
|
9449
|
-
var util = Object.create(__webpack_require__("
|
|
9449
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
9450
9450
|
util.inherits = __webpack_require__("c5");
|
|
9451
9451
|
util.inherits(Transform, Duplex);
|
|
9452
9452
|
function afterTransform(er, data) {
|
|
@@ -9546,7 +9546,7 @@
|
|
|
9546
9546
|
].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;
|
|
9547
9547
|
var Duplex;
|
|
9548
9548
|
Writable.WritableState = WritableState;
|
|
9549
|
-
var util = Object.create(__webpack_require__("
|
|
9549
|
+
var util = Object.create(__webpack_require__("TU"));
|
|
9550
9550
|
util.inherits = __webpack_require__("c5");
|
|
9551
9551
|
var internalUtil = {
|
|
9552
9552
|
deprecate: __webpack_require__("NQ")
|
|
@@ -9560,7 +9560,7 @@
|
|
|
9560
9560
|
function _isUint8Array(obj) {
|
|
9561
9561
|
return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
|
|
9562
9562
|
}
|
|
9563
|
-
var destroyImpl = __webpack_require__("
|
|
9563
|
+
var destroyImpl = __webpack_require__("R");
|
|
9564
9564
|
util.inherits(Writable, Stream);
|
|
9565
9565
|
function nop() {}
|
|
9566
9566
|
function WritableState(options, stream) {
|
|
@@ -10014,7 +10014,7 @@
|
|
|
10014
10014
|
return this.constructor.name + ' ' + obj;
|
|
10015
10015
|
};
|
|
10016
10016
|
},
|
|
10017
|
-
|
|
10017
|
+
R (module1, __unused_rspack_exports, __webpack_require__) {
|
|
10018
10018
|
"use strict";
|
|
10019
10019
|
var pna = __webpack_require__("sj");
|
|
10020
10020
|
function destroy(err, cb) {
|
|
@@ -10070,7 +10070,7 @@
|
|
|
10070
10070
|
};
|
|
10071
10071
|
},
|
|
10072
10072
|
O (module1, __unused_rspack_exports, __webpack_require__) {
|
|
10073
|
-
module1.exports = __webpack_require__("
|
|
10073
|
+
module1.exports = __webpack_require__("qU").EventEmitter;
|
|
10074
10074
|
},
|
|
10075
10075
|
Fo (module1, exports1, __webpack_require__) {
|
|
10076
10076
|
exports1 = module1.exports = __webpack_require__("w");
|
|
@@ -10560,7 +10560,7 @@
|
|
|
10560
10560
|
a () {},
|
|
10561
10561
|
Av () {},
|
|
10562
10562
|
eD () {},
|
|
10563
|
-
|
|
10563
|
+
Ly (module1) {
|
|
10564
10564
|
"use strict";
|
|
10565
10565
|
module1.exports = function(cssWithMappingToString) {
|
|
10566
10566
|
var list = [];
|
|
@@ -10614,13 +10614,13 @@
|
|
|
10614
10614
|
return list;
|
|
10615
10615
|
};
|
|
10616
10616
|
},
|
|
10617
|
-
|
|
10617
|
+
K7 (module1) {
|
|
10618
10618
|
"use strict";
|
|
10619
10619
|
module1.exports = function(i) {
|
|
10620
10620
|
return i[1];
|
|
10621
10621
|
};
|
|
10622
10622
|
},
|
|
10623
|
-
|
|
10623
|
+
q (module1) {
|
|
10624
10624
|
"use strict";
|
|
10625
10625
|
var stylesInDOM = [];
|
|
10626
10626
|
function getIndexByIdentifier(identifier) {
|
|
@@ -10699,7 +10699,7 @@
|
|
|
10699
10699
|
};
|
|
10700
10700
|
};
|
|
10701
10701
|
},
|
|
10702
|
-
|
|
10702
|
+
f (module1) {
|
|
10703
10703
|
"use strict";
|
|
10704
10704
|
var memo = {};
|
|
10705
10705
|
function getTarget(target) {
|
|
@@ -10721,7 +10721,7 @@
|
|
|
10721
10721
|
}
|
|
10722
10722
|
module1.exports = insertBySelector;
|
|
10723
10723
|
},
|
|
10724
|
-
|
|
10724
|
+
z7 (module1) {
|
|
10725
10725
|
"use strict";
|
|
10726
10726
|
function insertStyleElement(options) {
|
|
10727
10727
|
var element = document.createElement("style");
|
|
@@ -10731,7 +10731,7 @@
|
|
|
10731
10731
|
}
|
|
10732
10732
|
module1.exports = insertStyleElement;
|
|
10733
10733
|
},
|
|
10734
|
-
|
|
10734
|
+
g (module1, __unused_rspack_exports, __webpack_require__) {
|
|
10735
10735
|
"use strict";
|
|
10736
10736
|
function setAttributesWithoutAttributes(styleElement) {
|
|
10737
10737
|
var nonce = __webpack_require__.nc;
|
|
@@ -10739,7 +10739,7 @@
|
|
|
10739
10739
|
}
|
|
10740
10740
|
module1.exports = setAttributesWithoutAttributes;
|
|
10741
10741
|
},
|
|
10742
|
-
|
|
10742
|
+
V (module1) {
|
|
10743
10743
|
"use strict";
|
|
10744
10744
|
function apply(styleElement, options, obj) {
|
|
10745
10745
|
var css = "";
|
|
@@ -10776,7 +10776,7 @@
|
|
|
10776
10776
|
}
|
|
10777
10777
|
module1.exports = domAPI;
|
|
10778
10778
|
},
|
|
10779
|
-
|
|
10779
|
+
AD (module1) {
|
|
10780
10780
|
"use strict";
|
|
10781
10781
|
function styleTagTransform(css, styleElement) {
|
|
10782
10782
|
if (styleElement.styleSheet) styleElement.styleSheet.cssText = css;
|
|
@@ -11121,7 +11121,7 @@
|
|
|
11121
11121
|
var client = __webpack_require__("Q");
|
|
11122
11122
|
var client_default = /*#__PURE__*/ __webpack_require__.n(client);
|
|
11123
11123
|
var package_namespaceObject = {
|
|
11124
|
-
rE: "16.
|
|
11124
|
+
rE: "16.2.0"
|
|
11125
11125
|
};
|
|
11126
11126
|
function isAudioEnabled(options) {
|
|
11127
11127
|
return Boolean(options.audio.enabled);
|
|
@@ -11133,7 +11133,7 @@
|
|
|
11133
11133
|
function isAutoPauseEnabled(options) {
|
|
11134
11134
|
return options.enableAutoPause && options.enablePause;
|
|
11135
11135
|
}
|
|
11136
|
-
var util = __webpack_require__("
|
|
11136
|
+
var util = __webpack_require__("S");
|
|
11137
11137
|
var util_default = /*#__PURE__*/ __webpack_require__.n(util);
|
|
11138
11138
|
function getEventDetails(event) {
|
|
11139
11139
|
const eventWithErrorDetails = event;
|
|
@@ -14996,6 +14996,14 @@
|
|
|
14996
14996
|
}
|
|
14997
14997
|
}
|
|
14998
14998
|
const util_Despot = Despot;
|
|
14999
|
+
const EMAIL_SEPARATOR_REGEX = /[ ,]+/gu;
|
|
15000
|
+
function trimEmail(email) {
|
|
15001
|
+
return email.replace(EMAIL_SEPARATOR_REGEX, "");
|
|
15002
|
+
}
|
|
15003
|
+
function trimEmails(emails) {
|
|
15004
|
+
const trimmedEmails = emails.split(EMAIL_SEPARATOR_REGEX).map((item)=>item.trim()).filter(Boolean);
|
|
15005
|
+
return trimmedEmails;
|
|
15006
|
+
}
|
|
14999
15007
|
function getValidity(element) {
|
|
15000
15008
|
const validity = "validity" in element ? element.validity : void 0;
|
|
15001
15009
|
return validity;
|
|
@@ -15015,14 +15023,6 @@
|
|
|
15015
15023
|
element.style.removeProperty("display");
|
|
15016
15024
|
}
|
|
15017
15025
|
const html_showElement = showElement;
|
|
15018
|
-
const EMAIL_SEPARATOR_REGEX = /[ ,]+/gu;
|
|
15019
|
-
function trimEmail(email) {
|
|
15020
|
-
return email.replace(EMAIL_SEPARATOR_REGEX, "");
|
|
15021
|
-
}
|
|
15022
|
-
function trimEmails(emails) {
|
|
15023
|
-
const trimmedEmails = emails.split(EMAIL_SEPARATOR_REGEX).map((item)=>item.trim()).filter(Boolean);
|
|
15024
|
-
return trimmedEmails;
|
|
15025
|
-
}
|
|
15026
15026
|
const FormMethod = {
|
|
15027
15027
|
POST: "post",
|
|
15028
15028
|
PUT: "put",
|
|
@@ -15689,19 +15689,19 @@
|
|
|
15689
15689
|
return newOptions;
|
|
15690
15690
|
}
|
|
15691
15691
|
const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
|
|
15692
|
-
var injectStylesIntoStyleTag = __webpack_require__("
|
|
15692
|
+
var injectStylesIntoStyleTag = __webpack_require__("q");
|
|
15693
15693
|
var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
|
|
15694
|
-
var styleDomAPI = __webpack_require__("
|
|
15694
|
+
var styleDomAPI = __webpack_require__("V");
|
|
15695
15695
|
var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
|
|
15696
|
-
var insertBySelector = __webpack_require__("
|
|
15696
|
+
var insertBySelector = __webpack_require__("f");
|
|
15697
15697
|
var insertBySelector_default = /*#__PURE__*/ __webpack_require__.n(insertBySelector);
|
|
15698
|
-
var setAttributesWithoutAttributes = __webpack_require__("
|
|
15698
|
+
var setAttributesWithoutAttributes = __webpack_require__("g");
|
|
15699
15699
|
var setAttributesWithoutAttributes_default = /*#__PURE__*/ __webpack_require__.n(setAttributesWithoutAttributes);
|
|
15700
|
-
var insertStyleElement = __webpack_require__("
|
|
15700
|
+
var insertStyleElement = __webpack_require__("z7");
|
|
15701
15701
|
var insertStyleElement_default = /*#__PURE__*/ __webpack_require__.n(insertStyleElement);
|
|
15702
|
-
var styleTagTransform = __webpack_require__("
|
|
15702
|
+
var styleTagTransform = __webpack_require__("AD");
|
|
15703
15703
|
var styleTagTransform_default = /*#__PURE__*/ __webpack_require__.n(styleTagTransform);
|
|
15704
|
-
var main = __webpack_require__("
|
|
15704
|
+
var main = __webpack_require__("pJ");
|
|
15705
15705
|
var main_options = {};
|
|
15706
15706
|
main_options.styleTagTransform = styleTagTransform_default();
|
|
15707
15707
|
main_options.setAttributes = setAttributesWithoutAttributes_default();
|
|
@@ -16705,11 +16705,11 @@
|
|
|
16705
16705
|
const originalDocument = document1;
|
|
16706
16706
|
const currentScript = originalDocument.currentScript;
|
|
16707
16707
|
window1.DocumentFragment;
|
|
16708
|
-
const HTMLTemplateElement = window1.HTMLTemplateElement, Node = window1.Node,
|
|
16708
|
+
const HTMLTemplateElement = window1.HTMLTemplateElement, Node = window1.Node, Element1 = window1.Element, NodeFilter = window1.NodeFilter;
|
|
16709
16709
|
void 0 === window1.NamedNodeMap && (window1.NamedNodeMap || window1.MozNamedAttrMap);
|
|
16710
16710
|
window1.HTMLFormElement;
|
|
16711
16711
|
const DOMParser = window1.DOMParser, trustedTypes = window1.trustedTypes;
|
|
16712
|
-
const ElementPrototype =
|
|
16712
|
+
const ElementPrototype = Element1.prototype;
|
|
16713
16713
|
const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
|
|
16714
16714
|
const remove = lookupGetter(ElementPrototype, "remove");
|
|
16715
16715
|
const removeAttributeNode = lookupGetter(ElementPrototype, "removeAttributeNode");
|
|
@@ -17792,6 +17792,19 @@
|
|
|
17792
17792
|
return DOMPurify;
|
|
17793
17793
|
}
|
|
17794
17794
|
var purify_default = createDOMPurify();
|
|
17795
|
+
function isRecipientSelectionValid(recipients) {
|
|
17796
|
+
const hasTo = Boolean(recipients.to?.length);
|
|
17797
|
+
const hasCc = Boolean(recipients.cc?.length);
|
|
17798
|
+
const hasBcc = Boolean(recipients.bcc?.length);
|
|
17799
|
+
const hasConfiguredTo = "to" in recipients;
|
|
17800
|
+
const hasConfiguredCc = "cc" in recipients;
|
|
17801
|
+
const hasConfiguredBcc = "bcc" in recipients;
|
|
17802
|
+
let isValid = true;
|
|
17803
|
+
if (hasConfiguredTo) isValid = hasTo || hasConfiguredCc && hasCc || hasConfiguredBcc && hasBcc;
|
|
17804
|
+
else if (hasConfiguredCc) isValid = hasCc || !hasConfiguredBcc || hasBcc;
|
|
17805
|
+
return isValid;
|
|
17806
|
+
}
|
|
17807
|
+
const email_isRecipientSelectionValid = isRecipientSelectionValid;
|
|
17795
17808
|
function limitHeight(height, options, calledFrom) {
|
|
17796
17809
|
const dimension = {
|
|
17797
17810
|
unit: "px"
|
|
@@ -18388,7 +18401,7 @@
|
|
|
18388
18401
|
html_showElement(this.facingModeElement);
|
|
18389
18402
|
}
|
|
18390
18403
|
}
|
|
18391
|
-
const
|
|
18404
|
+
const recorder_facingMode = FacingMode;
|
|
18392
18405
|
class PausedNote {
|
|
18393
18406
|
visuals;
|
|
18394
18407
|
options;
|
|
@@ -18609,7 +18622,7 @@
|
|
|
18609
18622
|
this.recordTimer = new recordTimer(visuals, this.recordNote, options);
|
|
18610
18623
|
const browser = util_getBrowser(options);
|
|
18611
18624
|
if (options.video.countdown) this.countdown = new countdown(visuals, options);
|
|
18612
|
-
if (options.video.facingModeButton && browser.isMobile()) this.facingMode = new
|
|
18625
|
+
if (options.video.facingModeButton && browser.isMobile()) this.facingMode = new recorder_facingMode(visuals, options);
|
|
18613
18626
|
if (options.enablePause) this.pausedNote = new pausedNote(visuals, options);
|
|
18614
18627
|
}
|
|
18615
18628
|
startRecording() {
|
|
@@ -18942,6 +18955,18 @@
|
|
|
18942
18955
|
}
|
|
18943
18956
|
}
|
|
18944
18957
|
const notifier = Notifier;
|
|
18958
|
+
function summarize(title, contents) {
|
|
18959
|
+
const lines = Object.entries(contents).sort(([keyA], [keyB])=>keyA.localeCompare(keyB)).map(([key, value])=>{
|
|
18960
|
+
const formattedValue = void 0 === value ? "undefined" : "object" == typeof value ? string_pretty(value) : value;
|
|
18961
|
+
return ` • ${key}: ${formattedValue}`;
|
|
18962
|
+
});
|
|
18963
|
+
const line = [
|
|
18964
|
+
`🔎 ${title}`,
|
|
18965
|
+
...lines
|
|
18966
|
+
].join("\n");
|
|
18967
|
+
return line;
|
|
18968
|
+
}
|
|
18969
|
+
const string_summarize = summarize;
|
|
18945
18970
|
var animitter = __webpack_require__("b6");
|
|
18946
18971
|
var animitter_default = /*#__PURE__*/ __webpack_require__.n(animitter);
|
|
18947
18972
|
var typedarray_to_buffer = __webpack_require__("E");
|
|
@@ -19005,34 +19030,6 @@
|
|
|
19005
19030
|
canvas_to_buffer_modern_r.atob = void 0;
|
|
19006
19031
|
var websocket_stream_stream = __webpack_require__("U");
|
|
19007
19032
|
var stream_default = /*#__PURE__*/ __webpack_require__.n(websocket_stream_stream);
|
|
19008
|
-
const DEFAULT_MAX_QUEUED_BYTES = 1048576;
|
|
19009
|
-
class BoundedWriteQueue {
|
|
19010
|
-
maxBytes;
|
|
19011
|
-
writes = [];
|
|
19012
|
-
queuedBytes = 0;
|
|
19013
|
-
constructor(maxBytes = DEFAULT_MAX_QUEUED_BYTES){
|
|
19014
|
-
this.maxBytes = maxBytes;
|
|
19015
|
-
}
|
|
19016
|
-
enqueue(buffer, params) {
|
|
19017
|
-
if (this.queuedBytes + buffer.length > this.maxBytes) return false;
|
|
19018
|
-
this.writes.push({
|
|
19019
|
-
buffer,
|
|
19020
|
-
params
|
|
19021
|
-
});
|
|
19022
|
-
this.queuedBytes += buffer.length;
|
|
19023
|
-
return true;
|
|
19024
|
-
}
|
|
19025
|
-
dequeue() {
|
|
19026
|
-
const write = this.writes.shift();
|
|
19027
|
-
if (write) this.queuedBytes -= write.buffer.length;
|
|
19028
|
-
return write;
|
|
19029
|
-
}
|
|
19030
|
-
clear() {
|
|
19031
|
-
this.writes.length = 0;
|
|
19032
|
-
this.queuedBytes = 0;
|
|
19033
|
-
}
|
|
19034
|
-
}
|
|
19035
|
-
const boundedWriteQueue = BoundedWriteQueue;
|
|
19036
19033
|
function getEdgeCodes(value, amount, fromEnd) {
|
|
19037
19034
|
const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
|
|
19038
19035
|
const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
|
|
@@ -19112,1804 +19109,2115 @@
|
|
|
19112
19109
|
};
|
|
19113
19110
|
}
|
|
19114
19111
|
const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
|
|
19115
|
-
function figureMinHeight(height, options) {
|
|
19116
|
-
let minHeight;
|
|
19117
|
-
if (options.video.height) {
|
|
19118
|
-
minHeight = height ? Math.min(options.video.height, height) : options.video.height;
|
|
19119
|
-
if (minHeight < 1) throw error_createError({
|
|
19120
|
-
message: `Got a min height less than 1 (${minHeight})!`,
|
|
19121
|
-
options
|
|
19122
|
-
});
|
|
19123
|
-
} else minHeight = height;
|
|
19124
|
-
return minHeight;
|
|
19125
|
-
}
|
|
19126
|
-
const dimensions_figureMinHeight = figureMinHeight;
|
|
19127
|
-
function getRatio(options, videoHeight, videoWidth) {
|
|
19128
|
-
let ratio = 1;
|
|
19129
|
-
const hasVideoDimensions = videoHeight && videoWidth;
|
|
19130
|
-
const desiredHeight = options.video.height;
|
|
19131
|
-
const desiredWidth = options.video.width;
|
|
19132
|
-
const hasDesiredDimensions = desiredHeight && desiredWidth;
|
|
19133
|
-
if (hasDesiredDimensions) ratio = hasVideoDimensions ? videoHeight < desiredHeight || videoWidth < desiredWidth ? videoHeight / videoWidth : desiredHeight / desiredWidth : desiredHeight / desiredWidth;
|
|
19134
|
-
else if (hasVideoDimensions) ratio = videoHeight / videoWidth;
|
|
19135
|
-
return ratio;
|
|
19136
|
-
}
|
|
19137
|
-
const dimensions_getRatio = getRatio;
|
|
19138
|
-
function calculateHeight(responsive, videoWidth, options, ratio, element) {
|
|
19139
|
-
const dimension = {
|
|
19140
|
-
unit: "px"
|
|
19141
|
-
};
|
|
19142
|
-
let width = videoWidth;
|
|
19143
|
-
if (responsive && element) {
|
|
19144
|
-
const limitedDimension = dimensions_limitWidth(element, options, width);
|
|
19145
|
-
width = limitedDimension.value;
|
|
19146
|
-
}
|
|
19147
|
-
const chosenRatio = ratio ?? dimensions_getRatio(options, void 0, videoWidth);
|
|
19148
|
-
const height = width ? Math.round(width * chosenRatio) : void 0;
|
|
19149
|
-
if (height && Number.isInteger(height) && height < 1) throw error_createError({
|
|
19150
|
-
message: "Just calculated a height less than 1 which is wrong.",
|
|
19151
|
-
options
|
|
19152
|
-
});
|
|
19153
|
-
const minHeight = dimensions_figureMinHeight(height, options);
|
|
19154
|
-
dimension.value = minHeight;
|
|
19155
|
-
return dimension;
|
|
19156
|
-
}
|
|
19157
|
-
const dimensions_calculateHeight = calculateHeight;
|
|
19158
|
-
function calculateWidth(responsive, options, videoHeight, ratio) {
|
|
19159
|
-
const dimension = {
|
|
19160
|
-
unit: "px"
|
|
19161
|
-
};
|
|
19162
|
-
let height = dimensions_figureMinHeight(videoHeight, options);
|
|
19163
|
-
if (responsive) {
|
|
19164
|
-
const limitedDimension = dimensions_limitHeight(height, options, "calculateWidth");
|
|
19165
|
-
height = limitedDimension.value;
|
|
19166
|
-
}
|
|
19167
|
-
if (!height || height < 1) throw error_createError({
|
|
19168
|
-
message: `Height ${height} cannot be smaller than 1 when calculating width.`,
|
|
19169
|
-
options
|
|
19170
|
-
});
|
|
19171
|
-
const chosenRatio = ratio ?? dimensions_getRatio(options, videoHeight);
|
|
19172
|
-
const calculatedWidth = Math.round(height / chosenRatio);
|
|
19173
|
-
if (calculatedWidth < 1) throw error_createError({
|
|
19174
|
-
message: "Calculated width cannot be smaller than 1!",
|
|
19175
|
-
options
|
|
19176
|
-
});
|
|
19177
|
-
dimension.value = calculatedWidth;
|
|
19178
|
-
return dimension;
|
|
19179
|
-
}
|
|
19180
|
-
const dimensions_calculateWidth = calculateWidth;
|
|
19181
19112
|
function isAutomatedUserAgent(userAgent = navigator.userAgent) {
|
|
19182
19113
|
return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
|
|
19183
19114
|
}
|
|
19184
19115
|
const util_isAutomatedUserAgent = isAutomatedUserAgent;
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19191
|
-
|
|
19192
|
-
|
|
19193
|
-
|
|
19194
|
-
|
|
19195
|
-
|
|
19196
|
-
|
|
19197
|
-
|
|
19198
|
-
const videoTracks = localMediaStream.getVideoTracks();
|
|
19199
|
-
let videoTrack;
|
|
19200
|
-
if (videoTracks[0]) videoTrack = videoTracks[0];
|
|
19201
|
-
return videoTrack;
|
|
19202
|
-
}
|
|
19203
|
-
const media_getFirstVideoTrack = getFirstVideoTrack;
|
|
19204
|
-
const MEDIA_EVENTS = [
|
|
19205
|
-
"loadstart",
|
|
19206
|
-
"suspend",
|
|
19207
|
-
"progress",
|
|
19208
|
-
"abort",
|
|
19209
|
-
"emptied",
|
|
19210
|
-
"stalled",
|
|
19211
|
-
"pause",
|
|
19212
|
-
"loadeddata",
|
|
19213
|
-
"waiting",
|
|
19214
|
-
"playing",
|
|
19215
|
-
"canplay",
|
|
19216
|
-
"canplaythrough",
|
|
19217
|
-
"seeking",
|
|
19218
|
-
"seeked",
|
|
19219
|
-
"ended",
|
|
19220
|
-
"ratechange",
|
|
19221
|
-
"durationchange",
|
|
19222
|
-
"volumechange"
|
|
19223
|
-
];
|
|
19224
|
-
const mediaEvents = MEDIA_EVENTS;
|
|
19225
|
-
function _define_property(obj, key, value) {
|
|
19226
|
-
if (key in obj) Object.defineProperty(obj, key, {
|
|
19227
|
-
value: value,
|
|
19228
|
-
enumerable: true,
|
|
19229
|
-
configurable: true,
|
|
19230
|
-
writable: true
|
|
19231
|
-
});
|
|
19232
|
-
else obj[key] = value;
|
|
19233
|
-
return obj;
|
|
19234
|
-
}
|
|
19235
|
-
class AudioSample {
|
|
19236
|
-
toBuffer() {
|
|
19237
|
-
const l = this.float32Array.length;
|
|
19238
|
-
const arr = new Int16Array(l);
|
|
19239
|
-
this.float32Array.forEach((sample, i)=>{
|
|
19240
|
-
const clampedSample = Number.isFinite(sample) ? Math.max(-1, sample) : sample;
|
|
19241
|
-
arr[i] = 0x7fff * Math.min(1, clampedSample);
|
|
19116
|
+
const DEFAULT_MAX_QUEUED_BYTES = 1048576;
|
|
19117
|
+
class BoundedWriteQueue {
|
|
19118
|
+
maxBytes;
|
|
19119
|
+
writes = [];
|
|
19120
|
+
queuedBytes = 0;
|
|
19121
|
+
constructor(maxBytes = DEFAULT_MAX_QUEUED_BYTES){
|
|
19122
|
+
this.maxBytes = maxBytes;
|
|
19123
|
+
}
|
|
19124
|
+
enqueue(buffer, params) {
|
|
19125
|
+
if (this.queuedBytes + buffer.length > this.maxBytes) return false;
|
|
19126
|
+
this.writes.push({
|
|
19127
|
+
buffer,
|
|
19128
|
+
params
|
|
19242
19129
|
});
|
|
19243
|
-
|
|
19130
|
+
this.queuedBytes += buffer.length;
|
|
19131
|
+
return true;
|
|
19244
19132
|
}
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
this.
|
|
19133
|
+
dequeue() {
|
|
19134
|
+
const write = this.writes.shift();
|
|
19135
|
+
if (write) this.queuedBytes -= write.buffer.length;
|
|
19136
|
+
return write;
|
|
19137
|
+
}
|
|
19138
|
+
clear() {
|
|
19139
|
+
this.writes.length = 0;
|
|
19140
|
+
this.queuedBytes = 0;
|
|
19248
19141
|
}
|
|
19249
19142
|
}
|
|
19250
|
-
const
|
|
19251
|
-
const
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19143
|
+
const boundedWriteQueue = BoundedWriteQueue;
|
|
19144
|
+
const PIPE_SYMBOL = "°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ ";
|
|
19145
|
+
class RecorderStreamWriter extends util_Despot {
|
|
19146
|
+
pendingWrites = new boundedWriteQueue();
|
|
19147
|
+
samplesCount = 0;
|
|
19148
|
+
framesCount = 0;
|
|
19149
|
+
recordingStats;
|
|
19150
|
+
confirmedFrameNumber = 0;
|
|
19151
|
+
confirmedSampleNumber = 0;
|
|
19152
|
+
backpressureTimeout;
|
|
19153
|
+
recordingDimensions;
|
|
19154
|
+
stream;
|
|
19155
|
+
streamBackpressured = false;
|
|
19156
|
+
resumeAfterDrain = false;
|
|
19157
|
+
mediaTransmissionStarted = false;
|
|
19158
|
+
recordingActive = false;
|
|
19159
|
+
recordingTransportFailed = false;
|
|
19160
|
+
connecting = false;
|
|
19161
|
+
connected = false;
|
|
19162
|
+
reconnecting = false;
|
|
19163
|
+
constructor(options){
|
|
19164
|
+
super("Recorder", options);
|
|
19268
19165
|
}
|
|
19269
|
-
|
|
19270
|
-
return
|
|
19166
|
+
hasExceededWebSocketSendBuffer(buffer, opts) {
|
|
19167
|
+
return buffer.length > 0 && !this.pendingWrites.enqueue(buffer, opts);
|
|
19271
19168
|
}
|
|
19272
|
-
|
|
19273
|
-
if (
|
|
19274
|
-
|
|
19275
|
-
|
|
19276
|
-
|
|
19277
|
-
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19281
|
-
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
}
|
|
19289
|
-
this.audioInput?.disconnect();
|
|
19290
|
-
this.volume?.disconnect();
|
|
19169
|
+
writeStream(buffer, opts) {
|
|
19170
|
+
if (this.stream) if (this.stream.destroyed) {
|
|
19171
|
+
this.stopPings();
|
|
19172
|
+
const err = error_createError({
|
|
19173
|
+
message: "Already disconnected",
|
|
19174
|
+
explanation: "Sorry, connection to the server has been destroyed. Please reload.",
|
|
19175
|
+
options: this.options
|
|
19176
|
+
});
|
|
19177
|
+
this.handleWriteFailure(err);
|
|
19178
|
+
} else if (this.streamBackpressured) {
|
|
19179
|
+
if (this.hasExceededWebSocketSendBuffer(buffer, opts)) this.failMediaTransmission(error_createError({
|
|
19180
|
+
message: "WebSocket send buffer limit exceeded",
|
|
19181
|
+
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.",
|
|
19182
|
+
options: this.options
|
|
19183
|
+
}));
|
|
19184
|
+
} else this.writeToStream(this.stream, buffer, opts);
|
|
19291
19185
|
}
|
|
19292
|
-
|
|
19293
|
-
|
|
19294
|
-
this.disconnect();
|
|
19295
|
-
const volume = this.getAudioContext().createGain();
|
|
19296
|
-
this.volume = volume;
|
|
19186
|
+
writeToStream(stream, buffer, opts) {
|
|
19187
|
+
const onFlushedCallback = opts?.onFlushedCallback;
|
|
19297
19188
|
try {
|
|
19298
|
-
|
|
19189
|
+
const canContinue = stream.write(buffer, ()=>{
|
|
19190
|
+
if (!onFlushedCallback) return;
|
|
19191
|
+
try {
|
|
19192
|
+
onFlushedCallback(opts);
|
|
19193
|
+
} catch (exc) {
|
|
19194
|
+
const err = error_createError({
|
|
19195
|
+
message: "Failed to write stream buffer",
|
|
19196
|
+
explanation: `stream.write() failed because of ${string_pretty(exc)}`,
|
|
19197
|
+
options: this.options,
|
|
19198
|
+
exc
|
|
19199
|
+
});
|
|
19200
|
+
this.emit("ERROR", {
|
|
19201
|
+
err
|
|
19202
|
+
});
|
|
19203
|
+
}
|
|
19204
|
+
});
|
|
19205
|
+
if (!canContinue) {
|
|
19206
|
+
this.streamBackpressured = true;
|
|
19207
|
+
this.startBackpressureTimeout();
|
|
19208
|
+
this.pauseForBackpressure();
|
|
19209
|
+
return false;
|
|
19210
|
+
}
|
|
19211
|
+
return true;
|
|
19299
19212
|
} catch (exc) {
|
|
19300
|
-
|
|
19301
|
-
message: "
|
|
19302
|
-
exc
|
|
19303
|
-
options: this.options
|
|
19213
|
+
const err = error_createError({
|
|
19214
|
+
message: "Failed writing to server",
|
|
19215
|
+
explanation: `stream.write() failed because of ${string_pretty(exc)}`,
|
|
19216
|
+
options: this.options,
|
|
19217
|
+
exc
|
|
19304
19218
|
});
|
|
19219
|
+
this.handleWriteFailure(err);
|
|
19220
|
+
return false;
|
|
19305
19221
|
}
|
|
19306
|
-
let { bufferSize } = this.options.audio;
|
|
19307
|
-
if ("auto" === bufferSize) bufferSize = util_getBrowser(this.options).isFirefox() ? 512 : 2048;
|
|
19308
|
-
if (void 0 === bufferSize || !is_power_of_two_default()(bufferSize)) throw error_createError({
|
|
19309
|
-
message: `Audio buffer size must be a power of two. The current buffer size is ${bufferSize}.`,
|
|
19310
|
-
options: this.options
|
|
19311
|
-
});
|
|
19312
|
-
if (!Number.isFinite(this.options.audio.volume) || this.options.audio.volume < 0 || this.options.audio.volume > 1) throw error_createError({
|
|
19313
|
-
message: `Audio volume must be between zero and one. The current volume is ${this.options.audio.volume}.`,
|
|
19314
|
-
options: this.options
|
|
19315
|
-
});
|
|
19316
|
-
volume.gain.value = this.options.audio.volume;
|
|
19317
|
-
this.scriptProcessor = this.getAudioContext().createScriptProcessor(bufferSize, CHANNELS, CHANNELS);
|
|
19318
|
-
this.audioInput.connect(volume);
|
|
19319
|
-
volume.connect(this.scriptProcessor);
|
|
19320
|
-
this.scriptProcessor.connect(this.getAudioContext().destination);
|
|
19321
19222
|
}
|
|
19322
|
-
|
|
19323
|
-
this.
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19223
|
+
flushPendingWrites(stream) {
|
|
19224
|
+
if (this.isStaleStream(stream)) return;
|
|
19225
|
+
this.streamBackpressured = false;
|
|
19226
|
+
this.clearBackpressureTimeout();
|
|
19227
|
+
let pendingWrite = this.pendingWrites.dequeue();
|
|
19228
|
+
while(pendingWrite){
|
|
19229
|
+
if (!this.writeToStream(stream, pendingWrite.buffer, pendingWrite.params)) return;
|
|
19230
|
+
pendingWrite = this.pendingWrites.dequeue();
|
|
19231
|
+
}
|
|
19232
|
+
this.resumeRecordingAfterDrain();
|
|
19327
19233
|
}
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
if ("running" !== context.state) await context.resume();
|
|
19234
|
+
isStaleStream(stream) {
|
|
19235
|
+
return this.stream !== stream || stream.destroyed;
|
|
19331
19236
|
}
|
|
19332
|
-
|
|
19333
|
-
this.
|
|
19334
|
-
this.disconnect();
|
|
19335
|
-
if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
|
|
19336
|
-
this.options.logger.debug("AudioRecorder: audio context is closed");
|
|
19337
|
-
this.vcAudioContext = void 0;
|
|
19338
|
-
}).catch((err)=>{
|
|
19339
|
-
if (err instanceof Error) return void this.options.logger.error(error_createError({
|
|
19340
|
-
err,
|
|
19341
|
-
options: this.options
|
|
19342
|
-
}));
|
|
19343
|
-
this.options.logger.error(err);
|
|
19344
|
-
});
|
|
19237
|
+
canPauseForBackpressure() {
|
|
19238
|
+
return this.recordingActive && !this.isUserMediaPaused() && this.isCaptureLoopRunning();
|
|
19345
19239
|
}
|
|
19346
|
-
|
|
19347
|
-
if (this.
|
|
19348
|
-
|
|
19240
|
+
pauseForBackpressure() {
|
|
19241
|
+
if (!this.canPauseForBackpressure()) return;
|
|
19242
|
+
this.resumeAfterDrain = true;
|
|
19243
|
+
this.stopCaptureLoop();
|
|
19349
19244
|
}
|
|
19350
|
-
|
|
19351
|
-
|
|
19352
|
-
|
|
19353
|
-
|
|
19354
|
-
"virtual",
|
|
19355
|
-
"manycam",
|
|
19356
|
-
"vcam",
|
|
19357
|
-
"splitcam",
|
|
19358
|
-
"droidcam"
|
|
19359
|
-
];
|
|
19360
|
-
function isVirtualCamera(videoTrack) {
|
|
19361
|
-
const capabilities = videoTrack.getCapabilities();
|
|
19362
|
-
if (!capabilities.frameRate) return true;
|
|
19363
|
-
const isVirtual = VIRTUAL_KEYWORDS.some((keyword)=>videoTrack.label.toLowerCase().includes(keyword));
|
|
19364
|
-
return isVirtual;
|
|
19365
|
-
}
|
|
19366
|
-
const util_isVirtualCamera = isVirtualCamera;
|
|
19367
|
-
const EVENT_ASCII = "|—O—|";
|
|
19368
|
-
class UserMedia extends util_Despot {
|
|
19369
|
-
recorder;
|
|
19370
|
-
rawVisualUserMedia;
|
|
19371
|
-
paused = false;
|
|
19372
|
-
recording = false;
|
|
19373
|
-
audioRecorder;
|
|
19374
|
-
currentVisualStream;
|
|
19375
|
-
onPlayReached = false;
|
|
19376
|
-
onLoadedMetaDataReached = false;
|
|
19377
|
-
playingPromiseReached = false;
|
|
19378
|
-
videoTrackLabel;
|
|
19379
|
-
clearInitializationListeners;
|
|
19380
|
-
initializationId = 0;
|
|
19381
|
-
outputEvent = (e)=>{
|
|
19382
|
-
this.logEvent(e.type, {
|
|
19383
|
-
readyState: this.rawVisualUserMedia?.readyState
|
|
19384
|
-
});
|
|
19385
|
-
this.rawVisualUserMedia?.removeEventListener(e.type, this.outputEvent);
|
|
19386
|
-
};
|
|
19387
|
-
handleMediaError = (event)=>{
|
|
19388
|
-
this.options.logger.warn(`Caught video element error event: ${string_pretty(event)}`);
|
|
19389
|
-
};
|
|
19390
|
-
constructor(recorder, options){
|
|
19391
|
-
super("UserMedia", options);
|
|
19392
|
-
this.recorder = recorder;
|
|
19393
|
-
this.rawVisualUserMedia = recorder.getRawVisualUserMedia();
|
|
19394
|
-
mediaEvents.forEach((eventName)=>{
|
|
19395
|
-
this.rawVisualUserMedia?.addEventListener(eventName, this.outputEvent, false);
|
|
19396
|
-
});
|
|
19245
|
+
resumeRecordingAfterDrain() {
|
|
19246
|
+
if (!this.resumeAfterDrain) return;
|
|
19247
|
+
this.resumeAfterDrain = false;
|
|
19248
|
+
if (this.canResumeAfterDrain()) this.startCaptureLoop();
|
|
19397
19249
|
}
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
|
|
19405
|
-
});
|
|
19406
|
-
} else throw error_createError({
|
|
19407
|
-
message: "Error attaching stream to element.",
|
|
19408
|
-
explanation: "Contact the developer about this",
|
|
19409
|
-
options: this.options
|
|
19250
|
+
canResumeAfterDrain() {
|
|
19251
|
+
return this.recordingActive && !this.isUserMediaPaused();
|
|
19252
|
+
}
|
|
19253
|
+
handleWriteFailure(err) {
|
|
19254
|
+
if (this.mediaTransmissionStarted) return void this.failMediaTransmission(err);
|
|
19255
|
+
this.emit("ERROR", {
|
|
19256
|
+
err
|
|
19410
19257
|
});
|
|
19411
19258
|
}
|
|
19412
|
-
|
|
19413
|
-
if (
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
|
|
19259
|
+
failMediaTransmission(err) {
|
|
19260
|
+
if (this.recordingTransportFailed) return;
|
|
19261
|
+
this.recordingTransportFailed = true;
|
|
19262
|
+
this.recordingActive = false;
|
|
19263
|
+
this.mediaTransmissionStarted = false;
|
|
19264
|
+
this.streamBackpressured = false;
|
|
19265
|
+
this.resumeAfterDrain = false;
|
|
19266
|
+
this.pendingWrites.clear();
|
|
19267
|
+
this.connected = false;
|
|
19268
|
+
this.connecting = false;
|
|
19269
|
+
this.reconnecting = false;
|
|
19270
|
+
this.clearReconnectTimeout();
|
|
19271
|
+
this.clearBackpressureTimeout();
|
|
19272
|
+
this.discardSocket();
|
|
19273
|
+
this.reset();
|
|
19274
|
+
this.framesCount = this.samplesCount = 0;
|
|
19275
|
+
this.confirmedFrameNumber = this.confirmedSampleNumber = 0;
|
|
19276
|
+
delete this.recordingStats;
|
|
19277
|
+
this.recordingDimensions = void 0;
|
|
19278
|
+
this.emit("ERROR", {
|
|
19279
|
+
err
|
|
19280
|
+
});
|
|
19421
19281
|
}
|
|
19422
|
-
|
|
19423
|
-
if (!this.
|
|
19424
|
-
|
|
19425
|
-
|
|
19282
|
+
startBackpressureTimeout() {
|
|
19283
|
+
if (!this.shouldStartBackpressureTimeout()) return;
|
|
19284
|
+
this.backpressureTimeout = window.setTimeout(()=>{
|
|
19285
|
+
this.backpressureTimeout = void 0;
|
|
19286
|
+
if (this.shouldIgnoreBackpressureTimeout()) return;
|
|
19287
|
+
const err = error_createError({
|
|
19288
|
+
message: "WebSocket write timed out",
|
|
19289
|
+
explanation: "The server stopped accepting recording data. The recording has been stopped; please check your connection and start a new one.",
|
|
19290
|
+
options: this.options
|
|
19291
|
+
});
|
|
19292
|
+
this.failMediaTransmission(err);
|
|
19293
|
+
}, this.options.timeouts.connection);
|
|
19426
19294
|
}
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
const audioReady = !isAudioEnabled(this.options) || this.currentVisualStream?.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
|
|
19430
|
-
return Boolean(this.currentVisualStream?.active && videoReady && audioReady);
|
|
19295
|
+
shouldStartBackpressureTimeout() {
|
|
19296
|
+
return void 0 === this.backpressureTimeout && this.mediaTransmissionStarted;
|
|
19431
19297
|
}
|
|
19432
|
-
|
|
19433
|
-
|
|
19434
|
-
return !this.currentVisualStream?.active;
|
|
19298
|
+
shouldIgnoreBackpressureTimeout() {
|
|
19299
|
+
return !this.streamBackpressured || this.recordingTransportFailed;
|
|
19435
19300
|
}
|
|
19436
|
-
|
|
19437
|
-
|
|
19438
|
-
const
|
|
19439
|
-
|
|
19301
|
+
failInterruptedRecording() {
|
|
19302
|
+
const unconfirmedFrames = Math.max(0, this.framesCount - this.confirmedFrameNumber);
|
|
19303
|
+
const unconfirmedSamples = Math.max(0, this.samplesCount - this.confirmedSampleNumber);
|
|
19304
|
+
const err = error_createError({
|
|
19305
|
+
message: "Recording interrupted",
|
|
19306
|
+
explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
|
|
19307
|
+
options: this.options,
|
|
19308
|
+
cause: {
|
|
19309
|
+
unconfirmedFrames,
|
|
19310
|
+
unconfirmedSamples
|
|
19311
|
+
}
|
|
19312
|
+
});
|
|
19313
|
+
this.failMediaTransmission(err);
|
|
19440
19314
|
}
|
|
19441
|
-
|
|
19442
|
-
this.options.logger.debug(
|
|
19315
|
+
handleStreamDrain(stream) {
|
|
19316
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *drain* event emitted`);
|
|
19317
|
+
this.flushPendingWrites(stream);
|
|
19443
19318
|
}
|
|
19444
|
-
|
|
19445
|
-
this.
|
|
19446
|
-
this.
|
|
19447
|
-
|
|
19448
|
-
|
|
19319
|
+
clearBackpressureTimeout() {
|
|
19320
|
+
if (void 0 === this.backpressureTimeout) return;
|
|
19321
|
+
window.clearTimeout(this.backpressureTimeout);
|
|
19322
|
+
this.backpressureTimeout = void 0;
|
|
19323
|
+
}
|
|
19324
|
+
discardSocket() {
|
|
19325
|
+
this.options.logger.debug("Recorder: discarding socket stream ...");
|
|
19326
|
+
this.clearBackpressureTimeout();
|
|
19327
|
+
this.pendingWrites.clear();
|
|
19328
|
+
this.streamBackpressured = false;
|
|
19329
|
+
this.resumeAfterDrain = false;
|
|
19330
|
+
const stream = this.stream;
|
|
19331
|
+
this.stream = void 0;
|
|
19332
|
+
if (stream && !stream.destroyed) stream.destroy();
|
|
19333
|
+
}
|
|
19334
|
+
}
|
|
19335
|
+
const streamWriter = RecorderStreamWriter;
|
|
19336
|
+
var Buffer = __webpack_require__("dY").Buffer;
|
|
19337
|
+
class RecorderConnection extends streamWriter {
|
|
19338
|
+
reconnectTimeout;
|
|
19339
|
+
connectionTimeout;
|
|
19340
|
+
userMediaLoaded;
|
|
19341
|
+
userMediaLoading = false;
|
|
19342
|
+
unloaded;
|
|
19343
|
+
connectionFailed = false;
|
|
19344
|
+
reconnectAttempts = 0;
|
|
19345
|
+
blocking = false;
|
|
19346
|
+
connectingStartedAt;
|
|
19347
|
+
lastCloseEvent;
|
|
19348
|
+
lastSocketError;
|
|
19349
|
+
pingInterval;
|
|
19350
|
+
constructor(options){
|
|
19351
|
+
super(options);
|
|
19352
|
+
}
|
|
19353
|
+
canReconnectAfterStreamClose() {
|
|
19354
|
+
return Boolean(this.connected && this.userMediaLoaded);
|
|
19355
|
+
}
|
|
19356
|
+
shouldReportStreamClose() {
|
|
19357
|
+
return !this.connecting && !this.blocking;
|
|
19358
|
+
}
|
|
19359
|
+
handleStreamClose(url2Connect) {
|
|
19360
|
+
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream has closed:`, {
|
|
19361
|
+
connecting: this.connecting,
|
|
19362
|
+
connected: this.connected,
|
|
19363
|
+
userMediaLoaded: this.userMediaLoaded,
|
|
19364
|
+
blocking: this.blocking
|
|
19449
19365
|
});
|
|
19450
|
-
this.
|
|
19366
|
+
this.options.logger.debug(debugLine);
|
|
19367
|
+
const canReconnect = this.canReconnectAfterStreamClose();
|
|
19368
|
+
this.connected = false;
|
|
19369
|
+
this.stopPings();
|
|
19370
|
+
if (this.shouldIgnoreStreamClose()) return;
|
|
19371
|
+
if (this.mediaTransmissionStarted) this.failInterruptedRecording();
|
|
19372
|
+
else if (canReconnect) {
|
|
19373
|
+
this.pendingWrites.clear();
|
|
19374
|
+
this.streamBackpressured = false;
|
|
19375
|
+
this.resumeAfterDrain = false;
|
|
19376
|
+
this.reconnecting = true;
|
|
19377
|
+
this.initSocket();
|
|
19378
|
+
} else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
|
|
19379
|
+
this.handleConnectionFailure({
|
|
19380
|
+
url2Connect,
|
|
19381
|
+
cause: "closed"
|
|
19382
|
+
});
|
|
19383
|
+
}, 0);
|
|
19451
19384
|
}
|
|
19452
|
-
|
|
19453
|
-
this.
|
|
19454
|
-
this.audioRecorder?.record(audioCallback);
|
|
19385
|
+
shouldIgnoreStreamClose() {
|
|
19386
|
+
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
|
|
19455
19387
|
}
|
|
19456
|
-
|
|
19457
|
-
this.
|
|
19458
|
-
this.
|
|
19459
|
-
|
|
19460
|
-
|
|
19388
|
+
handleStreamError(url2Connect, err) {
|
|
19389
|
+
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
19390
|
+
else this.lastSocketError = serializeError(err);
|
|
19391
|
+
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
19392
|
+
error: serializeError(err)
|
|
19461
19393
|
});
|
|
19462
|
-
|
|
19463
|
-
this.
|
|
19464
|
-
|
|
19465
|
-
|
|
19466
|
-
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19394
|
+
this.options.logger.debug(debugLine);
|
|
19395
|
+
if (this.validate()) {
|
|
19396
|
+
this.connected = false;
|
|
19397
|
+
this.stopPings();
|
|
19398
|
+
this.discardSocket();
|
|
19399
|
+
return;
|
|
19400
|
+
}
|
|
19401
|
+
if (!this.connected) return void this.handleConnectionFailure({
|
|
19402
|
+
url2Connect,
|
|
19403
|
+
cause: "error"
|
|
19404
|
+
});
|
|
19405
|
+
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
19406
|
+
cause: this.lastSocketError ?? err
|
|
19407
|
+
});
|
|
19408
|
+
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
19409
|
+
this.emit("ERROR", {
|
|
19410
|
+
err: error_createError({
|
|
19411
|
+
message: "WebSocket stream error",
|
|
19412
|
+
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
19413
|
+
options: this.options,
|
|
19414
|
+
exc: streamError
|
|
19415
|
+
})
|
|
19416
|
+
});
|
|
19417
|
+
}
|
|
19418
|
+
sendPings() {
|
|
19419
|
+
this.stopPings();
|
|
19420
|
+
this.pingInterval = window.setInterval(()=>{
|
|
19421
|
+
this.options.logger.debug("Recorder: pinging...");
|
|
19422
|
+
this.writeStream(Buffer.from(""));
|
|
19423
|
+
}, this.options.timeouts.pingInterval);
|
|
19424
|
+
}
|
|
19425
|
+
stopPings() {
|
|
19426
|
+
if (void 0 === this.pingInterval) return;
|
|
19427
|
+
this.options.logger.debug("Recorder: stopping pings...");
|
|
19428
|
+
window.clearInterval(this.pingInterval);
|
|
19429
|
+
this.pingInterval = void 0;
|
|
19430
|
+
}
|
|
19431
|
+
clearConnectionTimeout() {
|
|
19432
|
+
if (void 0 === this.connectionTimeout) return;
|
|
19433
|
+
this.options.logger.debug("Recorder: clearConnectionTimeout()");
|
|
19434
|
+
window.clearTimeout(this.connectionTimeout);
|
|
19435
|
+
this.connectionTimeout = void 0;
|
|
19436
|
+
}
|
|
19437
|
+
clearReconnectTimeout() {
|
|
19438
|
+
if (void 0 === this.reconnectTimeout) return;
|
|
19439
|
+
this.options.logger.debug("Recorder: clearReconnectTimeout()");
|
|
19440
|
+
window.clearTimeout(this.reconnectTimeout);
|
|
19441
|
+
this.reconnectTimeout = void 0;
|
|
19442
|
+
}
|
|
19443
|
+
handleConnectionFailure(params) {
|
|
19444
|
+
if (this.retryConnection(params)) return;
|
|
19445
|
+
this.failConnection(params);
|
|
19446
|
+
}
|
|
19447
|
+
retryConnection(params) {
|
|
19448
|
+
if (!this.canRetryConnection()) return false;
|
|
19449
|
+
if (void 0 !== this.reconnectTimeout) return true;
|
|
19450
|
+
const delayMs = Math.min(1000 * 2 ** this.reconnectAttempts, 10000);
|
|
19451
|
+
this.reconnectAttempts++;
|
|
19452
|
+
this.connecting = false;
|
|
19453
|
+
this.clearConnectionTimeout();
|
|
19454
|
+
this.discardSocket();
|
|
19455
|
+
this.options.logger.debug(`Recorder: retrying WebSocket connection in ${delayMs}ms (cause ${params.cause}).`);
|
|
19456
|
+
this.reconnectTimeout = window.setTimeout(()=>{
|
|
19457
|
+
this.reconnectTimeout = void 0;
|
|
19458
|
+
if (this.shouldContinueReconnect()) this.initSocket();
|
|
19459
|
+
}, delayMs);
|
|
19460
|
+
return true;
|
|
19461
|
+
}
|
|
19462
|
+
canRetryConnection() {
|
|
19463
|
+
return Boolean(this.reconnecting && this.userMediaLoaded && this.isOnline() && !this.unloaded && !this.blocking);
|
|
19464
|
+
}
|
|
19465
|
+
shouldContinueReconnect() {
|
|
19466
|
+
return this.reconnecting && !this.unloaded && !this.blocking;
|
|
19467
|
+
}
|
|
19468
|
+
isOnline() {
|
|
19469
|
+
return navigator.onLine;
|
|
19470
|
+
}
|
|
19471
|
+
failConnection(params) {
|
|
19472
|
+
if (this.shouldIgnoreConnectionFailure()) return;
|
|
19473
|
+
this.connectionFailed = true;
|
|
19474
|
+
this.connecting = false;
|
|
19475
|
+
this.reconnecting = false;
|
|
19476
|
+
this.reconnectAttempts = 0;
|
|
19477
|
+
this.clearReconnectTimeout();
|
|
19478
|
+
this.clearConnectionTimeout();
|
|
19479
|
+
const { url2Connect, cause } = params;
|
|
19480
|
+
const online = this.isOnline();
|
|
19481
|
+
const elapsedMs = this.connectingStartedAt ? Date.now() - this.connectingStartedAt : void 0;
|
|
19482
|
+
const closeEvent = this.lastCloseEvent;
|
|
19483
|
+
const socketError = this.lastSocketError;
|
|
19484
|
+
const contents = {
|
|
19485
|
+
cause,
|
|
19486
|
+
closeCode: closeEvent?.code,
|
|
19487
|
+
closeReason: closeEvent?.reason,
|
|
19488
|
+
elapsedMs,
|
|
19489
|
+
online,
|
|
19490
|
+
secureContext: globalThis.isSecureContext,
|
|
19491
|
+
socketReadyState: this.stream?.socket.readyState,
|
|
19492
|
+
timeoutMs: this.options.timeouts.connection,
|
|
19493
|
+
url: url2Connect,
|
|
19494
|
+
unloaded: this.unloaded,
|
|
19495
|
+
userMediaLoaded: this.userMediaLoaded,
|
|
19496
|
+
userMediaLoading: this.userMediaLoading,
|
|
19497
|
+
wasClean: closeEvent?.wasClean,
|
|
19498
|
+
socketError,
|
|
19499
|
+
blocking: this.blocking,
|
|
19500
|
+
visibilityState: document.visibilityState
|
|
19479
19501
|
};
|
|
19480
|
-
const
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19487
|
-
|
|
19488
|
-
|
|
19489
|
-
|
|
19490
|
-
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
|
|
19502
|
+
const debugLine = string_summarize("Recorder: failConnection() diagnostic", contents);
|
|
19503
|
+
this.options.logger.debug(debugLine);
|
|
19504
|
+
let explanation;
|
|
19505
|
+
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.`;
|
|
19506
|
+
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.`;
|
|
19507
|
+
else {
|
|
19508
|
+
const closeSuffix = closeEvent ? ` (code ${closeEvent.code})` : "";
|
|
19509
|
+
explanation = `Connection to ${url2Connect} is closed${closeSuffix}. Please check your internet connection and try again. If the problem persists, contact us.`;
|
|
19510
|
+
}
|
|
19511
|
+
else explanation = "Your device appears to be offline. Please check your internet connection and try again.";
|
|
19512
|
+
this.discardSocket();
|
|
19513
|
+
const err = error_createError({
|
|
19514
|
+
message: "Unable to connect to the server",
|
|
19515
|
+
explanation,
|
|
19516
|
+
options: this.options,
|
|
19517
|
+
cause: contents,
|
|
19518
|
+
exc: socketError ? new Error("WebSocket connection error", {
|
|
19519
|
+
cause: socketError
|
|
19520
|
+
}) : void 0
|
|
19521
|
+
});
|
|
19522
|
+
this.emit("ERROR", {
|
|
19523
|
+
err
|
|
19524
|
+
});
|
|
19525
|
+
}
|
|
19526
|
+
shouldIgnoreConnectionFailure() {
|
|
19527
|
+
return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
|
|
19528
|
+
}
|
|
19529
|
+
initSocket(cb) {
|
|
19530
|
+
if (this.connected) {
|
|
19531
|
+
this.options.logger.debug("Recorder: already **connected**. Not going to initialize a new WebSocket connection.");
|
|
19532
|
+
cb?.();
|
|
19533
|
+
return;
|
|
19534
|
+
}
|
|
19535
|
+
if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
|
|
19536
|
+
this.clearReconnectTimeout();
|
|
19537
|
+
if (util_isAutomatedUserAgent()) {
|
|
19538
|
+
this.connecting = false;
|
|
19539
|
+
this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
|
|
19540
|
+
return;
|
|
19541
|
+
}
|
|
19542
|
+
this.connecting = true;
|
|
19543
|
+
this.connectionFailed = false;
|
|
19544
|
+
this.connectingStartedAt = Date.now();
|
|
19545
|
+
this.lastCloseEvent = void 0;
|
|
19546
|
+
this.lastSocketError = void 0;
|
|
19547
|
+
this.emit("CONNECTING");
|
|
19548
|
+
let url2Connect;
|
|
19549
|
+
try {
|
|
19550
|
+
const socketUrlObj = new URL(this.options.socketUrl);
|
|
19551
|
+
socketUrlObj.searchParams.set(constants.WHITELIST_KEY_LABEL, this.options.whitelistKey);
|
|
19552
|
+
url2Connect = socketUrlObj.toString();
|
|
19553
|
+
} catch (exc) {
|
|
19554
|
+
this.connecting = this.connected = false;
|
|
19555
|
+
const err = error_createError({
|
|
19556
|
+
message: "Invalid WebSocket URL",
|
|
19557
|
+
explanation: `The configured socketUrl "${this.options.socketUrl}" is not a valid URL. Please check your videomail-client configuration.`,
|
|
19558
|
+
options: this.options,
|
|
19559
|
+
exc
|
|
19560
|
+
});
|
|
19561
|
+
this.emit("ERROR", {
|
|
19562
|
+
err
|
|
19563
|
+
});
|
|
19564
|
+
return;
|
|
19565
|
+
}
|
|
19566
|
+
this.options.logger.debug(`Recorder: initializing web socket stream to ${url2Connect}`);
|
|
19567
|
+
let nativeSocket;
|
|
19568
|
+
try {
|
|
19569
|
+
nativeSocket = new WebSocket(url2Connect);
|
|
19570
|
+
nativeSocket.addEventListener("close", (event)=>{
|
|
19571
|
+
this.lastCloseEvent = {
|
|
19572
|
+
code: event.code,
|
|
19573
|
+
reason: event.reason,
|
|
19574
|
+
wasClean: event.wasClean
|
|
19522
19575
|
};
|
|
19523
|
-
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
}
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
|
|
19539
|
-
|
|
19540
|
-
|
|
19541
|
-
|
|
19542
|
-
|
|
19543
|
-
|
|
19544
|
-
|
|
19545
|
-
|
|
19576
|
+
});
|
|
19577
|
+
nativeSocket.addEventListener("error", (event)=>{
|
|
19578
|
+
this.lastSocketError = error_getEventDetails(event);
|
|
19579
|
+
});
|
|
19580
|
+
} catch (exc) {
|
|
19581
|
+
this.connecting = this.connected = false;
|
|
19582
|
+
const diagnostic = error_getWebSocketDiagnostic(url2Connect);
|
|
19583
|
+
const message = diagnostic.looksAutomated ? "Automated crawler: WebSocket not supported in this environment" : `Failed to construct WebSocket to ${url2Connect}`;
|
|
19584
|
+
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}`;
|
|
19585
|
+
const err = error_createError({
|
|
19586
|
+
message,
|
|
19587
|
+
explanation,
|
|
19588
|
+
options: this.options,
|
|
19589
|
+
exc
|
|
19590
|
+
});
|
|
19591
|
+
this.emit("ERROR", {
|
|
19592
|
+
err
|
|
19593
|
+
});
|
|
19594
|
+
return;
|
|
19595
|
+
}
|
|
19596
|
+
try {
|
|
19597
|
+
this.stream = stream_default()(nativeSocket);
|
|
19598
|
+
} catch (exc) {
|
|
19599
|
+
this.connecting = this.connected = false;
|
|
19600
|
+
const err = error_createError({
|
|
19601
|
+
message: `Failed to create a stream to ${url2Connect}`,
|
|
19602
|
+
explanation: "Please check your connection and try again. If the problem persists, contact us.",
|
|
19603
|
+
options: this.options,
|
|
19604
|
+
exc
|
|
19605
|
+
});
|
|
19606
|
+
this.emit("ERROR", {
|
|
19607
|
+
err
|
|
19608
|
+
});
|
|
19609
|
+
}
|
|
19610
|
+
const stream = this.stream;
|
|
19611
|
+
if (stream) {
|
|
19612
|
+
const connectionTimeoutMs = this.options.timeouts.connection;
|
|
19613
|
+
this.connectionTimeout = window.setTimeout(()=>{
|
|
19614
|
+
if (this.isOnline() && this.isUserMediaLoaded()) {
|
|
19615
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Reconnecting due to connection timeout.`);
|
|
19616
|
+
this.reconnecting = true;
|
|
19617
|
+
this.handleConnectionFailure({
|
|
19618
|
+
url2Connect,
|
|
19619
|
+
cause: "timeout"
|
|
19620
|
+
});
|
|
19621
|
+
} else this.handleConnectionFailure({
|
|
19622
|
+
url2Connect,
|
|
19623
|
+
cause: "timeout"
|
|
19546
19624
|
});
|
|
19547
|
-
|
|
19548
|
-
|
|
19549
|
-
|
|
19550
|
-
|
|
19551
|
-
|
|
19552
|
-
}));
|
|
19553
|
-
|
|
19554
|
-
|
|
19555
|
-
|
|
19625
|
+
}, connectionTimeoutMs);
|
|
19626
|
+
stream.on("close", ()=>{
|
|
19627
|
+
this.handleStreamClose(url2Connect);
|
|
19628
|
+
});
|
|
19629
|
+
stream.on("connect", (args)=>{
|
|
19630
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
|
|
19631
|
+
this.clearConnectionTimeout();
|
|
19632
|
+
const isClosing = this.stream?.socket.readyState === WebSocket.CLOSING;
|
|
19633
|
+
if (!this.connected && !isClosing && !this.unloaded) {
|
|
19634
|
+
this.connected = true;
|
|
19635
|
+
this.connecting = this.unloaded = false;
|
|
19636
|
+
this.reconnecting = false;
|
|
19637
|
+
this.reconnectAttempts = 0;
|
|
19638
|
+
this.clearReconnectTimeout();
|
|
19639
|
+
this.emit("CONNECTED");
|
|
19640
|
+
cb?.();
|
|
19556
19641
|
}
|
|
19557
|
-
}
|
|
19558
|
-
|
|
19559
|
-
|
|
19642
|
+
});
|
|
19643
|
+
stream.on("data", (data)=>{
|
|
19644
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
|
|
19645
|
+
try {
|
|
19646
|
+
const command = JSON.parse(data.toString());
|
|
19647
|
+
this.executeCommand(command);
|
|
19648
|
+
} catch (exc) {
|
|
19649
|
+
this.options.logger.error(`Failed to parse command: ${exc}`);
|
|
19650
|
+
const err = error_createError({
|
|
19651
|
+
message: "Invalid server command",
|
|
19652
|
+
explanation: `Contact us. The invalid command was: ${data.toString()}.`,
|
|
19653
|
+
options: this.options,
|
|
19654
|
+
exc
|
|
19655
|
+
});
|
|
19656
|
+
this.emit("ERROR", {
|
|
19657
|
+
err
|
|
19658
|
+
});
|
|
19659
|
+
}
|
|
19660
|
+
});
|
|
19661
|
+
stream.on("error", (err)=>{
|
|
19662
|
+
this.handleStreamError(url2Connect, err);
|
|
19663
|
+
});
|
|
19664
|
+
stream.on("drain", ()=>{
|
|
19665
|
+
this.handleStreamDrain(stream);
|
|
19666
|
+
});
|
|
19667
|
+
stream.on("preend", (args)=>{
|
|
19668
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
|
|
19669
|
+
});
|
|
19670
|
+
stream.on("end", ()=>{
|
|
19671
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *end* event emitted`);
|
|
19672
|
+
});
|
|
19673
|
+
stream.on("pipe", (src)=>{
|
|
19674
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *pipe* event emitted with src: ${string_pretty(src)}`);
|
|
19675
|
+
});
|
|
19676
|
+
stream.on("unpipe", (src)=>{
|
|
19677
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *unpipe* event emitted with src: ${string_pretty(src)}`);
|
|
19678
|
+
});
|
|
19679
|
+
stream.on("resume", (args)=>{
|
|
19680
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *resume* event emitted with args: ${string_pretty(args)}`);
|
|
19681
|
+
});
|
|
19682
|
+
stream.on("uncork", (args)=>{
|
|
19683
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *uncork* event emitted with args: ${string_pretty(args)}`);
|
|
19684
|
+
});
|
|
19685
|
+
stream.on("readable", (args)=>{
|
|
19686
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *readable* event emitted with args: ${string_pretty(args)}`);
|
|
19687
|
+
});
|
|
19688
|
+
stream.on("prefinish", (args)=>{
|
|
19689
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *prefinish* event emitted with args: ${string_pretty(args)}`);
|
|
19690
|
+
});
|
|
19691
|
+
stream.on("finish", (args)=>{
|
|
19692
|
+
this.options.logger.debug(`${PIPE_SYMBOL}Stream *finish* event emitted with args: ${string_pretty(args)}`);
|
|
19693
|
+
});
|
|
19694
|
+
}
|
|
19695
|
+
}
|
|
19696
|
+
writeCommand(command, args, cb) {
|
|
19697
|
+
if (this.connected) {
|
|
19698
|
+
if (this.stream) {
|
|
19699
|
+
if (args) this.options.logger.debug(`$ ${command} with ${string_pretty(args)}`);
|
|
19700
|
+
else this.options.logger.debug(`$ ${command}`);
|
|
19701
|
+
const commandObj = {
|
|
19702
|
+
command,
|
|
19703
|
+
args
|
|
19704
|
+
};
|
|
19705
|
+
this.writeStream(Buffer.from(JSON.stringify(commandObj)), cb ? {
|
|
19706
|
+
onFlushedCallback: ()=>{
|
|
19707
|
+
queueMicrotask(cb);
|
|
19708
|
+
}
|
|
19709
|
+
} : void 0);
|
|
19560
19710
|
}
|
|
19561
|
-
}
|
|
19562
|
-
|
|
19563
|
-
this.
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19568
|
-
|
|
19569
|
-
|
|
19711
|
+
} else {
|
|
19712
|
+
this.options.logger.debug(`Reconnecting for the command ${command} …`);
|
|
19713
|
+
this.initSocket(()=>{
|
|
19714
|
+
this.writeCommand(command, args, cb);
|
|
19715
|
+
});
|
|
19716
|
+
}
|
|
19717
|
+
}
|
|
19718
|
+
}
|
|
19719
|
+
const connection = RecorderConnection;
|
|
19720
|
+
function createUserMediaConstraints({ options, facingMode, limitedWidth, userMediaParams }) {
|
|
19721
|
+
const video = {
|
|
19722
|
+
frameRate: {
|
|
19723
|
+
ideal: options.video.fps
|
|
19724
|
+
}
|
|
19725
|
+
};
|
|
19726
|
+
if (userMediaParams?.switchingFacingMode || userMediaParams?.recovering) video.facingMode = userMediaParams.switchingFacingMode || facingMode;
|
|
19727
|
+
if (options.video.width) video.width = {
|
|
19728
|
+
ideal: options.video.width
|
|
19729
|
+
};
|
|
19730
|
+
else if (limitedWidth?.value) video.width = {
|
|
19731
|
+
ideal: limitedWidth.value
|
|
19732
|
+
};
|
|
19733
|
+
if (options.video.height) video.height = {
|
|
19734
|
+
ideal: options.video.height
|
|
19735
|
+
};
|
|
19736
|
+
return {
|
|
19737
|
+
audio: isAudioEnabled(options),
|
|
19738
|
+
video
|
|
19739
|
+
};
|
|
19740
|
+
}
|
|
19741
|
+
const mediaConstraints = createUserMediaConstraints;
|
|
19742
|
+
class RecorderMedia extends connection {
|
|
19743
|
+
visuals;
|
|
19744
|
+
recorderElement;
|
|
19745
|
+
userMedia;
|
|
19746
|
+
userMediaTimeout;
|
|
19747
|
+
retryTimeout;
|
|
19748
|
+
stopTimeout;
|
|
19749
|
+
mediaRequestId = 0;
|
|
19750
|
+
cameraRecovery;
|
|
19751
|
+
resolveCameraRecovery;
|
|
19752
|
+
resumeAfterCameraRecovery = false;
|
|
19753
|
+
submitting = false;
|
|
19754
|
+
built = false;
|
|
19755
|
+
key;
|
|
19756
|
+
facingMode;
|
|
19757
|
+
constructor(visuals, options){
|
|
19758
|
+
super(options);
|
|
19759
|
+
this.visuals = visuals;
|
|
19760
|
+
this.facingMode = options.video.facingMode;
|
|
19761
|
+
}
|
|
19762
|
+
show() {
|
|
19763
|
+
html_showElement(this.recorderElement);
|
|
19764
|
+
}
|
|
19765
|
+
clearRetryTimeout() {
|
|
19766
|
+
if (void 0 === this.retryTimeout) return;
|
|
19767
|
+
this.options.logger.debug("Recorder: clearRetryTimeout()");
|
|
19768
|
+
window.clearTimeout(this.retryTimeout);
|
|
19769
|
+
this.retryTimeout = void 0;
|
|
19770
|
+
}
|
|
19771
|
+
showUserMedia() {
|
|
19772
|
+
if (!this.connected) return false;
|
|
19773
|
+
const hidden = this.isHidden();
|
|
19774
|
+
if (!hidden) return true;
|
|
19775
|
+
const notifying = this.isNotifying();
|
|
19776
|
+
if (notifying) return true;
|
|
19777
|
+
return this.blocking;
|
|
19778
|
+
}
|
|
19779
|
+
userMediaErrorCallback(err, usedConstraints) {
|
|
19780
|
+
if (this.cameraRecovery) return void this.failCameraRecovery(err);
|
|
19781
|
+
if (this.unloaded) return void this.options.logger.debug(`Recorder: already unloaded. Not going to throw error ${string_pretty(err)}`);
|
|
19782
|
+
this.userMediaLoading = false;
|
|
19783
|
+
this.clearUserMediaTimeout();
|
|
19784
|
+
const characteristics = this.userMedia?.getCharacteristics();
|
|
19785
|
+
this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
|
|
19786
|
+
const errorListeners = this.getListeners("ERROR");
|
|
19787
|
+
if (errorListeners?.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
|
|
19788
|
+
const videomailError = error_createError({
|
|
19789
|
+
err,
|
|
19790
|
+
options: this.options,
|
|
19791
|
+
usedConstraints
|
|
19570
19792
|
});
|
|
19571
|
-
this.
|
|
19572
|
-
|
|
19573
|
-
|
|
19574
|
-
|
|
19575
|
-
|
|
19576
|
-
|
|
19577
|
-
|
|
19578
|
-
|
|
19579
|
-
|
|
19580
|
-
|
|
19581
|
-
|
|
19582
|
-
|
|
19583
|
-
|
|
19793
|
+
this.emit("ERROR", {
|
|
19794
|
+
err: videomailError
|
|
19795
|
+
});
|
|
19796
|
+
} else {
|
|
19797
|
+
this.options.logger.debug(`Recorder: ignore user media error ${string_pretty(err)}`);
|
|
19798
|
+
this.retryTimeout = window.setTimeout(()=>{
|
|
19799
|
+
this.retryTimeout = void 0;
|
|
19800
|
+
if (!this.unloaded) this.loadUserMedia();
|
|
19801
|
+
}, this.options.timeouts.userMedia);
|
|
19802
|
+
}
|
|
19803
|
+
else {
|
|
19804
|
+
this.options.logger.debug(`Recorder: no error listeners attached but throwing error ${string_pretty(err)}`);
|
|
19805
|
+
throw error_createError({
|
|
19806
|
+
err,
|
|
19807
|
+
message: "Unable to process this error since there are no error listeners anymore.",
|
|
19584
19808
|
options: this.options
|
|
19585
19809
|
});
|
|
19586
|
-
|
|
19587
|
-
|
|
19588
|
-
|
|
19589
|
-
|
|
19590
|
-
|
|
19591
|
-
|
|
19592
|
-
|
|
19593
|
-
|
|
19594
|
-
|
|
19810
|
+
}
|
|
19811
|
+
}
|
|
19812
|
+
getUserMediaCallback(localStream, params, requestId = this.mediaRequestId) {
|
|
19813
|
+
if (!this.userMedia) {
|
|
19814
|
+
localStream.getTracks().forEach((track)=>{
|
|
19815
|
+
track.stop();
|
|
19816
|
+
});
|
|
19817
|
+
throw error_createError({
|
|
19818
|
+
message: "No user media is defined",
|
|
19595
19819
|
options: this.options
|
|
19596
19820
|
});
|
|
19597
|
-
|
|
19598
|
-
|
|
19599
|
-
|
|
19600
|
-
this.
|
|
19601
|
-
|
|
19602
|
-
this.
|
|
19603
|
-
|
|
19821
|
+
}
|
|
19822
|
+
this.options.logger.debug(`Recorder: getUserMediaCallback(${params ? string_pretty(params) : ""})`);
|
|
19823
|
+
if (this.showUserMedia()) try {
|
|
19824
|
+
this.userMedia.init(localStream, ()=>{
|
|
19825
|
+
if (!this.unloaded && requestId === this.mediaRequestId) this.onUserMediaReady(params);
|
|
19826
|
+
}, this.onAudioSample.bind(this), (err)=>{
|
|
19827
|
+
if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(err, {
|
|
19828
|
+
audio: isAudioEnabled(this.options)
|
|
19829
|
+
});
|
|
19830
|
+
}, params);
|
|
19604
19831
|
} catch (exc) {
|
|
19605
|
-
|
|
19606
|
-
|
|
19832
|
+
localStream.getTracks().forEach((track)=>{
|
|
19833
|
+
track.stop();
|
|
19834
|
+
});
|
|
19835
|
+
this.userMediaLoading = false;
|
|
19836
|
+
this.clearUserMediaTimeout();
|
|
19837
|
+
this.userMediaErrorCallback(exc, {
|
|
19838
|
+
audio: isAudioEnabled(this.options)
|
|
19839
|
+
});
|
|
19840
|
+
}
|
|
19841
|
+
else {
|
|
19842
|
+
localStream.getTracks().forEach((track)=>{
|
|
19843
|
+
track.stop();
|
|
19844
|
+
});
|
|
19845
|
+
this.userMediaLoading = false;
|
|
19846
|
+
this.clearUserMediaTimeout();
|
|
19847
|
+
this.cancelCameraRecovery();
|
|
19607
19848
|
}
|
|
19608
19849
|
}
|
|
19609
|
-
|
|
19610
|
-
|
|
19611
|
-
|
|
19612
|
-
|
|
19613
|
-
|
|
19850
|
+
loadGenuineUserMedia(params) {
|
|
19851
|
+
this.options.logger.debug(`Recorder: loadGenuineUserMedia(${params ? string_pretty(params) : ""})`);
|
|
19852
|
+
this.emit("ASKING_WEBCAM_PERMISSION");
|
|
19853
|
+
this.userMediaLoading = true;
|
|
19854
|
+
const requestId = ++this.mediaRequestId;
|
|
19855
|
+
const limitedWidth = this.options.video.width ? void 0 : this.visuals.limitWidth();
|
|
19856
|
+
const constraints = mediaConstraints({
|
|
19857
|
+
facingMode: this.facingMode,
|
|
19858
|
+
limitedWidth,
|
|
19859
|
+
options: this.options,
|
|
19860
|
+
userMediaParams: params
|
|
19861
|
+
});
|
|
19862
|
+
this.options.logger.debug(string_summarize("Recorder: our webcam constraints are:", constraints));
|
|
19863
|
+
this.options.logger.debug(string_summarize("Recorder: available webcam constraints are:", navigator.mediaDevices.getSupportedConstraints()));
|
|
19864
|
+
const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
|
|
19865
|
+
streamPromise.then((localStream)=>{
|
|
19866
|
+
if (this.unloaded || requestId !== this.mediaRequestId) return void localStream.getTracks().forEach((track)=>{
|
|
19867
|
+
track.stop();
|
|
19868
|
+
});
|
|
19869
|
+
this.getUserMediaCallback(localStream, params, requestId);
|
|
19870
|
+
}).catch((reason)=>{
|
|
19871
|
+
if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(reason, constraints);
|
|
19872
|
+
});
|
|
19614
19873
|
}
|
|
19615
|
-
|
|
19874
|
+
loadUserMedia(params) {
|
|
19875
|
+
if (this.userMediaLoaded) {
|
|
19876
|
+
this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already loaded");
|
|
19877
|
+
this.onUserMediaReady(params);
|
|
19878
|
+
return;
|
|
19879
|
+
} else if (this.userMediaLoading) return void this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already asking for permission");
|
|
19880
|
+
this.options.logger.debug(`Recorder: loadUserMedia(${params ? string_pretty(params) : ""})`);
|
|
19881
|
+
this.emit("LOADING_USER_MEDIA");
|
|
19616
19882
|
try {
|
|
19617
|
-
|
|
19618
|
-
|
|
19619
|
-
this.
|
|
19620
|
-
|
|
19621
|
-
|
|
19622
|
-
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
}
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
}
|
|
19630
|
-
if (!params?.preserveRecordingState) this.paused = this.recording = false;
|
|
19883
|
+
if (!navigator.mediaDevices) throw new Error("No media devices are available.");
|
|
19884
|
+
this.userMediaTimeout = window.setTimeout(()=>{
|
|
19885
|
+
if (!this.userMediaLoaded) {
|
|
19886
|
+
this.userMediaLoading = false;
|
|
19887
|
+
const err = util_getBrowser(this.options).getNoAccessIssue();
|
|
19888
|
+
this.userMediaErrorCallback(err, {
|
|
19889
|
+
audio: isAudioEnabled(this.options)
|
|
19890
|
+
});
|
|
19891
|
+
}
|
|
19892
|
+
}, this.options.timeouts.userMedia);
|
|
19893
|
+
this.userMediaLoading = true;
|
|
19894
|
+
this.loadGenuineUserMedia(params);
|
|
19631
19895
|
} catch (exc) {
|
|
19632
|
-
this.
|
|
19896
|
+
this.clearUserMediaTimeout();
|
|
19897
|
+
this.options.logger.debug("Recorder: failed to load genuine user media");
|
|
19898
|
+
this.userMediaLoading = false;
|
|
19899
|
+
if (this.cameraRecovery) return void this.failCameraRecovery(exc);
|
|
19900
|
+
const errorListeners = this.getListeners("ERROR");
|
|
19901
|
+
if (errorListeners?.length) this.emit("ERROR", {
|
|
19633
19902
|
exc
|
|
19634
19903
|
});
|
|
19904
|
+
else {
|
|
19905
|
+
this.options.logger.debug("Recorder: no error listeners attached but throwing exception further");
|
|
19906
|
+
throw exc;
|
|
19907
|
+
}
|
|
19635
19908
|
}
|
|
19636
19909
|
}
|
|
19637
|
-
|
|
19638
|
-
|
|
19639
|
-
const rawWidthDimension = this.getRawWidth(true);
|
|
19640
|
-
if (rawWidthDimension.value) canvas.width = rawWidthDimension.value;
|
|
19641
|
-
const rawHeight = this.getRawHeight(true);
|
|
19642
|
-
if (rawHeight) canvas.height = rawHeight;
|
|
19643
|
-
return canvas;
|
|
19644
|
-
}
|
|
19645
|
-
getVideoHeight() {
|
|
19646
|
-
if (!this.rawVisualUserMedia) return;
|
|
19647
|
-
return this.rawVisualUserMedia.videoHeight || this.rawVisualUserMedia.height;
|
|
19648
|
-
}
|
|
19649
|
-
getVideoWidth() {
|
|
19650
|
-
if (!this.rawVisualUserMedia) return;
|
|
19651
|
-
return this.rawVisualUserMedia.videoWidth || this.rawVisualUserMedia.width;
|
|
19910
|
+
isNotifying() {
|
|
19911
|
+
return this.visuals.isNotifying();
|
|
19652
19912
|
}
|
|
19653
|
-
|
|
19654
|
-
|
|
19655
|
-
return videoWidth && videoWidth > 0;
|
|
19913
|
+
isHidden() {
|
|
19914
|
+
return !this.recorderElement || html_isHidden(this.recorderElement);
|
|
19656
19915
|
}
|
|
19657
|
-
|
|
19658
|
-
|
|
19659
|
-
|
|
19660
|
-
|
|
19661
|
-
|
|
19662
|
-
rawWidth = responsive ? this.getVideoWidth() : this.options.video.width ? this.options.video.width : this.getVideoWidth();
|
|
19663
|
-
if (responsive) {
|
|
19664
|
-
const widthDimension = this.recorder.limitWidth(rawWidth);
|
|
19665
|
-
rawWidth = widthDimension?.value;
|
|
19916
|
+
clearUserMediaTimeout() {
|
|
19917
|
+
if (void 0 !== this.userMediaTimeout) {
|
|
19918
|
+
this.options.logger.debug("Recorder: clearUserMediaTimeout()");
|
|
19919
|
+
window.clearTimeout(this.userMediaTimeout);
|
|
19920
|
+
this.userMediaTimeout = void 0;
|
|
19666
19921
|
}
|
|
19667
|
-
widthDimension.value = rawWidth;
|
|
19668
|
-
return widthDimension;
|
|
19669
19922
|
}
|
|
19670
|
-
|
|
19671
|
-
|
|
19672
|
-
|
|
19673
|
-
|
|
19674
|
-
const ratio = this.recorder.getRatio();
|
|
19675
|
-
const rawWidth = this.getRawWidth(responsive);
|
|
19676
|
-
if (void 0 !== ratio && rawWidth.value) rawHeight = rawWidth.value * ratio;
|
|
19677
|
-
} else rawHeight = this.options.video.height ? this.options.video.height : this.getVideoHeight();
|
|
19678
|
-
if (responsive) {
|
|
19679
|
-
const heightDimension = this.recorder.limitHeight(rawHeight);
|
|
19680
|
-
rawHeight = heightDimension.value;
|
|
19681
|
-
}
|
|
19682
|
-
return rawHeight;
|
|
19923
|
+
clearStopTimeout() {
|
|
19924
|
+
if (void 0 === this.stopTimeout) return;
|
|
19925
|
+
window.clearTimeout(this.stopTimeout);
|
|
19926
|
+
this.stopTimeout = void 0;
|
|
19683
19927
|
}
|
|
19684
|
-
|
|
19685
|
-
return this.
|
|
19928
|
+
isReady() {
|
|
19929
|
+
return this.userMedia?.isReady();
|
|
19686
19930
|
}
|
|
19687
|
-
|
|
19688
|
-
this.
|
|
19931
|
+
finishCameraRecovery(restored) {
|
|
19932
|
+
this.clearUserMediaTimeout();
|
|
19933
|
+
const resolve = this.resolveCameraRecovery;
|
|
19934
|
+
const resumeRequested = restored && this.resumeAfterCameraRecovery;
|
|
19935
|
+
this.cameraRecovery = void 0;
|
|
19936
|
+
this.resolveCameraRecovery = void 0;
|
|
19937
|
+
this.resumeAfterCameraRecovery = false;
|
|
19938
|
+
resolve?.(restored);
|
|
19939
|
+
if (resumeRequested && this.isPaused()) this.resume();
|
|
19689
19940
|
}
|
|
19690
|
-
|
|
19691
|
-
|
|
19941
|
+
cancelCameraRecovery() {
|
|
19942
|
+
if (!this.cameraRecovery) return;
|
|
19943
|
+
this.mediaRequestId++;
|
|
19944
|
+
this.userMediaLoading = false;
|
|
19945
|
+
this.userMediaLoaded = false;
|
|
19946
|
+
this.clearUserMediaTimeout();
|
|
19947
|
+
this.userMedia?.stop(void 0, {
|
|
19948
|
+
preserveRecordingState: true
|
|
19949
|
+
});
|
|
19950
|
+
this.finishCameraRecovery(false);
|
|
19951
|
+
}
|
|
19952
|
+
failCameraRecovery(exc) {
|
|
19953
|
+
this.cancelCameraRecovery();
|
|
19954
|
+
const err = error_createError({
|
|
19955
|
+
message: "Camera recovery failed",
|
|
19956
|
+
explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
|
|
19957
|
+
exc,
|
|
19958
|
+
options: this.options
|
|
19959
|
+
});
|
|
19960
|
+
this.emit("CAMERA_RECOVERY_FAILED", {
|
|
19961
|
+
err
|
|
19962
|
+
});
|
|
19692
19963
|
}
|
|
19693
|
-
|
|
19694
|
-
this.
|
|
19964
|
+
recoverCamera() {
|
|
19965
|
+
if (this.cameraRecovery) return this.cameraRecovery;
|
|
19966
|
+
if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
|
|
19967
|
+
const liveStream = this.userMedia.hasLiveStream();
|
|
19968
|
+
if (liveStream && this.isRecording()) return Promise.resolve(true);
|
|
19969
|
+
if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
|
|
19970
|
+
const recovery = new Promise((resolve)=>{
|
|
19971
|
+
this.resolveCameraRecovery = resolve;
|
|
19972
|
+
});
|
|
19973
|
+
this.cameraRecovery = recovery;
|
|
19974
|
+
this.emit("CAMERA_RECOVERING");
|
|
19975
|
+
if (liveStream) {
|
|
19976
|
+
this.userMediaTimeout = window.setTimeout(()=>{
|
|
19977
|
+
this.failCameraRecovery(new Error("Camera playback recovery timed out."));
|
|
19978
|
+
}, this.options.timeouts.userMedia);
|
|
19979
|
+
this.userMedia.restorePlayback().then(()=>{
|
|
19980
|
+
if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
|
|
19981
|
+
}).catch((exc)=>{
|
|
19982
|
+
if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
|
|
19983
|
+
});
|
|
19984
|
+
} else {
|
|
19985
|
+
this.userMediaLoaded = false;
|
|
19986
|
+
this.loadUserMedia({
|
|
19987
|
+
recovering: true
|
|
19988
|
+
});
|
|
19989
|
+
}
|
|
19990
|
+
return recovery;
|
|
19695
19991
|
}
|
|
19696
|
-
|
|
19697
|
-
this.
|
|
19992
|
+
switchFacingMode() {
|
|
19993
|
+
if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
|
|
19994
|
+
if ("user" === this.facingMode) this.facingMode = "environment";
|
|
19995
|
+
else if ("environment" === this.facingMode) this.facingMode = "user";
|
|
19996
|
+
else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
|
|
19997
|
+
this.loadGenuineUserMedia({
|
|
19998
|
+
switchingFacingMode: this.facingMode
|
|
19999
|
+
});
|
|
19698
20000
|
}
|
|
19699
|
-
|
|
19700
|
-
|
|
20001
|
+
hide() {
|
|
20002
|
+
if (!this.isHidden()) {
|
|
20003
|
+
if (this.recorderElement) html_hideElement(this.recorderElement);
|
|
20004
|
+
this.clearUserMediaTimeout();
|
|
20005
|
+
this.clearRetryTimeout();
|
|
20006
|
+
}
|
|
19701
20007
|
}
|
|
19702
|
-
|
|
19703
|
-
|
|
19704
|
-
return -1;
|
|
20008
|
+
isUnloaded() {
|
|
20009
|
+
return this.unloaded;
|
|
19705
20010
|
}
|
|
19706
|
-
|
|
19707
|
-
return
|
|
19708
|
-
audioSampleRate: this.getAudioSampleRate(),
|
|
19709
|
-
muted: this.rawVisualUserMedia?.muted,
|
|
19710
|
-
width: this.rawVisualUserMedia?.width,
|
|
19711
|
-
height: this.rawVisualUserMedia?.height,
|
|
19712
|
-
videoWidth: this.rawVisualUserMedia?.videoWidth,
|
|
19713
|
-
videoHeight: this.rawVisualUserMedia?.videoHeight
|
|
19714
|
-
};
|
|
20011
|
+
isUserMediaLoaded() {
|
|
20012
|
+
return this.userMediaLoaded;
|
|
19715
20013
|
}
|
|
19716
20014
|
}
|
|
19717
|
-
const
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
20015
|
+
const media = RecorderMedia;
|
|
20016
|
+
function calculateProgress(confirmedCount, totalCount) {
|
|
20017
|
+
return `${(confirmedCount / (totalCount || 1) * 100).toFixed(2)}%`;
|
|
20018
|
+
}
|
|
20019
|
+
function getConfirmedProgressNumber({ args, kind, options }) {
|
|
20020
|
+
const label = "frame" === kind ? "frame" : "audio sample";
|
|
20021
|
+
if (!args) throw error_createError({
|
|
20022
|
+
message: `Arguments are missing for updating the ${label} progress`,
|
|
20023
|
+
options
|
|
20024
|
+
});
|
|
20025
|
+
const confirmedNumber = args[kind];
|
|
20026
|
+
if (!confirmedNumber) throw error_createError({
|
|
20027
|
+
message: `The ${label} number is missing`,
|
|
20028
|
+
options
|
|
20029
|
+
});
|
|
20030
|
+
return confirmedNumber;
|
|
20031
|
+
}
|
|
20032
|
+
var capture_Buffer = __webpack_require__("dY").Buffer;
|
|
20033
|
+
class RecorderCapture extends media {
|
|
19722
20034
|
replay;
|
|
19723
|
-
pendingWrites = new boundedWriteQueue();
|
|
19724
20035
|
loop;
|
|
19725
20036
|
originalAnimationFrameObject;
|
|
19726
|
-
samplesCount = 0;
|
|
19727
|
-
framesCount = 0;
|
|
19728
20037
|
recordingAudioSampleRate;
|
|
19729
|
-
recordingStats;
|
|
19730
|
-
confirmedFrameNumber = 0;
|
|
19731
|
-
confirmedSampleNumber = 0;
|
|
19732
|
-
recorderElement;
|
|
19733
|
-
userMedia;
|
|
19734
|
-
userMediaTimeout;
|
|
19735
|
-
retryTimeout;
|
|
19736
|
-
reconnectTimeout;
|
|
19737
|
-
connectionTimeout;
|
|
19738
|
-
backpressureTimeout;
|
|
19739
|
-
stopTimeout;
|
|
19740
20038
|
frameProgress;
|
|
19741
20039
|
sampleProgress;
|
|
19742
20040
|
canvas;
|
|
19743
|
-
recordingDimensions;
|
|
19744
20041
|
ctx;
|
|
19745
|
-
userMediaLoaded;
|
|
19746
|
-
userMediaLoading = false;
|
|
19747
|
-
mediaRequestId = 0;
|
|
19748
|
-
cameraRecovery;
|
|
19749
|
-
resolveCameraRecovery;
|
|
19750
|
-
resumeAfterCameraRecovery = false;
|
|
19751
|
-
submitting = false;
|
|
19752
|
-
unloaded;
|
|
19753
20042
|
stopTime;
|
|
19754
|
-
stream;
|
|
19755
|
-
streamBackpressured = false;
|
|
19756
|
-
resumeAfterDrain = false;
|
|
19757
|
-
mediaTransmissionStarted = false;
|
|
19758
|
-
recordingActive = false;
|
|
19759
|
-
recordingTransportFailed = false;
|
|
19760
|
-
connecting = false;
|
|
19761
|
-
connected = false;
|
|
19762
|
-
connectionFailed = false;
|
|
19763
|
-
reconnecting = false;
|
|
19764
|
-
reconnectAttempts = 0;
|
|
19765
|
-
blocking = false;
|
|
19766
|
-
built = false;
|
|
19767
|
-
key;
|
|
19768
20043
|
waitingTime;
|
|
19769
|
-
connectingStartedAt;
|
|
19770
|
-
lastCloseEvent;
|
|
19771
|
-
lastSocketError;
|
|
19772
|
-
pingInterval;
|
|
19773
20044
|
frame;
|
|
19774
20045
|
recordingBuffer;
|
|
19775
|
-
facingMode;
|
|
19776
20046
|
constructor(visuals, replay, options){
|
|
19777
|
-
super(
|
|
19778
|
-
this.visuals = visuals;
|
|
20047
|
+
super(visuals, options);
|
|
19779
20048
|
this.replay = replay;
|
|
19780
|
-
this.facingMode = options.video.facingMode;
|
|
19781
|
-
}
|
|
19782
|
-
hasExceededWebSocketSendBuffer(buffer, opts) {
|
|
19783
|
-
return buffer.length > 0 && !this.pendingWrites.enqueue(buffer, opts);
|
|
19784
20049
|
}
|
|
19785
|
-
|
|
19786
|
-
if (this.
|
|
19787
|
-
|
|
19788
|
-
|
|
19789
|
-
|
|
19790
|
-
|
|
19791
|
-
|
|
19792
|
-
|
|
19793
|
-
this.handleWriteFailure(err);
|
|
19794
|
-
} else if (this.streamBackpressured) {
|
|
19795
|
-
if (this.hasExceededWebSocketSendBuffer(buffer, opts)) this.failMediaTransmission(error_createError({
|
|
19796
|
-
message: "WebSocket send buffer limit exceeded",
|
|
19797
|
-
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.",
|
|
19798
|
-
options: this.options
|
|
19799
|
-
}));
|
|
19800
|
-
} else this.writeToStream(this.stream, buffer, opts);
|
|
20050
|
+
onAudioSample(audioSample) {
|
|
20051
|
+
if (this.recordingTransportFailed || this.cameraRecovery) return;
|
|
20052
|
+
const sampleRate = this.userMedia?.getAudioSampleRate();
|
|
20053
|
+
if (sampleRate && sampleRate > 0) this.recordingAudioSampleRate ??= sampleRate;
|
|
20054
|
+
this.samplesCount++;
|
|
20055
|
+
this.mediaTransmissionStarted = true;
|
|
20056
|
+
const audioBuffer = audioSample.toBuffer();
|
|
20057
|
+
this.writeStream(audioBuffer);
|
|
19801
20058
|
}
|
|
19802
|
-
|
|
19803
|
-
const onFlushedCallback = opts?.onFlushedCallback;
|
|
20059
|
+
onUserMediaReady(params) {
|
|
19804
20060
|
try {
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
const err = error_createError({
|
|
19811
|
-
message: "Failed to write stream buffer",
|
|
19812
|
-
explanation: `stream.write() failed because of ${string_pretty(exc)}`,
|
|
19813
|
-
options: this.options,
|
|
19814
|
-
exc
|
|
19815
|
-
});
|
|
19816
|
-
this.emit("ERROR", {
|
|
19817
|
-
err
|
|
19818
|
-
});
|
|
19819
|
-
}
|
|
19820
|
-
});
|
|
19821
|
-
if (!canContinue) {
|
|
19822
|
-
this.streamBackpressured = true;
|
|
19823
|
-
this.startBackpressureTimeout();
|
|
19824
|
-
this.pauseForBackpressure();
|
|
19825
|
-
return false;
|
|
20061
|
+
this.options.logger.debug(`Recorder: onUserMediaReady(${params ? string_pretty(params) : ""})`);
|
|
20062
|
+
const switchingFacingMode = params?.switchingFacingMode;
|
|
20063
|
+
if (params?.recovering) {
|
|
20064
|
+
if (!this.userMedia?.hasLiveStream()) return void this.failCameraRecovery(new Error("The restored camera has no live tracks."));
|
|
20065
|
+
if (void 0 !== this.recordingAudioSampleRate && this.userMedia.getAudioSampleRate() !== this.recordingAudioSampleRate) return void this.failCameraRecovery(new Error("The audio sample rate changed during recovery."));
|
|
19826
20066
|
}
|
|
19827
|
-
|
|
20067
|
+
this.userMediaLoading = this.blocking = this.unloaded = this.submitting = false;
|
|
20068
|
+
this.userMediaLoaded = true;
|
|
20069
|
+
this.clearUserMediaTimeout();
|
|
20070
|
+
if (!switchingFacingMode && !params?.recovering) this.loop = this.createLoop();
|
|
20071
|
+
this.show();
|
|
20072
|
+
if (params?.recordWhenReady) this.record();
|
|
20073
|
+
this.emit("USER_MEDIA_READY", {
|
|
20074
|
+
switchingFacingMode: params?.switchingFacingMode,
|
|
20075
|
+
recovering: params?.recovering,
|
|
20076
|
+
paused: this.isPaused(),
|
|
20077
|
+
recordWhenReady: params?.recordWhenReady
|
|
20078
|
+
});
|
|
20079
|
+
if (params?.recovering) this.finishCameraRecovery(true);
|
|
19828
20080
|
} catch (exc) {
|
|
19829
|
-
|
|
19830
|
-
|
|
19831
|
-
explanation: `stream.write() failed because of ${string_pretty(exc)}`,
|
|
19832
|
-
options: this.options,
|
|
20081
|
+
if (params?.recovering) this.failCameraRecovery(exc);
|
|
20082
|
+
else this.emit("ERROR", {
|
|
19833
20083
|
exc
|
|
19834
20084
|
});
|
|
19835
|
-
this.handleWriteFailure(err);
|
|
19836
|
-
return false;
|
|
19837
20085
|
}
|
|
19838
20086
|
}
|
|
19839
|
-
|
|
19840
|
-
|
|
19841
|
-
|
|
19842
|
-
|
|
19843
|
-
|
|
19844
|
-
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
this.
|
|
20087
|
+
calculateFrameProgress() {
|
|
20088
|
+
return calculateProgress(this.confirmedFrameNumber, this.framesCount);
|
|
20089
|
+
}
|
|
20090
|
+
calculateSampleProgress() {
|
|
20091
|
+
return calculateProgress(this.confirmedSampleNumber, this.samplesCount);
|
|
20092
|
+
}
|
|
20093
|
+
updateOverallProgress() {
|
|
20094
|
+
this.frameProgress = this.calculateFrameProgress();
|
|
20095
|
+
if (isAudioEnabled(this.options)) this.sampleProgress = this.calculateSampleProgress();
|
|
20096
|
+
this.emit("PROGRESS", {
|
|
20097
|
+
frameProgress: this.frameProgress,
|
|
20098
|
+
sampleProgress: this.sampleProgress
|
|
20099
|
+
});
|
|
20100
|
+
}
|
|
20101
|
+
updateFrameProgress(args) {
|
|
20102
|
+
this.confirmedFrameNumber = getConfirmedProgressNumber({
|
|
20103
|
+
args,
|
|
20104
|
+
kind: "frame",
|
|
20105
|
+
options: this.options
|
|
20106
|
+
});
|
|
20107
|
+
this.frameProgress = this.calculateFrameProgress();
|
|
20108
|
+
this.updateOverallProgress();
|
|
20109
|
+
}
|
|
20110
|
+
updateSampleProgress(args) {
|
|
20111
|
+
this.confirmedSampleNumber = getConfirmedProgressNumber({
|
|
20112
|
+
args,
|
|
20113
|
+
kind: "sample",
|
|
20114
|
+
options: this.options
|
|
20115
|
+
});
|
|
20116
|
+
this.sampleProgress = this.calculateSampleProgress();
|
|
20117
|
+
this.updateOverallProgress();
|
|
20118
|
+
}
|
|
20119
|
+
preview(args) {
|
|
20120
|
+
if (!args) throw error_createError({
|
|
20121
|
+
message: "Preview arguments are missing.",
|
|
20122
|
+
options: this.options
|
|
20123
|
+
});
|
|
20124
|
+
const hasAudio = this.samplesCount > 0;
|
|
20125
|
+
this.mediaTransmissionStarted = false;
|
|
20126
|
+
this.recordingActive = false;
|
|
20127
|
+
this.recordingTransportFailed = false;
|
|
20128
|
+
this.confirmedFrameNumber = this.confirmedSampleNumber = this.samplesCount = this.framesCount = 0;
|
|
20129
|
+
this.sampleProgress = this.frameProgress = void 0;
|
|
20130
|
+
this.key = args.key;
|
|
20131
|
+
if (args.mp4) this.replay.setMp4Source(`${args.mp4 + constants.WHITELIST_KEY_LABEL}/${this.options.whitelistKey}/videomail.mp4`, true);
|
|
20132
|
+
if (args.webm) this.replay.setWebMSource(`${args.webm + constants.WHITELIST_KEY_LABEL}/${this.options.whitelistKey}/videomail.webm`, true);
|
|
20133
|
+
this.hide();
|
|
20134
|
+
const widthDimension = this.getRecorderWidth(true);
|
|
20135
|
+
const heightDimension = this.getRecorderHeight(true);
|
|
20136
|
+
const duration = args.duration ?? -1;
|
|
20137
|
+
this.emit("PREVIEW", {
|
|
20138
|
+
key: this.key,
|
|
20139
|
+
width: widthDimension?.value,
|
|
20140
|
+
height: heightDimension?.value,
|
|
20141
|
+
hasAudio,
|
|
20142
|
+
duration
|
|
20143
|
+
});
|
|
20144
|
+
if (this.stopTime) this.waitingTime = Date.now() - this.stopTime;
|
|
20145
|
+
this.recordingStats ??= {};
|
|
20146
|
+
this.recordingStats.waitingTime = this.waitingTime;
|
|
20147
|
+
}
|
|
20148
|
+
cancelAnimationFrame() {
|
|
20149
|
+
this.loop?.dispose();
|
|
19849
20150
|
}
|
|
19850
|
-
|
|
19851
|
-
|
|
20151
|
+
getIntervalSum() {
|
|
20152
|
+
if (!this.loop) return;
|
|
20153
|
+
return this.loop.getElapsedTime();
|
|
19852
20154
|
}
|
|
19853
|
-
|
|
19854
|
-
|
|
20155
|
+
getAvgInterval() {
|
|
20156
|
+
const intervalSum = this.getIntervalSum() ?? 0;
|
|
20157
|
+
if (!this.hasValidInterval(intervalSum)) return;
|
|
20158
|
+
return intervalSum / this.framesCount;
|
|
19855
20159
|
}
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
this.
|
|
19859
|
-
this.
|
|
20160
|
+
getAvgFps() {
|
|
20161
|
+
const intervalSum = this.getIntervalSum() ?? 0;
|
|
20162
|
+
if (!this.hasValidInterval(intervalSum)) return;
|
|
20163
|
+
return this.framesCount / intervalSum * 1000;
|
|
19860
20164
|
}
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
this.resumeAfterDrain = false;
|
|
19864
|
-
if (this.canResumeAfterDrain()) this.loop?.start();
|
|
20165
|
+
hasValidInterval(intervalSum) {
|
|
20166
|
+
return intervalSum > 0;
|
|
19865
20167
|
}
|
|
19866
|
-
|
|
19867
|
-
return this.
|
|
20168
|
+
getRecordingStats() {
|
|
20169
|
+
return this.recordingStats;
|
|
19868
20170
|
}
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
this.emit("ERROR", {
|
|
19872
|
-
err
|
|
19873
|
-
});
|
|
20171
|
+
getAudioSampleRate() {
|
|
20172
|
+
return this.recordingAudioSampleRate ?? this.userMedia?.getAudioSampleRate();
|
|
19874
20173
|
}
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
this.
|
|
20174
|
+
stop(params) {
|
|
20175
|
+
this.cancelCameraRecovery();
|
|
20176
|
+
if (!this.userMedia) throw new Error("No user media defined, unable to stop");
|
|
20177
|
+
this.options.logger.debug(`Recorder: stop(${params ? string_pretty(params) : ""})`);
|
|
19878
20178
|
this.recordingActive = false;
|
|
19879
|
-
|
|
19880
|
-
this.
|
|
19881
|
-
|
|
19882
|
-
this.pendingWrites.clear();
|
|
19883
|
-
this.connected = false;
|
|
19884
|
-
this.connecting = false;
|
|
19885
|
-
this.reconnecting = false;
|
|
19886
|
-
this.clearReconnectTimeout();
|
|
19887
|
-
this.clearBackpressureTimeout();
|
|
19888
|
-
this.discardSocket();
|
|
19889
|
-
this.reset();
|
|
19890
|
-
this.framesCount = this.samplesCount = 0;
|
|
19891
|
-
this.confirmedFrameNumber = this.confirmedSampleNumber = 0;
|
|
19892
|
-
delete this.recordingStats;
|
|
19893
|
-
this.recordingDimensions = void 0;
|
|
19894
|
-
this.emit("ERROR", {
|
|
19895
|
-
err
|
|
20179
|
+
const limitReached = params?.limitReached;
|
|
20180
|
+
this.emit("STOPPING", {
|
|
20181
|
+
limitReached
|
|
19896
20182
|
});
|
|
19897
|
-
|
|
19898
|
-
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
if (
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
|
|
19906
|
-
|
|
20183
|
+
this.loop?.complete?.();
|
|
20184
|
+
this.stopTimeout = window.setTimeout(()=>{
|
|
20185
|
+
this.stopTimeout = void 0;
|
|
20186
|
+
this.stopTime = Date.now();
|
|
20187
|
+
const videoType = this.replay.getVideoType();
|
|
20188
|
+
if (!videoType) throw new Error("Unable to video record when no video type is defined.");
|
|
20189
|
+
this.recordingStats = {
|
|
20190
|
+
avgFps: this.getAvgFps(),
|
|
20191
|
+
wantedFps: this.options.video.fps,
|
|
20192
|
+
avgInterval: this.getAvgInterval(),
|
|
20193
|
+
wantedInterval: 1e3 / this.options.video.fps,
|
|
20194
|
+
intervalSum: this.getIntervalSum(),
|
|
20195
|
+
framesCount: this.framesCount,
|
|
20196
|
+
videoType
|
|
20197
|
+
};
|
|
20198
|
+
if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
|
|
20199
|
+
if (isAudioEnabled(this.options) && this.userMedia) {
|
|
20200
|
+
this.recordingStats.samplesCount = this.samplesCount;
|
|
20201
|
+
this.recordingStats.sampleRate = this.recordingAudioSampleRate ?? this.userMedia.getAudioSampleRate();
|
|
20202
|
+
}
|
|
20203
|
+
this.writeCommand("stop", this.recordingStats, ()=>{
|
|
20204
|
+
this.emit("STOPPED", {
|
|
20205
|
+
recordingStats: this.recordingStats
|
|
20206
|
+
});
|
|
19907
20207
|
});
|
|
19908
|
-
this.
|
|
19909
|
-
},
|
|
19910
|
-
}
|
|
19911
|
-
shouldStartBackpressureTimeout() {
|
|
19912
|
-
return void 0 === this.backpressureTimeout && this.mediaTransmissionStarted;
|
|
20208
|
+
this.reset();
|
|
20209
|
+
}, 60);
|
|
19913
20210
|
}
|
|
19914
|
-
|
|
19915
|
-
|
|
20211
|
+
back(cb) {
|
|
20212
|
+
this.emit("GOING_BACK");
|
|
20213
|
+
this.key = void 0;
|
|
20214
|
+
this.recordingDimensions = void 0;
|
|
20215
|
+
this.unloaded = false;
|
|
20216
|
+
this.show();
|
|
20217
|
+
this.writeCommand("back", void 0, cb);
|
|
19916
20218
|
}
|
|
19917
|
-
|
|
19918
|
-
|
|
19919
|
-
|
|
19920
|
-
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
unconfirmedFrames,
|
|
19926
|
-
unconfirmedSamples
|
|
19927
|
-
}
|
|
19928
|
-
});
|
|
19929
|
-
this.failMediaTransmission(err);
|
|
20219
|
+
reInitializeAudio() {
|
|
20220
|
+
this.options.logger.debug("Recorder: reInitializeAudio()");
|
|
20221
|
+
this.clearUserMediaTimeout();
|
|
20222
|
+
this.userMediaLoading = false;
|
|
20223
|
+
this.userMedia?.stop();
|
|
20224
|
+
this.recordingDimensions = void 0;
|
|
20225
|
+
this.userMediaLoaded = this.key = this.canvas = this.ctx = void 0;
|
|
20226
|
+
this.loadUserMedia();
|
|
19930
20227
|
}
|
|
19931
|
-
|
|
19932
|
-
this.
|
|
19933
|
-
this.
|
|
20228
|
+
reset() {
|
|
20229
|
+
this.cancelCameraRecovery();
|
|
20230
|
+
if (!this.unloaded) {
|
|
20231
|
+
this.options.logger.debug("Recorder: reset()");
|
|
20232
|
+
this.emit("RESETTING");
|
|
20233
|
+
this.cancelAnimationFrame();
|
|
20234
|
+
this.userMedia?.stop();
|
|
20235
|
+
this.replay.reset();
|
|
20236
|
+
this.userMediaLoaded = this.key = this.canvas = this.ctx = this.recordingBuffer = void 0;
|
|
20237
|
+
}
|
|
19934
20238
|
}
|
|
19935
|
-
|
|
19936
|
-
return Boolean(this.
|
|
20239
|
+
validate() {
|
|
20240
|
+
return Boolean(this.key) && void 0 === this.canvas;
|
|
19937
20241
|
}
|
|
19938
|
-
|
|
19939
|
-
|
|
20242
|
+
pause(params) {
|
|
20243
|
+
this.cancelCameraRecovery();
|
|
20244
|
+
this.resumeAfterDrain = false;
|
|
20245
|
+
const loop = this.loop;
|
|
20246
|
+
if (!loop) throw new Error("No recording loop defined");
|
|
20247
|
+
if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
|
|
20248
|
+
else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
|
|
20249
|
+
this.userMedia?.pause();
|
|
20250
|
+
loop.stop();
|
|
20251
|
+
this.emit("PAUSED");
|
|
20252
|
+
this.sendPings();
|
|
19940
20253
|
}
|
|
19941
|
-
|
|
19942
|
-
|
|
19943
|
-
|
|
19944
|
-
|
|
19945
|
-
|
|
19946
|
-
|
|
19947
|
-
|
|
19948
|
-
|
|
19949
|
-
|
|
19950
|
-
this.connected = false;
|
|
20254
|
+
resume() {
|
|
20255
|
+
if (this.cameraRecovery || !this.userMedia?.hasLiveStream()) {
|
|
20256
|
+
this.recoverCamera();
|
|
20257
|
+
this.resumeAfterCameraRecovery = Boolean(this.cameraRecovery);
|
|
20258
|
+
return;
|
|
20259
|
+
}
|
|
20260
|
+
const loop = this.loop;
|
|
20261
|
+
if (!loop) throw new Error("No recording loop defined");
|
|
20262
|
+
this.options.logger.debug(`Recorder: resume() with frame ${this.framesCount}`);
|
|
19951
20263
|
this.stopPings();
|
|
19952
|
-
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
-
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
20264
|
+
this.emit("RESUMING");
|
|
20265
|
+
this.userMedia.resume();
|
|
20266
|
+
if (this.streamBackpressured) this.resumeAfterDrain = true;
|
|
20267
|
+
else loop.start();
|
|
20268
|
+
}
|
|
20269
|
+
onFlushed(opts) {
|
|
20270
|
+
const frameNumber = opts.frameNumber;
|
|
20271
|
+
if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
|
|
20272
|
+
}
|
|
20273
|
+
draw(_deltaTime, elapsedTime) {
|
|
20274
|
+
if (this.recordingTransportFailed) return;
|
|
20275
|
+
if (!this.userMedia) throw new Error("No user media defined, unable to draw on canvas");
|
|
20276
|
+
try {
|
|
20277
|
+
if (!this.isPaused() && this.stream && this.ctx) {
|
|
20278
|
+
if (0 === this.framesCount) this.emit("SENDING_FIRST_FRAME");
|
|
20279
|
+
this.framesCount++;
|
|
20280
|
+
const imageSource = this.userMedia.getRawVisuals();
|
|
20281
|
+
if (this.canvas && imageSource) this.ctx.drawImage(imageSource, 0, 0, this.canvas.width, this.canvas.height);
|
|
20282
|
+
else throw new Error("Unable to draw an image without a defined canvas");
|
|
20283
|
+
this.recordingBuffer = this.frame?.toBuffer();
|
|
20284
|
+
const recordingBufferLength = this.recordingBuffer?.length;
|
|
20285
|
+
if (recordingBufferLength) {
|
|
20286
|
+
if (this.recordingBuffer) {
|
|
20287
|
+
const frameControlBuffer = capture_Buffer.from(JSON.stringify({
|
|
20288
|
+
frameNumber: this.framesCount
|
|
20289
|
+
}));
|
|
20290
|
+
const frameBuffer = capture_Buffer.concat([
|
|
20291
|
+
this.recordingBuffer,
|
|
20292
|
+
frameControlBuffer
|
|
20293
|
+
]);
|
|
20294
|
+
this.mediaTransmissionStarted = true;
|
|
20295
|
+
this.writeStream(frameBuffer, {
|
|
20296
|
+
frameNumber: this.framesCount,
|
|
20297
|
+
onFlushedCallback: this.onFlushed.bind(this)
|
|
20298
|
+
});
|
|
20299
|
+
this.visuals.checkTimer(elapsedTime);
|
|
20300
|
+
}
|
|
20301
|
+
} else throw error_createError({
|
|
20302
|
+
message: "Failed to extract webcam data.",
|
|
20303
|
+
options: this.options
|
|
20304
|
+
});
|
|
20305
|
+
}
|
|
20306
|
+
} catch (exc) {
|
|
20307
|
+
this.emit("ERROR", {
|
|
20308
|
+
exc
|
|
20309
|
+
});
|
|
20310
|
+
}
|
|
20311
|
+
}
|
|
20312
|
+
createLoop() {
|
|
20313
|
+
const newLoop = animitter_default()({
|
|
20314
|
+
fps: this.options.video.fps
|
|
20315
|
+
}, this.draw.bind(this));
|
|
20316
|
+
this.originalAnimationFrameObject = newLoop.getRequestAnimationFrameObject();
|
|
20317
|
+
return newLoop;
|
|
20318
|
+
}
|
|
20319
|
+
record() {
|
|
20320
|
+
if (this.unloaded || this.cameraRecovery) return;
|
|
20321
|
+
if (!this.connected) {
|
|
20322
|
+
this.options.logger.debug("Recorder: reconnecting before recording …");
|
|
20323
|
+
this.initSocket(()=>{
|
|
20324
|
+
this.once("SERVER_READY", ()=>{
|
|
20325
|
+
if (this.unloaded) return;
|
|
20326
|
+
if (this.userMediaLoaded || this.options.loadUserMediaOnRecord) this.record();
|
|
20327
|
+
else this.once("USER_MEDIA_READY", ()=>{
|
|
20328
|
+
if (!this.unloaded) this.record();
|
|
20329
|
+
});
|
|
20330
|
+
});
|
|
19964
20331
|
});
|
|
19965
|
-
}, 0);
|
|
19966
|
-
}
|
|
19967
|
-
shouldIgnoreStreamClose() {
|
|
19968
|
-
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
|
|
19969
|
-
}
|
|
19970
|
-
handleStreamError(url2Connect, err) {
|
|
19971
|
-
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
19972
|
-
else this.lastSocketError = serializeError(err);
|
|
19973
|
-
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
19974
|
-
error: serializeError(err)
|
|
19975
|
-
});
|
|
19976
|
-
this.options.logger.debug(debugLine);
|
|
19977
|
-
if (this.validate()) {
|
|
19978
|
-
this.connected = false;
|
|
19979
|
-
this.stopPings();
|
|
19980
|
-
this.discardSocket();
|
|
19981
20332
|
return;
|
|
19982
20333
|
}
|
|
19983
|
-
if (!this.
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19988
|
-
|
|
20334
|
+
if (!this.userMediaLoaded) {
|
|
20335
|
+
if (this.options.loadUserMediaOnRecord) this.loadUserMedia({
|
|
20336
|
+
recordWhenReady: true
|
|
20337
|
+
});
|
|
20338
|
+
else {
|
|
20339
|
+
const err = error_createError({
|
|
20340
|
+
message: "Load and enable your camera first",
|
|
20341
|
+
options: this.options
|
|
20342
|
+
});
|
|
20343
|
+
this.emit("ERROR", {
|
|
20344
|
+
err
|
|
20345
|
+
});
|
|
20346
|
+
}
|
|
20347
|
+
return;
|
|
20348
|
+
}
|
|
20349
|
+
const loop = this.loop;
|
|
20350
|
+
if (!loop) throw new Error("No recording loop defined");
|
|
20351
|
+
this.recordingDimensions = void 0;
|
|
20352
|
+
try {
|
|
20353
|
+
if (!this.userMedia) throw new Error("No user media defined, unable to create canvas");
|
|
20354
|
+
this.canvas = this.userMedia.createCanvas();
|
|
20355
|
+
} catch (exc) {
|
|
20356
|
+
const err = error_createError({
|
|
20357
|
+
exc,
|
|
20358
|
+
options: this.options
|
|
20359
|
+
});
|
|
20360
|
+
this.emit("ERROR", {
|
|
20361
|
+
err
|
|
20362
|
+
});
|
|
20363
|
+
return;
|
|
20364
|
+
}
|
|
20365
|
+
this.ctx = this.canvas.getContext("2d");
|
|
20366
|
+
if (!this.canvas.width) {
|
|
20367
|
+
const err = error_createError({
|
|
20368
|
+
message: "Canvas has an invalid width.",
|
|
20369
|
+
options: this.options
|
|
20370
|
+
});
|
|
20371
|
+
this.emit("ERROR", {
|
|
20372
|
+
err
|
|
20373
|
+
});
|
|
20374
|
+
return;
|
|
20375
|
+
}
|
|
20376
|
+
if (!this.canvas.height) {
|
|
20377
|
+
const err = error_createError({
|
|
20378
|
+
message: "Canvas has an invalid height.",
|
|
20379
|
+
options: this.options
|
|
20380
|
+
});
|
|
20381
|
+
this.emit("ERROR", {
|
|
20382
|
+
err
|
|
20383
|
+
});
|
|
20384
|
+
return;
|
|
20385
|
+
}
|
|
20386
|
+
this.recordingDimensions = {
|
|
20387
|
+
width: this.canvas.width,
|
|
20388
|
+
height: this.canvas.height
|
|
20389
|
+
};
|
|
20390
|
+
this.frame = new canvas_to_buffer_modern_r(this.canvas, this.options.image.types, this.options.image.quality);
|
|
20391
|
+
this.options.logger.debug("Recorder: record()");
|
|
20392
|
+
this.recordingActive = true;
|
|
20393
|
+
this.recordingTransportFailed = false;
|
|
20394
|
+
this.mediaTransmissionStarted = false;
|
|
20395
|
+
this.recordingAudioSampleRate = void 0;
|
|
20396
|
+
this.userMedia.record();
|
|
20397
|
+
this.emit("RECORDING", {
|
|
20398
|
+
framesCount: this.framesCount
|
|
19989
20399
|
});
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
19995
|
-
options: this.options,
|
|
19996
|
-
exc: streamError
|
|
19997
|
-
})
|
|
20400
|
+
loop.on("update", (_deltaTime, elapsedTime)=>{
|
|
20401
|
+
let avgFPS;
|
|
20402
|
+
avgFPS = 0 !== elapsedTime ? Math.round(this.framesCount / elapsedTime * 1000) : void 0;
|
|
20403
|
+
this.options.logger.debug(`Recorder updates avgFps = ${avgFPS} at frame ${this.framesCount}`);
|
|
19998
20404
|
});
|
|
20405
|
+
loop.start();
|
|
19999
20406
|
}
|
|
20000
|
-
|
|
20001
|
-
this.
|
|
20002
|
-
|
|
20003
|
-
this.
|
|
20004
|
-
this.
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
stopPings() {
|
|
20008
|
-
if (void 0 === this.pingInterval) return;
|
|
20009
|
-
this.options.logger.debug("Recorder: stopping pings...");
|
|
20010
|
-
window.clearInterval(this.pingInterval);
|
|
20011
|
-
this.pingInterval = void 0;
|
|
20407
|
+
setAnimationFrameObject(newObj) {
|
|
20408
|
+
if (this.loop) {
|
|
20409
|
+
const isRecording = this.isRecording();
|
|
20410
|
+
this.loop.stop();
|
|
20411
|
+
this.loop.setRequestAnimationFrameObject(newObj);
|
|
20412
|
+
if (isRecording) this.loop.start();
|
|
20413
|
+
}
|
|
20012
20414
|
}
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
if (sampleRate && sampleRate > 0) this.recordingAudioSampleRate ??= sampleRate;
|
|
20017
|
-
this.samplesCount++;
|
|
20018
|
-
this.mediaTransmissionStarted = true;
|
|
20019
|
-
const audioBuffer = audioSample.toBuffer();
|
|
20020
|
-
this.writeStream(audioBuffer);
|
|
20415
|
+
restoreAnimationFrameObject() {
|
|
20416
|
+
this.options.logger.debug("Recorder: restoreAnimationFrameObject()");
|
|
20417
|
+
if (this.originalAnimationFrameObject) this.setAnimationFrameObject(this.originalAnimationFrameObject);
|
|
20021
20418
|
}
|
|
20022
|
-
|
|
20023
|
-
|
|
20419
|
+
loopWithTimeouts() {
|
|
20420
|
+
this.options.logger.debug("Recorder: loopWithTimeouts()");
|
|
20421
|
+
const wantedInterval = 1e3 / this.options.video.fps;
|
|
20422
|
+
let processingTime = 0;
|
|
20423
|
+
const raf = (fn)=>window.setTimeout(()=>{
|
|
20424
|
+
const start = Date.now();
|
|
20425
|
+
fn();
|
|
20426
|
+
processingTime = Date.now() - start;
|
|
20427
|
+
}, wantedInterval - processingTime);
|
|
20428
|
+
const cancel = (id)=>{
|
|
20429
|
+
window.clearTimeout(id);
|
|
20430
|
+
};
|
|
20431
|
+
this.setAnimationFrameObject({
|
|
20432
|
+
requestAnimationFrame: raf,
|
|
20433
|
+
cancelAnimationFrame: cancel
|
|
20434
|
+
});
|
|
20024
20435
|
}
|
|
20025
|
-
|
|
20026
|
-
|
|
20027
|
-
this.options.logger.debug(`Recorder: onUserMediaReady(${params ? string_pretty(params) : ""})`);
|
|
20028
|
-
const switchingFacingMode = params?.switchingFacingMode;
|
|
20029
|
-
if (params?.recovering) {
|
|
20030
|
-
if (!this.userMedia?.hasLiveStream()) return void this.failCameraRecovery(new Error("The restored camera has no live tracks."));
|
|
20031
|
-
if (void 0 !== this.recordingAudioSampleRate && this.userMedia.getAudioSampleRate() !== this.recordingAudioSampleRate) return void this.failCameraRecovery(new Error("The audio sample rate changed during recovery."));
|
|
20032
|
-
}
|
|
20033
|
-
this.userMediaLoading = this.blocking = this.unloaded = this.submitting = false;
|
|
20034
|
-
this.userMediaLoaded = true;
|
|
20035
|
-
this.clearUserMediaTimeout();
|
|
20036
|
-
if (!switchingFacingMode && !params?.recovering) this.loop = this.createLoop();
|
|
20037
|
-
this.show();
|
|
20038
|
-
if (params?.recordWhenReady) this.record();
|
|
20039
|
-
this.emit("USER_MEDIA_READY", {
|
|
20040
|
-
switchingFacingMode: params?.switchingFacingMode,
|
|
20041
|
-
recovering: params?.recovering,
|
|
20042
|
-
paused: this.isPaused(),
|
|
20043
|
-
recordWhenReady: params?.recordWhenReady
|
|
20044
|
-
});
|
|
20045
|
-
if (params?.recovering) this.finishCameraRecovery(true);
|
|
20046
|
-
} catch (exc) {
|
|
20047
|
-
if (params?.recovering) this.failCameraRecovery(exc);
|
|
20048
|
-
else this.emit("ERROR", {
|
|
20049
|
-
exc
|
|
20050
|
-
});
|
|
20051
|
-
}
|
|
20436
|
+
isPaused() {
|
|
20437
|
+
return this.userMedia?.isPaused() && !this.loop?.isRunning();
|
|
20052
20438
|
}
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
this.options.logger.debug("Recorder: clearRetryTimeout()");
|
|
20056
|
-
window.clearTimeout(this.retryTimeout);
|
|
20057
|
-
this.retryTimeout = void 0;
|
|
20439
|
+
isUserMediaPaused() {
|
|
20440
|
+
return this.userMedia?.isPaused() ?? false;
|
|
20058
20441
|
}
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
this.options.logger.debug("Recorder: clearConnectionTimeout()");
|
|
20062
|
-
window.clearTimeout(this.connectionTimeout);
|
|
20063
|
-
this.connectionTimeout = void 0;
|
|
20442
|
+
isCaptureLoopRunning() {
|
|
20443
|
+
return this.loop?.isRunning() ?? false;
|
|
20064
20444
|
}
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
window.clearTimeout(this.backpressureTimeout);
|
|
20068
|
-
this.backpressureTimeout = void 0;
|
|
20445
|
+
stopCaptureLoop() {
|
|
20446
|
+
this.loop?.stop();
|
|
20069
20447
|
}
|
|
20070
|
-
|
|
20071
|
-
|
|
20072
|
-
this.options.logger.debug("Recorder: clearReconnectTimeout()");
|
|
20073
|
-
window.clearTimeout(this.reconnectTimeout);
|
|
20074
|
-
this.reconnectTimeout = void 0;
|
|
20448
|
+
startCaptureLoop() {
|
|
20449
|
+
this.loop?.start();
|
|
20075
20450
|
}
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
this.failConnection(params);
|
|
20451
|
+
isRecording() {
|
|
20452
|
+
return Boolean(this.loop?.isRunning() && !this.isPaused() && !this.isNotifying() && this.stream && !this.stream.destroyed);
|
|
20079
20453
|
}
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
this.reconnectAttempts++;
|
|
20085
|
-
this.connecting = false;
|
|
20086
|
-
this.clearConnectionTimeout();
|
|
20087
|
-
this.discardSocket();
|
|
20088
|
-
this.options.logger.debug(`Recorder: retrying WebSocket connection in ${delayMs}ms (cause ${params.cause}).`);
|
|
20089
|
-
this.reconnectTimeout = window.setTimeout(()=>{
|
|
20090
|
-
this.reconnectTimeout = void 0;
|
|
20091
|
-
if (this.shouldContinueReconnect()) this.initSocket();
|
|
20092
|
-
}, delayMs);
|
|
20093
|
-
return true;
|
|
20454
|
+
getRecordingDimensions() {
|
|
20455
|
+
return this.recordingDimensions && {
|
|
20456
|
+
...this.recordingDimensions
|
|
20457
|
+
};
|
|
20094
20458
|
}
|
|
20095
|
-
|
|
20096
|
-
|
|
20459
|
+
}
|
|
20460
|
+
const capture = RecorderCapture;
|
|
20461
|
+
function figureMinHeight(height, options) {
|
|
20462
|
+
let minHeight;
|
|
20463
|
+
if (options.video.height) {
|
|
20464
|
+
minHeight = height ? Math.min(options.video.height, height) : options.video.height;
|
|
20465
|
+
if (minHeight < 1) throw error_createError({
|
|
20466
|
+
message: `Got a min height less than 1 (${minHeight})!`,
|
|
20467
|
+
options
|
|
20468
|
+
});
|
|
20469
|
+
} else minHeight = height;
|
|
20470
|
+
return minHeight;
|
|
20471
|
+
}
|
|
20472
|
+
const dimensions_figureMinHeight = figureMinHeight;
|
|
20473
|
+
function getRatio(options, videoHeight, videoWidth) {
|
|
20474
|
+
let ratio = 1;
|
|
20475
|
+
const hasVideoDimensions = videoHeight && videoWidth;
|
|
20476
|
+
const desiredHeight = options.video.height;
|
|
20477
|
+
const desiredWidth = options.video.width;
|
|
20478
|
+
const hasDesiredDimensions = desiredHeight && desiredWidth;
|
|
20479
|
+
if (hasDesiredDimensions) ratio = hasVideoDimensions ? videoHeight < desiredHeight || videoWidth < desiredWidth ? videoHeight / videoWidth : desiredHeight / desiredWidth : desiredHeight / desiredWidth;
|
|
20480
|
+
else if (hasVideoDimensions) ratio = videoHeight / videoWidth;
|
|
20481
|
+
return ratio;
|
|
20482
|
+
}
|
|
20483
|
+
const dimensions_getRatio = getRatio;
|
|
20484
|
+
function calculateHeight(responsive, videoWidth, options, ratio, element) {
|
|
20485
|
+
const dimension = {
|
|
20486
|
+
unit: "px"
|
|
20487
|
+
};
|
|
20488
|
+
let width = videoWidth;
|
|
20489
|
+
if (responsive && element) {
|
|
20490
|
+
const limitedDimension = dimensions_limitWidth(element, options, width);
|
|
20491
|
+
width = limitedDimension.value;
|
|
20097
20492
|
}
|
|
20098
|
-
|
|
20099
|
-
|
|
20493
|
+
const chosenRatio = ratio ?? dimensions_getRatio(options, void 0, videoWidth);
|
|
20494
|
+
const height = width ? Math.round(width * chosenRatio) : void 0;
|
|
20495
|
+
if (height && Number.isInteger(height) && height < 1) throw error_createError({
|
|
20496
|
+
message: "Just calculated a height less than 1 which is wrong.",
|
|
20497
|
+
options
|
|
20498
|
+
});
|
|
20499
|
+
const minHeight = dimensions_figureMinHeight(height, options);
|
|
20500
|
+
dimension.value = minHeight;
|
|
20501
|
+
return dimension;
|
|
20502
|
+
}
|
|
20503
|
+
const dimensions_calculateHeight = calculateHeight;
|
|
20504
|
+
function calculateWidth(responsive, options, videoHeight, ratio) {
|
|
20505
|
+
const dimension = {
|
|
20506
|
+
unit: "px"
|
|
20507
|
+
};
|
|
20508
|
+
let height = dimensions_figureMinHeight(videoHeight, options);
|
|
20509
|
+
if (responsive) {
|
|
20510
|
+
const limitedDimension = dimensions_limitHeight(height, options, "calculateWidth");
|
|
20511
|
+
height = limitedDimension.value;
|
|
20100
20512
|
}
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20513
|
+
if (!height || height < 1) throw error_createError({
|
|
20514
|
+
message: `Height ${height} cannot be smaller than 1 when calculating width.`,
|
|
20515
|
+
options
|
|
20516
|
+
});
|
|
20517
|
+
const chosenRatio = ratio ?? dimensions_getRatio(options, videoHeight);
|
|
20518
|
+
const calculatedWidth = Math.round(height / chosenRatio);
|
|
20519
|
+
if (calculatedWidth < 1) throw error_createError({
|
|
20520
|
+
message: "Calculated width cannot be smaller than 1!",
|
|
20521
|
+
options
|
|
20522
|
+
});
|
|
20523
|
+
dimension.value = calculatedWidth;
|
|
20524
|
+
return dimension;
|
|
20525
|
+
}
|
|
20526
|
+
const dimensions_calculateWidth = calculateWidth;
|
|
20527
|
+
class RecorderDimensions {
|
|
20528
|
+
context;
|
|
20529
|
+
constructor(context){
|
|
20530
|
+
this.context = context;
|
|
20110
20531
|
}
|
|
20111
|
-
|
|
20112
|
-
|
|
20532
|
+
correctDimensions() {
|
|
20533
|
+
const recorderElement = this.context.getRecorderElement();
|
|
20534
|
+
if (!recorderElement) return;
|
|
20535
|
+
const widthDimension = dimensions_useFullWidth(this.context.options.video.mobileBreakPoint);
|
|
20536
|
+
if (this.context.options.video.stretch || widthDimension) {
|
|
20537
|
+
recorderElement.style.width = "100%";
|
|
20538
|
+
recorderElement.style.removeProperty("height");
|
|
20539
|
+
} else {
|
|
20540
|
+
if (this.context.options.video.width) {
|
|
20541
|
+
const recorderWidth = this.getRecorderWidth(true);
|
|
20542
|
+
if (recorderWidth?.value) recorderElement.width = recorderWidth.value;
|
|
20543
|
+
else recorderElement.style.removeProperty("width");
|
|
20544
|
+
}
|
|
20545
|
+
if (this.context.options.video.height) {
|
|
20546
|
+
const recorderHeight = this.getRecorderHeight(true);
|
|
20547
|
+
if (recorderHeight?.value) recorderElement.height = recorderHeight.value;
|
|
20548
|
+
else recorderElement.style.removeProperty("height");
|
|
20549
|
+
}
|
|
20550
|
+
}
|
|
20113
20551
|
}
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
this.
|
|
20118
|
-
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
this.clearConnectionTimeout();
|
|
20122
|
-
const { url2Connect, cause } = params;
|
|
20123
|
-
const online = this.isOnline();
|
|
20124
|
-
const elapsedMs = this.connectingStartedAt ? Date.now() - this.connectingStartedAt : void 0;
|
|
20125
|
-
const closeEvent = this.lastCloseEvent;
|
|
20126
|
-
const socketError = this.lastSocketError;
|
|
20127
|
-
const contents = {
|
|
20128
|
-
cause,
|
|
20129
|
-
closeCode: closeEvent?.code,
|
|
20130
|
-
closeReason: closeEvent?.reason,
|
|
20131
|
-
elapsedMs,
|
|
20132
|
-
online,
|
|
20133
|
-
secureContext: globalThis.isSecureContext,
|
|
20134
|
-
socketReadyState: this.stream?.socket.readyState,
|
|
20135
|
-
timeoutMs: this.options.timeouts.connection,
|
|
20136
|
-
url: url2Connect,
|
|
20137
|
-
unloaded: this.unloaded,
|
|
20138
|
-
userMediaLoaded: this.userMediaLoaded,
|
|
20139
|
-
userMediaLoading: this.userMediaLoading,
|
|
20140
|
-
wasClean: closeEvent?.wasClean,
|
|
20141
|
-
socketError,
|
|
20142
|
-
blocking: this.blocking,
|
|
20143
|
-
visibilityState: document.visibilityState
|
|
20552
|
+
getRecorderWidth(responsive) {
|
|
20553
|
+
const userMedia = this.context.getUserMedia();
|
|
20554
|
+
if (userMedia?.hasVideoWidth()) return userMedia.getRawWidth(responsive);
|
|
20555
|
+
else if (responsive && this.context.options.video.width) return this.context.visuals.limitWidth(this.context.options.video.width);
|
|
20556
|
+
const dimension = {
|
|
20557
|
+
unit: "px",
|
|
20558
|
+
value: this.context.options.video.width
|
|
20144
20559
|
};
|
|
20145
|
-
|
|
20146
|
-
this.options.logger.debug(debugLine);
|
|
20147
|
-
let explanation;
|
|
20148
|
-
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.`;
|
|
20149
|
-
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.`;
|
|
20150
|
-
else {
|
|
20151
|
-
const closeSuffix = closeEvent ? ` (code ${closeEvent.code})` : "";
|
|
20152
|
-
explanation = `Connection to ${url2Connect} is closed${closeSuffix}. Please check your internet connection and try again. If the problem persists, contact us.`;
|
|
20153
|
-
}
|
|
20154
|
-
else explanation = "Your device appears to be offline. Please check your internet connection and try again.";
|
|
20155
|
-
this.discardSocket();
|
|
20156
|
-
const err = error_createError({
|
|
20157
|
-
message: "Unable to connect to the server",
|
|
20158
|
-
explanation,
|
|
20159
|
-
options: this.options,
|
|
20160
|
-
cause: contents,
|
|
20161
|
-
exc: socketError ? new Error("WebSocket connection error", {
|
|
20162
|
-
cause: socketError
|
|
20163
|
-
}) : void 0
|
|
20164
|
-
});
|
|
20165
|
-
this.emit("ERROR", {
|
|
20166
|
-
err
|
|
20167
|
-
});
|
|
20560
|
+
return dimension;
|
|
20168
20561
|
}
|
|
20169
|
-
|
|
20170
|
-
|
|
20562
|
+
getRecorderHeight(responsive, useBoundingClientRect) {
|
|
20563
|
+
const recorderElement = this.context.getRecorderElement();
|
|
20564
|
+
const userMedia = this.context.getUserMedia();
|
|
20565
|
+
let recorderHeight;
|
|
20566
|
+
if (recorderElement && useBoundingClientRect) {
|
|
20567
|
+
const height = recorderElement.getBoundingClientRect().height;
|
|
20568
|
+
recorderHeight = {
|
|
20569
|
+
unit: "px",
|
|
20570
|
+
value: height
|
|
20571
|
+
};
|
|
20572
|
+
} else if (userMedia) {
|
|
20573
|
+
let height = userMedia.getRawHeight(responsive);
|
|
20574
|
+
if (void 0 !== height && this.context.options.video.width) {
|
|
20575
|
+
const ratio = this.getRatio();
|
|
20576
|
+
if (void 0 !== ratio) {
|
|
20577
|
+
const idealHeight = this.context.options.video.width * ratio;
|
|
20578
|
+
height = Math.min(idealHeight, height);
|
|
20579
|
+
}
|
|
20580
|
+
}
|
|
20581
|
+
recorderHeight = {
|
|
20582
|
+
unit: "px",
|
|
20583
|
+
value: height
|
|
20584
|
+
};
|
|
20585
|
+
} else if (responsive && this.context.options.video.height) recorderHeight = this.calculateHeight(responsive);
|
|
20586
|
+
else if (this.context.options.video.height) {
|
|
20587
|
+
const height = this.context.options.video.height;
|
|
20588
|
+
recorderHeight = {
|
|
20589
|
+
unit: "px",
|
|
20590
|
+
value: height
|
|
20591
|
+
};
|
|
20592
|
+
}
|
|
20593
|
+
return recorderHeight;
|
|
20171
20594
|
}
|
|
20172
|
-
|
|
20173
|
-
|
|
20595
|
+
getRatio() {
|
|
20596
|
+
const userMedia = this.context.getUserMedia();
|
|
20597
|
+
let ratio;
|
|
20598
|
+
if (userMedia) {
|
|
20599
|
+
const userMediaVideoWidth = userMedia.getVideoWidth();
|
|
20600
|
+
const userMediaVideoHeight = userMedia.getVideoHeight();
|
|
20601
|
+
if (!userMediaVideoWidth || userMediaVideoWidth < 1) ratio = this.context.visuals.getRatio();
|
|
20602
|
+
else if (userMediaVideoHeight) ratio = userMediaVideoHeight / userMediaVideoWidth;
|
|
20603
|
+
} else ratio = dimensions_getRatio(this.context.options);
|
|
20604
|
+
return ratio;
|
|
20174
20605
|
}
|
|
20175
|
-
|
|
20176
|
-
|
|
20606
|
+
calculateWidth(responsive) {
|
|
20607
|
+
const userMedia = this.context.getUserMedia();
|
|
20608
|
+
const recorderElement = this.context.getRecorderElement();
|
|
20609
|
+
let videoHeight;
|
|
20610
|
+
if (userMedia) videoHeight = userMedia.getVideoHeight();
|
|
20611
|
+
else if (recorderElement) videoHeight = recorderElement.videoHeight || recorderElement.height;
|
|
20612
|
+
return dimensions_calculateWidth(responsive, this.context.options, videoHeight, this.getRatio());
|
|
20177
20613
|
}
|
|
20178
|
-
|
|
20179
|
-
|
|
20180
|
-
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
|
|
20184
|
-
|
|
20614
|
+
calculateHeight(responsive) {
|
|
20615
|
+
const userMedia = this.context.getUserMedia();
|
|
20616
|
+
const recorderElement = this.context.getRecorderElement();
|
|
20617
|
+
let videoDimension;
|
|
20618
|
+
if (userMedia) {
|
|
20619
|
+
const videoHeight = userMedia.getVideoHeight();
|
|
20620
|
+
videoDimension = {
|
|
20621
|
+
value: videoHeight,
|
|
20622
|
+
unit: "px"
|
|
20623
|
+
};
|
|
20624
|
+
} else if (recorderElement) {
|
|
20625
|
+
const videoHeight = recorderElement.videoHeight || recorderElement.height;
|
|
20626
|
+
videoDimension = {
|
|
20627
|
+
value: videoHeight,
|
|
20628
|
+
unit: "px"
|
|
20629
|
+
};
|
|
20630
|
+
} else videoDimension = dimensions_calculateHeight(responsive, void 0, this.context.options, this.getRatio(), recorderElement);
|
|
20631
|
+
return videoDimension;
|
|
20185
20632
|
}
|
|
20186
|
-
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20190
|
-
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
|
|
20633
|
+
}
|
|
20634
|
+
const recorder_dimensions = RecorderDimensions;
|
|
20635
|
+
function getFirstVideoTrack(localMediaStream) {
|
|
20636
|
+
const videoTracks = localMediaStream.getVideoTracks();
|
|
20637
|
+
let videoTrack;
|
|
20638
|
+
if (videoTracks[0]) videoTrack = videoTracks[0];
|
|
20639
|
+
return videoTrack;
|
|
20640
|
+
}
|
|
20641
|
+
const media_getFirstVideoTrack = getFirstVideoTrack;
|
|
20642
|
+
const MEDIA_EVENTS = [
|
|
20643
|
+
"loadstart",
|
|
20644
|
+
"suspend",
|
|
20645
|
+
"progress",
|
|
20646
|
+
"abort",
|
|
20647
|
+
"emptied",
|
|
20648
|
+
"stalled",
|
|
20649
|
+
"pause",
|
|
20650
|
+
"loadeddata",
|
|
20651
|
+
"waiting",
|
|
20652
|
+
"playing",
|
|
20653
|
+
"canplay",
|
|
20654
|
+
"canplaythrough",
|
|
20655
|
+
"seeking",
|
|
20656
|
+
"seeked",
|
|
20657
|
+
"ended",
|
|
20658
|
+
"ratechange",
|
|
20659
|
+
"durationchange",
|
|
20660
|
+
"volumechange"
|
|
20661
|
+
];
|
|
20662
|
+
const mediaEvents = MEDIA_EVENTS;
|
|
20663
|
+
function _define_property(obj, key, value) {
|
|
20664
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
20665
|
+
value: value,
|
|
20666
|
+
enumerable: true,
|
|
20667
|
+
configurable: true,
|
|
20668
|
+
writable: true
|
|
20669
|
+
});
|
|
20670
|
+
else obj[key] = value;
|
|
20671
|
+
return obj;
|
|
20672
|
+
}
|
|
20673
|
+
class AudioSample {
|
|
20674
|
+
toBuffer() {
|
|
20675
|
+
const l = this.float32Array.length;
|
|
20676
|
+
const arr = new Int16Array(l);
|
|
20677
|
+
this.float32Array.forEach((sample, i)=>{
|
|
20678
|
+
const clampedSample = Number.isFinite(sample) ? Math.max(-1, sample) : sample;
|
|
20679
|
+
arr[i] = 0x7fff * Math.min(1, clampedSample);
|
|
20195
20680
|
});
|
|
20196
|
-
|
|
20197
|
-
this.frameProgress = this.calculateFrameProgress();
|
|
20198
|
-
this.updateOverallProgress();
|
|
20681
|
+
return typedarray_to_buffer(arr);
|
|
20199
20682
|
}
|
|
20200
|
-
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
message: "The audio sample number is missing",
|
|
20204
|
-
options: this.options
|
|
20205
|
-
});
|
|
20206
|
-
} else throw error_createError({
|
|
20207
|
-
message: "Arguments are missing for updating the audio sample progress",
|
|
20208
|
-
options: this.options
|
|
20209
|
-
});
|
|
20210
|
-
this.confirmedSampleNumber = args.sample;
|
|
20211
|
-
this.sampleProgress = this.calculateSampleProgress();
|
|
20212
|
-
this.updateOverallProgress();
|
|
20683
|
+
constructor(float32Array){
|
|
20684
|
+
_define_property(this, "float32Array", void 0);
|
|
20685
|
+
this.float32Array = float32Array;
|
|
20213
20686
|
}
|
|
20214
|
-
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
this.
|
|
20233
|
-
key: this.key,
|
|
20234
|
-
width: widthDimension?.value,
|
|
20235
|
-
height: heightDimension?.value,
|
|
20236
|
-
hasAudio,
|
|
20237
|
-
duration
|
|
20238
|
-
});
|
|
20239
|
-
if (this.stopTime) this.waitingTime = Date.now() - this.stopTime;
|
|
20240
|
-
this.recordingStats ??= {};
|
|
20241
|
-
this.recordingStats.waitingTime = this.waitingTime;
|
|
20687
|
+
}
|
|
20688
|
+
const esm_src = AudioSample;
|
|
20689
|
+
const esm = esm_src;
|
|
20690
|
+
var is_power_of_two = __webpack_require__("dZ");
|
|
20691
|
+
var is_power_of_two_default = /*#__PURE__*/ __webpack_require__.n(is_power_of_two);
|
|
20692
|
+
const CHANNELS = 1;
|
|
20693
|
+
function getAudioContextClass() {
|
|
20694
|
+
return window.AudioContext;
|
|
20695
|
+
}
|
|
20696
|
+
class AudioRecorder {
|
|
20697
|
+
scriptProcessor;
|
|
20698
|
+
audioInput;
|
|
20699
|
+
volume;
|
|
20700
|
+
vcAudioContext;
|
|
20701
|
+
userMedia;
|
|
20702
|
+
options;
|
|
20703
|
+
constructor(userMedia, options){
|
|
20704
|
+
this.options = options;
|
|
20705
|
+
this.userMedia = userMedia;
|
|
20242
20706
|
}
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20249
|
-
|
|
20250
|
-
this.clearReconnectTimeout();
|
|
20251
|
-
if (util_isAutomatedUserAgent()) {
|
|
20252
|
-
this.connecting = false;
|
|
20253
|
-
this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
|
|
20254
|
-
return;
|
|
20255
|
-
}
|
|
20256
|
-
this.connecting = true;
|
|
20257
|
-
this.connectionFailed = false;
|
|
20258
|
-
this.connectingStartedAt = Date.now();
|
|
20259
|
-
this.lastCloseEvent = void 0;
|
|
20260
|
-
this.lastSocketError = void 0;
|
|
20261
|
-
this.emit("CONNECTING");
|
|
20262
|
-
let url2Connect;
|
|
20263
|
-
try {
|
|
20264
|
-
const socketUrlObj = new URL(this.options.socketUrl);
|
|
20265
|
-
socketUrlObj.searchParams.set(constants.WHITELIST_KEY_LABEL, this.options.whitelistKey);
|
|
20266
|
-
url2Connect = socketUrlObj.toString();
|
|
20267
|
-
} catch (exc) {
|
|
20268
|
-
this.connecting = this.connected = false;
|
|
20269
|
-
const err = error_createError({
|
|
20270
|
-
message: "Invalid WebSocket URL",
|
|
20271
|
-
explanation: `The configured socketUrl "${this.options.socketUrl}" is not a valid URL. Please check your videomail-client configuration.`,
|
|
20272
|
-
options: this.options,
|
|
20273
|
-
exc
|
|
20274
|
-
});
|
|
20275
|
-
this.emit("ERROR", {
|
|
20276
|
-
err
|
|
20277
|
-
});
|
|
20278
|
-
return;
|
|
20707
|
+
hasAudioContext() {
|
|
20708
|
+
return Boolean(getAudioContextClass()) && Boolean(this.getAudioContext());
|
|
20709
|
+
}
|
|
20710
|
+
getAudioContext() {
|
|
20711
|
+
if (!this.vcAudioContext) {
|
|
20712
|
+
const AudioContext = getAudioContextClass();
|
|
20713
|
+
this.vcAudioContext = new AudioContext();
|
|
20279
20714
|
}
|
|
20280
|
-
this.
|
|
20281
|
-
|
|
20282
|
-
|
|
20283
|
-
|
|
20284
|
-
|
|
20285
|
-
|
|
20286
|
-
|
|
20287
|
-
|
|
20288
|
-
|
|
20289
|
-
|
|
20290
|
-
|
|
20291
|
-
nativeSocket.addEventListener("error", (event)=>{
|
|
20292
|
-
this.lastSocketError = error_getEventDetails(event);
|
|
20293
|
-
});
|
|
20294
|
-
} catch (exc) {
|
|
20295
|
-
this.connecting = this.connected = false;
|
|
20296
|
-
const diagnostic = error_getWebSocketDiagnostic(url2Connect);
|
|
20297
|
-
const message = diagnostic.looksAutomated ? "Automated crawler: WebSocket not supported in this environment" : `Failed to construct WebSocket to ${url2Connect}`;
|
|
20298
|
-
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}`;
|
|
20299
|
-
const err = error_createError({
|
|
20300
|
-
message,
|
|
20301
|
-
explanation,
|
|
20302
|
-
options: this.options,
|
|
20303
|
-
exc
|
|
20304
|
-
});
|
|
20305
|
-
this.emit("ERROR", {
|
|
20306
|
-
err
|
|
20307
|
-
});
|
|
20308
|
-
return;
|
|
20715
|
+
return this.vcAudioContext;
|
|
20716
|
+
}
|
|
20717
|
+
onAudioProcess(e, cb) {
|
|
20718
|
+
if (!this.userMedia.isRecording() || this.userMedia.isPaused()) return;
|
|
20719
|
+
const float32Array = e.inputBuffer.getChannelData(0);
|
|
20720
|
+
cb(new esm(float32Array));
|
|
20721
|
+
}
|
|
20722
|
+
disconnect() {
|
|
20723
|
+
if (this.scriptProcessor) {
|
|
20724
|
+
this.scriptProcessor.onaudioprocess = null;
|
|
20725
|
+
this.scriptProcessor.disconnect();
|
|
20309
20726
|
}
|
|
20727
|
+
this.audioInput?.disconnect();
|
|
20728
|
+
this.volume?.disconnect();
|
|
20729
|
+
}
|
|
20730
|
+
init(localMediaStream) {
|
|
20731
|
+
this.options.logger.debug("AudioRecorder: init()");
|
|
20732
|
+
this.disconnect();
|
|
20733
|
+
const volume = this.getAudioContext().createGain();
|
|
20734
|
+
this.volume = volume;
|
|
20310
20735
|
try {
|
|
20311
|
-
this.
|
|
20736
|
+
this.audioInput = this.getAudioContext().createMediaStreamSource(localMediaStream);
|
|
20312
20737
|
} catch (exc) {
|
|
20313
|
-
|
|
20314
|
-
|
|
20315
|
-
|
|
20316
|
-
|
|
20317
|
-
options: this.options,
|
|
20318
|
-
exc
|
|
20319
|
-
});
|
|
20320
|
-
this.emit("ERROR", {
|
|
20321
|
-
err
|
|
20322
|
-
});
|
|
20323
|
-
}
|
|
20324
|
-
const stream = this.stream;
|
|
20325
|
-
if (stream) {
|
|
20326
|
-
const connectionTimeoutMs = this.options.timeouts.connection;
|
|
20327
|
-
this.connectionTimeout = window.setTimeout(()=>{
|
|
20328
|
-
if (this.isOnline() && this.isUserMediaLoaded()) {
|
|
20329
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Reconnecting due to connection timeout.`);
|
|
20330
|
-
this.reconnecting = true;
|
|
20331
|
-
this.handleConnectionFailure({
|
|
20332
|
-
url2Connect,
|
|
20333
|
-
cause: "timeout"
|
|
20334
|
-
});
|
|
20335
|
-
} else this.handleConnectionFailure({
|
|
20336
|
-
url2Connect,
|
|
20337
|
-
cause: "timeout"
|
|
20338
|
-
});
|
|
20339
|
-
}, connectionTimeoutMs);
|
|
20340
|
-
stream.on("close", ()=>{
|
|
20341
|
-
this.handleStreamClose(url2Connect);
|
|
20342
|
-
});
|
|
20343
|
-
stream.on("connect", (args)=>{
|
|
20344
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
|
|
20345
|
-
this.clearConnectionTimeout();
|
|
20346
|
-
const isClosing = this.stream?.socket.readyState === WebSocket.CLOSING;
|
|
20347
|
-
if (!this.connected && !isClosing && !this.unloaded) {
|
|
20348
|
-
this.connected = true;
|
|
20349
|
-
this.connecting = this.unloaded = false;
|
|
20350
|
-
this.reconnecting = false;
|
|
20351
|
-
this.reconnectAttempts = 0;
|
|
20352
|
-
this.clearReconnectTimeout();
|
|
20353
|
-
this.emit("CONNECTED");
|
|
20354
|
-
cb?.();
|
|
20355
|
-
}
|
|
20356
|
-
});
|
|
20357
|
-
stream.on("data", (data)=>{
|
|
20358
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
|
|
20359
|
-
try {
|
|
20360
|
-
const command = JSON.parse(data.toString());
|
|
20361
|
-
this.executeCommand(command);
|
|
20362
|
-
} catch (exc) {
|
|
20363
|
-
this.options.logger.error(`Failed to parse command: ${exc}`);
|
|
20364
|
-
const err = error_createError({
|
|
20365
|
-
message: "Invalid server command",
|
|
20366
|
-
explanation: `Contact us. The invalid command was: ${data.toString()}.`,
|
|
20367
|
-
options: this.options,
|
|
20368
|
-
exc
|
|
20369
|
-
});
|
|
20370
|
-
this.emit("ERROR", {
|
|
20371
|
-
err
|
|
20372
|
-
});
|
|
20373
|
-
}
|
|
20374
|
-
});
|
|
20375
|
-
stream.on("error", (err)=>{
|
|
20376
|
-
this.handleStreamError(url2Connect, err);
|
|
20377
|
-
});
|
|
20378
|
-
stream.on("drain", ()=>{
|
|
20379
|
-
this.handleStreamDrain(stream);
|
|
20380
|
-
});
|
|
20381
|
-
stream.on("preend", (args)=>{
|
|
20382
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
|
|
20383
|
-
});
|
|
20384
|
-
stream.on("end", ()=>{
|
|
20385
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *end* event emitted`);
|
|
20386
|
-
});
|
|
20387
|
-
stream.on("pipe", (src)=>{
|
|
20388
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *pipe* event emitted with src: ${string_pretty(src)}`);
|
|
20389
|
-
});
|
|
20390
|
-
stream.on("unpipe", (src)=>{
|
|
20391
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *unpipe* event emitted with src: ${string_pretty(src)}`);
|
|
20392
|
-
});
|
|
20393
|
-
stream.on("resume", (args)=>{
|
|
20394
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *resume* event emitted with args: ${string_pretty(args)}`);
|
|
20395
|
-
});
|
|
20396
|
-
stream.on("uncork", (args)=>{
|
|
20397
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *uncork* event emitted with args: ${string_pretty(args)}`);
|
|
20398
|
-
});
|
|
20399
|
-
stream.on("readable", (args)=>{
|
|
20400
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *readable* event emitted with args: ${string_pretty(args)}`);
|
|
20401
|
-
});
|
|
20402
|
-
stream.on("prefinish", (args)=>{
|
|
20403
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *prefinish* event emitted with args: ${string_pretty(args)}`);
|
|
20404
|
-
});
|
|
20405
|
-
stream.on("finish", (args)=>{
|
|
20406
|
-
this.options.logger.debug(`${PIPE_SYMBOL}Stream *finish* event emitted with args: ${string_pretty(args)}`);
|
|
20738
|
+
throw error_createError({
|
|
20739
|
+
message: "Webcam has no audio",
|
|
20740
|
+
exc,
|
|
20741
|
+
options: this.options
|
|
20407
20742
|
});
|
|
20408
20743
|
}
|
|
20744
|
+
let { bufferSize } = this.options.audio;
|
|
20745
|
+
if ("auto" === bufferSize) bufferSize = util_getBrowser(this.options).isFirefox() ? 512 : 2048;
|
|
20746
|
+
if (void 0 === bufferSize || !is_power_of_two_default()(bufferSize)) throw error_createError({
|
|
20747
|
+
message: `Audio buffer size must be a power of two. The current buffer size is ${bufferSize}.`,
|
|
20748
|
+
options: this.options
|
|
20749
|
+
});
|
|
20750
|
+
if (!Number.isFinite(this.options.audio.volume) || this.options.audio.volume < 0 || this.options.audio.volume > 1) throw error_createError({
|
|
20751
|
+
message: `Audio volume must be between zero and one. The current volume is ${this.options.audio.volume}.`,
|
|
20752
|
+
options: this.options
|
|
20753
|
+
});
|
|
20754
|
+
volume.gain.value = this.options.audio.volume;
|
|
20755
|
+
this.scriptProcessor = this.getAudioContext().createScriptProcessor(bufferSize, CHANNELS, CHANNELS);
|
|
20756
|
+
this.audioInput.connect(volume);
|
|
20757
|
+
volume.connect(this.scriptProcessor);
|
|
20758
|
+
this.scriptProcessor.connect(this.getAudioContext().destination);
|
|
20409
20759
|
}
|
|
20410
|
-
|
|
20411
|
-
|
|
20412
|
-
|
|
20413
|
-
|
|
20414
|
-
|
|
20415
|
-
|
|
20416
|
-
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
|
|
20420
|
-
|
|
20421
|
-
this.
|
|
20422
|
-
this.
|
|
20423
|
-
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
|
|
20428
|
-
err,
|
|
20429
|
-
options: this.options,
|
|
20430
|
-
usedConstraints
|
|
20431
|
-
});
|
|
20432
|
-
this.emit("ERROR", {
|
|
20433
|
-
err: videomailError
|
|
20434
|
-
});
|
|
20435
|
-
} else {
|
|
20436
|
-
this.options.logger.debug(`Recorder: ignore user media error ${string_pretty(err)}`);
|
|
20437
|
-
this.retryTimeout = window.setTimeout(()=>{
|
|
20438
|
-
this.retryTimeout = void 0;
|
|
20439
|
-
if (!this.unloaded) this.loadUserMedia();
|
|
20440
|
-
}, this.options.timeouts.userMedia);
|
|
20441
|
-
}
|
|
20442
|
-
else {
|
|
20443
|
-
this.options.logger.debug(`Recorder: no error listeners attached but throwing error ${string_pretty(err)}`);
|
|
20444
|
-
throw error_createError({
|
|
20760
|
+
record(cb) {
|
|
20761
|
+
this.options.logger.debug("AudioRecorder: record()");
|
|
20762
|
+
if (this.scriptProcessor) this.scriptProcessor.onaudioprocess = (e)=>{
|
|
20763
|
+
this.onAudioProcess(e, cb);
|
|
20764
|
+
};
|
|
20765
|
+
}
|
|
20766
|
+
async resume() {
|
|
20767
|
+
const context = this.getAudioContext();
|
|
20768
|
+
if ("running" !== context.state) await context.resume();
|
|
20769
|
+
}
|
|
20770
|
+
stop() {
|
|
20771
|
+
this.options.logger.debug("AudioRecorder: stop()");
|
|
20772
|
+
this.disconnect();
|
|
20773
|
+
if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
|
|
20774
|
+
this.options.logger.debug("AudioRecorder: audio context is closed");
|
|
20775
|
+
this.vcAudioContext = void 0;
|
|
20776
|
+
}).catch((err)=>{
|
|
20777
|
+
if (err instanceof Error) return void this.options.logger.error(error_createError({
|
|
20445
20778
|
err,
|
|
20446
|
-
message: "Unable to process this error since there are no error listeners anymore.",
|
|
20447
20779
|
options: this.options
|
|
20448
|
-
});
|
|
20449
|
-
|
|
20780
|
+
}));
|
|
20781
|
+
this.options.logger.error(err);
|
|
20782
|
+
});
|
|
20450
20783
|
}
|
|
20451
|
-
|
|
20452
|
-
if (
|
|
20453
|
-
|
|
20454
|
-
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
|
|
20467
|
-
|
|
20468
|
-
|
|
20469
|
-
|
|
20470
|
-
|
|
20471
|
-
|
|
20784
|
+
getSampleRate() {
|
|
20785
|
+
if (this.hasAudioContext()) return this.getAudioContext().sampleRate;
|
|
20786
|
+
return -1;
|
|
20787
|
+
}
|
|
20788
|
+
}
|
|
20789
|
+
const media_AudioRecorder = AudioRecorder;
|
|
20790
|
+
const VIRTUAL_KEYWORDS = [
|
|
20791
|
+
"obs",
|
|
20792
|
+
"virtual",
|
|
20793
|
+
"manycam",
|
|
20794
|
+
"vcam",
|
|
20795
|
+
"splitcam",
|
|
20796
|
+
"droidcam"
|
|
20797
|
+
];
|
|
20798
|
+
function isVirtualCamera(videoTrack) {
|
|
20799
|
+
const capabilities = videoTrack.getCapabilities();
|
|
20800
|
+
if (!capabilities.frameRate) return true;
|
|
20801
|
+
const isVirtual = VIRTUAL_KEYWORDS.some((keyword)=>videoTrack.label.toLowerCase().includes(keyword));
|
|
20802
|
+
return isVirtual;
|
|
20803
|
+
}
|
|
20804
|
+
const util_isVirtualCamera = isVirtualCamera;
|
|
20805
|
+
const EVENT_ASCII = "|—O—|";
|
|
20806
|
+
class UserMedia extends util_Despot {
|
|
20807
|
+
recorder;
|
|
20808
|
+
rawVisualUserMedia;
|
|
20809
|
+
paused = false;
|
|
20810
|
+
recording = false;
|
|
20811
|
+
audioRecorder;
|
|
20812
|
+
currentVisualStream;
|
|
20813
|
+
onPlayReached = false;
|
|
20814
|
+
onLoadedMetaDataReached = false;
|
|
20815
|
+
playingPromiseReached = false;
|
|
20816
|
+
videoTrackLabel;
|
|
20817
|
+
clearInitializationListeners;
|
|
20818
|
+
initializationId = 0;
|
|
20819
|
+
outputEvent = (e)=>{
|
|
20820
|
+
this.logEvent(e.type, {
|
|
20821
|
+
readyState: this.rawVisualUserMedia?.readyState
|
|
20822
|
+
});
|
|
20823
|
+
this.rawVisualUserMedia?.removeEventListener(e.type, this.outputEvent);
|
|
20824
|
+
};
|
|
20825
|
+
handleMediaError = (event)=>{
|
|
20826
|
+
this.options.logger.warn(`Caught video element error event: ${string_pretty(event)}`);
|
|
20827
|
+
};
|
|
20828
|
+
constructor(recorder, options){
|
|
20829
|
+
super("UserMedia", options);
|
|
20830
|
+
this.recorder = recorder;
|
|
20831
|
+
this.rawVisualUserMedia = recorder.getRawVisualUserMedia();
|
|
20832
|
+
mediaEvents.forEach((eventName)=>{
|
|
20833
|
+
this.rawVisualUserMedia?.addEventListener(eventName, this.outputEvent, false);
|
|
20834
|
+
});
|
|
20835
|
+
}
|
|
20836
|
+
attachMediaStream(stream) {
|
|
20837
|
+
if (this.rawVisualUserMedia) {
|
|
20838
|
+
this.rawVisualUserMedia.srcObject = stream;
|
|
20839
|
+
const previousStream = this.currentVisualStream;
|
|
20840
|
+
this.currentVisualStream = stream;
|
|
20841
|
+
if (previousStream && previousStream !== stream) previousStream.getTracks().forEach((track)=>{
|
|
20472
20842
|
track.stop();
|
|
20473
20843
|
});
|
|
20474
|
-
|
|
20475
|
-
|
|
20476
|
-
this
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
|
|
20844
|
+
} else throw error_createError({
|
|
20845
|
+
message: "Error attaching stream to element.",
|
|
20846
|
+
explanation: "Contact the developer about this",
|
|
20847
|
+
options: this.options
|
|
20848
|
+
});
|
|
20849
|
+
}
|
|
20850
|
+
setVisualStream(localMediaStream) {
|
|
20851
|
+
if (localMediaStream) this.attachMediaStream(localMediaStream);
|
|
20480
20852
|
else {
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
this.
|
|
20486
|
-
this.cancelCameraRecovery();
|
|
20853
|
+
if (this.rawVisualUserMedia) {
|
|
20854
|
+
this.rawVisualUserMedia.srcObject = null;
|
|
20855
|
+
this.rawVisualUserMedia.removeAttribute("src");
|
|
20856
|
+
}
|
|
20857
|
+
this.currentVisualStream = void 0;
|
|
20487
20858
|
}
|
|
20488
20859
|
}
|
|
20489
|
-
|
|
20490
|
-
this.
|
|
20491
|
-
this.
|
|
20492
|
-
this.
|
|
20493
|
-
|
|
20494
|
-
|
|
20495
|
-
|
|
20496
|
-
|
|
20497
|
-
|
|
20860
|
+
async restorePlayback() {
|
|
20861
|
+
if (!this.rawVisualUserMedia) throw new Error("No camera preview is available.");
|
|
20862
|
+
await this.rawVisualUserMedia.play();
|
|
20863
|
+
await this.audioRecorder?.resume();
|
|
20864
|
+
}
|
|
20865
|
+
hasLiveStream() {
|
|
20866
|
+
const videoReady = this.currentVisualStream?.getVideoTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
|
|
20867
|
+
const audioReady = !isAudioEnabled(this.options) || this.currentVisualStream?.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
|
|
20868
|
+
return Boolean(this.currentVisualStream?.active && videoReady && audioReady);
|
|
20869
|
+
}
|
|
20870
|
+
hasEnded() {
|
|
20871
|
+
if (this.rawVisualUserMedia?.ended) return this.rawVisualUserMedia.ended;
|
|
20872
|
+
return !this.currentVisualStream?.active;
|
|
20873
|
+
}
|
|
20874
|
+
hasInvalidDimensions() {
|
|
20875
|
+
if (!this.rawVisualUserMedia) return false;
|
|
20876
|
+
const { videoWidth, videoHeight } = this.rawVisualUserMedia;
|
|
20877
|
+
return videoWidth > 0 && videoWidth < 3 || videoHeight > 0 && videoHeight < 3;
|
|
20878
|
+
}
|
|
20879
|
+
logEvent(eventType, params) {
|
|
20880
|
+
this.options.logger.debug(`UserMedia: ... ${EVENT_ASCII} event ${eventType}: ${string_pretty(params)}`);
|
|
20881
|
+
}
|
|
20882
|
+
unloadRemainingEventListeners() {
|
|
20883
|
+
this.options.logger.debug("UserMedia: unloadRemainingEventListeners()");
|
|
20884
|
+
this.clearInitializationListeners?.();
|
|
20885
|
+
mediaEvents.forEach((eventName)=>{
|
|
20886
|
+
this.rawVisualUserMedia?.removeEventListener(eventName, this.outputEvent);
|
|
20887
|
+
});
|
|
20888
|
+
this.rawVisualUserMedia?.removeEventListener("error", this.handleMediaError);
|
|
20889
|
+
}
|
|
20890
|
+
audioRecord(audioCallback) {
|
|
20891
|
+
this.removeListener("SENDING_FIRST_FRAME");
|
|
20892
|
+
this.audioRecorder?.record(audioCallback);
|
|
20893
|
+
}
|
|
20894
|
+
init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, params) {
|
|
20895
|
+
this.clearInitializationListeners?.();
|
|
20896
|
+
this.stop(localMediaStream, {
|
|
20897
|
+
aboutToInitialize: true,
|
|
20898
|
+
preserveRecordingState: Boolean(params?.switchingFacingMode || params?.recovering)
|
|
20899
|
+
});
|
|
20900
|
+
const initializationId = ++this.initializationId;
|
|
20901
|
+
this.onPlayReached = false;
|
|
20902
|
+
this.onLoadedMetaDataReached = false;
|
|
20903
|
+
this.playingPromiseReached = false;
|
|
20904
|
+
this.videoTrackLabel = void 0;
|
|
20905
|
+
if (isAudioEnabled(this.options)) this.audioRecorder ??= new media_AudioRecorder(this, this.options);
|
|
20906
|
+
const unloadAllEventListeners = ()=>{
|
|
20907
|
+
this.options.logger.debug("UserMedia: unloadAllEventListeners()");
|
|
20908
|
+
this.unloadRemainingEventListeners();
|
|
20909
|
+
this.removeListener("SENDING_FIRST_FRAME");
|
|
20910
|
+
this.rawVisualUserMedia?.removeEventListener("play", onPlay);
|
|
20911
|
+
this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
|
|
20912
|
+
};
|
|
20913
|
+
this.clearInitializationListeners = ()=>{
|
|
20914
|
+
this.rawVisualUserMedia?.removeEventListener("play", onPlay);
|
|
20915
|
+
this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
|
|
20916
|
+
this.clearInitializationListeners = void 0;
|
|
20917
|
+
};
|
|
20918
|
+
const play = ()=>{
|
|
20919
|
+
try {
|
|
20920
|
+
this.rawVisualUserMedia?.load();
|
|
20921
|
+
if (this.rawVisualUserMedia?.paused) {
|
|
20922
|
+
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)}`);
|
|
20923
|
+
this.rawVisualUserMedia.play().then(()=>{
|
|
20924
|
+
if (initializationId !== this.initializationId) return;
|
|
20925
|
+
if (!this.playingPromiseReached) {
|
|
20926
|
+
this.options.logger.debug("UserMedia: play promise successful. Playing now.");
|
|
20927
|
+
this.playingPromiseReached = true;
|
|
20928
|
+
}
|
|
20929
|
+
}).catch((exc)=>{
|
|
20930
|
+
if (initializationId !== this.initializationId) return;
|
|
20931
|
+
if (exc instanceof Error && !params?.recovering) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
|
|
20932
|
+
else {
|
|
20933
|
+
unloadAllEventListeners();
|
|
20934
|
+
endedEarlyCallback(error_createError({
|
|
20935
|
+
message: "Failed to play user media upon play event.",
|
|
20936
|
+
exc,
|
|
20937
|
+
options: this.options
|
|
20938
|
+
}));
|
|
20939
|
+
}
|
|
20940
|
+
});
|
|
20941
|
+
}
|
|
20942
|
+
} catch (exc) {
|
|
20943
|
+
unloadAllEventListeners();
|
|
20944
|
+
endedEarlyCallback(exc);
|
|
20945
|
+
}
|
|
20946
|
+
};
|
|
20947
|
+
const fireCallbacks = ()=>{
|
|
20948
|
+
const readyState = this.rawVisualUserMedia?.readyState;
|
|
20949
|
+
this.options.logger.debug(`UserMedia: fireCallbacks(readyState=${readyState}, onPlayReached=${this.onPlayReached}, onLoadedMetaDataReached=${this.onLoadedMetaDataReached})`);
|
|
20950
|
+
if (this.onPlayReached && this.onLoadedMetaDataReached) if (this.audioRecorder) try {
|
|
20951
|
+
this.audioRecorder.init(localMediaStream);
|
|
20952
|
+
const onAudioReady = ()=>{
|
|
20953
|
+
if (initializationId !== this.initializationId || this.currentVisualStream !== localMediaStream) return;
|
|
20954
|
+
if (this.recording) this.audioRecord(audioCallback);
|
|
20955
|
+
else this.on("SENDING_FIRST_FRAME", ()=>{
|
|
20956
|
+
this.audioRecord(audioCallback);
|
|
20957
|
+
});
|
|
20958
|
+
this.clearInitializationListeners?.();
|
|
20959
|
+
videoCallback();
|
|
20960
|
+
};
|
|
20961
|
+
if (params?.recovering) this.audioRecorder.resume().then(onAudioReady).catch(endedEarlyCallback);
|
|
20962
|
+
else onAudioReady();
|
|
20963
|
+
} catch (exc) {
|
|
20964
|
+
unloadAllEventListeners();
|
|
20965
|
+
this.stop(void 0, {
|
|
20966
|
+
preserveRecordingState: Boolean(params?.recovering)
|
|
20967
|
+
});
|
|
20968
|
+
endedEarlyCallback(exc);
|
|
20969
|
+
}
|
|
20970
|
+
else {
|
|
20971
|
+
this.clearInitializationListeners?.();
|
|
20972
|
+
videoCallback();
|
|
20973
|
+
}
|
|
20974
|
+
};
|
|
20975
|
+
const onPlay = ()=>{
|
|
20976
|
+
try {
|
|
20977
|
+
this.logEvent("play", {
|
|
20978
|
+
readyState: this.rawVisualUserMedia?.readyState,
|
|
20979
|
+
audio: isAudioEnabled(this.options),
|
|
20980
|
+
width: this.rawVisualUserMedia?.width,
|
|
20981
|
+
height: this.rawVisualUserMedia?.height,
|
|
20982
|
+
videoWidth: this.rawVisualUserMedia?.videoWidth,
|
|
20983
|
+
videoHeight: this.rawVisualUserMedia?.videoHeight
|
|
20984
|
+
});
|
|
20985
|
+
this.rawVisualUserMedia?.removeEventListener("play", onPlay);
|
|
20986
|
+
if (this.hasEnded() || this.hasInvalidDimensions()) endedEarlyCallback(error_createError({
|
|
20987
|
+
message: "Already busy",
|
|
20988
|
+
explanation: "Probably another browser window is using your webcam?",
|
|
20989
|
+
options: this.options
|
|
20990
|
+
}));
|
|
20991
|
+
else {
|
|
20992
|
+
this.onPlayReached = true;
|
|
20993
|
+
fireCallbacks();
|
|
20498
20994
|
}
|
|
20499
|
-
}
|
|
20500
|
-
|
|
20995
|
+
} catch (exc) {
|
|
20996
|
+
unloadAllEventListeners();
|
|
20997
|
+
endedEarlyCallback(exc);
|
|
20998
|
+
}
|
|
20501
20999
|
};
|
|
20502
|
-
|
|
20503
|
-
|
|
20504
|
-
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
21000
|
+
const onLoadedMetaData = ()=>{
|
|
21001
|
+
this.logEvent("loadedmetadata", {
|
|
21002
|
+
readyState: this.rawVisualUserMedia?.readyState,
|
|
21003
|
+
paused: this.rawVisualUserMedia?.paused,
|
|
21004
|
+
width: this.rawVisualUserMedia?.width,
|
|
21005
|
+
height: this.rawVisualUserMedia?.height,
|
|
21006
|
+
videoWidth: this.rawVisualUserMedia?.videoWidth,
|
|
21007
|
+
videoHeight: this.rawVisualUserMedia?.videoHeight
|
|
20509
21008
|
});
|
|
20510
|
-
this.
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
const videoConstraints = "object" == typeof constraints.video ? constraints.video : void 0;
|
|
20517
|
-
if (!videoConstraints) return;
|
|
20518
|
-
if (params?.switchingFacingMode || params?.recovering) videoConstraints.facingMode = params.switchingFacingMode || this.facingMode;
|
|
20519
|
-
if (this.options.video.width) videoConstraints.width = {
|
|
20520
|
-
ideal: this.options.video.width
|
|
20521
|
-
};
|
|
20522
|
-
else {
|
|
20523
|
-
const limitedWidth = this.limitWidth();
|
|
20524
|
-
if (limitedWidth?.value) videoConstraints.width = {
|
|
20525
|
-
ideal: limitedWidth.value
|
|
20526
|
-
};
|
|
20527
|
-
}
|
|
20528
|
-
if (this.options.video.height) videoConstraints.height = {
|
|
20529
|
-
ideal: this.options.video.height
|
|
21009
|
+
this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
|
|
21010
|
+
if (!this.hasEnded() && !this.hasInvalidDimensions()) {
|
|
21011
|
+
this.emit("LOADED_META_DATA");
|
|
21012
|
+
this.onLoadedMetaDataReached = true;
|
|
21013
|
+
fireCallbacks();
|
|
21014
|
+
}
|
|
20530
21015
|
};
|
|
20531
|
-
}
|
|
20532
|
-
loadUserMedia(params) {
|
|
20533
|
-
if (this.userMediaLoaded) {
|
|
20534
|
-
this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already loaded");
|
|
20535
|
-
this.onUserMediaReady(params);
|
|
20536
|
-
return;
|
|
20537
|
-
} else if (this.userMediaLoading) return void this.options.logger.debug("Recorder: skipping loadUserMedia() because it is already asking for permission");
|
|
20538
|
-
this.options.logger.debug(`Recorder: loadUserMedia(${params ? string_pretty(params) : ""})`);
|
|
20539
|
-
this.emit("LOADING_USER_MEDIA");
|
|
20540
21016
|
try {
|
|
20541
|
-
|
|
20542
|
-
this.
|
|
20543
|
-
|
|
20544
|
-
|
|
20545
|
-
|
|
20546
|
-
|
|
20547
|
-
audio: isAudioEnabled(this.options)
|
|
20548
|
-
});
|
|
20549
|
-
}
|
|
20550
|
-
}, this.options.timeouts.userMedia);
|
|
20551
|
-
this.userMediaLoading = true;
|
|
20552
|
-
this.loadGenuineUserMedia(params);
|
|
20553
|
-
} catch (exc) {
|
|
20554
|
-
this.clearUserMediaTimeout();
|
|
20555
|
-
this.options.logger.debug("Recorder: failed to load genuine user media");
|
|
20556
|
-
this.userMediaLoading = false;
|
|
20557
|
-
if (this.cameraRecovery) return void this.failCameraRecovery(exc);
|
|
20558
|
-
const errorListeners = this.getListeners("ERROR");
|
|
20559
|
-
if (errorListeners?.length) this.emit("ERROR", {
|
|
20560
|
-
exc
|
|
21017
|
+
const videoTrack = media_getFirstVideoTrack(localMediaStream);
|
|
21018
|
+
this.videoTrackLabel = videoTrack?.label;
|
|
21019
|
+
if (videoTrack) if (videoTrack.enabled) if (util_isVirtualCamera(videoTrack)) throw error_createError({
|
|
21020
|
+
message: "Virtual cameras are not allowed.",
|
|
21021
|
+
explanation: `Please use a real camera, not "${videoTrack.label}".`,
|
|
21022
|
+
options: this.options
|
|
20561
21023
|
});
|
|
20562
21024
|
else {
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
}
|
|
20568
|
-
executeCommand(command) {
|
|
20569
|
-
if (this.unloaded) return;
|
|
20570
|
-
try {
|
|
20571
|
-
if (command.args) {
|
|
20572
|
-
const debugLine = string_summarize(`Server commanded: ${command.command} with:`, command.args);
|
|
20573
|
-
this.options.logger.debug(debugLine);
|
|
20574
|
-
} else this.options.logger.debug(`Server commanded: ${command.command}`);
|
|
20575
|
-
switch(command.command){
|
|
20576
|
-
case "ready":
|
|
20577
|
-
this.emit("SERVER_READY");
|
|
20578
|
-
if (!this.userMediaTimeout) if (this.options.loadUserMediaOnRecord) this.show();
|
|
20579
|
-
else this.loadUserMedia();
|
|
20580
|
-
break;
|
|
20581
|
-
case "preview":
|
|
20582
|
-
this.preview(command.args);
|
|
20583
|
-
break;
|
|
20584
|
-
case "error":
|
|
20585
|
-
{
|
|
20586
|
-
let explanation = "(No explanation given)";
|
|
20587
|
-
if (command.args?.err?.message) explanation = command.args.err.message;
|
|
20588
|
-
const err = error_createError({
|
|
20589
|
-
message: "Websocket error from Videomail server",
|
|
20590
|
-
explanation,
|
|
20591
|
-
err: deserializeError(command.args?.err),
|
|
20592
|
-
options: this.options
|
|
20593
|
-
});
|
|
20594
|
-
this.emit("ERROR", {
|
|
20595
|
-
err
|
|
20596
|
-
});
|
|
20597
|
-
break;
|
|
20598
|
-
}
|
|
20599
|
-
case "confirmFrame":
|
|
20600
|
-
this.updateFrameProgress(command.args);
|
|
20601
|
-
break;
|
|
20602
|
-
case "confirmSample":
|
|
20603
|
-
this.updateSampleProgress(command.args);
|
|
20604
|
-
break;
|
|
20605
|
-
case "beginAudioEncoding":
|
|
20606
|
-
this.emit("BEGIN_AUDIO_ENCODING");
|
|
20607
|
-
break;
|
|
20608
|
-
case "beginVideoEncoding":
|
|
20609
|
-
this.emit("BEGIN_VIDEO_ENCODING");
|
|
20610
|
-
break;
|
|
20611
|
-
default:
|
|
20612
|
-
{
|
|
20613
|
-
const err = error_createError({
|
|
20614
|
-
message: `Unknown server command: ${command.command}`,
|
|
20615
|
-
options: this.options
|
|
20616
|
-
});
|
|
20617
|
-
this.emit("ERROR", {
|
|
20618
|
-
err
|
|
20619
|
-
});
|
|
20620
|
-
break;
|
|
20621
|
-
}
|
|
20622
|
-
}
|
|
20623
|
-
} catch (exc) {
|
|
20624
|
-
this.emit("ERROR", {
|
|
20625
|
-
exc
|
|
20626
|
-
});
|
|
20627
|
-
}
|
|
20628
|
-
}
|
|
20629
|
-
isNotifying() {
|
|
20630
|
-
return this.visuals.isNotifying();
|
|
20631
|
-
}
|
|
20632
|
-
isHidden() {
|
|
20633
|
-
return !this.recorderElement || html_isHidden(this.recorderElement);
|
|
20634
|
-
}
|
|
20635
|
-
writeCommand(command, args, cb) {
|
|
20636
|
-
if (this.connected) {
|
|
20637
|
-
if (this.stream) {
|
|
20638
|
-
if (args) this.options.logger.debug(`$ ${command} with ${string_pretty(args)}`);
|
|
20639
|
-
else this.options.logger.debug(`$ ${command}`);
|
|
20640
|
-
const commandObj = {
|
|
20641
|
-
command,
|
|
20642
|
-
args
|
|
20643
|
-
};
|
|
20644
|
-
this.writeStream(Buffer.from(JSON.stringify(commandObj)), cb ? {
|
|
20645
|
-
onFlushedCallback: ()=>{
|
|
20646
|
-
queueMicrotask(cb);
|
|
20647
|
-
}
|
|
20648
|
-
} : void 0);
|
|
21025
|
+
let description = "";
|
|
21026
|
+
if (videoTrack.label.length > 0) description = description.concat(videoTrack.label);
|
|
21027
|
+
description = description.concat(` with enabled=${videoTrack.enabled}, muted=${videoTrack.muted}, readyState=${videoTrack.readyState}`);
|
|
21028
|
+
this.options.logger.debug(`UserMedia: ${videoTrack.kind} detected. ${description}`);
|
|
20649
21029
|
}
|
|
20650
|
-
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
this.
|
|
21030
|
+
else throw error_createError({
|
|
21031
|
+
message: "Webcam is disabled",
|
|
21032
|
+
explanation: "The video track seems to be disabled. Enable it in your system.",
|
|
21033
|
+
options: this.options
|
|
20654
21034
|
});
|
|
21035
|
+
else this.options.logger.debug("UserMedia: detected (but no video tracks exist");
|
|
21036
|
+
this.rawVisualUserMedia?.addEventListener("loadedmetadata", onLoadedMetaData);
|
|
21037
|
+
this.rawVisualUserMedia?.addEventListener("play", onPlay);
|
|
21038
|
+
this.rawVisualUserMedia?.removeEventListener("error", this.handleMediaError);
|
|
21039
|
+
this.rawVisualUserMedia?.addEventListener("error", this.handleMediaError);
|
|
21040
|
+
this.setVisualStream(localMediaStream);
|
|
21041
|
+
play();
|
|
21042
|
+
} catch (exc) {
|
|
21043
|
+
unloadAllEventListeners();
|
|
21044
|
+
endedEarlyCallback(exc);
|
|
20655
21045
|
}
|
|
20656
21046
|
}
|
|
20657
|
-
|
|
20658
|
-
this.
|
|
20659
|
-
}
|
|
20660
|
-
getIntervalSum() {
|
|
20661
|
-
if (!this.loop) return;
|
|
20662
|
-
return this.loop.getElapsedTime();
|
|
20663
|
-
}
|
|
20664
|
-
getAvgInterval() {
|
|
20665
|
-
const intervalSum = this.getIntervalSum() ?? 0;
|
|
20666
|
-
if (!this.hasValidInterval(intervalSum)) return;
|
|
20667
|
-
return intervalSum / this.framesCount;
|
|
20668
|
-
}
|
|
20669
|
-
getAvgFps() {
|
|
20670
|
-
const intervalSum = this.getIntervalSum() ?? 0;
|
|
20671
|
-
if (!this.hasValidInterval(intervalSum)) return;
|
|
20672
|
-
return this.framesCount / intervalSum * 1000;
|
|
20673
|
-
}
|
|
20674
|
-
hasValidInterval(intervalSum) {
|
|
20675
|
-
return intervalSum > 0;
|
|
20676
|
-
}
|
|
20677
|
-
getRecordingStats() {
|
|
20678
|
-
return this.recordingStats;
|
|
21047
|
+
getVideoTrackLabel() {
|
|
21048
|
+
return this.videoTrackLabel;
|
|
20679
21049
|
}
|
|
20680
|
-
|
|
20681
|
-
return this.
|
|
21050
|
+
isReady() {
|
|
21051
|
+
return Boolean(this.rawVisualUserMedia?.srcObject || this.rawVisualUserMedia?.currentSrc);
|
|
20682
21052
|
}
|
|
20683
|
-
stop(params) {
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
|
|
20687
|
-
|
|
20688
|
-
|
|
20689
|
-
|
|
20690
|
-
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
this.stopTimeout = window.setTimeout(()=>{
|
|
20694
|
-
this.stopTimeout = void 0;
|
|
20695
|
-
this.stopTime = Date.now();
|
|
20696
|
-
const videoType = this.replay.getVideoType();
|
|
20697
|
-
if (!videoType) throw new Error("Unable to video record when no video type is defined.");
|
|
20698
|
-
this.recordingStats = {
|
|
20699
|
-
avgFps: this.getAvgFps(),
|
|
20700
|
-
wantedFps: this.options.video.fps,
|
|
20701
|
-
avgInterval: this.getAvgInterval(),
|
|
20702
|
-
wantedInterval: 1e3 / this.options.video.fps,
|
|
20703
|
-
intervalSum: this.getIntervalSum(),
|
|
20704
|
-
framesCount: this.framesCount,
|
|
20705
|
-
videoType
|
|
20706
|
-
};
|
|
20707
|
-
if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
|
|
20708
|
-
if (isAudioEnabled(this.options) && this.userMedia) {
|
|
20709
|
-
this.recordingStats.samplesCount = this.samplesCount;
|
|
20710
|
-
this.recordingStats.sampleRate = this.recordingAudioSampleRate ?? this.userMedia.getAudioSampleRate();
|
|
20711
|
-
}
|
|
20712
|
-
this.writeCommand("stop", this.recordingStats, ()=>{
|
|
20713
|
-
this.emit("STOPPED", {
|
|
20714
|
-
recordingStats: this.recordingStats
|
|
21053
|
+
stop(visualStream, params) {
|
|
21054
|
+
try {
|
|
21055
|
+
let chosenStream = visualStream;
|
|
21056
|
+
if (!params?.aboutToInitialize) {
|
|
21057
|
+
this.initializationId++;
|
|
21058
|
+
this.clearInitializationListeners?.();
|
|
21059
|
+
chosenStream ??= this.currentVisualStream;
|
|
21060
|
+
const tracks = chosenStream?.getTracks();
|
|
21061
|
+
if (tracks) tracks.forEach((track)=>{
|
|
21062
|
+
track.stop();
|
|
20715
21063
|
});
|
|
21064
|
+
this.setVisualStream();
|
|
21065
|
+
this.audioRecorder?.stop();
|
|
21066
|
+
this.audioRecorder = void 0;
|
|
21067
|
+
}
|
|
21068
|
+
if (!params?.preserveRecordingState) this.paused = this.recording = false;
|
|
21069
|
+
} catch (exc) {
|
|
21070
|
+
this.emit("ERROR", {
|
|
21071
|
+
exc
|
|
20716
21072
|
});
|
|
20717
|
-
|
|
20718
|
-
}, 60);
|
|
21073
|
+
}
|
|
20719
21074
|
}
|
|
20720
|
-
|
|
20721
|
-
|
|
20722
|
-
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
|
|
21075
|
+
createCanvas() {
|
|
21076
|
+
const canvas = document.createElement("canvas");
|
|
21077
|
+
const rawWidthDimension = this.getRawWidth(true);
|
|
21078
|
+
if (rawWidthDimension.value) canvas.width = rawWidthDimension.value;
|
|
21079
|
+
const rawHeight = this.getRawHeight(true);
|
|
21080
|
+
if (rawHeight) canvas.height = rawHeight;
|
|
21081
|
+
return canvas;
|
|
20727
21082
|
}
|
|
20728
|
-
|
|
20729
|
-
this.
|
|
20730
|
-
this.
|
|
20731
|
-
this.userMediaLoading = false;
|
|
20732
|
-
this.userMedia?.stop();
|
|
20733
|
-
this.recordingDimensions = void 0;
|
|
20734
|
-
this.userMediaLoaded = this.key = this.canvas = this.ctx = void 0;
|
|
20735
|
-
this.loadUserMedia();
|
|
21083
|
+
getVideoHeight() {
|
|
21084
|
+
if (!this.rawVisualUserMedia) return;
|
|
21085
|
+
return this.rawVisualUserMedia.videoHeight || this.rawVisualUserMedia.height;
|
|
20736
21086
|
}
|
|
20737
|
-
|
|
20738
|
-
if (
|
|
20739
|
-
|
|
20740
|
-
let cause;
|
|
20741
|
-
if (e) cause = e.type;
|
|
20742
|
-
const prettyCause = cause ? `, cause: ${cause}` : "";
|
|
20743
|
-
this.options.logger.debug(`Recorder: unload()${prettyCause}`);
|
|
20744
|
-
this.recordingActive = false;
|
|
20745
|
-
this.mediaTransmissionStarted = false;
|
|
20746
|
-
this.reset();
|
|
20747
|
-
this.recordingDimensions = void 0;
|
|
20748
|
-
this.clearUserMediaTimeout();
|
|
20749
|
-
this.clearConnectionTimeout();
|
|
20750
|
-
this.clearReconnectTimeout();
|
|
20751
|
-
this.clearRetryTimeout();
|
|
20752
|
-
this.clearStopTimeout();
|
|
20753
|
-
this.stopPings();
|
|
20754
|
-
this.userMediaLoading = false;
|
|
20755
|
-
this.connecting = false;
|
|
20756
|
-
this.reconnecting = false;
|
|
20757
|
-
this.reconnectAttempts = 0;
|
|
20758
|
-
if (this.userMedia) this.userMedia.unloadRemainingEventListeners();
|
|
20759
|
-
if (this.submitting) ;
|
|
20760
|
-
else if (this.stream) this.discardSocket();
|
|
20761
|
-
this.unloaded = true;
|
|
20762
|
-
this.built = this.connecting = this.connected = false;
|
|
21087
|
+
getVideoWidth() {
|
|
21088
|
+
if (!this.rawVisualUserMedia) return;
|
|
21089
|
+
return this.rawVisualUserMedia.videoWidth || this.rawVisualUserMedia.width;
|
|
20763
21090
|
}
|
|
20764
|
-
|
|
20765
|
-
this.
|
|
20766
|
-
|
|
20767
|
-
this.options.logger.debug("Recorder: reset()");
|
|
20768
|
-
this.emit("RESETTING");
|
|
20769
|
-
this.cancelAnimationFrame();
|
|
20770
|
-
this.userMedia?.stop();
|
|
20771
|
-
this.replay.reset();
|
|
20772
|
-
this.userMediaLoaded = this.key = this.canvas = this.ctx = this.recordingBuffer = void 0;
|
|
20773
|
-
}
|
|
21091
|
+
hasVideoWidth() {
|
|
21092
|
+
const videoWidth = this.getVideoWidth();
|
|
21093
|
+
return videoWidth && videoWidth > 0;
|
|
20774
21094
|
}
|
|
20775
|
-
|
|
20776
|
-
|
|
20777
|
-
|
|
20778
|
-
|
|
20779
|
-
|
|
21095
|
+
getRawWidth(responsive) {
|
|
21096
|
+
let rawWidth;
|
|
21097
|
+
const widthDimension = {
|
|
21098
|
+
unit: "px"
|
|
21099
|
+
};
|
|
21100
|
+
rawWidth = responsive ? this.getVideoWidth() : this.options.video.width ? this.options.video.width : this.getVideoWidth();
|
|
21101
|
+
if (responsive) {
|
|
21102
|
+
const widthDimension = this.recorder.limitWidth(rawWidth);
|
|
21103
|
+
rawWidth = widthDimension?.value;
|
|
20780
21104
|
}
|
|
21105
|
+
widthDimension.value = rawWidth;
|
|
21106
|
+
return widthDimension;
|
|
20781
21107
|
}
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
21108
|
+
getRawHeight(responsive) {
|
|
21109
|
+
let rawHeight;
|
|
21110
|
+
if (responsive) {
|
|
21111
|
+
rawHeight = this.options.video.height || this.getVideoHeight();
|
|
21112
|
+
const ratio = this.recorder.getRatio();
|
|
21113
|
+
const rawWidth = this.getRawWidth(responsive);
|
|
21114
|
+
if (void 0 !== ratio && rawWidth.value) rawHeight = rawWidth.value * ratio;
|
|
21115
|
+
} else rawHeight = this.options.video.height ? this.options.video.height : this.getVideoHeight();
|
|
21116
|
+
if (responsive) {
|
|
21117
|
+
const heightDimension = this.recorder.limitHeight(rawHeight);
|
|
21118
|
+
rawHeight = heightDimension.value;
|
|
21119
|
+
}
|
|
21120
|
+
return rawHeight;
|
|
20786
21121
|
}
|
|
20787
|
-
|
|
20788
|
-
return
|
|
21122
|
+
getRawVisuals() {
|
|
21123
|
+
return this.rawVisualUserMedia;
|
|
20789
21124
|
}
|
|
20790
|
-
|
|
20791
|
-
|
|
21125
|
+
pause() {
|
|
21126
|
+
this.paused = true;
|
|
20792
21127
|
}
|
|
20793
|
-
|
|
20794
|
-
this.
|
|
20795
|
-
const resolve = this.resolveCameraRecovery;
|
|
20796
|
-
const resumeRequested = restored && this.resumeAfterCameraRecovery;
|
|
20797
|
-
this.cameraRecovery = void 0;
|
|
20798
|
-
this.resolveCameraRecovery = void 0;
|
|
20799
|
-
this.resumeAfterCameraRecovery = false;
|
|
20800
|
-
resolve?.(restored);
|
|
20801
|
-
if (resumeRequested && this.isPaused()) this.resume();
|
|
21128
|
+
isPaused() {
|
|
21129
|
+
return this.paused;
|
|
20802
21130
|
}
|
|
20803
|
-
|
|
20804
|
-
|
|
20805
|
-
this.mediaRequestId++;
|
|
20806
|
-
this.userMediaLoading = false;
|
|
20807
|
-
this.userMediaLoaded = false;
|
|
20808
|
-
this.clearUserMediaTimeout();
|
|
20809
|
-
this.userMedia?.stop(void 0, {
|
|
20810
|
-
preserveRecordingState: true
|
|
20811
|
-
});
|
|
20812
|
-
this.finishCameraRecovery(false);
|
|
21131
|
+
resume() {
|
|
21132
|
+
this.paused = false;
|
|
20813
21133
|
}
|
|
20814
|
-
|
|
20815
|
-
this.
|
|
20816
|
-
const err = error_createError({
|
|
20817
|
-
message: "Camera recovery failed",
|
|
20818
|
-
explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
|
|
20819
|
-
exc,
|
|
20820
|
-
options: this.options
|
|
20821
|
-
});
|
|
20822
|
-
this.emit("CAMERA_RECOVERY_FAILED", {
|
|
20823
|
-
err
|
|
20824
|
-
});
|
|
21134
|
+
record() {
|
|
21135
|
+
this.recording = true;
|
|
20825
21136
|
}
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
|
|
20829
|
-
const liveStream = this.userMedia.hasLiveStream();
|
|
20830
|
-
if (liveStream && this.isRecording()) return Promise.resolve(true);
|
|
20831
|
-
if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
|
|
20832
|
-
const recovery = new Promise((resolve)=>{
|
|
20833
|
-
this.resolveCameraRecovery = resolve;
|
|
20834
|
-
});
|
|
20835
|
-
this.cameraRecovery = recovery;
|
|
20836
|
-
this.emit("CAMERA_RECOVERING");
|
|
20837
|
-
if (liveStream) {
|
|
20838
|
-
this.userMediaTimeout = window.setTimeout(()=>{
|
|
20839
|
-
this.failCameraRecovery(new Error("Camera playback recovery timed out."));
|
|
20840
|
-
}, this.options.timeouts.userMedia);
|
|
20841
|
-
this.userMedia.restorePlayback().then(()=>{
|
|
20842
|
-
if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
|
|
20843
|
-
}).catch((exc)=>{
|
|
20844
|
-
if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
|
|
20845
|
-
});
|
|
20846
|
-
} else {
|
|
20847
|
-
this.userMediaLoaded = false;
|
|
20848
|
-
this.loadUserMedia({
|
|
20849
|
-
recovering: true
|
|
20850
|
-
});
|
|
20851
|
-
}
|
|
20852
|
-
return recovery;
|
|
21137
|
+
isRecording() {
|
|
21138
|
+
return this.recording;
|
|
20853
21139
|
}
|
|
20854
|
-
|
|
20855
|
-
this.
|
|
20856
|
-
|
|
20857
|
-
if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
|
|
20858
|
-
else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
|
|
20859
|
-
this.userMedia?.pause();
|
|
20860
|
-
this.loop.stop();
|
|
20861
|
-
this.emit("PAUSED");
|
|
20862
|
-
this.sendPings();
|
|
21140
|
+
getAudioSampleRate() {
|
|
21141
|
+
if (this.audioRecorder) return this.audioRecorder.getSampleRate();
|
|
21142
|
+
return -1;
|
|
20863
21143
|
}
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
this.
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
this.userMedia.resume();
|
|
20874
|
-
if (this.streamBackpressured) this.resumeAfterDrain = true;
|
|
20875
|
-
else this.loop.start();
|
|
21144
|
+
getCharacteristics() {
|
|
21145
|
+
return {
|
|
21146
|
+
audioSampleRate: this.getAudioSampleRate(),
|
|
21147
|
+
muted: this.rawVisualUserMedia?.muted,
|
|
21148
|
+
width: this.rawVisualUserMedia?.width,
|
|
21149
|
+
height: this.rawVisualUserMedia?.height,
|
|
21150
|
+
videoWidth: this.rawVisualUserMedia?.videoWidth,
|
|
21151
|
+
videoHeight: this.rawVisualUserMedia?.videoHeight
|
|
21152
|
+
};
|
|
20876
21153
|
}
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
21154
|
+
}
|
|
21155
|
+
const visuals_userMedia = UserMedia;
|
|
21156
|
+
class Recorder extends capture {
|
|
21157
|
+
dimensions;
|
|
21158
|
+
constructor(visuals, replay, options){
|
|
21159
|
+
super(visuals, replay, options);
|
|
21160
|
+
this.dimensions = new recorder_dimensions({
|
|
21161
|
+
options,
|
|
21162
|
+
visuals,
|
|
21163
|
+
getRecorderElement: ()=>this.recorderElement,
|
|
21164
|
+
getUserMedia: ()=>this.userMedia
|
|
21165
|
+
});
|
|
20880
21166
|
}
|
|
20881
|
-
|
|
20882
|
-
if (this.
|
|
20883
|
-
if (!this.userMedia) throw new Error("No user media defined, unable to draw on canvas");
|
|
21167
|
+
executeCommand(command) {
|
|
21168
|
+
if (this.unloaded) return;
|
|
20884
21169
|
try {
|
|
20885
|
-
if (
|
|
20886
|
-
|
|
20887
|
-
this.
|
|
20888
|
-
|
|
20889
|
-
|
|
20890
|
-
|
|
20891
|
-
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
21170
|
+
if (command.args) {
|
|
21171
|
+
const debugLine = string_summarize(`Server commanded: ${command.command} with:`, command.args);
|
|
21172
|
+
this.options.logger.debug(debugLine);
|
|
21173
|
+
} else this.options.logger.debug(`Server commanded: ${command.command}`);
|
|
21174
|
+
switch(command.command){
|
|
21175
|
+
case "ready":
|
|
21176
|
+
this.emit("SERVER_READY");
|
|
21177
|
+
if (!this.userMediaTimeout) if (this.options.loadUserMediaOnRecord) this.show();
|
|
21178
|
+
else this.loadUserMedia();
|
|
21179
|
+
break;
|
|
21180
|
+
case "preview":
|
|
21181
|
+
this.preview(command.args);
|
|
21182
|
+
break;
|
|
21183
|
+
case "error":
|
|
21184
|
+
{
|
|
21185
|
+
let explanation = "(No explanation given)";
|
|
21186
|
+
if (command.args?.err?.message) explanation = command.args.err.message;
|
|
21187
|
+
const err = error_createError({
|
|
21188
|
+
message: "Websocket error from Videomail server",
|
|
21189
|
+
explanation,
|
|
21190
|
+
err: deserializeError(command.args?.err),
|
|
21191
|
+
options: this.options
|
|
20906
21192
|
});
|
|
20907
|
-
this.
|
|
21193
|
+
this.emit("ERROR", {
|
|
21194
|
+
err
|
|
21195
|
+
});
|
|
21196
|
+
break;
|
|
21197
|
+
}
|
|
21198
|
+
case "confirmFrame":
|
|
21199
|
+
this.updateFrameProgress(command.args);
|
|
21200
|
+
break;
|
|
21201
|
+
case "confirmSample":
|
|
21202
|
+
this.updateSampleProgress(command.args);
|
|
21203
|
+
break;
|
|
21204
|
+
case "beginAudioEncoding":
|
|
21205
|
+
this.emit("BEGIN_AUDIO_ENCODING");
|
|
21206
|
+
break;
|
|
21207
|
+
case "beginVideoEncoding":
|
|
21208
|
+
this.emit("BEGIN_VIDEO_ENCODING");
|
|
21209
|
+
break;
|
|
21210
|
+
default:
|
|
21211
|
+
{
|
|
21212
|
+
const err = error_createError({
|
|
21213
|
+
message: `Unknown server command: ${command.command}`,
|
|
21214
|
+
options: this.options
|
|
21215
|
+
});
|
|
21216
|
+
this.emit("ERROR", {
|
|
21217
|
+
err
|
|
21218
|
+
});
|
|
21219
|
+
break;
|
|
20908
21220
|
}
|
|
20909
|
-
} else throw error_createError({
|
|
20910
|
-
message: "Failed to extract webcam data.",
|
|
20911
|
-
options: this.options
|
|
20912
|
-
});
|
|
20913
21221
|
}
|
|
20914
21222
|
} catch (exc) {
|
|
20915
21223
|
this.emit("ERROR", {
|
|
@@ -20917,156 +21225,34 @@
|
|
|
20917
21225
|
});
|
|
20918
21226
|
}
|
|
20919
21227
|
}
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
if (this.unloaded || this.cameraRecovery) return;
|
|
20929
|
-
if (!this.connected) {
|
|
20930
|
-
this.options.logger.debug("Recorder: reconnecting before recording …");
|
|
20931
|
-
this.initSocket(()=>{
|
|
20932
|
-
this.once("SERVER_READY", ()=>{
|
|
20933
|
-
if (this.unloaded) return;
|
|
20934
|
-
if (this.userMediaLoaded || this.options.loadUserMediaOnRecord) this.record();
|
|
20935
|
-
else this.once("USER_MEDIA_READY", ()=>{
|
|
20936
|
-
if (!this.unloaded) this.record();
|
|
20937
|
-
});
|
|
20938
|
-
});
|
|
20939
|
-
});
|
|
20940
|
-
return;
|
|
20941
|
-
}
|
|
20942
|
-
if (!this.userMediaLoaded) {
|
|
20943
|
-
if (this.options.loadUserMediaOnRecord) this.loadUserMedia({
|
|
20944
|
-
recordWhenReady: true
|
|
20945
|
-
});
|
|
20946
|
-
else {
|
|
20947
|
-
const err = error_createError({
|
|
20948
|
-
message: "Load and enable your camera first",
|
|
20949
|
-
options: this.options
|
|
20950
|
-
});
|
|
20951
|
-
this.emit("ERROR", {
|
|
20952
|
-
err
|
|
20953
|
-
});
|
|
20954
|
-
}
|
|
20955
|
-
return;
|
|
20956
|
-
}
|
|
20957
|
-
this.recordingDimensions = void 0;
|
|
20958
|
-
try {
|
|
20959
|
-
if (!this.userMedia) throw new Error("No user media defined, unable to create canvas");
|
|
20960
|
-
this.canvas = this.userMedia.createCanvas();
|
|
20961
|
-
} catch (exc) {
|
|
20962
|
-
const err = error_createError({
|
|
20963
|
-
exc,
|
|
20964
|
-
options: this.options
|
|
20965
|
-
});
|
|
20966
|
-
this.emit("ERROR", {
|
|
20967
|
-
err
|
|
20968
|
-
});
|
|
20969
|
-
return;
|
|
20970
|
-
}
|
|
20971
|
-
this.ctx = this.canvas.getContext("2d");
|
|
20972
|
-
if (!this.canvas.width) {
|
|
20973
|
-
const err = error_createError({
|
|
20974
|
-
message: "Canvas has an invalid width.",
|
|
20975
|
-
options: this.options
|
|
20976
|
-
});
|
|
20977
|
-
this.emit("ERROR", {
|
|
20978
|
-
err
|
|
20979
|
-
});
|
|
20980
|
-
return;
|
|
20981
|
-
}
|
|
20982
|
-
if (!this.canvas.height) {
|
|
20983
|
-
const err = error_createError({
|
|
20984
|
-
message: "Canvas has an invalid height.",
|
|
20985
|
-
options: this.options
|
|
20986
|
-
});
|
|
20987
|
-
this.emit("ERROR", {
|
|
20988
|
-
err
|
|
20989
|
-
});
|
|
20990
|
-
return;
|
|
20991
|
-
}
|
|
20992
|
-
this.recordingDimensions = {
|
|
20993
|
-
width: this.canvas.width,
|
|
20994
|
-
height: this.canvas.height
|
|
20995
|
-
};
|
|
20996
|
-
this.frame = new canvas_to_buffer_modern_r(this.canvas, this.options.image.types, this.options.image.quality);
|
|
20997
|
-
this.options.logger.debug("Recorder: record()");
|
|
20998
|
-
this.recordingActive = true;
|
|
20999
|
-
this.recordingTransportFailed = false;
|
|
21228
|
+
unload(params) {
|
|
21229
|
+
if (this.unloaded || !this.built) return;
|
|
21230
|
+
const e = params?.e;
|
|
21231
|
+
let cause;
|
|
21232
|
+
if (e) cause = e.type;
|
|
21233
|
+
const prettyCause = cause ? `, cause: ${cause}` : "";
|
|
21234
|
+
this.options.logger.debug(`Recorder: unload()${prettyCause}`);
|
|
21235
|
+
this.recordingActive = false;
|
|
21000
21236
|
this.mediaTransmissionStarted = false;
|
|
21001
|
-
this.
|
|
21002
|
-
this.
|
|
21003
|
-
this.
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
this.
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
this.
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21015
|
-
|
|
21016
|
-
|
|
21017
|
-
this.loop.setRequestAnimationFrameObject(newObj);
|
|
21018
|
-
if (isRecording) this.loop.start();
|
|
21019
|
-
}
|
|
21020
|
-
}
|
|
21021
|
-
restoreAnimationFrameObject() {
|
|
21022
|
-
this.options.logger.debug("Recorder: restoreAnimationFrameObject()");
|
|
21023
|
-
this.setAnimationFrameObject(this.originalAnimationFrameObject);
|
|
21024
|
-
}
|
|
21025
|
-
loopWithTimeouts() {
|
|
21026
|
-
this.options.logger.debug("Recorder: loopWithTimeouts()");
|
|
21027
|
-
const wantedInterval = 1e3 / this.options.video.fps;
|
|
21028
|
-
let processingTime = 0;
|
|
21029
|
-
let start;
|
|
21030
|
-
const raf = (fn)=>setTimeout(()=>{
|
|
21031
|
-
start = Date.now();
|
|
21032
|
-
fn();
|
|
21033
|
-
processingTime = Date.now() - start;
|
|
21034
|
-
}, wantedInterval - processingTime);
|
|
21035
|
-
const cancel = (id)=>{
|
|
21036
|
-
window.clearTimeout(id);
|
|
21037
|
-
};
|
|
21038
|
-
this.setAnimationFrameObject({
|
|
21039
|
-
requestAnimationFrame: raf,
|
|
21040
|
-
cancelAnimationFrame: cancel
|
|
21041
|
-
});
|
|
21237
|
+
this.reset();
|
|
21238
|
+
this.recordingDimensions = void 0;
|
|
21239
|
+
this.clearUserMediaTimeout();
|
|
21240
|
+
this.clearConnectionTimeout();
|
|
21241
|
+
this.clearReconnectTimeout();
|
|
21242
|
+
this.clearRetryTimeout();
|
|
21243
|
+
this.clearStopTimeout();
|
|
21244
|
+
this.stopPings();
|
|
21245
|
+
this.userMediaLoading = false;
|
|
21246
|
+
this.connecting = false;
|
|
21247
|
+
this.reconnecting = false;
|
|
21248
|
+
this.reconnectAttempts = 0;
|
|
21249
|
+
if (this.userMedia) this.userMedia.unloadRemainingEventListeners();
|
|
21250
|
+
this.unloaded = true;
|
|
21251
|
+
this.discardSocket();
|
|
21252
|
+
this.built = this.connecting = this.connected = false;
|
|
21042
21253
|
}
|
|
21043
21254
|
correctDimensions() {
|
|
21044
|
-
|
|
21045
|
-
const widthDimension = dimensions_useFullWidth(this.options.video.mobileBreakPoint);
|
|
21046
|
-
if (this.options.video.stretch || widthDimension) {
|
|
21047
|
-
this.recorderElement.style.width = "100%";
|
|
21048
|
-
this.recorderElement.style.removeProperty("height");
|
|
21049
|
-
} else {
|
|
21050
|
-
if (this.options.video.width) {
|
|
21051
|
-
const recorderWidth = this.getRecorderWidth(true);
|
|
21052
|
-
if (recorderWidth?.value) this.recorderElement.width = recorderWidth.value;
|
|
21053
|
-
else this.recorderElement.style.removeProperty("width");
|
|
21054
|
-
}
|
|
21055
|
-
if (this.options.video.height) {
|
|
21056
|
-
const recorderHeight = this.getRecorderHeight(true);
|
|
21057
|
-
if (recorderHeight?.value) this.recorderElement.height = recorderHeight.value;
|
|
21058
|
-
else this.recorderElement.style.removeProperty("height");
|
|
21059
|
-
}
|
|
21060
|
-
}
|
|
21061
|
-
}
|
|
21062
|
-
switchFacingMode() {
|
|
21063
|
-
if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
|
|
21064
|
-
if ("user" === this.facingMode) this.facingMode = "environment";
|
|
21065
|
-
else if ("environment" === this.facingMode) this.facingMode = "user";
|
|
21066
|
-
else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
|
|
21067
|
-
this.loadGenuineUserMedia({
|
|
21068
|
-
switchingFacingMode: this.facingMode
|
|
21069
|
-
});
|
|
21255
|
+
this.dimensions.correctDimensions();
|
|
21070
21256
|
}
|
|
21071
21257
|
initEvents() {
|
|
21072
21258
|
this.options.logger.debug("Recorder: initEvents()");
|
|
@@ -21138,100 +21324,20 @@
|
|
|
21138
21324
|
}
|
|
21139
21325
|
this.built = true;
|
|
21140
21326
|
}
|
|
21141
|
-
isPaused() {
|
|
21142
|
-
return this.userMedia?.isPaused() && !this.loop.isRunning();
|
|
21143
|
-
}
|
|
21144
|
-
isRecording() {
|
|
21145
|
-
return Boolean(this.loop?.isRunning() && !this.isPaused() && !this.isNotifying() && this.stream && !this.stream.destroyed);
|
|
21146
|
-
}
|
|
21147
|
-
hide() {
|
|
21148
|
-
if (!this.isHidden()) {
|
|
21149
|
-
if (this.recorderElement) html_hideElement(this.recorderElement);
|
|
21150
|
-
this.clearUserMediaTimeout();
|
|
21151
|
-
this.clearRetryTimeout();
|
|
21152
|
-
}
|
|
21153
|
-
}
|
|
21154
|
-
isUnloaded() {
|
|
21155
|
-
return this.unloaded;
|
|
21156
|
-
}
|
|
21157
21327
|
getRecorderWidth(responsive) {
|
|
21158
|
-
|
|
21159
|
-
else if (responsive && this.options.video.width) return this.limitWidth(this.options.video.width);
|
|
21160
|
-
const dimension = {
|
|
21161
|
-
unit: "px",
|
|
21162
|
-
value: this.options.video.width
|
|
21163
|
-
};
|
|
21164
|
-
return dimension;
|
|
21165
|
-
}
|
|
21166
|
-
getRecordingDimensions() {
|
|
21167
|
-
return this.recordingDimensions && {
|
|
21168
|
-
...this.recordingDimensions
|
|
21169
|
-
};
|
|
21328
|
+
return this.dimensions.getRecorderWidth(responsive);
|
|
21170
21329
|
}
|
|
21171
21330
|
getRecorderHeight(responsive, useBoundingClientRect) {
|
|
21172
|
-
|
|
21173
|
-
if (this.recorderElement && useBoundingClientRect) {
|
|
21174
|
-
const height = this.recorderElement.getBoundingClientRect().height;
|
|
21175
|
-
recorderHeight = {
|
|
21176
|
-
unit: "px",
|
|
21177
|
-
value: height
|
|
21178
|
-
};
|
|
21179
|
-
} else if (this.userMedia) {
|
|
21180
|
-
let height = this.userMedia.getRawHeight(responsive);
|
|
21181
|
-
if (void 0 !== height && this.options.video.width) {
|
|
21182
|
-
const ratio = this.getRatio();
|
|
21183
|
-
if (void 0 !== ratio) {
|
|
21184
|
-
const idealHeight = this.options.video.width * ratio;
|
|
21185
|
-
height = Math.min(idealHeight, height);
|
|
21186
|
-
}
|
|
21187
|
-
}
|
|
21188
|
-
recorderHeight = {
|
|
21189
|
-
unit: "px",
|
|
21190
|
-
value: height
|
|
21191
|
-
};
|
|
21192
|
-
} else if (responsive && this.options.video.height) recorderHeight = this.calculateHeight(responsive);
|
|
21193
|
-
else if (this.options.video.height) {
|
|
21194
|
-
const height = this.options.video.height;
|
|
21195
|
-
recorderHeight = {
|
|
21196
|
-
unit: "px",
|
|
21197
|
-
value: height
|
|
21198
|
-
};
|
|
21199
|
-
}
|
|
21200
|
-
return recorderHeight;
|
|
21331
|
+
return this.dimensions.getRecorderHeight(responsive, useBoundingClientRect);
|
|
21201
21332
|
}
|
|
21202
21333
|
getRatio() {
|
|
21203
|
-
|
|
21204
|
-
if (this.userMedia) {
|
|
21205
|
-
const userMediaVideoWidth = this.userMedia.getVideoWidth();
|
|
21206
|
-
const userMediaVideoHeight = this.userMedia.getVideoHeight();
|
|
21207
|
-
if (!userMediaVideoWidth || userMediaVideoWidth < 1) ratio = this.visuals.getRatio();
|
|
21208
|
-
else if (userMediaVideoHeight) ratio = userMediaVideoHeight / userMediaVideoWidth;
|
|
21209
|
-
} else ratio = dimensions_getRatio(this.options);
|
|
21210
|
-
return ratio;
|
|
21334
|
+
return this.dimensions.getRatio();
|
|
21211
21335
|
}
|
|
21212
21336
|
calculateWidth(responsive) {
|
|
21213
|
-
|
|
21214
|
-
if (this.userMedia) videoHeight = this.userMedia.getVideoHeight();
|
|
21215
|
-
else if (this.recorderElement) videoHeight = this.recorderElement.videoHeight || this.recorderElement.height;
|
|
21216
|
-
const ratio = this.getRatio();
|
|
21217
|
-
return dimensions_calculateWidth(responsive, this.options, videoHeight, ratio);
|
|
21337
|
+
return this.dimensions.calculateWidth(responsive);
|
|
21218
21338
|
}
|
|
21219
21339
|
calculateHeight(responsive) {
|
|
21220
|
-
|
|
21221
|
-
if (this.userMedia) {
|
|
21222
|
-
const videoHeight = this.userMedia.getVideoHeight();
|
|
21223
|
-
videoDimension = {
|
|
21224
|
-
value: videoHeight,
|
|
21225
|
-
unit: "px"
|
|
21226
|
-
};
|
|
21227
|
-
} else if (this.recorderElement) {
|
|
21228
|
-
const videoHeight = this.recorderElement.videoHeight || this.recorderElement.height;
|
|
21229
|
-
videoDimension = {
|
|
21230
|
-
value: videoHeight,
|
|
21231
|
-
unit: "px"
|
|
21232
|
-
};
|
|
21233
|
-
} else videoDimension = dimensions_calculateHeight(responsive, void 0, this.options, this.getRatio(), this.recorderElement);
|
|
21234
|
-
return videoDimension;
|
|
21340
|
+
return this.dimensions.calculateHeight(responsive);
|
|
21235
21341
|
}
|
|
21236
21342
|
getRawVisualUserMedia() {
|
|
21237
21343
|
return this.recorderElement;
|
|
@@ -21248,9 +21354,6 @@
|
|
|
21248
21354
|
limitHeight(height) {
|
|
21249
21355
|
return this.visuals.limitHeight(height);
|
|
21250
21356
|
}
|
|
21251
|
-
isUserMediaLoaded() {
|
|
21252
|
-
return this.userMediaLoaded;
|
|
21253
|
-
}
|
|
21254
21357
|
}
|
|
21255
21358
|
const visuals_recorder = Recorder;
|
|
21256
21359
|
class Replay extends util_Despot {
|
|
@@ -22162,7 +22265,8 @@
|
|
|
22162
22265
|
}
|
|
22163
22266
|
else runValidation = false;
|
|
22164
22267
|
if (runValidation) {
|
|
22165
|
-
const
|
|
22268
|
+
const target = event?.target;
|
|
22269
|
+
const targetName = target instanceof Element ? target.getAttribute("name") : void 0;
|
|
22166
22270
|
if (targetName) this.emit("VALIDATING", {
|
|
22167
22271
|
targetName
|
|
22168
22272
|
});
|
|
@@ -22197,27 +22301,10 @@
|
|
|
22197
22301
|
}
|
|
22198
22302
|
}
|
|
22199
22303
|
if (valid) {
|
|
22200
|
-
|
|
22201
|
-
|
|
22202
|
-
|
|
22203
|
-
const bccIsConfigured = "bcc" in recipients;
|
|
22204
|
-
const hasTo = recipients.to && recipients.to.length > 0;
|
|
22205
|
-
const hasCc = recipients.cc && recipients.cc.length > 0;
|
|
22206
|
-
const hasBcc = recipients.bcc && recipients.bcc.length > 0;
|
|
22207
|
-
if (toIsConfigured) {
|
|
22208
|
-
if (!hasTo) if (ccIsConfigured && bccIsConfigured) {
|
|
22209
|
-
if (!hasCc && !hasBcc) valid = false;
|
|
22210
|
-
} else if (ccIsConfigured) {
|
|
22211
|
-
if (!hasCc) valid = false;
|
|
22212
|
-
} else if (bccIsConfigured) {
|
|
22213
|
-
if (!hasBcc) valid = false;
|
|
22214
|
-
} else valid = false;
|
|
22215
|
-
} else if (ccIsConfigured) {
|
|
22216
|
-
if (!hasCc) {
|
|
22217
|
-
if (bccIsConfigured && !hasBcc) valid = false;
|
|
22218
|
-
}
|
|
22304
|
+
if (!email_isRecipientSelectionValid(this.form.getRecipients())) {
|
|
22305
|
+
valid = false;
|
|
22306
|
+
whyInvalid = "At least one recipient is required";
|
|
22219
22307
|
}
|
|
22220
|
-
if (!valid) whyInvalid = "At least one recipient is required";
|
|
22221
22308
|
}
|
|
22222
22309
|
} else {
|
|
22223
22310
|
valid = visualsValid;
|