diff --git a/modules/RTC/RTC.js b/modules/RTC/RTC.js index 7899b6f7d..1a5b7e6b6 100644 --- a/modules/RTC/RTC.js +++ b/modules/RTC/RTC.js @@ -198,7 +198,7 @@ var RTC = { }, changeLocalVideo: function (stream, isUsingScreenStream, callback) { var oldStream = this.localVideo.getOriginalStream(); - var type = (isUsingScreenStream? "screen" : "video"); + var type = (isUsingScreenStream ? "screen" : "camera"); var localCallback = callback; if(this.localVideo.isMuted() && this.localVideo.videoType !== type) { localCallback = function() { @@ -242,32 +242,6 @@ var RTC = { return APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted; } }, - /** - * Checks if video identified by given src is desktop stream. - * @param videoSrc eg. - * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395 - * @returns {boolean} - */ - isVideoSrcDesktop: function (jid) { - if(!jid) - return false; - var isDesktop = false; - var stream = null; - if (APP.xmpp.myJid() === jid) { - // local video - stream = this.localVideo; - } else { - var peerStreams = this.remoteStreams[jid]; - if(!peerStreams) - return false; - stream = peerStreams[MediaStreamType.VIDEO_TYPE]; - } - - if(stream) - isDesktop = (stream.videoType === "screen"); - - return isDesktop; - }, setVideoMute: function (mute, callback, options) { if (!this.localVideo) return; diff --git a/modules/RTC/RTCUtils.js b/modules/RTC/RTCUtils.js index e2d4c38a8..b84354ff5 100644 --- a/modules/RTC/RTCUtils.js +++ b/modules/RTC/RTCUtils.js @@ -522,7 +522,7 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions) { this.service.createLocalStream(audioStream, "audio", null, null, audioMuted, audioGUM); - this.service.createLocalStream(videoStream, "video", null, null, + this.service.createLocalStream(videoStream, "video", null, 'camera', videoMuted, videoGUM); }; diff --git a/modules/UI/UI.js b/modules/UI/UI.js index c8cdf98de..bb2083010 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -154,9 +154,6 @@ function registerListeners() { APP.RTC.addStreamListener(function (stream) { VideoLayout.onRemoteStreamAdded(stream); }, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED); - APP.RTC.addStreamListener(function (jid) { - VideoLayout.onVideoTypeChanged(jid); - }, StreamEventTypes.EVENT_TYPE_REMOTE_CHANGED); APP.RTC.addListener(RTCEvents.LASTN_CHANGED, onLastNChanged); APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (resourceJid) { @@ -264,6 +261,7 @@ function registerListeners() { APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad); APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, onAuthenticationRequired); + APP.xmpp.addListener(XMPPEvents.VIDEO_TYPE, onPeerVideoTypeChanged); APP.xmpp.addListener(XMPPEvents.DEVICE_AVAILABLE, function (resource, devices) { VideoLayout.setDeviceAvailabilityIcons(resource, devices); @@ -607,6 +605,10 @@ function onMucPresenceStatus(jid, info) { VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status); } +function onPeerVideoTypeChanged(resourceJid, newVideoType) { + VideoLayout.onVideoTypeChanged(resourceJid, newVideoType); +} + function onMucRoleChanged(role, displayName) { VideoLayout.showModeratorIndicator(); diff --git a/modules/UI/videolayout/LargeVideo.js b/modules/UI/videolayout/LargeVideo.js index 8ddd2ff14..a207ae2e0 100644 --- a/modules/UI/videolayout/LargeVideo.js +++ b/modules/UI/videolayout/LargeVideo.js @@ -255,7 +255,7 @@ function changeVideo(isVisible) { "none"; } - var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid); + var isDesktop = currentSmallVideo.getVideoType() === 'screen'; // Change the way we'll be measuring and positioning large video getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize; @@ -421,13 +421,12 @@ var LargeVideo = { currentSmallVideo.enableDominantSpeaker(false); } }, - onVideoTypeChanged: function (jid) { - if(!isEnabled) + onVideoTypeChanged: function (resourceJid, newVideoType) { + if (!isEnabled) return; - var resourceJid = Strophe.getResourceFromJid(jid); if (LargeVideo.isCurrentlyOnLarge(resourceJid)) { - var isDesktop = APP.RTC.isVideoSrcDesktop(jid); + var isDesktop = newVideoType === 'screen'; getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize; getVideoPosition = isDesktop ? getDesktopVideoPosition : getCameraVideoPosition; diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index f30e9a542..f2de63a0b 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -53,6 +53,22 @@ SmallVideo.prototype.setDeviceAvailabilityIcons = function (devices) { } }; +/** + * Sets the type of the video displayed by this instance. + * @param videoType 'camera' or 'screen' + */ +SmallVideo.prototype.setVideoType = function (videoType) { + this.videoType = videoType; +}; + +/** + * Returns the type of the video displayed by this instance. + * @returns {String} 'camera', 'screen' or undefined. + */ +SmallVideo.prototype.getVideoType = function () { + return this.videoType; +}; + /** * Shows the presence status message for the given video. */ diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 39302271f..39cb85f0a 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -12,6 +12,7 @@ var LargeVideo = require("./LargeVideo"); var LocalVideo = require("./LocalVideo"); var remoteVideos = {}; +var remoteVideoTypes = {}; var localVideoThumbnail = null; var currentDominantSpeaker = null; @@ -68,6 +69,8 @@ var VideoLayout = (function (my) { localVideoThumbnail.setDisplayName(); localVideoThumbnail.createConnectionIndicator(); + this.onVideoTypeChanged(APP.xmpp.myResource(), stream.videoType); + AudioLevels.updateAudioLevelCanvas(null, VideoLayout); localVideoThumbnail.changeVideo(stream, isMuted); @@ -252,14 +255,21 @@ var VideoLayout = (function (my) { var resourceJid = Strophe.getResourceFromJid(peerJid); - if(!remoteVideos[resourceJid]) { - remoteVideos[resourceJid] = new RemoteVideo(peerJid, VideoLayout); + if (!remoteVideos[resourceJid]) { + + var remoteVideo = new RemoteVideo(peerJid, VideoLayout); + remoteVideos[resourceJid] = remoteVideo; + + var videoType = remoteVideoTypes[resourceJid]; + if (videoType) { + remoteVideo.setVideoType(videoType); + } // In case this is not currently in the last n we don't show it. if (localLastNCount && localLastNCount > 0 && $('#remoteVideos>span').length >= localLastNCount + 2) { - remoteVideos[resourceJid].showPeerContainer('hide'); + remoteVideo.showPeerContainer('hide'); } else VideoLayout.resizeThumbnails(); @@ -809,8 +819,30 @@ var VideoLayout = (function (my) { VideoLayout.resizeThumbnails(); }; - my.onVideoTypeChanged = function (jid) { - LargeVideo.onVideoTypeChanged(jid); + my.onVideoTypeChanged = function (resourceJid, newVideoType) { + if (remoteVideoTypes[resourceJid] === newVideoType) { + return; + } + + console.info("Peer video type changed: ", resourceJid, newVideoType); + remoteVideoTypes[resourceJid] = newVideoType; + + var smallVideo; + if (resourceJid === APP.xmpp.myResource()) { + if (!localVideoThumbnail) { + console.warn("Local video not ready yet"); + return; + } + smallVideo = localVideoThumbnail; + } else if (remoteVideos[resourceJid]) { + smallVideo = remoteVideos[resourceJid]; + } else { + return; + } + + smallVideo.setVideoType(newVideoType); + LargeVideo.onVideoTypeChanged(resourceJid, newVideoType); + }; my.showMore = function (jid) { diff --git a/modules/xmpp/JingleSession.js b/modules/xmpp/JingleSession.js index d2a19c543..5b02be580 100644 --- a/modules/xmpp/JingleSession.js +++ b/modules/xmpp/JingleSession.js @@ -7,6 +7,7 @@ var async = require("async"); var transform = require("sdp-transform"); var XMPPEvents = require("../../service/xmpp/XMPPEvents"); var RTCBrowserType = require("../RTC/RTCBrowserType"); +var VideoSSRCHack = require("./VideoSSRCHack"); // Jingle stuff function JingleSession(me, sid, connection, service, eventEmitter) { @@ -210,10 +211,6 @@ function onIceConnectionStateChange(sid, session) { } } -JingleSession.prototype.getVideoType = function () { - return APP.desktopsharing.isUsingScreenStream() ? 'screen' : 'camera'; -}; - JingleSession.prototype.accept = function () { this.state = 'active'; @@ -245,8 +242,7 @@ JingleSession.prototype.accept = function () { prsdp.toJingle( accept, this.initiator == this.me ? 'initiator' : 'responder', - this.localStreamsSSRC, - self.getVideoType()); + this.localStreamsSSRC); var sdp = this.peerconnection.localDescription.sdp; while (SDPUtil.find_line(sdp, 'a=inactive')) { // FIXME: change any inactive to sendrecv or whatever they were originally @@ -258,6 +254,8 @@ JingleSession.prototype.accept = function () { //console.log('setLocalDescription success'); self.setLocalDescription(); + VideoSSRCHack.processSessionInit(accept); + self.connection.sendIQ(accept, function () { var ack = {}; @@ -348,8 +346,10 @@ JingleSession.prototype.sendIceCandidate = function (candidate) { self.localSDP.toJingle( init, self.initiator == self.me ? 'initiator' : 'responder', - ssrc, - self.getVideoType()); + ssrc); + + VideoSSRCHack.processSessionInit(init); + self.connection.sendIQ(init, function () { //console.log('session initiate ack'); @@ -465,8 +465,10 @@ JingleSession.prototype.createdOffer = function (sdp) { self.localSDP.toJingle( init, this.initiator == this.me ? 'initiator' : 'responder', - this.localStreamsSSRC, - self.getVideoType()); + this.localStreamsSSRC); + + VideoSSRCHack.processSessionInit(init); + self.connection.sendIQ(init, function () { var ack = {}; @@ -522,9 +524,7 @@ JingleSession.prototype.readSsrcInfo = function (contents) { $(this).find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]').each( function () { var owner = this.getAttribute('owner'); - var videoType = this.getAttribute('video-type'); self.ssrcOwners[ssrc] = owner; - self.ssrcVideoTypes[ssrc] = videoType; } ); }); @@ -731,8 +731,10 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) { self.localSDP.toJingle( accept, self.initiator == self.me ? 'initiator' : 'responder', - ssrcs, - self.getVideoType()); + ssrcs); + + VideoSSRCHack.processSessionInit(accept); + self.connection.sendIQ(accept, function () { var ack = {}; @@ -1129,7 +1131,13 @@ JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) { } ); var removed = sdpDiffer.toJingle(remove); - if (removed) { + + // Let 'source-remove' IQ through the hack and see if we're allowed to send + // it in the current form + if (removed) + remove = VideoSSRCHack.processSourceRemove(remove); + + if (removed && remove) { this.connection.sendIQ(remove, function (res) { console.info('got remove result', res); @@ -1152,8 +1160,14 @@ JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) { sid: this.sid } ); - var added = sdpDiffer.toJingle(add, this.getVideoType()); - if (added) { + var added = sdpDiffer.toJingle(add); + + // Let 'source-add' IQ through the hack and see if we're allowed to send + // it in the current form + if (added) + add = VideoSSRCHack.processSourceAdd(add); + + if (added & add) { this.connection.sendIQ(add, function (res) { console.info('got add result', res); @@ -1346,9 +1360,6 @@ JingleSession.prototype.setLocalDescription = function () { var ssrc = newssrcs[i-1].ssrc; var myJid = self.connection.emuc.myroomjid; self.ssrcOwners[ssrc] = myJid; - if (newssrcs[i-1].type === 'video'){ - self.ssrcVideoTypes[ssrc] = self.getVideoType(); - } } } } @@ -1420,9 +1431,7 @@ JingleSession.prototype.remoteStreamAdded = function (data, times) { return; } data.peerjid = self.ssrcOwners[thessrc]; - data.videoType = self.ssrcVideoTypes[thessrc] - console.log('associated jid', self.ssrcOwners[thessrc], - thessrc, data.videoType); + console.log('associated jid', self.ssrcOwners[thessrc]); } else { console.error("No SSRC lines for ", streamId); } diff --git a/modules/xmpp/SDP.js b/modules/xmpp/SDP.js index 5af47612f..eef32092c 100644 --- a/modules/xmpp/SDP.js +++ b/modules/xmpp/SDP.js @@ -125,7 +125,7 @@ SDP.prototype.removeMediaLines = function(mediaindex, prefix) { } // add content's to a jingle element -SDP.prototype.toJingle = function (elem, thecreator, ssrcs, videoType) { +SDP.prototype.toJingle = function (elem, thecreator, ssrcs) { // console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]); var i, j, k, mline, ssrc, rtpmap, tmp, line, lines; var self = this; @@ -258,14 +258,6 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs, videoType) { elem.up(); } } - // Video type - if (videoType && mline.media == "video") { - elem.c('ssrc-info', - { - xmlns: 'http://jitsi.org/jitmeet', - 'video-type': videoType - }).up(); - } elem.up(); // XEP-0339 handle ssrc-group attributes diff --git a/modules/xmpp/SDPDiffer.js b/modules/xmpp/SDPDiffer.js index b6279ceff..d8f5203b2 100644 --- a/modules/xmpp/SDPDiffer.js +++ b/modules/xmpp/SDPDiffer.js @@ -106,7 +106,7 @@ SDPDiffer.prototype.getNewMedia = function() { * @param toJid destination Jid * @param isAdd indicates if this is remove or add operation. */ -SDPDiffer.prototype.toJingle = function(modify, videoType) { +SDPDiffer.prototype.toJingle = function(modify) { var sdpMediaSsrcs = this.getNewMedia(); var self = this; @@ -141,15 +141,6 @@ SDPDiffer.prototype.toJingle = function(modify, videoType) { } modify.up(); // end of parameter }); - // indicate video type - if (videoType && media.mid == 'video') { - modify.c('ssrc-info', - { - xmlns: 'http://jitsi.org/jitmeet', - 'video-type': videoType - }) - .up(); - } modify.up(); // end of source }); diff --git a/modules/xmpp/TraceablePeerConnection.js b/modules/xmpp/TraceablePeerConnection.js index 4970dd0c7..1bc68f122 100644 --- a/modules/xmpp/TraceablePeerConnection.js +++ b/modules/xmpp/TraceablePeerConnection.js @@ -1,6 +1,7 @@ var RTC = require('../RTC/RTC'); var RTCBrowserType = require("../RTC/RTCBrowserType.js"); var XMPPEvents = require("../../service/xmpp/XMPPEvents"); +var VideoSSRCHack = require("./VideoSSRCHack"); function TraceablePeerConnection(ice_config, constraints, session) { var self = this; @@ -211,6 +212,9 @@ if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) { 'localDescription', function() { var desc = this.peerconnection.localDescription; + + desc = VideoSSRCHack.mungeLocalVideoSSRC(desc); + this.trace('getLocalDescription::preTransform', dumpSDP(desc)); // if we're running on FF, transform to Plan B first. @@ -359,14 +363,17 @@ TraceablePeerConnection.prototype.createOffer this.peerconnection.createOffer( function (offer) { self.trace('createOfferOnSuccess::preTransform', dumpSDP(offer)); - // if we're running on FF, transform to Plan B first. // NOTE this is not tested because in meet the focus generates the // offer. + + // if we're running on FF, transform to Plan B first. if (RTCBrowserType.usesUnifiedPlan()) { offer = self.interop.toPlanB(offer); self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer)); } + offer = VideoSSRCHack.mungeLocalVideoSSRC(offer); + if (config.enableSimulcast && self.simulcast.isSupported()) { offer = self.simulcast.mungeLocalDescription(offer); self.trace('createOfferOnSuccess::postTransform (simulcast)', dumpSDP(offer)); @@ -393,6 +400,10 @@ TraceablePeerConnection.prototype.createAnswer answer = self.interop.toPlanB(answer); self.trace('createAnswerOnSuccess::postTransform (Plan B)', dumpSDP(answer)); } + + // munge local video SSRC + answer = VideoSSRCHack.mungeLocalVideoSSRC(answer); + if (config.enableSimulcast && self.simulcast.isSupported()) { answer = self.simulcast.mungeLocalDescription(answer); self.trace('createAnswerOnSuccess::postTransform (simulcast)', dumpSDP(answer)); diff --git a/modules/xmpp/VideoSSRCHack.js b/modules/xmpp/VideoSSRCHack.js new file mode 100644 index 000000000..9e28d82d9 --- /dev/null +++ b/modules/xmpp/VideoSSRCHack.js @@ -0,0 +1,169 @@ +/* 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: + + 1. Stores the SSRC of the first video stream created by + a) scanning Jingle session-accept/session-invite for existing video SSRC + b) watching for 'source-add' for new video stream if it has not been + created in step a) + 2. Exposes method 'mungeLocalVideoSSRC' which replaces any new video SSRC with + the stored one. It is called by 'TracablePeerConnection' before local SDP is + returned to the other parts of the application. + 3. Scans 'source-remove'/'source-add' notifications for stored video SSRC and + blocks those notifications. This makes Jicofo and all participants think + that it exists all the time even if the video stream has been removed or + replaced locally. Thanks to that there is no additional signaling activity + on video mute or when switching to the desktop stream. + */ + +var SDP = require('./SDP'); + +/** + * Stored SSRC of local video stream. + */ +var localVideoSSRC; + +/** + * Method removes element which describes localVideoSSRC + * from given Jingle IQ. + * @param modifyIq 'source-add' or 'source-remove' Jingle IQ. + * @param actionName display name of the action which will be printed in log + * messages. + * @returns {*} modified Jingle IQ, so that it does not contain element + * corresponding to localVideoSSRC or null if no + * other SSRCs left to be signaled after removing it. + */ +var filterOutSource = function (modifyIq, actionName) { + if (!localVideoSSRC) + return modifyIq; + + var modifyIqTree = $(modifyIq.tree()); + var videoSSRC = modifyIqTree.find( + '>jingle>content[name="video"]' + + '>description>source[ssrc="' + localVideoSSRC + '"]'); + + if (!videoSSRC.length) { + return modifyIqTree; + } + + console.info( + 'Blocking ' + actionName + ' for local video SSRC: ' + localVideoSSRC); + + videoSSRC.remove(); + + // Check if any sources still left to be added/removed + if (modifyIqTree.find('>jingle>content>description>source').length) { + return modifyIqTree; + } else { + return null; + } +}; + +/** + * Scans given Jingle IQ for video SSRC and stores it. + * @param jingleIq the Jingle IQ to be scanned for video SSRC. + */ +var storeLocalVideoSSRC = function (jingleIq) { + var videoSSRCs = + $(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 element'); + } +}; + +var LocalVideoSSRCHack = { + /** + * Method must be called before 'session-initiate' or 'session-invite' is + * sent. Scans the IQ for local video SSRC and stores it if detected. + * + * @param sessionInit our 'session-initiate' or 'session-accept' Jingle IQ + * which will be scanned for local video SSRC. + */ + processSessionInit: function (sessionInit) { + if (localVideoSSRC) { + console.error("Local SSRC stored already: " + localVideoSSRC); + return; + } + storeLocalVideoSSRC(sessionInit); + }, + /** + * If we have local video SSRC stored searched given + * localDescription for video SSRC and makes sure it is replaced + * with the stored one. + * @param localDescription local description object that will have local + * video SSRC replaced with the stored one + * @returns modified localDescription object. + */ + mungeLocalVideoSSRC: function (localDescription) { + // IF we have local video SSRC stored make sure it is replaced + // with old SSRC + if (localVideoSSRC) { + var newSdp = new SDP(localDescription.sdp); + if (newSdp.media[1].indexOf("a=ssrc:") !== -1 && + !newSdp.containsSSRC(localVideoSSRC)) { + // Get new video SSRC + var map = newSdp.getMediaSsrcMap(); + var videoPart = map[1]; + var videoSSRCs = videoPart.ssrcs; + var newSSRC = Object.keys(videoSSRCs)[0]; + + console.info( + "Replacing new video SSRC: " + newSSRC + + " with " + localVideoSSRC); + + localDescription.sdp = + newSdp.raw.replace( + new RegExp('a=ssrc:' + newSSRC, 'g'), + 'a=ssrc:' + localVideoSSRC); + } + } + return localDescription; + }, + /** + * Method must be called before 'source-add' notification is sent. In case + * we have local video SSRC advertised already it will be removed from the + * notification. If no other SSRCs are described by given IQ null will be + * returned which means that there is no point in sending the notification. + * @param sourceAdd 'source-add' Jingle IQ to be processed + * @returns modified 'source-add' IQ which can be sent to the focus or + * null if no notification shall be sent. It is no longer + * a Strophe IQ Builder instance, but DOM element tree. + */ + processSourceAdd: function (sourceAdd) { + if (!localVideoSSRC) { + // Store local SSRC if available + storeLocalVideoSSRC(sourceAdd); + return sourceAdd; + } else { + return filterOutSource(sourceAdd, 'source-add'); + } + }, + /** + * Method must be called before 'source-remove' notification is sent. + * Removes local video SSRC from the notification. If there are no other + * SSRCs described in the given IQ null will be returned which + * means that there is no point in sending the notification. + * @param sourceRemove 'source-remove' Jingle IQ to be processed + * @returns modified 'source-remove' IQ which can be sent to the focus or + * null if no notification shall be sent. It is no longer + * a Strophe IQ Builder instance, but DOM element tree. + */ + processSourceRemove: function (sourceRemove) { + return filterOutSource(sourceRemove, 'source-remove'); + } +}; + +module.exports = LocalVideoSSRCHack; diff --git a/modules/xmpp/strophe.emuc.js b/modules/xmpp/strophe.emuc.js index 8c70051ce..866d03af5 100644 --- a/modules/xmpp/strophe.emuc.js +++ b/modules/xmpp/strophe.emuc.js @@ -174,6 +174,16 @@ module.exports = function(XMPP, eventEmitter) { Strophe.getResourceFromJid(from), devicesValues); } + var videoType = $(pres).find('>videoType'); + if (videoType.length) + { + if (videoType.text().length) + { + eventEmitter.emit(XMPPEvents.VIDEO_TYPE, + Strophe.getResourceFromJid(from), videoType.text()); + } + } + var stats = $(pres).find('>stats'); if (stats.length) { var statsObj = {}; @@ -482,6 +492,11 @@ module.exports = function(XMPP, eventEmitter) { .t(this.presMap['videomuted']).up(); } + if (this.presMap['videoTypeNs']) { + pres.c('videoType', { xmlns: this.presMap['videoTypeNs'] }) + .t(this.presMap['videoType']).up(); + } + if (this.presMap['statsns']) { var stats = pres.c('stats', {xmlns: this.presMap['statsns']}); for (var stat in this.presMap["stats"]) @@ -514,6 +529,14 @@ module.exports = function(XMPP, eventEmitter) { addDevicesToPresence: function (devices) { this.presMap['devices'] = devices; }, + /** + * Adds the info about the type of our video stream. + * @param videoType 'camera' or 'screen' + */ + addVideoTypeToPresence: function (videoType) { + this.presMap['videoTypeNs'] = 'http://jitsi.org/jitmeet/video'; + this.presMap['videoType'] = videoType; + }, addPreziToPresence: function (url, currentSlide) { this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi'; this.presMap['preziurl'] = url; diff --git a/modules/xmpp/xmpp.js b/modules/xmpp/xmpp.js index 8ca0f1553..38ab670d2 100644 --- a/modules/xmpp/xmpp.js +++ b/modules/xmpp/xmpp.js @@ -171,9 +171,28 @@ function initStrophePlugins() require("./strophe.logger")(); } +/** + * If given localStream is video one this method will advertise it's + * video type in MUC presence. + * @param localStream new or modified LocalStream. + */ +function broadcastLocalVideoType(localStream) { + if (localStream.videoType) + XMPP.addToPresence('videoType', localStream.videoType); +} + function registerListeners() { - APP.RTC.addStreamListener(maybeDoJoin, - StreamEventTypes.EVENT_TYPE_LOCAL_CREATED); + APP.RTC.addStreamListener( + function (localStream) { + maybeDoJoin(); + broadcastLocalVideoType(localStream); + }, + StreamEventTypes.EVENT_TYPE_LOCAL_CREATED + ); + APP.RTC.addStreamListener( + broadcastLocalVideoType, + StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED + ); APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) { XMPP.addToPresence("devices", devices); }); @@ -439,6 +458,9 @@ var XMPP = { case "devices": connection.emuc.addDevicesToPresence(value); break; + case "videoType": + connection.emuc.addVideoTypeToPresence(value); + break; case "startMuted": if(!Moderator.isModerator()) return; diff --git a/service/RTC/StreamEventTypes.js b/service/RTC/StreamEventTypes.js index 51b678fb0..2c611bce4 100644 --- a/service/RTC/StreamEventTypes.js +++ b/service/RTC/StreamEventTypes.js @@ -7,9 +7,7 @@ var StreamEventTypes = { EVENT_TYPE_REMOTE_CREATED: "stream.remote_created", - EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended", - - EVENT_TYPE_REMOTE_CHANGED: "stream.changed" + EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended" }; module.exports = StreamEventTypes; \ No newline at end of file diff --git a/service/xmpp/XMPPEvents.js b/service/xmpp/XMPPEvents.js index 5ae1813c0..f7bed70a3 100644 --- a/service/xmpp/XMPPEvents.js +++ b/service/xmpp/XMPPEvents.js @@ -32,6 +32,7 @@ var XMPPEvents = { CHAT_ERROR_RECEIVED: "xmpp.chat_error_received", ETHERPAD: "xmpp.etherpad", DEVICE_AVAILABLE: "xmpp.device_available", + VIDEO_TYPE: "xmpp.video_type", PEERCONNECTION_READY: "xmpp.peerconnection_ready", CONFERENCE_SETUP_FAILED: "xmpp.conference_setup_failed", AUDIO_MUTED: "xmpp.audio_muted",