diff --git a/JitsiConference.js b/JitsiConference.js index f05f315b8..662c27f50 100644 --- a/JitsiConference.js +++ b/JitsiConference.js @@ -3,13 +3,13 @@ var logger = require("jitsi-meet-logger").getLogger(__filename); var RTC = require("./modules/RTC/RTC"); var XMPPEvents = require("./service/xmpp/XMPPEvents"); -var StreamEventTypes = require("./service/RTC/StreamEventTypes"); var RTCEvents = require("./service/RTC/RTCEvents"); var EventEmitter = require("events"); var JitsiConferenceEvents = require("./JitsiConferenceEvents"); var JitsiParticipant = require("./JitsiParticipant"); var Statistics = require("./modules/statistics/statistics"); var JitsiDTMFManager = require('./modules/DTMF/JitsiDTMFManager'); +var JitsiTrackEvents = require("./JitsiTrackEvents"); /** * Creates a JitsiConference object with the given name and properties. @@ -33,7 +33,7 @@ function JitsiConference(options) { this.room = this.xmpp.createRoom(this.options.name, null, null, this.options.config); this.room.updateDeviceAvailability(RTC.getDeviceAvailability()); this.rtc = new RTC(this.room, options); - if(!options.config.disableAudioLevels) + if(!RTC.options.disableAudioLevels) this.statistics = new Statistics(); setupListeners(this); this.participants = {}; @@ -102,7 +102,8 @@ JitsiConference.prototype.addEventListener = JitsiConference.prototype.on; JitsiConference.prototype.removeEventListener = JitsiConference.prototype.off; /** - * Receives notifications from another participants for commands / custom events(send by sendPresenceCommand method). + * Receives notifications from another participants for commands / custom events + * (send by sendPresenceCommand method). * @param command {String} the name of the command * @param handler {Function} handler for the command */ @@ -168,11 +169,7 @@ JitsiConference.prototype.removeCommand = function (name) { */ JitsiConference.prototype.setDisplayName = function(name) { if(this.room){ - this.room.addToPresence("nick", { - attributes: { - xmlns: 'http://jabber.org/protocol/nick' - }, value: name - }); + this.room.addToPresence("nick", {attributes: {xmlns: 'http://jabber.org/protocol/nick'}, value: name}); this.room.sendPresence(); } }; @@ -182,8 +179,42 @@ JitsiConference.prototype.setDisplayName = function(name) { * @param track the JitsiLocalTrack object. */ JitsiConference.prototype.addTrack = function (track) { - this.rtc.addLocalStream(track); - this.room.addStream(track.getOriginalStream(), function () {}); + this.room.addStream(track.getOriginalStream(), function () { + this.rtc.addLocalStream(track); + 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); + }); + this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track); + }.bind(this)); +}; + +/** + * Fires TRACK_AUDIO_LEVEL_CHANGED change conference event. + * @param audioLevel the audio level + */ +JitsiConference.prototype._fireAudioLevelChangeEvent = function (audioLevel) { + this.eventEmitter.emit( + JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, + this.myUserId(), audioLevel); +}; + +/** + * Fires TRACK_MUTE_CHANGED change conference event. + * @param track the JitsiTrack object related to the event. + */ +JitsiConference.prototype._fireMuteChangeEvent = function (track) { + this.eventEmitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track); }; /** @@ -191,8 +222,10 @@ JitsiConference.prototype.addTrack = function (track) { * @param track the JitsiLocalTrack object. */ JitsiConference.prototype.removeTrack = function (track) { - this.room.removeStream(track.getOriginalStream()); - this.rtc.removeLocalStream(track); + this.room.removeStream(track.getOriginalStream(), function(){ + this.rtc.removeLocalStream(track); + this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track); + }.bind(this)); }; /** @@ -289,22 +322,41 @@ JitsiConference.prototype.onDisplayNameChanged = function (jid, displayName) { this.eventEmitter.emit(JitsiConferenceEvents.DISPLAY_NAME_CHANGED, id, displayName); }; - JitsiConference.prototype.onTrackAdded = function (track) { var id = track.getParticipantId(); var participant = this.getParticipantById(id); - participant._tracks.push(track); - this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track); -}; - -JitsiConference.prototype.onTrackRemoved = function (track) { - var id = track.getParticipantId(); - var participant = this.getParticipantById(id); - var pos = participant._tracks.indexOf(track); - if (pos > -1) { - participant._tracks.splice(pos, 1); + if (!participant) { + return; } - this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track); + // add track to JitsiParticipant + participant._tracks.push(track); + + var emitter = this.eventEmitter; + track.addEventListener( + JitsiTrackEvents.TRACK_STOPPED, + function () { + // remove track from JitsiParticipant + var pos = participant._tracks.indexOf(track); + if (pos > -1) { + participant._tracks.splice(pos, 1); + } + emitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track); + } + ); + track.addEventListener( + JitsiTrackEvents.TRACK_MUTE_CHANGED, + function () { + emitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track); + } + ); + track.addEventListener( + JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, + function (audioLevel) { + emitter.emit(JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, id, audioLevel); + } + ); + + this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track); }; JitsiConference.prototype.updateDTMFSupport = function () { @@ -372,8 +424,15 @@ function setupListeners(conference) { if(conference.statistics) conference.statistics.startRemoteStats(event.peerconnection); }); + conference.room.addListener(XMPPEvents.REMOTE_STREAM_RECEIVED, - conference.rtc.createRemoteStream.bind(conference.rtc)); + function (data, sid, thessrc) { + var track = conference.rtc.createRemoteStream(data, sid, thessrc); + if (track) { + conference.onTrackAdded(track); + } + } + ); conference.room.addListener(XMPPEvents.MUC_JOINED, function () { conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED); @@ -404,24 +463,6 @@ function setupListeners(conference) { conference.eventEmitter.emit(JitsiConferenceEvents.SETUP_FAILED); }); - conference.rtc.addListener( - StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, conference.onTrackAdded.bind(conference) - ); - -//FIXME: Maybe remove event should not be associated with the conference. - conference.rtc.addListener( - StreamEventTypes.EVENT_TYPE_REMOTE_ENDED, conference.onTrackRemoved.bind(conference) - ); -//FIXME: Maybe remove event should not be associated with the conference. - conference.rtc.addListener(StreamEventTypes.EVENT_TYPE_LOCAL_ENDED, function (stream) { - conference.removeTrack(stream); - conference.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, stream); - }); - - conference.rtc.addListener(StreamEventTypes.TRACK_MUTE_CHANGED, function (track) { - conference.eventEmitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track); - }); - conference.rtc.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (id) { if(conference.lastActiveSpeaker !== id && conference.room) { conference.lastActiveSpeaker = id; @@ -443,20 +484,11 @@ function setupListeners(conference) { //FIXME: Maybe remove event should not be associated with the conference. conference.statistics.addAudioLevelListener(function (ssrc, level) { var userId = null; - if (ssrc === Statistics.LOCAL_JID) { - userId = conference.myUserId(); - } else { - var jid = conference.room.getJidBySSRC(ssrc); - if (!jid) - return; + var jid = conference.room.getJidBySSRC(ssrc); + if (!jid) + return; - userId = Strophe.getResourceFromJid(jid); - } - conference.eventEmitter.emit(JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, - userId, level); - }); - conference.rtc.addListener(StreamEventTypes.EVENT_TYPE_LOCAL_CREATED, function (stream) { - conference.statistics.startLocalStats(stream); + conference.rtc.setAudioLevel(jid, level); }); conference.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, function () { diff --git a/JitsiMeetJS.js b/JitsiMeetJS.js index 4659357b0..24d5be77e 100644 --- a/JitsiMeetJS.js +++ b/JitsiMeetJS.js @@ -7,6 +7,7 @@ var JitsiTrackEvents = require("./JitsiTrackEvents"); var JitsiTrackErrors = require("./JitsiTrackErrors"); var Logger = require("jitsi-meet-logger"); var RTC = require("./modules/RTC/RTC"); +var Statistics = require("./modules/statistics/statistics"); /** * Namespace for the interface of Jitsi Meet Library. @@ -42,11 +43,29 @@ var LibJitsiMeet = { * will be returned trough the Promise, otherwise JitsiTrack objects will be returned. * @param {string} options.cameraDeviceId * @param {string} options.micDeviceId - * @returns {Promise.<{Array.}, JitsiConferenceError>} A promise that returns an array of created JitsiTracks if resolved, + * @returns {Promise.<{Array.}, JitsiConferenceError>} + * A promise that returns an array of created JitsiTracks if resolved, * or a JitsiConferenceError if rejected. */ createLocalTracks: function (options) { - return RTC.obtainAudioAndVideoPermissions(options || {}); + return RTC.obtainAudioAndVideoPermissions(options || {}).then( + function(tracks) { + if(!RTC.options.disableAudioLevels) + for(var i = 0; i < tracks.length; i++) { + var track = tracks[i]; + var mStream = track.getOriginalStream(); + if(track.getType() === "audio"){ + Statistics.startLocalStats(mStream, + track.setAudioLevel.bind(track)); + track.addEventListener( + JitsiTrackEvents.TRACK_STOPPED, + function(){ + Statistics.stopLocalStats(mStream); + }); + } + } + return tracks; + }); }, isDeviceListAvailable: function () { return RTC.isDeviceListAvailable(); diff --git a/JitsiTrackEvents.js b/JitsiTrackEvents.js index b7b229efe..cace3e680 100644 --- a/JitsiTrackEvents.js +++ b/JitsiTrackEvents.js @@ -10,7 +10,7 @@ var JitsiTrackEvents = { /** * The media track was removed to the conference. */ - TRACK_STOPPED: "track.TRACK_STOPPED", + TRACK_STOPPED: "track.stopped" }; module.exports = JitsiTrackEvents; diff --git a/doc/example/example.js b/doc/example/example.js index 9c165513c..06f82f389 100644 --- a/doc/example/example.js +++ b/doc/example/example.js @@ -9,8 +9,7 @@ var options = { } var confOptions = { - openSctp: true, - disableAudioLevels: true + openSctp: true } /** @@ -22,6 +21,18 @@ function onLocalTracks(tracks) localTracks = tracks; for(var i = 0; i < localTracks.length; i++) { + localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED, + function (audioLevel) { + console.debug("Audio Level local: " + audioLevel); + }); + localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED, + function () { + console.debug("local track muted"); + }); + localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED, + function () { + console.debug("local track stoped"); + }); if(localTracks[i].getType() == "video") { $("body").append("