openvidu-recording-java and openvidu-recording-node

This commit is contained in:
pabloFuente 2018-03-15 12:52:59 +01:00
parent e1871f7f7d
commit a7a742b165
42 changed files with 33121 additions and 0 deletions

4
.gitignore vendored
View File

@ -20,3 +20,7 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*/.settings
*/.classpath
*/.project

24
openvidu-recording-java/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
target/

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

View File

@ -0,0 +1,13 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Documentation badge](https://readthedocs.org/projects/fiware-orion/badge/?version=latest)](http://openvidu.io/docs/home/)
[![Docker badge](https://img.shields.io/docker/pulls/fiware/orion.svg)](https://hub.docker.com/r/openvidu/)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://groups.google.com/forum/#!forum/openvidu)
[![][OpenViduLogo]](http://openvidu.io)
openvidu-recording-java
===
Visit [openvidu.io/docs/tutorials/openvidu-js-java/](http://openvidu.io/docs/tutorials/openvidu-js-java/)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -0,0 +1,51 @@
FROM ubuntu:16.04
MAINTAINER openvidu@gmail.com
# Install Kurento Media Server (KMS)
RUN echo "deb http://ubuntu.kurento.org xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
&& apt-key adv --keyserver keyserver.ubuntu.com --recv 2F819BC0 \
&& apt-get update \
&& apt-get -y dist-upgrade \
&& apt-get -y install kurento-media-server-6.0 \
&& rm -rf /var/lib/apt/lists/*
COPY kms.sh /kms.sh
COPY ngrok.sh /ngrok.sh
# Install Java
RUN apt-get update && apt-get install -y openjdk-8-jdk && rm -rf /var/lib/apt/lists/*
# ngrok
RUN apt-get update && apt-get install unzip
RUN set -x \
&& apt-get update \
&& apt-get install wget \
&& wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip \
&& unzip ngrok-stable-linux-amd64.zip -d /home/ngrok \
&& rm -f ngrok-stable-linux-amd64.zip ngrok
COPY ngrok.yml /home/ngrok/ngrok.yml
# Configure Supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN apt-get update && apt-get install -y supervisor && rm -rf /var/lib/apt/lists/*
COPY openvidu-server.jar openvidu-server.jar
COPY openvidu-js-java.jar app.jar
RUN sh -c 'touch /openvidu-server.jar'
RUN sh -c 'touch /app.jar'
RUN set -x \
&& echo 'ngrok:x:6737:6737:Ngrok user:/home/ngrok:/bin/false' >> /etc/passwd \
&& echo 'ngrok:x:6737:' >> /etc/group \
&& chown ngrok:ngrok /home/ngrok \
&& chmod -R go=u,go-w /home/ngrok \
&& chmod go= /home/ngrok
EXPOSE 3000
EXPOSE 5000
EXPOSE 4040
# Exec supervisord
CMD ["/usr/bin/supervisord"]

View File

@ -0,0 +1,15 @@
# Build and package openvidu-js-java maven project
cd .. && mvn clean compile package
# Copy openvidu-js-java.jar in docker build path
cp target/openvidu-js-java-"$1".jar ./docker/openvidu-js-java.jar
# Copy compiled openvidu-server.jar
cd ./docker && cp ../../../openvidu/openvidu-server/target/openvidu-server-"$1".jar ./openvidu-server.jar
### Build Docker container and remove unwanted files ###
docker build -t openvidu/basic-webinar-demo .
rm ./openvidu-js-java.jar
rm ./openvidu-server.jar
rm -rf ./openvidu-server

View File

@ -0,0 +1,19 @@
#!/bin/bash -x
set -e
if [ -n "$KMS_TURN_URL" ]; then
echo "turnURL=$KMS_TURN_URL" > /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
fi
if [ -n "$KMS_STUN_IP" -a -n "$KMS_STUN_PORT" ]; then
# Generate WebRtcEndpoint configuration
echo "stunServerAddress=$KMS_STUN_IP" > /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
echo "stunServerPort=$KMS_STUN_PORT" >> /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
fi
# Remove ipv6 local loop until ipv6 is supported
cat /etc/hosts | sed '/::1/d' | tee /etc/hosts > /dev/null
export GST_DEBUG=Kurento*:5
exec /usr/bin/kurento-media-server "$@"

View File

@ -0,0 +1,6 @@
exec $HOME/ngrok start --all -config=$HOME/ngrok.yml > $HOME/ngrok.log &
while true
do
sleep 100000
done
exit 0

View File

@ -0,0 +1,8 @@
web_addr: 0.0.0.0:4040
tunnels:
app:
addr: 3000
proto: http
server:
addr: 5000
proto: http

View File

@ -0,0 +1,26 @@
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log;
pidfile=/var/run/supervisord.pid;
loglevel=debug
[program:kms]
command=/bin/bash /kms.sh
redirect_stderr=true
priority=2
[program:openvidu-server]
command=/bin/bash -c "java -Dspring.profiles.active=ngrok -jar /openvidu-server.jar"
redirect_stderr=true
priority=3
[program:openvidu-js-java]
command=/bin/bash -c "java -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=container -jar /app.jar"
redirect_stderr=true
priority=4
[program:ngrok]
environment=HOME="/home/ngrok",USER="ngrok"
command=/bin/bash /ngrok.sh
redirect_stderr=true
priority=1

View File

@ -0,0 +1,102 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-recording-java</artifactId>
<version>1.1.0</version>
<packaging>jar</packaging>
<name>openvidu-recording-java</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<start-class>io.openvidu.js.java.App</start-class>
<docker.image.prefix>openvidu</docker.image.prefix>
</properties>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.2.3</version>
<configuration>
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.war</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</dependency>
<dependency>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-java-client</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,13 @@
package io.openvidu.js.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}

View File

@ -0,0 +1,250 @@
package io.openvidu.js.java;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.openvidu.java.client.Archive;
import io.openvidu.java.client.OpenVidu;
import io.openvidu.java.client.OpenViduException;
import io.openvidu.java.client.Session;
import io.openvidu.java.client.TokenOptions;
import io.openvidu.java.client.OpenViduRole;
@RestController
@RequestMapping("/api")
public class MyRestController {
OpenVidu openVidu;
private Map<String, Session> mapSessions = new ConcurrentHashMap<>();
private Map<String, Map<String, OpenViduRole>> mapSessionIdsTokens = new ConcurrentHashMap<>();
private Map<String, Boolean> sessionRecordings = new ConcurrentHashMap<>();
private String OPENVIDU_URL;
private String SECRET;
public MyRestController(@Value("${openvidu.secret}") String secret, @Value("${openvidu.url}") String openviduUrl) {
this.SECRET = secret;
this.OPENVIDU_URL = openviduUrl;
this.openVidu = new OpenVidu(OPENVIDU_URL, SECRET);
}
/*******************/
/*** Session API ***/
/*******************/
@RequestMapping(value = "/get-sessionid-token", method = RequestMethod.POST)
public ResponseEntity<JSONObject> getSessionIdToken(@RequestBody String sessionNameParam) throws ParseException {
System.out.println("Getting sessionId and token | {sessionName}=" + sessionNameParam);
JSONObject sessionJSON = (JSONObject) new JSONParser().parse(sessionNameParam);
// The video-call to connect ("TUTORIAL")
String sessionName = (String) sessionJSON.get("session");
// Role associated to this user
OpenViduRole role = OpenViduRole.PUBLISHER;
// Build tokenOptions object with the serverData and the role
TokenOptions tokenOptions = new TokenOptions.Builder().role(role).build();
JSONObject responseJson = new JSONObject();
if (this.mapSessions.get(sessionName) != null) {
// Session already exists: return existing sessionId and a new token
System.out.println("Existing session " + sessionName);
try {
// Get the existing sessionId from our collection with
// the sessionName param ("TUTORIAL")
String sessionId = this.mapSessions.get(sessionName).getSessionId();
// Generate a new token with the recently created tokenOptions
String token = this.mapSessions.get(sessionName).generateToken(tokenOptions);
// Update our collection storing the new token
this.mapSessionIdsTokens.get(sessionId).put(token, role);
// Prepare the response with the sessionId and the token
responseJson.put(0, sessionId);
responseJson.put(1, token);
// Return the response to the client
return new ResponseEntity<>(responseJson, HttpStatus.OK);
} catch (Exception e) {
// If error generate an error message and return it to client
return getErrorResponse(e);
}
} else {
// New session: return a new sessionId and token
System.out.println("New session " + sessionName);
try {
// Create a new OpenVidu Session
Session session = this.openVidu.createSession();
// Get the sessionId
String sessionId = session.getSessionId();
// Generate a new token with the recently created tokenOptions
String token = session.generateToken(tokenOptions);
// Store the session and the token in our collections
this.mapSessions.put(sessionName, session);
this.mapSessionIdsTokens.put(sessionId, new ConcurrentHashMap<>());
this.mapSessionIdsTokens.get(sessionId).put(token, role);
// Prepare the response with the sessionId and the token
responseJson.put(0, sessionId);
responseJson.put(1, token);
// Return the response to the client
return new ResponseEntity<>(responseJson, HttpStatus.OK);
} catch (Exception e) {
// If error generate an error message and return it to client
return getErrorResponse(e);
}
}
}
@RequestMapping(value = "/remove-user", method = RequestMethod.POST)
public ResponseEntity<JSONObject> removeUser(@RequestBody String sessionNameToken) throws Exception {
System.out.println("Removing user | {sessionName, token}=" + sessionNameToken);
// Retrieve the params from BODY
JSONObject sessionNameTokenJSON = (JSONObject) new JSONParser().parse(sessionNameToken);
String sessionName = (String) sessionNameTokenJSON.get("sessionName");
String token = (String) sessionNameTokenJSON.get("token");
// If the session exists ("TUTORIAL" in this case)
if (this.mapSessions.get(sessionName) != null) {
String sessionId = this.mapSessions.get(sessionName).getSessionId();
if (this.mapSessionIdsTokens.containsKey(sessionId)) {
// If the token exists
if (this.mapSessionIdsTokens.get(sessionId).remove(token) != null) {
// User left the session
if (this.mapSessionIdsTokens.get(sessionId).isEmpty()
&& !this.sessionRecordings.containsKey(sessionId)) {
// Last user left and recording is not taking place: session must be removed
this.mapSessions.remove(sessionName);
}
return new ResponseEntity<>(HttpStatus.OK);
} else {
// The TOKEN wasn't valid
System.out.println("Problems in the app server: the TOKEN wasn't valid");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
} else {
// The SESSIONID wasn't valid
System.out.println("Problems in the app server: the SESSIONID wasn't valid");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
} else {
// The SESSION does not exist
System.out.println("Problems in the app server: the SESSION does not exist");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/*******************/
/** Recording API **/
/*******************/
@RequestMapping(value = "/recording/start", method = RequestMethod.POST)
public ResponseEntity<?> startRecording(@RequestBody String param) throws ParseException {
JSONObject json = (JSONObject) new JSONParser().parse(param);
String sessionId = (String) json.get("session");
System.out.println("Starting recording | {sessionId}=" + sessionId);
try {
Archive archive = this.openVidu.startRecording(sessionId);
this.sessionRecordings.put(sessionId, true);
return new ResponseEntity<>(archive, HttpStatus.OK);
} catch (OpenViduException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
@RequestMapping(value = "/recording/stop", method = RequestMethod.POST)
public ResponseEntity<?> stopRecording(@RequestBody String param) throws ParseException {
JSONObject json = (JSONObject) new JSONParser().parse(param);
String recordingId = (String) json.get("recording");
System.out.println("Stoping recording | {recordingId}=" + recordingId);
try {
Archive archive = this.openVidu.stopRecording(recordingId);
this.sessionRecordings.remove(archive.getSessionId());
return new ResponseEntity<>(archive, HttpStatus.OK);
} catch (OpenViduException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
@RequestMapping(value = "/recording/delete", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteRecording(@RequestBody String param) throws ParseException {
JSONObject json = (JSONObject) new JSONParser().parse(param);
String recordingId = (String) json.get("recording");
System.out.println("Deleting recording | {recordingId}=" + recordingId);
try {
this.openVidu.deleteRecording(recordingId);
return new ResponseEntity<>(HttpStatus.OK);
} catch (OpenViduException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
@RequestMapping(value = "/recording/get/{recordingId}", method = RequestMethod.GET)
public ResponseEntity<?> getRecording(@PathVariable(value = "recordingId") String recordingId) {
System.out.println("Getting recording | {recordingId}=" + recordingId);
try {
Archive archive = this.openVidu.getRecording(recordingId);
return new ResponseEntity<>(archive, HttpStatus.OK);
} catch (OpenViduException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
@RequestMapping(value = "/recording/list", method = RequestMethod.GET)
public ResponseEntity<?> listRecordings() {
System.out.println("Listing recordings");
try {
List<Archive> archives = this.openVidu.listRecordings();
return new ResponseEntity<>(archives, HttpStatus.OK);
} catch (OpenViduException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
private ResponseEntity<JSONObject> getErrorResponse(Exception e) {
JSONObject json = new JSONObject();
json.put("cause", e.getCause());
json.put("error", e.getMessage());
json.put("exception", e.getClass());
return new ResponseEntity<>(json, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

View File

@ -0,0 +1,8 @@
spring.profiles.active=container
server.port: 3000
server.ssl.enabled: false
openvidu.url: http://localhost:5000/
openvidu.secret: MY_SECRET
openvidu.publicurl: ngrok

View File

@ -0,0 +1,9 @@
server.port: 5000
server.ssl.enabled: true
server.ssl.key-store: classpath:openvidu-selfsigned.jks
server.ssl.key-store-password: openvidu
server.ssl.key-store-type: JKS
server.ssl.key-alias: openvidu-selfsigned
openvidu.url: https://localhost:8443/
openvidu.secret: MY_SECRET

View File

@ -0,0 +1,242 @@
var OV;
var session;
var sessionId;
var token;
var sessionName;
var numVideos = 0;
/* OPENVIDU METHODS */
function joinSession() {
getSessionIdAndToken(function () {
// --- 1) Get an OpenVidu object and init a session with the retrieved sessionId ---
OV = new OpenVidu();
session = OV.initSession(sessionId);
// --- 2) Specify the actions when events take place ---
// On every new Stream received...
session.on('streamCreated', function (event) {
// Subscribe to the Stream to receive it
// HTML video will be appended to element with 'video-container' id
var subscriber = session.subscribe(event.stream, 'video-container');
// When the HTML video has been appended to DOM...
subscriber.on('videoElementCreated', function (event) {
// Add a new HTML element for the user's name and nickname over its video
updateNumVideos(1);
});
// When the HTML video has been appended to DOM...
subscriber.on('videoElementDestroyed', function (event) {
// Add a new HTML element for the user's name and nickname over its video
updateNumVideos(-1);
});
});
// --- 3) Connect to the session passing the retrieved token ---
session.connect(token, null, function (error) {
// If the connection is successful, initialize a publisher and publish to the session
if (!error) {
// --- 4) Get your own camera stream ---
var publisher = OV.initPublisher('video-container', {
audio: true, // Whether you want to transmit audio or not
video: true, // Whether you want to transmit video or not
audioActive: true, // Whether you want to start the publishing with your audio unmuted or muted
videoActive: true, // Whether you want to start the publishing with your video enabled or disabled
quality: 'MEDIUM', // The quality of your video ('LOW', 'MEDIUM', 'HIGH')
screen: false // true to get your screen as video source instead of your camera
});
// When our HTML video has been added to DOM...
publisher.on('videoElementCreated', function (event) {
updateNumVideos(1);
$(event.element).prop('muted', true); // Mute local video
});
// When the HTML video has been appended to DOM...
publisher.on('videoElementDestroyed', function (event) {
// Add a new HTML element for the user's name and nickname over its video
updateNumVideos(-1);
});
// --- 5) Publish your stream ---
session.publish(publisher);
} else {
console.warn('There was an error connecting to the session:', error.code, error.message);
}
});
$('#session-title').text(sessionName);
$('#join').hide();
$('#session').show();
return false;
});
}
function leaveSession() {
// 6) Leave the session by calling 'disconnect' method over the Session object
session.disconnect();
session = null;
numVideos = 0;
$('#join').show();
$('#session').hide();
}
/* OPENVIDU METHODS */
/* APPLICATION REST METHODS */
function getSessionIdAndToken(callback) {
sessionName = $("#sessionName").val(); // Video-call chosen by the user
var jsonBody = JSON.stringify({ // Body of POST request
'session': sessionName
});
// Send POST request
httpRequest('POST', 'api/get-sessionid-token', jsonBody, 'Request of SESSIONID and TOKEN gone WRONG:', function successCallback(response) {
sessionId = response[0]; // Get sessionId from response
token = response[1]; // Get token from response
console.warn('Request of SESSIONID and TOKEN gone WELL (SESSIONID:' + sessionId + ", TOKEN:" + token + ")");
callback(); // Continue the join operation
});
}
function removeUser() {
// Body of POST request with the name of the session and the token of the leaving user
var jsonBody = JSON.stringify({
'sessionName': sessionName,
'token': token
});
// Send POST request
httpRequest('POST', 'api/remove-user', jsonBody, 'User couldn\'t be removed from session', function successCallback(response) {
console.warn("User correctly removed from session");
});
}
function httpRequest(method, url, body, errorMsg, callback) {
$('#text-area').text('');
var http = new XMLHttpRequest();
http.open(method, url, true);
http.setRequestHeader('Content-type', 'application/json');
http.addEventListener('readystatechange', processRequest, false);
http.send(body);
function processRequest() {
if (http.readyState == 4) {
if (http.status == 200) {
try {
callback(JSON.parse(http.responseText));
} catch (e) {
callback();
}
} else {
console.warn(errorMsg + ' (' + http.status + ')');
console.warn(http.responseText);
$('#text-area').text(errorMsg + ": HTTP " + http.status + " (" + http.responseText + ")");
}
}
}
}
var recordingId;
function startRecording() {
var jsonBody = JSON.stringify({
'session': session.sessionId
});
httpRequest('POST', 'api/recording/start', jsonBody, 'Start recording WRONG', function successCallback(response) {
console.log(response);
recordingId = response.id;
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
function stopRecording() {
var jsonBody = JSON.stringify({
'recording': recordingId
});
httpRequest('POST', 'api/recording/stop', jsonBody, 'Stop recording WRONG', function successCallback(response) {
console.log(response);
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
function deleteRecording() {
var jsonBody = JSON.stringify({
'recording': recordingId
});
httpRequest('DELETE', 'api/recording/delete', jsonBody, 'Delete recording WRONG', function successCallback() {
console.log("DELETE ok");
$('#text-area').text("DELETE ok");
});
}
function getRecording() {
httpRequest('GET', 'api/recording/get/' + recordingId, '', 'Get recording WRONG', function successCallback(response) {
console.log(response);
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
function listRecordings() {
httpRequest('GET', 'api/recording/list', '', 'List recordings WRONG', function successCallback(response) {
console.log(response);
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
/* APPLICATION REST METHODS */
/* APPLICATION BROWSER METHODS */
window.onbeforeunload = function () { // Gracefully leave session
if (session) {
removeUser();
leaveSession();
}
}
function updateNumVideos(i) {
numVideos += i;
$('video').removeClass();
switch (numVideos) {
case 1:
$('video').addClass('two');
break;
case 2:
$('video').addClass('two');
break;
case 3:
$('video').addClass('three');
break;
case 4:
$('video').addClass('four');
break;
}
}
/* APPLICATION BROWSER METHODS */

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -0,0 +1,99 @@
<html>
<head>
<title>openvidu-recording-java</title>
<meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- Bootstrap -->
<link rel="styleSheet" href="style.css" type="text/css" media="screen">
<script src="openvidu-browser-1.8.0.js"></script>
<script src="app.js"></script>
<script>
$(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({
html: true
});
});
</script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">
<img class="demo-logo" src="images/openvidu_vert_white_bg_trans_cropped.png" /> Recording Java</a>
<a class="navbar-brand nav-icon" href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-recording-java"
title="GitHub Repository" target="_blank">
<i class="fa fa-github" aria-hidden="true"></i>
</a>
<a class="navbar-brand nav-icon" href="http://www.openvidu.io/docs/tutorials/openvidu-js-java/" title="Documentation" target="_blank">
<i class="fa fa-book" aria-hidden="true"></i>
</a>
</div>
</div>
</nav>
<div id="main-container" class="container">
<div id="join" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<div id="join-dialog" class="jumbotron">
<h1>Join a video session</h1>
<form class="form-group" onsubmit="return false">
<p>
<label>Session</label>
<input class="form-control" type="text" id="sessionName" value="SessionA" required>
</p>
<p class="text-center">
<button class="btn btn-lg btn-success" onclick="joinSession()">Join!</button>
</p>
</form>
<hr>
</div>
</div>
<div id="session" style="display: none">
<div id="session-header">
<h1 id="session-title"></h1>
<input class="btn btn-large btn-danger" type="button" id="buttonLeaveSession" onmouseup="removeUser(); leaveSession()" value="Leave session">
</div>
<div id="video-container" class="col-md-12"></div>
<div id="recording-btns">
<div id="btns">
<input class="btn btn-large" type="button" onmouseup="startRecording()" value="Start recording">
<input class="btn btn-large" type="button" onmouseup="stopRecording()" value="Stop recording">
<input class="btn btn-large" type="button" onmouseup="deleteRecording()" value="Delete recording">
<input class="btn btn-large" type="button" onmouseup="getRecording()" value="Get recording">
<input class="btn btn-large" type="button" onmouseup="listRecordings()" value="List recordings">
</div>
<textarea id="text-area" readonly="true" class="form-control" name="comment">HTTP responses...</textarea>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="text-muted">OpenVidu © 2017</div>
<a href="http://www.openvidu.io/" target="_blank">
<img class="openvidu-logo" src="images/openvidu_globe_bg_transp_cropped.png" />
</a>
</div>
</footer>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,399 @@
html {
position: relative;
min-height: 100%;
}
body {
min-height: 100%;
}
nav {
height: 50px;
width: 100%;
z-index: 1;
background-color: #4d4d4d !important;
border-color: #4d4d4d !important;
border-top-right-radius: 0 !important;
border-top-left-radius: 0 !important;
}
.navbar-header {
width: 100%;
}
.nav-icon {
padding: 5px 15px 5px 15px;
float: right;
}
nav a {
color: #ccc !important;
}
nav i.fa {
font-size: 40px;
color: #ccc;
}
nav a:hover {
color: #a9a9a9 !important;
}
nav i.fa:hover {
color: #a9a9a9;
}
#main-container {
padding-bottom: 80px;
height: 100%;
margin-top: -70px;
}
.vertical-center {
width: -webkit-fit-content;
width: fit-content;
margin: auto;
}
.vertical-center#not-logged form {
width: -moz-fit-content;
margin: auto;
}
.vertical-center#not-logged table {
width: -moz-fit-content;
margin: auto;
}
.vertical-center table {
margin-top: 3em !important;
}
.horizontal-center {
margin: 0 auto;
}
.form-control {
color: #0088aa;
font-weight: bold;
}
.form-control:focus {
border-color: #0088aa;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
}
input.btn {
font-weight: bold;
}
.btn {
font-weight: bold !important;
}
.btn-success {
background-color: #06d362 !important;
border-color: #06d362;
}
.btn-success:hover {
background-color: #1abd61 !important;
border-color: #1abd61;
}
.btn-info {
background-color: #0088aa !important;
border-color: #0088aa;
}
.btn-info:hover {
background-color: #00708c !important;
border-color: #00708c;
}
.btn-warning {
background-color: #ffcc00 !important;
border-color: #ffcc00;
color: #4d4d4d;
}
.btn-warning:hover {
background-color: #eabb3a !important;
border-color: #eabb3a;
color: #4d4d4d;
}
.btn-warning:active {
color: #4d4d4d;
}
.btn-warning:focus {
color: #4d4d4d;
}
.btn-warning:active:focus {
color: #4d4d4d;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
background-color: #4d4d4d;
}
.footer .text-muted {
margin: 20px 0;
float: left;
color: #ccc;
}
.openvidu-logo {
height: 35px;
float: right;
margin: 12px 0;
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.openvidu-logo:hover {
-webkit-filter: grayscale(0.5);
filter: grayscale(0.5);
}
.demo-logo {
margin: 0;
height: 22px;
float: left;
padding-right: 8px;
}
a:hover .demo-logo {
-webkit-filter: brightness(0.7);
filter: brightness(0.7);
}
#join {
padding-top: 40px;
}
#not-logged {
padding-top: 40px;
}
#join-dialog h1 {
color: #4d4d4d;
font-weight: bold;
text-align: center;
}
#join-dialog label {
color: #0088aa;
}
#join-dialog input.btn {
margin-top: 15px;
}
#join-dialog hr {
background: #4d4d4d;
}
#img-div {
text-align: center;
padding-bottom: 3em;
}
#img-div img {
height: 15%;
}
#logged {
width: 100%;
}
#join {
max-width: 700px;
margin: auto;
margin-top: 100px;
}
#session-header {
margin-bottom: 20px;
height: 8%;
margin-top: 70px;
}
#session-header form {
display: inline;
}
#session-title {
display: inline-block;
}
#buttonLeaveSession {
float: right;
margin-top: 20px;
}
#video-container {
width: 100%;
max-height: 45%;
display: inline-block;
overflow: hidden;
}
#video-container video.two{
width: 50%;
}
#video-container video.three{
width: 33.33%;
}
#video-container video.four{
width: 25%;
}
#video-container div {
position: absolute;
display: inline-flex;
margin-left: calc(-50% + 15px);
}
#video-container p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#video-container p.userName {
float: right;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
video {
width: 100%;
height: auto;
max-height: 100%;
object-fit: cover;
}
#session {
height: 100%;
padding-bottom: 80px;
}
#session img {
width: 100%;
height: auto;
display: inline-block;
object-fit: contain;
vertical-align: baseline;
}
#session #video-container img {
position: relative;
float: left;
width: 50%;
cursor: pointer;
object-fit: cover;
height: 180px;
}
table i {
cursor: pointer;
margin-left: 1em;
}
#tooltip-div {
text-align: left;
}
#tooltip-div hr {
margin: 5px 0;
}
#login-info {
text-align: right;
}
#login-info form {
display: inline;
}
#login-info div {
display: inline;
margin-right: 1em;
}
#name-user {
font-weight: bold;
}
#recording-btns {
display: flow-root;
padding-left: 15px;
padding-top: 20px;
width: 100%;
height: 40%;
}
#recording-btns input {
margin-top: 5px;
}
#recording-btns #btns {
margin-top: 5px;
}
#recording-btns #text-area {
display: inline-block;
width: 100%;
height: 80%;
margin-top: 20px;
}
/* xs ans md screen resolutions*/
@media screen and (max-width: 991px) {
#join {
padding-top: inherit;
}
#not-logged {
padding-top: inherit;
}
.container .navbar-header {
margin-right: 0 !important;
margin-left: 0 !important;
}
.nav-icon {
padding: 9px 8px 9px 8px;
}
nav i.fa {
font-size: 32px;
}
.vertical-center {
padding-top: 10px;
}
#img-div {
padding-bottom: 1em;
}
#img-div img {
height: 10%;
}
}

