openvidu-server recording layout updated to use StreamManager API

This commit is contained in:
pabloFuente 2018-06-01 14:19:14 +02:00
parent 2f2a42c439
commit 9e79fe351e
15 changed files with 978 additions and 847 deletions

View File

@ -4,7 +4,7 @@ MAINTAINER openvidu@gmail.com
# Install Chrome
RUN apt-get update && apt-get install -y wget
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list && \
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list && \
apt-get update && apt-get install -y google-chrome-stable
# Install media packages
@ -25,4 +25,3 @@ RUN ["chmod", "+x", "/entrypoint.sh"]
RUN mkdir /recordings
ENTRYPOINT /entrypoint.sh

View File

@ -11,8 +11,8 @@ fi
URL=${URL:-https://www.youtube.com/watch?v=JMuzlEQz3uo}
RESOLUTION=${RESOLUTION:-1920x1080}
FRAMERATE=${FRAMERATE:-24}
VIDEO_SIZE=$RESOLUTION
ARRAY=(${VIDEO_SIZE//x/ })
WIDTH="$(cut -d'x' -f1 <<< $RESOLUTION)"
HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)"
VIDEO_ID=${VIDEO_ID:-video}
VIDEO_NAME=${VIDEO_NAME:-video}
VIDEO_FORMAT=${VIDEO_FORMAT:-mp4}
@ -21,8 +21,8 @@ RECORDING_JSON="${RECORDING_JSON}"
export URL
export RESOLUTION
export FRAMERATE
export VIDEO_SIZE
export ARRAY
export WIDTH
export HEIGHT
export VIDEO_ID
export VIDEO_NAME
export VIDEO_FORMAT
@ -83,7 +83,7 @@ touch xvfb.log
chmod 777 xvfb.log
function3() {
xvfb-run --server-num=${DISPLAY_NUM} --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome -no-sandbox -test-type -disable-infobars -window-size=${ARRAY[0]},${ARRAY[1]} -no-first-run -ignore-certificate-errors --kiosk $URL &> xvfb.log &
xvfb-run --server-num=${DISPLAY_NUM} --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome -start-maximized -no-sandbox -test-type -disable-infobars -window-size=$WIDTH,$HEIGHT -no-first-run -ignore-certificate-errors --kiosk $URL &> xvfb.log &
}
export -f function3
if [[ $CURRENT_UID != $USER_ID ]]; then

View File

@ -2,7 +2,7 @@ FROM ubuntu:16.04
MAINTAINER openvidu@gmail.com
# Install Kurento Media Server (KMS)
RUN echo "deb http://ubuntu.openvidu.io/6.7.0 xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
RUN echo "deb http://ubuntu.openvidu.io/6.7.2 xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5AFA7A83 \
&& apt-get update \
&& apt-get -y dist-upgrade \

View File

@ -17,6 +17,7 @@ import { DashboardComponent } from './components/dashboard/dashboard.component';
import { SessionDetailsComponent } from './components/session-details/session-details.component';
import { CredentialsDialogComponent } from './components/dashboard/credentials-dialog.component';
import { LayoutBestFitComponent } from './components/layouts/layout-best-fit/layout-best-fit.component';
import { OpenViduVideoComponent } from './components/layouts/ov-video.component';
@NgModule({
@ -26,6 +27,7 @@ import { LayoutBestFitComponent } from './components/layouts/layout-best-fit/lay
SessionDetailsComponent,
CredentialsDialogComponent,
LayoutBestFitComponent,
OpenViduVideoComponent,
],
imports: [
BrowserModule,

View File

@ -1,7 +1,7 @@
<div id="layout" class="bounds">
<div *ngFor="let s of streams" class="OT_root OT_publisher custom-class">
<div *ngFor="let s of subscribers" class="OT_root OT_publisher custom-class">
<div class="OT_widget-container">
<video [id]="'native-video-' + s.streamId" autoplay="true" [srcObject]="s.getMediaStream()" (playing)="onVideoPlaying($event)"></video>
<app-ov-video [subscriber]="s"></app-ov-video>
</div>
</div>
</div>

View File

@ -1,6 +1,6 @@
import { Component, OnInit, OnDestroy, HostListener, ViewEncapsulation, ApplicationRef } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { OpenVidu, Session, Stream, Subscriber, StreamEvent } from 'openvidu-browser';
import { OpenVidu, Session, Stream, Subscriber, StreamEvent, StreamManagerEvent } from 'openvidu-browser';
import { OpenViduLayout } from '../openvidu-layout';
@ -17,7 +17,7 @@ export class LayoutBestFitComponent implements OnInit, OnDestroy {
secret: string;
session: Session;
streams: Stream[] = [];
subscribers: Subscriber[] = [];
layout: any;
resizeTimeout;
@ -51,13 +51,17 @@ export class LayoutBestFitComponent implements OnInit, OnDestroy {
this.session = OV.initSession();
this.session.on('streamCreated', (event: StreamEvent) => {
const subscriber: Subscriber = this.session.subscribe(event.stream, '');
this.addRemoteStream(event.stream);
const subscriber: Subscriber = this.session.subscribe(event.stream, undefined);
subscriber.on('streamPlaying', (e: StreamManagerEvent) => {
const video: HTMLVideoElement = subscriber.videos[0].video;
video.parentElement.parentElement.classList.remove('custom-class');
this.openviduLayout.updateLayout();
});
this.addSubscriber(subscriber);
});
this.session.on('streamDestroyed', (event: StreamEvent) => {
event.preventDefault();
this.deleteRemoteStream(event.stream);
this.deleteSubscriber(<Subscriber>event.stream.streamManager);
this.openviduLayout.updateLayout();
});
@ -83,36 +87,29 @@ export class LayoutBestFitComponent implements OnInit, OnDestroy {
});
}
private addRemoteStream(stream: Stream): void {
this.streams.push(stream);
private addSubscriber(subscriber: Subscriber): void {
this.subscribers.push(subscriber);
this.appRef.tick();
}
private deleteRemoteStream(stream: Stream): void {
private deleteSubscriber(subscriber: Subscriber): void {
let index = -1;
for (let i = 0; i < this.streams.length; i++) {
if (this.streams[i].streamId === stream.streamId) {
for (let i = 0; i < this.subscribers.length; i++) {
if (this.subscribers[i] === subscriber) {
index = i;
break;
}
}
if (index > -1) {
this.streams.splice(index, 1);
this.subscribers.splice(index, 1);
}
this.appRef.tick();
}
leaveSession() {
if (this.session) { this.session.disconnect(); };
this.streams = [];
this.subscribers = [];
this.session = null;
}
onVideoPlaying(event) {
const video: HTMLVideoElement = event.target;
video.parentElement.parentElement.classList.remove('custom-class');
this.openviduLayout.updateLayout();
}
}

View File

@ -0,0 +1,26 @@
import { Component, Input, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Subscriber } from 'openvidu-browser';
@Component({
selector: 'app-ov-video',
template: '<video #videoElement></video>'
})
export class OpenViduVideoComponent implements AfterViewInit {
@ViewChild('videoElement') elementRef: ElementRef;
_subscriber: Subscriber;
ngAfterViewInit() {
this._subscriber.addVideoElement(this.elementRef.nativeElement);
}
@Input()
set subscriber(subscriber: Subscriber) {
this._subscriber = subscriber;
if (!!this.elementRef) {
this._subscriber.addVideoElement(this.elementRef.nativeElement);
}
}
}

View File

@ -208,7 +208,6 @@ public class OpenViduServer implements JsonRpcConfigurer {
default:
URL url = new URL(publicUrl);
int port = url.getPort();
type = "custom";
@ -223,11 +222,6 @@ public class OpenViduServer implements JsonRpcConfigurer {
if (!OpenViduServer.publicUrl.startsWith("wss://")) {
OpenViduServer.publicUrl = "wss://" + OpenViduServer.publicUrl;
}
if (port == -1) {
OpenViduServer.publicUrl += ":" + openviduConf.getServerPort();
}
break;
}
if (OpenViduServer.publicUrl == null) {

View File

@ -2,7 +2,7 @@ spring.profiles.active=docker
server.address: 0.0.0.0
server.ssl.enabled: true
openvidu.recording.version: 2.0.1
openvidu.recording.version: 2.1.0

View File

@ -3,7 +3,7 @@ spring.profiles.active=ngrok
server.port: 5000
server.address: 0.0.0.0
server.ssl.enabled: false
openvidu.recording.version: 2.0.1
openvidu.recording.version: 2.1.0
kms.uris=[\"ws://localhost:8888/kurento\"]
openvidu.secret: MY_SECRET

View File

@ -1,6 +1,6 @@
server.address: 0.0.0.0
server.ssl.enabled: true
openvidu.recording.version: 2.0.1
openvidu.recording.version: 2.1.0

View File

@ -1 +1 @@
{"version":3,"sources":["webpack/bootstrap 347280613b71b908df0e"],"names":[],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAQ,oBAAoB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAY,2BAA2B;AACvC;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA,kDAA0C,oBAAoB,WAAW","file":"inline.bundle.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t\"inline\": 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 347280613b71b908df0e"],"sourceRoot":"webpack:///"}
{"version":3,"sources":["webpack/bootstrap 20d6fb8adf9ed6d01cc2"],"names":[],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAQ,oBAAoB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAY,2BAA2B;AACvC;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA,kDAA0C,oBAAoB,WAAW","file":"inline.bundle.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t\"inline\": 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 20d6fb8adf9ed6d01cc2"],"sourceRoot":"webpack:///"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long