From 5f358868aade7689071565d881c9dd9050620d87 Mon Sep 17 00:00:00 2001 From: Becky Gilbert Date: Tue, 30 Jan 2024 09:34:19 -0800 Subject: [PATCH] Replace pipe with RecordRTC (#349) Replace pipe video recorder with in-house RecordRTC and store directly on S3. --- .eslintrc.js | 5 +- .../exp-lookit-calibration/component.js | 14 +- .../exp-lookit-change-detection/component.js | 37 +- .../exp-lookit-observation/component.js | 151 ++++-- .../exp-lookit-stimuli-preview/component.js | 15 +- .../exp-lookit-stop-recording/component.js | 32 +- .../exp-lookit-video-assent/component.js | 37 +- .../exp-lookit-video-consent/component.js | 68 +-- .../component.js | 2 +- app/components/exp-lookit-video/component.js | 42 +- .../exp-video-config-quality/component.js | 101 +++- .../exp-video-config-quality/template.hbs | 12 +- app/components/exp-video-config/component.js | 1 + app/index.html | 5 +- app/mixins/frame-player-route.js | 4 +- app/mixins/pause-unpause.js | 2 +- app/mixins/session-record.js | 46 +- app/mixins/video-record.js | 131 +++-- app/models/response.js | 3 +- app/services/mic-check-processor.js | 34 ++ app/services/s3.js | 106 ++++ app/services/video-recorder.js | 468 ++++++++++++++---- app/styles/app.scss | 8 + .../components/exp-lookit-observation.scss | 4 +- .../components/exp-video-config-quality.scss | 76 +++ config/environment.js | 6 + public/assets/mic-check-processor.js | 34 ++ translations/en-us.yaml | 2 +- 28 files changed, 1103 insertions(+), 343 deletions(-) create mode 100644 app/services/mic-check-processor.js create mode 100644 app/services/s3.js create mode 100644 public/assets/mic-check-processor.js diff --git a/.eslintrc.js b/.eslintrc.js index 3e7673ef..05f3f636 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,7 +3,7 @@ module.exports = { root: true, parserOptions: { - ecmaVersion: 6, + ecmaVersion: 8, sourceType: 'module', ecmaFeatures: { 'impliedStrict': true @@ -18,7 +18,8 @@ module.exports = { 'moment': false, 'jsPDF': false, 'Ajv': false, - 'PipeSDK': false + 'RecordRTCPromisesHandler': false + //'PipeSDK': false }, rules: { // 'warn', 'error', or 'off' 'no-console': 'off', // allow console.log diff --git a/app/components/exp-lookit-calibration/component.js b/app/components/exp-lookit-calibration/component.js index 0725b3e6..88159e8d 100644 --- a/app/components/exp-lookit-calibration/component.js +++ b/app/components/exp-lookit-calibration/component.js @@ -115,18 +115,22 @@ export default ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, ExpandAss // already been destroyed at that point. window.clearInterval(this.get('calTimer')); this.disablePausing(); - var _this = this; - if (this.get('doRecording')) { + if (this.get('doRecording') && (!this.get('stoppedRecording')) && !(this.get('stopping'))) { + this.set('stopping', true); + var _this = this; this.stopRecorder().then(() => { _this.set('stoppedRecording', true); + _this.destroyRecorder(); _this.send('next'); - return; }, () => { + _this.destroyRecorder(); _this.send('next'); - return; }); + } else if (this.get('doRecording') && this.get('stoppedRecording') && !(this.get('stopping'))) { + this.destroyRecorder(); + this.send('next'); } else { - _this.send('next'); + this.send('next'); } }, diff --git a/app/components/exp-lookit-change-detection/component.js b/app/components/exp-lookit-change-detection/component.js index a1eb099d..f0f77a26 100644 --- a/app/components/exp-lookit-change-detection/component.js +++ b/app/components/exp-lookit-change-detection/component.js @@ -360,15 +360,25 @@ export default ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, ExpandAss * @event stoppingCapture */ this.disablePausing(); - var _this = this; - this.stopRecorder().then(() => { - _this.set('stoppedRecording', true); - _this.send('next'); - return; - }, () => { - _this.send('next'); - return; - }); + if (this.get('doRecording')) { + if (!this.get('stoppedRecording') && (!this.get('stopping'))) { + var _this = this; + this.stopRecorder().then(() => { + _this.set('stoppedRecording', true); + _this.destroyRecorder(); + _this.send('next'); + }, () => { + _this.destroyRecorder(); + _this.send('next'); + }); + } else if (this.get('stoppedRecording') && (!this.get('stopping'))) { + this.destroyRecorder(); + this.send('next'); + } + // if the recorder is still stopping/uploading, then wait + } else { + this.send('next'); + } this._super(...arguments); } @@ -442,9 +452,9 @@ export default ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, ExpandAss // When triangles have been shown for time indicated: play end-audio if // present, or just move on. endTrial() { - this.stopRecorder(); + if (this.get('endAudioSources').length) { - $('#player-endaudio')[0].play(); + $('#player-endaudio')[0].play(); // onended calls finish } else { this.send('finish'); } @@ -607,8 +617,9 @@ export default ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, ExpandAss this.pause(); }); - if (this.get('doRecording')) { - let _this = this; + if (this.get('doRecording') && !this.get('stoppedRecording') && !this.get('stopping')) { + var _this = this; + this.set('stopping', true); return this.stopRecorder().finally(() => { _this.set('stoppedRecording', true); _this.destroyRecorder(); diff --git a/app/components/exp-lookit-observation/component.js b/app/components/exp-lookit-observation/component.js index c994304d..9c213a44 100644 --- a/app/components/exp-lookit-observation/component.js +++ b/app/components/exp-lookit-observation/component.js @@ -64,11 +64,15 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { okayToProceedTimer: null, timerStart: null, - hasStartedRecording: false, + // Flag to prevent recorder from continuing to automatically start when 'startRecordingAutomatically' + // We can't use the recorder's hasCreatedRecording property to track this because a new recorder is created after each recording ends. + hasMadeRecording: false, recordingStarted: false, toggling: false, hidden: false, recorderElement: '#recorder', + // Flag to track that user requests to move on via a next button click while upload is still in progress + proceedClicked: false, frameSchemaProperties: { /** @@ -209,10 +213,10 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { }, // Override to deal with whether or not recording is starting automatically - whenPossibleToRecord: observer('recorder.hasCamAccess', 'recorderReady', function() { - - if (this.get('recorder.hasCamAccess') && this.get('recorderReady')) { - if (this.get('startRecordingAutomatically')) { + whenPossibleToRecordObserver: observer('recorder.hasCamAccess', 'recorderReady', function() { + if (this.get('recorder.hasCamAccess') && this.get('recorderReady') && !(this.get('recorder.recording')) && !(this.get('starting'))) { + if (this.get('startRecordingAutomatically') && !(this.get('hasMadeRecording'))) { + this.set('starting', true); this.send('record'); } else { $('#recordButton').show(); @@ -222,7 +226,7 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { if (this.get('hideWebcam')) { $('#webcamToggleButton').html(this._translate('exp-lookit-observation.Show')); $('#hiddenWebcamMessage').show(); - $(this.get('recorderElement') + ' div').addClass('exp-lookit-observation-hidevideo'); + $('video').parent('div.lookit-video-recorder').parent().addClass('exp-lookit-observation-hidevideo'); this.set('hidden', true); /** * Webcam display hidden from participant @@ -233,9 +237,13 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { } } - }), + // Override to set startedRecording flag when starting automatically or via the record action + onRecordingStarted() { + this.set('recordingStarted', true); + }, + didInsertElement() { // initial state of all buttons/text $('#hiddenWebcamMessage').hide(); $('#recordButton').hide(); @@ -255,52 +263,90 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { $('#nextbutton').text(this.get('nextButtonText')); }, + onDestroyed() { + // reset any states here, since the recorder variable and states are not actually overwritten/reset + // by the recorder's destroy event + this.set('starting', false); + this.set('stopping', false); + this.set('recordingStarted', false); + this.set('stoppedRecording', false); + }, + actions: { record() { - this.startRecorder(); // TODO: use then - var _this = this; - if (this.get('recordSegmentLength')) { // no timer if 0 - window.clearTimeout(this.get('recordingTimer')); // as a precaution in case still running - window.clearInterval(this.get('progressTimer')); - window.clearTimeout(this.get('okayToProceedTimer')); - this.set('timerStart', new Date().getTime()); - this.set('recordingTimer', window.setTimeout(function() { - /** - * Video recording automatically paused upon reaching time limit - * - * @event recorderTimeout - */ - _this.send('setTimeEvent', 'recorderTimeout'); - _this.send('pause'); - }, _this.get('recordSegmentLength') * 1000)); - this.set('progressTimer', window.setInterval(function() { - var prctDone = (_this.get('recordSegmentLength') * 1000 - (new Date().getTime() - _this.get('timerStart'))) / (_this.get('recordSegmentLength') * 10); - $('.progress-bar').css('width', prctDone + '%'); - }, 100)); - if (this.get('recordingRequired')) { - this.set('okayToProceedTimer', window.setTimeout(function() { - _this.enableNext(); - }, 1000 * this.get('recordingRequired'))); + this.startRecorder().then(() => { + _this.set('starting', false); + + // set up timer and progress bar if necessary + if (this.get('recordSegmentLength')) { // no timer if 0 + window.clearTimeout(this.get('recordingTimer')); // as a precaution in case still running + window.clearInterval(this.get('progressTimer')); + window.clearTimeout(this.get('okayToProceedTimer')); + this.set('timerStart', new Date().getTime()); + this.set('recordingTimer', window.setTimeout(function() { + /** + * Video recording automatically paused upon reaching time limit + * @event recorderTimeout + */ + _this.send('setTimeEvent', 'recorderTimeout'); + _this.send('pause'); + }, _this.get('recordSegmentLength') * 1000)); + this.set('progressTimer', window.setInterval(function() { + var prctDone = (_this.get('recordSegmentLength') * 1000 - (new Date().getTime() - _this.get('timerStart'))) / (_this.get('recordSegmentLength') * 10); + $('.progress-bar').css('width', prctDone + '%'); + }, 100)); + if (this.get('recordingRequired')) { + this.set('okayToProceedTimer', window.setTimeout(function() { + _this.enableNext(); + }, 1000 * this.get('recordingRequired'))); + } } - } - $('#pauseButton').show(); - $('#recordButton').hide(); - $('#recordingIndicator').show(); - $('#recordingText').text(`${this._translate('exp-lookit-observation.Recording')}...`); - $('#recordButtonText').text(this._translate('exp-lookit-observation.Record')); + + $('#pauseButton').show(); + $('#recordButton').hide(); + $('#recordingIndicator').show(); + $('#recordingText').text(`${this._translate('exp-lookit-observation.Recording')}...`); + $('#recordButtonText').text(this._translate('exp-lookit-observation.Record')); + + }); + }, proceed() { // make sure 'next' fires while still on this frame window.clearTimeout(this.get('recordingTimer')); // no need for current timer window.clearTimeout(this.get('okayToProceedTimer')); window.clearInterval(this.get('progressTimer')); - this.stopRecorder().finally(() => { - this.destroyRecorder(); + let rec = this.get('recorder'); + this.set('proceedClicked', true); + if (rec && !(rec._recorderIsDestroyed)) { + var _this = this; + rec.get('recorder').getState().then((state) => { + if (state == 'recording') { + _this.set('stopping', true); + _this.stopRecorder().finally(() => { + _this.set('stoppedRecording', true); + _this.destroyRecorder(); + _this.onDestroyed(); + _this.send('next'); + }); + } else if ((_this.recordingStarted && _this.stoppedRecording && rec.isUploaded) || !(_this.recordingStarted)) { + // recorder is paused/stopped/inactive, and either it never started recording or has but upload has finished + _this.destroyRecorder(); + _this.onDestroyed(); + _this.send('next'); + } + // Do not do anything if is currently stopping/uploading - the destroy and next actions will be handled via the existing stop promise and proceedClicked flag + }, () => { + _this.send('next'); + }); + } else { + // recorder does not exist or exists but has been destroyed this.send('next'); - }); + } }, + pause() { var _this = this; $('#recordingText').text(`${this._translate('exp-lookit-observation.stopping-and-uploading')}...`); @@ -310,13 +356,34 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { window.clearInterval(_this.get('progressTimer')); $('.progress-bar').css('width', '100%'); $('#recordingIndicator').hide(); - this.stopRecorder().finally(() => { + this.set('stopping', true); + this.stopRecorder().then(() => { + _this.set('hasMadeRecording', true); + _this.set('stoppedRecording', true); + $('#recordButton').show(); + $('#recordingText').text(_this._translate('exp-lookit-observation.Paused')); + _this.destroyRecorder(); + _this.onDestroyed(); + if (_this.get('proceedClicked')) { + _this.send('proceed'); + } else { + _this.setupRecorder(_this.$(_this.get('recorderElement'))); + } + }, () => { + _this.set('hasMadeRecording', true); + _this.set('stoppedRecording', true); $('#recordButton').show(); $('#recordingText').text(_this._translate('exp-lookit-observation.Paused')); _this.destroyRecorder(); - _this.setupRecorder(_this.$(_this.get('recorderElement'))); + _this.onDestroyed(); + if (_this.get('proceedClicked')) { + _this.send('proceed'); + } else { + _this.setupRecorder(_this.$(_this.get('recorderElement'))); + } }); }, + toggleWebcamButton() { if (!this.toggling) { this.set('toggling', true); diff --git a/app/components/exp-lookit-stimuli-preview/component.js b/app/components/exp-lookit-stimuli-preview/component.js index 84ae34d9..6600b57b 100644 --- a/app/components/exp-lookit-stimuli-preview/component.js +++ b/app/components/exp-lookit-stimuli-preview/component.js @@ -17,7 +17,6 @@ export default ExpFrameBaseComponent.extend(VideoRecord, ExpandAssets, { videoIndex: 0, - recordingStopped: false, recordingStarted: false, noNext: computed('videoIndex', function() { @@ -192,17 +191,23 @@ export default ExpFrameBaseComponent.extend(VideoRecord, ExpandAssets, { this.send('setTimeEvent', 'previousStimulus'); this.set('videoIndex', this.get('videoIndex') - 1); }, - finish() { + finish() { // continue button press if (this.get('doRecording')) { - if (!this.get('recordingStopped')) { - this.set('recordingStopped', true); + if (!this.get('stoppedRecording') && !this.get('stopping')) { + this.set('stopping', true); var _this = this; this.stopRecorder().then(() => { + _this.set('stoppedRecording', true); + _this.destroyRecorder(); _this.send('next'); }, () => { + _this.destroyRecorder(); _this.send('next'); }); - } else { + } else if (this.get('stoppedRecording') && !this.get('stopping')) { + // if recorder has finished stopping/uploading then we can destroy it and move on + // (if recorder is already stopping, then do nothing - need to wait for it to finish) + this.destroyRecorder(); this.send('next'); } } else { diff --git a/app/components/exp-lookit-stop-recording/component.js b/app/components/exp-lookit-stop-recording/component.js index 17b8ae7f..49e62f30 100644 --- a/app/components/exp-lookit-stop-recording/component.js +++ b/app/components/exp-lookit-stop-recording/component.js @@ -129,38 +129,40 @@ export default ExpFrameBaseComponent.extend(ExpandAssets, { }); this.set('progressTimer', window.setInterval(function() { - let msg = $('.pipeMsgOverlay').html(); - let match = msg.match(/\d*%/); - if (match) { - $('#progress').html(`Uploading video... ${match[0]}`); - $('.progress-bar').css('width', match); - _this.set('hasStartedUpload', true); - } else if (msg) { - $('#progress').html(msg); - } else { - $('#progress').html('Uploading video...') + $('#progress').html('Uploading video...     '); + if (!_this.get('_recording') && _this.get('sessionRecorder').s3.hasStartedCompletingUpload) { + const percComplete = _this.get('sessionRecorder').s3.percentUploadComplete; + if (percComplete > 0) { + _this.set('hasStartedUpload', true); + window.clearInterval(_this.get('allowProceedTimer')); + } + let progressPercStr = percComplete.toString()+'%'; + $('#progress').html(`Uploading video... ${progressPercStr}`); + $('.progress-bar').css('width', progressPercStr); } }, 100)); this.set('allowProceedTimer', window.setTimeout(function() { if (!_this.get('hasStartedUpload')) { /** - * If no progress update about upload is available within 10s, and - * frame proceeds automatically. Otherwise if the upload has started - * (e.g. we know it is 10% done) it will continue waiting. + * Note: this timer waits a long time (5 minutes) before checking if at least one part of the multi-part * upload has completed, and if not, allowing the participant to proceed. + * This is a longer wait time (vs the old Pipe system) because we don't have as fine-grained upload + * progress info with RecordRTC/S3 as we used to have with Pipe. We might want to reconsider the way + * this timer works and its duration in the future, if we get reports about participants getting 'stuck' + * while waiting for uploads. * * @event warningUploadTimeoutError */ _this.send('setTimeEvent', 'warningUploadTimeoutError'); _this.send('next'); } - }, 5000)); + }, 300000)); }, willDestroyElement() { window.clearInterval(this.get('progressTimer')); - window.clearInterval(this.get('allowProceedTimer')); + window.clearTimeout(this.get('allowProceedTimer')); this._super(...arguments); } diff --git a/app/components/exp-lookit-video-assent/component.js b/app/components/exp-lookit-video-assent/component.js index 78437834..3e34a469 100644 --- a/app/components/exp-lookit-video-assent/component.js +++ b/app/components/exp-lookit-video-assent/component.js @@ -32,6 +32,9 @@ export default ExpFrameBaseComponent.extend(VideoRecord, ExpandAssets, { disableRecord: Em.computed('recorder.recording', 'recorder.hasCamAccess', function () { return !this.get('recorder.hasCamAccess') || this.get('recorder.recording'); }), + // keep track of whether a recorder has already started, + // in case of recording last page and moving forward/backward through pages, + // and to prevent race conditions when recorder is still installing startedRecording: false, pageIndex: null, @@ -132,7 +135,8 @@ export default ExpFrameBaseComponent.extend(VideoRecord, ExpandAssets, { }, updatePage() { - if (this.get('recordLastPage') && !this.get('recordWholeProcedure') && (this.get('pageIndex') === this.get('pages').length - 1)) { + if (this.get('recordLastPage') && !this.get('recordWholeProcedure') && !(this.get('startedRecording')) && (this.get('pageIndex') === this.get('pages').length - 1)) { + this.set('startedRecording', true); this.startRecorder(); } if (this.get('pageHasAudio')) { @@ -195,15 +199,30 @@ export default ExpFrameBaseComponent.extend(VideoRecord, ExpandAssets, { this.send('setTimeEvent', 'assentQuestionSubmit', {childResponse: this.get('childResponse')}); - var _this = this; - if (_this.get('childResponse') === 'Yes') { - this.stopRecorder().then(() => { - _this.send('next'); - }, () => { - _this.send('next'); - }); + if (this.get('childResponse') === 'Yes') { + if (this.get('sessionRecorder') && this.get('sessionRecordingInProgress')) { + this.send('next'); + } else { + if (!this.get('stoppedRecording') && (!this.get('stopping'))) { + this.set('stopping', true); + var _this = this; + this.stopRecorder().then(() => { + _this.set('stoppedRecording', true); + _this.destroyRecorder(); + _this.send('next'); + }, () => { + _this.destroyRecorder(); + _this.send('next'); + }); + } else if (this.get('stoppedRecording') && (!this.get('stopping'))) { + // if recorder has finished stopping/uploading then we can destroy it and move on + // (if recorder is already stopping but hasn't finished, then do nothing - need to wait for it to finish) + this.destroyRecorder(); + this.send('next'); + } + } } else { - _this.send('exit'); + this.send('exit'); } }, diff --git a/app/components/exp-lookit-video-consent/component.js b/app/components/exp-lookit-video-consent/component.js index 8d094aa9..ece04c4a 100644 --- a/app/components/exp-lookit-video-consent/component.js +++ b/app/components/exp-lookit-video-consent/component.js @@ -51,25 +51,27 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { startRecorderAndUpdateDisplay() { this.set('startedRecording', true); // keep track of if ANY recorder has been set up yet let _this = this; - this.startRecorder().then(() => { - // Require at least 2 s recording - setTimeout(function() { - $('#stopbutton').prop('disabled', false); - }, 2000); - $('#recordingIndicator').show(); - $('#recordingText').text(_this._translate('exp-lookit-video-consent.Recording')); - }, () => { - $('#recordingText').text(_this._translate('exp-lookit-video-consent.Error-starting-recorder')); - $('#recordbutton').prop('disabled', false); - }); + this.startRecorder() + .then(() => { + // Require at least 2 s recording + setTimeout(function() { + $('#stopbutton').prop('disabled', false); + }, 2000); + $('#recordingIndicator').show(); + $('#recordingText').text(_this._translate('exp-lookit-video-consent.Recording')); + }) + .catch((err) => { + $('#recordingText').text(_this._translate('exp-lookit-video-consent.Error-starting-recorder')); + $('#recordbutton').prop('disabled', false); + console.error(`Error starting recorder: ${err.name}: ${err.message}`); + console.trace(); + }); }, actions: { record() { - $('#recordingStatus').show(); $('#recordingText').text(`${this._translate('exp-lookit-video-consent.Starting-recorder')}...`); - $('[id^=pipeMenu]').hide(); $('#recordbutton').prop('disabled', true); $('#playbutton').prop('disabled', true); this.set('showWarning', false); @@ -77,18 +79,22 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { this.set('hasMadeVideo', false); if (this.get('startedRecording')) { - if (this.get('recorder') && this.get('recorder').get('recorder')) { - this.get('recorder').get('recorder').pause(); + if (this.get('recorder')) { + this.get('recorder').pause(); } this.destroyRecorder(); // Need to destroy between recordings or else the same video ID is sent as payload. // Don't destroy after stopRecorder call because then can't replay. var _this = this; - this.setupRecorder(_this.$(_this.get('recorderElement'))).then(() => { - _this.startRecorderAndUpdateDisplay(); - }, () => { - $('#recordingText').text(_this._translate('exp-lookit-video-consent.Error-starting-recorder')); - $('#recordbutton').prop('disabled', false); - }); + this.setupRecorder(_this.$(_this.get('recorderElement'))) + .then(() => { + _this.startRecorderAndUpdateDisplay(); + }) + .catch((err) => { + $('#recordingText').text(_this._translate('exp-lookit-video-consent.Error-starting-recorder')); + $('#recordbutton').prop('disabled', false); + console.error(`Error restarting recorder: ${err.name}: ${err.message}`); + console.trace(); + }); } else { this.startRecorderAndUpdateDisplay(); // First time - can use current recorder } @@ -100,24 +106,23 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { $('#stopbutton').prop('disabled', true); var _this = this; - this.stopRecorder().finally(() => { - _this.set('stoppedRecording', true); - _this.set('hasMadeVideo', true); - $('#recordingText').text(_this._translate('exp-lookit-video-consent.Not-recording')); - $('#playbutton').prop('disabled', false); - $('#recordbutton').prop('disabled', false); - }); + this.stopRecorder() + .finally(() => { + _this.set('stoppedRecording', true); + _this.set('hasMadeVideo', true); + $('#recordingText').text(_this._translate('exp-lookit-video-consent.Not-recording')); + $('#playbutton').prop('disabled', false); + $('#recordbutton').prop('disabled', false); + }); }, playvideo() { $('#recordingText').text(''); $('#recordingStatus').hide(); - this.get('recorder').get('recorder').playVideo(); - $('[id^=pipeMenu]').show(); + this.setUpPlayback(); this.set('hasCheckedVideo', true); }, finish() { - if (!this.get('hasMadeVideo') || !this.get('hasCheckedVideo')) { this.set('showWarning', true); } else { @@ -636,7 +641,6 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { this.set('consentFormText', $('#consent-form-text').text()); $('#recordingIndicator').hide(); $('#recordingText').text(this._translate('exp-lookit-video-consent.Not-recording-yet')); - $('[id^=pipeMenu]').hide(); $('#recordbutton').prop('disabled', false); $('#stopbutton').prop('disabled', true); $('#playbutton').prop('disabled', true); diff --git a/app/components/exp-lookit-video-infant-control/component.js b/app/components/exp-lookit-video-infant-control/component.js index 480dee6d..4e559686 100644 --- a/app/components/exp-lookit-video-infant-control/component.js +++ b/app/components/exp-lookit-video-infant-control/component.js @@ -35,7 +35,7 @@ export default ExpLookitVideo.extend(InfantControlledTiming, { actions: { videoStarted() { - if (this.get('testVideoTimesPlayed') === 0) { + if ((this.get('testVideoTimesPlayed') === 0) && !this.get('_finishing')) { this.startParentControl(); } this._super(...arguments); diff --git a/app/components/exp-lookit-video/component.js b/app/components/exp-lookit-video/component.js index bf2c7e61..6344ff1f 100644 --- a/app/components/exp-lookit-video/component.js +++ b/app/components/exp-lookit-video/component.js @@ -48,7 +48,6 @@ let ExpLookitVideo = ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, Exp skip: false, hasParentText: true, - /** comment some text */ maximizeVideoArea: false, _finishing: false, @@ -202,7 +201,7 @@ let ExpLookitVideo = ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, Exp * * @event videoStarted */ - if (this.get('isDestroying') || this.get('isDestroyed')) { + if (this.get('isDestroying') || this.get('isDestroyed') || this.get('_finishing')) { return; } @@ -229,7 +228,7 @@ let ExpLookitVideo = ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, Exp }, videoStopped() { - if (this.get('isDestroying') || this.get('isDestroyed')) { + if (this.get('isDestroying') || this.get('isDestroyed') || (this.get('_finishing'))) { return; } this.set('testVideoTimesPlayed', this.get('testVideoTimesPlayed') + 1); @@ -281,10 +280,6 @@ let ExpLookitVideo = ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, Exp // to call next AFTER recording is stopped and we don't want this to have // already been destroyed at that point. - // Pause audio/video so we don't trigger started/stopped handlers while destroying - $('audio, video').each(function() { - this.pause(); - }); /** * When trial is complete and begins cleanup (may then wait for video upload) * @@ -296,19 +291,31 @@ let ExpLookitVideo = ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, Exp this.set('testVideoTimesPlayed', 0); this.set('testAudioTimesPlayed', 0); this.set('satisfiedDuration', false); - var _this = this; if (!this.get('_finishing')) { this.set('_finishing', true); + // Pause audio/video so we don't trigger started/stopped handlers while destroying + $('audio, video').each(function() { + this.pause(); + }); if (this.get('doRecording')) { - this.set('doingTest', false); - this.stopRecorder().then(() => { - _this.set('stoppedRecording', true); - _this.send('next'); - }, () => { - _this.send('next'); - }); + if (!this.get('stopping') && !this.get('stoppedRecording')) { + this.set('stopping', true); + this.set('doingTest', false); + var _this = this; + this.stopRecorder().then(() => { + _this.set('stoppedRecording', true); + _this.destroyRecorder(); + _this.send('next'); + }, () => { + _this.destroyRecorder(); + _this.send('next'); + }); + } else if (!this.get('stopping') && this.get('stoppedRecording')) { + this.destroyRecorder(); + this.send('next'); + } } else { - _this.send('next'); + this.send('next'); } } } @@ -356,8 +363,9 @@ let ExpLookitVideo = ExpFrameBaseComponent.extend(VideoRecord, PauseUnpause, Exp this.set('satisfiedDuration', false); $('.exp-lookit-video').hide(); $('#nextbutton').prop('disabled', true); // disable Next while paused - if (this.get('doRecording')) { + if (this.get('doRecording') && (!this.get('stoppedRecording')) && (!this.get('stopping'))) { let _this = this; + this.set('stopping', true); return this.stopRecorder().finally(() => { _this.set('stoppedRecording', true); _this.destroyRecorder(); diff --git a/app/components/exp-video-config-quality/component.js b/app/components/exp-video-config-quality/component.js index f5294e5c..f77a1544 100644 --- a/app/components/exp-video-config-quality/component.js +++ b/app/components/exp-video-config-quality/component.js @@ -320,12 +320,17 @@ export default ExpFrameBaseComponent.extend(VideoRecord, { if (!this.get('showRecordMenu')) { this.set('requireTestVideo', false); + $('#recordingStatus').hide(); + $('#recordingIndicator').hide(); $('.exp-video-config-quality').append($('