View File

@ -0,0 +1,38 @@
package io.openvidu.js.java.test;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}

60
openvidu-recording-node/.gitignore vendored Normal file
View File

@ -0,0 +1,60 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.vscode/

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

View File

@ -0,0 +1,13 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Documentation badge](https://readthedocs.org/projects/fiware-orion/badge/?version=latest)](http://openvidu.io/docs/home/)
[![Docker badge](https://img.shields.io/docker/pulls/fiware/orion.svg)](https://hub.docker.com/r/openvidu/)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://groups.google.com/forum/#!forum/openvidu)
[![][OpenViduLogo]](http://openvidu.io)
openvidu-recording-node
===
Visit [openvidu.io/docs/tutorials/openvidu-js-node/](http://openvidu.io/docs/tutorials/openvidu-js-node/)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDgzCCAmugAwIBAgIJALDBPXTrlAhlMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
BAYTAkVTMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQKDAhPcGVuVmlkdTEh
MB8GCSqGSIb3DQEJARYSb3BlbnZpZHVAZ21haWwuY29tMB4XDTE3MDUzMDE1MjQx
N1oXDTI3MDUyODE1MjQxN1owWDELMAkGA1UEBhMCRVMxEzARBgNVBAgMClNvbWUt
U3RhdGUxETAPBgNVBAoMCE9wZW5WaWR1MSEwHwYJKoZIhvcNAQkBFhJvcGVudmlk
dUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDbK/i
LN80IBnGUbzA4AFl4KQEX6RCreythnfOSnIcQSTP+KjesZMFgsGV0LteDxeCJ+kq
4YoS+CW7ojvOEz3xjCgo4tFdevz8ZoeO0RBQIbARbPako4OXC6vWs6LHwCR0aDWo
9HfS1Uusb8g77csRPRlNA3DGR8dcRTiEBdfHS6Jh/7V7XiDlaxPXj+iIY8PyCqOf
gv4clDt17R+dendDsgYxbmZaodrppNocMQIyUaDIwI4DZOa8nQYk9uuUhkiAFAQB
O642bx6NI0qDu5KgtMmbaS6s+rMnrL8eeZOicqff6XoC6tk6GE8Fo4iYkxp2gAiT
sigaSwpNRWhupISVAgMBAAGjUDBOMB0GA1UdDgQWBBTi3sXqpf42vUNehFU6y4Pn
PTJFRDAfBgNVHSMEGDAWgBTi3sXqpf42vUNehFU6y4PnPTJFRDAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBfl0C7XAbPdJzCjCsboMTzfC2B4uwspbST
MbUGnmNkTwzeOOsrLQmlCznKGprYV9vGR0MwBzYw2swWCzg/2MLN8swW7NF9gMkK
K61AANxT0qZV/aZhdM7W//pHJoQYsBsQT7cuTrL/VB/niD81uMA/mSWyXIn8KDIy
CPY6jiZ4qiIJnQWhWm1Cazv6O7wjDisvB1cCJhDvBv42KkFwtigt5MQnBEGOI2LD
iKCkXfj33E5B6n0sEel68WgYi6rx2tsR9lzAjCRF+jgNd7FfeUi999m7ykgEACR5
OYRqkVcIXz30r9RxQyFqZLNzyO9oaVZpex8ZbWwEzNXG2ccddnMJ
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw2yv4izfNCAZxlG8wOABZeCkBF+kQq3srYZ3zkpyHEEkz/io
3rGTBYLBldC7Xg8XgifpKuGKEvglu6I7zhM98YwoKOLRXXr8/GaHjtEQUCGwEWz2
pKODlwur1rOix8AkdGg1qPR30tVLrG/IO+3LET0ZTQNwxkfHXEU4hAXXx0uiYf+1
e14g5WsT14/oiGPD8gqjn4L+HJQ7de0fnXp3Q7IGMW5mWqHa6aTaHDECMlGgyMCO
A2TmvJ0GJPbrlIZIgBQEATuuNm8ejSNKg7uSoLTJm2kurPqzJ6y/HnmTonKn3+l6
AurZOhhPBaOImJMadoAIk7IoGksKTUVobqSElQIDAQABAoIBAGr/2HFjFjbpGJOw
b0O/oqRQUh2e7EYiCoOcK37E3iPAO1KvmG6OFayfwjSwG9bNNpbqGU2EPeBTA/3v
PwV/HZxinB5+yhl/3IKp9LDqoR7uwwNXgNf2O3d5SXX91zO9bXhbEn5WlEDYzl00
uxKtCVF//ZlgN+AoruxDbkVDGbkhKHGzvqOW+BWwbYHPOttQ9TQx2ss5+DekpDFV
/FuvXGOcSSV/N+WbDwsqUiM8ovkcflEgQjZYlAY4Ro2U2buf3fKEmy1jxSznNp30
QA4WyYypyS8Hz6S/F4nsjtS3ufCurrWBv4vt2qB+8tH//07NUacjQWZdvEAtaJ+G
IyJgsOUCgYEA38b2SQiw74fwoK6so2XdpCzOObLQBOT1aeZQjt3v8XZ0MPzwWJZm
VkMBmolcEw24xA3jHhOQYafAGjRdaKxlRbJyGJqwENfAs7hO5JSLLWWXsorJQcb7
1OVTFPt7NZopx2Kw7kGGuj9884w57cSl9lsuMptqk8W7pbTo5opOfl8CgYEA35CR
x23kaFDh8+zKUuSdIrkUJ66cazgOfF0FYKiDewd1sGEdZT+gq7Wo8Hi8cp9q+Kuq
MkGrTzu21yMi2fo7RKoSOtV4RQViu6PuoYR/KvRq4F4abHEAivAG4xhPQife84IB
NkDQXaE9EXTP7DXPwj4HV8CnC9LC2qPFU7GVeYsCgYBWPK6c5qSJKrIouif9sDwC
EOJIighwWmvZK9DPvefB/gw49MEK4qr9g0US8Oxyy07w/wkPhiqV97eoYZW9yPIe
Me6WXMaNNxgkKlr86+HW1NfpDmMQ3kYefWHPLDsHJSoElJvqtYXeMKlOkjOg1a+/
iNP83Lftyr3N1jIK5jHpsQKBgE1+clm7qOnT547C7JrxLdrEZs0ehI/R3YuUPvHz
V6gEvPHHqAXZmVsL3CSG5WOiCNVrw9Ip2zTa0RUf08vVJkg1353PMyJRrJi4SVZp
dB8ym/1sASLHxNVkQC7l1UtsQKcN0Fe6/b8GzgFICW6qdHqzP55WZFD/3JUnIZZS
PyrjAoGBAMfuE0Nrw9Fq7f8+U/SsqiW9djJp4R34EF5n8qHkotfRoAbGktCGxLoF
QMbB9X0ibRMpxEKPHj+V0FXu9zUKFuqoriRc3alQYLNzJEdKeQQ/AN5xjG3ilM6D
/FunImbvSdJLjR9Ue6vlFdOquHevBCFDiHOJPQJ/y4qyZR5Avluw
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,25 @@
{
"name": "openvidu-js-node",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "ng test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/OpenVidu/openvidu-tutorials.git"
},
"author": "openvidu@gmail.com",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/OpenVidu/openvidu-tutorials/issues"
},
"homepage": "https://github.com/OpenVidu/openvidu-tutorials#readme",
"dependencies": {
"body-parser": "^1.17.2",
"express": "^4.15.3",
"express-session": "^1.15.3",
"openvidu-node-client": "1.7.0"
}
}

View File

@ -0,0 +1,242 @@
var OV;
var session;
var sessionId;
var token;
var sessionName;
var numVideos = 0;
/* OPENVIDU METHODS */
function joinSession() {
getSessionIdAndToken(function () {
// --- 1) Get an OpenVidu object and init a session with the retrieved sessionId ---
OV = new OpenVidu();
session = OV.initSession(sessionId);
// --- 2) Specify the actions when events take place ---
// On every new Stream received...
session.on('streamCreated', function (event) {
// Subscribe to the Stream to receive it
// HTML video will be appended to element with 'video-container' id
var subscriber = session.subscribe(event.stream, 'video-container');
// When the HTML video has been appended to DOM...
subscriber.on('videoElementCreated', function (event) {
// Add a new HTML element for the user's name and nickname over its video
updateNumVideos(1);
});
// When the HTML video has been appended to DOM...
subscriber.on('videoElementDestroyed', function (event) {
// Add a new HTML element for the user's name and nickname over its video
updateNumVideos(-1);
});
});
// --- 3) Connect to the session passing the retrieved token ---
session.connect(token, null, function (error) {
// If the connection is successful, initialize a publisher and publish to the session
if (!error) {
// --- 4) Get your own camera stream ---
var publisher = OV.initPublisher('video-container', {
audio: true, // Whether you want to transmit audio or not
video: true, // Whether you want to transmit video or not
audioActive: true, // Whether you want to start the publishing with your audio unmuted or muted
videoActive: true, // Whether you want to start the publishing with your video enabled or disabled
quality: 'MEDIUM', // The quality of your video ('LOW', 'MEDIUM', 'HIGH')
screen: false // true to get your screen as video source instead of your camera
});
// When our HTML video has been added to DOM...
publisher.on('videoElementCreated', function (event) {
updateNumVideos(1);
$(event.element).prop('muted', true); // Mute local video
});
// When the HTML video has been appended to DOM...
publisher.on('videoElementDestroyed', function (event) {
// Add a new HTML element for the user's name and nickname over its video
updateNumVideos(-1);
});
// --- 5) Publish your stream ---
session.publish(publisher);
} else {
console.warn('There was an error connecting to the session:', error.code, error.message);
}
});
$('#session-title').text(sessionName);
$('#join').hide();
$('#session').show();
return false;
});
}
function leaveSession() {
// 6) Leave the session by calling 'disconnect' method over the Session object
session.disconnect();
session = null;
numVideos = 0;
$('#join').show();
$('#session').hide();
}
/* OPENVIDU METHODS */
/* APPLICATION REST METHODS */
function getSessionIdAndToken(callback) {
sessionName = $("#sessionName").val(); // Video-call chosen by the user
var jsonBody = JSON.stringify({ // Body of POST request
'session': sessionName
});
// Send POST request
httpRequest('POST', 'api/get-sessionid-token', jsonBody, 'Request of SESSIONID and TOKEN gone WRONG:', function successCallback(response) {
sessionId = response[0]; // Get sessionId from response
token = response[1]; // Get token from response
console.warn('Request of SESSIONID and TOKEN gone WELL (SESSIONID:' + sessionId + ", TOKEN:" + token + ")");
callback(); // Continue the join operation
});
}
function removeUser() {
// Body of POST request with the name of the session and the token of the leaving user
var jsonBody = JSON.stringify({
'sessionName': sessionName,
'token': token
});
// Send POST request
httpRequest('POST', 'api/remove-user', jsonBody, 'User couldn\'t be removed from session', function successCallback(response) {
console.warn("User correctly removed from session");
});
}
function httpRequest(method, url, body, errorMsg, callback) {
$('#text-area').text('');
var http = new XMLHttpRequest();
http.open(method, url, true);
http.setRequestHeader('Content-type', 'application/json');
http.addEventListener('readystatechange', processRequest, false);
http.send(body);
function processRequest() {
if (http.readyState == 4) {
if (http.status == 200) {
try {
callback(JSON.parse(http.responseText));
} catch (e) {
callback();
}
} else {
console.warn(errorMsg + ' (' + http.status + ')');
console.warn(http.responseText);
$('#text-area').text(errorMsg + ": HTTP " + http.status + " (" + http.responseText + ")");
}
}
}
}
var recordingId;
function startRecording() {
var jsonBody = JSON.stringify({
'session': session.sessionId
});
httpRequest('POST', 'api/recording/start', jsonBody, 'Start recording WRONG', function successCallback(response) {
console.log(response);
recordingId = response.id;
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
function stopRecording() {
var jsonBody = JSON.stringify({
'recording': recordingId
});
httpRequest('POST', 'api/recording/stop', jsonBody, 'Stop recording WRONG', function successCallback(response) {
console.log(response);
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
function deleteRecording() {
var jsonBody = JSON.stringify({
'recording': recordingId
});
httpRequest('DELETE', 'api/recording/delete', jsonBody, 'Delete recording WRONG', function successCallback() {
console.log("DELETE ok");
$('#text-area').text("DELETE ok");
});
}
function getRecording() {
httpRequest('GET', 'api/recording/get/' + recordingId, '', 'Get recording WRONG', function successCallback(response) {
console.log(response);
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
function listRecordings() {
httpRequest('GET', 'api/recording/list', '', 'List recordings WRONG', function successCallback(response) {
console.log(response);
$('#text-area').text(JSON.stringify(response, null, "\t"));
});
}
/* APPLICATION REST METHODS */
/* APPLICATION BROWSER METHODS */
window.onbeforeunload = function () { // Gracefully leave session
if (session) {
removeUser();
leaveSession();
}
}
function updateNumVideos(i) {
numVideos += i;
$('video').removeClass();
switch (numVideos) {
case 1:
$('video').addClass('two');
break;
case 2:
$('video').addClass('two');
break;
case 3:
$('video').addClass('three');
break;
case 4:
$('video').addClass('four');
break;
}
}
/* APPLICATION BROWSER METHODS */

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -0,0 +1,99 @@
<html>
<head>
<title>openvidu-recording-node</title>
<meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- Bootstrap -->
<link rel="styleSheet" href="style.css" type="text/css" media="screen">
<script src="openvidu-browser-1.8.0.js"></script>
<script src="app.js"></script>
<script>
$(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({
html: true
});
});
</script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">
<img class="demo-logo" src="images/openvidu_vert_white_bg_trans_cropped.png" /> Recording Node</a>
<a class="navbar-brand nav-icon" href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-recording-node"
title="GitHub Repository" target="_blank">
<i class="fa fa-github" aria-hidden="true"></i>
</a>
<a class="navbar-brand nav-icon" href="http://www.openvidu.io/docs/tutorials/openvidu-js-node/" title="Documentation" target="_blank">
<i class="fa fa-book" aria-hidden="true"></i>
</a>
</div>
</div>
</nav>
<div id="main-container" class="container">
<div id="join" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<div id="join-dialog" class="jumbotron">
<h1>Join a video session</h1>
<form class="form-group" onsubmit="return false">
<p>
<label>Session</label>
<input class="form-control" type="text" id="sessionName" value="SessionA" required>
</p>
<p class="text-center">
<button class="btn btn-lg btn-success" onclick="joinSession()">Join!</button>
</p>
</form>
<hr>
</div>
</div>
<div id="session" style="display: none">
<div id="session-header">
<h1 id="session-title"></h1>
<input class="btn btn-large btn-danger" type="button" id="buttonLeaveSession" onmouseup="removeUser(); leaveSession()" value="Leave session">
</div>
<div id="video-container" class="col-md-12"></div>
<div id="recording-btns">
<div id="btns">
<input class="btn btn-large" type="button" onmouseup="startRecording()" value="Start recording">
<input class="btn btn-large" type="button" onmouseup="stopRecording()" value="Stop recording">
<input class="btn btn-large" type="button" onmouseup="deleteRecording()" value="Delete recording">
<input class="btn btn-large" type="button" onmouseup="getRecording()" value="Get recording">
<input class="btn btn-large" type="button" onmouseup="listRecordings()" value="List recordings">
</div>
<textarea id="text-area" readonly="true" class="form-control" name="comment">HTTP responses...</textarea>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="text-muted">OpenVidu © 2017</div>
<a href="http://www.openvidu.io/" target="_blank">
<img class="openvidu-logo" src="images/openvidu_globe_bg_transp_cropped.png" />
</a>
</div>
</footer>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,399 @@
html {
position: relative;
min-height: 100%;
}
body {
min-height: 100%;
}
nav {
height: 50px;
width: 100%;
z-index: 1;
background-color: #4d4d4d !important;
border-color: #4d4d4d !important;
border-top-right-radius: 0 !important;
border-top-left-radius: 0 !important;
}
.navbar-header {
width: 100%;
}
.nav-icon {
padding: 5px 15px 5px 15px;
float: right;
}
nav a {
color: #ccc !important;
}
nav i.fa {
font-size: 40px;
color: #ccc;
}
nav a:hover {
color: #a9a9a9 !important;
}
nav i.fa:hover {
color: #a9a9a9;
}
#main-container {
padding-bottom: 80px;
height: 100%;
margin-top: -70px;
}
.vertical-center {
width: -webkit-fit-content;
width: fit-content;
margin: auto;
}
.vertical-center#not-logged form {
width: -moz-fit-content;
margin: auto;
}
.vertical-center#not-logged table {
width: -moz-fit-content;
margin: auto;
}
.vertical-center table {
margin-top: 3em !important;
}
.horizontal-center {
margin: 0 auto;
}
.form-control {
color: #0088aa;
font-weight: bold;
}
.form-control:focus {
border-color: #0088aa;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
}
input.btn {
font-weight: bold;
}
.btn {
font-weight: bold !important;
}
.btn-success {
background-color: #06d362 !important;
border-color: #06d362;
}
.btn-success:hover {
background-color: #1abd61 !important;
border-color: #1abd61;
}
.btn-info {
background-color: #0088aa !important;
border-color: #0088aa;
}
.btn-info:hover {
background-color: #00708c !important;
border-color: #00708c;
}
.btn-warning {
background-color: #ffcc00 !important;
border-color: #ffcc00;
color: #4d4d4d;
}
.btn-warning:hover {
background-color: #eabb3a !important;
border-color: #eabb3a;
color: #4d4d4d;
}
.btn-warning:active {
color: #4d4d4d;
}
.btn-warning:focus {
color: #4d4d4d;
}
.btn-warning:active:focus {
color: #4d4d4d;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
background-color: #4d4d4d;
}
.footer .text-muted {
margin: 20px 0;
float: left;
color: #ccc;
}
.openvidu-logo {
height: 35px;
float: right;
margin: 12px 0;
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.openvidu-logo:hover {
-webkit-filter: grayscale(0.5);
filter: grayscale(0.5);
}
.demo-logo {
margin: 0;
height: 22px;
float: left;
padding-right: 8px;
}
a:hover .demo-logo {
-webkit-filter: brightness(0.7);
filter: brightness(0.7);
}
#join {
padding-top: 40px;
}
#not-logged {
padding-top: 40px;
}
#join-dialog h1 {
color: #4d4d4d;
font-weight: bold;
text-align: center;
}
#join-dialog label {
color: #0088aa;
}
#join-dialog input.btn {
margin-top: 15px;
}
#join-dialog hr {
background: #4d4d4d;
}
#img-div {
text-align: center;
padding-bottom: 3em;
}
#img-div img {
height: 15%;
}
#logged {
width: 100%;
}
#join {
max-width: 700px;
margin: auto;
margin-top: 100px;
}
#session-header {
margin-bottom: 20px;
height: 8%;
margin-top: 70px;
}
#session-header form {
display: inline;
}
#session-title {
display: inline-block;
}
#buttonLeaveSession {
float: right;
margin-top: 20px;
}
#video-container {
width: 100%;
max-height: 45%;
display: inline-block;
overflow: hidden;
}
#video-container video.two{
width: 50%;
}
#video-container video.three{
width: 33.33%;
}
#video-container video.four{
width: 25%;
}
#video-container div {
position: absolute;
display: inline-flex;
margin-left: calc(-50% + 15px);
}
#video-container p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#video-container p.userName {
float: right;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
video {
width: 100%;
height: auto;
max-height: 100%;
object-fit: cover;
}
#session {
height: 100%;
padding-bottom: 80px;
}
#session img {
width: 100%;
height: auto;
display: inline-block;
object-fit: contain;
vertical-align: baseline;
}
#session #video-container img {
position: relative;
float: left;
width: 50%;
cursor: pointer;
object-fit: cover;
height: 180px;
}
table i {
cursor: pointer;
margin-left: 1em;
}
#tooltip-div {
text-align: left;
}
#tooltip-div hr {
margin: 5px 0;
}
#login-info {
text-align: right;
}
#login-info form {
display: inline;
}
#login-info div {
display: inline;
margin-right: 1em;
}
#name-user {
font-weight: bold;
}
#recording-btns {
display: flow-root;
padding-left: 15px;
padding-top: 20px;
width: 100%;
height: 40%;
}
#recording-btns input {
margin-top: 5px;
}
#recording-btns #btns {
margin-top: 5px;
}
#recording-btns #text-area {
display: inline-block;
width: 100%;
height: 80%;
margin-top: 20px;
}
/* xs ans md screen resolutions*/
@media screen and (max-width: 991px) {
#join {
padding-top: inherit;
}
#not-logged {
padding-top: inherit;
}
.container .navbar-header {
margin-right: 0 !important;
margin-left: 0 !important;
}
.nav-icon {
padding: 9px 8px 9px 8px;
}
nav i.fa {
font-size: 32px;
}
.vertical-center {
padding-top: 10px;
}
#img-div {
padding-bottom: 1em;
}
#img-div img {
height: 10%;
}
}

