openvidu-ipcameras
This commit is contained in:
parent
ed489a9685
commit
01eb95591c
24
openvidu-ipcameras/.gitignore
vendored
Normal file
24
openvidu-ipcameras/.gitignore
vendored
Normal 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/
|
||||
59
openvidu-ipcameras/pom.xml
Normal file
59
openvidu-ipcameras/pom.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<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>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<groupId>io.openvidu</groupId>
|
||||
<artifactId>openvidu-ipcameras</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>openvidu-ipcameras</name>
|
||||
<url>https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-ipcameras</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.openvidu</groupId>
|
||||
<artifactId>openvidu-java-client</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,60 @@
|
||||
package io.openvidu.ipcameras;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
/**
|
||||
* Run this application with the following command:
|
||||
*
|
||||
* java -jar -Dopenvidu-url=https://your.url.com -Dopenvidu-secret=your_secret openvidu-ipcameras-1.0.0.jar
|
||||
*
|
||||
* @author Pablo Fuente (pablofuenteperez@gmail.com)
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@PropertySource("classpath:application.properties")
|
||||
public class App {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(App.class);
|
||||
|
||||
static String OPENVIDU_URL;
|
||||
static String OPENVIDU_SECRET;
|
||||
static Map<String, String> IP_CAMERAS = new HashMap<String, String>() {
|
||||
{
|
||||
put("Beach camera 1", "rtsp://b1.dnsdojo.com:1935/live/sys3.stream");
|
||||
put("Beach camera 2", "rtsp://b1.dnsdojo.com:1935/live/sys5.stream");
|
||||
put("City", "rtsp://91.191.213.49:554/live_mpeg4.sdp");
|
||||
}
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// Start application
|
||||
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
|
||||
|
||||
OPENVIDU_URL = context.getEnvironment().getProperty("openvidu-url");
|
||||
OPENVIDU_SECRET = context.getEnvironment().getProperty("openvidu-secret");
|
||||
|
||||
// Check OPENVIDU_URL parameter
|
||||
try {
|
||||
new URL(OPENVIDU_URL).toURI();
|
||||
OPENVIDU_URL = OPENVIDU_URL.endsWith("/") ? OPENVIDU_URL : (OPENVIDU_URL + "/");
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
log.error("Parameter \"openvidu-url\" ({}) has not a valid URL format", OPENVIDU_URL);
|
||||
System.exit(1);
|
||||
}
|
||||
log.info("OpenVidu URL is {}", OPENVIDU_URL);
|
||||
log.info("OpenVidu secret is {}", OPENVIDU_SECRET);
|
||||
log.info("Camera list is {}", IP_CAMERAS.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package io.openvidu.ipcameras;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import io.openvidu.java.client.OpenVidu;
|
||||
import io.openvidu.java.client.OpenViduHttpException;
|
||||
import io.openvidu.java.client.OpenViduJavaClientException;
|
||||
import io.openvidu.java.client.Session;
|
||||
import io.openvidu.java.client.SessionProperties;
|
||||
|
||||
/**
|
||||
* Rest controller that offers a single entry point ("/"), where users can
|
||||
* request for a token to enter the OpenVidu session if their credentials are
|
||||
* right. First time a user provides the required credentials, the OpenVidu
|
||||
* session will be created and the cameras will be published just before
|
||||
* generating and returning the user's token
|
||||
*
|
||||
* @author Pablo Fuente (pablofuenteperez@gmail.com)
|
||||
*/
|
||||
@Controller
|
||||
public class MyRestController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(App.class);
|
||||
|
||||
private final String USER_CREDENTIALS = "PASSWORD";
|
||||
private final String SESSION_ID = "MySurveillanceSession";
|
||||
|
||||
// OpenVidu objects
|
||||
private OpenVidu OV;
|
||||
private Session session;
|
||||
|
||||
// A simple HTTP client to perform OpenVidu REST API operations that are not
|
||||
// available yet in OpenVidu Java Client
|
||||
private final SimpleHttpClient httpClient = new SimpleHttpClient();
|
||||
|
||||
@RequestMapping(value = "/")
|
||||
public String subscribe(@RequestParam(name = "credentials", required = false) String credentials, Model model) {
|
||||
|
||||
try {
|
||||
checkCredentials(credentials);
|
||||
} catch (Exception e) {
|
||||
return generateError(model, "Wrong credentials");
|
||||
}
|
||||
|
||||
// Create our surveillance session if not available yet
|
||||
if (OV == null || session == null) {
|
||||
try {
|
||||
createOpenViduSession();
|
||||
publishCameras();
|
||||
} catch (OpenViduJavaClientException | OpenViduHttpException e) {
|
||||
return generateError(model,
|
||||
"Error sending request to OpenVidu Server: " + e.getCause() + ". " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a token for the user
|
||||
String token = null;
|
||||
try {
|
||||
token = this.session.generateToken();
|
||||
} catch (OpenViduJavaClientException | OpenViduHttpException e) {
|
||||
return generateError(model,
|
||||
"Error creating OpenVidu token for session " + SESSION_ID + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
model.addAttribute("token", token);
|
||||
return "index";
|
||||
}
|
||||
|
||||
private void createOpenViduSession() throws OpenViduJavaClientException, OpenViduHttpException {
|
||||
// Init OpenVidu entrypoint object
|
||||
OV = new OpenVidu(App.OPENVIDU_URL, App.OPENVIDU_SECRET);
|
||||
// Get active sessions from OpenVidu Server
|
||||
OV.fetch();
|
||||
try {
|
||||
// See if our surveillance session is already created in OpenVidu Server
|
||||
session = OV.getActiveSessions().stream().filter(s -> s.getSessionId().equals(SESSION_ID)).findFirst()
|
||||
.get();
|
||||
log.info("Session {} already existed in OpenViduU Server", SESSION_ID);
|
||||
} catch (NoSuchElementException e) {
|
||||
// Create our surveillance session if it does not exist yet in OpenVidu Server
|
||||
log.info("Session {} does not in OpenViduU Servery yet. Creating it...", SESSION_ID);
|
||||
SessionProperties properties = new SessionProperties.Builder().customSessionId(SESSION_ID).build();
|
||||
session = OV.createSession(properties);
|
||||
log.info("Session {} created", SESSION_ID);
|
||||
}
|
||||
}
|
||||
|
||||
private void publishCameras() throws OpenViduJavaClientException, OpenViduHttpException {
|
||||
|
||||
// See if we have already published any of our cameras
|
||||
// We fetch our only session current status and search for connections with
|
||||
// platform "IPCAM". Finally we get their server data field with the camera name
|
||||
session.fetch();
|
||||
List<String> alreadyPublishedCameras = session.getActiveConnections().stream()
|
||||
.filter(connection -> "IPCAM".equals(connection.getPlatform()))
|
||||
.map(connection -> connection.getServerData()).collect(Collectors.toList());
|
||||
|
||||
for (Entry<String, String> cameraMapEntry : App.IP_CAMERAS.entrySet()) {
|
||||
try {
|
||||
String cameraUri = cameraMapEntry.getValue();
|
||||
String cameraName = cameraMapEntry.getKey();
|
||||
if (!alreadyPublishedCameras.contains(cameraName)) {
|
||||
// Publish the camera only if it is not already published
|
||||
httpClient.publishIpCamera(SESSION_ID, cameraUri, cameraName, true, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error publishing camera {}", cameraMapEntry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCredentials(String credentials) throws Exception {
|
||||
// Dummy security: if not expected string, then throw error
|
||||
if (!credentials.equals(USER_CREDENTIALS)) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
private String generateError(Model model, String message) {
|
||||
log.error(message);
|
||||
model.addAttribute("error", message);
|
||||
return "index";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package io.openvidu.ipcameras;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.apache.http.ssl.TrustStrategy;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple HTTP client able to send REST API requests to insecure servers
|
||||
* (self-signed certificates are accepted). It only implements a single method
|
||||
* to publish an IP camera to an OpenVidu Server session
|
||||
*
|
||||
* @author Pablo Fuente (pablofuenteperez@gmail.com)
|
||||
*/
|
||||
public class SimpleHttpClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(App.class);
|
||||
|
||||
private CloseableHttpClient httpClient;
|
||||
|
||||
public SimpleHttpClient() {
|
||||
|
||||
TrustStrategy trustStrategy = new TrustStrategy() {
|
||||
@Override
|
||||
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
SSLContext sslContext;
|
||||
try {
|
||||
sslContext = new SSLContextBuilder().loadTrustMaterial(null, trustStrategy).build();
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
RequestConfig.Builder requestBuilder = RequestConfig.custom();
|
||||
requestBuilder = requestBuilder.setConnectTimeout(30000);
|
||||
requestBuilder = requestBuilder.setConnectionRequestTimeout(30000);
|
||||
|
||||
httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestBuilder.build())
|
||||
.setConnectionTimeToLive(30, TimeUnit.SECONDS).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
|
||||
.setSSLContext(sslContext).build();
|
||||
}
|
||||
|
||||
public void publishIpCamera(String sessionId, String rtspUri, String cameraName, boolean adaptativeBitrate,
|
||||
boolean onlyPlayWhenSubscribers) throws Exception {
|
||||
|
||||
HttpPost request = new HttpPost(App.OPENVIDU_URL + "api/sessions/" + sessionId + "/connection");
|
||||
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("rtspUri", rtspUri);
|
||||
json.put("data", cameraName);
|
||||
json.put("adaptativeBitrate", adaptativeBitrate);
|
||||
json.put("onlyPlayWithSubscribers", onlyPlayWhenSubscribers);
|
||||
StringEntity params = new StringEntity(json.toString());
|
||||
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
|
||||
request.setHeader(HttpHeaders.AUTHORIZATION,
|
||||
"Basic " + Base64.getEncoder().encodeToString(("OPENVIDUAPP:" + App.OPENVIDU_SECRET).getBytes()));
|
||||
request.setEntity(params);
|
||||
|
||||
HttpResponse response;
|
||||
try {
|
||||
response = this.httpClient.execute(request);
|
||||
} catch (IOException e) {
|
||||
log.error("Error publishing IP camera {}: {}", rtspUri, e.getMessage());
|
||||
throw new Exception(e.getMessage(), e.getCause());
|
||||
}
|
||||
try {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
|
||||
log.info("IP camera '{}' published", rtspUri);
|
||||
} else {
|
||||
log.error("Error publishing IP camera {}: Http status {}", rtspUri, statusCode);
|
||||
throw new Exception(Integer.toString(statusCode));
|
||||
}
|
||||
} finally {
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
server.port: 8080
|
||||
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:4443/
|
||||
openvidu-secret: MY_SECRET
|
||||
BIN
openvidu-ipcameras/src/main/resources/openvidu-selfsigned.jks
Normal file
BIN
openvidu-ipcameras/src/main/resources/openvidu-selfsigned.jks
Normal file
Binary file not shown.
1
openvidu-ipcameras/src/main/resources/static/openvidu-browser-2.12.0-SNAPSHOT.min.js
vendored
Normal file
1
openvidu-ipcameras/src/main/resources/static/openvidu-browser-2.12.0-SNAPSHOT.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
44
openvidu-ipcameras/src/main/resources/static/style.css
Normal file
44
openvidu-ipcameras/src/main/resources/static/style.css
Normal file
@ -0,0 +1,44 @@
|
||||
form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cameras {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.camera-name {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
video {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 16px solid #f3f3f3;
|
||||
border-top: 16px solid #0088aa;
|
||||
border-radius: 50%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
animation: spin 2s linear infinite;
|
||||
position: absolute;
|
||||
left: calc(50% - 66px);
|
||||
top: calc(50% - 66px);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
128
openvidu-ipcameras/src/main/resources/templates/index.html
Normal file
128
openvidu-ipcameras/src/main/resources/templates/index.html
Normal file
@ -0,0 +1,128 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>OpenVidu IP cameras demo</title>
|
||||
<link rel="styleSheet" href="style.css" type="text/css" media="screen">
|
||||
</link>
|
||||
<script src="openvidu-browser-2.12.0-SNAPSHOT.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>OpenVidu IP cameras demo</h1>
|
||||
<form class="form-group" action="/" method="get">
|
||||
<input type="text" name="credentials" placeholder="credentials" required="true" value="PASSWORD"></input>
|
||||
<button type="submit">Subscribe to cameras</button>
|
||||
</form>
|
||||
<button onclick="unsubscribe()">Unsubscribe from cameras</button>
|
||||
<div id="cameras"></div>
|
||||
</body>
|
||||
|
||||
<script th:inline="javascript">
|
||||
// OpenVidu objects
|
||||
var OV
|
||||
var session;
|
||||
var videosContainer = document.getElementById('cameras');
|
||||
|
||||
// Get all the attributes from the template in Thymeleaf style
|
||||
var error = [[${error}]];
|
||||
var token = [[${token}]];
|
||||
|
||||
if (!!error) {
|
||||
alert(error);
|
||||
} else if (!!token) {
|
||||
// Subscribe to OpenVidu session only when token is available
|
||||
subscribe(token);
|
||||
}
|
||||
|
||||
function subscribe() {
|
||||
|
||||
// Initialize OpenVidu and Session objects
|
||||
OV = new OpenVidu();
|
||||
session = OV.initSession();
|
||||
|
||||
// On every new Stream received...
|
||||
session.on('streamCreated', event => {
|
||||
|
||||
// Subscribe to the Stream to receive it
|
||||
// HTML video will be appended to a new element created inside <div id='cameras'>
|
||||
var videoDiv = document.createElement('div');
|
||||
var stream = event.stream;
|
||||
videoDiv.classList.add('video-container');
|
||||
videoDiv.id = stream.streamId;
|
||||
videosContainer.appendChild(videoDiv);
|
||||
// Append video inside our brand new <div> element
|
||||
var subscriber = session.subscribe(stream, videoDiv);
|
||||
|
||||
// When the HTML video has been appended to DOM...
|
||||
subscriber.on('videoElementCreated', ev => {
|
||||
// ...append camera name on top of video
|
||||
var cameraName = document.createElement('div');
|
||||
cameraName.innerText = stream.connection.data;
|
||||
cameraName.classList.add('camera-name');
|
||||
ev.element.parentNode.insertBefore(cameraName, ev.element);
|
||||
// ...start loader
|
||||
var loader = document.createElement('div');
|
||||
loader.classList.add('loader');
|
||||
ev.element.parentNode.insertBefore(loader, ev.element.nextSibling);
|
||||
});
|
||||
|
||||
// When the HTML video starts playing...
|
||||
subscriber.on('streamPlaying', ev => {
|
||||
// ...remove loader
|
||||
var cameraVideoElement = subscriber.videos[0].video;
|
||||
cameraVideoElement.parentNode.removeChild(cameraVideoElement.nextSibling);
|
||||
// ... mute video if browser blocked autoplay
|
||||
autoplayMutedVideoIfBlocked(cameraVideoElement);
|
||||
});
|
||||
|
||||
// When the HTML video has been removed from DOM...
|
||||
subscriber.on('videoElementDestroyed', ev => {
|
||||
// ...remove the HTML elements related to the destroyed video
|
||||
var videoContainer = document.getElementById(stream.streamId);
|
||||
videoContainer.parentNode.removeChild(videoContainer);
|
||||
});
|
||||
});
|
||||
|
||||
// Connect to session. We will receive all necessary events when success
|
||||
session.connect(token)
|
||||
.catch(error => {
|
||||
var msg = 'There was an error connecting to the session. Code: ' + error.code + '. Message: ' + error
|
||||
.message;
|
||||
console.error(msg);
|
||||
alert(msg);
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribe() {
|
||||
if (!!session) {
|
||||
// Leave OpenVidu Session
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
function autoplayMutedVideoIfBlocked(video) {
|
||||
// Browser can block video playback if it is auto played without user interaction
|
||||
// One solution is to mute the video and let the user know
|
||||
video.controls = true;
|
||||
var promise = video.play();
|
||||
if (promise !== undefined) {
|
||||
promise.then(() => {
|
||||
// Autoplay started
|
||||
}).catch(error => {
|
||||
// The video must play muted until user hits play button
|
||||
video.muted = true;
|
||||
video.play();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe from OpenVidu session when leaving the page
|
||||
window.addEventListener("beforeunload", () => {
|
||||
unsubscribe();
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@ -0,0 +1,38 @@
|
||||
package es.codeurjc.openvidu.ipcameras;
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user