Merge remote-tracking branch 'origin/master' into issue/toolbar-refactor

This commit is contained in:
Issac Gerges 2015-08-11 13:30:08 -05:00
commit de30ce0f5c
27 changed files with 3102 additions and 2288 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ node_modules
.idea/
*.iml
.*.tmp
deploy-local.sh

View File

@ -18,7 +18,10 @@ app:
$(NPM) update && $(BROWSERIFY) $(FLAGS) app.js -s APP -o $(OUTPUT_DIR)/app.bundle.js
clean:
@rm -f $(OUTPUT_DIR)/*.bundle.js
rm -f $(OUTPUT_DIR)/*.bundle.js
deploy:
@mkdir -p $(DEPLOY_DIR) && cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR) && ./bump-js-versions.sh
mkdir -p $(DEPLOY_DIR) && \
cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR) && \
./bump-js-versions.sh && \
([ ! -x deploy-local.sh ] || ./deploy-local.sh)

View File

@ -446,6 +446,11 @@
background-position: center;
}
.videoMessageFilter {
-webkit-filter: grayscale(.5) opacity(0.8);
filter: grayscale(.5) opacity(0.8);
}
.videoProblemFilter {
-webkit-filter: blur(10px) grayscale(.5) opacity(0.8);
filter: blur(10px) grayscale(.5) opacity(0.8);

View File

@ -29,6 +29,11 @@ constructor.
```
If you don't specify room the user will enter in new conference with random room name.
You can enable the "film strip only" mode(only the small videos are visible) by setting 6th parameter to ```true```:
```javascript
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, true);
```
Controlling embedded Jitsi Meet Conference
=========

View File

@ -23,9 +23,12 @@ var JitsiMeetExternalAPI = (function()
* @param width width of the iframe
* @param height height of the iframe
* @param parent_node the node that will contain the iframe
* @param filmStripOnly if the value is true only the small videos will be
* visible.
* @constructor
*/
function JitsiMeetExternalAPI(domain, room_name, width, height, parentNode) {
function JitsiMeetExternalAPI(domain, room_name, width, height, parentNode,
filmStripOnly) {
if(!width || width < MIN_WIDTH)
width = MIN_WIDTH;
if(!height || height < MIN_HEIGHT)
@ -49,6 +52,9 @@ var JitsiMeetExternalAPI = (function()
if(room_name)
this.url += room_name;
this.url += "#external=true";
if(filmStripOnly)
this.url += "&interfaceConfig.filmStripOnly=true";
JitsiMeetExternalAPI.id++;
this.frame = document.createElement("iframe");

View File

@ -22,7 +22,7 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
<script src="interface_config.js?v=5"></script>
<script src="libs/app.bundle.js?v=120"></script>
<script src="libs/app.bundle.js?v=121"></script>
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<link rel="stylesheet" href="css/font.css?v=7"/>
<link rel="stylesheet" href="css/toastr.css?v=1">

View File

@ -15,5 +15,9 @@ var interfaceConfig = {
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
APP_NAME: "Jitsi Meet",
INVITATION_POWERED_BY: true,
ACTIVE_SPEAKER_AVATAR_SIZE: 100
ACTIVE_SPEAKER_AVATAR_SIZE: 100,
/**
* Whether to only show the filmstrip (and hide the toolbar).
*/
filmStripOnly: false
};

View File

@ -240,5 +240,11 @@
"FETCH_SESSION_ID": "Obtaining session-id...",
"GOT_SESSION_ID": "Obtaining session-id... Done",
"GET_SESSION_ID_ERROR": "Get session-id error: "
},
"recording":
{
"toaster": "Currently recording!",
"pending": "Your recording will start as soon as another participant joins",
"on": "Recording has been started"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,24 @@
/* global APP */
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
var RTCEvents = require("../../service/RTC/RTCEvents");
var RTCBrowserType = require("./RTCBrowserType");
/**
* This implements 'onended' callback normally fired by WebRTC after the stream
* is stopped. There is no such behaviour yet in FF, so we have to add it.
* @param stream original WebRTC stream object to which 'onended' handling
* will be added.
*/
function implementOnEndedHandling(stream) {
var originalStop = stream.stop;
stream.stop = function () {
originalStop.apply(stream);
if (!stream.ended) {
stream.ended = true;
stream.onended();
}
};
}
function LocalStream(stream, type, eventEmitter, videoType, isGUMStream) {
this.stream = stream;
@ -21,9 +39,12 @@ function LocalStream(stream, type, eventEmitter, videoType, isGUMStream) {
};
}
this.stream.onended = function() {
this.stream.onended = function () {
self.streamEnded();
};
if (RTCBrowserType.isFirefox()) {
implementOnEndedHandling(this.stream);
}
}
LocalStream.prototype.streamEnded = function () {
@ -45,9 +66,11 @@ LocalStream.prototype.setMute = function (mute)
var eventType = isAudio ? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE;
if ((window.location.protocol != "https:" && this.isGUMStream) ||
(isAudio && this.isGUMStream) || this.videoType === "screen") {
var tracks = this.getTracks();
(isAudio && this.isGUMStream) || this.videoType === "screen" ||
// FIXME FF does not support 'removeStream' method used to mute
RTCBrowserType.isFirefox()) {
var tracks = this.getTracks();
for (var idx = 0; idx < tracks.length; idx++) {
tracks[idx].enabled = !mute;
}

View File

@ -23,21 +23,17 @@ function getPreviousResolution(resolution) {
}
function setResolutionConstraints(constraints, resolution, isAndroid) {
if (resolution && !constraints.video || isAndroid) {
// same behaviour as true
constraints.video = { mandatory: {}, optional: [] };
}
if(Resolutions[resolution]) {
if (Resolutions[resolution]) {
constraints.video.mandatory.minWidth = Resolutions[resolution].width;
constraints.video.mandatory.minHeight = Resolutions[resolution].height;
}
else {
if (isAndroid) {
constraints.video.mandatory.minWidth = 320;
constraints.video.mandatory.minHeight = 240;
constraints.video.mandatory.maxFrameRate = 15;
}
else if (isAndroid) {
// FIXME can't remember if the purpose of this was to always request
// low resolution on Android ? if yes it should be moved up front
constraints.video.mandatory.minWidth = 320;
constraints.video.mandatory.minHeight = 240;
constraints.video.mandatory.maxFrameRate = 15;
}
if (constraints.video.mandatory.minWidth)
@ -55,10 +51,28 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid
if (um.indexOf('video') >= 0) {
// same behaviour as true
constraints.video = { mandatory: {}, optional: [] };
constraints.video.optional.push({ googLeakyBucket: true });
setResolutionConstraints(constraints, resolution, isAndroid);
}
if (um.indexOf('audio') >= 0) {
// same behaviour as true
constraints.audio = { mandatory: {}, optional: []};
if (!RTCBrowserType.isFirefox()) {
// same behaviour as true
constraints.audio = { mandatory: {}, optional: []};
// if it is good enough for hangouts...
constraints.audio.optional.push(
{googEchoCancellation: true},
{googAutoGainControl: true},
{googNoiseSupression: true},
{googHighpassFilter: true},
{googNoisesuppression2: true},
{googEchoCancellation2: true},
{googAutoGainControl2: true}
);
} else {
constraints.audio = true;
}
}
if (um.indexOf('screen') >= 0) {
if (RTCBrowserType.isChrome()) {
@ -100,30 +114,6 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid
};
}
if (constraints.audio) {
// if it is good enough for hangouts...
constraints.audio.optional.push(
{googEchoCancellation: true},
{googAutoGainControl: true},
{googNoiseSupression: true},
{googHighpassFilter: true},
{googNoisesuppression2: true},
{googEchoCancellation2: true},
{googAutoGainControl2: true}
);
}
if (constraints.video) {
if (um.indexOf('video') >= 0) {
constraints.video.optional.push(
{googLeakyBucket: true}
);
}
}
if (um.indexOf('video') >= 0) {
setResolutionConstraints(constraints, resolution, isAndroid);
}
if (bandwidth) {
if (!constraints.video) {
//same behaviour as true

View File

@ -21,6 +21,7 @@ var messageHandler = UI.messageHandler;
var Authentication = require("./authentication/Authentication");
var UIUtil = require("./util/UIUtil");
var NicknameHandler = require("./util/NicknameHandler");
var JitsiPopover = require("./util/JitsiPopover");
var CQEvents = require("../../service/connectionquality/CQEvents");
var DesktopSharingEventTypes
= require("../../service/desktopsharing/DesktopSharingEventTypes");
@ -169,13 +170,13 @@ function registerListeners() {
VideoLayout.setDeviceAvailabilityIcons(null, devices);
});
APP.RTC.addListener(RTCEvents.VIDEO_MUTE, UI.setVideoMuteButtonsState);
APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function() {
APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function () {
// when the data channel becomes available, tell the bridge about video
// selections so that it can do adaptive simulcast,
// we want the notification to trigger even if userJid is undefined,
// or null.
var userJid = APP.UI.getLargeVideoJid();
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, userJid);
var userResource = APP.UI.getLargeVideoResource();
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, userResource);
});
APP.statistics.addAudioLevelListener(function(jid, audioLevel) {
var resourceJid;
@ -189,7 +190,7 @@ function registerListeners() {
}
AudioLevels.updateAudioLevel(resourceJid, audioLevel,
UI.getLargeVideoJid());
UI.getLargeVideoResource());
});
APP.desktopsharing.addListener(function () {
ToolbarToggler.showDesktopSharingButton();
@ -254,10 +255,8 @@ function registerListeners() {
APP.xmpp.addListener(XMPPEvents.MUC_ROLE_CHANGED, onMucRoleChanged);
APP.xmpp.addListener(XMPPEvents.PRESENCE_STATUS, onMucPresenceStatus);
APP.xmpp.addListener(XMPPEvents.SUBJECT_CHANGED, chatSetSubject);
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, onMucMemberLeft);
APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordRequired);
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad);
APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED,
onAuthenticationRequired);
@ -269,11 +268,11 @@ function registerListeners() {
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED, VideoLayout.onAudioMute);
APP.xmpp.addListener(XMPPEvents.VIDEO_MUTED, VideoLayout.onVideoMute);
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function(doMuteAudio) {
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function (doMuteAudio) {
UI.setAudioMuted(doMuteAudio);
});
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
onDtmfSupportChanged);
onDtmfSupportChanged);
APP.xmpp.addListener(XMPPEvents.START_MUTED_SETTING_CHANGED, function (audio, video) {
SettingsMenu.setStartMuted(audio, video);
});
@ -286,43 +285,43 @@ function registerListeners() {
"dialog.internalError");
});
APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function() {
APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function () {
messageHandler.showError("dialog.error",
"dialog.SLDFailure");
"dialog.SLDFailure");
});
APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function() {
APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function () {
messageHandler.showError("dialog.error",
"dialog.SRDFailure");
});
APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function() {
APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function () {
messageHandler.showError();
});
APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function() {
APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function () {
// FIXME: re-use LoginDialog which supports retries
UI.showLoginPopup(connect);
});
APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function(focusComponent, retrySec) {
APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focusComponent, retrySec) {
UI.messageHandler.notify(
null, "notify.focus",
'disconnected', "notify.focusFail",
{component: focusComponent, ms: retrySec});
});
APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function(pres) {
APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
UI.messageHandler.openReportDialog(null,
"dialog.joinError", pres);
});
APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function(pres) {
APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
UI.messageHandler.openReportDialog(null,
"dialog.connectError", pres);
});
APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function() {
APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function () {
var roomName = UI.generateRoomName();
APP.xmpp.allocateConferenceFocus(roomName, UI.checkForNicknameAndJoin);
});
//NicknameHandler emits this event
UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
APP.xmpp.addToPresence("displayName", nickname);
@ -332,10 +331,14 @@ function registerListeners() {
AudioLevels.init();
});
// Listens for video interruption events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_INTERRUPTED, VideoLayout.onVideoInterrupted);
// Listens for video restores events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_RESTORED, VideoLayout.onVideoRestored);
if (!interfaceConfig.filmStripOnly) {
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
// Listens for video interruption events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_INTERRUPTED, VideoLayout.onVideoInterrupted);
// Listens for video restores events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_RESTORED, VideoLayout.onVideoRestored);
}
}
@ -385,9 +388,6 @@ UI.start = function (init) {
$("#welcome_page").hide();
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
// Set the defaults for prompt dialogs.
$.prompt.setDefaults({persistent: false});
@ -399,34 +399,39 @@ UI.start = function (init) {
bindEvents();
setupPrezi();
setupToolbars();
setupChat();
if (!interfaceConfig.filmStripOnly) {
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
setupToolbars();
setupChat();
// Display notice message at the top of the toolbar
if (config.noticeMessage) {
$('#noticeText').text(config.noticeMessage);
$('#notice').css({display: 'block'});
}
$("#downloadlog").click(function (event) {
dump(event.target);
});
}
else
{
$("#header").css("display", "none");
$("#bottomToolbar").css("display", "none");
$("#downloadlog").css("display", "none");
$("#remoteVideos").css("padding", "0px 0px 18px 0px");
$("#remoteVideos").css("right", "0px");
messageHandler.disableNotifications();
$('body').popover("disable");
// $("[data-toggle=popover]").popover("disable");
JitsiPopover.enabled = false;
}
document.title = interfaceConfig.APP_NAME;
$("#downloadlog").click(function (event) {
dump(event.target);
});
if(config.enableWelcomePage && window.location.pathname == "/" &&
(!window.localStorage.welcomePageDisabled ||
window.localStorage.welcomePageDisabled == "false")) {
$("#videoconference_page").hide();
if (!setupWelcomePage)
setupWelcomePage = require("./welcome_page/WelcomePage");
setupWelcomePage();
return;
}
$("#welcome_page").hide();
// Display notice message at the top of the toolbar
if (config.noticeMessage) {
$('#noticeText').text(config.noticeMessage);
$('#notice').css({display: 'block'});
}
if(config.requireDisplayName) {
var currentSettings = Settings.getSettings();
@ -437,30 +442,33 @@ UI.start = function (init) {
init();
toastr.options = {
"closeButton": true,
"debug": false,
"positionClass": "notification-bottom-right",
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "2000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut",
"reposition": function() {
if(PanelToggler.isVisible()) {
$("#toast-container").addClass("notification-bottom-right-center");
} else {
$("#toast-container").removeClass("notification-bottom-right-center");
}
},
"newestOnTop": false
};
if (!interfaceConfig.filmStripOnly) {
toastr.options = {
"closeButton": true,
"debug": false,
"positionClass": "notification-bottom-right",
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "2000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut",
"reposition": function () {
if (PanelToggler.isVisible()) {
$("#toast-container").addClass("notification-bottom-right-center");
} else {
$("#toast-container").removeClass("notification-bottom-right-center");
}
},
"newestOnTop": false
};
SettingsMenu.init();
SettingsMenu.init();
}
};
@ -533,6 +541,8 @@ function onLocalRoleChanged(jid, info, pres, isModerator) {
Authentication.closeAuthenticationWindow();
messageHandler.notify(null, "notify.me",
'connected', "notify.moderator");
Toolbar.checkAutoRecord();
}
}
@ -667,8 +677,8 @@ UI.inputDisplayNameHandler = function (value) {
VideoLayout.inputDisplayNameHandler(value);
};
UI.getLargeVideoJid = function() {
return VideoLayout.getLargeVideoJid();
UI.getLargeVideoResource = function () {
return VideoLayout.getLargeVideoResource();
};
UI.generateRoomName = function() {

View File

@ -13,6 +13,7 @@ var AuthenticationEvents
var roomUrl = null;
var sharedKey = '';
var UI = null;
var recordingToaster = null;
var buttonHandlers = {
"toolbar_button_mute": function () {
@ -122,8 +123,13 @@ function hangup() {
* Starts or stops the recording for the conference.
*/
function toggleRecording() {
function toggleRecording(predefinedToken) {
APP.xmpp.toggleRecording(function (callback) {
if (predefinedToken) {
callback(UIUtil.escapeHtml(predefinedToken));
return;
}
var msg = APP.translation.generateTranslationHTML(
"dialog.recordingToken");
var token = APP.translation.translateString("dialog.token");
@ -147,7 +153,7 @@ function toggleRecording() {
function () { },
':input:first'
);
}, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
}, Toolbar.setRecordingButtonState);
}
/**
@ -548,17 +554,54 @@ var Toolbar = (function (my) {
};
// Sets the state of the recording button
my.setRecordingButtonState = function (isRecording) {
my.setRecordingButtonState = function (recordingState) {
var selector = $('#toolbar_button_record');
if (isRecording) {
if (recordingState === 'on') {
selector.removeClass("icon-recEnable");
selector.addClass("icon-recEnable active");
} else {
$("#largeVideo").toggleClass("videoMessageFilter", true);
var recordOnKey = "recording.on";
$('#videoConnectionMessage').attr("data-i18n", recordOnKey);
$('#videoConnectionMessage').text(APP.translation.translateString(recordOnKey));
setTimeout(function(){
$("#largeVideo").toggleClass("videoMessageFilter", false);
$('#videoConnectionMessage').css({display: "none"});
}, 1500);
recordingToaster = messageHandler.notify(null, "recording.toaster", null,
null, null, {timeOut: 0, closeButton: null, tapToDismiss: false});
} else if (recordingState === 'off') {
selector.removeClass("icon-recEnable active");
selector.addClass("icon-recEnable");
$("#largeVideo").toggleClass("videoMessageFilter", false);
$('#videoConnectionMessage').css({display: "none"});
if (recordingToaster)
messageHandler.remove(recordingToaster);
} else if (recordingState === 'pending') {
selector.removeClass("icon-recEnable active");
selector.addClass("icon-recEnable");
$("#largeVideo").toggleClass("videoMessageFilter", true);
var recordPendingKey = "recording.pending";
$('#videoConnectionMessage').attr("data-i18n", recordPendingKey);
$('#videoConnectionMessage').text(APP.translation.translateString(recordPendingKey));
$('#videoConnectionMessage').css({display: "block"});
}
};
// checks whether recording is enabled and whether we have params to start automatically recording
my.checkAutoRecord = function () {
if (config.enableRecording && config.autoRecord) {
toggleRecording(config.autoRecordToken);
}
}
// Shows or hides SIP calls button
my.showSipCallButton = function (show) {
if (APP.xmpp.isSipGatewayEnabled() && show) {

View File

@ -53,6 +53,8 @@ var ToolbarToggler = {
* Shows the main toolbar.
*/
showToolbar: function () {
if (interfaceConfig.filmStripOnly)
return;
var header = $("#header"),
bottomToolbar = $("#bottomToolbar");
if (!header.is(':visible') || !bottomToolbar.is(":visible")) {
@ -88,6 +90,9 @@ var ToolbarToggler = {
* @param isDock indicates what operation to perform
*/
dockToolbar: function (isDock) {
if (interfaceConfig.filmStripOnly)
return;
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {

View File

@ -46,6 +46,8 @@ var JitsiPopover = (function () {
* Shows the popover
*/
JitsiPopover.prototype.show = function () {
if(!JitsiPopover.enabled)
return;
this.createPopover();
this.popoverShown = true;
};
@ -118,6 +120,8 @@ var JitsiPopover = (function () {
this.createPopover();
};
JitsiPopover.enabled = true;
return JitsiPopover;
})();

View File

@ -1,4 +1,11 @@
/* global $, APP, jQuery, toastr */
/**
* Flag for enable/disable of the notifications.
* @type {boolean}
*/
var notificationsEnabled = true;
var messageHandler = (function(my) {
/**
@ -172,8 +179,19 @@ var messageHandler = (function(my) {
messageHandler.openMessageDialog(titleKey, msgKey);
};
/**
* Displayes notification.
* @param displayName display name of the participant that is associated with the notification.
* @param displayNameKey the key from the language file for the display name.
* @param cls css class for the notification
* @param messageKey the key from the language file for the text of the message.
* @param messageArguments object with the arguments for the message.
* @param options object with language options.
*/
my.notify = function(displayName, displayNameKey,
cls, messageKey, messageArguments, options) {
if(!notificationsEnabled)
return;
var displayNameSpan = '<span class="nickname" ';
if (displayName) {
displayNameSpan += ">" + displayName;
@ -182,7 +200,7 @@ var messageHandler = (function(my) {
"'>" + APP.translation.translateString(displayNameKey);
}
displayNameSpan += "</span>";
toastr.info(
return toastr.info(
displayNameSpan + '<br>' +
'<span class=' + cls + ' data-i18n="' + messageKey + '"' +
(messageArguments?
@ -193,6 +211,28 @@ var messageHandler = (function(my) {
'</span>', null, options);
};
/**
* Removes the toaster.
* @param toasterElement
*/
my.remove = function(toasterElement) {
toasterElement.remove();
};
/**
* Disables notifications.
*/
my.disableNotifications = function () {
notificationsEnabled = false;
};
/**
* Enables notifications.
*/
my.enableNotifications = function () {
notificationsEnabled = true;
};
return my;
}(messageHandler || {}));

View File

@ -469,6 +469,14 @@ var LargeVideo = {
largeVideoHeight,
horizontalIndent, verticalIndent, animate);
},
/**
* Resizes the large html elements.
* @param animate boolean property that indicates whether the resize should be animated or not.
* @param isChatVisible boolean property that indicates whether the chat area is displayed or not.
* If that parameter is null the method will check the chat pannel visibility.
* @param completeFunction a function to be called when the video space is resized
* @returns {*[]} array with the current width and height values of the largeVideo html element.
*/
resize: function (animate, isVisible, completeFunction) {
if(!isEnabled)
return;
@ -481,18 +489,8 @@ var LargeVideo = {
var top = availableHeight / 2 - avatarSize / 4 * 3;
$('#activeSpeaker').css('top', top);
this.VideoLayout.resizeVideoSpace(animate, isVisible, completeFunction);
if(animate) {
$('#videospace').animate({
right: window.innerWidth - availableWidth,
width: availableWidth,
height: availableHeight
},
{
queue: false,
duration: 500,
complete: completeFunction
});
$('#largeVideoContainer').animate({
width: availableWidth,
height: availableHeight
@ -502,8 +500,6 @@ var LargeVideo = {
duration: 500
});
} else {
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
$('#largeVideoContainer').width(availableWidth);
$('#largeVideoContainer').height(availableHeight);
}

View File

@ -42,85 +42,96 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
* @param jid the jid indicating the video for which we're adding a menu.
* @param parentElement the parent element where this menu will be added
*/
RemoteVideo.prototype.addRemoteVideoMenu = function () {
var spanElement = document.createElement('span');
spanElement.className = 'remotevideomenu';
this.container.appendChild(spanElement);
if (!interfaceConfig.filmStripOnly) {
RemoteVideo.prototype.addRemoteVideoMenu = function () {
var spanElement = document.createElement('span');
spanElement.className = 'remotevideomenu';
var menuElement = document.createElement('i');
menuElement.className = 'fa fa-angle-down';
menuElement.title = 'Remote user controls';
spanElement.appendChild(menuElement);
this.container.appendChild(spanElement);
var menuElement = document.createElement('i');
menuElement.className = 'fa fa-angle-down';
menuElement.title = 'Remote user controls';
spanElement.appendChild(menuElement);
var popupmenuElement = document.createElement('ul');
popupmenuElement.className = 'popupmenu';
popupmenuElement.id = 'remote_popupmenu_' + this.getResourceJid();
spanElement.appendChild(popupmenuElement);
var popupmenuElement = document.createElement('ul');
popupmenuElement.className = 'popupmenu';
popupmenuElement.id = 'remote_popupmenu_' + this.getResourceJid();
spanElement.appendChild(popupmenuElement);
var muteMenuItem = document.createElement('li');
var muteLinkItem = document.createElement('a');
var muteMenuItem = document.createElement('li');
var muteLinkItem = document.createElement('a');
var mutedIndicator = "<i style='float:left;' class='icon-mic-disabled'></i>";
var mutedIndicator = "<i style='float:left;' " +
"class='icon-mic-disabled'></i>";
if (!this.isMuted) {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
muteLinkItem.className = 'mutelink';
}
else {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
muteLinkItem.className = 'mutelink disabled';
}
var self = this;
muteLinkItem.onclick = function(){
if ($(this).attr('disabled') != undefined) {
event.preventDefault();
}
var isMute = self.isMuted == true;
APP.xmpp.setMute(self.peerJid, !isMute);
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
this.className = 'mutelink disabled';
if (!this.isMuted) {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.domute'></div>";
muteLinkItem.className = 'mutelink';
}
else {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
this.className = 'mutelink';
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.muted'></div>";
muteLinkItem.className = 'mutelink disabled';
}
var self = this;
muteLinkItem.onclick = function(){
if ($(this).attr('disabled') != undefined) {
event.preventDefault();
}
var isMute = self.isMuted == true;
APP.xmpp.setMute(self.peerJid, !isMute);
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.muted'></div>";
this.className = 'mutelink disabled';
}
else {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.domute'></div>";
this.className = 'mutelink';
}
};
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
var ejectText = "<div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.kick'>&nbsp;</div>";
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.onclick = function(){
APP.xmpp.eject(self.peerJid);
popupmenuElement.setAttribute('style', 'display:none;');
};
ejectMenuItem.appendChild(ejectLinkItem);
popupmenuElement.appendChild(ejectMenuItem);
var paddingSpan = document.createElement('span');
paddingSpan.className = 'popupmenuPadding';
popupmenuElement.appendChild(paddingSpan);
APP.translation.translateElement(
$("#" + popupmenuElement.id + " > li > a > div"));
};
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
var ejectText = "<div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.kick'>&nbsp;</div>";
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.onclick = function(){
APP.xmpp.eject(self.peerJid);
popupmenuElement.setAttribute('style', 'display:none;');
};
ejectMenuItem.appendChild(ejectLinkItem);
popupmenuElement.appendChild(ejectMenuItem);
var paddingSpan = document.createElement('span');
paddingSpan.className = 'popupmenuPadding';
popupmenuElement.appendChild(paddingSpan);
APP.translation.translateElement(
$("#" + popupmenuElement.id + " > li > a > div"));
};
} else {
RemoteVideo.prototype.addRemoteVideoMenu = function() {}
}
/**
* Removes the remote stream element corresponding to the given stream and

View File

@ -3,6 +3,7 @@ var AudioLevels = require("../audio_levels/AudioLevels");
var ContactList = require("../side_pannels/contactlist/ContactList");
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
var UIEvents = require("../../../service/UI/UIEvents");
var UIUtil = require("../util/UIUtil");
var RTC = require("../../RTC/RTC");
var RTCBrowserType = require('../../RTC/RTCBrowserType');
@ -11,6 +12,7 @@ var RemoteVideo = require("./RemoteVideo");
var LargeVideo = require("./LargeVideo");
var LocalVideo = require("./LocalVideo");
var remoteVideos = {};
var remoteVideoTypes = {};
var localVideoThumbnail = null;
@ -34,8 +36,12 @@ var VideoLayout = (function (my) {
my.init = function (emitter) {
eventEmitter = emitter;
localVideoThumbnail = new LocalVideo(VideoLayout);
if (interfaceConfig.filmStripOnly) {
LargeVideo.disable();
} else {
LargeVideo.init(VideoLayout, emitter);
}
LargeVideo.init(VideoLayout, emitter);
VideoLayout.resizeLargeVideoContainer();
};
@ -164,7 +170,7 @@ var VideoLayout = (function (my) {
}
};
my.getLargeVideoJid = function () {
my.getLargeVideoResource = function () {
return LargeVideo.getResourceJid();
};
@ -192,7 +198,7 @@ var VideoLayout = (function (my) {
resourceJid) {
if(focusedVideoResourceJid) {
var oldSmallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
if(oldSmallVideo)
if (oldSmallVideo && !interfaceConfig.filmStripOnly)
oldSmallVideo.focus(false);
}
@ -219,7 +225,7 @@ var VideoLayout = (function (my) {
// Update focused/pinned interface.
if (resourceJid) {
if(smallVideo)
if (smallVideo && !interfaceConfig.filmStripOnly)
smallVideo.focus(true);
if (!noPinnedEndpointChangedEvent) {
@ -354,7 +360,11 @@ var VideoLayout = (function (my) {
* Resizes the large video container.
*/
my.resizeLargeVideoContainer = function () {
LargeVideo.resize();
if(LargeVideo.isEnabled()) {
LargeVideo.resize();
} else {
VideoLayout.resizeVideoSpace();
}
VideoLayout.resizeThumbnails();
LargeVideo.position();
};
@ -373,7 +383,7 @@ var VideoLayout = (function (my) {
if(animate) {
$('#remoteVideos').animate({
height: height
height: height + 2 // adds 2 px because of small video 1px border
},
{
queue: false,
@ -398,7 +408,7 @@ var VideoLayout = (function (my) {
} else {
// size videos so that while keeping AR and max height, we have a
// nice fit
$('#remoteVideos').height(height);
$('#remoteVideos').height(height + 2);// adds 2 px because of small video 1px border
$('#remoteVideos>span').width(width);
$('#remoteVideos>span').height(height);
@ -431,7 +441,7 @@ var VideoLayout = (function (my) {
var availableWidth = availableWinWidth / numvids;
var aspectRatio = 16.0 / 9.0;
var maxHeight = Math.min(160, availableHeight);
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio);
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio, window.innerHeight - 18);
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
}
@ -886,6 +896,37 @@ var VideoLayout = (function (my) {
VideoLayout.resizeThumbnails(true);
};
/**
* Resizes the #videospace html element
* @param animate boolean property that indicates whether the resize should be animated or not.
* @param isChatVisible boolean property that indicates whether the chat area is displayed or not.
* If that parameter is null the method will check the chat pannel visibility.
* @param completeFunction a function to be called when the video space is resized
*/
my.resizeVideoSpace = function (animate, isChatVisible, completeFunction) {
var availableHeight = window.innerHeight;
var availableWidth = UIUtil.getAvailableVideoWidth(isChatVisible);
if (availableWidth < 0 || availableHeight < 0) return;
if(animate) {
$('#videospace').animate({
right: window.innerWidth - availableWidth,
width: availableWidth,
height: availableHeight
},
{
queue: false,
duration: 500,
complete: completeFunction
});
} else {
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
}
};
my.getSmallVideo = function (resourceJid) {
if(resourceJid == APP.xmpp.myResource()) {
return localVideoThumbnail;

View File

@ -1,4 +1,4 @@
/* global $, $iq, config */
/* global $, $iq, config, interfaceConfig */
var params = {};
function getConfigParamsFromUrl() {
if(!location.hash)
@ -17,22 +17,31 @@ params = getConfigParamsFromUrl();
var URLProcessor = {
setConfigParametersFromUrl: function () {
for(var k in params)
{
if(typeof k !== "string" || k.indexOf("config.") === -1)
for(var key in params) {
if(typeof key !== "string")
continue;
var v = params[k];
var confKey = k.substr(7);
if(config[confKey] && typeof config[confKey] !== typeof v)
var confObj = null, confKey;
if (key.indexOf("config.") === 0) {
confObj = config;
confKey = key.substr("config.".length);
} else if (key.indexOf("interfaceConfig.") === 0) {
confObj = interfaceConfig;
confKey = key.substr("interfaceConfig.".length);
}
if (!confObj)
continue;
var value = params[key];
if (confObj[confKey] && typeof confObj[confKey] !== typeof value)
{
console.warn("The type of " + k +
console.warn("The type of " + key +
" is wrong. That parameter won't be updated in config.js.");
continue;
}
config[confKey] = v;
confObj[confKey] = value;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,21 @@
/* global $ */
/*
The purpose of this hack is to re-use SSRC of first video stream ever created
for any video streams created later on. In order to do that this hack:
Here we do modifications of local video SSRCs. There are 2 situations we have
to handle:
1. We generate SSRC for local recvonly video stream. This is the case when we
have no local camera and it is not generated automatically, but SSRC=1 is
used implicitly. If that happens RTCP packets will be dropped by the JVB
and we won't be able to request video key frames correctly.
2. A hack to re-use SSRC of the first video stream for any new stream created
in future. It turned out that Chrome may keep on using the SSRC of removed
video stream in RTCP even though a new one has been created. So we just
want to avoid that by re-using it. Jingle 'source-remove'/'source-add'
notifications are blocked once first video SSRC is advertised to the focus.
What this hack does:
1. Stores the SSRC of the first video stream created by
a) scanning Jingle session-accept/session-invite for existing video SSRC
@ -19,12 +32,32 @@
*/
var SDP = require('./SDP');
var RTCBrowserType = require('../RTC/RTCBrowserType');
/**
* The hack is enabled on all browsers except FF by default
* FIXME finish the hack once removeStream method is implemented in FF
* @type {boolean}
*/
var isEnabled = !RTCBrowserType.isFirefox();
/**
* Stored SSRC of local video stream.
*/
var localVideoSSRC;
/**
* SSRC used for recvonly video stream when we have no local camera.
* This is in order to tell Chrome what SSRC should be used in RTCP requests
* instead of 1.
*/
var localRecvOnlySSRC;
/**
* cname for <tt>localRecvOnlySSRC</tt>
*/
var localRecvOnlyCName;
/**
* Method removes <source> element which describes <tt>localVideoSSRC</tt>
* from given Jingle IQ.
@ -36,16 +69,17 @@ var localVideoSSRC;
* other SSRCs left to be signaled after removing it.
*/
var filterOutSource = function (modifyIq, actionName) {
if (!localVideoSSRC)
return modifyIq;
var modifyIqTree = $(modifyIq.tree());
if (!localVideoSSRC)
return modifyIqTree[0];
var videoSSRC = modifyIqTree.find(
'>jingle>content[name="video"]' +
'>description>source[ssrc="' + localVideoSSRC + '"]');
if (!videoSSRC.length) {
return modifyIqTree;
return modifyIqTree[0];
}
console.info(
@ -55,7 +89,7 @@ var filterOutSource = function (modifyIq, actionName) {
// Check if any sources still left to be added/removed
if (modifyIqTree.find('>jingle>content>description>source').length) {
return modifyIqTree;
return modifyIqTree[0];
} else {
return null;
}
@ -70,21 +104,41 @@ var storeLocalVideoSSRC = function (jingleIq) {
$(jingleIq.tree())
.find('>jingle>content[name="video"]>description>source');
console.info('Video desc: ', videoSSRCs);
if (!videoSSRCs.length)
return;
var ssrc = videoSSRCs.attr('ssrc');
if (ssrc) {
localVideoSSRC = ssrc;
console.info(
'Stored local video SSRC for future re-use: ' + localVideoSSRC);
} else {
console.error('No "ssrc" attribute present in <source> element');
}
videoSSRCs.each(function (idx, ssrcElem) {
if (localVideoSSRC)
return;
// We consider SSRC real only if it has msid attribute
// recvonly streams in FF do not have it as well as local SSRCs
// we generate for recvonly streams in Chrome
var ssrSel = $(ssrcElem);
var msid = ssrSel.find('>parameter[name="msid"]');
if (msid.length) {
var ssrcVal = ssrSel.attr('ssrc');
if (ssrcVal) {
localVideoSSRC = ssrcVal;
console.info('Stored local video SSRC' +
' for future re-use: ' + localVideoSSRC);
}
}
});
};
var LocalVideoSSRCHack = {
/**
* Generates new SSRC for local video recvonly stream.
* FIXME what about eventual SSRC collision ?
*/
function generateRecvonlySSRC() {
//
localRecvOnlySSRC =
Math.random().toString(10).substring(2, 11);
localRecvOnlyCName =
Math.random().toString(36).substring(2);
console.info(
"Generated local recvonly SSRC: " + localRecvOnlySSRC +
", cname: " + localRecvOnlyCName);
}
var LocalSSRCReplacement = {
/**
* Method must be called before 'session-initiate' or 'session-invite' is
* sent. Scans the IQ for local video SSRC and stores it if detected.
@ -93,6 +147,9 @@ var LocalVideoSSRCHack = {
* which will be scanned for local video SSRC.
*/
processSessionInit: function (sessionInit) {
if (!isEnabled)
return;
if (localVideoSSRC) {
console.error("Local SSRC stored already: " + localVideoSSRC);
return;
@ -108,6 +165,9 @@ var LocalVideoSSRCHack = {
* @returns modified <tt>localDescription</tt> object.
*/
mungeLocalVideoSSRC: function (localDescription) {
if (!isEnabled)
return localDescription;
// IF we have local video SSRC stored make sure it is replaced
// with old SSRC
if (localVideoSSRC) {
@ -129,6 +189,25 @@ var LocalVideoSSRCHack = {
new RegExp('a=ssrc:' + newSSRC, 'g'),
'a=ssrc:' + localVideoSSRC);
}
} else {
// Make sure we have any SSRC for recvonly video stream
var sdp = new SDP(localDescription.sdp);
if (sdp.media[1] && sdp.media[1].indexOf('a=ssrc:') === -1 &&
sdp.media[1].indexOf('a=recvonly') !== -1) {
if (!localRecvOnlySSRC) {
generateRecvonlySSRC();
}
console.info('No SSRC in video recvonly stream' +
' - adding SSRC: ' + localRecvOnlySSRC);
sdp.media[1] += 'a=ssrc:' + localRecvOnlySSRC +
' cname:' + localRecvOnlyCName + '\r\n';
localDescription.sdp = sdp.session + sdp.media.join('');
}
}
return localDescription;
},
@ -143,6 +222,9 @@ var LocalVideoSSRCHack = {
* a Strophe IQ Builder instance, but DOM element tree.
*/
processSourceAdd: function (sourceAdd) {
if (!isEnabled)
return sourceAdd;
if (!localVideoSSRC) {
// Store local SSRC if available
storeLocalVideoSSRC(sourceAdd);
@ -162,8 +244,20 @@ var LocalVideoSSRCHack = {
* a Strophe IQ Builder instance, but DOM element tree.
*/
processSourceRemove: function (sourceRemove) {
if (!isEnabled)
return sourceRemove;
return filterOutSource(sourceRemove, 'source-remove');
},
/**
* Turns the hack on or off
* @param enabled <tt>true</tt> to enable the hack or <tt>false</tt>
* to disable it
*/
setEnabled: function (enabled) {
isEnabled = enabled;
}
};
module.exports = LocalVideoSSRCHack;
module.exports = LocalSSRCReplacement;

View File

@ -1,7 +1,7 @@
var RTC = require('../RTC/RTC');
var RTCBrowserType = require("../RTC/RTCBrowserType.js");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var VideoSSRCHack = require("./VideoSSRCHack");
var SSRCReplacement = require("./LocalSSRCReplacement");
function TraceablePeerConnection(ice_config, constraints, session) {
var self = this;
@ -213,7 +213,7 @@ if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
function() {
var desc = this.peerconnection.localDescription;
desc = VideoSSRCHack.mungeLocalVideoSSRC(desc);
desc = SSRCReplacement.mungeLocalVideoSSRC(desc);
this.trace('getLocalDescription::preTransform', dumpSDP(desc));
@ -372,7 +372,7 @@ TraceablePeerConnection.prototype.createOffer
self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
}
offer = VideoSSRCHack.mungeLocalVideoSSRC(offer);
offer = SSRCReplacement.mungeLocalVideoSSRC(offer);
if (config.enableSimulcast && self.simulcast.isSupported()) {
offer = self.simulcast.mungeLocalDescription(offer);
@ -402,7 +402,7 @@ TraceablePeerConnection.prototype.createAnswer
}
// munge local video SSRC
answer = VideoSSRCHack.mungeLocalVideoSSRC(answer);
answer = SSRCReplacement.mungeLocalVideoSSRC(answer);
if (config.enableSimulcast && self.simulcast.isSupported()) {
answer = self.simulcast.mungeLocalDescription(answer);

View File

@ -19,6 +19,12 @@ var useJirecon = (typeof config.hosts.jirecon != "undefined");
*/
var jireconRid = null;
/**
* The callback to update the recording button. Currently used from colibri
* after receiving a pending status.
*/
var recordingStateChangeCallback = null;
function setRecordingToken(token) {
recordingToken = token;
}
@ -30,9 +36,9 @@ function setRecordingJirecon(state, token, callback, connection) {
var iq = $iq({to: config.hosts.jirecon, type: 'set'})
.c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
action: state ? 'start' : 'stop',
action: (state === 'on') ? 'start' : 'stop',
mucjid: connection.emuc.roomjid});
if (!state){
if (state === 'off'){
iq.attrs({rid: jireconRid});
}
@ -44,10 +50,10 @@ function setRecordingJirecon(state, token, callback, connection) {
// TODO wait for an IQ with the real status, since this is
// provisional?
jireconRid = $(result).find('recording').attr('rid');
console.log('Recording ' + (state ? 'started' : 'stopped') +
console.log('Recording ' + ((state === 'on') ? 'started' : 'stopped') +
'(jirecon)' + result);
recordingEnabled = state;
if (!state){
if (state === 'off'){
jireconRid = null;
}
@ -73,10 +79,19 @@ function setRecordingColibri(state, token, callback, connection) {
function (result) {
console.log('Set recording "', state, '". Result:', result);
var recordingElem = $(result).find('>conference>recording');
var newState = ('true' === recordingElem.attr('state'));
var newState = recordingElem.attr('state');
recordingEnabled = newState;
callback(newState);
if (newState === 'pending' && recordingStateChangeCallback == null) {
recordingStateChangeCallback = callback;
connection.addHandler(function(iq){
var state = $(iq).find('recording').attr('state');
if (state)
recordingStateChangeCallback(state);
}, 'http://jitsi.org/protocol/colibri', 'iq', null, null, null);
}
},
function (error) {
console.warn(error);
@ -94,8 +109,7 @@ function setRecording(state, token, callback, connection) {
}
var Recording = {
toggleRecording: function (tokenEmptyCallback,
startingCallback, startedCallback, connection) {
toggleRecording: function (tokenEmptyCallback, recordingStateChangeCallback, connection) {
if (!Moderator.isModerator()) {
console.log(
'non-focus, or conference not yet organized:' +
@ -108,16 +122,16 @@ var Recording = {
if (!recordingToken && !useJirecon) {
tokenEmptyCallback(function (value) {
setRecordingToken(value);
self.toggleRecording(tokenEmptyCallback,
startingCallback, startedCallback, connection);
self.toggleRecording(tokenEmptyCallback, recordingStateChangeCallback, connection);
});
return;
}
var oldState = recordingEnabled;
startingCallback(!oldState);
setRecording(!oldState,
var newState = (oldState === 'off' || !oldState) ? 'on' : 'off';
setRecording(newState,
recordingToken,
function (state) {
console.log("New recording state: ", state);
@ -143,7 +157,7 @@ var Recording = {
// have been wrong
setRecordingToken(null);
}
startedCallback(state);
recordingStateChangeCallback(state);
},
connection

View File

@ -1,27 +1,11 @@
/* jshint -W117 */
var JingleSession = require("./JingleSession");
var JingleSession = require("./JingleSessionPC");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var RTCBrowserType = require("../RTC/RTCBrowserType");
module.exports = function(XMPP, eventEmitter) {
function CallIncomingJingle(sid, connection) {
var sess = connection.jingle.sessions[sid];
// TODO: do we check activecall == null?
connection.jingle.activecall = sess;
eventEmitter.emit(XMPPEvents.CALL_INCOMING, 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();
}
Strophe.addConnectionPlugin('jingle', {
connection: null,
sessions: {},
@ -126,20 +110,30 @@ module.exports = function(XMPP, eventEmitter) {
sess.pc_constraints = this.pc_constraints;
sess.ice_config = this.ice_config;
sess.initiate(fromJid, false);
sess.initialize(fromJid, false);
// FIXME: setRemoteDescription should only be done when this call is to be accepted
sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
sess.setOffer($(iq).find('>jingle'));
this.sessions[sess.sid] = sess;
this.jid2session[sess.peerjid] = sess;
// the callback should either
// .sendAnswer and .accept
// or .sendTerminate -- not necessarily synchronus
CallIncomingJingle(sess.sid, this.connection);
// or .sendTerminate -- not necessarily synchronous
// TODO: do we check activecall == null?
this.connection.jingle.activecall = sess;
eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess);
// TODO: check affiliation and/or role
console.log('emuc data for', sess.peerjid,
this.connection.emuc.members[sess.peerjid]);
sess.sendAnswer();
sess.accept();
break;
case 'session-accept':
sess.setRemoteDescription($(iq).find('>jingle'), 'answer');
sess.setAnswer($(iq).find('>jingle'));
sess.accept();
$(document).trigger('callaccepted.jingle', [sess.sid]);
break;
@ -206,7 +200,7 @@ module.exports = function(XMPP, eventEmitter) {
sess.pc_constraints = this.pc_constraints;
sess.ice_config = this.ice_config;
sess.initiate(peerjid, true);
sess.initialize(peerjid, true);
this.sessions[sess.sid] = sess;
this.jid2session[sess.peerjid] = sess;
sess.sendOffer();

View File

@ -434,9 +434,9 @@ var XMPP = {
return true;
},
toggleRecording: function (tokenEmptyCallback,
startingCallback, startedCallback) {
recordingStateChangeCallback) {
Recording.toggleRecording(tokenEmptyCallback,
startingCallback, startedCallback, connection);
recordingStateChangeCallback, connection);
},
addToPresence: function (name, value, dontSend) {
switch (name) {