diff --git a/app.js b/app.js index 33a6b0e7d..3ace8c34c 100644 --- a/app.js +++ b/app.js @@ -223,7 +223,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) { var sess = connection.jingle.sessions[sid]; if (data.stream.id === 'mixedmslabel') return; var videoTracks = data.stream.getVideoTracks(); - console.log("waiting..", videoTracks, selector[0]); +// console.log("waiting..", videoTracks, selector[0]); if (videoTracks.length === 0 || selector[0].currentTime > 0) { RTC.attachMediaStream(selector, data.stream); // FIXME: why do i have to do this for FF? @@ -673,6 +673,32 @@ $(document).bind('passwordrequired.muc', function (event, jid) { }); }); +$(document).bind('audiomuted.muc', function (event, jid, isMuted) { + var videoSpanId = null; + if (jid === connection.emuc.myroomjid) { + videoSpanId = 'localVideoContainer'; + } else { + ensurePeerContainerExists(jid); + videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid); + } + + if (videoSpanId) + showAudioIndicator(videoSpanId, isMuted); +}); + +$(document).bind('videomuted.muc', function (event, jid, isMuted) { + var videoSpanId = null; + if (jid === connection.emuc.myroomjid) { + videoSpanId = 'localVideoContainer'; + } else { + ensurePeerContainerExists(jid); + videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid); + } + + if (videoSpanId) + showAudioIndicator(videoSpanId, isMuted); +}); + /** * Updates the large video with the given new video source. */ @@ -744,11 +770,11 @@ function isVideoSrcDesktop(videoSrc){ function setLargeVideoVisible(isVisible) { if (isVisible) { $('#largeVideo').css({visibility:'visible'}); - $('#watermark').css({visibility:'visible'}); + $('.watermark').css({visibility:'visible'}); } else { $('#largeVideo').css({visibility:'hidden'}); - $('#watermark').css({visibility:'hidden'}); + $('.watermark').css({visibility:'hidden'}); } } @@ -757,35 +783,49 @@ function getConferenceHandler() { } function toggleVideo() { - if (!(connection && connection.jingle.localVideo)) return; + if (!(connection && connection.jingle.localVideo)) + return; var sess = getConferenceHandler(); if (sess) { sess.toggleVideoMute( function(isMuted){ if(isMuted) { - $('#video').removeClass("fa fa-video-camera fa-lg"); - $('#video').addClass("fa fa-video-camera no-fa-video-camera fa-lg"); + $('#video').removeClass("icon-camera"); + $('#video').addClass("icon-camera icon-camera-disabled"); } else { - $('#video').removeClass("fa fa-video-camera no-fa-video-camera fa-lg"); - $('#video').addClass("fa fa-video-camera fa-lg"); + $('#video').removeClass("icon-camera icon-camera-disabled"); + $('#video').addClass("icon-camera"); } } ); } + var sess = focus || activecall; if (!sess) { return; } + sess.pendingop = ismuted ? 'unmute' : 'mute'; +// connection.emuc.addVideoInfoToPresence(!ismuted); +// connection.emuc.sendPresence(); + sess.modifySources(); } +/** + * Mutes / unmutes audio for the local participant. + */ function toggleAudio() { - if (!(connection && connection.jingle.localAudio)) return; + if (!(connection && connection.jingle.localAudio)) + return; var localAudio = connection.jingle.localAudio; for (var idx = 0; idx < localAudio.getAudioTracks().length; idx++) { - localAudio.getAudioTracks()[idx].enabled = !localAudio.getAudioTracks()[idx].enabled; + var audioEnabled = localAudio.getAudioTracks()[idx].enabled; + + localAudio.getAudioTracks()[idx].enabled = !audioEnabled; + connection.emuc.addAudioInfoToPresence(audioEnabled); //isMuted is the opposite of audioEnabled + connection.emuc.sendPresence(); } } @@ -1561,6 +1601,62 @@ function createEditDisplayNameButton() { return editButton; } +/** + * Shows audio muted indicator over small videos. + */ +function showAudioIndicator(videoSpanId, isMuted) { + var audioMutedSpan = $('#' + videoSpanId + '>span.audioMuted'); + + if (isMuted === 'false') { + if (audioMutedSpan.length > 0) { + audioMutedSpan.remove(); + } + } + else { + var videoMutedSpan = $('#' + videoSpanId + '>span.videoMuted'); + + audioMutedSpan = document.createElement('span'); + audioMutedSpan.className = 'audioMuted'; + if (videoMutedSpan) { + audioMutedSpan.right = '30px'; + } + $('#' + videoSpanId)[0].appendChild(audioMutedSpan); + + var mutedIndicator = document.createElement('i'); + mutedIndicator.className = 'icon-mic-disabled'; + mutedIndicator.title = "Participant is muted"; + audioMutedSpan.appendChild(mutedIndicator); + } +} + +/** + * Shows video muted indicator over small videos. + */ +function showVideoIndicator(videoSpanId, isMuted) { + var videoMutedSpan = $('#' + videoSpanId + '>span.videoMuted'); + + if (isMuted === 'false') { + if (videoMutedSpan.length > 0) { + videoMutedSpan.remove(); + } + } + else { + var audioMutedSpan = $('#' + videoSpanId + '>span.audioMuted'); + + videoMutedSpan = document.createElement('span'); + videoMutedSpan.className = 'videoMuted'; + if (audioMutedSpan) { + videoMutedSpan.right = '30px'; + } + $('#' + videoSpanId)[0].appendChild(videoMutedSpan); + + var mutedIndicator = document.createElement('i'); + mutedIndicator.className = 'icon-camera-disabled'; + mutedIndicator.title = "Participant has stopped the camera."; + videoMutedSpan.appendChild(mutedIndicator); + } +} + /** * Resizes and repositions videos in full screen mode. */ diff --git a/css/videolayout_default.css b/css/videolayout_default.css index 92888af87..f69222a9e 100644 --- a/css/videolayout_default.css +++ b/css/videolayout_default.css @@ -178,6 +178,34 @@ z-index: 2; } +.videocontainer>span.audioMuted { + display: inline-block; + position: absolute; + color: #FFFFFF; + top: 0; + right: 0; + padding: 8px 5px; + width: 25px; + font-size: 8pt; + text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7); + border: 0px; + z-index: 3; +} + +.videocontainer>span.videoMuted { + display: inline-block; + position: absolute; + color: #FFFFFF; + top: 0; + right: 0; + padding: 8px 5px; + width: 25px; + font-size: 8pt; + text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7); + border: 0px; + z-index: 3; +} + #reloadPresentation { display: none; position: absolute; @@ -220,15 +248,25 @@ border-bottom-right-radius: 12px; } -#watermark { +.watermark { display: block; position: absolute; - left: 15; top: 15; width: 20%; height: 10%; - background-image:url(../images/watermark.png); background-size: contain; background-repeat: no-repeat; z-index: 2; } + +#leftwatermark { + left: 15; + background-image:url(../images/watermark.png); + background-position: center left; +} + +#rightwatermark { + right: 15; + background-image:url(../images/rightwatermark.png); + background-position: center right; +} \ No newline at end of file diff --git a/index.html b/index.html index f97d39c27..c25f95c9d 100644 --- a/index.html +++ b/index.html @@ -20,10 +20,10 @@ - + - + @@ -33,7 +33,7 @@ - +
diff --git a/muc.js b/muc.js index 1f84c974d..5d7497af6 100644 --- a/muc.js +++ b/muc.js @@ -72,6 +72,19 @@ Strophe.addConnectionPlugin('emuc', { $(document).trigger('presentationremoved.muc', [from, url]); } + // Parse audio info tag. + var audioMuted = $(pres).find('>audiomuted'); + if (audioMuted.length) { + $(document).trigger('audiomuted.muc', [from, audioMuted.text()]); + } + + // Parse video info tag. + var videoMuted = $(pres).find('>videomuted'); + if (videoMuted.length) { + $(document).trigger('videomuted.muc', [from, videoMuted.text()]); + } + + // Parse status. if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) { // http://xmpp.org/extensions/xep-0045.html#createroom-instant this.isOwner = true; @@ -81,6 +94,7 @@ Strophe.addConnectionPlugin('emuc', { this.connection.send(create); // fire away } + // Parse roles. var member = {}; member.show = $(pres).find('>show').text(); member.status = $(pres).find('>status').text(); @@ -186,16 +200,31 @@ Strophe.addConnectionPlugin('emuc', { if (this.presMap['displayName']) { // XEP-0172 - pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(this.presMap['displayName']).up(); + pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}) + .t(this.presMap['displayName']).up(); + } + + if (this.presMap['audions']) { + pres.c('audiomuted', {xmlns: this.presMap['audions']}) + .t(this.presMap['audiomuted']).up(); + } + + if (this.presMap['videons']) { + console.log("SEND VIDEO MUTED", this.presMap['videomuted']); + pres.c('videomuted', {xmlns: this.presMap['videons']}) + .t(this.presMap['videomuted']).up(); } if (this.presMap['prezins']) { - pres.c('prezi', {xmlns: this.presMap['prezins'], 'url': this.presMap['preziurl']}). - c('current').t(this.presMap['prezicurrent']).up().up(); + pres.c('prezi', + {xmlns: this.presMap['prezins'], + 'url': this.presMap['preziurl']}) + .c('current').t(this.presMap['prezicurrent']).up().up(); } if (this.presMap['etherpadns']) { - pres.c('etherpad', {xmlns: this.presMap['etherpadns']}).t(this.presMap['etherpadname']).up(); + pres.c('etherpad', {xmlns: this.presMap['etherpadns']}) + .t(this.presMap['etherpadname']).up(); } if (this.presMap['medians']) @@ -212,7 +241,8 @@ Strophe.addConnectionPlugin('emuc', { pres.c('source', {type: this.presMap['source' + i + '_type'], ssrc: this.presMap['source' + i + '_ssrc'], - direction: this.presMap['source'+ i + '_direction'] || 'sendrecv' } + direction: this.presMap['source'+ i + '_direction'] + || 'sendrecv' } ).up(); } } @@ -257,5 +287,13 @@ Strophe.addConnectionPlugin('emuc', { addEtherpadToPresence: function(etherpadName) { this.presMap['etherpadns'] = 'http://jitsi.org/jitmeet/etherpad'; this.presMap['etherpadname'] = etherpadName; + }, + addAudioInfoToPresence: function(isMuted) { + this.presMap['audions'] = 'http://jitsi.org/jitmeet/audio'; + this.presMap['audiomuted'] = isMuted.toString(); + }, + addVideoInfoToPresence: function(isMuted) { + this.presMap['videons'] = 'http://jitsi.org/jitmeet/video'; + this.presMap['videomuted'] = isMuted.toString(); } });