Device selection has live previews that reuse the current local audio and video tracks for the sake of internet explorer. This means when the local video was muted, device selection would show a muted message. It is preferred to show a live preview even when muted. The changes include: - Passing device ids into DeviceSelectionDialog, not tracks. - Setting default selected devices to use for live previews. - Removing all checks in DeviceSelectionDialog involving local tracks. - Catching and displaying errors when creating a live video preview.
256 lines
7.6 KiB
JavaScript
256 lines
7.6 KiB
JavaScript
import React, { Component } from 'react';
|
|
|
|
import { translate } from '../../base/i18n';
|
|
|
|
const VIDEO_ERROR_CLASS = 'video-preview-has-error';
|
|
|
|
/**
|
|
* React component for displaying video. This component defers to lib-jitsi-meet
|
|
* logic for rendering the video.
|
|
*
|
|
* @extends Component
|
|
*/
|
|
class VideoInputPreview extends Component {
|
|
/**
|
|
* VideoInputPreview component's property types.
|
|
*
|
|
* @static
|
|
*/
|
|
static propTypes = {
|
|
/**
|
|
* An error message to display instead of a preview. Displaying an error
|
|
* will take priority over displaying a video preview.
|
|
*/
|
|
error: React.PropTypes.string,
|
|
|
|
/**
|
|
* Invoked to obtain translated strings.
|
|
*/
|
|
t: React.PropTypes.func,
|
|
|
|
/**
|
|
* The JitsiLocalTrack to display.
|
|
*/
|
|
track: React.PropTypes.object
|
|
}
|
|
|
|
/**
|
|
* Initializes a new VideoInputPreview instance.
|
|
*
|
|
* @param {Object} props - The read-only React Component props with which
|
|
* the new instance is to be initialized.
|
|
*/
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
/**
|
|
* The internal reference to the DOM/HTML element intended for showing
|
|
* error messages.
|
|
*
|
|
* @private
|
|
* @type {HTMLDivElement}
|
|
*/
|
|
this._errorElement = null;
|
|
|
|
/**
|
|
* The internal reference to topmost DOM/HTML element backing the React
|
|
* {@code Component}. Accessed directly for toggling a classname to
|
|
* indicate an error is present so styling can be changed to display it.
|
|
*
|
|
* @private
|
|
* @type {HTMLDivElement}
|
|
*/
|
|
this._rootElement = null;
|
|
|
|
/**
|
|
* The internal reference to the DOM/HTML element intended for
|
|
* displaying a video. This element may be an HTML video element or a
|
|
* temasys video object.
|
|
*
|
|
* @private
|
|
* @type {HTMLVideoElement|Object}
|
|
*/
|
|
this._videoElement = null;
|
|
|
|
// Bind event handlers so they are only bound once for every instance.
|
|
this._setErrorElement = this._setErrorElement.bind(this);
|
|
this._setRootElement = this._setRootElement.bind(this);
|
|
this._setVideoElement = this._setVideoElement.bind(this);
|
|
}
|
|
|
|
/**
|
|
* Invokes the library for rendering the video on initial display.
|
|
*
|
|
* @inheritdoc
|
|
* @returns {void}
|
|
*/
|
|
componentDidMount() {
|
|
if (this.props.error) {
|
|
this._updateErrorView(this.props.error);
|
|
} else {
|
|
this._attachTrack(this.props.track);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove any existing associations between the current previewed track and
|
|
* the component's video element.
|
|
*
|
|
* @inheritdoc
|
|
* @returns {void}
|
|
*/
|
|
componentWillUnmount() {
|
|
this._detachTrack(this.props.track);
|
|
}
|
|
|
|
/**
|
|
* Implements React's {@link Component#render()}.
|
|
*
|
|
* @inheritdoc
|
|
* @returns {ReactElement}
|
|
*/
|
|
render() {
|
|
return (
|
|
<div
|
|
className = 'video-input-preview'
|
|
ref = { this._setRootElement }>
|
|
<video
|
|
autoPlay = { true }
|
|
className = 'video-input-preview-display flipVideoX'
|
|
ref = { this._setVideoElement } />
|
|
<div
|
|
className = 'video-input-preview-error'
|
|
ref = { this._setErrorElement } />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Only update when the deviceId has changed. This component is somewhat
|
|
* black-boxed from React's rendering so lib-jitsi-meet can instead handle
|
|
* updating of the video preview, which takes browser differences into
|
|
* consideration. For example, temasys's video object must be visible to
|
|
* update the displayed track, but React's re-rendering could potentially
|
|
* remove the video object from the page.
|
|
*
|
|
* @inheritdoc
|
|
* @returns {void}
|
|
*/
|
|
shouldComponentUpdate(nextProps) {
|
|
const hasNewTrack = nextProps.track !== this.props.track;
|
|
|
|
if (hasNewTrack || nextProps.error) {
|
|
this._detachTrack(this.props.track);
|
|
this._updateErrorView(nextProps.error);
|
|
}
|
|
|
|
// Never attempt to show the new track if there is an error present.
|
|
if (hasNewTrack && !nextProps.error) {
|
|
this._attachTrack(nextProps.track);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Calls into the passed in track to associate the track with the
|
|
* component's video element and render video. Also sets the instance
|
|
* variable for the video element as the element the track attached to,
|
|
* which could be an Object if on a temasys supported browser.
|
|
*
|
|
* @param {JitsiLocalTrack} track - The library's track model which will be
|
|
* displayed.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_attachTrack(track) {
|
|
if (!track) {
|
|
return;
|
|
}
|
|
|
|
const updatedVideoElement = track.attach(this._videoElement);
|
|
|
|
this._setVideoElement(updatedVideoElement);
|
|
}
|
|
|
|
/**
|
|
* Removes the association to the component's video element from the passed
|
|
* in JitsiLocalTrack to stop the track from rendering. With temasys, the
|
|
* video element must still be visible for detaching to complete.
|
|
*
|
|
* @param {JitsiLocalTrack} track - The library's track model which needs
|
|
* to stop previewing in the video element.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_detachTrack(track) {
|
|
// Detach the video element from the track only if it has already
|
|
// been attached. This accounts for a special case with temasys
|
|
// where if detach is being called before attach, the video
|
|
// element is converted to Object without updating this
|
|
// component's reference to the video element.
|
|
if (this._videoElement
|
|
&& track
|
|
&& track.containers.includes(this._videoElement)) {
|
|
track.detach(this._videoElement);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets an instance variable for the component's element intended for
|
|
* displaying error messages. The element will be accessed directly to
|
|
* display an error message.
|
|
*
|
|
* @param {Object} element - DOM element intended for displaying errors.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_setErrorElement(element) {
|
|
this._errorElement = element;
|
|
}
|
|
|
|
/**
|
|
* Sets the component's root element.
|
|
*
|
|
* @param {Object} element - The highest DOM element in the component.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_setRootElement(element) {
|
|
this._rootElement = element;
|
|
}
|
|
|
|
/**
|
|
* Sets an instance variable for the component's video element so it can be
|
|
* referenced later for attaching and detaching a JitsiLocalTrack.
|
|
*
|
|
* @param {Object} element - DOM element for the component's video display.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_setVideoElement(element) {
|
|
this._videoElement = element;
|
|
}
|
|
|
|
/**
|
|
* Adds or removes a class to the component's parent node to indicate an
|
|
* error has occurred. Also sets the error text.
|
|
*
|
|
* @param {string} error - The error message to display. If falsy, error
|
|
* message display will be hidden.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_updateErrorView(error) {
|
|
if (error) {
|
|
this._rootElement.classList.add(VIDEO_ERROR_CLASS);
|
|
} else {
|
|
this._rootElement.classList.remove(VIDEO_ERROR_CLASS);
|
|
}
|
|
|
|
this._errorElement.innerText = error || '';
|
|
}
|
|
}
|
|
|
|
export default translate(VideoInputPreview);
|