bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx

864 lines
26 KiB
React
Raw Normal View History

2018-11-07 07:10:56 +08:00
import PropTypes from 'prop-types';
import React, { Component } from 'react';
2019-03-29 05:53:42 +08:00
import {
defineMessages, injectIntl, FormattedMessage,
2019-03-29 05:53:42 +08:00
} from 'react-intl';
2022-02-15 04:20:50 +08:00
import Button from '/imports/ui/components/common/button/component';
import VirtualBgSelector from '/imports/ui/components/video-preview/virtual-background/component'
import logger from '/imports/startup/client/logger';
import browserInfo from '/imports/utils/browserInfo';
import PreviewService from './service';
import VideoService from '../video-provider/service';
2021-10-28 21:16:36 +08:00
import Styled from './styles';
import deviceInfo from '/imports/utils/deviceInfo';
import MediaStreamUtils from '/imports/utils/media-stream-utils';
import { notify } from '/imports/ui/services/notification';
import {
EFFECT_TYPES,
SHOW_THUMBNAILS,
setSessionVirtualBackgroundInfo,
getSessionVirtualBackgroundInfo,
} from '/imports/ui/services/virtual-background/service';
2021-10-28 21:16:36 +08:00
import Settings from '/imports/ui/services/settings';
import { isVirtualBackgroundsEnabled } from '/imports/ui/services/features';
const VIEW_STATES = {
finding: 'finding',
found: 'found',
error: 'error',
};
2018-11-07 07:10:56 +08:00
const propTypes = {
intl: PropTypes.object.isRequired,
2018-11-07 07:10:56 +08:00
closeModal: PropTypes.func.isRequired,
startSharing: PropTypes.func.isRequired,
2019-12-19 01:50:16 +08:00
stopSharing: PropTypes.func.isRequired,
2019-07-24 06:24:31 +08:00
resolve: PropTypes.func,
camCapReached: PropTypes.bool,
2020-03-12 03:42:53 +08:00
hasVideoStream: PropTypes.bool.isRequired,
2019-07-27 00:48:51 +08:00
webcamDeviceId: PropTypes.string,
sharedDevices: PropTypes.arrayOf(PropTypes.string),
2018-11-07 07:10:56 +08:00
};
2019-07-24 06:24:31 +08:00
const defaultProps = {
resolve: null,
camCapReached: true,
2019-07-27 00:48:51 +08:00
webcamDeviceId: null,
2019-12-19 01:50:16 +08:00
sharedDevices: [],
2019-07-24 06:24:31 +08:00
};
2018-11-07 07:10:56 +08:00
const intlMessages = defineMessages({
webcamEffectsTitle: {
id: 'app.videoPreview.webcamEffectsTitle',
description: 'Title for the video effects modal',
},
2018-11-07 07:10:56 +08:00
webcamSettingsTitle: {
id: 'app.videoPreview.webcamSettingsTitle',
description: 'Title for the video preview modal',
},
closeLabel: {
id: 'app.videoPreview.closeLabel',
description: 'Close button label',
},
webcamPreviewLabel: {
id: 'app.videoPreview.webcamPreviewLabel',
description: 'Webcam preview label',
},
cameraLabel: {
id: 'app.videoPreview.cameraLabel',
description: 'Camera dropdown label',
},
qualityLabel: {
id: 'app.videoPreview.profileLabel',
description: 'Quality dropdown label',
},
low: {
id: 'app.videoPreview.quality.low',
description: 'Low quality option label',
},
medium: {
id: 'app.videoPreview.quality.medium',
description: 'Medium quality option label',
},
high: {
id: 'app.videoPreview.quality.high',
description: 'High quality option label',
},
hd: {
id: 'app.videoPreview.quality.hd',
description: 'High definition option label',
},
2018-11-07 07:10:56 +08:00
startSharingLabel: {
id: 'app.videoPreview.startSharingLabel',
2019-12-19 01:44:56 +08:00
description: 'Start sharing button label',
},
stopSharingLabel: {
id: 'app.videoPreview.stopSharingLabel',
description: 'Stop sharing button label',
},
2020-03-12 03:42:53 +08:00
stopSharingAllLabel: {
id: 'app.videoPreview.stopSharingAllLabel',
description: 'Stop sharing all button label',
},
2019-12-19 01:44:56 +08:00
sharedCameraLabel: {
id: 'app.videoPreview.sharedCameraLabel',
description: 'Already Shared camera label',
2018-11-07 07:10:56 +08:00
},
findingWebcamsLabel: {
id: 'app.videoPreview.findingWebcamsLabel',
description: 'Finding webcams label',
},
2018-11-07 07:10:56 +08:00
webcamOptionLabel: {
id: 'app.videoPreview.webcamOptionLabel',
description: 'Default webcam option label',
},
webcamNotFoundLabel: {
id: 'app.videoPreview.webcamNotFoundLabel',
description: 'Webcam not found label',
},
profileNotFoundLabel: {
id: 'app.videoPreview.profileNotFoundLabel',
description: 'Profile not found label',
},
permissionError: {
id: 'app.video.permissionError',
description: 'Error message for webcam permission',
},
AbortError: {
id: 'app.video.abortError',
description: 'Some problem occurred which prevented the device from being used',
},
OverconstrainedError: {
id: 'app.video.overconstrainedError',
description: 'No candidate devices which met the criteria requested',
},
SecurityError: {
id: 'app.video.securityError',
description: 'Media support is disabled on the Document',
},
TypeError: {
id: 'app.video.typeError',
description: 'List of constraints specified is empty, or has all constraints set to false',
},
NotFoundError: {
id: 'app.video.notFoundError',
description: 'error message when can not get webcam video',
},
NotAllowedError: {
id: 'app.video.notAllowed',
description: 'error message when webcam had permission denied',
},
NotSupportedError: {
id: 'app.video.notSupportedError',
description: 'error message when origin do not have ssl valid',
},
NotReadableError: {
id: 'app.video.notReadableError',
description: 'error message When the webcam is being used by other software',
},
TimeoutError: {
id: 'app.video.timeoutError',
description: 'error message when promise did not return',
},
iOSError: {
id: 'app.audioModal.iOSBrowser',
description: 'Audio/Video Not supported warning',
},
iOSErrorDescription: {
id: 'app.audioModal.iOSErrorDescription',
description: 'Audio/Video not supported description',
},
iOSErrorRecommendation: {
id: 'app.audioModal.iOSErrorRecommendation',
description: 'Audio/Video recommended action',
},
genericError: {
id: 'app.video.genericError',
description: 'error message for when the webcam sharing fails with unknown error',
},
camCapReached: {
id: 'app.video.camCapReached',
description: 'message for when the camera cap has been reached',
},
virtualBgGenericError: {
id: 'app.video.virtualBackground.genericError',
description: 'Failed to apply camera effect',
2021-07-20 21:41:14 +08:00
},
2018-11-07 07:10:56 +08:00
});
class VideoPreview extends Component {
constructor(props) {
super(props);
const {
webcamDeviceId,
2018-11-07 07:10:56 +08:00
} = props;
this.handleProceed = this.handleProceed.bind(this);
this.handleStartSharing = this.handleStartSharing.bind(this);
this.handleStopSharing = this.handleStopSharing.bind(this);
2020-03-12 03:42:53 +08:00
this.handleStopSharingAll = this.handleStopSharingAll.bind(this);
this.handleSelectWebcam = this.handleSelectWebcam.bind(this);
this.handleSelectProfile = this.handleSelectProfile.bind(this);
this.handleVirtualBgSelected = this.handleVirtualBgSelected.bind(this);
2019-04-09 06:07:26 +08:00
this._isMounted = false;
this.state = {
2019-01-18 00:33:43 +08:00
webcamDeviceId,
availableWebcams: null,
2019-04-09 06:07:26 +08:00
selectedProfile: null,
isStartSharingDisabled: true,
viewState: VIEW_STATES.finding,
deviceError: null,
previewError: null,
};
}
set currentVideoStream (bbbVideoStream) {
this._currentVideoStream = bbbVideoStream;
}
get currentVideoStream () {
return this._currentVideoStream;
}
componentDidMount() {
2019-11-21 02:35:16 +08:00
const {
webcamDeviceId,
forceOpen,
2019-11-21 02:35:16 +08:00
} = this.props;
this._isMounted = true;
if (deviceInfo.hasMediaDevices) {
navigator.mediaDevices.enumerateDevices().then((devices) => {
VideoService.updateNumberOfDevices(devices);
// Video preview skip is activated, short circuit via a simpler procedure
if (PreviewService.getSkipVideoPreview() && !forceOpen) return this.skipVideoPreview();
// Late enumerateDevices resolution, stop.
if (!this._isMounted) return;
let {
webcams,
areLabelled,
areIdentified
} = PreviewService.digestVideoDevices(devices, webcamDeviceId);
logger.debug({
logCode: 'video_preview_enumerate_devices',
extraInfo: {
devices,
webcams,
},
}, `Enumerate devices came back. There are ${devices.length} devices and ${webcams.length} are video inputs`);
if (webcams.length > 0) {
this.getInitialCameraStream(webcams[0].deviceId)
.then(async () => {
// Late gUM resolve, stop.
if (!this._isMounted) return;
if (!areLabelled || !areIdentified) {
// If they aren't labelled or have nullish deviceIds, run
// enumeration again and get their full versions
// Why: fingerprinting countermeasures obfuscate those when
// no permission was granted via gUM
try {
const newDevices = await navigator.mediaDevices.enumerateDevices();
webcams = PreviewService.digestVideoDevices(newDevices, webcamDeviceId).webcams;
} catch (error) {
// Not a critical error beucase it should only affect UI; log it
// and go ahead
logger.error({
logCode: 'video_preview_enumerate_relabel_failure',
extraInfo: {
errorName: error.name, errorMessage: error.message,
},
}, 'enumerateDevices for relabelling failed');
}
}
this.setState({
availableWebcams: webcams,
viewState: VIEW_STATES.found,
});
this.displayPreview();
});
} else {
// There were no webcams coming from enumerateDevices. Throw an error.
const noWebcamsError = new Error('NotFoundError');
this.handleDeviceError('enumerate', noWebcamsError, ': no webcams found');
}
}).catch((error) => {
// enumerateDevices failed
this.handleDeviceError('enumerate', error, 'enumerating devices');
});
} else {
// Top-level navigator.mediaDevices is not supported.
// The session went through the version checking, but somehow ended here.
// Nothing we can do.
const error = new Error('NotSupportedError');
this.handleDeviceError('mount', error, ': navigator.mediaDevices unavailable');
}
}
componentWillUnmount() {
const { webcamDeviceId } = this.state;
PreviewService.terminateCameraStream(this.currentVideoStream, webcamDeviceId);
this.cleanupStreamAndVideo();
this._isMounted = false;
}
2018-11-07 07:10:56 +08:00
handleSelectWebcam(event) {
const webcamValue = event.target.value;
2019-04-09 06:07:26 +08:00
this.getInitialCameraStream(webcamValue).then(() => {
this.displayPreview();
});
2019-04-09 06:07:26 +08:00
}
// Resolves into true if the background switch is successful, false otherwise
handleVirtualBgSelected(type, name, customParams) {
if (type !== EFFECT_TYPES.NONE_TYPE) {
return this.startVirtualBackground(this.currentVideoStream, type, name, customParams);
2021-07-20 21:41:14 +08:00
} else {
this.stopVirtualBackground(this.currentVideoStream);
return Promise.resolve(true);
2021-07-20 21:41:14 +08:00
}
}
stopVirtualBackground(bbbVideoStream) {
if (bbbVideoStream) {
bbbVideoStream.stopVirtualBackground();
this.displayPreview();
2021-07-20 21:41:14 +08:00
}
}
startVirtualBackground(bbbVideoStream, type, name, customParams) {
this.setState({ isStartSharingDisabled: true });
2021-07-20 21:41:14 +08:00
if (bbbVideoStream == null) return Promise.resolve(false);
2021-07-20 21:41:14 +08:00
return bbbVideoStream.startVirtualBackground(type, name, customParams).then(() => {
this.displayPreview();
return true;
}).catch(error => {
this.handleVirtualBgError(error, type, name);
return false;
}).finally(() => {
this.setState({ isStartSharingDisabled: false });
});
2021-07-20 21:41:14 +08:00
}
2019-04-09 06:07:26 +08:00
handleSelectProfile(event) {
const profileValue = event.target.value;
const { webcamDeviceId } = this.state;
const selectedProfile = PreviewService.getCameraProfile(profileValue);
this.getCameraStream(webcamDeviceId, selectedProfile).then(() => {
this.displayPreview();
});
2018-11-07 07:10:56 +08:00
}
handleStartSharing() {
2019-01-18 00:33:43 +08:00
const { resolve, startSharing } = this.props;
2019-11-28 00:19:09 +08:00
const { webcamDeviceId } = this.state;
// Only streams that will be shared should be stored in the service. // If the store call returns false, we're duplicating stuff. So clean this one
// up because it's an impostor.
if(!PreviewService.storeStream(webcamDeviceId, this.currentVideoStream)) {
this.currentVideoStream.stop();
}
// Update this session's virtual camera effect information if it's enabled
setSessionVirtualBackgroundInfo(
this.currentVideoStream.virtualBgType,
this.currentVideoStream.virtualBgName,
webcamDeviceId,
);
this.cleanupStreamAndVideo();
2019-11-28 00:19:09 +08:00
startSharing(webcamDeviceId);
if (resolve) resolve();
}
2019-12-19 01:44:56 +08:00
handleStopSharing() {
const { resolve, stopSharing } = this.props;
2019-12-19 01:44:56 +08:00
const { webcamDeviceId } = this.state;
PreviewService.deleteStream(webcamDeviceId);
2019-12-19 01:44:56 +08:00
stopSharing(webcamDeviceId);
this.cleanupStreamAndVideo();
if (resolve) resolve();
}
2020-03-12 03:42:53 +08:00
handleStopSharingAll() {
const { resolve, stopSharing } = this.props;
stopSharing();
if (resolve) resolve();
}
handleProceed() {
2019-01-18 00:33:43 +08:00
const { resolve, closeModal } = this.props;
const { webcamDeviceId } = this.state;
PreviewService.terminateCameraStream(this.currentVideoStream, webcamDeviceId);
2019-01-18 00:33:43 +08:00
closeModal();
if (resolve) resolve();
}
handlePreviewError(logCode, error, description) {
logger.warn({
logCode: `video_preview_${logCode}_error`,
extraInfo: {
errorName: error.name,
errorMessage: error.message,
},
}, `Error ${description}`);
this.setState({
previewError: this.handleGUMError(error),
});
}
handleDeviceError(logCode, error, description) {
logger.warn({
logCode: `video_preview_${logCode}_error`,
extraInfo: {
errorName: error.name,
errorMessage: error.message,
},
}, `Error ${description}`);
this.setState({
viewState: VIEW_STATES.error,
deviceError: this.handleGUMError(error),
});
}
handleGUMError(error) {
const { intl } = this.props;
logger.error({
logCode: 'video_preview_gum_failure',
extraInfo: {
errorName: error.name, errorMessage: error.message,
},
}, 'getUserMedia failed in video-preview');
const intlError = intlMessages[error.name] || intlMessages[error.message];
if (intlError) {
return intl.formatMessage(intlError);
}
return intl.formatMessage(intlMessages.genericError,
{ 0: `${error.name}: ${error.message}` });
}
cleanupStreamAndVideo() {
this.currentVideoStream = null;
if (this.video) this.video.srcObject = null;
}
handleVirtualBgError(error, type, name) {
const { intl } = this.props;
logger.error({
logCode: `video_preview_virtualbg_error`,
2021-07-20 21:41:14 +08:00
extraInfo: {
errorName: error.name,
errorMessage: error.message,
virtualBgType: type,
virtualBgName: name,
2021-07-20 21:41:14 +08:00
},
}, `Failed to toggle virtual background: ${error.message}`);
notify(intl.formatMessage(intlMessages.virtualBgGenericError), 'error', 'video');
2021-07-20 21:41:14 +08:00
}
updateDeviceId (deviceId) {
let actualDeviceId = deviceId;
if (!actualDeviceId && this.currentVideoStream) {
actualDeviceId = MediaStreamUtils.extractDeviceIdFromStream(
this.currentVideoStream.mediaStream,
'video',
);
}
this.setState({ webcamDeviceId: actualDeviceId, });
PreviewService.changeWebcam(actualDeviceId);
}
2019-04-09 06:07:26 +08:00
getInitialCameraStream(deviceId) {
const defaultProfile = PreviewService.getDefaultProfile();
return this.getCameraStream(deviceId, defaultProfile).then(() => {
this.updateDeviceId(deviceId);
});
2019-04-09 06:07:26 +08:00
}
getCameraStream(deviceId, profile) {
const { webcamDeviceId } = this.state;
this.setState({
selectedProfile: profile.id,
isStartSharingDisabled: true,
previewError: undefined,
});
PreviewService.changeProfile(profile.id);
PreviewService.terminateCameraStream(this.currentVideoStream, webcamDeviceId);
this.cleanupStreamAndVideo();
2019-04-09 06:07:26 +08:00
// The return of doGUM is an instance of BBBVideoStream (a thin wrapper over a MediaStream)
return PreviewService.doGUM(deviceId, profile).then((bbbVideoStream) => {
// Late GUM resolve, clean up tracks, stop.
if (!this._isMounted) return PreviewService.terminateCameraStream(bbbVideoStream, deviceId);
2019-04-09 06:07:26 +08:00
this.currentVideoStream = bbbVideoStream;
2019-04-09 06:07:26 +08:00
this.setState({
isStartSharingDisabled: false,
});
}).catch((error) => {
// When video preview is set to skip, we need some way to bubble errors
// up to users; so re-throw the error
if (!PreviewService.getSkipVideoPreview()) {
this.handlePreviewError('do_gum_preview', error, 'displaying final selection');
} else {
throw error;
}
2019-04-09 06:07:26 +08:00
});
}
displayPreview() {
if (this.currentVideoStream && this.video) {
this.video.srcObject = this.currentVideoStream.mediaStream;
}
}
skipVideoPreview() {
this.getInitialCameraStream().then(() => {
this.handleStartSharing();
}).catch(error => {
this.cleanupStreamAndVideo();
notify(this.handleGUMError(error), 'error', 'video');
});
}
supportWarning() {
const { intl } = this.props;
return (
<div>
2021-10-28 21:16:36 +08:00
<Styled.Warning>!</Styled.Warning>
<Styled.Main>{intl.formatMessage(intlMessages.iOSError)}</Styled.Main>
<Styled.Text>{intl.formatMessage(intlMessages.iOSErrorDescription)}</Styled.Text>
<Styled.Text>
{intl.formatMessage(intlMessages.iOSErrorRecommendation)}
2021-10-28 21:16:36 +08:00
</Styled.Text>
</div>
);
}
getFallbackLabel(webcam, index) {
const { intl } = this.props;
return `${intl.formatMessage(intlMessages.cameraLabel)} ${index}`
}
renderDeviceSelectors() {
2018-11-07 07:10:56 +08:00
const {
intl,
sharedDevices,
isVisualEffects,
2018-11-07 07:10:56 +08:00
} = this.props;
2019-01-18 00:33:43 +08:00
const {
webcamDeviceId,
availableWebcams,
2019-04-09 06:07:26 +08:00
selectedProfile,
} = this.state;
2019-12-19 01:44:56 +08:00
const shared = sharedDevices.includes(webcamDeviceId);
if (isVisualEffects) {
return (
<Styled.Col>
{isVirtualBackgroundsEnabled() && this.renderVirtualBgSelector()}
</Styled.Col>
)
}
return (
2021-10-28 21:16:36 +08:00
<Styled.Col>
<Styled.Label htmlFor="setCam">
{intl.formatMessage(intlMessages.cameraLabel)}
2021-10-28 21:16:36 +08:00
</Styled.Label>
2019-12-19 01:44:56 +08:00
{ availableWebcams && availableWebcams.length > 0
? (
2021-10-28 21:16:36 +08:00
<Styled.Select
2019-12-19 01:44:56 +08:00
id="setCam"
value={webcamDeviceId || ''}
onChange={this.handleSelectWebcam}
>
{availableWebcams.map((webcam, index) => (
2019-12-19 01:44:56 +08:00
<option key={webcam.deviceId} value={webcam.deviceId}>
{webcam.label || this.getFallbackLabel(webcam, index)}
2019-12-19 01:44:56 +08:00
</option>
))}
2021-10-28 21:16:36 +08:00
</Styled.Select>
2019-12-19 01:44:56 +08:00
)
: (
<span>
{intl.formatMessage(intlMessages.webcamNotFoundLabel)}
</span>
)
}
2019-12-19 01:44:56 +08:00
{ shared
? (
2021-10-28 21:16:36 +08:00
<Styled.Label>
{intl.formatMessage(intlMessages.sharedCameraLabel)}
2021-10-28 21:16:36 +08:00
</Styled.Label>
)
: (
<>
2021-10-28 21:16:36 +08:00
<Styled.Label htmlFor="setQuality">
{intl.formatMessage(intlMessages.qualityLabel)}
2021-10-28 21:16:36 +08:00
</Styled.Label>
{PreviewService.PREVIEW_CAMERA_PROFILES.length > 0
? (
2021-10-28 21:16:36 +08:00
<Styled.Select
id="setQuality"
value={selectedProfile || ''}
onChange={this.handleSelectProfile}
>
{PreviewService.PREVIEW_CAMERA_PROFILES.map((profile) => {
const label = intlMessages[`${profile.id}`]
? intl.formatMessage(intlMessages[`${profile.id}`])
: profile.name;
return (
<option key={profile.id} value={profile.id}>
{`${label}`}
</option>
);
})}
2021-10-28 21:16:36 +08:00
</Styled.Select>
)
: (
<span>
{intl.formatMessage(intlMessages.profileNotFoundLabel)}
</span>
)
}
</>
2019-12-19 01:44:56 +08:00
)
}
{isVirtualBackgroundsEnabled() && this.renderVirtualBgSelector()}
2021-10-28 21:16:36 +08:00
</Styled.Col>
);
}
renderVirtualBgSelector() {
const { isVisualEffects } = this.props;
const { isStartSharingDisabled, webcamDeviceId } = this.state;
const initialVirtualBgState = this.currentVideoStream ? {
type: this.currentVideoStream.virtualBgType,
name: this.currentVideoStream.virtualBgName
} : getSessionVirtualBackgroundInfo(webcamDeviceId);
return (
<VirtualBgSelector
handleVirtualBgSelected={this.handleVirtualBgSelected}
locked={isStartSharingDisabled}
showThumbnails={SHOW_THUMBNAILS}
initialVirtualBgState={initialVirtualBgState}
isVisualEffects={isVisualEffects}
/>
);
}
renderContent() {
const {
intl,
} = this.props;
const {
viewState,
deviceError,
previewError,
} = this.state;
2021-10-28 21:16:36 +08:00
const { animations } = Settings.application;
switch (viewState) {
case VIEW_STATES.finding:
return (
2021-10-28 21:16:36 +08:00
<Styled.Content>
<Styled.VideoCol>
<div>
<span>{intl.formatMessage(intlMessages.findingWebcamsLabel)}</span>
2021-10-28 21:16:36 +08:00
<Styled.FetchingAnimation animations={animations} />
</div>
2021-10-28 21:16:36 +08:00
</Styled.VideoCol>
</Styled.Content>
);
case VIEW_STATES.error:
return (
2021-10-28 21:16:36 +08:00
<Styled.Content>
<Styled.VideoCol><div>{deviceError}</div></Styled.VideoCol>
</Styled.Content>
);
case VIEW_STATES.found:
default:
return (
2021-10-28 21:16:36 +08:00
<Styled.Content>
<Styled.VideoCol>
{
previewError
? (
<div>{previewError}</div>
)
: (
2021-10-28 21:16:36 +08:00
<Styled.VideoPreview
mirroredVideo={VideoService.mirrorOwnWebcam()}
id="preview"
data-test={VideoService.mirrorOwnWebcam() ? 'mirroredVideoPreview' : 'videoPreview'}
ref={(ref) => { this.video = ref; }}
autoPlay
playsInline
muted
/>
)
}
2021-10-28 21:16:36 +08:00
</Styled.VideoCol>
{this.renderDeviceSelectors()}
2021-10-28 21:16:36 +08:00
</Styled.Content>
);
}
}
renderModalContent() {
const {
intl,
2019-12-19 01:44:56 +08:00
sharedDevices,
2020-03-12 03:42:53 +08:00
hasVideoStream,
forceOpen,
camCapReached,
isVisualEffects,
} = this.props;
const {
2019-01-18 00:33:43 +08:00
isStartSharingDisabled,
2019-12-19 01:44:56 +08:00
webcamDeviceId,
deviceError,
previewError,
2019-01-18 00:33:43 +08:00
} = this.state;
const shouldDisableButtons = PreviewService.getSkipVideoPreview()
&& !forceOpen
&& !(deviceError || previewError);
2019-12-19 01:44:56 +08:00
const shared = sharedDevices.includes(webcamDeviceId);
const { isIe } = browserInfo;
const title = isVisualEffects
? intl.formatMessage(intlMessages.webcamEffectsTitle)
: intl.formatMessage(intlMessages.webcamSettingsTitle);
2018-11-07 07:10:56 +08:00
return (
2021-11-11 01:52:58 +08:00
<>
{isIe ? (
2021-10-28 21:16:36 +08:00
<Styled.BrowserWarning>
<FormattedMessage
id="app.audioModal.unsupportedBrowserLabel"
description="Warning when someone joins with a browser that isnt supported"
values={{
0: <a href="https://www.google.com/chrome/">Chrome</a>,
1: <a href="https://getfirefox.com">Firefox</a>,
}}
/>
2021-10-28 21:16:36 +08:00
</Styled.BrowserWarning>
2019-07-24 06:24:31 +08:00
) : null}
2021-10-28 21:16:36 +08:00
<Styled.Title>
{title}
2021-10-28 21:16:36 +08:00
</Styled.Title>
{this.renderContent()}
{!isVisualEffects ? (
<Styled.Footer>
{hasVideoStream && VideoService.isMultipleCamerasEnabled()
? (
<Styled.ExtraActions>
<Button
color="danger"
label={intl.formatMessage(intlMessages.stopSharingAllLabel)}
onClick={this.handleStopSharingAll}
disabled={shouldDisableButtons}
/>
</Styled.ExtraActions>
)
: null
}
<Styled.Actions>
{!shared && camCapReached ? (
<span>{intl.formatMessage(intlMessages.camCapReached)}</span>
) : (<Button
data-test="startSharingWebcam"
color={shared ? 'danger' : 'primary'}
label={intl.formatMessage(shared ? intlMessages.stopSharingLabel : intlMessages.startSharingLabel)}
onClick={shared ? this.handleStopSharing : this.handleStartSharing}
disabled={isStartSharingDisabled || isStartSharingDisabled === null || shouldDisableButtons}
/>)}
</Styled.Actions>
</Styled.Footer>
) : null }
2021-11-11 01:52:58 +08:00
</>
);
}
render() {
const {
intl,
isCamLocked,
forceOpen,
} = this.props;
if (isCamLocked === true) {
this.handleProceed();
return null;
}
if (PreviewService.getSkipVideoPreview() && !forceOpen) {
return null;
}
const {
deviceError,
previewError,
} = this.state;
const allowCloseModal = !!(deviceError || previewError)
|| !PreviewService.getSkipVideoPreview()
|| forceOpen;
return (
2021-10-28 21:16:36 +08:00
<Styled.VideoPreviewModal
onRequestClose={this.handleProceed}
hideBorder
contentLabel={intl.formatMessage(intlMessages.webcamSettingsTitle)}
shouldShowCloseButton={allowCloseModal}
shouldCloseOnOverlayClick={allowCloseModal}
2021-12-10 22:55:37 +08:00
isPhone={deviceInfo.isPhone}
2022-01-20 21:03:18 +08:00
data-test="webcamSettingsModal"
>
{deviceInfo.hasMediaDevices
? this.renderModalContent()
: this.supportWarning()
2019-07-24 06:24:31 +08:00
}
2021-10-28 21:16:36 +08:00
</Styled.VideoPreviewModal>
2018-11-07 07:10:56 +08:00
);
}
}
VideoPreview.propTypes = propTypes;
2019-07-24 06:24:31 +08:00
VideoPreview.defaultProps = defaultProps;
2018-11-07 07:10:56 +08:00
export default injectIntl(VideoPreview);