2015-07-28 19:42:01 +00:00
|
|
|
/* global APP */
|
2014-12-19 13:59:08 +00:00
|
|
|
var EventEmitter = require("events");
|
2015-07-10 09:57:20 +00:00
|
|
|
var RTCBrowserType = require("./RTCBrowserType");
|
2014-12-19 13:59:08 +00:00
|
|
|
var RTCUtils = require("./RTCUtils.js");
|
2014-12-22 09:03:21 +00:00
|
|
|
var LocalStream = require("./LocalStream.js");
|
|
|
|
var DataChannels = require("./DataChannels");
|
|
|
|
var MediaStream = require("./MediaStream.js");
|
2015-01-28 14:35:22 +00:00
|
|
|
var DesktopSharingEventTypes
|
|
|
|
= require("../../service/desktopsharing/DesktopSharingEventTypes");
|
|
|
|
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
|
|
|
|
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
|
2015-03-27 09:36:39 +00:00
|
|
|
var RTCEvents = require("../../service/RTC/RTCEvents.js");
|
2015-01-28 14:35:22 +00:00
|
|
|
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
|
|
|
var UIEvents = require("../../service/UI/UIEvents");
|
2014-12-19 13:59:08 +00:00
|
|
|
|
|
|
|
var eventEmitter = new EventEmitter();
|
|
|
|
|
2015-04-22 09:31:08 +00:00
|
|
|
|
|
|
|
function getMediaStreamUsage()
|
|
|
|
{
|
|
|
|
var result = {
|
2015-05-19 15:03:01 +00:00
|
|
|
audio: true,
|
|
|
|
video: true
|
2015-04-22 09:31:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** There are some issues with the desktop sharing
|
|
|
|
* when this property is enabled.
|
2015-05-19 15:03:01 +00:00
|
|
|
* WARNING: We must change the implementation to start video/audio if we
|
|
|
|
* receive from the focus that the peer is not muted.
|
2015-04-22 09:31:08 +00:00
|
|
|
|
|
|
|
var isSecureConnection = window.location.protocol == "https:";
|
|
|
|
|
|
|
|
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
|
|
|
|
{
|
2015-05-19 15:03:01 +00:00
|
|
|
result = {
|
|
|
|
audio: false,
|
|
|
|
video: false
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
**/
|
2015-04-22 09:31:08 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-12-19 13:59:08 +00:00
|
|
|
var RTC = {
|
2015-09-22 23:51:24 +00:00
|
|
|
// Exposes DataChannels to public consumption (e.g. jitsi-meet-torture)
|
|
|
|
// without the necessity to require the module.
|
|
|
|
"DataChannels": DataChannels,
|
|
|
|
|
2014-12-19 13:59:08 +00:00
|
|
|
rtcUtils: null,
|
2015-03-27 09:36:39 +00:00
|
|
|
devices: {
|
2015-04-22 09:31:08 +00:00
|
|
|
audio: true,
|
|
|
|
video: true
|
2015-03-27 09:36:39 +00:00
|
|
|
},
|
2014-12-19 13:59:08 +00:00
|
|
|
remoteStreams: {},
|
|
|
|
localAudio: null,
|
|
|
|
localVideo: null,
|
|
|
|
addStreamListener: function (listener, eventType) {
|
|
|
|
eventEmitter.on(eventType, listener);
|
|
|
|
},
|
2015-01-27 09:56:22 +00:00
|
|
|
addListener: function (type, listener) {
|
|
|
|
eventEmitter.on(type, listener);
|
|
|
|
},
|
2014-12-19 13:59:08 +00:00
|
|
|
removeStreamListener: function (listener, eventType) {
|
|
|
|
if(!(eventType instanceof StreamEventTypes))
|
|
|
|
throw "Illegal argument";
|
|
|
|
|
|
|
|
eventEmitter.removeListener(eventType, listener);
|
|
|
|
},
|
2015-09-11 02:42:15 +00:00
|
|
|
createLocalStream: function (stream, type, change, videoType,
|
|
|
|
isMuted, isGUMStream) {
|
2014-12-22 09:03:21 +00:00
|
|
|
|
2015-09-11 02:42:15 +00:00
|
|
|
var localStream =
|
|
|
|
new LocalStream(stream, type, eventEmitter, videoType, isGUMStream);
|
2015-04-22 09:31:08 +00:00
|
|
|
if(isMuted === true)
|
2015-07-30 15:51:50 +00:00
|
|
|
localStream.setMute(true);
|
2015-03-27 09:36:39 +00:00
|
|
|
|
2015-11-06 21:59:38 +00:00
|
|
|
if (MediaStreamType.AUDIO_TYPE === type) {
|
2014-12-19 13:59:08 +00:00
|
|
|
this.localAudio = localStream;
|
2015-07-30 15:51:50 +00:00
|
|
|
} else {
|
2014-12-19 13:59:08 +00:00
|
|
|
this.localVideo = localStream;
|
|
|
|
}
|
2015-01-13 13:11:05 +00:00
|
|
|
var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED;
|
|
|
|
if(change)
|
|
|
|
eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
|
|
|
|
|
2015-04-22 09:31:08 +00:00
|
|
|
eventEmitter.emit(eventType, localStream, isMuted);
|
2014-12-19 13:59:08 +00:00
|
|
|
return localStream;
|
|
|
|
},
|
2015-10-01 20:10:55 +00:00
|
|
|
createRemoteStream: function (data, ssrc) {
|
2015-01-28 14:35:22 +00:00
|
|
|
var jid = data.peerjid || APP.xmpp.myJid();
|
2015-09-02 21:05:00 +00:00
|
|
|
|
|
|
|
// check the video muted state from last stored presence if any
|
|
|
|
var muted = false;
|
|
|
|
var pres = APP.xmpp.getLastPresence(jid);
|
2015-09-11 03:26:29 +00:00
|
|
|
if (pres && pres.videoMuted) {
|
2015-09-02 21:05:00 +00:00
|
|
|
muted = pres.videoMuted;
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:24:18 +00:00
|
|
|
var self = this;
|
|
|
|
[MediaStreamType.AUDIO_TYPE, MediaStreamType.VIDEO_TYPE].forEach(
|
2015-11-06 21:59:38 +00:00
|
|
|
function (type) {
|
2015-10-28 16:24:18 +00:00
|
|
|
var tracks =
|
|
|
|
type == MediaStreamType.AUDIO_TYPE
|
2015-11-06 21:59:38 +00:00
|
|
|
? data.stream.getAudioTracks() : data.stream.getVideoTracks();
|
2015-10-28 16:24:18 +00:00
|
|
|
if (!tracks || !Array.isArray(tracks) || !tracks.length) {
|
2015-11-06 21:59:38 +00:00
|
|
|
console.log("Not creating a(n) " + type + " stream: no tracks");
|
2015-10-28 16:24:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-09-02 21:05:00 +00:00
|
|
|
|
2015-10-28 16:24:18 +00:00
|
|
|
var remoteStream = new MediaStream(data, ssrc,
|
|
|
|
RTCBrowserType.getBrowserType(), eventEmitter, muted, type);
|
|
|
|
|
|
|
|
if (!self.remoteStreams[jid]) {
|
|
|
|
self.remoteStreams[jid] = {};
|
|
|
|
}
|
|
|
|
self.remoteStreams[jid][type] = remoteStream;
|
|
|
|
eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED,
|
|
|
|
remoteStream);
|
|
|
|
});
|
2014-12-19 13:59:08 +00:00
|
|
|
},
|
|
|
|
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);
|
|
|
|
},
|
2015-07-15 08:45:51 +00:00
|
|
|
attachMediaStream: function (elSelector, stream) {
|
|
|
|
this.rtcUtils.attachMediaStream(elSelector, stream);
|
2014-12-19 13:59:08 +00:00
|
|
|
},
|
|
|
|
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);
|
|
|
|
},
|
2015-07-10 09:57:20 +00:00
|
|
|
getVideoElementName: function () {
|
|
|
|
return RTCBrowserType.isTemasysPluginUsed() ? 'object' : 'video';
|
|
|
|
},
|
2014-12-19 13:59:08 +00:00
|
|
|
dispose: function() {
|
|
|
|
if (this.rtcUtils) {
|
|
|
|
this.rtcUtils = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
stop: function () {
|
|
|
|
this.dispose();
|
|
|
|
},
|
|
|
|
start: function () {
|
2015-01-13 13:11:05 +00:00
|
|
|
var self = this;
|
2015-01-28 14:35:22 +00:00
|
|
|
APP.desktopsharing.addListener(
|
2015-08-24 19:59:26 +00:00
|
|
|
DesktopSharingEventTypes.NEW_STREAM_CREATED,
|
2015-01-13 13:11:05 +00:00
|
|
|
function (stream, isUsingScreenStream, callback) {
|
|
|
|
self.changeLocalVideo(stream, isUsingScreenStream, callback);
|
2015-08-24 19:59:26 +00:00
|
|
|
});
|
2015-01-28 14:35:22 +00:00
|
|
|
APP.xmpp.addListener(XMPPEvents.CALL_INCOMING, function(event) {
|
2015-01-27 09:56:22 +00:00
|
|
|
DataChannels.init(event.peerconnection, eventEmitter);
|
2015-01-24 14:28:02 +00:00
|
|
|
});
|
2015-07-20 23:01:50 +00:00
|
|
|
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT,
|
|
|
|
DataChannels.handleSelectedEndpointEvent);
|
|
|
|
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
|
|
|
|
DataChannels.handlePinnedEndpointEvent);
|
2015-07-10 09:57:20 +00:00
|
|
|
|
|
|
|
// 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());
|
|
|
|
};
|
|
|
|
|
2015-11-05 16:53:15 +00:00
|
|
|
this.rtcUtils = new RTCUtils(this, eventEmitter, onReady);
|
2015-07-10 09:57:20 +00:00
|
|
|
|
|
|
|
// Call onReady() if Temasys plugin is not used
|
|
|
|
if (!RTCBrowserType.isTemasysPluginUsed()) {
|
|
|
|
onReady();
|
|
|
|
}
|
2014-12-19 13:59:08 +00:00
|
|
|
},
|
|
|
|
muteRemoteVideoStream: function (jid, value) {
|
|
|
|
var stream;
|
|
|
|
|
|
|
|
if(this.remoteStreams[jid] &&
|
2015-07-28 19:42:01 +00:00
|
|
|
this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
2014-12-19 13:59:08 +00:00
|
|
|
stream = this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!stream)
|
2015-03-30 14:19:35 +00:00
|
|
|
return true;
|
2014-12-19 13:59:08 +00:00
|
|
|
|
2015-01-13 09:33:45 +00:00
|
|
|
if (value != stream.muted) {
|
|
|
|
stream.setMute(value);
|
2014-12-19 13:59:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2015-01-12 13:23:29 +00:00
|
|
|
},
|
2015-01-13 13:11:05 +00:00
|
|
|
changeLocalVideo: function (stream, isUsingScreenStream, callback) {
|
|
|
|
var oldStream = this.localVideo.getOriginalStream();
|
2015-08-05 12:09:12 +00:00
|
|
|
var type = (isUsingScreenStream ? "screen" : "camera");
|
2015-03-23 16:12:24 +00:00
|
|
|
var localCallback = callback;
|
2015-07-28 19:42:01 +00:00
|
|
|
if(this.localVideo.isMuted() && this.localVideo.videoType !== type) {
|
2015-03-23 16:12:24 +00:00
|
|
|
localCallback = function() {
|
2015-07-18 04:39:09 +00:00
|
|
|
APP.xmpp.setVideoMute(false, function(mute) {
|
|
|
|
eventEmitter.emit(RTCEvents.VIDEO_MUTE, mute);
|
|
|
|
});
|
2015-11-05 16:53:15 +00:00
|
|
|
|
2015-03-23 16:12:24 +00:00
|
|
|
callback();
|
|
|
|
};
|
|
|
|
}
|
2015-07-10 09:57:20 +00:00
|
|
|
// FIXME: Workaround for FF/IE/Safari
|
|
|
|
if (stream && stream.videoStream) {
|
|
|
|
stream = stream.videoStream;
|
|
|
|
}
|
2015-04-22 09:31:08 +00:00
|
|
|
var videoStream = this.rtcUtils.createStream(stream, true);
|
2015-09-11 02:42:15 +00:00
|
|
|
this.localVideo =
|
|
|
|
this.createLocalStream(videoStream, "video", true, type);
|
2015-10-30 19:25:06 +00:00
|
|
|
// Stop the stream
|
|
|
|
this.stopMediaStream(oldStream);
|
2015-07-09 13:04:08 +00:00
|
|
|
|
2015-03-24 15:43:33 +00:00
|
|
|
APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
|
2015-01-20 15:56:00 +00:00
|
|
|
},
|
2015-04-22 09:31:08 +00:00
|
|
|
changeLocalAudio: function (stream, callback) {
|
|
|
|
var oldStream = this.localAudio.getOriginalStream();
|
|
|
|
var newStream = this.rtcUtils.createStream(stream);
|
2015-11-06 21:59:38 +00:00
|
|
|
this.localAudio
|
|
|
|
= this.createLocalStream(
|
|
|
|
newStream, MediaStreamType.AUDIO_TYPE, true);
|
2015-10-30 19:25:06 +00:00
|
|
|
// Stop the stream
|
|
|
|
this.stopMediaStream(oldStream);
|
2015-04-22 09:31:08 +00:00
|
|
|
APP.xmpp.switchStreams(newStream, oldStream, callback, true);
|
|
|
|
},
|
2015-06-29 14:24:21 +00:00
|
|
|
isVideoMuted: function (jid) {
|
|
|
|
if (jid === APP.xmpp.myJid()) {
|
|
|
|
var localVideo = APP.RTC.localVideo;
|
|
|
|
return (!localVideo || localVideo.isMuted());
|
2015-07-28 19:42:01 +00:00
|
|
|
} else {
|
|
|
|
if (!APP.RTC.remoteStreams[jid] ||
|
|
|
|
!APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
2015-06-29 14:24:21 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted;
|
|
|
|
}
|
|
|
|
},
|
2015-07-30 12:11:11 +00:00
|
|
|
setVideoMute: function (mute, callback, options) {
|
|
|
|
if (!this.localVideo)
|
2015-03-23 16:12:24 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (mute == APP.RTC.localVideo.isMuted())
|
|
|
|
{
|
|
|
|
APP.xmpp.sendVideoInfoPresence(mute);
|
2015-07-30 12:11:11 +00:00
|
|
|
if (callback)
|
2015-04-22 09:31:08 +00:00
|
|
|
callback(mute);
|
2015-03-23 16:12:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-07-30 12:11:11 +00:00
|
|
|
APP.RTC.localVideo.setMute(mute);
|
2015-03-23 16:12:24 +00:00
|
|
|
APP.xmpp.setVideoMute(
|
|
|
|
mute,
|
|
|
|
callback,
|
|
|
|
options);
|
|
|
|
}
|
2015-03-27 09:36:39 +00:00
|
|
|
},
|
|
|
|
setDeviceAvailability: function (devices) {
|
2015-03-27 13:56:17 +00:00
|
|
|
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;
|
2015-03-27 09:36:39 +00:00
|
|
|
eventEmitter.emit(RTCEvents.AVAILABLE_DEVICES_CHANGED, this.devices);
|
2015-10-30 19:25:06 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* A method to handle stopping of the stream.
|
|
|
|
* One point to handle the differences in various implementations.
|
2015-10-30 22:25:20 +00:00
|
|
|
* @param mediaStream MediaStream object to stop.
|
2015-10-30 19:25:06 +00:00
|
|
|
*/
|
|
|
|
stopMediaStream: function (mediaStream) {
|
2015-11-02 21:39:28 +00:00
|
|
|
mediaStream.getTracks().forEach(function (track) {
|
2015-10-30 19:25:06 +00:00
|
|
|
// stop() not supported with IE
|
|
|
|
if (track.stop) {
|
|
|
|
track.stop();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-11-02 21:39:28 +00:00
|
|
|
// leave stop for implementation still using it
|
2015-10-30 19:25:06 +00:00
|
|
|
if (mediaStream.stop) {
|
|
|
|
mediaStream.stop();
|
|
|
|
}
|
2015-10-30 22:25:20 +00:00
|
|
|
},
|
|
|
|
/**
|
2015-11-02 21:40:59 +00:00
|
|
|
* Adds onended/inactive handler to a MediaStream.
|
|
|
|
* @param mediaStream a MediaStream to attach onended/inactive handler
|
2015-10-30 22:25:20 +00:00
|
|
|
* @param handler the handler
|
|
|
|
*/
|
|
|
|
addMediaStreamInactiveHandler: function (mediaStream, handler) {
|
|
|
|
if (mediaStream.addEventListener) {
|
|
|
|
// chrome
|
2015-11-02 21:40:59 +00:00
|
|
|
if(typeof mediaStream.active !== "undefined")
|
2015-11-17 20:27:26 +00:00
|
|
|
mediaStream.oninactive = handler;
|
2015-11-02 21:40:59 +00:00
|
|
|
else
|
|
|
|
mediaStream.onended = handler;
|
2015-10-30 22:25:20 +00:00
|
|
|
} else {
|
|
|
|
// themasys
|
|
|
|
mediaStream.attachEvent('ended', function () {
|
|
|
|
handler(mediaStream);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
2015-11-02 21:40:59 +00:00
|
|
|
* Removes onended/inactive handler.
|
2015-10-30 22:25:20 +00:00
|
|
|
* @param mediaStream the MediaStream to remove the handler from.
|
|
|
|
* @param handler the handler to remove.
|
|
|
|
*/
|
|
|
|
removeMediaStreamInactiveHandler: function (mediaStream, handler) {
|
|
|
|
if (mediaStream.removeEventListener) {
|
|
|
|
// chrome
|
2015-11-02 21:40:59 +00:00
|
|
|
if(typeof mediaStream.active !== "undefined")
|
2015-11-17 20:27:26 +00:00
|
|
|
mediaStream.oninactive = null;
|
2015-11-02 21:40:59 +00:00
|
|
|
else
|
|
|
|
mediaStream.onended = null;
|
2015-10-30 22:25:20 +00:00
|
|
|
} else {
|
|
|
|
// themasys
|
|
|
|
mediaStream.detachEvent('ended', handler);
|
|
|
|
}
|
2015-01-20 15:56:00 +00:00
|
|
|
}
|
2014-12-19 13:59:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = RTC;
|