Implements partly multi conference support for rtc module.

This commit is contained in:
hristoterezov 2015-08-30 01:15:05 -05:00
parent 1bba8d2032
commit 06a1472d1e
4 changed files with 271 additions and 231 deletions

View File

@ -1,3 +1,5 @@
var RTC = require("./modules/RTC/RTC");
var XMPPEvents = require("./service/xmpp/XMPPEvents");
/**
* Creates a JitsiConference object with the given name and properties.
@ -14,6 +16,10 @@ function JitsiConference(options) {
this.connection = this.options.connection;
this.xmpp = this.connection.xmpp;
this.room = this.xmpp.createRoom(this.options.name, null, null);
this.rtc = new RTC();
this.xmpp.addListener(XMPPEvents.CALL_INCOMING,
this.rtc.onIncommingCall.bind(this.rtc));
}
/**
@ -41,14 +47,14 @@ JitsiConference.prototype.leave = function () {
* or a JitsiConferenceError if rejected.
*/
JitsiConference.prototype.createLocalTracks = function (options) {
this.rtc.obtainAudioAndVideoPermissions();
}
/**
* Returns the local tracks.
*/
JitsiConference.prototype.getLocalTracks = function () {
return this.rtc.localStreams;
};
@ -61,7 +67,7 @@ JitsiConference.prototype.getLocalTracks = function () {
* Note: consider adding eventing functionality by extending an EventEmitter impl, instead of rolling ourselves
*/
JitsiConference.prototype.on = function (eventId, handler) {
this.add.addListener(eventId, handler);
this.room.addListener(eventId, handler);
}
/**
@ -147,7 +153,15 @@ JitsiConference.prototype.setDisplayName = function(name) {
* @param id the identifier of the participant
*/
JitsiConference.prototype.selectParticipant = function(participantId) {
this.rtc.selectedEndpoint(participantId);
}
/**
*
* @param id the identifier of the participant
*/
JitsiConference.prototype.pinParticipant = function(participantId) {
this.rtc.pinEndpoint(participantId);
}
/**

View File

@ -2,9 +2,9 @@
* Represents a single media track (either audio or video).
* @constructor
*/
function JitsiTrack()
function JitsiTrack(stream)
{
this.stream = stream;
}
/**
@ -23,34 +23,35 @@ JitsiTrack.AUDIO = "audio";
* Returns the type (audio or video) of this track.
*/
JitsiTrack.prototype.getType = function() {
return this.stream.type;
};
/**
* @returns {JitsiParticipant} to which this track belongs, or null if it is a local track.
*/
JitsiTrack.prototype.getParitcipant() = function() {
JitsiTrack.prototype.getParitcipant = function() {
};
/**
* Returns the RTCMediaStream from the browser (?).
*/
JitsiTrack.prototype.getOriginalStream() {
JitsiTrack.prototype.getOriginalStream = function() {
return this.stream.getOriginalStream();
}
/**
* Mutes the track.
*/
JitsiTrack.prototype.mute = function () {
this.stream.setMute(true);
}
/**
* Unmutes the stream.
*/
JitsiTrack.prototype.unmute = function () {
this.stream.setMute(false);
}
/**

View File

@ -12,9 +12,7 @@ var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
var RTCEvents = require("../../service/RTC/RTCEvents.js");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var UIEvents = require("../../service/UI/UIEvents");
var eventEmitter = new EventEmitter();
var desktopsharing = require("../desktopsharing/desktopsharing");
function getMediaStreamUsage()
{
@ -43,233 +41,263 @@ function getMediaStreamUsage()
return result;
}
var RTC = {
rtcUtils: null,
devices: {
function RTC()
{
this.rtcUtils = null;
this.devices = {
audio: true,
video: true
},
localStreams: [],
remoteStreams: {},
localAudio: null,
localVideo: null,
addStreamListener: function (listener, eventType) {
eventEmitter.on(eventType, listener);
},
addListener: function (type, listener) {
eventEmitter.on(type, listener);
},
removeStreamListener: function (listener, eventType) {
if(!(eventType instanceof StreamEventTypes))
throw "Illegal argument";
};
this.localStreams = [];
this.remoteStreams = {};
this.localAudio = null;
this.localVideo = null;
this.eventEmitter = new EventEmitter();
var self = this;
desktopsharing.addListener(
function (stream, isUsingScreenStream, callback) {
self.changeLocalVideo(stream, isUsingScreenStream, callback);
}, DesktopSharingEventTypes.NEW_STREAM_CREATED);
eventEmitter.removeListener(eventType, listener);
},
createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) {
// In case of IE we continue from 'onReady' callback
// passed to RTCUtils constructor. It will be invoked by Temasys plugin
// once it is initialized.
var onReady = function () {
self.eventEmitter.emit(RTCEvents.RTC_READY, true);
};
var localStream = new LocalStream(stream, type, eventEmitter, videoType, isGUMStream);
//in firefox we have only one stream object
if(this.localStreams.length == 0 ||
this.localStreams[0].getOriginalStream() != stream)
this.localStreams.push(localStream);
if(isMuted === true)
this.rtcUtils = new RTCUtils(this, onReady);
// Call onReady() if Temasys plugin is not used
if (!RTCBrowserType.isTemasysPluginUsed()) {
onReady();
}
}
RTC.prototype.obtainAudioAndVideoPermissions = function () {
this.rtcUtils.obtainAudioAndVideoPermissions(
null, null, getMediaStreamUsage());
}
RTC.prototype.onIncommingCall = function(event) {
DataChannels.init(event.peerconnection, self.eventEmitter);
}
RTC.prototype.selectedEndpoint = function (id) {
DataChannels.handleSelectedEndpointEvent(id);
}
RTC.prototype.pinEndpoint = function (id) {
DataChannels.handlePinnedEndpointEvent(id);
}
RTC.prototype.addStreamListener = function (listener, eventType) {
this.eventEmitter.on(eventType, listener);
};
RTC.prototype.addListener = function (type, listener) {
this.eventEmitter.on(type, listener);
};
RTC.prototype.removeStreamListener = function (listener, eventType) {
if(!(eventType instanceof StreamEventTypes))
throw "Illegal argument";
this.removeListener(eventType, listener);
};
RTC.prototype.createLocalStreams = function (streams, change) {
for (var i = 0; i < streams.length; i++) {
var localStream = new LocalStream(streams.stream,
streams.type, this.eventEmitter, streams.videoType,
streams.isGUMStream);
this.localStreams.push(localStream);
if (streams.isMuted === true)
localStream.setMute(true);
if(type == "audio") {
if (streams.type == "audio") {
this.localAudio = localStream;
} else {
this.localVideo = localStream;
}
var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED;
if(change)
if (change)
eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
eventEmitter.emit(eventType, localStream, isMuted);
return localStream;
},
removeLocalStream: function (stream) {
for(var i = 0; i < this.localStreams.length; i++) {
if(this.localStreams[i].getOriginalStream() === stream) {
delete this.localStreams[i];
return;
}
}
},
createRemoteStream: function (data, sid, thessrc) {
var remoteStream = new MediaStream(data, sid, thessrc,
RTCBrowserType.getBrowserType(), eventEmitter);
var jid = data.peerjid || APP.xmpp.myJid();
if(!this.remoteStreams[jid]) {
this.remoteStreams[jid] = {};
}
this.remoteStreams[jid][remoteStream.type]= remoteStream;
eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, remoteStream);
return remoteStream;
},
getPCConstraints: function () {
return this.rtcUtils.pc_constraints;
},
getUserMediaWithConstraints:function(um, success_callback,
failure_callback, resolution,
bandwidth, fps, desktopStream)
{
return this.rtcUtils.getUserMediaWithConstraints(um, success_callback,
failure_callback, resolution, bandwidth, fps, desktopStream);
},
attachMediaStream: function (elSelector, stream) {
this.rtcUtils.attachMediaStream(elSelector, stream);
},
getStreamID: function (stream) {
return this.rtcUtils.getStreamID(stream);
},
getVideoSrc: function (element) {
return this.rtcUtils.getVideoSrc(element);
},
setVideoSrc: function (element, src) {
this.rtcUtils.setVideoSrc(element, src);
},
getVideoElementName: function () {
return RTCBrowserType.isTemasysPluginUsed() ? 'object' : 'video';
},
dispose: function() {
if (this.rtcUtils) {
this.rtcUtils = null;
}
},
stop: function () {
this.dispose();
},
start: function () {
var self = this;
APP.desktopsharing.addListener(
function (stream, isUsingScreenStream, callback) {
self.changeLocalVideo(stream, isUsingScreenStream, callback);
}, DesktopSharingEventTypes.NEW_STREAM_CREATED);
APP.xmpp.addListener(XMPPEvents.CALL_INCOMING, function(event) {
DataChannels.init(event.peerconnection, eventEmitter);
});
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT,
DataChannels.handleSelectedEndpointEvent);
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
DataChannels.handlePinnedEndpointEvent);
this.eventEmitter.emit(eventType, localStream, streams.isMuted);
}
return this.localStreams;
};
// In case of IE we continue from 'onReady' callback
// passed to RTCUtils constructor. It will be invoked by Temasys plugin
// once it is initialized.
var onReady = function () {
eventEmitter.emit(RTCEvents.RTC_READY, true);
self.rtcUtils.obtainAudioAndVideoPermissions(
null, null, getMediaStreamUsage());
};
this.rtcUtils = new RTCUtils(this, onReady);
// Call onReady() if Temasys plugin is not used
if (!RTCBrowserType.isTemasysPluginUsed()) {
onReady();
}
},
muteRemoteVideoStream: function (jid, value) {
var stream;
if(this.remoteStreams[jid] &&
this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
stream = this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
}
if(!stream)
return true;
if (value != stream.muted) {
stream.setMute(value);
return true;
}
return false;
},
switchVideoStreams: function (new_stream) {
this.localVideo.stream = new_stream;
this.localStreams = [];
//in firefox we have only one stream object
if (this.localAudio.getOriginalStream() != new_stream)
this.localStreams.push(this.localAudio);
this.localStreams.push(this.localVideo);
},
changeLocalVideo: function (stream, isUsingScreenStream, callback) {
var oldStream = this.localVideo.getOriginalStream();
var type = (isUsingScreenStream ? "screen" : "camera");
var localCallback = callback;
if(this.localVideo.isMuted() && this.localVideo.videoType !== type) {
localCallback = function() {
APP.xmpp.setVideoMute(false, function(mute) {
eventEmitter.emit(RTCEvents.VIDEO_MUTE, mute);
});
callback();
};
}
// FIXME: Workaround for FF/IE/Safari
if (stream && stream.videoStream) {
stream = stream.videoStream;
}
var videoStream = this.rtcUtils.createStream(stream, true);
this.localVideo = this.createLocalStream(videoStream, "video", true, type);
// Stop the stream to trigger onended event for old stream
oldStream.stop();
this.switchVideoStreams(videoStream, oldStream);
APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
},
changeLocalAudio: function (stream, callback) {
var oldStream = this.localAudio.getOriginalStream();
var newStream = this.rtcUtils.createStream(stream);
this.localAudio = this.createLocalStream(newStream, "audio", true);
// Stop the stream to trigger onended event for old stream
oldStream.stop();
APP.xmpp.switchStreams(newStream, oldStream, callback, true);
},
isVideoMuted: function (jid) {
if (jid === APP.xmpp.myJid()) {
var localVideo = APP.RTC.localVideo;
return (!localVideo || localVideo.isMuted());
} else {
if (!APP.RTC.remoteStreams[jid] ||
!APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
return null;
}
return APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted;
}
},
setVideoMute: function (mute, callback, options) {
if (!this.localVideo)
RTC.prototype.removeLocalStream = function (stream) {
for(var i = 0; i < this.localStreams.length; i++) {
if(this.localStreams[i].getOriginalStream() === stream) {
delete this.localStreams[i];
return;
if (mute == APP.RTC.localVideo.isMuted())
{
APP.xmpp.sendVideoInfoPresence(mute);
if (callback)
callback(mute);
}
else
{
APP.RTC.localVideo.setMute(mute);
APP.xmpp.setVideoMute(
mute,
callback,
options);
}
},
setDeviceAvailability: function (devices) {
if(!devices)
return;
if(devices.audio === true || devices.audio === false)
this.devices.audio = devices.audio;
if(devices.video === true || devices.video === false)
this.devices.video = devices.video;
eventEmitter.emit(RTCEvents.AVAILABLE_DEVICES_CHANGED, this.devices);
}
};
RTC.prototype.createRemoteStream = function (data, sid, thessrc) {
var remoteStream = new MediaStream(data, sid, thessrc,
RTCBrowserType.getBrowserType(), this.eventEmitter);
if(data.peerjid)
return;
var jid = data.peerjid;
if(!this.remoteStreams[jid]) {
this.remoteStreams[jid] = {};
}
this.remoteStreams[jid][remoteStream.type]= remoteStream;
this.eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, remoteStream);
return remoteStream;
};
RTC.prototype.getPCConstraints = function () {
return this.rtcUtils.pc_constraints;
};
RTC.prototype.getUserMediaWithConstraints = function(um, success_callback,
failure_callback, resolution,
bandwidth, fps, desktopStream)
{
return this.rtcUtils.getUserMediaWithConstraints(um, success_callback,
failure_callback, resolution, bandwidth, fps, desktopStream);
};
RTC.prototype.attachMediaStream = function (elSelector, stream) {
this.rtcUtils.attachMediaStream(elSelector, stream);
};
RTC.prototype.getStreamID = function (stream) {
return this.rtcUtils.getStreamID(stream);
};
RTC.prototype.getVideoSrc = function (element) {
return this.rtcUtils.getVideoSrc(element);
};
RTC.prototype.setVideoSrc = function (element, src) {
this.rtcUtils.setVideoSrc(element, src);
};
RTC.prototype.getVideoElementName = function () {
return RTCBrowserType.isTemasysPluginUsed() ? 'object' : 'video';
};
RTC.prototype.dispose = function() {
if (this.rtcUtils) {
this.rtcUtils = null;
}
};
RTC.prototype.muteRemoteVideoStream = function (jid, value) {
var stream;
if(this.remoteStreams[jid] &&
this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
stream = this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
}
if(!stream)
return true;
if (value != stream.muted) {
stream.setMute(value);
return true;
}
return false;
};
RTC.prototype.switchVideoStreams = function (new_stream) {
this.localVideo.stream = new_stream;
this.localStreams = [];
//in firefox we have only one stream object
if (this.localAudio.getOriginalStream() != new_stream)
this.localStreams.push(this.localAudio);
this.localStreams.push(this.localVideo);
};
RTC.prototype.changeLocalVideo = function (stream, isUsingScreenStream, callback) {
var oldStream = this.localVideo.getOriginalStream();
var type = (isUsingScreenStream ? "screen" : "camera");
var localCallback = callback;
if(this.localVideo.isMuted() && this.localVideo.videoType !== type) {
localCallback = function() {
APP.xmpp.setVideoMute(false, function(mute) {
self.eventEmitter.emit(RTCEvents.VIDEO_MUTE, mute);
});
callback();
};
}
// FIXME: Workaround for FF/IE/Safari
if (stream && stream.videoStream) {
stream = stream.videoStream;
}
var videoStream = this.rtcUtils.createStream(stream, true);
this.localVideo = this.createLocalStream(videoStream, "video", true, type);
// Stop the stream to trigger onended event for old stream
oldStream.stop();
this.switchVideoStreams(videoStream, oldStream);
APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
};
RTC.prototype.changeLocalAudio = function (stream, callback) {
var oldStream = this.localAudio.getOriginalStream();
var newStream = this.rtcUtils.createStream(stream);
this.localAudio = this.createLocalStream(newStream, "audio", true);
// Stop the stream to trigger onended event for old stream
oldStream.stop();
APP.xmpp.switchStreams(newStream, oldStream, callback, true);
};
RTC.prototype.isVideoMuted = function (jid) {
if (jid === APP.xmpp.myJid()) {
var localVideo = APP.RTC.localVideo;
return (!localVideo || localVideo.isMuted());
} else {
if (!this.remoteStreams[jid] ||
!this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
return null;
}
return this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted;
}
};
RTC.prototype.setVideoMute = function (mute, callback, options) {
if (!this.localVideo)
return;
if (mute == this.localVideo.isMuted())
{
APP.xmpp.sendVideoInfoPresence(mute);
if (callback)
callback(mute);
}
else
{
this.localVideo.setMute(mute);
APP.xmpp.setVideoMute(
mute,
callback,
options);
}
};
RTC.prototype.setDeviceAvailability = function (devices) {
if(!devices)
return;
if(devices.audio === true || devices.audio === false)
this.devices.audio = devices.audio;
if(devices.video === true || devices.video === false)
this.devices.video = devices.video;
this.eventEmitter.emit(RTCEvents.AVAILABLE_DEVICES_CHANGED, this.devices);
};
module.exports = RTC;

View File

@ -436,7 +436,7 @@ RTCUtils.prototype.successCallback = function (stream, usageOptions) {
if (stream && stream.getAudioTracks && stream.getVideoTracks)
console.log('got', stream, stream.getAudioTracks().length,
stream.getVideoTracks().length);
this.handleLocalStream(stream, usageOptions);
return this.handleLocalStream(stream, usageOptions);
};
RTCUtils.prototype.errorCallback = function (error) {
@ -475,7 +475,6 @@ RTCUtils.prototype.errorCallback = function (error) {
RTCUtils.prototype.handleLocalStream = function(stream, usageOptions) {
// If this is FF, the stream parameter is *not* a MediaStream object, it's
// an object with two properties: audioStream, videoStream.
var audioStream, videoStream;
if(window.webkitMediaStream)
{
audioStream = new webkitMediaStream();
@ -493,6 +492,7 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions) {
videoStream.addTrack(videoTracks[i]);
}
}
}
else if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed())
{ // Firefox and Temasys plugin
@ -513,12 +513,9 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions) {
var audioGUM = (!usageOptions || usageOptions.audio !== false),
videoGUM = (!usageOptions || usageOptions.video !== false);
this.service.createLocalStream(audioStream, "audio", null, null,
audioMuted, audioGUM);
this.service.createLocalStream(videoStream, "video", null, 'camera',
videoMuted, videoGUM);
return this.service.createLocalStreams(
[{stream: audioStream, type: "audio", isMuted: audioMuted, isGUMStream: audioGUM, videoType: null},
{stream: videoStream, type: "video", isMuted: videoMuted, isGUMStream: videoGUM, videoType: "camera"}]);
};
function DummyMediaStream(id) {