refactor: remove ensureInitialized
This commit is contained in:
parent
337cea6488
commit
ce308eaa8b
|
@ -95,11 +95,11 @@ class LocalRecordingInfoDialog extends Component<Props, State> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentWillMount()}.
|
||||
* Implements React's {@link Component#componentDidMount()}.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillMount() {
|
||||
componentDidMount() {
|
||||
this._timer = setInterval(
|
||||
() => {
|
||||
this.setState((_prevState, props) => {
|
||||
|
|
|
@ -398,8 +398,7 @@ class RecordingController {
|
|||
this._state = ControllerState.RECORDING;
|
||||
const delegate = this._adapters[this._currentSessionToken];
|
||||
|
||||
delegate.ensureInitialized()
|
||||
.then(() => delegate.start())
|
||||
delegate.start()
|
||||
.then(() => {
|
||||
logger.log('Local recording engaged.');
|
||||
const message = i18next.t('localRecording.messages.engaged');
|
||||
|
|
|
@ -4,8 +4,8 @@ import { downloadBlob, timestampString } from './Utils';
|
|||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* RecordingAdapter implementation that uses MediaRecorder
|
||||
* (default browser encoding with Opus codec)
|
||||
* Recording adapter that uses {@code MediaRecorder} (default browser encoding
|
||||
* with Opus codec).
|
||||
*/
|
||||
export class OggAdapter extends RecordingAdapter {
|
||||
|
||||
|
@ -15,35 +15,9 @@ export class OggAdapter extends RecordingAdapter {
|
|||
_mediaRecorder = null;
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#ensureInitialized()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* Initialization promise.
|
||||
*/
|
||||
ensureInitialized() {
|
||||
let p = null;
|
||||
|
||||
if (this._mediaRecorder === null) {
|
||||
p = new Promise((resolve, error) => {
|
||||
this._getAudioStream(0)
|
||||
.then(stream => {
|
||||
this._mediaRecorder = new MediaRecorder(stream);
|
||||
this._mediaRecorder.ondataavailable
|
||||
= e => this._saveMediaData(e.data);
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Error calling getUserMedia(): ${err}`);
|
||||
error();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
p = new Promise(resolve => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
_initPromise = null;
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#start()}.
|
||||
|
@ -51,10 +25,16 @@ export class OggAdapter extends RecordingAdapter {
|
|||
* @inheritdoc
|
||||
*/
|
||||
start() {
|
||||
return new Promise(resolve => {
|
||||
this._mediaRecorder.start();
|
||||
resolve();
|
||||
});
|
||||
if (!this._initPromise) {
|
||||
this._initPromise = this._initialize();
|
||||
}
|
||||
|
||||
return this._initPromise.then(() =>
|
||||
new Promise(resolve => {
|
||||
this._mediaRecorder.start();
|
||||
resolve();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,14 +62,39 @@ export class OggAdapter extends RecordingAdapter {
|
|||
|
||||
downloadBlob(audioURL, `recording${timestampString()}.ogg`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for encoded data.
|
||||
* Initialize the adapter.
|
||||
*
|
||||
* @private
|
||||
* @param {*} data - Encoded data.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_initialize() {
|
||||
if (this._mediaRecorder) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise((resolve, error) => {
|
||||
this._getAudioStream(0)
|
||||
.then(stream => {
|
||||
this._mediaRecorder = new MediaRecorder(stream);
|
||||
this._mediaRecorder.ondataavailable
|
||||
= e => this._saveMediaData(e.data);
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Error calling getUserMedia(): ${err}`);
|
||||
error();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for storing the encoded data.
|
||||
*
|
||||
* @private
|
||||
* @param {Blob} data - Encoded data.
|
||||
* @returns {void}
|
||||
*/
|
||||
_saveMediaData(data) {
|
||||
|
|
|
@ -5,15 +5,6 @@ import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
|||
*/
|
||||
export class RecordingAdapter {
|
||||
|
||||
/**
|
||||
* Initialize the recording backend.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
ensureInitialized() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recording.
|
||||
*
|
||||
|
|
|
@ -11,17 +11,43 @@ const WAV_SAMPLE_RATE = 44100;
|
|||
*/
|
||||
export class WavAdapter extends RecordingAdapter {
|
||||
|
||||
/**
|
||||
* {@code AudioContext} instance.
|
||||
*/
|
||||
_audioContext = null;
|
||||
|
||||
/**
|
||||
* {@code ScriptProcessorNode} instance, which receives the raw PCM bits.
|
||||
*/
|
||||
_audioProcessingNode = null;
|
||||
|
||||
/**
|
||||
* {@code MediaStreamAudioSourceNode} instance, which represents the mic.
|
||||
*/
|
||||
_audioSource = null;
|
||||
|
||||
/**
|
||||
* Length of the WAVE file, in units of {@code sizeof(Float32)}.
|
||||
*/
|
||||
_wavLength = 0;
|
||||
|
||||
/**
|
||||
* The {@code ArrayBuffer}s that stores the PCM bits.
|
||||
*/
|
||||
_wavBuffers = [];
|
||||
|
||||
/**
|
||||
* Whether or not the {@code WavAdapter} is in a ready state.
|
||||
*/
|
||||
_isInitialized = false;
|
||||
|
||||
/**
|
||||
* Initialization promise.
|
||||
*/
|
||||
_initPromise = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -29,50 +55,16 @@ export class WavAdapter extends RecordingAdapter {
|
|||
this._saveWavPCM = this._saveWavPCM.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#ensureInitialized()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
ensureInitialized() {
|
||||
if (this._isInitialized) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const p = new Promise((resolve, reject) => {
|
||||
this._getAudioStream(0)
|
||||
.then(stream => {
|
||||
this._audioContext = new AudioContext();
|
||||
this._audioSource
|
||||
= this._audioContext.createMediaStreamSource(stream);
|
||||
this._audioProcessingNode
|
||||
= this._audioContext.createScriptProcessor(4096, 1, 1);
|
||||
this._audioProcessingNode.onaudioprocess = e => {
|
||||
const channelLeft = e.inputBuffer.getChannelData(0);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/
|
||||
// Web/API/AudioBuffer/getChannelData
|
||||
// the returned value is an Float32Array
|
||||
this._saveWavPCM(channelLeft);
|
||||
};
|
||||
this._isInitialized = true;
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Error calling getUserMedia(): ${err}`);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#start()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
start() {
|
||||
if (!this._initPromise) {
|
||||
this._initPromise = this._initialize();
|
||||
}
|
||||
|
||||
return new Promise(
|
||||
(resolve, /* eslint-disable */_reject/* eslint-enable */) => {
|
||||
this._wavBuffers = [];
|
||||
|
@ -95,6 +87,10 @@ export class WavAdapter extends RecordingAdapter {
|
|||
this._audioProcessingNode.disconnect();
|
||||
this._audioSource.disconnect();
|
||||
this._data = this._exportMonoWAV(this._wavBuffers, this._wavLength);
|
||||
this._audioContext = null;
|
||||
this._audioProcessingNode = null;
|
||||
this._audioSource = null;
|
||||
this._isInitialized = false;
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -110,7 +106,6 @@ export class WavAdapter extends RecordingAdapter {
|
|||
|
||||
downloadBlob(audioURL, `recording${timestampString()}.wav`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,6 +162,44 @@ export class WavAdapter extends RecordingAdapter {
|
|||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the adapter.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_initialize() {
|
||||
if (this._isInitialized) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const p = new Promise((resolve, reject) => {
|
||||
this._getAudioStream(0)
|
||||
.then(stream => {
|
||||
this._audioContext = new AudioContext();
|
||||
this._audioSource
|
||||
= this._audioContext.createMediaStreamSource(stream);
|
||||
this._audioProcessingNode
|
||||
= this._audioContext.createScriptProcessor(4096, 1, 1);
|
||||
this._audioProcessingNode.onaudioprocess = e => {
|
||||
const channelLeft = e.inputBuffer.getChannelData(0);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/
|
||||
// Web/API/AudioBuffer/getChannelData
|
||||
// the returned value is an Float32Array
|
||||
this._saveWavPCM(channelLeft);
|
||||
};
|
||||
this._isInitialized = true;
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Error calling getUserMedia(): ${err}`);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function that saves the PCM bits.
|
||||
|
|
|
@ -21,35 +21,86 @@ export class FlacAdapter extends RecordingAdapter {
|
|||
_audioProcessingNode = null;
|
||||
_audioSource = null;
|
||||
|
||||
/**
|
||||
* Resolve function of the promise returned by {@code stop()}.
|
||||
* This is called after the WebWorker sends back {@code WORKER_BLOB_READY}.
|
||||
*/
|
||||
_stopPromiseResolver = null;
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#ensureInitialized}.
|
||||
* Initialization promise.
|
||||
*/
|
||||
_initPromise = null;
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#start()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
ensureInitialized() {
|
||||
start() {
|
||||
if (!this._initPromise) {
|
||||
this._initPromise = this._initialize();
|
||||
}
|
||||
|
||||
return this._initPromise.then(() => {
|
||||
this._audioSource.connect(this._audioProcessingNode);
|
||||
this._audioProcessingNode.connect(this._audioContext.destination);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#stop()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
stop() {
|
||||
if (!this._encoder) {
|
||||
logger.error('Attempting to stop but has nothing to stop.');
|
||||
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this._initPromise = null;
|
||||
this._audioProcessingNode.onaudioprocess = undefined;
|
||||
this._audioProcessingNode.disconnect();
|
||||
this._audioSource.disconnect();
|
||||
this._stopPromiseResolver = resolve;
|
||||
this._encoder.postMessage({
|
||||
command: MAIN_THREAD_FINISH
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#download()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
download() {
|
||||
if (this._data !== null) {
|
||||
const audioURL = window.URL.createObjectURL(this._data);
|
||||
|
||||
downloadBlob(audioURL, `recording${timestampString()}.flac`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the adapter.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_initialize() {
|
||||
if (this._encoder !== null) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const promiseInitWorker = new Promise((resolve, reject) => {
|
||||
// FIXME: workaround for different file names in development/
|
||||
// production environments.
|
||||
// We cannot import flacEncodeWorker as a webpack module,
|
||||
// because it is in a different bundle and should be lazy-loaded
|
||||
// only when flac recording is in use.
|
||||
try {
|
||||
// try load the minified version first
|
||||
this._encoder = new Worker('/libs/flacEncodeWorker.min.js');
|
||||
} catch (exception1) {
|
||||
// if failed, try unminified version
|
||||
try {
|
||||
this._encoder = new Worker('/libs/flacEncodeWorker.js');
|
||||
} catch (exception2) {
|
||||
logger.error('Failed to load flacEncodeWorker.');
|
||||
reject();
|
||||
}
|
||||
this._loadWebWorker();
|
||||
} catch (e) {
|
||||
reject();
|
||||
}
|
||||
|
||||
// set up listen for messages from the WebWorker
|
||||
|
@ -60,6 +111,8 @@ export class FlacAdapter extends RecordingAdapter {
|
|||
if (this._stopPromiseResolver !== null) {
|
||||
this._stopPromiseResolver();
|
||||
this._stopPromiseResolver = null;
|
||||
this._encoder.terminate();
|
||||
this._encoder = null;
|
||||
}
|
||||
} else if (e.data.command === DEBUG) {
|
||||
logger.log(e.data);
|
||||
|
@ -116,43 +169,27 @@ export class FlacAdapter extends RecordingAdapter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#start()}.
|
||||
* Loads the WebWorker.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
start() {
|
||||
this._audioSource.connect(this._audioProcessingNode);
|
||||
this._audioProcessingNode.connect(this._audioContext.destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#stop()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
stop() {
|
||||
return new Promise(resolve => {
|
||||
this._audioProcessingNode.onaudioprocess = undefined;
|
||||
this._audioProcessingNode.disconnect();
|
||||
this._audioSource.disconnect();
|
||||
this._stopPromiseResolver = resolve;
|
||||
this._encoder.postMessage({
|
||||
command: MAIN_THREAD_FINISH
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link RecordingAdapter#download()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
download() {
|
||||
if (this._data !== null) {
|
||||
const audioURL = window.URL.createObjectURL(this._data);
|
||||
|
||||
downloadBlob(audioURL, `recording${timestampString()}.flac`);
|
||||
_loadWebWorker() {
|
||||
// FIXME: workaround for different file names in development/
|
||||
// production environments.
|
||||
// We cannot import flacEncodeWorker as a webpack module,
|
||||
// because it is in a different bundle and should be lazy-loaded
|
||||
// only when flac recording is in use.
|
||||
try {
|
||||
// try load the minified version first
|
||||
this._encoder = new Worker('/libs/flacEncodeWorker.min.js');
|
||||
} catch (exception1) {
|
||||
// if failed, try unminified version
|
||||
try {
|
||||
this._encoder = new Worker('/libs/flacEncodeWorker.js');
|
||||
} catch (exception2) {
|
||||
throw new Error('Failed to load flacEncodeWorker.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ importScripts('/libs/libflac3-1.3.2.min.js');
|
|||
// So we disable the ESLint rule `new-cap` in this file.
|
||||
/* eslint-disable new-cap */
|
||||
|
||||
// Flow will complain about the number keys in `FLAC_ERRORS,
|
||||
// Flow will complain about the number keys in `FLAC_ERRORS`,
|
||||
// ESLint will complain about the `declare` statement.
|
||||
// As the current workaround, add an exception for eslint.
|
||||
/* eslint-disable flowtype/no-types-missing-file-annotation*/
|
||||
/* eslint-disable flowtype/no-types-missing-file-annotation */
|
||||
declare var Flac: Object;
|
||||
|
||||
const FLAC_ERRORS = {
|
||||
|
@ -151,7 +151,7 @@ class Encoder {
|
|||
_state = EncoderState.UNINTIALIZED;
|
||||
|
||||
/**
|
||||
* The ready-for-grab downloadable blob.
|
||||
* The ready-for-grab downloadable Blob.
|
||||
*/
|
||||
_data = null;
|
||||
|
||||
|
|
Loading…
Reference in New Issue