mirror of
https://github.com/edumeet/edumeet.git
synced 2026-07-25 20:03:55 +00:00
Merge pull request #945 from edumeet/feat-localrecording-disclaimer-v2
Localrecording Disclaimer
This commit is contained in:
commit
22b18ff3b9
45 changed files with 529 additions and 167 deletions
|
|
@ -35,6 +35,7 @@ can be found here: [config.example.js](public/config/config.example.js).
|
|||
| simulcastSharing | Enable or disable simulcast for screen sharing video. | `"boolean"` | ``false`` |
|
||||
| simulcastProfiles | Define different encodings for various resolutions of the video. | `"object"` | ``{ "320": [ { "scaleResolutionDownBy": 1, "maxBitRate": 150000 } ], "640": [ { "scaleResolutionDownBy": 2, "maxBitRate": 150000 }, { "scaleResolutionDownBy": 1, "maxBitRate": 500000 } ], "1280": [ { "scaleResolutionDownBy": 4, "maxBitRate": 150000 }, { "scaleResolutionDownBy": 2, "maxBitRate": 500000 }, { "scaleResolutionDownBy": 1, "maxBitRate": 1200000 } ], "1920": [ { "scaleResolutionDownBy": 6, "maxBitRate": 150000 }, { "scaleResolutionDownBy": 3, "maxBitRate": 500000 }, { "scaleResolutionDownBy": 1, "maxBitRate": 3500000 } ], "3840": [ { "scaleResolutionDownBy": 12, "maxBitRate": 150000 }, { "scaleResolutionDownBy": 6, "maxBitRate": 500000 }, { "scaleResolutionDownBy": 1, "maxBitRate": 10000000 } ]}`` |
|
||||
| adaptiveScalingFactor | The adaptive spatial layer selection scaling factor in the range [0.5, 1.0]. | | ``0.75`` |
|
||||
| localRecordingEnabled | If set to true Local Recording feature will be enabled. | `"boolean"` | ``false`` |
|
||||
| audioOutputSupportedBrowsers | White listing browsers that support audio output device selection. | `"array"` | ``[ "chrome", "opera"]`` |
|
||||
| requestTimeout | The Socket.io request timeout. | `"nat"` | ``20000`` |
|
||||
| requestRetries | The Socket.io request maximum retries. | `"nat"` | ``3`` |
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ var config = {
|
|||
// The adaptive spatial layer selection scaling factor in the range [0.5, 1.0].
|
||||
adaptiveScalingFactor : 0.75,
|
||||
|
||||
// If set to true Local Recording feature will be enabled.
|
||||
localRecordingEnabled : false,
|
||||
|
||||
// White listing browsers that support audio output device selection.
|
||||
audioOutputSupportedBrowsers : [
|
||||
'chrome',
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ import Logger from './Logger';
|
|||
import streamSaver from 'streamsaver';
|
||||
import { WritableStream } from 'web-streams-polyfill/ponyfill';
|
||||
import { openDB, deleteDB } from 'idb';
|
||||
import * as meActions from './actions/meActions';
|
||||
import { store } from './store';
|
||||
import * as requestActions from './actions/requestActions';
|
||||
import { RECORDING_PAUSE, RECORDING_RESUME, RECORDING_STOP, RECORDING_START } from './actions/recorderActions';
|
||||
import * as recorderActions from './actions/recorderActions';
|
||||
|
||||
export default class BrowserRecorder
|
||||
{
|
||||
|
|
@ -21,7 +20,7 @@ export default class BrowserRecorder
|
|||
this.recorderStream = null;
|
||||
this.gdmStream = null;
|
||||
this.roomClient = null;
|
||||
this.fileName = 'apple.webm';
|
||||
this.fileName = 'default.webm';
|
||||
this.logger = new Logger('Recorder');
|
||||
|
||||
// IndexedDB
|
||||
|
|
@ -45,7 +44,7 @@ export default class BrowserRecorder
|
|||
displaySurface : 'browser',
|
||||
width : { ideal: 1920 }
|
||||
},
|
||||
audio : true,
|
||||
audio : false,
|
||||
advanced : [
|
||||
{ width: 1920, height: 1080 },
|
||||
{ width: 1280, height: 720 }
|
||||
|
|
@ -81,7 +80,7 @@ export default class BrowserRecorder
|
|||
|
||||
async startLocalRecording(
|
||||
{
|
||||
roomClient, additionalAudioTracks, recordingMimeType
|
||||
roomClient, additionalAudioTracks, recordingMimeType, roomname
|
||||
})
|
||||
{
|
||||
this.roomClient = roomClient;
|
||||
|
|
@ -106,6 +105,8 @@ export default class BrowserRecorder
|
|||
|
||||
try
|
||||
{
|
||||
store.dispatch(recorderActions.setLocalRecordingState('start'));
|
||||
|
||||
// Screensharing
|
||||
this.gdmStream = await navigator.mediaDevices.getDisplayMedia(
|
||||
this.RECORDING_CONSTRAINTS
|
||||
|
|
@ -135,10 +136,17 @@ export default class BrowserRecorder
|
|||
this.recorderStream = this.mixer(null, this.gdmStream);
|
||||
}
|
||||
|
||||
const dt = new Date();
|
||||
const rdt = `${dt.getFullYear() }-${ (`0${ dt.getMonth()+1}`).slice(-2) }-${ (`0${ dt.getDate()}`).slice(-2) }_${dt.getHours() }_${(`0${ dt.getMinutes()}`).slice(-2) }_${dt.getSeconds()}`;
|
||||
|
||||
this.recorder = new MediaRecorder(
|
||||
this.recorderStream, { mimeType: this.recordingMimeType }
|
||||
);
|
||||
|
||||
const ext = this.recorder.mimeType.split(';')[0].split('/')[1];
|
||||
|
||||
this.fileName = `${roomname}-recording-${rdt}.${ext}`;
|
||||
|
||||
if (typeof indexedDB === 'undefined' || typeof indexedDB.open === 'undefined')
|
||||
{
|
||||
this.logger.warn('IndexedDB API is not available in this browser. Fallback to ');
|
||||
|
|
@ -195,7 +203,7 @@ export default class BrowserRecorder
|
|||
|
||||
this.recorder.onerror = (error) =>
|
||||
{
|
||||
this.logger.err(`Recorder onerror: ${error}`);
|
||||
this.logger.error(`Recorder onerror: ${error}`);
|
||||
switch (error.name)
|
||||
{
|
||||
case 'SecurityError':
|
||||
|
|
@ -271,7 +279,18 @@ export default class BrowserRecorder
|
|||
};
|
||||
|
||||
this.recorder.start(this.RECORDING_SLICE_SIZE);
|
||||
meActions.setLocalRecordingState(RECORDING_START);
|
||||
// abort so it dose not look stuck
|
||||
|
||||
window.onbeforeunload = () =>
|
||||
{
|
||||
if (this.recorder !== null)
|
||||
{
|
||||
this.stopLocalRecording();
|
||||
|
||||
// evt.returnValue = 'Are you sure you want to leave? Recording in process';
|
||||
}
|
||||
};
|
||||
recorderActions.setLocalRecordingState('start');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -288,7 +307,7 @@ export default class BrowserRecorder
|
|||
this.logger.error('startLocalRecording() [error:"%o"]', error);
|
||||
|
||||
if (this.recorder) this.recorder.stop();
|
||||
store.dispatch(meActions.setLocalRecordingState(RECORDING_STOP));
|
||||
store.dispatch(recorderActions.setLocalRecordingState('stop'));
|
||||
if (typeof this.gdmStream !== 'undefined' && this.gdmStream && typeof this.gdmStream.getTracks === 'function')
|
||||
{
|
||||
this.gdmStream.getTracks().forEach((track) => track.stop());
|
||||
|
|
@ -303,9 +322,8 @@ export default class BrowserRecorder
|
|||
|
||||
try
|
||||
{
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: RECORDING_START });
|
||||
|
||||
store.dispatch(meActions.setLocalRecordingState(RECORDING_START));
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: 'start' });
|
||||
|
||||
store.dispatch(requestActions.notify(
|
||||
{
|
||||
|
|
@ -329,6 +347,7 @@ export default class BrowserRecorder
|
|||
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async stopLocalRecording()
|
||||
{
|
||||
this.logger.debug('stopLocalRecording()');
|
||||
|
|
@ -344,9 +363,8 @@ export default class BrowserRecorder
|
|||
})
|
||||
}));
|
||||
|
||||
store.dispatch(meActions.setLocalRecordingState(RECORDING_STOP));
|
||||
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: RECORDING_STOP });
|
||||
store.dispatch(recorderActions.setLocalRecordingState('stop'));
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: 'stop' });
|
||||
|
||||
}
|
||||
catch (error)
|
||||
|
|
@ -367,14 +385,14 @@ export default class BrowserRecorder
|
|||
async pauseLocalRecording()
|
||||
{
|
||||
this.recorder.pause();
|
||||
store.dispatch(meActions.setLocalRecordingState(RECORDING_PAUSE));
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: RECORDING_PAUSE });
|
||||
store.dispatch(recorderActions.setLocalRecordingState('pause'));
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: 'pause' });
|
||||
}
|
||||
async resumeLocalRecording()
|
||||
{
|
||||
this.recorder.resume();
|
||||
store.dispatch(meActions.setLocalRecordingState(RECORDING_RESUME));
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: RECORDING_RESUME });
|
||||
store.dispatch(recorderActions.setLocalRecordingState('resume'));
|
||||
await this.roomClient.sendRequest('setLocalRecording', { localRecordingState: 'resume' });
|
||||
}
|
||||
invokeSaveAsDialog(blob)
|
||||
{
|
||||
|
|
@ -520,12 +538,10 @@ export default class BrowserRecorder
|
|||
// is it already appended to stream?
|
||||
if (this.recorder != null && (this.recorder.state === 'recording' || this.recorder.state === 'paused'))
|
||||
{
|
||||
|
||||
const micProducer = Object.values(producers).find((p) => p.source === 'mic');
|
||||
const micProducer = Object.values(producers).find((p) => p.kind === 'audio');
|
||||
|
||||
if (micProducer && this.micProducerId !== micProducer.id)
|
||||
{
|
||||
|
||||
// delete/dc previous one
|
||||
if (this.micProducerStreamSource)
|
||||
{
|
||||
|
|
@ -542,11 +558,11 @@ export default class BrowserRecorder
|
|||
}
|
||||
}
|
||||
}
|
||||
checkAudioConsumer(consumers)
|
||||
checkAudioConsumer(consumers, recordingConsents)
|
||||
{
|
||||
if (this.recorder != null && (this.recorder.state === 'recording' || this.recorder.state === 'paused'))
|
||||
if (this.recorder != null && (this.recorder.state === 'recording' || this.recorder.state === 'paused') && recordingConsents!==undefined)
|
||||
{
|
||||
const audioConsumers = Object.values(consumers).filter((p) => p.kind === 'audio');
|
||||
const audioConsumers = Object.values(consumers).filter((p) => p.kind === 'audio' && recordingConsents.includes(p.peerId));
|
||||
|
||||
for (let i = 0; i < audioConsumers.length; i++)
|
||||
{
|
||||
|
|
@ -572,4 +588,5 @@ export default class BrowserRecorder
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export const recorder = new BrowserRecorder();
|
||||
|
|
@ -20,7 +20,7 @@ import Spotlights from './Spotlights';
|
|||
import { permissions } from './permissions';
|
||||
import * as locales from './translations/locales';
|
||||
import { createIntl } from 'react-intl';
|
||||
import { RECORDING_START, RECORDING_PAUSE, RECORDING_RESUME, RECORDING_STOP } from './actions/recorderActions';
|
||||
import * as recorderActions from './actions/recorderActions';
|
||||
import { directReceiverTransform, opusReceiverTransform } from './transforms/receiver';
|
||||
import { config } from './config';
|
||||
|
||||
|
|
@ -3593,7 +3593,16 @@ export default class RoomClient
|
|||
|
||||
break;
|
||||
}
|
||||
case 'addConsentForRecording':
|
||||
{
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { peerId, consent } = notification.data;
|
||||
|
||||
store.dispatch(
|
||||
peerActions.setPeerLocalRecordingConsent(peerId, consent));
|
||||
|
||||
break;
|
||||
}
|
||||
case 'setLocalRecording':
|
||||
{
|
||||
const { peerId, localRecordingState } = notification.data;
|
||||
|
|
@ -3616,7 +3625,7 @@ export default class RoomClient
|
|||
|
||||
switch (localRecordingState)
|
||||
{
|
||||
case RECORDING_START:
|
||||
case 'start':
|
||||
store.dispatch(requestActions.notify(
|
||||
{
|
||||
text : intl.formatMessage({
|
||||
|
|
@ -3627,7 +3636,7 @@ export default class RoomClient
|
|||
})
|
||||
}));
|
||||
break;
|
||||
case RECORDING_RESUME:
|
||||
case 'resume':
|
||||
store.dispatch(requestActions.notify(
|
||||
{
|
||||
text : intl.formatMessage({
|
||||
|
|
@ -3638,7 +3647,7 @@ export default class RoomClient
|
|||
})
|
||||
}));
|
||||
break;
|
||||
case RECORDING_PAUSE:
|
||||
case 'pause':
|
||||
{
|
||||
store.dispatch(requestActions.notify(
|
||||
{
|
||||
|
|
@ -3651,7 +3660,7 @@ export default class RoomClient
|
|||
}));
|
||||
break;
|
||||
}
|
||||
case RECORDING_STOP:
|
||||
case 'stop':
|
||||
store.dispatch(requestActions.notify(
|
||||
{
|
||||
text : intl.formatMessage({
|
||||
|
|
@ -4174,6 +4183,23 @@ export default class RoomClient
|
|||
}
|
||||
}
|
||||
|
||||
async addConsentForRecording(consent)
|
||||
{
|
||||
logger.debug('addConsentForRecording()');
|
||||
|
||||
try
|
||||
{
|
||||
store.dispatch(
|
||||
recorderActions.setLocalRecordingConsent(consent));
|
||||
await this.sendRequest('addConsentForRecording', { consent });
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
|
||||
logger.error('addConsentForRecording() [error:"%o"]', error);
|
||||
}
|
||||
}
|
||||
|
||||
async setAccessCode(code)
|
||||
{
|
||||
logger.debug('setAccessCode()');
|
||||
|
|
|
|||
|
|
@ -116,9 +116,3 @@ export const setAutoMuted = (flag) =>
|
|||
type : 'SET_AUTO_MUTED',
|
||||
payload : { flag }
|
||||
});
|
||||
|
||||
export const setLocalRecordingState = (localRecordingState) =>
|
||||
({
|
||||
type : 'SET_LOCAL_RECORDING_STATE',
|
||||
payload : { localRecordingState }
|
||||
});
|
||||
|
|
@ -104,3 +104,9 @@ export const setPeerLocalRecordingState = (peerId, localRecordingState) =>
|
|||
type : 'SET_PEER_LOCAL_RECORDING_STATE',
|
||||
payload : { peerId, localRecordingState }
|
||||
});
|
||||
|
||||
export const setPeerLocalRecordingConsent = (peerId, consent) =>
|
||||
({
|
||||
type : 'SET_PEER_LOCAL_RECORDING_CONSENT',
|
||||
payload : { peerId, consent }
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Recoding STATE
|
||||
import BrowserRecorder from '../BrowserRecorder';
|
||||
|
||||
export const RECORDING_START = 'start';
|
||||
export const RECORDING_STOP = 'stop';
|
||||
export const RECORDING_PAUSE = 'pause';
|
||||
export const RECORDING_RESUME = 'resume';
|
||||
export const RECORDING_INIT = null;
|
||||
|
||||
export const recorder = new BrowserRecorder();
|
||||
export const setLocalRecordingState = (status) =>
|
||||
({
|
||||
type : 'SET_LOCAL_RECORDING_STATE',
|
||||
payload : { status }
|
||||
});
|
||||
export const setLocalRecordingConsent = (agreed) =>
|
||||
({
|
||||
type : 'SET_LOCAL_RECORDING_CONSENT',
|
||||
payload : { agreed }
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
meProducersSelector,
|
||||
makePermissionSelector
|
||||
makePermissionSelector,
|
||||
recordingConsentsPeersSelector
|
||||
} from '../Selectors';
|
||||
import { permissions } from '../../permissions';
|
||||
import { withRoomContext } from '../../RoomContext';
|
||||
|
|
@ -169,7 +170,9 @@ const Me = (props) =>
|
|||
transports,
|
||||
noiseVolume,
|
||||
classes,
|
||||
theme
|
||||
theme,
|
||||
recordingConsents,
|
||||
localRecordingState
|
||||
} = props;
|
||||
|
||||
// const width = style.width;
|
||||
|
|
@ -812,6 +815,8 @@ const Me = (props) =>
|
|||
{/* /CONTROLS BUTTONS (inside) */}
|
||||
|
||||
<VideoView
|
||||
localRecordingState={localRecordingState}
|
||||
recordingConsents={recordingConsents}
|
||||
isMe
|
||||
isMirrored={settings.mirrorOwnVideo}
|
||||
VideoView
|
||||
|
|
@ -938,6 +943,8 @@ const Me = (props) =>
|
|||
</div>
|
||||
|
||||
<VideoView
|
||||
localRecordingState={localRecordingState}
|
||||
recordingConsents={recordingConsents}
|
||||
isMe
|
||||
isMirrored={settings.mirrorOwnVideo}
|
||||
isExtraVideo
|
||||
|
|
@ -998,6 +1005,8 @@ const Me = (props) =>
|
|||
</p>
|
||||
|
||||
<VideoView
|
||||
localRecordingState={localRecordingState}
|
||||
recordingConsents={recordingConsents}
|
||||
isMe
|
||||
isScreen
|
||||
advancedMode={advancedMode}
|
||||
|
|
@ -1032,7 +1041,9 @@ Me.propTypes =
|
|||
noiseVolume : PropTypes.number,
|
||||
classes : PropTypes.object.isRequired,
|
||||
theme : PropTypes.object.isRequired,
|
||||
transports : PropTypes.object.isRequired
|
||||
transports : PropTypes.object.isRequired,
|
||||
localRecordingState : PropTypes.string,
|
||||
recordingConsents : PropTypes.array
|
||||
};
|
||||
|
||||
const makeMapStateToProps = () =>
|
||||
|
|
@ -1067,7 +1078,9 @@ const makeMapStateToProps = () =>
|
|||
hasVideoPermission : canShareVideo(state),
|
||||
hasScreenPermission : canShareScreen(state),
|
||||
noiseVolume : noise,
|
||||
transports : state.transports
|
||||
transports : state.transports,
|
||||
localRecordingState : state.recorderReducer.localRecordingState.status,
|
||||
recordingConsents : recordingConsentsPeersSelector(state)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -1089,7 +1102,10 @@ export default withRoomContext(connect(
|
|||
prev.peers === next.peers &&
|
||||
prev.producers === next.producers &&
|
||||
prev.settings === next.settings &&
|
||||
prev.transports === next.transports
|
||||
prev.transports === next.transports &&
|
||||
prev.recorderReducer.localRecordingState.status ===
|
||||
next.recorderReducer.localRecordingState.status &&
|
||||
recordingConsentsPeersSelector(prev)===recordingConsentsPeersSelector(next)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makePeerConsumerSelector } from '../Selectors';
|
||||
import { makePeerConsumerSelector, recordingConsentsPeersSelector } from '../Selectors';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import * as appPropTypes from '../appPropTypes';
|
||||
|
|
@ -156,7 +156,9 @@ const Peer = (props) =>
|
|||
enableLayersSwitch,
|
||||
isSelected,
|
||||
mode,
|
||||
theme
|
||||
theme,
|
||||
localRecordingState,
|
||||
recordingConsents
|
||||
} = props;
|
||||
|
||||
const micEnabled = (
|
||||
|
|
@ -658,6 +660,8 @@ const Peer = (props) =>
|
|||
</div>
|
||||
|
||||
<VideoView
|
||||
localRecordingState={localRecordingState}
|
||||
recordingConsents={recordingConsents}
|
||||
showQuality
|
||||
advancedMode={advancedMode}
|
||||
peer={peer}
|
||||
|
|
@ -832,6 +836,8 @@ const Peer = (props) =>
|
|||
</div>
|
||||
|
||||
<VideoView
|
||||
localRecordingState={localRecordingState}
|
||||
recordingConsents={recordingConsents}
|
||||
showQuality
|
||||
advancedMode={advancedMode}
|
||||
peer={peer}
|
||||
|
|
@ -994,6 +1000,8 @@ const Peer = (props) =>
|
|||
</Tooltip>
|
||||
</div>
|
||||
<VideoView
|
||||
localRecordingState={localRecordingState}
|
||||
recordingConsents={recordingConsents}
|
||||
showQuality
|
||||
advancedMode={advancedMode}
|
||||
videoContain
|
||||
|
|
@ -1051,7 +1059,9 @@ Peer.propTypes =
|
|||
theme : PropTypes.object.isRequired,
|
||||
enableLayersSwitch : PropTypes.bool,
|
||||
isSelected : PropTypes.bool,
|
||||
mode : PropTypes.string.isRequired
|
||||
mode : PropTypes.string.isRequired,
|
||||
localRecordingState : PropTypes.string,
|
||||
recordingConsents : PropTypes.array
|
||||
};
|
||||
|
||||
const makeMapStateToProps = (initialState, { id }) =>
|
||||
|
|
@ -1061,14 +1071,16 @@ const makeMapStateToProps = (initialState, { id }) =>
|
|||
const mapStateToProps = (state) =>
|
||||
{
|
||||
return {
|
||||
peer : state.peers[id],
|
||||
peer : state.peers[id],
|
||||
...getPeerConsumers(state, id),
|
||||
windowConsumer : state.room.windowConsumer,
|
||||
fullScreenConsumer : state.room.fullScreenConsumer,
|
||||
activeSpeaker : id === state.room.activeSpeakerId,
|
||||
browser : state.me.browser,
|
||||
isSelected : state.room.selectedPeers.includes(id),
|
||||
mode : state.room.mode
|
||||
windowConsumer : state.room.windowConsumer,
|
||||
fullScreenConsumer : state.room.fullScreenConsumer,
|
||||
activeSpeaker : id === state.room.activeSpeakerId,
|
||||
browser : state.me.browser,
|
||||
isSelected : state.room.selectedPeers.includes(id),
|
||||
mode : state.room.mode,
|
||||
localRecordingState : state.recorderReducer.localRecordingState.status,
|
||||
recordingConsents : recordingConsentsPeersSelector(state)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -1107,7 +1119,10 @@ export default withRoomContext(connect(
|
|||
prev.room.mode === next.room.mode &&
|
||||
prev.room.selectedPeers === next.room.selectedPeers &&
|
||||
prev.me.browser === next.me.browser &&
|
||||
prev.enableLayersSwitch === next.enableLayersSwitch
|
||||
prev.enableLayersSwitch === next.enableLayersSwitch &&
|
||||
prev.recorderReducer.localRecordingState.status ===
|
||||
next.recorderReducer.localRecordingState.status &&
|
||||
recordingConsentsPeersSelector(prev)===recordingConsentsPeersSelector(next)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import {
|
|||
peersLengthSelector,
|
||||
raisedHandsSelector,
|
||||
makePermissionSelector,
|
||||
recordingInProgressSelector
|
||||
recordingInProgressSelector,
|
||||
recordingInProgressPeersSelector,
|
||||
recordingConsentsPeersSelector
|
||||
} from '../Selectors';
|
||||
import { permissions } from '../../permissions';
|
||||
import * as appPropTypes from '../appPropTypes';
|
||||
|
|
@ -48,11 +50,8 @@ import PauseCircleOutlineIcon from '@material-ui/icons/PauseCircleOutline';
|
|||
import PauseCircleFilledIcon from '@material-ui/icons/PauseCircleFilled';
|
||||
import StopIcon from '@material-ui/icons/Stop';
|
||||
import randomString from 'random-string';
|
||||
import { recorder, RECORDING_START, RECORDING_PAUSE, RECORDING_RESUME } from '../../actions/recorderActions';
|
||||
import * as meActions from '../../actions/meActions';
|
||||
import { store } from '../../store';
|
||||
// import producers from '../../reducers/producers';
|
||||
// import logger from 'redux-logger';
|
||||
import { recorder } from './../../BrowserRecorder';
|
||||
|
||||
import Logger from '../../Logger';
|
||||
import { config } from '../../config';
|
||||
|
||||
|
|
@ -215,8 +214,8 @@ const TopBar = (props) =>
|
|||
const [ mobileMoreAnchorEl, setMobileMoreAnchorEl ] = useState(null);
|
||||
const [ anchorEl, setAnchorEl ] = useState(null);
|
||||
const [ currentMenu, setCurrentMenu ] = useState(null);
|
||||
const [ recordingConsentNotificationId,
|
||||
setRecordingConsentNotificationId ] = useState(null);
|
||||
const [ recordingNotificationsId,
|
||||
setRecordingNotificationsId ] = useState(null);
|
||||
|
||||
const handleExited = () =>
|
||||
{
|
||||
|
|
@ -255,6 +254,7 @@ const TopBar = (props) =>
|
|||
drawerOverlayed,
|
||||
toolAreaOpen,
|
||||
isSafari,
|
||||
meId,
|
||||
isMobile,
|
||||
loggedIn,
|
||||
loginEnabled,
|
||||
|
|
@ -275,29 +275,48 @@ const TopBar = (props) =>
|
|||
unread,
|
||||
canProduceExtraVideo,
|
||||
canLock,
|
||||
canRecord,
|
||||
canPromote,
|
||||
classes,
|
||||
locale,
|
||||
localesList,
|
||||
localRecordingState,
|
||||
recordingInProgress,
|
||||
recordingPeers,
|
||||
recordingMimeType,
|
||||
producers,
|
||||
consumers
|
||||
consumers,
|
||||
recordingConsents
|
||||
} = props;
|
||||
|
||||
// did it change?
|
||||
recorder.checkMicProducer(producers);
|
||||
recorder.checkAudioConsumer(consumers);
|
||||
recorder.checkAudioConsumer(consumers, recordingConsents);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
// someone else is recording (need consent) or only me(dont need consent notif)
|
||||
const hasConsent = (
|
||||
(
|
||||
(
|
||||
localRecordingState === undefined ||
|
||||
localRecordingState.consent!=='init'
|
||||
)
|
||||
|| (
|
||||
recordingPeers.includes(meId) && recordingPeers.length === 1
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (
|
||||
recordingInProgress &&
|
||||
!recordingConsentNotificationId)
|
||||
!recordingNotificationsId &&
|
||||
!hasConsent
|
||||
)
|
||||
{
|
||||
const notificationId = randomString({ length: 6 }).toLowerCase();
|
||||
|
||||
setRecordingConsentNotificationId(notificationId);
|
||||
setRecordingNotificationsId(notificationId);
|
||||
addNotification(
|
||||
{
|
||||
id : notificationId,
|
||||
|
|
@ -309,21 +328,24 @@ const TopBar = (props) =>
|
|||
defaultMessage : 'When attending this meeting you agree and give your consent that the meeting will be audio and video recorded and/or live broadcasted through web streaming'
|
||||
}
|
||||
),
|
||||
persist : true
|
||||
peerid : meId,
|
||||
roomClient : roomClient,
|
||||
persist : true
|
||||
}
|
||||
);
|
||||
}
|
||||
if (
|
||||
!recordingInProgress
|
||||
&& recordingConsentNotificationId)
|
||||
&& recordingNotificationsId)
|
||||
{
|
||||
closeNotification(recordingConsentNotificationId);
|
||||
setRecordingConsentNotificationId(null);
|
||||
closeNotification(recordingNotificationsId);
|
||||
setRecordingNotificationsId(null);
|
||||
}
|
||||
},
|
||||
[
|
||||
localRecordingState, recordingInProgress, recordingConsentNotificationId,
|
||||
addNotification, closeNotification, intl
|
||||
localRecordingState, recordingInProgress, recordingNotificationsId,
|
||||
addNotification, closeNotification, intl, meId, recordingPeers, roomClient,
|
||||
room
|
||||
]);
|
||||
|
||||
const isMenuOpen = Boolean(anchorEl);
|
||||
|
|
@ -340,8 +362,8 @@ const TopBar = (props) =>
|
|||
defaultMessage : 'Lock room'
|
||||
});
|
||||
|
||||
const recordingTooltip = (localRecordingState === RECORDING_START ||
|
||||
localRecordingState === RECORDING_RESUME) ?
|
||||
const recordingTooltip = (localRecordingState.status === 'start' ||
|
||||
localRecordingState.status === 'resume') ?
|
||||
intl.formatMessage({
|
||||
id : 'tooltip.stopLocalRecording',
|
||||
defaultMessage : 'Stop local recording'
|
||||
|
|
@ -352,7 +374,7 @@ const TopBar = (props) =>
|
|||
defaultMessage : 'Start local recording'
|
||||
});
|
||||
|
||||
const recordingPausedTooltip = localRecordingState === RECORDING_PAUSE ?
|
||||
const recordingPausedTooltip = localRecordingState.status === 'pause' ?
|
||||
intl.formatMessage({
|
||||
id : 'tooltip.resumeLocalRecording',
|
||||
defaultMessage : 'Resume local recording'
|
||||
|
|
@ -676,9 +698,9 @@ const TopBar = (props) =>
|
|||
<Paper>
|
||||
{
|
||||
(
|
||||
localRecordingState === RECORDING_START ||
|
||||
localRecordingState === RECORDING_RESUME ||
|
||||
localRecordingState === RECORDING_PAUSE
|
||||
localRecordingState.status === 'start' ||
|
||||
localRecordingState.status === 'resume' ||
|
||||
localRecordingState.status === 'pause'
|
||||
)
|
||||
&&
|
||||
<MenuItem
|
||||
|
|
@ -686,7 +708,7 @@ const TopBar = (props) =>
|
|||
onClick={() =>
|
||||
{
|
||||
handleMenuClose();
|
||||
if (localRecordingState === RECORDING_PAUSE)
|
||||
if (localRecordingState.status === 'pause')
|
||||
{
|
||||
recorder.resumeLocalRecording();
|
||||
}
|
||||
|
|
@ -700,13 +722,13 @@ const TopBar = (props) =>
|
|||
<Badge
|
||||
color='primary'
|
||||
>
|
||||
{ localRecordingState === RECORDING_PAUSE ?
|
||||
{ localRecordingState.status === 'pause' ?
|
||||
<PauseCircleFilledIcon />
|
||||
:
|
||||
<PauseCircleOutlineIcon />
|
||||
}
|
||||
</Badge>
|
||||
{ localRecordingState === RECORDING_PAUSE ?
|
||||
{ localRecordingState.status === 'pause' ?
|
||||
<p className={classes.moreAction}>
|
||||
<FormattedMessage
|
||||
id='tooltip.resumeLocalRecording'
|
||||
|
|
@ -724,38 +746,39 @@ const TopBar = (props) =>
|
|||
|
||||
</MenuItem>
|
||||
}
|
||||
{ isSafari &&
|
||||
{ config.localRecordingEnabled && isSafari
|
||||
&& canRecord &&
|
||||
<MenuItem
|
||||
aria-label={recordingTooltip}
|
||||
onClick={async () =>
|
||||
{
|
||||
handleMenuClose();
|
||||
if (localRecordingState === RECORDING_START ||
|
||||
localRecordingState === RECORDING_PAUSE ||
|
||||
localRecordingState === RECORDING_RESUME)
|
||||
if (localRecordingState.status === 'start' ||
|
||||
localRecordingState.status === 'pause' ||
|
||||
localRecordingState.status === 'resume')
|
||||
{
|
||||
recorder.stopLocalRecording();
|
||||
recorder.stopLocalRecording(meId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
const recordingMimeType =
|
||||
store.getState().settings.recorderPreferredMimeType;
|
||||
const additionalAudioTracks = [];
|
||||
const micProducer = Object.values(producers).find((p) => p.source === 'mic');
|
||||
|
||||
if (micProducer) additionalAudioTracks.push(micProducer.track);
|
||||
await recorder.startLocalRecording({
|
||||
const roomname = room.name;
|
||||
|
||||
recorder.startLocalRecording({
|
||||
roomClient,
|
||||
additionalAudioTracks,
|
||||
recordingMimeType
|
||||
recordingMimeType,
|
||||
roomname
|
||||
});
|
||||
|
||||
recorder.checkAudioConsumer(consumers);
|
||||
|
||||
meActions.setLocalRecordingState(RECORDING_START);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
|
|
@ -770,9 +793,9 @@ const TopBar = (props) =>
|
|||
color='primary'
|
||||
>
|
||||
{
|
||||
(localRecordingState === RECORDING_START ||
|
||||
localRecordingState === RECORDING_PAUSE ||
|
||||
localRecordingState === RECORDING_RESUME) ?
|
||||
(localRecordingState.status === 'start' ||
|
||||
localRecordingState.status === 'pause' ||
|
||||
localRecordingState.status === 'resume') ?
|
||||
<StopIcon />
|
||||
:
|
||||
<FiberManualRecordIcon />
|
||||
|
|
@ -780,9 +803,9 @@ const TopBar = (props) =>
|
|||
</Badge>
|
||||
|
||||
{
|
||||
(localRecordingState === RECORDING_START ||
|
||||
localRecordingState === RECORDING_PAUSE ||
|
||||
localRecordingState === RECORDING_RESUME) ?
|
||||
(localRecordingState.status === 'start' ||
|
||||
localRecordingState.status === 'pause' ||
|
||||
localRecordingState.status === 'resume') ?
|
||||
<p className={classes.moreAction}>
|
||||
<FormattedMessage
|
||||
id='tooltip.stopLocalRecording'
|
||||
|
|
@ -959,9 +982,9 @@ const TopBar = (props) =>
|
|||
}
|
||||
{
|
||||
(
|
||||
localRecordingState === RECORDING_PAUSE ||
|
||||
localRecordingState === RECORDING_RESUME ||
|
||||
localRecordingState === RECORDING_START
|
||||
localRecordingState.status === 'pause' ||
|
||||
localRecordingState.status === 'resume' ||
|
||||
localRecordingState.status === 'start'
|
||||
)
|
||||
&&
|
||||
<MenuItem
|
||||
|
|
@ -970,7 +993,7 @@ const TopBar = (props) =>
|
|||
onClick={() =>
|
||||
{
|
||||
handleMenuClose();
|
||||
if (localRecordingState === RECORDING_PAUSE)
|
||||
if (localRecordingState.status === 'pause')
|
||||
{
|
||||
recorder.resumeLocalRecording();
|
||||
}
|
||||
|
|
@ -984,14 +1007,14 @@ const TopBar = (props) =>
|
|||
<Badge
|
||||
color='primary'
|
||||
>
|
||||
{ localRecordingState === RECORDING_PAUSE ?
|
||||
{ localRecordingState.status === 'pause' ?
|
||||
<PauseCircleFilledIcon />
|
||||
:
|
||||
<PauseCircleOutlineIcon />
|
||||
}
|
||||
</Badge>
|
||||
|
||||
{ localRecordingState === RECORDING_PAUSE ?
|
||||
{ localRecordingState.status === 'pause' ?
|
||||
<p className={classes.moreAction}>
|
||||
<FormattedMessage
|
||||
id='tooltip.resumeLocalRecording'
|
||||
|
|
@ -1225,6 +1248,7 @@ TopBar.propTypes =
|
|||
roomClient : PropTypes.object.isRequired,
|
||||
room : appPropTypes.Room.isRequired,
|
||||
isSafari : PropTypes.bool,
|
||||
meId : PropTypes.string,
|
||||
isMobile : PropTypes.bool.isRequired,
|
||||
peersLength : PropTypes.number,
|
||||
lobbyPeers : PropTypes.array,
|
||||
|
|
@ -1251,6 +1275,7 @@ TopBar.propTypes =
|
|||
unread : PropTypes.number.isRequired,
|
||||
canProduceExtraVideo : PropTypes.bool.isRequired,
|
||||
canLock : PropTypes.bool.isRequired,
|
||||
canRecord : PropTypes.bool.isRequired,
|
||||
canPromote : PropTypes.bool.isRequired,
|
||||
classes : PropTypes.object.isRequired,
|
||||
theme : PropTypes.object.isRequired,
|
||||
|
|
@ -1259,9 +1284,11 @@ TopBar.propTypes =
|
|||
localesList : PropTypes.array.isRequired,
|
||||
localRecordingState : PropTypes.string,
|
||||
recordingInProgress : PropTypes.bool,
|
||||
recordingPeers : PropTypes.array,
|
||||
recordingMimeType : PropTypes.string,
|
||||
producers : PropTypes.object,
|
||||
consumers : PropTypes.object
|
||||
consumers : PropTypes.object,
|
||||
recordingConsents : PropTypes.array
|
||||
};
|
||||
|
||||
const makeMapStateToProps = () =>
|
||||
|
|
@ -1272,6 +1299,8 @@ const makeMapStateToProps = () =>
|
|||
const hasLockPermission =
|
||||
makePermissionSelector(permissions.CHANGE_ROOM_LOCK);
|
||||
|
||||
const hasRecordPermission =
|
||||
makePermissionSelector(permissions.LOCAL_RECORD_ROOM);
|
||||
const hasPromotionPermission =
|
||||
makePermissionSelector(permissions.PROMOTE_PEER);
|
||||
|
||||
|
|
@ -1279,6 +1308,7 @@ const makeMapStateToProps = () =>
|
|||
({
|
||||
room : state.room,
|
||||
isSafari : state.me.browser.name !== 'safari',
|
||||
meId : state.me.id,
|
||||
isMobile : state.me.browser.platform === 'mobile',
|
||||
peersLength : peersLengthSelector(state),
|
||||
lobbyPeers : lobbyPeersKeySelector(state),
|
||||
|
|
@ -1287,16 +1317,19 @@ const makeMapStateToProps = () =>
|
|||
toolAreaOpen : state.toolarea.toolAreaOpen,
|
||||
loggedIn : state.me.loggedIn,
|
||||
loginEnabled : state.me.loginEnabled,
|
||||
localRecordingState : state.me.localRecordingState,
|
||||
localRecordingState : state.recorderReducer.localRecordingState,
|
||||
recordingInProgress : recordingInProgressSelector(state),
|
||||
recordingPeers : recordingInProgressPeersSelector(state),
|
||||
recordingConsents : recordingConsentsPeersSelector(state),
|
||||
unread : state.toolarea.unreadMessages +
|
||||
state.toolarea.unreadFiles + raisedHandsSelector(state),
|
||||
canProduceExtraVideo : hasExtraVideoPermission(state),
|
||||
canLock : hasLockPermission(state),
|
||||
canRecord : hasRecordPermission(state),
|
||||
canPromote : hasPromotionPermission(state),
|
||||
locale : state.intl.locale,
|
||||
localesList : state.intl.list,
|
||||
recordingMimeType : state.settings.recordingMimeType,
|
||||
recordingMimeType : state.settings.recorderPreferredMimeType,
|
||||
producers : state.producers,
|
||||
consumers : state.consumers
|
||||
});
|
||||
|
|
@ -1375,14 +1408,18 @@ export default withRoomContext(connect(
|
|||
prev.me.loginEnabled === next.me.loginEnabled &&
|
||||
prev.me.picture === next.me.picture &&
|
||||
prev.me.roles === next.me.roles &&
|
||||
prev.me.localRecordingState === next.me.localRecordingState &&
|
||||
prev.recorderReducer.localRecordingState.status ===
|
||||
next.recorderReducer.localRecordingState.status &&
|
||||
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
||||
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
|
||||
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen &&
|
||||
prev.intl.locale === next.intl.locale &&
|
||||
prev.intl.localesList === next.intl.localesList &&
|
||||
prev.producers === next.producers &&
|
||||
prev.consumers === next.consumers
|
||||
prev.consumers === next.consumers &&
|
||||
prev.settings.recorderPreferredMimeType ===
|
||||
next.settings.recorderPreferredMimeType &&
|
||||
recordingConsentsPeersSelector(prev)===recordingConsentsPeersSelector(next)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ import { withSnackbar } from 'notistack';
|
|||
import * as notificationActions from '../../actions/notificationActions';
|
||||
import { config } from '../../config';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import ButtonGroup from '@material-ui/core/ButtonGroup';
|
||||
import VerifiedUserIcon from '@material-ui/icons/VerifiedUser';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Lock from '@material-ui/icons/Lock';
|
||||
class Notifications extends Component
|
||||
{
|
||||
displayed = [];
|
||||
|
|
@ -63,15 +64,39 @@ class Notifications extends Component
|
|||
// customized
|
||||
const okAction = (key) => (
|
||||
<Fragment>
|
||||
<Button
|
||||
variant='contained'
|
||||
size='small'
|
||||
color='default'
|
||||
startIcon={<VerifiedUserIcon />}
|
||||
onClick={() => { this.props.closeSnackbar(key); }}
|
||||
>
|
||||
<FormattedMessage id='room.consentUnderstand' defaultMessage='I understand' />
|
||||
</Button>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
variant='contained'
|
||||
size='small'
|
||||
color='primary'
|
||||
startIcon={<VerifiedUserIcon />}
|
||||
onClick={() =>
|
||||
{
|
||||
notification.roomClient.addConsentForRecording(
|
||||
'agreed'
|
||||
);
|
||||
this.props.closeSnackbar(key);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id='room.recordingConsentAccept' defaultMessage='I Accept' />
|
||||
</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
size='small'
|
||||
color='secondary'
|
||||
startIcon={<Lock />}
|
||||
onClick={() =>
|
||||
{
|
||||
notification.roomClient.addConsentForRecording(
|
||||
'denied'
|
||||
);
|
||||
this.props.closeSnackbar(key);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id='room.recordingConsentDeny' defaultMessage='Deny' />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
|
|
@ -81,6 +106,8 @@ class Notifications extends Component
|
|||
variant : notification.type,
|
||||
autoHideDuration : notification.timeout,
|
||||
persist : notification.persist,
|
||||
peerid : notification.peerid,
|
||||
roomClient : notification.roomClient,
|
||||
key : notification.id,
|
||||
action : notification.persist? okAction: null,
|
||||
anchorOrigin : {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { RECORDING_INIT, RECORDING_START, RECORDING_STOP, RECORDING_PAUSE, RECORDING_RESUME } from '../actions/recorderActions';
|
||||
|
||||
const meRolesSelect = (state) => state.me.roles;
|
||||
const userRolesSelect = (state) => state.room.userRoles;
|
||||
|
|
@ -10,6 +9,7 @@ const consumersSelect = (state) => state.consumers;
|
|||
const spotlightsSelector = (state) => state.room.spotlights;
|
||||
const peersSelector = (state) => state.peers;
|
||||
const meSelector = (state) => state.me;
|
||||
const recorderReducerSelect = (state) => state.recorderReducer;
|
||||
const lobbyPeersSelector = (state) => state.lobbyPeers;
|
||||
const getPeerConsumers = (state, id) =>
|
||||
(state.peers[id] ? state.peers[id].consumers : null);
|
||||
|
|
@ -304,30 +304,93 @@ export const makePermissionSelector = (permission) =>
|
|||
|
||||
export const recordingInProgressSelector = createSelector(
|
||||
peersValueSelector,
|
||||
meSelector,
|
||||
(peers, me) =>
|
||||
recorderReducerSelect,
|
||||
(peers, recorderReducer) =>
|
||||
{
|
||||
if (
|
||||
me.localRecordingState === RECORDING_START ||
|
||||
me.localRecordingState === RECORDING_RESUME ||
|
||||
recorderReducer.localRecordingState.status === 'start' ||
|
||||
recorderReducer.localRecordingState.status === 'resume' ||
|
||||
peers.findIndex((e) =>
|
||||
e.localRecordingState === RECORDING_START ||
|
||||
e.localRecordingState === RECORDING_RESUME
|
||||
e.localRecordingState !== undefined &&
|
||||
(
|
||||
e.localRecordingState === 'start' ||
|
||||
e.localRecordingState === 'resume'
|
||||
)
|
||||
) !== -1
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (
|
||||
me.localRecordingState === RECORDING_INIT ||
|
||||
me.localRecordingState === RECORDING_STOP ||
|
||||
me.localRecordingState === RECORDING_PAUSE ||
|
||||
recorderReducer.localRecordingState.status === 'init' ||
|
||||
recorderReducer.localRecordingState.status === 'stop' ||
|
||||
recorderReducer.localRecordingState.status === 'pause' ||
|
||||
peers.findIndex((e) =>
|
||||
e.localRecordingState === RECORDING_START ||
|
||||
e.localRecordingState === RECORDING_RESUME
|
||||
e.localRecordingState !== undefined &&
|
||||
(
|
||||
e.localRecordingState === 'start' ||
|
||||
e.localRecordingState === 'resume'
|
||||
)
|
||||
) === -1
|
||||
|
||||
)
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
export const recordingConsentsPeersSelector = createSelector(
|
||||
peersValueSelector,
|
||||
(peers) =>
|
||||
{
|
||||
const recordingconsents = [];
|
||||
|
||||
peers.forEach((e) =>
|
||||
{
|
||||
if (
|
||||
e.localRecordingConsent !== undefined &&
|
||||
e.localRecordingConsent === 'agreed'
|
||||
)
|
||||
{
|
||||
recordingconsents.push(e.id);
|
||||
}
|
||||
});
|
||||
|
||||
return recordingconsents;
|
||||
}
|
||||
);
|
||||
|
||||
export const recordingInProgressPeersSelector = createSelector(
|
||||
peersValueSelector,
|
||||
meSelector,
|
||||
recorderReducerSelect,
|
||||
(peers, me, recorderReducer) =>
|
||||
{
|
||||
const recordingpeers = [];
|
||||
|
||||
if (
|
||||
recorderReducer !== undefined &&
|
||||
(
|
||||
recorderReducer.localRecordingState.status === 'start' ||
|
||||
recorderReducer.localRecordingState.status === 'resume'
|
||||
)
|
||||
)
|
||||
{
|
||||
recordingpeers.push(me.id);
|
||||
}
|
||||
peers.forEach((e) =>
|
||||
{
|
||||
if (
|
||||
e.recorderReducer !== undefined &&
|
||||
(
|
||||
e.recorderReducer.localRecordingState.status === 'start' ||
|
||||
e.recorderReducer.localRecordingState.status === 'resume'
|
||||
)
|
||||
)
|
||||
{
|
||||
recordingpeers.push(e.id);
|
||||
}
|
||||
});
|
||||
|
||||
return recordingpeers;
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -231,7 +231,10 @@ class VideoView extends React.PureComponent
|
|||
netInfo,
|
||||
width,
|
||||
height,
|
||||
opusConfig
|
||||
opusConfig,
|
||||
localRecordingState,
|
||||
recordingConsents,
|
||||
peer
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
|
@ -427,7 +430,17 @@ class VideoView extends React.PureComponent
|
|||
</React.Fragment>
|
||||
:
|
||||
<span className={classes.displayNameStatic}>
|
||||
{displayName}
|
||||
{
|
||||
(
|
||||
(
|
||||
localRecordingState==='start' ||
|
||||
localRecordingState==='resume'
|
||||
)&&
|
||||
(
|
||||
!recordingConsents.includes(peer.id)
|
||||
)
|
||||
) ? '':displayName
|
||||
}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -438,7 +451,18 @@ class VideoView extends React.PureComponent
|
|||
<video
|
||||
ref='videoElement'
|
||||
className={classnames(classes.video, {
|
||||
hidden : !videoVisible,
|
||||
hidden : (!videoVisible ||
|
||||
(
|
||||
!isMe &&
|
||||
(
|
||||
localRecordingState==='start' ||
|
||||
localRecordingState==='resume'
|
||||
)&&
|
||||
(
|
||||
!recordingConsents.includes(peer.id)
|
||||
)
|
||||
)
|
||||
),
|
||||
'isMirrored' : isMirrored,
|
||||
contain : videoContain
|
||||
})}
|
||||
|
|
@ -637,7 +661,11 @@ VideoView.propTypes =
|
|||
netInfo : PropTypes.object,
|
||||
width : PropTypes.number,
|
||||
height : PropTypes.number,
|
||||
opusConfig : PropTypes.string
|
||||
opusConfig : PropTypes.string,
|
||||
localRecordingState : PropTypes.string,
|
||||
recordingConsents : PropTypes.array,
|
||||
peer : PropTypes.string
|
||||
|
||||
};
|
||||
|
||||
export default withStyles(styles)(VideoView);
|
||||
|
|
|
|||
|
|
@ -202,6 +202,13 @@ This value must match exactly one of the values defined in aspectRatios.`,
|
|||
default : 0.75
|
||||
},
|
||||
|
||||
localRecordingEnabled :
|
||||
{
|
||||
doc : 'If set to true Local Recording feature will be enabled.',
|
||||
format : 'Boolean',
|
||||
default : false
|
||||
},
|
||||
|
||||
/**
|
||||
* White listing browsers that support audio output device selection.
|
||||
* It is not yet fully implemented in Firefox.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import { SnackbarProvider } from 'notistack';
|
|||
import * as serviceWorker from './serviceWorker';
|
||||
import { LazyPreload } from './components/Loader/LazyPreload';
|
||||
import { detectDevice } from 'mediasoup-client';
|
||||
import { recorder } from './actions/recorderActions';
|
||||
import { recorder } from './BrowserRecorder';
|
||||
|
||||
import './index.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,28 @@
|
|||
export const permissions = {
|
||||
// The role(s) have permission to lock/unlock a room
|
||||
CHANGE_ROOM_LOCK : 'CHANGE_ROOM_LOCK',
|
||||
CHANGE_ROOM_LOCK : 'CHANGE_ROOM_LOCK',
|
||||
// The role(s) have permission to promote a peer from the lobby
|
||||
PROMOTE_PEER : 'PROMOTE_PEER',
|
||||
PROMOTE_PEER : 'PROMOTE_PEER',
|
||||
// The role(s) have permission to give/remove other peers roles
|
||||
MODIFY_ROLE : 'MODIFY_ROLE',
|
||||
MODIFY_ROLE : 'MODIFY_ROLE',
|
||||
// The role(s) have permission to send chat messages
|
||||
SEND_CHAT : 'SEND_CHAT',
|
||||
SEND_CHAT : 'SEND_CHAT',
|
||||
// The role(s) have permission to moderate chat
|
||||
MODERATE_CHAT : 'MODERATE_CHAT',
|
||||
MODERATE_CHAT : 'MODERATE_CHAT',
|
||||
// The role(s) have permission to share audio
|
||||
SHARE_AUDIO : 'SHARE_AUDIO',
|
||||
SHARE_AUDIO : 'SHARE_AUDIO',
|
||||
// The role(s) have permission to share video
|
||||
SHARE_VIDEO : 'SHARE_VIDEO',
|
||||
SHARE_VIDEO : 'SHARE_VIDEO',
|
||||
// The role(s) have permission to share screen
|
||||
SHARE_SCREEN : 'SHARE_SCREEN',
|
||||
SHARE_SCREEN : 'SHARE_SCREEN',
|
||||
// The role(s) have permission to produce extra video
|
||||
EXTRA_VIDEO : 'EXTRA_VIDEO',
|
||||
EXTRA_VIDEO : 'EXTRA_VIDEO',
|
||||
// The role(s) have permission to share files
|
||||
SHARE_FILE : 'SHARE_FILE',
|
||||
SHARE_FILE : 'SHARE_FILE',
|
||||
// The role(s) have permission to moderate files
|
||||
MODERATE_FILES : 'MODERATE_FILES',
|
||||
MODERATE_FILES : 'MODERATE_FILES',
|
||||
// The role(s) have permission to moderate room (e.g. kick user)
|
||||
MODERATE_ROOM : 'MODERATE_ROOM'
|
||||
MODERATE_ROOM : 'MODERATE_ROOM',
|
||||
// The role(s) have permission to start room recording localy
|
||||
LOCAL_RECORD_ROOM : 'LOCAL_RECORD_ROOM'
|
||||
};
|
||||
|
|
@ -19,8 +19,7 @@ const initialState =
|
|||
raisedHandInProgress : false,
|
||||
loggedIn : false,
|
||||
isSpeaking : false,
|
||||
isAutoMuted : true,
|
||||
localRecordingState : null
|
||||
isAutoMuted : true
|
||||
};
|
||||
|
||||
const me = (state = initialState, action) =>
|
||||
|
|
@ -168,13 +167,6 @@ const me = (state = initialState, action) =>
|
|||
return { ...state, isAutoMuted: flag };
|
||||
}
|
||||
|
||||
case 'SET_LOCAL_RECORDING_STATE':
|
||||
{
|
||||
const { localRecordingState } = action.payload;
|
||||
|
||||
return { ...state, localRecordingState };
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,15 @@ const notifications = (state = [], action) =>
|
|||
return [ ...state, notification ];
|
||||
}
|
||||
|
||||
case 'ADD_CONSENT_NOTIFICATION':
|
||||
{
|
||||
const { notification } = action.payload;
|
||||
|
||||
notification.toBeClosed=false;
|
||||
|
||||
return [ ...state, notification ];
|
||||
}
|
||||
|
||||
case 'REMOVE_NOTIFICATION':
|
||||
{
|
||||
const { notificationId } = action.payload;
|
||||
|
|
|
|||
|
|
@ -97,6 +97,12 @@ const peer = (state = initialState, action) =>
|
|||
localRecordingState : action.payload.localRecordingState
|
||||
};
|
||||
|
||||
case 'SET_PEER_LOCAL_RECORDING_CONSENT':
|
||||
return {
|
||||
...state,
|
||||
localRecordingConsent : action.payload.consent
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -147,7 +153,16 @@ const peers = (state = initialState, action) =>
|
|||
|
||||
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||
}
|
||||
case 'SET_PEER_LOCAL_RECORDING_CONSENT':
|
||||
{
|
||||
const oldPeer = state[action.payload.peerId];
|
||||
|
||||
// NOTE: This means that the Peer was closed before, so it's ok.
|
||||
if (!oldPeer)
|
||||
return state;
|
||||
|
||||
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||
}
|
||||
case 'CLEAR_PEERS':
|
||||
{
|
||||
return initialState;
|
||||
|
|
|
|||
44
app/src/reducers/recorder.js
Normal file
44
app/src/reducers/recorder.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
const initialState = {
|
||||
localRecordingState : {
|
||||
status : 'init',
|
||||
consent : 'init'
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
const recorderReducer = (state = initialState, action) =>
|
||||
{
|
||||
switch (action.type)
|
||||
{
|
||||
case 'SET_LOCAL_RECORDING_STATE':
|
||||
{
|
||||
const { status } = action.payload;
|
||||
|
||||
const localRecordingState = state['localRecordingState'];
|
||||
|
||||
localRecordingState.status = status;
|
||||
|
||||
return { ...state,
|
||||
localRecordingState : localRecordingState
|
||||
};
|
||||
}
|
||||
|
||||
case 'SET_LOCAL_RECORDING_CONSENT':
|
||||
{
|
||||
const { agreed } = action.payload;
|
||||
const localRecordingState = state['localRecordingState'];
|
||||
|
||||
localRecordingState.consent = agreed;
|
||||
|
||||
return { ...state,
|
||||
localRecordingState : localRecordingState
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default recorderReducer;
|
||||
|
|
@ -8,6 +8,7 @@ import consumers from './consumers';
|
|||
import peerVolumes from './peerVolumes';
|
||||
import notifications from './notifications';
|
||||
import chat from './chat';
|
||||
import recorderReducer from './recorder';
|
||||
import toolarea from './toolarea';
|
||||
import files from './files';
|
||||
import settings from './settings';
|
||||
|
|
@ -30,6 +31,7 @@ export default combineReducers({
|
|||
toolarea,
|
||||
files,
|
||||
settings,
|
||||
recorderReducer,
|
||||
// intl : intlReducer
|
||||
intl,
|
||||
config
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -75,11 +75,12 @@
|
|||
"room.localRecordingPaused": "{displayName} paused local recording",
|
||||
"room.localRecordingResumed": "{displayName} resumed local recording",
|
||||
"room.localRecordingStopped": "{displayName} stopped local recording",
|
||||
"room.recordingConsent": "When attending this meeting you agree and give your consent that the meeting will be audio and video recorded and/or live broadcasted through web streaming.",
|
||||
"room.localRecordingSecurityError": "Recording the specified source is not allowed due to security restrictions. Check you client settings!",
|
||||
"room.leavingTheRoom": "Leaving the room",
|
||||
"room.leaveConfirmationMessage": "Do you want to leave the room?",
|
||||
|
||||
"room.recordingConsent": "When attending this meeting you agree and give your consent that the meeting will be audio and video recorded and/or live broadcasted through web streaming.",
|
||||
"room.recordingConsentAccept" : "I Accept",
|
||||
"room.recordingConsentDeny" : "Deny",
|
||||
"me.mutedPTT": "You are muted{br}hold down SPACE-BAR to talk",
|
||||
|
||||
"roles.gotRole": "You got the role: {role}",
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": "{displayName} visszaindította a helyi rögzítést",
|
||||
"room.localRecordingStopped": "{displayName} leállította a helyi rögzítést",
|
||||
"room.recordingConsent": "A konferencián való részvételeddel beleegyezésed adod hogy a konferenciáról hang és videó felvétel, és/vagy élő közvetítés készüljön!",
|
||||
"room.recordingConsentAccept" : "Elfogadom",
|
||||
"room.recordingConsentDeny" : "Megtagadom",
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": "Opuszczanie pokoju",
|
||||
"room.leaveConfirmationMessage": "Czy na pewno chcesz opuścić pokój?",
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.hideSelfView": "Kendi görüntümü gizle",
|
||||
"room.showSelfView": "Kendi görüntümü göster",
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
"room.localRecordingResumed": null,
|
||||
"room.localRecordingStopped": null,
|
||||
"room.recordingConsent": null,
|
||||
"room.recordingConsentAccept" : null,
|
||||
"room.recordingConsentDeny" : null,
|
||||
"room.localRecordingSecurityError": null,
|
||||
"room.leavingTheRoom": null,
|
||||
"room.leaveConfirmationMessage": null,
|
||||
|
|
|
|||
|
|
@ -54,13 +54,13 @@ Look at the default `config/config.example.js` file for documentation.
|
|||
| routerScaleSize | Room size before spreading to a new router. | `"nat"` | ``40`` |
|
||||
| requestTimeout | Socket timeout value (ms). | `"nat"` | ``20000`` |
|
||||
| requestRetries | Socket retries when a timeout occurs. | `"nat"` | ``3`` |
|
||||
| mediasoup.numWorkers | The number of Mediasoup workers to spawn. Defaults to the available CPUs count. | `"nat"` | ``4`` |
|
||||
| mediasoup.numWorkers | The number of Mediasoup workers to spawn. Defaults to the available CPUs count. | `"nat"` | ``6`` |
|
||||
| mediasoup.worker.logLevel | The Mediasoup log level. | `"string"` | ``"warn"`` |
|
||||
| mediasoup.worker.logTags | The Mediasoup log tags. | `"array"` | ``[ "info", "ice", "dtls", "rtp", "srtp", "rtcp"]`` |
|
||||
| mediasoup.worker.rtcMinPort | The Mediasoup start listening port number. | `"port"` | ``40000`` |
|
||||
| mediasoup.worker.rtcMaxPort | The Mediasoup end listening port number. | `"port"` | ``49999`` |
|
||||
| mediasoup.router.mediaCodecs | The Mediasoup codecs settings. [supportedRtpCapabilities](https://github.com/versatica/mediasoup/blob/v3/src/supportedRtpCapabilities.ts) | `"*"` | ``[ { "kind": "audio", "mimeType": "audio/opus", "clockRate": 48000, "channels": 2 }, { "kind": "video", "mimeType": "video/VP8", "clockRate": 90000, "parameters": { "x-google-start-bitrate": 1000 } }, { "kind": "video", "mimeType": "video/VP9", "clockRate": 90000, "parameters": { "profile-id": 2, "x-google-start-bitrate": 1000 } }, { "kind": "video", "mimeType": "video/h264", "clockRate": 90000, "parameters": { "packetization-mode": 1, "profile-level-id": "4d0032", "level-asymmetry-allowed": 1, "x-google-start-bitrate": 1000 } }, { "kind": "video", "mimeType": "video/h264", "clockRate": 90000, "parameters": { "packetization-mode": 1, "profile-level-id": "42e01f", "level-asymmetry-allowed": 1, "x-google-start-bitrate": 1000 } }]`` |
|
||||
| mediasoup.webRtcTransport.listenIps | The Mediasoup listen IPs. [TransportListenIp](https://mediasoup.org/documentation/v3/mediasoup/api/#TransportListenIp) | `"array"` | ``[ { "ip": "192.168.246.104", "announcedIp": null }]`` |
|
||||
| mediasoup.webRtcTransport.listenIps | The Mediasoup listen IPs. [TransportListenIp](https://mediasoup.org/documentation/v3/mediasoup/api/#TransportListenIp) | `"array"` | ``[ { "ip": "10.0.0.1", "announcedIp": null }, { "ip": "db19:25c4:5f01:9683:cc5a:bcac:fd6e:b38d", "announcedIp": null }]`` |
|
||||
| mediasoup.webRtcTransport.initialAvailableOutgoingBitrate | The Mediasoup initial available outgoing bitrate (in bps). [WebRtcTransportOptions](https://mediasoup.org/documentation/v3/mediasoup/api/#WebRtcTransportOptions) | `"nat"` | ``1000000`` |
|
||||
| mediasoup.webRtcTransport.maxIncomingBitrate | The Mediasoup maximum incoming bitrate for each transport. (in bps). [setMaxIncomingBitrate](https://mediasoup.org/documentation/v3/mediasoup/api/#transport-setMaxIncomingBitrate) | `"nat"` | ``1500000`` |
|
||||
| prometheus.enabled | Enables the Prometheus metrics exporter. | `"boolean"` | ``false`` |
|
||||
|
|
|
|||
|
|
@ -1561,6 +1561,21 @@ class Room extends EventEmitter
|
|||
break;
|
||||
}
|
||||
|
||||
case 'addConsentForRecording':
|
||||
{
|
||||
const {consent} = request.data;
|
||||
// Spread to others
|
||||
this._notification(peer.socket, 'addConsentForRecording', {
|
||||
peerId : peer.id,
|
||||
consent: consent
|
||||
}, true);
|
||||
|
||||
// Return no error
|
||||
cb();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'unlockRoom':
|
||||
{
|
||||
if (!this._hasPermission(peer, CHANGE_ROOM_LOCK))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue