bigbluebutton-Github/bigbluebutton-client/resources/prod/lib/kurento-extension.js

512 lines
14 KiB
JavaScript
Raw Normal View History

2018-03-08 05:20:37 +08:00
const isFirefox = typeof window.InstallTrigger !== 'undefined';
const isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
const isChrome = !!window.chrome && !isOpera;
const isSafari = navigator.userAgent.indexOf('Safari') >= 0 && !isChrome;
const kurentoHandler = null;
Kurento = function (
2018-03-08 05:20:37 +08:00
tag,
voiceBridge,
conferenceUsername,
internalMeetingId,
onFail = null,
chromeExtension = null,
) {
this.ws = null;
this.video;
this.screen;
this.webRtcPeer;
this.extensionInstalled = false;
this.screenConstraints = {};
this.mediaCallback = null;
2018-03-08 05:20:37 +08:00
this.voiceBridge = `${voiceBridge}-SCREENSHARE`;
this.internalMeetingId = internalMeetingId;
this.vid_width = window.screen.width;
this.vid_height = window.screen.height;
// TODO properly generate a uuid
this.sessid = Math.random().toString();
this.renderTag = 'remote-media';
this.caller_id_name = conferenceUsername;
this.caller_id_number = conferenceUsername;
2018-03-08 05:20:37 +08:00
this.kurentoPort = 'bbb-webrtc-sfu';
this.hostName = window.location.hostname;
2018-03-08 05:20:37 +08:00
this.socketUrl = `wss://${this.hostName}/${this.kurentoPort}`;
this.iceServers = null;
if (chromeExtension != null) {
this.chromeExtension = chromeExtension;
window.chromeExtension = chromeExtension;
}
if (onFail != null) {
this.onFail = Kurento.normalizeCallback(onFail);
} else {
2018-03-08 05:20:37 +08:00
const _this = this;
this.onFail = function () {
_this.logError('Default error handler');
};
}
};
2018-03-08 05:20:37 +08:00
this.KurentoManager = function () {
this.kurentoVideo = null;
this.kurentoScreenshare = null;
};
KurentoManager.prototype.exitScreenShare = function () {
2018-03-08 05:20:37 +08:00
console.log(' [exitScreenShare] Exiting screensharing');
if (typeof this.kurentoScreenshare !== 'undefined' && this.kurentoScreenshare) {
if (this.kurentoScreenshare.ws !== null) {
this.kurentoScreenshare.ws.onclose = function () {};
this.kurentoScreenshare.ws.close();
}
this.kurentoScreenshare.disposeScreenShare();
this.kurentoScreenshare = null;
}
if (this.kurentoScreenshare) {
this.kurentoScreenshare = null;
}
2018-03-08 05:20:37 +08:00
if (typeof this.kurentoVideo !== 'undefined' && this.kurentoVideo) {
this.exitVideo();
}
};
KurentoManager.prototype.exitVideo = function () {
2018-03-08 05:20:37 +08:00
console.log(' [exitScreenShare] Exiting screensharing viewing');
if (typeof this.kurentoVideo !== 'undefined' && this.kurentoVideo) {
if (this.kurentoVideo.ws !== null) {
this.kurentoVideo.ws.onclose = function () {};
this.kurentoVideo.ws.close();
}
this.kurentoVideo.disposeScreenShare();
this.kurentoVideo = null;
}
if (this.kurentoVideo) {
this.kurentoVideo = null;
}
};
KurentoManager.prototype.shareScreen = function (tag) {
this.exitScreenShare();
2018-03-08 05:20:37 +08:00
const obj = Object.create(Kurento.prototype);
Kurento.apply(obj, arguments);
this.kurentoScreenshare = obj;
this.kurentoScreenshare.setScreenShare(tag);
};
KurentoManager.prototype.joinWatchVideo = function (tag) {
this.exitVideo();
2018-03-08 05:20:37 +08:00
const obj = Object.create(Kurento.prototype);
Kurento.apply(obj, arguments);
this.kurentoVideo = obj;
this.kurentoVideo.setWatchVideo(tag);
};
Kurento.prototype.setScreenShare = function (tag) {
this.mediaCallback = this.makeShare.bind(this);
this.create(tag);
};
Kurento.prototype.create = function (tag) {
this.setRenderTag(tag);
this.iceServers = true;
this.init();
};
Kurento.prototype.init = function () {
2018-03-08 05:20:37 +08:00
const self = this;
if ('WebSocket' in window) {
console.log('this browser supports websockets');
this.ws = new WebSocket(this.socketUrl);
this.ws.onmessage = this.onWSMessage.bind(this);
this.ws.onclose = (close) => {
kurentoManager.exitScreenShare();
2018-03-08 05:20:37 +08:00
self.onFail('Websocket connection closed');
};
this.ws.onerror = (error) => {
kurentoManager.exitScreenShare();
2018-03-08 05:20:37 +08:00
self.onFail('Websocket connection error');
};
this.ws.onopen = function () {
self.mediaCallback();
2018-03-08 05:20:37 +08:00
};
} else { console.log('this browser does not support websockets'); }
};
Kurento.prototype.onWSMessage = function (message) {
2018-03-08 05:20:37 +08:00
const parsedMessage = JSON.parse(message.data);
switch (parsedMessage.id) {
case 'presenterResponse':
this.presenterResponse(parsedMessage);
break;
case 'viewerResponse':
this.viewerResponse(parsedMessage);
break;
case 'stopSharing':
kurentoManager.exitScreenShare();
break;
case 'iceCandidate':
this.webRtcPeer.addIceCandidate(parsedMessage.candidate);
break;
default:
console.error('Unrecognized message', parsedMessage);
}
};
Kurento.prototype.setRenderTag = function (tag) {
this.renderTag = tag;
};
Kurento.prototype.presenterResponse = function (message) {
2018-03-08 05:20:37 +08:00
if (message.response !== 'accepted') {
const errorMsg = message.message ? message.message : 'Unknown error';
console.warn(`Call not accepted for the following reason: ${JSON.stringify(errorMsg, null, 2)}`);
kurentoManager.exitScreenShare();
this.onFail(errorMessage);
} else {
2018-03-08 05:20:37 +08:00
console.log(`Presenter call was accepted with SDP => ${message.sdpAnswer}`);
this.webRtcPeer.processAnswer(message.sdpAnswer);
}
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.viewerResponse = function (message) {
2018-03-08 05:20:37 +08:00
if (message.response !== 'accepted') {
const errorMsg = message.message ? message.message : 'Unknown error';
console.warn(`Call not accepted for the following reason: ${errorMsg}`);
kurentoManager.exitScreenShare();
this.onFail(errorMessage);
} else {
2018-03-08 05:20:37 +08:00
console.log(`Viewer call was accepted with SDP => ${message.sdpAnswer}`);
this.webRtcPeer.processAnswer(message.sdpAnswer);
}
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.serverResponse = function (message) {
2018-03-08 05:20:37 +08:00
if (message.response !== 'accepted') {
const errorMsg = message.message ? message.message : 'Unknow error';
console.warn(`Call not accepted for the following reason: ${errorMsg}`);
kurentoManager.exitScreenShare();
} else {
this.webRtcPeer.processAnswer(message.sdpAnswer);
}
2018-03-08 05:20:37 +08:00
};
2018-03-08 05:20:37 +08:00
Kurento.prototype.makeShare = function () {
const self = this;
if (!this.webRtcPeer) {
2018-03-08 05:20:37 +08:00
const options = {
onicecandidate: self.onIceCandidate.bind(self),
};
this.startScreenStreamFrom();
}
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.onOfferPresenter = function (error, offerSdp) {
2018-03-08 05:20:37 +08:00
const self = this;
if (error) {
console.log(`Kurento.prototype.onOfferPresenter Error ${error}`);
this.onFail(error);
return;
}
2018-03-08 05:20:37 +08:00
const message = {
id: 'presenter',
type: 'screenshare',
role: 'presenter',
internalMeetingId: self.internalMeetingId,
voiceBridge: self.voiceBridge,
2018-03-08 05:20:37 +08:00
callerName: self.caller_id_name,
sdpOffer: offerSdp,
vh: self.vid_height,
2018-03-08 05:20:37 +08:00
vw: self.vid_width,
};
2018-03-08 05:20:37 +08:00
console.log(`onOfferPresenter sending to screenshare server => ${JSON.stringify(message, null, 2)}`);
this.sendMessage(message);
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.startScreenStreamFrom = function () {
2018-03-08 05:20:37 +08:00
const self = this;
if (window.chrome) {
if (!self.chromeExtension) {
self.logError({
2018-03-08 05:20:37 +08:00
status: 'failed',
message: 'Missing Chrome Extension key',
});
self.onFail();
return;
}
}
// TODO it would be nice to check those constraints
if (typeof screenConstraints !== undefined) {
self.screenConstraints = {};
}
self.screenConstraints.video = {};
console.log(self);
2018-03-08 05:20:37 +08:00
const options = {
localVideo: document.getElementById(this.renderTag),
2018-03-08 05:20:37 +08:00
onicecandidate: self.onIceCandidate.bind(self),
mediaConstraints: self.screenConstraints,
sendSource: 'desktop',
};
2018-03-08 05:20:37 +08:00
console.log(` Peer options => ${JSON.stringify(options, null, 2)}`);
2018-03-08 05:20:37 +08:00
self.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, (error) => {
if (error) {
console.log(`WebRtcPeerSendonly constructor error ${JSON.stringify(error, null, 2)}`);
self.onFail(error);
return kurentoManager.exitScreenShare();
}
self.webRtcPeer.generateOffer(self.onOfferPresenter.bind(self));
2018-03-08 05:20:37 +08:00
console.log(`Generated peer offer w/ options ${JSON.stringify(options)}`);
const localStream = self.webRtcPeer.peerConnection.getLocalStreams()[0];
localStream.getVideoTracks()[0].onended = function () {
return kurentoManager.exitScreenShare();
};
localStream.getVideoTracks()[0].oninactive = function () {
return kurentoManager.exitScreenShare();
};
});
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.onIceCandidate = function (candidate) {
2018-03-08 05:20:37 +08:00
const self = this;
console.log(`Local candidate${JSON.stringify(candidate)}`);
2018-03-08 05:20:37 +08:00
const message = {
id: 'onIceCandidate',
role: 'presenter',
type: 'screenshare',
voiceBridge: self.voiceBridge,
2018-03-08 05:20:37 +08:00
candidate,
};
this.sendMessage(message);
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.onViewerIceCandidate = function (candidate) {
2018-03-08 05:20:37 +08:00
const self = this;
console.log(`Viewer local candidate${JSON.stringify(candidate)}`);
2018-03-08 05:20:37 +08:00
const message = {
id: 'viewerIceCandidate',
role: 'viewer',
type: 'screenshare',
voiceBridge: self.voiceBridge,
2018-03-08 05:20:37 +08:00
candidate,
callerName: self.caller_id_name,
};
this.sendMessage(message);
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.setWatchVideo = function (tag) {
this.useVideo = true;
this.useCamera = 'none';
this.useMic = 'none';
this.mediaCallback = this.viewer;
this.create(tag);
};
Kurento.prototype.viewer = function () {
2018-03-08 05:20:37 +08:00
const self = this;
if (!this.webRtcPeer) {
2018-03-08 05:20:37 +08:00
const options = {
remoteVideo: document.getElementById(this.renderTag),
2018-03-08 05:20:37 +08:00
onicecandidate: this.onViewerIceCandidate.bind(this),
};
2018-03-08 05:20:37 +08:00
self.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, function (error) {
if (error) {
return self.onFail(error);
}
this.generateOffer(self.onOfferViewer.bind(self));
});
}
};
Kurento.prototype.onOfferViewer = function (error, offerSdp) {
2018-03-08 05:20:37 +08:00
const self = this;
if (error) {
console.log(`Kurento.prototype.onOfferViewer Error ${error}`);
return this.onFail();
}
2018-03-08 05:20:37 +08:00
const message = {
id: 'viewer',
type: 'screenshare',
role: 'viewer',
internalMeetingId: self.internalMeetingId,
voiceBridge: self.voiceBridge,
2018-03-08 05:20:37 +08:00
callerName: self.caller_id_name,
sdpOffer: offerSdp,
};
2018-03-08 05:20:37 +08:00
console.log(`onOfferViewer sending to screenshare server => ${JSON.stringify(message, null, 2)}`);
this.sendMessage(message);
};
2018-03-08 05:20:37 +08:00
Kurento.prototype.stop = function () {
// if (this.webRtcPeer) {
// var message = {
// id : 'stop',
// type : 'screenshare',
// voiceBridge: kurentoHandler.voiceBridge
// }
// kurentoHandler.sendMessage(message);
// kurentoHandler.disposeScreenShare();
2018-03-08 05:20:37 +08:00
// }
};
2018-03-08 05:20:37 +08:00
Kurento.prototype.dispose = function () {
if (this.webRtcPeer) {
this.webRtcPeer.dispose();
this.webRtcPeer = null;
}
2018-03-08 05:20:37 +08:00
};
2018-03-08 05:20:37 +08:00
Kurento.prototype.disposeScreenShare = function () {
if (this.webRtcPeer) {
this.webRtcPeer.dispose();
this.webRtcPeer = null;
}
2018-03-08 05:20:37 +08:00
};
2018-03-08 05:20:37 +08:00
Kurento.prototype.sendMessage = function (message) {
const jsonMessage = JSON.stringify(message);
console.log(`Sending message: ${jsonMessage}`);
this.ws.send(jsonMessage);
2018-03-08 05:20:37 +08:00
};
Kurento.prototype.logger = function (obj) {
console.log(obj);
};
Kurento.prototype.logError = function (obj) {
console.error(obj);
};
Kurento.normalizeCallback = function (callback) {
2018-03-08 05:20:37 +08:00
if (typeof callback === 'function') {
return callback;
}
2018-03-08 05:20:37 +08:00
console.log(document.getElementById('BigBlueButton')[callback]);
return function (args) {
document.getElementById('BigBlueButton')[callback](args);
};
};
/* Global methods */
// this function explains how to use above methods/objects
2018-03-08 05:20:37 +08:00
window.getScreenConstraints = function (sendSource, callback) {
const chromeMediaSourceId = sendSource;
const screenConstraints = { video: {} };
// Limiting FPS to a range of 5-10 (5 ideal)
2018-03-08 05:20:37 +08:00
screenConstraints.video.frameRate = { ideal: 5, max: 10 };
// Limiting max resolution to screen size
2018-03-08 05:20:37 +08:00
screenConstraints.video.height = { max: window.screen.height };
screenConstraints.video.width = { max: window.screen.width };
2018-03-08 05:20:37 +08:00
if (isChrome) {
getChromeScreenConstraints((constraints) => {
if (!constraints) {
2018-03-08 05:20:37 +08:00
document.dispatchEvent(new Event('installChromeExtension'));
return;
}
2018-03-08 05:20:37 +08:00
const sourceId = constraints.streamId;
kurentoManager.kurentoScreenshare.extensionInstalled = true;
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
2018-03-08 05:20:37 +08:00
screenConstraints.video.chromeMediaSource = { exact: [sendSource] };
screenConstraints.video.chromeMediaSourceId = sourceId;
2018-03-08 05:20:37 +08:00
console.log('getScreenConstraints for Chrome returns => ');
console.log(screenConstraints);
// now invoking native getUserMedia API
callback(null, screenConstraints);
}, chromeExtension);
2018-03-08 05:20:37 +08:00
} else if (isFirefox) {
screenConstraints.video.mediaSource = 'screen';
2018-03-08 05:20:37 +08:00
console.log('getScreenConstraints for Firefox returns => ');
console.log(screenConstraints);
// now invoking native getUserMedia API
callback(null, screenConstraints);
2018-03-08 05:20:37 +08:00
} else if (isSafari) {
screenConstraints.video.mediaSource = 'screen';
2018-03-08 05:20:37 +08:00
console.log('getScreenConstraints for Safari returns => ');
console.log(screenConstraints);
// now invoking native getUserMedia API
callback(null, screenConstraints);
}
2018-03-08 05:20:37 +08:00
};
window.kurentoInitialize = function () {
2018-03-08 05:20:37 +08:00
if (window.kurentoManager == null || window.KurentoManager === undefined) {
window.kurentoManager = new KurentoManager();
}
};
2018-03-08 05:20:37 +08:00
window.kurentoShareScreen = function () {
window.kurentoInitialize();
window.kurentoManager.shareScreen.apply(window.kurentoManager, arguments);
};
window.kurentoExitScreenShare = function () {
window.kurentoInitialize();
window.kurentoManager.exitScreenShare();
};
window.kurentoWatchVideo = function () {
window.kurentoInitialize();
window.kurentoManager.joinWatchVideo.apply(window.kurentoManager, arguments);
};
window.kurentoExitVideo = function () {
window.kurentoInitialize();
window.kurentoManager.exitVideo();
2018-03-08 05:20:37 +08:00
};
window.getChromeScreenConstraints = function (callback, extensionId) {
chrome.runtime.sendMessage(
extensionId, {
getStream: true,
sources: [
'window',
'screen',
'tab',
],
},
(response) => {
console.log(response);
callback(response);
2018-03-08 05:20:37 +08:00
},
);
};