Просмотр исходного кода

refactor: remove ensureInitialized

master
Radium Zheng 7 лет назад
Родитель
Сommit
ce308eaa8b

+ 2
- 2
react/features/local-recording/components/LocalRecordingInfoDialog.js Просмотреть файл

95
     }
95
     }
96
 
96
 
97
     /**
97
     /**
98
-     * Implements React's {@link Component#componentWillMount()}.
98
+     * Implements React's {@link Component#componentDidMount()}.
99
      *
99
      *
100
      * @returns {void}
100
      * @returns {void}
101
      */
101
      */
102
-    componentWillMount() {
102
+    componentDidMount() {
103
         this._timer = setInterval(
103
         this._timer = setInterval(
104
             () => {
104
             () => {
105
                 this.setState((_prevState, props) => {
105
                 this.setState((_prevState, props) => {

+ 1
- 2
react/features/local-recording/controller/RecordingController.js Просмотреть файл

398
             this._state = ControllerState.RECORDING;
398
             this._state = ControllerState.RECORDING;
399
             const delegate = this._adapters[this._currentSessionToken];
399
             const delegate = this._adapters[this._currentSessionToken];
400
 
400
 
401
-            delegate.ensureInitialized()
402
-            .then(() => delegate.start())
401
+            delegate.start()
403
             .then(() => {
402
             .then(() => {
404
                 logger.log('Local recording engaged.');
403
                 logger.log('Local recording engaged.');
405
                 const message = i18next.t('localRecording.messages.engaged');
404
                 const message = i18next.t('localRecording.messages.engaged');

+ 41
- 36
react/features/local-recording/recording/OggAdapter.js Просмотреть файл

4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
5
 
5
 
6
 /**
6
 /**
7
- * RecordingAdapter implementation that uses MediaRecorder
8
- * (default browser encoding with Opus codec)
7
+ * Recording adapter that uses {@code MediaRecorder} (default browser encoding
8
+ * with Opus codec).
9
  */
9
  */
10
 export class OggAdapter extends RecordingAdapter {
10
 export class OggAdapter extends RecordingAdapter {
11
 
11
 
15
     _mediaRecorder = null;
15
     _mediaRecorder = null;
16
 
16
 
17
     /**
17
     /**
18
-     * Implements {@link RecordingAdapter#ensureInitialized()}.
19
-     *
20
-     * @inheritdoc
18
+     * Initialization promise.
21
      */
19
      */
22
-    ensureInitialized() {
23
-        let p = null;
24
-
25
-        if (this._mediaRecorder === null) {
26
-            p = new Promise((resolve, error) => {
27
-                this._getAudioStream(0)
28
-                .then(stream => {
29
-                    this._mediaRecorder = new MediaRecorder(stream);
30
-                    this._mediaRecorder.ondataavailable
31
-                        = e => this._saveMediaData(e.data);
32
-                    resolve();
33
-                })
34
-                .catch(err => {
35
-                    logger.error(`Error calling getUserMedia(): ${err}`);
36
-                    error();
37
-                });
38
-            });
39
-        } else {
40
-            p = new Promise(resolve => {
41
-                resolve();
42
-            });
43
-        }
44
-
45
-        return p;
46
-    }
20
+    _initPromise = null;
47
 
21
 
48
     /**
22
     /**
49
      * Implements {@link RecordingAdapter#start()}.
23
      * Implements {@link RecordingAdapter#start()}.
51
      * @inheritdoc
25
      * @inheritdoc
52
      */
26
      */
53
     start() {
27
     start() {
54
-        return new Promise(resolve => {
55
-            this._mediaRecorder.start();
56
-            resolve();
57
-        });
28
+        if (!this._initPromise) {
29
+            this._initPromise = this._initialize();
30
+        }
31
+
32
+        return this._initPromise.then(() =>
33
+            new Promise(resolve => {
34
+                this._mediaRecorder.start();
35
+                resolve();
36
+            })
37
+        );
58
     }
38
     }
59
 
39
 
60
     /**
40
     /**
82
 
62
 
83
             downloadBlob(audioURL, `recording${timestampString()}.ogg`);
63
             downloadBlob(audioURL, `recording${timestampString()}.ogg`);
84
         }
64
         }
65
+    }
66
+
67
+    /**
68
+     * Initialize the adapter.
69
+     *
70
+     * @private
71
+     * @returns {Promise}
72
+     */
73
+    _initialize() {
74
+        if (this._mediaRecorder) {
75
+            return Promise.resolve();
76
+        }
85
 
77
 
78
+        return new Promise((resolve, error) => {
79
+            this._getAudioStream(0)
80
+            .then(stream => {
81
+                this._mediaRecorder = new MediaRecorder(stream);
82
+                this._mediaRecorder.ondataavailable
83
+                    = e => this._saveMediaData(e.data);
84
+                resolve();
85
+            })
86
+            .catch(err => {
87
+                logger.error(`Error calling getUserMedia(): ${err}`);
88
+                error();
89
+            });
90
+        });
86
     }
91
     }
87
 
92
 
88
     /**
93
     /**
89
-     * Callback for encoded data.
94
+     * Callback for storing the encoded data.
90
      *
95
      *
91
      * @private
96
      * @private
92
-     * @param {*} data - Encoded data.
97
+     * @param {Blob} data - Encoded data.
93
      * @returns {void}
98
      * @returns {void}
94
      */
99
      */
95
     _saveMediaData(data) {
100
     _saveMediaData(data) {

+ 0
- 9
react/features/local-recording/recording/RecordingAdapter.js Просмотреть файл

5
  */
5
  */
6
 export class RecordingAdapter {
6
 export class RecordingAdapter {
7
 
7
 
8
-    /**
9
-     * Initialize the recording backend.
10
-     *
11
-     * @returns {Promise}
12
-     */
13
-    ensureInitialized() {
14
-        throw new Error('Not implemented');
15
-    }
16
-
17
     /**
8
     /**
18
      * Starts recording.
9
      * Starts recording.
19
      *
10
      *

+ 73
- 40
react/features/local-recording/recording/WavAdapter.js Просмотреть файл

11
  */
11
  */
12
 export class WavAdapter extends RecordingAdapter {
12
 export class WavAdapter extends RecordingAdapter {
13
 
13
 
14
+    /**
15
+     * {@code AudioContext} instance.
16
+     */
14
     _audioContext = null;
17
     _audioContext = null;
18
+
19
+    /**
20
+     * {@code ScriptProcessorNode} instance, which receives the raw PCM bits.
21
+     */
15
     _audioProcessingNode = null;
22
     _audioProcessingNode = null;
23
+
24
+    /**
25
+     * {@code MediaStreamAudioSourceNode} instance, which represents the mic.
26
+     */
16
     _audioSource = null;
27
     _audioSource = null;
17
 
28
 
29
+    /**
30
+     * Length of the WAVE file, in units of {@code sizeof(Float32)}.
31
+     */
18
     _wavLength = 0;
32
     _wavLength = 0;
33
+
34
+    /**
35
+     * The {@code ArrayBuffer}s that stores the PCM bits.
36
+     */
19
     _wavBuffers = [];
37
     _wavBuffers = [];
38
+
39
+    /**
40
+     * Whether or not the {@code WavAdapter} is in a ready state.
41
+     */
20
     _isInitialized = false;
42
     _isInitialized = false;
21
 
43
 
44
+    /**
45
+     * Initialization promise.
46
+     */
47
+    _initPromise = null;
48
+
22
     /**
49
     /**
23
      * Constructor.
50
      * Constructor.
24
-     *
25
      */
51
      */
26
     constructor() {
52
     constructor() {
27
         super();
53
         super();
29
         this._saveWavPCM = this._saveWavPCM.bind(this);
55
         this._saveWavPCM = this._saveWavPCM.bind(this);
30
     }
56
     }
31
 
57
 
32
-    /**
33
-     * Implements {@link RecordingAdapter#ensureInitialized()}.
34
-     *
35
-     * @inheritdoc
36
-     */
37
-    ensureInitialized() {
38
-        if (this._isInitialized) {
39
-            return Promise.resolve();
40
-        }
41
-
42
-        const p = new Promise((resolve, reject) => {
43
-            this._getAudioStream(0)
44
-            .then(stream => {
45
-                this._audioContext = new AudioContext();
46
-                this._audioSource
47
-                    = this._audioContext.createMediaStreamSource(stream);
48
-                this._audioProcessingNode
49
-                    = this._audioContext.createScriptProcessor(4096, 1, 1);
50
-                this._audioProcessingNode.onaudioprocess = e => {
51
-                    const channelLeft = e.inputBuffer.getChannelData(0);
52
-
53
-                    // https://developer.mozilla.org/en-US/docs/
54
-                    // Web/API/AudioBuffer/getChannelData
55
-                    // the returned value is an Float32Array
56
-                    this._saveWavPCM(channelLeft);
57
-                };
58
-                this._isInitialized = true;
59
-                resolve();
60
-            })
61
-            .catch(err => {
62
-                logger.error(`Error calling getUserMedia(): ${err}`);
63
-                reject();
64
-            });
65
-        });
66
-
67
-        return p;
68
-    }
69
-
70
     /**
58
     /**
71
      * Implements {@link RecordingAdapter#start()}.
59
      * Implements {@link RecordingAdapter#start()}.
72
      *
60
      *
73
      * @inheritdoc
61
      * @inheritdoc
74
      */
62
      */
75
     start() {
63
     start() {
64
+        if (!this._initPromise) {
65
+            this._initPromise = this._initialize();
66
+        }
67
+
76
         return new Promise(
68
         return new Promise(
77
             (resolve, /* eslint-disable */_reject/* eslint-enable */) => {
69
             (resolve, /* eslint-disable */_reject/* eslint-enable */) => {
78
                 this._wavBuffers = [];
70
                 this._wavBuffers = [];
95
         this._audioProcessingNode.disconnect();
87
         this._audioProcessingNode.disconnect();
96
         this._audioSource.disconnect();
88
         this._audioSource.disconnect();
97
         this._data = this._exportMonoWAV(this._wavBuffers, this._wavLength);
89
         this._data = this._exportMonoWAV(this._wavBuffers, this._wavLength);
90
+        this._audioContext = null;
91
+        this._audioProcessingNode = null;
92
+        this._audioSource = null;
93
+        this._isInitialized = false;
98
 
94
 
99
         return Promise.resolve();
95
         return Promise.resolve();
100
     }
96
     }
110
 
106
 
111
             downloadBlob(audioURL, `recording${timestampString()}.wav`);
107
             downloadBlob(audioURL, `recording${timestampString()}.wav`);
112
         }
108
         }
113
-
114
     }
109
     }
115
 
110
 
116
     /**
111
     /**
167
         return new Uint8Array(buffer);
162
         return new Uint8Array(buffer);
168
     }
163
     }
169
 
164
 
165
+    /**
166
+     * Initialize the adapter.
167
+     *
168
+     * @private
169
+     * @returns {Promise}
170
+     */
171
+    _initialize() {
172
+        if (this._isInitialized) {
173
+            return Promise.resolve();
174
+        }
175
+
176
+        const p = new Promise((resolve, reject) => {
177
+            this._getAudioStream(0)
178
+            .then(stream => {
179
+                this._audioContext = new AudioContext();
180
+                this._audioSource
181
+                    = this._audioContext.createMediaStreamSource(stream);
182
+                this._audioProcessingNode
183
+                    = this._audioContext.createScriptProcessor(4096, 1, 1);
184
+                this._audioProcessingNode.onaudioprocess = e => {
185
+                    const channelLeft = e.inputBuffer.getChannelData(0);
186
+
187
+                    // https://developer.mozilla.org/en-US/docs/
188
+                    // Web/API/AudioBuffer/getChannelData
189
+                    // the returned value is an Float32Array
190
+                    this._saveWavPCM(channelLeft);
191
+                };
192
+                this._isInitialized = true;
193
+                resolve();
194
+            })
195
+            .catch(err => {
196
+                logger.error(`Error calling getUserMedia(): ${err}`);
197
+                reject();
198
+            });
199
+        });
200
+
201
+        return p;
202
+    }
170
 
203
 
171
     /**
204
     /**
172
      * Callback function that saves the PCM bits.
205
      * Callback function that saves the PCM bits.

+ 89
- 52
react/features/local-recording/recording/flac/FlacAdapter.js Просмотреть файл

21
     _audioProcessingNode = null;
21
     _audioProcessingNode = null;
22
     _audioSource = null;
22
     _audioSource = null;
23
 
23
 
24
+    /**
25
+     * Resolve function of the promise returned by {@code stop()}.
26
+     * This is called after the WebWorker sends back {@code WORKER_BLOB_READY}.
27
+     */
24
     _stopPromiseResolver = null;
28
     _stopPromiseResolver = null;
25
 
29
 
26
     /**
30
     /**
27
-     * Implements {@link RecordingAdapter#ensureInitialized}.
31
+     * Initialization promise.
32
+     */
33
+    _initPromise = null;
34
+
35
+    /**
36
+     * Implements {@link RecordingAdapter#start()}.
37
+     *
38
+     * @inheritdoc
39
+     */
40
+    start() {
41
+        if (!this._initPromise) {
42
+            this._initPromise = this._initialize();
43
+        }
44
+
45
+        return this._initPromise.then(() => {
46
+            this._audioSource.connect(this._audioProcessingNode);
47
+            this._audioProcessingNode.connect(this._audioContext.destination);
48
+        });
49
+    }
50
+
51
+    /**
52
+     * Implements {@link RecordingAdapter#stop()}.
28
      *
53
      *
29
      * @inheritdoc
54
      * @inheritdoc
30
      */
55
      */
31
-    ensureInitialized() {
56
+    stop() {
57
+        if (!this._encoder) {
58
+            logger.error('Attempting to stop but has nothing to stop.');
59
+
60
+            return Promise.reject();
61
+        }
62
+
63
+        return new Promise(resolve => {
64
+            this._initPromise = null;
65
+            this._audioProcessingNode.onaudioprocess = undefined;
66
+            this._audioProcessingNode.disconnect();
67
+            this._audioSource.disconnect();
68
+            this._stopPromiseResolver = resolve;
69
+            this._encoder.postMessage({
70
+                command: MAIN_THREAD_FINISH
71
+            });
72
+        });
73
+    }
74
+
75
+    /**
76
+     * Implements {@link RecordingAdapter#download()}.
77
+     *
78
+     * @inheritdoc
79
+     */
80
+    download() {
81
+        if (this._data !== null) {
82
+            const audioURL = window.URL.createObjectURL(this._data);
83
+
84
+            downloadBlob(audioURL, `recording${timestampString()}.flac`);
85
+        }
86
+    }
87
+
88
+    /**
89
+     * Initialize the adapter.
90
+     *
91
+     * @private
92
+     * @returns {Promise}
93
+     */
94
+    _initialize() {
32
         if (this._encoder !== null) {
95
         if (this._encoder !== null) {
33
             return Promise.resolve();
96
             return Promise.resolve();
34
         }
97
         }
35
 
98
 
36
         const promiseInitWorker = new Promise((resolve, reject) => {
99
         const promiseInitWorker = new Promise((resolve, reject) => {
37
-            // FIXME: workaround for different file names in development/
38
-            // production environments.
39
-            // We cannot import flacEncodeWorker as a webpack module,
40
-            // because it is in a different bundle and should be lazy-loaded
41
-            // only when flac recording is in use.
42
             try {
100
             try {
43
-                // try load the minified version first
44
-                this._encoder = new Worker('/libs/flacEncodeWorker.min.js');
45
-            } catch (exception1) {
46
-                // if failed, try unminified version
47
-                try {
48
-                    this._encoder = new Worker('/libs/flacEncodeWorker.js');
49
-                } catch (exception2) {
50
-                    logger.error('Failed to load flacEncodeWorker.');
51
-                    reject();
52
-                }
101
+                this._loadWebWorker();
102
+            } catch (e) {
103
+                reject();
53
             }
104
             }
54
 
105
 
55
             // set up listen for messages from the WebWorker
106
             // set up listen for messages from the WebWorker
60
                     if (this._stopPromiseResolver !== null) {
111
                     if (this._stopPromiseResolver !== null) {
61
                         this._stopPromiseResolver();
112
                         this._stopPromiseResolver();
62
                         this._stopPromiseResolver = null;
113
                         this._stopPromiseResolver = null;
114
+                        this._encoder.terminate();
115
+                        this._encoder = null;
63
                     }
116
                     }
64
                 } else if (e.data.command === DEBUG) {
117
                 } else if (e.data.command === DEBUG) {
65
                     logger.log(e.data);
118
                     logger.log(e.data);
116
     }
169
     }
117
 
170
 
118
     /**
171
     /**
119
-     * Implements {@link RecordingAdapter#start()}.
120
-     *
121
-     * @inheritdoc
122
-     */
123
-    start() {
124
-        this._audioSource.connect(this._audioProcessingNode);
125
-        this._audioProcessingNode.connect(this._audioContext.destination);
126
-    }
127
-
128
-    /**
129
-     * Implements {@link RecordingAdapter#stop()}.
130
-     *
131
-     * @inheritdoc
132
-     */
133
-    stop() {
134
-        return new Promise(resolve => {
135
-            this._audioProcessingNode.onaudioprocess = undefined;
136
-            this._audioProcessingNode.disconnect();
137
-            this._audioSource.disconnect();
138
-            this._stopPromiseResolver = resolve;
139
-            this._encoder.postMessage({
140
-                command: MAIN_THREAD_FINISH
141
-            });
142
-        });
143
-    }
144
-
145
-    /**
146
-     * Implements {@link RecordingAdapter#download()}.
172
+     * Loads the WebWorker.
147
      *
173
      *
148
-     * @inheritdoc
174
+     * @private
175
+     * @returns {void}
149
      */
176
      */
150
-    download() {
151
-        if (this._data !== null) {
152
-            const audioURL = window.URL.createObjectURL(this._data);
153
-
154
-            downloadBlob(audioURL, `recording${timestampString()}.flac`);
177
+    _loadWebWorker() {
178
+        // FIXME: workaround for different file names in development/
179
+        // production environments.
180
+        // We cannot import flacEncodeWorker as a webpack module,
181
+        // because it is in a different bundle and should be lazy-loaded
182
+        // only when flac recording is in use.
183
+        try {
184
+            // try load the minified version first
185
+            this._encoder = new Worker('/libs/flacEncodeWorker.min.js');
186
+        } catch (exception1) {
187
+            // if failed, try unminified version
188
+            try {
189
+                this._encoder = new Worker('/libs/flacEncodeWorker.js');
190
+            } catch (exception2) {
191
+                throw new Error('Failed to load flacEncodeWorker.');
192
+            }
155
         }
193
         }
156
-
157
     }
194
     }
158
 }
195
 }

+ 3
- 3
react/features/local-recording/recording/flac/flacEncodeWorker.js Просмотреть файл

19
 // So we disable the ESLint rule `new-cap` in this file.
19
 // So we disable the ESLint rule `new-cap` in this file.
20
 /* eslint-disable new-cap */
20
 /* eslint-disable new-cap */
21
 
21
 
22
-// Flow will complain about the number keys in `FLAC_ERRORS,
22
+// Flow will complain about the number keys in `FLAC_ERRORS`,
23
 // ESLint will complain about the `declare` statement.
23
 // ESLint will complain about the `declare` statement.
24
 // As the current workaround, add an exception for eslint.
24
 // As the current workaround, add an exception for eslint.
25
-/* eslint-disable flowtype/no-types-missing-file-annotation*/
25
+/* eslint-disable flowtype/no-types-missing-file-annotation */
26
 declare var Flac: Object;
26
 declare var Flac: Object;
27
 
27
 
28
 const FLAC_ERRORS = {
28
 const FLAC_ERRORS = {
151
     _state = EncoderState.UNINTIALIZED;
151
     _state = EncoderState.UNINTIALIZED;
152
 
152
 
153
     /**
153
     /**
154
-     * The ready-for-grab downloadable blob.
154
+     * The ready-for-grab downloadable Blob.
155
      */
155
      */
156
     _data = null;
156
     _data = null;
157
 
157
 

Загрузка…
Отмена
Сохранить