Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

SmallVideo.js 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. /* global $, APP, JitsiMeetJS, interfaceConfig */
  2. /* eslint-disable no-unused-vars */
  3. import React from 'react';
  4. import ReactDOM from 'react-dom';
  5. import { AudioLevelIndicator }
  6. from '../../../react/features/audio-level-indicator';
  7. /* eslint-enable no-unused-vars */
  8. const logger = require("jitsi-meet-logger").getLogger(__filename);
  9. import Avatar from "../avatar/Avatar";
  10. import UIUtil from "../util/UIUtil";
  11. import UIEvents from "../../../service/UI/UIEvents";
  12. const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper;
  13. /**
  14. * Display mode constant used when video is being displayed on the small video.
  15. * @type {number}
  16. * @constant
  17. */
  18. const DISPLAY_VIDEO = 0;
  19. /**
  20. * Display mode constant used when the user's avatar is being displayed on
  21. * the small video.
  22. * @type {number}
  23. * @constant
  24. */
  25. const DISPLAY_AVATAR = 1;
  26. /**
  27. * Display mode constant used when neither video nor avatar is being displayed
  28. * on the small video. And we just show the display name.
  29. * @type {number}
  30. * @constant
  31. */
  32. const DISPLAY_BLACKNESS_WITH_NAME = 2;
  33. /**
  34. * Display mode constant used when video is displayed and display name
  35. * at the same time.
  36. * @type {number}
  37. * @constant
  38. */
  39. const DISPLAY_VIDEO_WITH_NAME = 3;
  40. /**
  41. * Display mode constant used when neither video nor avatar is being displayed
  42. * on the small video. And we just show the display name.
  43. * @type {number}
  44. * @constant
  45. */
  46. const DISPLAY_AVATAR_WITH_NAME = 4;
  47. function SmallVideo(VideoLayout) {
  48. this.isAudioMuted = false;
  49. this.hasAvatar = false;
  50. this.isVideoMuted = false;
  51. this.videoStream = null;
  52. this.audioStream = null;
  53. this.VideoLayout = VideoLayout;
  54. this.videoIsHovered = false;
  55. this.hideDisplayName = false;
  56. // we can stop updating the thumbnail
  57. this.disableUpdateView = false;
  58. }
  59. /**
  60. * Returns the identifier of this small video.
  61. *
  62. * @returns the identifier of this small video
  63. */
  64. SmallVideo.prototype.getId = function () {
  65. return this.id;
  66. };
  67. /* Indicates if this small video is currently visible.
  68. *
  69. * @return <tt>true</tt> if this small video isn't currently visible and
  70. * <tt>false</tt> - otherwise.
  71. */
  72. SmallVideo.prototype.isVisible = function () {
  73. return $('#' + this.videoSpanId).is(':visible');
  74. };
  75. /**
  76. * Enables / disables the device availability icons for this small video.
  77. * @param {enable} set to {true} to enable and {false} to disable
  78. */
  79. SmallVideo.prototype.enableDeviceAvailabilityIcons = function (enable) {
  80. if (typeof enable === "undefined")
  81. return;
  82. this.deviceAvailabilityIconsEnabled = enable;
  83. };
  84. /**
  85. * Sets the device "non" availability icons.
  86. * @param devices the devices, which will be checked for availability
  87. */
  88. SmallVideo.prototype.setDeviceAvailabilityIcons = function (devices) {
  89. if (!this.deviceAvailabilityIconsEnabled)
  90. return;
  91. if(!this.container)
  92. return;
  93. var noMic = $("#" + this.videoSpanId + " > .noMic");
  94. var noVideo = $("#" + this.videoSpanId + " > .noVideo");
  95. noMic.remove();
  96. noVideo.remove();
  97. if (!devices.audio) {
  98. this.container.appendChild(
  99. document.createElement("div")).setAttribute("class", "noMic");
  100. }
  101. if (!devices.video) {
  102. this.container.appendChild(
  103. document.createElement("div")).setAttribute("class", "noVideo");
  104. }
  105. if (!devices.audio && !devices.video) {
  106. noMic.css("background-position", "75%");
  107. noVideo.css("background-position", "25%");
  108. noVideo.css("background-color", "transparent");
  109. }
  110. };
  111. /**
  112. * Sets the type of the video displayed by this instance.
  113. * Note that this is a string without clearly defined or checked values, and
  114. * it is NOT one of the strings defined in service/RTC/VideoType in
  115. * lib-jitsi-meet.
  116. * @param videoType 'camera' or 'desktop', or 'sharedvideo'.
  117. */
  118. SmallVideo.prototype.setVideoType = function (videoType) {
  119. this.videoType = videoType;
  120. };
  121. /**
  122. * Returns the type of the video displayed by this instance.
  123. * Note that this is a string without clearly defined or checked values, and
  124. * it is NOT one of the strings defined in service/RTC/VideoType in
  125. * lib-jitsi-meet.
  126. * @returns {String} 'camera', 'screen', 'sharedvideo', or undefined.
  127. */
  128. SmallVideo.prototype.getVideoType = function () {
  129. return this.videoType;
  130. };
  131. /**
  132. * Creates an audio or video element for a particular MediaStream.
  133. */
  134. SmallVideo.createStreamElement = function (stream) {
  135. let isVideo = stream.isVideoTrack();
  136. let element = isVideo
  137. ? document.createElement('video')
  138. : document.createElement('audio');
  139. if (isVideo) {
  140. element.setAttribute("muted", "true");
  141. }
  142. RTCUIHelper.setAutoPlay(element, true);
  143. element.id = SmallVideo.getStreamElementID(stream);
  144. return element;
  145. };
  146. /**
  147. * Returns the element id for a particular MediaStream.
  148. */
  149. SmallVideo.getStreamElementID = function (stream) {
  150. let isVideo = stream.isVideoTrack();
  151. return (isVideo ? 'remoteVideo_' : 'remoteAudio_') + stream.getId();
  152. };
  153. /**
  154. * Configures hoverIn/hoverOut handlers. Depends on connection indicator.
  155. */
  156. SmallVideo.prototype.bindHoverHandler = function () {
  157. // Add hover handler
  158. $(this.container).hover(
  159. () => {
  160. this.videoIsHovered = true;
  161. this.updateView();
  162. },
  163. () => {
  164. this.videoIsHovered = false;
  165. this.updateView();
  166. }
  167. );
  168. if (this.connectionIndicator) {
  169. this.connectionIndicator.addPopoverHoverListener(
  170. () => {
  171. this.updateView();
  172. });
  173. }
  174. };
  175. /**
  176. * Updates the data for the indicator
  177. * @param id the id of the indicator
  178. * @param percent the percent for connection quality
  179. * @param object the data
  180. */
  181. SmallVideo.prototype.updateStatsIndicator = function (percent, object) {
  182. if(this.connectionIndicator)
  183. this.connectionIndicator.updateConnectionQuality(percent, object);
  184. };
  185. SmallVideo.prototype.hideIndicator = function () {
  186. if(this.connectionIndicator)
  187. this.connectionIndicator.hideIndicator();
  188. };
  189. /**
  190. * Shows / hides the audio muted indicator over small videos.
  191. *
  192. * @param {boolean} isMuted indicates if the muted element should be shown
  193. * or hidden
  194. */
  195. SmallVideo.prototype.showAudioIndicator = function (isMuted) {
  196. let mutedIndicator = this.getAudioMutedIndicator();
  197. UIUtil.setVisible(mutedIndicator, isMuted);
  198. this.isAudioMuted = isMuted;
  199. };
  200. /**
  201. * Returns the audio muted indicator jquery object. If it doesn't exists -
  202. * creates it.
  203. *
  204. * @returns {HTMLElement} the audio muted indicator
  205. */
  206. SmallVideo.prototype.getAudioMutedIndicator = function () {
  207. let selector = '#' + this.videoSpanId + ' .audioMuted';
  208. let audioMutedSpan = document.querySelector(selector);
  209. if (audioMutedSpan) {
  210. return audioMutedSpan;
  211. }
  212. audioMutedSpan = document.createElement('span');
  213. audioMutedSpan.className = 'audioMuted toolbar-icon';
  214. UIUtil.setTooltip(audioMutedSpan,
  215. "videothumbnail.mute",
  216. "top");
  217. let mutedIndicator = document.createElement('i');
  218. mutedIndicator.className = 'icon-mic-disabled';
  219. audioMutedSpan.appendChild(mutedIndicator);
  220. this.container
  221. .querySelector('.videocontainer__toolbar')
  222. .appendChild(audioMutedSpan);
  223. return audioMutedSpan;
  224. };
  225. /**
  226. * Shows video muted indicator over small videos and disables/enables avatar
  227. * if video muted.
  228. *
  229. * @param {boolean} isMuted indicates if we should set the view to muted view
  230. * or not
  231. */
  232. SmallVideo.prototype.setVideoMutedView = function(isMuted) {
  233. this.isVideoMuted = isMuted;
  234. this.updateView();
  235. let element = this.getVideoMutedIndicator();
  236. UIUtil.setVisible(element, isMuted);
  237. };
  238. /**
  239. * Returns the video muted indicator jquery object. If it doesn't exists -
  240. * creates it.
  241. *
  242. * @returns {jQuery|HTMLElement} the video muted indicator
  243. */
  244. SmallVideo.prototype.getVideoMutedIndicator = function () {
  245. var selector = '#' + this.videoSpanId + ' .videoMuted';
  246. var videoMutedSpan = document.querySelector(selector);
  247. if (videoMutedSpan) {
  248. return videoMutedSpan;
  249. }
  250. videoMutedSpan = document.createElement('span');
  251. videoMutedSpan.className = 'videoMuted toolbar-icon';
  252. this.container
  253. .querySelector('.videocontainer__toolbar')
  254. .appendChild(videoMutedSpan);
  255. var mutedIndicator = document.createElement('i');
  256. mutedIndicator.className = 'icon-camera-disabled';
  257. UIUtil.setTooltip(mutedIndicator,
  258. "videothumbnail.videomute",
  259. "top");
  260. videoMutedSpan.appendChild(mutedIndicator);
  261. return videoMutedSpan;
  262. };
  263. /**
  264. * Adds the element indicating the moderator(owner) of the conference.
  265. */
  266. SmallVideo.prototype.addModeratorIndicator = function () {
  267. // Don't create moderator indicator if DISABLE_FOCUS_INDICATOR is true
  268. if (interfaceConfig.DISABLE_FOCUS_INDICATOR)
  269. return false;
  270. // Show moderator indicator
  271. var indicatorSpan = $('#' + this.videoSpanId + ' .focusindicator');
  272. if (indicatorSpan.length) {
  273. return;
  274. }
  275. indicatorSpan = document.createElement('span');
  276. indicatorSpan.className = 'focusindicator toolbar-icon right';
  277. this.container
  278. .querySelector('.videocontainer__toolbar')
  279. .appendChild(indicatorSpan);
  280. var moderatorIndicator = document.createElement('i');
  281. moderatorIndicator.className = 'icon-star';
  282. UIUtil.setTooltip(moderatorIndicator,
  283. "videothumbnail.moderator",
  284. "top-left");
  285. indicatorSpan.appendChild(moderatorIndicator);
  286. };
  287. /**
  288. * Adds the element indicating the audio level of the participant.
  289. *
  290. * @returns {void}
  291. */
  292. SmallVideo.prototype.addAudioLevelIndicator = function () {
  293. let audioLevelContainer = this._getAudioLevelContainer();
  294. if (audioLevelContainer) {
  295. return;
  296. }
  297. audioLevelContainer = document.createElement('span');
  298. audioLevelContainer.className = 'audioindicator-container';
  299. this.container.appendChild(audioLevelContainer);
  300. this.updateAudioLevelIndicator();
  301. };
  302. /**
  303. * Removes the element indicating the audio level of the participant.
  304. *
  305. * @returns {void}
  306. */
  307. SmallVideo.prototype.removeAudioLevelIndicator = function () {
  308. const audioLevelContainer = this._getAudioLevelContainer();
  309. if (audioLevelContainer) {
  310. ReactDOM.unmountComponentAtNode(audioLevelContainer);
  311. }
  312. };
  313. /**
  314. * Updates the audio level for this small video.
  315. *
  316. * @param lvl the new audio level to set
  317. * @returns {void}
  318. */
  319. SmallVideo.prototype.updateAudioLevelIndicator = function (lvl = 0) {
  320. const audioLevelContainer = this._getAudioLevelContainer();
  321. if (audioLevelContainer) {
  322. /* jshint ignore:start */
  323. ReactDOM.render(
  324. <AudioLevelIndicator
  325. audioLevel = { lvl }/>,
  326. audioLevelContainer);
  327. /* jshint ignore:end */
  328. }
  329. };
  330. /**
  331. * Queries the component's DOM for the element that should be the parent to the
  332. * AudioLevelIndicator.
  333. *
  334. * @returns {HTMLElement} The DOM element that holds the AudioLevelIndicator.
  335. */
  336. SmallVideo.prototype._getAudioLevelContainer = function () {
  337. return this.container.querySelector('.audioindicator-container');
  338. };
  339. /**
  340. * Removes the element indicating the moderator(owner) of the conference.
  341. */
  342. SmallVideo.prototype.removeModeratorIndicator = function () {
  343. $('#' + this.videoSpanId + ' .focusindicator').remove();
  344. };
  345. /**
  346. * This is an especially interesting function. A naive reader might think that
  347. * it returns this SmallVideo's "video" element. But it is much more exciting.
  348. * It first finds this video's parent element using jquery, then uses a utility
  349. * from lib-jitsi-meet to extract the video element from it (with two more
  350. * jquery calls), and finally uses jquery again to encapsulate the video element
  351. * in an array. This last step allows (some might prefer "forces") users of
  352. * this function to access the video element via the 0th element of the returned
  353. * array (after checking its length of course!).
  354. */
  355. SmallVideo.prototype.selectVideoElement = function () {
  356. return $(RTCUIHelper.findVideoElement($('#' + this.videoSpanId)[0]));
  357. };
  358. /**
  359. * Selects the HTML image element which displays user's avatar.
  360. *
  361. * @return {jQuery|HTMLElement} a jQuery selector pointing to the HTML image
  362. * element which displays the user's avatar.
  363. */
  364. SmallVideo.prototype.$avatar = function () {
  365. return $('#' + this.videoSpanId + ' .userAvatar');
  366. };
  367. /**
  368. * Returns the display name element, which appears on the video thumbnail.
  369. *
  370. * @return {jQuery} a jQuery selector pointing to the display name element of
  371. * the video thumbnail
  372. */
  373. SmallVideo.prototype.$displayName = function () {
  374. return $('#' + this.videoSpanId + ' .displayname');
  375. };
  376. /**
  377. * Enables / disables the css responsible for focusing/pinning a video
  378. * thumbnail.
  379. *
  380. * @param isFocused indicates if the thumbnail should be focused/pinned or not
  381. */
  382. SmallVideo.prototype.focus = function(isFocused) {
  383. var focusedCssClass = "videoContainerFocused";
  384. var isFocusClassEnabled = $(this.container).hasClass(focusedCssClass);
  385. if (!isFocused && isFocusClassEnabled) {
  386. $(this.container).removeClass(focusedCssClass);
  387. }
  388. else if (isFocused && !isFocusClassEnabled) {
  389. $(this.container).addClass(focusedCssClass);
  390. }
  391. };
  392. SmallVideo.prototype.hasVideo = function () {
  393. return this.selectVideoElement().length !== 0;
  394. };
  395. /**
  396. * Checks whether the user associated with this <tt>SmallVideo</tt> is currently
  397. * being displayed on the "large video".
  398. *
  399. * @return {boolean} <tt>true</tt> if the user is displayed on the large video
  400. * or <tt>false</tt> otherwise.
  401. */
  402. SmallVideo.prototype.isCurrentlyOnLargeVideo = function () {
  403. return this.VideoLayout.isCurrentlyOnLarge(this.id);
  404. };
  405. /**
  406. * Checks whether there is a playable video stream available for the user
  407. * associated with this <tt>SmallVideo</tt>.
  408. *
  409. * @return {boolean} <tt>true</tt> if there is a playable video stream available
  410. * or <tt>false</tt> otherwise.
  411. */
  412. SmallVideo.prototype.isVideoPlayable = function() {
  413. return this.videoStream // Is there anything to display ?
  414. && !this.isVideoMuted && !this.videoStream.isMuted(); // Muted ?
  415. };
  416. /**
  417. * Determines what should be display on the thumbnail.
  418. *
  419. * @return {number} one of <tt>DISPLAY_VIDEO</tt>,<tt>DISPLAY_AVATAR</tt>
  420. * or <tt>DISPLAY_BLACKNESS_WITH_NAME</tt>.
  421. */
  422. SmallVideo.prototype.selectDisplayMode = function() {
  423. // Display name is always and only displayed when user is on the stage
  424. if (this.isCurrentlyOnLargeVideo()) {
  425. return DISPLAY_BLACKNESS_WITH_NAME;
  426. } else if (this.isVideoPlayable()
  427. && this.selectVideoElement().length
  428. && !APP.conference.isAudioOnly()) {
  429. // check hovering and change state to video with name
  430. return this._isHovered() ?
  431. DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO;
  432. } else {
  433. // check hovering and change state to avatar with name
  434. return this._isHovered() ?
  435. DISPLAY_AVATAR_WITH_NAME : DISPLAY_AVATAR;
  436. }
  437. };
  438. /**
  439. * Checks whether current video is considered hovered. Currently it is hovered
  440. * if the mouse is over the video, or if the connection
  441. * indicator is shown(hovered).
  442. * @private
  443. */
  444. SmallVideo.prototype._isHovered = function () {
  445. return this.videoIsHovered
  446. || (this.connectionIndicator
  447. && this.connectionIndicator.popover.popoverIsHovered);
  448. };
  449. /**
  450. * Hides or shows the user's avatar.
  451. * This update assumes that large video had been updated and we will
  452. * reflect it on this small video.
  453. *
  454. * @param show whether we should show the avatar or not
  455. * video because there is no dominant speaker and no focused speaker
  456. */
  457. SmallVideo.prototype.updateView = function () {
  458. if (this.disableUpdateView)
  459. return;
  460. if (!this.hasAvatar) {
  461. if (this.id) {
  462. // Init avatar
  463. this.avatarChanged(Avatar.getAvatarUrl(this.id));
  464. } else {
  465. logger.error("Unable to init avatar - no id", this);
  466. return;
  467. }
  468. }
  469. // Determine whether video, avatar or blackness should be displayed
  470. let displayMode = this.selectDisplayMode();
  471. // Show/hide video.
  472. UIUtil.setVisibleBySelector(this.selectVideoElement(),
  473. (displayMode === DISPLAY_VIDEO
  474. || displayMode === DISPLAY_VIDEO_WITH_NAME));
  475. // Show/hide the avatar.
  476. UIUtil.setVisibleBySelector(this.$avatar(),
  477. (displayMode === DISPLAY_AVATAR
  478. || displayMode === DISPLAY_AVATAR_WITH_NAME));
  479. // Show/hide the display name.
  480. UIUtil.setVisibleBySelector(this.$displayName(),
  481. !this.hideDisplayName
  482. && (displayMode === DISPLAY_BLACKNESS_WITH_NAME
  483. || displayMode === DISPLAY_VIDEO_WITH_NAME
  484. || displayMode === DISPLAY_AVATAR_WITH_NAME));
  485. // show hide overlay when there is a video or avatar under
  486. // the display name
  487. UIUtil.setVisibleBySelector($('#' + this.videoSpanId
  488. + ' .videocontainer__hoverOverlay'),
  489. (displayMode === DISPLAY_AVATAR_WITH_NAME
  490. || displayMode === DISPLAY_VIDEO_WITH_NAME));
  491. };
  492. SmallVideo.prototype.avatarChanged = function (avatarUrl) {
  493. var thumbnail = $('#' + this.videoSpanId);
  494. var avatarSel = this.$avatar();
  495. this.hasAvatar = true;
  496. // set the avatar in the thumbnail
  497. if (avatarSel && avatarSel.length > 0) {
  498. avatarSel[0].src = avatarUrl;
  499. } else {
  500. if (thumbnail && thumbnail.length > 0) {
  501. var avatarElement = document.createElement('img');
  502. avatarElement.className = 'userAvatar';
  503. avatarElement.src = avatarUrl;
  504. thumbnail.append(avatarElement);
  505. }
  506. }
  507. };
  508. /**
  509. * Shows or hides the dominant speaker indicator.
  510. * @param show whether to show or hide.
  511. */
  512. SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
  513. // Don't create and show dominant speaker indicator if
  514. // DISABLE_DOMINANT_SPEAKER_INDICATOR is true
  515. if (interfaceConfig.DISABLE_DOMINANT_SPEAKER_INDICATOR)
  516. return;
  517. if (!this.container) {
  518. logger.warn( "Unable to set dominant speaker indicator - "
  519. + this.videoSpanId + " does not exist");
  520. return;
  521. }
  522. let indicatorSpanId = "dominantspeakerindicator";
  523. let content = `<i id="indicatoricon"
  524. class="indicatoricon fa fa-bullhorn"></i>`;
  525. let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
  526. videoSpanId: this.videoSpanId,
  527. indicatorId: indicatorSpanId,
  528. content,
  529. tooltip: 'speaker'
  530. });
  531. UIUtil.setVisible(indicatorSpan, show);
  532. };
  533. /**
  534. * Shows or hides the raised hand indicator.
  535. * @param show whether to show or hide.
  536. */
  537. SmallVideo.prototype.showRaisedHandIndicator = function (show) {
  538. if (!this.container) {
  539. logger.warn( "Unable to raised hand indication - "
  540. + this.videoSpanId + " does not exist");
  541. return;
  542. }
  543. let indicatorSpanId = "raisehandindicator";
  544. let content = `<i id="indicatoricon"
  545. class="icon-raised-hand indicatoricon"></i>`;
  546. let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
  547. indicatorId: indicatorSpanId,
  548. videoSpanId: this.videoSpanId,
  549. content,
  550. tooltip: 'raisedHand'
  551. });
  552. UIUtil.setVisible(indicatorSpan, show);
  553. };
  554. /**
  555. * Adds a listener for onresize events for this video, which will monitor for
  556. * resolution changes, will calculate the delay since the moment the listened
  557. * is added, and will fire a RESOLUTION_CHANGED event.
  558. */
  559. SmallVideo.prototype.waitForResolutionChange = function() {
  560. let beforeChange = window.performance.now();
  561. let videos = this.selectVideoElement();
  562. if (!videos || !videos.length || videos.length <= 0)
  563. return;
  564. let video = videos[0];
  565. let oldWidth = video.videoWidth;
  566. let oldHeight = video.videoHeight;
  567. video.onresize = () => {
  568. if (video.videoWidth != oldWidth || video.videoHeight != oldHeight) {
  569. // Only run once.
  570. video.onresize = null;
  571. let delay = window.performance.now() - beforeChange;
  572. let emitter = this.VideoLayout.getEventEmitter();
  573. if (emitter) {
  574. emitter.emit(
  575. UIEvents.RESOLUTION_CHANGED,
  576. this.getId(),
  577. oldWidth + "x" + oldHeight,
  578. video.videoWidth + "x" + video.videoHeight,
  579. delay);
  580. }
  581. }
  582. };
  583. };
  584. /**
  585. * Initalizes any browser specific properties. Currently sets the overflow
  586. * property for Qt browsers on Windows to hidden, thus fixing the following
  587. * problem:
  588. * Some browsers don't have full support of the object-fit property for the
  589. * video element and when we set video object-fit to "cover" the video
  590. * actually overflows the boundaries of its container, so it's important
  591. * to indicate that the "overflow" should be hidden.
  592. *
  593. * Setting this property for all browsers will result in broken audio levels,
  594. * which makes this a temporary solution, before reworking audio levels.
  595. */
  596. SmallVideo.prototype.initBrowserSpecificProperties = function() {
  597. var userAgent = window.navigator.userAgent;
  598. if (userAgent.indexOf("QtWebEngine") > -1
  599. && (userAgent.indexOf("Windows") > -1
  600. || userAgent.indexOf("Linux") > -1)) {
  601. $('#' + this.videoSpanId).css("overflow", "hidden");
  602. }
  603. };
  604. export default SmallVideo;