View File

@ -0,0 +1,239 @@
/* CONFIGURATION */
var OpenVidu = require('openvidu-node-client').OpenVidu;
var Session = require('openvidu-node-client').Session;
var OpenViduRole = require('openvidu-node-client').OpenViduRole;
var TokenOptions = require('openvidu-node-client').TokenOptions;
// Check launch arguments: must receive openvidu-server URL and the secret
if (process.argv.length != 4) {
console.log("Usage: node " + __filename + " OPENVIDU_URL OPENVIDU_SECRET");
process.exit(-1);
}
// For demo purposes we ignore self-signed certificate
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
// Node imports
var express = require('express');
var fs = require('fs');
var session = require('express-session');
var https = require('https');
var bodyParser = require('body-parser'); // Pull information from HTML POST (express4)
var app = express(); // Create our app with express
// Server configuration
app.use(session({
saveUninitialized: true,
resave: false,
secret: 'MY_SECRET'
}));
app.use(express.static(__dirname + '/public')); // Set the static files location
app.use(bodyParser.urlencoded({
'extended': 'true'
})); // Parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // Parse application/json
app.use(bodyParser.json({
type: 'application/vnd.api+json'
})); // Parse application/vnd.api+json as json
// Listen (start app with node server.js)
var options = {
key: fs.readFileSync('openvidukey.pem'),
cert: fs.readFileSync('openviducert.pem')
};
https.createServer(options, app).listen(5000);
// Environment variable: URL where our OpenVidu server is listening
var OPENVIDU_URL = process.argv[2];
// Environment variable: secret shared with our OpenVidu server
var OPENVIDU_SECRET = process.argv[3];
// OpenVidu object to ask openvidu-server for sessionId and token
var OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
// Collection to pair session names and OpenVidu Session objects
var mapSessionNameSession = {};
// Collection to pair sessionId's (identifiers of Session objects) and tokens
var mapSessionIdTokens = {};
console.log("App listening on port 5000");
/* Session API */
// Get sessionId and token (add new user to session)
app.post('/api/get-sessionid-token', function (req, res) {
// The video-call to connect ("TUTORIAL")
var sessionName = req.body.session;
console.log("Getting sessionId and token | {sessionName}={" + sessionName + "}");
// Role associated to this user
var role = OpenViduRole.PUBLISHER;
// Build tokenOptions object with the serverData and the role
var tokenOptions = new TokenOptions.Builder()
.role(role)
.build();
if (mapSessionNameSession[sessionName]) {
// Session already exists: return existing sessionId and a new token
console.log('Existing session ' + sessionName);
// Get the existing Session from the collection
var mySession = mapSessionNameSession[sessionName];
// Generate a new token asynchronously with the recently created tokenOptions
mySession.generateToken(tokenOptions, function (token) {
// Get the existing sessionId
mySession.getSessionId(function (sessionId) {
// Store the new token in the collection of tokens
mapSessionIdTokens[sessionId].push(token);
// Return the sessionId and token to the client
console.log('SESSIONID: ' + sessionId);
console.log('TOKEN: ' + token);
res.status(200).send({
0: sessionId,
1: token
});
});
});
} else { // New session: return a new sessionId and a new token
console.log('New session ' + sessionName);
// Create a new OpenVidu Session
var mySession = OV.createSession();
// Get the sessionId asynchronously
mySession.getSessionId(function (sessionId) {
// Store the new Session in the collection of Sessions
mapSessionNameSession[sessionName] = mySession;
// Store a new empty array in the collection of tokens
mapSessionIdTokens[sessionId] = [];
// Generate a new token asynchronously with the recently created tokenOptions
mySession.generateToken(tokenOptions, function (token) {
// Store the new token in the collection of tokens
mapSessionIdTokens[sessionId].push(token);
console.log('SESSIONID: ' + sessionId);
console.log('TOKEN: ' + token);
// Return the sessionId and token to the client
res.status(200).send({
0: sessionId,
1: token
});
});
});
}
});
// Remove user from session
app.post('/api/remove-user', function (req, res) {
// Retrieve params from POST body
var sessionName = req.body.sessionName;
var token = req.body.token;
console.log('Removing user | {sessionName, token}={' + sessionName + ', ' + token + '}');
// If the session exists
var mySession = mapSessionNameSession[sessionName];
if (mySession) {
mySession.getSessionId(function (sessionId) {
var tokens = mapSessionIdTokens[sessionId];
if (tokens) {
var index = tokens.indexOf(token);
// If the token exists
if (index !== -1) {
// Token removed!
tokens.splice(index, 1);
console.log(sessionName + ': ' + mapSessionIdTokens[sessionId].toString());
} else {
var msg = 'Problems in the app server: the TOKEN wasn\'t valid';
console.log(msg);
res.status(500).send(msg);
}
if (mapSessionIdTokens[sessionId].length == 0) {
// Last user left: session must be removed
console.log(sessionName + ' empty!');
delete mapSessionNameSession[sessionName];
}
res.status(200).send();
} else {
var msg = 'Problems in the app server: the SESSIONID wasn\'t valid';
console.log(msg);
res.status(500).send(msg);
}
});
} else {
var msg = 'Problems in the app server: the SESSION does not exist';
console.log(msg);
res.status(500).send(msg);
}
});
/* Recording API */
// Start session recording
app.post('/api/recording/start', function (req, res) {
// Retrieve params from POST body
var sessionId = req.body.session;
console.log("Starting recording | {sessionId}=" + sessionId);
OV.startRecording(sessionId)
.then(archive => res.status(200).send(archive))
.catch(error => res.status(400).send(error.message));
});
// Start session recording
app.post('/api/recording/stop', function (req, res) {
// Retrieve params from POST body
var recordingId = req.body.recording;
console.log("Stoping recording | {recordingId}=" + recordingId);
OV.stopRecording(recordingId)
.then(archive => res.status(200).send(archive))
.catch(error => res.status(400).send(error.message));
});
// Start session recording
app.delete('/api/recording/delete', function (req, res) {
// Retrieve params from DELETE body
var recordingId = req.body.recording;
console.log("Deleting recording | {recordingId}=" + recordingId);
OV.deleteRecording(recordingId)
.then(() => res.status(200).send())
.catch(error => res.status(400).send(error.message));
});
// Start session recording
app.get('/api/recording/get/:recordingId', function (req, res) {
// Retrieve params from GET url
var recordingId = req.params.recordingId;
console.log("Getting recording | {recordingId}=" + recordingId);
OV.getRecording(recordingId)
.then(archive => res.status(200).send(archive))
.catch(error => res.status(400).send(error.message));
});
// Start session recording
app.get('/api/recording/list', function (req, res) {
console.log("Listing recordings");
OV.listRecordings()
.then(archives => res.status(200).send(archives))
.catch(error => res.status(400).send(error.message));
});