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.

VideoInputPreview.js 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import React, { Component } from 'react';
  2. import { translate } from '../../base/i18n';
  3. const VIDEO_ERROR_CLASS = 'video-preview-has-error';
  4. /**
  5. * React component for displaying video. This component defers to lib-jitsi-meet
  6. * logic for rendering the video.
  7. *
  8. * @extends Component
  9. */
  10. class VideoInputPreview extends Component {
  11. /**
  12. * VideoInputPreview component's property types.
  13. *
  14. * @static
  15. */
  16. static propTypes = {
  17. /**
  18. * An error message to display instead of a preview. Displaying an error
  19. * will take priority over displaying a video preview.
  20. */
  21. error: React.PropTypes.string,
  22. /**
  23. * Invoked to obtain translated strings.
  24. */
  25. t: React.PropTypes.func,
  26. /**
  27. * The JitsiLocalTrack to display.
  28. */
  29. track: React.PropTypes.object
  30. };
  31. /**
  32. * Initializes a new VideoInputPreview instance.
  33. *
  34. * @param {Object} props - The read-only React Component props with which
  35. * the new instance is to be initialized.
  36. */
  37. constructor(props) {
  38. super(props);
  39. /**
  40. * The internal reference to the DOM/HTML element intended for showing
  41. * error messages.
  42. *
  43. * @private
  44. * @type {HTMLDivElement}
  45. */
  46. this._errorElement = null;
  47. /**
  48. * The internal reference to topmost DOM/HTML element backing the React
  49. * {@code Component}. Accessed directly for toggling a classname to
  50. * indicate an error is present so styling can be changed to display it.
  51. *
  52. * @private
  53. * @type {HTMLDivElement}
  54. */
  55. this._rootElement = null;
  56. /**
  57. * The internal reference to the DOM/HTML element intended for
  58. * displaying a video. This element may be an HTML video element or a
  59. * temasys video object.
  60. *
  61. * @private
  62. * @type {HTMLVideoElement|Object}
  63. */
  64. this._videoElement = null;
  65. // Bind event handlers so they are only bound once for every instance.
  66. this._setErrorElement = this._setErrorElement.bind(this);
  67. this._setRootElement = this._setRootElement.bind(this);
  68. this._setVideoElement = this._setVideoElement.bind(this);
  69. }
  70. /**
  71. * Invokes the library for rendering the video on initial display.
  72. *
  73. * @inheritdoc
  74. * @returns {void}
  75. */
  76. componentDidMount() {
  77. if (this.props.error) {
  78. this._updateErrorView(this.props.error);
  79. } else {
  80. this._attachTrack(this.props.track);
  81. }
  82. }
  83. /**
  84. * Remove any existing associations between the current previewed track and
  85. * the component's video element.
  86. *
  87. * @inheritdoc
  88. * @returns {void}
  89. */
  90. componentWillUnmount() {
  91. this._detachTrack(this.props.track);
  92. }
  93. /**
  94. * Implements React's {@link Component#render()}.
  95. *
  96. * @inheritdoc
  97. * @returns {ReactElement}
  98. */
  99. render() {
  100. return (
  101. <div
  102. className = 'video-input-preview'
  103. ref = { this._setRootElement }>
  104. <video
  105. autoPlay = { true }
  106. className = 'video-input-preview-display flipVideoX'
  107. ref = { this._setVideoElement } />
  108. <div
  109. className = 'video-input-preview-error'
  110. ref = { this._setErrorElement } />
  111. </div>
  112. );
  113. }
  114. /**
  115. * Only update when the deviceId has changed. This component is somewhat
  116. * black-boxed from React's rendering so lib-jitsi-meet can instead handle
  117. * updating of the video preview, which takes browser differences into
  118. * consideration. For example, temasys's video object must be visible to
  119. * update the displayed track, but React's re-rendering could potentially
  120. * remove the video object from the page.
  121. *
  122. * @inheritdoc
  123. * @returns {void}
  124. */
  125. shouldComponentUpdate(nextProps) {
  126. const hasNewTrack = nextProps.track !== this.props.track;
  127. if (hasNewTrack || nextProps.error) {
  128. this._detachTrack(this.props.track);
  129. this._updateErrorView(nextProps.error);
  130. }
  131. // Never attempt to show the new track if there is an error present.
  132. if (hasNewTrack && !nextProps.error) {
  133. this._attachTrack(nextProps.track);
  134. }
  135. return false;
  136. }
  137. /**
  138. * Calls into the passed in track to associate the track with the
  139. * component's video element and render video. Also sets the instance
  140. * variable for the video element as the element the track attached to,
  141. * which could be an Object if on a temasys supported browser.
  142. *
  143. * @param {JitsiLocalTrack} track - The library's track model which will be
  144. * displayed.
  145. * @private
  146. * @returns {void}
  147. */
  148. _attachTrack(track) {
  149. if (!track) {
  150. return;
  151. }
  152. const updatedVideoElement = track.attach(this._videoElement);
  153. this._setVideoElement(updatedVideoElement);
  154. }
  155. /**
  156. * Removes the association to the component's video element from the passed
  157. * in JitsiLocalTrack to stop the track from rendering. With temasys, the
  158. * video element must still be visible for detaching to complete.
  159. *
  160. * @param {JitsiLocalTrack} track - The library's track model which needs
  161. * to stop previewing in the video element.
  162. * @private
  163. * @returns {void}
  164. */
  165. _detachTrack(track) {
  166. // Detach the video element from the track only if it has already
  167. // been attached. This accounts for a special case with temasys
  168. // where if detach is being called before attach, the video
  169. // element is converted to Object without updating this
  170. // component's reference to the video element.
  171. if (this._videoElement
  172. && track
  173. && track.containers.includes(this._videoElement)) {
  174. track.detach(this._videoElement);
  175. }
  176. }
  177. /**
  178. * Sets an instance variable for the component's element intended for
  179. * displaying error messages. The element will be accessed directly to
  180. * display an error message.
  181. *
  182. * @param {Object} element - DOM element intended for displaying errors.
  183. * @private
  184. * @returns {void}
  185. */
  186. _setErrorElement(element) {
  187. this._errorElement = element;
  188. }
  189. /**
  190. * Sets the component's root element.
  191. *
  192. * @param {Object} element - The highest DOM element in the component.
  193. * @private
  194. * @returns {void}
  195. */
  196. _setRootElement(element) {
  197. this._rootElement = element;
  198. }
  199. /**
  200. * Sets an instance variable for the component's video element so it can be
  201. * referenced later for attaching and detaching a JitsiLocalTrack.
  202. *
  203. * @param {Object} element - DOM element for the component's video display.
  204. * @private
  205. * @returns {void}
  206. */
  207. _setVideoElement(element) {
  208. this._videoElement = element;
  209. }
  210. /**
  211. * Adds or removes a class to the component's parent node to indicate an
  212. * error has occurred. Also sets the error text.
  213. *
  214. * @param {string} error - The error message to display. If falsy, error
  215. * message display will be hidden.
  216. * @private
  217. * @returns {void}
  218. */
  219. _updateErrorView(error) {
  220. if (error) {
  221. this._rootElement.classList.add(VIDEO_ERROR_CLASS);
  222. } else {
  223. this._rootElement.classList.remove(VIDEO_ERROR_CLASS);
  224. }
  225. this._errorElement.innerText = error || '';
  226. }
  227. }
  228. export default translate(VideoInputPreview);