diff --git a/react/features/base/media/components/web/Video.js b/react/features/base/media/components/web/Video.js
index e69de29bb..82c7604e5 100644
--- a/react/features/base/media/components/web/Video.js
+++ b/react/features/base/media/components/web/Video.js
@@ -0,0 +1,218 @@
+import React, { Component } from 'react';
+
+/**
+ * Component that renders a video element for a passed in video track.
+ *
+ * @extends Component
+ */
+class Video extends Component {
+ /**
+ * Default values for {@code Video} component's properties.
+ *
+ * @static
+ */
+ static defaultProps = {
+ className: '',
+
+ id: ''
+ };
+
+ /**
+ * {@code Video} component's property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ /**
+ * CSS classes to add to the video element.
+ */
+ className: React.PropTypes.string,
+
+ /**
+ * The value of the id attribute of the video. Used by the torture tests
+ * to locate video elements.
+ */
+ id: React.PropTypes.string,
+
+ /**
+ * Optional callback to invoke once the video starts playing.
+ */
+ onVideoPlaying: React.PropTypes.func,
+
+ /**
+ * The JitsiLocalTrack to display.
+ */
+ videoTrack: React.PropTypes.object
+ };
+
+ /**
+ * Initializes a new {@code Video} instance.
+ *
+ * @param {Object} props - The read-only properties with which the new
+ * instance is to be initialized.
+ */
+ constructor(props) {
+ super(props);
+
+ /**
+ * 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._onVideoPlaying = this._onVideoPlaying.bind(this);
+ this._setVideoElement = this._setVideoElement.bind(this);
+ }
+
+ /**
+ * Invokes the library for rendering the video on initial display. Sets the
+ * volume level to zero to ensure no sound plays.
+ *
+ * @inheritdoc
+ * @returns {void}
+ */
+ componentDidMount() {
+ // Add these attributes directly onto the video element so temasys can
+ // use them when converting the video to an object.
+ this._videoElement.volume = 0;
+ this._videoElement.onplaying = this._onVideoPlaying;
+
+ this._attachTrack(this.props.videoTrack);
+ }
+
+ /**
+ * Remove any existing associations between the current video track and the
+ * component's video element.
+ *
+ * @inheritdoc
+ * @returns {void}
+ */
+ componentWillUnmount() {
+ this._detachTrack(this.props.videoTrack);
+ }
+
+ /**
+ * Updates the video display only if a new track is added. This component's
+ * updating is blackboxed from React to prevent re-rendering of video
+ * element, as the lib uses track.attach(videoElement) instead. Also,
+ * re-rendering cannot be used with temasys, which replaces video elements
+ * with an object.
+ *
+ * @inheritdoc
+ * @returns {boolean} - False is always returned to blackbox this component.
+ * from React.
+ */
+ shouldComponentUpdate(nextProps) {
+ const currentJitsiTrack = this.props.videoTrack
+ && this.props.videoTrack.jitsiTrack;
+ const nextJitsiTrack = nextProps.videoTrack
+ && nextProps.videoTrack.jitsiTrack;
+
+ if (currentJitsiTrack !== nextJitsiTrack) {
+ this._detachTrack(this.props.videoTrack);
+ this._attachTrack(nextProps.videoTrack);
+ }
+
+ return false;
+ }
+
+ /**
+ * Renders the video element.
+ *
+ * @override
+ * @returns {ReactElement}
+ */
+ render() {
+ // The wrapping div is necessary because temasys will replace the video
+ // with an object but react will keep expecting the video element. The
+ // div gives a constant element for react to keep track of.
+ return (
+
+
+
+ );
+ }
+
+ /**
+ * Calls into the passed in track to associate the track with the
+ * component's video element and render video.
+ *
+ * @param {Object} videoTrack - The redux representation of the
+ * {@code JitsiLocalTrack}.
+ * @private
+ * @returns {void}
+ */
+ _attachTrack(videoTrack) {
+ if (!videoTrack || !videoTrack.jitsiTrack) {
+ return;
+ }
+
+ const updatedVideoElement
+ = videoTrack.jitsiTrack.attach(this._videoElement);
+
+ // Sets the instance variable for the video element again as the element
+ // maybe have been replaced with a new object by temasys.
+ this._setVideoElement(updatedVideoElement);
+ }
+
+ /**
+ * Removes the association to the component's video element from the passed
+ * in redux representation of jitsi video track to stop the track from
+ * rendering. With temasys, the video element must still be visible for
+ * detaching to complete.
+ *
+ * @param {Object} videoTrack - The redux representation of the
+ * {@code JitsiLocalTrack}.
+ * @private
+ * @returns {void}
+ */
+ _detachTrack(videoTrack) {
+ // 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
+ && videoTrack
+ && videoTrack.jitsiTrack
+ && videoTrack.jitsiTrack.containers.includes(this._videoElement)) {
+ videoTrack.jitsiTrack.detach(this._videoElement);
+ }
+ }
+
+ /**
+ * Invokes the onvideoplaying callback if defined.
+ *
+ * @private
+ * @returns {void}
+ */
+ _onVideoPlaying() {
+ if (this.props.onVideoPlaying) {
+ this.props.onVideoPlaying();
+ }
+ }
+
+ /**
+ * 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;
+ }
+}
+
+export default Video;
diff --git a/react/features/base/media/components/web/VideoTrack.js b/react/features/base/media/components/web/VideoTrack.js
index 7e891728d..506a7a5e0 100644
--- a/react/features/base/media/components/web/VideoTrack.js
+++ b/react/features/base/media/components/web/VideoTrack.js
@@ -3,6 +3,8 @@ import { connect } from 'react-redux';
import AbstractVideoTrack from '../AbstractVideoTrack';
+import Video from './Video';
+
/**
* Component that renders a video element for a passed in video track.
*
@@ -42,82 +44,6 @@ class VideoTrack extends AbstractVideoTrack {
id: React.PropTypes.string
};
- /**
- * Initializes a new VideoTrack instance.
- *
- * @param {Object} props - The read-only properties with which the new
- * instance is to be initialized.
- */
- constructor(props) {
- super(props);
-
- /**
- * 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._setVideoElement = this._setVideoElement.bind(this);
- }
-
- /**
- * Invokes the library for rendering the video on initial display. Sets the
- * volume level to zero to ensure no sound plays.
- *
- * @inheritdoc
- * @returns {void}
- */
- componentDidMount() {
- // Add these attributes directly onto the video element so temasys can
- // use them when converting the video to an object.
- this._videoElement.volume = 0;
- this._videoElement.onplaying = this._onVideoPlaying;
-
- this._attachTrack(this.props.videoTrack);
- }
-
- /**
- * Remove any existing associations between the current video track and the
- * component's video element.
- *
- * @inheritdoc
- * @returns {void}
- */
- componentWillUnmount() {
- this._detachTrack(this.props.videoTrack);
- }
-
- /**
- * Updates the video display only if a new track is added. This component's
- * updating is blackboxed from React to prevent re-rendering of video
- * element, as the lib uses track.attach(videoElement) instead. Also,
- * re-rendering cannot be used with temasys, which replaces video elements
- * with an object.
- *
- * @inheritdoc
- * @returns {boolean} - False is always returned to blackbox this component.
- * from React.
- */
- shouldComponentUpdate(nextProps) {
- const currentJitsiTrack = this.props.videoTrack
- && this.props.videoTrack.jitsiTrack;
- const nextJitsiTrack = nextProps.videoTrack
- && nextProps.videoTrack.jitsiTrack;
-
- if (currentJitsiTrack !== nextJitsiTrack) {
- this._detachTrack(this.props.videoTrack);
- this._attachTrack(nextProps.videoTrack);
- }
-
- return false;
- }
-
/**
* Renders the video element.
*
@@ -125,78 +51,15 @@ class VideoTrack extends AbstractVideoTrack {
* @returns {ReactElement}
*/
render() {
- // The wrapping div is necessary because temasys will replace the video
- // with an object but react will keep expecting the video element. The
- // div gives a constant element for react to keep track of.
return (
-
-
-
+
);
}
-
- /**
- * Calls into the passed in track to associate the track with the
- * component's video element and render video.
- *
- * @param {Object} videoTrack - The redux representation of the
- * {@code JitsiLocalTrack}.
- * @private
- * @returns {void}
- */
- _attachTrack(videoTrack) {
- if (!videoTrack || !videoTrack.jitsiTrack) {
- return;
- }
-
- const updatedVideoElement
- = videoTrack.jitsiTrack.attach(this._videoElement);
-
- // Sets the instance variable for the video element again as the element
- // maybe have been replaced with a new object by temasys.
- this._setVideoElement(updatedVideoElement);
- }
-
- /**
- * Removes the association to the component's video element from the passed
- * in redux representation of jitsi video track to stop the track from
- * rendering. With temasys, the video element must still be visible for
- * detaching to complete.
- *
- * @param {Object} videoTrack - The redux representation of the
- * {@code JitsiLocalTrack}.
- * @private
- * @returns {void}
- */
- _detachTrack(videoTrack) {
- // 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
- && videoTrack
- && videoTrack.jitsiTrack
- && videoTrack.jitsiTrack.containers.includes(this._videoElement)) {
- videoTrack.jitsiTrack.detach(this._videoElement);
- }
- }
-
- /**
- * 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;
- }
}
export default connect()(VideoTrack);
diff --git a/react/features/device-selection/components/VideoInputPreview.js b/react/features/device-selection/components/VideoInputPreview.js
index 70921b933..d43e55d11 100644
--- a/react/features/device-selection/components/VideoInputPreview.js
+++ b/react/features/device-selection/components/VideoInputPreview.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
-import { VideoTrack } from '../../base/media';
+import { Video } from '../../base/media';
const VIDEO_ERROR_CLASS = 'video-preview-has-error';
@@ -42,7 +42,7 @@ class VideoInputPreview extends Component {
return (
-