jiti-meet/react/features/base/media/components/web/Video.js

349 lines
8.4 KiB
JavaScript

/* @flow */
import React, { Component } from 'react';
/**
* The type of the React {@code Component} props of {@link Video}.
*/
type Props = {
/**
* CSS classes to add to the video element.
*/
className: string,
/**
* The value of the id attribute of the video. Used by the torture tests to
* locate video elements.
*/
id: string,
/**
* Optional callback to invoke once the video starts playing.
*/
onVideoPlaying?: Function,
/**
* The JitsiLocalTrack to display.
*/
videoTrack: ?Object,
/**
* Used to determine the value of the autoplay attribute of the underlying
* video element.
*/
autoPlay: boolean,
/**
* Used to determine the value of the autoplay attribute of the underlying
* video element.
*/
playsinline: boolean,
/**
* A map of the event handlers for the video HTML element.
*/
eventHandlers?: {|
/**
* OnAbort event handler.
*/
onAbort?: ?Function,
/**
* OnCanPlay event handler.
*/
onCanPlay?: ?Function,
/**
* OnCanPlayThrough event handler.
*/
onCanPlayThrough?: ?Function,
/**
* OnEmptied event handler.
*/
onEmptied?: ?Function,
/**
* OnEnded event handler.
*/
onEnded?: ?Function,
/**
* OnError event handler.
*/
onError?: ?Function,
/**
* OnLoadedData event handler.
*/
onLoadedData?: ?Function,
/**
* OnLoadedMetadata event handler.
*/
onLoadedMetadata?: ?Function,
/**
* OnLoadStart event handler.
*/
onLoadStart?: ?Function,
/**
* OnPause event handler.
*/
onPause?: ?Function,
/**
* OnPlay event handler.
*/
onPlay?: ?Function,
/**
* OnPlaying event handler.
*/
onPlaying?: ?Function,
/**
* OnRateChange event handler.
*/
onRateChange?: ?Function,
/**
* OnStalled event handler.
*/
onStalled?: ?Function,
/**
* OnSuspend event handler.
*/
onSuspend?: ?Function,
/**
* OnWaiting event handler.
*/
onWaiting?: ?Function
|},
/**
* A styles that will be applied on the video element.
*/
style?: Object,
/**
* The value of the muted attribute for the underlying video element.
*/
muted?: boolean
};
/**
* Component that renders a video element for a passed in video track.
*
* @augments Component
*/
class Video extends Component<Props> {
_videoElement: ?Object;
_mounted: boolean;
/**
* Default values for {@code Video} component's properties.
*
* @static
*/
static defaultProps = {
className: '',
autoPlay: true,
id: '',
playsinline: true
};
/**
* Initializes a new {@code Video} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props: Props) {
super(props);
/**
* The internal reference to the DOM/HTML element intended for
* displaying a video.
*
* @private
* @type {HTMLVideoElement}
*/
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() {
this._mounted = true;
if (this._videoElement) {
this._videoElement.volume = 0;
this._videoElement.onplaying = this._onVideoPlaying;
}
this._attachTrack(this.props.videoTrack);
if (this._videoElement && this.props.autoPlay) {
// Ensure the video gets play() called on it. This may be necessary in the
// case where the local video container was moved and re-attached, in which
// case video does not autoplay.
this._videoElement.play()
.catch(error => {
// Prevent uncaught "DOMException: The play() request was interrupted by a new load request"
// when video playback takes long to start and it starts after the component was unmounted.
if (this._mounted) {
throw error;
}
});
}
}
/**
* Remove any existing associations between the current video track and the
* component's video element.
*
* @inheritdoc
* @returns {void}
*/
componentWillUnmount() {
this._mounted = false;
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 {@code track.attach(videoElement)} instead.
*
* @inheritdoc
* @returns {boolean} - False is always returned to blackbox this component
* from React.
*/
shouldComponentUpdate(nextProps: Props) {
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);
}
if (this.props.style !== nextProps.style || this.props.className !== nextProps.className) {
return true;
}
return false;
}
/**
* Renders the video element.
*
* @override
* @returns {ReactElement}
*/
render() {
const {
autoPlay,
className,
id,
muted,
playsinline,
style,
eventHandlers
} = this.props;
return (
<video
autoPlay = { autoPlay }
className = { className }
id = { id }
muted = { muted }
playsInline = { playsinline }
ref = { this._setVideoElement }
style = { style }
{ ...eventHandlers } />
);
}
/**
* 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;
}
videoTrack.jitsiTrack.attach(this._videoElement);
}
/**
* 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.
*
* @param {Object} videoTrack - The redux representation of the
* {@code JitsiLocalTrack}.
* @private
* @returns {void}
*/
_detachTrack(videoTrack) {
if (this._videoElement && videoTrack && videoTrack.jitsiTrack) {
videoTrack.jitsiTrack.detach(this._videoElement);
}
}
_onVideoPlaying: () => void;
/**
* Invokes the onvideoplaying callback if defined.
*
* @private
* @returns {void}
*/
_onVideoPlaying() {
if (this.props.onVideoPlaying) {
this.props.onVideoPlaying();
}
}
_setVideoElement: () => void;
/**
* 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;