refactor: use createLocalTracks instead of gUM; fix some docs;

pull/3223/head
Radium Zheng 6 years ago
parent 3241c7a929
commit e125861b29
  1. 2
      react/features/local-recording/controller/RecordingController.js
  2. 4
      react/features/local-recording/middleware.js
  3. 37
      react/features/local-recording/recording/OggAdapter.js
  4. 31
      react/features/local-recording/recording/RecordingAdapter.js
  5. 56
      react/features/local-recording/recording/WavAdapter.js
  6. 62
      react/features/local-recording/recording/flac/FlacAdapter.js
  7. 28
      react/features/local-recording/recording/flac/flacEncodeWorker.js
  8. 4
      react/features/local-recording/reducer.js

@ -258,7 +258,7 @@ class RecordingController {
this._format = newFormat;
logger.log(`Recording format switched to ${newFormat}`);
// will be used next time
// the new format will be used in the next recording session
}
/**

@ -14,7 +14,6 @@ MiddlewareRegistry.register(({ getState, dispatch }) => next => action => {
switch (action.type) {
case CONFERENCE_JOINED: {
// the Conference object is ready
const { conference } = getState()['features/base/conference'];
recordingController.registerEvents(conference);
@ -48,5 +47,8 @@ MiddlewareRegistry.register(({ getState, dispatch }) => next => action => {
break;
}
// @todo: detect change in features/base/settings micDeviceID
// @todo: SET_AUDIO_MUTED, when audio is muted
return result;
});

@ -9,6 +9,9 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
*/
export class OggAdapter extends RecordingAdapter {
/**
* Instance of MediaRecorder.
*/
_mediaRecorder = null;
/**
@ -21,29 +24,17 @@ export class OggAdapter extends RecordingAdapter {
if (this._mediaRecorder === null) {
p = new Promise((resolve, error) => {
navigator.getUserMedia(
// constraints, only audio needed
{
audioBitsPerSecond: 44100, // 44 kbps
audio: true,
mimeType: 'application/ogg'
},
// success callback
stream => {
this._mediaRecorder = new MediaRecorder(stream);
this._mediaRecorder.ondataavailable
= e => this._saveMediaData(e.data);
resolve();
},
// Error callback
err => {
logger.error(`Error calling getUserMedia(): ${err}`);
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 => {

@ -1,5 +1,7 @@
import JitsiMeetJS from '../../base/lib-jitsi-meet';
/**
* Common interface for recording mechanisms
* Base class for recording backends.
*/
export class RecordingAdapter {
@ -38,4 +40,31 @@ export class RecordingAdapter {
download() {
throw new Error('Not implemented');
}
/**
* Helper method for getting an audio MediaStream. Use this instead of
* calling browser APIs directly.
*
* @protected
* @param {number} micDeviceId - The ID of the current audio device.
* @returns {Promise}
*/
_getAudioStream(micDeviceId) {
return JitsiMeetJS.createLocalTracks({
devices: [ 'audio' ],
micDeviceId
}).then(result => {
if (result.length !== 1) {
throw new Error('Unexpected number of streams '
+ 'from createLocalTracks.');
}
const mediaStream = result[0].stream;
if (mediaStream === undefined) {
throw new Error('Failed to get MediaStream.');
}
return mediaStream;
});
}
}

@ -40,40 +40,28 @@ export class WavAdapter extends RecordingAdapter {
}
const p = new Promise((resolve, reject) => {
navigator.getUserMedia(
// constraints - only audio needed for this app
{
audioBitsPerSecond: WAV_SAMPLE_RATE * WAV_BITS_PER_SAMPLE,
audio: true,
mimeType: 'application/ogg' // useless?
},
// Success callback
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();
},
// Error callback
err => {
logger.error(`Error calling getUserMedia(): ${err}`);
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;

@ -12,7 +12,7 @@ import {
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Recording adapter that uses libflac in the background
* Recording adapter that uses libflac.js in the background.
*/
export class FlacAdapter extends RecordingAdapter {
@ -43,7 +43,7 @@ export class FlacAdapter extends RecordingAdapter {
// try load the minified version first
this._encoder = new Worker('/libs/flacEncodeWorker.min.js');
} catch (exception1) {
// if failed, try un minified version
// if failed, try unminified version
try {
this._encoder = new Worker('/libs/flacEncodeWorker.js');
} catch (exception2) {
@ -83,41 +83,29 @@ export class FlacAdapter extends RecordingAdapter {
});
const callbackInitAudioContext = (resolve, reject) => {
navigator.getUserMedia(
// constraints - only audio needed for this app
{
audioBitsPerSecond: 44100, // 44 kbps
audio: true,
mimeType: 'application/ogg' // useless?
},
// Success callback
stream => {
this._audioContext = new AudioContext();
this._audioSource
= this._audioContext.createMediaStreamSource(stream);
this._audioProcessingNode
= this._audioContext.createScriptProcessor(4096, 1, 1);
this._audioProcessingNode.onaudioprocess = e => {
// delegate to the WebWorker to do the encoding
const channelLeft = e.inputBuffer.getChannelData(0);
this._encoder.postMessage({
command: MAIN_THREAD_NEW_DATA_ARRIVED,
buf: channelLeft
});
};
logger.debug('AudioContext is set up.');
resolve();
},
// Error callback
err => {
logger.error(`Error calling getUserMedia(): ${err}`);
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 => {
// delegate to the WebWorker to do the encoding
const channelLeft = e.inputBuffer.getChannelData(0);
this._encoder.postMessage({
command: MAIN_THREAD_NEW_DATA_ARRIVED,
buf: channelLeft
});
};
logger.debug('AudioContext is set up.');
resolve();
})
.catch(err => {
logger.error(`Error calling getUserMedia(): ${err}`);
reject();
});
};
// FIXME: because Promise constructor immediately executes the executor

@ -26,39 +26,33 @@ importScripts('/libs/libflac3-1.3.2.min.js');
declare var Flac: Object;
const FLAC_ERRORS = {
// The encoder is in the normal OK state and
// samples can be processed.
// The encoder is in the normal OK state and samples can be processed.
0: 'FLAC__STREAM_ENCODER_OK',
// The encoder is in the
// uninitialized state one of the FLAC__stream_encoder_init_*() functions
// must be called before samples can be processed.
// The encoder is in the uninitialized state one of the
// FLAC__stream_encoder_init_*() functions must be called before samples can
// be processed.
1: 'FLAC__STREAM_ENCODER_UNINITIALIZED',
// An error occurred in the underlying Ogg layer.
2: 'FLAC__STREAM_ENCODER_OGG_ERROR',
// An error occurred in the
// underlying verify stream decoder; check
// An error occurred in the underlying verify stream decoder; check
// FLAC__stream_encoder_get_verify_decoder_state().
3: 'FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR',
// The verify decoder detected a mismatch between the
// original audio signal and the decoded audio signal.
// The verify decoder detected a mismatch between the original audio signal
// and the decoded audio signal.
4: 'FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA',
// One of the callbacks returned
// a fatal error.
// One of the callbacks returned a fatal error.
5: 'FLAC__STREAM_ENCODER_CLIENT_ERROR',
// An I/O error occurred while
// opening/reading/writing a file. Check errno.
// An I/O error occurred while opening/reading/writing a file. Check errno.
6: 'FLAC__STREAM_ENCODER_IO_ERROR',
// An error occurred while writing
// the stream; usually, the write_callback returned an error.
// An error occurred while writing the stream; usually, the write_callback
// returned an error.
7: 'FLAC__STREAM_ENCODER_FRAMING_ERROR',
// Memory allocation failed.

@ -9,11 +9,7 @@ import {
} from './actionTypes';
import { recordingController } from './controller';
const logger = require('jitsi-meet-logger').getLogger(__filename);
ReducerRegistry.register('features/local-recording', (state = {}, action) => {
logger.debug(`Redux state (features/local-recording):\n ${
JSON.stringify(state)}`);
switch (action.type) {
case LOCAL_RECORDING_ENGAGED: {
return {

Loading…
Cancel
Save