diff --git a/conference.js b/conference.js index 70ac0c0d2..7197e2f75 100644 --- a/conference.js +++ b/conference.js @@ -246,6 +246,7 @@ export default { _createRoom () { room = connection.initJitsiConference(APP.conference.roomName, this._getConferenceOptions()); + this.localId = room.myUserId(); localTracks.forEach((track) => { if(track.isAudioTrack()) { localAudio = track; @@ -258,7 +259,6 @@ export default { }); roomLocker = createRoomLocker(room); this._room = room; // FIXME do not use this - this.localId = room.myUserId(); let email = APP.settings.getEmail(); email && sendEmail(email); diff --git a/libs/lib-jitsi-meet.js b/libs/lib-jitsi-meet.js index 107381663..79e27c81b 100644 --- a/libs/lib-jitsi-meet.js +++ b/libs/lib-jitsi-meet.js @@ -258,20 +258,12 @@ JitsiConference.prototype.addTrack = function (track) { if (track.startMuted) { track.mute(); } - var muteHandler = this._fireMuteChangeEvent.bind(this, track); - var stopHandler = this.removeTrack.bind(this, track); - var audioLevelHandler = this._fireAudioLevelChangeEvent.bind(this); - track.addEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, muteHandler); - track.addEventListener(JitsiTrackEvents.TRACK_STOPPED, stopHandler); - track.addEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, audioLevelHandler); - this.addEventListener(JitsiConferenceEvents.TRACK_REMOVED, function (someTrack) { - if (someTrack !== track) { - return; - } - track.removeEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, muteHandler); - track.removeEventListener(JitsiTrackEvents.TRACK_STOPPED, stopHandler); - track.removeEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, audioLevelHandler); - }); + track.muteHandler = this._fireMuteChangeEvent.bind(this, track); + track.stopHandler = this.removeTrack.bind(this, track); + track.audioLevelHandler = this._fireAudioLevelChangeEvent.bind(this); + track.addEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, track.muteHandler); + track.addEventListener(JitsiTrackEvents.TRACK_STOPPED, track.stopHandler); + track.addEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, track.audioLevelHandler); this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track); }.bind(this)); }; @@ -306,6 +298,9 @@ JitsiConference.prototype.removeTrack = function (track) { } this.room.removeStream(track.getOriginalStream(), function(){ this.rtc.removeLocalStream(track); + track.removeEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, track.muteHandler); + track.removeEventListener(JitsiTrackEvents.TRACK_STOPPED, track.stopHandler); + track.removeEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, track.audioLevelHandler); this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track); }.bind(this)); }; @@ -1303,7 +1298,6 @@ var LibJitsiMeet = { } }; -require("es6-promise").polyfill() //Setups the promise object. window.Promise = window.Promise || require("es6-promise").Promise; @@ -1601,7 +1595,7 @@ DataChannels.prototype.onDataChannel = function (event) { // selections so that it can do adaptive simulcast, // we want the notification to trigger even if userJid is undefined, // or null. - this.handleSelectedEndpointEvent(this.lastSelectedEndpoint); + self.handleSelectedEndpointEvent(self.lastSelectedEndpoint); }; dataChannel.onerror = function (error) { @@ -1959,6 +1953,10 @@ JitsiRemoteTrack.prototype.constructor = JitsiRemoteTrack; * @param value the muted status. */ JitsiRemoteTrack.prototype.setMute = function (value) { + + if(this.muted === value) + return; + this.stream.muted = value; this.muted = value; this.eventEmitter.emit(JitsiTrackEvents.TRACK_MUTE_CHANGED); @@ -5187,6 +5185,14 @@ var WEBAUDIO_ANALYZER_FFT_SIZE = 2048; */ var WEBAUDIO_ANALYZER_SMOOTING_TIME = 0.8; +window.AudioContext = window.AudioContext || window.webkitAudioContext; + +var context = null; + +if(window.AudioContext) { + context = new AudioContext(); +} + /** * Converts time domain data array to audio level. * @param samples the time domain data array. @@ -5238,7 +5244,6 @@ function animateLevel(newLevel, lastLevel) { * @constructor */ function LocalStatsCollector(stream, interval, callback) { - window.AudioContext = window.AudioContext || window.webkitAudioContext; this.stream = stream; this.intervalId = null; this.intervalMilis = interval; @@ -5250,16 +5255,14 @@ function LocalStatsCollector(stream, interval, callback) { * Starts the collecting the statistics. */ LocalStatsCollector.prototype.start = function () { - if (!window.AudioContext || + if (!context || RTCBrowserType.isTemasysPluginUsed()) return; - var context = new AudioContext(); var analyser = context.createAnalyser(); analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME; analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE; - var source = context.createMediaStreamSource(this.stream); source.connect(analyser); diff --git a/modules/UI/videolayout/LargeVideo.js b/modules/UI/videolayout/LargeVideo.js index fa9aabd70..79699e354 100644 --- a/modules/UI/videolayout/LargeVideo.js +++ b/modules/UI/videolayout/LargeVideo.js @@ -341,7 +341,7 @@ export default class LargeVideoManager { return this.videoContainer.id; } - updateLargeVideo (stream, videoType) { + updateLargeVideo (stream, videoType, largeVideoUpdatedCallBack) { let id = getStreamId(stream); let container = this.getContainer(this.state); @@ -351,6 +351,7 @@ export default class LargeVideoManager { this.state = VideoContainerType; this.videoContainer.setStream(stream, videoType); this.videoContainer.show(); + largeVideoUpdatedCallBack(); }); } diff --git a/modules/UI/videolayout/LocalVideo.js b/modules/UI/videolayout/LocalVideo.js index 3250e4b78..eac370a65 100644 --- a/modules/UI/videolayout/LocalVideo.js +++ b/modules/UI/videolayout/LocalVideo.js @@ -17,6 +17,7 @@ function LocalVideo(VideoLayout, emitter) { this.flipX = true; this.isLocal = true; this.emitter = emitter; + SmallVideo.call(this); } LocalVideo.prototype = Object.create(SmallVideo.prototype); diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index 2b09f67f8..9ac07a3a6 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -24,6 +24,7 @@ function RemoteVideo(id, VideoLayout, emitter) { this.bindHoverHandler(); this.flipX = false; this.isLocal = false; + SmallVideo.call(this); } RemoteVideo.prototype = Object.create(SmallVideo.prototype); @@ -274,10 +275,10 @@ RemoteVideo.prototype.showPeerContainer = function (state) { resizeThumbnails = true; $(this.container).show(); } - // Call showAvatar with undefined, so that we'll figure out if avatar + // Call updateView, so that we'll figure out if avatar // should be displayed based on video muted status and whether or not // it's in the lastN set - this.showAvatar(undefined); + this.updateView(); } else if ($(this.container).is(':visible') && isHide) { diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index 824eb59f1..3be2c8410 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -2,13 +2,13 @@ /* jshint -W101 */ import Avatar from "../avatar/Avatar"; import UIUtil from "../util/UIUtil"; -import LargeVideo from "./LargeVideo"; var RTCBrowserType = require("../../RTC/RTCBrowserType"); function SmallVideo() { this.isMuted = false; this.hasAvatar = false; + this.isVideoMuted = false; this.stream = null; } @@ -203,10 +203,12 @@ SmallVideo.prototype.showAudioIndicator = function(isMuted) { }; /** - * Shows video muted indicator over small videos. + * Shows video muted indicator over small videos and disables/enables avatar + * if video muted. */ -SmallVideo.prototype.showVideoIndicator = function(isMuted) { - this.showAvatar(isMuted); +SmallVideo.prototype.setMutedView = function(isMuted) { + this.isVideoMuted = isMuted; + this.updateView(); var videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted'); @@ -233,36 +235,9 @@ SmallVideo.prototype.showVideoIndicator = function(isMuted) { } this.updateIconPositions(); - } }; -SmallVideo.prototype.enableDominantSpeaker = function (isEnable) { - var displayName = this.id; - var nameSpan = $('#' + this.videoSpanId + '>span.displayname'); - if (nameSpan.length > 0) - displayName = nameSpan.html(); - - console.log("UI enable dominant speaker", - displayName, - this.id, - isEnable); - - - if (!this.container) { - return; - } - - if (isEnable) { - this.showDisplayName(this.VideoLayout.isLargeVideoVisible()); - } - else { - this.showDisplayName(false); - } - - this.showAvatar(); -}; - SmallVideo.prototype.updateIconPositions = function () { var audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted'); var connectionIndicator = $('#' + this.videoSpanId + '>div.connectionindicator'); @@ -310,15 +285,15 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () { }; SmallVideo.prototype.selectVideoElement = function () { - return $('#' + this.videoSpanId).find(videoElem); - // FIXME maybe move this to the library? - var videoElem = APP.RTC.getVideoElementName(); + var videoElemName; if (!RTCBrowserType.isTemasysPluginUsed()) { - return $('#' + this.videoSpanId).find(videoElem); + videoElemName = 'video'; + return $('#' + this.videoSpanId).find(videoElemName); } else { + videoElemName = 'object'; var matching = $('#' + this.videoSpanId + (this.isLocal ? '>>' : '>') + - videoElem + '>param[value="video"]'); + videoElemName + '>param[value="video"]'); if (matching.length < 2) { return matching.parent(); } @@ -352,11 +327,12 @@ SmallVideo.prototype.hasVideo = function () { }; /** - * Hides or shows the user's avatar + * Hides or shows the user's avatar. + * * @param show whether we should show the avatar or not * video because there is no dominant speaker and no focused speaker */ -SmallVideo.prototype.showAvatar = function (show) { +SmallVideo.prototype.updateView = function () { if (!this.hasAvatar) { if (this.id) { // Init avatar @@ -371,28 +347,31 @@ SmallVideo.prototype.showAvatar = function (show) { let avatar = $(`#avatar_${this.id}`); - if (show === undefined || show === null) { - if (!this.isLocal && - !this.VideoLayout.isInLastN(this.id)) { - show = true; - } else { - // We want to show the avatar when the video is muted or not exists - // that is when 'true' or 'null' is returned - show = !this.stream || this.stream.isMuted(); - } + var showVideo = !this.isVideoMuted + && !this.VideoLayout.isCurrentlyOnLarge(this.id); + var showAvatar; + if ((!this.isLocal && + !this.VideoLayout.isInLastN(this.id)) || + this.isVideoMuted) { + showAvatar = true; + } else { + // We want to show the avatar when the video is muted or not exists + // that is when 'true' or 'null' is returned + showAvatar = !this.stream || this.stream.isMuted(); } - if (this.VideoLayout.isCurrentlyOnLarge(this.id) - && this.VideoLayout.isLargeVideoVisible()) { + if (video && video.length > 0) { + setVisibility(video, showVideo); + } + setVisibility(avatar, showAvatar); - this.VideoLayout.showLargeVideoAvatar(show); - setVisibility(avatar, false); - setVisibility(video, false); - } else { - if (video && video.length > 0) { - setVisibility(video, !show); - } - setVisibility(avatar, show); + var showDisplayName = !showVideo && !showAvatar; + + if (showDisplayName) { + this.showDisplayName(this.VideoLayout.isLargeVideoVisible()); + } + else { + this.showDisplayName(false); } }; diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 9d524fe82..2c89da939 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -2,6 +2,7 @@ /* jshint -W101 */ import AudioLevels from "../audio_levels/AudioLevels"; +import Avatar from "../avatar/Avatar"; import BottomToolbar from "../toolbars/BottomToolbar"; import UIEvents from "../../../service/UI/UIEvents"; @@ -262,6 +263,15 @@ var VideoLayout = { onRemoteStreamAdded (stream) { let id = stream.getParticipantId(); remoteVideos[id].addRemoteStreamElement(stream); + + // if track is muted make sure we reflect that + if(stream.isMuted()) + { + if(stream.getType() === "audio") + this.onAudioMute(stream.getParticipantId(), true); + else + this.onVideoMute(stream.getParticipantId(), true); + } }, /** @@ -499,10 +509,10 @@ var VideoLayout = { */ onVideoMute (id, value) { if (APP.conference.isLocalId(id)) { - localVideoThumbnail.showVideoIndicator(value); + localVideoThumbnail.setMutedView(value); } else { var remoteVideo = remoteVideos[id]; - remoteVideo.showVideoIndicator(value); + remoteVideo.setMutedView(value); var el = remoteVideo.selectVideoElement(); if (!value) @@ -510,6 +520,9 @@ var VideoLayout = { else el.hide(); } + + if(this.isCurrentlyOnLarge(id)) + largeVideo.showAvatar(value); }, /** @@ -960,26 +973,33 @@ var VideoLayout = { eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id); } if (currentId) { - let currentSmallVideo = this.getSmallVideo(currentId); - currentSmallVideo && currentSmallVideo.enableDominantSpeaker(false); + var oldSmallVideo = this.getSmallVideo(currentId); } let smallVideo = this.getSmallVideo(id); let videoType = this.getRemoteVideoType(id); - largeVideo.updateLargeVideo(smallVideo.stream, videoType); + largeVideo.updateLargeVideo( + smallVideo.stream, + videoType, + // LargeVideoUpdatedCallBack + function() { + // update current small video and the old one + smallVideo.updateView(); + oldSmallVideo && oldSmallVideo.updateView(); + + // change the avatar url on large + largeVideo.updateAvatar(Avatar.getThumbUrl(smallVideo.id)); + // show the avatar on large if needed + largeVideo.showAvatar(show); + }); - smallVideo.enableDominantSpeaker(true); } else if (currentId) { let currentSmallVideo = this.getSmallVideo(currentId); - currentSmallVideo.showAvatar(); + currentSmallVideo.updateView(); } }, - showLargeVideoAvatar (show) { - largeVideo && largeVideo.showAvatar(show); - }, - addLargeVideoContainer (type, container) { largeVideo && largeVideo.addContainer(type, container); },