videomail-client 16.0.2 → 16.1.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 +19 -19
- package/dist/cjs/index.cjs +340 -55
- package/dist/esm/client.d.ts +2 -0
- package/dist/esm/index.js +269 -53
- package/dist/esm/types/events/index.d.ts +3 -1
- package/dist/esm/types/events/params.d.ts +4 -0
- package/dist/esm/util/Despot.d.ts +2 -0
- package/dist/esm/util/html/media/AudioRecorder.d.ts +3 -0
- package/dist/esm/wrappers/container.d.ts +2 -0
- package/dist/esm/wrappers/form.d.ts +9 -2
- package/dist/esm/wrappers/visuals/recorder.d.ts +9 -0
- package/dist/esm/wrappers/visuals/userMedia.d.ts +8 -3
- package/dist/esm/wrappers/visuals.d.ts +3 -0
- package/dist/umd/index.js +280 -51
- package/package.json +3 -3
package/dist/umd/index.js
CHANGED
|
@@ -11121,7 +11121,7 @@
|
|
|
11121
11121
|
var client = __webpack_require__("Q");
|
|
11122
11122
|
var client_default = /*#__PURE__*/ __webpack_require__.n(client);
|
|
11123
11123
|
var package_namespaceObject = {
|
|
11124
|
-
rE: "16.0
|
|
11124
|
+
rE: "16.1.0"
|
|
11125
11125
|
};
|
|
11126
11126
|
function isAudioEnabled(options) {
|
|
11127
11127
|
return Boolean(options.audio.enabled);
|
|
@@ -15029,12 +15029,14 @@
|
|
|
15029
15029
|
GET: "get"
|
|
15030
15030
|
};
|
|
15031
15031
|
class Form extends util_Despot {
|
|
15032
|
+
static nextFormId = 0;
|
|
15032
15033
|
container;
|
|
15033
15034
|
formElement;
|
|
15034
|
-
validate = (event)=>{
|
|
15035
|
-
this.container.validate(event);
|
|
15036
|
-
};
|
|
15037
15035
|
keyInput;
|
|
15036
|
+
submitButton;
|
|
15037
|
+
submitButtonFormAttribute = null;
|
|
15038
|
+
generatedFormId;
|
|
15039
|
+
isSubmitting = false;
|
|
15038
15040
|
FORM_FIELDS = {};
|
|
15039
15041
|
constructor(container, formElement, options){
|
|
15040
15042
|
super("Form", options);
|
|
@@ -15052,6 +15054,12 @@
|
|
|
15052
15054
|
sendCopy: options.selectors.sendCopyInputName
|
|
15053
15055
|
};
|
|
15054
15056
|
}
|
|
15057
|
+
validate = (event)=>{
|
|
15058
|
+
this.container.validate(event);
|
|
15059
|
+
};
|
|
15060
|
+
handleSubmit = (event)=>{
|
|
15061
|
+
if (!event.defaultPrevented) this.doTheSubmit(event);
|
|
15062
|
+
};
|
|
15055
15063
|
getData() {
|
|
15056
15064
|
return getFormData(this.formElement, {
|
|
15057
15065
|
includeDisabled: true
|
|
@@ -15235,19 +15243,42 @@
|
|
|
15235
15243
|
if (type?.toLowerCase() === "hidden") inputElement.value = "";
|
|
15236
15244
|
}
|
|
15237
15245
|
}
|
|
15238
|
-
|
|
15246
|
+
connectSubmitButton() {
|
|
15239
15247
|
const submitButton = this.container.getSubmitButton();
|
|
15240
|
-
if (submitButton)
|
|
15248
|
+
if (!submitButton) return;
|
|
15249
|
+
if (submitButton.form === this.formElement) return;
|
|
15250
|
+
if (!this.formElement.id) {
|
|
15251
|
+
let formId;
|
|
15252
|
+
do formId = `videomail-form-${Form.nextFormId++}`;
|
|
15253
|
+
while (document.getElementById(formId))
|
|
15254
|
+
this.formElement.id = formId;
|
|
15255
|
+
this.generatedFormId = formId;
|
|
15256
|
+
}
|
|
15257
|
+
this.submitButton = submitButton;
|
|
15258
|
+
this.submitButtonFormAttribute = submitButton.getAttribute("form");
|
|
15259
|
+
submitButton.setAttribute("form", this.formElement.id);
|
|
15260
|
+
}
|
|
15261
|
+
startListeningToSubmitEvents() {
|
|
15262
|
+
this.connectSubmitButton();
|
|
15263
|
+
this.formElement.addEventListener("submit", this.handleSubmit);
|
|
15241
15264
|
}
|
|
15242
15265
|
stopListeningToSubmitEvents() {
|
|
15243
|
-
|
|
15244
|
-
if (submitButton)
|
|
15266
|
+
this.formElement.removeEventListener("submit", this.handleSubmit);
|
|
15267
|
+
if (this.submitButton) {
|
|
15268
|
+
if (null === this.submitButtonFormAttribute) this.submitButton.removeAttribute("form");
|
|
15269
|
+
else this.submitButton.setAttribute("form", this.submitButtonFormAttribute);
|
|
15270
|
+
this.submitButton = void 0;
|
|
15271
|
+
this.submitButtonFormAttribute = null;
|
|
15272
|
+
}
|
|
15273
|
+
if (this.generatedFormId === this.formElement.id) this.formElement.removeAttribute("id");
|
|
15274
|
+
this.generatedFormId = void 0;
|
|
15245
15275
|
}
|
|
15246
15276
|
async doTheSubmit(e) {
|
|
15247
15277
|
if (e) {
|
|
15248
15278
|
this.options.logger.debug(`Form: doTheSubmit(${string_pretty(e)})`);
|
|
15249
15279
|
e.preventDefault();
|
|
15250
15280
|
} else this.options.logger.debug("Form: doTheSubmit()");
|
|
15281
|
+
if (this.isSubmitting) return false;
|
|
15251
15282
|
const hasHostAction = this.formElement.hasAttribute("action");
|
|
15252
15283
|
const url = this.formElement.getAttribute("action") ?? this.options.apiUrl;
|
|
15253
15284
|
const method = this.formElement.getAttribute("method");
|
|
@@ -15262,7 +15293,14 @@
|
|
|
15262
15293
|
default:
|
|
15263
15294
|
chosenMethod = FormMethod.POST;
|
|
15264
15295
|
}
|
|
15265
|
-
if (this.container.hasElement())
|
|
15296
|
+
if (this.container.hasElement()) {
|
|
15297
|
+
this.isSubmitting = true;
|
|
15298
|
+
try {
|
|
15299
|
+
await this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
|
|
15300
|
+
} finally{
|
|
15301
|
+
this.isSubmitting = false;
|
|
15302
|
+
}
|
|
15303
|
+
}
|
|
15266
15304
|
return false;
|
|
15267
15305
|
}
|
|
15268
15306
|
getInvalidElement() {
|
|
@@ -17888,7 +17926,7 @@
|
|
|
17888
17926
|
html_disableElement(radioButtonElement);
|
|
17889
17927
|
return radioButtonElement;
|
|
17890
17928
|
}
|
|
17891
|
-
makeButton(buttonClass, text, clickHandler, show, id, type, selector, disabled = true) {
|
|
17929
|
+
makeButton(buttonClass, text, clickHandler, show, id, type = "button", selector, disabled = true) {
|
|
17892
17930
|
let buttonElement;
|
|
17893
17931
|
buttonElement = id ? document.querySelector(`#${id}`) : selector ? document.querySelector(selector) : this.buttonsElement?.querySelector(`.${buttonClass}`);
|
|
17894
17932
|
if (buttonElement) buttonElement = html_adjustButton(buttonElement, show, type, disabled);
|
|
@@ -18598,10 +18636,14 @@
|
|
|
18598
18636
|
this.on("USER_MEDIA_READY", ()=>{
|
|
18599
18637
|
this.facingMode?.show();
|
|
18600
18638
|
});
|
|
18639
|
+
this.on("CAMERA_RECOVERING", ()=>{
|
|
18640
|
+
this.facingMode?.hide();
|
|
18641
|
+
});
|
|
18601
18642
|
this.on("RECORDING", ()=>{
|
|
18602
18643
|
this.startRecording();
|
|
18603
18644
|
});
|
|
18604
18645
|
this.on("RESUMING", ()=>{
|
|
18646
|
+
this.hidePause();
|
|
18605
18647
|
this.resumeRecording();
|
|
18606
18648
|
});
|
|
18607
18649
|
this.on("STOPPING", ()=>{
|
|
@@ -19216,6 +19258,7 @@
|
|
|
19216
19258
|
class AudioRecorder {
|
|
19217
19259
|
scriptProcessor;
|
|
19218
19260
|
audioInput;
|
|
19261
|
+
volume;
|
|
19219
19262
|
vcAudioContext;
|
|
19220
19263
|
userMedia;
|
|
19221
19264
|
options;
|
|
@@ -19238,9 +19281,19 @@
|
|
|
19238
19281
|
const float32Array = e.inputBuffer.getChannelData(0);
|
|
19239
19282
|
cb(new esm(float32Array));
|
|
19240
19283
|
}
|
|
19284
|
+
disconnect() {
|
|
19285
|
+
if (this.scriptProcessor) {
|
|
19286
|
+
this.scriptProcessor.onaudioprocess = null;
|
|
19287
|
+
this.scriptProcessor.disconnect();
|
|
19288
|
+
}
|
|
19289
|
+
this.audioInput?.disconnect();
|
|
19290
|
+
this.volume?.disconnect();
|
|
19291
|
+
}
|
|
19241
19292
|
init(localMediaStream) {
|
|
19242
19293
|
this.options.logger.debug("AudioRecorder: init()");
|
|
19294
|
+
this.disconnect();
|
|
19243
19295
|
const volume = this.getAudioContext().createGain();
|
|
19296
|
+
this.volume = volume;
|
|
19244
19297
|
try {
|
|
19245
19298
|
this.audioInput = this.getAudioContext().createMediaStreamSource(localMediaStream);
|
|
19246
19299
|
} catch (exc) {
|
|
@@ -19272,10 +19325,13 @@
|
|
|
19272
19325
|
this.onAudioProcess(e, cb);
|
|
19273
19326
|
};
|
|
19274
19327
|
}
|
|
19328
|
+
async resume() {
|
|
19329
|
+
const context = this.getAudioContext();
|
|
19330
|
+
if ("running" !== context.state) await context.resume();
|
|
19331
|
+
}
|
|
19275
19332
|
stop() {
|
|
19276
19333
|
this.options.logger.debug("AudioRecorder: stop()");
|
|
19277
|
-
|
|
19278
|
-
if (this.audioInput) this.audioInput.disconnect();
|
|
19334
|
+
this.disconnect();
|
|
19279
19335
|
if (this.hasAudioContext()) this.getAudioContext().close().then(()=>{
|
|
19280
19336
|
this.options.logger.debug("AudioRecorder: audio context is closed");
|
|
19281
19337
|
this.vcAudioContext = void 0;
|
|
@@ -19320,6 +19376,8 @@
|
|
|
19320
19376
|
onLoadedMetaDataReached = false;
|
|
19321
19377
|
playingPromiseReached = false;
|
|
19322
19378
|
videoTrackLabel;
|
|
19379
|
+
clearInitializationListeners;
|
|
19380
|
+
initializationId = 0;
|
|
19323
19381
|
outputEvent = (e)=>{
|
|
19324
19382
|
this.logEvent(e.type, {
|
|
19325
19383
|
readyState: this.rawVisualUserMedia?.readyState
|
|
@@ -19361,6 +19419,16 @@
|
|
|
19361
19419
|
this.currentVisualStream = void 0;
|
|
19362
19420
|
}
|
|
19363
19421
|
}
|
|
19422
|
+
async restorePlayback() {
|
|
19423
|
+
if (!this.rawVisualUserMedia) throw new Error("No camera preview is available.");
|
|
19424
|
+
await this.rawVisualUserMedia.play();
|
|
19425
|
+
await this.audioRecorder?.resume();
|
|
19426
|
+
}
|
|
19427
|
+
hasLiveStream() {
|
|
19428
|
+
const videoReady = this.currentVisualStream?.getVideoTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
|
|
19429
|
+
const audioReady = !isAudioEnabled(this.options) || this.currentVisualStream?.getAudioTracks().some((track)=>"live" === track.readyState && track.enabled && !track.muted);
|
|
19430
|
+
return Boolean(this.currentVisualStream?.active && videoReady && audioReady);
|
|
19431
|
+
}
|
|
19364
19432
|
hasEnded() {
|
|
19365
19433
|
if (this.rawVisualUserMedia?.ended) return this.rawVisualUserMedia.ended;
|
|
19366
19434
|
return !this.currentVisualStream?.active;
|
|
@@ -19375,6 +19443,7 @@
|
|
|
19375
19443
|
}
|
|
19376
19444
|
unloadRemainingEventListeners() {
|
|
19377
19445
|
this.options.logger.debug("UserMedia: unloadRemainingEventListeners()");
|
|
19446
|
+
this.clearInitializationListeners?.();
|
|
19378
19447
|
mediaEvents.forEach((eventName)=>{
|
|
19379
19448
|
this.rawVisualUserMedia?.removeEventListener(eventName, this.outputEvent);
|
|
19380
19449
|
});
|
|
@@ -19384,11 +19453,13 @@
|
|
|
19384
19453
|
this.removeListener("SENDING_FIRST_FRAME");
|
|
19385
19454
|
this.audioRecorder?.record(audioCallback);
|
|
19386
19455
|
}
|
|
19387
|
-
init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback,
|
|
19456
|
+
init(localMediaStream, videoCallback, audioCallback, endedEarlyCallback, params) {
|
|
19457
|
+
this.clearInitializationListeners?.();
|
|
19388
19458
|
this.stop(localMediaStream, {
|
|
19389
19459
|
aboutToInitialize: true,
|
|
19390
|
-
switchingFacingMode
|
|
19460
|
+
preserveRecordingState: Boolean(params?.switchingFacingMode || params?.recovering)
|
|
19391
19461
|
});
|
|
19462
|
+
const initializationId = ++this.initializationId;
|
|
19392
19463
|
this.onPlayReached = false;
|
|
19393
19464
|
this.onLoadedMetaDataReached = false;
|
|
19394
19465
|
this.playingPromiseReached = false;
|
|
@@ -19401,18 +19472,25 @@
|
|
|
19401
19472
|
this.rawVisualUserMedia?.removeEventListener("play", onPlay);
|
|
19402
19473
|
this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
|
|
19403
19474
|
};
|
|
19475
|
+
this.clearInitializationListeners = ()=>{
|
|
19476
|
+
this.rawVisualUserMedia?.removeEventListener("play", onPlay);
|
|
19477
|
+
this.rawVisualUserMedia?.removeEventListener("loadedmetadata", onLoadedMetaData);
|
|
19478
|
+
this.clearInitializationListeners = void 0;
|
|
19479
|
+
};
|
|
19404
19480
|
const play = ()=>{
|
|
19405
19481
|
try {
|
|
19406
19482
|
this.rawVisualUserMedia?.load();
|
|
19407
19483
|
if (this.rawVisualUserMedia?.paused) {
|
|
19408
19484
|
this.options.logger.debug(`UserMedia: play(): media.readyState=${this.rawVisualUserMedia.readyState}, media.paused=${this.rawVisualUserMedia.paused}, media.ended=${this.rawVisualUserMedia.ended}, media.played=${string_pretty(this.rawVisualUserMedia.played)}`);
|
|
19409
19485
|
this.rawVisualUserMedia.play().then(()=>{
|
|
19486
|
+
if (initializationId !== this.initializationId) return;
|
|
19410
19487
|
if (!this.playingPromiseReached) {
|
|
19411
19488
|
this.options.logger.debug("UserMedia: play promise successful. Playing now.");
|
|
19412
19489
|
this.playingPromiseReached = true;
|
|
19413
19490
|
}
|
|
19414
19491
|
}).catch((exc)=>{
|
|
19415
|
-
if (
|
|
19492
|
+
if (initializationId !== this.initializationId) return;
|
|
19493
|
+
if (exc instanceof Error && !params?.recovering) this.options.logger.warn(`Caught pending user media promise exception: ${exc.toString()}`);
|
|
19416
19494
|
else {
|
|
19417
19495
|
unloadAllEventListeners();
|
|
19418
19496
|
endedEarlyCallback(error_createError({
|
|
@@ -19433,16 +19511,28 @@
|
|
|
19433
19511
|
this.options.logger.debug(`UserMedia: fireCallbacks(readyState=${readyState}, onPlayReached=${this.onPlayReached}, onLoadedMetaDataReached=${this.onLoadedMetaDataReached})`);
|
|
19434
19512
|
if (this.onPlayReached && this.onLoadedMetaDataReached) if (this.audioRecorder) try {
|
|
19435
19513
|
this.audioRecorder.init(localMediaStream);
|
|
19436
|
-
|
|
19437
|
-
this.
|
|
19438
|
-
|
|
19439
|
-
|
|
19514
|
+
const onAudioReady = ()=>{
|
|
19515
|
+
if (initializationId !== this.initializationId || this.currentVisualStream !== localMediaStream) return;
|
|
19516
|
+
if (this.recording) this.audioRecord(audioCallback);
|
|
19517
|
+
else this.on("SENDING_FIRST_FRAME", ()=>{
|
|
19518
|
+
this.audioRecord(audioCallback);
|
|
19519
|
+
});
|
|
19520
|
+
this.clearInitializationListeners?.();
|
|
19521
|
+
videoCallback();
|
|
19522
|
+
};
|
|
19523
|
+
if (params?.recovering) this.audioRecorder.resume().then(onAudioReady).catch(endedEarlyCallback);
|
|
19524
|
+
else onAudioReady();
|
|
19440
19525
|
} catch (exc) {
|
|
19441
19526
|
unloadAllEventListeners();
|
|
19442
|
-
this.stop(
|
|
19527
|
+
this.stop(void 0, {
|
|
19528
|
+
preserveRecordingState: Boolean(params?.recovering)
|
|
19529
|
+
});
|
|
19443
19530
|
endedEarlyCallback(exc);
|
|
19444
19531
|
}
|
|
19445
|
-
else
|
|
19532
|
+
else {
|
|
19533
|
+
this.clearInitializationListeners?.();
|
|
19534
|
+
videoCallback();
|
|
19535
|
+
}
|
|
19446
19536
|
};
|
|
19447
19537
|
const onPlay = ()=>{
|
|
19448
19538
|
try {
|
|
@@ -19512,9 +19602,8 @@
|
|
|
19512
19602
|
this.setVisualStream(localMediaStream);
|
|
19513
19603
|
play();
|
|
19514
19604
|
} catch (exc) {
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
});
|
|
19605
|
+
unloadAllEventListeners();
|
|
19606
|
+
endedEarlyCallback(exc);
|
|
19518
19607
|
}
|
|
19519
19608
|
}
|
|
19520
19609
|
getVideoTrackLabel() {
|
|
@@ -19527,6 +19616,8 @@
|
|
|
19527
19616
|
try {
|
|
19528
19617
|
let chosenStream = visualStream;
|
|
19529
19618
|
if (!params?.aboutToInitialize) {
|
|
19619
|
+
this.initializationId++;
|
|
19620
|
+
this.clearInitializationListeners?.();
|
|
19530
19621
|
chosenStream ??= this.currentVisualStream;
|
|
19531
19622
|
const tracks = chosenStream?.getTracks();
|
|
19532
19623
|
if (tracks) tracks.forEach((track)=>{
|
|
@@ -19536,7 +19627,7 @@
|
|
|
19536
19627
|
this.audioRecorder?.stop();
|
|
19537
19628
|
this.audioRecorder = void 0;
|
|
19538
19629
|
}
|
|
19539
|
-
if (!params?.
|
|
19630
|
+
if (!params?.preserveRecordingState) this.paused = this.recording = false;
|
|
19540
19631
|
} catch (exc) {
|
|
19541
19632
|
this.emit("ERROR", {
|
|
19542
19633
|
exc
|
|
@@ -19634,6 +19725,7 @@
|
|
|
19634
19725
|
originalAnimationFrameObject;
|
|
19635
19726
|
samplesCount = 0;
|
|
19636
19727
|
framesCount = 0;
|
|
19728
|
+
recordingAudioSampleRate;
|
|
19637
19729
|
recordingStats;
|
|
19638
19730
|
confirmedFrameNumber = 0;
|
|
19639
19731
|
confirmedSampleNumber = 0;
|
|
@@ -19652,6 +19744,10 @@
|
|
|
19652
19744
|
ctx;
|
|
19653
19745
|
userMediaLoaded;
|
|
19654
19746
|
userMediaLoading = false;
|
|
19747
|
+
mediaRequestId = 0;
|
|
19748
|
+
cameraRecovery;
|
|
19749
|
+
resolveCameraRecovery;
|
|
19750
|
+
resumeAfterCameraRecovery = false;
|
|
19655
19751
|
submitting = false;
|
|
19656
19752
|
unloaded;
|
|
19657
19753
|
stopTime;
|
|
@@ -19915,7 +20011,9 @@
|
|
|
19915
20011
|
this.pingInterval = void 0;
|
|
19916
20012
|
}
|
|
19917
20013
|
onAudioSample(audioSample) {
|
|
19918
|
-
if (this.recordingTransportFailed) return;
|
|
20014
|
+
if (this.recordingTransportFailed || this.cameraRecovery) return;
|
|
20015
|
+
const sampleRate = this.userMedia?.getAudioSampleRate();
|
|
20016
|
+
if (sampleRate && sampleRate > 0) this.recordingAudioSampleRate ??= sampleRate;
|
|
19919
20017
|
this.samplesCount++;
|
|
19920
20018
|
this.mediaTransmissionStarted = true;
|
|
19921
20019
|
const audioBuffer = audioSample.toBuffer();
|
|
@@ -19928,19 +20026,26 @@
|
|
|
19928
20026
|
try {
|
|
19929
20027
|
this.options.logger.debug(`Recorder: onUserMediaReady(${params ? string_pretty(params) : ""})`);
|
|
19930
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
|
+
}
|
|
19931
20033
|
this.userMediaLoading = this.blocking = this.unloaded = this.submitting = false;
|
|
19932
20034
|
this.userMediaLoaded = true;
|
|
19933
20035
|
this.clearUserMediaTimeout();
|
|
19934
|
-
if (!switchingFacingMode) this.loop = this.createLoop();
|
|
20036
|
+
if (!switchingFacingMode && !params?.recovering) this.loop = this.createLoop();
|
|
19935
20037
|
this.show();
|
|
19936
20038
|
if (params?.recordWhenReady) this.record();
|
|
19937
20039
|
this.emit("USER_MEDIA_READY", {
|
|
19938
20040
|
switchingFacingMode: params?.switchingFacingMode,
|
|
20041
|
+
recovering: params?.recovering,
|
|
19939
20042
|
paused: this.isPaused(),
|
|
19940
20043
|
recordWhenReady: params?.recordWhenReady
|
|
19941
20044
|
});
|
|
20045
|
+
if (params?.recovering) this.finishCameraRecovery(true);
|
|
19942
20046
|
} catch (exc) {
|
|
19943
|
-
this.
|
|
20047
|
+
if (params?.recovering) this.failCameraRecovery(exc);
|
|
20048
|
+
else this.emit("ERROR", {
|
|
19944
20049
|
exc
|
|
19945
20050
|
});
|
|
19946
20051
|
}
|
|
@@ -20311,6 +20416,7 @@
|
|
|
20311
20416
|
return this.blocking;
|
|
20312
20417
|
}
|
|
20313
20418
|
userMediaErrorCallback(err, usedConstraints) {
|
|
20419
|
+
if (this.cameraRecovery) return void this.failCameraRecovery(err);
|
|
20314
20420
|
if (this.unloaded) return void this.options.logger.debug(`Recorder: already unloaded. Not going to throw error ${string_pretty(err)}`);
|
|
20315
20421
|
this.userMediaLoading = false;
|
|
20316
20422
|
this.clearUserMediaTimeout();
|
|
@@ -20342,7 +20448,7 @@
|
|
|
20342
20448
|
});
|
|
20343
20449
|
}
|
|
20344
20450
|
}
|
|
20345
|
-
getUserMediaCallback(localStream, params) {
|
|
20451
|
+
getUserMediaCallback(localStream, params, requestId = this.mediaRequestId) {
|
|
20346
20452
|
if (!this.userMedia) {
|
|
20347
20453
|
localStream.getTracks().forEach((track)=>{
|
|
20348
20454
|
track.stop();
|
|
@@ -20355,20 +20461,20 @@
|
|
|
20355
20461
|
this.options.logger.debug(`Recorder: getUserMediaCallback(${params ? string_pretty(params) : ""})`);
|
|
20356
20462
|
if (this.showUserMedia()) try {
|
|
20357
20463
|
this.userMedia.init(localStream, ()=>{
|
|
20358
|
-
if (!this.unloaded) this.onUserMediaReady(params);
|
|
20464
|
+
if (!this.unloaded && requestId === this.mediaRequestId) this.onUserMediaReady(params);
|
|
20359
20465
|
}, this.onAudioSample.bind(this), (err)=>{
|
|
20360
|
-
if (!this.unloaded) this.userMediaErrorCallback(err, {
|
|
20466
|
+
if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(err, {
|
|
20361
20467
|
audio: isAudioEnabled(this.options)
|
|
20362
20468
|
});
|
|
20363
|
-
}, params
|
|
20469
|
+
}, params);
|
|
20364
20470
|
} catch (exc) {
|
|
20365
20471
|
localStream.getTracks().forEach((track)=>{
|
|
20366
20472
|
track.stop();
|
|
20367
20473
|
});
|
|
20368
20474
|
this.userMediaLoading = false;
|
|
20369
20475
|
this.clearUserMediaTimeout();
|
|
20370
|
-
this.
|
|
20371
|
-
|
|
20476
|
+
this.userMediaErrorCallback(exc, {
|
|
20477
|
+
audio: isAudioEnabled(this.options)
|
|
20372
20478
|
});
|
|
20373
20479
|
}
|
|
20374
20480
|
else {
|
|
@@ -20377,12 +20483,14 @@
|
|
|
20377
20483
|
});
|
|
20378
20484
|
this.userMediaLoading = false;
|
|
20379
20485
|
this.clearUserMediaTimeout();
|
|
20486
|
+
this.cancelCameraRecovery();
|
|
20380
20487
|
}
|
|
20381
20488
|
}
|
|
20382
20489
|
loadGenuineUserMedia(params) {
|
|
20383
20490
|
this.options.logger.debug(`Recorder: loadGenuineUserMedia(${params ? string_pretty(params) : ""})`);
|
|
20384
20491
|
this.emit("ASKING_WEBCAM_PERMISSION");
|
|
20385
20492
|
this.userMediaLoading = true;
|
|
20493
|
+
const requestId = ++this.mediaRequestId;
|
|
20386
20494
|
const constraints = {
|
|
20387
20495
|
video: {
|
|
20388
20496
|
frameRate: {
|
|
@@ -20396,18 +20504,18 @@
|
|
|
20396
20504
|
this.options.logger.debug(string_summarize("Recorder: available webcam constraints are:", navigator.mediaDevices.getSupportedConstraints()));
|
|
20397
20505
|
const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
|
|
20398
20506
|
streamPromise.then((localStream)=>{
|
|
20399
|
-
if (this.unloaded) return void localStream.getTracks().forEach((track)=>{
|
|
20507
|
+
if (this.unloaded || requestId !== this.mediaRequestId) return void localStream.getTracks().forEach((track)=>{
|
|
20400
20508
|
track.stop();
|
|
20401
20509
|
});
|
|
20402
|
-
this.getUserMediaCallback(localStream, params);
|
|
20510
|
+
this.getUserMediaCallback(localStream, params, requestId);
|
|
20403
20511
|
}).catch((reason)=>{
|
|
20404
|
-
if (!this.unloaded) this.userMediaErrorCallback(reason, constraints);
|
|
20512
|
+
if (!this.unloaded && requestId === this.mediaRequestId) this.userMediaErrorCallback(reason, constraints);
|
|
20405
20513
|
});
|
|
20406
20514
|
}
|
|
20407
20515
|
configureVideoConstraints(constraints, params) {
|
|
20408
20516
|
const videoConstraints = "object" == typeof constraints.video ? constraints.video : void 0;
|
|
20409
20517
|
if (!videoConstraints) return;
|
|
20410
|
-
if (params?.switchingFacingMode) videoConstraints.facingMode = params.switchingFacingMode;
|
|
20518
|
+
if (params?.switchingFacingMode || params?.recovering) videoConstraints.facingMode = params.switchingFacingMode || this.facingMode;
|
|
20411
20519
|
if (this.options.video.width) videoConstraints.width = {
|
|
20412
20520
|
ideal: this.options.video.width
|
|
20413
20521
|
};
|
|
@@ -20435,8 +20543,8 @@
|
|
|
20435
20543
|
if (!this.userMediaLoaded) {
|
|
20436
20544
|
this.userMediaLoading = false;
|
|
20437
20545
|
const err = util_getBrowser(this.options).getNoAccessIssue();
|
|
20438
|
-
this.
|
|
20439
|
-
|
|
20546
|
+
this.userMediaErrorCallback(err, {
|
|
20547
|
+
audio: isAudioEnabled(this.options)
|
|
20440
20548
|
});
|
|
20441
20549
|
}
|
|
20442
20550
|
}, this.options.timeouts.userMedia);
|
|
@@ -20446,6 +20554,7 @@
|
|
|
20446
20554
|
this.clearUserMediaTimeout();
|
|
20447
20555
|
this.options.logger.debug("Recorder: failed to load genuine user media");
|
|
20448
20556
|
this.userMediaLoading = false;
|
|
20557
|
+
if (this.cameraRecovery) return void this.failCameraRecovery(exc);
|
|
20449
20558
|
const errorListeners = this.getListeners("ERROR");
|
|
20450
20559
|
if (errorListeners?.length) this.emit("ERROR", {
|
|
20451
20560
|
exc
|
|
@@ -20569,9 +20678,10 @@
|
|
|
20569
20678
|
return this.recordingStats;
|
|
20570
20679
|
}
|
|
20571
20680
|
getAudioSampleRate() {
|
|
20572
|
-
return this.userMedia?.getAudioSampleRate();
|
|
20681
|
+
return this.recordingAudioSampleRate ?? this.userMedia?.getAudioSampleRate();
|
|
20573
20682
|
}
|
|
20574
20683
|
stop(params) {
|
|
20684
|
+
this.cancelCameraRecovery();
|
|
20575
20685
|
if (!this.userMedia) throw new Error("No user media defined, unable to stop");
|
|
20576
20686
|
this.options.logger.debug(`Recorder: stop(${params ? string_pretty(params) : ""})`);
|
|
20577
20687
|
this.recordingActive = false;
|
|
@@ -20597,7 +20707,7 @@
|
|
|
20597
20707
|
if (this.userMedia) this.recordingStats.videoTrackLabel = this.userMedia.getVideoTrackLabel();
|
|
20598
20708
|
if (isAudioEnabled(this.options) && this.userMedia) {
|
|
20599
20709
|
this.recordingStats.samplesCount = this.samplesCount;
|
|
20600
|
-
this.recordingStats.sampleRate = this.userMedia.getAudioSampleRate();
|
|
20710
|
+
this.recordingStats.sampleRate = this.recordingAudioSampleRate ?? this.userMedia.getAudioSampleRate();
|
|
20601
20711
|
}
|
|
20602
20712
|
this.writeCommand("stop", this.recordingStats, ()=>{
|
|
20603
20713
|
this.emit("STOPPED", {
|
|
@@ -20652,6 +20762,7 @@
|
|
|
20652
20762
|
this.built = this.connecting = this.connected = false;
|
|
20653
20763
|
}
|
|
20654
20764
|
reset() {
|
|
20765
|
+
this.cancelCameraRecovery();
|
|
20655
20766
|
if (!this.unloaded) {
|
|
20656
20767
|
this.options.logger.debug("Recorder: reset()");
|
|
20657
20768
|
this.emit("RESETTING");
|
|
@@ -20679,7 +20790,70 @@
|
|
|
20679
20790
|
isReady() {
|
|
20680
20791
|
return this.userMedia?.isReady();
|
|
20681
20792
|
}
|
|
20793
|
+
finishCameraRecovery(restored) {
|
|
20794
|
+
this.clearUserMediaTimeout();
|
|
20795
|
+
const resolve = this.resolveCameraRecovery;
|
|
20796
|
+
const resumeRequested = restored && this.resumeAfterCameraRecovery;
|
|
20797
|
+
this.cameraRecovery = void 0;
|
|
20798
|
+
this.resolveCameraRecovery = void 0;
|
|
20799
|
+
this.resumeAfterCameraRecovery = false;
|
|
20800
|
+
resolve?.(restored);
|
|
20801
|
+
if (resumeRequested && this.isPaused()) this.resume();
|
|
20802
|
+
}
|
|
20803
|
+
cancelCameraRecovery() {
|
|
20804
|
+
if (!this.cameraRecovery) return;
|
|
20805
|
+
this.mediaRequestId++;
|
|
20806
|
+
this.userMediaLoading = false;
|
|
20807
|
+
this.userMediaLoaded = false;
|
|
20808
|
+
this.clearUserMediaTimeout();
|
|
20809
|
+
this.userMedia?.stop(void 0, {
|
|
20810
|
+
preserveRecordingState: true
|
|
20811
|
+
});
|
|
20812
|
+
this.finishCameraRecovery(false);
|
|
20813
|
+
}
|
|
20814
|
+
failCameraRecovery(exc) {
|
|
20815
|
+
this.cancelCameraRecovery();
|
|
20816
|
+
const err = error_createError({
|
|
20817
|
+
message: "Camera recovery failed",
|
|
20818
|
+
explanation: "Your recording is still paused. Check camera permissions, then try Resume again or stop to keep the recorded part.",
|
|
20819
|
+
exc,
|
|
20820
|
+
options: this.options
|
|
20821
|
+
});
|
|
20822
|
+
this.emit("CAMERA_RECOVERY_FAILED", {
|
|
20823
|
+
err
|
|
20824
|
+
});
|
|
20825
|
+
}
|
|
20826
|
+
recoverCamera() {
|
|
20827
|
+
if (this.cameraRecovery) return this.cameraRecovery;
|
|
20828
|
+
if (!this.built || this.unloaded || !this.userMedia || this.userMediaLoading || this.submitting || this.key || void 0 !== this.stopTimeout || this.recordingTransportFailed) return Promise.resolve(false);
|
|
20829
|
+
const liveStream = this.userMedia.hasLiveStream();
|
|
20830
|
+
if (liveStream && this.isRecording()) return Promise.resolve(true);
|
|
20831
|
+
if (!liveStream && this.recordingActive && !this.isPaused()) this.pause();
|
|
20832
|
+
const recovery = new Promise((resolve)=>{
|
|
20833
|
+
this.resolveCameraRecovery = resolve;
|
|
20834
|
+
});
|
|
20835
|
+
this.cameraRecovery = recovery;
|
|
20836
|
+
this.emit("CAMERA_RECOVERING");
|
|
20837
|
+
if (liveStream) {
|
|
20838
|
+
this.userMediaTimeout = window.setTimeout(()=>{
|
|
20839
|
+
this.failCameraRecovery(new Error("Camera playback recovery timed out."));
|
|
20840
|
+
}, this.options.timeouts.userMedia);
|
|
20841
|
+
this.userMedia.restorePlayback().then(()=>{
|
|
20842
|
+
if (this.cameraRecovery === recovery) this.finishCameraRecovery(true);
|
|
20843
|
+
}).catch((exc)=>{
|
|
20844
|
+
if (this.cameraRecovery === recovery) this.failCameraRecovery(exc);
|
|
20845
|
+
});
|
|
20846
|
+
} else {
|
|
20847
|
+
this.userMediaLoaded = false;
|
|
20848
|
+
this.loadUserMedia({
|
|
20849
|
+
recovering: true
|
|
20850
|
+
});
|
|
20851
|
+
}
|
|
20852
|
+
return recovery;
|
|
20853
|
+
}
|
|
20682
20854
|
pause(params) {
|
|
20855
|
+
this.cancelCameraRecovery();
|
|
20856
|
+
this.resumeAfterDrain = false;
|
|
20683
20857
|
if (params) this.options.logger.debug(`pause() at frame ${this.framesCount} with ${string_pretty(params)}`);
|
|
20684
20858
|
else this.options.logger.debug(`pause() at frame ${this.framesCount}`);
|
|
20685
20859
|
this.userMedia?.pause();
|
|
@@ -20688,10 +20862,15 @@
|
|
|
20688
20862
|
this.sendPings();
|
|
20689
20863
|
}
|
|
20690
20864
|
resume() {
|
|
20865
|
+
if (this.cameraRecovery || !this.userMedia?.hasLiveStream()) {
|
|
20866
|
+
this.recoverCamera();
|
|
20867
|
+
this.resumeAfterCameraRecovery = Boolean(this.cameraRecovery);
|
|
20868
|
+
return;
|
|
20869
|
+
}
|
|
20691
20870
|
this.options.logger.debug(`Recorder: resume() with frame ${this.framesCount}`);
|
|
20692
20871
|
this.stopPings();
|
|
20693
20872
|
this.emit("RESUMING");
|
|
20694
|
-
this.userMedia
|
|
20873
|
+
this.userMedia.resume();
|
|
20695
20874
|
if (this.streamBackpressured) this.resumeAfterDrain = true;
|
|
20696
20875
|
else this.loop.start();
|
|
20697
20876
|
}
|
|
@@ -20746,7 +20925,7 @@
|
|
|
20746
20925
|
return newLoop;
|
|
20747
20926
|
}
|
|
20748
20927
|
record() {
|
|
20749
|
-
if (this.unloaded) return;
|
|
20928
|
+
if (this.unloaded || this.cameraRecovery) return;
|
|
20750
20929
|
if (!this.connected) {
|
|
20751
20930
|
this.options.logger.debug("Recorder: reconnecting before recording …");
|
|
20752
20931
|
this.initSocket(()=>{
|
|
@@ -20819,6 +20998,7 @@
|
|
|
20819
20998
|
this.recordingActive = true;
|
|
20820
20999
|
this.recordingTransportFailed = false;
|
|
20821
21000
|
this.mediaTransmissionStarted = false;
|
|
21001
|
+
this.recordingAudioSampleRate = void 0;
|
|
20822
21002
|
this.userMedia.record();
|
|
20823
21003
|
this.emit("RECORDING", {
|
|
20824
21004
|
framesCount: this.framesCount
|
|
@@ -20880,7 +21060,7 @@
|
|
|
20880
21060
|
}
|
|
20881
21061
|
}
|
|
20882
21062
|
switchFacingMode() {
|
|
20883
|
-
if (!util_getBrowser(this.options).isMobile()) return;
|
|
21063
|
+
if (this.cameraRecovery || !util_getBrowser(this.options).isMobile()) return;
|
|
20884
21064
|
if ("user" === this.facingMode) this.facingMode = "environment";
|
|
20885
21065
|
else if ("environment" === this.facingMode) this.facingMode = "user";
|
|
20886
21066
|
else this.options.logger.warn(`Recorder: unsupported facing mode ${string_pretty(this.facingMode)}`);
|
|
@@ -20891,12 +21071,14 @@
|
|
|
20891
21071
|
initEvents() {
|
|
20892
21072
|
this.options.logger.debug("Recorder: initEvents()");
|
|
20893
21073
|
this.on("SUBMITTING", ()=>{
|
|
21074
|
+
this.cancelCameraRecovery();
|
|
20894
21075
|
this.submitting = true;
|
|
20895
21076
|
});
|
|
20896
21077
|
this.on("SUBMITTED", ()=>{
|
|
20897
21078
|
this.submitting = false;
|
|
20898
21079
|
});
|
|
20899
21080
|
this.on("BLOCKING", ()=>{
|
|
21081
|
+
this.cancelCameraRecovery();
|
|
20900
21082
|
this.blocking = true;
|
|
20901
21083
|
this.clearUserMediaTimeout();
|
|
20902
21084
|
});
|
|
@@ -21324,6 +21506,7 @@
|
|
|
21324
21506
|
notifier;
|
|
21325
21507
|
visualsElement;
|
|
21326
21508
|
built = false;
|
|
21509
|
+
cameraRecoveryFailed = false;
|
|
21327
21510
|
constructor(container, options){
|
|
21328
21511
|
super("Visuals", options);
|
|
21329
21512
|
this.container = container;
|
|
@@ -21358,6 +21541,17 @@
|
|
|
21358
21541
|
this.endWaiting();
|
|
21359
21542
|
this.container.enableForm(false);
|
|
21360
21543
|
});
|
|
21544
|
+
this.on("CAMERA_RECOVERING", ()=>{
|
|
21545
|
+
this.clearCameraRecoveryNotice();
|
|
21546
|
+
});
|
|
21547
|
+
this.on("CAMERA_RECOVERY_FAILED", ({ err })=>{
|
|
21548
|
+
this.cameraRecoveryFailed = true;
|
|
21549
|
+
this.options.logger.warn(err);
|
|
21550
|
+
if (this.options.displayErrors) this.notifier.notify(err.message, err.explanation, {
|
|
21551
|
+
problem: true
|
|
21552
|
+
});
|
|
21553
|
+
this.endWaiting();
|
|
21554
|
+
});
|
|
21361
21555
|
this.on("PREVIEW", ()=>{
|
|
21362
21556
|
this.endWaiting();
|
|
21363
21557
|
});
|
|
@@ -21436,6 +21630,7 @@
|
|
|
21436
21630
|
child.remove();
|
|
21437
21631
|
}
|
|
21438
21632
|
reset() {
|
|
21633
|
+
this.clearCameraRecoveryNotice();
|
|
21439
21634
|
this.endWaiting();
|
|
21440
21635
|
this.recorder.reset();
|
|
21441
21636
|
}
|
|
@@ -21446,11 +21641,13 @@
|
|
|
21446
21641
|
this.container.endWaiting();
|
|
21447
21642
|
}
|
|
21448
21643
|
stop(params) {
|
|
21644
|
+
this.clearCameraRecoveryNotice();
|
|
21449
21645
|
this.recorder.stop(params);
|
|
21450
21646
|
this.recorderInsides.hidePause();
|
|
21451
21647
|
}
|
|
21452
21648
|
back(keepHidden = false, cb) {
|
|
21453
21649
|
this.options.logger.debug(`Visuals: back(keepHidden = ${keepHidden})`);
|
|
21650
|
+
this.cameraRecoveryFailed = false;
|
|
21454
21651
|
this.replay.hide();
|
|
21455
21652
|
this.notifier.hide();
|
|
21456
21653
|
if (keepHidden) {
|
|
@@ -21472,6 +21669,7 @@
|
|
|
21472
21669
|
if (!this.built) return;
|
|
21473
21670
|
const e = params?.e;
|
|
21474
21671
|
this.options.logger.debug(`Visuals: unload(${e ? string_pretty(e) : ""})`);
|
|
21672
|
+
this.cameraRecoveryFailed = false;
|
|
21475
21673
|
this.recorder.unload(params);
|
|
21476
21674
|
this.recorderInsides.unload();
|
|
21477
21675
|
this.replay.unload(params);
|
|
@@ -21485,13 +21683,31 @@
|
|
|
21485
21683
|
this.recorder.pause(params);
|
|
21486
21684
|
this.recorderInsides.showPause();
|
|
21487
21685
|
}
|
|
21686
|
+
clearCameraRecoveryNotice() {
|
|
21687
|
+
if (this.cameraRecoveryFailed) {
|
|
21688
|
+
this.notifier.hide();
|
|
21689
|
+
this.recorder.show();
|
|
21690
|
+
this.cameraRecoveryFailed = false;
|
|
21691
|
+
}
|
|
21692
|
+
}
|
|
21693
|
+
recoverCamera() {
|
|
21694
|
+
if (this.replay.isShown() || this.isNotifying() && !this.cameraRecoveryFailed) return Promise.resolve(false);
|
|
21695
|
+
this.clearCameraRecoveryNotice();
|
|
21696
|
+
return this.recorder.recoverCamera();
|
|
21697
|
+
}
|
|
21488
21698
|
resume() {
|
|
21489
|
-
|
|
21490
|
-
|
|
21491
|
-
|
|
21699
|
+
this.clearCameraRecoveryNotice();
|
|
21700
|
+
if (this.recorderInsides.isCountingDown()) {
|
|
21701
|
+
this.recorderInsides.resumeCountdown();
|
|
21702
|
+
this.recorderInsides.hidePause();
|
|
21703
|
+
} else {
|
|
21704
|
+
this.recorder.resume();
|
|
21705
|
+
if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
|
|
21706
|
+
}
|
|
21492
21707
|
}
|
|
21493
21708
|
pauseOrResume() {
|
|
21494
|
-
if (this.
|
|
21709
|
+
if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
|
|
21710
|
+
else if (this.isRecordable()) {
|
|
21495
21711
|
if (this.isRecording()) this.pause();
|
|
21496
21712
|
else if (this.recorder.isPaused()) this.resume();
|
|
21497
21713
|
else if (this.recorder.isReady()) this.recorder.record();
|
|
@@ -21734,10 +21950,10 @@
|
|
|
21734
21950
|
});
|
|
21735
21951
|
if (!playerOnly) this.stopListeningToVisibility = this.visibility.onChange((visible)=>{
|
|
21736
21952
|
if (this.built) if (visible) {
|
|
21737
|
-
if (isAutoPauseEnabled(this.options)
|
|
21953
|
+
if (isAutoPauseEnabled(this.options)) this.recoverCamera();
|
|
21738
21954
|
this.emit("VISIBLE");
|
|
21739
21955
|
} else {
|
|
21740
|
-
if (isAutoPauseEnabled(this.options)
|
|
21956
|
+
if (isAutoPauseEnabled(this.options)) this.suspend();
|
|
21741
21957
|
this.emit("INVISIBLE");
|
|
21742
21958
|
}
|
|
21743
21959
|
});
|
|
@@ -21925,6 +22141,13 @@
|
|
|
21925
22141
|
pause(params) {
|
|
21926
22142
|
this.visuals.pause(params);
|
|
21927
22143
|
}
|
|
22144
|
+
suspend() {
|
|
22145
|
+
if (this.built && (this.isCountingDown() || this.isRecording() || this.isPaused())) this.pause();
|
|
22146
|
+
}
|
|
22147
|
+
recoverCamera() {
|
|
22148
|
+
if (!this.built || this.hasError || this.submitted) return Promise.resolve(false);
|
|
22149
|
+
return this.visuals.recoverCamera();
|
|
22150
|
+
}
|
|
21928
22151
|
validate(event, force = false) {
|
|
21929
22152
|
let runValidation = true;
|
|
21930
22153
|
let valid = true;
|
|
@@ -22243,6 +22466,12 @@
|
|
|
22243
22466
|
record() {
|
|
22244
22467
|
this.container.record();
|
|
22245
22468
|
}
|
|
22469
|
+
pause() {
|
|
22470
|
+
this.container.suspend();
|
|
22471
|
+
}
|
|
22472
|
+
recoverCamera() {
|
|
22473
|
+
return this.container.recoverCamera();
|
|
22474
|
+
}
|
|
22246
22475
|
getLogLines() {
|
|
22247
22476
|
if (this.options.logger.getLines) return this.options.logger.getLines();
|
|
22248
22477
|
}
|