diff --git a/interface_config.js b/interface_config.js index 3baaee4c6a..bd586baeed 100644 --- a/interface_config.js +++ b/interface_config.js @@ -93,5 +93,6 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars * * @type {boolean} */ - MOBILE_APP_PROMO: true + MOBILE_APP_PROMO: true, + MAXIMUM_ZOOMING_COEFFICIENT: 1.3 }; diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js index e68fa12bb2..08521c9464 100644 --- a/modules/UI/videolayout/LargeVideoManager.js +++ b/modules/UI/videolayout/LargeVideoManager.js @@ -60,6 +60,14 @@ export default class LargeVideoManager { this.width = 0; this.height = 0; + /** + * Cache the aspect ratio of the video displayed to detect changes to + * the aspect ratio on video resize events. + * + * @type {number} + */ + this._videoAspectRatio = 0; + this.$container = $('#largeVideoContainer'); this.$container.css({ @@ -72,11 +80,10 @@ export default class LargeVideoManager { ); // Bind event handler so it is only bound once for every instance. - this._updateVideoResolutionStatus - = this._updateVideoResolutionStatus.bind(this); + this._onVideoResolutionUpdate + = this._onVideoResolutionUpdate.bind(this); - this.videoContainer.addResizeListener( - this._updateVideoResolutionStatus); + this.videoContainer.addResizeListener(this._onVideoResolutionUpdate); if (!JitsiMeetJS.util.RTCUIHelper.isResizeEventSupported()) { /** @@ -88,7 +95,7 @@ export default class LargeVideoManager { * @type {timeoutId} */ this._updateVideoResolutionInterval = window.setInterval( - this._updateVideoResolutionStatus, + this._onVideoResolutionUpdate, VIDEO_RESOLUTION_POLL_INTERVAL); } } @@ -102,7 +109,7 @@ export default class LargeVideoManager { destroy() { window.clearInterval(this._updateVideoResolutionInterval); this.videoContainer.removeResizeListener( - this._updateVideoResolutionStatus); + this._onVideoResolutionUpdate); } onHoverIn (e) { @@ -565,15 +572,21 @@ export default class LargeVideoManager { /** * Dispatches an action to update the known resolution state of the - * large video. + * large video and adjusts container sizes when the resolution changes. * * @private * @returns {void} */ - _updateVideoResolutionStatus() { + _onVideoResolutionUpdate() { const { height, width } = this.videoContainer.getStreamSize(); + const currentAspectRatio = width/ height; const isCurrentlyHD = Math.min(height, width) >= config.minHDHeight; APP.store.dispatch(setLargeVideoHDStatus(isCurrentlyHD)); + + if (this._videoAspectRatio !== currentAspectRatio) { + this._videoAspectRatio = currentAspectRatio; + this.resize(); + } } } diff --git a/modules/UI/videolayout/VideoContainer.js b/modules/UI/videolayout/VideoContainer.js index 9f1ab8dbc4..29e4d318b4 100644 --- a/modules/UI/videolayout/VideoContainer.js +++ b/modules/UI/videolayout/VideoContainer.js @@ -38,7 +38,7 @@ function getStreamOwnerId(stream) { * @param videoSpaceHeight the height of the available space * @return an array with 2 elements, the video width and the video height */ -function getDesktopVideoSize(videoWidth, +function computeDesktopVideoSize(videoWidth, videoHeight, videoSpaceWidth, videoSpaceHeight) { @@ -75,25 +75,22 @@ function getDesktopVideoSize(videoWidth, * @param videoSpaceHeight the height of the video space * @return an array with 2 elements, the video width and the video height */ -function getCameraVideoSize(videoWidth, +function computeCameraVideoSize(videoWidth, videoHeight, videoSpaceWidth, videoSpaceHeight) { - let aspectRatio = videoWidth / videoHeight; - + const aspectRatio = videoWidth / videoHeight; let availableWidth = videoWidth; let availableHeight = videoHeight; - if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') { + if (interfaceConfig.VIDEO_LAYOUT_FIT === 'height') { availableHeight = videoSpaceHeight; - availableWidth = availableHeight*aspectRatio; - } - else if (interfaceConfig.VIDEO_LAYOUT_FIT == 'width') { + availableWidth = availableHeight * aspectRatio; + } else if (interfaceConfig.VIDEO_LAYOUT_FIT === 'width') { availableWidth = videoSpaceWidth; - availableHeight = availableWidth/aspectRatio; - } - else if (interfaceConfig.VIDEO_LAYOUT_FIT == 'both') { + availableHeight = availableWidth / aspectRatio; + } else if (interfaceConfig.VIDEO_LAYOUT_FIT === 'both') { availableWidth = Math.max(videoWidth, videoSpaceWidth); availableHeight = Math.max(videoHeight, videoSpaceHeight); @@ -102,13 +99,22 @@ function getCameraVideoSize(videoWidth, availableWidth = availableHeight * aspectRatio; } - if (availableHeight * aspectRatio < videoSpaceWidth) { + if (aspectRatio <= 1) { + const zoomRateHeight = videoSpaceHeight / videoHeight; + const zoomRateWidth = videoSpaceWidth / videoWidth; + const maxZoomRate + = interfaceConfig.MAXIMUM_ZOOMING_COEFFICIENT || Infinity; + let zoomRate = Math.min(zoomRateWidth, maxZoomRate); + + zoomRate = Math.max(zoomRate, zoomRateHeight); + availableWidth = videoWidth * zoomRate; + availableHeight = videoHeight * zoomRate; + } else if (availableHeight * aspectRatio < videoSpaceWidth) { availableWidth = videoSpaceWidth; availableHeight = availableWidth / aspectRatio; } } - return [ availableWidth, availableHeight ]; } @@ -269,19 +275,20 @@ export class VideoContainer extends LargeContainer { * @param {number} containerHeight container height * @returns {{availableWidth, availableHeight}} */ - getVideoSize (containerWidth, containerHeight) { + getVideoSize(containerWidth, containerHeight) { let { width, height } = this.getStreamSize(); + if (this.stream && this.isScreenSharing()) { - return getDesktopVideoSize( width, - height, - containerWidth, - containerHeight); - } else { - return getCameraVideoSize( width, + return computeDesktopVideoSize(width, height, containerWidth, containerHeight); } + + return computeCameraVideoSize(width, + height, + containerWidth, + containerHeight); } /**