Merge branch 'ssfocus'
This commit is contained in:
commit
4776605dec
232
app.js
232
app.js
|
@ -2,12 +2,11 @@
|
|||
/* application specific logic */
|
||||
var connection = null;
|
||||
var authenticatedUser = false;
|
||||
var focus = null;
|
||||
var activecall = null;
|
||||
var RTC = null;
|
||||
var nickname = null;
|
||||
var sharedKey = '';
|
||||
var recordingToken ='';
|
||||
var focusJid = null;
|
||||
var roomUrl = null;
|
||||
var roomName = null;
|
||||
var ssrc2jid = {};
|
||||
|
@ -41,6 +40,16 @@ var ssrc2videoType = {};
|
|||
*/
|
||||
var focusedVideoSrc = null;
|
||||
var mutedAudios = {};
|
||||
/**
|
||||
* Remembers if we were muted by the focus.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var forceMuted = false;
|
||||
/**
|
||||
* Indicates if we have muted our audio before the conference has started.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var preMuted = false;
|
||||
|
||||
var localVideoSrc = null;
|
||||
var flipXLocalVideo = true;
|
||||
|
@ -219,11 +228,9 @@ function maybeDoJoin() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function doJoin() {
|
||||
function generateRoomName() {
|
||||
var roomnode = null;
|
||||
var path = window.location.pathname;
|
||||
var roomjid;
|
||||
|
||||
// determinde the room node from the url
|
||||
// TODO: just the roomnode or the whole bare jid?
|
||||
|
@ -251,7 +258,20 @@ function doJoin() {
|
|||
}
|
||||
|
||||
roomName = roomnode + '@' + config.hosts.muc;
|
||||
}
|
||||
|
||||
function doJoin() {
|
||||
if (!roomName) {
|
||||
generateRoomName();
|
||||
}
|
||||
|
||||
Moderator.allocateConferenceFocus(
|
||||
roomName, doJoinAfterFocus);
|
||||
}
|
||||
|
||||
function doJoinAfterFocus() {
|
||||
|
||||
var roomjid;
|
||||
roomjid = roomName;
|
||||
|
||||
if (config.useNicks) {
|
||||
|
@ -410,7 +430,10 @@ function waitForPresence(data, sid) {
|
|||
container = document.getElementById(
|
||||
'participant_' + Strophe.getResourceFromJid(data.peerjid));
|
||||
} else {
|
||||
if (data.stream.id !== 'mixedmslabel') {
|
||||
if (data.stream.id !== 'mixedmslabel'
|
||||
// FIXME: default stream is added always with new focus
|
||||
// (to be investigated)
|
||||
&& data.stream.id !== 'default') {
|
||||
console.error('can not associate stream',
|
||||
data.stream.id,
|
||||
'with a participant');
|
||||
|
@ -638,16 +661,6 @@ $(document).bind('conferenceCreated.jingle', function (event, focus)
|
|||
}
|
||||
});
|
||||
|
||||
$(document).bind('callterminated.jingle', function (event, sid, jid, reason) {
|
||||
// Leave the room if my call has been remotely terminated.
|
||||
if (connection.emuc.joined && focus == null && reason === 'kick') {
|
||||
sessionTerminated = true;
|
||||
connection.emuc.doLeave();
|
||||
messageHandler.openMessageDialog("Session Terminated",
|
||||
"Ouch! You have been kicked out of the meet!");
|
||||
}
|
||||
});
|
||||
|
||||
$(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];
|
||||
|
@ -736,27 +749,6 @@ $(document).bind('joined.muc', function (event, jid, info) {
|
|||
document.createTextNode(Strophe.getResourceFromJid(jid) + ' (me)')
|
||||
);
|
||||
|
||||
if (Object.keys(connection.emuc.members).length < 1) {
|
||||
focus = new ColibriFocus(connection, config.hosts.bridge);
|
||||
if (nickname !== null) {
|
||||
focus.setEndpointDisplayName(connection.emuc.myroomjid,
|
||||
nickname);
|
||||
}
|
||||
Toolbar.showSipCallButton(true);
|
||||
Toolbar.showRecordingButton(false);
|
||||
}
|
||||
|
||||
if (!focus)
|
||||
{
|
||||
Toolbar.showSipCallButton(false);
|
||||
}
|
||||
|
||||
if (focus && config.etherpad_base) {
|
||||
Etherpad.init();
|
||||
}
|
||||
|
||||
VideoLayout.showFocusIndicator();
|
||||
|
||||
// Add myself to the contact list.
|
||||
ContactList.addContact(jid, SettingsMenu.getEmail() || SettingsMenu.getUID());
|
||||
|
||||
|
@ -777,7 +769,12 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
|
|||
'connected',
|
||||
'connected');
|
||||
|
||||
console.log('is focus? ' + (focus ? 'true' : 'false'));
|
||||
if (info.isFocus)
|
||||
{
|
||||
focusJid = jid;
|
||||
console.info("Ignore focus: " + jid +", real JID: " + info.jid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add Peer's container
|
||||
var id = $(pres).find('>userID').text();
|
||||
|
@ -792,7 +789,7 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
|
|||
APIConnector.triggerEvent("participantJoined",{jid: jid});
|
||||
}
|
||||
|
||||
if (focus !== null) {
|
||||
/*if (focus !== null) {
|
||||
// FIXME: this should prepare the video
|
||||
if (focus.confid === null) {
|
||||
console.log('make new conference with', jid);
|
||||
|
@ -807,7 +804,7 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
|
|||
console.log('invite', jid, 'into conference');
|
||||
focus.addNewParticipant(jid);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
});
|
||||
|
||||
$(document).bind('left.muc', function (event, jid) {
|
||||
|
@ -848,41 +845,6 @@ $(document).bind('left.muc', function (event, jid) {
|
|||
|
||||
connection.jingle.terminateByJid(jid);
|
||||
|
||||
if (focus == null
|
||||
// I shouldn't be the one that left to enter here.
|
||||
&& jid !== connection.emuc.myroomjid
|
||||
&& connection.emuc.myroomjid === connection.emuc.list_members[0]
|
||||
// If our session has been terminated for some reason
|
||||
// (kicked, hangup), don't try to become the focus
|
||||
&& !sessionTerminated) {
|
||||
console.log('welcome to our new focus... myself');
|
||||
focus = new ColibriFocus(connection, config.hosts.bridge);
|
||||
if (nickname !== null) {
|
||||
focus.setEndpointDisplayName(connection.emuc.myroomjid,
|
||||
nickname);
|
||||
}
|
||||
|
||||
Toolbar.showSipCallButton(true);
|
||||
|
||||
if (Object.keys(connection.emuc.members).length > 0) {
|
||||
focus.makeConference(Object.keys(connection.emuc.members));
|
||||
Toolbar.showRecordingButton(true);
|
||||
}
|
||||
$(document).trigger('focusechanged.muc', [focus]);
|
||||
}
|
||||
else if (focus && Object.keys(connection.emuc.members).length === 0) {
|
||||
console.log('everyone left');
|
||||
// FIXME: closing the connection is a hack to avoid some
|
||||
// problems with reinit
|
||||
disposeConference();
|
||||
focus = new ColibriFocus(connection, config.hosts.bridge);
|
||||
if (nickname !== null) {
|
||||
focus.setEndpointDisplayName(connection.emuc.myroomjid,
|
||||
nickname);
|
||||
}
|
||||
Toolbar.showSipCallButton(true);
|
||||
Toolbar.showRecordingButton(false);
|
||||
}
|
||||
if (connection.emuc.getPrezi(jid)) {
|
||||
$(document).trigger('presentationremoved.muc',
|
||||
[jid, connection.emuc.getPrezi(jid)]);
|
||||
|
@ -929,12 +891,16 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
|
|||
|
||||
if (displayName && displayName.length > 0)
|
||||
$(document).trigger('displaynamechanged',
|
||||
[jid, displayName]);
|
||||
|
||||
if (focus !== null && info.displayName !== null) {
|
||||
focus.setEndpointDisplayName(jid, info.displayName);
|
||||
[jid, info.displayName]);
|
||||
if (info.isFocus)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*if (focus !== null && info.displayName !== null) {
|
||||
focus.setEndpointDisplayName(jid, info.displayName);
|
||||
}*/
|
||||
|
||||
//check if the video bridge is available
|
||||
if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
|
||||
bridgeIsDown = true;
|
||||
|
@ -958,6 +924,17 @@ $(document).bind('presence.status.muc', function (event, jid, info, pres) {
|
|||
|
||||
});
|
||||
|
||||
$(document).bind('kicked.muc', function (event, jid) {
|
||||
console.info(jid + " has been kicked from MUC!");
|
||||
if (connection.emuc.myroomjid === jid) {
|
||||
sessionTerminated = true;
|
||||
disposeConference(false);
|
||||
connection.emuc.doLeave();
|
||||
messageHandler.openMessageDialog("Session Terminated",
|
||||
"Ouch! You have been kicked out of the meet!");
|
||||
}
|
||||
});
|
||||
|
||||
$(document).bind('passwordrequired.muc', function (event, jid) {
|
||||
console.log('on password required', jid);
|
||||
|
||||
|
@ -1046,7 +1023,7 @@ function isVideoSrcDesktop(jid) {
|
|||
}
|
||||
|
||||
function getConferenceHandler() {
|
||||
return focus ? focus : activecall;
|
||||
return activecall;
|
||||
}
|
||||
|
||||
function toggleVideo() {
|
||||
|
@ -1076,28 +1053,45 @@ function toggleVideo() {
|
|||
* Mutes / unmutes audio for the local participant.
|
||||
*/
|
||||
function toggleAudio() {
|
||||
setAudioMuted(!isAudioMuted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets muted audio state for the local participant.
|
||||
*/
|
||||
function setAudioMuted(mute) {
|
||||
if (!(connection && connection.jingle.localAudio)) {
|
||||
preMuted = mute;
|
||||
// We still click the button.
|
||||
buttonClick("#mute", "icon-microphone icon-mic-disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (forceMuted && !mute) {
|
||||
console.info("Asking focus for unmute");
|
||||
connection.moderate.setMute(connection.emuc.myroomjid, mute);
|
||||
// FIXME: wait for result before resetting muted status
|
||||
forceMuted = false;
|
||||
}
|
||||
|
||||
if (mute == isAudioMuted()) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
// It is not clear what is the right way to handle multiple tracks.
|
||||
// So at least make sure that they are all muted or all unmuted and
|
||||
// that we send presence just once.
|
||||
var localAudioTracks = connection.jingle.localAudio.getAudioTracks();
|
||||
if (localAudioTracks.length > 0) {
|
||||
var audioEnabled = localAudioTracks[0].enabled;
|
||||
|
||||
for (var idx = 0; idx < localAudioTracks.length; idx++) {
|
||||
localAudioTracks[idx].enabled = !audioEnabled;
|
||||
localAudioTracks[idx].enabled = !mute;
|
||||
}
|
||||
}
|
||||
|
||||
// isMuted is the opposite of audioEnabled
|
||||
connection.emuc.addAudioInfoToPresence(audioEnabled);
|
||||
connection.emuc.addAudioInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
VideoLayout.showLocalAudioIndicator(audioEnabled);
|
||||
}
|
||||
VideoLayout.showLocalAudioIndicator(mute);
|
||||
|
||||
buttonClick("#mute", "icon-microphone icon-mic-disabled");
|
||||
}
|
||||
|
@ -1118,51 +1112,7 @@ function isAudioMuted()
|
|||
|
||||
// Starts or stops the recording for the conference.
|
||||
function toggleRecording() {
|
||||
if (focus === null || focus.confid === null) {
|
||||
console.log('non-focus, or conference not yet organized: not enabling recording');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recordingToken)
|
||||
{
|
||||
messageHandler.openTwoButtonDialog(null,
|
||||
'<h2>Enter recording token</h2>' +
|
||||
'<input id="recordingToken" type="text" placeholder="token" autofocus>',
|
||||
false,
|
||||
"Save",
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var token = document.getElementById('recordingToken');
|
||||
|
||||
if (token.value) {
|
||||
setRecordingToken(Util.escapeHtml(token.value));
|
||||
toggleRecording();
|
||||
}
|
||||
}
|
||||
},
|
||||
function (event) {
|
||||
document.getElementById('recordingToken').focus();
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var oldState = focus.recordingEnabled;
|
||||
Toolbar.toggleRecordingButtonState();
|
||||
focus.setRecording(!oldState,
|
||||
recordingToken,
|
||||
function (state) {
|
||||
console.log("New recording state: ", state);
|
||||
if (state == oldState) //failed to change, reset the token because it might have been wrong
|
||||
{
|
||||
Toolbar.toggleRecordingButtonState();
|
||||
setRecordingToken(null);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Recording.toggleRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1380,6 +1330,8 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
Moderator.init();
|
||||
|
||||
// Set the defaults for prompt dialogs.
|
||||
jQuery.prompt.setDefaults({persistent: false});
|
||||
|
||||
|
@ -1495,7 +1447,6 @@ function disposeConference(onUnload) {
|
|||
if(onUnload) {
|
||||
stopLocalRtpStatsCollector();
|
||||
}
|
||||
focus = null;
|
||||
activecall = null;
|
||||
}
|
||||
|
||||
|
@ -1562,10 +1513,6 @@ function setSharedKey(sKey) {
|
|||
sharedKey = sKey;
|
||||
}
|
||||
|
||||
function setRecordingToken(token) {
|
||||
recordingToken = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the room invite url.
|
||||
*/
|
||||
|
@ -1585,10 +1532,12 @@ function updateRoomUrl(newRoomUrl) {
|
|||
* Warning to the user that the conference window is about to be closed.
|
||||
*/
|
||||
function closePageWarning() {
|
||||
/*
|
||||
FIXME: do we need a warning when the focus is a server-side one now ?
|
||||
if (focus !== null)
|
||||
return "You are the owner of this conference call and"
|
||||
+ " you are about to end it.";
|
||||
else
|
||||
else*/
|
||||
return "You are about to leave this conversation.";
|
||||
}
|
||||
|
||||
|
@ -1626,6 +1575,13 @@ function setView(viewName) {
|
|||
// }
|
||||
}
|
||||
|
||||
$(document).bind('error.jingle',
|
||||
function (event, session, error)
|
||||
{
|
||||
console.error("Jingle error", error);
|
||||
}
|
||||
);
|
||||
|
||||
$(document).bind('fatalError.jingle',
|
||||
function (event, session, error)
|
||||
{
|
||||
|
|
|
@ -4,7 +4,8 @@ var config = {
|
|||
//anonymousdomain: 'guest.example.com',
|
||||
muc: 'conference.jitsi-meet.example.com', // FIXME: use XEP-0030
|
||||
bridge: 'jitsi-videobridge.jitsi-meet.example.com', // FIXME: use XEP-0030
|
||||
//call_control: 'callcontrol.jitsi-meet.example.com'
|
||||
//call_control: 'callcontrol.jitsi-meet.example.com',
|
||||
focus: 'focus.jitsi-meet.example.com'
|
||||
},
|
||||
// getroomnode: function (path) { return 'someprefixpossiblybasedonpath'; },
|
||||
// useStunTurn: true, // use XEP-0215 to fetch STUN and TURN server
|
||||
|
@ -12,6 +13,7 @@ var config = {
|
|||
useNicks: false,
|
||||
bosh: '//jitsi-meet.example.com/http-bind', // FIXME: use xep-0156 for that
|
||||
clientNode: 'http://jitsi.org/jitsimeet', // The name of client node advertised in XEP-0115 'c' stanza
|
||||
focusUserJid: 'focus@auth.jitsi-meet.example.com', // The real JID of focus participant
|
||||
//defaultSipNumber: '', // Default SIP number
|
||||
desktopSharing: 'ext', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
|
||||
chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
|
||||
|
@ -27,6 +29,7 @@ var config = {
|
|||
enableRecording: false,
|
||||
enableWelcomePage: true,
|
||||
enableSimulcast: false,
|
||||
enableFirefoxSupport: false //firefox support is still experimental, only one-to-one conferences with chrome focus
|
||||
enableFirefoxSupport: false, //firefox support is still experimental, only one-to-one conferences with chrome focus
|
||||
// will work when simulcast, bundle, mux, lastN and SCTP are disabled.
|
||||
logStats: false // Enable logging of PeerConnection stats via the focus
|
||||
};
|
||||
|
|
|
@ -23,6 +23,8 @@ case "$1" in
|
|||
|
||||
. /etc/jitsi/videobridge/config
|
||||
|
||||
. /etc/jitsi/jicofo/config
|
||||
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
|
@ -41,9 +43,25 @@ case "$1" in
|
|||
cp /usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example $PROSODY_HOST_CONFIG
|
||||
sed -i "s/jitmeet.example.com/$JVB_HOSTNAME/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/jitmeetSecret/$JVB_SECRET/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/focusSecret/$JICOFO_SECRET/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/focusUser/$JICOFO_AUTH_USER/g" $PROSODY_HOST_CONFIG
|
||||
if [ ! -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua ]; then
|
||||
ln -s $PROSODY_HOST_CONFIG /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
|
||||
fi
|
||||
# create 'focus@auth.domain' prosody user
|
||||
# FIXME this duplicates with below
|
||||
prosodyctl register $JICOFO_AUTH_USER $JICOFO_AUTH_DOMAIN $JICOFO_AUTH_PASSWORD
|
||||
fi
|
||||
# on UPGRADE to server side focus check if focus is configured
|
||||
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"auth.$JVB_HOSTNAME\"" $PROSODY_HOST_CONFIG; then
|
||||
echo -e "\nVirtualHost \"auth.$JVB_HOSTNAME\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " authentication = \"internal_plain\"\n" >> $PROSODY_HOST_CONFIG
|
||||
echo -e "admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n" >> $PROSODY_HOST_CONFIG
|
||||
echo -e "Component \"focus.$JVB_HOSTNAME\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " component_secret=\"$JICOFO_SECRET\"\n" >> $PROSODY_HOST_CONFIG
|
||||
# create 'focus@auth.domain' prosody user
|
||||
# FIXME this duplicates with above
|
||||
prosodyctl register $JICOFO_AUTH_USER $JICOFO_AUTH_DOMAIN $JICOFO_AUTH_PASSWORD
|
||||
fi
|
||||
|
||||
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
|
||||
|
@ -60,6 +78,7 @@ case "$1" in
|
|||
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
|
||||
invoke-rc.d prosody restart
|
||||
invoke-rc.d jitsi-videobridge restart
|
||||
invoke-rc.d jicofo restart
|
||||
fi
|
||||
;;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global $, config, connection, chrome, alert, getUserMediaWithConstraints, changeLocalVideo, getConferenceHandler */
|
||||
/* global $, alert, changeLocalVideo, chrome, config, connection, getConferenceHandler, getUserMediaWithConstraints, VideoLayout */
|
||||
/**
|
||||
* Indicates that desktop stream is currently in use(for toggle purpose).
|
||||
* @type {boolean}
|
||||
|
@ -283,9 +283,9 @@ function toggleScreenSharing() {
|
|||
}
|
||||
switchInProgress = true;
|
||||
|
||||
// Only the focus is able to set a shared key.
|
||||
if (!isUsingScreenStream)
|
||||
{
|
||||
// Switch to desktop stream
|
||||
obtainDesktopStream(
|
||||
function (stream) {
|
||||
// We now use screen stream
|
||||
|
|
|
@ -19,3 +19,11 @@ Component "conference.jitmeet.example.com" "muc"
|
|||
|
||||
Component "jitsi-videobridge.jitmeet.example.com"
|
||||
component_secret = "jitmeetSecret"
|
||||
|
||||
VirtualHost "auth.jitmeet.example.com"
|
||||
authentication = "internal_plain"
|
||||
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
|
||||
Component "focus.jitmeet.example.com"
|
||||
component_secret = "focusSecret"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* global $, config, Prezi, Util, connection, setLargeVideoVisible, dockToolbar */
|
||||
/* global $, config, connection, dockToolbar, Moderator, Prezi,
|
||||
setLargeVideoVisible, ToolbarToggler, Util, VideoLayout */
|
||||
var Etherpad = (function (my) {
|
||||
var etherpadName = null;
|
||||
var etherpadIFrame = null;
|
||||
|
@ -161,7 +162,7 @@ var Etherpad = (function (my) {
|
|||
*/
|
||||
$(document).bind('etherpadadded.muc', function (event, jid, etherpadName) {
|
||||
console.log("Etherpad added", etherpadName);
|
||||
if (config.etherpad_base && !focus) {
|
||||
if (config.etherpad_base && !Moderator.isModerator()) {
|
||||
Etherpad.init(etherpadName);
|
||||
}
|
||||
});
|
||||
|
@ -169,6 +170,7 @@ var Etherpad = (function (my) {
|
|||
/**
|
||||
* On focus changed event.
|
||||
*/
|
||||
// FIXME: there is no such event as 'focusechanged.muc'
|
||||
$(document).bind('focusechanged.muc', function (event, focus) {
|
||||
console.log("Focus changed");
|
||||
if (config.etherpad_base)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<script src="libs/rayo.js?v=1"></script>
|
||||
<script src="libs/tooltip.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||
<script src="libs/pako.bundle.js?v=1"></script><!-- zlib deflate -->
|
||||
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
|
||||
<script src="interface_config.js?v=4"></script>
|
||||
<script src="muc.js?v=17"></script><!-- simple MUC library -->
|
||||
|
@ -56,8 +57,10 @@
|
|||
<script src="audio_levels.js?v=2"></script><!-- audio levels plugin -->
|
||||
<script src="media_stream.js?v=2"></script><!-- media stream -->
|
||||
<script src="bottom_toolbar.js?v=6"></script><!-- media stream -->
|
||||
<script src="moderator.js?v=1"></script><!-- media stream -->
|
||||
<script src="roomname_generator.js?v=1"></script><!-- generator for random room names -->
|
||||
<script src="keyboard_shortcut.js?v=3"></script>
|
||||
<script src="recording.js?v=1"></script>
|
||||
<script src="tracking.js?v=1"></script><!-- tracking -->
|
||||
<script src="jitsipopover.js?v=3"></script>
|
||||
<script src="message_handler.js?v=2"></script>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -372,6 +372,15 @@ TraceablePeerConnection.prototype.modifySources = function(successCallback) {
|
|||
});
|
||||
this.removessrc = [];
|
||||
|
||||
// FIXME:
|
||||
// this was a hack for the situation when only one peer exists
|
||||
// in the conference.
|
||||
// check if still required and remove
|
||||
if (sdp.media[0])
|
||||
sdp.media[0] = sdp.media[0].replace('a=recvonly', 'a=sendrecv');
|
||||
if (sdp.media[1])
|
||||
sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
|
||||
|
||||
sdp.raw = sdp.session + sdp.media.join('');
|
||||
this.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}),
|
||||
function() {
|
||||
|
|
|
@ -30,8 +30,12 @@ Strophe.addConnectionPlugin('jingle', {
|
|||
// this is dealt with by SDP O/A so we don't need to annouce this
|
||||
//this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
|
||||
//this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
|
||||
if (config.useRtcpMux) {
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
|
||||
//this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
|
||||
}
|
||||
if (config.useBundle) {
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
|
||||
}
|
||||
//this.connection.disco.addFeature('urn:ietf:rfc:5576'); // a=ssrc
|
||||
}
|
||||
this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
|
||||
|
|
|
@ -131,22 +131,6 @@ JingleSession.prototype.accept = function () {
|
|||
responder: this.responder,
|
||||
sid: this.sid });
|
||||
prsdp.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
|
||||
this.connection.sendIQ(accept,
|
||||
function () {
|
||||
var ack = {};
|
||||
ack.source = 'answer';
|
||||
$(document).trigger('ack.jingle', [self.sid, ack]);
|
||||
},
|
||||
function (stanza) {
|
||||
var error = ($(stanza).find('error').length) ? {
|
||||
code: $(stanza).find('error').attr('code'),
|
||||
reason: $(stanza).find('error :first')[0].tagName,
|
||||
}:{};
|
||||
error.source = 'answer';
|
||||
$(document).trigger('error.jingle', [self.sid, error]);
|
||||
},
|
||||
10000);
|
||||
|
||||
var sdp = this.peerconnection.localDescription.sdp;
|
||||
while (SDPUtil.find_line(sdp, 'a=inactive')) {
|
||||
// FIXME: change any inactive to sendrecv or whatever they were originally
|
||||
|
@ -156,6 +140,22 @@ JingleSession.prototype.accept = function () {
|
|||
function () {
|
||||
//console.log('setLocalDescription success');
|
||||
$(document).trigger('setLocalDescription.jingle', [self.sid]);
|
||||
|
||||
this.connection.sendIQ(accept,
|
||||
function () {
|
||||
var ack = {};
|
||||
ack.source = 'answer';
|
||||
$(document).trigger('ack.jingle', [self.sid, ack]);
|
||||
},
|
||||
function (stanza) {
|
||||
var error = ($(stanza).find('error').length) ? {
|
||||
code: $(stanza).find('error').attr('code'),
|
||||
reason: $(stanza).find('error :first')[0].tagName
|
||||
}:{};
|
||||
error.source = 'answer';
|
||||
$(document).trigger('error.jingle', [self.sid, error]);
|
||||
},
|
||||
10000);
|
||||
},
|
||||
function (e) {
|
||||
console.error('setLocalDescription failed', e);
|
||||
|
|
|
@ -105,11 +105,11 @@ SessionBase.prototype.switchStreams = function (new_stream, oldStream, success_c
|
|||
self.modifySources(function() {
|
||||
console.log('modify sources done');
|
||||
|
||||
success_callback();
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", oldSdp, newSdp);
|
||||
self.notifyMySSRCUpdate(oldSdp, newSdp);
|
||||
|
||||
success_callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
/* global $, $iq, config, connection, focusJid, forceMuted, messageHandler,
|
||||
setAudioMuted, Strophe, toggleAudio */
|
||||
/**
|
||||
* Moderate connection plugin.
|
||||
*/
|
||||
Strophe.addConnectionPlugin('moderate', {
|
||||
connection: null,
|
||||
roomjid: null,
|
||||
myroomjid: null,
|
||||
members: {},
|
||||
list_members: [], // so we can elect a new focus
|
||||
presMap: {},
|
||||
preziMap: {},
|
||||
joined: false,
|
||||
isOwner: false,
|
||||
init: function (conn) {
|
||||
this.connection = conn;
|
||||
|
||||
|
@ -22,32 +16,45 @@ Strophe.addConnectionPlugin('moderate', {
|
|||
null);
|
||||
},
|
||||
setMute: function (jid, mute) {
|
||||
var iq = $iq({to: jid, type: 'set'})
|
||||
.c('mute', {xmlns: 'http://jitsi.org/jitmeet/audio'})
|
||||
console.info("set mute", mute);
|
||||
var iqToFocus = $iq({to: focusJid, type: 'set'})
|
||||
.c('mute', {
|
||||
xmlns: 'http://jitsi.org/jitmeet/audio',
|
||||
jid: jid
|
||||
})
|
||||
.t(mute.toString())
|
||||
.up();
|
||||
|
||||
this.connection.sendIQ(
|
||||
iq,
|
||||
iqToFocus,
|
||||
function (result) {
|
||||
console.log('set mute', result);
|
||||
},
|
||||
function (error) {
|
||||
console.log('set mute error', error);
|
||||
messageHandler.openReportDialog(null, 'Failed to mute ' +
|
||||
$("#participant_" + jid).find(".displayname").text() ||
|
||||
"participant" + '.', error);
|
||||
// FIXME: this causes an exception
|
||||
//messageHandler.openReportDialog(null, 'Failed to mute ' +
|
||||
// $("#participant_" + jid).find(".displayname").text() ||
|
||||
//"participant" + '.', error);
|
||||
});
|
||||
},
|
||||
onMute: function (iq) {
|
||||
var from = iq.getAttribute('from');
|
||||
if (from !== focusJid) {
|
||||
console.warn("Ignored mute from non focus peer");
|
||||
return false;
|
||||
}
|
||||
var mute = $(iq).find('mute');
|
||||
if (mute.length) {
|
||||
toggleAudio();
|
||||
var doMuteAudio = mute.text() === "true";
|
||||
setAudioMuted(doMuteAudio);
|
||||
forceMuted = doMuteAudio;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
eject: function (jid) {
|
||||
connection.jingle.terminateRemoteByJid(jid, 'kick');
|
||||
// We're not the focus, so can't terminate
|
||||
//connection.jingle.terminateRemoteByJid(jid, 'kick');
|
||||
connection.emuc.kick(jid);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,141 @@
|
|||
/* global $, $iq, config, connection, Etherpad, hangUp, roomName, Strophe,
|
||||
Toolbar, Util, VideoLayout */
|
||||
/**
|
||||
* Contains logic responsible for enabling/disabling functionality available
|
||||
* only to moderator users.
|
||||
*/
|
||||
var Moderator = (function (my) {
|
||||
|
||||
var getNextTimeout = Util.createExpBackoffTimer(1000);
|
||||
var getNextErrorTimeout = Util.createExpBackoffTimer(1000);
|
||||
|
||||
my.isModerator = function () {
|
||||
return connection.emuc.isModerator();
|
||||
};
|
||||
|
||||
my.onModeratorStatusChanged = function (isModerator) {
|
||||
|
||||
Toolbar.showSipCallButton(isModerator);
|
||||
Toolbar.showRecordingButton(
|
||||
isModerator); //&&
|
||||
// FIXME:
|
||||
// Recording visible if
|
||||
// there are at least 2(+ 1 focus) participants
|
||||
//Object.keys(connection.emuc.members).length >= 3);
|
||||
|
||||
if (isModerator && config.etherpad_base) {
|
||||
Etherpad.init();
|
||||
}
|
||||
|
||||
$(document).trigger('local.role.moderator', [isModerator]);
|
||||
};
|
||||
|
||||
my.init = function () {
|
||||
$(document).bind(
|
||||
'role.changed.muc',
|
||||
function (event, jid, info, pres) {
|
||||
console.info(
|
||||
"Role changed for " + jid + ", new role: " + info.role);
|
||||
VideoLayout.showModeratorIndicator();
|
||||
}
|
||||
);
|
||||
|
||||
$(document).bind(
|
||||
'local.role.changed.muc',
|
||||
function (event, jid, info, pres) {
|
||||
console.info("My role changed, new role: " + info.role);
|
||||
VideoLayout.showModeratorIndicator();
|
||||
Moderator.onModeratorStatusChanged(Moderator.isModerator());
|
||||
}
|
||||
);
|
||||
|
||||
$(document).bind(
|
||||
'left.muc',
|
||||
function (event, jid) {
|
||||
console.info("Someone left is it focus ? " + jid);
|
||||
var resource = Strophe.getResourceFromJid(jid);
|
||||
if (resource === 'focus') {
|
||||
console.info(
|
||||
"Focus has left the room - leaving conference");
|
||||
//hangUp();
|
||||
// We'd rather reload to have everything re-initialized
|
||||
// FIXME: show some message before reload
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
my.createConferenceIq = function () {
|
||||
var elem = $iq({to: config.hosts.focus, type: 'set'});
|
||||
elem.c('conference', {
|
||||
xmlns: 'http://jitsi.org/protocol/focus',
|
||||
room: roomName
|
||||
});
|
||||
if (config.channelLastN !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'channelLastN', value: config.channelLastN})
|
||||
.up();
|
||||
}
|
||||
if (config.adaptiveLastN !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'adaptiveLastN', value: config.adaptiveLastN})
|
||||
.up();
|
||||
}
|
||||
if (config.adaptiveSimulcast !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'adaptiveSimulcast', value: config.adaptiveSimulcast})
|
||||
.up();
|
||||
}
|
||||
elem.up();
|
||||
return elem;
|
||||
};
|
||||
|
||||
// FIXME: we need to show the fact that we're waiting for the focus
|
||||
// to the user(or that focus is not available)
|
||||
my.allocateConferenceFocus = function (roomName, callback) {
|
||||
var iq = Moderator.createConferenceIq();
|
||||
connection.sendIQ(
|
||||
iq,
|
||||
function (result) {
|
||||
if ('true' === $(result).find('conference').attr('ready')) {
|
||||
// Reset both timers
|
||||
getNextTimeout(true);
|
||||
getNextErrorTimeout(true);
|
||||
callback();
|
||||
} else {
|
||||
var waitMs = getNextTimeout();
|
||||
console.info("Waiting for the focus... " + waitMs);
|
||||
// Reset error timeout
|
||||
getNextErrorTimeout(true);
|
||||
window.setTimeout(
|
||||
function () {
|
||||
Moderator.allocateConferenceFocus(
|
||||
roomName, callback);
|
||||
}, waitMs);
|
||||
}
|
||||
},
|
||||
function (error) {
|
||||
var waitMs = getNextErrorTimeout();
|
||||
console.error("Focus error, retry after " + waitMs, error);
|
||||
// Reset response timeout
|
||||
getNextTimeout(true);
|
||||
window.setTimeout(
|
||||
function () {
|
||||
Moderator.allocateConferenceFocus(roomName, callback);
|
||||
}, waitMs);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return my;
|
||||
}(Moderator || {}));
|
||||
|
||||
|
||||
|
27
muc.js
27
muc.js
|
@ -12,6 +12,7 @@ Strophe.addConnectionPlugin('emuc', {
|
|||
preziMap: {},
|
||||
joined: false,
|
||||
isOwner: false,
|
||||
role: null,
|
||||
init: function (conn) {
|
||||
this.connection = conn;
|
||||
},
|
||||
|
@ -122,11 +123,22 @@ Strophe.addConnectionPlugin('emuc', {
|
|||
member.affiliation = tmp.attr('affiliation');
|
||||
member.role = tmp.attr('role');
|
||||
|
||||
// Focus recognition
|
||||
member.jid = tmp.attr('jid');
|
||||
member.isFocus = false;
|
||||
if (member.jid && member.jid.indexOf(config.focusUserJid + "/") == 0) {
|
||||
member.isFocus = true;
|
||||
}
|
||||
|
||||
var nicktag = $(pres).find('>nick[xmlns="http://jabber.org/protocol/nick"]');
|
||||
member.displayName = (nicktag.length > 0 ? nicktag.text() : null);
|
||||
|
||||
if (from == this.myroomjid) {
|
||||
if (member.affiliation == 'owner') this.isOwner = true;
|
||||
if (this.role !== member.role) {
|
||||
this.role = member.role;
|
||||
$(document).trigger('local.role.changed.muc', [from, member, pres]);
|
||||
}
|
||||
if (!this.joined) {
|
||||
this.joined = true;
|
||||
$(document).trigger('joined.muc', [from, member]);
|
||||
|
@ -137,9 +149,16 @@ Strophe.addConnectionPlugin('emuc', {
|
|||
this.members[from] = member;
|
||||
this.list_members.push(from);
|
||||
$(document).trigger('entered.muc', [from, member, pres]);
|
||||
} else {
|
||||
// Presence update for existing participant
|
||||
// Watch role change:
|
||||
if (this.members[from].role != member.role) {
|
||||
this.members[from].role = member.role
|
||||
$(document).trigger('role.changed.muc', [from, member, pres]);
|
||||
}
|
||||
}
|
||||
// Always trigger presence to update bindings
|
||||
console.log('presence change from', from);
|
||||
console.log('presence change from', from, pres);
|
||||
$(document).trigger('presence.muc', [from, member, pres]);
|
||||
|
||||
// Trigger status message update
|
||||
|
@ -167,6 +186,9 @@ Strophe.addConnectionPlugin('emuc', {
|
|||
$(document).trigger('left.muc', member);
|
||||
}
|
||||
}
|
||||
if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length) {
|
||||
$(document).trigger('kicked.muc', [from]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onPresenceError: function (pres) {
|
||||
|
@ -470,5 +492,8 @@ Strophe.addConnectionPlugin('emuc', {
|
|||
},
|
||||
addUserIdToPresence: function(userId) {
|
||||
this.presMap['userId'] = userId;
|
||||
},
|
||||
isModerator: function() {
|
||||
return this.role === 'moderator';
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* global $, $iq, config, connection, focusJid, messageHandler, Moderator,
|
||||
Toolbar, Util */
|
||||
var Recording = (function (my) {
|
||||
var recordingToken = null;
|
||||
var recordingEnabled;
|
||||
|
||||
my.setRecordingToken = function (token) {
|
||||
recordingToken = token;
|
||||
};
|
||||
|
||||
// Sends a COLIBRI message which enables or disables (according to 'state')
|
||||
// the recording on the bridge. Waits for the result IQ and calls 'callback'
|
||||
// with the new recording state, according to the IQ.
|
||||
my.setRecording = function (state, token, callback) {
|
||||
var self = this;
|
||||
var elem = $iq({to: focusJid, type: 'set'});
|
||||
elem.c('conference', {
|
||||
xmlns: 'http://jitsi.org/protocol/colibri'
|
||||
});
|
||||
elem.c('recording', {state: state, token: token});
|
||||
elem.up();
|
||||
|
||||
connection.sendIQ(elem,
|
||||
function (result) {
|
||||
console.log('Set recording "', state, '". Result:', result);
|
||||
var recordingElem = $(result).find('>conference>recording');
|
||||
var newState = ('true' === recordingElem.attr('state'));
|
||||
|
||||
recordingEnabled = newState;
|
||||
callback(newState);
|
||||
},
|
||||
function (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
my.toggleRecording = function () {
|
||||
if (!Moderator.isModerator()) {
|
||||
console.log(
|
||||
'non-focus, or conference not yet organized:' +
|
||||
' not enabling recording');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recordingToken)
|
||||
{
|
||||
messageHandler.openTwoButtonDialog(null,
|
||||
'<h2>Enter recording token</h2>' +
|
||||
'<input id="recordingToken" type="text" placeholder="token" autofocus>',
|
||||
false,
|
||||
"Save",
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var token = document.getElementById('recordingToken');
|
||||
|
||||
if (token.value) {
|
||||
my.setRecordingToken(
|
||||
Util.escapeHtml(token.value));
|
||||
my.toggleRecording();
|
||||
}
|
||||
}
|
||||
},
|
||||
function (event) {
|
||||
document.getElementById('recordingToken').focus();
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var oldState = recordingEnabled;
|
||||
Toolbar.setRecordingButtonState(!oldState);
|
||||
my.setRecording(!oldState,
|
||||
recordingToken,
|
||||
function (state) {
|
||||
console.log("New recording state: ", state);
|
||||
if (state === oldState)
|
||||
{
|
||||
// FIXME: new focus:
|
||||
// this will not work when moderator changes
|
||||
// during active session. Then it will assume that
|
||||
// recording status has changed to true, but it might have
|
||||
// been already true(and we only received actual status from
|
||||
// the focus).
|
||||
//
|
||||
// SO we start with status null, so that it is initialized
|
||||
// here and will fail only after second click, so if invalid
|
||||
// token was used we have to press the button twice before
|
||||
// current status will be fetched and token will be reset.
|
||||
//
|
||||
// Reliable way would be to return authentication error.
|
||||
// Or status update when moderator connects.
|
||||
// Or we have to stop recording session when current
|
||||
// moderator leaves the room.
|
||||
|
||||
// Failed to change, reset the token because it might
|
||||
// have been wrong
|
||||
my.setRecordingToken(null);
|
||||
}
|
||||
// Update with returned status
|
||||
Toolbar.setRecordingButtonState(state);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return my;
|
||||
}(Recording || {}));
|
112
rtp_sts.js
112
rtp_sts.js
|
@ -1,4 +1,5 @@
|
|||
/* global ssrc2jid */
|
||||
/* jshint -W117 */
|
||||
/**
|
||||
* Calculates packet lost percent using the number of lost packets and the
|
||||
* number of all packet.
|
||||
|
@ -133,6 +134,37 @@ function StatsCollector(peerconnection, audioLevelsInterval,
|
|||
this.currentStatsReport = null;
|
||||
this.baselineStatsReport = null;
|
||||
this.audioLevelsIntervalId = null;
|
||||
|
||||
/**
|
||||
* Gather PeerConnection stats once every this many milliseconds.
|
||||
*/
|
||||
this.GATHER_INTERVAL = 10000;
|
||||
|
||||
/**
|
||||
* Log stats via the focus once every this many milliseconds.
|
||||
*/
|
||||
this.LOG_INTERVAL = 60000;
|
||||
|
||||
/**
|
||||
* Gather stats and store them in this.statsToBeLogged.
|
||||
*/
|
||||
this.gatherStatsIntervalId = null;
|
||||
|
||||
/**
|
||||
* Send the stats already saved in this.statsToBeLogged to be logged via
|
||||
* the focus.
|
||||
*/
|
||||
this.logStatsIntervalId = null;
|
||||
|
||||
/**
|
||||
* Stores the statistics which will be send to the focus to be logged.
|
||||
*/
|
||||
this.statsToBeLogged =
|
||||
{
|
||||
timestamps: [],
|
||||
stats: {}
|
||||
};
|
||||
|
||||
// Updates stats interval
|
||||
this.audioLevelsIntervalMilis = audioLevelsInterval;
|
||||
|
||||
|
@ -156,6 +188,10 @@ StatsCollector.prototype.stop = function ()
|
|||
this.audioLevelsIntervalId = null;
|
||||
clearInterval(this.statsIntervalId);
|
||||
this.statsIntervalId = null;
|
||||
clearInterval(this.logStatsIntervalId);
|
||||
this.logStatsIntervalId = null;
|
||||
clearInterval(this.gatherStatsIntervalId);
|
||||
this.gatherStatsIntervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -238,8 +274,84 @@ StatsCollector.prototype.start = function ()
|
|||
},
|
||||
self.statsIntervalMilis
|
||||
);
|
||||
|
||||
if (config.logStats) {
|
||||
this.gatherStatsIntervalId = setInterval(
|
||||
function () {
|
||||
self.peerconnection.getStats(
|
||||
function (report) {
|
||||
self.addStatsToBeLogged(report.result());
|
||||
},
|
||||
function () {
|
||||
}
|
||||
);
|
||||
},
|
||||
this.GATHER_INTERVAL
|
||||
);
|
||||
|
||||
this.logStatsIntervalId = setInterval(
|
||||
function() { self.logStats(); },
|
||||
this.LOG_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the stats to the format used for logging, and saves the data in
|
||||
* this.statsToBeLogged.
|
||||
* @param reports Reports as given by webkitRTCPerConnection.getStats.
|
||||
*/
|
||||
StatsCollector.prototype.addStatsToBeLogged = function (reports) {
|
||||
var self = this;
|
||||
var num_records = this.statsToBeLogged.timestamps.length;
|
||||
this.statsToBeLogged.timestamps.push(new Date().getTime());
|
||||
reports.map(function (report) {
|
||||
var stat = self.statsToBeLogged.stats[report.id];
|
||||
if (!stat) {
|
||||
stat = self.statsToBeLogged.stats[report.id] = {};
|
||||
}
|
||||
stat.type = report.type;
|
||||
report.names().map(function (name) {
|
||||
var values = stat[name];
|
||||
if (!values) {
|
||||
values = stat[name] = [];
|
||||
}
|
||||
while (values.length < num_records) {
|
||||
values.push(null);
|
||||
}
|
||||
values.push(report.stat(name));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
StatsCollector.prototype.logStats = function () {
|
||||
if (!focusJid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var deflate = true;
|
||||
|
||||
var content = JSON.stringify(this.statsToBeLogged);
|
||||
if (deflate) {
|
||||
content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
|
||||
}
|
||||
content = Base64.encode(content);
|
||||
|
||||
// XEP-0337-ish
|
||||
var message = $msg({to: focusJid, type: 'normal'});
|
||||
message.c('log', { xmlns: 'urn:xmpp:eventlog',
|
||||
id: 'PeerConnectionStats'});
|
||||
message.c('message').t(content).up();
|
||||
if (deflate) {
|
||||
message.c('tag', {name: "deflated", value: "true"}).up();
|
||||
}
|
||||
message.up();
|
||||
|
||||
connection.send(message);
|
||||
|
||||
// Reset the stats
|
||||
this.statsToBeLogged.stats = {};
|
||||
this.statsToBeLogged.timestamps = [];
|
||||
};
|
||||
var keyMap = {
|
||||
"firefox": {
|
||||
"ssrc": "ssrc",
|
||||
|
|
30
toolbar.js
30
toolbar.js
|
@ -1,3 +1,5 @@
|
|||
/* global $, buttonClick, config, lockRoom, messageHandler, Moderator, roomUrl,
|
||||
setSharedKey, sharedKey, Util */
|
||||
var Toolbar = (function (my) {
|
||||
|
||||
/**
|
||||
|
@ -8,14 +10,14 @@ var Toolbar = (function (my) {
|
|||
{
|
||||
$("#prezi_button").css({display: "none"});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the lock room dialog.
|
||||
*/
|
||||
my.openLockDialog = function () {
|
||||
// Only the focus is able to set a shared key.
|
||||
if (focus === null) {
|
||||
if (Moderator.isModerator()) {
|
||||
if (sharedKey) {
|
||||
messageHandler.openMessageDialog(null,
|
||||
"This conversation is currently protected by" +
|
||||
|
@ -73,7 +75,7 @@ var Toolbar = (function (my) {
|
|||
*/
|
||||
my.openLinkDialog = function () {
|
||||
var inviteLink;
|
||||
if (roomUrl == null) {
|
||||
if (roomUrl === null) {
|
||||
inviteLink = "Your conference is currently being created...";
|
||||
} else {
|
||||
inviteLink = encodeURI(roomUrl);
|
||||
|
@ -106,7 +108,7 @@ var Toolbar = (function (my) {
|
|||
* Invite participants to conference.
|
||||
*/
|
||||
function inviteParticipants() {
|
||||
if (roomUrl == null)
|
||||
if (roomUrl === null)
|
||||
return;
|
||||
|
||||
var sharedKeyText = "";
|
||||
|
@ -126,7 +128,8 @@ var Toolbar = (function (my) {
|
|||
roomUrl +
|
||||
"%0D%0A%0D%0A" +
|
||||
sharedKeyText +
|
||||
"Note that Jitsi Meet is currently only supported by Chromium," +
|
||||
"Note that Jitsi Meet is currently" +
|
||||
" only supported by Chromium," +
|
||||
" Google Chrome and Opera, so you need" +
|
||||
" to be using one of these browsers.%0D%0A%0D%0A" +
|
||||
"Talk to you in a sec!";
|
||||
|
@ -232,9 +235,15 @@ var Toolbar = (function (my) {
|
|||
}
|
||||
};
|
||||
|
||||
// Toggle the state of the recording button
|
||||
my.toggleRecordingButtonState = function() {
|
||||
$('#recordButton').toggleClass('active');
|
||||
// Sets the state of the recording button
|
||||
my.setRecordingButtonState = function (isRecording) {
|
||||
if (isRecording) {
|
||||
$('#recordButton').removeClass("icon-recEnable");
|
||||
$('#recordButton').addClass("icon-recEnable active");
|
||||
} else {
|
||||
$('#recordButton').removeClass("icon-recEnable active");
|
||||
$('#recordButton').addClass("icon-recEnable");
|
||||
}
|
||||
};
|
||||
|
||||
// Shows or hides SIP calls button
|
||||
|
@ -247,7 +256,8 @@ var Toolbar = (function (my) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Sets the state of the button. The button has blue glow if desktop streaming is active.
|
||||
* Sets the state of the button. The button has blue glow if desktop
|
||||
* streaming is active.
|
||||
* @param active the state of the desktop streaming.
|
||||
*/
|
||||
my.changeDesktopSharingButtonState = function (active) {
|
||||
|
@ -260,7 +270,7 @@ var Toolbar = (function (my) {
|
|||
{
|
||||
button.removeClass("glow");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return my;
|
||||
}(Toolbar || {}));
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* global $, interfaceConfig, Moderator, showDesktopSharingButton */
|
||||
var ToolbarToggler = (function (my) {
|
||||
var toolbarTimeoutObject,
|
||||
toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
|
@ -12,7 +13,8 @@ var ToolbarToggler = (function(my) {
|
|||
header.show("slide", { direction: "up", duration: 300});
|
||||
$('#subject').animate({top: "+=40"}, 300);
|
||||
if (!bottomToolbar.is(":visible")) {
|
||||
bottomToolbar.show("slide", {direction: "right",duration: 300});
|
||||
bottomToolbar.show(
|
||||
"slide", {direction: "right", duration: 300});
|
||||
}
|
||||
|
||||
if (toolbarTimeoutObject) {
|
||||
|
@ -23,9 +25,10 @@ var ToolbarToggler = (function(my) {
|
|||
toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (focus != null)
|
||||
if (Moderator.isModerator())
|
||||
{
|
||||
// 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"});
|
||||
}
|
||||
|
||||
|
@ -57,7 +60,8 @@ var ToolbarToggler = (function(my) {
|
|||
header.hide("slide", { direction: "up", duration: 300});
|
||||
$('#subject').animate({top: "-=40"}, 300);
|
||||
if ($("#remoteVideos").hasClass("hidden")) {
|
||||
bottomToolbar.hide("slide", {direction: "right", duration: 300});
|
||||
bottomToolbar.hide(
|
||||
"slide", {direction: "right", duration: 300});
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
15
util.js
15
util.js
|
@ -82,5 +82,20 @@ var Util = (function (my) {
|
|||
element.setAttribute("data-container", "body");
|
||||
};
|
||||
|
||||
my.createExpBackoffTimer = function (step) {
|
||||
var count = 1;
|
||||
return function (reset) {
|
||||
// Reset call
|
||||
if (reset) {
|
||||
count = 1;
|
||||
return;
|
||||
}
|
||||
// Calculate next timeout
|
||||
var timeout = Math.pow(2, count - 1);
|
||||
count += 1;
|
||||
return timeout * step;
|
||||
};
|
||||
};
|
||||
|
||||
return my;
|
||||
}(Util || {}));
|
||||
|
|
|
@ -30,6 +30,10 @@ var VideoLayout = (function (my) {
|
|||
RTC.attachMediaStream($('#localAudio'), stream);
|
||||
document.getElementById('localAudio').autoplay = true;
|
||||
document.getElementById('localAudio').volume = 0;
|
||||
if (preMuted) {
|
||||
setAudioMuted(true);
|
||||
preMuted = false;
|
||||
}
|
||||
};
|
||||
|
||||
my.changeLocalVideo = function(stream, flipX) {
|
||||
|
@ -438,7 +442,7 @@ var VideoLayout = (function (my) {
|
|||
if ($('#' + videoSpanId).length > 0) {
|
||||
// If there's been a focus change, make sure we add focus related
|
||||
// interface!!
|
||||
if (focus && $('#remote_popupmenu_' + resourceJid).length <= 0) {
|
||||
if (Moderator.isModerator() && $('#remote_popupmenu_' + resourceJid).length <= 0) {
|
||||
addRemoteVideoMenu(peerJid,
|
||||
document.getElementById(videoSpanId));
|
||||
}
|
||||
|
@ -476,7 +480,7 @@ var VideoLayout = (function (my) {
|
|||
|
||||
// If the peerJid is null then this video span couldn't be directly
|
||||
// associated with a participant (this could happen in the case of prezi).
|
||||
if (focus && peerJid != null)
|
||||
if (Moderator.isModerator() && peerJid !== null)
|
||||
addRemoteVideoMenu(peerJid, container);
|
||||
|
||||
remotes.appendChild(container);
|
||||
|
@ -844,43 +848,48 @@ var VideoLayout = (function (my) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Shows a visual indicator for the focus of the conference.
|
||||
* Currently if we're not the owner of the conference we obtain the focus
|
||||
* from the connection.jingle.sessions.
|
||||
* Shows a visual indicator for the moderator of the conference.
|
||||
*/
|
||||
my.showFocusIndicator = function() {
|
||||
if (focus !== null) {
|
||||
my.showModeratorIndicator = function () {
|
||||
if (Moderator.isModerator()) {
|
||||
var indicatorSpan = $('#localVideoContainer .focusindicator');
|
||||
|
||||
if (indicatorSpan.children().length === 0)
|
||||
{
|
||||
createFocusIndicatorElement(indicatorSpan[0]);
|
||||
createModeratorIndicatorElement(indicatorSpan[0]);
|
||||
}
|
||||
}
|
||||
else if (Object.keys(connection.jingle.sessions).length > 0) {
|
||||
// If we're only a participant the focus will be the only session we have.
|
||||
var session
|
||||
= connection.jingle.sessions
|
||||
[Object.keys(connection.jingle.sessions)[0]];
|
||||
var focusId
|
||||
= 'participant_' + Strophe.getResourceFromJid(session.peerjid);
|
||||
} else {
|
||||
Object.keys(connection.emuc.members).forEach(function (jid) {
|
||||
var member = connection.emuc.members[jid];
|
||||
if (member.role === 'moderator') {
|
||||
var moderatorId
|
||||
= 'participant_' + Strophe.getResourceFromJid(jid);
|
||||
|
||||
var focusContainer = document.getElementById(focusId);
|
||||
if (!focusContainer) {
|
||||
console.error("No focus container!");
|
||||
var moderatorContainer
|
||||
= document.getElementById(moderatorId);
|
||||
|
||||
if (Strophe.getResourceFromJid(jid) === 'focus') {
|
||||
// Skip server side focus
|
||||
return;
|
||||
}
|
||||
var indicatorSpan = $('#' + focusId + ' .focusindicator');
|
||||
if (!moderatorContainer) {
|
||||
console.error("No moderator container for " + jid);
|
||||
return;
|
||||
}
|
||||
var indicatorSpan
|
||||
= $('#' + moderatorId + ' .focusindicator');
|
||||
|
||||
if (!indicatorSpan || indicatorSpan.length === 0) {
|
||||
indicatorSpan = document.createElement('span');
|
||||
indicatorSpan.className = 'focusindicator';
|
||||
|
||||
focusContainer.appendChild(indicatorSpan);
|
||||
moderatorContainer.appendChild(indicatorSpan);
|
||||
|
||||
createFocusIndicatorElement(indicatorSpan);
|
||||
createModeratorIndicatorElement(indicatorSpan);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1197,15 +1206,15 @@ var VideoLayout = (function (my) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates the element indicating the focus of the conference.
|
||||
* Creates the element indicating the moderator(owner) of the conference.
|
||||
*
|
||||
* @param parentElement the parent element where the focus indicator will
|
||||
* @param parentElement the parent element where the owner indicator will
|
||||
* be added
|
||||
*/
|
||||
function createFocusIndicatorElement(parentElement) {
|
||||
var focusIndicator = document.createElement('i');
|
||||
focusIndicator.className = 'fa fa-star';
|
||||
parentElement.appendChild(focusIndicator);
|
||||
function createModeratorIndicatorElement(parentElement) {
|
||||
var moderatorIndicator = document.createElement('i');
|
||||
moderatorIndicator.className = 'fa fa-star';
|
||||
parentElement.appendChild(moderatorIndicator);
|
||||
|
||||
Util.setTooltip(parentElement,
|
||||
"The owner of<br/>this conference",
|
||||
|
@ -1308,8 +1317,8 @@ var VideoLayout = (function (my) {
|
|||
if ($(this).attr('disabled') != undefined) {
|
||||
event.preventDefault();
|
||||
}
|
||||
var isMute = !mutedAudios[jid];
|
||||
connection.moderate.setMute(jid, isMute);
|
||||
var isMute = mutedAudios[jid] == true;
|
||||
connection.moderate.setMute(jid, !isMute);
|
||||
popupmenuElement.setAttribute('style', 'display:none;');
|
||||
|
||||
if (isMute) {
|
||||
|
@ -1389,19 +1398,27 @@ var VideoLayout = (function (my) {
|
|||
* On audio muted event.
|
||||
*/
|
||||
$(document).bind('audiomuted.muc', function (event, jid, isMuted) {
|
||||
/*
|
||||
// FIXME: but focus can not mute in this case ? - check
|
||||
if (jid === connection.emuc.myroomjid) {
|
||||
|
||||
// The local mute indicator is controlled locally
|
||||
return;
|
||||
}*/
|
||||
var videoSpanId = null;
|
||||
if (jid === connection.emuc.myroomjid) {
|
||||
videoSpanId = 'localVideoContainer';
|
||||
} else {
|
||||
VideoLayout.ensurePeerContainerExists(jid);
|
||||
videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid);
|
||||
}
|
||||
|
||||
VideoLayout.ensurePeerContainerExists(jid);
|
||||
|
||||
if (focus) {
|
||||
mutedAudios[jid] = isMuted;
|
||||
|
||||
if (Moderator.isModerator()) {
|
||||
VideoLayout.updateRemoteVideoMenu(jid, isMuted);
|
||||
}
|
||||
|
||||
var videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid);
|
||||
if (videoSpanId)
|
||||
VideoLayout.showAudioIndicator(videoSpanId, isMuted);
|
||||
});
|
||||
|
@ -1665,7 +1682,7 @@ var VideoLayout = (function (my) {
|
|||
VideoLayout.updateLargeVideo(RTC.getVideoSrc(videoelem[0]), 1, parentResourceJid);
|
||||
}
|
||||
|
||||
VideoLayout.showFocusIndicator();
|
||||
VideoLayout.showModeratorIndicator();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue