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 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import React, { Component } from 'react';
  2. import { translate } from '../../base/i18n';
  3. const VIDEO_MUTE_CLASS = 'video-muted';
  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. * Invoked to obtain translated strings.
  19. */
  20. t: React.PropTypes.func,
  21. /*
  22. * The JitsiLocalTrack to display.
  23. */
  24. track: React.PropTypes.object
  25. }
  26. /**
  27. * Initializes a new VideoInputPreview instance.
  28. *
  29. * @param {Object} props - The read-only React Component props with which
  30. * the new instance is to be initialized.
  31. */
  32. constructor(props) {
  33. super(props);
  34. this._rootElement = null;
  35. this._videoElement = null;
  36. this._setRootElement = this._setRootElement.bind(this);
  37. this._setVideoElement = this._setVideoElement.bind(this);
  38. }
  39. /**
  40. * Invokes the library for rendering the video on initial display.
  41. *
  42. * @inheritdoc
  43. * @returns {void}
  44. */
  45. componentDidMount() {
  46. this._attachTrack(this.props.track);
  47. }
  48. /**
  49. * Remove any existing associations between the current previewed track and
  50. * the component's video element.
  51. *
  52. * @inheritdoc
  53. * @returns {void}
  54. */
  55. componentWillUnmount() {
  56. this._detachTrack(this.props.track);
  57. }
  58. /**
  59. * Implements React's {@link Component#render()}.
  60. *
  61. * @inheritdoc
  62. * @returns {ReactElement}
  63. */
  64. render() {
  65. return (
  66. <div
  67. className = 'video-input-preview'
  68. ref = { this._setRootElement }>
  69. <video
  70. autoPlay = { true }
  71. className = 'video-input-preview-display flipVideoX'
  72. ref = { this._setVideoElement } />
  73. <div className = 'video-input-preview-muted'>
  74. { this.props.t('videothumbnail.muted') }
  75. </div>
  76. </div>
  77. );
  78. }
  79. /**
  80. * Only update when the deviceId has changed. This component is somewhat
  81. * black-boxed from React's rendering so lib-jitsi-meet can instead handle
  82. * updating of the video preview, which takes browser differences into
  83. * consideration. For example, temasys's video object must be visible to
  84. * update the displayed track, but React's re-rendering could potentially
  85. * remove the video object from the page.
  86. *
  87. * @inheritdoc
  88. * @returns {void}
  89. */
  90. shouldComponentUpdate(nextProps) {
  91. if (nextProps.track !== this.props.track) {
  92. this._detachTrack(this.props.track);
  93. this._attachTrack(nextProps.track);
  94. }
  95. return false;
  96. }
  97. /**
  98. * Calls into the passed in track to associate the track with the
  99. * component's video element and render video. Also sets the instance
  100. * variable for the video element as the element the track attached to,
  101. * which could be an Object if on a temasys supported browser.
  102. *
  103. * @param {JitsiLocalTrack} track - The library's track model which will be
  104. * displayed.
  105. * @private
  106. * @returns {void}
  107. */
  108. _attachTrack(track) {
  109. if (!track) {
  110. return;
  111. }
  112. // Do not attempt to display a preview if the track is muted, as the
  113. // library will simply return a falsy value for the element anyway.
  114. if (track.isMuted()) {
  115. this._showMuteOverlay(true);
  116. } else {
  117. this._showMuteOverlay(false);
  118. const updatedVideoElement = track.attach(this._videoElement);
  119. this._setVideoElement(updatedVideoElement);
  120. }
  121. }
  122. /**
  123. * Removes the association to the component's video element from the passed
  124. * in JitsiLocalTrack to stop the track from rendering. With temasys, the
  125. * video element must still be visible for detaching to complete.
  126. *
  127. * @param {JitsiLocalTrack} track - The library's track model which needs
  128. * to stop previewing in the video element.
  129. * @private
  130. * @returns {void}
  131. */
  132. _detachTrack(track) {
  133. // Detach the video element from the track only if it has already
  134. // been attached. This accounts for a special case with temasys
  135. // where if detach is being called before attach, the video
  136. // element is converted to Object without updating this
  137. // component's reference to the video element.
  138. if (this._videoElement
  139. && track
  140. && track.containers.includes(this._videoElement)) {
  141. track.detach(this._videoElement);
  142. }
  143. }
  144. /**
  145. * Sets the component's root element.
  146. *
  147. * @param {Object} element - The highest DOM element in the component.
  148. * @private
  149. * @returns {void}
  150. */
  151. _setRootElement(element) {
  152. this._rootElement = element;
  153. }
  154. /**
  155. * Sets an instance variable for the component's video element so it can be
  156. * referenced later for attaching and detaching a JitsiLocalTrack.
  157. *
  158. * @param {Object} element - DOM element for the component's video display.
  159. * @private
  160. * @returns {void}
  161. */
  162. _setVideoElement(element) {
  163. this._videoElement = element;
  164. }
  165. /**
  166. * Adds or removes a class to the component's parent node to indicate mute
  167. * status.
  168. *
  169. * @param {boolean} shouldShow - True if the mute class should be added and
  170. * false if the class should be removed.
  171. * @private
  172. * @returns {void}
  173. */
  174. _showMuteOverlay(shouldShow) {
  175. if (shouldShow) {
  176. this._rootElement.classList.add(VIDEO_MUTE_CLASS);
  177. } else {
  178. this._rootElement.classList.remove(VIDEO_MUTE_CLASS);
  179. }
  180. }
  181. }
  182. export default translate(VideoInputPreview);