Merge branch 'master' into mediasoup FIXME CONFLICT

This merge commit includes 2 merge conflicts:

* openvidu-browser/src/OpenVidu/Session.ts
In modern code, the OpenVidu version is obtained through other means.
However in the mediasoup branch this value is used in several of the
*LEGACY.ts files.

* openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts
In the mediasoup branch, the special treatment for ionic was removed:
    openvidu-browser: removed Ionic iOS timeout on first subscription

23d64be806
and also the method was converted into async and returned a Promise.
However in master the code is still like the old one.
pull/600/head
Juan Navarro 2021-01-22 14:10:10 +01:00
commit 5e9511a376
272 changed files with 21746 additions and 15537 deletions

View File

@ -20,7 +20,7 @@ Visit [openvidu.io](https://openvidu.io)
## Contributors
This project exists thanks to all the people who contribute.
<a href="https://github.com/undefined/undefinedgraphs/contributors"><img src="https://opencollective.com/openvidu/contributors.svg?width=890&button=false" /></a>
<a href="https://github.com/OpenVidu/openvidu/contributors"><img src="https://opencollective.com/openvidu/contributors.svg?width=890&button=false" /></a>
## Backers

View File

@ -19,6 +19,7 @@ module.exports = {
],
excludeExternals: true,
excludePrivate: true,
excludeProtected: true,
excludeNotExported: true,
theme: "default",
readme: "none",

View File

@ -1,31 +1,31 @@
{
"author": "OpenVidu",
"dependencies": {
"@types/node": "13.13.2",
"@types/platform": "1.3.2",
"freeice": "2.2.2",
"hark": "1.2.3",
"platform": "1.3.5",
"uuid": "7.0.3",
"platform": "1.3.6",
"uuid": "8.3.1",
"wolfy87-eventemitter": "5.2.9"
},
"description": "OpenVidu Browser",
"devDependencies": {
"browserify": "16.5.1",
"grunt": "1.1.0",
"@types/node": "14.14.7",
"@types/platform": "1.3.3",
"browserify": "17.0.0",
"grunt": "1.3.0",
"grunt-cli": "1.3.2",
"grunt-contrib-copy": "1.0.0",
"grunt-contrib-sass": "1.0.0",
"grunt-contrib-uglify": "4.0.1",
"grunt-contrib-sass": "2.0.0",
"grunt-contrib-uglify": "5.0.0",
"grunt-contrib-watch": "1.1.0",
"grunt-postcss": "0.9.0",
"grunt-string-replace": "1.3.1",
"grunt-ts": "6.0.0-beta.22",
"terser": "4.6.11",
"tsify": "4.0.1",
"tslint": "6.1.1",
"typedoc": "0.17.4",
"typescript": "3.8.3"
"terser": "5.3.8",
"tsify": "5.0.2",
"tslint": "6.1.3",
"typedoc": "0.19.2",
"typescript": "4.0.5"
},
"license": "Apache-2.0",
"main": "lib/index.js",
@ -35,11 +35,11 @@
"url": "git://github.com/OpenVidu/openvidu"
},
"scripts": {
"browserify": "VERSION=${VERSION:-dev}; cd src && ../node_modules/browserify/bin/cmd.js Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/openvidu-browser-$VERSION.js -v",
"browserify-prod": "VERSION=${VERSION:-dev}; cd src && ../node_modules/browserify/bin/cmd.js --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | ../node_modules/terser/bin/terser --source-map content=inline --output ../static/js/openvidu-browser-$VERSION.min.js",
"browserify": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/openvidu-browser-$VERSION.js -v",
"browserify-prod": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | ../node_modules/terser/bin/terser --source-map content=inline --output ../static/js/openvidu-browser-$VERSION.min.js",
"build": "cd src/OpenVidu && ./../../node_modules/typescript/bin/tsc && cd ../.. && ./node_modules/typescript/bin/tsc --declaration src/index.ts --outDir ./lib --sourceMap --lib dom,es5,es2015.promise,scripthost",
"docs": "./generate-docs.sh"
},
"types": "lib/index.d.ts",
"version": "2.15.0"
"version": "2.16.0"
}

View File

@ -1,4 +0,0 @@
npm run build
npm pack
cp openvidu-browser-2.14.0.tgz ../../openvidu-tutorials/openvidu-react-native
cp openvidu-browser-2.14.0.tgz ../../openvidu-react-native-adapter/

View File

@ -18,7 +18,8 @@
import { Session } from './Session';
import { Stream } from './Stream';
import { StreamLEGACY } from './StreamLEGACY';
import { ConnectionOptions } from '../OpenViduInternal/Interfaces/Private/ConnectionOptions';
import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions';
import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions';
import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions';
import { StreamOptionsServer } from '../OpenViduInternal/Interfaces/Private/StreamOptionsServer';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
@ -52,14 +53,36 @@ export class Connection {
data: string;
/**
* @hidden
* Role of the connection.
* - `SUBSCRIBER`: can subscribe to published Streams of other users by calling [[Session.subscribe]]
* - `PUBLISHER`: SUBSCRIBER permissions + can publish their own Streams by calling [[Session.publish]]
* - `MODERATOR`: SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection by call [[Session.forceUnpublish]] and [[Session.forceDisconnect]]
*
* **Only defined for the local connection. In remote connections will be `undefined`**
*/
stream: Stream;
role: string;
/**
* Whether the streams published by this connection will be recorded or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded) <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* **Only defined for the local connection. In remote connections will be `undefined`**
*/
record: boolean;
/**
* @hidden
*/
options: ConnectionOptions | undefined;
stream?: Stream;
/**
* @hidden
*/
localOptions: LocalConnectionOptions | undefined;
/**
* @hidden
*/
remoteOptions: RemoteConnectionOptions | undefined;
/**
* @hidden
@ -74,23 +97,30 @@ export class Connection {
/**
* @hidden
*/
constructor(private session: Session, opts?: ConnectionOptions) {
constructor(private session: Session, connectionOptions: LocalConnectionOptions | RemoteConnectionOptions) {
let msg = "'Connection' created ";
if (!!opts) {
// Connection is remote
msg += "(remote) with 'connectionId' [" + opts.id + ']';
this.options = opts;
this.connectionId = opts.id;
this.creationTime = opts.createdAt;
if (opts.metadata) {
this.data = opts.metadata;
}
if (opts.streams) {
this.initRemoteStreams(opts.streams);
}
} else {
if (!!(<LocalConnectionOptions>connectionOptions).role) {
// Connection is local
this.localOptions = <LocalConnectionOptions>connectionOptions;
this.connectionId = this.localOptions.id;
this.creationTime = this.localOptions.createdAt;
this.data = this.localOptions.metadata;
this.rpcSessionId = this.localOptions.sessionId;
this.role = this.localOptions.role;
this.record = this.localOptions.record;
msg += '(local)';
} else {
// Connection is remote
this.remoteOptions = <RemoteConnectionOptions>connectionOptions;
this.connectionId = this.remoteOptions.id;
this.creationTime = this.remoteOptions.createdAt;
if (this.remoteOptions.metadata) {
this.data = this.remoteOptions.metadata;
}
if (this.remoteOptions.streams) {
this.initRemoteStreams(this.remoteOptions.streams);
}
msg += "(remote) with 'connectionId' [" + this.remoteOptions.id + ']';
}
logger.info(msg);
}
@ -103,7 +133,7 @@ export class Connection {
*/
sendIceCandidate(candidate: RTCIceCandidate): void {
logger.debug((!!this.stream.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' +
logger.debug((!!this.stream!.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' +
this.connectionId, candidate);
this.session.openvidu.sendRequest('onIceCandidate', {
@ -149,7 +179,7 @@ export class Connection {
this.addStream(stream);
});
logger.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + '] is now configured for receiving Streams with options: ', this.stream.inboundStreamOpts);
logger.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + '] is now configured for receiving Streams with options: ', this.stream!.inboundStreamOpts);
}
/**
@ -177,4 +207,4 @@ export class Connection {
this.disposed = true;
}
}
}

View File

@ -119,9 +119,9 @@ export class Filter {
} else {
logger.info('Filter method successfully executed on Stream ' + this.stream.streamId);
const oldValue = (<any>Object).assign({}, this.stream.filter);
this.stream.filter.lastExecMethod = { method, params: JSON.parse(stringParams) };
this.stream.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.session, this.stream, 'filter', this.stream.filter, oldValue, 'execFilterMethod')]);
this.stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.streamManager, this.stream, 'filter', this.stream.filter, oldValue, 'execFilterMethod')]);
this.stream.filter!.lastExecMethod = { method, params: JSON.parse(stringParams) };
this.stream.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.session, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
this.stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.streamManager, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
resolve();
}
}

View File

@ -17,8 +17,8 @@
import { Stream } from './Stream';
import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState';
import platform = require('platform');
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
@ -30,6 +30,11 @@ declare var MediaRecorder: any;
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Easy recording of [[Stream]] objects straightaway from the browser. Initialized with [[OpenVidu.initLocalRecorder]] method
@ -45,7 +50,7 @@ export class LocalRecorder {
private connectionId: string;
private mediaRecorder: any;
private chunks: any[] = [];
private blob: Blob;
private blob?: Blob;
private id: string;
private videoPreviewSrc: string;
private videoPreview: HTMLVideoElement;
@ -54,6 +59,7 @@ export class LocalRecorder {
* @hidden
*/
constructor(private stream: Stream) {
platform = PlatformUtils.getInstance();
this.connectionId = (!!this.stream.connection) ? this.stream.connection.connectionId : 'default-connection';
this.id = this.stream.streamId + '_' + this.connectionId + '_localrecord';
this.state = LocalRecorderState.READY;
@ -170,6 +176,7 @@ export class LocalRecorder {
}
this.mediaRecorder.pause();
this.state = LocalRecorderState.PAUSED;
resolve();
} catch (error) {
reject(error);
}
@ -188,6 +195,7 @@ export class LocalRecorder {
}
this.mediaRecorder.resume();
this.state = LocalRecorderState.RECORDING;
resolve();
} catch (error) {
reject(error);
}
@ -209,7 +217,7 @@ export class LocalRecorder {
this.videoPreview.id = this.id;
this.videoPreview.autoplay = true;
if (platform.name === 'Safari') {
if (platform.isSafariBrowser()) {
this.videoPreview.setAttribute('playsinline', 'true');
}
@ -274,7 +282,7 @@ export class LocalRecorder {
if (this.state !== LocalRecorderState.FINISHED) {
throw (Error('Call \'LocalRecord.stop()\' before getting Blob file'));
} else {
return this.blob;
return this.blob!;
}
}
@ -344,7 +352,7 @@ export class LocalRecorder {
}
const sendable = new FormData();
sendable.append('file', this.blob, this.id + '.webm');
sendable.append('file', this.blob!, this.id + '.webm');
http.onreadystatechange = () => {
if (http.readyState === 4) {

View File

@ -19,6 +19,7 @@ import { LocalRecorder } from './LocalRecorder';
import { Publisher } from './Publisher';
import { Session } from './Session';
import { Stream } from './Stream';
import { SessionDisconnectedEvent } from '../OpenViduInternal/Events/SessionDisconnectedEvent';
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
import { Device } from '../OpenViduInternal/Interfaces/Public/Device';
import { OpenViduAdvancedConfiguration } from '../OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration';
@ -27,6 +28,7 @@ import { CustomMediaStreamConstraints } from '../OpenViduInternal/Interfaces/Pri
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
import * as screenSharingAuto from '../OpenViduInternal/ScreenSharing/Screen-Capturing-Auto';
import * as screenSharing from '../OpenViduInternal/ScreenSharing/Screen-Capturing';
@ -38,13 +40,6 @@ import EventEmitter = require('wolfy87-eventemitter');
* @hidden
*/
import RpcBuilder = require('../OpenViduInternal/KurentoUtils/kurento-jsonrpc');
/**
* @hidden
*/
import platform = require('platform');
platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1;
platform['isIonicAndroid'] = platform.os!!.family === 'Android' && platform.name == "Android Browser";
/**
* @hidden
@ -59,6 +54,11 @@ declare var cordova: any;
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Entrypoint of OpenVidu Browser library.
* Use it to initialize objects of type [[Session]], [[Publisher]] and [[LocalRecorder]]
@ -121,11 +121,12 @@ export class OpenVidu {
ee = new EventEmitter()
constructor() {
platform = PlatformUtils.getInstance();
this.libraryVersion = packageJson.version;
logger.info("'OpenVidu' initialized");
logger.info("openvidu-browser version: " + this.libraryVersion);
if (platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') {
if (platform.isMobileDevice()) {
// Listen to orientationchange only on mobile devices
(<any>window).addEventListener('orientationchange', () => {
this.publishers.forEach(publisher => {
@ -138,7 +139,7 @@ export class OpenVidu {
const getNewVideoDimensions = (): Promise<{ newWidth: number, newHeight: number }> => {
return new Promise((resolve, reject) => {
if (platform['isIonicIos']) {
if (platform.isIonicIos()) {
// iOS Ionic. Limitation: must get new dimensions from an existing video element already inserted into DOM
resolve({
newWidth: publisher.stream.streamManager.videos[0].video.videoWidth,
@ -149,8 +150,8 @@ export class OpenVidu {
// New resolution got from different places for Chrome and Firefox. Chrome needs a videoWidth and videoHeight of a videoElement.
// Firefox needs getSettings from the videoTrack
const firefoxSettings = publisher.stream.getMediaStream().getVideoTracks()[0].getSettings();
const newWidth = <number>((platform.name!!.toLowerCase().indexOf('firefox') !== -1) ? firefoxSettings.width : publisher.videoReference.videoWidth);
const newHeight = <number>((platform.name!!.toLowerCase().indexOf('firefox') !== -1) ? firefoxSettings.height : publisher.videoReference.videoHeight);
const newWidth = <number>((platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) ? firefoxSettings.width : publisher.videoReference.videoWidth);
const newHeight = <number>((platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) ? firefoxSettings.height : publisher.videoReference.videoHeight);
resolve({ newWidth, newHeight });
}
});
@ -186,6 +187,7 @@ export class OpenVidu {
} else {
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, publisher.stream, 'videoDimensions', publisher.stream.videoDimensions, { width: oldWidth, height: oldHeight }, 'deviceRotated')]);
publisher.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(publisher, publisher.stream, 'videoDimensions', publisher.stream.videoDimensions, { width: oldWidth, height: oldHeight }, 'deviceRotated')]);
this.session.sendVideoData(publisher);
}
});
clearTimeout(repeatUntilChange);
@ -337,26 +339,20 @@ export class OpenVidu {
* @returns 1 if the browser supports OpenVidu, 0 otherwise
*/
checkSystemRequirements(): number {
const browser = platform.name;
const family = platform.os!!.family;
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
if(this.isIPhoneOrIPad(userAgent)) {
if(this.isIOSWithSafari(userAgent) || platform['isIonicIos']){
return 1;
}
if (platform.isIPhoneOrIPad()) {
if (platform.isIOSWithSafari() || platform.isIonicIos()) {
return 1;
}
return 0;
}
// Accept: Chrome (desktop and Android), Firefox (desktop and Android), Opera (desktop and Android),
// Safari (OSX and iOS), Ionic (Android and iOS), Samsung Internet Browser (Android)
if (
(browser === 'Safari') ||
(browser === 'Chrome') || (browser === 'Chrome Mobile') ||
(browser === 'Firefox') || (browser === 'Firefox Mobile') ||
(browser === 'Opera') || (browser === 'Opera Mobile') ||
(browser === 'Android Browser') || (browser === 'Electron') ||
(browser === 'Samsung Internet Mobile') || (browser === 'Samsung Internet')
// Safari (OSX and iOS), Edge Chromium (>= 80), Ionic (Android and iOS), Samsung Internet Browser (Android)
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() ||
platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser() || platform.isOperaBrowser() ||
platform.isOperaMobileBrowser() || platform.isEdgeBrowser() || platform.isEdgeMobileBrowser() ||
platform.isSafariBrowser() || platform.isAndroidBrowser() || platform.isElectron() || platform.isSamsungBrowser()
) {
return 1;
}
@ -370,22 +366,8 @@ export class OpenVidu {
* Checks if the browser supports screen-sharing. Desktop Chrome, Firefox and Opera support screen-sharing
* @returns 1 if the browser supports screen-sharing, 0 otherwise
*/
checkScreenSharingCapabilities(): number {
const browser = platform.name;
const version = platform?.version ? parseFloat(platform.version) : -1;
const family = platform.os!!.family;
// Reject mobile devices
if (family === 'iOS' || family === 'Android') {
return 0;
}
if ((browser !== 'Chrome') && (browser !== 'Firefox') && (browser !== 'Opera') && (browser !== 'Electron') &&
(browser === 'Safari' && version < 13)) {
return 0;
} else {
return 1;
}
checkScreenSharingCapabilities(): boolean {
return platform.canScreenShare();
}
@ -398,7 +380,7 @@ export class OpenVidu {
const devices: Device[] = [];
// Ionic Android devices
if (platform['isIonicAndroid'] && typeof cordova != "undefined" && cordova?.plugins?.EnumerateDevicesPlugin) {
if (platform.isIonicAndroid() && typeof cordova != "undefined" && cordova?.plugins?.EnumerateDevicesPlugin) {
cordova.plugins.EnumerateDevicesPlugin.getEnumerateDevices().then((pluginDevices: Device[]) => {
let pluginAudioDevices: Device[] = [];
let videoDevices: Device[] = [];
@ -590,10 +572,10 @@ export class OpenVidu {
// Video is deviceId or screen sharing
if (options.videoSource === 'screen' ||
options.videoSource === 'window' ||
(platform.name === 'Electron' && options.videoSource.startsWith('screen:'))) {
(platform.isElectron() && options.videoSource.startsWith('screen:'))) {
// Video is screen sharing
mustAskForAudioTrackLater = !myConstraints.audioTrack && (options.audioSource !== null && options.audioSource !== false);
if (navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') {
if (navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) {
// getDisplayMedia supported
navigator.mediaDevices['getDisplayMedia']({ video: true })
.then(mediaStream => {
@ -663,6 +645,7 @@ export class OpenVidu {
* - `iceServers`: set custom STUN/TURN servers to be used by OpenVidu Browser
* - `screenShareChromeExtension`: url to a custom screen share extension for Chrome to be used instead of the default one, based on ours [https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension)
* - `publisherSpeakingEventsOptions`: custom configuration for the [[PublisherSpeakingEvent]] feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/streammanagerevent.html) feature
* - `forceMediaReconnectionAfterNetworkDrop`: always force WebRTC renegotiation of all the streams of a client after a network loss and reconnection. This can help reducing frozen videos in low quality networks.
*
* Call this method to override previous values at any moment.
*/
@ -763,9 +746,8 @@ export class OpenVidu {
startWs(onConnectSucces: (error: Error) => void): void {
const config = {
heartbeat: 5000,
sendCloseMessage: false,
ws: {
uri: this.wsUri,
uri: this.wsUri + '?sessionId=' + this.session.sessionId,
onconnected: onConnectSucces,
ondisconnect: this.disconnectCallback.bind(this),
onreconnecting: this.reconnectingCallback.bind(this),
@ -782,6 +764,8 @@ export class OpenVidu {
recordingStopped: this.session.onRecordingStopped.bind(this.session),
sendMessage: this.session.onNewMessage.bind(this.session),
streamPropertyChanged: this.session.onStreamPropertyChanged.bind(this.session),
connectionPropertyChanged: this.session.onConnectionPropertyChanged.bind(this.session),
networkQualityLevelChanged: this.session.onNetworkQualityLevelChangedChanged.bind(this.session),
filterEventDispatched: this.session.onFilterEventDispatched.bind(this.session),
iceCandidate: this.session.recvIceCandidate.bind(this.session),
mediaError: this.session.onMediaError.bind(this.session)
@ -899,12 +883,12 @@ export class OpenVidu {
// Screen sharing
if (!this.checkScreenSharingCapabilities()) {
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0) or Electron. Detected client: ' + platform.name);
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0), Edge (>= 80) or Electron. Detected client: ' + platform.getName() + ' ' + platform.getVersion());
logger.error(error);
reject(error);
} else {
if (platform.name === 'Electron') {
if (platform.isElectron()) {
const prefix = "screen:";
const videoSourceString: string = videoSource;
const electronScreenId = videoSourceString.substr(videoSourceString.indexOf(prefix) + prefix.length);
@ -918,7 +902,7 @@ export class OpenVidu {
} else {
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) {
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) && !navigator.mediaDevices['getDisplayMedia']) {
// Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia()
@ -957,7 +941,7 @@ export class OpenVidu {
resolve(myConstraints);
} else {
// Default screen sharing extension for Chrome/Opera, or is Firefox < 66
const firefoxString = platform.name!.indexOf('Firefox') !== -1 ? publisherProperties.videoSource : undefined;
const firefoxString = (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) ? publisherProperties.videoSource : undefined;
screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => {
if (!!error) {
@ -1029,17 +1013,25 @@ export class OpenVidu {
private reconnectedCallback(): void {
logger.warn('Websocket reconnected');
if (this.isRoomAvailable()) {
this.sendRequest('connect', { sessionId: this.session.connection.rpcSessionId }, (error, response) => {
if (!!error) {
logger.error(error);
logger.warn('Websocket was able to reconnect to OpenVidu Server, but your Connection was already destroyed due to timeout. You are no longer a participant of the Session and your media streams have been destroyed');
this.session.onLostConnection("networkDisconnect");
this.jsonRpcClient.close(4101, "Reconnection fault");
} else {
this.jsonRpcClient.resetPing();
this.session.onRecoveredConnection();
}
});
if (!!this.session.connection) {
this.sendRequest('connect', { sessionId: this.session.connection.rpcSessionId }, (error, response) => {
if (!!error) {
logger.error(error);
logger.warn('Websocket was able to reconnect to OpenVidu Server, but your Connection was already destroyed due to timeout. You are no longer a participant of the Session and your media streams have been destroyed');
this.session.onLostConnection("networkDisconnect");
this.jsonRpcClient.close(4101, "Reconnection fault");
} else {
this.jsonRpcClient.resetPing();
this.session.onRecoveredConnection();
}
});
} else {
logger.warn('There was no previous connection when running reconnection callback');
// Make Session object dispatch 'sessionDisconnected' event
const sessionDisconnectEvent = new SessionDisconnectedEvent(this.session, 'networkDisconnect');
this.session.ee.emitEvent('sessionDisconnected', [sessionDisconnectEvent]);
sessionDisconnectEvent.callDefaultBehavior();
}
} else {
alert('Connection error. Please reload page.');
}
@ -1057,21 +1049,7 @@ export class OpenVidu {
private isScreenShare(videoSource: string) {
return videoSource === 'screen' ||
videoSource === 'window' ||
(platform.name === 'Electron' && videoSource.startsWith('screen:'))
(platform.isElectron() && videoSource.startsWith('screen:'))
}
private isIPhoneOrIPad(userAgent): boolean {
const isTouchable = 'ontouchend' in document;
const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable;
const isIPhone = /\b(\w*iPhone\w*)\b/.test(userAgent) && /\b(\w*Mobile\w*)\b/.test(userAgent) && isTouchable;
return isIPad || isIPhone;
}
private isIOSWithSafari(userAgent): boolean{
return /\b(\w*Apple\w*)\b/.test(navigator.vendor) && /\b(\w*Safari\w*)\b/.test(userAgent)
&& !/\b(\w*CriOS\w*)\b/.test(userAgent) && !/\b(\w*FxiOS\w*)\b/.test(userAgent);
}
}

View File

@ -28,15 +28,19 @@ import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPro
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import platform = require('platform');
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Packs local media streams. Participants can publish it to a session. Initialized with [[OpenVidu.initPublisher]] method
*
@ -95,10 +99,32 @@ export class Publisher extends StreamManager {
// TODO: CLEAN 2.15.0 LEGACY CODE
// THIS LINE:
super(!openvidu.openviduServerVersion ? /*2.15.0 or 2.16.0 NOT CONNECTED (LEGACY)*/ new StreamLEGACY((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }) : /*2.16.0 CONNECTED (NORMAL API)*/ new Stream(openvidu.session, { publisherProperties: properties, mediaConstraints: {} }), targEl);
super(
!openvidu.openviduServerVersion
// 2.15.0 or 2.16.0 NOT CONNECTED (LEGACY)
? new StreamLEGACY(
!!openvidu.session
? openvidu.session
: new Session(openvidu),
{ publisherProperties: properties, mediaConstraints: {} }
)
// 2.16.0 CONNECTED (NORMAL API)
: new Stream(openvidu.session, {
publisherProperties: properties,
mediaConstraints: {},
}),
targEl
);
// SHOULD GET BACK TO:
// super(new Stream((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }), targEl);
// super(
// new Stream(
// !!openvidu.session ? openvidu.session : new Session(openvidu),
// { publisherProperties: properties, mediaConstraints: {} }
// ),
// targEl
// );
platform = PlatformUtils.getInstance();
this.properties = properties;
this.openvidu = openvidu;
@ -130,7 +156,7 @@ export class Publisher extends StreamManager {
*/
publishAudio(value: boolean): void {
if (this.stream.audioActive !== value) {
const affectedMediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote : this.stream.getMediaStream();
const affectedMediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote! : this.stream.getMediaStream();
affectedMediaStream.getAudioTracks().forEach((track) => {
track.enabled = value;
});
@ -149,6 +175,7 @@ export class Publisher extends StreamManager {
} else {
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this.stream, 'audioActive', value, !value, 'publishAudio')]);
this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, this.stream, 'audioActive', value, !value, 'publishAudio')]);
this.session.sendVideoData(this.stream.streamManager);
}
});
}
@ -177,7 +204,7 @@ export class Publisher extends StreamManager {
*/
publishVideo(value: boolean): void {
if (this.stream.videoActive !== value) {
const affectedMediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote : this.stream.getMediaStream();
const affectedMediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote! : this.stream.getMediaStream();
affectedMediaStream.getVideoTracks().forEach((track) => {
track.enabled = value;
});
@ -196,6 +223,7 @@ export class Publisher extends StreamManager {
} else {
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this.stream, 'videoActive', value, !value, 'publishVideo')]);
this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, this.stream, 'videoActive', value, !value, 'publishVideo')]);
this.session.sendVideoData(this.stream.streamManager);
}
});
}
@ -305,7 +333,7 @@ export class Publisher extends StreamManager {
replaceTrack(track: MediaStreamTrack): Promise<any> {
const replaceMediaStreamTrack = () => {
const mediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote : this.stream.getMediaStream();
const mediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote! : this.stream.getMediaStream();
let removedTrack: MediaStreamTrack;
if (track.kind === 'video') {
removedTrack = mediaStream.getVideoTracks()[0];
@ -315,6 +343,7 @@ export class Publisher extends StreamManager {
mediaStream.removeTrack(removedTrack);
removedTrack.stop();
mediaStream.addTrack(track);
this.session.sendVideoData(this.stream.streamManager, 5, true, 5);
}
return new Promise((resolve, reject) => {
@ -404,7 +433,7 @@ export class Publisher extends StreamManager {
if (this.stream.isSendVideo()) {
if (!this.stream.isSendScreen()) {
if (platform['isIonicIos'] || platform.name === 'Safari') {
if (platform.isIonicIos() || platform.isSafariBrowser()) {
// iOS Ionic or Safari. Limitation: cannot set videoDimensions directly, as the videoReference is not loaded
// if not added to DOM. Must add it to DOM and wait for videoWidth and videoHeight properties to be defined
@ -440,7 +469,7 @@ export class Publisher extends StreamManager {
// Orientation must be checked for mobile devices (width and height are reversed)
const { width, height } = this.getVideoDimensions(mediaStream);
if ((platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') && (window.innerHeight > window.innerWidth)) {
if (platform.isMobileDevice() && (window.innerHeight > window.innerWidth)) {
// Mobile portrait mode
this.stream.videoDimensions = {
width: height || 0,
@ -464,8 +493,8 @@ export class Publisher extends StreamManager {
};
this.screenShareResizeInterval = setInterval(() => {
const firefoxSettings = mediaStream.getVideoTracks()[0].getSettings();
const newWidth = (platform.name === 'Chrome' || platform.name === 'Opera') ? this.videoReference.videoWidth : firefoxSettings.width;
const newHeight = (platform.name === 'Chrome' || platform.name === 'Opera') ? this.videoReference.videoHeight : firefoxSettings.height;
const newWidth = (platform.isChromeBrowser() || platform.isOperaBrowser()) ? this.videoReference.videoWidth : firefoxSettings.width;
const newHeight = (platform.isChromeBrowser() || platform.isOperaBrowser()) ? this.videoReference.videoHeight : firefoxSettings.height;
if (this.stream.isLocalStreamPublished &&
(newWidth !== this.stream.videoDimensions.width ||
newHeight !== this.stream.videoDimensions.height)) {
@ -488,6 +517,7 @@ export class Publisher extends StreamManager {
} else {
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this.stream, 'videoDimensions', this.stream.videoDimensions, oldValue, 'screenResized')]);
this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, this.stream, 'videoDimensions', this.stream.videoDimensions, oldValue, 'screenResized')]);
this.session.sendVideoData(this.stream.streamManager);
}
});
}
@ -634,7 +664,7 @@ export class Publisher extends StreamManager {
startTime = Date.now();
this.setPermissionDialogTimer(timeForDialogEvent);
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') {
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) {
navigator.mediaDevices['getDisplayMedia']({ video: true })
.then(mediaStream => {
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
@ -683,7 +713,7 @@ export class Publisher extends StreamManager {
initializeVideoReference(mediaStream: MediaStream) {
this.videoReference = document.createElement('video');
if (platform.name === 'Safari') {
if (platform.isSafariBrowser()) {
this.videoReference.setAttribute('playsinline', 'true');
}

View File

@ -26,7 +26,8 @@ import { Capabilities } from '../OpenViduInternal/Interfaces/Public/Capabilities
import { EventDispatcher } from './EventDispatcher';
import { SignalOptions } from '../OpenViduInternal/Interfaces/Public/SignalOptions';
import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties';
import { ConnectionOptions } from '../OpenViduInternal/Interfaces/Private/ConnectionOptions';
import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions';
import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions';
import { ObjMap } from '../OpenViduInternal/Interfaces/Private/ObjMap';
import { SessionOptions } from '../OpenViduInternal/Interfaces/Private/SessionOptions';
import { ConnectionEvent } from '../OpenViduInternal/Events/ConnectionEvent';
@ -37,17 +38,23 @@ import { SessionDisconnectedEvent } from '../OpenViduInternal/Events/SessionDisc
import { SignalEvent } from '../OpenViduInternal/Events/SignalEvent';
import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent';
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
import { ConnectionPropertyChangedEvent } from '../OpenViduInternal/Events/ConnectionPropertyChangedEvent';
import { NetworkQualityLevelChangedEvent } from '../OpenViduInternal/Events/NetworkQualityLevelChangedEvent';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import platform = require('platform');
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Represents a video call. It can also be seen as a videoconference room where multiple users can connect.
* Participants who publish their videos to a session can be seen by the rest of users connected to that specific session.
@ -57,6 +64,7 @@ const logger: OpenViduLogger = OpenViduLogger.getInstance();
*
* - connectionCreated ([[ConnectionEvent]])
* - connectionDestroyed ([[ConnectionEvent]])
* - connectionPropertyChanged ([[ConnectionPropertyChangedEvent]]) <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
* - sessionDisconnected ([[SessionDisconnectedEvent]])
* - streamCreated ([[StreamEvent]])
* - streamDestroyed ([[StreamEvent]])
@ -66,9 +74,9 @@ const logger: OpenViduLogger = OpenViduLogger.getInstance();
* - signal ([[SignalEvent]])
* - recordingStarted ([[RecordingEvent]])
* - recordingStopped ([[RecordingEvent]])
* - networkQualityLevelChanged ([[NetworkQualityLevelChangedEvent]])
* - reconnecting
* - reconnected
*
*/
export class Session extends EventDispatcher {
@ -88,7 +96,7 @@ export class Session extends EventDispatcher {
streamManagers: StreamManager[] = [];
/**
* Object defining the methods that the client is able to call. These are defined by the role of the token used to connect to the Session.
* Object defining the methods that the client is able to call. These are defined by the [[Connection.role]].
* This object is only defined after [[Session.connect]] has been successfully resolved
*/
capabilities: Capabilities;
@ -127,15 +135,23 @@ export class Session extends EventDispatcher {
* @hidden
*/
stopSpeakingEventsEnabledOnce = false;
/**
* @hidden
*/
private videoDataInterval: NodeJS.Timeout;
/**
* @hidden
*/
private videoDataTimeout: NodeJS.Timeout;
// TODO: CLEAN 2.15.0 LEGACY CODE
/**
* @hidden
* @hidden
*/
isFirstIonicIosSubscriber = true;
/**
* @hidden
* @hidden
*/
countDownForIonicIosSubscribersActive = true;
// END LEGACY CODE
@ -146,6 +162,7 @@ export class Session extends EventDispatcher {
*/
constructor(openvidu: OpenVidu) {
super();
platform = PlatformUtils.getInstance();
this.openvidu = openvidu;
}
@ -197,7 +214,7 @@ export class Session extends EventDispatcher {
reject(error);
});
} else {
reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.name + ' (version ' + platform.version + ') for ' + platform.os!!.family + ' is not supported in OpenVidu'));
reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.getName() + ' (version ' + platform.getVersion() + ') for ' + platform.getFamily() + ' is not supported in OpenVidu'));
}
});
}
@ -382,6 +399,7 @@ export class Session extends EventDispatcher {
this.connection.addStream(publisher.stream);
publisher.stream.publish()
.then(() => {
this.sendVideoData(publisher, 8, true, 5);
resolve();
})
.catch(error => {
@ -395,6 +413,7 @@ export class Session extends EventDispatcher {
publisher.reestablishStreamPlayingEvent();
publisher.stream.publish()
.then(() => {
this.sendVideoData(publisher, 8, true, 5);
resolve();
})
.catch(error => {
@ -596,7 +615,7 @@ export class Session extends EventDispatcher {
/**
* See [[EventDispatcher.on]]
*/
on(type: string, handler: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent) => void): EventDispatcher {
on(type: string, handler: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent | NetworkQualityLevelChangedEvent) => void): EventDispatcher {
super.onAux(type, "Event '" + type + "' triggered by 'Session'", handler);
@ -628,7 +647,7 @@ export class Session extends EventDispatcher {
/**
* See [[EventDispatcher.once]]
*/
once(type: string, handler: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent) => void): Session {
once(type: string, handler: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent | NetworkQualityLevelChangedEvent) => void): Session {
super.onceAux(type, "Event '" + type + "' triggered once by 'Session'", handler);
@ -660,7 +679,7 @@ export class Session extends EventDispatcher {
/**
* See [[EventDispatcher.off]]
*/
off(type: string, handler?: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent) => void): Session {
off(type: string, handler?: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent | NetworkQualityLevelChangedEvent) => void): Session {
super.off(type, handler);
@ -699,7 +718,7 @@ export class Session extends EventDispatcher {
/**
* @hidden
*/
onParticipantJoined(response: ConnectionOptions): void {
onParticipantJoined(response: RemoteConnectionOptions): void {
// Connection shouldn't exist
this.getConnection(response.id, '')
@ -751,9 +770,10 @@ export class Session extends EventDispatcher {
/**
* @hidden
*/
onParticipantPublished(response: ConnectionOptions): void {
onParticipantPublished(response: RemoteConnectionOptions): void {
const afterConnectionFound = (connection) => {
this.remoteConnections[connection.connectionId] = connection;
if (!this.remoteStreamsCreated[connection.stream.streamId]) {
@ -777,7 +797,7 @@ export class Session extends EventDispatcher {
// Update existing Connection
connection = con;
response.metadata = con.data;
connection.options = response;
connection.remoteOptions = response;
connection.initRemoteStreams(response.streams);
afterConnectionFound(connection);
})
@ -801,12 +821,12 @@ export class Session extends EventDispatcher {
.then(connection => {
const streamEvent = new StreamEvent(true, this, 'streamDestroyed', connection.stream, msg.reason);
const streamEvent = new StreamEvent(true, this, 'streamDestroyed', connection.stream!, msg.reason);
this.ee.emitEvent('streamDestroyed', [streamEvent]);
streamEvent.callDefaultBehavior();
// Deleting the remote stream
const streamId: string = connection.stream.streamId;
const streamId: string = connection.stream!.streamId;
delete this.remoteStreamsCreated[streamId];
@ -934,6 +954,44 @@ export class Session extends EventDispatcher {
}
}
/**
* @hidden
*/
onConnectionPropertyChanged(msg): void {
let oldValue;
switch (msg.property) {
case 'role':
oldValue = this.connection.role.slice();
this.connection.role = msg.newValue;
this.connection.localOptions!.role = msg.newValue;
break;
case 'record':
oldValue = this.connection.record;
msg.newValue = msg.newValue === 'true';
this.connection.record = msg.newValue;
this.connection.localOptions!.record = msg.newValue;
break;
}
this.ee.emitEvent('connectionPropertyChanged', [new ConnectionPropertyChangedEvent(this, this.connection, msg.property, msg.newValue, oldValue)]);
}
/**
* @hidden
*/
onNetworkQualityLevelChangedChanged(msg): void {
if (msg.connectionId === this.connection.connectionId) {
this.ee.emitEvent('networkQualityLevelChanged', [new NetworkQualityLevelChangedEvent(this, msg.newValue, msg.oldValue, this.connection)]);
} else {
this.getConnection(msg.connectionId, 'Connection not found for connectionId ' + msg.connectionId)
.then((connection: Connection) => {
this.ee.emitEvent('networkQualityLevelChanged', [new NetworkQualityLevelChangedEvent(this, msg.newValue, msg.oldValue, connection)]);
})
.catch(openViduError => {
logger.error(openViduError);
});
}
}
/**
* @hidden
*/
@ -958,9 +1016,9 @@ export class Session extends EventDispatcher {
};
this.getConnection(msg.senderConnectionId, 'Connection not found for connectionId ' + msg.senderConnectionId + ' owning endpoint ' + msg.endpointName + '. Ice candidate will be ignored: ' + candidate)
.then(connection => {
const stream = connection.stream;
const stream: Stream = connection.stream!;
stream.getWebRtcPeer().addIceCandidate(candidate).catch(error => {
logger.error('Error adding candidate for ' + stream.streamId
logger.error('Error adding candidate for ' + stream!.streamId
+ ' stream of endpoint ' + msg.endpointName + ': ' + error);
});
})
@ -989,7 +1047,7 @@ export class Session extends EventDispatcher {
*/
onLostConnection(reason: string): void {
logger.warn('Lost connection in Session ' + this.sessionId);
if (!!this.sessionId && !this.connection.disposed) {
if (!!this.sessionId && !!this.connection && !this.connection.disposed) {
this.leave(true, reason);
}
}
@ -1042,8 +1100,8 @@ export class Session extends EventDispatcher {
this.getConnection(connectionId, 'No connection found for connectionId ' + connectionId)
.then(connection => {
logger.info('Filter event dispatched');
const stream: Stream = connection.stream;
stream.filter.handlers[response.eventType](new FilterEvent(stream.filter, response.eventType, response.data));
const stream: Stream = connection.stream!;
stream.filter!.handlers[response.eventType](new FilterEvent(stream.filter!, response.eventType, response.data));
});
}
@ -1086,6 +1144,7 @@ export class Session extends EventDispatcher {
forced = !!forced;
logger.info('Leaving Session (forced=' + forced + ')');
this.stopVideoDataIntervals();
if (!!this.connection) {
if (!this.connection.disposed && !forced) {
@ -1119,7 +1178,7 @@ export class Session extends EventDispatcher {
const joinParams = {
token: (!!token) ? token : '',
session: this.sessionId,
platform: !!platform.description ? platform.description : 'unknown',
platform: !!platform.getDescription() ? platform.getDescription() : 'unknown',
metadata: !!this.options.metadata ? this.options.metadata : '',
secret: this.openvidu.getSecret(),
recorder: this.openvidu.getRecorder()
@ -1127,6 +1186,63 @@ export class Session extends EventDispatcher {
return joinParams;
}
sendVideoData(streamManager: StreamManager, intervalSeconds: number = 1, doInterval: boolean = false, maxLoops: number = 1) {
if (
platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() ||
platform.isOperaMobileBrowser() || platform.isEdgeBrowser() || platform.isElectron() ||
(platform.isSafariBrowser() && !platform.isIonicIos()) || platform.isAndroidBrowser() ||
platform.isSamsungBrowser() || platform.isIonicAndroid() || (platform.isIPhoneOrIPad() && platform.isIOSWithSafari())
) {
const obtainAndSendVideo = async () => {
const statsMap = await streamManager.stream.getRTCPeerConnection().getStats();
const arr: any[] = [];
statsMap.forEach(stats => {
if (("frameWidth" in stats) && ("frameHeight" in stats) && (arr.length === 0)) {
arr.push(stats);
}
});
if (arr.length > 0) {
this.openvidu.sendRequest('videoData', {
height: arr[0].frameHeight,
width: arr[0].frameWidth,
videoActive: streamManager.stream.videoActive != null ? streamManager.stream.videoActive : false,
audioActive: streamManager.stream.audioActive != null ? streamManager.stream.audioActive : false
}, (error, response) => {
if (error) {
logger.error("Error sending 'videoData' event", error);
}
});
}
}
if (doInterval) {
let loops = 1;
this.videoDataInterval = setInterval(() => {
if (loops < maxLoops) {
loops++;
obtainAndSendVideo();
}else {
clearInterval(this.videoDataInterval);
}
}, intervalSeconds * 1000);
} else {
this.videoDataTimeout = setTimeout(obtainAndSendVideo, intervalSeconds * 1000);
}
} else if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser() || platform.isIonicIos() || platform.isReactNative()) {
// Basic version for Firefox and Ionic iOS. They do not support stats
this.openvidu.sendRequest('videoData', {
height: streamManager.stream.videoDimensions?.height || 0,
width: streamManager.stream.videoDimensions?.width || 0,
videoActive: streamManager.stream.videoActive != null ? streamManager.stream.videoActive : false,
audioActive: streamManager.stream.audioActive != null ? streamManager.stream.audioActive : false
}, (error, response) => {
if (error) {
logger.error("Error sending 'videoData' event", error);
}
});
} else {
logger.error('Browser ' + platform.getName() + ' (version ' + platform.getVersion() + ') for ' + platform.getFamily() + ' is not supported in OpenVidu for Network Quality');
}
}
/* Private methods */
@ -1139,35 +1255,33 @@ export class Session extends EventDispatcher {
const joinParams = this.initializeParams(token);
this.openvidu.sendRequest('joinRoom', joinParams, (error, response) => {
this.openvidu.sendRequest('joinRoom', joinParams, (error, response: LocalConnectionOptions) => {
if (!!error) {
reject(error);
} else {
// Initialize capabilities object with the role
this.capabilities = {
subscribe: true,
publish: this.openvidu.role !== 'SUBSCRIBER',
forceUnpublish: this.openvidu.role === 'MODERATOR',
forceDisconnect: this.openvidu.role === 'MODERATOR'
};
this.processJoinRoomResponse(response);
// Initialize local Connection object with values returned by openvidu-server
this.connection = new Connection(this);
// FIXME CONFLICT WITH MASTER
// In modern code, the OpenVidu version is obtained through other means.
// However in the mediasoup branch this value is used in several of the
// *LEGACY.ts files.
<<<<<<< HEAD
this.openvidu.openviduServerVersion = response.openviduServerVersion;
this.connection.connectionId = response.id;
this.connection.creationTime = response.createdAt;
this.connection.data = response.metadata;
this.connection.rpcSessionId = response.sessionId;
=======
this.connection = new Connection(this, response);
>>>>>>> master
// Initialize remote Connections with value returned by openvidu-server
const events = {
connections: new Array<Connection>(),
streams: new Array<Stream>()
};
const existingParticipants: ConnectionOptions[] = response.value;
existingParticipants.forEach(participant => {
const connection = new Connection(this, participant);
const existingParticipants: RemoteConnectionOptions[] = response.value;
existingParticipants.forEach((remoteConnectionOptions: RemoteConnectionOptions) => {
const connection = new Connection(this, remoteConnectionOptions);
this.remoteConnections[connection.connectionId] = connection;
events.connections.push(connection);
if (!!connection.stream) {
@ -1208,6 +1322,11 @@ export class Session extends EventDispatcher {
}
}
private stopVideoDataIntervals(): void {
clearInterval(this.videoDataInterval);
clearTimeout(this.videoDataTimeout);
}
private stringClientMetadata(metadata: any): string {
if (typeof metadata !== 'string') {
return JSON.stringify(metadata);
@ -1272,12 +1391,7 @@ export class Session extends EventDispatcher {
this.sessionId = <string>queryParams['sessionId'];
const secret = queryParams['secret'];
const recorder = queryParams['recorder'];
const coturnIp = queryParams['coturnIp'];
const turnUsername = queryParams['turnUsername'];
const turnCredential = queryParams['turnCredential'];
const role = queryParams['role'];
const webrtcStatsInterval = queryParams['webrtcStatsInterval'];
const openviduServerVersion = queryParams['version'];
if (!!secret) {
this.openvidu.secret = secret;
@ -1285,31 +1399,9 @@ export class Session extends EventDispatcher {
if (!!recorder) {
this.openvidu.recorder = true;
}
if (!!turnUsername && !!turnCredential) {
const stunUrl = 'stun:' + coturnIp + ':3478';
const turnUrl1 = 'turn:' + coturnIp + ':3478';
const turnUrl2 = turnUrl1 + '?transport=tcp';
this.openvidu.iceServers = [
{ urls: [stunUrl] },
{ urls: [turnUrl1, turnUrl2], username: turnUsername, credential: turnCredential }
];
logger.log("STUN/TURN server IP: " + coturnIp);
logger.log('TURN temp credentials [' + turnUsername + ':' + turnCredential + ']');
}
if (!!role) {
this.openvidu.role = role;
}
if (!!webrtcStatsInterval) {
this.openvidu.webrtcStatsInterval = +webrtcStatsInterval;
}
if (!!openviduServerVersion) {
logger.info("openvidu-server version: " + openviduServerVersion);
if (openviduServerVersion !== this.openvidu.libraryVersion) {
logger.warn('OpenVidu Server (' + openviduServerVersion +
') and OpenVidu Browser (' + this.openvidu.libraryVersion +
') versions do NOT match. There may be incompatibilities')
}
}
this.openvidu.wsUri = 'wss://' + url.host + '/openvidu';
this.openvidu.httpUri = 'https://' + url.host;
@ -1319,4 +1411,32 @@ export class Session extends EventDispatcher {
}
}
private processJoinRoomResponse(opts: LocalConnectionOptions) {
this.sessionId = opts.session;
if (opts.coturnIp != null && opts.turnUsername != null && opts.turnCredential != null) {
const stunUrl = 'stun:' + opts.coturnIp + ':3478';
const turnUrl1 = 'turn:' + opts.coturnIp + ':3478';
const turnUrl2 = turnUrl1 + '?transport=tcp';
this.openvidu.iceServers = [
{ urls: [stunUrl] },
{ urls: [turnUrl1, turnUrl2], username: opts.turnUsername, credential: opts.turnCredential }
];
logger.log("STUN/TURN server IP: " + opts.coturnIp);
logger.log('TURN temp credentials [' + opts.turnUsername + ':' + opts.turnCredential + ']');
}
this.openvidu.role = opts.role;
this.capabilities = {
subscribe: true,
publish: this.openvidu.role !== 'SUBSCRIBER',
forceUnpublish: this.openvidu.role === 'MODERATOR',
forceDisconnect: this.openvidu.role === 'MODERATOR'
};
logger.info("openvidu-server version: " + opts.version);
if (opts.version !== this.openvidu.libraryVersion) {
logger.warn('OpenVidu Server (' + opts.version +
') and OpenVidu Browser (' + this.openvidu.libraryVersion +
') versions do NOT match. There may be incompatibilities')
}
}
}

View File

@ -30,19 +30,22 @@ import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpea
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
* @hidden
*/
import hark = require('hark');
import platform = require('platform');
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Represents each one of the media streams available in OpenVidu Server for certain session.
@ -108,7 +111,7 @@ export class Stream extends EventDispatcher {
* - `"SCREEN"`: when the video source comes from screen-sharing.
* - `"CUSTOM"`: when [[PublisherProperties.videoSource]] has been initialized in the Publisher side with a custom MediaStreamTrack when calling [[OpenVidu.initPublisher]]).
* - `"IPCAM"`: when the video source comes from an IP camera participant instead of a regular participant (see [IP cameras](/en/stable/advanced-features/ip-cameras/)).
*
*
* If [[hasVideo]] is false, this property is undefined
*/
typeOfVideo?: string;
@ -136,10 +139,10 @@ export class Stream extends EventDispatcher {
* [[Filter.execMethod]] and remove it with [[Stream.removeFilter]]. Be aware that the client calling this methods must have the
* necessary permissions: the token owned by the client must have been initialized with the appropriated `allowedFilters` array.
*/
filter: Filter;
filter?: Filter;
protected webRtcPeer: WebRtcPeer;
protected mediaStream: MediaStream;
protected mediaStream?: MediaStream;
private webRtcStats: WebRtcStats;
private isSubscribeToRemote = false;
@ -203,7 +206,7 @@ export class Stream extends EventDispatcher {
/**
* @hidden
*/
localMediaStreamWhenSubscribedToRemote: MediaStream;
localMediaStreamWhenSubscribedToRemote?: MediaStream;
/**
@ -212,7 +215,7 @@ export class Stream extends EventDispatcher {
constructor(session: Session, options: InboundStreamOptions | OutboundStreamOptions | {}) {
super();
platform = PlatformUtils.getInstance();
this.session = session;
if (options.hasOwnProperty('id')) {
@ -262,7 +265,7 @@ export class Stream extends EventDispatcher {
}
this.ee.on('mediastream-updated', () => {
this.streamManager.updateMediaStream(this.mediaStream);
this.streamManager.updateMediaStream(this.mediaStream!);
logger.debug('Video srcObject [' + this.mediaStream + '] updated in stream [' + this.streamId + ']');
});
}
@ -323,7 +326,7 @@ export class Stream extends EventDispatcher {
}
} else {
logger.info('Filter successfully applied on Stream ' + this.streamId);
const oldValue: Filter = this.filter;
const oldValue: Filter = this.filter!;
this.filter = new Filter(type, options);
this.filter.stream = this;
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter')]);
@ -356,10 +359,10 @@ export class Stream extends EventDispatcher {
}
} else {
logger.info('Filter successfully removed from Stream ' + this.streamId);
const oldValue = this.filter;
const oldValue = this.filter!;
delete this.filter;
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter')]);
this.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter, oldValue, 'applyFilter')]);
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter!, oldValue, 'applyFilter')]);
this.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter!, oldValue, 'applyFilter')]);
resolve();
}
}
@ -382,7 +385,7 @@ export class Stream extends EventDispatcher {
* @returns Native MediaStream Web API object
*/
getMediaStream(): MediaStream {
return this.mediaStream;
return this.mediaStream!;
}
/* Hidden methods */
@ -537,7 +540,7 @@ export class Stream extends EventDispatcher {
*/
isSendScreen(): boolean {
let screen = this.outboundStreamOpts.publisherProperties.videoSource === 'screen';
if (platform.name === 'Electron') {
if (platform.isElectron()) {
screen = typeof this.outboundStreamOpts.publisherProperties.videoSource === 'string' &&
this.outboundStreamOpts.publisherProperties.videoSource.startsWith('screen:');
}
@ -776,7 +779,7 @@ export class Stream extends EventDispatcher {
if (!this.getWebRtcPeer() || !this.getRTCPeerConnection()) {
return false;
}
if (this.isLocal && !!this.session.openvidu.advancedConfiguration.forceMediaReconnectionAfterNetworkDrop) {
if (this.isLocal() && !!this.session.openvidu.advancedConfiguration.forceMediaReconnectionAfterNetworkDrop) {
logger.warn('OpenVidu Browser advanced configuration option "forceMediaReconnectionAfterNetworkDrop" is enabled. Publisher stream ' + this.streamId + 'will force a reconnection');
return true;
}
@ -1022,8 +1025,8 @@ export class Stream extends EventDispatcher {
}
}
protected initHarkEvents(): void {
if (!!this.mediaStream.getAudioTracks()[0]) {
private initHarkEvents(): void {
if (!!this.mediaStream!.getAudioTracks()[0]) {
// Hark events can only be set if audio track is available
if (this.streamManager.remote) {
// publisherStartSpeaking/publisherStopSpeaking is only defined for remote streams
@ -1226,4 +1229,4 @@ export class Stream extends EventDispatcher {
(report.type === 'candidate-pair' && report.nominated && report.bytesSent > 0);
}
}
}

View File

@ -22,14 +22,19 @@ import { Event } from '../OpenViduInternal/Events/Event';
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import platform = require('platform');
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Interface in charge of displaying the media streams in the HTML DOM. This wraps any [[Publisher]] and [[Subscriber]] object.
* You can insert as many video players fo the same Stream as you want by calling [[StreamManager.addVideoElement]] or
@ -82,7 +87,7 @@ export class StreamManager extends EventDispatcher {
/**
* @hidden
*/
firstVideoElement: StreamManagerVideo;
firstVideoElement?: StreamManagerVideo;
/**
* @hidden
*/
@ -101,7 +106,7 @@ export class StreamManager extends EventDispatcher {
*/
constructor(stream: Stream, targetElement?: HTMLElement | string) {
super();
platform = PlatformUtils.getInstance();
this.stream = stream;
this.stream.streamManager = this;
this.remote = !this.stream.isLocal();
@ -121,7 +126,7 @@ export class StreamManager extends EventDispatcher {
id: '',
canplayListenerAdded: false
};
if (platform.name === 'Safari') {
if (platform.isSafariBrowser()) {
this.firstVideoElement.video.setAttribute('playsinline', 'true');
}
this.targetElement = targEl;
@ -379,7 +384,7 @@ export class StreamManager extends EventDispatcher {
video.autoplay = true;
video.controls = false;
if (platform.name === 'Safari') {
if (platform.isSafariBrowser()) {
video.setAttribute('playsinline', 'true');
}
@ -463,7 +468,7 @@ export class StreamManager extends EventDispatcher {
updateMediaStream(mediaStream: MediaStream) {
this.videos.forEach(streamManagerVideo => {
streamManagerVideo.video.srcObject = mediaStream;
if (platform['isIonicIos']) {
if (platform.isIonicIos()) {
// iOS Ionic. LIMITATION: must reinsert the video in the DOM for
// the media stream to be updated
const vParent = streamManagerVideo.video.parentElement;
@ -506,7 +511,7 @@ export class StreamManager extends EventDispatcher {
}
private mirrorVideo(video): void {
if (!platform['isIonicIos']) {
if (!platform.isIonicIos()) {
video.style.transform = 'rotateY(180deg)';
video.style.webkitTransform = 'rotateY(180deg)';
}

View File

@ -0,0 +1,74 @@
/*
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
*
* 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 { Connection } from '../../OpenVidu/Connection';
import { Session } from '../../OpenVidu/Session';
import { Event } from './Event';
/**
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* Defines event `connectionPropertyChanged` dispatched by [[Session]] object.
* This event is fired when any property of the local [[Connection]] object changes.
* The properties that may change are [[Connection.role]] and [[Connection.record]].
*
* The only way the Connection properties may change is by updating them through:
*
* - [API REST](/en/stable/reference-docs/REST-API/#patch-openviduapisessionsltsession_idgtconnectionltconnection_idgt)
* - [openvidu-java-client](/en/stable/reference-docs/openvidu-java-client/#update-a-connection)
* - [openvidu-node-client](/en/stable/reference-docs/openvidu-node-client/#update-a-connection)<br><br>
*/
export class ConnectionPropertyChangedEvent extends Event {
/**
* The Connection whose property has changed
*/
connection: Connection;
/**
* The property of the stream that changed. This value is either `"role"` or `"record"`
*/
changedProperty: string;
/**
* New value of the property (after change, current value)
*/
newValue: Object;
/**
* Previous value of the property (before change)
*/
oldValue: Object;
/**
* @hidden
*/
constructor(target: Session, connection: Connection, changedProperty: string, newValue: Object, oldValue: Object) {
super(false, target, 'connectionPropertyChanged');
this.connection = connection;
this.changedProperty = changedProperty;
this.newValue = newValue;
this.oldValue = oldValue;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -16,7 +16,6 @@
*/
import { Event } from './Event';
import { Stream } from '../../OpenVidu/Stream';
import { Filter } from '../../OpenVidu/Filter';

View File

@ -0,0 +1,61 @@
/*
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
*
* 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 { Event } from './Event';
import { Session } from '../../OpenVidu/Session';
import { Connection } from '../../OpenVidu/Connection';
/**
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* Defines event `networkQualityLevelChanged` dispatched by [[Session]].
* This event is fired when the network quality level of a [[Connection]] changes. See [network quality](/en/stable/advanced-features/network-quality/)
*/
export class NetworkQualityLevelChangedEvent extends Event {
/**
* New value of the network quality level
*/
newValue: number;
/**
* Old value of the network quality level
*/
oldValue: number;
/**
* Connection for whom the network quality level changed
*/
connection: Connection
/**
* @hidden
*/
constructor(target: Session, newValue: number, oldValue: number, connection: Connection) {
super(false, target, 'networkQualityLevelChanged');
this.newValue = newValue;
this.oldValue = oldValue;
this.connection = connection;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -62,12 +62,12 @@ export class SessionDisconnectedEvent extends Event {
// Dispose and delete all remote Connections
for (const connectionId in session.remoteConnections) {
if (!!session.remoteConnections[connectionId].stream) {
session.remoteConnections[connectionId].stream.disposeWebRtcPeer();
session.remoteConnections[connectionId].stream.disposeMediaStream();
if (session.remoteConnections[connectionId].stream.streamManager) {
session.remoteConnections[connectionId].stream.streamManager.removeAllVideos();
session.remoteConnections[connectionId].stream!.disposeWebRtcPeer();
session.remoteConnections[connectionId].stream!.disposeMediaStream();
if (session.remoteConnections[connectionId].stream!.streamManager) {
session.remoteConnections[connectionId].stream!.streamManager.removeAllVideos();
}
delete session.remoteStreamsCreated[session.remoteConnections[connectionId].stream.streamId];
delete session.remoteStreamsCreated[session.remoteConnections[connectionId].stream!.streamId];
session.remoteConnections[connectionId].dispose();
}
delete session.remoteConnections[connectionId];

View File

@ -101,8 +101,8 @@ export class StreamEvent extends Event {
// Delete StreamOptionsServer from remote Connection
const remoteConnection = this.stream.session.remoteConnections[this.stream.connection.connectionId];
if (!!remoteConnection && !!remoteConnection.options) {
const streamOptionsServer = remoteConnection.options.streams;
if (!!remoteConnection && !!remoteConnection.remoteOptions) {
const streamOptionsServer = remoteConnection.remoteOptions.streams;
for (let i = streamOptionsServer.length - 1; i >= 0; --i) {
if (streamOptionsServer[i].id === this.stream.streamId) {
streamOptionsServer.splice(i, 1);

View File

@ -0,0 +1,33 @@
/*
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
*
* 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 { RemoteConnectionOptions } from './RemoteConnectionOptions';
export interface LocalConnectionOptions {
id: string;
createdAt: number;
metadata: string;
value: RemoteConnectionOptions[];
session: string; // OpenVidu Session identifier
sessionId: string; // JSON-RPC session identifier
role: string;
record: boolean;
coturnIp: string;
turnUsername: string;
turnCredential: string;
version: string;
}

View File

@ -17,7 +17,7 @@
import { StreamOptionsServer } from './StreamOptionsServer';
export interface ConnectionOptions {
export interface RemoteConnectionOptions {
id: string;
createdAt: number;
metadata: string;

View File

@ -17,6 +17,7 @@
var RpcBuilder = require('../');
var WebSocketWithReconnection = require('./transports/webSocketWithReconnection');
var OpenViduLogger = require('../../../Logger/OpenViduLogger').OpenViduLogger;
Date.now = Date.now || function () {
return +new Date;
@ -28,12 +29,11 @@ var RECONNECTING = 'RECONNECTING';
var CONNECTED = 'CONNECTED';
var DISCONNECTED = 'DISCONNECTED';
var Logger = console;
var Logger = OpenViduLogger.getInstance();
/**
*
* heartbeat: interval in ms for each heartbeat message,
* sendCloseMessage : true / false, before closing the connection, it sends a closeSession message
* <pre>
* ws : {
* uri : URI to conntect to,
@ -250,25 +250,13 @@ function JsonRpcClient(configuration) {
this.close = function (code, reason) {
Logger.debug("Closing with code: " + code + " because: " + reason);
if (pingInterval != undefined) {
Logger.debug("Clearing ping interval");
clearInterval(pingInterval);
}
pingPongStarted = false;
enabledPings = false;
if (configuration.sendCloseMessage) {
Logger.debug("Sending close message")
this.send('closeSession', null, function (error, result) {
if (error) {
Logger.error("Error sending close message: " + JSON.stringify(error));
}
ws.close(code, reason);
});
} else {
ws.close(code, reason);
}
ws.close(code, reason);
}
// This method is only for testing

View File

@ -13,7 +13,7 @@
getScreenId(function (error, sourceId, screen_constraints) {
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
// sourceId == null || 'string' || 'firefox'
if(microsoftEdge) {
navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
}
@ -217,4 +217,4 @@ function postGetChromeExtensionStatusMessage() {
}, '*');
}
exports.getScreenId = getScreenId;
exports.getScreenId = window.getScreenId;

View File

@ -0,0 +1,205 @@
import platform = require("platform");
export class PlatformUtils {
protected static instance: PlatformUtils;
constructor() {}
static getInstance(): PlatformUtils {
if (!this.instance) {
this.instance = new PlatformUtils();
}
return PlatformUtils.instance;
}
public isChromeBrowser(): boolean {
return platform.name === "Chrome";
}
/**
* @hidden
*/
public isSafariBrowser(): boolean {
return platform.name === "Safari";
}
/**
* @hidden
*/
public isChromeMobileBrowser(): boolean {
return platform.name === "Chrome Mobile";
}
/**
* @hidden
*/
public isFirefoxBrowser(): boolean {
return platform.name === "Firefox";
}
/**
* @hidden
*/
public isFirefoxMobileBrowser(): boolean {
return platform.name === "Firefox Mobile";
}
/**
* @hidden
*/
public isOperaBrowser(): boolean {
return platform.name === "Opera";
}
/**
* @hidden
*/
public isOperaMobileBrowser(): boolean {
return platform.name === "Opera Mobile";
}
/**
* @hidden
*/
public isEdgeBrowser(): boolean {
const version = platform?.version ? parseFloat(platform.version) : -1;
return platform.name === "Microsoft Edge" && version >= 80;
}
/**
* @hidden
*/
public isEdgeMobileBrowser(): boolean {
const version = platform?.version ? parseFloat(platform.version) : -1;
return platform.name === "Microsoft Edge" && platform.os?.family === 'Android' && version > 45;
}
/**
* @hidden
*/
public isAndroidBrowser(): boolean {
return platform.name === "Android Browser";
}
/**
* @hidden
*/
public isElectron(): boolean {
return platform.name === "Electron";
}
/**
* @hidden
*/
public isSamsungBrowser(): boolean {
return (
platform.name === "Samsung Internet Mobile" ||
platform.name === "Samsung Internet"
);
}
/**
* @hidden
*/
public isIPhoneOrIPad(): boolean {
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
const isTouchable = "ontouchend" in document;
const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable;
const isIPhone =
/\b(\w*iPhone\w*)\b/.test(userAgent) &&
/\b(\w*Mobile\w*)\b/.test(userAgent) &&
isTouchable;
return isIPad || isIPhone;
}
/**
* @hidden
*/
public isIOSWithSafari(): boolean {
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
return (
/\b(\w*Apple\w*)\b/.test(navigator.vendor) &&
/\b(\w*Safari\w*)\b/.test(userAgent) &&
!/\b(\w*CriOS\w*)\b/.test(userAgent) &&
!/\b(\w*FxiOS\w*)\b/.test(userAgent)
);
}
/**
* @hidden
*/
public isIonicIos(): boolean {
return this.isIPhoneOrIPad() && platform.ua!!.indexOf("Safari") === -1;
}
/**
* @hidden
*/
public isIonicAndroid(): boolean {
return (
platform.os!!.family === "Android" && platform.name == "Android Browser"
);
}
/**
* @hidden
*/
public isMobileDevice(): boolean {
return platform.os!!.family === "iOS" || platform.os!!.family === "Android";
}
/**
* @hidden
*/
public isReactNative(): boolean {
return false;
}
/**
* @hidden
*/
public canScreenShare(): boolean {
const version = platform?.version ? parseFloat(platform.version) : -1;
// Reject mobile devices
if (this.isMobileDevice()) {
return false;
}
return (
this.isChromeBrowser() ||
this.isFirefoxBrowser() ||
this.isOperaBrowser() ||
this.isElectron() ||
this.isEdgeBrowser() ||
(this.isSafariBrowser() && version >= 13)
);
}
/**
* @hidden
*/
public getName(): string {
return platform.name || "";
}
/**
* @hidden
*/
public getVersion(): string {
return platform.version || "";
}
/**
* @hidden
*/
public getFamily(): string {
return platform.os!!.family || "";
}
/**
* @hidden
*/
public getDescription(): string {
return platform.description || "";
}
}

View File

@ -17,12 +17,17 @@
import freeice = require('freeice');
import uuid = require('uuid');
import platform = require('platform');
import { OpenViduLogger } from '../Logger/OpenViduLogger';
import { PlatformUtils } from '../Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
export interface WebRtcPeerConfiguration {
@ -50,6 +55,7 @@ export class WebRtcPeer {
private candidategatheringdone = false;
constructor(protected configuration: WebRtcPeerConfiguration) {
platform = PlatformUtils.getInstance();
this.configuration.iceServers = (!!this.configuration.iceServers && this.configuration.iceServers.length > 0) ? this.configuration.iceServers : freeice();
this.pc = new RTCPeerConnection({ iceServers: this.configuration.iceServers });
@ -139,8 +145,7 @@ export class WebRtcPeer {
logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
if (platform.name === 'Safari' && platform.ua!!.indexOf('Safari') !== -1) {
if (platform.isSafariBrowser() && !platform.isIonicIos()) {
// Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
if (offerAudio) {
this.pc.addTransceiver('audio', {
@ -280,8 +285,35 @@ export class WebRtcPeer {
/**
* @hidden
*/
// FIXME CONFLICT WITH MASTER
// In the mediasoup branch, the special treatment for ionic was removed:
// openvidu-browser: removed Ionic iOS timeout on first subscription
// https://github.com/OpenVidu/openvidu/commit/23d64be8063f8fdb2a212ca845e304762f2803f5
// and also the method was converted into async and returned a Promise.
// However in master the code is still like the old one.
<<<<<<< HEAD
async setRemoteDescription(sdp: RTCSessionDescriptionInit): Promise<void> {
return this.pc.setRemoteDescription(sdp);
=======
setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike<string> | undefined) => void, reject: (reason?: any) => void) {
if (platform.isIonicIos()) {
// Ionic iOS platform
if (needsTimeoutOnProcessAnswer) {
// 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
setTimeout(() => {
logger.info('setRemoteDescription run after timeout for Ionic iOS device');
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
}, 250);
} else {
// 400 ms have elapsed
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
}
} else {
// Rest of platforms
this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
}
>>>>>>> master
}
/**
@ -369,4 +401,4 @@ export class WebRtcPeerSendrecv extends WebRtcPeer {
configuration.mode = 'sendrecv';
super(configuration);
}
}
}

View File

@ -18,13 +18,16 @@
// tslint:disable:no-string-literal
import { Stream } from '../../OpenVidu/Stream';
import platform = require('platform');
import { OpenViduLogger } from '../Logger/OpenViduLogger';
import { PlatformUtils } from '../Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
export class WebRtcStats {
@ -60,7 +63,9 @@ export class WebRtcStats {
}
};
constructor(private stream: Stream) { }
constructor(private stream: Stream) {
platform = PlatformUtils.getInstance();
}
public isEnabled(): boolean {
return this.webRtcStatsEnabled;
@ -103,7 +108,7 @@ export class WebRtcStats {
return new Promise((resolve, reject) => {
this.getStatsAgnostic(this.stream.getRTCPeerConnection(),
(stats) => {
if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) {
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
let localCandidateId, remoteCandidateId, googCandidatePair;
const localCandidates = {};
const remoteCandidates = {};
@ -181,7 +186,7 @@ export class WebRtcStats {
const f = (stats) => {
if (platform.name!.indexOf('Firefox') !== -1) {
if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) {
stats.forEach((stat) => {
let json = {};
@ -278,7 +283,7 @@ export class WebRtcStats {
sendPost(JSON.stringify(json));
}
});
} else if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) {
} else if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
for (const key of Object.keys(stats)) {
const stat = stats[key];
if (stat.type === 'ssrc') {
@ -377,7 +382,7 @@ export class WebRtcStats {
logger.log(response);
const standardReport = {};
if (platform.name!.indexOf('Firefox') !== -1) {
if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) {
Object.keys(response).forEach(key => {
logger.log(response[key]);
});
@ -400,13 +405,13 @@ export class WebRtcStats {
}
private getStatsAgnostic(pc, successCb, failureCb) {
if (platform.name!.indexOf('Firefox') !== -1) {
if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) {
// getStats takes args in different order in Chrome and Firefox
return pc.getStats(null).then(response => {
const report = this.standardizeReport(response);
successCb(report);
}).catch(failureCb);
} else if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) {
} else if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
// In Chrome, the first two arguments are reversed
return pc.getStats((response) => {
const report = this.standardizeReport(response);

View File

@ -22,7 +22,9 @@ export { StreamEvent } from './OpenViduInternal/Events/StreamEvent';
export { StreamManagerEvent } from './OpenViduInternal/Events/StreamManagerEvent';
export { VideoElementEvent } from './OpenViduInternal/Events/VideoElementEvent';
export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamPropertyChangedEvent';
export { ConnectionPropertyChangedEvent } from './OpenViduInternal/Events/ConnectionPropertyChangedEvent';
export { FilterEvent } from './OpenViduInternal/Events/FilterEvent';
export { NetworkQualityLevelChangedEvent } from './OpenViduInternal/Events/NetworkQualityLevelChangedEvent';
export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities';
export { Device } from './OpenViduInternal/Interfaces/Public/Device';

View File

@ -50,8 +50,10 @@ public class OpenViduException extends JsonRpcErrorException {
DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704),
RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701),
FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800);
FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800),
MEDIA_NODE_NOT_FOUND(900), MEDIA_NODE_STATUS_WRONG(901);
private int value;

View File

@ -98,6 +98,15 @@ public class ProtocolElements {
public static final String STREAMPROPERTYCHANGED_NEWVALUE_PARAM = "newValue";
public static final String STREAMPROPERTYCHANGED_REASON_PARAM = "reason";
public static final String CONNECTIONPERTYCHANGED_METHOD = "connectionPropertyChanged";
public static final String CONNECTIONROPERTYCHANGED_PROPERTY_PARAM = "property";
public static final String CONNECTIONPROPERTYCHANGED_NEWVALUE_PARAM = "newValue";
public static final String NETWORKQUALITYLEVELCHANGED_METHOD = "networkQualityLevelChanged";
public static final String NETWORKQUALITYCHANGED_CONNECTIONID_PARAM = "connectionId";
public static final String NETWORKQUALITYCHANGED_NEWVALUE_PARAM = "newValue";
public static final String NETWORKQUALITYCHANGED_OLDVALUE_PARAM = "oldValue";
public static final String FORCEDISCONNECT_METHOD = "forceDisconnect";
public static final String FORCEDISCONNECT_CONNECTIONID_PARAM = "connectionId";
@ -127,12 +136,22 @@ public class ProtocolElements {
public static final String RECONNECTSTREAM_STREAM_PARAM = "stream";
public static final String RECONNECTSTREAM_SDPSTRING_PARAM = "sdpString";
public static final String VIDEODATA_METHOD = "videoData";
// ---------------------------- SERVER RESPONSES & EVENTS -----------------
public static final String PARTICIPANTJOINED_METHOD = "participantJoined";
public static final String PARTICIPANTJOINED_USER_PARAM = "id";
public static final String PARTICIPANTJOINED_CREATEDAT_PARAM = "createdAt";
public static final String PARTICIPANTJOINED_METADATA_PARAM = "metadata";
public static final String PARTICIPANTJOINED_VALUE_PARAM = "value";
public static final String PARTICIPANTJOINED_SESSION_PARAM = "session";
public static final String PARTICIPANTJOINED_VERSION_PARAM = "version";
public static final String PARTICIPANTJOINED_RECORD_PARAM = "record";
public static final String PARTICIPANTJOINED_ROLE_PARAM = "role";
public static final String PARTICIPANTJOINED_COTURNIP_PARAM = "coturnIp";
public static final String PARTICIPANTJOINED_TURNUSERNAME_PARAM = "turnUsername";
public static final String PARTICIPANTJOINED_TURNCREDENTIAL_PARAM = "turnCredential";
public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
public static final String PARTICIPANTLEFT_NAME_PARAM = "connectionId";

View File

@ -10,7 +10,7 @@
</parent>
<artifactId>openvidu-java-client</artifactId>
<version>2.15.1</version>
<version>2.16.0</version>
<packaging>jar</packaging>
<name>OpenVidu Java Client</name>

View File

@ -18,77 +18,194 @@
package io.openvidu.java.client;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* See {@link io.openvidu.java.client.Session#getActiveConnections()}
* See {@link io.openvidu.java.client.Session#getConnections()}
*/
public class Connection {
private String connectionId;
private long createdAt;
private OpenViduRole role;
private String token;
private String status;
private Long createdAt;
private Long activeAt;
private String location;
private String platform;
private String serverData;
private String clientData;
private ConnectionProperties connectionProperties;
private String token;
protected Map<String, Publisher> publishers;
protected List<String> subscribers;
protected Map<String, Publisher> publishers = new ConcurrentHashMap<>();
protected List<String> subscribers = new ArrayList<>();
protected Connection(String connectionId, long createdAt, OpenViduRole role, String token, String location,
String platform, String serverData, String clientData, Map<String, Publisher> publishers,
List<String> subscribers) {
this.connectionId = connectionId;
this.createdAt = createdAt;
this.role = role;
this.token = token;
this.location = location;
this.platform = platform;
this.serverData = serverData;
this.clientData = clientData;
this.publishers = publishers;
this.subscribers = subscribers;
protected Connection(JsonObject json) {
this.resetWithJson(json);
}
/**
* Returns the identifier of the connection. You can call
* {@link io.openvidu.java.client.Session#forceDisconnect(String)} passing this
* property as parameter
* Returns the identifier of the Connection. You can call methods
* {@link io.openvidu.java.client.Session#forceDisconnect(String)} or
* {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}
* passing this property as parameter
*/
public String getConnectionId() {
return connectionId;
}
/**
* Timestamp when this connection was established, in UTC milliseconds (ms since
* Jan 1, 1970, 00:00:00 UTC)
* Returns the status of the Connection. Can be:
* <ul>
* <li><code>pending</code>: if the Connection is waiting for any user to use
* its internal token to connect to the session, calling method <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect"
* target ="_blank">Session.connect</a> in OpenVidu Browser.</li>
* <li><code>active</code>: if the internal token of the Connection has already
* been used by some user to connect to the session, and it cannot be used
* again.</li>
*/
public long createdAt() {
public String getStatus() {
return this.status;
}
/**
* Timestamp when this Connection was created, in UTC milliseconds (ms since Jan
* 1, 1970, 00:00:00 UTC)
*/
public Long createdAt() {
return this.createdAt;
}
/**
* Returns the role of the connection
* Timestamp when this Connection was taken by a user (passing from status
* "pending" to "active"), in UTC milliseconds (ms since Jan 1, 1970, 00:00:00
* UTC)
*/
public Long activeAt() {
return this.activeAt;
}
/**
* Returns the type of Connection.
*/
public ConnectionType getType() {
return this.connectionProperties.getType();
}
/**
* Returns the data associated to the Connection on the server-side. This value
* is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)}
* when calling {@link io.openvidu.java.client.Session#generateToken()}
*/
public String getServerData() {
return this.connectionProperties.getData();
}
/**
* Whether the streams published by this Connection will be recorded or not.
* This only affects <a href=
* "https://docs.openvidu.io/en/stable/advanced-features/recording#selecting-streams-to-be-recorded"
* target="_blank">INDIVIDUAL recording</a>.
*/
public boolean record() {
return this.connectionProperties.record();
}
/**
* Returns the role of the Connection.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#WEBRTC}</strong>
*/
public OpenViduRole getRole() {
return role;
return this.connectionProperties.getRole();
}
/**
* Returns the token associated to the connection
* Returns the RTSP URI of the Connection.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public String getRtspUri() {
return this.connectionProperties.getRtspUri();
}
/**
* Whether the Connection uses adaptative bitrate (and therefore adaptative
* quality) or not. For local network connections that do not require media
* transcoding this can be disabled to save CPU power. If you are not sure if
* transcoding might be necessary, setting this property to false <strong>may
* result in media connections not being established</strong>.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Boolean adaptativeBitrate() {
return this.connectionProperties.adaptativeBitrate();
}
/**
* Whether the IP camera stream of this Connection will only be enabled when
* some user is subscribed to it, or not. This allows you to reduce power
* consumption and network bandwidth in your server while nobody is asking to
* receive the camera's video. On the counterpart, first user subscribing to the
* IP camera stream will take a little longer to receive its video.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Boolean onlyPlayWithSubscribers() {
return this.connectionProperties.onlyPlayWithSubscribers();
}
/**
* Returns the size of the buffer of the endpoint receiving the IP camera's
* stream, in milliseconds. The smaller it is, the less delay the signal will
* have, but more problematic will be in unstable networks. Use short buffers
* only if there is a quality connection between the IP camera and OpenVidu
* Server.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Integer getNetworkCache() {
return this.connectionProperties.getNetworkCache();
}
/**
* Returns the token string associated to the Connection. This is the value that
* must be sent to the client-side to be consumed in OpenVidu Browser method
* <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect"
* target="_blank">Session.connect</a>.
*/
public String getToken() {
return token;
return this.token;
}
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display:
* inline-block; background-color: rgb(0, 136, 170); color: white; font-weight:
* bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size:
* 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a>
*
* Returns the geo location of the connection, with the following format:
* <code>"CITY, COUNTRY"</code> (<code>"unknown"</code> if it wasn't possible to
@ -106,20 +223,11 @@ public class Connection {
return platform;
}
/**
* Returns the data associated to the connection on the server-side. This value
* is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)}
* when calling {@link io.openvidu.java.client.Session#generateToken()}
*/
public String getServerData() {
return serverData;
}
/**
* Returns the data associated to the connection on the client-side. This value
* is set with second parameter of method
* <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect" target
* ="_blank">Session.connect</a> in OpenVidu Browser
* is set with second parameter of method <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect"
* target ="_blank">Session.connect</a> in OpenVidu Browser
*/
public String getClientData() {
return clientData;
@ -147,8 +255,171 @@ public class Connection {
return this.subscribers;
}
protected JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("connectionId", this.getConnectionId());
json.addProperty("status", this.getStatus());
json.addProperty("createdAt", this.createdAt());
json.addProperty("activeAt", this.activeAt());
json.addProperty("location", this.getLocation());
json.addProperty("platform", this.getPlatform());
json.addProperty("clientData", this.getClientData());
json.addProperty("token", this.getToken());
JsonObject jsonConnectionProperties = this.connectionProperties.toJson("");
jsonConnectionProperties.remove("session");
json.addProperty("serverData", jsonConnectionProperties.get("data").getAsString());
jsonConnectionProperties.remove("data");
jsonConnectionProperties.entrySet().forEach(entry -> {
json.add(entry.getKey(), entry.getValue());
});
JsonArray pubs = new JsonArray();
this.getPublishers().forEach(p -> {
pubs.add(p.toJson());
});
JsonArray subs = new JsonArray();
this.getSubscribers().forEach(s -> {
subs.add(s);
});
json.add("publishers", pubs);
json.add("subscribers", subs);
return json;
}
protected void overrideConnectionProperties(ConnectionProperties newConnectionProperties) {
ConnectionProperties.Builder builder = new ConnectionProperties.Builder();
// For now only properties role and record can be updated
if (newConnectionProperties.getRole() != null) {
builder.role(newConnectionProperties.getRole());
} else {
builder.role(this.connectionProperties.getRole());
}
if (newConnectionProperties.record() != null) {
builder.record(newConnectionProperties.record());
} else {
builder.record(this.connectionProperties.record());
}
// Keep old configuration in the rest of properties
builder.type(this.connectionProperties.getType()).data(this.connectionProperties.getData())
.kurentoOptions(this.connectionProperties.getKurentoOptions())
.rtspUri(this.connectionProperties.getRtspUri());
if (this.connectionProperties.adaptativeBitrate() != null) {
builder.adaptativeBitrate(this.connectionProperties.adaptativeBitrate());
}
if (this.connectionProperties.onlyPlayWithSubscribers() != null) {
builder.onlyPlayWithSubscribers(this.connectionProperties.onlyPlayWithSubscribers());
}
if (this.connectionProperties.getNetworkCache() != null) {
builder.networkCache(this.connectionProperties.getNetworkCache());
}
this.connectionProperties = builder.build();
}
protected void setSubscribers(List<String> subscribers) {
this.subscribers = subscribers;
}
protected Connection resetWithJson(JsonObject json) {
this.connectionId = json.get("connectionId").getAsString();
this.status = json.get("status").getAsString();
this.token = !json.get("token").isJsonNull() ? json.get("token").getAsString() : null;
if (!json.get("publishers").isJsonNull()) {
JsonArray jsonArrayPublishers = json.get("publishers").getAsJsonArray();
// 1. Set to store fetched publishers and later remove closed ones
Set<String> fetchedPublisherIds = new HashSet<>();
jsonArrayPublishers.forEach(publisherJsonElement -> {
JsonObject publisherJson = publisherJsonElement.getAsJsonObject();
Publisher publisherObj = new Publisher(publisherJson);
String id = publisherObj.getStreamId();
fetchedPublisherIds.add(id);
// 2. Update existing Publisher
this.publishers.computeIfPresent(id, (pId, p) -> {
p = p.resetWithJson(publisherJson);
return p;
});
// 3. Add new Publisher
this.publishers.computeIfAbsent(id, pId -> {
return publisherObj;
});
});
// 4. Remove closed connections from local collection
this.publishers.entrySet().removeIf(entry -> !fetchedPublisherIds.contains(entry.getValue().getStreamId()));
}
if (!json.get("subscribers").isJsonNull()) {
JsonArray jsonArraySubscribers = json.get("subscribers").getAsJsonArray();
// 1. Array to store fetched Subscribers and later remove closed ones
Set<String> fetchedSubscriberIds = new HashSet<>();
jsonArraySubscribers.forEach(subscriber -> {
String sub = subscriber.getAsJsonObject().get("streamId").getAsString();
fetchedSubscriberIds.add(sub);
if (!this.subscribers.contains(sub)) {
// 2. Add new Subscriber
this.subscribers.add(sub);
}
});
// 3. Remove closed Subscribers from local collection
this.subscribers.removeIf(subId -> !fetchedSubscriberIds.contains(subId));
}
if (!json.get("createdAt").isJsonNull()) {
this.createdAt = json.get("createdAt").getAsLong();
}
if (!json.get("activeAt").isJsonNull()) {
this.activeAt = json.get("activeAt").getAsLong();
}
if (!json.get("location").isJsonNull()) {
this.location = json.get("location").getAsString();
}
if (!json.get("platform").isJsonNull()) {
this.platform = json.get("platform").getAsString();
}
if (!json.get("clientData").isJsonNull()) {
this.clientData = json.get("clientData").getAsString();
}
// COMMON
ConnectionType type = ConnectionType.valueOf(json.get("type").getAsString());
String data = (json.has("serverData") && !json.get("serverData").isJsonNull())
? json.get("serverData").getAsString()
: null;
Boolean record = (json.has("record") && !json.get("record").isJsonNull()) ? json.get("record").getAsBoolean()
: null;
// WEBRTC
OpenViduRole role = (json.has("role") && !json.get("role").isJsonNull())
? OpenViduRole.valueOf(json.get("role").getAsString())
: null;
// IPCAM
String rtspUri = (json.has("rtspUri") && !json.get("rtspUri").isJsonNull()) ? json.get("rtspUri").getAsString()
: null;
Boolean adaptativeBitrate = (json.has("adaptativeBitrate") && !json.get("adaptativeBitrate").isJsonNull())
? json.get("adaptativeBitrate").getAsBoolean()
: null;
Boolean onlyPlayWithSubscribers = (json.has("onlyPlayWithSubscribers")
&& !json.get("onlyPlayWithSubscribers").isJsonNull())
? json.get("onlyPlayWithSubscribers").getAsBoolean()
: null;
Integer networkCache = (json.has("networkCache") && !json.get("networkCache").isJsonNull())
? json.get("networkCache").getAsInt()
: null;
this.connectionProperties = new ConnectionProperties(type, data, record, role, null, rtspUri, adaptativeBitrate,
onlyPlayWithSubscribers, networkCache);
return this;
}
}

View File

@ -0,0 +1,403 @@
package io.openvidu.java.client;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
/**
* See
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
*/
public class ConnectionProperties {
private ConnectionType type;
// COMMON
private String data;
private Boolean record;
// WEBRTC
private OpenViduRole role;
private KurentoOptions kurentoOptions;
// IPCAM
private String rtspUri;
private Boolean adaptativeBitrate;
private Boolean onlyPlayWithSubscribers;
private Integer networkCache;
/**
*
* Builder for {@link io.openvidu.java.client.ConnectionProperties}
*
*/
public static class Builder {
private ConnectionType type;
// COMMON
private String data;
private Boolean record;
// WEBRTC
private OpenViduRole role;
private KurentoOptions kurentoOptions;
// IPCAM
private String rtspUri;
private Boolean adaptativeBitrate;
private Boolean onlyPlayWithSubscribers;
private Integer networkCache;
/**
* Builder for {@link io.openvidu.java.client.ConnectionProperties}.
*/
public ConnectionProperties build() {
return new ConnectionProperties(this.type, this.data, this.record, this.role, this.kurentoOptions,
this.rtspUri, this.adaptativeBitrate, this.onlyPlayWithSubscribers, this.networkCache);
}
/**
* Call this method to set the type of Connection. The
* {@link io.openvidu.java.client.ConnectionType} dictates what properties will
* have effect:
* <ul>
* <li>{@link io.openvidu.java.client.ConnectionType#WEBRTC}:
* {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)
* data},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#record(boolean)
* record},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#role(OpenViduRole)
* role},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#kurentoOptions(KurentoOptions)
* kurentoOptions}</li>
* <li>{@link io.openvidu.java.client.ConnectionType#IPCAM}:
* {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)
* data},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#record(boolean)
* record},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#rtspUri(String)
* rtspUri},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#adaptativeBitrate(boolean)
* adaptativeBitrate},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#onlyPlayWithSubscribers(boolean)
* onlyPlayWithSubscribers},
* {@link io.openvidu.java.client.ConnectionProperties.Builder#networkCache(int)
* networkCache}</li>
* </ul>
* If not set by default will be
* {@link io.openvidu.java.client.ConnectionType#WEBRTC}.
*/
public Builder type(ConnectionType type) {
this.type = type;
return this;
}
/**
* Call this method to set the secure (server-side) data associated to this
* Connection. Every client will receive this data in property
* <code>Connection.data</code>. Object <code>Connection</code> can be retrieved
* by subscribing to event <code>connectionCreated</code> of Session object in
* your clients.
* <ul>
* <li>If you have provided no data in your clients when calling method
* <code>Session.connect(TOKEN, DATA)</code> (<code>DATA</code> not defined),
* then <code>Connection.data</code> will only have this
* {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)}
* property.</li>
* <li>If you have provided some data when calling
* <code>Session.connect(TOKEN, DATA)</code> (<code>DATA</code> defined), then
* <code>Connection.data</code> will have the following structure:
* <code>&quot;CLIENT_DATA%/%SERVER_DATA&quot;</code>, being
* <code>CLIENT_DATA</code> the second parameter passed in OpenVidu Browser in
* method <code>Session.connect</code> and <code>SERVER_DATA</code> this
* {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)}
* property.</li>
* </ul>
*/
public Builder data(String data) {
this.data = data;
return this;
}
/**
* Call this method to flag the streams published by this Connection to be
* recorded or not. This only affects <a href=
* "https://docs.openvidu.io/en/stable/advanced-features/recording#selecting-streams-to-be-recorded"
* target="_blank">INDIVIDUAL recording</a>. If not set by default will be true.
*/
public Builder record(boolean record) {
this.record = record;
return this;
}
/**
* Call this method to set the role assigned to this Connection. If not set by
* default will be {@link io.openvidu.java.client.OpenViduRole#PUBLISHER
* PUBLISHER}.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#WEBRTC}</strong>
*/
public Builder role(OpenViduRole role) {
this.role = role;
return this;
}
/**
* Call this method to set a {@link io.openvidu.java.client.KurentoOptions}
* object for this Connection.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#WEBRTC}</strong>
*/
public Builder kurentoOptions(KurentoOptions kurentoOptions) {
this.kurentoOptions = kurentoOptions;
return this;
}
/**
* Call this method to set the RTSP URI of an IP camera. For example:
* <code>rtsp://your.camera.ip:7777/path</code>
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Builder rtspUri(String rtspUri) {
this.rtspUri = rtspUri;
return this;
}
/**
* Call this method to set whether to use adaptative bitrate (and therefore
* adaptative quality) or not. For local network connections that do not require
* media transcoding this can be disabled to save CPU power. If you are not sure
* if transcoding might be necessary, setting this property to false <strong>may
* result in media connections not being established</strong>. Default to
* <code>true</code>.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Builder adaptativeBitrate(boolean adaptativeBitrate) {
this.adaptativeBitrate = adaptativeBitrate;
return this;
}
/**
* Call this method to set whether to enable the IP camera stream only when some
* user is subscribed to it, or not. This allows you to reduce power consumption
* and network bandwidth in your server while nobody is asking to receive the
* camera's video. On the counterpart, first user subscribing to the IP camera
* stream will take a little longer to receive its video. Default to
* <code>true</code>.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Builder onlyPlayWithSubscribers(boolean onlyPlayWithSubscribers) {
this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
return this;
}
/**
* Call this method to set the size of the buffer of the endpoint receiving the
* IP camera's stream, in milliseconds. The smaller it is, the less delay the
* signal will have, but more problematic will be in unstable networks. Use
* short buffers only if there is a quality connection between the IP camera and
* OpenVidu Server. Default to <code>2000</code>.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Builder networkCache(int networkCache) {
this.networkCache = networkCache;
return this;
}
}
ConnectionProperties(ConnectionType type, String data, Boolean record, OpenViduRole role,
KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers,
Integer networkCache) {
this.type = type;
this.data = data;
this.record = record;
this.role = role;
this.kurentoOptions = kurentoOptions;
this.rtspUri = rtspUri;
this.adaptativeBitrate = adaptativeBitrate;
this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
this.networkCache = networkCache;
}
/**
* Returns the type of Connection.
*/
public ConnectionType getType() {
return this.type;
}
/**
* Returns the secure (server-side) metadata assigned to this Connection.
*/
public String getData() {
return this.data;
}
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a> Whether the streams published by this Connection will be
* recorded or not. This only affects <a href=
* "https://docs.openvidu.io/en/stable/advanced-features/recording#selecting-streams-to-be-recorded"
* target="_blank">INDIVIDUAL recording</a>.
*/
public Boolean record() {
return this.record;
}
/**
* Returns the role assigned to this Connection.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#WEBRTC}</strong>
*/
public OpenViduRole getRole() {
return this.role;
}
/**
* Returns the KurentoOptions assigned to this Connection.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#WEBRTC}</strong>
*/
public KurentoOptions getKurentoOptions() {
return this.kurentoOptions;
}
/**
* Returns the RTSP URI of this Connection.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public String getRtspUri() {
return this.rtspUri;
}
/**
* Whether this Connection uses adaptative bitrate (and therefore adaptative
* quality) or not. For local network connections that do not require media
* transcoding this can be disabled to save CPU power. If you are not sure if
* transcoding might be necessary, setting this property to false <strong>may
* result in media connections not being established</strong>.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Boolean adaptativeBitrate() {
return this.adaptativeBitrate;
}
/**
* Whether to enable the IP camera stream only when some user is subscribed to
* it. This allows you to reduce power consumption and network bandwidth in your
* server while nobody is asking to receive the camera's video. On the
* counterpart, first user subscribing to the IP camera stream will take a
* little longer to receive its video.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Boolean onlyPlayWithSubscribers() {
return this.onlyPlayWithSubscribers;
}
/**
* Size of the buffer of the endpoint receiving the IP camera's stream, in
* milliseconds. The smaller it is, the less delay the signal will have, but
* more problematic will be in unstable networks. Use short buffers only if
* there is a quality connection between the IP camera and OpenVidu Server.
*
* <br>
* <br>
* <strong>Only for
* {@link io.openvidu.java.client.ConnectionType#IPCAM}</strong>
*/
public Integer getNetworkCache() {
return this.networkCache;
}
public JsonObject toJson(String sessionId) {
JsonObject json = new JsonObject();
json.addProperty("session", sessionId);
// COMMON
if (getType() != null) {
json.addProperty("type", getType().name());
} else {
json.add("type", JsonNull.INSTANCE);
}
if (getData() != null) {
json.addProperty("data", getData());
} else {
json.add("data", JsonNull.INSTANCE);
}
if (record() != null) {
json.addProperty("record", record());
} else {
json.add("record", JsonNull.INSTANCE);
}
// WEBRTC
if (getRole() != null) {
json.addProperty("role", getRole().name());
} else {
json.add("role", JsonNull.INSTANCE);
}
if (this.kurentoOptions != null) {
json.add("kurentoOptions", kurentoOptions.toJson());
} else {
json.add("kurentoOptions", JsonNull.INSTANCE);
}
// IPCAM
if (getRtspUri() != null) {
json.addProperty("rtspUri", getRtspUri());
} else {
json.add("rtspUri", JsonNull.INSTANCE);
}
if (adaptativeBitrate() != null) {
json.addProperty("adaptativeBitrate", adaptativeBitrate());
} else {
json.add("adaptativeBitrate", JsonNull.INSTANCE);
}
if (onlyPlayWithSubscribers() != null) {
json.addProperty("onlyPlayWithSubscribers", onlyPlayWithSubscribers());
} else {
json.add("onlyPlayWithSubscribers", JsonNull.INSTANCE);
}
if (getNetworkCache() != null) {
json.addProperty("networkCache", getNetworkCache());
} else {
json.add("networkCache", JsonNull.INSTANCE);
}
return json;
}
}

View File

@ -0,0 +1,20 @@
package io.openvidu.java.client;
/**
* See
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
*/
public enum ConnectionType {
/**
* WebRTC connection. This is the normal type of Connection for a regular user
* connecting to a session from an application.
*/
WEBRTC,
/**
* IP camera connection. This is the type of Connection used by IP cameras to
* connect to a session.
*/
IPCAM
}

View File

@ -17,6 +17,11 @@
package io.openvidu.java.client;
import java.util.Arrays;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* See {@link io.openvidu.java.client.TokenOptions#getKurentoOptions()}
*/
@ -105,10 +110,10 @@ public class KurentoOptions {
}
/**
* Defines the maximum number of Kbps that the client owning the token will be
* able to receive from Kurento Media Server. 0 means unconstrained. Giving a
* value to this property will override the global configuration set in <a
* href="https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* Defines the maximum number of Kbps that the Connection will be able to
* receive from Kurento Media Server per media stream. 0 means unconstrained.
* Giving a value to this property will override the global configuration set in
* <a href="https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* target="_blank">OpenVidu Server configuration</a> (parameter
* <code>OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH</code>) for every incoming
* stream of the user owning the token. <br>
@ -122,10 +127,10 @@ public class KurentoOptions {
}
/**
* Defines the minimum number of Kbps that the client owning the token will try
* to receive from Kurento Media Server. 0 means unconstrained. Giving a value
* to this property will override the global configuration set in <a href=
* "https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* Defines the minimum number of Kbps that the Connection will try to receive
* from Kurento Media Server per media stream. 0 means unconstrained. Giving a
* value to this property will override the global configuration set in
* <a href= "https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* target="_blank">OpenVidu Server configuration</a> (parameter
* <code>OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH</code>) for every incoming
* stream of the user owning the token.
@ -135,10 +140,10 @@ public class KurentoOptions {
}
/**
* Defines the maximum number of Kbps that the client owning the token will be
* able to send to Kurento Media Server. 0 means unconstrained. Giving a value
* to this property will override the global configuration set in <a href=
* "https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* Defines the maximum number of Kbps that the Connection will be able to send
* to Kurento Media Server per media stream. 0 means unconstrained. Giving a
* value to this property will override the global configuration set in
* <a href= "https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* target="_blank">OpenVidu Server configuration</a> (parameter
* <code>OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH</code>) for every outgoing
* stream of the user owning the token. <br>
@ -151,10 +156,10 @@ public class KurentoOptions {
}
/**
* Defines the minimum number of Kbps that the client owning the token will try
* to send to Kurento Media Server. 0 means unconstrained. Giving a value to
* this property will override the global configuration set in <a href=
* "https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* Defines the minimum number of Kbps that the Connection will try to send to
* Kurento Media Server per media stream. 0 means unconstrained. Giving a value
* to this property will override the global configuration set in
* <a href= "https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* target="_blank">OpenVidu Server configuration</a> (parameter
* <code>OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH</code>) for every outgoing
* stream of the user owning the token.
@ -164,13 +169,50 @@ public class KurentoOptions {
}
/**
* Defines the names of the filters the user owning the token will be able to
* apply. See
* <a href="https://docs.openvidu.io/en/stable/advanced-features/filters/" target= "_blank">Voice and
* video filters</a>
* Defines the names of the filters the Connection will be able to apply to its
* published streams. See
* <a href="https://docs.openvidu.io/en/stable/advanced-features/filters/"
* target= "_blank">Voice and video filters</a>.
*/
public String[] getAllowedFilters() {
return allowedFilters;
}
/**
* See if the Connection can apply certain filter. See
* <a href="https://docs.openvidu.io/en/stable/advanced-features/filters/"
* target= "_blank">Voice and video filters</a>.
*/
public boolean isFilterAllowed(String filterType) {
if (filterType == null) {
return false;
}
return Arrays.stream(allowedFilters).anyMatch(filterType::equals);
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
if (this.getVideoMaxRecvBandwidth() != null) {
json.addProperty("videoMaxRecvBandwidth", this.getVideoMaxRecvBandwidth());
}
if (this.getVideoMinRecvBandwidth() != null) {
json.addProperty("videoMinRecvBandwidth", this.getVideoMinRecvBandwidth());
}
if (this.getVideoMaxSendBandwidth() != null) {
json.addProperty("videoMaxSendBandwidth", this.getVideoMaxSendBandwidth());
}
if (this.getVideoMinSendBandwidth() != null) {
json.addProperty("videoMinSendBandwidth", this.getVideoMinSendBandwidth());
}
if (this.getAllowedFilters().length > 0) {
JsonArray filtersJson = new JsonArray();
String[] filters = this.getAllowedFilters();
for (String filter : filters) {
filtersJson.add(filter);
}
json.add("allowedFilters", filtersJson);
}
return json;
}
}

View File

@ -31,7 +31,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
@ -70,16 +69,19 @@ public class OpenVidu {
protected HttpClient httpClient;
protected Map<String, Session> activeSessions = new ConcurrentHashMap<>();
protected final static String API_SESSIONS = "api/sessions";
protected final static String API_TOKENS = "api/tokens";
protected final static String API_RECORDINGS = "api/recordings";
protected final static String API_RECORDINGS_START = "/start";
protected final static String API_RECORDINGS_STOP = "/stop";
protected final static String API_PATH = "openvidu/api";
protected final static String API_SESSIONS = API_PATH + "/sessions";
protected final static String API_TOKENS = API_PATH + "/tokens";
protected final static String API_RECORDINGS = API_PATH + "/recordings";
protected final static String API_RECORDINGS_START = API_RECORDINGS + "/start";
protected final static String API_RECORDINGS_STOP = API_RECORDINGS + "/stop";
/**
* @param urlOpenViduServer Public accessible IP where your instance of OpenVidu
* Server is up an running
* @param secret Secret used on OpenVidu Server initialization
* @param hostname URL where your instance of OpenVidu Server is up an running.
* It must be the full URL (e.g.
* <code>https://12.34.56.78:1234/</code>)
*
* @param secret Secret used on OpenVidu Server initialization
*/
public OpenVidu(String hostname, String secret) {
@ -192,16 +194,19 @@ public class OpenVidu {
public Recording startRecording(String sessionId, RecordingProperties properties)
throws OpenViduJavaClientException, OpenViduHttpException {
HttpPost request = new HttpPost(this.hostname + API_RECORDINGS + API_RECORDINGS_START);
HttpPost request = new HttpPost(this.hostname + API_RECORDINGS_START);
JsonObject json = new JsonObject();
json.addProperty("session", sessionId);
json.addProperty("name", properties.name());
json.addProperty("outputMode", properties.outputMode().name());
json.addProperty("outputMode", properties.outputMode() != null ? properties.outputMode().name() : null);
json.addProperty("hasAudio", properties.hasAudio());
json.addProperty("hasVideo", properties.hasVideo());
json.addProperty("shmSize", properties.shmSize());
json.addProperty("mediaNode", properties.mediaNode());
if ((Recording.OutputMode.COMPOSED.equals(properties.outputMode()) || (Recording.OutputMode.COMPOSED_QUICK_START.equals(properties.outputMode())))
if ((properties.outputMode() == null || Recording.OutputMode.COMPOSED.equals(properties.outputMode())
|| (Recording.OutputMode.COMPOSED_QUICK_START.equals(properties.outputMode())))
&& properties.hasVideo()) {
json.addProperty("resolution", properties.resolution());
json.addProperty("recordingLayout",
@ -236,8 +241,9 @@ public class OpenVidu {
if (activeSession != null) {
activeSession.setIsBeingRecorded(true);
} else {
log.warn("No active session found for sessionId '" + r.getSessionId()
+ "'. This instance of OpenVidu Java Client didn't create this session");
log.warn(
"No active session found for sessionId '{}'. This instance of OpenVidu Java Client didn't create this session",
r.getSessionId());
}
return r;
} else {
@ -352,7 +358,7 @@ public class OpenVidu {
* </ul>
*/
public Recording stopRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
HttpPost request = new HttpPost(this.hostname + API_RECORDINGS + API_RECORDINGS_STOP + "/" + recordingId);
HttpPost request = new HttpPost(this.hostname + API_RECORDINGS_STOP + "/" + recordingId);
HttpResponse response;
try {
response = this.httpClient.execute(request);
@ -368,8 +374,9 @@ public class OpenVidu {
if (activeSession != null) {
activeSession.setIsBeingRecorded(false);
} else {
log.warn("No active session found for sessionId '" + r.getSessionId()
+ "'. This instance of OpenVidu Java Client didn't create this session");
log.warn(
"No active session found for sessionId '{}'. This instance of OpenVidu Java Client didn't create this session",
r.getSessionId());
}
return r;
} else {
@ -492,6 +499,10 @@ public class OpenVidu {
* since the last time method {@link io.openvidu.java.client.OpenVidu#fetch()}
* was called</strong>. Exceptions to this rule are:
* <ul>
* <li>Calling
* {@link io.openvidu.java.client.OpenVidu#createSession(SessionProperties)
* OpenVidu.createSession} automatically adds the new Session object to the
* local collection.</li>
* <li>Calling {@link io.openvidu.java.client.Session#fetch()} updates that
* specific Session status</li>
* <li>Calling {@link io.openvidu.java.client.Session#close()} automatically
@ -531,7 +542,7 @@ public class OpenVidu {
* @throws OpenViduJavaClientException
*/
public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException {
HttpGet request = new HttpGet(this.hostname + API_SESSIONS);
HttpGet request = new HttpGet(this.hostname + API_SESSIONS + "?pendingConnections=true");
HttpResponse response;
try {
@ -543,44 +554,55 @@ public class OpenVidu {
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
JsonObject jsonSessions = httpResponseToJson(response);
JsonArray jsonArraySessions = jsonSessions.get("content").getAsJsonArray();
// Set to store fetched sessionIds and later remove closed sessions
Set<String> fetchedSessionIds = new HashSet<>();
// Boolean to store if any Session has changed
final boolean[] hasChanged = { false };
jsonArraySessions.forEach(session -> {
String sessionId = (session.getAsJsonObject()).get("sessionId").getAsString();
fetchedSessionIds.add(sessionId);
this.activeSessions.computeIfPresent(sessionId, (sId, s) -> {
// 1. Set to store fetched sessionIds and later remove closed ones
Set<String> fetchedSessionIds = new HashSet<>();
jsonArraySessions.forEach(sessionJsonElement -> {
JsonObject sessionJson = sessionJsonElement.getAsJsonObject();
final Session sessionObj = new Session(this, sessionJson);
String id = sessionObj.getSessionId();
fetchedSessionIds.add(id);
// 2. Update existing Session
this.activeSessions.computeIfPresent(id, (sId, s) -> {
String beforeJSON = s.toJson();
s = s.resetSessionWithJson(session.getAsJsonObject());
s = s.resetWithJson(sessionJson);
String afterJSON = s.toJson();
boolean changed = !beforeJSON.equals(afterJSON);
hasChanged[0] = hasChanged[0] || changed;
log.info("Available session '{}' info fetched. Any change: {}", sessionId, changed);
log.info("Available session '{}' info fetched. Any change: {}", id, changed);
return s;
});
this.activeSessions.computeIfAbsent(sessionId, sId -> {
log.info("New session '{}' fetched", sessionId);
// 3. Add new Session
this.activeSessions.computeIfAbsent(id, sId -> {
log.info("New session '{}' fetched", id);
hasChanged[0] = true;
return new Session(this, session.getAsJsonObject());
return sessionObj;
});
});
// Remove closed sessions from activeSessions map
this.activeSessions = this.activeSessions.entrySet().stream().filter(entry -> {
// 4. Remove closed sessions from local collection
this.activeSessions.entrySet().removeIf(entry -> {
if (fetchedSessionIds.contains(entry.getKey())) {
return true;
return false;
} else {
log.info("Removing closed session {}", entry.getKey());
hasChanged[0] = true;
return false;
return true;
}
}).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
});
log.info("Active sessions info fetched: {}", this.activeSessions.keySet());
return hasChanged[0];
} else {
throw new OpenViduHttpException(statusCode);
}

View File

@ -17,16 +17,15 @@
package io.openvidu.java.client;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* See {@link io.openvidu.java.client.Connection#getPublishers()}.
*
* <br>
* This is a backend representation of a published media stream (see
* <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target="_blank"> OpenVidu
* Browser Stream class</a>).
* This is a backend representation of a published media stream (see <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target="_blank"> OpenVidu Browser Stream class</a>).
*/
public class Publisher {
@ -40,36 +39,18 @@ public class Publisher {
private String typeOfVideo;
private String videoDimensions;
protected Publisher(String streamId, long createdAt, boolean hasAudio, boolean hasVideo, JsonElement audioActive,
JsonElement videoActive, JsonElement frameRate, JsonElement typeOfVideo, JsonElement videoDimensions) {
this.streamId = streamId;
this.createdAt = createdAt;
this.hasAudio = hasAudio;
this.hasVideo = hasVideo;
if (audioActive != null && !audioActive.isJsonNull()) {
this.audioActive = audioActive.getAsBoolean();
}
if (videoActive != null && !videoActive.isJsonNull()) {
this.videoActive = videoActive.getAsBoolean();
}
if (frameRate != null && !frameRate.isJsonNull()) {
this.frameRate = frameRate.getAsInt();
}
if (typeOfVideo != null && !typeOfVideo.isJsonNull()) {
this.typeOfVideo = typeOfVideo.getAsString();
}
if (videoDimensions != null && !videoDimensions.isJsonNull()) {
this.videoDimensions = videoDimensions.getAsString();
}
protected Publisher(JsonObject json) {
this.resetWithJson(json);
}
/**
* Returns the unique identifier of the
* <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> associated to this Publisher. Each Publisher is paired
* with only one Stream, so you can identify each Publisher by its
* <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html#streamid" target=
* "_blank"><code>Stream.streamId</code></a>
* Returns the unique identifier of the <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> associated to this Publisher. Each Publisher is
* paired with only one Stream, so you can identify each Publisher by its
* <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html#streamid"
* target= "_blank"><code>Stream.streamId</code></a>
*/
public String getStreamId() {
return streamId;
@ -84,56 +65,70 @@ public class Publisher {
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public boolean hasVideo() {
return this.hasVideo;
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public boolean hasAudio() {
return this.hasAudio;
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public Boolean isAudioActive() {
return this.audioActive;
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public Boolean isVideoActive() {
return this.videoActive;
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public Integer getFrameRate() {
return this.frameRate;
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public String getTypeOfVideo() {
return this.typeOfVideo;
}
/**
* See properties of <a href="https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html" target=
* "_blank">Stream</a> object in OpenVidu Browser library to find out more
* See properties of <a href=
* "https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/stream.html"
* target= "_blank">Stream</a> object in OpenVidu Browser library to find out
* more
*/
public String getVideoDimensions() {
return this.videoDimensions;
@ -152,4 +147,32 @@ public class Publisher {
return json;
}
protected Publisher resetWithJson(JsonObject json) {
this.streamId = json.get("streamId").getAsString();
this.createdAt = json.get("createdAt").getAsLong();
if (json.has("mediaOptions") && !json.get("mediaOptions").isJsonNull()) {
JsonObject mediaOptions = json.get("mediaOptions").getAsJsonObject();
this.hasAudio = mediaOptions.get("hasAudio").getAsBoolean();
this.hasVideo = mediaOptions.get("hasVideo").getAsBoolean();
if (mediaOptions.has("audioActive") && !mediaOptions.get("audioActive").isJsonNull()) {
this.audioActive = mediaOptions.get("audioActive").getAsBoolean();
}
if (mediaOptions.has("videoActive") && !mediaOptions.get("videoActive").isJsonNull()) {
this.videoActive = mediaOptions.get("videoActive").getAsBoolean();
}
if (mediaOptions.has("frameRate") && !mediaOptions.get("frameRate").isJsonNull()) {
this.frameRate = mediaOptions.get("frameRate").getAsInt();
}
if (mediaOptions.has("typeOfVideo") && !mediaOptions.get("typeOfVideo").isJsonNull()) {
this.typeOfVideo = mediaOptions.get("typeOfVideo").getAsString();
}
if (mediaOptions.has("videoDimensions") && !mediaOptions.get("videoDimensions").isJsonNull()) {
this.videoDimensions = mediaOptions.get("videoDimensions").getAsString();
}
}
return this;
}
}

View File

@ -48,9 +48,8 @@ public class Recording {
stopped,
/**
* The recording has finished OK and is available for download through OpenVidu
* Server recordings endpoint:
* https://YOUR_OPENVIDUSERVER_IP/recordings/{RECORDING_ID}/{RECORDING_NAME}.{EXTENSION}
* The recording has finished being processed and is available for download
* through property {@link Recording#getUrl}
*/
ready,
@ -75,27 +74,25 @@ public class Recording {
* Record each stream individually
*/
INDIVIDUAL,
/**
* Works the same way as COMPOSED mode, but the necessary recorder
* service module will start some time in advance and won't be terminated
* once a specific session recording has ended. This module will remain
* up and running as long as the session remains active.<br><br>
* Works the same way as COMPOSED mode, but the necessary recorder service
* module will start some time in advance and won't be terminated once a
* specific session recording has ended. This module will remain up and running
* as long as the session remains active.<br>
* <br>
*
* <ul>
* <li>
* <strong>Pros vs COMPOSED</strong>: the process of starting the recording will be noticeably
* faster. This can be very useful in use cases where a session needs to be
* recorded multiple times over time, when a better response time is usually
* desirable.
* </li>
* <li>
* <strong>Cons vs COMPOSED</strong>: for every session initialized with COMPOSED_QUICK_START
* recording output mode, extra CPU power will be required in OpenVidu Server.
* The recording module will be continuously rendering all of the streams being
* published to the session even when the session is not being recorded. And that
* is for every session configured with COMPOSED_QUICK_START.
* </li>
* <li><strong>Pros vs COMPOSED</strong>: the process of starting the recording
* will be noticeably faster. This can be very useful in use cases where a
* session needs to be recorded multiple times over time, when a better response
* time is usually desirable.</li>
* <li><strong>Cons vs COMPOSED</strong>: for every session initialized with
* COMPOSED_QUICK_START recording output mode, extra CPU power will be required
* in OpenVidu Server. The recording module will be continuously rendering all
* of the streams being published to the session even when the session is not
* being recorded. And that is for every session configured with
* COMPOSED_QUICK_START.</li>
* </ul>
*/
COMPOSED_QUICK_START;
@ -129,7 +126,8 @@ public class Recording {
OutputMode outputMode = OutputMode.valueOf(json.get("outputMode").getAsString());
RecordingProperties.Builder builder = new RecordingProperties.Builder().name(json.get("name").getAsString())
.outputMode(outputMode).hasAudio(hasAudio).hasVideo(hasVideo);
if ((OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode)) && hasVideo) {
if ((OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode))
&& hasVideo) {
builder.resolution(json.get("resolution").getAsString());
builder.recordingLayout(RecordingLayout.valueOf(json.get("recordingLayout").getAsString()));
JsonElement customLayout = json.get("customLayout");
@ -219,8 +217,8 @@ public class Recording {
/**
* URL of the recording. You can access the file from there. It is
* <code>null</code> until recording reaches "ready" or "failed" status. If
* <a href="https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/" target=
* "_blank">OpenVidu Server configuration</a> property
* <a href="https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"
* target= "_blank">OpenVidu Server configuration</a> property
* <code>OPENVIDU_RECORDING_PUBLIC_ACCESS</code> is false, this path will be
* secured with OpenVidu credentials
*/
@ -230,7 +228,8 @@ public class Recording {
/**
* Resolution of the video file. Only defined if OutputMode of the Recording is
* set to {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}
* set to {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}
*/
public String getResolution() {
return this.recordingProperties.resolution();

View File

@ -17,8 +17,6 @@
package io.openvidu.java.client;
import io.openvidu.java.client.Recording.OutputMode;
/**
* See
* {@link io.openvidu.java.client.OpenVidu#startRecording(String, RecordingProperties)}
@ -33,6 +31,7 @@ public class RecordingProperties {
private boolean hasAudio;
private boolean hasVideo;
private long shmSize; // For COMPOSED recording
private String mediaNode;
/**
* Builder for {@link io.openvidu.java.client.RecordingProperties}
@ -40,27 +39,21 @@ public class RecordingProperties {
public static class Builder {
private String name = "";
private Recording.OutputMode outputMode = Recording.OutputMode.COMPOSED;
private Recording.OutputMode outputMode;
private RecordingLayout recordingLayout;
private String customLayout;
private String resolution;
private boolean hasAudio = true;
private boolean hasVideo = true;
private long shmSize = 536870912L;
private String mediaNode;
/**
* Builder for {@link io.openvidu.java.client.RecordingProperties}
*/
public RecordingProperties build() {
if (OutputMode.COMPOSED.equals(this.outputMode) || OutputMode.COMPOSED_QUICK_START.equals(this.outputMode)) {
this.recordingLayout = this.recordingLayout != null ? this.recordingLayout : RecordingLayout.BEST_FIT;
this.resolution = this.resolution != null ? this.resolution : "1920x1080";
if (RecordingLayout.CUSTOM.equals(this.recordingLayout)) {
this.customLayout = this.customLayout != null ? this.customLayout : "";
}
}
return new RecordingProperties(this.name, this.outputMode, this.recordingLayout, this.customLayout,
this.resolution, this.hasAudio, this.hasVideo, this.shmSize);
this.resolution, this.hasAudio, this.hasVideo, this.shmSize, this.mediaNode);
}
/**
@ -102,11 +95,14 @@ public class RecordingProperties {
* method to set the relative path to the specific custom layout you want to
* use.<br>
* Will only have effect if
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
* has been called with value
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}.<br>
* See <a href="https://docs.openvidu.io/en/stable/advanced-features/recording#custom-recording-layouts"
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)
* Builder.outputMode()} has been called with value
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED
* OutputMode.COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START
* OutputMode.COMPOSED_QUICK_START}.<br>
* See <a href=
* "https://docs.openvidu.io/en/stable/advanced-features/recording#custom-recording-layouts"
* target="_blank">Custom recording layouts</a> to learn more
*/
public RecordingProperties.Builder customLayout(String path) {
@ -119,13 +115,15 @@ public class RecordingProperties {
* format "WIDTHxHEIGHT", being both WIDTH and HEIGHT the number of pixels
* between 100 and 1999.<br>
* Will only have effect if
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
* has been called with value
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}. For
* {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all
* individual video files will have the native resolution of the published
* stream
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)
* Builder.outputMode()} has been called with value
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED
* OutputMode.COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START
* OutputMode.COMPOSED_QUICK_START}. For
* {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL
* OutputMode.INDIVIDUAL} all individual video files will have the native
* resolution of the published stream
*/
public RecordingProperties.Builder resolution(String resolution) {
this.resolution = resolution;
@ -134,7 +132,8 @@ public class RecordingProperties {
/**
* Call this method to specify whether to record audio or not. Cannot be set to
* false at the same time as {@link hasVideo(boolean)}
* false at the same time as
* {@link RecordingProperties.Builder#hasVideo(boolean)}
*/
public RecordingProperties.Builder hasAudio(boolean hasAudio) {
this.hasAudio = hasAudio;
@ -143,7 +142,8 @@ public class RecordingProperties {
/**
* Call this method to specify whether to record video or not. Cannot be set to
* false at the same time as {@link hasAudio(boolean)}
* false at the same time as
* {@link RecordingProperties.Builder#hasAudio(boolean)}
*/
public RecordingProperties.Builder hasVideo(boolean hasVideo) {
this.hasVideo = hasVideo;
@ -160,10 +160,26 @@ public class RecordingProperties {
return this;
}
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a> Call this method to force the recording to be hosted in
* the Media Node with identifier <code>mediaNodeId</code>. This property only
* applies to COMPOSED recordings and is ignored for INDIVIDUAL recordings, that
* are always hosted in the same Media Node hosting its Session
*/
public RecordingProperties.Builder mediaNode(String mediaNodeId) {
this.mediaNode = mediaNodeId;
return this;
}
}
protected RecordingProperties(String name, Recording.OutputMode outputMode, RecordingLayout layout,
String customLayout, String resolution, boolean hasAudio, boolean hasVideo, long shmSize) {
String customLayout, String resolution, boolean hasAudio, boolean hasVideo, long shmSize,
String mediaNode) {
this.name = name;
this.outputMode = outputMode;
this.recordingLayout = layout;
@ -172,6 +188,7 @@ public class RecordingProperties {
this.hasAudio = hasAudio;
this.hasVideo = hasVideo;
this.shmSize = shmSize;
this.mediaNode = mediaNode;
}
/**
@ -185,12 +202,12 @@ public class RecordingProperties {
/**
* Defines the mode of recording: {@link Recording.OutputMode#COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START} for a
* single archive in a grid layout or {@link Recording.OutputMode#INDIVIDUAL}
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START} for
* a single archive in a grid layout or {@link Recording.OutputMode#INDIVIDUAL}
* for one archive for each stream.<br>
* <br>
*
* Default to {@link Recording.OutputMode#COMPOSED}
* Default to {@link Recording.OutputMode#COMPOSED OutputMode.COMPOSED}
*/
public Recording.OutputMode outputMode() {
return this.outputMode;
@ -199,12 +216,14 @@ public class RecordingProperties {
/**
* Defines the layout to be used in the recording.<br>
* Will only have effect if
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
* has been called with value {@link Recording.OutputMode#COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}.<br>
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)
* Builder.outputMode()} has been called with value
* {@link Recording.OutputMode#COMPOSED OutputMode.COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START
* OutputMode.COMPOSED_QUICK_START}.<br>
* <br>
*
* Default to {@link RecordingLayout#BEST_FIT}
* Default to {@link RecordingLayout#BEST_FIT RecordingLayout.BEST_FIT}
*/
public RecordingLayout recordingLayout() {
return this.recordingLayout;
@ -214,7 +233,8 @@ public class RecordingProperties {
* If {@link io.openvidu.java.client.RecordingProperties#recordingLayout()} is
* set to {@link io.openvidu.java.client.RecordingLayout#CUSTOM}, this property
* defines the relative path to the specific custom layout you want to use.<br>
* See <a href="https://docs.openvidu.io/en/stable/advanced-features/recording#custom-recording-layouts"
* See <a href=
* "https://docs.openvidu.io/en/stable/advanced-features/recording#custom-recording-layouts"
* target="_blank">Custom recording layouts</a> to learn more
*/
public String customLayout() {
@ -227,8 +247,8 @@ public class RecordingProperties {
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
* has been called with value
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}. For
* {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}.
* For {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all
* individual video files will have the native resolution of the published
* stream.<br>
* <br>
@ -241,7 +261,7 @@ public class RecordingProperties {
/**
* Defines whether to record audio or not. Cannot be set to false at the same
* time as {@link hasVideo()}.<br>
* time as {@link RecordingProperties#hasVideo()}.<br>
* <br>
*
* Default to true
@ -252,7 +272,7 @@ public class RecordingProperties {
/**
* Defines whether to record video or not. Cannot be set to false at the same
* time as {@link hasAudio()}.<br>
* time as {@link RecordingProperties#hasAudio()}.<br>
* <br>
*
* Default to true
@ -273,4 +293,18 @@ public class RecordingProperties {
return this.shmSize;
}
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a> The Media Node where to host the recording. The default
* option if this property is not defined is the same Media Node hosting the
* Session to record. This property only applies to COMPOSED recordings and is
* ignored for INDIVIDUAL recordings
*/
public String mediaNode() {
return this.mediaNode;
}
}

View File

@ -19,9 +19,10 @@ package io.openvidu.java.client;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@ -34,6 +35,7 @@ import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
@ -48,7 +50,7 @@ public class Session {
private long createdAt;
private OpenVidu openVidu;
private SessionProperties properties;
private Map<String, Connection> activeConnections = new ConcurrentHashMap<>();
private Map<String, Connection> connections = new ConcurrentHashMap<>();
private boolean recording = false;
protected Session(OpenVidu openVidu) throws OpenViduJavaClientException, OpenViduHttpException {
@ -66,11 +68,11 @@ public class Session {
protected Session(OpenVidu openVidu, JsonObject json) {
this.openVidu = openVidu;
this.resetSessionWithJson(json);
this.resetWithJson(json);
}
/**
* Gets the unique identifier of the Session
* Gets the unique identifier of the Session.
*
* @return The sessionId
*/
@ -80,78 +82,49 @@ public class Session {
/**
* Timestamp when this session was created, in UTC milliseconds (ms since Jan 1,
* 1970, 00:00:00 UTC)
* 1970, 00:00:00 UTC).
*/
public long createdAt() {
return this.createdAt;
}
/**
* Gets a new token associated to Session object with default values for
* {@link io.openvidu.java.client.TokenOptions}. This always translates into a
* new request to OpenVidu Server
* @deprecated Use {@link Session#createConnection() Session.createConnection()}
* instead to get a {@link io.openvidu.java.client.Connection}
* object.
*
* @return The generated token String
*
* @return The generated token
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@Deprecated
public String generateToken() throws OpenViduJavaClientException, OpenViduHttpException {
return this.generateToken(new TokenOptions.Builder().role(OpenViduRole.PUBLISHER).build());
return generateToken(new TokenOptions.Builder().data("").role(OpenViduRole.PUBLISHER).build());
}
/**
* Gets a new token associated to Session object configured with
* <code>tokenOptions</code>. This always translates into a new request to
* OpenVidu Server
* @deprecated Use
* {@link Session#createConnection(io.openvidu.java.client.ConnectionProperties)
* Session.createConnection(ConnectionProperties)} instead to get a
* {@link io.openvidu.java.client.Connection} object.
*
* @return The generated token String
*
* @return The generated token
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@Deprecated
public String generateToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException {
if (!this.hasSessionId()) {
this.getSessionId();
}
HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_TOKENS);
JsonObject json = new JsonObject();
json.addProperty("session", this.sessionId);
json.addProperty("role", tokenOptions.getRole().name());
json.addProperty("data", tokenOptions.getData());
if (tokenOptions.getKurentoOptions() != null) {
JsonObject kurentoOptions = new JsonObject();
if (tokenOptions.getKurentoOptions().getVideoMaxRecvBandwidth() != null) {
kurentoOptions.addProperty("videoMaxRecvBandwidth",
tokenOptions.getKurentoOptions().getVideoMaxRecvBandwidth());
}
if (tokenOptions.getKurentoOptions().getVideoMinRecvBandwidth() != null) {
kurentoOptions.addProperty("videoMinRecvBandwidth",
tokenOptions.getKurentoOptions().getVideoMinRecvBandwidth());
}
if (tokenOptions.getKurentoOptions().getVideoMaxSendBandwidth() != null) {
kurentoOptions.addProperty("videoMaxSendBandwidth",
tokenOptions.getKurentoOptions().getVideoMaxSendBandwidth());
}
if (tokenOptions.getKurentoOptions().getVideoMinSendBandwidth() != null) {
kurentoOptions.addProperty("videoMinSendBandwidth",
tokenOptions.getKurentoOptions().getVideoMinSendBandwidth());
}
if (tokenOptions.getKurentoOptions().getAllowedFilters().length > 0) {
JsonArray allowedFilters = new JsonArray();
for (String filter : tokenOptions.getKurentoOptions().getAllowedFilters()) {
allowedFilters.add(filter);
}
kurentoOptions.add("allowedFilters", allowedFilters);
}
json.add("kurentoOptions", kurentoOptions);
}
StringEntity params;
try {
params = new StringEntity(json.toString());
params = new StringEntity(tokenOptions.toJsonObject(sessionId).toString());
} catch (UnsupportedEncodingException e1) {
throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
}
@ -180,10 +153,79 @@ public class Session {
}
}
/**
* Same as
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
* but with default ConnectionProperties values.
*
* @return The generated {@link io.openvidu.java.client.Connection Connection}
* object.
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
public Connection createConnection() throws OpenViduJavaClientException, OpenViduHttpException {
return createConnection(
new ConnectionProperties.Builder().data("").role(OpenViduRole.PUBLISHER).record(true).build());
}
/**
* Creates a new Connection object associated to Session object and configured
* with <code>connectionProperties</code>. Each user connecting to the Session
* requires a Connection. The token string value to send to the client side can
* be retrieved with {@link io.openvidu.java.client.Connection#getToken()
* Connection.getToken()}.
*
* @return The generated {@link io.openvidu.java.client.Connection Connection}
* object.
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
public Connection createConnection(ConnectionProperties connectionProperties)
throws OpenViduJavaClientException, OpenViduHttpException {
if (!this.hasSessionId()) {
this.getSessionId();
}
HttpPost request = new HttpPost(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection");
StringEntity params;
try {
params = new StringEntity(connectionProperties.toJson(sessionId).toString());
} catch (UnsupportedEncodingException e1) {
throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
}
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(params);
HttpResponse response;
try {
response = this.openVidu.httpClient.execute(request);
} catch (IOException e2) {
throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause());
}
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
Connection connection = new Connection(httpResponseToJson(response));
this.connections.put(connection.getConnectionId(), connection);
return connection;
} else {
throw new OpenViduHttpException(statusCode);
}
} finally {
EntityUtils.consumeQuietly(response.getEntity());
}
}
/**
* Gracefully closes the Session: unpublishes all streams and evicts every
* participant
*
* participant.
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@ -217,22 +259,25 @@ public class Session {
* connections to the Session
* ({@link io.openvidu.java.client.Session#getActiveConnections()}) and use
* those values to call
* {@link io.openvidu.java.client.Session#forceDisconnect(Connection)} or
* {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)}. <br>
*
* To update every Session object owned by OpenVidu object, call
* {@link io.openvidu.java.client.OpenVidu#fetch()}
*
* {@link io.openvidu.java.client.Session#forceDisconnect(Connection)},
* {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)} or
* {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}.<br>
* <br>
*
* To update all Session objects owned by OpenVidu object at once, call
* {@link io.openvidu.java.client.OpenVidu#fetch()}.
*
* @return true if the Session status has changed with respect to the server,
* false if not. This applies to any property or sub-property of the
* object
*
* object.
*
* @throws OpenViduHttpException
* @throws OpenViduJavaClientException
*/
public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException {
String beforeJSON = this.toJson();
HttpGet request = new HttpGet(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId);
final String beforeJSON = this.toJson();
HttpGet request = new HttpGet(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "?pendingConnections=true");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
HttpResponse response;
@ -245,8 +290,8 @@ public class Session {
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
this.resetSessionWithJson(httpResponseToJson(response));
String afterJSON = this.toJson();
this.resetWithJson(httpResponseToJson(response));
final String afterJSON = this.toJson();
boolean hasChanged = !beforeJSON.equals(afterJSON);
log.info("Session info fetched for session '{}'. Any change: {}", this.sessionId, hasChanged);
return hasChanged;
@ -259,16 +304,31 @@ public class Session {
}
/**
* Forces the user represented by <code>connection</code> to leave the session.
* OpenVidu Browser will trigger the proper events on the client-side
* (<code>streamDestroyed</code>, <code>connectionDestroyed</code>,
* Removes the Connection from the Session. This can translate into a forced
* eviction of a user from the Session if the Connection had status
* <code>active</code>, or into a token invalidation if no user had taken the
* Connection yet (status <code>pending</code>). <br>
* <br>
*
* In the first case, OpenVidu Browser will trigger the proper events on the
* client-side (<code>streamDestroyed</code>, <code>connectionDestroyed</code>,
* <code>sessionDisconnected</code>) with reason set to
* "forceDisconnectByServer" <br>
*
* You can get <code>connection</code> parameter with
* {@link io.openvidu.java.client.Session#fetch()} and then
* {@link io.openvidu.java.client.Session#getActiveConnections()}
*
* <code>"forceDisconnectByServer"</code>. <br>
* <br>
*
* In the second case, the token of the Connection will be invalidated and no
* user will be able to connect to the session with it. <br>
* <br>
*
* This method automatically updates the properties of the local affected
* objects. This means that there is no need to call
* {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the
* changes consequence of the execution of this method applied in the local
* objects.
*
* @param connection The Connection to remove
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@ -277,17 +337,13 @@ public class Session {
}
/**
* Forces the user with Connection <code>connectionId</code> to leave the
* session. OpenVidu Browser will trigger the proper events on the client-side
* (<code>streamDestroyed</code>, <code>connectionDestroyed</code>,
* <code>sessionDisconnected</code>) with reason set to
* "forceDisconnectByServer" <br>
*
* You can get <code>connectionId</code> parameter with
* {@link io.openvidu.java.client.Session#fetch()} (use
* {@link io.openvidu.java.client.Connection#getConnectionId()} to get the
* `connectionId` you want)
*
* Same as {@link io.openvidu.java.client.Session#forceDisconnect(Connection)
* forceDisconnect(ConnectionProperties)} but providing the
* {@link io.openvidu.java.client.Connection#getConnectionId() connectionId}
* instead of the Connection object.
*
* @param connectionId The identifier of the Connection object to remove
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@ -307,13 +363,13 @@ public class Session {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
// Remove connection from activeConnections map
Connection connectionClosed = this.activeConnections.remove(connectionId);
Connection connectionClosed = this.connections.remove(connectionId);
// Remove every Publisher of the closed connection from every subscriber list of
// other connections
if (connectionClosed != null) {
for (Publisher publisher : connectionClosed.getPublishers()) {
String streamId = publisher.getStreamId();
for (Connection connection : this.activeConnections.values()) {
for (Connection connection : this.connections.values()) {
connection.setSubscribers(connection.getSubscribers().stream()
.filter(subscriber -> !streamId.equals(subscriber)).collect(Collectors.toList()));
}
@ -334,17 +390,28 @@ public class Session {
}
/**
* Forces some user to unpublish a Stream. OpenVidu Browser will trigger the
* proper events on the client-side (<code>streamDestroyed</code>) with reason
* set to "forceUnpublishByServer".<br>
*
* Forces some Connection to unpublish a Stream. OpenVidu Browser will trigger
* the proper events in the client-side (<code>streamDestroyed</code>) with
* reason set to <code>"forceUnpublishByServer"</code>. <br>
* <br>
*
* You can get <code>publisher</code> parameter with
* {@link io.openvidu.java.client.Session#getActiveConnections()} and then for
* each Connection you can call
* {@link io.openvidu.java.client.Connection#getPublishers()}. Remember to call
* {@link io.openvidu.java.client.Session#fetch()} before to fetch the current
* actual properties of the Session from OpenVidu Server
*
* actual properties of the Session from OpenVidu Server.<br>
* <br>
*
* This method automatically updates the properties of the local affected
* objects. This means that there is no need to call
* {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the
* changes consequence of the execution of this method applied in the local
* objects.
*
* @param publisher The Publisher object to unpublish
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@ -353,19 +420,13 @@ public class Session {
}
/**
* Forces some user to unpublish a Stream. OpenVidu Browser will trigger the
* proper events on the client-side (<code>streamDestroyed</code>) with reason
* set to "forceUnpublishByServer". <br>
*
* You can get <code>streamId</code> parameter with
* {@link io.openvidu.java.client.Session#getActiveConnections()} and then for
* each Connection you can call
* {@link io.openvidu.java.client.Connection#getPublishers()}. Finally
* {@link io.openvidu.java.client.Publisher#getStreamId()}) will give you the
* <code>streamId</code>. Remember to call
* {@link io.openvidu.java.client.Session#fetch()} before to fetch the current
* actual properties of the Session from OpenVidu Server
*
* Same as {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)
* forceUnpublish(Publisher)} but providing the
* {@link io.openvidu.java.client.Publisher#getStreamId() streamId} instead of
* the Publisher object.
*
* @param streamId The identifier of the Publisher object to remove
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@ -384,7 +445,7 @@ public class Session {
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
for (Connection connection : this.activeConnections.values()) {
for (Connection connection : this.connections.values()) {
// Try to remove the Publisher from the Connection publishers collection
if (connection.publishers.remove(streamId) != null) {
continue;
@ -402,44 +463,194 @@ public class Session {
}
/**
* Returns the list of active connections to the session. <strong>This value
* will remain unchanged since the last time method
* {@link io.openvidu.java.client.Session#fetch()} was called</strong>.
* Exceptions to this rule are:
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a> Updates the properties of a Connection with a
* {@link io.openvidu.java.client.ConnectionProperties} object. Only these
* properties can be updated:
* <ul>
* <li>Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)}
* updates each affected Connection status</li>
* <li>Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)}
* updates each affected Connection status</li>
* <li>{@link io.openvidu.java.client.ConnectionProperties.Builder#role(OpenViduRole)
* ConnectionProperties.Builder.role(OpenViduRole)}</li>
* <li>{@link io.openvidu.java.client.ConnectionProperties.Builder#record(boolean)
* ConnectionProperties.Builder.record(boolean)}</li>
* </ul>
* <br>
* To get the list of active connections with their current actual value, you
* must call first {@link io.openvidu.java.client.Session#fetch()} and then
* {@link io.openvidu.java.client.Session#getActiveConnections()}
*
* This method automatically updates the properties of the local affected
* objects. This means that there is no need to call
* {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the
* changes consequence of the execution of this method applied in the local
* objects.<br>
* <br>
*
* The affected client will trigger one <a href=
* "/en/stable/api/openvidu-browser/classes/connectionpropertychangedevent.html"
* target="_blank">ConnectionPropertyChangedEvent</a> for each modified
* property.
*
* @param connectionId The Connection to modify
* @param connectionProperties A ConnectionProperties object with the new values
* to apply
*
* @return The updated {@link io.openvidu.java.client.Connection Connection}
* object
*
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
public List<Connection> getActiveConnections() {
return new ArrayList<>(this.activeConnections.values());
public Connection updateConnection(String connectionId, ConnectionProperties connectionProperties)
throws OpenViduJavaClientException, OpenViduHttpException {
HttpPatch request = new HttpPatch(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId);
StringEntity params;
try {
params = new StringEntity(connectionProperties.toJson(this.sessionId).toString());
} catch (UnsupportedEncodingException e1) {
throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
}
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(params);
HttpResponse response;
try {
response = this.openVidu.httpClient.execute(request);
} catch (IOException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
}
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
log.info("Connection {} updated", connectionId);
} else if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
log.info("Properties of Connection {} remain the same", connectionId);
} else {
throw new OpenViduHttpException(statusCode);
}
JsonObject json = httpResponseToJson(response);
// Update the actual Connection object with the new options
Connection existingConnection = this.connections.get(connectionId);
if (existingConnection == null) {
// The updated Connection is not available in local map
Connection newConnection = new Connection(json);
this.connections.put(connectionId, newConnection);
return newConnection;
} else {
// The updated Connection was available in local map
existingConnection.overrideConnectionProperties(connectionProperties);
return existingConnection;
}
} finally {
EntityUtils.consumeQuietly(response.getEntity());
}
}
/**
* Returns whether the session is being recorded or not
* Returns a Connection of the Session. This method only returns the local
* available object and does not query OpenVidu Server. To get the current
* actual value you must call first
* {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}.
*
* @param id The Connection to get
*
* @return The {@link io.openvidu.java.client.Connection Connection} object, or
* <code>null</code> if no Connection is found for param <code>id</code>
*/
public Connection getConnection(String id) {
return this.connections.get(id);
}
/**
* Returns all the Connections of the Session. This method only returns the
* local available objects and does not query OpenVidu Server. To get the
* current actual value you must call first
* {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}.
*
* <strong>The list of Connections will remain unchanged since the last time
* method {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} was
* called</strong>. Exceptions to this rule are:
* <ul>
* <li>Calling
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)
* createConnection(ConnectionProperties)} automatically adds the new Connection
* object to the local collection.</li>
* <li>Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)}
* automatically updates each affected local Connection object.</li>
* <li>Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)}
* automatically updates each affected local Connection object.</li>
* <li>Calling
* {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}
* automatically updates the attributes of the affected local Connection
* object.</li>
* </ul>
* <br>
* To get the list of connections with their current actual value, you must call
* first {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
* {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}.
*/
public List<Connection> getConnections() {
return this.connections.values().stream().collect(Collectors.toList());
}
/**
* Returns the list of active Connections of the Session. These are the
* Connections returning <code>active</code> in response to
* {@link io.openvidu.java.client.Connection#getStatus()}. This method only
* returns the local available objects and does not query OpenVidu Server.
* <strong>The list of active Connections will remain unchanged since the last
* time method {@link io.openvidu.java.client.Session#fetch() Session.fetch()}
* or {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} was
* called</strong>. Exceptions to this rule are:
* <ul>
* <li>Calling
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)
* createConnection(ConnectionProperties)} automatically adds the new Connection
* object to the local collection.</li>
* <li>Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)}
* automatically updates each affected local Connection object.</li>
* <li>Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)}
* automatically updates each affected local Connection object.</li>
* <li>Calling
* {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}
* automatically updates the attributes of the affected local Connection
* object.</li>
* </ul>
* <br>
* To get the list of active connections with their current actual value, you
* must call first {@link io.openvidu.java.client.Session#fetch()
* Session.fetch()} or {@link io.openvidu.java.client.OpenVidu#fetch()
* OpenVidu.fetch()} OpenVidu.fetch()}.
*/
public List<Connection> getActiveConnections() {
return this.connections.values().stream().filter(con -> "active".equals(con.getStatus()))
.collect(Collectors.toList());
}
/**
* Returns whether the session is being recorded or not.
*/
public boolean isBeingRecorded() {
return this.recording;
}
/**
* Returns the properties defining the session
* Returns the properties defining the session.
*/
public SessionProperties getProperties() {
return this.properties;
}
@Override
public String toString() {
return this.sessionId;
}
private boolean hasSessionId() {
return (this.sessionId != null && !this.sessionId.isEmpty());
}
@ -450,27 +661,9 @@ public class Session {
}
HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_SESSIONS);
JsonObject json = new JsonObject();
json.addProperty("mediaMode", properties.mediaMode().name());
json.addProperty("recordingMode", properties.recordingMode().name());
json.addProperty("defaultOutputMode", properties.defaultOutputMode().name());
json.addProperty("defaultRecordingLayout", properties.defaultRecordingLayout().name());
json.addProperty("defaultCustomLayout", properties.defaultCustomLayout());
json.addProperty("customSessionId", properties.customSessionId());
// forcedVideoCodec codec and allowTranscoding could be null because
// both default values are loaded by openvidu server
if (properties.forcedVideoCodec() != null) {
json.addProperty("forcedVideoCodec", properties.forcedVideoCodec().name());
}
if (properties.isTranscodingAllowed() != null) {
json.addProperty("allowTranscoding", properties.isTranscodingAllowed());
}
StringEntity params = null;
try {
params = new StringEntity(json.toString());
params = new StringEntity(properties.toJson().toString());
} catch (UnsupportedEncodingException e1) {
throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
}
@ -490,19 +683,24 @@ public class Session {
JsonObject responseJson = httpResponseToJson(response);
this.sessionId = responseJson.get("id").getAsString();
this.createdAt = responseJson.get("createdAt").getAsLong();
// forcedVideoCodec and allowTranscoding values are configured in OpenVidu Server
// via configuration or session
VideoCodec forcedVideoCodec = VideoCodec.valueOf(responseJson.get("forcedVideoCodec").getAsString());
Boolean allowTranscoding = responseJson.get("allowTranscoding").getAsBoolean();
SessionProperties responseProperties = new SessionProperties.Builder()
.mediaMode(properties.mediaMode())
.recordingMode(properties.recordingMode())
.defaultOutputMode(properties.defaultOutputMode())
.defaultRecordingLayout(properties.defaultRecordingLayout())
.defaultCustomLayout(properties.defaultCustomLayout())
.forcedVideoCodec(forcedVideoCodec)
.allowTranscoding(allowTranscoding)
.build();
.customSessionId(properties.customSessionId())
.mediaMode(properties.mediaMode())
.recordingMode(properties.recordingMode())
.defaultOutputMode(properties.defaultOutputMode())
.defaultRecordingLayout(properties.defaultRecordingLayout())
.defaultCustomLayout(properties.defaultCustomLayout())
.mediaNode(properties.mediaNode())
.forcedVideoCodec(forcedVideoCodec)
.allowTranscoding(allowTranscoding)
.build();
this.properties = responseProperties;
log.info("Session '{}' created", this.sessionId);
} else if (statusCode == org.apache.http.HttpStatus.SC_CONFLICT) {
@ -530,7 +728,7 @@ public class Session {
this.recording = recording;
}
protected Session resetSessionWithJson(JsonObject json) {
protected Session resetWithJson(JsonObject json) {
this.sessionId = json.get("sessionId").getAsString();
this.createdAt = json.get("createdAt").getAsLong();
this.recording = json.get("recording").getAsBoolean();
@ -544,50 +742,44 @@ public class Session {
if (json.has("defaultCustomLayout")) {
builder.defaultCustomLayout(json.get("defaultCustomLayout").getAsString());
}
if (json.has("customSessionId")) {
builder.customSessionId(json.get("customSessionId").getAsString());
}
if (json.has("forcedVideoCodec")) {
builder.forcedVideoCodec(VideoCodec.valueOf(json.get("forcedVideoCodec").getAsString()));
}
if (json.has("allowTranscoding")) {
builder.allowTranscoding(json.get("allowTranscoding").getAsBoolean());
}
if (this.properties != null && this.properties.customSessionId() != null) {
builder.customSessionId(this.properties.customSessionId());
} else if (json.has("customSessionId")) {
builder.customSessionId(json.get("customSessionId").getAsString());
}
this.properties = builder.build();
JsonArray jsonArrayConnections = (json.get("connections").getAsJsonObject()).get("content").getAsJsonArray();
this.activeConnections.clear();
jsonArrayConnections.forEach(connection -> {
JsonObject con = connection.getAsJsonObject();
Map<String, Publisher> publishers = new ConcurrentHashMap<>();
JsonArray jsonArrayPublishers = con.get("publishers").getAsJsonArray();
jsonArrayPublishers.forEach(publisher -> {
JsonObject pubJson = publisher.getAsJsonObject();
JsonObject mediaOptions = pubJson.get("mediaOptions").getAsJsonObject();
Publisher pub = new Publisher(pubJson.get("streamId").getAsString(),
pubJson.get("createdAt").getAsLong(), mediaOptions.get("hasAudio").getAsBoolean(),
mediaOptions.get("hasVideo").getAsBoolean(), mediaOptions.get("audioActive"),
mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), mediaOptions.get("typeOfVideo"),
mediaOptions.get("videoDimensions"));
publishers.put(pub.getStreamId(), pub);
// 1. Set to store fetched connections and later remove closed ones
Set<String> fetchedConnectionIds = new HashSet<>();
jsonArrayConnections.forEach(connectionJsonElement -> {
JsonObject connectionJson = connectionJsonElement.getAsJsonObject();
Connection connectionObj = new Connection(connectionJson);
String id = connectionObj.getConnectionId();
fetchedConnectionIds.add(id);
// 2. Update existing Connection
this.connections.computeIfPresent(id, (cId, c) -> {
c = c.resetWithJson(connectionJson);
return c;
});
List<String> subscribers = new ArrayList<>();
JsonArray jsonArraySubscribers = con.get("subscribers").getAsJsonArray();
jsonArraySubscribers.forEach(subscriber -> {
subscribers.add((subscriber.getAsJsonObject()).get("streamId").getAsString());
// 3. Add new Connection
this.connections.computeIfAbsent(id, cId -> {
return connectionObj;
});
Connection c = new Connection(con.get("connectionId").getAsString(), con.get("createdAt").getAsLong(),
OpenViduRole.valueOf(con.get("role").getAsString()),
(con.has("token") ? con.get("token").getAsString() : null), con.get("location").getAsString(),
con.get("platform").getAsString(), con.get("serverData").getAsString(),
con.get("clientData").getAsString(), publishers, subscribers);
this.activeConnections.put(con.get("connectionId").getAsString(), c);
});
// 4. Remove closed connections from local collection
this.connections.entrySet()
.removeIf(entry -> !fetchedConnectionIds.contains(entry.getValue().getConnectionId()));
return this;
}
@ -609,26 +801,10 @@ public class Session {
json.addProperty("allowTranscoding", this.properties.isTranscodingAllowed());
}
JsonObject connections = new JsonObject();
connections.addProperty("numberOfElements", this.getActiveConnections().size());
connections.addProperty("numberOfElements", this.getConnections().size());
JsonArray jsonArrayConnections = new JsonArray();
this.getActiveConnections().forEach(con -> {
JsonObject c = new JsonObject();
c.addProperty("connectionId", con.getConnectionId());
c.addProperty("role", con.getRole().name());
c.addProperty("token", con.getToken());
c.addProperty("clientData", con.getClientData());
c.addProperty("serverData", con.getServerData());
JsonArray pubs = new JsonArray();
con.getPublishers().forEach(p -> {
pubs.add(p.toJson());
});
JsonArray subs = new JsonArray();
con.getSubscribers().forEach(s -> {
subs.add(s);
});
c.add("publishers", pubs);
c.add("subscribers", subs);
jsonArrayConnections.add(c);
this.getConnections().forEach(con -> {
jsonArrayConnections.add(con.toJson());
});
connections.add("content", jsonArrayConnections);
json.add("connections", connections);

View File

@ -17,6 +17,8 @@
package io.openvidu.java.client;
import com.google.gson.JsonObject;
import io.openvidu.java.client.Recording.OutputMode;
/**
@ -30,6 +32,7 @@ public class SessionProperties {
private RecordingLayout defaultRecordingLayout;
private String defaultCustomLayout;
private String customSessionId;
private String mediaNode;
private VideoCodec forcedVideoCodec;
private Boolean allowTranscoding;
@ -44,6 +47,7 @@ public class SessionProperties {
private RecordingLayout defaultRecordingLayout = RecordingLayout.BEST_FIT;
private String defaultCustomLayout = "";
private String customSessionId = "";
private String mediaNode;
private VideoCodec forcedVideoCodec;
private Boolean allowTranscoding;
@ -53,7 +57,7 @@ public class SessionProperties {
*/
public SessionProperties build() {
return new SessionProperties(this.mediaMode, this.recordingMode, this.defaultOutputMode,
this.defaultRecordingLayout, this.defaultCustomLayout, this.customSessionId,
this.defaultRecordingLayout, this.defaultCustomLayout, this.customSessionId, this.mediaNode,
this.forcedVideoCodec, this.allowTranscoding);
}
@ -62,7 +66,7 @@ public class SessionProperties {
* your clients: routed through OpenVidu Media Node
* (<code>MediaMode.ROUTED</code>) or attempting direct p2p connections
* (<code>MediaMode.RELAYED</code>, <i>not available yet</i>)
*
*
* Default value is <code>MediaMode.ROUTED</code>
*/
public SessionProperties.Builder mediaMode(MediaMode mediaMode) {
@ -120,11 +124,11 @@ public class SessionProperties {
* {@link io.openvidu.java.client.RecordingProperties.Builder#customLayout(String)}
* with any other value.<br>
* <br>
*
*
* Custom layouts are only applicable to recordings with OutputMode
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} (or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}) and
* RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} (or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START})
* and RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
*/
public SessionProperties.Builder defaultCustomLayout(String path) {
this.defaultCustomLayout = path;
@ -142,20 +146,55 @@ public class SessionProperties {
this.customSessionId = customSessionId;
return this;
}
/**
* Call this method to define which video codec do you want to be forcibly used for this session.
* This allows browsers/clients to use the same codec avoiding transcoding in the media server.
* If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(Boolean)}
* If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(Boolean)}
* is <code>false</code> and exception will occur.
*
* If forcedVideoCodec is set to NONE, no codec will be forced.
*
* If forcedVideoCodec is set to NONE, no codec will be forced.
*/
public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) {
this.forcedVideoCodec = forcedVideoCodec;
return this;
}
/**
* Call this method to define if you want to allow transcoding in the media server or not
* when {@link #forcedVideoCodec(VideoCodec)} is not compatible with the browser/client.
*/
public SessionProperties.Builder allowTranscoding(Boolean allowTranscoding) {
this.allowTranscoding = allowTranscoding;
return this;
}
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a> Call this method to force the session to be hosted in the
* Media Node with identifier <code>mediaNodeId</code>
*/
public SessionProperties.Builder mediaNode(String mediaNodeId) {
this.mediaNode = mediaNodeId;
return this;
}
/**
* Call this method to define which video codec do you want to be forcibly used for this session.
* This allows browsers/clients to use the same codec avoiding transcoding in the media server.
* If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(Boolean)}
* is <code>false</code> and exception will occur.
*
* If forcedVideoCodec is set to NONE, no codec will be forced.
*/
public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) {
this.forcedVideoCodec = forcedVideoCodec;
return this;
}
/**
* Call this method to define if you want to allow transcoding in the media server or not
* when {@link #forcedVideoCodec(VideoCodec)} is not compatible with the browser/client.
@ -174,11 +213,11 @@ public class SessionProperties {
this.defaultRecordingLayout = RecordingLayout.BEST_FIT;
this.defaultCustomLayout = "";
this.customSessionId = "";
this.allowTranscoding = false;
this.mediaNode = "";
}
private SessionProperties(MediaMode mediaMode, RecordingMode recordingMode, OutputMode outputMode,
RecordingLayout layout, String defaultCustomLayout, String customSessionId,
RecordingLayout layout, String defaultCustomLayout, String customSessionId, String mediaNode,
VideoCodec forcedVideoCodec, Boolean allowTranscoding) {
this.mediaMode = mediaMode;
this.recordingMode = recordingMode;
@ -186,6 +225,7 @@ public class SessionProperties {
this.defaultRecordingLayout = layout;
this.defaultCustomLayout = defaultCustomLayout;
this.customSessionId = customSessionId;
this.mediaNode = mediaNode;
this.forcedVideoCodec = forcedVideoCodec;
this.allowTranscoding = allowTranscoding;
}
@ -244,8 +284,8 @@ public class SessionProperties {
* with any other value.<br>
* Custom layouts are only applicable to recordings with OutputMode
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} (or
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}) and
* RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START})
* and RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
*/
public String defaultCustomLayout() {
return this.defaultCustomLayout;
@ -261,14 +301,14 @@ public class SessionProperties {
public String customSessionId() {
return this.customSessionId;
}
/**
* Defines which video codec is being forced to be used in the browser/client
*/
public VideoCodec forcedVideoCodec() {
return this.forcedVideoCodec;
}
/**
* Defines if transcoding is allowed or not when {@link #forcedVideoCodec}
* is not a compatible codec with the browser/client.
@ -277,4 +317,54 @@ public class SessionProperties {
return this.allowTranscoding;
}
}
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank"
* style="display: inline-block; background-color: rgb(0, 136, 170); color:
* white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius:
* 3px; font-size: 13px; line-height:21px; font-family: Montserrat,
* sans-serif">PRO</a> The Media Node where to host the session. The default
* option if this property is not defined is the less loaded Media Node at the
* moment the first user joins the session.
*/
public String mediaNode() {
return this.mediaNode;
}
/**
* Defines which video codec is being forced to be used in the browser/client
*/
public VideoCodec forcedVideoCodec() {
return this.forcedVideoCodec;
}
/**
* Defines if transcoding is allowed or not when {@link #forcedVideoCodec}
* is not a compatible codec with the browser/client.
*/
public Boolean isTranscodingAllowed() {
return this.allowTranscoding;
}
protected JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("mediaMode", mediaMode().name());
json.addProperty("recordingMode", recordingMode().name());
json.addProperty("defaultOutputMode", defaultOutputMode().name());
json.addProperty("defaultRecordingLayout", defaultRecordingLayout().name());
json.addProperty("defaultCustomLayout", defaultCustomLayout());
json.addProperty("customSessionId", customSessionId());
if (mediaNode() != null) {
JsonObject mediaNodeJson = new JsonObject();
mediaNodeJson.addProperty("id", mediaNode());
json.add("mediaNode", mediaNodeJson);
}
if (forcedVideoCodec() != null) {
json.addProperty("forcedVideoCodec", forcedVideoCodec().name());
}
if (isTranscodingAllowed() != null) {
json.addProperty("allowTranscoding", isTranscodingAllowed());
}
return json;
}
}

View File

@ -17,31 +17,42 @@
package io.openvidu.java.client;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
/**
* See {@link io.openvidu.java.client.Session#generateToken(TokenOptions)}
* @deprecated Use {@link io.openvidu.java.client.ConnectionProperties
* ConnectionProperties} instead
*/
public class TokenOptions {
private String data;
private OpenViduRole role;
private String data;
private KurentoOptions kurentoOptions;
/**
*
* Builder for {@link io.openvidu.java.client.TokenOptions}
*
* @deprecated Use {@link io.openvidu.java.client.ConnectionProperties.Builder
* ConnectionProperties.Builder} instead
*/
public static class Builder {
private String data = "";
private OpenViduRole role = OpenViduRole.PUBLISHER;
private String data;
private KurentoOptions kurentoOptions;
/**
* Builder for {@link io.openvidu.java.client.TokenOptions}
* Builder for {@link io.openvidu.java.client.TokenOptions}.
*/
public TokenOptions build() {
return new TokenOptions(this.data, this.role, this.kurentoOptions);
return new TokenOptions(this.role, this.data, this.kurentoOptions);
}
/**
* Call this method to set the role assigned to this token.
*/
public Builder role(OpenViduRole role) {
this.role = role;
return this;
}
/**
@ -71,17 +82,9 @@ public class TokenOptions {
return this;
}
/**
* Call this method to set the role assigned to this token
*/
public Builder role(OpenViduRole role) {
this.role = role;
return this;
}
/**
* Call this method to set a {@link io.openvidu.java.client.KurentoOptions}
* object for this token
* object for this token.
*/
public Builder kurentoOptions(KurentoOptions kurentoOptions) {
this.kurentoOptions = kurentoOptions;
@ -90,26 +93,26 @@ public class TokenOptions {
}
private TokenOptions(String data, OpenViduRole role, KurentoOptions kurentoOptions) {
this.data = data;
TokenOptions(OpenViduRole role, String data, KurentoOptions kurentoOptions) {
this.role = role;
this.data = data;
this.kurentoOptions = kurentoOptions;
}
/**
* Returns the secure (server-side) metadata assigned to this token
*/
public String getData() {
return this.data;
}
/**
* Returns the role assigned to this token
* Returns the role assigned to this token.
*/
public OpenViduRole getRole() {
return this.role;
}
/**
* Returns the secure (server-side) metadata assigned to this token.
*/
public String getData() {
return this.data;
}
/**
* Returns the Kurento options assigned to this token
*/
@ -117,4 +120,23 @@ public class TokenOptions {
return this.kurentoOptions;
}
protected JsonObject toJsonObject(String sessionId) {
JsonObject json = new JsonObject();
json.addProperty("session", sessionId);
if (getRole() != null) {
json.addProperty("role", getRole().name());
} else {
json.add("role", JsonNull.INSTANCE);
}
if (getData() != null) {
json.addProperty("data", getData());
} else {
json.add("data", JsonNull.INSTANCE);
}
if (this.kurentoOptions != null) {
json.add("kurentoOptions", kurentoOptions.toJson());
}
return json;
}
}

View File

@ -45,4 +45,6 @@ Thumbs.db
/lib/
docs/
lib/
lib/
*.tgz

View File

@ -12,6 +12,7 @@ module.exports = {
externalPattern: "node_modules",
excludeExternals: true,
excludePrivate: true,
excludeProtected: true,
excludeNotExported: true,
theme: "default",
readme: "none",

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,24 @@
{
"author": "OpenVidu",
"dependencies": {
"axios": "0.19.2",
"buffer": "5.6.0"
"axios": "0.21.1",
"buffer": "6.0.2"
},
"description": "OpenVidu Node Client",
"devDependencies": {
"@types/node": "14.0.13",
"grunt": "1.1.0",
"@types/node": "14.14.7",
"grunt": "1.3.0",
"grunt-cli": "1.3.2",
"grunt-contrib-copy": "1.0.0",
"grunt-contrib-sass": "1.0.0",
"grunt-contrib-uglify": "4.0.1",
"grunt-contrib-sass": "2.0.0",
"grunt-contrib-uglify": "5.0.0",
"grunt-contrib-watch": "1.1.0",
"grunt-postcss": "0.9.0",
"grunt-string-replace": "1.3.1",
"grunt-ts": "6.0.0-beta.22",
"ts-node": "8.10.2",
"tslint": "6.1.1",
"typedoc": "0.17.7",
"ts-node": "9.0.0",
"tslint": "6.1.3",
"typedoc": "0.19.2",
"typescript": "3.8.3"
},
"license": "Apache-2.0",
@ -33,5 +33,5 @@
"docs": "./generate-docs.sh"
},
"typings": "lib/index.d.ts",
"version": "2.15.0"
"version": "2.16.0"
}

View File

@ -15,37 +15,47 @@
*
*/
import { OpenViduRole } from './OpenViduRole';
import { Publisher } from './Publisher';
import { ConnectionProperties } from './ConnectionProperties';
import { OpenViduRole } from './OpenViduRole';
/**
* See [[Session.activeConnections]]
* See [[Session.connections]]
*/
export class Connection {
/**
* Identifier of the connection. You can call [[Session.forceDisconnect]] passing this property as parameter
* Identifier of the Connection. You can call methods [[Session.forceDisconnect]]
* or [[Session.updateConnection]] passing this property as parameter
*/
connectionId: string;
/**
* Timestamp when this connection was established, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC)
* Returns the status of the Connection. Can be:
* - `pending`: if the Connection is waiting for any user to use
* its internal token to connect to the session, calling method
* [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect)
* in OpenVidu Browser.
* - `active`: if the internal token of the Connection has already
* been used by some user to connect to the session, and it cannot be used
* again.
*/
status: string;
/**
* Timestamp when the Connection was created, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC)
*/
createdAt: number;
/**
* Role of the connection
* Timestamp when the Connection was taken by a user (passing from status "pending" to "active")
* in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC)
*/
role: OpenViduRole;
/**
* Token associated to the connection
*/
token: string;
activeAt: number;
/**
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
* Geo location of the connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it)
* Geo location of the Connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it)
*/
location: string;
@ -55,16 +65,22 @@ export class Connection {
platform: string;
/**
* Data associated to the connection on the server-side. This value is set with property [[TokenOptions.data]] when calling [[Session.generateToken]]
*/
serverData: string;
/**
* Data associated to the connection on the client-side. This value is set with second parameter of method
* Data associated to the Connection on the client-side. This value is set with second parameter of method
* [Session.connect](/en/stable/api/openvidu-browser/classes/session.html#connect) in OpenVidu Browser
*/
clientData: string;
/**
* The [[ConnectionProperties]] assigned to the Connection
*/
connectionProperties: ConnectionProperties;
/**
* Token associated to the Connection. This is the value that must be sent to the client-side to be consumed in OpenVidu Browser
* method [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect).
*/
token: string;
/**
* Array of Publisher objects this particular Connection is publishing to the Session (each Publisher object has one Stream, uniquely
* identified by its `streamId`). You can call [[Session.forceUnpublish]] passing any of this values as parameter
@ -77,21 +93,112 @@ export class Connection {
*/
subscribers: string[] = [];
/**
* @hidden deprecated. Inside ConnectionProperties
*/
role?: OpenViduRole;
/**
* @hidden deprecated. Inside ConnectionProperties
*/
serverData?: string;
/**
* @hidden
*/
constructor(connectionId: string, createdAt: number, role: OpenViduRole, token: string, location: string, platform: string, serverData: string, clientData: string,
publishers: Publisher[], subscribers: string[]) {
this.connectionId = connectionId;
this.createdAt = createdAt;
this.role = role;
this.token = token;
this.location = location;
this.platform = platform;
this.serverData = serverData;
this.clientData = clientData;
this.publishers = publishers;
this.subscribers = subscribers;
constructor(json) {
this.resetWithJson(json);
}
/**
* @hidden
*/
resetWithJson(json): Connection {
this.connectionId = json.connectionId;
this.status = json.status;
this.createdAt = json.createdAt;
this.activeAt = json.activeAt;
this.location = json.location;
this.platform = json.platform;
this.clientData = json.clientData;
this.token = json.token;
if (this.connectionProperties != null) {
this.connectionProperties.type = json.type;
this.connectionProperties.data = json.serverData;
this.connectionProperties.record = json.record;
this.connectionProperties.role = json.role;
this.connectionProperties.kurentoOptions = json.kurentoOptions;
this.connectionProperties.rtspUri = json.rtspUri;
this.connectionProperties.adaptativeBitrate = json.adaptativeBitrate;
this.connectionProperties.onlyPlayWithSubscribers = json.onlyPlayWithSubscribers;
this.connectionProperties.networkCache = json.networkCache;
} else {
this.connectionProperties = {
type: json.type,
data: json.serverData,
record: json.record,
role: json.role,
kurentoOptions: json.kurentoOptions,
rtspUri: json.rtspUri,
adaptativeBitrate: json.adaptativeBitrate,
onlyPlayWithSubscribers: json.onlyPlayWithSubscribers,
networkCache: json.networkCache
}
}
this.role = json.role;
this.serverData = json.serverData;
// publishers may be null
if (json.publishers != null) {
// 1. Array to store fetched Publishers and later remove closed ones
const fetchedPublisherIds: string[] = [];
json.publishers.forEach(jsonPublisher => {
const publisherObj: Publisher = new Publisher(jsonPublisher);
fetchedPublisherIds.push(publisherObj.streamId);
let storedPublisher = this.publishers.find(c => c.streamId === publisherObj.streamId);
if (!!storedPublisher) {
// 2. Update existing Publisher
storedPublisher.resetWithJson(jsonPublisher);
} else {
// 3. Add new Publisher
this.publishers.push(publisherObj);
}
});
// 4. Remove closed Publishers from local collection
for (var i = this.publishers.length - 1; i >= 0; --i) {
if (!fetchedPublisherIds.includes(this.publishers[i].streamId)) {
this.publishers.splice(i, 1);
}
}
}
// subscribers may be null
if (json.subscribers != null) {
// 1. Array to store fetched Subscribers and later remove closed ones
const fetchedSubscriberIds: string[] = [];
json.subscribers.forEach(jsonSubscriber => {
fetchedSubscriberIds.push(jsonSubscriber.streamId)
if (this.subscribers.indexOf(jsonSubscriber.streamId) === -1) {
// 2. Add new Subscriber
this.subscribers.push(jsonSubscriber.streamId);
}
});
// 3. Remove closed Subscribers from local collection
for (var i = this.subscribers.length - 1; i >= 0; --i) {
if (!fetchedSubscriberIds.includes(this.subscribers[i])) {
this.subscribers.splice(i, 1);
}
}
}
return this;
}
/**
@ -100,17 +207,32 @@ export class Connection {
equalTo(other: Connection): boolean {
let equals: boolean = (
this.connectionId === other.connectionId &&
this.status === other.status &&
this.createdAt === other.createdAt &&
this.role === other.role &&
this.activeAt === other.activeAt &&
this.connectionProperties.type === other.connectionProperties.type &&
this.connectionProperties.data === other.connectionProperties.data &&
this.connectionProperties.record === other.connectionProperties.record &&
this.connectionProperties.role === other.connectionProperties.role &&
this.connectionProperties.rtspUri === other.connectionProperties.rtspUri &&
this.connectionProperties.adaptativeBitrate === other.connectionProperties.adaptativeBitrate &&
this.connectionProperties.onlyPlayWithSubscribers === other.connectionProperties.onlyPlayWithSubscribers &&
this.connectionProperties.networkCache === other.connectionProperties.networkCache &&
this.token === other.token &&
this.location === other.location &&
this.platform === other.platform &&
this.serverData === other.serverData &&
this.clientData === other.clientData &&
this.subscribers.length === other.subscribers.length &&
this.publishers.length === other.publishers.length);
if (equals) {
equals = JSON.stringify(this.subscribers) === JSON.stringify(other.subscribers);
if (this.connectionProperties.kurentoOptions != null) {
equals = JSON.stringify(this.connectionProperties.kurentoOptions) === JSON.stringify(other.connectionProperties.kurentoOptions);
} else {
equals = (this.connectionProperties.kurentoOptions === other.connectionProperties.kurentoOptions);
}
}
if (equals) {
equals = JSON.stringify(this.subscribers.sort()) === JSON.stringify(other.subscribers.sort());
if (equals) {
let i = 0;
while (equals && i < this.publishers.length) {
@ -125,4 +247,18 @@ export class Connection {
return false;
}
}
/**
* @hidden
*/
overrideConnectionProperties(newConnectionProperties: ConnectionProperties): void {
// For now only properties record and role
if (newConnectionProperties.record != null) {
this.connectionProperties.record = newConnectionProperties.record;
}
if (newConnectionProperties.role != null) {
this.connectionProperties.role = newConnectionProperties.role;
}
}
}

View File

@ -0,0 +1,130 @@
/*
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
*
* 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 { ConnectionType } from './ConnectionType';
import { OpenViduRole } from './OpenViduRole';
export interface ConnectionProperties {
/**
* Type of Connection. The [[ConnectionType]] dictates what properties will have effect:
*
* - **[[ConnectionType.WEBRTC]]**: [[data]], [[record]], [[role]], [[kurentoOptions]]
* - **[[ConnectionType.IPCAM]]**: [[data]], [[record]], [[rtspUri]], [[adaptativeBitrate]], [[onlyPlayWithSubscribers]], [[networkCache]]
*
* @default WEBRTC
*/
type?: ConnectionType;
/**
* Secure (server-side) data associated to this Connection. Every client will receive this data in property `Connection.data`. Object `Connection` can be retrieved by subscribing to event `connectionCreated` of Session object.
* - If you have provided no data in your clients when calling method `Session.connect(TOKEN, DATA)` (`DATA` not defined), then `Connection.data` will only have this [[ConnectionProperties.data]] property.
* - If you have provided some data when calling `Session.connect(TOKEN, DATA)` (`DATA` defined), then `Connection.data` will have the following structure: `"CLIENT_DATA%/%SERVER_DATA"`, being `CLIENT_DATA` the second
* parameter passed in OpenVidu Browser in method `Session.connect` and `SERVER_DATA` this [[ConnectionProperties.data]] property.
*/
data?: string;
/**
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* Whether to record the streams published by this Connection or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded)
*
* @default true
*/
record?: boolean;
/**
* The role assigned to this Connection
*
* **Only for [[ConnectionType.WEBRTC]]**
*
* @default PUBLISHER
*/
role?: OpenViduRole;
/**
* **WARNING**: experimental option. This interface may change in the near future
*
* Some advanced properties setting the configuration that the WebRTC streams of the Connection will have in Kurento Media Server.
* You can adjust:
* - `videoMaxRecvBandwidth`: maximum number of Kbps that the Connection will be able to receive from Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
* the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
* (parameter `OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH`) for every incoming stream of the Connection.
* _**WARNING**: the lower value set to this property limits every other bandwidth of the WebRTC pipeline this server-to-client stream belongs to. This includes the user publishing the stream and every other user subscribed to the stream_
* - `videoMinRecvBandwidth`: minimum number of Kbps that the cConnection will try to receive from Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
* the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
* (parameter `OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH`) for every incoming stream of the Connection.
* - `videoMaxSendBandwidth`: maximum number of Kbps that the Connection will be able to send to Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
* the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
* (parameter `OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH`) for every outgoing stream of the Connection.
* _**WARNING**: this value limits every other bandwidth of the WebRTC pipeline this client-to-server stream belongs to. This includes every other user subscribed to the stream_
* - `videoMinSendBandwidth`: minimum number of Kbps that the Connection will try to send to Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
* the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
* (parameter `OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH`) for every outgoing stream of the Connection.
* - `allowedFilters`: names of the filters the Connection will be able to apply. See [Voice and video filters](/en/stable/advanced-features/filters/)
*
* **Only for [[ConnectionType.WEBRTC]]**
*/
kurentoOptions?: {
videoMaxRecvBandwidth?: number,
videoMinRecvBandwidth?: number,
videoMaxSendBandwidth?: number,
videoMinSendBandwidth?: number,
allowedFilters?: string[]
};
/**
* RTSP URI of an IP camera. For example: `rtsp://your.camera.ip:7777/path`
*
* **Only for [[ConnectionType.IPCAM]]**
*/
rtspUri?: string;
/**
* Whether to use adaptative bitrate (and therefore adaptative quality) or not. For local network connections
* that do not require media transcoding this can be disabled to save CPU power. If you are not sure if transcoding
* might be necessary, setting this property to false **may result in media connections not being established**.
*
* **Only for [[ConnectionType.IPCAM]]**
*
* @default true
*/
adaptativeBitrate?: boolean;
/**
* Whether to enable the IP camera stream only when some user is subscribed to it, or not. This allows you to reduce
* power consumption and network bandwidth in your server while nobody is asking to receive the camera's video.
* On the counterpart, first user subscribing to the IP camera stream will take a little longer to receive its video.
*
* **Only for [[ConnectionType.IPCAM]]**
*
* @default true
*/
onlyPlayWithSubscribers?: boolean;
/**
* Size of the buffer of the endpoint receiving the IP camera's stream, in milliseconds. The smaller it is, the less
* delay the signal will have, but more problematic will be in unstable networks. Use short buffers only if there is
* a quality connection between the IP camera and OpenVidu Server.
*
* **Only for [[ConnectionType.IPCAM]]**
*
* @default 2000
*/
networkCache?: number;
}

View File

@ -0,0 +1,34 @@
/*
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
*
* 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.
*
*/
/**
* See [[Session.createConnection]]
*/
export enum ConnectionType {
/**
* WebRTC connection. This is the normal type of Connection for a regular user
* connecting to a session from an application.
*/
WEBRTC = 'WEBRTC',
/**
* IP camera connection. This is the type of Connection used by IP cameras to
* connect to a session.
*/
IPCAM = 'IPCAM'
}

View File

@ -17,13 +17,11 @@
import axios from 'axios';
import { Connection } from './Connection';
import { Publisher } from './Publisher';
import { Recording } from './Recording';
import { RecordingProperties } from './RecordingProperties';
import { Session } from './Session';
import { SessionProperties } from './SessionProperties';
import { RecordingLayout } from './RecordingLayout';
import { RecordingMode } from 'RecordingMode';
/**
* @hidden
@ -47,29 +45,36 @@ export class OpenVidu {
/**
* @hidden
*/
static readonly API_RECORDINGS: string = '/api/recordings';
static readonly API_PATH: string = '/openvidu/api';
/**
* @hidden
*/
static readonly API_RECORDINGS_START: string = '/start';
static readonly API_SESSIONS = OpenVidu.API_PATH + '/sessions';
/**
* @hidden
*/
static readonly API_RECORDINGS_STOP: string = '/stop';
static readonly API_TOKENS = OpenVidu.API_PATH + '/tokens';
/**
* @hidden
*/
static readonly API_SESSIONS = '/api/sessions';
static readonly API_RECORDINGS: string = OpenVidu.API_PATH + '/recordings';
/**
* @hidden
*/
static readonly API_TOKENS = '/api/tokens';
static readonly API_RECORDINGS_START: string = OpenVidu.API_RECORDINGS + '/start';
/**
* @hidden
*/
static readonly API_RECORDINGS_STOP: string = OpenVidu.API_RECORDINGS + '/stop';
/**
* Array of active sessions. **This value will remain unchanged since the last time method [[OpenVidu.fetch]]
* was called**. Exceptions to this rule are:
*
* - Calling [[OpenVidu.createSession]] automatically adds the new Session object to the local collection.
* - Calling [[Session.fetch]] updates that specific Session status
* - Calling [[Session.close]] automatically removes the Session from the list of active Sessions
* - Calling [[Session.forceDisconnect]] automatically updates the inner affected connections for that specific Session
@ -83,16 +88,18 @@ export class OpenVidu {
activeSessions: Session[] = [];
/**
* @param urlOpenViduServer Public accessible IP where your instance of OpenVidu Server is up an running
* @param hostname URL where your instance of OpenVidu Server is up an running.
* It must be the full URL (e.g. `https://12.34.56.78:1234/`)
*
* @param secret Secret used on OpenVidu Server initialization
*/
constructor(private urlOpenViduServer: string, secret: string) {
constructor(private hostname: string, secret: string) {
this.setHostnameAndPort();
this.basicAuth = this.getBasicAuth(secret);
}
/**
* Creates an OpenVidu session. You can call [[Session.getSessionId]] inside the resolved promise to retrieve the `sessionId`
* Creates an OpenVidu session. The session identifier will be available at property [[Session.sessionId]]
*
* @returns A Promise that is resolved to the [[Session]] if success and rejected with an Error object if not.
*/
@ -140,14 +147,16 @@ export class OpenVidu {
data = {
session: sessionId,
name: !!properties.name ? properties.name : '',
outputMode: !!properties.outputMode ? properties.outputMode : Recording.OutputMode.COMPOSED,
hasAudio: !!(properties.hasAudio),
hasVideo: !!(properties.hasVideo)
outputMode: properties.outputMode,
hasAudio: properties.hasAudio != null ? properties.hasAudio : null,
hasVideo: properties.hasVideo != null ? properties.hasVideo : null,
shmSize: properties.shmSize,
mediaNode: properties.mediaNode
};
if (data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]
|| data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) {
data.resolution = !!properties.resolution ? properties.resolution : '1920x1080';
data.recordingLayout = !!properties.recordingLayout ? properties.recordingLayout : RecordingLayout.BEST_FIT;
if ((data.hasVideo == null || data.hasVideo) && (data.outputMode == null || data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]
|| data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START])) {
data.resolution = properties.resolution;
data.recordingLayout = !!properties.recordingLayout ? properties.recordingLayout : '';
if (data.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) {
data.customLayout = !!properties.customLayout ? properties.customLayout : '';
}
@ -156,20 +165,18 @@ export class OpenVidu {
} else {
data = JSON.stringify({
session: sessionId,
name: param2,
outputMode: Recording.OutputMode.COMPOSED
name: param2
});
}
} else {
data = JSON.stringify({
session: sessionId,
name: '',
outputMode: Recording.OutputMode.COMPOSED
name: ''
});
}
axios.post(
this.host + OpenVidu.API_RECORDINGS + OpenVidu.API_RECORDINGS_START,
this.host + OpenVidu.API_RECORDINGS_START,
data,
{
headers: {
@ -223,7 +230,7 @@ export class OpenVidu {
return new Promise<Recording>((resolve, reject) => {
axios.post(
this.host + OpenVidu.API_RECORDINGS + OpenVidu.API_RECORDINGS_STOP + '/' + recordingId,
this.host + OpenVidu.API_RECORDINGS_STOP + '/' + recordingId,
undefined,
{
headers: {
@ -412,7 +419,7 @@ export class OpenVidu {
public fetch(): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
axios.get(
this.host + OpenVidu.API_SESSIONS,
this.host + OpenVidu.API_SESSIONS + '?pendingConnections=true',
{
headers: {
Authorization: this.basicAuth
@ -422,47 +429,44 @@ export class OpenVidu {
.then(res => {
if (res.status === 200) {
// Array to store fetched sessionIds and later remove closed sessions
const fetchedSessionIds: string[] = [];
// Boolean to store if any Session has changed
let hasChanged = false;
res.data.content.forEach(session => {
fetchedSessionIds.push(session.sessionId);
let sessionIndex = -1;
let storedSession = this.activeSessions.find((s, index) => {
if (s.sessionId === session.sessionId) {
sessionIndex = index;
return true;
} else {
return false;
}
});
// 1. Array to store fetched sessionIds and later remove closed ones
const fetchedSessionIds: string[] = [];
res.data.content.forEach(jsonSession => {
const fetchedSession: Session = new Session(this, jsonSession);
fetchedSessionIds.push(fetchedSession.sessionId);
let storedSession = this.activeSessions.find(s => s.sessionId === fetchedSession.sessionId);
if (!!storedSession) {
const fetchedSession: Session = new Session(this).resetSessionWithJson(session);
// 2. Update existing Session
const changed: boolean = !storedSession.equalTo(fetchedSession);
if (changed) {
storedSession = fetchedSession;
this.activeSessions[sessionIndex] = storedSession;
}
storedSession.resetWithJson(jsonSession);
console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed);
hasChanged = hasChanged || changed;
} else {
this.activeSessions.push(new Session(this, session));
console.log("New session '" + session.sessionId + "' info fetched");
// 3. Add new Session
this.activeSessions.push(fetchedSession);
console.log("New session '" + fetchedSession.sessionId + "' info fetched");
hasChanged = true;
}
});
// Remove closed sessions from activeSessions array
this.activeSessions = this.activeSessions.filter(session => {
if (fetchedSessionIds.includes(session.sessionId)) {
return true;
} else {
console.log("Removing closed session '" + session.sessionId + "'");
// 4. Remove closed sessions from local collection
for (var i = this.activeSessions.length - 1; i >= 0; --i) {
let sessionId = this.activeSessions[i].sessionId;
if (!fetchedSessionIds.includes(sessionId)) {
console.log("Removing closed session '" + sessionId + "'");
hasChanged = true;
return false;
this.activeSessions.splice(i, 1);
}
});
}
console.log('Active sessions info fetched: ', fetchedSessionIds);
resolve(hasChanged);
} else {
@ -498,26 +502,9 @@ export class OpenVidu {
const addWebRtcStatsToConnections = (connection: Connection, connectionsExtendedInfo: any) => {
const connectionExtended = connectionsExtendedInfo.find(c => c.connectionId === connection.connectionId);
if (!!connectionExtended) {
const publisherArray = [];
connection.publishers.forEach(pub => {
const publisherExtended = connectionExtended.publishers.find(p => p.streamId === pub.streamId);
const pubAux = {};
// Standard properties
pubAux['streamId'] = pub.streamId;
pubAux['createdAt'] = pub.createdAt;
const mediaOptions = {
audioActive: pub.audioActive,
videoActive: pub.videoActive,
hasAudio: pub.hasAudio,
hasVideo: pub.hasVideo,
typeOfVideo: pub.typeOfVideo,
frameRate: pub.frameRate,
videoDimensions: pub.videoDimensions
};
pubAux['mediaOptions'] = mediaOptions;
const newPublisher = new Publisher(pubAux);
// WebRtc properties
newPublisher['webRtc'] = {
pub['webRtc'] = {
kms: {
events: publisherExtended.events,
localCandidate: publisherExtended.localCandidate,
@ -529,11 +516,10 @@ export class OpenVidu {
remoteSdp: publisherExtended.remoteSdp
}
};
newPublisher['localCandidatePair'] = parseRemoteCandidatePair(newPublisher['webRtc'].kms.remoteCandidate);
pub['localCandidatePair'] = parseRemoteCandidatePair(pub['webRtc'].kms.remoteCandidate);
if (!!publisherExtended.serverStats) {
newPublisher['webRtc'].kms.serverStats = publisherExtended.serverStats;
pub['webRtc'].kms.serverStats = publisherExtended.serverStats;
}
publisherArray.push(newPublisher);
});
const subscriberArray = [];
connection.subscribers.forEach(sub => {
@ -562,7 +548,6 @@ export class OpenVidu {
}
subscriberArray.push(subAux);
});
connection.publishers = publisherArray;
connection.subscribers = subscriberArray;
}
};
@ -594,68 +579,64 @@ export class OpenVidu {
.then(res => {
if (res.status === 200) {
// Array to store fetched sessionIds and later remove closed sessions
const fetchedSessionIds: string[] = [];
// Global changes
let globalChanges = false;
// Collection of sessionIds telling whether each one of them has changed or not
const sessionChanges: ObjMap<boolean> = {};
res.data.content.forEach(session => {
fetchedSessionIds.push(session.sessionId);
let sessionIndex = -1;
let storedSession = this.activeSessions.find((s, index) => {
if (s.sessionId === session.sessionId) {
sessionIndex = index;
return true;
} else {
return false;
}
});
if (!!storedSession) {
const fetchedSession: Session = new Session(this).resetSessionWithJson(session);
fetchedSession.activeConnections.forEach(connection => {
addWebRtcStatsToConnections(connection, session.connections.content);
});
// 1. Array to store fetched sessionIds and later remove closed ones
const fetchedSessionIds: string[] = [];
res.data.content.forEach(jsonSession => {
const fetchedSession: Session = new Session(this, jsonSession);
fetchedSession.connections.forEach(connection => {
addWebRtcStatsToConnections(connection, jsonSession.connections.content);
});
fetchedSessionIds.push(fetchedSession.sessionId);
let storedSession = this.activeSessions.find(s => s.sessionId === fetchedSession.sessionId);
if (!!storedSession) {
// 2. Update existing Session
let changed = !storedSession.equalTo(fetchedSession);
if (!changed) { // Check if server webrtc information has changed in any Publisher object (Session.equalTo does not check Publisher.webRtc auxiliary object)
fetchedSession.activeConnections.forEach((connection, index1) => {
fetchedSession.connections.forEach((connection, index1) => {
for (let index2 = 0; (index2 < connection['publishers'].length && !changed); index2++) {
changed = changed || JSON.stringify(connection['publishers'][index2]['webRtc']) !== JSON.stringify(storedSession.activeConnections[index1]['publishers'][index2]['webRtc']);
changed = changed || JSON.stringify(connection['publishers'][index2]['webRtc']) !== JSON.stringify(storedSession.connections[index1]['publishers'][index2]['webRtc']);
}
});
}
if (changed) {
storedSession = fetchedSession;
this.activeSessions[sessionIndex] = storedSession;
}
storedSession.resetWithJson(jsonSession);
storedSession.connections.forEach(connection => {
addWebRtcStatsToConnections(connection, jsonSession.connections.content);
});
console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed);
sessionChanges[storedSession.sessionId] = changed;
globalChanges = globalChanges || changed;
} else {
const newSession = new Session(this, session);
newSession.activeConnections.forEach(connection => {
addWebRtcStatsToConnections(connection, session.connections.content);
});
this.activeSessions.push(newSession);
console.log("New session '" + session.sessionId + "' info fetched");
sessionChanges[session.sessionId] = true;
// 3. Add new Session
this.activeSessions.push(fetchedSession);
console.log("New session '" + fetchedSession.sessionId + "' info fetched");
sessionChanges[fetchedSession.sessionId] = true;
globalChanges = true;
}
});
// Remove closed sessions from activeSessions array
this.activeSessions = this.activeSessions.filter(session => {
if (fetchedSessionIds.includes(session.sessionId)) {
return true;
} else {
console.log("Removing closed session '" + session.sessionId + "'");
sessionChanges[session.sessionId] = true;
// 4. Remove closed sessions from local collection
for (var i = this.activeSessions.length - 1; i >= 0; --i) {
let sessionId = this.activeSessions[i].sessionId;
if (!fetchedSessionIds.includes(sessionId)) {
console.log("Removing closed session '" + sessionId + "'");
sessionChanges[sessionId] = true;
globalChanges = true;
return false;
this.activeSessions.splice(i, 1);
}
});
}
console.log('Active sessions info fetched: ', fetchedSessionIds);
resolve({ changes: globalChanges, sessionChanges });
} else {
@ -687,7 +668,7 @@ export class OpenVidu {
private setHostnameAndPort(): void {
let url: URL;
try {
url = new URL(this.urlOpenViduServer);
url = new URL(this.hostname);
} catch (error) {
console.error('URL format incorrect', error);
throw new Error('URL format incorrect: ' + error);

View File

@ -74,6 +74,13 @@ export class Publisher {
* @hidden
*/
constructor(json) {
this.resetWithJson(json);
}
/**
* @hidden
*/
resetWithJson(json): Publisher {
this.streamId = json.streamId;
this.createdAt = json.createdAt;
this.hasAudio = json.mediaOptions.hasAudio;
@ -83,6 +90,7 @@ export class Publisher {
this.frameRate = json.mediaOptions.frameRate;
this.typeOfVideo = json.mediaOptions.typeOfVideo;
this.videoDimensions = json.mediaOptions.videoDimensions;
return this;
}
/**

View File

@ -83,7 +83,7 @@ export class Recording {
hasVideo: !!json['hasVideo']
};
if (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]
|| this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) {
|| this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) {
this.properties.resolution = !!(json['resolution']) ? json['resolution'] : '1920x1080';
this.properties.recordingLayout = !!(json['recordingLayout']) ? json['recordingLayout'] : RecordingLayout.BEST_FIT;
if (this.properties.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) {
@ -103,7 +103,7 @@ export namespace Recording {
/**
* The recording is starting (cannot be stopped). Some recording may not go
* through this status and directly reach "started" status
* through this status and directly reach "started" status
*/
starting = 'starting',
@ -113,21 +113,20 @@ export namespace Recording {
started = 'started',
/**
* The recording has stopped and is being processed. At some point it will reach
* "ready" status
*/
* The recording has stopped and is being processed. At some point it will reach
* "ready" status
*/
stopped = 'stopped',
/**
* The recording has finished OK and is available for download through OpenVidu
* Server recordings endpoint:
* https://YOUR_OPENVIDUSERVER_IP/recordings/{RECORDING_ID}/{RECORDING_NAME}.{EXTENSION}
* The recording has finished being processed and is available for download through
* property [[Recording.url]]
*/
ready = 'ready',
/**
* The recording has failed. This status may be reached from "starting",
* "started" and "stopped" status
* "started" and "stopped" status
*/
failed = 'failed'
}
@ -141,24 +140,24 @@ export namespace Recording {
* Record all streams in a grid layout in a single archive
*/
COMPOSED = 'COMPOSED',
/**
* Works the same way as COMPOSED mode, but the necessary recorder
* service module will start some time in advance and won't be terminated
* once a specific session recording has ended. This module will remain
* up and running as long as the session remains active.
*
* - **Pros vs COMPOSED**: the process of starting the recording will be noticeably
* faster. This can be very useful in use cases where a session needs to be
* recorded multiple times over time, when a better response time is usually
* desirable.
* - **Cons vs COMPOSED**: for every session initialized with COMPOSED_QUICK_START
* recording output mode, extra CPU power will be required in OpenVidu Server.
* The recording module will be continuously rendering all of the streams being
* published to the session even when the session is not being recorded. And that
* is for every session configured with COMPOSED_QUICK_START.
*/
/**
* Works the same way as COMPOSED mode, but the necessary recorder
* service module will start some time in advance and won't be terminated
* once a specific session recording has ended. This module will remain
* up and running as long as the session remains active.
*
* - **Pros vs COMPOSED**: the process of starting the recording will be noticeably
* faster. This can be very useful in use cases where a session needs to be
* recorded multiple times over time, when a better response time is usually
* desirable.
* - **Cons vs COMPOSED**: for every session initialized with COMPOSED_QUICK_START
* recording output mode, extra CPU power will be required in OpenVidu Server.
* The recording module will be continuously rendering all of the streams being
* published to the session even when the session is not being recorded. And that
* is for every session configured with COMPOSED_QUICK_START.
*/
COMPOSED_QUICK_START = 'COMPOSED_QUICK_START',
/**

View File

@ -67,4 +67,22 @@ export interface RecordingProperties {
* Whether or not to record video. Cannot be set to false at the same time as [[RecordingProperties.hasAudio]]
*/
hasVideo?: boolean;
/**
* If COMPOSED recording, the amount of shared memory reserved for the recording process in bytes.
* Minimum 134217728 (128MB). Property ignored if INDIVIDUAL recording. Default to 536870912 (512 MB)
*/
shmSize?: number;
/**
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* The Media Node where to host the recording. The default option if this property is not defined is the same
* Media Node hosting the Session to record. This object defines the following properties as Media Node selector:
* - `id`: Media Node unique identifier
*/
mediaNode?: {
id: string;
}
}

View File

@ -15,11 +15,11 @@
*
*/
import axios from 'axios';
import axios, { AxiosError } from 'axios';
import { Connection } from './Connection';
import { ConnectionProperties } from './ConnectionProperties';
import { MediaMode } from './MediaMode';
import { OpenVidu } from './OpenVidu';
import { OpenViduRole } from './OpenViduRole';
import { Publisher } from './Publisher';
import { Recording } from './Recording';
import { RecordingLayout } from './RecordingLayout';
@ -28,7 +28,6 @@ import { SessionProperties } from './SessionProperties';
import { TokenOptions } from './TokenOptions';
import { VideoCodec } from './VideoCodec';
export class Session {
/**
@ -47,14 +46,26 @@ export class Session {
properties: SessionProperties;
/**
* Array of active connections to the session. This property always initialize as an empty array and
* **will remain unchanged since the last time method [[Session.fetch]] was called**. Exceptions to this rule are:
* Array of Connections to the Session. This property always initialize as an empty array and
* **will remain unchanged since the last time method [[Session.fetch]] or [[OpenVidu.fetch]] was called**.
* Exceptions to this rule are:
*
* - Calling [[Session.forceUnpublish]] also automatically updates each affected Connection status
* - Calling [[Session.forceDisconnect]] automatically updates each affected Connection status
* - Calling [[Session.createConnection]] automatically adds the new Connection object to the local collection.
* - Calling [[Session.forceUnpublish]] automatically updates each affected local Connection object.
* - Calling [[Session.forceDisconnect]] automatically updates each affected local Connection object.
* - Calling [[Session.updateConnection]] automatically updates the attributes of the affected local Connection object.
*
* To get the array of active connections with their current actual value, you must call [[Session.fetch]] before consulting
* property [[activeConnections]]
* To get the array of Connections with their current actual value, you must call [[Session.fetch]] or [[OpenVidu.fetch]]
* before consulting property [[connections]]
*/
connections: Connection[] = [];
/**
* Array containing the active Connections of the Session. It is a subset of [[Session.connections]] array containing only
* those Connections with property [[Connection.status]] to `active`.
*
* To get the array of active Connections with their current actual value, you must call [[Session.fetch]] or [[OpenVidu.fetch]]
* before consulting property [[activeConnections]]
*/
activeConnections: Connection[] = [];
@ -71,7 +82,7 @@ export class Session {
// Defined parameter
if (!!propertiesOrJson.sessionId) {
// Parameter is a JSON representation of Session ('sessionId' property always defined)
this.resetSessionWithJson(propertiesOrJson);
this.resetWithJson(propertiesOrJson);
} else {
// Parameter is a SessionProperties object
this.properties = propertiesOrJson;
@ -84,32 +95,23 @@ export class Session {
this.properties.recordingMode = !!this.properties.recordingMode ? this.properties.recordingMode : RecordingMode.MANUAL;
this.properties.defaultOutputMode = !!this.properties.defaultOutputMode ? this.properties.defaultOutputMode : Recording.OutputMode.COMPOSED;
this.properties.defaultRecordingLayout = !!this.properties.defaultRecordingLayout ? this.properties.defaultRecordingLayout : RecordingLayout.BEST_FIT;
this.properties.allowTranscoding = !!this.properties.allowTranscoding ? this.properties.allowTranscoding : null;
this.properties.forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : null;
this.properties.forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : undefined;
this.properties.allowTranscoding = this.properties.allowTranscoding != null ? this.properties.allowTranscoding : undefined;
}
/**
* Gets the unique identifier of the Session
*/
public getSessionId(): string {
return this.sessionId;
}
/**
* Gets a new token associated to Session object
* @deprecated Use [[Session.createConnection]] instead to get a [[Connection]] object.
*
* @returns A Promise that is resolved to the _token_ if success and rejected with an Error object if not
* @returns A Promise that is resolved to the generated _token_ string if success and rejected with an Error object if not
*/
public generateToken(tokenOptions?: TokenOptions): Promise<string> {
return new Promise<string>((resolve, reject) => {
const data = JSON.stringify({
session: this.sessionId,
role: (!!tokenOptions && !!tokenOptions.role) ? tokenOptions.role : OpenViduRole.PUBLISHER,
data: (!!tokenOptions && !!tokenOptions.data) ? tokenOptions.data : '',
kurentoOptions: (!!tokenOptions && !!tokenOptions.kurentoOptions) ? tokenOptions.kurentoOptions : {},
role: (!!tokenOptions && !!tokenOptions.role) ? tokenOptions.role : null,
data: (!!tokenOptions && !!tokenOptions.data) ? tokenOptions.data : null,
kurentoOptions: (!!tokenOptions && !!tokenOptions.kurentoOptions) ? tokenOptions.kurentoOptions : null
});
axios.post(
this.ov.host + OpenVidu.API_TOKENS,
data,
@ -123,26 +125,57 @@ export class Session {
.then(res => {
if (res.status === 200) {
// SUCCESS response from openvidu-server. Resolve token
resolve(res.data.id);
resolve(res.data.token);
} else {
// ERROR response from openvidu-server. Resolve HTTP status
reject(new Error(res.status.toString()));
}
}).catch(error => {
if (error.response) {
// The request was made and the server responded with a status code (not 2xx)
reject(new Error(error.response.status.toString()));
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(error.request);
reject(new Error(error.request));
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error', error.message);
reject(new Error(error.message));
this.handleError(error, reject);
});
});
}
/**
* Creates a new Connection object associated to Session object and configured with
* `connectionProperties`. Each user connecting to the Session requires a Connection.
* The token string value to send to the client side is available at [[Connection.token]].
*
* @returns A Promise that is resolved to the generated [[Connection]] object if success and rejected with an Error object if not
*/
public createConnection(connectionProperties?: ConnectionProperties): Promise<Connection> {
return new Promise<Connection>((resolve, reject) => {
const data = JSON.stringify({
role: (!!connectionProperties && !!connectionProperties.role) ? connectionProperties.role : null,
data: (!!connectionProperties && !!connectionProperties.data) ? connectionProperties.data : null,
record: !!connectionProperties ? connectionProperties.record : null,
kurentoOptions: (!!connectionProperties && !!connectionProperties.kurentoOptions) ? connectionProperties.kurentoOptions : null
});
axios.post(
this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection',
data,
{
headers: {
'Authorization': this.ov.basicAuth,
'Content-Type': 'application/json'
}
}
)
.then(res => {
if (res.status === 200) {
// SUCCESS response from openvidu-server. Store and resolve Connection
const connection = new Connection(res.data);
this.connections.push(connection);
if (connection.status === 'active') {
this.activeConnections.push(connection);
}
resolve(new Connection(res.data));
} else {
// ERROR response from openvidu-server. Resolve HTTP status
reject(new Error(res.status.toString()));
}
}).catch(error => {
this.handleError(error, reject);
});
});
}
@ -174,29 +207,17 @@ export class Session {
reject(new Error(res.status.toString()));
}
}).catch(error => {
if (error.response) {
// The request was made and the server responded with a status code (not 2xx)
reject(new Error(error.response.status.toString()));
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(error.request);
reject(new Error(error.request));
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error', error.message);
reject(new Error(error.message));
}
this.handleError(error, reject);
});
});
}
/**
* Updates every property of the Session with the current status it has in OpenVidu Server. This is especially useful for accessing the list of active
* connections of the Session ([[Session.activeConnections]]) and use those values to call [[Session.forceDisconnect]] or [[Session.forceUnpublish]].
* Updates every property of the Session with the current status it has in OpenVidu Server. This is especially useful for accessing the list of
* Connections of the Session ([[Session.connections]], [[Session.activeConnections]]) and use those values to call [[Session.forceDisconnect]],
* [[Session.forceUnpublish]] or [[Session.updateConnection]].
*
* To update every Session object owned by OpenVidu object, call [[OpenVidu.fetch]]
* To update all Session objects owned by OpenVidu object at once, call [[OpenVidu.fetch]]
*
* @returns A promise resolved to true if the Session status has changed with respect to the server, or to false if not.
* This applies to any property or sub-property of the Session object
@ -205,7 +226,7 @@ export class Session {
return new Promise<boolean>((resolve, reject) => {
const beforeJSON: string = JSON.stringify(this, this.removeCircularOpenViduReference);
axios.get(
this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId,
this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '?pendingConnections=true',
{
headers: {
'Authorization': this.ov.basicAuth,
@ -216,7 +237,7 @@ export class Session {
.then(res => {
if (res.status === 200) {
// SUCCESS response from openvidu-server
this.resetSessionWithJson(res.data);
this.resetWithJson(res.data);
const afterJSON: string = JSON.stringify(this, this.removeCircularOpenViduReference);
const hasChanged: boolean = !(beforeJSON === afterJSON);
console.log("Session info fetched for session '" + this.sessionId + "'. Any change: " + hasChanged);
@ -226,32 +247,26 @@ export class Session {
reject(new Error(res.status.toString()));
}
}).catch(error => {
if (error.response) {
// The request was made and the server responded with a status code (not 2xx)
reject(new Error(error.response.status.toString()));
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(error.request);
reject(new Error(error.request));
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error', error.message);
reject(new Error(error.message));
}
this.handleError(error, reject);
});
});
}
/**
* Forces the user with Connection `connectionId` to leave the session. OpenVidu Browser will trigger the proper events on the client-side
* (`streamDestroyed`, `connectionDestroyed`, `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`
* Removes the Connection from the Session. This can translate into a forced eviction of a user from the Session if the
* Connection had status `active` or into a token invalidation if no user had taken the Connection yet (status `pending`).
*
* You can get `connection` parameter from [[Session.activeConnections]] array ([[Connection.connectionId]] for getting each `connectionId` property).
* Remember to call [[Session.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server
* In the first case, OpenVidu Browser will trigger the proper events in the client-side (`streamDestroyed`, `connectionDestroyed`,
* `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`.
*
* @returns A Promise that is resolved if the user was successfully disconnected and rejected with an Error object if not
* In the second case, the token of the Connection will be invalidated and no user will be able to connect to the session with it.
*
* This method automatically updates the properties of the local affected objects. This means that there is no need to call
* [[Session.fetch]] or [[OpenVidu.fetch]]] to see the changes consequence of the execution of this method applied in the local objects.
*
* @param connection The Connection object to remove from the session, or its `connectionId` property
*
* @returns A Promise that is resolved if the Connection was successfully removed from the Session and rejected with an Error object if not
*/
public forceDisconnect(connection: string | Connection): Promise<any> {
return new Promise<any>((resolve, reject) => {
@ -267,9 +282,9 @@ export class Session {
.then(res => {
if (res.status === 204) {
// SUCCESS response from openvidu-server
// Remove connection from activeConnections array
// Remove connection from connections array
let connectionClosed;
this.activeConnections = this.activeConnections.filter(con => {
this.connections = this.connections.filter(con => {
if (con.connectionId !== connectionId) {
return true;
} else {
@ -280,7 +295,7 @@ export class Session {
// Remove every Publisher of the closed connection from every subscriber list of other connections
if (!!connectionClosed) {
connectionClosed.publishers.forEach(publisher => {
this.activeConnections.forEach(con => {
this.connections.forEach(con => {
con.subscribers = con.subscribers.filter(subscriber => {
// tslint:disable:no-string-literal
if (!!subscriber['streamId']) {
@ -295,8 +310,9 @@ export class Session {
});
});
} else {
console.warn("The closed connection wasn't fetched in OpenVidu Java Client. No changes in the collection of active connections of the Session");
console.warn("The closed connection wasn't fetched in OpenVidu Node Client. No changes in the collection of active connections of the Session");
}
this.updateActiveConnectionsArray();
console.log("Connection '" + connectionId + "' closed");
resolve();
} else {
@ -305,30 +321,22 @@ export class Session {
}
})
.catch(error => {
if (error.response) {
// The request was made and the server responded with a status code (not 2xx)
reject(new Error(error.response.status.toString()));
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(error.request);
reject(new Error(error.request));
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error', error.message);
reject(new Error(error.message));
}
this.handleError(error, reject);
});
});
}
/**
* Forces some user to unpublish a Stream (identified by its `streamId` or the corresponding [[Publisher]] object owning it).
* Forces some Connection to unpublish a Stream (identified by its `streamId` or the corresponding [[Publisher]] object owning it).
* OpenVidu Browser will trigger the proper events on the client-side (`streamDestroyed`) with reason set to `"forceUnpublishByServer"`.
*
* You can get `publisher` parameter from [[Connection.publishers]] array ([[Publisher.streamId]] for getting each `streamId` property).
* Remember to call [[Session.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server
* Remember to call [[Session.fetch]] or [[OpenVidu.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server
*
* This method automatically updates the properties of the local affected objects. This means that there is no need to call
* [[Session.fetch]] or [[OpenVidu.fetch]] to see the changes consequence of the execution of this method applied in the local objects.
*
* @param publisher The Publisher object to unpublish, or its `streamId` property
*
* @returns A Promise that is resolved if the stream was successfully unpublished and rejected with an Error object if not
*/
@ -347,7 +355,7 @@ export class Session {
.then(res => {
if (res.status === 204) {
// SUCCESS response from openvidu-server
this.activeConnections.forEach(connection => {
this.connections.forEach(connection => {
// Try to remove the Publisher from the Connection publishers collection
connection.publishers = connection.publishers.filter(pub => pub.streamId !== streamId);
// Try to remove the Publisher from the Connection subscribers collection
@ -363,6 +371,7 @@ export class Session {
}
}
});
this.updateActiveConnectionsArray();
console.log("Stream '" + streamId + "' unpublished");
resolve();
} else {
@ -370,24 +379,79 @@ export class Session {
reject(new Error(res.status.toString()));
}
}).catch(error => {
if (error.response) {
// The request was made and the server responded with a status code (not 2xx)
reject(new Error(error.response.status.toString()));
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(error.request);
reject(new Error(error.request));
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error', error.message);
reject(new Error(error.message));
}
this.handleError(error, reject);
});
});
}
/**
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* Updates the properties of a Connection with a [[ConnectionProperties]] object.
* Only these properties can be updated:
*
* - [[ConnectionProperties.role]]
* - [[ConnectionProperties.record]]
*
* This method automatically updates the properties of the local affected objects. This means that there is no need to call
* [[Session.fetch]] or [[OpenVidu.fetch]] to see the changes consequence of the execution of this method applied in the local objects.
*
* The affected client will trigger one [ConnectionPropertyChangedEvent](/en/stable/api/openvidu-browser/classes/connectionpropertychangedevent.html)
* for each modified property.
*
* @param connectionId The [[Connection.connectionId]] of the Connection object to modify
* @param connectionProperties A new [[ConnectionProperties]] object with the updated values to apply
*
* @returns A Promise that is resolved to the updated [[Connection]] object if the operation was
* successful and rejected with an Error object if not
*/
public updateConnection(connectionId: string, connectionProperties: ConnectionProperties): Promise<Connection | undefined> {
return new Promise<any>((resolve, reject) => {
axios.patch(
this.ov.host + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId,
connectionProperties,
{
headers: {
'Authorization': this.ov.basicAuth,
'Content-Type': 'application/json'
}
}
)
.then(res => {
if (res.status === 200) {
console.log('Connection ' + connectionId + ' updated');
} else {
// ERROR response from openvidu-server. Resolve HTTP status
reject(new Error(res.status.toString()));
return;
}
// Update the actual Connection object with the new options
const existingConnection: Connection = this.connections.find(con => con.connectionId === connectionId);
if (!existingConnection) {
// The updated Connection is not available in local map
const newConnection: Connection = new Connection(res.data);
this.connections.push(newConnection);
this.updateActiveConnectionsArray();
resolve(newConnection);
} else {
// The updated Connection was available in local map
existingConnection.overrideConnectionProperties(connectionProperties);
this.updateActiveConnectionsArray();
resolve(existingConnection);
}
}).catch(error => {
this.handleError(error, reject);
});
});
}
/**
* @hidden
*/
public getSessionId(): string {
return this.sessionId;
}
/**
* @hidden
*/
@ -399,18 +463,17 @@ export class Session {
}
const mediaMode = !!this.properties.mediaMode ? this.properties.mediaMode : MediaMode.ROUTED;
const recordingMode = !!this.properties.recordingMode ? this.properties.recordingMode : RecordingMode.MANUAL;
const recordingMode = !!this.properties.recordingMode ? this.properties.recordingMode : RecordingMode.MANUAL;
const defaultOutputMode = !!this.properties.defaultOutputMode ? this.properties.defaultOutputMode : Recording.OutputMode.COMPOSED;
const defaultRecordingLayout = !!this.properties.defaultRecordingLayout ? this.properties.defaultRecordingLayout : RecordingLayout.BEST_FIT;
const defaultCustomLayout = !!this.properties.defaultCustomLayout ? this.properties.defaultCustomLayout : '';
const defaultRecordingLayout = !!this.properties.defaultRecordingLayout ? this.properties.defaultRecordingLayout : RecordingLayout.BEST_FIT;
const defaultCustomLayout = !!this.properties.defaultCustomLayout ? this.properties.defaultCustomLayout : '';
const customSessionId = !!this.properties.customSessionId ? this.properties.customSessionId : '';
const forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : null;
const allowTranscoding = !!this.properties.allowTranscoding ? this.properties.allowTranscoding : null;
const data = JSON.stringify({ mediaMode, recordingMode,
defaultOutputMode, defaultRecordingLayout, defaultCustomLayout,
customSessionId, forcedVideoCodec, allowTranscoding
});
const mediaNode = !!this.properties.mediaNode ? this.properties.mediaNode : undefined;
const forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : undefined;
const allowTranscoding = this.properties.allowTranscoding != null ? this.properties.allowTranscoding : undefined;
const data = JSON.stringify({mediaMode, recordingMode, defaultOutputMode, defaultRecordingLayout, defaultCustomLayout,
customSessionId, mediaNode, forcedVideoCodec, allowTranscoding});
axios.post(
this.ov.host + OpenVidu.API_SESSIONS,
@ -431,7 +494,9 @@ export class Session {
this.properties.recordingMode = recordingMode;
this.properties.defaultOutputMode = defaultOutputMode;
this.properties.defaultRecordingLayout = defaultRecordingLayout;
this.properties.defaultCustomLayout = defaultCustomLayout;
this.properties.customSessionId = customSessionId;
this.properties.mediaNode = mediaNode;
this.properties.forcedVideoCodec = res.data.forcedVideoCodec;
this.properties.allowTranscoding = res.data.allowTranscoding;
resolve(this.sessionId);
@ -467,58 +532,67 @@ export class Session {
/**
* @hidden
*/
public resetSessionWithJson(json): Session {
public resetWithJson(json): Session {
this.sessionId = json.sessionId;
this.createdAt = json.createdAt;
this.recording = json.recording;
let customSessionId: string;
let defaultCustomLayout: string;
if (!!this.properties) {
customSessionId = this.properties.customSessionId;
defaultCustomLayout = !!json.defaultCustomLayout ? json.defaultCustomLayout : this.properties.defaultCustomLayout;
}
this.properties = {
customSessionId: json.customSessionId,
mediaMode: json.mediaMode,
recordingMode: json.recordingMode,
defaultOutputMode: json.defaultOutputMode,
defaultRecordingLayout: json.defaultRecordingLayout,
forcedVideoCodec: !!json.forcedVideoCodec ? json.forcedVideoCodec : null,
allowTranscoding: !!json.allowTranscoding ? json.allowTranscoding : null
defaultCustomLayout: json.defaultCustomLayout,
forcedVideoCodec: json.forcedVideoCodec,
allowTranscoding: json.allowTranscoding
};
if (!!customSessionId) {
this.properties.customSessionId = customSessionId;
} else if (!!json.customSessionId) {
this.properties.customSessionId = json.customSessionId;
if (json.defaultRecordingLayout == null) {
delete this.properties.defaultRecordingLayout;
}
if (!!defaultCustomLayout) {
this.properties.defaultCustomLayout = defaultCustomLayout;
if (json.customSessionId == null) {
delete this.properties.customSessionId;
}
if (json.defaultCustomLayout == null) {
delete this.properties.defaultCustomLayout;
}
if (json.mediaNode == null) {
delete this.properties.mediaNode;
}
if (json.forcedVideoCodec == null) {
delete this.properties.forcedVideoCodec;
}
if (json.allowTranscoding == null) {
delete this.properties.allowTranscoding;
}
this.activeConnections = [];
json.connections.content.forEach(connection => {
const publishers: Publisher[] = [];
connection.publishers.forEach(publisher => {
publishers.push(new Publisher(publisher));
});
const subscribers: string[] = [];
connection.subscribers.forEach(subscriber => {
subscribers.push(subscriber.streamId);
});
this.activeConnections.push(
new Connection(
connection.connectionId,
connection.createdAt,
connection.role,
connection.token,
connection.location,
connection.platform,
connection.serverData,
connection.clientData,
publishers,
subscribers));
// 1. Array to store fetched connections and later remove closed ones
const fetchedConnectionIds: string[] = [];
json.connections.content.forEach(jsonConnection => {
const connectionObj: Connection = new Connection(jsonConnection);
fetchedConnectionIds.push(connectionObj.connectionId);
let storedConnection = this.connections.find(c => c.connectionId === connectionObj.connectionId);
if (!!storedConnection) {
// 2. Update existing Connection
storedConnection.resetWithJson(jsonConnection);
} else {
// 3. Add new Connection
this.connections.push(connectionObj);
}
});
// 4. Remove closed sessions from local collection
for (var i = this.connections.length - 1; i >= 0; --i) {
if (!fetchedConnectionIds.includes(this.connections[i].connectionId)) {
this.connections.splice(i, 1);
}
}
// Order connections by time of creation
this.activeConnections.sort((c1, c2) => (c1.createdAt > c2.createdAt) ? 1 : ((c2.createdAt > c1.createdAt) ? -1 : 0));
this.connections.sort((c1, c2) => (c1.createdAt > c2.createdAt) ? 1 : ((c2.createdAt > c1.createdAt) ? -1 : 0));
// Populate activeConnections array
this.updateActiveConnectionsArray();
return this;
}
@ -530,13 +604,13 @@ export class Session {
this.sessionId === other.sessionId &&
this.createdAt === other.createdAt &&
this.recording === other.recording &&
this.activeConnections.length === other.activeConnections.length &&
this.connections.length === other.connections.length &&
JSON.stringify(this.properties) === JSON.stringify(other.properties)
);
if (equals) {
let i = 0;
while (equals && i < this.activeConnections.length) {
equals = this.activeConnections[i].equalTo(other.activeConnections[i]);
while (equals && i < this.connections.length) {
equals = this.connections[i].equalTo(other.connections[i]);
i++;
}
return equals;
@ -556,4 +630,36 @@ export class Session {
}
}
}
/**
* @hidden
*/
private updateActiveConnectionsArray() {
this.activeConnections = [];
this.connections.forEach(con => {
if (con.status === 'active') {
this.activeConnections.push(con);
}
});
}
/**
* @hidden
*/
private handleError(error: AxiosError, reject: (reason?: any) => void) {
if (error.response) {
// The request was made and the server responded with a status code (not 2xx)
reject(new Error(error.response.status.toString()));
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(error.request);
reject(new Error(error.request));
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error', error.message);
reject(new Error(error.message));
}
}
}

View File

@ -65,21 +65,31 @@ export interface SessionProperties {
* If this parameter is undefined or an empty string, OpenVidu Server will generate a random sessionId for you.
*/
customSessionId?: string;
/**
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* The Media Node where to host the session. The default option if this property is not defined is the less loaded
* Media Node at the moment the first user joins the session. This object defines the following properties as Media Node selector:
* - `id`: Media Node unique identifier
*/
mediaNode?: {
id: string;
}
/**
* It defines which video codec do you want to be forcibly used for this session.
* This allows browsers/clients to use the same codec avoiding transcoding in the media server.
* If the browser/client is not compatible with the specified codec and [[allowTranscoding]]
* If the browser/client is not compatible with the specified codec and [[allowTranscoding]]
* is <code>false</code> and exception will occur.
*
* If forcedVideoCodec is set to NONE, no codec will be forced.
*
* If forcedVideoCodec is set to NONE, no codec will be forced.
*/
forcedVideoCodec?: VideoCodec;
forcedVideoCodec?: string;
/**
* It defines if you want to allow transcoding in the media server or not
* when [[forcedVideoCodec]] is not compatible with the browser/client.
*/
allowTranscoding?: boolean;
}

View File

@ -18,10 +18,17 @@
import { OpenViduRole } from './OpenViduRole';
/**
* See [[Session.generateToken]]
* @deprecated Use [[ConnectionProperties]] instead
*/
export interface TokenOptions {
/**
* The role assigned to this token
*
* @default PUBLISHER
*/
role?: OpenViduRole;
/**
* Secure (server-side) data associated to this token. Every client will receive this data in property `Connection.data`. Object `Connection` can be retrieved by subscribing to event `connectionCreated` of Session object.
* - If you have provided no data in your clients when calling method `Session.connect(TOKEN, DATA)` (`DATA` not defined), then `Connection.data` will only have this [[TokenOptions.data]] property.
@ -30,11 +37,6 @@ export interface TokenOptions {
*/
data?: string;
/**
* The role assigned to this token
*/
role?: OpenViduRole;
/**
* **WARNING**: experimental option. This interface may change in the near future
*

View File

@ -3,6 +3,8 @@ export * from './OpenViduRole';
export * from './Session';
export * from './SessionProperties';
export * from './TokenOptions';
export * from './ConnectionProperties';
export * from './ConnectionType';
export * from './MediaMode';
export * from './RecordingLayout';
export * from './RecordingMode';

View File

@ -44,18 +44,18 @@ Parameters:
# OpenVidu configuration
OpenViduSecret:
Description: "Secret to connect to this OpenVidu Platform. No whitespaces or quotations allowed"
Description: "Secret to connect to this OpenVidu Platform. Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
Type: String
AllowedPattern: ^((?!")(?! ).)+$
AllowedPattern: ^[a-zA-Z0-9_-]+$
NoEcho: true
ConstraintDescription: OpenVidu Secret is mandatory
ConstraintDescription: "Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
# EC2 Instance configuration
InstanceType:
Description: "Specifies the EC2 instance type for your OpenVidu instance"
Type: String
Default: t2.xlarge
Default: c5.xlarge
AllowedValues:
- t2.large
- t2.xlarge
@ -115,10 +115,12 @@ Parameters:
- false
Default: true
#start_mappings
Mappings:
OVAMIMAP:
eu-west-1:
AMI: OV_AMI_ID
#end_mappings
Metadata:
'AWS::CloudFormation::Interface':
@ -272,6 +274,10 @@ Resources:
# Restart all services
pushd /opt/openvidu
docker-compose up -d kms
until docker-compose ps | grep kms | grep healthy; do
echo "Waiting kms..."
done
docker-compose up -d
popd
mode: "000755"
@ -293,6 +299,8 @@ Resources:
cfn-init --region ${AWS::Region} --stack ${AWS::StackId} --resource OpenviduServer
export HOME="/root"
# Replace .env variables
/usr/local/bin/feedGroupVars.sh || { echo "[Openvidu] Parameters incorrect/insufficient"; exit 1; }
@ -306,6 +314,10 @@ Resources:
# Start openvidu application
pushd /opt/openvidu
docker-compose up -d kms
until docker-compose ps | grep kms | grep healthy; do
echo "Waiting kms..."
done
docker-compose up -d
popd
@ -327,10 +339,9 @@ Resources:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: io1
Iops: 200
VolumeType: gp2
DeleteOnTermination: true
VolumeSize: 100
VolumeSize: 200
MyEIP:
Type: 'AWS::EC2::EIPAssociation'
@ -373,11 +384,11 @@ Resources:
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: 40000
ToPort: 65535
ToPort: 57000
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 40000
ToPort: 65535
ToPort: 57000
CidrIp: 0.0.0.0/0
Outputs:

View File

@ -141,5 +141,5 @@ Resources:
Type: AWS::CloudFormation::WaitCondition
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Timeout: PT20M
Count: 1

View File

@ -1,20 +1,15 @@
#!/bin/bash -x
set -eu -o pipefail
CF_OVP_TARGET=${CF_OVP_TARGET:-nomarket}
CF_RELEASE=${CF_RELEASE:-false}
AWS_KEY_NAME=${AWS_KEY_NAME:-}
if [[ $CF_RELEASE == "true" ]]; then
git checkout v$OPENVIDU_VERSION
fi
if [ ${CF_OVP_TARGET} == "market" ]; then
export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
export AWS_DEFAULT_REGION=us-east-1
else
export AWS_DEFAULT_REGION=eu-west-1
fi
export AWS_DEFAULT_REGION=eu-west-1
DATESTAMP=$(date +%s)
TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
@ -22,13 +17,13 @@ TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
# Get Latest Ubuntu AMI id from specified region
# Parameters
# $1 Aws region
getUbuntuAmiId() {
local AMI_ID=$(
aws --region ${1} ec2 describe-images \
--filters Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64* \
--query 'Images[*].[ImageId,CreationDate]' \
--output text \
| sort -k2 -r | head -n1 | cut -d$'\t' -f1
--filters "Name=name,Values=*ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*" \
--query "sort_by(Images, &CreationDate)" \
| jq -r 'del(.[] | select(.ImageOwnerAlias != null)) | .[-1].ImageId'
)
echo $AMI_ID
}
@ -59,6 +54,16 @@ TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-ov-ce-am
# Update installation script
if [[ ${UPDATE_INSTALLATION_SCRIPT} == "true" ]]; then
# Avoid overriding existing versions
# Only master and non existing versions can be overriden
if [[ ${OPENVIDU_VERSION} != "master" ]]; then
INSTALL_SCRIPT_EXISTS=true
aws s3api head-object --bucket aws.openvidu.io --key install_openvidu_$OPENVIDU_VERSION.sh || INSTALL_SCRIPT_EXISTS=false
if [[ ${INSTALL_SCRIPT_EXISTS} == "true" ]]; then
echo "Aborting updating s3://aws.openvidu.io/install_openvidu_${OPENVIDU_VERSION}.sh. File actually exists."
exit 1
fi
fi
aws s3 cp ../docker-compose/install_openvidu.sh s3://aws.openvidu.io/install_openvidu_$OPENVIDU_VERSION.sh --acl public-read
fi
@ -85,11 +90,34 @@ echo "Cleaning up"
aws cloudformation delete-stack --stack-name openvidu-ce-${DATESTAMP}
# Wait for the instance
aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID}
# Unfortunately, aws cli does not have a way to increase timeout
WAIT_RETRIES=0
WAIT_MAX_RETRIES=3
until [ "${WAIT_RETRIES}" -ge "${WAIT_MAX_RETRIES}" ]
do
aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID} && break
WAIT_RETRIES=$((WAIT_RETRIES+1))
sleep 5
done
# Updating the template
sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" CF-OpenVidu.yaml.template > CF-OpenVidu-${OPENVIDU_VERSION}.yaml
sed -i "s/OPENVIDU_VERSION/${OPENVIDU_VERSION}/g" CF-OpenVidu-${OPENVIDU_VERSION}.yaml
# Update CF template
if [[ ${UPDATE_CF} == "true" ]]; then
# Avoid overriding existing versions
# Only master and non existing versions can be overriden
if [[ ${OPENVIDU_VERSION} != "master" ]]; then
CF_EXIST=true
aws s3api head-object --bucket aws.openvidu.io --key CF-OpenVidu-${OPENVIDU_VERSION}.yaml || CF_EXIST=false
if [[ ${CF_EXIST} == "true" ]]; then
echo "Aborting updating s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml. File actually exists."
exit 1
fi
fi
aws s3 cp CF-OpenVidu-${OPENVIDU_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml --acl public-read
fi
rm $TEMPJSON
rm cfn-mkt-ov-ce-ami.yaml

View File

@ -5,14 +5,12 @@ set -eu -o pipefail
#
# Input parameters:
#
# OV_AMI_NAME OpenVidu AMI Name
# OV_AMI_ID OpenVidu AMI ID
# OV_AMI_NAME OpenVidu AMI Name
# OV_AMI_ID OpenVidu AMI ID
# UPDATE_CF Boolean, true if you want to update CF template by OPENVIDU_VERSION
# OPENVIDU_VERSION OpenVidu Version of the CF you want to update. It will update CF-OpenVidu-OPENVIDU_VERSION
export AWS_DEFAULT_REGION=eu-west-1
if [ ${CF_OVP_TARGET} == "market" ]; then
export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
fi
echo "Making original AMI public"
aws ec2 wait image-exists --image-ids ${OV_AMI_ID}
@ -71,13 +69,54 @@ do
ITER=$(expr $ITER + 1)
done
# Print and generate replicated AMIS
REPLICATED_AMIS_FILE="replicated_amis.yaml"
echo "OV IDs"
{
echo "#start_mappings"
echo "Mappings:"
echo " OVAMIMAP:"
ITER=0
for i in "${AMI_IDS[@]}"
do
AMI_ID=${AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
echo " ${REGION}:"
echo " AMI: ${AMI_ID}"
ITER=$(expr $ITER + 1)
done
echo "#end_mappings"
echo ""
} > "${REPLICATED_AMIS_FILE}" 2>&1
# Print replicated AMIs
cat "${REPLICATED_AMIS_FILE}"
if [[ ${UPDATE_CF} == "true" ]]; then
if [[ ! -z ${OPENVIDU_VERSION} ]]; then
# Download s3 file
aws s3 cp s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml CF-OpenVidu-${OPENVIDU_VERSION}.yaml
sed -e "/^#end_mappings/r ${REPLICATED_AMIS_FILE}" -e '/^#start_mappings/,/^#end_mappings/d' -i CF-OpenVidu-${OPENVIDU_VERSION}.yaml
aws s3 cp CF-OpenVidu-${OPENVIDU_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml --acl public-read
fi
fi
# Print AMI_LIST for delete_amis.sh
AMI_LIST=""
ITER=0
for i in "${AMI_IDS[@]}"
do
AMI_ID=${AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
echo " ${REGION}:"
echo " AMI: ${AMI_ID}"
if [[ ${ITER} -eq 0 ]]; then
AMI_LIST="${REGION}:${AMI_ID}"
else
AMI_LIST="${AMI_LIST},${REGION}:${AMI_ID}"
fi
ITER=$(expr $ITER + 1)
done
done
echo "AMI_LIST: ${AMI_LIST}"
# Cleaning the house
rm "${REPLICATED_AMIS_FILE}"
rm CF-OpenVidu-${OPENVIDU_VERSION}.yaml

View File

@ -39,6 +39,22 @@ LETSENCRYPT_EMAIL=user@example.com
# SDKs, REST clients and browsers will have to connect to this port
# HTTPS_PORT=443
# Old paths are considered now deprecated, but still supported by default.
# OpenVidu Server will log a WARN message every time a deprecated path is called, indicating
# the new path that should be used instead. You can set property SUPPORT_DEPRECATED_API=false
# to stop allowing the use of old paths.
# Default value is true
# SUPPORT_DEPRECATED_API=true
# If true request to with www will be redirected to non-www requests
# Default value is false
# REDIRECT_WWW=false
# How many workers to configure in nginx proxy.
# The more workers, the more requests will be handled
# Default value is 10240
# WORKER_CONNECTIONS=10240
# Access restrictions
# In this section you will be able to restrict the IPs from which you can access to
# Openvidu API and the Administration Panel
@ -110,6 +126,15 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
# All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true
# when a codec can not be forced, transcoding will be allowed
# Default value is VP8
# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
# Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied
# Default value is false
# OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
# true to enable OpenVidu Webhook service. false' otherwise
# Values: true | false
OPENVIDU_WEBHOOK=false
@ -138,10 +163,10 @@ OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
# All sessions of OpenVidu will try to force this codec. If OPENVIDU_ALLOW_TRANSCODING=true
# when a codec can not be forced, transcoding will be allowed
# when a codec can not be forced, transcoding will be allowed
# OPENVIDU_FORCED_CODEC=VP8
# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied
# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied
# OPENVIDU_ALLOW_TRANSCODING=false
# Call Detail Record enabled
@ -154,16 +179,16 @@ OPENVIDU_CDR_PATH=/opt/openvidu/cdr
# Kurento Media Server image
# --------------------------
# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server-dev
# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server
# Uncomment the next line and define this variable with KMS image that you want use
# KMS_IMAGE=kurento/kurento-media-server:6.14.0
# KMS_IMAGE=kurento/kurento-media-server:6.15.0
# Kurento Media Server Level logs
# -------------------------------
# Uncomment the next line and define this variable to change
# the verbosity level of the logs of KMS
# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
# KMS_DEBUG_LEVEL=3,Kurento*:4,kms*:4,sdp*:4,webrtc*:4,*rtpendpoint:4,rtp*handler:4,rtpsynchronizer:4,agnosticbin:4
# KMS_DOCKER_ENV_GST_DEBUG=
# Openvidu Server Level logs
# --------------------------

View File

@ -9,11 +9,11 @@ services:
#
# Default Application
#
# Openvidu-Call Version: 2.15.0
# Openvidu-Call Version: 2.16.0
#
# --------------------------------------------------------------
app:
image: openvidu/openvidu-call:2.15.0
image: openvidu/openvidu-call:2.16.0
restart: on-failure
network_mode: host
environment:
@ -21,3 +21,6 @@ services:
- OPENVIDU_URL=http://localhost:5443
- OPENVIDU_SECRET=${OPENVIDU_SECRET}
- CALL_OPENVIDU_CERTTYPE=${CERTIFICATE_TYPE}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"

View File

@ -7,11 +7,11 @@
# Application based on OpenVidu should be specified in
# docker-compose.override.yml file
#
# This docker-compose file coordinates all services of OpenVidu CE Platform.
# This docker-compose file coordinates all services of OpenVidu CE Platform
#
# This file will be overridden when update OpenVidu Platform
#
# Openvidu Version: 2.15.0
# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
@ -22,10 +22,10 @@ version: '3.1'
services:
openvidu-server:
image: openvidu/openvidu-server:2.15.0
image: openvidu/openvidu-server:2.17.0-dev2
restart: on-failure
network_mode: host
entrypoint: ['/bin/bash', '-c', 'export COTURN_IP=`/usr/local/bin/discover_my_public_ip.sh`; /usr/local/bin/entrypoint.sh']
entrypoint: ['/usr/local/bin/entrypoint.sh']
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
@ -39,9 +39,13 @@ services:
- KMS_URIS=["ws://localhost:8888/kurento"]
- COTURN_REDIS_IP=127.0.0.1
- COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET}
- COTURN_IP=${COTURN_IP:-auto-ipv4}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
kms:
image: ${KMS_IMAGE:-kurento/kurento-media-server:6.14.0}
image: ${KMS_IMAGE:-kurento/kurento-media-server:6.15.0}
restart: always
network_mode: host
ulimits:
@ -49,20 +53,29 @@ services:
volumes:
- /opt/openvidu/kms-crashes:/opt/openvidu/kms-crashes
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
- /opt/openvidu/kurento-logs:/opt/openvidu/kurento-logs
environment:
- KMS_MIN_PORT=40000
- KMS_MAX_PORT=57000
- GST_DEBUG=${KMS_DEBUG_LEVEL:-}
- GST_DEBUG=${KMS_DOCKER_ENV_GST_DEBUG:-}
- KURENTO_LOG_FILE_SIZE=${KMS_DOCKER_ENV_KURENTO_LOG_FILE_SIZE:-100}
- KURENTO_LOGS_PATH=/opt/openvidu/kurento-logs
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
redis:
image: openvidu/openvidu-redis:1.0.0
image: openvidu/openvidu-redis:2.0.0-dev2
restart: always
network_mode: host
environment:
- REDIS_PASSWORD=${OPENVIDU_SECRET}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
coturn:
image: openvidu/openvidu-coturn:1.0.0
image: openvidu/openvidu-coturn:3.0.0-dev2
restart: on-failure
network_mode: host
environment:
@ -72,14 +85,19 @@ services:
- DB_PASSWORD=${OPENVIDU_SECRET}
- MIN_PORT=57001
- MAX_PORT=65535
- ENABLE_COTURN_LOGS=true
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
nginx:
image: openvidu/openvidu-proxy:3.0.0
image: openvidu/openvidu-proxy:5.0.0-dev2
restart: on-failure
network_mode: host
volumes:
- ./certificates:/etc/letsencrypt
- ./owncert:/owncert
- ./custom-nginx-vhosts:/etc/nginx/vhost.d/
- ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:/opt/openvidu/custom-layout
environment:
- DOMAIN_OR_PUBLIC_IP=${DOMAIN_OR_PUBLIC_IP}
@ -91,3 +109,10 @@ services:
- ALLOWED_ACCESS_TO_RESTAPI=${ALLOWED_ACCESS_TO_RESTAPI:-}
- PROXY_MODE=CE
- WITH_APP=true
- SUPPORT_DEPRECATED_API=${SUPPORT_DEPRECATED_API:-true}
- REDIRECT_WWW=${REDIRECT_WWW:-false}
- WORKER_CONNECTIONS=${WORKER_CONNECTIONS:-10240}
- PUBLIC_IP=${PROXY_PUBLIC_IP:-auto-ipv4}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"

View File

@ -3,6 +3,7 @@
# Global variables
OPENVIDU_FOLDER=openvidu
OPENVIDU_VERSION=master
OPENVIDU_UPGRADABLE_VERSION="2.16"
DOWNLOAD_URL=https://raw.githubusercontent.com/OpenVidu/openvidu/${OPENVIDU_VERSION}
fatal_error() {
@ -42,10 +43,6 @@ new_ov_installation() {
--output "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
printf '\n - openvidu'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/ce/docker-compose/readme.md \
--output "${OPENVIDU_FOLDER}/readme.md" || fatal_error "Error when downloading the file 'readme.md'"
printf '\n - readme.md'
# Add execution permissions
printf "\n => Adding permission to 'openvidu' program..."
chmod +x "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error while adding permission to 'openvidu' program"
@ -54,6 +51,10 @@ new_ov_installation() {
printf "\n => Creating folder 'owncert'..."
mkdir "${OPENVIDU_FOLDER}/owncert" || fatal_error "Error while creating the folder 'owncert'"
# Create vhost nginx folder
printf "\n => Creating folder 'custom-nginx-vhosts'..."
mkdir "${OPENVIDU_FOLDER}/custom-nginx-vhosts" || fatal_error "Error while creating the folder 'custom-nginx-vhosts'"
# Ready to use
printf '\n'
printf '\n'
@ -105,10 +106,13 @@ upgrade_ov() {
# Uppgrade Openvidu
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && OPENVIDU_PREVIOUS_VERSION=2.13.0
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
fi
printf '\n'
printf '\n ======================================='
@ -146,10 +150,6 @@ upgrade_ov() {
--output "${TMP_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
printf '\n - openvidu'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/ce/docker-compose/readme.md \
--output "${TMP_FOLDER}/readme.md" || fatal_error "Error when downloading the file 'readme.md'"
printf '\n - readme.md'
# Dowloading new images and stoped actual Openvidu
printf '\n => Dowloading new images...'
printf '\n'
@ -187,12 +187,14 @@ upgrade_ov() {
mv "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - openvidu'
mv "${OPENVIDU_PREVIOUS_FOLDER}/readme.md" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'readme.md'"
printf '\n - readme.md'
cp "${OPENVIDU_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env'
if [ -d "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" ]; then
mv "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous directory 'custom-nginx-vhosts'"
printf '\n - custom-nginx-vhosts'
fi
# Move tmp files to Openvidu
printf '\n => Updating files:'
@ -213,9 +215,6 @@ upgrade_ov() {
mv "${TMP_FOLDER}/openvidu" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'openvidu'"
printf '\n - openvidu'
mv "${TMP_FOLDER}/readme.md" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'readme.md'"
printf '\n - readme.md'
printf "\n => Deleting 'tmp' folder"
rm -rf "${TMP_FOLDER}" || fatal_error "Error deleting 'tmp' folder"
@ -249,7 +248,9 @@ upgrade_ov() {
printf '\n'
printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
printf '\n For more information, check readme.md'
printf '\n For more information, check:'
printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/deployment/deploying-on-premises/"
printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/deployment/upgrading/"
printf '\n'
printf '\n'
}

View File

@ -159,6 +159,16 @@ generate_report() {
printf '\n'
done
printf '\n'
printf "\n ---------------------------------------"
printf "\n KMS"
printf "\n ---------------------------------------"
printf '\n'
kurento_logs
printf "\n ---------------------------------------"
printf '\n'
printf '\n'
printf "\n ======================================="
printf "\n = CONTAINER ENVS VARIABLES ="
printf "\n ======================================="
@ -189,7 +199,8 @@ usage() {
printf "\n\tstart\t\t\tStart all services"
printf "\n\tstop\t\t\tStop all services"
printf "\n\trestart\t\t\tRestart all stoped and running services"
printf "\n\tlogs\t\t\tShow openvidu-server logs"
printf "\n\tlogs [-f]\t\tShow openvidu logs."
printf "\n\tkms-logs [-f]\t\tShow kms logs"
printf "\n\tupgrade\t\t\tUpgrade to the lastest Openvidu version"
printf "\n\tupgrade [version]\tUpgrade to the specific Openvidu version"
printf "\n\tversion\t\t\tShow version of Openvidu Server"
@ -198,6 +209,14 @@ usage() {
printf "\n"
}
kurento_logs() {
if [[ "$1" == "-f" ]]; then
tail -f /opt/openvidu/kurento-logs/*.log
else
cat /opt/openvidu/kurento-logs/*.log
fi
}
case $1 in
start)
docker-compose up -d
@ -215,7 +234,18 @@ case $1 in
;;
logs)
docker-compose logs -f openvidu-server
case $2 in
"-f")
docker-compose logs -f openvidu-server
;;
*)
docker-compose logs openvidu-server
;;
esac
;;
kms-logs)
kurento_logs $2
;;
upgrade)

View File

@ -1,6 +1,6 @@
---
global:
owner: openvidu@gmail.com
owner: info@openvidu.io
qsname: openvidu-ce
regions:
- us-east-1

View File

@ -142,5 +142,5 @@ Resources:
Type: AWS::CloudFormation::WaitCondition
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Timeout: PT20M
Count: 1

View File

@ -104,6 +104,12 @@ Resources:
Tags:
- Key: Name
Value: !Ref AWS::StackName
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp2
DeleteOnTermination: true
VolumeSize: 10
UserData:
"Fn::Base64":
!Sub |
@ -138,5 +144,5 @@ Resources:
Type: AWS::CloudFormation::WaitCondition
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Timeout: PT20M
Count: 1

View File

@ -41,6 +41,22 @@ Parameters:
LetsEncryptEmail:
Description: "If certificate type is 'letsencrypt', this email will be used for Let's Encrypt notifications"
Type: String
Recording:
Description: |
If 'disabled', recordings will not be active.
If 'local' recordings will be saved in EC2 instance locally.
If 's3', recordings will be stored in a S3 bucket"
Type: String
AllowedValues:
- disabled
- local
- s3
Default: local
S3RecordingsBucketName:
Description: "S3 Bucket Name"
Type: String
# OpenVidu Configuration
@ -52,39 +68,51 @@ Parameters:
ConstraintDescription: OpenVidu Pro License is mandatory
OpenViduSecret:
Description: "Secret to connect to this OpenVidu Platform. No whitespaces or quotations allowed"
Description: "Secret to connect to this OpenVidu Platform. Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
Type: String
AllowedPattern: ^((?!")(?! ).)+$
AllowedPattern: ^[a-zA-Z0-9_-]+$
NoEcho: true
ConstraintDescription: OpenVidu Secret is mandatory (no whitespaces or quotations allowed)
ConstraintDescription: "Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
MediaNodesStartNumber:
Description: "How many Media Nodes do you want on startup (EC2 instances will be launched)"
Type: Number
Default: 1
# Kibana configuration
KibanaUser:
Description: "Username for Kibana Dashboard"
# Elasticsearch configuration
ElasticsearchUser:
Description: "Username for Elasticsearch and Kibana"
Type: String
AllowedPattern: ^((?!")(?! ).)+$
ConstraintDescription: Kibana user is mandatory (no whitespaces or quotations allowed)
Default: kibanaadmin
ConstraintDescription: Elasticsearch user is mandatory (no whitespaces or quotations allowed)
Default: elasticadmin
KibanaPassword:
Description: "Password for Kibana Dashboard"
ElasticsearchPassword:
Description: "Password for Elasticsearch and Kibana"
Type: String
AllowedPattern: ^((?!")(?! ).)+$
NoEcho: true
ConstraintDescription: Kibana password is mandatory (no whitespaces or quotations allowed)
ConstraintDescription: Elasticsearch password is mandatory (no whitespaces or quotations allowed)
# Elasticsearch configuration
ElasticsearchUrl:
Description: "If you have an external Elasticsearch service running, put here the url to the service. If empty, an Elasticsearch service will be deployed next to OpenVidu."
Type: String
AllowedPattern: (^(http|https):\/\/.*:[1-9]{1,5}+.*$|^$)
ConstraintDescription: "It is very important to specify the Elasticsearch URL with the port used by this service. For example: https://es-example:443"
KibanaUrl:
Description: "If you have an external Kibana service running, put here the url to the service. If empty, a Kibana service will be deployed next to OpenVidu."
Type: String
AllowedPattern: (^(http|https):\/\/.*:[1-9]{1,5}+.*$|^$)
ConstraintDescription: "It is very important to specify the url with port used by this service. For example: https://kibana-example:443"
# EC2 Instance configuration
AwsInstanceTypeOV:
Description: "Specifies the EC2 instance type for your OpenVidu Server Pro Node"
Type: String
Default: t2.xlarge
Default: c5.xlarge
AllowedValues:
- t2.large
- t2.xlarge
@ -124,7 +152,7 @@ Parameters:
AwsInstanceTypeKMS:
Description: "Specifies the EC2 instance type for your Media Nodes"
Type: String
Default: t2.xlarge
Default: c5.xlarge
AllowedValues:
- t2.large
- t2.xlarge
@ -186,6 +214,7 @@ Parameters:
- false
Default: true
#start_mappings
Mappings:
OVAMIMAP:
eu-west-1:
@ -194,6 +223,7 @@ Mappings:
KMSAMIMAP:
eu-west-1:
AMI: KMS_AMI_ID
#end_mappings
Metadata:
'AWS::CloudFormation::Interface':
@ -214,10 +244,17 @@ Metadata:
- OpenViduSecret
- MediaNodesStartNumber
- Label:
default: Kibana configuration
default: OpenVidu Recording Configuration
Parameters:
- KibanaUser
- KibanaPassword
- Recording
- S3RecordingsBucketName
- Label:
default: Elasticsearch and Kibana configuration
Parameters:
- ElasticsearchUrl
- KibanaUrl
- ElasticsearchUser
- ElasticsearchPassword
- Label:
default: EC2 Instance configuration
Parameters:
@ -248,6 +285,10 @@ Metadata:
default: "URL to the key file (owncert)"
LetsEncryptEmail:
default: "Email for Let's Encrypt (letsencrypt)"
Recording:
default: "OpenVidu Recording"
S3RecordingsBucketName:
default: "S3 Bucket where recordings will be stored"
# OpenVidu configuration
OpenViduLicense:
default: "OpenVidu Pro License key"
@ -256,10 +297,14 @@ Metadata:
OpenViduSecret:
default: "Openvidu Secret"
# Kibana configuration
KibanaUser:
default: "Kibana username"
KibanaPassword:
default: "Kibana password"
ElasticsearchUrl:
default: "Elasticsearch URL"
KibanaUrl:
default: "Kibana URL"
ElasticsearchUser:
default: "Elasticsearch and Kibana username"
ElasticsearchPassword:
default: "Elasticsearch and Kibana password"
# EC2 instance configuration
AwsInstanceTypeOV:
default: "Instance type for Openvidu Server Pro Node"
@ -277,8 +322,22 @@ Metadata:
default: "Deploy OpenVidu Call application"
Conditions:
WhichCertPresent: !Not [ !Equals [!Ref WhichCert, ""] ]
PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ]
WhichCertPresent: !Not [ !Equals [!Ref WhichCert, ''] ]
PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ''] ]
RecordingStorageIsS3: !Equals [ !Ref Recording, 's3' ]
CreateS3Bucket: !And
- !Equals [!Ref Recording, 's3' ]
- !Equals [!Ref S3RecordingsBucketName, '']
Rules:
RecordingValidation:
RuleCondition:
Fn::Or: [ !Equals [!Ref Recording, 'disabled' ], !Equals [!Ref Recording, 'local' ] ]
Assertions:
- AssertDescription: If recording Storage is 'disabled' or 'local', you don't need to specify a S3 bucket.
Assert:
Fn::Equals: [ !Ref S3RecordingsBucketName, "" ]
Resources:
@ -313,6 +372,45 @@ Resources:
- 'route53:ChangeResourceRecordSets'
- 'route53:ListHostedZones'
Resource: '*'
- Fn::If:
# Only apply this policy if S3 is configured
- RecordingStorageIsS3
- Effect: Allow
Action:
- 's3:DeleteObject'
- 's3:GetObject'
- 's3:PutObject'
Resource:
- Fn::If:
# Get bucket name depending if the user defines a bucket name or not
- CreateS3Bucket
### Unique bucket name using Stack ID
- !Join [ "", [ 'arn:aws:s3:::', 'openvidu-recordings-', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]], "/*" ]]
- !Join [ "", [ 'arn:aws:s3:::', !Ref S3RecordingsBucketName, '/*'] ]
- Ref: AWS::NoValue
- Fn::If:
# Only apply this policy if S3 is configured
- RecordingStorageIsS3
- Effect: Allow
Action:
- 's3:ListBucket'
- 's3:GetBucketLocation'
Resource:
- Fn::If:
# Get bucket name depending if the user defines a bucket name or not
- CreateS3Bucket
### Unique bucket name using Stack ID
- !Join [ "", [ 'arn:aws:s3:::', 'openvidu-recordings-', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
- !Join [ "", [ 'arn:aws:s3:::', !Ref S3RecordingsBucketName ] ]
- Ref: AWS::NoValue
- Fn::If:
# Only apply this policy if S3 is configured
- RecordingStorageIsS3
- Effect: Allow
Action:
- s3:ListAllMyBuckets
Resource: 'arn:aws:s3:::'
- Ref: AWS::NoValue
RoleName: !Join [ "-", [ OpenViduManageEC2Role, !Ref 'AWS::StackName', !Ref 'AWS::Region'] ]
OpenviduInstancesProfile:
@ -325,6 +423,21 @@ Resources:
DependsOn:
- OpenViduManageEC2Role
S3bucket:
Type: 'AWS::S3::Bucket'
Properties:
### Unique bucket name using Stack ID
BucketName: !Join ["-" , [ 'openvidu-recordings', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls : true
RestrictPublicBuckets: true
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Condition: CreateS3Bucket
OpenViduServer:
Type: AWS::EC2::Instance
Metadata:
@ -383,9 +496,15 @@ Resources:
sed -i "s/CERTIFICATE_TYPE=selfsigned/CERTIFICATE_TYPE=${WhichCert}/" $WORKINGDIR/.env
sed -i "s/LETSENCRYPT_EMAIL=user@example.com/LETSENCRYPT_EMAIL=${LetsEncryptEmail}/" $WORKINGDIR/.env
# Replace Kibana Conf
sed -i "s/KIBANA_USER=kibanaadmin/KIBANA_USER=${KibanaUser}/" $WORKINGDIR/.env
sed -i "s/KIBANA_PASSWORD=/KIBANA_PASSWORD=${KibanaPassword}/" $WORKINGDIR/.env
# Replace Elastic Search Conf
if [[ ! -z "${ElasticsearchUrl}" ]]; then
sed -i "s,#OPENVIDU_PRO_ELASTICSEARCH_HOST=,OPENVIDU_PRO_ELASTICSEARCH_HOST=${ElasticsearchUrl}," $WORKINGDIR/.env
fi
if [[ ! -z "${KibanaUrl}" ]]; then
sed -i "s,#OPENVIDU_PRO_KIBANA_HOST=,OPENVIDU_PRO_KIBANA_HOST=${KibanaUrl}," $WORKINGDIR/.env
fi
sed -i "s/ELASTICSEARCH_USERNAME=elasticadmin/ELASTICSEARCH_USERNAME=${ElasticsearchUser}/" $WORKINGDIR/.env
sed -i "s/ELASTICSEARCH_PASSWORD=/ELASTICSEARCH_PASSWORD=${ElasticsearchPassword}/" $WORKINGDIR/.env
# Replace vars AWS
sed -i "s/#AWS_DEFAULT_REGION=/AWS_DEFAULT_REGION=${AWS::Region}/" $WORKINGDIR/.env
@ -395,6 +514,8 @@ Resources:
sed -i "s/#AWS_SUBNET_ID=/AWS_SUBNET_ID=${OpenViduSubnet}/" $WORKINGDIR/.env
sed -i "s/#AWS_STACK_ID=/AWS_STACK_ID=$(echo ${AWS::StackId} | sed 's#/#\\/#g')/" $WORKINGDIR/.env
sed -i "s/#AWS_STACK_NAME=/AWS_STACK_NAME=${AWS::StackName}/" $WORKINGDIR/.env
sed -i "s/#AWS_CLI_DOCKER_TAG=/AWS_CLI_DOCKER_TAG=AWS_DOCKER_TAG/" $WORKINGDIR/.env
sed -i "s/#AWS_VOLUME_SIZE=/AWS_VOLUME_SIZE=50/" $WORKINGDIR/.env
# Get security group id of kms and use it as env variable
SECGRPIDKMS=$(/usr/local/bin/getSecurityGroupKms.sh)
@ -405,7 +526,20 @@ Resources:
sed -i "s/WITH_APP=true/WITH_APP=false/" $WORKINGDIR/docker-compose.yml
rm $WORKINGDIR/docker-compose.override.yml
fi
# Recording Configuration
if [ "${Recording}" != "disabled" ]; then
sed -i "s/OPENVIDU_RECORDING=false/OPENVIDU_RECORDING=true/" $WORKINGDIR/.env
sed -i "s/#OPENVIDU_PRO_RECORDING_STORAGE=/OPENVIDU_PRO_RECORDING_STORAGE=${Recording}/" $WORKINGDIR/.env
if [ ! -z "${S3RecordingsBucketName}" ]; then
sed -i "s/#OPENVIDU_PRO_AWS_S3_BUCKET=/OPENVIDU_PRO_AWS_S3_BUCKET=${S3RecordingsBucketName}/" $WORKINGDIR/.env
elif [ "${Recording}" == "s3" ]; then
sed -i "s/#OPENVIDU_PRO_AWS_S3_BUCKET=/OPENVIDU_PRO_AWS_S3_BUCKET=${s3BucketName}/" $WORKINGDIR/.env
fi
fi
- kmsAmi: !FindInMap [KMSAMIMAP, !Ref 'AWS::Region', AMI]
### Unique bucket name using Stack ID
s3BucketName: !Join ["" , [ 'openvidu-recordings-', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
mode: "000755"
owner: "root"
group: "root"
@ -421,7 +555,7 @@ Resources:
'/usr/local/bin/getSecurityGroupKms.sh':
content: !Sub |
#!/bin/bash -x
docker run --rm amazon/aws-cli:2.0.7 ec2 describe-security-groups \
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 describe-security-groups \
--region ${AWS::Region} \
--output text \
--filters "Name=tag:aws:cloudformation:logical-id,Values=KMSSecurityGroup" \
@ -433,7 +567,7 @@ Resources:
'/usr/local/bin/getSecurityGroupOpenVidu.sh':
content: !Sub |
#!/bin/bash -x
docker run --rm amazon/aws-cli:2.0.7 ec2 describe-security-groups \
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 describe-security-groups \
--region ${AWS::Region} \
--output text \
--filters "Name=tag:aws:cloudformation:logical-id,Values=OpenViduSecurityGroup" \
@ -445,7 +579,7 @@ Resources:
'/usr/local/bin/getCidrBlocKSubnet.sh':
content: !Sub |
#!/bin/bash -x
docker run --rm amazon/aws-cli:2.0.7 ec2 describe-subnets \
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 describe-subnets \
--region ${AWS::Region} \
--output text \
--filters "Name=subnet-id,Values=${OpenViduSubnet}" \
@ -454,25 +588,25 @@ Resources:
owner: "root"
group: "root"
'/usr/local/bin/create_security_group_rules.sh':
content: !Sub |
content: |
#!/bin/bash -x
SECGRPIDKMS=$(/usr/local/bin/getSecurityGroupKms.sh)
SECGRPIDOV=$(/usr/local/bin/getSecurityGroupOpenVidu.sh)
SUBNET_CIDR=$(/usr/local/bin/getCidrBlocKSubnet.sh)
# Create Security group rules OpenVidu
docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 5044 --cidr $SUBNET_CIDR
docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 9200 --cidr $SUBNET_CIDR
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 5044 --cidr $SUBNET_CIDR
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 9200 --cidr $SUBNET_CIDR
# Create security group rules for KMS
docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 8888 --cidr $SUBNET_CIDR
docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 3000 --cidr $SUBNET_CIDR
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 8888 --cidr $SUBNET_CIDR
docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 3000 --cidr $SUBNET_CIDR
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/restartPRO.sh':
content: !Sub |
content: |
#!/bin/bash -x
WORKINGDIR=/opt/openvidu
@ -484,7 +618,8 @@ Resources:
# Restart all services
pushd /opt/openvidu
docker-compose up -d
export FOLLOW_OPENVIDU_LOGS=false
./openvidu start
popd
mode: "000755"
owner: "root"
@ -509,6 +644,8 @@ Resources:
cfn-init --region ${AWS::Region} --stack ${AWS::StackId} --resource OpenViduServer
export HOME="/root"
# Replace .env variables
/usr/local/bin/feedGroupVars.sh || { echo "[Openvidu] Parameters incorrect/insufficient"; exit 1; }
@ -525,7 +662,8 @@ Resources:
# Start openvidu application
pushd /opt/openvidu
docker-compose up -d
export FOLLOW_OPENVIDU_LOGS=false
./openvidu start
popd
# Wait for the app
@ -537,10 +675,9 @@ Resources:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: io1
Iops: 200
VolumeType: gp2
DeleteOnTermination: true
VolumeSize: 50
VolumeSize: 200
KMSSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'

View File

@ -1,18 +1,14 @@
#!/bin/bash -x
set -eu -o pipefail
CF_OVP_TARGET=${CF_OVP_TARGET:-nomarket}
CF_RELEASE=${CF_RELEASE:-false}
AWS_KEY_NAME=${AWS_KEY_NAME:-}
if [ ${CF_OVP_TARGET} == "market" ]; then
export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
export AWS_DEFAULT_REGION=us-east-1
else
export AWS_DEFAULT_REGION=eu-west-1
if [[ $CF_RELEASE == "true" ]]; then
git checkout v$OPENVIDU_PRO_VERSION
fi
if [ "${OPENVIDU_PRO_IS_SNAPSHOT}" == "true" ]; then
OPENVIDU_PRO_VERSION=${OPENVIDU_PRO_VERSION}-SNAPSHOT
fi
export AWS_DEFAULT_REGION=eu-west-1
DATESTAMP=$(date +%s)
TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
@ -23,10 +19,9 @@ TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
getUbuntuAmiId() {
local AMI_ID=$(
aws --region ${1} ec2 describe-images \
--filters Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64* \
--query 'Images[*].[ImageId,CreationDate]' \
--output text \
| sort -k2 -r | head -n1 | cut -d$'\t' -f1
--filters "Name=name,Values=*ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*" \
--query "sort_by(Images, &CreationDate)" \
| jq -r 'del(.[] | select(.ImageOwnerAlias != null)) | .[-1].ImageId'
)
echo $AMI_ID
}
@ -61,12 +56,22 @@ sed -i "s/AMIUSEAST1/${AMIUSEAST1}/g" cfn-mkt-kms-ami.yaml
## KMS AMI
# Copy template to S3
if [ ${CF_OVP_TARGET} == "market" ]; then
aws s3 cp cfn-mkt-kms-ami.yaml s3://naeva-openvidu-pro
TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/naeva-openvidu-pro/cfn-mkt-kms-ami.yaml
else
aws s3 cp cfn-mkt-kms-ami.yaml s3://aws.openvidu.io
TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-kms-ami.yaml
aws s3 cp cfn-mkt-kms-ami.yaml s3://aws.openvidu.io
TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-kms-ami.yaml
# Update installation script
if [[ ${UPDATE_INSTALLATION_SCRIPT} == "true" ]]; then
# Avoid overriding existing versions
# Only master and non existing versions can be overriden
if [[ ${OPENVIDU_PRO_VERSION} != "master" ]]; then
INSTALL_SCRIPT_EXISTS=true
aws s3api head-object --bucket aws.openvidu.io --key install_media_node_$OPENVIDU_PRO_VERSION.sh || INSTALL_SCRIPT_EXISTS=false
if [[ ${INSTALL_SCRIPT_EXISTS} == "true" ]]; then
echo "Aborting updating s3://aws.openvidu.io/install_media_node_${OPENVIDU_PRO_VERSION}.sh. File actually exists."
exit 1
fi
fi
aws s3 cp ../docker-compose/media-node/install_media_node.sh s3://aws.openvidu.io/install_media_node_$OPENVIDU_PRO_VERSION.sh --acl public-read
fi
aws cloudformation create-stack \
@ -94,12 +99,22 @@ aws cloudformation delete-stack --stack-name kms-${DATESTAMP}
## OpenVidu AMI
# Copy template to S3
if [ ${CF_OVP_TARGET} == "market" ]; then
aws s3 cp cfn-mkt-ov-ami.yaml s3://naeva-openvidu-pro
TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/naeva-openvidu-pro/cfn-mkt-ov-ami.yaml
else
aws s3 cp cfn-mkt-ov-ami.yaml s3://aws.openvidu.io
TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-ov-ami.yaml
# Update installation script
if [[ ${UPDATE_INSTALLATION_SCRIPT} == "true" ]]; then
# Avoid overriding existing versions
# Only master and non existing versions can be overriden
if [[ ${OPENVIDU_PRO_VERSION} != "master" ]]; then
INSTALL_SCRIPT_EXISTS=true
aws s3api head-object --bucket aws.openvidu.io --key install_openvidu_pro_$OPENVIDU_PRO_VERSION.sh || INSTALL_SCRIPT_EXISTS=false
if [[ ${INSTALL_SCRIPT_EXISTS} == "true" ]]; then
echo "Aborting updating s3://aws.openvidu.io/install_openvidu_pro_${OPENVIDU_PRO_VERSION}.sh. File actually exists."
exit 1
fi
fi
aws s3 cp ../docker-compose/openvidu-server-pro/install_openvidu_pro.sh s3://aws.openvidu.io/install_openvidu_pro_$OPENVIDU_PRO_VERSION.sh --acl public-read
fi
aws cloudformation create-stack \
@ -125,15 +140,35 @@ echo "Cleaning up"
aws cloudformation delete-stack --stack-name openvidu-${DATESTAMP}
# Wait for the instance
aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID}
# Unfortunately, aws cli does not have a way to increase timeout
WAIT_RETRIES=0
WAIT_MAX_RETRIES=3
until [ "${WAIT_RETRIES}" -ge "${WAIT_MAX_RETRIES}" ]
do
aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID} && break
WAIT_RETRIES=$((WAIT_RETRIES+1))
sleep 5
done
# Updating the template
if [ ${CF_OVP_TARGET} == "market" ]; then
sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" cfn-mkt-openvidu-server-pro.yaml.template > cfn-mkt-openvidu-server-pro-${OPENVIDU_PRO_VERSION}.yaml
sed -i "s/KMS_AMI_ID/${KMS_RAW_AMI_ID}/g" cfn-mkt-openvidu-server-pro-${OPENVIDU_PRO_VERSION}.yaml
else
sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" cfn-openvidu-server-pro-no-market.yaml.template > cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
sed -i "s/KMS_AMI_ID/${KMS_RAW_AMI_ID}/g" cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" cfn-openvidu-server-pro-no-market.yaml.template > cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
sed -i "s/KMS_AMI_ID/${KMS_RAW_AMI_ID}/g" cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
sed -i "s/AWS_DOCKER_TAG/${AWS_DOCKER_TAG}/g" cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
# Update CF template
if [[ ${UPDATE_CF} == "true" ]]; then
# Avoid overriding existing versions
# Only master and non existing versions can be overriden
if [[ ${OPENVIDU_PRO_VERSION} != "master" ]]; then
CF_EXIST=true
aws s3api head-object --bucket aws.openvidu.io --key CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml || CF_EXIST=false
if [[ ${CF_EXIST} == "true" ]]; then
echo "Aborting updating s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml. File actually exists."
exit 1
fi
fi
aws s3 cp cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml --acl public-read
fi
rm $TEMPJSON

View File

@ -1,47 +1,24 @@
#!/bin/bash -x
set -eu -o pipefail
# Process AMI_LIST
AMI_LIST=($(echo "$AMI_LIST" | tr ',' '\n'))
# Remove the list of AMIs in each region
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
LIST="us-east-2:ami-0b779580e2c11e904
us-west-1:ami-085b7176f53c6d7fe
us-west-2:ami-029d0ac01cf0f56be
ap-south-1:ami-044a9335de8413f90
ap-northeast-2:ami-031f6637449d2821d
ap-southeast-1:ami-0aba433c88526cc8a
ap-southeast-2:ami-0ee526f6103ac2bd9
ap-northeast-1:ami-03b3cc03809d43b36
ca-central-1:ami-071388f538500db04
eu-central-1:ami-080a9cbd1d3e64583
eu-west-1:ami-05e6bc185f28b6338
eu-west-2:ami-0f06e2c003eef90f1
eu-west-3:ami-0fd9b5eaf08fc0936
eu-north-1:ami-0e7717a400ba2f1c1
sa-east-1:ami-0cd51a71e9791197a
us-east-2:ami-0064508c09a32a93f
us-west-1:ami-088d1a7099fa57038
us-west-2:ami-080d4d462cff92974
ap-south-1:ami-00a60b079166e7dd4
ap-northeast-2:ami-0f4edc3faf639e044
ap-southeast-1:ami-0235dbfa3662608a0
ap-southeast-2:ami-0f5f46178512e6e07
ap-northeast-1:ami-047b086aa0745ce19
ca-central-1:ami-0777b151c44ef9944
eu-central-1:ami-00dd31f7b896f233f
eu-west-1:ami-0fb9f924ede905546
eu-west-2:ami-0defcea3b8c198e1e
eu-west-3:ami-0c56da0b482bdf48e
eu-north-1:ami-054fc49723d3d516a
sa-east-1:ami-0dca8c31325d33c72"
for line in ${LIST}
for line in "${AMI_LIST[@]}"
do
REGION=$(echo ${line} | cut -d":" -f1)
AMI_ID=$(echo ${line} | cut -d":" -f2)
export AWS_DEFAULT_REGION=${REGION}
aws ec2 deregister-image --image-id $AMI_ID
sleep 1
REGION=$(echo "${line}" | cut -d":" -f1)
AMI_ID=$(echo "${line}" | cut -d":" -f2)
export AWS_DEFAULT_REGION=${REGION}
mapfile -t SNAPSHOTS < <(aws ec2 describe-images --image-ids "$AMI_ID" --output text --query 'Images[*].BlockDeviceMappings[*].Ebs.SnapshotId')
echo "Deregistering $AMI_ID"
aws ec2 deregister-image --image-id "${AMI_ID}"
sleep 1
for snapshot in "${SNAPSHOTS[@]}";
do
echo "Deleting Snapshot $snapshot from $AMI_ID"
aws ec2 delete-snapshot --snapshot-id "${snapshot}"
sleep 1
done
done

View File

@ -1,4 +1,4 @@
#!/bin/bash -x
#!/bin/bash
set -eu -o pipefail
# Replicate AMIs in all regions
@ -10,12 +10,11 @@ set -eu -o pipefail
#
# OV_AMI_NAME OpenVidu AMI Name
# OV_AMI_ID OpenVidu AMI ID
#
# UPDATE_CF Boolean, true if you want to update CF template by OPENVIDU_PRO_VERSION
# OPENVIDU_VERSION OpenVidu Version of the CF you want to update. It will update CF-OpenVidu-Pro-OPENVIDU_PRO_VERSION
export AWS_DEFAULT_REGION=eu-west-1
if [ ${CF_OVP_TARGET} == "market" ]; then
export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
fi
echo "Making original AMIs public"
@ -96,26 +95,82 @@ do
ITER=$(expr $ITER + 1)
done
echo
echo "OpenVidu Server Pro Node AMI IDs"
# Print and generate replicated AMIS
REPLICATED_AMIS_FILE="replicated_amis.yaml"
echo "OV AMIs and KMS AMIs replication:"
{
echo "#start_mappings"
echo "Mappings:"
echo " OVAMIMAP:"
ITER=0
for i in "${OPENVIDU_SERVER_PRO_AMI_IDS[@]}"
do
AMI_ID=${OPENVIDU_SERVER_PRO_AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
echo " ${REGION}:"
echo " AMI: ${AMI_ID}"
ITER=$(expr $ITER + 1)
done
echo ""
echo " KMSAMIMAP:"
ITER=0
for i in "${MEDIA_NODE_AMI_IDS[@]}"
do
AMI_ID=${MEDIA_NODE_AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
echo " ${REGION}:"
echo " AMI: ${AMI_ID}"
ITER=$(expr $ITER + 1)
done
echo "#end_mappings"
echo ""
} > "${REPLICATED_AMIS_FILE}" 2>&1
# Print replicated AMIs
cat "${REPLICATED_AMIS_FILE}"
if [[ ${UPDATE_CF} == "true" ]]; then
if [[ ! -z ${OPENVIDU_PRO_VERSION} ]]; then
# Download s3 file
aws s3 cp s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml
sed -e "/^#end_mappings/r ${REPLICATED_AMIS_FILE}" -e '/^#start_mappings/,/^#end_mappings/d' -i CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml
aws s3 cp CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml --acl public-read
fi
fi
# Print AMI_LIST for delete_amis.sh
AMI_LIST=""
ITER=0
for i in "${OPENVIDU_SERVER_PRO_AMI_IDS[@]}"
do
AMI_ID=${OPENVIDU_SERVER_PRO_AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
echo " ${REGION}:"
echo " AMI: ${AMI_ID}"
if [[ ${ITER} -eq 0 ]]; then
AMI_LIST="${REGION}:${AMI_ID}"
else
AMI_LIST="${AMI_LIST},${REGION}:${AMI_ID}"
fi
ITER=$(expr $ITER + 1)
done
echo "AMI_LIST_OV: ${AMI_LIST}"
echo
echo "Media Node AMI IDs"
# Print AMI_LIST for delete_amis.sh
AMI_LIST=""
ITER=0
for i in "${MEDIA_NODE_AMI_IDS[@]}"
do
AMI_ID=${MEDIA_NODE_AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
echo " ${REGION}:"
echo " AMI: ${AMI_ID}"
if [[ ${ITER} -eq 0 ]]; then
AMI_LIST="${REGION}:${AMI_ID}"
else
AMI_LIST="${AMI_LIST},${REGION}:${AMI_ID}"
fi
ITER=$(expr $ITER + 1)
done
done
echo "AMI_LIST_KMS: ${AMI_LIST}"
# Cleaning the house
rm "${REPLICATED_AMIS_FILE}"
rm CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml

View File

@ -0,0 +1,352 @@
# OpenVidu configuration
# ----------------------
# Documentation: https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/
# NOTE: This file doesn't need to quote assignment values, like most shells do.
# All values are stored as-is, even if they contain spaces, so don't quote them.
# Domain name. If you do not have one, the public IP of the machine.
# For example: 198.51.100.1, or openvidu.example.com
DOMAIN_OR_PUBLIC_IP=
# OpenVidu PRO License
OPENVIDU_PRO_LICENSE=
# OpenVidu SECRET used for apps to connect to OpenVidu server and users to access to OpenVidu Dashboard
OPENVIDU_SECRET=
# Certificate type:
# - selfsigned: Self signed certificate. Not recommended for production use.
# Users will see an ERROR when connected to web page.
# - owncert: Valid certificate purchased in a Internet services company.
# Please put the certificates files inside folder ./owncert
# with names certificate.key and certificate.cert
# - letsencrypt: Generate a new certificate using letsencrypt. Please set the
# required contact email for Let's Encrypt in LETSENCRYPT_EMAIL
# variable.
CERTIFICATE_TYPE=selfsigned
# If CERTIFICATE_TYPE=letsencrypt, you need to configure a valid email for notifications
LETSENCRYPT_EMAIL=user@example.com
# Proxy configuration
# If you want to change the ports on which openvidu listens, uncomment the following lines
# Allows any request to http://DOMAIN_OR_PUBLIC_IP:HTTP_PORT/ to be automatically
# redirected to https://DOMAIN_OR_PUBLIC_IP:HTTPS_PORT/.
# WARNING: the default port 80 cannot be changed during the first boot
# if you have chosen to deploy with the option CERTIFICATE_TYPE=letsencrypt
# HTTP_PORT=80
# Changes the port of all services exposed by OpenVidu.
# SDKs, REST clients and browsers will have to connect to this port
# HTTPS_PORT=443
# Old paths are considered now deprecated, but still supported by default.
# OpenVidu Server will log a WARN message every time a deprecated path is called, indicating
# the new path that should be used instead. You can set property SUPPORT_DEPRECATED_API=false
# to stop allowing the use of old paths.
# Default value is true
# SUPPORT_DEPRECATED_API=true
# If true request to with www will be redirected to non-www requests
# Default value is false
# REDIRECT_WWW=false
# How many workers to configure in nginx proxy.
# The more workers, the more requests will be handled
# Default value is 10240
# WORKER_CONNECTIONS=10240
# Access restrictions
# In this section you will be able to restrict the IPs from which you can access to
# Openvidu API and the Administration Panel
# WARNING! If you touch this configuration you can lose access to the platform from some IPs.
# Use it carefully.
# This section limits access to the /dashboard (OpenVidu CE) and /inspector (OpenVidu Pro) pages.
# The form for a single IP or an IP range is:
# ALLOWED_ACCESS_TO_DASHBOARD=198.51.100.1 and ALLOWED_ACCESS_TO_DASHBOARD=198.51.100.0/24
# To limit multiple IPs or IP ranges, separate by commas like this:
# ALLOWED_ACCESS_TO_DASHBOARD=198.51.100.1, 198.51.100.0/24
# ALLOWED_ACCESS_TO_DASHBOARD=
# This section limits access to the Openvidu REST API.
# The form for a single IP or an IP range is:
# ALLOWED_ACCESS_TO_RESTAPI=198.51.100.1 and ALLOWED_ACCESS_TO_RESTAPI=198.51.100.0/24
# To limit multiple IPs or or IP ranges, separate by commas like this:
# ALLOWED_ACCESS_TO_RESTAPI=198.51.100.1, 198.51.100.0/24
# ALLOWED_ACCESS_TO_RESTAPI=
# Mode of cluster management. Can be auto (OpenVidu manages Media Nodes on its own.
# Parameter KMS_URIS is ignored) or manual (user must manage Media Nodes. Parameter
# KMS_URIS is used: if any uri is provided it must be valid)
OPENVIDU_PRO_CLUSTER_MODE=manual
# Which environment are you using
# Possibles values: aws, on_premise
OPENVIDU_PRO_CLUSTER_ENVIRONMENT=on_premise
# Unique identifier of your cluster. Each OpenVidu Server Pro instance corresponds to one cluster.
# You can launch as many clusters as you want with your license key.
# Cluster ID will always be stored to disk so restarting OpenVidu Server Pro will keep the same previous cluster ID
# if this configuration parameter is not given a distinct value.
# OPENVIDU_PRO_CLUSTER_ID=
# The desired number of Media Nodes on startup. First the autodiscovery process is performed.
# If there are too many Media Nodes after that, they will be dropped until this number is reached.
# If there are not enough, more will be launched.
# This only takes place if OPENVIDU_PRO_CLUSTER_MODE is set to auto
# If set to zero no media servers will be lauched.
# Type: number >= 0
#OPENVIDU_PRO_CLUSTER_MEDIA_NODES=
# How often each running Media Node will send OpenVidu Server Pro Node load metrics, in seconds.
# This property is only used when OPENVIDU_PRO_CLUSTER_LOAD_STRATEGY is 'cpu'. Other load strategies
# gather information synchronously when required
# Type: number >= 0
# OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL=
# Whether to enable or disable autoscaling. With autoscaling the number of Media Nodes will
# be automatically adjusted according to existing load
# Values: true | false
# OPENVIDU_PRO_CLUSTER_AUTOSCALING=false
# How often the autoscaling algorithm runs, in seconds
# Type number >= 0
# OPENVIDU_PRO_CLUSTER_AUTOSCALING_INTERVAL=
# If autoscaling is enabled, the upper limit of Media Nodes that can be reached.
# Even when the average load exceeds the threshold, no more Media Nodes will be added to cluster
# Type number >= 0
# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MAX_NODES=
# If autoscaling is enabled, the lower limit of Media Nodes that can be reached.
# Even when the average load is inferior to the threshold, no more Media Nodes will
# be removed from the cluster
# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MIN_NODES=
# If autoscaling is enabled, the upper average load threshold that will trigger the addition
# of a new Media Node.
# Percentage value (0 min, 100 max)
# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MAX_LOAD=
# If autoscaling is enabled, the lower average load threshold that will trigger the removal
# of an existing Media Node.
# Percentage value (0 min, 100 max)
# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MIN_LOAD=
# What parameter should be used to distribute the creation of new sessions
# (and therefore distribution of load) among all available Media Nodes
OPENVIDU_PRO_CLUSTER_LOAD_STRATEGY=streams
# Whether to enable or disable Network Quality API. You can monitor and
# warn users about the quality of their networks with this feature
# OPENVIDU_PRO_NETWORK_QUALITY=false
# If OPENVIDU_PRO_NETWORK_QUALITY=true, how often the network quality
# algorithm will be invoked for each user, in seconds
# OPENVIDU_PRO_NETWORK_QUALITY_INTERVAL=5
# Max days until delete indexes in state of rollover on Elasticsearch
# Type number >= 0
# Default Value is 15
# OPENVIDU_PRO_ELASTICSEARCH_MAX_DAYS_DELETE=
# If you have an external Elasticsearch and Kibana already running, put here the url to elasticsearch and kibana services.
# It is very important that both url have the port specified in the url.
# If you want to use the deployed Elasticsearch and Kibana locally, keep these variables commented.
#OPENVIDU_PRO_ELASTICSEARCH_HOST=
#OPENVIDU_PRO_KIBANA_HOST=
# Where to store recording files. Can be 'local' (local storage) or 's3' (AWS bucket).
# You will need to define a OPENVIDU_PRO_AWS_S3_BUCKET if you use it.
#OPENVIDU_PRO_RECORDING_STORAGE=
# S3 Bucket where to store recording files. May include paths to allow navigating
# folder structures inside the bucket. This property is only taken into account
# if OPENVIDU_PRO_RECORDING_STORAGE=s3
#OPENVIDU_PRO_AWS_S3_BUCKET=
# If OPENVIDU_PRO_RECORDING_STORAGE=s3, the collection of HTTP header values that the internal AWS client will use during
# the upload process. The property is a key-value map of strings, following the format of a JSON object. For example, for applying
# server-side encryption with AES-256, this header is mandatory: {"x-amz-server-side-encryption":"AES256"}.
# The list of available headers can be found here: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/Headers.html
# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
#OPENVIDU_PRO_AWS_S3_HEADERS=
# If you're instance has a role which has access to read
# and write into the s3 bucket, you don't need this parameter
# OPENVIDU_PRO_AWS_ACCESS_KEY=
# AWS credentials secret key from OPENVIDU_PRO_AWS_ACCESS_KEY. This property is only
# taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
# If you're instance has a role which has access to read
# and write into the s3 bucket, you don't need this parameter
# OPENVIDU_PRO_AWS_SECRET_KEY=
# AWS region in which the S3 bucket is located (e.g. eu-west-1). If not provided,
# the region will try to be discovered automatically, although this is not always possible.
# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
# OPENVIDU_PRO_AWS_REGION=
# Whether to enable recording module or not
OPENVIDU_RECORDING=false
# Use recording module with debug mode.
OPENVIDU_RECORDING_DEBUG=false
# Openvidu Folder Record used for save the openvidu recording videos. Change it
# with the folder you want to use from your host.
OPENVIDU_RECORDING_PATH=/opt/openvidu/recordings
# System path where OpenVidu Server should look for custom recording layouts
OPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/custom-layout
# if true any client can connect to
# https://OPENVIDU_SERVER_IP:OPENVIDU_PORT/recordings/any_session_file.mp4
# and access any recorded video file. If false this path will be secured with
# OPENVIDU_SECRET param just as OpenVidu Server dashboard at
# https://OPENVIDU_SERVER_IP:OPENVIDU_PORT
# Values: true | false
OPENVIDU_RECORDING_PUBLIC_ACCESS=false
# Which users should receive the recording events in the client side
# (recordingStarted, recordingStopped). Can be all (every user connected to
# the session), publisher_moderator (users with role 'PUBLISHER' or
# 'MODERATOR'), moderator (only users with role 'MODERATOR') or none
# (no user will receive these events)
OPENVIDU_RECORDING_NOTIFICATION=publisher_moderator
# Timeout in seconds for recordings to automatically stop (and the session involved to be closed)
# when conditions are met: a session recording is started but no user is publishing to it or a session
# is being recorded and last user disconnects. If a user publishes within the timeout in either case,
# the automatic stop of the recording is cancelled
# 0 means no timeout
OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT=120
# Maximum video bandwidth sent from clients to OpenVidu Server, in kbps.
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000
# Minimum video bandwidth sent from clients to OpenVidu Server, in kbps.
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300
# Maximum video bandwidth sent from OpenVidu Server to clients, in kbps.
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
# Minimum video bandwidth sent from OpenVidu Server to clients, in kbps.
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
# All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true
# when a codec can not be forced, transcoding will be allowed
# Default value is VP8
# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
# Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied
# Default value is false
# OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
# true to enable OpenVidu Webhook service. false' otherwise
# Values: true | false
OPENVIDU_WEBHOOK=false
# HTTP endpoint where OpenVidu Server will send Webhook HTTP POST messages
# Must be a valid URL: http(s)://ENDPOINT
#OPENVIDU_WEBHOOK_ENDPOINT=
# List of headers that OpenVidu Webhook service will attach to HTTP POST messages
#OPENVIDU_WEBHOOK_HEADERS=
# List of events that will be sent by OpenVidu Webhook service
# Default value is all available events
OPENVIDU_WEBHOOK_EVENTS=[sessionCreated,sessionDestroyed,participantJoined,participantLeft,webrtcConnectionCreated,webrtcConnectionDestroyed,recordingStatusChanged,filterEventDispatched,mediaNodeStatusChanged]
# How often the garbage collector of non active sessions runs.
# This helps cleaning up sessions that have been initialized through
# REST API (and maybe tokens have been created for them) but have had no users connected.
# Default to 900s (15 mins). 0 to disable non active sessions garbage collector
OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
# Minimum time in seconds that a non active session must have been in existence
# for the garbage collector of non active sessions to remove it. Default to 3600s (1 hour).
# If non active sessions garbage collector is disabled
# (property 'OPENVIDU_SESSIONS_GARBAGE_INTERVAL' to 0) this property is ignored
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
# Call Detail Record enabled
# Whether to enable Call Detail Record or not
# Values: true | false
OPENVIDU_CDR=false
# Path where the cdr log files are hosted
OPENVIDU_CDR_PATH=/opt/openvidu/cdr
# Openvidu Server Level logs
# --------------------------
# Uncomment the next line and define this variable to change
# the verbosity level of the logs of Openvidu Service
# RECOMENDED VALUES: INFO for normal logs DEBUG for more verbose logs
# OV_CE_DEBUG_LEVEL=INFO
# OpenVidu Java Options
# --------------------------
# Uncomment the next line and define this to add options to java command
# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
# JAVA_OPTIONS=-Xms2048m -Xmx4096m
# ElasticSearch Java Options
# --------------------------
# Uncomment the next line and define this to add options to java command of Elasticsearch
# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
# By default ElasticSearch is configured to use "-Xms2g -Xmx2g" as Java Min and Max memory heap allocation
# ES_JAVA_OPTS=-Xms2048m -Xmx4096m
# Kibana And ElasticSearch Credentials Configuration
# --------------------------
# Kibana And ElasticSearch Basic Auth configuration (Credentials)
# This credentials will aso be valid for Kibana dashboard
ELASTICSEARCH_USERNAME=elasticadmin
ELASTICSEARCH_PASSWORD=
# Media Node Configuration
# --------------------------
# You can add any KMS environment variable as described in the
# documentation of the docker image: https://hub.docker.com/r/kurento/kurento-media-server
# If you want to add an environment variable to KMS, you must add a variable using this prefix: 'KMS_DOCKER_ENV_',
# followed by the environment variable you want to setup.
# For example if you want to setup KMS_MIN_PORT to 50000, it would be KMS_DOCKER_ENV_KMS_MIN_PORT=50000
# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server
# Uncomment the next line and define this variable with KMS image that you want use
# By default, KMS_IMAGE is defined in media nodes and it does not need to be specified unless
# you want to use a specific version of KMS
# KMS_IMAGE=kurento/kurento-media-server:6.15.0
# Uncomment the next line and define this variable to change
# the verbosity level of the logs of KMS
# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
# KMS_DOCKER_ENV_GST_DEBUG=
# Cloudformation configuration
# --------------------------
# If you're working outside AWS ignore this section
#AWS_DEFAULT_REGION=
#AWS_IMAGE_ID=
#AWS_INSTANCE_TYPE=
#AWS_KEY_NAME=
#AWS_SUBNET_ID=
#AWS_SECURITY_GROUP=
#AWS_STACK_ID=
#AWS_STACK_NAME=
#AWS_CLI_DOCKER_TAG=
#AWS_VOLUME_SIZE=
# AWS ASG configuration
# --------------------------
RM_REDIS_IP=
RM_REDIS_PORT=
RM_SQS_QUEUE=

View File

@ -0,0 +1,48 @@
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- add_docker_metadata:
host: "unix:///var/run/docker.sock"
- add_host_metadata:
netinfo.enabled: true
- decode_json_fields:
fields: ["message"]
target: "json"
overwrite_keys: true
- drop_event:
when.or:
- contains:
container.image.name: openvidu/openvidu-coturn
- contains:
container.image.name: docker.elastic.co/elasticsearch/elasticsearch
- contains:
container.image.name: docker.elastic.co/kibana/kibana
- contains:
container.image.name: docker.elastic.co/beats/filebeat-oss
- contains:
container.image.name: docker.elastic.co/beats/metricbeat-oss
- add_fields:
fields:
cluster-id: ${OPENVIDU_PRO_CLUSTER_ID:undefined}
output:
elasticsearch:
hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]
indices:
- index: "filebeat-redis-%{+yyyy.MM.dd}"
when.or:
- contains:
container.image.name: openvidu/openvidu-redis
- index: "filebeat-nginx-%{+yyyy.MM.dd}"
when.or:
- contains:
container.image.name: openvidu/openvidu-proxy
logging.json: true
logging.metrics.enabled: false
setup.ilm.enabled: false

View File

@ -0,0 +1,10 @@
metricbeat.modules:
- module: nginx
metricsets: ["stubstatus"]
enabled: true
period: 10s
hosts: ["http://127.0.0.1"]
server_status_path: "nginx_status"
output:
elasticsearch:
hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]

View File

@ -0,0 +1,18 @@
#!/bin/bash
set -eu -o pipefail
# Set debug mode
DEBUG=${DEBUG:-false}
[ "$DEBUG" == "true" ] && set -x
OUTPUT=$(mktemp -t openvidu-autodiscover-XXX --suffix .json)
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 describe-instances \
--output text \
--filters "Name=instance-state-name,Values=running" \
"Name=tag:ov-cluster-member,Values=kms" \
"Name=tag:ov-stack-name,Values=${AWS_STACK_NAME}" \
"Name=tag:ov-stack-region,Values=${AWS_DEFAULT_REGION}" \
--query 'Reservations[*].Instances[*].{id:InstanceId,ip:PrivateIpAddress}' > ${OUTPUT}
cat ${OUTPUT} | jq --raw-input --slurp 'split("\n") | map(split("\t")) | .[0:-1] | map( { "id": .[0], "ip": .[1] } )'

View File

@ -0,0 +1,11 @@
#!/bin/bash
set -e -o pipefail
# Set debug mode
DEBUG=${DEBUG:-false}
[ "$DEBUG" == "true" ] && set -x
ID=$1
[ -z "${ID}" ] && { echo "Must provide instance ID"; exit 1; }
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 terminate-instances --instance-ids ${ID} --output json

View File

@ -0,0 +1,69 @@
#!/bin/bash
set -e -o pipefail
# Set debug mode
DEBUG=${DEBUG:-false}
[ "$DEBUG" == "true" ] && set -x
TMPFILE=$(mktemp -t openvidu-userdata-XXX --suffix .txt)
OUTPUT=$(mktemp -t openvidu-launch-kms-XXX --suffix .json)
ERROUTPUT=$(mktemp -t openvidu-launch-kms-XXX --suffix .err)
trap exit_on_error ERR
exit_on_error () {
ERROR_TYPE=$(cat ${ERROUTPUT} | awk '{ print $4 }' | sed -r 's/\(|\)//g' | tr -d '\n')
case ${ERROR_TYPE}
in
"InvalidParameterValue")
echo -e "Parameter invalid " $(cat ${ERROUTPUT}) >&2
exit 1
;;
"UnauthorizedOperation")
MSG_COD=$(cat ${ERROUTPUT} | awk -F: '{ print $3 }')
MSG_DEC=$(docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} sts decode-authorization-message --encoded-message ${MSG_COD})
echo -e "Unauthorized " $(cat ${MSG_DEC}) >&2
exit 1
;;
*)
echo -e "Unknown error " $(cat ${ERROUTPUT}) >&2
exit 1
;;
esac
}
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 run-instances \
--image-id ${AWS_IMAGE_ID} --count 1 \
--instance-type ${AWS_INSTANCE_TYPE} \
--key-name ${AWS_KEY_NAME} \
--subnet-id ${AWS_SUBNET_ID} \
--tag-specifications "ResourceType=instance,Tags=[{Key='Name',Value='Kurento Media Server'},{Key='ov-cluster-member',Value='kms'},{Key='ov-stack-name',Value='${AWS_STACK_NAME}'},{Key='ov-stack-region',Value='${AWS_DEFAULT_REGION}'}]" \
--iam-instance-profile Name="OpenViduInstanceProfile-${AWS_STACK_NAME}-${AWS_DEFAULT_REGION}" \
--block-device-mappings "DeviceName=/dev/sda1,Ebs={DeleteOnTermination=True,VolumeType='gp2',VolumeSize='${AWS_VOLUME_SIZE}'}" \
--security-group-ids ${AWS_SECURITY_GROUP} > ${OUTPUT} 2> ${ERROUTPUT}
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 wait instance-running --instance-ids $(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
# Generating the output
KMS_IP=$(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .NetworkInterfaces[0] | .PrivateIpAddress')
KMS_ID=$(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
# Wait media-node controller
attempt_counter=0
max_attempts=10
until $(curl --output /dev/null --silent --head --fail -u OPENVIDUAPP:${OPENVIDU_SECRET} http://${KMS_IP}:3000/media-node/status); do
if [ ${attempt_counter} -eq ${max_attempts} ];then
exit 1
fi
attempt_counter=$(($attempt_counter+1))
sleep 5
done
jq -n \
--arg id "${KMS_ID}" \
--arg ip "${KMS_IP}" \
'{ id: $id, ip: $ip }'

View File

@ -0,0 +1,26 @@
version: '3.1'
services:
# --------------------------------------------------------------
#
# Change this if your want use your own application.
# It's very important expose your application in port 5442
# and use the http protocol.
#
# Default Application
#
# Openvidu-Call Version: 2.16.0
#
# --------------------------------------------------------------
app:
image: openvidu/openvidu-call:2.16.0
restart: on-failure
network_mode: host
environment:
- SERVER_PORT=5442
- OPENVIDU_URL=http://localhost:5443
- OPENVIDU_SECRET=${OPENVIDU_SECRET}
- CALL_OPENVIDU_CERTTYPE=${CERTIFICATE_TYPE}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"

View File

@ -0,0 +1,141 @@
# ------------------------------------------------------------------------------
#
# DO NOT MODIFY THIS FILE !!!
#
# Configuration properties should be specified in .env file
#
# Application based on OpenVidu should be specified in
# docker-compose.override.yml file
#
# This docker-compose file coordinates all services of OpenVidu Pro Platform
#
# This file will be overridden when update OpenVidu Platform
#
# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
# ------------------------------------------------------------------------------
version: '3.1'
services:
openvidu-server:
image: openvidu/openvidu-server-pro:2.17.0-dev5
restart: on-failure
network_mode: host
entrypoint: ['/usr/local/bin/entrypoint.sh']
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
- ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:${OPENVIDU_RECORDING_CUSTOM_LAYOUT}
- ${OPENVIDU_CDR_PATH}:${OPENVIDU_CDR_PATH}
- ./cluster:/opt/openvidu/cluster
- .env:${PWD}/.env
env_file:
- .env
environment:
- SERVER_SSL_ENABLED=false
- SERVER_PORT=5443
- KMS_URIS=[]
- OPENVIDU_WEBHOOK=false
- OPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook
- OPENVIDU_PRO_REPLICATION_MANAGER_WEBHOOK=http://127.0.0.1:4443/openvidu/replication-manager-webhook?OPENVIDU_SECRET=${OPENVIDU_SECRET}
- COTURN_REDIS_IP=127.0.0.1
- COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET}
- COTURN_IP=${COTURN_IP:-auto-ipv4}
- OPENVIDU_PRO_CLUSTER=true
- OPENVIDU_PRO_KIBANA_HOST=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
- OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
- WAIT_KIBANA_URL=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
- DOTENV_PATH=${PWD}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
replication-manager:
image: openvidu/replication-manager:1.0.0-dev5
restart: on-failure
network_mode: host
environment:
- SERVER_PORT=4443
- SERVER_SSL_ENABLED=false
- OPENVIDU_SECRET=${OPENVIDU_SECRET}
- LOCAL_OPENVIDU_SERVER_URI=http://127.0.0.1:5443/
- REDIS_HOST=${RM_REDIS_IP}
- REDIS_PORT=${RM_REDIS_PORT}
- REDIS_PASS=${OPENVIDU_SECRET}
- REDIS_TIMEOUT=5
- REDIS_DB=replicationmanager
- MEDIANODE_AS_NOTIFICATION_QUEUE=${RM_SQS_QUEUE}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
redis:
image: openvidu/openvidu-redis:2.0.0-dev2
restart: always
network_mode: host
environment:
- REDIS_PASSWORD=${OPENVIDU_SECRET}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
coturn:
image: openvidu/openvidu-coturn:3.0.0-dev2
restart: on-failure
network_mode: host
environment:
- REDIS_IP=127.0.0.1
- TURN_LISTEN_PORT=3478
- DB_NAME=0
- DB_PASSWORD=${OPENVIDU_SECRET}
- MIN_PORT=40000
- MAX_PORT=65535
- TURN_PUBLIC_IP=${TURN_PUBLIC_IP:-auto-ipv4}
- ENABLE_COTURN_LOGS=true
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
metricbeat:
image: docker.elastic.co/beats/metricbeat-oss:7.8.0
network_mode: host
restart: always
user: root
env_file:
- .env
environment:
- OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
volumes:
- ./beats/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
command: >
/bin/bash -c "metricbeat -e -strict.perms=false
`if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
`if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
filebeat:
image: docker.elastic.co/beats/filebeat-oss:7.8.0
network_mode: host
restart: always
user: root
env_file:
- .env
environment:
- OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
volumes:
- ./beats/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker:/var/lib/docker:ro
- /var/run/docker.sock:/var/run/docker.sock
command: >
/bin/bash -c "filebeat -e -strict.perms=false
`if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
`if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"

View File

@ -0,0 +1,398 @@
#!/usr/bin/env bash
# Global variables
OPENVIDU_FOLDER=openvidu
OPENVIDU_VERSION=master
OPENVIDU_UPGRADABLE_VERSION="master"
AWS_SCRIPTS_FOLDER=${OPENVIDU_FOLDER}/cluster/aws
ELASTICSEARCH_FOLDER=${OPENVIDU_FOLDER}/elasticsearch
BEATS_FOLDER=${OPENVIDU_FOLDER}/beats
DOWNLOAD_URL=https://raw.githubusercontent.com/OpenVidu/openvidu/${OPENVIDU_VERSION}
fatal_error() {
printf "\n =======¡ERROR!======="
printf "\n %s" "$1"
printf "\n"
exit 0
}
new_ov_installation() {
printf '\n'
printf '\n ======================================='
printf '\n Install Openvidu PRO %s' "${OPENVIDU_VERSION}"
printf '\n ======================================='
printf '\n'
# Create folder openvidu-docker-compose
printf '\n => Creating folder '%s'...' "${OPENVIDU_FOLDER}"
mkdir "${OPENVIDU_FOLDER}" || fatal_error "Error while creating the folder '${OPENVIDU_FOLDER}'"
# Create aws scripts folder
printf "\n => Creating folder 'cluster/aws'..."
mkdir -p "${AWS_SCRIPTS_FOLDER}" || fatal_error "Error while creating the folder 'cluster/aws'"
# Create beats folder
printf "\n => Creating folder 'beats'..."
mkdir -p "${BEATS_FOLDER}" || fatal_error "Error while creating the folder 'beats'"
# Create elasticsearch folder
printf "\n => Creating folder 'elasticsearch'..."
mkdir -p "${ELASTICSEARCH_FOLDER}" || fatal_error "Error while creating the folder 'elasticsearch'"
printf "\n => Changing permission to 'elasticsearch' folder..."
chown 1000:1000 "${ELASTICSEARCH_FOLDER}" || fatal_error "Error while changing permission to 'elasticsearch' folder"
# Download necessary files
printf '\n => Downloading Openvidu PRO files:'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh \
--output "${AWS_SCRIPTS_FOLDER}/openvidu_autodiscover.sh" || fatal_error "Error when downloading the file 'openvidu_autodiscover.sh'"
printf '\n - openvidu_autodiscover.sh'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh \
--output "${AWS_SCRIPTS_FOLDER}/openvidu_drop.sh" || fatal_error "Error when downloading the file 'openvidu_drop.sh'"
printf '\n - openvidu_drop.sh'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh \
--output "${AWS_SCRIPTS_FOLDER}/openvidu_launch_kms.sh" || fatal_error "Error when downloading the file 'openvidu_launch_kms.sh'"
printf '\n - openvidu_launch_kms.sh'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml \
--output "${BEATS_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
printf '\n - filebeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml \
--output "${BEATS_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
printf '\n - metricbeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env \
--output "${OPENVIDU_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml \
--output "${OPENVIDU_FOLDER}/docker-compose.override.yml" || fatal_error "Error when downloading the file 'docker-compose.override.yml'"
printf '\n - docker-compose.override.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml \
--output "${OPENVIDU_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
printf '\n - docker-compose.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu \
--output "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
printf '\n - openvidu'
# Add execution permissions
printf "\n => Adding permission:"
chmod +x "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error while adding permission to 'openvidu' program"
printf '\n - openvidu'
chmod +x "${AWS_SCRIPTS_FOLDER}/openvidu_autodiscover.sh" || fatal_error "Error while adding permission to 'openvidu_autodiscover.sh' program"
printf '\n - openvidu_autodiscover.sh'
chmod +x "${AWS_SCRIPTS_FOLDER}/openvidu_drop.sh" || fatal_error "Error while adding permission to 'openvidu' openvidu_drop.sh"
printf '\n - openvidu_drop.sh'
chmod +x "${AWS_SCRIPTS_FOLDER}/openvidu_launch_kms.sh" || fatal_error "Error while adding permission to 'openvidu_launch_kms.sh' program"
printf '\n - openvidu_launch_kms.sh'
# Create own certificated folder
printf "\n => Creating folder 'owncert'..."
mkdir "${OPENVIDU_FOLDER}/owncert" || fatal_error "Error while creating the folder 'owncert'"
# Create vhost nginx folder
printf "\n => Creating folder 'custom-nginx-vhosts'..."
mkdir "${OPENVIDU_FOLDER}/custom-nginx-vhosts" || fatal_error "Error while creating the folder 'custom-nginx-vhosts'"
# Ready to use
printf '\n'
printf '\n'
printf '\n ======================================='
printf '\n Openvidu PRO successfully installed.'
printf '\n ======================================='
printf '\n'
printf '\n 1. Go to openvidu folder:'
printf '\n $ cd openvidu'
printf '\n'
printf '\n 2. Configure OPENVIDU_DOMAIN_OR_PUBLIC_IP, OPENVIDU_PRO_LICENSE, '
printf '\n OPENVIDU_SECRET, and ELASTICSEARCH_PASSWORD in .env file:'
printf '\n $ nano .env'
printf '\n'
printf '\n 3. Start OpenVidu'
printf '\n $ ./openvidu start'
printf '\n'
printf "\n CAUTION: The folder 'openvidu/elasticsearch' use user and group 1000 permissions. "
printf "\n This folder is necessary for store elasticsearch data."
printf "\n For more information, check:"
printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#deployment-instructions"
printf '\n'
printf '\n'
exit 0
}
upgrade_ov() {
# Search local Openvidu installation
printf '\n'
printf '\n ============================================'
printf '\n Search Previous Installation of Openvidu'
printf '\n ============================================'
printf '\n'
SEARCH_IN_FOLDERS=(
"${PWD}"
"/opt/${OPENVIDU_FOLDER}"
)
for folder in "${SEARCH_IN_FOLDERS[@]}"; do
printf "\n => Searching in '%s' folder..." "${folder}"
if [ -f "${folder}/docker-compose.yml" ]; then
OPENVIDU_PREVIOUS_FOLDER="${folder}"
printf "\n => Found installation in folder '%s'" "${folder}"
break
fi
done
[ -z "${OPENVIDU_PREVIOUS_FOLDER}" ] && fatal_error "No previous Openvidu installation found"
# Uppgrade Openvidu
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
fi
printf '\n'
printf '\n ======================================='
printf '\n Upgrade Openvidu PRO %s to %s' "${OPENVIDU_PREVIOUS_VERSION}" "${OPENVIDU_VERSION}"
printf '\n ======================================='
printf '\n'
ROLL_BACK_FOLDER="${OPENVIDU_PREVIOUS_FOLDER}/.old-${OPENVIDU_PREVIOUS_VERSION}"
TMP_FOLDER="${OPENVIDU_PREVIOUS_FOLDER}/tmp"
ACTUAL_FOLDER="${PWD}"
USE_OV_CALL=$(grep -E '^ image: openvidu/openvidu-call:.*$' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.override.yml" | tr -d '[:space:]')
printf "\n Creating rollback folder '%s'..." ".old-${OPENVIDU_PREVIOUS_VERSION}"
mkdir "${ROLL_BACK_FOLDER}" || fatal_error "Error while creating the folder '.old-${OPENVIDU_PREVIOUS_VERSION}'"
printf "\n Creating temporal folder 'tmp'..."
mkdir "${TMP_FOLDER}" || fatal_error "Error while creating the folder 'temporal'"
# Download necessary files
printf '\n => Downloading new Openvidu PRO files:'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh \
--output "${TMP_FOLDER}/openvidu_autodiscover.sh" || fatal_error "Error when downloading the file 'openvidu_autodiscover.sh'"
printf '\n - openvidu_autodiscover.sh'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh \
--output "${TMP_FOLDER}/openvidu_drop.sh" || fatal_error "Error when downloading the file 'openvidu_drop.sh'"
printf '\n - openvidu_drop.sh'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh \
--output "${TMP_FOLDER}/openvidu_launch_kms.sh" || fatal_error "Error when downloading the file 'openvidu_launch_kms.sh'"
printf '\n - openvidu_launch_kms.sh'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml \
--output "${TMP_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
printf '\n - filebeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml \
--output "${TMP_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
printf '\n - metricbeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env \
--output "${TMP_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml \
--output "${TMP_FOLDER}/docker-compose.override.yml" || fatal_error "Error when downloading the file 'docker-compose.override.yml'"
printf '\n - docker-compose.override.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml \
--output "${TMP_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
printf '\n - docker-compose.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu \
--output "${TMP_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
printf '\n - openvidu'
# Dowloading new images and stoped actual Openvidu
printf '\n => Dowloading new images...'
printf '\n'
sleep 1
printf "\n => Moving to 'tmp' folder..."
printf '\n'
cd "${TMP_FOLDER}" || fatal_error "Error when moving to 'tmp' folder"
printf '\n'
docker-compose pull | true
printf '\n => Stoping Openvidu...'
printf '\n'
sleep 1
printf "\n => Moving to 'openvidu' folder..."
printf '\n'
cd "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error when moving to 'openvidu' folder"
printf '\n'
docker-compose down | true
printf '\n'
printf '\n => Moving to working dir...'
cd "${ACTUAL_FOLDER}" || fatal_error "Error when moving to working dir"
# Move old files to rollback folder
printf '\n => Moving previous installation files to rollback folder:'
mv "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'docker-compose.yml'"
printf '\n - docker-compose.yml'
if [ ! -z "${USE_OV_CALL}" ]; then
mv "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.override.yml" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'docker-compose.override.yml'"
printf '\n - docker-compose.override.yml'
fi
mv "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - openvidu'
mv "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'cluster/aws'"
printf '\n - cluster/aws'
mv "${OPENVIDU_PREVIOUS_FOLDER}/beats" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'beats'"
printf '\n - beats'
cp "${OPENVIDU_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env'
if [ -d "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" ]; then
mv "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous directory 'custom-nginx-vhosts'"
printf '\n - custom-nginx-vhosts'
fi
# Move tmp files to Openvidu
printf '\n => Updating files:'
mv "${TMP_FOLDER}/docker-compose.yml" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'docker-compose.yml'"
printf '\n - docker-compose.yml'
if [ ! -z "${USE_OV_CALL}" ]; then
mv "${TMP_FOLDER}/docker-compose.override.yml" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'docker-compose.override.yml'"
printf '\n - docker-compose.override.yml'
else
mv "${TMP_FOLDER}/docker-compose.override.yml" "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.override.yml-${OPENVIDU_VERSION}" || fatal_error "Error while updating 'docker-compose.override.yml'"
printf '\n - docker-compose.override.yml-%s' "${OPENVIDU_VERSION}"
fi
mv "${TMP_FOLDER}/.env" "${OPENVIDU_PREVIOUS_FOLDER}/.env-${OPENVIDU_VERSION}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env-%s' "${OPENVIDU_VERSION}"
mv "${TMP_FOLDER}/openvidu" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'openvidu'"
printf '\n - openvidu'
mkdir "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while creating the folder 'cluster/aws'"
mkdir "${OPENVIDU_PREVIOUS_FOLDER}/beats" || fatal_error "Error while creating the folder 'beats'"
mv "${TMP_FOLDER}/openvidu_autodiscover.sh" "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while updating 'openvidu_autodiscover.sh'"
printf '\n - openvidu_autodiscover.sh'
mv "${TMP_FOLDER}/openvidu_drop.sh" "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while updating 'openvidu_drop.sh'"
printf '\n - openvidu_drop.sh'
mv "${TMP_FOLDER}/openvidu_launch_kms.sh" "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while updating 'openvidu_launch_kms.sh'"
printf '\n - openvidu_launch_kms.sh'
mv "${TMP_FOLDER}/filebeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/filebeat.yml" || fatal_error "Error while updating 'filebeat.yml'"
printf '\n - filebeat.yml'
mv "${TMP_FOLDER}/metricbeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/metricbeat.yml" || fatal_error "Error while updating 'metricbeat.yml'"
printf '\n - metricbeat.yml'
printf "\n => Deleting 'tmp' folder"
rm -rf "${TMP_FOLDER}" || fatal_error "Error deleting 'tmp' folder"
# Add execution permissions
printf "\n => Adding permission to 'openvidu' program..."
chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" || fatal_error "Error while adding permission to 'openvidu' program"
printf '\n - openvidu'
chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws/openvidu_autodiscover.sh" || fatal_error "Error while adding permission to 'openvidu_autodiscover.sh' program"
printf '\n - openvidu_autodiscover.sh'
chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws/openvidu_drop.sh" || fatal_error "Error while adding permission to 'openvidu' openvidu_drop.sh"
printf '\n - openvidu_drop.sh'
chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws/openvidu_launch_kms.sh" || fatal_error "Error while adding permission to 'openvidu_launch_kms.sh' program"
printf '\n - openvidu_launch_kms.sh'
# Define old mode: On Premise or Cloud Formation
OLD_MODE=$(grep -E "Installation Mode:.*$" "${ROLL_BACK_FOLDER}/docker-compose.yml" | awk '{ print $4,$5 }')
[ ! -z "${OLD_MODE}" ] && sed -i -r "s/Installation Mode:.+/Installation Mode: ${OLD_MODE}/" "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml"
# In Aws, update AMI ID
AWS_REGION=$(grep -E "AWS_DEFAULT_REGION=.*$" "${OPENVIDU_PREVIOUS_FOLDER}/.env" | cut -d'=' -f2)
if [[ ! -z ${AWS_REGION} ]]; then
NEW_AMI_ID=$(curl https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_VERSION//v}.yaml --silent |
sed -n -e '/KMSAMIMAP:/,/Metadata:/ p' |
grep -A 1 ${AWS_REGION} | grep AMI | tr -d " " | cut -d":" -f2)
[[ -z ${NEW_AMI_ID} ]] && fatal_error "Error while getting new AWS_IMAGE_ID for Media Nodes"
sed -i "s/.*AWS_IMAGE_ID=.*/AWS_IMAGE_ID=${NEW_AMI_ID}/" "${OPENVIDU_PREVIOUS_FOLDER}/.env" || fatal_error "Error while updating new AWS_IMAGE_ID for Media Nodes"
fi
# Ready to use
printf '\n'
printf '\n'
printf '\n ================================================'
printf "\n Openvidu successfully upgraded to version %s" "${OPENVIDU_VERSION}"
printf '\n ================================================'
printf '\n'
printf "\n 1. A new file 'docker-compose.yml' has been created with the new OpenVidu %s services" "${OPENVIDU_VERSION}"
printf '\n'
printf "\n 2. The previous file '.env' remains intact, but a new file '.env-%s' has been created." "${OPENVIDU_VERSION}"
printf "\n Transfer any configuration you wish to keep in the upgraded version from '.env' to '.env-%s'." "${OPENVIDU_VERSION}"
printf "\n When you are OK with it, rename and leave as the only '.env' file of the folder the new '.env-%s'." "${OPENVIDU_VERSION}"
printf '\n'
printf "\n 3. If you were using Openvidu Call application, it has been automatically updated in file 'docker-compose.override.yml'."
printf "\n However, if you were using your own application, a file called 'docker-compose.override.yml-%s'" "${OPENVIDU_VERSION}"
printf "\n has been created with the latest version of Openvidu Call. If you don't plan to use it you can delete it."
printf '\n'
printf '\n 4. Start new version of Openvidu'
printf '\n $ ./openvidu start'
printf '\n'
printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
printf '\n'
printf '\n'
}
# Check docker and docker-compose installation
if ! command -v docker > /dev/null; then
echo "You don't have docker installed, please install it and re-run the command"
exit 0
fi
if ! command -v docker-compose > /dev/null; then
echo "You don't have docker-compose installed, please install it and re-run the command"
exit 0
else
COMPOSE_VERSION=$(docker-compose version --short | sed "s/-rc[0-9]*//")
if ! printf '%s\n%s\n' "1.24" "$COMPOSE_VERSION" | sort -V -C; then
echo "You need a docker-compose version equal or higher than 1.24, please update your docker-compose and re-run the command"; \
exit 0
fi
fi
# Check type of installation
if [[ ! -z "$1" && "$1" == "upgrade" ]]; then
upgrade_ov
else
new_ov_installation
fi

View File

@ -0,0 +1,301 @@
#!/bin/bash
upgrade_ov() {
UPGRADE_SCRIPT_URL="https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/install_openvidu_pro_OVVERSION.sh"
HTTP_STATUS=$(curl -s -o /dev/null -I -w "%{http_code}" ${UPGRADE_SCRIPT_URL//OVVERSION/$1})
printf " => Upgrading Openvidu PRO to '%s' version" "$1"
if [ "$HTTP_STATUS" == "200" ]; then
printf "\n => Downloading and upgrading new version"
printf "\n"
curl --silent ${UPGRADE_SCRIPT_URL//OVVERSION/$1} | bash -s upgrade
else
printf "\n =======¡ERROR!======="
printf "\n Openvidu PRO Version '%s' not exist" "$1"
printf "\n"
exit 0
fi
}
collect_basic_information() {
LINUX_VERSION=$(lsb_release -d)
DOCKER_PS=$(docker ps)
DOCKER_VERSION=$(docker version --format '{{.Server.Version}}')
DOCKER_COMPOSE_VERSION=$(docker-compose version --short)
OV_FOLDER="${PWD}"
OV_VERSION=$(grep 'Openvidu Version:' "${OV_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
CONTAINERS=$(docker ps | awk '{if(NR>1) print $NF}')
if [ ! -z "$(grep -E '^ image: openvidu/openvidu-call:.*$' "${OV_FOLDER}/docker-compose.override.yml" | tr -d '[:space:]')" ]; then
OV_CALL_VERSION=$(grep -E 'Openvidu-Call Version:' "${OV_FOLDER}/docker-compose.override.yml" | awk '{ print $4 }')
fi
[ -z "${OV_CALL_VERSION}" ] && OV_CALL_VERSION="No present"
OV_TYPE_INSTALLATION=$(grep 'Installation Mode:' "${OV_FOLDER}/docker-compose.yml" | awk '{ print $4,$5 }')
TREE_OV_DIRECTORY=$(find "." ! -path '*/0/*' | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/")
}
version_ov() {
collect_basic_information
printf '\nOpenvidu Information:'
printf '\n'
printf '\n Installation Type: %s' "${OV_TYPE_INSTALLATION}"
printf '\n Openvidu Version: %s' "${OV_VERSION}"
printf '\n Openvidu Call Version: %s' "${OV_CALL_VERSION}"
printf '\n'
printf '\nSystem Information:'
printf '\n'
printf '\n Linux Version:'
printf '\n - %s' "${LINUX_VERSION}"
printf '\n Docker Version: %s' "${DOCKER_VERSION}"
printf '\n Docker Compose Version: %s' "${DOCKER_COMPOSE_VERSION}"
printf '\n'
printf '\nInstallation Information:'
printf '\n'
printf '\n Installation Folder: %s' "${OV_FOLDER}"
printf '\n Installation Folder Tree:'
printf '\n%s' "$(echo "${TREE_OV_DIRECTORY}" | sed -e 's/.//' -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
printf '\n'
printf '\nDocker Running Services:'
printf '\n'
printf '\n %s' "$(echo "${DOCKER_PS}" | sed -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
printf '\n'
}
generate_report() {
collect_basic_information
REPORT_CREATION_DATE=$(date +"%d-%m-%Y")
REPORT_CREATION_TIME=$(date +"%H:%M:%S")
REPORT_NAME="openvidu-report-${REPORT_CREATION_DATE}-$(date +"%H-%M").txt"
REPORT_OUPUT="${OV_FOLDER}/${REPORT_NAME}"
{
printf "\n ======================================="
printf "\n = REPORT INFORMATION ="
printf "\n ======================================="
printf '\n'
printf '\n Creation Date: %s' "${REPORT_CREATION_DATE}"
printf '\n Creation Time: %s' "${REPORT_CREATION_TIME}"
printf '\n'
printf "\n ======================================="
printf "\n = OPENVIDU INFORMATION ="
printf "\n ======================================="
printf '\n'
printf '\n Installation Type: %s' "${OV_TYPE_INSTALLATION}"
printf '\n Openvidu Version: %s' "${OV_VERSION}"
printf '\n Openvidu Call Version: %s' "${OV_CALL_VERSION}"
printf '\n'
printf "\n ======================================="
printf "\n = SYSTEM INFORMATION ="
printf "\n ======================================="
printf '\n'
printf '\n Linux Version:'
printf '\n - %s' "${LINUX_VERSION}"
printf '\n Docker Version: %s' "${DOCKER_VERSION}"
printf '\n Docker Compose Version: %s' "${DOCKER_COMPOSE_VERSION}"
printf '\n'
printf "\n ======================================="
printf "\n = INSTALLATION INFORMATION ="
printf "\n ======================================="
printf '\n'
printf '\n Installation Folder: %s' "${OV_FOLDER}"
printf '\n Installation Folder Tree:'
printf '\n%s' "$(echo "${TREE_OV_DIRECTORY}" | sed -e 's/.//' -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
printf '\n'
printf "\n ======================================="
printf "\n = DOCKER RUNNING SERVICES ="
printf "\n ======================================="
printf '\n'
printf '\n %s' "$(echo "${DOCKER_PS}" | sed -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
printf '\n'
printf "\n ======================================="
printf "\n = CONFIGURATION FILES ="
printf "\n ======================================="
printf '\n'
printf '\n ================ .env ================='
printf '\n'
printf '\n'
cat < "${OV_FOLDER}/.env" | sed -r -e "s/OPENVIDU_SECRET=.+/OPENVIDU_SECRET=****/" -e "s/OPENVIDU_PRO_LICENSE=.+/OPENVIDU_PRO_LICENSE=****/" -e "s/ELASTICSEARCH_PASSWORD=.+/ELASTICSEARCH_PASSWORD=****/"
printf '\n'
printf '\n ========= docker-compose.yml =========='
printf '\n'
printf '\n'
cat "${OV_FOLDER}/docker-compose.yml"
printf '\n'
printf '\n ==== docker-compose.override.yml ===='
printf '\n'
printf '\n'
if [ -f "${OV_FOLDER}/docker-compose.override.yml" ]; then
cat < "${OV_FOLDER}/docker-compose.override.yml"
else
printf '\n The docker-compose.override.yml file is not present'
fi
printf '\n'
printf '\n'
printf "\n ======================================="
printf "\n = LOGS ="
printf "\n ======================================="
for CONTAINER in $CONTAINERS
do
printf '\n'
printf "\n ---------------------------------------"
printf "\n %s" $CONTAINER
printf "\n ---------------------------------------"
printf '\n'
docker logs $CONTAINER
printf "\n ---------------------------------------"
printf '\n'
printf '\n'
done
printf "\n ======================================="
printf "\n = CONTAINER ENVS VARIABLES ="
printf "\n ======================================="
for CONTAINER in $CONTAINERS
do
printf '\n'
printf "\n ======================================="
printf "\n %s" $CONTAINER
printf "\n ---------------------------------------"
printf '\n'
docker exec $CONTAINER env
printf "\n ---------------------------------------"
printf '\n'
printf '\n'
done
} >> "${REPORT_OUPUT}" 2>&1
printf "\n Generation of the report completed with success"
printf "\n You can get your report at path '%s'" "${REPORT_OUPUT}"
printf "\n"
}
is_external_url() {
local URL=$1
if [[ -z "$URL" ]]; then
return 1
fi
if [[ "${URL}" == *"localhost"* ]] || [[ "${URL}" == *"127.0.0.1"* ]] || [[ "${URL}" == *"::1"* ]]; then
return 1
else
return 0
fi
}
start_openvidu() {
local RUN_LOCAL_ES
local RUN_LOCAL_KIBANA
local CONFIGURED_ELASTICSEARCH_HOST
local CONFIGURED_KIBANA_HOST
CONFIGURED_ELASTICSEARCH_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_ELASTICSEARCH_HOST | cut -d '=' -f2)
CONFIGURED_KIBANA_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_KIBANA_HOST | cut -d '=' -f2)
RUN_LOCAL_ES=true
RUN_LOCAL_KIBANA=true
if is_external_url "${CONFIGURED_ELASTICSEARCH_HOST}"; then
printf "Configured external elasticsearch: %s" "${CONFIGURED_ELASTICSEARCH_HOST}"
printf "\n"
RUN_LOCAL_ES=false
fi
if is_external_url "${CONFIGURED_KIBANA_HOST}"; then
printf "Configured external kibana: %s" "${CONFIGURED_KIBANA_HOST}"
printf "\n"
RUN_LOCAL_KIBANA=false
fi
docker-compose up -d \
$(if [ "${RUN_LOCAL_ES}" == "false" ]; then echo '--scale elasticsearch=0'; fi) \
$(if [ "${RUN_LOCAL_KIBANA}" == "false" ]; then echo '--scale kibana=0'; fi)
}
usage() {
printf "Usage: \n\t openvidu [command]"
printf "\n\nAvailable Commands:"
printf "\n\tstart\t\t\tStart all services"
printf "\n\tstop\t\t\tStop all services"
printf "\n\trestart\t\t\tRestart all stoped and running services"
printf "\n\tlogs\t\t\tShow openvidu-server logs"
printf "\n\tupgrade\t\t\tUpgrade to the lastest Openvidu version"
printf "\n\tupgrade [version]\tUpgrade to the specific Openvidu version"
printf "\n\tversion\t\t\tShow version of Openvidu Server"
printf "\n\treport\t\t\tGenerate a report with the current status of Openvidu"
printf "\n\thelp\t\t\tShow help for openvidu command"
printf "\n"
}
[[ -z "${FOLLOW_OPENVIDU_LOGS}" ]] && FOLLOW_OPENVIDU_LOGS=true
case $1 in
start)
start_openvidu
if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
docker-compose logs -f openvidu-server
fi
;;
stop)
docker-compose down
;;
restart)
docker-compose down
start_openvidu
if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
docker-compose logs -f openvidu-server
fi
;;
logs)
docker-compose logs -f openvidu-server
;;
upgrade)
if [ -z "$2" ]; then
UPGRADE_VERSION="latest"
else
UPGRADE_VERSION="$2"
fi
read -r -p " You're about to update Openvidu PRO to '${UPGRADE_VERSION}' version. Are you sure? [y/N]: " response
case "$response" in
[yY][eE][sS]|[yY])
upgrade_ov "${UPGRADE_VERSION}"
;;
*)
exit 0
;;
esac
;;
version)
version_ov
;;
report)
read -r -p " You are about to generate a report on the current status of Openvidu, this may take some time. Do you want to continue? [y/N]: " response
case "$response" in
[yY][eE][sS]|[yY])
generate_report
;;
*)
exit 0
;;
esac
;;
*)
usage
;;
esac

View File

@ -1,30 +0,0 @@
# KMS Configuration
# --------------------------
# NOTE: This file doesn't need to quote assignment values, like most shells do.
# All values are stored as-is, even if they contain spaces, so don't quote them.
# Kurento Media Server image
# --------------------------
# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server-dev
# Uncomment the next line and define this variable with KMS image that you want use
# KMS_IMAGE=kurento/kurento-media-server:6.14.0
# Kurento Media Server Level logs
# -------------------------------
# Uncomment the next line and define this variable to change
# the verbosity level of the logs of KMS
# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
# KMS_DEBUG_LEVEL=3,Kurento*:4,kms*:4,sdp*:4,webrtc*:4,*rtpendpoint:4,rtp*handler:4,rtpsynchronizer:4,agnosticbin:4
# Metricbeat ElasticSearch Image
# -------------------------------
# Docker hub kurento media server: https://www.docker.elastic.co/
# Uncomment the next line and define this variable with METRICBEAT_IMAGE image that you want use
# METRICBEAT_IMAGE=docker.elastic.co/beats/metricbeat:7.8.0
# FileBeat ElasticSearch Image
# -------------------------------
# Docker hub kurento media server: https://www.docker.elastic.co/
# Uncomment the next line and define this variable with FILEBEAT_IMAGE image that you want use
# FILEBEAT_IMAGE=docker.elastic.co/beats/filebeat:7.8.0

View File

@ -3,5 +3,4 @@ echo "Creating dir for beats"
mkdir -p /opt/openvidu/beats
echo "Copying beat config files"
cp /beats/filebeat.yml /opt/openvidu/beats/filebeat.yml
cp /beats/metricbeat-elasticsearch.yml /opt/openvidu/beats/metricbeat-elasticsearch.yml
cp /beats/metricbeat-openvidu.yml /opt/openvidu/beats/metricbeat-openvidu.yml
cp /beats/metricbeat-elasticsearch.yml /opt/openvidu/beats/metricbeat-elasticsearch.yml

View File

@ -5,6 +5,14 @@ filebeat.inputs:
multiline.pattern: '^\d*:\d*:\d*'
multiline.negate: true
multiline.match: after
- type: log
paths:
- /opt/openvidu/kurento-logs/*.log
fields:
kurento-media-server: true
ip: ${MEDIA_NODE_IP}
cluster-id: ${CLUSTER_ID}
fields_under_root: true
processors:
- add_docker_metadata:
@ -19,23 +27,21 @@ processors:
- add_fields:
fields:
ip: ${MEDIA_NODE_IP}
cluster-id: ${CLUSTER_ID}
output:
elasticsearch:
hosts: ["${OPENVIDU_SERVER_PRO_IP}:9200"]
indices:
- index: "filebeat-kurento-%{+yyyy.MM.dd}"
when.or:
- contains:
container.image.name: kurento/kurento-media-server
- equals:
kurento-media-server: true
pipelines:
- pipeline: kurento-pipeline
when.or:
- contains:
container.image.name: kurento/kurento-media-server
- equals:
kurento-media-server: true
logging.json: true
logging.metrics.enabled: false
setup.ilm.enabled: true
setup.ilm.policy_name: "openvidu_cleanup_policy"
setup.ilm.enabled: false

View File

@ -1,14 +1,34 @@
output:
elasticsearch:
hosts: ["${OPENVIDU_SERVER_PRO_IP}:9200"]
metricbeat.modules:
- module: system
metricsets: [cpu]
enabled: true
period: ${OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL}s
metricsets:
- cpu
#- diskio
- memory
- network
- filesystem
- fsstat
#- process
- process_summary
- uptime
filesystem.ignore_types: [nfs, smbfs, autofs, devtmpfs, devpts, hugetlbfs, tmpfs, sysfs, securityfs, cgroup2, cgroup, pstore, debugfs, configfs, fusectl, proc, fuse.lxcfs, squashfs]
processes: ['.*']
# process.include_top_n:
# by_cpu: 2
# by_memory: 2
processors:
- drop_event:
when:
or:
- regexp:
system.network.name: '^(veth|lo|docker|br-)($|)'
- regexp:
system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host)($|/)'
- regexp:
system.filesystem.mount_point: '^/hostfs/(sys|cgroup|proc|dev|etc|host)($|/)'
enabled: true
period: ${OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL}0s
cpu.metrics: [normalized_percentages]
fields: {ip: "${MEDIA_NODE_IP}"}
fields: {ip: "${MEDIA_NODE_IP}", cluster-id: "${CLUSTER_ID}"}
pipeline:
queue.mem.events: 0
setup.ilm.enabled: false
setup.ilm.enabled: false

View File

@ -1,12 +0,0 @@
output:
logstash.hosts: ["${OPENVIDU_SERVER_PRO_IP}:5044"]
metricbeat.modules:
- module: system
metricsets: [cpu]
enabled: true
period: ${OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL}s
processes: ['.*']
cpu.metrics: [normalized_percentages]
fields: {ip: "${MEDIA_NODE_IP}"}
pipeline:
queue.mem.events: 0

View File

@ -6,7 +6,7 @@
#
# This docker-compose file coordinates all services of OpenVidu CE Platform.
#
# Openvidu Version: 2.15.0
# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
@ -16,20 +16,24 @@ version: '3.1'
services:
media-node-controller:
image: openvidu/media-node-controller:1.0.0
image: openvidu/media-node-controller:3.0.0-dev4
restart: always
ulimits:
core: -1
entrypoint: ['/bin/sh', '-c', '/beats/copy_config_files.sh && /usr/local/bin/entrypoint.sh']
environment:
- KMS_IMAGE=${KMS_IMAGE:-kurento/kurento-media-server:6.14.0}
- KMS_DEBUG_LEVEL=${KMS_DEBUG_LEVEL:-}
- METRICBEAT_IMAGE=${METRICBEAT_IMAGE:-docker.elastic.co/beats/metricbeat:7.8.0}
- FILEBEAT_IMAGE=${FILEBEAT_IMAGE:-docker.elastic.co/beats/filebeat:7.8.0}
- KMS_IMAGE=kurento/kurento-media-server:6.15.0
- METRICBEAT_IMAGE=docker.elastic.co/beats/metricbeat-oss:7.8.0
- FILEBEAT_IMAGE=docker.elastic.co/beats/filebeat-oss:7.8.0
- OPENVIDU_RECORDING_IMAGE=openvidu/openvidu-recording:2.17.0-dev1
ports:
- 3000:3000
volumes:
- /opt/openvidu/recordings:/opt/openvidu/recordings
- /opt/openvidu/beats:/opt/openvidu/beats
- /var/run/docker.sock:/var/run/docker.sock
- /opt/openvidu/kurento-logs:/opt/openvidu/kurento-logs
- ./beats:/beats
logging:
options:
max-size: "100M"

View File

@ -2,8 +2,16 @@
MEDIA_NODE_FOLDER=kms
MEDIA_NODE_VERSION=master
OPENVIDU_UPGRADABLE_VERSION="2.16"
BEATS_FOLDER=${MEDIA_NODE_FOLDER}/beats
DOWNLOAD_URL=https://raw.githubusercontent.com/OpenVidu/openvidu/${MEDIA_NODE_VERSION}
IMAGES=(
"kurento-media-server"
"docker.elastic.co/beats/filebeat"
"docker.elastic.co/beats/metricbeat"
"openvidu/media-node-controller"
)
fatal_error() {
printf "\n =======¡ERROR!======="
printf "\n %s" "$1"
@ -11,6 +19,27 @@ fatal_error() {
exit 0
}
docker_command_by_container_image() {
IMAGE_NAME=$1
COMMAND=$2
if [[ ! -z "${IMAGE_NAME}" ]]; then
CONTAINERS=$(docker ps -a | grep "${IMAGE_NAME}" | awk '{print $1}')
for CONTAINER_ID in ${CONTAINERS[@]}; do
if [[ ! -z "${CONTAINER_ID}" ]] && [[ ! -z "${COMMAND}" ]]; then
bash -c "docker ${COMMAND} ${CONTAINER_ID}"
fi
done
fi
}
stop_containers() {
printf "Stopping containers..."
for IMAGE in ${IMAGES[@]}; do
docker_command_by_container_image "${IMAGE}" "rm -f"
done
}
new_media_node_installation() {
printf '\n'
printf '\n ======================================='
@ -33,10 +62,6 @@ new_media_node_installation() {
--output "${MEDIA_NODE_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
printf '\n - docker-compose.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/.env \
--output "${MEDIA_NODE_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/media_node \
--output "${MEDIA_NODE_FOLDER}/media_node" || fatal_error "Error when downloading the file 'media_node'"
printf '\n - media_node'
@ -49,10 +74,6 @@ new_media_node_installation() {
--output "${BEATS_FOLDER}/metricbeat-elasticsearch.yml" || fatal_error "Error when downloading the file 'metricbeat-elasticsearch.yml'"
printf '\n - metricbeat-elasticsearch.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml \
--output "${BEATS_FOLDER}/metricbeat-openvidu.yml" || fatal_error "Error when downloading the file 'metricbeat-openvidu.yml'"
printf '\n - metricbeat-openvidu.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh \
--output "${BEATS_FOLDER}/copy_config_files.sh" || fatal_error "Error when downloading the file 'copy_config_files.sh'"
printf '\n - copy_config_files.sh'
@ -68,12 +89,14 @@ new_media_node_installation() {
# Pull images
printf "\n => Pulling images...\n"
cd "${MEDIA_NODE_FOLDER}" || fatal_error "Error when moving to '${MEDIA_NODE_FOLDER}' folder"
KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | sed 's/\(^.*KMS_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | sed 's/\(^.*METRICBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | sed 's/\(^.*FILEBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | cut -d"=" -f2)
METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | cut -d"=" -f2)
FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | cut -d"=" -f2)
OPENVIDU_RECORDING_IMAGE=$(cat docker-compose.yml | grep OPENVIDU_RECORDING_IMAGE | cut -d"=" -f2)
docker pull $KMS_IMAGE || fatal "Error while pulling docker image: $KMS_IMAGE"
docker pull $METRICBEAT_IMAGE || fatal "Error while pulling docker image: $METRICBEAT_IMAGE"
docker pull $FILEBEAT_IMAGE || fatal "Error while pulling docker image: $FILEBEAT_IMAGE"
docker pull $OPENVIDU_RECORDING_IMAGE || fatal "Error while pulling docker image: $OPENVIDU_RECORDING_IMAGE"
docker-compose pull | true
# Ready to use
@ -81,16 +104,28 @@ new_media_node_installation() {
printf '\n ======================================='
printf "\n Media Node successfully installed."
printf '\n ======================================='
printf "\n"
printf '\n'
printf '\n 1. Go to kms folder:'
printf '\n $ cd kms'
printf "\n"
printf '\n'
printf '\n 2. Start Media Node Controller'
printf '\n $ ./media_node start'
printf '\n'
printf "\n For more information, check:"
printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#deployment-instructions"
printf '\n 3. This will run a service at port 3000 which OpenVidu will use to deploy necessary containers.'
printf '\n Add the private ip of this media node in "KMS_URIS=[]" in OpenVidu Pro machine'
printf '\n in file located at "/opt/openvidu/.env" with this format:'
printf '\n ...'
printf '\n KMS_URIS=["ws://<MEDIA_NODE_PRIVATE_IP>:8888/kurento"]'
printf '\n ...'
printf '\n You can also add this node from inspector'
printf '\n'
printf '\n 4. Start or restart OpenVidu Pro and all containers will be provisioned'
printf '\n automatically to all the media nodes configured in "KMS_URIS"'
printf '\n More info about Media Nodes deployment here:'
printf "\n --> https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#set-the-number-of-media-nodes-on-startup"
printf '\n'
printf '\n'
printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
exit 0
}
@ -123,10 +158,13 @@ upgrade_media_node() {
# Uppgrade Media Node
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${MEDIA_NODE_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && OPENVIDU_PREVIOUS_VERSION=2.14.0
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
fi
printf '\n'
printf '\n ======================================='
@ -151,10 +189,6 @@ upgrade_media_node() {
--output "${TMP_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
printf '\n - docker-compose.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/.env \
--output "${TMP_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/media_node \
--output "${TMP_FOLDER}/media_node" || fatal_error "Error when downloading the file 'media_node'"
printf '\n - media_node'
@ -167,10 +201,6 @@ upgrade_media_node() {
--output "${TMP_FOLDER}/metricbeat-elasticsearch.yml" || fatal_error "Error when downloading the file 'metricbeat-elasticsearch.yml'"
printf '\n - metricbeat-elasticsearch.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml \
--output "${TMP_FOLDER}/metricbeat-openvidu.yml" || fatal_error "Error when downloading the file 'metricbeat-openvidu.yml'"
printf '\n - metricbeat-openvidu.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh \
--output "${TMP_FOLDER}/copy_config_files.sh" || fatal_error "Error when downloading the file 'copy_config_files.sh'"
printf '\n - copy_config_files.sh'
@ -182,16 +212,24 @@ upgrade_media_node() {
printf "\n => Moving to 'tmp' folder..."
printf '\n'
cd "${TMP_FOLDER}" || fatal_error "Error when moving to '${TMP_FOLDER}' folder"
printf '\n => Stoping Media Node containers...'
printf '\n'
sleep 1
stop_containers
# Pull images
printf "\n => Pulling images...\n"
cd "${TMP_FOLDER}" || fatal_error "Error when moving to '${TMP_FOLDER}' folder"
KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | sed 's/\(^.*KMS_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | sed 's/\(^.*METRICBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | sed 's/\(^.*FILEBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | cut -d"=" -f2)
METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | cut -d"=" -f2)
FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | cut -d"=" -f2)
OPENVIDU_RECORDING_IMAGE=$(cat docker-compose.yml | grep OPENVIDU_RECORDING_IMAGE | cut -d"=" -f2)
docker pull $KMS_IMAGE || fatal "Error while pulling docker image: $KMS_IMAGE"
docker pull $METRICBEAT_IMAGE || fatal "Error while pulling docker image: $METRICBEAT_IMAGE"
docker pull $FILEBEAT_IMAGE || fatal "Error while pulling docker image: $FILEBEAT_IMAGE"
docker pull $OPENVIDU_RECORDING_IMAGE || fatal "Error while pulling docker image: $OPENVIDU_RECORDING_IMAGE"
docker-compose pull | true
printf '\n => Stoping Media Node...'
@ -216,14 +254,8 @@ upgrade_media_node() {
mv "${MEDIA_NODE_PREVIOUS_FOLDER}/media_node" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - media_node'
mv "${MEDIA_NODE_PREVIOUS_FOLDER}/readme.md" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'readme.md'"
printf '\n - readme.md'
mv "${MEDIA_NODE_PREVIOUS_FOLDER}/nginx_conf" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'nginx_conf'"
printf '\n - nginx_conf'
cp "${MEDIA_NODE_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env'
mv "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'beats' folder"
printf '\n - beats'
# Move tmp files to Openvidu
printf '\n => Updating files:'
@ -231,9 +263,6 @@ upgrade_media_node() {
mv "${TMP_FOLDER}/docker-compose.yml" "${MEDIA_NODE_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'docker-compose.yml'"
printf '\n - docker-compose.yml'
mv "${TMP_FOLDER}/.env" "${MEDIA_NODE_PREVIOUS_FOLDER}/.env-${MEDIA_NODE_VERSION}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env-%s' "${MEDIA_NODE_VERSION}"
mv "${TMP_FOLDER}/media_node" "${MEDIA_NODE_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'media_node'"
printf '\n - media_node'
@ -245,9 +274,6 @@ upgrade_media_node() {
mv "${TMP_FOLDER}/metricbeat-elasticsearch.yml" "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" || fatal_error "Error while updating 'metricbeat-elasticsearch.yml'"
printf '\n - metricbeat-elasticsearch.yml'
mv "${TMP_FOLDER}/metricbeat-openvidu.yml" "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" || fatal_error "Error while updating 'metricbeat-openvidu.yml'"
printf '\n - metricbeat-openvidu.yml'
mv "${TMP_FOLDER}/copy_config_files.sh" "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" || fatal_error "Error while updating 'copy_config_files.sh'"
printf '\n - copy_config_files.sh'
@ -275,17 +301,27 @@ upgrade_media_node() {
printf '\n'
printf "\n 1. A new file 'docker-compose.yml' has been created with the new OpenVidu %s services" "${OPENVIDU_VERSION}"
printf '\n'
printf "\n 2. The previous file '.env' remains intact, but a new file '.env-%s' has been created." "${OPENVIDU_VERSION}"
printf "\n Transfer any configuration you wish to keep in the upgraded version from '.env' to '.env-%s'." "${OPENVIDU_VERSION}"
printf "\n When you are OK with it, rename and leave as the only '.env' file of the folder the new '.env-%s'." "${OPENVIDU_VERSION}"
printf "\n 2. This new version %s does not need any .env file. Everything is configured from OpenVidu Pro" "${OPENVIDU_VERSION}"
printf '\n'
printf '\n 3. Start new version of Media Node'
printf '\n $ ./media_node start'
printf '\n'
printf '\n 4. This will run a service at port 3000 which OpenVidu will use to deploy necessary containers.'
printf '\n Add the private ip of this media node in "KMS_URIS=[]" in OpenVidu Pro machine'
printf '\n in file located at "/opt/openvidu/.env" with this format:'
printf '\n ...'
printf '\n KMS_URIS=["ws://<MEDIA_NODE_PRIVATE_IP>:8888/kurento"]'
printf '\n ...'
printf '\n You can also add Media Nodes from inspector'
printf '\n'
printf '\n 5. Start or restart OpenVidu Pro and all containers will be provisioned'
printf '\n automatically to all the media nodes configured in "KMS_URIS"'
printf '\n More info about Media Nodes deployment here:'
printf "\n --> https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#set-the-number-of-media-nodes-on-startup"
printf '\n'
printf '\n'
printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
printf '\n'
printf '\n'
}
# Check docker and docker-compose installation

View File

@ -1,5 +1,41 @@
#!/bin/bash
# Deployed images in media-node
IMAGES=(
"kurento-media-server"
"docker.elastic.co/beats/filebeat"
"docker.elastic.co/beats/metricbeat"
"openvidu/media-node-controller"
)
docker_command_by_container_image() {
IMAGE_NAME=$1
COMMAND=$2
if [[ ! -z "${IMAGE_NAME}" ]]; then
CONTAINERS=$(docker ps -a | grep "${IMAGE_NAME}" | awk '{print $1}')
for CONTAINER_ID in ${CONTAINERS[@]}; do
if [[ ! -z "${CONTAINER_ID}" ]] && [[ ! -z "${COMMAND}" ]]; then
bash -c "docker ${COMMAND} ${CONTAINER_ID}"
fi
done
fi
}
stop_containers() {
printf "Stopping containers..."
for IMAGE in ${IMAGES[@]}; do
docker_command_by_container_image "${IMAGE}" "rm -f"
done
}
kurento_logs() {
if [[ "$1" == "-f" ]]; then
tail -f /opt/openvidu/kurento-logs/*.log
else
cat /opt/openvidu/kurento-logs/*.log
fi
}
upgrade_media_node() {
UPGRADE_SCRIPT_URL="https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/install_media_node_OVVERSION.sh"
HTTP_STATUS=$(curl -s -o /dev/null -I -w "%{http_code}" ${UPGRADE_SCRIPT_URL//OVVERSION/$1})
@ -140,6 +176,16 @@ generate_report() {
printf '\n'
done
printf '\n'
printf "\n ---------------------------------------"
printf "\n KMS"
printf "\n ---------------------------------------"
printf '\n'
kurento_logs
printf "\n ---------------------------------------"
printf '\n'
printf '\n'
printf "\n ======================================="
printf "\n = CONTAINER ENVS VARIABLES ="
printf "\n ======================================="
@ -170,7 +216,8 @@ usage() {
printf "\n\tstart\t\t\tStart media node service"
printf "\n\tstop\t\t\tStop media node service"
printf "\n\trestart\t\t\tRestart media node service"
printf "\n\tlogs\t\t\tShow media node logs"
printf "\n\tlogs [-f]\t\tShow media-node-controller logs."
printf "\n\tkms-logs [-f]\t\tShow kms logs"
printf "\n\tupgrade\t\t\tUpgrade to the lastest Media Node version"
printf "\n\tupgrade [version]\tUpgrade to the specific Media Node version"
printf "\n\tversion\t\t\tShow version of Media Node"
@ -179,15 +226,6 @@ usage() {
printf "\n"
}
stop_containers() {
CONTAINERS=$(docker ps | awk '{if(NR>1) print $NF}')
for CONTAINER in $CONTAINERS
do
[ "$(docker ps -a | grep ${CONTAINER})" ] && docker stop ${CONTAINER}
done
}
case $1 in
start)
@ -208,7 +246,18 @@ case $1 in
;;
logs)
docker-compose logs -f media-node-controller
case $2 in
"-f")
docker-compose logs -f media-node-controller
;;
*)
docker-compose logs media-node-controller
;;
esac
;;
kms-logs)
kurento_logs $2
;;
upgrade)

View File

@ -42,6 +42,22 @@ LETSENCRYPT_EMAIL=user@example.com
# SDKs, REST clients and browsers will have to connect to this port
# HTTPS_PORT=443
# Old paths are considered now deprecated, but still supported by default.
# OpenVidu Server will log a WARN message every time a deprecated path is called, indicating
# the new path that should be used instead. You can set property SUPPORT_DEPRECATED_API=false
# to stop allowing the use of old paths.
# Default value is true
# SUPPORT_DEPRECATED_API=true
# If true request to with www will be redirected to non-www requests
# Default value is false
# REDIRECT_WWW=false
# How many workers to configure in nginx proxy.
# The more workers, the more requests will be handled
# Default value is 10240
# WORKER_CONNECTIONS=10240
# Access restrictions
# In this section you will be able to restrict the IPs from which you can access to
# Openvidu API and the Administration Panel
@ -71,6 +87,12 @@ OPENVIDU_PRO_CLUSTER_MODE=manual
# Possibles values: aws, on_premise
OPENVIDU_PRO_CLUSTER_ENVIRONMENT=on_premise
# Unique identifier of your cluster. Each OpenVidu Server Pro instance corresponds to one cluster.
# You can launch as many clusters as you want with your license key.
# Cluster ID will always be stored to disk so restarting OpenVidu Server Pro will keep the same previous cluster ID
# if this configuration parameter is not given a distinct value.
# OPENVIDU_PRO_CLUSTER_ID=
# The desired number of Media Nodes on startup. First the autodiscovery process is performed.
# If there are too many Media Nodes after that, they will be dropped until this number is reached.
# If there are not enough, more will be launched.
@ -85,7 +107,6 @@ OPENVIDU_PRO_CLUSTER_ENVIRONMENT=on_premise
# Type: number >= 0
# OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL=
# Whether to enable or disable autoscaling. With autoscaling the number of Media Nodes will
# be automatically adjusted according to existing load
# Values: true | false
@ -119,13 +140,55 @@ OPENVIDU_PRO_CLUSTER_AUTOSCALING=false
# (and therefore distribution of load) among all available Media Nodes
OPENVIDU_PRO_CLUSTER_LOAD_STRATEGY=streams
# Whether to enable or disable Network Quality API. You can monitor and
# warn users about the quality of their networks with this feature
# OPENVIDU_PRO_NETWORK_QUALITY=false
# If OPENVIDU_PRO_NETWORK_QUALITY=true, how often the network quality
# algorithm will be invoked for each user, in seconds
# OPENVIDU_PRO_NETWORK_QUALITY_INTERVAL=5
# Max days until delete indexes in state of rollover on Elasticsearch
# Type number >= 0
# Default Value is 15
# OPENVIDU_PRO_ELASTICSEARCH_MAX_DAYS_DELETE=
# Private IP of OpenVidu Server Pro
# For example 192.168.1.101
# OPENVIDU_PRO_PRIVATE_IP=
# If you have an external Elasticsearch and Kibana already running, put here the url to elasticsearch and kibana services.
# It is very important that both url have the port specified in the url.
# If you want to use the deployed Elasticsearch and Kibana locally, keep these variables commented.
#OPENVIDU_PRO_ELASTICSEARCH_HOST=
#OPENVIDU_PRO_KIBANA_HOST=
# Where to store recording files. Can be 'local' (local storage) or 's3' (AWS bucket).
# You will need to define a OPENVIDU_PRO_AWS_S3_BUCKET if you use it.
#OPENVIDU_PRO_RECORDING_STORAGE=
# S3 Bucket where to store recording files. May include paths to allow navigating
# folder structures inside the bucket. This property is only taken into account
# if OPENVIDU_PRO_RECORDING_STORAGE=s3
#OPENVIDU_PRO_AWS_S3_BUCKET=
# If OPENVIDU_PRO_RECORDING_STORAGE=s3, the collection of HTTP header values that the internal AWS client will use during
# the upload process. The property is a key-value map of strings, following the format of a JSON object. For example, for applying
# server-side encryption with AES-256, this header is mandatory: {"x-amz-server-side-encryption":"AES256"}.
# The list of available headers can be found here: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/Headers.html
# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
#OPENVIDU_PRO_AWS_S3_HEADERS=
# If you're instance has a role which has access to read
# and write into the s3 bucket, you don't need this parameter
# OPENVIDU_PRO_AWS_ACCESS_KEY=
# AWS credentials secret key from OPENVIDU_PRO_AWS_ACCESS_KEY. This property is only
# taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
# If you're instance has a role which has access to read
# and write into the s3 bucket, you don't need this parameter
# OPENVIDU_PRO_AWS_SECRET_KEY=
# AWS region in which the S3 bucket is located (e.g. eu-west-1). If not provided,
# the region will try to be discovered automatically, although this is not always possible.
# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
# OPENVIDU_PRO_AWS_REGION=
# Whether to enable recording module or not
OPENVIDU_RECORDING=false
@ -178,12 +241,14 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
# All sessions of OpenVidu will try to force this codec. If OPENVIDU_ALLOW_TRANSCODING=true
# when a codec can not be forced, transcoding will be allowed
# OPENVIDU_FORCED_CODEC=VP8
# All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true
# when a codec can not be forced, transcoding will be allowed
# Default value is VP8
# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied
# OPENVIDU_ALLOW_TRANSCODING=false
# Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied
# Default value is false
# OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
# true to enable OpenVidu Webhook service. false' otherwise
# Values: true | false
@ -227,17 +292,44 @@ OPENVIDU_CDR_PATH=/opt/openvidu/cdr
# RECOMENDED VALUES: INFO for normal logs DEBUG for more verbose logs
# OV_CE_DEBUG_LEVEL=INFO
# Java Options
# OpenVidu Java Options
# --------------------------
# Uncomment the next line and define this to add options to java command
# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
# JAVA_OPTIONS=-Xms2048m -Xmx4096m
# Kibana And ElasticSearch Configuration
# ElasticSearch Java Options
# --------------------------
# Kibana dashboard configuration (Credentials)
KIBANA_USER=kibanaadmin
KIBANA_PASSWORD=
# Uncomment the next line and define this to add options to java command of Elasticsearch
# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
# By default ElasticSearch is configured to use "-Xms2g -Xmx2g" as Java Min and Max memory heap allocation
# ES_JAVA_OPTS=-Xms2048m -Xmx4096m
# Kibana And ElasticSearch Credentials Configuration
# --------------------------
# Kibana And ElasticSearch Basic Auth configuration (Credentials)
# This credentials will aso be valid for Kibana dashboard
ELASTICSEARCH_USERNAME=elasticadmin
ELASTICSEARCH_PASSWORD=
# Media Node Configuration
# --------------------------
# You can add any KMS environment variable as described in the
# documentation of the docker image: https://hub.docker.com/r/kurento/kurento-media-server
# If you want to add an environment variable to KMS, you must add a variable using this prefix: 'KMS_DOCKER_ENV_',
# followed by the environment variable you want to setup.
# For example if you want to setup KMS_MIN_PORT to 50000, it would be KMS_DOCKER_ENV_KMS_MIN_PORT=50000
# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server
# Uncomment the next line and define this variable with KMS image that you want use
# By default, KMS_IMAGE is defined in media nodes and it does not need to be specified unless
# you want to use a specific version of KMS
# KMS_IMAGE=kurento/kurento-media-server:6.15.0
# Uncomment the next line and define this variable to change
# the verbosity level of the logs of KMS
# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
# KMS_DOCKER_ENV_GST_DEBUG=
# Cloudformation configuration
# --------------------------
@ -250,3 +342,5 @@ KIBANA_PASSWORD=
#AWS_SECURITY_GROUP=
#AWS_STACK_ID=
#AWS_STACK_NAME=
#AWS_CLI_DOCKER_TAG=
#AWS_VOLUME_SIZE=

View File

@ -13,20 +13,36 @@ processors:
fields: ["message"]
target: "json"
overwrite_keys: true
- drop_event:
when.or:
- contains:
container.image.name: openvidu/openvidu-coturn
- contains:
container.image.name: docker.elastic.co/elasticsearch/elasticsearch
- contains:
container.image.name: docker.elastic.co/kibana/kibana
- contains:
container.image.name: docker.elastic.co/beats/filebeat-oss
- contains:
container.image.name: docker.elastic.co/beats/metricbeat-oss
- add_fields:
fields:
cluster-id: ${OPENVIDU_PRO_CLUSTER_ID:undefined}
output:
elasticsearch:
hosts: ["elasticsearch:9200"]
hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]
indices:
- index: "filebeat-redis-%{+yyyy.MM.dd}"
when.or:
- contains:
container.image.name: openvidu/openvidu-redis
- index: "filebeat-coturn-%{+yyyy.MM.dd}"
- index: "filebeat-nginx-%{+yyyy.MM.dd}"
when.or:
- contains:
container.image.name: openvidu/openvidu-coturn
container.image.name: openvidu/openvidu-proxy
logging.json: true
logging.metrics.enabled: false
logging.metrics.enabled: false
setup.ilm.enabled: false

View File

@ -0,0 +1,10 @@
metricbeat.modules:
- module: nginx
metricsets: ["stubstatus"]
enabled: true
period: 10s
hosts: ["http://127.0.0.1"]
server_status_path: "nginx_status"
output:
elasticsearch:
hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]

View File

@ -7,7 +7,7 @@ DEBUG=${DEBUG:-false}
OUTPUT=$(mktemp -t openvidu-autodiscover-XXX --suffix .json)
docker run --rm amazon/aws-cli:2.0.7 ec2 describe-instances \
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 describe-instances \
--output text \
--filters "Name=instance-state-name,Values=running" \
"Name=tag:ov-cluster-member,Values=kms" \

View File

@ -8,4 +8,4 @@ DEBUG=${DEBUG:-false}
ID=$1
[ -z "${ID}" ] && { echo "Must provide instance ID"; exit 1; }
docker run --rm amazon/aws-cli:2.0.7 ec2 terminate-instances --instance-ids ${ID} --output json
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 terminate-instances --instance-ids ${ID} --output json

View File

@ -23,7 +23,7 @@ exit_on_error () {
"UnauthorizedOperation")
MSG_COD=$(cat ${ERROUTPUT} | awk -F: '{ print $3 }')
MSG_DEC=$(docker run --rm amazon/aws-cli:2.0.7 sts decode-authorization-message --encoded-message ${MSG_COD})
MSG_DEC=$(docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} sts decode-authorization-message --encoded-message ${MSG_COD})
echo -e "Unauthorized " $(cat ${MSG_DEC}) >&2
exit 1
@ -35,16 +35,17 @@ exit_on_error () {
esac
}
docker run --rm amazon/aws-cli:2.0.7 ec2 run-instances \
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 run-instances \
--image-id ${AWS_IMAGE_ID} --count 1 \
--instance-type ${AWS_INSTANCE_TYPE} \
--key-name ${AWS_KEY_NAME} \
--subnet-id ${AWS_SUBNET_ID} \
--tag-specifications "ResourceType=instance,Tags=[{Key='Name',Value='Kurento Media Server'},{Key='ov-cluster-member',Value='kms'},{Key='ov-stack-name',Value='${AWS_STACK_NAME}'},{Key='ov-stack-region',Value='${AWS_DEFAULT_REGION}'}]" \
--iam-instance-profile Name="OpenViduInstanceProfile-${AWS_STACK_NAME}-${AWS_DEFAULT_REGION}" \
--block-device-mappings "DeviceName=/dev/sda1,Ebs={DeleteOnTermination=True,VolumeType='gp2',VolumeSize='${AWS_VOLUME_SIZE}'}" \
--security-group-ids ${AWS_SECURITY_GROUP} > ${OUTPUT} 2> ${ERROUTPUT}
docker run --rm amazon/aws-cli:2.0.7 ec2 wait instance-running --instance-ids $(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 wait instance-running --instance-ids $(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
# Generating the output
KMS_IP=$(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .NetworkInterfaces[0] | .PrivateIpAddress')

View File

@ -9,11 +9,11 @@ services:
#
# Default Application
#
# Openvidu-Call Version: 2.15.0
# Openvidu-Call Version: 2.16.0
#
# --------------------------------------------------------------
app:
image: openvidu/openvidu-call:2.15.0
image: openvidu/openvidu-call:2.16.0
restart: on-failure
network_mode: host
environment:
@ -21,3 +21,6 @@ services:
- OPENVIDU_URL=http://localhost:5443
- OPENVIDU_SECRET=${OPENVIDU_SECRET}
- CALL_OPENVIDU_CERTTYPE=${CERTIFICATE_TYPE}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"

View File

@ -7,11 +7,11 @@
# Application based on OpenVidu should be specified in
# docker-compose.override.yml file
#
# This docker-compose file coordinates all services of OpenVidu CE Platform.
# This docker-compose file coordinates all services of OpenVidu Pro Platform
#
# This file will be overridden when update OpenVidu Platform
#
# Openvidu Version: 2.15.1
# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
@ -22,10 +22,10 @@ version: '3.1'
services:
openvidu-server:
image: openvidu/openvidu-server-pro:2.15.1
image: openvidu/openvidu-server-pro:2.17.0-dev5
restart: on-failure
network_mode: host
entrypoint: ['/bin/bash', '-c', 'export COTURN_IP=`/usr/local/bin/discover_my_public_ip.sh`; /usr/local/bin/entrypoint.sh']
entrypoint: ['/usr/local/bin/entrypoint.sh']
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
@ -41,21 +41,28 @@ services:
- KMS_URIS=[]
- COTURN_REDIS_IP=127.0.0.1
- COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET}
- COTURN_IP=${COTURN_IP:-auto-ipv4}
- OPENVIDU_PRO_CLUSTER=true
- OPENVIDU_PRO_KIBANA_HOST=http://127.0.0.1/kibana
- OPENVIDU_PRO_ELASTICSEARCH_HOST=http://127.0.0.1:9200
- WAIT_KIBANA_URL=http://127.0.0.1:5601/api/status
- OPENVIDU_PRO_KIBANA_HOST=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
- OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
- WAIT_KIBANA_URL=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
- DOTENV_PATH=${PWD}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
redis:
image: openvidu/openvidu-redis:1.0.0
image: openvidu/openvidu-redis:2.0.0-dev2
restart: always
network_mode: host
environment:
- REDIS_PASSWORD=${OPENVIDU_SECRET}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
coturn:
image: openvidu/openvidu-coturn:1.0.0
image: openvidu/openvidu-coturn:3.0.0-dev2
restart: on-failure
network_mode: host
environment:
@ -63,17 +70,22 @@ services:
- TURN_LISTEN_PORT=3478
- DB_NAME=0
- DB_PASSWORD=${OPENVIDU_SECRET}
- MIN_PORT=57001
- MIN_PORT=40000
- MAX_PORT=65535
- TURN_PUBLIC_IP=${TURN_PUBLIC_IP:-auto-ipv4}
- ENABLE_COTURN_LOGS=true
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
nginx:
image: openvidu/openvidu-proxy:3.0.0
image: openvidu/openvidu-proxy:5.0.0-dev2
restart: on-failure
network_mode: host
entrypoint: ['/bin/sh', '-c', 'htpasswd -bc /etc/nginx/kibana.htpasswd "${KIBANA_USER}" "${KIBANA_PASSWORD}" && /usr/local/bin/entrypoint.sh']
volumes:
- ./certificates:/etc/letsencrypt
- ./owncert:/owncert
- ./custom-nginx-vhosts:/etc/nginx/vhost.d/
- ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:/opt/openvidu/custom-layout
environment:
- DOMAIN_OR_PUBLIC_IP=${DOMAIN_OR_PUBLIC_IP}
@ -85,31 +97,84 @@ services:
- ALLOWED_ACCESS_TO_RESTAPI=${ALLOWED_ACCESS_TO_RESTAPI:-}
- PROXY_MODE=PRO
- WITH_APP=true
- SUPPORT_DEPRECATED_API=${SUPPORT_DEPRECATED_API:-true}
- REDIRECT_WWW=${REDIRECT_WWW:-false}
- WORKER_CONNECTIONS=${WORKER_CONNECTIONS:-10240}
- PUBLIC_IP=${PROXY_PUBLIC_IP:-auto-ipv4}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.8.0
restart: always
environment:
- discovery.type=single-node
- xpack.security.enabled=true
- "ES_JAVA_OPTS=${ES_JAVA_OPTS:--Xms2g -Xmx2g}"
ports:
- 9200:9200
volumes:
- ./elasticsearch:/usr/share/elasticsearch/data
command: >
/bin/bash -c "elasticsearch-users useradd ${ELASTICSEARCH_USERNAME}
-p ${ELASTICSEARCH_PASSWORD} -r superuser;
docker-entrypoint.sh"
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
kibana:
image: docker.elastic.co/kibana/kibana:7.8.0
restart: always
environment:
- SERVER_BASEPATH="/kibana"
- xpack.security.enabled=true
- ELASTICSEARCH_USERNAME=${ELASTICSEARCH_USERNAME}
- ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_PASSWORD}
ports:
- 5601:5601
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
filebeat:
image: docker.elastic.co/beats/filebeat:7.8.0
metricbeat:
image: docker.elastic.co/beats/metricbeat-oss:7.8.0
network_mode: host
restart: always
user: root
env_file:
- .env
environment:
- OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
volumes:
- ./beats/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
command: >
/bin/bash -c "metricbeat -e -strict.perms=false
`if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
`if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
filebeat:
image: docker.elastic.co/beats/filebeat-oss:7.8.0
network_mode: host
restart: always
user: root
env_file:
- .env
environment:
- OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
volumes:
- ./beats/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker:/var/lib/docker:ro
- /var/run/docker.sock:/var/run/docker.sock
command: filebeat -e -strict.perms=false
command: >
/bin/bash -c "filebeat -e -strict.perms=false
`if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
`if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"

View File

@ -3,6 +3,7 @@
# Global variables
OPENVIDU_FOLDER=openvidu
OPENVIDU_VERSION=master
OPENVIDU_UPGRADABLE_VERSION="2.16"
AWS_SCRIPTS_FOLDER=${OPENVIDU_FOLDER}/cluster/aws
ELASTICSEARCH_FOLDER=${OPENVIDU_FOLDER}/elasticsearch
BEATS_FOLDER=${OPENVIDU_FOLDER}/beats
@ -60,6 +61,10 @@ new_ov_installation() {
--output "${BEATS_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
printf '\n - filebeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml \
--output "${BEATS_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
printf '\n - metricbeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env \
--output "${OPENVIDU_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
@ -95,6 +100,10 @@ new_ov_installation() {
printf "\n => Creating folder 'owncert'..."
mkdir "${OPENVIDU_FOLDER}/owncert" || fatal_error "Error while creating the folder 'owncert'"
# Create vhost nginx folder
printf "\n => Creating folder 'custom-nginx-vhosts'..."
mkdir "${OPENVIDU_FOLDER}/custom-nginx-vhosts" || fatal_error "Error while creating the folder 'custom-nginx-vhosts'"
# Ready to use
printf '\n'
printf '\n'
@ -105,13 +114,15 @@ new_ov_installation() {
printf '\n 1. Go to openvidu folder:'
printf '\n $ cd openvidu'
printf '\n'
printf '\n 2. Configure OPENVIDU_DOMAIN_OR_PUBLIC_IP, OPENVIDU_PRO_LICENSE, OPENVIDU_SECRET, and KIBANA_PASSWORD in .env file:'
printf '\n 2. Configure OPENVIDU_DOMAIN_OR_PUBLIC_IP, OPENVIDU_PRO_LICENSE, '
printf '\n OPENVIDU_SECRET, and ELASTICSEARCH_PASSWORD in .env file:'
printf '\n $ nano .env'
printf '\n'
printf '\n 3. Start OpenVidu'
printf '\n $ ./openvidu start'
printf '\n'
printf "\n CAUTION: The folder 'openvidu/elasticsearch' use user and group 1000 permissions. This folder is necessary for store elasticsearch data."
printf "\n CAUTION: The folder 'openvidu/elasticsearch' use user and group 1000 permissions. "
printf "\n This folder is necessary for store elasticsearch data."
printf "\n For more information, check:"
printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#deployment-instructions"
printf '\n'
@ -147,10 +158,13 @@ upgrade_ov() {
# Uppgrade Openvidu
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && OPENVIDU_PREVIOUS_VERSION=2.13.0
[ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
fi
printf '\n'
printf '\n ======================================='
@ -188,6 +202,10 @@ upgrade_ov() {
--output "${TMP_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
printf '\n - filebeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml \
--output "${TMP_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
printf '\n - metricbeat.yml'
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env \
--output "${TMP_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
@ -243,15 +261,20 @@ upgrade_ov() {
mv "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - openvidu'
mv "${OPENVIDU_PREVIOUS_FOLDER}/readme.md" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'readme.md'"
printf '\n - readme.md'
mv "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'cluster/aws'"
printf '\n - cluster/aws'
mv "${OPENVIDU_PREVIOUS_FOLDER}/beats" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'beats'"
printf '\n - beats'
cp "${OPENVIDU_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env'
if [ -d "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" ]; then
mv "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous directory 'custom-nginx-vhosts'"
printf '\n - custom-nginx-vhosts'
fi
# Move tmp files to Openvidu
printf '\n => Updating files:'
@ -288,6 +311,9 @@ upgrade_ov() {
mv "${TMP_FOLDER}/filebeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/filebeat.yml" || fatal_error "Error while updating 'filebeat.yml'"
printf '\n - filebeat.yml'
mv "${TMP_FOLDER}/metricbeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/metricbeat.yml" || fatal_error "Error while updating 'metricbeat.yml'"
printf '\n - metricbeat.yml'
printf "\n => Deleting 'tmp' folder"
rm -rf "${TMP_FOLDER}" || fatal_error "Error deleting 'tmp' folder"
@ -311,16 +337,15 @@ upgrade_ov() {
[ ! -z "${OLD_MODE}" ] && sed -i -r "s/Installation Mode:.+/Installation Mode: ${OLD_MODE}/" "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml"
# In Aws, update AMI ID
CHECK_AWS=$(curl -s -o /dev/null -w "%{http_code}" http://169.254.169.254)
if [[ ${CHECK_AWS} == "200" ]]; then
AWS_REGION=$(grep -E "AWS_DEFAULT_REGION=.*$" "${OPENVIDU_PREVIOUS_FOLDER}/.env" | cut -d'=' -f2)
[[ -z ${AWS_REGION} ]] && fatal_error "Error while getting AWS_REGION"
AWS_REGION=$(grep -E "AWS_DEFAULT_REGION=.*$" "${OPENVIDU_PREVIOUS_FOLDER}/.env" | cut -d'=' -f2)
if [[ ! -z ${AWS_REGION} ]]; then
NEW_AMI_ID=$(curl https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_VERSION//v}.yaml --silent |
sed -n -e '/KMSAMIMAP:/,/Metadata:/ p' |
grep -A 1 ${AWS_REGION} | grep AMI | tr -d " " | cut -d":" -f2)
[[ -z ${NEW_AMI_ID} ]] && fatal_error "Error while getting new AWS_IMAGE_ID for Media Nodes"
sed -i "s/.*AWS_IMAGE_ID=.*/AWS_IMAGE_ID=${NEW_AMI_ID}/" "${OPENVIDU_PREVIOUS_FOLDER}/.env" || fatal_error "Error while updating new AWS_IMAGE_ID for Media Nodes"
fi
# Ready to use
printf '\n'

View File

@ -120,7 +120,7 @@ generate_report() {
printf '\n'
printf '\n'
cat < "${OV_FOLDER}/.env" | sed -r -e "s/OPENVIDU_SECRET=.+/OPENVIDU_SECRET=****/" -e "s/OPENVIDU_PRO_LICENSE=.+/OPENVIDU_PRO_LICENSE=****/" -e "s/KIBANA_PASSWORD=.+/KIBANA_PASSWORD=****/"
cat < "${OV_FOLDER}/.env" | sed -r -e "s/OPENVIDU_SECRET=.+/OPENVIDU_SECRET=****/" -e "s/OPENVIDU_PRO_LICENSE=.+/OPENVIDU_PRO_LICENSE=****/" -e "s/ELASTICSEARCH_PASSWORD=.+/ELASTICSEARCH_PASSWORD=****/"
printf '\n'
printf '\n ========= docker-compose.yml =========='
@ -183,6 +183,42 @@ generate_report() {
printf "\n"
}
is_external_url() {
local URL=$1
if [[ -z "$URL" ]]; then
return 1
fi
if [[ "${URL}" == *"localhost"* ]] || [[ "${URL}" == *"127.0.0.1"* ]] || [[ "${URL}" == *"::1"* ]]; then
return 1
else
return 0
fi
}
start_openvidu() {
local RUN_LOCAL_ES
local RUN_LOCAL_KIBANA
local CONFIGURED_ELASTICSEARCH_HOST
local CONFIGURED_KIBANA_HOST
CONFIGURED_ELASTICSEARCH_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_ELASTICSEARCH_HOST | cut -d '=' -f2)
CONFIGURED_KIBANA_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_KIBANA_HOST | cut -d '=' -f2)
RUN_LOCAL_ES=true
RUN_LOCAL_KIBANA=true
if is_external_url "${CONFIGURED_ELASTICSEARCH_HOST}"; then
printf "Configured external elasticsearch: %s" "${CONFIGURED_ELASTICSEARCH_HOST}"
printf "\n"
RUN_LOCAL_ES=false
fi
if is_external_url "${CONFIGURED_KIBANA_HOST}"; then
printf "Configured external kibana: %s" "${CONFIGURED_KIBANA_HOST}"
printf "\n"
RUN_LOCAL_KIBANA=false
fi
docker-compose up -d \
$(if [ "${RUN_LOCAL_ES}" == "false" ]; then echo '--scale elasticsearch=0'; fi) \
$(if [ "${RUN_LOCAL_KIBANA}" == "false" ]; then echo '--scale kibana=0'; fi)
}
usage() {
printf "Usage: \n\t openvidu [command]"
printf "\n\nAvailable Commands:"
@ -198,11 +234,15 @@ usage() {
printf "\n"
}
[[ -z "${FOLLOW_OPENVIDU_LOGS}" ]] && FOLLOW_OPENVIDU_LOGS=true
case $1 in
start)
docker-compose up -d
docker-compose logs -f openvidu-server
start_openvidu
if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
docker-compose logs -f openvidu-server
fi
;;
stop)
@ -211,8 +251,10 @@ case $1 in
restart)
docker-compose down
docker-compose up -d
docker-compose logs -f openvidu-server
start_openvidu
if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
docker-compose logs -f openvidu-server
fi
;;
logs)

View File

@ -1,6 +1,6 @@
---
global:
owner: openvidu@gmail.com
owner: info@openvidu.io
qsname: openvidu-pro-clustering
regions:
- us-east-1

View File

@ -1,7 +1,7 @@
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y coturn curl
&& apt-get install -y coturn curl dnsutils
COPY ./configuration-files.sh /tmp/
COPY ./entrypoint.sh /usr/local/bin
@ -11,4 +11,4 @@ RUN chmod +x /tmp/configuration-files.sh \
&& chmod +x /usr/local/bin/entrypoint.sh \
&& chmod +x /usr/local/bin/discover_my_public_ip.sh
CMD /usr/local/bin/entrypoint.sh
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ]

Some files were not shown because too many files have changed in this diff Show More