Commit f2973b5f authored by Dele Olajide's avatar Dele Olajide Committed by dele

Jitsi Videobridge - Refresh jitmeet

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13980 b35dd754-fafc-0310-a699-88a17e54d16e
parent cb021ac3
......@@ -2,7 +2,8 @@
/* application specific logic */
var connection = null;
var focus = null;
var RTC;
var activecall = null;
var RTC = null;
var RTCPeerConnection = null;
var nickname = null;
var sharedKey = '';
......@@ -83,7 +84,8 @@ function doJoin() {
roomnode = path.substr(1).toLowerCase();
} else {
roomnode = Math.random().toString(36).substr(2, 20);
window.history.pushState('VideoChat', 'Room: ' + roomnode, window.location.pathname + roomnode);
window.history.pushState('VideoChat',
'Room: ' + roomnode, window.location.pathname + roomnode);
}
}
......@@ -165,12 +167,14 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
var remotes = document.getElementById('remoteVideos');
if (data.peerjid) {
container = document.getElementById('participant_' + Strophe.getResourceFromJid(data.peerjid));
container = document.getElementById(
'participant_' + Strophe.getResourceFromJid(data.peerjid));
if (!container) {
console.warn('no container for', data.peerjid);
// create for now...
// FIXME: should be removed
container = addRemoteVideoContainer('participant_' + Strophe.getResourceFromJid(data.peerjid));
container = addRemoteVideoContainer(
'participant_' + Strophe.getResourceFromJid(data.peerjid));
} else {
//console.log('found container for', data.peerjid);
}
......@@ -182,6 +186,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
container = document.createElement('span');
container.className = 'videocontainer';
remotes.appendChild(container);
Util.playSoundNotification('userJoined');
}
var vid = document.createElement('video');
var id = 'remoteVideo_' + sid + '_' + data.stream.id;
......@@ -212,11 +217,12 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
if (pick) {
if (pick.src === localVideoSrc)
isLocalVideo = true;
updateLargeVideo(pick.src, isLocalVideo, pick.volume);
}
}
$('#' + id).parent().remove();
Util.playSoundNotification('userLeft');
resizeThumbnails();
};
sel.click(
......@@ -263,13 +269,84 @@ function sendKeyframe(pc) {
);
}
function demonstrateabug(pc) {
// funny way of doing mute. the subsequent offer contains things like rtcp-mux
// and triggers all new ice candidates (ice restart)
// this code is here to demonstrate a bug
pc.createOffer(
function (offer) {
console.log(offer);
var sdp = new SDP(offer.sdp);
if (sdp.media.length > 1) {
sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
sdp.raw = sdp.session + sdp.media.join('');
offer.sdp = sdp.raw;
pc.setLocalDescription(offer,
function () {
console.log('mute SLD ok');
},
function(error) {
console.log('mute SLD error');
}
);
}
},
function (error) {
console.warn(error);
},
{mandatory: {OfferToReceiveAudio: true, OfferToReceiveVideo: false}}
);
}
// really mute video, i.e. dont even send black frames
function muteVideo(pc, unmute) {
// FIXME: this probably needs another of those lovely state safeguards...
// which checks for iceconn == connected and sigstate == stable
pc.setRemoteDescription(pc.remoteDescription,
function () {
pc.createAnswer(
function (answer) {
var sdp = new SDP(answer.sdp);
if (sdp.media.length > 1) {
if (unmute)
sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
else
sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
sdp.raw = sdp.session + sdp.media.join('');
answer.sdp = sdp.raw;
}
pc.setLocalDescription(answer,
function () {
console.log('mute SLD ok');
},
function(error) {
console.log('mute SLD error');
}
);
},
function (error) {
console.log(error);
}
);
},
function (error) {
console.log('muteVideo SRD error');
}
);
}
$(document).bind('callincoming.jingle', function (event, sid) {
var sess = connection.jingle.sessions[sid];
// TODO: do we check activecall == null?
activecall = sess;
// TODO: check affiliation and/or role
console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
sess.usedrip = true; // not-so-naive trickle ice
sess.sendAnswer();
sess.accept();
});
$(document).bind('callactive.jingle', function (event, videoelem, sid) {
......@@ -292,13 +369,17 @@ $(document).bind('setLocalDescription.jingle', function (event, sid) {
// put our ssrcs into presence so other clients can identify our stream
var sess = connection.jingle.sessions[sid];
var newssrcs = {};
var directions = {};
var localSDP = new SDP(sess.peerconnection.localDescription.sdp);
localSDP.media.forEach(function (media) {
var type = SDPUtil.parse_mline(media.split('\r\n')[0]).media;
if (SDPUtil.find_line(media, 'a=ssrc:')) {
var ssrc = SDPUtil.find_line(media, 'a=ssrc:').substring(7).split(' ')[0];
// assumes a single local ssrc
var ssrc = SDPUtil.find_line(media, 'a=ssrc:').substring(7).split(' ')[0];
newssrcs[type] = ssrc;
directions[type] = (SDPUtil.find_line(media, 'a=sendrecv') || SDPUtil.find_line(media, 'a=recvonly') || SDPUtil.find_line('a=sendonly') || SDPUtil.find_line('a=inactive') || 'a=sendrecv').substr(2);
}
});
console.log('new ssrcs', newssrcs);
......@@ -306,7 +387,7 @@ $(document).bind('setLocalDescription.jingle', function (event, sid) {
var i = 0;
Object.keys(newssrcs).forEach(function (mtype) {
i++;
connection.emuc.addMediaToPresence(i, mtype, newssrcs[mtype]);
connection.emuc.addMediaToPresence(i, mtype, newssrcs[mtype], directions[mtype]);
});
if (i > 0) {
connection.emuc.sendPresence();
......@@ -411,6 +492,19 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
$(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;
// might need to update the direction if participant just went from sendrecv to recvonly
if (ssrc.getAttribute('type') == 'video') {
var el = $('#participant_' + Strophe.getResourceFromJid(jid) + '>video');
switch(ssrc.getAttribute('direction')) {
case 'sendrecv':
el.show();
break;
case 'recvonly':
el.hide();
break;
}
}
});
if (info.displayName) {
......@@ -511,7 +605,7 @@ $(document).bind('presentationadded.muc', function (event, jid, presUrl, current
});
$('#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) {
......@@ -620,9 +714,19 @@ function updateLargeVideo(newSrc, localVideo, vol) {
function toggleVideo() {
if (!(connection && connection.jingle.localStream)) return;
var ismuted = false;
for (var idx = 0; idx < connection.jingle.localStream.getVideoTracks().length; idx++) {
ismuted = !connection.jingle.localStream.getVideoTracks()[idx].enabled;
}
for (var idx = 0; idx < connection.jingle.localStream.getVideoTracks().length; idx++) {
connection.jingle.localStream.getVideoTracks()[idx].enabled = !connection.jingle.localStream.getVideoTracks()[idx].enabled;
}
var sess = focus || activecall;
if (!sess) {
return;
}
sess.pendingop = ismuted ? 'unmute' : 'mute';
sess.modifySources();
}
function toggleAudio() {
......@@ -632,8 +736,8 @@ function toggleAudio() {
}
}
function resizeLarge() {
resizeChat();
var resizeLarge = function () {
Chat.resizeChat();
var availableHeight = window.innerHeight;
var chatspaceWidth = $('#chatspace').is(":visible")
? $('#chatspace').width()
......@@ -666,7 +770,7 @@ function resizeLarge() {
}
resizeThumbnails();
}
};
function resizeThumbnails() {
// Calculate the available height, which is the inner window height minus 39px for the header
......@@ -691,70 +795,8 @@ function resizeThumbnails() {
$('#remoteVideos>span').height(availableHeight);
}
function resizeChat() {
var availableHeight = window.innerHeight;
var availableWidth = window.innerWidth;
var chatWidth = 200;
if (availableWidth*0.2 < 200)
chatWidth = availableWidth*0.2;
$('#chatspace').width(chatWidth);
$('#chatspace').height(availableHeight - 40);
resizeChatConversation();
}
function resizeChatConversation() {
var usermsgStyleHeight = document.getElementById("usermsg").style.height;
var usermsgHeight = usermsgStyleHeight.substring(0, usermsgStyleHeight.indexOf('px'));
$('#chatconversation').width($('#chatspace').width() - 10);
$('#chatconversation').height(window.innerHeight - 50 - parseInt(usermsgHeight));
}
$(document).ready(function () {
var storedDisplayName = window.localStorage.displayname;
if (storedDisplayName) {
nickname = storedDisplayName;
setChatConversationMode(true);
}
$('#nickinput').keydown(function(event) {
if (event.keyCode == 13) {
event.preventDefault();
var val = this.value;
this.value = '';
if (!nickname) {
nickname = val;
window.localStorage.displayname = nickname;
connection.emuc.addDisplayNameToPresence(nickname);
connection.emuc.sendPresence();
setChatConversationMode(true);
return;
}
}
});
$('#usermsg').keydown(function(event) {
if (event.keyCode == 13) {
event.preventDefault();
var message = this.value;
$('#usermsg').val('').trigger('autosize.resize');
this.focus();
connection.emuc.sendMessage(message, nickname);
}
});
var onTextAreaResize = function() {
resizeChatConversation();
scrollChatToBottom();
};
$('#usermsg').autosize({callback: onTextAreaResize});
Chat.init();
// Set the defaults for prompt dialogs.
jQuery.prompt.setDefaults({persistent: false});
......@@ -826,24 +868,6 @@ function dump(elem, filename){
return false;
}
/*
* Appends the given message to the chat conversation.
*/
function updateChatConversation(nick, message)
{
var divClassName = '';
if (nickname == nick)
divClassName = "localuser";
else
divClassName = "remoteuser";
//replace links and smileys
message = processReplacements(message);
$('#chatconversation').append('<div class="' + divClassName + '"><b>' + nick + ': </b>' + message + '</div>');
$('#chatconversation').animate({ scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
}
/*
* Changes the style class of the element given by id.
*/
......@@ -900,10 +924,10 @@ function openLockDialog() {
if(v)
{
var lockKey = document.getElementById('lockKey');
if (lockKey.value)
{
setSharedKey(lockKey.value);
setSharedKey(Util.escapeHtml(lockKey.value));
lockRoom(true);
}
}
......@@ -916,7 +940,8 @@ function openLockDialog() {
* Opens the invite link dialog.
*/
function openLinkDialog() {
$.prompt('<input id="inviteLinkRef" type="text" value="' + roomUrl + '" onclick="this.select();">',
$.prompt('<input id="inviteLinkRef" type="text" value="'
+ encodeURI(roomUrl) + '" onclick="this.select();" readonly>',
{
title: "Share this link with everyone you want to invite",
persistent: false,
......@@ -952,7 +977,7 @@ function openSettingsDialog() {
if ($('#requireNicknames').is(":checked"))
{
// it is checked
// it is checked
}
/*
var lockKey = document.getElementById('lockKey');
......@@ -989,7 +1014,8 @@ function openPreziDialog() {
});
}
else if (preziPlayer != null) {
$.prompt("Another participant is already sharing a Prezi. This conference allows only one Prezi at a time.",
$.prompt("Another participant is already sharing a Prezi." +
"This conference allows only one Prezi at a time.",
{
title: "Share a Prezi",
buttons: { "Ok": true},
......@@ -1015,20 +1041,24 @@ function openPreziDialog() {
if (preziUrl.value)
{
if (preziUrl.value.indexOf('http://prezi.com/') != 0
&& preziUrl.value.indexOf('https://prezi.com/') != 0)
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 = preziUrl.value.substring(preziUrl.value.indexOf("prezi.com/") + 10);
if (presIdTmp.indexOf('/') < 2) {
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(preziUrl.value, 0);
connection.emuc.addPreziToPresence(urlValue, 0);
connection.emuc.sendPresence();
$.prompt.close();
}
......@@ -1056,7 +1086,7 @@ function openPreziDialog() {
};
var myPrompt = jQuery.prompt(openPreziState);
myPrompt.on('impromptu:loaded', function(e) {
document.getElementById('preziUrl').focus();
});
......@@ -1074,7 +1104,7 @@ function lockRoom(lock) {
connection.emuc.lockRoom(sharedKey);
else
connection.emuc.lockRoom('');
updateLockButton();
}
......@@ -1092,44 +1122,6 @@ function updateLockButton() {
buttonClick("#lockIcon", "fa fa-unlock fa-lg fa fa-lock fa-lg");
}
/*
* Opens / closes the chat area.
*/
function openChat() {
var chatspace = $('#chatspace');
var videospace = $('#videospace');
var onShow = function () {
resizeLarge();
$('#chatspace').show("slide", { direction: "right", duration: 500});
};
var onHide = function () {
$('#chatspace').hide("slide", { direction: "right", duration: 500});
resizeLarge();
};
if (chatspace.css("display") == 'block') {
videospace.animate({right: 0}, {queue: false, duration: 500, progress: onHide});
}
else {
videospace.animate({right: chatspace.width()},
{queue: false,
duration: 500,
progress: onShow,
complete: function() {
scrollChatToBottom();
}
});
}
// Request the focus in the nickname field or the chat input field.
if ($('#nickname').css('visibility') == 'visible')
$('#nickinput').focus();
else {
$('#usermsg').focus();
}
}
/*
* Shows the call main toolbar.
*/
......@@ -1199,7 +1191,7 @@ function addRemoteVideoContainer(id) {
return container;
}
/*
/**
* Creates the element indicating the focus of the conference.
*/
function createFocusIndicatorElement(parentElement) {
......@@ -1209,13 +1201,7 @@ function createFocusIndicatorElement(parentElement) {
parentElement.appendChild(focusIndicator);
}
function scrollChatToBottom() {
setTimeout(function() {
$('#chatconversation').scrollTop($('#chatconversation')[0].scrollHeight);
}, 5);
}
/*
/**
* Toggles the application in and out of full screen mode
* (a.k.a. presentation mode in Chrome).
*/
......@@ -1246,9 +1232,11 @@ function toggleFullScreen() {
}
/**
*
* Shows the display name for the given video.
*/
function showDisplayName(videoSpanId, displayName) {
var escDisplayName = Util.escapeHtml(displayName);
var nameSpan = $('#' + videoSpanId + '>span.displayname');
// If we already have a display name for this video.
......@@ -1256,21 +1244,21 @@ function showDisplayName(videoSpanId, displayName) {
var nameSpanElement = nameSpan.get(0);
if (nameSpanElement.id == 'localDisplayName'
&& $('#localDisplayName').html() != displayName)
$('#localDisplayName').html(displayName);
&& $('#localDisplayName').html() != escDisplayName)
$('#localDisplayName').html(escDisplayName);
else
$('#' + videoSpanId + '_name').html(displayName);
$('#' + videoSpanId + '_name').html(escDisplayName);
}
else {
var editButton = null;
if (videoSpanId == 'localVideoContainer') {
editButton = createEditDisplayNameButton();
}
if (displayName.length) {
if (escDisplayName.length) {
nameSpan = document.createElement('span');
nameSpan.className = 'displayname';
nameSpan.innerHTML = displayName;
nameSpan.innerHTML = escDisplayName;
$('#' + videoSpanId)[0].appendChild(nameSpan);
}
......@@ -1280,13 +1268,14 @@ function showDisplayName(videoSpanId, displayName) {
else {
nameSpan.id = 'localDisplayName';
$('#' + videoSpanId)[0].appendChild(editButton);
var editableText = document.createElement('input');
editableText.className = 'displayname';
editableText.id = 'editDisplayName';
if (displayName.length)
editableText.value = displayName.substring(0, displayName.indexOf(' (me)'));
if (escDisplayName.length)
editableText.value
= escDisplayName.substring(0, escDisplayName.indexOf(' (me)'));
editableText.setAttribute('style', 'display:none;');
editableText.setAttribute('placeholder', 'ex. Jane Pink');
......@@ -1301,16 +1290,16 @@ function showDisplayName(videoSpanId, displayName) {
var inputDisplayNameHandler = function(name) {
if (nickname != name) {
nickname = name;
nickname = Util.escapeHtml(name);
window.localStorage.displayname = nickname;
connection.emuc.addDisplayNameToPresence(nickname);
connection.emuc.sendPresence();
setChatConversationMode(true);
Chat.setChatConversationMode(true);
}
if (!$('#localDisplayName').is(":visible")) {
$('#localDisplayName').html(name + " (me)");
$('#localDisplayName').html(nickname + " (me)");
$('#localDisplayName').show();
$('#editDisplayName').hide();
}
......@@ -1338,12 +1327,3 @@ function createEditDisplayNameButton() {
return editButton;
}
function setChatConversationMode(isConversationMode) {
if (isConversationMode) {
$('#nickname').css({visibility:"hidden"});
$('#chatconversation').css({visibility:'visible'});
$('#usermsg').css({visibility:'visible'});
$('#usermsg').focus();
}
}
/**
* Chat related user interface.
*/
var Chat = (function (my) {
var notificationInterval = false;
var unreadMessages = 0;
/**
* Initializes chat related interface.
*/
my.init = function () {
var storedDisplayName = window.localStorage.displayname;
if (storedDisplayName) {
nickname = storedDisplayName;
Chat.setChatConversationMode(true);
}
$('#nickinput').keydown(function(event) {
if (event.keyCode == 13) {
event.preventDefault();
var val = Util.escapeHtml(this.value);
this.value = '';
if (!nickname) {
nickname = val;
window.localStorage.displayname = nickname;
connection.emuc.addDisplayNameToPresence(nickname);
connection.emuc.sendPresence();
Chat.setChatConversationMode(true);
return;
}
}
});
$('#usermsg').keydown(function(event) {
if (event.keyCode == 13) {
event.preventDefault();
var message = Util.escapeHtml(this.value);
$('#usermsg').val('').trigger('autosize.resize');
this.focus();
connection.emuc.sendMessage(message, nickname);
}
});
var onTextAreaResize = function() {
resizeChatConversation();
scrollChatToBottom();
};
$('#usermsg').autosize({callback: onTextAreaResize});
$("#chatspace").bind("shown",
function() {
unreadMessages = 0;
setVisualNotification(false);
});
};
/**
* Appends the given message to the chat conversation.
*/
my.updateChatConversation = function (from, displayName, message) {
var divClassName = '';
if (connection.emuc.myroomjid == from) {
divClassName = "localuser";
}
else {
divClassName = "remoteuser";
if (!$('#chatspace').is(":visible")) {
unreadMessages++;
Util.playSoundNotification('chatNotification');
setVisualNotification(true);
}
}
//replace links and smileys
var escMessage = Util.escapeHtml(message);
var escDisplayName = Util.escapeHtml(displayName);
message = processReplacements(escMessage);
$('#chatconversation').append('<div class="' + divClassName + '"><b>'
+ escDisplayName + ': </b>'
+ message + '</div>');
$('#chatconversation').animate(
{ scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
};
/**
* Opens / closes the chat area.
*/
my.toggleChat = function () {
var chatspace = $('#chatspace');
var videospace = $('#videospace');
var onShow = function () {
resizeLarge();
$('#chatspace').show("slide", { direction: "right", duration: 500});
};
var onHide = function () {
$('#chatspace').hide("slide", { direction: "right", duration: 500});
resizeLarge();
};
if (chatspace.is(":visible")) {
videospace.animate( {right: 0},
{queue: false,
duration: 500,
progress: onHide});
}
else {
videospace.animate({right: chatspace.width()},
{queue: false,
duration: 500,
progress: onShow,
complete: function() {
scrollChatToBottom();
chatspace.trigger('shown');
}
});
}
// Request the focus in the nickname field or the chat input field.
if ($('#nickname').css('visibility') == 'visible')
$('#nickinput').focus();
else {
$('#usermsg').focus();
}
};
/**
* Sets the chat conversation mode.
*/
my.setChatConversationMode = function (isConversationMode) {
if (isConversationMode) {
$('#nickname').css({visibility:"hidden"});
$('#chatconversation').css({visibility:'visible'});
$('#usermsg').css({visibility:'visible'});
$('#usermsg').focus();
}
};
/**
* Resizes the chat area.
*/
my.resizeChat = function () {
var availableHeight = window.innerHeight;
var availableWidth = window.innerWidth;
var chatWidth = 200;
if (availableWidth*0.2 < 200)
chatWidth = availableWidth*0.2;
$('#chatspace').width(chatWidth);
$('#chatspace').height(availableHeight - 40);
resizeChatConversation();
};
/**
* Resizes the chat conversation.
*/
function resizeChatConversation() {
var usermsgStyleHeight = document.getElementById("usermsg").style.height;
var usermsgHeight = usermsgStyleHeight
.substring(0, usermsgStyleHeight.indexOf('px'));
$('#chatconversation').width($('#chatspace').width() - 10);
$('#chatconversation')
.height(window.innerHeight - 50 - parseInt(usermsgHeight));
};
/**
* Shows/hides a visual notification, indicating that a message has arrived.
*/
function setVisualNotification(show) {
var unreadMsgElement = document.getElementById('unreadMessages');
if (unreadMessages) {
unreadMsgElement.innerHTML = unreadMessages.toString();
var chatButtonElement
= document.getElementById('chat').parentNode;
var leftIndent = (Util.getTextWidth(chatButtonElement)
- Util.getTextWidth(unreadMsgElement) - 5)/2;
var topIndent = (Util.getTextHeight(chatButtonElement)
- Util.getTextHeight(unreadMsgElement))/2 - 2;
unreadMsgElement.setAttribute(
'style',
'top:' + topIndent
+ '; left:' + leftIndent +';');
}
else
unreadMsgElement.innerHTML = '';
var glower = $('#chat');
if (show && !notificationInterval) {
notificationInterval = window.setInterval(function() {
glower.toggleClass('active');
}, 800);
}
else if (!show && notificationInterval) {
window.clearInterval(notificationInterval);
notificationInterval = false;
glower.removeClass('active');
}
}
/**
* Scrolls chat to the bottom.
*/
function scrollChatToBottom() {
setTimeout(function() {
$('#chatconversation').scrollTop(
$('#chatconversation')[0].scrollHeight);
}, 5);
}
return my;
}(Chat || {}));
\ No newline at end of file
<html>
<head>
<title></title>
<title>JitMeet: Unsupported Browser</title>
<link rel="stylesheet" type="text/css" media="screen" href="css/chromeonly.css" />
</head>
......@@ -10,8 +10,9 @@
<a href="http://google.com/chrome"><div id="left"></div></a>
<div id="middle"></div>
<div id="text">
<p>This service only works with Chrome.</p>
<p>This application is currently only supported by <a href="http://google.com/chrome">Chrome</a>, <a href="http://www.chromium.org/">Chromium</a> and <a href="http://www.opera.com">Opera</a></p>
<p><a href="http://google.com/chrome">Download Chrome</a></p>
<p class="firefox">We are hoping that <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=977864">multistream support</a> for Firefox would not be long so that we could all use this application with our favorite browser.</p>
</div>
<!-- wrap ends here -->
</div>
......
......@@ -10,7 +10,7 @@ body {
#wrap{
display: block;
position: absolute;
width:766px;
width:900px;
height: 262px;
overflow:hidden;
text-align: center;
......@@ -25,6 +25,10 @@ body {
height:262px;
float: left;
}
.firefox{
font-size: 11pt;
color: #c8c8c8;
}
#middle{
display:inline-block;
background-image:url(../images/chromepointer.png);
......@@ -35,8 +39,8 @@ body {
}
#text{
display:inline-block;
font-size: 20pt;
width: 400px;
font-size: 18pt;
width: 560px;
vertical-align:middle;
padding-top: 30px;
}
......
......@@ -3,7 +3,7 @@ html, body{
height:100%;
color: #424242;
font-family:'Helvetica Neue', Helvetica, sans-serif;
font-weight: 400;
font-weight: 400;
background: #e9e9e9;
overflow-x: hidden;
}
......@@ -35,6 +35,10 @@ html, body{
z-index: 0;
}
#etherpadButton {
display: none;
}
.videocontainer>span {
display: none; /* enable when you want nicks to be shown */
position: absolute;
......@@ -185,16 +189,16 @@ html, body{
}
#left {
display:block;
display:block;
position: absolute;
left: 0px;
left: 0px;
top: 0px;
width: 100px;
height: 39px;
background-image:url(../images/left1.png);
background-repeat:no-repeat;
margin: 0;
padding: 0;
background-image:url(../images/left1.png);
background-repeat:no-repeat;
margin: 0;
padding: 0;
}
#leftlogo {
......@@ -222,6 +226,11 @@ html, body{
visibility: hidden;
}
.toolbar_span {
display: inline-block;
position: relative;
}
.button {
display: inline-block;
position: relative;
......@@ -233,6 +242,30 @@ html, body{
font-size: 11pt;
text-align: center;
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
z-index: 1;
}
.toolbar_span>span {
display: inline-block;
position: absolute;
font-size: 7pt;
color: #ffffff;
text-align:center;
cursor: pointer;
}
#chat {
font-size:1.65em;
-webkit-transition: all .5s ease-in-out;;
-moz-transition: all .5s ease-in-out;;
transition: all .5s ease-in-out;;
}
#chat.active {
-webkit-text-shadow: 0 0 10px #ffffff;
-moz-text-shadow: 0 0 10px #ffffff;
text-shadow: 0 0 10px #ffffff;
/* -webkit-transform: scale(1.1); */
}
a.button:hover {
......@@ -266,15 +299,15 @@ a.button:hover {
}
#right {
display:block;
display:block;
position:absolute;
right: 0px;
top: 0px;
background-image:url(../images/right1.png);
background-repeat:no-repeat;
margin:0;
padding:0;
width:100px;
background-image:url(../images/right1.png);
background-repeat:no-repeat;
margin:0;
padding:0;
width:100px;
height:39px;
}
......
......@@ -15,15 +15,16 @@ var Etherpad = (function (my) {
if (!name) {
// In case we're the focus we generate the name.
etherpadName = Math.random().toString(36).substring(7) + '_' + (new Date().getTime()).toString();
etherpadName = Math.random().toString(36).substring(7)
+ '_' + (new Date().getTime()).toString();
shareEtherpad();
}
else
etherpadName = name;
createEtherpadButton();
enableEtherpadButton();
}
}
};
/**
* Opens/hides the Etherpad.
......@@ -76,26 +77,9 @@ var Etherpad = (function (my) {
/**
* Creates the Etherpad button and adds it to the toolbar.
*/
function createEtherpadButton() {
//<div class="header_button_separator"></div>
//<a class="button" onclick='Etherpad.openEtherpad("teeest");'>
//<i title="Open shared document" class="fa fa-file-text fa-lg"></i></a>
var separator = document.createElement('div');
separator.className = 'header_button_separator';
var button = document.createElement('a');
button.className = 'button';
button.setAttribute('onclick', 'Etherpad.toggleEtherpad(0);');
var buttonImage = document.createElement('i');
buttonImage.setAttribute('title', 'Open shared document');
buttonImage.className = 'fa fa-file-text fa-lg';
button.appendChild(buttonImage);
var toolbar = document.getElementById('toolbar');
toolbar.insertBefore(button, toolbar.childNodes[toolbar.childNodes.length - 4]);
toolbar.insertBefore(separator, button);
function enableEtherpadButton() {
if (!$('#etherpadButton').is(":visible"))
$('#etherpadButton').css({display:'inline-block'});
}
/**
......
......@@ -5,14 +5,16 @@
<script src="libs/strophejingle.bundle.js?v=7"></script><!-- strophe.jingle bundle -->
<script src="libs/colibri.js?v=7"></script><!-- colibri focus implementation -->
<script src="//code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<script src="muc.js?v=6"></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="app.js?v=20"></script><!-- application logic -->
<script src="etherpad.js?v=2"></script><!-- etherpad plugin -->
<script src="app.js?v=23"></script><!-- application logic -->
<script src="chat.js?v=3"></script><!-- chat logic -->
<script src="util.js?v=2"></script><!-- utility functions -->
<script src="etherpad.js?v=5"></script><!-- etherpad plugin -->
<script src="smileys.js?v=1"></script><!-- smiley images -->
<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 rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=17"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=19"/>
<link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
<link rel="stylesheet" href="css/modaldialog.css?v=3">
<script src="libs/jquery-impromptu.js"></script>
......@@ -25,17 +27,26 @@
<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>
<span id="toolbar">
<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>
<div class="header_button_separator"></div>
<a class="button" onclick='buttonClick("#video", "fa fa-video-camera fa-lg fa fa-video-camera no-fa-video-camera fa-lg");toggleVideo();'><i id="video" title="Start / stop camera" class="fa fa-video-camera fa-lg"></i></a>
<a class="button" onclick='buttonClick("#video", "fa fa-video-camera fa-lg fa fa-video-camera no-fa-video-camera fa-lg");toggleVideo();'>
<i id="video" title="Start / stop camera" class="fa fa-video-camera fa-lg"></i></a>
<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>
<div class="header_button_separator"></div>
<a class="button" onclick="openLinkDialog();"><i title="Invite others" class="fa fa-link fa-lg"></i></a>
<div class="header_button_separator"></div>
<a class="button" onclick='openChat();'><i id="chat" title="Open chat" class="fa fa-comments fa-lg"></i></a>
<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>
<span id="unreadMessages"></span>
</span>
<div class="header_button_separator"></div>
<a class="button" onclick='openPreziDialog();'><i title="Share prezi" class="fa fa-desktop fa-lg"></i></a>
<a class="button" onclick='openPreziDialog();'><i title="Share prezi" class="fa fa-picture-o fa-lg"></i></a>
<span id="etherpadButton">
<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>
</span>
<div class="header_button_separator"></div>
<a class="button" onclick='toggleFullScreen();'><i title="Enter / Exit Full Screen" class="fa fa-arrows-alt fa-lg"></i></a>
</span>
......@@ -65,6 +76,8 @@
<video id="localVideo" autoplay oncontextmenu="return false;" muted></video>
<span class="focusindicator"></span>
</span>
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
</div>
</div>
<div id="chatspace">
......@@ -74,9 +87,10 @@
<input type='text' id="nickinput" placeholder='Choose a nickname' autofocus>
</form>
</div>
<!--div><i class="fa fa-comments">&nbsp;</i><span class='nick'></span>:&nbsp;<span class='chattext'></span></div-->
<div id="chatconversation"></div>
<audio id="chatNotification" src="sounds/incomingMessage.wav" preload="auto"></audio>
<textarea id="usermsg" placeholder='Enter text...' autofocus></textarea>
</div>
<a id="downloadlog" onclick='dump(event.target);'><i title="Download support information" class="fa fa-cloud-download"></i></a>
......
......@@ -55,6 +55,8 @@ function ColibriFocus(connection, bridgejid) {
this.addssrc = [];
// ssrc lines to be removed on next update
this.removessrc = [];
// pending mute/unmute video op that modify local description
this.pendingop = null;
// container for candidates from the focus
// gathered before confid is known
......@@ -118,11 +120,7 @@ ColibriFocus.prototype.makeConference = function (peers) {
console.log('end of candidates');
return;
}
if (self.confid === 0) {
self.drip_container.push(event.candidate);
} else {
self.sendIceCandidate(event.candidate);
}
self.sendIceCandidate(event.candidate);
};
this._makeConference();
/*
......@@ -712,12 +710,21 @@ ColibriFocus.prototype.addIceCandidate = function (session, elem) {
// send our own candidate to the bridge
ColibriFocus.prototype.sendIceCandidate = function (candidate) {
var self = this;
//console.log('candidate', candidate);
if (!candidate) {
console.log('end of candidates');
return;
}
this.sendIceCandidates([candidate]);
if (this.drip_container.length === 0) {
// start 20ms callout
window.setTimeout(function () {
if (self.drip_container.length === 0) return;
self.sendIceCandidates(self.drip_container);
self.drip_container = [];
}, 20);
}
this.drip_container.push(candidate);
};
// sort and send multiple candidates
......@@ -802,8 +809,8 @@ ColibriFocus.prototype.terminate = function (session, reason) {
ColibriFocus.prototype.modifySources = function () {
var self = this;
if (!(this.addssrc.length || this.removessrc.length)) return;
if (this.peerconnection.signalingState == 'closed') return;
if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null)) return;
// FIXME: this is a big hack
// https://code.google.com/p/webrtc/issues/detail?id=2688
......@@ -844,6 +851,25 @@ ColibriFocus.prototype.modifySources = function () {
self.peerconnection.createAnswer(
function (modifiedAnswer) {
console.log('modifiedAnswer created');
// change video direction, see https://github.com/jitsi/jitmeet/issues/41
if (self.pendingop !== null) {
var sdp = new SDP(modifiedAnswer.sdp);
if (sdp.media.length > 1) {
switch(self.pendingop) {
case 'mute':
sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
break;
case 'unmute':
sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
break;
}
sdp.raw = sdp.session + sdp.media.join('');
modifiedAnswer.sdp = sdp.raw;
}
self.pendingop = null;
}
// FIXME: pushing down an answer while ice connection state
// is still checking is bad...
//console.log(self.peerconnection.iceConnectionState);
......@@ -900,6 +926,15 @@ ColibriFocus.prototype.modifySources = function () {
*/
};
ColibriFocus.prototype.hardMuteVideo = function (muted) {
this.pendingop = muted ? 'mute' : 'unmute';
this.modifySources();
this.connection.jingle.localStream.getVideoTracks().forEach(function (track) {
track.enabled = !muted;
});
};
// A colibri session is similar to a jingle session, it just implements some things differently
// FIXME: inherit jinglesession, see https://github.com/legastero/Jingle-RTCPeerConnection/blob/master/index.js
function ColibriSession(me, sid, connection) {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -139,11 +139,12 @@ Strophe.addConnectionPlugin('emuc', {
var txt = $(msg).find('>body').text();
// TODO: <subject/>
// FIXME: this is a hack. but jingle on muc makes nickchanges hard
var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(msg.getAttribute('from'));
var from = msg.getAttribute('from');
var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(from);
if (txt) {
console.log('chat', nick, txt);
updateChatConversation(nick, txt);
Chat.updateChatConversation(from, nick, txt);
}
return true;
},
......@@ -209,10 +210,12 @@ Strophe.addConnectionPlugin('emuc', {
}
});
if (sourceNumber > 0)
for (var i = 1; i <= sourceNumber/2; i ++) {
for (var i = 1; i <= sourceNumber/3; i ++) {
pres.c('source',
{type: this.presMap['source' + i + '_type'],
ssrc: this.presMap['source' + i + '_ssrc']}).up();
ssrc: this.presMap['source' + i + '_ssrc'],
direction: this.presMap['source'+ i + '_direction'] || 'sendrecv' }
).up();
}
}
pres.up();
......@@ -221,12 +224,13 @@ Strophe.addConnectionPlugin('emuc', {
addDisplayNameToPresence: function (displayName) {
this.presMap['displayName'] = displayName;
},
addMediaToPresence: function (sourceNumber, mtype, ssrcs) {
addMediaToPresence: function (sourceNumber, mtype, ssrcs, direction) {
if (!this.presMap['medians'])
this.presMap['medians'] = 'http://estos.de/ns/mjs';
this.presMap['source' + sourceNumber + '_type'] = mtype;
this.presMap['source' + sourceNumber + '_ssrc'] = ssrcs;
this.presMap['source' + sourceNumber + '_direction'] = direction;
},
addPreziToPresence: function (url, currentSlide) {
this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi';
......
/**
* Utility functions.
*/
var Util = (function (my) {
/**
* Returns the text width for the given element.
*
* @param el the element
*/
my.getTextWidth = function(el) {
return (el.clientWidth + 1);
};
/**
* Returns the text height for the given element.
*
* @param el the element
*/
my.getTextHeight = function(el) {
return (el.clientHeight + 1);
};
/**
* Casts the given number to integer.
*
* @param number the number to cast
*/
my.toInteger = function(number) {
return Math.round(Number(number));
};
/**
* Plays the sound given by id.
*
* @param id the identifier of the audio element.
*/
my.playSoundNotification = function(id) {
document.getElementById(id).play();
};
/**
* Escapes the given text.
*/
my.escapeHtml = function(unsafeText) {
return $('<div/>').text(unsafeText).html();
};
/**
* Indicates if the given string is an alphanumeric string.
* 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) {
var regex = /^[a-z0-9-_\/]+$/i;
return regex.test(unsafeText);
};
return my;
}(Util || {}));
\ No newline at end of file
......@@ -1069,8 +1069,12 @@ public class PluginImpl implements Plugin, PropertyEventListener
if (this.mediaStream != null)
{
mediaStream.removePropertyChangeListener(streamPropertyChangeListener);
recorder.done();
recorder = null;
if (recorder != null)
{
recorder.done();
recorder = null;
}
}
}
}
......@@ -1588,7 +1592,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
String recordingPath = JiveGlobals.getHomeDirectory() + File.separator + "resources" + File.separator + "spank" + File.separator + "rayo" + File.separator + "video_recordings";
String fileName = "audio-" + focusName + "-" + System.currentTimeMillis() + ".au";
boolean pcmu = format.getEncoding() == AudioFormat.ULAW;
int sampleRate = (int) Math.round(format.getSampleRate());
int sampleRate = (int) format.getSampleRate();
int channels = format.getChannels();
recorder = new Recorder(recordingPath, fileName, "au", true, sampleRate, channels);
......
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