jiti-meet/modules/UI/videolayout/LargeVideo.js

464 lines
14 KiB
JavaScript
Raw Normal View History

2015-06-23 08:00:46 +00:00
var Avatar = require("../avatar/Avatar");
var RTCBrowserType = require("../../RTC/RTCBrowserType");
2015-06-23 08:00:46 +00:00
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');
2015-06-23 08:00:46 +00:00
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 (!currentSmallVideo.isLocal &&
!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);
}
}
2015-06-23 08:00:46 +00:00
function changeVideo(isVisible) {
updateActiveSpeakerAvatarSrc();
2015-06-23 08:00:46 +00:00
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);
2015-06-23 08:00:46 +00:00
}
if(oldSmallVideo)
oldSmallVideo.showAvatar();
2015-06-23 08:00:46 +00:00
}
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;
2015-06-23 08:00:46 +00:00
},
/**
* Indicates if the large video is currently visible.
*
* @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
*/
isLargeVideoVisible: function() {
return $('#largeVideo').is(':visible');
2015-06-23 08:00:46 +00:00
},
/**
* 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);
2015-06-23 08:00:46 +00:00
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()));
2015-06-23 08:00:46 +00:00
} else {
if(currentSmallVideo) {
currentSmallVideo.showAvatar();
}
2015-06-23 08:00:46 +00:00
}
},
/**
* 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;
2015-06-23 08:00:46 +00:00
}
}
module.exports = LargeVideo;