2017-05-10 10:55:31 +02:00
|
|
|
/*
|
2017-09-19 11:51:02 +02:00
|
|
|
* (C) Copyright 2017 OpenVidu (http://openvidu.io/)
|
2017-05-10 10:55:31 +02:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
import { SessionInternal, SessionOptions } from './SessionInternal';
|
2018-03-02 11:03:45 +01:00
|
|
|
import { Connection } from './Connection';
|
2017-10-04 10:30:15 +02:00
|
|
|
import { OpenViduError, OpenViduErrorName } from './OpenViduError';
|
2018-01-18 15:59:28 +01:00
|
|
|
import { Stream, OutboundStreamOptions } from './Stream';
|
2017-09-22 15:57:59 +02:00
|
|
|
import * as RpcBuilder from '../KurentoUtils/kurento-jsonrpc';
|
2017-05-10 10:55:31 +02:00
|
|
|
|
|
|
|
export type Callback<T> = (error?: any, openVidu?: T) => void;
|
|
|
|
|
|
|
|
export class OpenViduInternal {
|
|
|
|
|
2017-06-14 14:14:45 +02:00
|
|
|
private wsUri;
|
2017-05-10 10:55:31 +02:00
|
|
|
private session: SessionInternal;
|
|
|
|
private jsonRpcClient: any;
|
|
|
|
private rpcParams: any;
|
|
|
|
private callback: Callback<OpenViduInternal>;
|
2018-01-18 15:59:28 +01:00
|
|
|
private localStream: Stream;
|
2017-05-10 10:55:31 +02:00
|
|
|
private remoteStreams: Stream[] = [];
|
2017-08-29 17:31:34 +02:00
|
|
|
private secret: string;
|
2018-01-29 15:26:31 +01:00
|
|
|
private recorder: boolean = false;
|
2017-08-29 17:31:34 +02:00
|
|
|
|
2017-05-10 10:55:31 +02:00
|
|
|
/* NEW METHODS */
|
|
|
|
initSession(sessionId) {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.info("'Session' initialized with 'sessionId' [" + sessionId + "]");
|
2017-05-10 10:55:31 +02:00
|
|
|
this.session = new SessionInternal(this, sessionId);
|
|
|
|
return this.session;
|
|
|
|
}
|
|
|
|
|
2018-01-18 15:59:28 +01:00
|
|
|
initPublisherTagged(parentId: string, cameraOptions: OutboundStreamOptions, newStream: boolean, callback?: Function): Stream {
|
|
|
|
|
|
|
|
if (newStream) {
|
|
|
|
if (cameraOptions == null) {
|
|
|
|
cameraOptions = {
|
|
|
|
sendAudio: true,
|
|
|
|
sendVideo: true,
|
|
|
|
activeAudio: true,
|
|
|
|
activeVideo: true,
|
|
|
|
dataChannel: true,
|
|
|
|
mediaConstraints: {
|
|
|
|
audio: true,
|
|
|
|
video: { width: { ideal: 1280 } }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.localStream = new Stream(this, true, this.session, cameraOptions);
|
|
|
|
}
|
2017-05-10 10:55:31 +02:00
|
|
|
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.requestCameraAccess((error, localStream) => {
|
2017-10-03 15:02:36 +02:00
|
|
|
if (error) {
|
2018-01-18 15:59:28 +01:00
|
|
|
// Neither localStream or microphone device is allowed/able to capture media
|
2017-10-04 10:30:15 +02:00
|
|
|
console.error(error);
|
2017-10-03 15:02:36 +02:00
|
|
|
if (callback) {
|
|
|
|
callback(error);
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.ee.emitEvent('access-denied-by-publisher');
|
2017-10-03 15:02:36 +02:00
|
|
|
} else {
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.setVideoElement(this.cameraReady(localStream!, parentId));
|
2017-10-03 15:02:36 +02:00
|
|
|
if (callback) {
|
|
|
|
callback(undefined);
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
2017-10-03 15:02:36 +02:00
|
|
|
}
|
|
|
|
});
|
2018-01-18 15:59:28 +01:00
|
|
|
return this.localStream;
|
2017-10-03 15:02:36 +02:00
|
|
|
}
|
|
|
|
|
2018-01-18 15:59:28 +01:00
|
|
|
initPublisherScreen(parentId: string, newStream: boolean, callback?): Stream {
|
|
|
|
|
|
|
|
if (newStream) {
|
|
|
|
this.localStream = new Stream(this, true, this.session, 'screen-options');
|
2018-01-10 15:00:52 +01:00
|
|
|
}
|
2018-01-18 15:59:28 +01:00
|
|
|
|
|
|
|
this.localStream.addOnceEventListener('can-request-screen', () => {
|
|
|
|
this.localStream.requestCameraAccess((error, localStream) => {
|
2017-05-10 10:55:31 +02:00
|
|
|
if (error) {
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.ee.emitEvent('access-denied-by-publisher');
|
2017-10-04 10:30:15 +02:00
|
|
|
let errorName: OpenViduErrorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED;
|
|
|
|
let errorMessage = 'You must allow access to one window of your desktop';
|
|
|
|
let e = new OpenViduError(errorName, errorMessage);
|
|
|
|
console.error(e);
|
2017-10-03 15:02:36 +02:00
|
|
|
if (callback) {
|
2017-10-04 10:30:15 +02:00
|
|
|
callback(e);
|
2017-10-03 15:02:36 +02:00
|
|
|
}
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.setVideoElement(this.cameraReady(localStream!, parentId));
|
|
|
|
if (this.localStream.getSendAudio()) {
|
2017-10-03 15:02:36 +02:00
|
|
|
// If the user wants to send audio with the screen capturing
|
|
|
|
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
|
|
|
|
.then(userStream => {
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.getMediaStream().addTrack(userStream.getAudioTracks()[0]);
|
|
|
|
|
|
|
|
// Mute audio if 'activeAudio' property is false
|
|
|
|
if (userStream.getAudioTracks()[0] != null) {
|
|
|
|
userStream.getAudioTracks()[0].enabled = this.localStream.outboundOptions.activeAudio;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.localStream.isScreenRequestedReady = true;
|
|
|
|
this.localStream.ee.emitEvent('screen-ready');
|
2017-10-03 15:02:36 +02:00
|
|
|
if (callback) {
|
|
|
|
callback(undefined);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.ee.emitEvent('access-denied-by-publisher');
|
2017-10-04 10:30:15 +02:00
|
|
|
console.error("Error accessing the microphone", error);
|
|
|
|
if (callback) {
|
|
|
|
let errorName: OpenViduErrorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
|
|
|
|
let errorMessage = error.toString();
|
|
|
|
callback(new OpenViduError(errorName, errorMessage));
|
|
|
|
}
|
2017-10-03 15:02:36 +02:00
|
|
|
});
|
|
|
|
} else {
|
2018-01-18 15:59:28 +01:00
|
|
|
this.localStream.isScreenRequestedReady = true;
|
|
|
|
this.localStream.ee.emitEvent('screen-ready');
|
2017-10-03 15:02:36 +02:00
|
|
|
if (callback) {
|
|
|
|
callback(undefined);
|
|
|
|
}
|
|
|
|
}
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
|
|
|
});
|
2017-10-03 15:02:36 +02:00
|
|
|
});
|
2018-01-18 15:59:28 +01:00
|
|
|
return this.localStream;
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
|
|
|
|
2018-01-18 15:59:28 +01:00
|
|
|
cameraReady(localStream: Stream, parentId: string): HTMLVideoElement {
|
|
|
|
this.localStream = localStream;
|
|
|
|
let videoElement = this.localStream.playOnlyVideo(parentId, null);
|
|
|
|
this.localStream.emitStreamReadyEvent();
|
2017-05-10 10:55:31 +02:00
|
|
|
return videoElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
getLocalStream() {
|
2018-01-18 15:59:28 +01:00
|
|
|
return this.localStream;
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getRemoteStreams() {
|
|
|
|
return this.remoteStreams;
|
|
|
|
}
|
|
|
|
/* NEW METHODS */
|
|
|
|
|
2017-06-14 14:14:45 +02:00
|
|
|
getWsUri() {
|
|
|
|
return this.wsUri;
|
|
|
|
}
|
2017-05-10 10:55:31 +02:00
|
|
|
|
2017-06-14 14:14:45 +02:00
|
|
|
setWsUri(wsUri: string) {
|
|
|
|
this.wsUri = wsUri;
|
|
|
|
}
|
2017-05-10 10:55:31 +02:00
|
|
|
|
2017-08-29 17:31:34 +02:00
|
|
|
getSecret() {
|
|
|
|
return this.secret;
|
|
|
|
}
|
|
|
|
|
|
|
|
setSecret(secret: string) {
|
|
|
|
this.secret = secret;
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:26:31 +01:00
|
|
|
getRecorder() {
|
|
|
|
return this.recorder;
|
|
|
|
}
|
|
|
|
|
|
|
|
setRecorder(recorder: boolean) {
|
|
|
|
this.recorder = recorder;
|
|
|
|
}
|
|
|
|
|
2017-05-31 17:31:26 +02:00
|
|
|
getOpenViduServerURL() {
|
|
|
|
return 'https://' + this.wsUri.split("wss://")[1].split("/room")[0];
|
|
|
|
}
|
2017-05-10 10:55:31 +02:00
|
|
|
|
|
|
|
getRoom() {
|
|
|
|
return this.session;
|
|
|
|
}
|
|
|
|
|
|
|
|
connect(callback: Callback<OpenViduInternal>): void {
|
|
|
|
|
|
|
|
this.callback = callback;
|
|
|
|
|
|
|
|
this.initJsonRpcClient(this.wsUri);
|
|
|
|
}
|
|
|
|
|
|
|
|
private initJsonRpcClient(wsUri: string): void {
|
|
|
|
|
|
|
|
let config = {
|
|
|
|
heartbeat: 3000,
|
|
|
|
sendCloseMessage: false,
|
|
|
|
ws: {
|
|
|
|
uri: wsUri,
|
|
|
|
useSockJS: false,
|
|
|
|
onconnected: this.connectCallback.bind(this),
|
|
|
|
ondisconnect: this.disconnectCallback.bind(this),
|
|
|
|
onreconnecting: this.reconnectingCallback.bind(this),
|
|
|
|
onreconnected: this.reconnectedCallback.bind(this)
|
|
|
|
},
|
|
|
|
rpc: {
|
|
|
|
requestTimeout: 15000,
|
|
|
|
//notifications
|
|
|
|
participantJoined: this.onParticipantJoined.bind(this),
|
|
|
|
participantPublished: this.onParticipantPublished.bind(this),
|
2018-01-10 15:00:52 +01:00
|
|
|
participantUnpublished: this.onParticipantUnpublished.bind(this),
|
2017-05-10 10:55:31 +02:00
|
|
|
participantLeft: this.onParticipantLeft.bind(this),
|
|
|
|
participantEvicted: this.onParticipantEvicted.bind(this),
|
|
|
|
sendMessage: this.onNewMessage.bind(this),
|
|
|
|
iceCandidate: this.iceCandidateEvent.bind(this),
|
|
|
|
mediaError: this.onMediaError.bind(this),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
this.jsonRpcClient = new RpcBuilder.clients.JsonRpcClient(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
private connectCallback(error) {
|
|
|
|
if (error) {
|
|
|
|
this.callback(error);
|
|
|
|
} else {
|
|
|
|
this.callback(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private isRoomAvailable() {
|
|
|
|
if (this.session !== undefined && this.session instanceof SessionInternal) {
|
|
|
|
return true;
|
|
|
|
} else {
|
2018-02-27 14:37:39 +01:00
|
|
|
console.warn('Session instance not found');
|
2017-05-10 10:55:31 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private disconnectCallback() {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.warn('Websocket connection lost');
|
2017-05-10 10:55:31 +02:00
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onLostConnection();
|
|
|
|
} else {
|
|
|
|
alert('Connection error. Please reload page.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private reconnectingCallback() {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.warn('Websocket connection lost (reconnecting)');
|
2017-05-10 10:55:31 +02:00
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onLostConnection();
|
|
|
|
} else {
|
|
|
|
alert('Connection error. Please reload page.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private reconnectedCallback() {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.warn('Websocket reconnected');
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private onParticipantJoined(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onParticipantJoined(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private onParticipantPublished(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onParticipantPublished(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 15:00:52 +01:00
|
|
|
private onParticipantUnpublished(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onParticipantUnpublished(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-10 10:55:31 +02:00
|
|
|
private onParticipantLeft(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onParticipantLeft(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private onParticipantEvicted(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onParticipantEvicted(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private onNewMessage(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onNewMessage(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private iceCandidateEvent(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.recvIceCandidate(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private onRoomClosed(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onRoomClosed(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private onMediaError(params) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.onMediaError(params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setRpcParams(params: any) {
|
|
|
|
this.rpcParams = params;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendRequest(method, params, callback?) {
|
|
|
|
|
|
|
|
if (params && params instanceof Function) {
|
|
|
|
callback = params;
|
|
|
|
params = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
params = params || {};
|
|
|
|
|
|
|
|
if (this.rpcParams && this.rpcParams !== null && this.rpcParams !== undefined) {
|
|
|
|
for (let index in this.rpcParams) {
|
|
|
|
if (this.rpcParams.hasOwnProperty(index)) {
|
|
|
|
params[index] = this.rpcParams[index];
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug('RPC param added to request {' + index + ': ' + this.rpcParams[index] + '}');
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug('Sending request: {method:"' + method + '", params: ' + JSON.stringify(params) + '}');
|
2017-05-10 10:55:31 +02:00
|
|
|
|
|
|
|
this.jsonRpcClient.send(method, params, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(forced) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.leave(forced, this.jsonRpcClient);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
disconnectParticipant(stream) {
|
|
|
|
if (this.isRoomAvailable()) {
|
|
|
|
this.session.disconnect(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//CHAT
|
2018-01-10 15:00:52 +01:00
|
|
|
sendMessage(message) {
|
2017-05-10 10:55:31 +02:00
|
|
|
this.sendRequest('sendMessage', {
|
2018-01-10 15:00:52 +01:00
|
|
|
message: message
|
2017-05-10 10:55:31 +02:00
|
|
|
}, function (error, response) {
|
|
|
|
if (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2017-09-29 20:00:00 +02:00
|
|
|
generateMediaConstraints(cameraOptions: any) {
|
2017-05-19 16:04:56 +02:00
|
|
|
let mediaConstraints = {
|
2017-09-29 20:00:00 +02:00
|
|
|
audio: cameraOptions.audio,
|
2017-05-19 16:04:56 +02:00
|
|
|
video: {}
|
|
|
|
}
|
2017-09-29 20:00:00 +02:00
|
|
|
if (!cameraOptions.video) {
|
|
|
|
mediaConstraints.video = false
|
|
|
|
} else {
|
|
|
|
let w, h;
|
|
|
|
switch (cameraOptions.quality) {
|
|
|
|
case 'LOW':
|
|
|
|
w = 320;
|
|
|
|
h = 240;
|
|
|
|
break;
|
|
|
|
case 'MEDIUM':
|
|
|
|
w = 640;
|
|
|
|
h = 480;
|
|
|
|
break;
|
|
|
|
case 'HIGH':
|
|
|
|
w = 1280;
|
|
|
|
h = 720;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
w = 640;
|
|
|
|
h = 480;
|
|
|
|
}
|
|
|
|
mediaConstraints.video['width'] = { exact: w };
|
|
|
|
mediaConstraints.video['height'] = { exact: h };
|
|
|
|
//mediaConstraints.video['frameRate'] = { ideal: Number((<HTMLInputElement>document.getElementById('frameRate')).value) };
|
2017-05-19 16:04:56 +02:00
|
|
|
}
|
|
|
|
return mediaConstraints;
|
|
|
|
}
|
|
|
|
|
2017-05-10 10:55:31 +02:00
|
|
|
}
|