463 lines
14 KiB
JavaScript
463 lines
14 KiB
JavaScript
var Avatar = require("../avatar/Avatar");
|
|
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
|
var UIUtil = require("../util/UIUtil");
|
|
var UIEvents = require("../../../service/UI/UIEvents");
|
|
var xmpp = require("../../xmpp/xmpp");
|
|
|
|
// FIXME: With Temasys we have to re-select everytime
|
|
//var video = $('#largeVideo');
|
|
|
|
var currentVideoWidth = null;
|
|
var currentVideoHeight = null;
|
|
// By default we use camera
|
|
var getVideoSize = getCameraVideoSize;
|
|
var getVideoPosition = getCameraVideoPosition;
|
|
var currentSmallVideo = null;
|
|
var oldSmallVideo = null;
|
|
|
|
|
|
|
|
/**
|
|
* Sets the size and position of the given video element.
|
|
*
|
|
* @param video the video element to position
|
|
* @param width the desired video width
|
|
* @param height the desired video height
|
|
* @param horizontalIndent the left and right indent
|
|
* @param verticalIndent the top and bottom indent
|
|
*/
|
|
function positionVideo(video,
|
|
width,
|
|
height,
|
|
horizontalIndent,
|
|
verticalIndent,
|
|
animate) {
|
|
if(animate)
|
|
{
|
|
video.animate({
|
|
width: width,
|
|
height: height,
|
|
top: verticalIndent,
|
|
bottom: verticalIndent,
|
|
left: horizontalIndent,
|
|
right: horizontalIndent
|
|
},
|
|
{
|
|
queue: false,
|
|
duration: 500
|
|
});
|
|
}
|
|
else
|
|
{
|
|
video.width(width);
|
|
video.height(height);
|
|
video.css({ top: verticalIndent + 'px',
|
|
bottom: verticalIndent + 'px',
|
|
left: horizontalIndent + 'px',
|
|
right: horizontalIndent + 'px'});
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns an array of the video dimensions, so that it keeps it's aspect
|
|
* ratio and fits available area with it's larger dimension. This method
|
|
* ensures that whole video will be visible and can leave empty areas.
|
|
*
|
|
* @return an array with 2 elements, the video width and the video height
|
|
*/
|
|
function getDesktopVideoSize(videoWidth,
|
|
videoHeight,
|
|
videoSpaceWidth,
|
|
videoSpaceHeight) {
|
|
if (!videoWidth)
|
|
videoWidth = currentVideoWidth;
|
|
if (!videoHeight)
|
|
videoHeight = currentVideoHeight;
|
|
|
|
var aspectRatio = videoWidth / videoHeight;
|
|
|
|
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
|
|
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
|
|
|
|
videoSpaceHeight -= $('#remoteVideos').outerHeight();
|
|
|
|
if (availableWidth / aspectRatio >= videoSpaceHeight)
|
|
{
|
|
availableHeight = videoSpaceHeight;
|
|
availableWidth = availableHeight * aspectRatio;
|
|
}
|
|
|
|
if (availableHeight * aspectRatio >= videoSpaceWidth)
|
|
{
|
|
availableWidth = videoSpaceWidth;
|
|
availableHeight = availableWidth / aspectRatio;
|
|
}
|
|
|
|
return [availableWidth, availableHeight];
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns an array of the video horizontal and vertical indents,
|
|
* so that if fits its parent.
|
|
*
|
|
* @return an array with 2 elements, the horizontal indent and the vertical
|
|
* indent
|
|
*/
|
|
function getCameraVideoPosition(videoWidth,
|
|
videoHeight,
|
|
videoSpaceWidth,
|
|
videoSpaceHeight) {
|
|
// Parent height isn't completely calculated when we position the video in
|
|
// full screen mode and this is why we use the screen height in this case.
|
|
// Need to think it further at some point and implement it properly.
|
|
var isFullScreen = document.fullScreen ||
|
|
document.mozFullScreen ||
|
|
document.webkitIsFullScreen;
|
|
if (isFullScreen)
|
|
videoSpaceHeight = window.innerHeight;
|
|
|
|
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
|
var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
|
|
|
|
return [horizontalIndent, verticalIndent];
|
|
}
|
|
|
|
/**
|
|
* Returns an array of the video horizontal and vertical indents.
|
|
* Centers horizontally and top aligns vertically.
|
|
*
|
|
* @return an array with 2 elements, the horizontal indent and the vertical
|
|
* indent
|
|
*/
|
|
function getDesktopVideoPosition(videoWidth,
|
|
videoHeight,
|
|
videoSpaceWidth,
|
|
videoSpaceHeight) {
|
|
|
|
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
|
|
|
var verticalIndent = 0;// Top aligned
|
|
|
|
return [horizontalIndent, verticalIndent];
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns an array of the video dimensions, so that it covers the screen.
|
|
* It leaves no empty areas, but some parts of the video might not be visible.
|
|
*
|
|
* @return an array with 2 elements, the video width and the video height
|
|
*/
|
|
function getCameraVideoSize(videoWidth,
|
|
videoHeight,
|
|
videoSpaceWidth,
|
|
videoSpaceHeight) {
|
|
if (!videoWidth)
|
|
videoWidth = currentVideoWidth;
|
|
if (!videoHeight)
|
|
videoHeight = currentVideoHeight;
|
|
|
|
var aspectRatio = videoWidth / videoHeight;
|
|
|
|
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
|
|
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
|
|
|
|
if (availableWidth / aspectRatio < videoSpaceHeight) {
|
|
availableHeight = videoSpaceHeight;
|
|
availableWidth = availableHeight * aspectRatio;
|
|
}
|
|
|
|
if (availableHeight * aspectRatio < videoSpaceWidth) {
|
|
availableWidth = videoSpaceWidth;
|
|
availableHeight = availableWidth / aspectRatio;
|
|
}
|
|
|
|
return [availableWidth, availableHeight];
|
|
}
|
|
|
|
/**
|
|
* Updates the src of the active speaker avatar
|
|
* @param jid of the current active speaker
|
|
*/
|
|
function updateActiveSpeakerAvatarSrc() {
|
|
var avatar = $("#activeSpeakerAvatar")[0];
|
|
var jid = currentSmallVideo.peerJid;
|
|
var url = Avatar.getGravatarUrl(jid);
|
|
if(avatar.src === url)
|
|
return;
|
|
var isMuted = null;
|
|
if(!LargeVideo.VideoLayout.isInLastN(currentSmallVideo.resourceJid)) {
|
|
isMuted = true;
|
|
}
|
|
else
|
|
{
|
|
isMuted = APP.RTC.isVideoMuted(jid);
|
|
}
|
|
|
|
if (jid && isMuted !== null) {
|
|
avatar.src = url;
|
|
$("#largeVideo").css("visibility", isMuted ? "hidden" : "visible");
|
|
currentSmallVideo.showAvatar(isMuted);
|
|
}
|
|
}
|
|
|
|
function changeVideo(isVisible) {
|
|
updateActiveSpeakerAvatarSrc();
|
|
|
|
APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc());
|
|
|
|
var videoTransform = document.getElementById('largeVideo')
|
|
.style.webkitTransform;
|
|
|
|
var flipX = currentSmallVideo.flipX;
|
|
|
|
if (flipX && videoTransform !== 'scaleX(-1)') {
|
|
document.getElementById('largeVideo').style.webkitTransform
|
|
= "scaleX(-1)";
|
|
}
|
|
else if (!flipX && videoTransform === 'scaleX(-1)') {
|
|
document.getElementById('largeVideo').style.webkitTransform
|
|
= "none";
|
|
}
|
|
|
|
var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid);
|
|
// Change the way we'll be measuring and positioning large video
|
|
|
|
getVideoSize = isDesktop
|
|
? getDesktopVideoSize
|
|
: getCameraVideoSize;
|
|
getVideoPosition = isDesktop
|
|
? getDesktopVideoPosition
|
|
: getCameraVideoPosition;
|
|
|
|
|
|
// Only if the large video is currently visible.
|
|
// Disable previous dominant speaker video.
|
|
if (oldSmallVideo) {
|
|
oldSmallVideo.enableDominantSpeaker(false);
|
|
}
|
|
|
|
// Enable new dominant speaker in the remote videos section.
|
|
if (currentSmallVideo) {
|
|
currentSmallVideo.enableDominantSpeaker(true);
|
|
}
|
|
|
|
if (isVisible) {
|
|
$('#largeVideo').fadeIn(300);
|
|
}
|
|
|
|
if(oldSmallVideo)
|
|
oldSmallVideo.showAvatar();
|
|
}
|
|
|
|
var LargeVideo = {
|
|
|
|
init: function (VideoLayout, emitter) {
|
|
this.VideoLayout = VideoLayout;
|
|
this.eventEmitter = emitter;
|
|
var self = this;
|
|
// Listen for large video size updates
|
|
var largeVideo = $('#largeVideo')[0];
|
|
var onplaying = function (arg1, arg2, arg3) {
|
|
// re-select
|
|
if (RTCBrowserType.isTemasysPluginUsed())
|
|
largeVideo = $('#largeVideo')[0];
|
|
currentVideoWidth = largeVideo.videoWidth;
|
|
currentVideoHeight = largeVideo.videoHeight;
|
|
self.position(currentVideoWidth, currentVideoHeight);
|
|
};
|
|
largeVideo.onplaying = onplaying;
|
|
},
|
|
/**
|
|
* Indicates if the large video is currently visible.
|
|
*
|
|
* @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
|
|
*/
|
|
isLargeVideoVisible: function() {
|
|
return $('#largeVideo').is(':visible');
|
|
},
|
|
/**
|
|
* Updates the large video with the given new video source.
|
|
*/
|
|
updateLargeVideo: function(resourceJid, forceUpdate) {
|
|
var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
|
|
console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
|
|
|
|
if ((currentSmallVideo && currentSmallVideo.resourceJid !== resourceJid)
|
|
|| forceUpdate) {
|
|
$('#activeSpeaker').css('visibility', 'hidden');
|
|
|
|
if(currentSmallVideo) {
|
|
oldSmallVideo = currentSmallVideo;
|
|
} else {
|
|
oldSmallVideo = null;
|
|
}
|
|
|
|
currentSmallVideo = newSmallVideo;
|
|
var oldJid = null;
|
|
if(oldSmallVideo)
|
|
oldJid = oldSmallVideo.peerJid;
|
|
|
|
if (oldJid !== resourceJid) {
|
|
// we want the notification to trigger even if userJid is undefined,
|
|
// or null.
|
|
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
|
|
resourceJid);
|
|
}
|
|
$('#largeVideo').fadeOut(300,
|
|
changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
|
|
} else {
|
|
if(currentSmallVideo) {
|
|
currentSmallVideo.showAvatar();
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Shows/hides the large video.
|
|
*/
|
|
setLargeVideoVisible: function(isVisible) {
|
|
if (isVisible) {
|
|
$('#largeVideo').css({visibility: 'visible'});
|
|
$('.watermark').css({visibility: 'visible'});
|
|
if(currentSmallVideo)
|
|
currentSmallVideo.enableDominantSpeaker(true);
|
|
}
|
|
else {
|
|
$('#largeVideo').css({visibility: 'hidden'});
|
|
$('#activeSpeaker').css('visibility', 'hidden');
|
|
$('.watermark').css({visibility: 'hidden'});
|
|
if(currentSmallVideo)
|
|
currentSmallVideo.enableDominantSpeaker(false);
|
|
}
|
|
},
|
|
onVideoTypeChanged: function (jid) {
|
|
if(jid && currentSmallVideo && jid === currentSmallVideo.peerJid)
|
|
{
|
|
var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
|
|
getVideoSize = isDesktop
|
|
? getDesktopVideoSize
|
|
: getCameraVideoSize;
|
|
getVideoPosition = isDesktop
|
|
? getDesktopVideoPosition
|
|
: getCameraVideoPosition;
|
|
this.position(null, null);
|
|
}
|
|
},
|
|
/**
|
|
* Positions the large video.
|
|
*
|
|
* @param videoWidth the stream video width
|
|
* @param videoHeight the stream video height
|
|
*/
|
|
position: function (videoWidth, videoHeight,
|
|
videoSpaceWidth, videoSpaceHeight, animate) {
|
|
if(!videoSpaceWidth)
|
|
videoSpaceWidth = $('#videospace').width();
|
|
if(!videoSpaceHeight)
|
|
videoSpaceHeight = window.innerHeight;
|
|
|
|
var videoSize = getVideoSize(videoWidth,
|
|
videoHeight,
|
|
videoSpaceWidth,
|
|
videoSpaceHeight);
|
|
|
|
var largeVideoWidth = videoSize[0];
|
|
var largeVideoHeight = videoSize[1];
|
|
|
|
var videoPosition = getVideoPosition(largeVideoWidth,
|
|
largeVideoHeight,
|
|
videoSpaceWidth,
|
|
videoSpaceHeight);
|
|
|
|
var horizontalIndent = videoPosition[0];
|
|
var verticalIndent = videoPosition[1];
|
|
|
|
positionVideo($('#largeVideo'),
|
|
largeVideoWidth,
|
|
largeVideoHeight,
|
|
horizontalIndent, verticalIndent, animate);
|
|
},
|
|
|
|
isLargeVideoOnTop: function () {
|
|
var Etherpad = require("../etherpad/Etherpad");
|
|
var Prezi = require("../prezi/Prezi");
|
|
return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
|
|
},
|
|
resize: function (animate, isVisible, completeFunction) {
|
|
var availableHeight = window.innerHeight;
|
|
var availableWidth = UIUtil.getAvailableVideoWidth(isVisible);
|
|
|
|
if (availableWidth < 0 || availableHeight < 0) return;
|
|
|
|
var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
|
|
var top = availableHeight / 2 - avatarSize / 4 * 3;
|
|
$('#activeSpeaker').css('top', top);
|
|
|
|
if(animate)
|
|
{
|
|
$('#videospace').animate({
|
|
right: window.innerWidth - availableWidth,
|
|
width: availableWidth,
|
|
height: availableHeight
|
|
},
|
|
{
|
|
queue: false,
|
|
duration: 500,
|
|
complete: completeFunction
|
|
});
|
|
|
|
$('#largeVideoContainer').animate({
|
|
width: availableWidth,
|
|
height: availableHeight
|
|
},
|
|
{
|
|
queue: false,
|
|
duration: 500
|
|
});
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
$('#videospace').width(availableWidth);
|
|
$('#videospace').height(availableHeight);
|
|
$('#largeVideoContainer').width(availableWidth);
|
|
$('#largeVideoContainer').height(availableHeight);
|
|
}
|
|
return [availableWidth, availableHeight];
|
|
|
|
},
|
|
resizeVideoAreaAnimated: function (isVisible, completeFunction) {
|
|
var size = this.resize(true, isVisible, completeFunction);
|
|
this.position(null, null, size[0], size[1], true);
|
|
},
|
|
getResourceJid: function () {
|
|
if(!currentSmallVideo)
|
|
return null;
|
|
return currentSmallVideo.resourceJid;
|
|
},
|
|
updateAvatar: function (resourceJid) {
|
|
if (resourceJid === this.getResourceJid()) {
|
|
updateActiveSpeakerAvatarSrc();
|
|
}
|
|
},
|
|
showAvatar: function (resourceJid, show) {
|
|
if(this.getResourceJid() === resourceJid
|
|
&& LargeVideo.isLargeVideoOnTop())
|
|
{
|
|
$("#largeVideo").css("visibility", show ? "hidden" : "visible");
|
|
$('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
module.exports = LargeVideo; |