Commit 93daa5f9 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Updated ofmeet and jitmeet. video recording is still not yet working

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@14004 b35dd754-fafc-0310-a699-88a17e54d16e
parent dc695cd8
...@@ -11,4 +11,13 @@ ...@@ -11,4 +11,13 @@
<url-pattern>/proxy</url-pattern> <url-pattern>/proxy</url-pattern>
</servlet-mapping> </servlet-mapping>
<servlet>
<servlet-name>config</servlet-name>
<servlet-class>org.jitsi.videobridge.openfire.Config</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>config</servlet-name>
<url-pattern>/config</url-pattern>
</servlet-mapping>
</web-app> </web-app>
\ No newline at end of file
...@@ -8,9 +8,25 @@ var nickname = null; ...@@ -8,9 +8,25 @@ var nickname = null;
var sharedKey = ''; var sharedKey = '';
var roomUrl = null; var roomUrl = null;
var ssrc2jid = {}; var ssrc2jid = {};
/**
* Indicates whether ssrc is camera video or desktop stream.
* FIXME: remove those maps
*/
var ssrc2videoType = {};
var videoSrcToSsrc = {};
var localVideoSrc = null; var localVideoSrc = null;
var flipXLocalVideo = true; var flipXLocalVideo = true;
var preziPlayer = null; var isFullScreen = false;
var toolbarTimeout = null;
var currentVideoWidth = null;
var currentVideoHeight = null;
/**
* Method used to calculate large video size.
* @type {function()}
*/
var getVideoSize;
/* window.onbeforeunload = closePageWarning; */ /* window.onbeforeunload = closePageWarning; */
...@@ -48,10 +64,13 @@ function init() { ...@@ -48,10 +64,13 @@ function init() {
if (config.useStunTurn) { if (config.useStunTurn) {
connection.jingle.getStunAndTurnCredentials(); connection.jingle.getStunAndTurnCredentials();
} }
getUserMediaWithConstraints( ['audio'], audioStreamReady, obtainAudioAndVideoPermissions(function(){
function(error){ getUserMediaWithConstraints( ['audio'], audioStreamReady,
console.error('failed to obtain audio stream - stop', error); function(error){
}); console.error('failed to obtain audio stream - stop', error);
});
});
document.getElementById('connect').disabled = true; document.getElementById('connect').disabled = true;
} else { } else {
console.log('status', status); console.log('status', status);
...@@ -59,6 +78,29 @@ function init() { ...@@ -59,6 +78,29 @@ function init() {
}); });
} }
/**
* HTTPS only:
* We first ask for audio and video combined stream in order to get permissions and not to ask twice.
* Then we dispose the stream and continue with separate audio, video streams(required for desktop sharing).
*/
function obtainAudioAndVideoPermissions(callback){
// This makes sense only on https sites otherwise we'll be asked for permissions every time
if(location.protocol !== 'https:') {
callback();
return;
}
// Get AV
getUserMediaWithConstraints(
['audio', 'video'],
function(avStream) {
avStream.stop();
callback();
},
function(error){
console.error('failed to obtain audio/video stream - stop', error);
});
}
function audioStreamReady(stream) { function audioStreamReady(stream) {
change_local_audio(stream); change_local_audio(stream);
...@@ -128,7 +170,6 @@ function doJoin() { ...@@ -128,7 +170,6 @@ function doJoin() {
} }
function change_local_audio(stream) { function change_local_audio(stream) {
connection.jingle.localAudio = stream; connection.jingle.localAudio = stream;
RTC.attachMediaStream($('#localAudio'), stream); RTC.attachMediaStream($('#localAudio'), stream);
document.getElementById('localAudio').autoplay = true; document.getElementById('localAudio').autoplay = true;
...@@ -145,7 +186,7 @@ function change_local_video(stream, flipX) { ...@@ -145,7 +186,7 @@ function change_local_video(stream, flipX) {
localVideo.volume = 0; // is it required if audio is separated ? localVideo.volume = 0; // is it required if audio is separated ?
localVideo.oncontextmenu = function () { return false; }; localVideo.oncontextmenu = function () { return false; };
var localVideoContainer = document.getElementById('localVideoContainer'); var localVideoContainer = document.getElementById('localVideoWrapper');
localVideoContainer.appendChild(localVideo); localVideoContainer.appendChild(localVideo);
var localVideoSelector = $('#' + localVideo.id); var localVideoSelector = $('#' + localVideo.id);
...@@ -169,25 +210,35 @@ function change_local_video(stream, flipX) { ...@@ -169,25 +210,35 @@ function change_local_video(stream, flipX) {
} }
$(document).bind('remotestreamadded.jingle', function (event, data, sid) { $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
function waitForRemoteVideo(selector, sid) { function waitForRemoteVideo(selector, sid, ssrc) {
if(selector.removed) { if(selector.removed) {
console.warn("media removed before had started", selector); console.warn("media removed before had started", selector);
return; return;
} }
var sess = connection.jingle.sessions[sid]; var sess = connection.jingle.sessions[sid];
if (data.stream.id === 'mixedmslabel') return; if (data.stream.id === 'mixedmslabel') return;
videoTracks = data.stream.getVideoTracks(); var videoTracks = data.stream.getVideoTracks();
console.log("waiting..", videoTracks, selector[0]); console.log("waiting..", videoTracks, selector[0]);
if (videoTracks.length === 0 || selector[0].currentTime > 0) { if (videoTracks.length === 0 || selector[0].currentTime > 0) {
RTC.attachMediaStream(selector, data.stream); // FIXME: why do i have to do this for FF? RTC.attachMediaStream(selector, data.stream); // FIXME: why do i have to do this for FF?
// FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
// in order to get rid of too many maps
if(ssrc) {
videoSrcToSsrc[sel.attr('src')] = ssrc;
} else {
console.warn("No ssrc given for video", sel);
}
$(document).trigger('callactive.jingle', [selector, sid]); $(document).trigger('callactive.jingle', [selector, sid]);
console.log('waitForremotevideo', sess.peerconnection.iceConnectionState, sess.peerconnection.signalingState); console.log('waitForremotevideo', sess.peerconnection.iceConnectionState, sess.peerconnection.signalingState);
} else { } else {
setTimeout(function () { waitForRemoteVideo(selector, sid); }, 250); setTimeout(function () { waitForRemoteVideo(selector, sid, ssrc); }, 250);
} }
} }
var sess = connection.jingle.sessions[sid]; var sess = connection.jingle.sessions[sid];
var thessrc;
// look up an associated JID for a stream id // look up an associated JID for a stream id
if (data.stream.id.indexOf('mixedmslabel') === -1) { if (data.stream.id.indexOf('mixedmslabel') === -1) {
var ssrclines = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc'); var ssrclines = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc');
...@@ -211,11 +262,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) { ...@@ -211,11 +262,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
container = document.getElementById( container = document.getElementById(
'participant_' + Strophe.getResourceFromJid(data.peerjid)); 'participant_' + Strophe.getResourceFromJid(data.peerjid));
if (!container) { if (!container) {
console.warn('no container for', data.peerjid); console.error('no container for', data.peerjid);
// create for now...
// FIXME: should be removed
container = addRemoteVideoContainer(
'participant_' + Strophe.getResourceFromJid(data.peerjid));
} else { } else {
//console.log('found container for', data.peerjid); //console.log('found container for', data.peerjid);
} }
...@@ -239,7 +286,9 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) { ...@@ -239,7 +286,9 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
vid.id = id; vid.id = id;
vid.autoplay = true; vid.autoplay = true;
vid.oncontextmenu = function () { return false; }; vid.oncontextmenu = function () { return false; };
container.appendChild(vid); container.appendChild(vid);
// TODO: make mixedstream display:none via css? // TODO: make mixedstream display:none via css?
if (id.indexOf('mixedmslabel') !== -1) { if (id.indexOf('mixedmslabel') !== -1) {
container.id = 'mixedstream'; container.id = 'mixedstream';
...@@ -251,7 +300,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) { ...@@ -251,7 +300,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
RTC.attachMediaStream(sel, data.stream); RTC.attachMediaStream(sel, data.stream);
if(isVideo) { if(isVideo) {
waitForRemoteVideo(sel, sid); waitForRemoteVideo(sel, sid, thessrc);
} }
data.stream.onended = function () { data.stream.onended = function () {
...@@ -295,8 +344,7 @@ function handleVideoThumbClicked(videoSrc) { ...@@ -295,8 +344,7 @@ function handleVideoThumbClicked(videoSrc) {
updateLargeVideo(videoSrc, 1); updateLargeVideo(videoSrc, 1);
$('audio').each(function (idx, el) { $('audio').each(function (idx, el) {
// We no longer mix so we check for local audio now if (el.id.indexOf('mixedmslabel') !== -1) {
if(el.id != 'localAudio') {
el.volume = 0; el.volume = 0;
el.volume = 1; el.volume = 1;
} }
...@@ -317,6 +365,11 @@ function checkChangeLargeVideo(removedVideoSrc){ ...@@ -317,6 +365,11 @@ function checkChangeLargeVideo(removedVideoSrc){
if(!pick) { if(!pick) {
console.info("Last visible video no longer exists"); console.info("Last visible video no longer exists");
pick = $('#remoteVideos>span[id!="mixedstream"]>video').get(0); pick = $('#remoteVideos>span[id!="mixedstream"]>video').get(0);
if(!pick) {
// Try local video
console.info("Fallback to local video...");
pick = $('#remoteVideos>span>span>video').get(0);
}
} }
// mute if localvideo // mute if localvideo
...@@ -450,8 +503,6 @@ $(document).bind('callactive.jingle', function (event, videoelem, sid) { ...@@ -450,8 +503,6 @@ $(document).bind('callactive.jingle', function (event, videoelem, sid) {
$(document).bind('callterminated.jingle', function (event, sid, reason) { $(document).bind('callterminated.jingle', function (event, sid, reason) {
// FIXME // FIXME
focus = null;
activecall = null;
}); });
$(document).bind('setLocalDescription.jingle', function (event, sid) { $(document).bind('setLocalDescription.jingle', function (event, sid) {
...@@ -525,16 +576,8 @@ $(document).bind('entered.muc', function (event, jid, info, pres) { ...@@ -525,16 +576,8 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
console.log('entered', jid, info); console.log('entered', jid, info);
console.log('is focus?' + focus ? 'true' : 'false'); console.log('is focus?' + focus ? 'true' : 'false');
var videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid); // Add Peer's container
var container = addRemoteVideoContainer(videoSpanId); ensurePeerContainerExists(jid);
if (info.displayName)
showDisplayName(videoSpanId, info.displayName);
var nickfield = document.createElement('span');
nickfield.appendChild(document.createTextNode(Strophe.getResourceFromJid(jid)));
container.appendChild(nickfield);
resizeThumbnails();
if (focus !== null) { if (focus !== null) {
// FIXME: this should prepare the video // FIXME: this should prepare the video
...@@ -549,11 +592,6 @@ $(document).bind('entered.muc', function (event, jid, info, pres) { ...@@ -549,11 +592,6 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
else if (sharedKey) { else if (sharedKey) {
updateLockButton(); updateLockButton();
} }
$(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) {
//console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
ssrc2jid[ssrc.getAttribute('ssrc')] = jid;
});
}); });
$(document).bind('left.muc', function (event, jid) { $(document).bind('left.muc', function (event, jid) {
...@@ -575,14 +613,10 @@ $(document).bind('left.muc', function (event, jid) { ...@@ -575,14 +613,10 @@ $(document).bind('left.muc', function (event, jid) {
} }
else if (focus && Object.keys(connection.emuc.members).length === 0) { else if (focus && Object.keys(connection.emuc.members).length === 0) {
console.log('everyone left'); console.log('everyone left');
if (focus !== null) { // FIXME: closing the connection is a hack to avoid some
// FIXME: closing the connection is a hack to avoid some // problemswith reinit
// problemswith reinit disposeConference();
if (focus.peerconnection !== null) { focus = new ColibriFocus(connection, config.hosts.bridge);
focus.peerconnection.close();
}
focus = new ColibriFocus(connection, config.hosts.bridge);
}
} }
if (connection.emuc.getPrezi(jid)) { if (connection.emuc.getPrezi(jid)) {
$(document).trigger('presentationremoved.muc', [jid, connection.emuc.getPrezi(jid)]); $(document).trigger('presentationremoved.muc', [jid, connection.emuc.getPrezi(jid)]);
...@@ -596,13 +630,19 @@ $(document).bind('presence.muc', function (event, jid, info, pres) { ...@@ -596,13 +630,19 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
if(ssrc2jid[ssrc] == jid){ if(ssrc2jid[ssrc] == jid){
delete ssrc2jid[ssrc]; delete ssrc2jid[ssrc];
} }
if(ssrc2videoType == jid){
delete ssrc2videoType[ssrc];
}
}); });
$(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) { $(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) {
//console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc')); //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
ssrc2jid[ssrc.getAttribute('ssrc')] = jid; var ssrcV = ssrc.getAttribute('ssrc');
ssrc2jid[ssrcV] = jid;
var type = ssrc.getAttribute('type'); var type = ssrc.getAttribute('type');
ssrc2videoType[ssrcV] = type;
// might need to update the direction if participant just went from sendrecv to recvonly // might need to update the direction if participant just went from sendrecv to recvonly
if (type === 'video' || type === 'screen') { if (type === 'video' || type === 'screen') {
var el = $('#participant_' + Strophe.getResourceFromJid(jid) + '>video'); var el = $('#participant_' + Strophe.getResourceFromJid(jid) + '>video');
...@@ -616,22 +656,16 @@ $(document).bind('presence.muc', function (event, jid, info, pres) { ...@@ -616,22 +656,16 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
//checkChangeLargeVideo(el); //checkChangeLargeVideo(el);
break; break;
} }
// Camera video or shared screen ?
if (type === 'screen') {
// Shared screen
//console.info("Have screen ssrc from "+jid, ssrc);
} else {
// Camera video
//console.info("Have camera ssrc from "+jid, ssrc);
}
} }
}); });
if (info.displayName) { if (info.displayName) {
if (jid === connection.emuc.myroomjid) if (jid === connection.emuc.myroomjid) {
showDisplayName('localVideoContainer', info.displayName + ' (me)'); showDisplayName('localVideoContainer', info.displayName + ' (me)');
else } else {
ensurePeerContainerExists(jid);
showDisplayName('participant_' + Strophe.getResourceFromJid(jid), info.displayName); showDisplayName('participant_' + Strophe.getResourceFromJid(jid), info.displayName);
}
} }
}); });
...@@ -662,160 +696,15 @@ $(document).bind('passwordrequired.muc', function (event, jid) { ...@@ -662,160 +696,15 @@ $(document).bind('passwordrequired.muc', function (event, jid) {
}); });
}); });
/*
* Presentation has been removed.
*/
$(document).bind('presentationremoved.muc', function(event, jid, presUrl) {
console.log('presentation removed', presUrl);
var presId = getPresentationId(presUrl);
setPresentationVisible(false);
$('#participant_' + Strophe.getResourceFromJid(jid) + '_' + presId).remove();
$('#presentation>iframe').remove();
if (preziPlayer !== null) {
preziPlayer.destroy();
preziPlayer = null;
}
});
/*
* Presentation has been added.
*/
$(document).bind('presentationadded.muc', function (event, jid, presUrl, currentSlide) {
console.log("presentation added", presUrl);
var presId = getPresentationId(presUrl);
var elementId = 'participant_' + Strophe.getResourceFromJid(jid) + '_' + presId;
var container = addRemoteVideoContainer(elementId);
resizeThumbnails();
var controlsEnabled = false;
if (jid === connection.emuc.myroomjid)
controlsEnabled = true;
setPresentationVisible(true);
$('#largeVideoContainer').hover(
function (event) {
if (isPresentationVisible())
$('#reloadPresentation').css({display:'inline-block'});
},
function (event) {
if (!isPresentationVisible())
$('#reloadPresentation').css({display:'none'});
else {
var e = event.toElement || event.relatedTarget;
while(e && e.parentNode && e.parentNode !== window) {
if (e.parentNode === this || e === this) {
return false;
}
e = e.parentNode;
}
$('#reloadPresentation').css({display:'none'});
}
});
preziPlayer = new PreziPlayer(
'presentation',
{preziId: presId,
width: $('#largeVideoContainer').width(),
height: $('#largeVideoContainer').height(),
controls: controlsEnabled,
debug: true
});
$('#presentation>iframe').attr('id', preziPlayer.options.preziId);
preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
console.log("prezi status", event.value);
if (event.value === PreziPlayer.STATUS_CONTENT_READY) {
if (jid !== connection.emuc.myroomjid)
preziPlayer.flyToStep(currentSlide);
}
});
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
console.log("event value", event.value);
connection.emuc.addCurrentSlideToPresence(event.value);
connection.emuc.sendPresence();
});
$("#" + elementId).css('background-image','url(../images/avatarprezi.png)');
$("#" + elementId).click(
function () {
setPresentationVisible(true);
}
);
});
/*
* Indicates presentation slide change.
*/
$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
if (preziPlayer) {
preziPlayer.flyToStep(current);
}
});
/**
* Returns the presentation id from the given url.
*/
function getPresentationId (presUrl) {
var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
return presIdTmp.substring(0, presIdTmp.indexOf('/'));
}
/*
* Reloads the current presentation.
*/
function reloadPresentation() {
var iframe = document.getElementById(preziPlayer.options.preziId);
iframe.src = iframe.src;
}
/*
* Shows/hides a presentation.
*/
function setPresentationVisible(visible) {
if (visible) {
// Trigger the video.selected event to indicate a change in the large video.
$(document).trigger("video.selected", [true]);
$('#largeVideo').fadeOut(300, function () {
$('#largeVideo').css({visibility:'hidden'});
$('#presentation>iframe').fadeIn(300, function() {
$('#presentation>iframe').css({opacity:'1'});
});
});
}
else {
if ($('#presentation>iframe')) {
$('#presentation>iframe').fadeOut(300, function () {
$('#presentation>iframe').css({opacity:'0'});
$('#largeVideo').fadeIn(300, function() {
$('#largeVideo').css({visibility:'visible'});
});
});
}
}
}
function isPresentationVisible() {
return ($('#presentation>iframe') !== null && $('#presentation>iframe').css('opacity') == 1);
}
/** /**
* Updates the large video with the given new video source. * Updates the large video with the given new video source.
*/ */
function updateLargeVideo(newSrc, vol) { function updateLargeVideo(newSrc, vol) {
console.log('hover in', newSrc); console.log('hover in', newSrc);
setPresentationVisible(false); if ($('#largeVideo').attr('src') != newSrc) {
if ($('#largeVideo').attr('src') !== newSrc) {
// FIXME: is it still required ? audio is separated var isVisible = $('#largeVideo').is(':visible');
//document.getElementById('largeVideo').volume = vol;
$('#largeVideo').fadeOut(300, function () { $('#largeVideo').fadeOut(300, function () {
$(this).attr('src', newSrc); $(this).attr('src', newSrc);
...@@ -831,11 +720,59 @@ function updateLargeVideo(newSrc, vol) { ...@@ -831,11 +720,59 @@ function updateLargeVideo(newSrc, vol) {
document.getElementById('largeVideo').style.webkitTransform = "none"; document.getElementById('largeVideo').style.webkitTransform = "none";
} }
$(this).fadeIn(300); // Change the way we'll be measuring large video
getVideoSize = isVideoSrcDesktop(newSrc) ? getVideoSizeFit : getVideoSizeCover;
if (isVisible)
$(this).fadeIn(300);
}); });
} }
} }
/**
* Checks if video identified by given src is desktop stream.
* @param videoSrc eg. blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
* @returns {boolean}
*/
function isVideoSrcDesktop(videoSrc){
// FIXME: fix this mapping mess...
// figure out if large video is desktop stream or just a camera
var isDesktop = false;
if(localVideoSrc === videoSrc) {
// local video
isDesktop = isUsingScreenStream;
} else {
// Do we have associations...
var videoSsrc = videoSrcToSsrc[videoSrc];
if(videoSsrc) {
var videoType = ssrc2videoType[videoSsrc];
if(videoType) {
// Finally there...
isDesktop = videoType === 'screen';
} else {
console.error("No video type for ssrc: " + videoSsrc);
}
} else {
console.error("No ssrc for src: " + videoSrc);
}
}
return isDesktop;
}
/**
* Shows/hides the large video.
*/
function setLargeVideoVisible(isVisible) {
if (isVisible) {
$('#largeVideo').css({visibility:'visible'});
$('#watermark').css({visibility:'visible'});
}
else {
$('#largeVideo').css({visibility:'hidden'});
$('#watermark').css({visibility:'hidden'});
}
}
function getConferenceHandler() { function getConferenceHandler() {
return focus ? focus : activecall; return focus ? focus : activecall;
} }
...@@ -857,6 +794,12 @@ function toggleVideo() { ...@@ -857,6 +794,12 @@ function toggleVideo() {
} }
); );
} }
var sess = focus || activecall;
if (!sess) {
return;
}
sess.pendingop = ismuted ? 'unmute' : 'mute';
sess.modifySources();
} }
function toggleAudio() { function toggleAudio() {
...@@ -867,47 +810,174 @@ function toggleAudio() { ...@@ -867,47 +810,174 @@ function toggleAudio() {
} }
} }
var resizeLarge = function () { /**
Chat.resizeChat(); * Positions the large video.
var availableHeight = window.innerHeight; *
var chatspaceWidth = $('#chatspace').is(":visible") * @param videoWidth the stream video width
? $('#chatspace').width() * @param videoHeight the stream video height
: 0; */
var positionLarge = function(videoWidth, videoHeight) {
var videoSpaceWidth = $('#videospace').width();
var 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);
};
var numvids = $('#remoteVideos>video:visible').length; /**
if (numvids < 5) * Returns an array of the video horizontal and vertical indents,
availableHeight -= 100; // min thumbnail height for up to 4 videos * so that if fits its parent.
else *
availableHeight -= 50; // min thumbnail height for more than 5 videos * @return an array with 2 elements, the horizontal indent and the vertical
* indent
*/
var getVideoPosition = function ( 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];
};
availableHeight -= 79; // padding + link ontop /**
var availableWidth = window.innerWidth - chatspaceWidth; * Returns an array of the video dimensions, so that it covers the screen.
var aspectRatio = 16.0 / 9.0; * It leaves no empty areas, but some parts of the video might not be visible.
if (availableHeight < availableWidth / aspectRatio) { *
availableWidth = Math.floor(availableHeight * aspectRatio); * @return an array with 2 elements, the video width and the video height
*/
function getVideoSizeCover(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 (availableWidth < 0 || availableHeight < 0) return;
$('#largeVideo').parent().width(availableWidth);
$('#largeVideo').parent().height(availableWidth / aspectRatio);
if ($('#presentation>iframe')) { if (availableHeight*aspectRatio < videoSpaceWidth) {
$('#presentation>iframe').width(availableWidth); availableWidth = videoSpaceWidth;
$('#presentation>iframe').height(availableWidth / aspectRatio); availableHeight = availableWidth / aspectRatio;
}
return [availableWidth, availableHeight];
}
/**
* 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 getVideoSizeFit(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 ($('#etherpad>iframe')) { if (availableHeight*aspectRatio >= videoSpaceWidth) {
$('#etherpad>iframe').width(availableWidth); availableWidth = videoSpaceWidth;
$('#etherpad>iframe').height(availableWidth / aspectRatio); availableHeight = availableWidth / aspectRatio;
} }
return [availableWidth, availableHeight];
}
/**
* 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) {
video.width(width);
video.height(height);
video.css({ top: verticalIndent + 'px',
bottom: verticalIndent + 'px',
left: horizontalIndent + 'px',
right: horizontalIndent + 'px'});
}
var resizeLargeVideoContainer = function () {
Chat.resizeChat();
var availableHeight = window.innerHeight;
var availableWidth = Util.getAvailableVideoWidth();
if (availableWidth < 0 || availableHeight < 0) return;
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
$('#largeVideoContainer').width(availableWidth);
$('#largeVideoContainer').height(availableHeight);
resizeThumbnails(); resizeThumbnails();
}; };
function resizeThumbnails() { function resizeThumbnails() {
// Calculate the available height, which is the inner window height minus 39px for the header // Calculate the available height, which is the inner window height minus
// minus 2px for the delimiter lines on the top and bottom of the large video, // 39px for the header minus 2px for the delimiter lines on the top and
// minus the 36px space inside the remoteVideos container used for highlighting shadow. // bottom of the large video, minus the 36px space inside the remoteVideos
var availableHeight = window.innerHeight - $('#largeVideo').height() - 59; // container used for highlighting shadow.
var availableHeight = 100;
var numvids = $('#remoteVideos>span:visible').length; var numvids = $('#remoteVideos>span:visible').length;
// Remove the 1px borders arround videos and the chat width. // Remove the 1px borders arround videos and the chat width.
...@@ -932,10 +1002,25 @@ $(document).ready(function () { ...@@ -932,10 +1002,25 @@ $(document).ready(function () {
// Set the defaults for prompt dialogs. // Set the defaults for prompt dialogs.
jQuery.prompt.setDefaults({persistent: false}); jQuery.prompt.setDefaults({persistent: false});
resizeLarge(); // Set default desktop sharing method
setDesktopSharing(config.desktopSharing);
// By default we cover the whole screen with video
getVideoSize = getVideoSizeCover;
resizeLargeVideoContainer();
$(window).resize(function () { $(window).resize(function () {
resizeLarge(); resizeLargeVideoContainer();
positionLarge();
}); });
// Listen for large video size updates
document.getElementById('largeVideo')
.addEventListener('loadedmetadata', function(e){
currentVideoWidth = this.videoWidth;
currentVideoHeight = this.videoHeight;
positionLarge(currentVideoWidth, currentVideoHeight);
});
if (!$('#settings').is(':visible')) { if (!$('#settings').is(':visible')) {
console.log('init'); console.log('init');
init(); init();
...@@ -967,8 +1052,25 @@ $(window).bind('beforeunload', function () { ...@@ -967,8 +1052,25 @@ $(window).bind('beforeunload', function () {
} }
}); });
} }
disposeConference();
}); });
function disposeConference() {
var handler = getConferenceHandler();
if(handler && handler.peerconnection) {
// FIXME: probably removing streams is not required and close() should be enough
if(connection.jingle.localAudio) {
handler.peerconnection.removeStream(connection.jingle.localAudio);
}
if(connection.jingle.localVideo) {
handler.peerconnection.removeStream(connection.jingle.localVideo);
}
handler.peerconnection.close();
}
focus = null;
activecall = null;
}
function dump(elem, filename){ function dump(elem, filename){
elem = elem.parentNode; elem = elem.parentNode;
elem.download = filename || 'meetlog.json'; elem.download = filename || 'meetlog.json';
...@@ -999,14 +1101,14 @@ function dump(elem, filename){ ...@@ -999,14 +1101,14 @@ function dump(elem, filename){
return false; return false;
} }
/* /**
* Changes the style class of the element given by id. * Changes the style class of the element given by id.
*/ */
function buttonClick(id, classname) { function buttonClick(id, classname) {
$(id).toggleClass(classname); // add the class to the clicked element $(id).toggleClass(classname); // add the class to the clicked element
} }
/* /**
* Opens the lock room dialog. * Opens the lock room dialog.
*/ */
function openLockDialog() { function openLockDialog() {
...@@ -1067,7 +1169,7 @@ function openLockDialog() { ...@@ -1067,7 +1169,7 @@ function openLockDialog() {
} }
} }
/* /**
* Opens the invite link dialog. * Opens the invite link dialog.
*/ */
function openLinkDialog() { function openLinkDialog() {
...@@ -1083,7 +1185,7 @@ function openLinkDialog() { ...@@ -1083,7 +1185,7 @@ function openLinkDialog() {
}); });
} }
/* /**
* Opens the settings dialog. * Opens the settings dialog.
*/ */
function openSettingsDialog() { function openSettingsDialog() {
...@@ -1124,110 +1226,7 @@ function openSettingsDialog() { ...@@ -1124,110 +1226,7 @@ function openSettingsDialog() {
}); });
} }
/* /**
* Opens the Prezi dialog, from which the user could choose a presentation to load.
*/
function openPreziDialog() {
var myprezi = connection.emuc.getPrezi(connection.emuc.myroomjid);
if (myprezi) {
$.prompt("Are you sure you would like to remove your Prezi?",
{
title: "Remove Prezi",
buttons: { "Remove": true, "Cancel": false},
defaultButton: 1,
submit: function(e,v,m,f){
if(v)
{
connection.emuc.removePreziFromPresence();
connection.emuc.sendPresence();
}
}
});
}
else if (preziPlayer !== null) {
$.prompt("Another participant is already sharing a Prezi." +
"This conference allows only one Prezi at a time.",
{
title: "Share a Prezi",
buttons: { "Ok": true},
defaultButton: 0,
submit: function(e,v,m,f){
$.prompt.close();
}
});
}
else {
var openPreziState = {
state0: {
html: '<h2>Share a Prezi</h2>' +
'<input id="preziUrl" type="text" placeholder="e.g. http://prezi.com/wz7vhjycl7e6/my-prezi" autofocus>',
persistent: false,
buttons: { "Share": true , "Cancel": false},
defaultButton: 1,
submit: function(e,v,m,f){
e.preventDefault();
if(v)
{
var preziUrl = document.getElementById('preziUrl');
if (preziUrl.value)
{
var urlValue
= encodeURI(Util.escapeHtml(preziUrl.value));
if (urlValue.indexOf('http://prezi.com/') !== 0
&& urlValue.indexOf('https://prezi.com/') !== 0)
{
$.prompt.goToState('state1');
return false;
}
else {
var presIdTmp = urlValue.substring(urlValue.indexOf("prezi.com/") + 10);
if (!Util.isAlphanumeric(presIdTmp)
|| presIdTmp.indexOf('/') < 2) {
$.prompt.goToState('state1');
return false;
}
else {
connection.emuc.addPreziToPresence(urlValue, 0);
connection.emuc.sendPresence();
$.prompt.close();
}
}
}
}
else
$.prompt.close();
}
},
state1: {
html: '<h2>Share a Prezi</h2>' +
'Please provide a correct prezi link.',
persistent: false,
buttons: { "Back": true, "Cancel": false },
defaultButton: 1,
submit:function(e,v,m,f) {
e.preventDefault();
if (v === 0)
$.prompt.close();
else
$.prompt.goToState('state0');
}
}
};
var myPrompt = jQuery.prompt(openPreziState);
myPrompt.on('impromptu:loaded', function(e) {
document.getElementById('preziUrl').focus();
});
myPrompt.on('impromptu:statechanged', function(e) {
document.getElementById('preziUrl').focus();
});
}
}
/*
* Locks / unlocks the room. * Locks / unlocks the room.
*/ */
function lockRoom(lock) { function lockRoom(lock) {
...@@ -1239,42 +1238,101 @@ function lockRoom(lock) { ...@@ -1239,42 +1238,101 @@ function lockRoom(lock) {
updateLockButton(); updateLockButton();
} }
/* /**
* Sets the shared key. * Sets the shared key.
*/ */
function setSharedKey(sKey) { function setSharedKey(sKey) {
sharedKey = sKey; sharedKey = sKey;
} }
/* /**
* Updates the lock button state. * Updates the lock button state.
*/ */
function updateLockButton() { function updateLockButton() {
buttonClick("#lockIcon", "fa fa-unlock fa-lg fa fa-lock fa-lg"); buttonClick("#lockIcon", "icon-security icon-security-locked");
} }
/* /**
* Hides the toolbar.
*/
var hideToolbar = function() {
var isToolbarHover = false;
$('#header').find('*').each(function(){
var id = $(this).attr('id');
if ($("#" + id + ":hover").length > 0) {
isToolbarHover = true;
}
});
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
if (!isToolbarHover) {
$('#header').hide("slide", { direction: "up", duration: 300});
}
else {
toolbarTimeout = setTimeout(hideToolbar, 2000);
}
};
/**
* Shows the call main toolbar. * Shows the call main toolbar.
*/ */
function showToolbar() { function showToolbar() {
$('#toolbar').css({visibility:"visible"}); if (!$('#header').is(':visible')) {
if (focus !== null) $('#header').show("slide", { direction: "up", duration: 300});
if (toolbarTimeout) {
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
toolbarTimeout = setTimeout(hideToolbar, 2000);
}
if (focus != null)
{ {
// TODO: Enable settings functionality. Need to uncomment the settings button in index.html. // TODO: Enable settings functionality. Need to uncomment the settings button in index.html.
// $('#settingsButton').css({visibility:"visible"}); // $('#settingsButton').css({visibility:"visible"});
} }
// Set desktop sharing method
setDesktopSharing(config.desktopSharing); // Show/hide desktop sharing button
showDesktopSharingButton();
} }
/* /**
* Docks/undocks the toolbar.
*
* @param isDock indicates what operation to perform
*/
function dockToolbar(isDock) {
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {
showToolbar();
}
// Then clear the time out, to dock the toolbar.
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
else {
if (!$('#header').is(':visible')) {
showToolbar();
}
else {
toolbarTimeout = setTimeout(hideToolbar, 2000);
}
}
}
/**
* Updates the room invite url. * Updates the room invite url.
*/ */
function updateRoomUrl(newRoomUrl) { function updateRoomUrl(newRoomUrl) {
roomUrl = newRoomUrl; roomUrl = newRoomUrl;
} }
/* /**
* Warning to the user that the conference window is about to be closed. * Warning to the user that the conference window is about to be closed.
*/ */
function closePageWarning() { function closePageWarning() {
...@@ -1284,7 +1342,7 @@ function closePageWarning() { ...@@ -1284,7 +1342,7 @@ function closePageWarning() {
return "You are about to leave this conversation."; return "You are about to leave this conversation.";
} }
/* /**
* Shows a visual indicator for the focus of the conference. * Shows a visual indicator for the focus of the conference.
* Currently if we're not the owner of the conference we obtain the focus * Currently if we're not the owner of the conference we obtain the focus
* from the connection.jingle.sessions. * from the connection.jingle.sessions.
...@@ -1319,6 +1377,28 @@ function showFocusIndicator() { ...@@ -1319,6 +1377,28 @@ function showFocusIndicator() {
} }
} }
/**
* Checks if container for participant identified by given peerJid exists in the document and creates it eventually.
* @param peerJid peer Jid to check.
*/
function ensurePeerContainerExists(peerJid){
var peerResource = Strophe.getResourceFromJid(peerJid);
var videoSpanId = 'participant_' + peerResource;
if($('#'+videoSpanId).length > 0) {
return;
}
var container = addRemoteVideoContainer(videoSpanId);
var nickfield = document.createElement('span');
nickfield.className = "nick";
nickfield.appendChild(document.createTextNode(peerResource));
container.appendChild(nickfield);
resizeThumbnails();
}
function addRemoteVideoContainer(id) { function addRemoteVideoContainer(id) {
var container = document.createElement('span'); var container = document.createElement('span');
container.id = id; container.id = id;
...@@ -1349,21 +1429,17 @@ function toggleFullScreen() { ...@@ -1349,21 +1429,17 @@ function toggleFullScreen() {
//Enter Full Screen //Enter Full Screen
if (fsElement.mozRequestFullScreen) { if (fsElement.mozRequestFullScreen) {
fsElement.mozRequestFullScreen(); fsElement.mozRequestFullScreen();
} }
else { else {
fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} }
} else { } else {
//Exit Full Screen //Exit Full Screen
if (document.mozCancelFullScreen) { if (document.mozCancelFullScreen) {
document.mozCancelFullScreen(); document.mozCancelFullScreen();
} else { } else {
document.webkitCancelFullScreen(); document.webkitCancelFullScreen();
document.webkitCancelFullScreen();
} }
} }
} }
...@@ -1457,6 +1533,11 @@ function showDisplayName(videoSpanId, displayName) { ...@@ -1457,6 +1533,11 @@ function showDisplayName(videoSpanId, displayName) {
} }
} }
/**
* Creates the edit display name button.
*
* @returns the edit button
*/
function createEditDisplayNameButton() { function createEditDisplayNameButton() {
var editButton = document.createElement('a'); var editButton = document.createElement('a');
editButton.className = 'displayname'; editButton.className = 'displayname';
...@@ -1464,3 +1545,36 @@ function createEditDisplayNameButton() { ...@@ -1464,3 +1545,36 @@ function createEditDisplayNameButton() {
return editButton; return editButton;
} }
/**
* Resizes and repositions videos in full screen mode.
*/
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange',
function() {
resizeLargeVideoContainer();
positionLarge();
isFullScreen = document.fullScreen
|| document.mozFullScreen
|| document.webkitIsFullScreen;
if (isFullScreen) {
setView("fullscreen");
}
else {
setView("default");
}
});
/**
* Sets the current view.
*/
function setView(viewName) {
// if (viewName == "fullscreen") {
// document.getElementById('videolayout_fullscreen').disabled = false;
// document.getElementById('videolayout_default').disabled = true;
// }
// else {
// document.getElementById('videolayout_default').disabled = false;
// document.getElementById('videolayout_fullscreen').disabled = true;
// }
}
\ No newline at end of file
...@@ -96,31 +96,78 @@ var Chat = (function (my) { ...@@ -96,31 +96,78 @@ var Chat = (function (my) {
var chatspace = $('#chatspace'); var chatspace = $('#chatspace');
var videospace = $('#videospace'); var videospace = $('#videospace');
var onShow = function () { var chatSize = (chatspace.is(":visible")) ? [0, 0] : Chat.getChatSize();
resizeLarge(); var videospaceWidth = window.innerWidth - chatSize[0];
$('#chatspace').show("slide", { direction: "right", duration: 500}); var videospaceHeight = window.innerHeight;
}; var videoSize
var onHide = function () { = getVideoSize(null, null, videospaceWidth, videospaceHeight);
$('#chatspace').hide("slide", { direction: "right", duration: 500}); var videoWidth = videoSize[0];
resizeLarge(); var videoHeight = videoSize[1];
}; var videoPosition = getVideoPosition( videoWidth,
videoHeight,
videospaceWidth,
videospaceHeight);
var horizontalIndent = videoPosition[0];
var verticalIndent = videoPosition[1];
if (chatspace.is(":visible")) { if (chatspace.is(":visible")) {
videospace.animate( {right: 0}, videospace.animate( {right: chatSize[0],
width: videospaceWidth,
height: videospaceHeight},
{queue: false, {queue: false,
duration: 500, duration: 500});
progress: onHide});
$('#largeVideoContainer').animate({ width: videospaceWidth,
height: videospaceHeight},
{queue: false,
duration: 500
});
$('#largeVideo').animate({ width: videoWidth,
height: videoHeight,
top: verticalIndent,
bottom: verticalIndent,
left: horizontalIndent,
right: horizontalIndent},
{ queue: false,
duration: 500
});
$('#chatspace').hide("slide", { direction: "right",
queue: false,
duration: 500});
} }
else { else {
videospace.animate({right: chatspace.width()}, videospace.animate({right: chatSize[0],
width: videospaceWidth,
height: videospaceHeight},
{queue: false, {queue: false,
duration: 500, duration: 500,
progress: onShow,
complete: function() { complete: function() {
scrollChatToBottom(); scrollChatToBottom();
chatspace.trigger('shown'); chatspace.trigger('shown');
} }
}); });
$('#largeVideoContainer').animate({ width: videospaceWidth,
height: videospaceHeight},
{queue: false,
duration: 500
});
$('#largeVideo').animate({ width: videoWidth,
height: videoHeight,
top: verticalIndent,
bottom: verticalIndent,
left: horizontalIndent,
right: horizontalIndent},
{queue: false,
duration: 500
});
$('#chatspace').show("slide", { direction: "right",
queue: false,
duration: 500});
} }
// Request the focus in the nickname field or the chat input field. // Request the focus in the nickname field or the chat input field.
...@@ -147,6 +194,18 @@ var Chat = (function (my) { ...@@ -147,6 +194,18 @@ var Chat = (function (my) {
* Resizes the chat area. * Resizes the chat area.
*/ */
my.resizeChat = function () { my.resizeChat = function () {
var chatSize = Chat.getChatSize();
$('#chatspace').width(chatSize[0]);
$('#chatspace').height(chatSize[1]);
resizeChatConversation();
};
/**
* Returns the size of the chat.
*/
my.getChatSize = function() {
var availableHeight = window.innerHeight; var availableHeight = window.innerHeight;
var availableWidth = window.innerWidth; var availableWidth = window.innerWidth;
...@@ -154,10 +213,7 @@ var Chat = (function (my) { ...@@ -154,10 +213,7 @@ var Chat = (function (my) {
if (availableWidth*0.2 < 200) if (availableWidth*0.2 < 200)
chatWidth = availableWidth*0.2; chatWidth = availableWidth*0.2;
$('#chatspace').width(chatWidth); return [chatWidth, availableHeight];
$('#chatspace').height(availableHeight - 40);
resizeChatConversation();
}; };
/** /**
...@@ -168,10 +224,11 @@ var Chat = (function (my) { ...@@ -168,10 +224,11 @@ var Chat = (function (my) {
var usermsgHeight = usermsgStyleHeight var usermsgHeight = usermsgStyleHeight
.substring(0, usermsgStyleHeight.indexOf('px')); .substring(0, usermsgStyleHeight.indexOf('px'));
$('#usermsg').width($('#chatspace').width() - 10);
$('#chatconversation').width($('#chatspace').width() - 10); $('#chatconversation').width($('#chatspace').width() - 10);
$('#chatconversation') $('#chatconversation')
.height(window.innerHeight - 50 - parseInt(usermsgHeight)); .height(window.innerHeight - 10 - parseInt(usermsgHeight));
} };
/** /**
* Shows/hides a visual notification, indicating that a message has arrived. * Shows/hides a visual notification, indicating that a message has arrived.
...@@ -179,25 +236,35 @@ var Chat = (function (my) { ...@@ -179,25 +236,35 @@ var Chat = (function (my) {
function setVisualNotification(show) { function setVisualNotification(show) {
var unreadMsgElement = document.getElementById('unreadMessages'); var unreadMsgElement = document.getElementById('unreadMessages');
var glower = $('#chatButton');
if (unreadMessages) { if (unreadMessages) {
unreadMsgElement.innerHTML = unreadMessages.toString(); unreadMsgElement.innerHTML = unreadMessages.toString();
showToolbar();
var chatButtonElement var chatButtonElement
= document.getElementById('chat').parentNode; = document.getElementById('chatButton').parentNode;
var leftIndent = (Util.getTextWidth(chatButtonElement) var leftIndent = (Util.getTextWidth(chatButtonElement)
- Util.getTextWidth(unreadMsgElement) - 5)/2; - Util.getTextWidth(unreadMsgElement))/2;
var topIndent = (Util.getTextHeight(chatButtonElement) var topIndent = (Util.getTextHeight(chatButtonElement)
- Util.getTextHeight(unreadMsgElement))/2 - 2; - Util.getTextHeight(unreadMsgElement))/2 - 3;
unreadMsgElement.setAttribute( unreadMsgElement.setAttribute(
'style', 'style',
'top:' + topIndent 'top:' + topIndent
+ '; left:' + leftIndent +';'); + '; left:' + leftIndent +';');
if (!glower.hasClass('icon-chat-simple')) {
glower.removeClass('icon-chat');
glower.addClass('icon-chat-simple');
}
} }
else else {
unreadMsgElement.innerHTML = ''; unreadMsgElement.innerHTML = '';
glower.removeClass('icon-chat-simple');
var glower = $('#chat'); glower.addClass('icon-chat');
}
if (show && !notificationInterval) { if (show && !notificationInterval) {
notificationInterval = window.setInterval(function() { notificationInterval = window.setInterval(function() {
......
var config = { config.desktopSharing = 'webrtc'; // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
hosts: { config.chromeExtensionId = 'diibjkoicjeejcmhdnailmkgecihlobk'; // Id of desktop streamer Chrome extension
domain: 'btg199251', config.minChromeExtVersion = '0.0.8'; // Required version of Chrome extension
muc: 'conference.btg199251', // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.btg199251' // FIXME: use XEP-0030
},
// useStunTurn: true, // use XEP-0215 to fetch STUN and TURN server
// useIPv6: true, // ipv6 support. use at your own risk
useNicks: false,
bosh: 'https://btg199251:7443/http-bind/', // FIXME: use xep-0156 for that
desktopSharing: 'webrtc', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
minChromeExtVersion: '0.0.8', // Required version of Chrome extension
getroomnode: function (path) config.getroomnode = function (path)
{ {
console.log('getroomnode', path); console.log('getroomnode', path);
var name = "r"; var name = "r";
var roomnode = null; var roomnode = null;
var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href); var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (!results) if (!results)
roomnode = null; roomnode = null;
else roomnode = results[1] || undefined; else roomnode = results[1] || undefined;
...@@ -31,5 +19,4 @@ var config = { ...@@ -31,5 +19,4 @@ var config = {
window.history.pushState('VideoChat', 'Room: ' + roomnode, path + "?r=" + roomnode); window.history.pushState('VideoChat', 'Room: ' + roomnode, path + "?r=" + roomnode);
} }
return roomnode; return roomnode;
} }
};
@font-face {
font-family: 'jitsi';
src:url('../fonts/jitsi.eot?94d075');
src:url('../fonts/jitsi.eot?#iefix94d075') format('embedded-opentype'),
url('../fonts/jitsi.woff?94d075') format('woff'),
url('../fonts/jitsi.ttf?94d075') format('truetype'),
url('../fonts/jitsi.svg?94d075#jitsi') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
font-family: 'jitsi';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 0.75em;
font-size: 1.22em;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-share-desktop:before {
content: "\e602";
}
.icon-chat-simple:before {
content: "\e606";
}
.icon-full-screen:before {
content: "\e60d";
}
.icon-exit-full-screen:before {
content: "\e60e";
}
.icon-prezi:before {
content: "\e60c";
}
.icon-link:before {
content: "\e600";
}
.icon-chat:before {
content: "\e601";
}
.icon-presentation:before {
content: "\e603";
}
.icon-security:before {
content: "\e604";
}
.icon-share-doc:before {
content: "\e605";
}
.icon-security-locked:before {
content: "\e607";
}
.icon-camera:before {
content: "\e608";
}
.icon-camera-disabled:before {
content: "\e609";
}
.icon-mic-disabled:before {
content: "\e60a";
}
.icon-microphone:before {
content: "\e60b";
}
...@@ -4,99 +4,15 @@ html, body{ ...@@ -4,99 +4,15 @@ html, body{
color: #424242; color: #424242;
font-family:'Helvetica Neue', Helvetica, sans-serif; font-family:'Helvetica Neue', Helvetica, sans-serif;
font-weight: 400; font-weight: 400;
background: #e9e9e9; background: #000000;
overflow-x: hidden; overflow-x: hidden;
} }
#videospace {
display: block;
position: absolute;
top: 39px;
left: 0px;
right: 0px;
float: left;
}
.videocontainer {
position: relative;
margin-left: auto;
margin-right: auto;
}
#presentation, #etherpad, .videocontainer>video {
position: absolute;
left: 0px;
top: 0px;
z-index: 1;
width: 100%;
height: 100%;
}
#etherpad {
z-index: 0;
}
#etherpadButton {
display: none;
}
.videocontainer>span {
display: none; /* enable when you want nicks to be shown */
position: absolute;
left: 0px;
bottom: -20px;
z-index: 0;
width: 100%;
font-size: 10pt;
}
.flipVideoX {
-moz-transform: scaleX(-1);
-webkit-transform: scaleX(-1);
-o-transform: scaleX(-1);
transform: scaleX(-1);
}
#remoteVideos {
display:block;
position:relative;
text-align:center;
height:196px;
padding: 5px 0px;
width:auto;
overflow: hidden;
border:1px solid transparent;
z-index: 2;
}
#remoteVideos>span {
display: inline-block;
z-index:0;
border:1px solid #FFFFFF;
background-image:url(../images/avatar1.png);
background-size: contain;
}
#remoteVideos>span:hover {
cursor: pointer;
cursor: hand;
transform:scale(1.08, 1.08);
-webkit-transform:scale(1.08, 1.08);
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
background-color: #FFFFFF;
-webkit-animation-name: greyPulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1;
-webkit-box-shadow: 0 0 18px #515151;
border:1px solid #FFFFFF;
z-index: 10;
}
#chatspace { #chatspace {
display:none; display:none;
position:absolute; position:absolute;
float: right; float: right;
top: 40px; top: 0px;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
width: 20%; width: 20%;
...@@ -105,9 +21,11 @@ html, body{ ...@@ -105,9 +21,11 @@ html, body{
/* background-color:#dfebf1;*/ /* background-color:#dfebf1;*/
background-color:#FFFFFF; background-color:#FFFFFF;
border-left:1px solid #424242; border-left:1px solid #424242;
z-index: 5;
} }
#chatconversation { #chatconversation {
visibility: hidden;
position: relative; position: relative;
top: 5px; top: 5px;
padding: 5px; padding: 5px;
...@@ -119,7 +37,6 @@ html, body{ ...@@ -119,7 +37,6 @@ html, body{
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
word-wrap: break-word; word-wrap: break-word;
visibility: hidden;
} }
.localuser { .localuser {
...@@ -131,20 +48,20 @@ html, body{ ...@@ -131,20 +48,20 @@ html, body{
} }
#usermsg { #usermsg {
visibility:hidden;
position: relative; position: relative;
width: 100%; width: 100%;
height: 5%; height: 5%;
padding: 5px; padding: 5px;
z-index: 5;
max-height:150px; max-height:150px;
min-height:50px; min-height:50px;
visibility:hidden;
border: 0px none; border: 0px none;
border-top: 1px solid #cccccc; border-top: 1px solid #cccccc;
background: #FFFFFF; background: #FFFFFF;
box-shadow: none; box-shadow: none;
border-radius:0; border-radius:0;
font-size: 10pt; font-size: 10pt;
overflow: hidden;
} }
#usermsg: hover { #usermsg: hover {
...@@ -169,10 +86,6 @@ html, body{ ...@@ -169,10 +86,6 @@ html, body{
font-size: 14; font-size: 14;
} }
#spacer {
height:5px;
}
#settings { #settings {
display:none; display:none;
} }
...@@ -181,47 +94,6 @@ html, body{ ...@@ -181,47 +94,6 @@ html, body{
display:none; display:none;
} }
#header{
display:block;
height:39px;
text-align:center;
background-color: #2591e5;
}
#left {
display:block;
position: absolute;
left: 0px;
top: 0px;
width: 100px;
height: 39px;
background-image:url(../images/left1.png);
background-repeat:no-repeat;
margin: 0;
padding: 0;
}
#leftlogo {
position:absolute;
top: 5px;
left: 15px;
background-image:url(../images/jitsilogo.png);
background-repeat:no-repeat;
height: 31px;
width: 68px;
z-index:1;
}
#toolbar {
display:block;
position:relative;
height:39px;
width:auto;
overflow: hidden;
z-index:0;
visibility: hidden;
}
#settingsButton { #settingsButton {
visibility: hidden; visibility: hidden;
} }
...@@ -254,18 +126,16 @@ html, body{ ...@@ -254,18 +126,16 @@ html, body{
cursor: pointer; cursor: pointer;
} }
#chat { #chatButton {
font-size:1.65em;
-webkit-transition: all .5s ease-in-out;; -webkit-transition: all .5s ease-in-out;;
-moz-transition: all .5s ease-in-out;; -moz-transition: all .5s ease-in-out;;
transition: all .5s ease-in-out;; transition: all .5s ease-in-out;;
} }
#chat.active { #chatButton.active {
-webkit-text-shadow: 0 0 10px #ffffff; -webkit-text-shadow: 0 0 10px #ffffff;
-moz-text-shadow: 0 0 10px #ffffff; -moz-text-shadow: 0 0 10px #ffffff;
text-shadow: 0 0 10px #ffffff; text-shadow: 0 0 10px #ffffff;
/* -webkit-transform: scale(1.1); */
} }
a.button:hover { a.button:hover {
...@@ -282,44 +152,13 @@ a.button:hover { ...@@ -282,44 +152,13 @@ a.button:hover {
color: #636363; color: #636363;
} }
.fade_line {
height: 1px;
background: black;
background: -webkit-gradient(linear, 0 0, 100% 0, from(#e9e9e9), to(#e9e9e9), color-stop(50%, black));
}
.header_button_separator { .header_button_separator {
display: inline-block; display: inline-block;
position:relative; position:relative;
top: 7; top: 5;
width: 1px; width: 1px;
height: 25px; height: 20px;
background: white; background: #676767;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#087dba), to(#087dba), color-stop(50%, white));
}
#right {
display:block;
position:absolute;
right: 0px;
top: 0px;
background-image:url(../images/right1.png);
background-repeat:no-repeat;
margin:0;
padding:0;
width:100px;
height:39px;
}
#rightlogo {
position:absolute;
top: 6px;
right: 15px;
background-image:url(../images/estoslogo.png);
background-repeat:no-repeat;
height: 25px;
width: 62px;
z-index:1;
} }
input[type='text'], textarea { input[type='text'], textarea {
...@@ -387,70 +226,3 @@ form { ...@@ -387,70 +226,3 @@ form {
overflow: visible; overflow: visible;
z-index: 100; z-index: 100;
} }
.videocontainer>span.focusindicator {
display: inline-block;
position: absolute;
color: #FFFFFF;
top: 0;
padding: 5px 0px;
width: 25px;
font-size: 11pt;
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
border: 0px;
z-index: 2;
}
.videocontainer>span.displayname,
.videocontainer>input.displayname {
display: inline-block;
position: absolute;
background: -webkit-linear-gradient(left, rgba(0,0,0,.7), rgba(0,0,0,0));
color: #b7b7b7;
bottom: 0;
left: 0;
padding: 3px 5px;
width: 100%;
height: auto;
max-height: 18px;
font-size: 9pt;
text-align: left;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
z-index: 2;
box-sizing: border-box;
}
#localVideoContainer>span.displayname:hover {
cursor: text;
}
.videocontainer>a.displayname {
display: inline-block;
position: absolute;
color: #b7b7b7;
bottom: 0;
right: 0;
padding: 3px 5px;
font-size: 9pt;
cursor: pointer;
z-index: 2;
}
#reloadPresentation {
display: none;
position: absolute;
color: #FFFFFF;
top: 0;
right: 0;
padding: 10px 10px;
font-size: 11pt;
cursor: pointer;
background: rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-clip: padding-box;
-webkit-border-radius: 5px;
-webkit-background-clip: padding-box;
z-index: 3;
}
#videospace {
display: block;
position: absolute;
top: 0px;
left: 0px;
right: 0px;
}
#remoteVideos {
display:block;
position:absolute;
text-align:right;
height:196px;
padding: 18px;
bottom: 0;
left: 0;
right: 0;
width:auto;
overflow: hidden;
border:1px solid transparent;
z-index: 2;
}
.videocontainer {
position: relative;
margin-left: auto;
margin-right: auto;
}
#remoteVideos .videocontainer {
display: inline-block;
background-image:url(../images/avatar1.png);
background-size: contain;
border-radius:8px;
border: 2px solid #212425;
}
#remoteVideos .videocontainer:hover {
width: 100%;
height: 100%;
content:"";
cursor: pointer;
cursor: hand;
transform:scale(1.08, 1.08);
-webkit-transform:scale(1.08, 1.08);
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
-webkit-animation-name: greyPulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1;
-webkit-box-shadow: 0 0 18px #388396;
border: 2px solid #388396;
z-index: 3;
}
#localVideoWrapper {
display:inline-block;
-webkit-mask-box-image: url(http://emcho.com/db/videomask.svg);
border-radius:0px !important;
border: 0px !important;
}
#remoteVideos .videocontainer>video {
border-radius:6px;
}
.flipVideoX {
transform: scale(-1, 1);
-moz-transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
-o-transform: scale(-1, 1);
}
#localVideoWrapper>video {
border-radius:0px !important;
}
#largeVideo,
#largeVideoContainer {
overflow: hidden;
text-align: center;
}
#presentation,
#etherpad,
#localVideoWrapper>video,
#localVideoWrapper,
.videocontainer>video {
position: absolute;
left: 0;
top: 0;
z-index: 1;
width: 100%;
height: 100%;
}
#etherpad,
#presentation {
text-align: center;
}
#etherpad {
z-index: 0;
}
#etherpadButton {
display: none;
}
#remoteVideos .videocontainer>span.focusindicator {
display: inline-block;
position: absolute;
color: #FFFFFF;
top: 0;
left: 0;
padding: 5px 0px;
width: 25px;
font-size: 11pt;
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
border: 0px;
z-index: 2;
}
#remoteVideos .nick {
display: none; /* enable when you want nicks to be shown */
position: absolute;
left: 0px;
bottom: -20px;
z-index: 0;
width: 100%;
font-size: 10pt;
}
.videocontainer>span.displayname,
.videocontainer>input.displayname {
display: inline-block;
position: absolute;
background: -webkit-linear-gradient(left, rgba(0,0,0,.7), rgba(0,0,0,0));
color: #FFFFFF;
bottom: 0;
left: 0;
padding: 3px 5px;
width: 100%;
height: auto;
max-height: 18px;
font-size: 9pt;
text-align: left;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
z-index: 2;
box-sizing: border-box;
border-bottom-left-radius:6px;
border-bottom-right-radius:6px;
}
#localVideoContainer>span.displayname:hover {
cursor: text;
}
.videocontainer>a.displayname {
display: inline-block;
position: absolute;
color: #FFFFFF;
bottom: 0;
right: 0;
padding: 3px 5px;
font-size: 9pt;
cursor: pointer;
z-index: 2;
}
#reloadPresentation {
display: none;
position: absolute;
color: #FFFFFF;
top: 0;
right:0;
padding: 10px 10px;
font-size: 11pt;
cursor: pointer;
background: rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-clip: padding-box;
-webkit-border-radius: 5px;
-webkit-background-clip: padding-box;
z-index: 20; /*The reload button should appear on top of the header!*/
}
#header{
display:none;
position:absolute;
height:39px;
text-align:center;
top:0;
left:0;
right:0;
z-index:10;
}
#toolbar {
display:inline-block;
position:relative;
margin-left:auto;
margin-right:auto;
height:39px;
width:auto;
overflow: hidden;
background: linear-gradient(to bottom, rgba(103,103,103,.65) , rgba(0,0,0,.65));
-webkit-box-shadow: 0 0 2px #000000, 0 0 10px #000000;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
#watermark {
display: block;
position: absolute;
left: 15;
top: 15;
width: 20%;
height: 10%;
background-image:url(../images/watermark.png);
background-size: contain;
background-repeat: no-repeat;
z-index: 2;
}
\ No newline at end of file
...@@ -16,20 +16,29 @@ var switchInProgress = false; ...@@ -16,20 +16,29 @@ var switchInProgress = false;
*/ */
var obtainDesktopStream = null; var obtainDesktopStream = null;
/**
* Flag used to cache desktop sharing enabled state. Do not use directly as it can be <tt>null</tt>.
* @type {null|boolean}
*/
var _desktopSharingEnabled = null;
/** /**
* @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled. * @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled.
*/ */
function isDesktopSharingEnabled() { function isDesktopSharingEnabled() {
if(obtainDesktopStream === obtainScreenFromExtension) { if(_desktopSharingEnabled === null){
// Parse chrome version if(obtainDesktopStream === obtainScreenFromExtension) {
var userAgent = navigator.userAgent.toLowerCase(); // Parse chrome version
// We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set var userAgent = navigator.userAgent.toLowerCase();
var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10); // We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set
console.log("Chrome version" + userAgent, ver); var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
return ver >= 35; console.log("Chrome version" + userAgent, ver);
} else { _desktopSharingEnabled = ver >= 35;
return obtainDesktopStream === obtainWebRTCScreen; } else {
_desktopSharingEnabled = obtainDesktopStream === obtainWebRTCScreen;
}
} }
return _desktopSharingEnabled;
} }
/** /**
...@@ -39,21 +48,21 @@ function isDesktopSharingEnabled() { ...@@ -39,21 +48,21 @@ function isDesktopSharingEnabled() {
* must be enabled), pass any other string or nothing in order to disable this feature completely. * must be enabled), pass any other string or nothing in order to disable this feature completely.
*/ */
function setDesktopSharing(method) { function setDesktopSharing(method) {
if(method == "ext") { // Check if we are running chrome
if(RTC.browser === 'chrome') { if(!navigator.webkitGetUserMedia){
obtainDesktopStream = obtainScreenFromExtension;
console.info("Using Chrome extension for desktop sharing");
} else {
console.error("Chrome is required to use extension method");
obtainDesktopStream = null;
}
} else if(method == "webrtc") {
obtainDesktopStream = obtainWebRTCScreen;
console.info("Using WebRTC for desktop sharing");
} else {
obtainDesktopStream = null; obtainDesktopStream = null;
console.info("Desktop sharing disabled"); console.info("Desktop sharing disabled");
} else if(method == "ext") {
obtainDesktopStream = obtainScreenFromExtension;
console.info("Using Chrome extension for desktop sharing");
} else if(method == "webrtc") {
obtainDesktopStream = obtainWebRTCScreen;
console.info("Using Chrome WebRTC for desktop sharing");
} }
// Reset enabled cache
_desktopSharingEnabled = null;
showDesktopSharingButton(); showDesktopSharingButton();
} }
......
...@@ -33,21 +33,23 @@ var Etherpad = (function (my) { ...@@ -33,21 +33,23 @@ var Etherpad = (function (my) {
if (!etherpadIFrame) if (!etherpadIFrame)
createIFrame(); createIFrame();
// TODO FIX large video and prezi toggling. Too many calls from different places.
var largeVideo = null; var largeVideo = null;
if (isPresentationVisible()) if (Prezi.isPresentationVisible())
largeVideo = $('#presentation>iframe'); largeVideo = $('#presentation>iframe');
else else
largeVideo = $('#largeVideo'); largeVideo = $('#largeVideo');
if ($('#etherpad>iframe').css('visibility') === 'hidden') { if ($('#etherpad>iframe').css('visibility') === 'hidden') {
largeVideo.fadeOut(300, function () { largeVideo.fadeOut(300, function () {
if (isPresentationVisible()) if (Prezi.isPresentationVisible())
largeVideo.css({opacity:'0'}); largeVideo.css({opacity:'0'});
else else {
largeVideo.css({visibility:'hidden'}); setLargeVideoVisible(false);
dockToolbar(true);
}
$('#etherpad>iframe').fadeIn(300, function() { $('#etherpad>iframe').fadeIn(300, function() {
document.body.style.background = '#eeeeee';
$('#etherpad>iframe').css({visibility:'visible'}); $('#etherpad>iframe').css({visibility:'visible'});
$('#etherpad').css({zIndex:2}); $('#etherpad').css({zIndex:2});
}); });
...@@ -57,15 +59,33 @@ var Etherpad = (function (my) { ...@@ -57,15 +59,33 @@ var Etherpad = (function (my) {
$('#etherpad>iframe').fadeOut(300, function () { $('#etherpad>iframe').fadeOut(300, function () {
$('#etherpad>iframe').css({visibility:'hidden'}); $('#etherpad>iframe').css({visibility:'hidden'});
$('#etherpad').css({zIndex:0}); $('#etherpad').css({zIndex:0});
document.body.style.background = 'black';
if (!isPresentation) { if (!isPresentation) {
$('#largeVideo').fadeIn(300, function() { $('#largeVideo').fadeIn(300, function() {
$('#largeVideo').css({visibility:'visible'}); setLargeVideoVisible(true);
dockToolbar(false);
}); });
} }
}); });
} }
resize();
}; };
/**
* Resizes the etherpad.
*/
function resize() {
if ($('#etherpad>iframe').length) {
var remoteVideos = $('#remoteVideos');
var availableHeight
= window.innerHeight - remoteVideos.outerHeight();
var availableWidth = Util.getAvailableVideoWidth();
$('#etherpad>iframe').width(availableWidth);
$('#etherpad>iframe').height(availableHeight);
}
}
/** /**
* Shares the Etherpad name with other participants. * Shares the Etherpad name with other participants.
*/ */
...@@ -127,5 +147,12 @@ var Etherpad = (function (my) { ...@@ -127,5 +147,12 @@ var Etherpad = (function (my) {
Etherpad.toggleEtherpad(isPresentation); Etherpad.toggleEtherpad(isPresentation);
}); });
/**
* Resizes the etherpad, when the window is resized.
*/
$(window).resize(function () {
resize();
});
return my; return my;
}(Etherpad || {})); }(Etherpad || {}));
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="jitsi" horiz-adv-x="512">
<font-face units-per-em="512" ascent="480" descent="-32" />
<missing-glyph horiz-adv-x="512" />
<glyph unicode="&#x20;" d="" horiz-adv-x="256" />
<glyph unicode="&#xe600;" d="M162.143 99.956c-13.522-0.146-24.863 7.774-29.852 20.164-1.911 4.822-1.9 9.598-1.865 13.781 0.146 11.159 5.852 18.418 10.793 23.563l176.701 185.143c4.673 4.908 10.599 7.942 18.122 9.315 1.569 0.284 3.145 0.427 4.69 0.443 11.064 0.12 22.613-6.356 27.503-15.401 2.315-4.202 3.714-8.478 4.301-13.042 1.106-8.555-0.829-16.432-5.736-23.433-1.303-1.836-2.858-3.44-4.421-5.014l-158.308-166.022c-6.047-6.321-12.078-12.64-17.983-19.085-6.311-6.825-13.743-10.366-22.099-10.457 0 0.002-1.811 0.046-1.846 0.046zM339.482 168.63c-10.974 0-21.341 4.307-29.194 12.099l-110.512 110.16c-7.819 7.809-12.137 18.148-12.137 29.133 0.016 10.763 4.192 20.82 11.727 28.332l120.504 120.138c7.565 7.526 17.655 11.675 28.437 11.675 10.971 0 21.372-4.306 29.192-12.1l110.494-110.142c15.955-15.98 16.142-41.753 0.411-57.482l-120.487-120.12c-7.568-7.542-17.654-11.693-28.435-11.693zM338.851 229.352l100.377 100.053-90.29 90.057-100.406-100.082 90.319-90.028zM151.84-31.065c-10.987 0-21.341 4.274-29.191 12.070l-110.512 110.188c-7.817 7.793-12.138 18.13-12.138 29.103 0.015 10.777 4.192 20.836 11.727 28.348l120.492 120.137c7.565 7.527 17.667 11.676 28.435 11.676 11.003 0 21.375-4.305 29.209-12.101l110.495-110.158c15.952-15.967 16.143-41.738 0.411-57.45l-120.49-120.154c-7.565-7.512-17.672-11.658-28.438-11.658zM151.227 29.654l100.362 100.038-90.306 90.060-100.389-100.087 90.334-90.012z" horiz-adv-x="501" />
<glyph unicode="&#xe601;" d="M478.031 481.466h-409.912c-36.518 0-66.245-30.358-66.245-67.658v-270.103c0-37.269 29.727-67.623 66.245-67.623h17.913v-106.471l172.493 106.471h219.506c36.482 0 66.21 30.356 66.21 67.623v270.101c0 37.301-29.728 67.659-66.21 67.659zM248.25 146.943l-97.857-61.998v61.998h-79.13v263.628h403.594l0.032-263.627h-226.639zM119.531 357.777h302.563v-55.31h-302.563v55.31zM119.531 261.238h302.563v-55.308h-302.563v55.308z" horiz-adv-x="544" />
<glyph unicode="&#xe602;" d="M477.908 480.159h-411.421c-36.651 0-66.487-30.466-66.487-67.922v-271.093c0-37.425 29.836-67.892 66.487-67.892h411.421c36.62 0 66.458 30.467 66.458 67.892v271.093c0 37.457-29.838 67.922-66.458 67.922zM474.755 144.392h-405.113v264.611h405.082l0.031-264.611zM472.61 0.17c0-17.869-14.13-32.33-31.603-32.33h-337.614c-17.475 0-31.605 14.461-31.605 32.33v14.809c0 17.85 14.13 32.329 31.605 32.329h337.614c17.473 0 31.603-14.478 31.603-32.329v-14.809zM388.396 171.217l-151.334 151.303 56.206 56.158 151.273-151.301v-56.159z" horiz-adv-x="545" />
<glyph unicode="&#xe603;" d="M476.248 477.533h-409.344c-36.405 0-66.091-30.315-66.091-67.581v-375.36c0-37.236 29.686-67.55 66.091-67.55h409.343c36.468 0 66.157 30.313 66.157 67.55v375.361c0.001 37.266-29.689 67.579-66.156 67.579zM473.173 37.825h-403.070v368.911h403.007l0.063-368.911zM342.877 337.272h108.455v-283.379h-108.456v283.379zM214.336 273.001h108.456v-219.108h-108.456v219.108zM86.169 208.73h108.081v-154.839h-108.081v154.839z" horiz-adv-x="544" />
<glyph unicode="&#xe604;" d="M439.13 482.757c-81.772 0-148.286-66.518-148.286-148.306v-21.876h-224.454c-36.57 0-66.388-30.454-66.388-67.875v-206.384c0-37.389 29.819-67.839 66.388-67.839h282.076c36.632 0 66.459 30.45 66.459 67.839v206.384c0 35.027-26.134 63.948-59.52 67.504v22.247c0 46.183 37.577 83.745 83.726 83.745 46.152 0 83.731-37.56 83.731-83.745v-38.711c0-17.841 14.442-32.282 32.278-32.282 17.845 0 32.284 14.441 32.284 32.282v38.711c-0.001 81.788-66.514 148.306-148.293 148.306z" horiz-adv-x="590" />
<glyph unicode="&#xe605;" d="M0.759 320.807h138.767v159.899c0 0-39.017-4.051-88.090-55.817-49.069-51.764-50.676-104.082-50.676-104.082zM341.64 480.706h-169.842v-192.298l-171.040 0.125-0.757 1.734v-255.251c0-36.923 30.7-66.99 68.424-66.99h273.217c37.757 0 68.456 30.068 68.456 66.99v378.702c-0.002 36.921-30.699 66.988-68.457 66.988zM345.927 72.582h-286.424v46.394h286.423v-46.394zM345.927 169.401h-286.424v46.392h286.423v-46.392z" horiz-adv-x="410" />
<glyph unicode="&#xe606;" d="M476.95 481.193h-409.887c-36.483 0-66.209-30.356-66.209-67.672v-270.084c0-37.284 29.727-67.639 66.209-67.639h17.912v-106.445l172.483 106.445h219.493c36.482 0 66.208 30.355 66.208 67.639v270.084c0.001 37.316-29.725 67.672-66.207 67.672zM247.214 146.677l-97.885-62v62h-79.092v263.626h403.539l0.062-263.626h-226.625z" horiz-adv-x="545" />
<glyph unicode="&#xe607;" d="M354.757 310.047v22.227c0 81.545-66.331 147.875-147.875 147.875-81.546 0-147.876-66.329-147.876-147.875v-22.227c-33.113-3.697-59.007-32.458-59.007-67.304v-205.811c0-37.315 29.741-67.683 66.236-67.683h281.291c36.529 0 66.267 30.368 66.267 67.683v205.811c0 34.848-25.896 63.609-59.037 67.304zM206.882 415.769c46.022 0 83.493-37.472 83.493-83.494v-21.816h-166.989v21.816c0 46.022 37.441 83.494 83.495 83.494z" horiz-adv-x="414" />
<glyph unicode="&#xe608;" d="M613.039 358.427l-90.297-88.124v109.103c0 37.441-29.829 67.911-66.474 67.911h-318.101c-36.644 0-66.469-30.47-66.469-67.911v-305.329c0-37.425 29.826-67.894 66.469-67.894h318.101c36.645 0 66.474 30.469 66.474 67.894v86.562l96.954-77.070c24.451-17.791 48.463 2.608 48.463 20.292v242.327c0.001 17.682-28.015 35.754-55.122 12.24zM301.315 99.154c-70.723 0-128.065 57.342-128.065 128.066s57.341 128.065 128.065 128.065c70.724 0 128.067-57.341 128.067-128.065 0-70.725-57.344-128.066-128.067-128.066zM296.894 299.788c-40.335 0-73.037-32.704-73.037-73.036 0-40.335 32.703-73.040 73.037-73.040 40.331 0 73.036 32.707 73.036 73.040 0 40.332-32.705 73.036-73.036 73.036zM296.894 260.049c-14.916 0-27.014-12.116-27.014-27.013 0-4.423-3.594-8.004-8.005-8.004-4.44 0-8.002 3.58-8.002 8.004 0 23.716 19.291 43.024 43.022 43.024 4.409 0 8.002-3.58 8.002-8.005-0.002-4.426-3.596-8.005-8.002-8.005z" horiz-adv-x="667" />
<glyph unicode="&#xe609;" d="M611.967 358.573l-90.149-87.978v108.924c0 3.831-0.333 7.574-0.951 11.216l36.847 32.673c13.174 11.705 14.42 31.9 2.684 45.12-11.737 13.203-31.902 14.436-45.136 2.7l-504.51-447.356c-13.204-11.705-14.421-31.903-2.699-45.104 6.303-7.118 15.091-10.769 23.925-10.769 7.538 0 15.107 2.652 21.195 8.050l47.92 42.49c10.498-7.313 23.13-11.616 36.796-11.616h317.596c36.55 0 66.33 30.404 66.33 67.769v86.434l96.83-76.978c24.408-17.73 48.383 2.624 48.383 20.292v241.914c0.001 17.653-27.966 35.693-55.062 12.22zM300.757 99.724c-29.405 0-56.283 10.108-77.763 26.899l41.465 36.767c10.431-5.832 22.425-9.193 35.235-9.193 40.267 0 72.916 32.649 72.916 72.918 0 9.71-1.948 18.928-5.428 27.357l43.423 38.501c11.424-19.13 18.006-41.484 18.006-65.391 0-70.607-57.246-127.855-127.853-127.855zM172.899 227.58c0 70.608 57.248 127.857 127.858 127.857 10.75 0 21.038-1.717 30.993-4.214l108.379 96.096h-302.237c-36.569 0-66.349-30.419-66.349-67.799v-259.037l102.947 91.272c-0.654 5.243-1.592 10.426-1.592 15.826z" horiz-adv-x="667" />
<glyph unicode="&#xe60a;" d="M560.562 469.433c-11.74 13.207-31.942 14.425-45.148 2.686l-504.653-447.452c-13.207-11.709-14.426-31.908-2.717-45.116 6.306-7.122 15.112-10.774 23.947-10.774 7.525 0 15.112 2.654 21.201 8.054l128.536 113.967c16.613-11.432 34.994-19.839 54.305-24.856-35.096-17.827-59.386-53.858-59.386-95.947h215.936c0 42.868-25.164 79.558-61.382 97.039 27.475 7.245 52.921 19.983 73.748 38.748 25.944 23.356 56.856 65.757 56.856 135.165v65.162c0 9.406-3.962 17.883-10.293 23.899l106.332 94.279c13.21 11.738 14.424 31.908 2.717 45.147zM395.767 240.946c0-78.993-58.825-114.961-113.495-114.961-17.607 0-34.329 3.608-49.142 10.393l27.868 24.708c7.366-2.295 15.178-3.566 23.305-3.566 44.678 0 80.992 36.344 80.992 80.99v15.050l30.474 27.021v-39.635zM365.295 396.933c0 44.649-36.313 80.992-80.992 80.992-44.649 0-80.992-36.344-80.992-80.992v-158.425c0-0.125 0-0.249 0-0.374l161.984 143.625v15.174zM175.398 213.345c-1.623 8.741-2.559 17.891-2.559 27.601v65.161c0 18.203-14.8 33.002-33.003 33.002-18.233 0-33.002-14.798-33.002-33.002v-65.161c0-28.599 5.558-53.513 14.549-75.466l54.015 47.865z" horiz-adv-x="569" />
<glyph unicode="&#xe60b;" d="M429.207 339.972c-18.298 0-33.123-14.826-33.123-33.091v-65.362c0-79.211-58.991-115.298-113.817-115.298-29.337 0-56.309 9.935-75.93 27.98-22.115 20.409-33.848 50.601-33.848 87.32v65.363c0 18.265-14.827 33.091-33.091 33.091-18.265 0-33.091-14.826-33.091-33.091v-65.363c0-97.917 59.747-157.382 129.589-175.52-35.204-17.855-59.588-54.007-59.588-96.216h216.559c0 42.996-25.204 79.81-61.514 97.286 27.539 7.32 53.060 20.063 73.943 38.895 26.025 23.438 57.004 65.963 57.004 135.553v65.363c0 18.265-14.795 33.091-33.092 33.091zM284.286 157.86c-44.794 0-81.23 36.466-81.23 81.26v158.832c0 44.795 36.435 81.23 81.23 81.23 44.796 0 81.262-36.435 81.262-81.23v-158.832c0.002-44.796-36.464-81.26-81.262-81.26z" horiz-adv-x="569" />
<glyph unicode="&#xe60c;" d="M256.178 480c-141.228 0-256.178-114.919-256.178-256.239 0-141.195 114.95-256.113 256.178-256.113 141.257 0 256.207 114.919 256.207 256.113 0 141.32-114.95 256.239-256.207 256.239zM256.178 7.428c-119.272 0-216.335 97.063-216.335 216.333 0 119.398 97.063 216.429 216.335 216.429 119.3 0 216.428-97.031 216.428-216.429 0-119.27-97.127-216.333-216.428-216.333zM256.272 427.481c-112.377 0-203.754-91.375-203.754-203.657 0-112.281 91.375-203.657 203.754-203.657 112.219 0 203.594 91.377 203.594 203.658-0.002 112.283-91.375 203.658-203.594 203.658zM256.272 63.661c-88.358 0-160.226 71.902-160.226 160.162 0 88.262 71.868 160.162 160.226 160.162 88.262 0 160.098-71.901 160.098-160.162 0-88.26-71.837-160.162-160.098-160.162zM141.925 281.394l-0.477-0.699v-117.207l0.477-0.699c7.879-11.53 18.237-22.271 30.85-31.899l4.481-3.401v189.171l-4.481-3.368c-12.55-9.595-22.907-20.272-30.85-31.899zM207.819 332.865l-1.81-0.667v-220.18l1.81-0.699c9.341-3.527 19.444-5.97 30.883-7.466l3.112-0.381v237.207l-3.082-0.381c-11.119-1.398-21.508-3.876-30.913-7.435zM273.683 340.299l-3.082 0.381v-237.208l3.082 0.381c11.151 1.397 21.538 3.906 30.882 7.432l1.842 0.7v220.244l-1.842 0.667c-9.406 3.526-19.762 6.005-30.882 7.403zM370.49 281.394c-7.846 11.501-18.236 22.24-30.849 31.899l-4.447 3.43v-189.234l4.447 3.401c12.675 9.69 23.066 20.431 30.849 31.93l0.445 0.699v117.176l-0.445 0.7z" horiz-adv-x="513" />
<glyph unicode="&#xe60d;" d="M476.183 480.067h-410.238c-36.514 0-66.266-30.38-66.266-67.728v-376.179c0-37.33 29.752-67.712 66.266-67.712h410.24c36.545 0 66.298 30.383 66.298 67.712v376.179c-0.001 37.347-29.754 67.728-66.299 67.728zM473.067 39.401h-403.947v369.731h403.917l0.029-369.731zM284.871 255.938l45.886 48.433-38.652 38.654 158.197 42.52-42.49-158.195-37.678 37.647-45.917-48.433zM257.382 192.281l-45.883-48.433 38.65-38.652-158.194-42.522 42.489 158.194 37.678-37.645 45.917 48.435z" horiz-adv-x="545" />
<glyph unicode="&#xe60e;" d="M476.613 479.59h-410.332c-36.523 0-66.281-30.388-66.281-67.744v-376.262c0-37.324 29.759-67.71 66.281-67.71h410.33c36.553 0 66.312 30.388 66.312 67.711v376.262c0.001 37.356-29.758 67.744-66.311 67.744zM473.497 38.824h-404.039v369.798h404.009l0.031-369.798zM457.769 353.35l-45.897-48.445 38.663-38.661-158.232-42.515 42.5 158.232 37.687-37.67 45.926 48.445zM85.313 94.111l45.897 48.442-38.661 38.663 158.232 42.514-42.499-158.23-37.686 37.671-45.928-48.445z" horiz-adv-x="545" />
</font></defs></svg>
\ No newline at end of file
<html> <html>
<head> <head>
<title>WebRTC, meet the Jitsi Videobridge</title> <title>WebRTC, meet the Jitsi Videobridge</title>
<script src="/jitsi/config"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="libs/strophe/strophe.jingle.adapter.js?v=1"></script><!-- strophe.jingle bundles --> <script src="libs/strophe/strophe.jingle.adapter.js?v=1"></script><!-- strophe.jingle bundles -->
<script src="libs/strophe/strophe.jingle.bundle.js?v=8"></script> <script src="libs/strophe/strophe.jingle.bundle.js?v=8"></script>
...@@ -16,14 +17,17 @@ ...@@ -16,14 +17,17 @@
<script src="muc.js?v=9"></script><!-- simple MUC library --> <script src="muc.js?v=9"></script><!-- simple MUC library -->
<script src="estos_log.js?v=2"></script><!-- simple stanza logger --> <script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
<script src="desktopsharing.js?v=1"></script><!-- desktop sharing --> <script src="desktopsharing.js?v=1"></script><!-- desktop sharing -->
<script src="app.js?v=23"></script><!-- application logic --> <script src="app.js?v=24"></script><!-- application logic -->
<script src="chat.js?v=3"></script><!-- chat logic --> <script src="chat.js?v=4"></script><!-- chat logic -->
<script src="util.js?v=2"></script><!-- utility functions --> <script src="util.js?v=3"></script><!-- utility functions -->
<script src="etherpad.js?v=5"></script><!-- etherpad plugin --> <script src="etherpad.js?v=7"></script><!-- etherpad plugin -->
<script src="prezi.js?v=2"></script><!-- prezi plugin -->
<script src="smileys.js?v=1"></script><!-- smiley images --> <script src="smileys.js?v=1"></script><!-- smiley images -->
<script src="replacement.js?v=5"></script><!-- link and smiley replacement --> <script src="replacement.js?v=5"></script><!-- link and smiley replacement -->
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet"> <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=19"/> <link rel="stylesheet" href="css/font.css"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=20"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=1" id="videolayout_default"/>
<link rel="stylesheet" href="css/jquery-impromptu.css?v=4"> <link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
<link rel="stylesheet" href="css/modaldialog.css?v=3"> <link rel="stylesheet" href="css/modaldialog.css?v=3">
<!-- <!--
...@@ -39,32 +43,32 @@ ...@@ -39,32 +43,32 @@
<a href="http://jitsi.org" target="_blank"><div id="leftlogo"></div></a> <a href="http://jitsi.org" target="_blank"><div id="leftlogo"></div></a>
<a href="http://www.estos.com/" target="_blank"><div id="rightlogo"></div></a> <a href="http://www.estos.com/" target="_blank"><div id="rightlogo"></div></a>
<span id="toolbar"> <span id="toolbar">
<a class="button" onclick='buttonClick("#mute", "fa fa-microphone fa-lg fa fa-microphone-slash fa-lg");toggleAudio();'> <a class="button" onclick='buttonClick("#mute", "icon-microphone icon-mic-disabled");toggleAudio();'>
<i id="mute" title="Mute / unmute" class="fa fa-microphone fa-lg"></i></a> <i id="mute" title="Mute / unmute" class="icon-microphone"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='buttonClick("#video");toggleVideo();'> <a class="button" onclick='buttonClick("#video", "icon-camera icon-camera-disabled");toggleVideo();'>
<i id="video" title="Start / stop camera" class="fa fa-video-camera fa-lg"></i></a> <i id="video" title="Start / stop camera" class="icon-camera"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick="openLockDialog();"><i id="lockIcon" title="Lock/unlock room" class="fa fa-unlock fa-lg"></i></a> <a class="button" onclick="openLockDialog();"><i id="lockIcon" title="Lock/unlock room" class="icon-security"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick="openLinkDialog();"><i title="Invite others" class="fa fa-link fa-lg"></i></a> <a class="button" onclick="openLinkDialog();"><i title="Invite others" class="icon-link"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<span class="toolbar_span"> <span class="toolbar_span">
<a class="button" onclick='Chat.toggleChat();'><i id="chat" title="Open chat" class="fa fa-comments-o fa-lg"></i></a> <a class="button" onclick='Chat.toggleChat();'><i id="chatButton" title="Open chat" class="icon-chat"></i></a>
<span id="unreadMessages"></span> <span id="unreadMessages"></span>
</span> </span>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='openPreziDialog();'><i title="Share prezi" class="fa fa-picture-o fa-lg"></i></a> <a class="button" onclick='Prezi.openPreziDialog();'><i title="Share prezi" class="icon-prezi"></i></a>
<span id="etherpadButton"> <span id="etherpadButton">
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='Etherpad.toggleEtherpad(0);'><i title="Open shared document" class="fa fa-file-text fa-lg"></i></a> <a class="button" onclick='Etherpad.toggleEtherpad(0);'><i title="Open shared document" class="icon-share-doc"></i></a>
</span> </span>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<span id="desktopsharing" style="display: none"> <span id="desktopsharing" style="display: none">
<a class="button" onclick="toggleScreenSharing();"><i title="Share screen" class="fa fa-desktop fa-lg"></i></a> <a class="button" onclick="toggleScreenSharing();"><i title="Share screen" class="icon-share-desktop"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
</span> </span>
<a class="button" onclick='toggleFullScreen();'><i title="Enter / Exit Full Screen" class="fa fa-arrows-alt fa-lg"></i></a> <a class="button" onclick='buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");toggleFullScreen();'><i id="fullScreen" title="Enter / Exit Full Screen" class="icon-full-screen"></i></a>
</span> </span>
</div> </div>
<div id="settings"> <div id="settings">
...@@ -76,20 +80,20 @@ ...@@ -76,20 +80,20 @@
<input id="connect" type="submit" value="Connect" /> <input id="connect" type="submit" value="Connect" />
</form> </form>
</div> </div>
<div id="reloadPresentation"><a onclick='Prezi.reloadPresentation();'><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
<div id="videospace"> <div id="videospace" onmousemove="showToolbar();">
<div class="fade_line"></div>
<div id="largeVideoContainer" class="videocontainer"> <div id="largeVideoContainer" class="videocontainer">
<div id="reloadPresentation"><a onclick='reloadPresentation();'><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
<div id="presentation"></div> <div id="presentation"></div>
<div id="etherpad"></div> <div id="etherpad"></div>
<a href="http://jitsi.org" target="_new"><div id="watermark"></div></a>
<video id="largeVideo" autoplay oncontextmenu="return false;"></video> <video id="largeVideo" autoplay oncontextmenu="return false;"></video>
</div> </div>
<div class="fade_line"></div>
<div id="remoteVideos"> <div id="remoteVideos">
<span id="localVideoContainer" class="videocontainer"> <span id="localVideoContainer" class="videocontainer">
<span id="localNick"></span> <span id="localNick" class="nick"></span>
<!--<video id="localVideo" autoplay oncontextmenu="return false;" muted></video> - is now per stream generated --> <span id="localVideoWrapper">
<!--<video id="localVideo" autoplay oncontextmenu="return false;" muted></video> - is now per stream generated -->
</span>
<audio id="localAudio" autoplay oncontextmenu="return false;" muted></audio> <audio id="localAudio" autoplay oncontextmenu="return false;" muted></audio>
<span class="focusindicator"></span> <span class="focusindicator"></span>
</span> </span>
......
...@@ -75,11 +75,16 @@ ColibriFocus.prototype.makeConference = function (peers) { ...@@ -75,11 +75,16 @@ ColibriFocus.prototype.makeConference = function (peers) {
self.channels.push([]); self.channels.push([]);
}); });
if(connection.jingle.localAudio) { this.peerconnection
this.peerconnection.addStream(connection.jingle.localAudio); = new TraceablePeerConnection(
this.connection.jingle.ice_config,
this.connection.jingle.pc_constraints );
if(this.connection.jingle.localAudio) {
this.peerconnection.addStream(this.connection.jingle.localAudio);
} }
if(connection.jingle.localVideo) { if(this.connection.jingle.localVideo) {
this.peerconnection.addStream(connection.jingle.localVideo); this.peerconnection.addStream(this.connection.jingle.localVideo);
} }
this.peerconnection.oniceconnectionstatechange = function (event) { this.peerconnection.oniceconnectionstatechange = function (event) {
console.warn('ice connection state changed to', self.peerconnection.iceConnectionState); console.warn('ice connection state changed to', self.peerconnection.iceConnectionState);
......
...@@ -328,6 +328,12 @@ TraceablePeerConnection.prototype.modifySources = function(successCallback) { ...@@ -328,6 +328,12 @@ TraceablePeerConnection.prototype.modifySources = function(successCallback) {
sdp.raw = sdp.session + sdp.media.join(''); sdp.raw = sdp.session + sdp.media.join('');
this.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}), this.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}),
function() { function() {
if(self.signalingState == 'closed') {
console.error("createAnswer attempt on closed state");
return;
}
self.createAnswer( self.createAnswer(
function(modifiedAnswer) { function(modifiedAnswer) {
// change video direction, see https://github.com/jitsi/jitmeet/issues/41 // change video direction, see https://github.com/jitsi/jitmeet/issues/41
...@@ -531,7 +537,10 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res ...@@ -531,7 +537,10 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res
} }
} }
if (resolution && !constraints.video) { // Check if we are running on Android device
var isAndroid = navigator.userAgent.indexOf('Android') != -1;
if (resolution && !constraints.video || isAndroid) {
constraints.video = {mandatory: {}};// same behaviour as true constraints.video = {mandatory: {}};// same behaviour as true
} }
// see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
...@@ -574,7 +583,7 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res ...@@ -574,7 +583,7 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res
constraints.video.mandatory.minHeight = 240; constraints.video.mandatory.minHeight = 240;
break; break;
default: default:
if (navigator.userAgent.indexOf('Android') != -1) { if (isAndroid) {
constraints.video.mandatory.minWidth = 320; constraints.video.mandatory.minWidth = 320;
constraints.video.mandatory.minHeight = 240; constraints.video.mandatory.minHeight = 240;
constraints.video.mandatory.maxFrameRate = 15; constraints.video.mandatory.maxFrameRate = 15;
......
...@@ -53,6 +53,12 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) { ...@@ -53,6 +53,12 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
this.hadstuncandidate = false; this.hadstuncandidate = false;
this.hadturncandidate = false; this.hadturncandidate = false;
this.lasticecandidate = false; this.lasticecandidate = false;
this.peerconnection
= new TraceablePeerConnection(
this.connection.jingle.ice_config,
this.connection.jingle.pc_constraints );
this.peerconnection.onicecandidate = function (event) { this.peerconnection.onicecandidate = function (event) {
self.sendIceCandidate(event.candidate); self.sendIceCandidate(event.candidate);
}; };
......
...@@ -8,10 +8,6 @@ function SessionBase(connection, sid){ ...@@ -8,10 +8,6 @@ function SessionBase(connection, sid){
this.connection = connection; this.connection = connection;
this.sid = sid; this.sid = sid;
this.peerconnection
= new TraceablePeerConnection(
connection.jingle.ice_config,
connection.jingle.pc_constraints);
} }
...@@ -48,26 +44,27 @@ SessionBase.prototype.switchStreams = function (new_stream, oldStream, success_c ...@@ -48,26 +44,27 @@ SessionBase.prototype.switchStreams = function (new_stream, oldStream, success_c
var self = this; var self = this;
// Remember SDP to figure out added/removed SSRCs
var oldSdp = null;
if(self.peerconnection.localDescription) {
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
}
// Stop the stream to trigger onended event for old stream // Stop the stream to trigger onended event for old stream
oldStream.stop(); oldStream.stop();
self.peerconnection.removeStream(oldStream); // Remember SDP to figure out added/removed SSRCs
var oldSdp = null;
if(self.peerconnection) {
if(self.peerconnection.localDescription) {
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
}
self.peerconnection.removeStream(oldStream);
self.peerconnection.addStream(new_stream);
}
self.connection.jingle.localVideo = new_stream; self.connection.jingle.localVideo = new_stream;
self.peerconnection.addStream(self.connection.jingle.localVideo);
self.connection.jingle.localStreams = []; self.connection.jingle.localStreams = [];
self.connection.jingle.localStreams.push(self.connection.jingle.localAudio); self.connection.jingle.localStreams.push(self.connection.jingle.localAudio);
self.connection.jingle.localStreams.push(self.connection.jingle.localVideo); self.connection.jingle.localStreams.push(self.connection.jingle.localVideo);
// Conference is not active // Conference is not active
if(!oldSdp) { if(!oldSdp || !self.peerconnection) {
success_callback(); success_callback();
return; return;
} }
......
...@@ -98,17 +98,15 @@ Strophe.addConnectionPlugin('emuc', { ...@@ -98,17 +98,15 @@ Strophe.addConnectionPlugin('emuc', {
$(document).trigger('joined.muc', [from, member]); $(document).trigger('joined.muc', [from, member]);
this.list_members.push(from); this.list_members.push(from);
} }
else
$(document).trigger('presence.muc', [from, member, pres]);
} else if (this.members[from] === undefined) { } else if (this.members[from] === undefined) {
// new participant // new participant
this.members[from] = member; this.members[from] = member;
this.list_members.push(from); this.list_members.push(from);
$(document).trigger('entered.muc', [from, member, pres]); $(document).trigger('entered.muc', [from, member, pres]);
} else {
console.log('presence change from', from);
$(document).trigger('presence.muc', [from, member, pres]);
} }
// Always trigger presence to update bindings
console.log('presence change from', from);
$(document).trigger('presence.muc', [from, member, pres]);
return true; return true;
}, },
onPresenceUnavailable: function (pres) { onPresenceUnavailable: function (pres) {
......
var Prezi = (function (my) {
var preziPlayer = null;
/**
* Reloads the current presentation.
*/
my.reloadPresentation = function() {
var iframe = document.getElementById(preziPlayer.options.preziId);
iframe.src = iframe.src;
};
/**
* Shows/hides a presentation.
*/
my.setPresentationVisible = function (visible) {
if (visible) {
// Trigger the video.selected event to indicate a change in the
// large video.
$(document).trigger("video.selected", [true]);
$('#largeVideo').fadeOut(300, function () {
setLargeVideoVisible(false);
$('#presentation>iframe').fadeIn(300, function() {
$('#presentation>iframe').css({opacity:'1'});
dockToolbar(true);
});
});
}
else {
if ($('#presentation>iframe').css('opacity') == '1') {
$('#presentation>iframe').fadeOut(300, function () {
$('#presentation>iframe').css({opacity:'0'});
$('#reloadPresentation').css({display:'none'});
$('#largeVideo').fadeIn(300, function() {
setLargeVideoVisible(true);
dockToolbar(false);
});
});
}
}
};
/**
* Returns <tt>true</tt> if the presentation is visible, <tt>false</tt> -
* otherwise.
*/
my.isPresentationVisible = function () {
return ($('#presentation>iframe') != null
&& $('#presentation>iframe').css('opacity') == 1);
};
/**
* Opens the Prezi dialog, from which the user could choose a presentation
* to load.
*/
my.openPreziDialog = function() {
var myprezi = connection.emuc.getPrezi(connection.emuc.myroomjid);
if (myprezi) {
$.prompt("Are you sure you would like to remove your Prezi?",
{
title: "Remove Prezi",
buttons: { "Remove": true, "Cancel": false},
defaultButton: 1,
submit: function(e,v,m,f){
if(v)
{
connection.emuc.removePreziFromPresence();
connection.emuc.sendPresence();
}
}
});
}
else if (preziPlayer != null) {
$.prompt("Another participant is already sharing a Prezi." +
"This conference allows only one Prezi at a time.",
{
title: "Share a Prezi",
buttons: { "Ok": true},
defaultButton: 0,
submit: function(e,v,m,f){
$.prompt.close();
}
});
}
else {
var openPreziState = {
state0: {
html: '<h2>Share a Prezi</h2>' +
'<input id="preziUrl" type="text" ' +
'placeholder="e.g. ' +
'http://prezi.com/wz7vhjycl7e6/my-prezi" autofocus>',
persistent: false,
buttons: { "Share": true , "Cancel": false},
defaultButton: 1,
submit: function(e,v,m,f){
e.preventDefault();
if(v)
{
var preziUrl = document.getElementById('preziUrl');
if (preziUrl.value)
{
var urlValue
= encodeURI(Util.escapeHtml(preziUrl.value));
if (urlValue.indexOf('http://prezi.com/') != 0
&& urlValue.indexOf('https://prezi.com/') != 0)
{
$.prompt.goToState('state1');
return false;
}
else {
var presIdTmp = urlValue.substring(
urlValue.indexOf("prezi.com/") + 10);
if (!isAlphanumeric(presIdTmp)
|| presIdTmp.indexOf('/') < 2) {
$.prompt.goToState('state1');
return false;
}
else {
connection.emuc
.addPreziToPresence(urlValue, 0);
connection.emuc.sendPresence();
$.prompt.close();
}
}
}
}
else
$.prompt.close();
}
},
state1: {
html: '<h2>Share a Prezi</h2>' +
'Please provide a correct prezi link.',
persistent: false,
buttons: { "Back": true, "Cancel": false },
defaultButton: 1,
submit:function(e,v,m,f) {
e.preventDefault();
if(v==0)
$.prompt.close();
else
$.prompt.goToState('state0');
}
}
};
var myPrompt = jQuery.prompt(openPreziState);
myPrompt.on('impromptu:loaded', function(e) {
document.getElementById('preziUrl').focus();
});
myPrompt.on('impromptu:statechanged', function(e) {
document.getElementById('preziUrl').focus();
});
}
};
/**
* A new presentation has been added.
*
* @param event the event indicating the add of a presentation
* @param jid the jid from which the presentation was added
* @param presUrl url of the presentation
* @param currentSlide the current slide to which we should move
*/
var presentationAdded = function(event, jid, presUrl, currentSlide) {
console.log("presentation added", presUrl);
var presId = getPresentationId(presUrl);
var elementId = 'participant_'
+ Strophe.getResourceFromJid(jid)
+ '_' + presId;
addRemoteVideoContainer(elementId);
resizeThumbnails();
var controlsEnabled = false;
if (jid === connection.emuc.myroomjid)
controlsEnabled = true;
Prezi.setPresentationVisible(true);
$('#largeVideoContainer').hover(
function (event) {
if (Prezi.isPresentationVisible()) {
var reloadButtonRight = window.innerWidth
- $('#presentation>iframe').offset().left
- $('#presentation>iframe').width();
$('#reloadPresentation').css({ right: reloadButtonRight,
display:'inline-block'});
}
},
function (event) {
if (!Prezi.isPresentationVisible())
$('#reloadPresentation').css({display:'none'});
else {
var e = event.toElement || event.relatedTarget;
if (e && e.id != 'reloadPresentation' && e.id != 'header')
$('#reloadPresentation').css({display:'none'});
}
});
preziPlayer = new PreziPlayer(
'presentation',
{preziId: presId,
width: getPresentationWidth(),
height: getPresentationHeihgt(),
controls: controlsEnabled,
debug: true
});
$('#presentation>iframe').attr('id', preziPlayer.options.preziId);
preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
console.log("prezi status", event.value);
if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
if (jid != connection.emuc.myroomjid)
preziPlayer.flyToStep(currentSlide);
}
});
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
console.log("event value", event.value);
connection.emuc.addCurrentSlideToPresence(event.value);
connection.emuc.sendPresence();
});
$("#" + elementId).css( 'background-image',
'url(../images/avatarprezi.png)');
$("#" + elementId).click(
function () {
Prezi.setPresentationVisible(true);
}
);
};
/**
* A presentation has been removed.
*
* @param event the event indicating the remove of a presentation
* @param jid the jid for which the presentation was removed
* @param the url of the presentation
*/
var presentationRemoved = function (event, jid, presUrl) {
console.log('presentation removed', presUrl);
var presId = getPresentationId(presUrl);
Prezi.setPresentationVisible(false);
$('#participant_'
+ Strophe.getResourceFromJid(jid)
+ '_' + presId).remove();
$('#presentation>iframe').remove();
if (preziPlayer != null) {
preziPlayer.destroy();
preziPlayer = null;
}
};
/**
* Indicates if the given string is an alphanumeric string.
* Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the
* purpose of checking URIs.
*/
function isAlphanumeric(unsafeText) {
var regex = /^[a-z0-9-_\/&\?=;]+$/i;
return regex.test(unsafeText);
}
/**
* Returns the presentation id from the given url.
*/
function getPresentationId (presUrl) {
var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
return presIdTmp.substring(0, presIdTmp.indexOf('/'));
}
/**
* Returns the presentation width.
*/
function getPresentationWidth() {
var availableWidth = Util.getAvailableVideoWidth();
var availableHeight = getPresentationHeihgt();
var aspectRatio = 16.0 / 9.0;
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
}
return availableWidth;
}
/**
* Returns the presentation height.
*/
function getPresentationHeihgt() {
var remoteVideos = $('#remoteVideos');
return window.innerHeight - remoteVideos.outerHeight();
}
/**
* Resizes the presentation iframe.
*/
function resize() {
if ($('#presentation>iframe')) {
$('#presentation>iframe').width(getPresentationWidth());
$('#presentation>iframe').height(getPresentationHeihgt());
}
}
/**
* Presentation has been removed.
*/
$(document).bind('presentationremoved.muc', presentationRemoved);
/**
* Presentation has been added.
*/
$(document).bind('presentationadded.muc', presentationAdded);
/*
* Indicates presentation slide change.
*/
$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
if (preziPlayer) {
preziPlayer.flyToStep(current);
}
});
/**
* On video selected event.
*/
$(document).bind('video.selected', function (event, isPresentation) {
if (!isPresentation && $('#presentation>iframe'))
Prezi.setPresentationVisible(false);
});
$(window).resize(function () {
resize();
});
return my;
}(Prezi || {}));
\ No newline at end of file
...@@ -47,14 +47,14 @@ var Util = (function (my) { ...@@ -47,14 +47,14 @@ var Util = (function (my) {
}; };
/** /**
* Indicates if the given string is an alphanumeric string. * Returns the available video width.
* Note that some special characters are also allowed (-, _ , /) for the
* purpose of checking URIs. (FIXME: This should maybe moved to another not
* so generic method in the future.)
*/ */
my.isAlphanumeric = function(unsafeText) { my.getAvailableVideoWidth = function() {
var regex = /^[a-z0-9-_\/]+$/i; var chatspaceWidth = $('#chatspace').is(":visible")
return regex.test(unsafeText); ? $('#chatspace').width()
: 0;
return window.innerWidth - chatspaceWidth;
}; };
return my; return my;
......
...@@ -2,84 +2,85 @@ html, body{ ...@@ -2,84 +2,85 @@ html, body{
margin:0px; margin:0px;
height:100%; height:100%;
color: #424242; color: #424242;
font-family:'YanoneKaffeesatzLight',Verdana,Tahoma,Arial; font-family:'Helvetica Neue', Helvetica, sans-serif;
font-weight: 400; font-weight: 400;
background: #e9e9e9; background: #363636;
overflow: hidden;
} }
#unreadMessages {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#videospace { #videospace {
display: block; display: block;
position: absolute; position: absolute;
top: 39px; top: 0px;
left: 0px; left: 0px;
right: 0px; right: 0px;
float: left;
} }
#largeVideo { #largeVideo {
display:block; overflow: hidden;
position:relative; text-align: center;
width:1280px;
height:720px;
margin-left:auto;
margin-right:auto;
z-index: 0;
} }
#pdfViewer { #pdfViewer {
display:block; display:block;
position:relative; position:absolute;
width:auto; width:auto;
height:auto; height:auto;
z-index: 1;
} }
#remoteVideos { #remoteVideos {
display:block; display:block;
position:relative; position:absolute;
text-align:center; text-align:right;
height:170px; height:196px;
padding: 18px;
bottom: 0;
left: 0;
right: 0;
width:auto; width:auto;
overflow: hidden; overflow: hidden;
border:1px solid transparent; border:1px solid transparent;
font-size:0;
z-index: 2; z-index: 2;
} }
#remoteVideos video { #remoteVideos video {
position:relative; display: inline-block;
top:18px;
height:160px;
width:auto;
z-index:0;
border:1px solid #FFFFFF;
background-repeat:no-repeat;
background-image:url(../images/avatar1.png); background-image:url(../images/avatar1.png);
background-size: contain; background-size: contain;
border-radius:8px;
border: 2px solid #212425;
} }
#remoteVideos video:hover { #remoteVideos video:hover {
width: 100%;
height: 100%;
content:"";
cursor: pointer; cursor: pointer;
cursor: hand; cursor: hand;
transform:scale(1.08, 1.08); transform:scale(1.08, 1.08);
-webkit-transform:scale(1.08, 1.08); -webkit-transform:scale(1.08, 1.08);
transition-duration: 0.5s; transition-duration: 0.5s;
-webkit-transition-duration: 0.5s; -webkit-transition-duration: 0.5s;
background-color: #FFFFFF;
-webkit-animation-name: greyPulse; -webkit-animation-name: greyPulse;
-webkit-animation-duration: 2s; -webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1; -webkit-animation-iteration-count: 1;
-webkit-box-shadow: 0 0 18px #515151; -webkit-box-shadow: 0 0 18px #388396;
border:1px solid #FFFFFF; border: 2px solid #388396;
z-index: 10; z-index: 3;
} }
#chatspace { #chatspace {
display:block; display:block;
position:absolute; position:absolute;
float: right; float: right;
top: 40px; top: 0px;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
width:450px; width:450px;
...@@ -166,11 +167,28 @@ html, body{ ...@@ -166,11 +167,28 @@ html, body{
} }
#header{ #header{
display:block; display:none;
position:absolute;
height:39px; height:39px;
z-index: 1;
text-align:center; text-align:center;
background-color:#087dba; top:0;
left:0;
right:0;
z-index:20;
}
#toolbar {
display:inline-block;
position:relative;
margin-left:auto;
margin-right:auto;
height:39px;
width:auto;
overflow: hidden;
background: linear-gradient(to bottom, rgba(103,103,103,.65) , rgba(0,0,0,.65));
-webkit-box-shadow: 0 0 2px #000000, 0 0 10px #000000;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
} }
#left { #left {
...@@ -196,15 +214,6 @@ html, body{ ...@@ -196,15 +214,6 @@ html, body{
z-index:1; z-index:1;
} }
#toolbar {
display:block;
position:relative;
height:39px;
width:auto;
overflow: hidden;
z-index:0;
visibility: hidden;
}
.button { .button {
display: inline-block; display: inline-block;
......
...@@ -6,8 +6,6 @@ div#ofmeet { ...@@ -6,8 +6,6 @@ div#ofmeet {
background-color: #F9F9F9; background-color: #F9F9F9;
-moz-border-radius: 5px; -moz-border-radius: 5px;
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
display:block; display:block;
position:relative; position:relative;
top: -100px; top: -100px;
...@@ -17,7 +15,8 @@ div#ofmeet { ...@@ -17,7 +15,8 @@ div#ofmeet {
font-size:14px; font-size:14px;
padding:5px; padding:5px;
overflow:scroll; overflow:scroll;
visibility:hidden; visibility:hidden;
overflow: hidden;
} }
...@@ -26,8 +25,8 @@ div#ofmeet div.ofmeet-clear { ...@@ -26,8 +25,8 @@ div#ofmeet div.ofmeet-clear {
} }
div#ofmeet form { div#ofmeet form {
margin: 0; margin: 10px;
padding: 0; padding: 10px;
} }
div#ofmeet div#ofmeet-login { div#ofmeet div#ofmeet-login {
......
<html> <html>
<head> <head>
<title>Openfire Meet</title> <title>Openfire Meet</title>
<script src="/jitsi/config"></script>
<script src="js/jquery.min.js"></script> <script src="js/jquery.min.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/jquery-impromptu.js"></script> <script src="js/jquery-impromptu.js"></script>
<script src="js/jquery.autosize.js"></script> <script src="js/jquery.autosize.js"></script>
<script src="js/md5.js"></script> <script src="js/md5.js"></script>
<script src="js/base64.js"></script> <script src="js/base64.js"></script>
<script src="js/strophe.js"></script> <script src="js/strophe.js"></script>
<script src="js/muc.js"></script> <script src="js/muc.js"></script>
<script src="js/config.js"></script>
<script src="js/webrtc.sdp.js"></script> <script src="js/webrtc.sdp.js"></script>
<script src="js/strophe-openfire.js"></script> <script src="js/strophe-openfire.js"></script>
<script src="js/main.js"></script> <script src="js/main.js"></script>
...@@ -26,7 +27,7 @@ ...@@ -26,7 +27,7 @@
<div id="header"> <div id="header">
<a href="http://community.igniterealtime.org/community/support/jitsi_videobridge_plugin" target="_blank"><div id="leftlogo"><nobr>OfMeet</nobr></div></a> <a href="http://community.igniterealtime.org/community/support/jitsi_videobridge_plugin" target="_blank"><div id="leftlogo"><nobr>OfMeet</nobr></div></a>
<a href="http://jitsi.org" target="_blank"><div id="rightlogo"></div></a> <a href="http://jitsi.org" target="_blank"><div id="rightlogo"></div></a>
<div id="toolbar"> <span id="toolbar">
<a class="button" onclick='toggleScreenShare();'><i id="screen" title="share/unshare desktop" class="fa fa-desktop fa-lg"></i></a> <a class="button" onclick='toggleScreenShare();'><i id="screen" title="share/unshare desktop" class="fa fa-desktop fa-lg"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='buttonClick("#mute", "fa fa-microphone fa-lg fa fa-microphone-slash fa-lg");toggleAudio();'><i id="mute" title="Mute / unmute" class="fa fa-microphone fa-lg"></i></a> <a class="button" onclick='buttonClick("#mute", "fa fa-microphone fa-lg fa fa-microphone-slash fa-lg");toggleAudio();'><i id="mute" title="Mute / unmute" class="fa fa-microphone fa-lg"></i></a>
...@@ -38,25 +39,21 @@ ...@@ -38,25 +39,21 @@
<a class="button" onclick="openLinkDialog();"><i title="Invite others" class="fa fa-link fa-lg"></i></a> <a class="button" onclick="openLinkDialog();"><i title="Invite others" class="fa fa-link fa-lg"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<span class="toolbar_span"> <span class="toolbar_span">
<a class="button" onclick='openChat();'><i id="chat" title="Open chat" class="fa fa-comments-o fa-lg"></i></a> <a class="button" onclick='openChat();'><i id="chatButton" title="Open chat" class="fa fa-comments-o fa-lg"></i></a>
<span id="unreadMessages"></span> <span id="unreadMessages"></span>
</span> </span>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='openPDFDialog();'><i id="pdf" title="Share PDF" class="fa fa-file fa-lg"></i></a> <a class="button" onclick='openPDFDialog();'><i id="pdf" title="Share PDF" class="fa fa-file fa-lg"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='goFullScreen();'><i id="fullscreen" title="Full Screen" class="fa fa-arrows-alt fa-lg"></i></a> <a class="button" onclick='toggleFullScreen();'><i id="fullscreen" title="Full Screen" class="fa fa-arrows-alt fa-lg"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='inviteParticipant();'><i id="invite" title="Invite Participant" class="fa fa-phone fa-lg"></i></a> <a class="button" onclick='inviteParticipant();'><i id="invite" title="Invite Participant" class="fa fa-phone fa-lg"></i></a>
<!-- </span>
<div class="header_button_separator"></div>
<a class="button" onclick='goAltView();'><i id="altview" title="Alternate View" class="fa fa-stop fa-lg"></i></a>
-->
</div>
</div> </div>
<div id="videospace"> <div id="videospace" onmousemove="showToolbar();">
<div class="fade_line"></div> <div class="fade_line"></div>
<video id="largeVideo" onDblClick="goFullScreen();" autoplay oncontextmenu="return false;"></video> <video id="largeVideo" onDblClick="toggleFullScreen();" autoplay oncontextmenu="return false;"></video>
<iframe id="pdfViewer" style="display:none"></iframe> <iframe id="pdfViewer" style="display:none"></iframe>
<div class="fade_line"></div> <div class="fade_line"></div>
<div id="remoteVideos"> <div id="remoteVideos">
...@@ -72,8 +69,9 @@ ...@@ -72,8 +69,9 @@
</form> </form>
</div> </div>
<div id="ofmeet"><div id="ofmeet-messaging"><div id="ofmeet-log"></div></div></div> <div id="ofmeet"><div id="ofmeet-messaging"><div id="ofmeet-log"></div></div>
<textarea id="usermsg" class= "animated" placeholder='Enter text...' autofocus></textarea> </div>
<textarea id="usermsg" class= "animated" placeholder='Enter text...' autofocus ></textarea>
</div> </div>
<script> <script>
</script> </script>
......
var config = {
hosts: {
domain: window.location.hostname,
muc: 'conference.' + window.location.hostname, // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.' + window.location.hostname // FIXME: use XEP-0030
},
useIPv6: false, // ipv6 support. use at your own risk
useNicks: false,
useWebsockets: true,
resolution: "360",
bosh: window.location.protocol + "//" + window.location.host + '/http-bind/' // FIXME: use xep-0156 for that
};
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -11,11 +11,18 @@ var altView = false; ...@@ -11,11 +11,18 @@ var altView = false;
var sipUri = null; var sipUri = null;
var notificationInterval = false; var notificationInterval = false;
var unreadMessages = 0; var unreadMessages = 0;
var toolbarTimeout = null;
var getVideoSize = null;
var currentVideoWidth = null;
var currentVideoHeight = null;
$(document).ready(function () $(document).ready(function ()
{ {
var storedDisplayName = window.localStorage.displayname; var storedDisplayName = window.localStorage.displayname;
getVideoSize = getVideoSizeCover;
if (storedDisplayName) { if (storedDisplayName) {
nickname = unescape(storedDisplayName); nickname = unescape(storedDisplayName);
$("#localVideo").attr("title", nickname); $("#localVideo").attr("title", nickname);
...@@ -76,8 +83,21 @@ $(document).ready(function () ...@@ -76,8 +83,21 @@ $(document).ready(function ()
$(window).resize(function () { $(window).resize(function () {
resizeLarge(); resizeLarge();
positionLarge();
}); });
document.getElementById('largeVideo').addEventListener('loadedmetadata', function(e)
{
currentVideoWidth = this.videoWidth;
currentVideoHeight = this.videoHeight;
positionLarge(currentVideoWidth, currentVideoHeight);
});
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange',
function() {
resizeLarge();
positionLarge();
});
RTCPeerconnection = RTC.peerconnection; RTCPeerconnection = RTC.peerconnection;
...@@ -109,7 +129,7 @@ $(document).ready(function () ...@@ -109,7 +129,7 @@ $(document).ready(function ()
getConstraints(['audio', 'video'], config.resolution); getConstraints(['audio', 'video'], config.resolution);
$("#screen").removeClass("fa-border"); $("#screen").removeClass("fa-border");
} }
showToolbar(); //showToolbar();
updateRoomUrl(window.location.href); updateRoomUrl(window.location.href);
getUserMedia(); getUserMedia();
...@@ -451,80 +471,131 @@ function getConstraints(um, resolution, bandwidth, fps) ...@@ -451,80 +471,131 @@ function getConstraints(um, resolution, bandwidth, fps)
if (!window.RTC.rayo.constraints.video) window.RTC.rayo.constraints.video = {mandatory: {}};// same behaviour as tru; if (!window.RTC.rayo.constraints.video) window.RTC.rayo.constraints.video = {mandatory: {}};// same behaviour as tru;
window.RTC.rayo.constraints.video.mandatory.minFrameRate = fps; window.RTC.rayo.constraints.video.mandatory.minFrameRate = fps;
} }
} }
function goAltView() function positionLarge (videoWidth, videoHeight)
{ {
if (altView) var videoSpaceWidth = $('#videospace').width();
{ var videoSpaceHeight = window.innerHeight;
$("#altview").addClass("fa-stop");
$("#altview").removeClass("fa-th");
$('#largeVideo').css("display", "");
resizeLarge();
} else { var videoSize = getVideoSize(videoWidth, videoHeight, videoSpaceWidth, videoSpaceHeight);
$("#altview").removeClass("fa-stop");
$("#altview").addClass("fa-th"); var largeVideoWidth = videoSize[0];
arrangeVideos(); var largeVideoHeight = videoSize[1];
}
var videoPosition = getVideoPosition(largeVideoWidth, largeVideoHeight, videoSpaceWidth, videoSpaceHeight);
var horizontalIndent = videoPosition[0];
var verticalIndent = videoPosition[1];
altView = !altView; positionVideo( $('#largeVideo'),largeVideoWidth, largeVideoHeight, horizontalIndent, verticalIndent);
if (pdfFrame)
{
$('#pdfViewer').height(largeVideoHeight);
$('#pdfViewer').width(largeVideoWidth);
}
};
function positionVideo( video, width, height, horizontalIndent, verticalIndent)
{
video.width(width);
video.height(height);
video.css({ top: verticalIndent + 'px',
bottom: verticalIndent + 'px',
left: horizontalIndent + 'px',
right: horizontalIndent + 'px'});
} }
function arrangeVideos() function getVideoPosition ( 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];
};
function resizeLarge() function resizeLarge()
{ {
var availableHeight = window.innerHeight; var availableHeight = window.innerHeight;
var numvids = $('#remoteVideos>video:visible').length; var availableWidth = getAvailableVideoWidth();
if (numvids < 5) if (availableWidth < 0 || availableHeight < 0) return;
availableHeight -= 100; // min thumbnail height for up to 4 videos
else $('#videospace').width(availableWidth);
availableHeight -= 50; // min thumbnail height for more than 5 videos $('#videospace').height(availableHeight);
$('#largeVideoContainer').width(availableWidth);
availableHeight -= 79; // padding + link ontop $('#largeVideoContainer').height(availableHeight);
var availableWidth = window.innerWidth;
var aspectRatio = 16.0 / 9.0; resizeThumbnails();
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
}
if (availableWidth < 0 || availableHeight < 0) return;
$('#largeVideo').width(availableWidth);
$('#largeVideo').height(availableWidth/aspectRatio);
if (availableWidth <= 450) $('#chatspace').width(availableWidth);
resizeThumbnails() ;
} }
function getAvailableVideoWidth()
{
var chatspaceWidth = $('#chatspace').is(":visible") ? $('#chatspace').width() : 0;
return window.innerWidth - chatspaceWidth;
};
function resizeThumbnails() function resizeThumbnails()
{ {
// Calculate the available height, which is the inner window height minus 39px for the header // Calculate the available height, which is the inner window height minus
// minus 4px for the delimiter lines on the top and bottom of the large video, // 39px for the header minus 2px for the delimiter lines on the top and
// minus the 36px space inside the remoteVideos container used for highlighting shadow. // bottom of the large video, minus the 36px space inside the remoteVideos
var availableHeight = window.innerHeight - $('#largeVideo').height() - 79; // container used for highlighting shadow.
var numvids = $('#remoteVideos>video:visible').length; var availableHeight = 100;
// Remove the 1px borders arround videos.
var availableWinWidth = $('#remoteVideos').width() - numvids*2; var numvids = $('#remoteVideos>video:visible').length;
var availableWidth = availableWinWidth / numvids;
var aspectRatio = 16.0 / 9.0; // Remove the 1px borders arround videos and the chat width.
var maxHeight = Math.min(160, availableHeight); var availableWinWidth = $('#remoteVideos').width() - 2 * numvids - 50;
var availableHeight = Math.min(maxHeight, availableWidth / aspectRatio); var availableWidth = availableWinWidth / numvids;
if (availableHeight < availableWidth / aspectRatio) { var aspectRatio = 16.0 / 9.0;
availableWidth = Math.floor(availableHeight * aspectRatio); var maxHeight = Math.min(160, availableHeight);
} availableHeight = Math.min(maxHeight, availableWidth / aspectRatio);
// size videos so that while keeping AR and max height, we have a nice fit if (availableHeight < availableWidth / aspectRatio) {
$('#remoteVideos').height(availableHeight + 36); // add the 2*18px border used for highlighting shadow. availableWidth = Math.floor(availableHeight * aspectRatio);
$('#remoteVideos>video:visible').width(availableWidth); }
$('#remoteVideos>video:visible').height(availableHeight);
$('#remoteVideos>video:visible').css('position', 'relative'); // size videos so that while keeping AR and max height, we have a nice fit
$('#remoteVideos>video:visible').css({top: '18px', left: '0px', right: '0px', bottom: '0px'}); $('#remoteVideos').height(availableHeight);
$('#remoteVideos>video').width(availableWidth);
$('#remoteVideos>video').height(availableHeight);
}
function getVideoSizeCover(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];
} }
function urlParam(name) function urlParam(name)
...@@ -1229,13 +1300,17 @@ function openChat() { ...@@ -1229,13 +1300,17 @@ function openChat() {
var videospace = $('#videospace'); var videospace = $('#videospace');
var chatspaceWidth = chatspace.width(); var chatspaceWidth = chatspace.width();
$('#usermsg').focus(function(){
resetVisualNotification();
});
if (chatspace.css("opacity") == 1) { if (chatspace.css("opacity") == 1) {
chatspace.animate({opacity: 0}, "fast"); chatspace.animate({opacity: 0}, "fast");
chatspace.animate({width: 0}, "slow"); chatspace.animate({width: 0}, "slow");
videospace.animate({right: 0, width:"100%"}, "slow"); videospace.animate({right: 0, width:"100%"}, "slow");
} }
else { else {
chatspace.animate({width: "23%"}, "slow"); chatspace.animate({width: "20%"}, "slow");
chatspace.animate({opacity: 1}, "slow"); chatspace.animate({opacity: 1}, "slow");
videospace.animate({right:chatspaceWidth, width:"80%"}, "slow"); videospace.animate({right:chatspaceWidth, width:"80%"}, "slow");
} }
...@@ -1247,39 +1322,86 @@ function openChat() { ...@@ -1247,39 +1322,86 @@ function openChat() {
$('#usermsg').focus(); $('#usermsg').focus();
} }
function showToolbar() { function hideToolbar()
$('#toolbar').css({visibility:"visible"}); {
var isToolbarHover = false;
$('#header').find('*').each(function(){
var id = $(this).attr('id');
if ($("#" + id + ":hover").length > 0) {
isToolbarHover = true;
}
});
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
if (!isToolbarHover) {
$('#header').hide("slide", { direction: "up", duration: 300});
}
else {
toolbarTimeout = setTimeout(hideToolbar, 2000);
}
};
function showToolbar()
{
if (!$('#header').is(':visible')) {
$('#header').show("slide", { direction: "up", duration: 300});
if (toolbarTimeout) {
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
toolbarTimeout = setTimeout(hideToolbar, 2000);
}
}
function dockToolbar(isDock) {
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {
showToolbar();
}
// Then clear the time out, to dock the toolbar.
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
else {
if (!$('#header').is(':visible')) {
showToolbar();
}
else {
toolbarTimeout = setTimeout(hideToolbar, 2000);
}
}
} }
function updateRoomUrl(newRoomUrl) { function updateRoomUrl(newRoomUrl) {
roomUrl = newRoomUrl; roomUrl = newRoomUrl;
} }
function goFullScreen() function toggleFullScreen() {
{ var fsElement = document.documentElement;
var tag = "largeVideo";
if (pdfFrame != null) tag = "pdfViewer";
var videoElement = document.getElementById(tag);
if (!document.mozFullScreen && !document.webkitFullScreen)
{
if (videoElement.mozRequestFullScreen) {
videoElement.mozRequestFullScreen();
} else {
videoElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else { if (!document.mozFullScreen && !document.webkitIsFullScreen){
if (document.mozCancelFullScreen) { //Enter Full Screen
document.mozCancelFullScreen(); if (fsElement.mozRequestFullScreen) {
} else { fsElement.mozRequestFullScreen();
document.webkitCancelFullScreen(); }
} else {
} fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
//Exit Full Screen
if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else {
document.webkitCancelFullScreen();
}
}
} }
function inviteParticipant() function inviteParticipant()
...@@ -1393,23 +1515,30 @@ function linkify(inputText) ...@@ -1393,23 +1515,30 @@ function linkify(inputText)
return replacedText; return replacedText;
} }
function resetVisualNotification()
{
unreadMessages = 0;
setVisualNotification(false);
}
function setVisualNotification(show) function setVisualNotification(show)
{ {
var unreadMsgElement = document.getElementById('unreadMessages'); var unreadMsgElement = document.getElementById('unreadMessages');
if (unreadMessages) { if (unreadMessages) {
unreadMsgElement.innerHTML = unreadMessages.toString(); unreadMsgElement.innerHTML = "&nbsp;" + unreadMessages.toString() + "&nbsp;";
var chatButtonElement = document.getElementById('chat').parentNode; showToolbar();
var leftIndent = (Util.getTextWidth(chatButtonElement) - Util.getTextWidth(unreadMsgElement) - 5)/2; var chatButtonElement = document.getElementById('chatButton').parentNode;
var topIndent = (Util.getTextHeight(chatButtonElement) - Util.getTextHeight(unreadMsgElement))/2 - 2; var leftIndent = (Util.getTextWidth(chatButtonElement) - Util.getTextWidth(unreadMsgElement))/2 - 2;
var topIndent = (Util.getTextHeight(chatButtonElement) - Util.getTextHeight(unreadMsgElement))/2 - 3;
unreadMsgElement.setAttribute('style', 'top:' + topIndent + '; left:' + leftIndent +';'); unreadMsgElement.setAttribute('style', 'top:' + topIndent + '; left:' + leftIndent +';background-color: red');
} }
else else
unreadMsgElement.innerHTML = ''; unreadMsgElement.innerHTML = '';
var glower = $('#chat'); var glower = $('#chatButton');
if (show && !notificationInterval) { if (show && !notificationInterval) {
notificationInterval = window.setInterval(function() { notificationInterval = window.setInterval(function() {
......
/*
var vid = new Whammy.Video();
vid.add(canvas or data url)
vid.compile()
*/
var Whammy = (function(){
// in this case, frames has a very specific meaning, which will be
// detailed once i finish writing the code
function toWebM(frames){
var info = checkFrames(frames);
var counter = 0;
var EBML = [
{
"id": 0x1a45dfa3, // EBML
"data": [
{
"data": 1,
"id": 0x4286 // EBMLVersion
},
{
"data": 1,
"id": 0x42f7 // EBMLReadVersion
},
{
"data": 4,
"id": 0x42f2 // EBMLMaxIDLength
},
{
"data": 8,
"id": 0x42f3 // EBMLMaxSizeLength
},
{
"data": "webm",
"id": 0x4282 // DocType
},
{
"data": 2,
"id": 0x4287 // DocTypeVersion
},
{
"data": 2,
"id": 0x4285 // DocTypeReadVersion
}
]
},
{
"id": 0x18538067, // Segment
"data": [
{
"id": 0x1549a966, // Info
"data": [
{
"data": 1e6, //do things in millisecs (num of nanosecs for duration scale)
"id": 0x2ad7b1 // TimecodeScale
},
{
"data": "whammy",
"id": 0x4d80 // MuxingApp
},
{
"data": "whammy",
"id": 0x5741 // WritingApp
},
{
"data": doubleToString(info.duration),
"id": 0x4489 // Duration
}
]
},
{
"id": 0x1654ae6b, // Tracks
"data": [
{
"id": 0xae, // TrackEntry
"data": [
{
"data": 1,
"id": 0xd7 // TrackNumber
},
{
"data": 1,
"id": 0x63c5 // TrackUID
},
{
"data": 0,
"id": 0x9c // FlagLacing
},
{
"data": "und",
"id": 0x22b59c // Language
},
{
"data": "V_VP8",
"id": 0x86 // CodecID
},
{
"data": "VP8",
"id": 0x258688 // CodecName
},
{
"data": 1,
"id": 0x83 // TrackType
},
{
"id": 0xe0, // Video
"data": [
{
"data": info.width,
"id": 0xb0 // PixelWidth
},
{
"data": info.height,
"id": 0xba // PixelHeight
}
]
}
]
}
]
},
{
"id": 0x1f43b675, // Cluster
"data": [
{
"data": 0,
"id": 0xe7 // Timecode
}
].concat(frames.map(function(webp){
var block = makeSimpleBlock({
discardable: 0,
frame: webp.data.slice(4),
invisible: 0,
keyframe: 1,
lacing: 0,
trackNum: 1,
timecode: Math.round(counter)
});
counter += webp.duration;
return {
data: block,
id: 0xa3
};
}))
}
]
}
];
return generateEBML(EBML)
}
// sums the lengths of all the frames and gets the duration, woo
function checkFrames(frames){
var width = frames[0].width,
height = frames[0].height,
duration = frames[0].duration;
for(var i = 1; i < frames.length; i++){
if(frames[i].width != width) throw "Frame " + (i + 1) + " has a different width";
if(frames[i].height != height) throw "Frame " + (i + 1) + " has a different height";
if(frames[i].duration < 0) throw "Frame " + (i + 1) + " has a weird duration";
duration += frames[i].duration;
}
return {
duration: duration,
width: width,
height: height
};
}
function numToBuffer(num){
var parts = [];
while(num > 0){
parts.push(num & 0xff)
num = num >> 8
}
return new Uint8Array(parts.reverse());
}
function strToBuffer(str){
// return new Blob([str]);
var arr = new Uint8Array(str.length);
for(var i = 0; i < str.length; i++){
arr[i] = str.charCodeAt(i)
}
return arr;
// this is slower
// return new Uint8Array(str.split('').map(function(e){
// return e.charCodeAt(0)
// }))
}
//sorry this is ugly, and sort of hard to understand exactly why this was done
// at all really, but the reason is that there's some code below that i dont really
// feel like understanding, and this is easier than using my brain.
function bitsToBuffer(bits){
var data = [];
var pad = (bits.length % 8) ? (new Array(1 + 8 - (bits.length % 8))).join('0') : '';
bits = pad + bits;
for(var i = 0; i < bits.length; i+= 8){
data.push(parseInt(bits.substr(i,8),2))
}
return new Uint8Array(data);
}
function generateEBML(json){
var ebml = [];
for(var i = 0; i < json.length; i++){
var data = json[i].data;
// console.log(data);
if(typeof data == 'object') data = generateEBML(data);
if(typeof data == 'number') data = bitsToBuffer(data.toString(2));
if(typeof data == 'string') data = strToBuffer(data);
// console.log(data)
var len = data.size || data.byteLength;
var zeroes = Math.ceil(Math.ceil(Math.log(len)/Math.log(2))/8);
var size_str = len.toString(2);
var padded = (new Array((zeroes * 7 + 7 + 1) - size_str.length)).join('0') + size_str;
var size = (new Array(zeroes)).join('0') + '1' + padded;
//i actually dont quite understand what went on up there, so I'm not really
//going to fix this, i'm probably just going to write some hacky thing which
//converts that string into a buffer-esque thing
ebml.push(numToBuffer(json[i].id));
ebml.push(bitsToBuffer(size));
ebml.push(data)
}
return new Blob(ebml, {
type: "video/webm"
});
}
//OKAY, so the following two functions are the string-based old stuff, the reason they're
//still sort of in here, is that they're actually faster than the new blob stuff because
//getAsFile isn't widely implemented, or at least, it doesn't work in chrome, which is the
// only browser which supports get as webp
//Converting between a string of 0010101001's and binary back and forth is probably inefficient
//TODO: get rid of this function
function toBinStr_old(bits){
var data = '';
var pad = (bits.length % 8) ? (new Array(1 + 8 - (bits.length % 8))).join('0') : '';
bits = pad + bits;
for(var i = 0; i < bits.length; i+= 8){
data += String.fromCharCode(parseInt(bits.substr(i,8),2))
}
return data;
}
function generateEBML_old(json){
var ebml = '';
for(var i = 0; i < json.length; i++){
var data = json[i].data;
if(typeof data == 'object') data = generateEBML_old(data);
if(typeof data == 'number') data = toBinStr_old(data.toString(2));
var len = data.length;
var zeroes = Math.ceil(Math.ceil(Math.log(len)/Math.log(2))/8);
var size_str = len.toString(2);
var padded = (new Array((zeroes * 7 + 7 + 1) - size_str.length)).join('0') + size_str;
var size = (new Array(zeroes)).join('0') + '1' + padded;
ebml += toBinStr_old(json[i].id.toString(2)) + toBinStr_old(size) + data;
}
return ebml;
}
//woot, a function that's actually written for this project!
//this parses some json markup and makes it into that binary magic
//which can then get shoved into the matroska comtainer (peaceably)
function makeSimpleBlock(data){
var flags = 0;
if (data.keyframe) flags |= 128;
if (data.invisible) flags |= 8;
if (data.lacing) flags |= (data.lacing << 1);
if (data.discardable) flags |= 1;
if (data.trackNum > 127) {
throw "TrackNumber > 127 not supported";
}
var out = [data.trackNum | 0x80, data.timecode >> 8, data.timecode & 0xff, flags].map(function(e){
return String.fromCharCode(e)
}).join('') + data.frame;
return out;
}
// here's something else taken verbatim from weppy, awesome rite?
function parseWebP(riff){
var VP8 = riff.RIFF[0].WEBP[0];
var frame_start = VP8.indexOf('\x9d\x01\x2a'); //A VP8 keyframe starts with the 0x9d012a header
for(var i = 0, c = []; i < 4; i++) c[i] = VP8.charCodeAt(frame_start + 3 + i);
var width, horizontal_scale, height, vertical_scale, tmp;
//the code below is literally copied verbatim from the bitstream spec
tmp = (c[1] << 8) | c[0];
width = tmp & 0x3FFF;
horizontal_scale = tmp >> 14;
tmp = (c[3] << 8) | c[2];
height = tmp & 0x3FFF;
vertical_scale = tmp >> 14;
return {
width: width,
height: height,
data: VP8,
riff: riff
}
}
// i think i'm going off on a riff by pretending this is some known
// idiom which i'm making a casual and brilliant pun about, but since
// i can't find anything on google which conforms to this idiomatic
// usage, I'm assuming this is just a consequence of some psychotic
// break which makes me make up puns. well, enough riff-raff (aha a
// rescue of sorts), this function was ripped wholesale from weppy
function parseRIFF(string){
var offset = 0;
var chunks = {};
while (offset < string.length) {
var id = string.substr(offset, 4);
var len = parseInt(string.substr(offset + 4, 4).split('').map(function(i){
var unpadded = i.charCodeAt(0).toString(2);
return (new Array(8 - unpadded.length + 1)).join('0') + unpadded
}).join(''),2);
var data = string.substr(offset + 4 + 4, len);
offset += 4 + 4 + len;
chunks[id] = chunks[id] || [];
if (id == 'RIFF' || id == 'LIST') {
chunks[id].push(parseRIFF(data));
} else {
chunks[id].push(data);
}
}
return chunks;
}
// here's a little utility function that acts as a utility for other functions
// basically, the only purpose is for encoding "Duration", which is encoded as
// a double (considerably more difficult to encode than an integer)
function doubleToString(num){
return [].slice.call(
new Uint8Array(
(
new Float64Array([num]) //create a float64 array
).buffer) //extract the array buffer
, 0) // convert the Uint8Array into a regular array
.map(function(e){ //since it's a regular array, we can now use map
return String.fromCharCode(e) // encode all the bytes individually
})
.reverse() //correct the byte endianness (assume it's little endian for now)
.join('') // join the bytes in holy matrimony as a string
}
function WhammyVideo(speed, quality){ // a more abstract-ish API
this.frames = [];
this.duration = 1000 / speed;
this.quality = quality || 0.8;
}
WhammyVideo.prototype.add = function(frame, duration){
if(typeof duration != 'undefined' && this.duration) throw "you can't pass a duration if the fps is set";
if('canvas' in frame){ //CanvasRenderingContext2D
frame = frame.canvas;
}
if('toDataURL' in frame){
frame = frame.toDataURL('image/webp', this.quality)
}else if(typeof frame != "string"){
throw "frame must be a a HTMLCanvasElement, a CanvasRenderingContext2D or a DataURI formatted string"
}
if (!(/^data:image\/webp;base64,/ig).test(frame)) {
throw "Input must be formatted properly as a base64 encoded DataURI of type image/webp";
}
this.frames.push({
image: frame,
duration: duration || this.duration
})
}
WhammyVideo.prototype.compile = function(){
return new toWebM(this.frames.map(function(frame){
var webp = parseWebP(parseRIFF(atob(frame.image.slice(23))));
webp.duration = frame.duration;
return webp;
}))
}
return {
Video: WhammyVideo,
fromImageArray: function(images, fps){
return toWebM(images.map(function(image){
var webp = parseWebP(parseRIFF(atob(image.slice(23))))
webp.duration = 1000 / fps;
return webp;
}))
},
toWebM: toWebM
// expose methods of madness
}
})()
\ No newline at end of file
/*
* Jitsi Videobridge, OpenSource video conferencing.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.videobridge.openfire;
import org.jivesoftware.util.*;
import org.jivesoftware.openfire.*;
import org.slf4j.*;
import org.slf4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;
import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
public class Config extends HttpServlet
{
private static final Logger Log = LoggerFactory.getLogger(Config.class);
public static final long serialVersionUID = 24362462L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Log.info("Config servlet");
String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
boolean websockets = XMPPServer.getInstance().getPluginManager().getPlugin("websockets") != null;
writeHeader(response);
ServletOutputStream out = response.getOutputStream();
out.println("var config = {");
out.println(" hosts: {");
out.println(" domain: '" + domain + "',");
out.println(" muc: 'conference." + domain + "',");
out.println(" bridge: 'jitsi-videobridge." + domain + "',");
out.println(" },");
out.println(" useIPv6: false,");
out.println(" useNicks: false,");
out.println(" useWebsockets: " + (websockets ? "true" : "false") + ",");
out.println(" resolution: '720',");
out.println(" bosh: window.location.protocol + '//' + window.location.host + '/http-bind/'");
out.println("}; ");
}
catch(Exception e) {
Log.info("Config doGet Error", e);
}
}
private void writeHeader(HttpServletResponse response)
{
try {
response.setHeader("Expires", "Sat, 6 May 1995 12:00:00 GMT");
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.setHeader("Pragma", "no-cache");
response.setHeader("Content-Type", "application/javascript");
response.setHeader("Connection", "close");
}
catch(Exception e)
{
Log.info("Config writeHeader Error", e);
}
}
}
...@@ -263,7 +263,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -263,7 +263,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
Properties properties = new Properties(); Properties properties = new Properties();
String hostName = XMPPServer.getInstance().getServerInfo().getHostname(); String hostName = XMPPServer.getInstance().getServerInfo().getHostname();
String logDir = pluginDirectory.getAbsolutePath() + File.separator + ".." + File.separator + ".." + File.separator + "logs" + File.separator; String logDir = pluginDirectory.getAbsolutePath() + File.separator + ".." + File.separator + ".." + File.separator + "logs" + File.separator;
String port = JiveGlobals.getProperty(SIP_PORT_PROPERTY_NAME, "5060"); String port = JiveGlobals.getProperty(SIP_PORT_PROPERTY_NAME, "5070");
properties.setProperty("com.voxbone.kelpie.hostname", hostName); properties.setProperty("com.voxbone.kelpie.hostname", hostName);
properties.setProperty("com.voxbone.kelpie.ip", hostName); properties.setProperty("com.voxbone.kelpie.ip", hostName);
...@@ -1016,7 +1016,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1016,7 +1016,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
private byte partial[]; private byte partial[];
private int lastseqnum = -1; private int lastseqnum = -1;
private Participant me = this; private Participant me = this;
private boolean snapshot = false; private int snapshot = 0;
/** /**
* *
* *
...@@ -1052,7 +1052,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1052,7 +1052,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
*/ */
public void recordData(RawPacket packet) public void recordData(RawPacket packet)
{ {
if (!snapshot) Log.info("transferData " + packet.getPayloadLength() + " " + packet.getHeaderLength() + " " + packet.getExtensionLength()); if (snapshot < 10) Log.info("transferData " + packet.getPayloadLength() + " " + packet.getHeaderLength() + " " + packet.getExtensionLength());
try { try {
...@@ -1069,42 +1069,56 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1069,42 +1069,56 @@ public class PluginImpl implements Plugin, PropertyEventListener
partial = new byte[0]; partial = new byte[0];
} }
if (!snapshot) Log.info("expecting X R N S PartID"); if (snapshot < 10) Log.info("expecting X R N S PartID");
byte x = rtp[payloadOffset]; //X R N S PartID byte x = rtp[payloadOffset]; //X R N S PartID
payloadOffset++; payloadOffset++;
vp8Length--; vp8Length--;
if ((x & 0x80) != 0) if ((x & 0x80) != 0) // extened bits
{ {
if (!snapshot) Log.info("found I L T RSV-A"); if (snapshot < 10) Log.info("found I L T RSV-A");
byte ilt = rtp[payloadOffset]; //I L T RSV-A byte ilt = rtp[payloadOffset]; //I L T RSV-A
payloadOffset++; payloadOffset++;
vp8Length--; vp8Length--;
if ((ilt & 0x80) != 0) { //picture ID if ((ilt & 0x80) != 0) { //picture ID
if (!snapshot) Log.info("found picture ID"); if (snapshot < 10) Log.info("found picture ID 1");
payloadOffset++; payloadOffset++;
vp8Length--; vp8Length--;
byte m = rtp[payloadOffset]; //picture ID
if ((m & 0x80) != 0)
{
if (snapshot < 10) Log.info("found picture ID 2");
payloadOffset++;
vp8Length--;
}
} }
if ((ilt & 0x40) != 0) { //TL0PICIDX if ((ilt & 0x40) != 0) { //TL0PICIDX
if (!snapshot) Log.info("found TL0PICIDX"); if (snapshot < 10) Log.info("found TL0PICIDX");
payloadOffset++; payloadOffset++;
vp8Length--; vp8Length--;
} }
if ((ilt & 0x20) != 0) { //TID RSV-B
if (!snapshot) Log.info("found TID RSV-B"); if ((ilt & 0x20) != 0 || (ilt & 0x10) != 0) { //TID RSV-B
if (snapshot < 10) Log.info("found TID RSV-B or keyframe index");
payloadOffset++; payloadOffset++;
vp8Length--; vp8Length--;
} }
} }
if ((x & 0x10) != 0) // start of partition if ((x & 0x10) != 0 && (x & 0x0f) == 0 && vp8Length >= 3) // start of partition
{ {
if (!snapshot) Log.info("found start of partition " + x); if (snapshot < 10) Log.info("found start of partition " + x);
partial = new byte[0]; partial = new byte[0];
} }
boolean isKeyframe = (rtp[payloadOffset] & 0x1) == 0;
int partialLength = partial.length; int partialLength = partial.length;
partial = Arrays.copyOf(partial, partial.length + vp8Length); partial = Arrays.copyOf(partial, partial.length + vp8Length);
System.arraycopy(rtp, payloadOffset, partial, partialLength, vp8Length); System.arraycopy(rtp, payloadOffset, partial, partialLength, vp8Length);
...@@ -1112,7 +1126,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1112,7 +1126,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
int thisseqnum = packet.getSequenceNumber(); int thisseqnum = packet.getSequenceNumber();
if (lastseqnum != -1 && thisseqnum != lastseqnum + 1) { if (lastseqnum != -1 && thisseqnum != lastseqnum + 1) {
if (!snapshot) Log.info("VP8:Received packet out of order, discarding frame."); if (snapshot < 10) Log.info("VP8:Received packet out of order, discarding frame.");
partial = null; partial = null;
lastseqnum = -1; lastseqnum = -1;
return; return;
...@@ -1124,15 +1138,14 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1124,15 +1138,14 @@ public class PluginImpl implements Plugin, PropertyEventListener
if (recorder != null && partial != null) if (recorder != null && partial != null)
{ {
byte[] full = Arrays.copyOf(partial, partial.length); byte[] full = Arrays.copyOf(partial, partial.length);
boolean isKeyframe = (full[0] & 0x1) == 0; if (snapshot < 10) Log.info("recordData " + " " + packet.getPayloadType() + " " + full + " " + packet.getSequenceNumber() + " " + isKeyframe);
if (!snapshot) Log.info("recordData " + " " + packet.getPayloadType() + " " + full + " " + packet.getSequenceNumber() + " " + isKeyframe);
recorder.write(full, 0, full.length, isKeyframe, packet.getTimestamp()); recorder.write(full, 0, full.length, isKeyframe, packet.getTimestamp());
if (isKeyframe && snapshot == false) if (isKeyframe && snapshot < 10)
{ {
recorder.writeWebPImage(full, 0, full.length, packet.getTimestamp()); recorder.writeWebPImage(full, 0, full.length, packet.getTimestamp());
snapshot = true; snapshot++;
} }
} }
...@@ -1225,7 +1238,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1225,7 +1238,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
{ {
recorder.done(); recorder.done();
recorder = null; recorder = null;
snapshot = false; snapshot = 0;
} }
} }
} }
...@@ -1383,7 +1396,8 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1383,7 +1396,8 @@ public class PluginImpl implements Plugin, PropertyEventListener
if (videoChannel != null) if (videoChannel != null)
{ {
participant.addMediaStream(videoChannel.getMediaStream()); // webm file creation not working yet
//participant.addMediaStream(videoChannel.getMediaStream());
} }
} }
} }
......
<%@ page import="org.jitsi.videobridge.openfire.*" %>
<%@ page import="org.jivesoftware.openfire.*" %>
<%@ page import="org.jivesoftware.openfire.container.*" %>
<%@ page import="org.jivesoftware.util.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<%
String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
boolean websockets = XMPPServer.getInstance().getPluginManager().getPlugin("websockets") != null;
response.setHeader("Content-Type", "application/javascript");
%>
var config = {
hosts: {
domain: <%=domain%>,
muc: 'conference.<%=domain%>',
bridge: 'jitsi-videobridge.<%=domain%>',
},
useIPv6: false,
useNicks: false,
useWebsockets: <%= websockets ? "true" : "false" %>,
resolution: "360",
bosh: window.location.protocol + "//" + window.location.host + '/http-bind/'
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment