You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Video.tsx 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. import React, { Component, ReactEventHandler } from 'react';
  2. import { ITrack } from '../../../tracks/types';
  3. /**
  4. * The type of the React {@code Component} props of {@link Video}.
  5. */
  6. interface IProps {
  7. /**
  8. * Used to determine the value of the autoplay attribute of the underlying
  9. * video element.
  10. */
  11. autoPlay: boolean;
  12. /**
  13. * CSS classes to add to the video element.
  14. */
  15. className: string;
  16. /**
  17. * A map of the event handlers for the video HTML element.
  18. */
  19. eventHandlers?: {
  20. /**
  21. * OnAbort event handler.
  22. */
  23. onAbort?: ReactEventHandler<HTMLVideoElement>;
  24. /**
  25. * OnCanPlay event handler.
  26. */
  27. onCanPlay?: ReactEventHandler<HTMLVideoElement>;
  28. /**
  29. * OnCanPlayThrough event handler.
  30. */
  31. onCanPlayThrough?: ReactEventHandler<HTMLVideoElement>;
  32. /**
  33. * OnEmptied event handler.
  34. */
  35. onEmptied?: ReactEventHandler<HTMLVideoElement>;
  36. /**
  37. * OnEnded event handler.
  38. */
  39. onEnded?: ReactEventHandler<HTMLVideoElement>;
  40. /**
  41. * OnError event handler.
  42. */
  43. onError?: ReactEventHandler<HTMLVideoElement>;
  44. /**
  45. * OnLoadStart event handler.
  46. */
  47. onLoadStart?: ReactEventHandler<HTMLVideoElement>;
  48. /**
  49. * OnLoadedData event handler.
  50. */
  51. onLoadedData?: ReactEventHandler<HTMLVideoElement>;
  52. /**
  53. * OnLoadedMetadata event handler.
  54. */
  55. onLoadedMetadata?: ReactEventHandler<HTMLVideoElement>;
  56. /**
  57. * OnPause event handler.
  58. */
  59. onPause?: ReactEventHandler<HTMLVideoElement>;
  60. /**
  61. * OnPlay event handler.
  62. */
  63. onPlay?: ReactEventHandler<HTMLVideoElement>;
  64. /**
  65. * OnPlaying event handler.
  66. */
  67. onPlaying?: ReactEventHandler<HTMLVideoElement>;
  68. /**
  69. * OnRateChange event handler.
  70. */
  71. onRateChange?: ReactEventHandler<HTMLVideoElement>;
  72. /**
  73. * OnStalled event handler.
  74. */
  75. onStalled?: ReactEventHandler<HTMLVideoElement>;
  76. /**
  77. * OnSuspend event handler.
  78. */
  79. onSuspend?: ReactEventHandler<HTMLVideoElement>;
  80. /**
  81. * OnWaiting event handler.
  82. */
  83. onWaiting?: ReactEventHandler<HTMLVideoElement>;
  84. };
  85. /**
  86. * The value of the id attribute of the video. Used by the torture tests to
  87. * locate video elements.
  88. */
  89. id: string;
  90. /**
  91. * Used on native.
  92. */
  93. mirror?: boolean;
  94. /**
  95. * The value of the muted attribute for the underlying video element.
  96. */
  97. muted?: boolean;
  98. /**
  99. * Used on native.
  100. */
  101. onPlaying?: Function;
  102. /**
  103. * Used on native.
  104. */
  105. onPress?: Function;
  106. /**
  107. * Optional callback to invoke once the video starts playing.
  108. */
  109. onVideoPlaying?: Function;
  110. /**
  111. * Used to determine the value of the autoplay attribute of the underlying
  112. * video element.
  113. */
  114. playsinline: boolean;
  115. /**
  116. * Used on native.
  117. */
  118. stream?: any;
  119. /**
  120. * A styles that will be applied on the video element.
  121. */
  122. style?: Object;
  123. /**
  124. * The JitsiLocalTrack to display.
  125. */
  126. videoTrack?: Partial<ITrack>;
  127. /**
  128. * Used on native.
  129. */
  130. zOrder?: number;
  131. /**
  132. * Used on native.
  133. */
  134. zoomEnabled?: boolean;
  135. }
  136. /**
  137. * Component that renders a video element for a passed in video track.
  138. *
  139. * @augments Component
  140. */
  141. class Video extends Component<IProps> {
  142. _videoElement?: HTMLVideoElement | null;
  143. _mounted: boolean;
  144. /**
  145. * Default values for {@code Video} component's properties.
  146. *
  147. * @static
  148. */
  149. static defaultProps = {
  150. className: '',
  151. autoPlay: true,
  152. id: '',
  153. playsinline: true
  154. };
  155. /**
  156. * Initializes a new {@code Video} instance.
  157. *
  158. * @param {Object} props - The read-only properties with which the new
  159. * instance is to be initialized.
  160. */
  161. constructor(props: IProps) {
  162. super(props);
  163. /**
  164. * The internal reference to the DOM/HTML element intended for
  165. * displaying a video.
  166. *
  167. * @private
  168. * @type {HTMLVideoElement}
  169. */
  170. this._videoElement = null;
  171. // Bind event handlers so they are only bound once for every instance.
  172. this._onVideoPlaying = this._onVideoPlaying.bind(this);
  173. this._setVideoElement = this._setVideoElement.bind(this);
  174. }
  175. /**
  176. * Invokes the library for rendering the video on initial display. Sets the
  177. * volume level to zero to ensure no sound plays.
  178. *
  179. * @inheritdoc
  180. * @returns {void}
  181. */
  182. componentDidMount() {
  183. this._mounted = true;
  184. if (this._videoElement) {
  185. this._videoElement.volume = 0;
  186. this._videoElement.onplaying = this._onVideoPlaying;
  187. }
  188. this._attachTrack(this.props.videoTrack);
  189. if (this._videoElement && this.props.autoPlay) {
  190. // Ensure the video gets play() called on it. This may be necessary in the
  191. // case where the local video container was moved and re-attached, in which
  192. // case video does not autoplay.
  193. this._videoElement.play()
  194. .catch(error => {
  195. // Prevent uncaught "DOMException: The play() request was interrupted by a new load request"
  196. // when video playback takes long to start and it starts after the component was unmounted.
  197. if (this._mounted) {
  198. throw error;
  199. }
  200. });
  201. }
  202. }
  203. /**
  204. * Remove any existing associations between the current video track and the
  205. * component's video element.
  206. *
  207. * @inheritdoc
  208. * @returns {void}
  209. */
  210. componentWillUnmount() {
  211. this._mounted = false;
  212. this._detachTrack(this.props.videoTrack);
  213. }
  214. /**
  215. * Updates the video display only if a new track is added. This component's
  216. * updating is blackboxed from React to prevent re-rendering of video
  217. * element, as the lib uses {@code track.attach(videoElement)} instead.
  218. *
  219. * @inheritdoc
  220. * @returns {boolean} - False is always returned to blackbox this component
  221. * from React.
  222. */
  223. shouldComponentUpdate(nextProps: IProps) {
  224. const currentJitsiTrack = this.props.videoTrack?.jitsiTrack;
  225. const nextJitsiTrack = nextProps.videoTrack?.jitsiTrack;
  226. if (currentJitsiTrack !== nextJitsiTrack) {
  227. this._detachTrack(this.props.videoTrack);
  228. this._attachTrack(nextProps.videoTrack);
  229. }
  230. if (this.props.style !== nextProps.style || this.props.className !== nextProps.className) {
  231. return true;
  232. }
  233. return false;
  234. }
  235. /**
  236. * Renders the video element.
  237. *
  238. * @override
  239. * @returns {ReactElement}
  240. */
  241. render() {
  242. const {
  243. autoPlay,
  244. className,
  245. id,
  246. muted,
  247. playsinline,
  248. style,
  249. eventHandlers
  250. } = this.props;
  251. return (
  252. <video
  253. autoPlay = { autoPlay }
  254. className = { className }
  255. id = { id }
  256. muted = { muted }
  257. playsInline = { playsinline }
  258. ref = { this._setVideoElement }
  259. style = { style }
  260. { ...eventHandlers } />
  261. );
  262. }
  263. /**
  264. * Calls into the passed in track to associate the track with the
  265. * component's video element and render video.
  266. *
  267. * @param {Object} videoTrack - The redux representation of the
  268. * {@code JitsiLocalTrack}.
  269. * @private
  270. * @returns {void}
  271. */
  272. _attachTrack(videoTrack?: Partial<ITrack>) {
  273. if (!videoTrack?.jitsiTrack) {
  274. return;
  275. }
  276. videoTrack.jitsiTrack.attach(this._videoElement);
  277. }
  278. /**
  279. * Removes the association to the component's video element from the passed
  280. * in redux representation of jitsi video track to stop the track from
  281. * rendering.
  282. *
  283. * @param {Object} videoTrack - The redux representation of the
  284. * {@code JitsiLocalTrack}.
  285. * @private
  286. * @returns {void}
  287. */
  288. _detachTrack(videoTrack?: Partial<ITrack>) {
  289. if (this._videoElement && videoTrack && videoTrack.jitsiTrack) {
  290. videoTrack.jitsiTrack.detach(this._videoElement);
  291. }
  292. }
  293. /**
  294. * Invokes the onvideoplaying callback if defined.
  295. *
  296. * @private
  297. * @returns {void}
  298. */
  299. _onVideoPlaying() {
  300. if (this.props.onVideoPlaying) {
  301. this.props.onVideoPlaying();
  302. }
  303. }
  304. /**
  305. * Sets an instance variable for the component's video element so it can be
  306. * referenced later for attaching and detaching a JitsiLocalTrack.
  307. *
  308. * @param {Object} element - DOM element for the component's video display.
  309. * @private
  310. * @returns {void}
  311. */
  312. _setVideoElement(element: HTMLVideoElement | null) {
  313. this._videoElement = element;
  314. }
  315. }
  316. export default Video;