openvidu-insecure-js 🔗
A client-side only application built with JavaScript, HTML and CSS.
If it is the first time you use OpenVidu, it is highly recommended to start with openvidu-hello-world tutorial, as this app is no more than an extension of it with some new features and styles.
Understanding this tutorial 🔗
OpenVidu is composed by the three modules displayed on the image above in its insecure version.
- openvidu-browser: JavaScript library for the browser. It allows you to manage your video-calls straight away from your clients
- openvidu-server: Java application that controls Kurento Media Server
- Kurento Media Server: server that handles low level operations of media flows transmission
Running this tutorial 🔗
1) Clone the repo:
git clone https://github.com/OpenVidu/openvidu-tutorials.git
2) You will need an http web server installed in your development computer to execute the sample application. If you have node.js installed, you can use http-server to serve application files. It can be installed with:
npm install -g http-server
3) Run the tutorial:
http-server openvidu-tutorials/openvidu-insecure-js/web
4) OpenVidu Platform service must be up and running in your development machine. The easiest way is running this Docker container which wraps both of them (you will need Docker CE):
# WARNING: this container is not suitable for production deployments of OpenVidu Platform
# Visit https://docs.openvidu.io/en/stable/deployment
docker run -p 4443:4443 --rm -e OPENVIDU_SECRET=MY_SECRET openvidu/openvidu-server-kms:2.17.0
5) Go to http://localhost:8080
to test the app once the server is running. The first time you use the docker container, an alert message will suggest you accept the self-signed certificate of openvidu-server when you first try to join a video-call.
If you are using Windows, read this FAQ to properly run the tutorial
To learn some tips to develop with OpenVidu, check this FAQ
Understanding the code 🔗
This application is very simple. It has only 4 files:
openvidu-browser-VERSION.js
: openvidu-browser library. You don't have to manipulate this file.app.js
: sample application main JavaScript file, which makes use of openvidu-browser-VERSION.js. You can manipulate this file to suit your needs.style.css
: some CSS classes to style index.html. You can manipulate this file to suit your needs.index.html
: HTML code for the form to connect to a video-call and for the video-call itself. You can manipulate this file to suit your needs. It has two links to both JavaScript files:
<script src="openvidu-browser-VERSION.js"></script>
<script src="app.js"></script>
Let's see how app.js
uses openvidu-browser-VERSION.js
:
First lines declare the variables that will be needed in different points along the code 🔗
var OV;
var session;
OV
will be our OpenVidu object (entrypoint to the library). session
will be the video-call we will connect to. As first sentences in the join method, we initialize the two parameters whose value is retrieved from the HTML inputs.
var mySessionId = document.getElementById("sessionId").value; // Session the user will join
var myUserName = document.getElementById("userName").value; // Nickname of the user in the session
Let's initialize a new session and configure our events: 🔗
// --- 1) Get an OpenVidu object ---
OV = new OpenVidu();
// --- 2) Init a session ---
session = OV.initSession();
As you can see in the code, the process is very simple: get an OpenVidu object and initialize a Session object with it.
// --- 3) Specify the actions when events take place in the session ---
// On every new Stream received...
session.on('streamCreated', 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', event => {
// Add a new <p> element for the user's nickname just below its video
appendUserData(event.element, subscriber.stream.connection);
});
});
// On every Stream destroyed...
session.on('streamDestroyed', event => {
// Delete the HTML element with the user's nickname. HTML videos are automatically removed from DOM
removeUserData(event.stream.connection);
});
Here we subscribe to the events that interest us. In this case, we want to receive all videos published to the session, as well as displaying every user's nickname next to its video. To achieve this:
-
streamCreated
: for each new Stream received by OpenVidu, we immediately subscribe to it so we can see its video. A new HTML video element will be appended to element with id 'video-container'. -
videoElementCreated
: event triggered by Subscriber object (returned by the previousSession.subscribe
method). This allows us to add the participant nickname to the new video previously added instreamCreated
event. Auxiliary methodappendUserData
is responsible for appending a new paragraph element just below theevent.element
video, containingsubscriber.stream.connection.data
field. In this case, this field contains the user's nickName. You can see how to feed this property from the client in a later step. -
streamDestroyed
: for each Stream that has been destroyed (which means a user has left the video-call), we remove the element with the user's nickname that we added in the previous event with the auxiliary methodremoveUserData
(appendUserData
method created the element with an id containingevent.stream.connection.connectionId
unique value, so we can now identify the right element to be removed). OpenVidu automatically deletes the proper video element by default, so we don't need to do anything else.
Check Application specific methods section to see all the auxiliary methods used in this app
Get a token from OpenVidu Server 🔗
// --- 4) Connect to the session with a valid user token ---
// 'getToken' method is simulating what your server-side should do.
// 'token' parameter should be retrieved and returned by your own backend
getToken(mySessionId).then(token => {
// See next point to see how to connect to the session using 'token'
});
Now we need a token from OpenVidu Server. In a production environment we would perform this operations in our application backend, by making use of the REST API, OpenVidu Java Client or OpenVidu Node Client. Here we have implemented the POST requests to OpenVidu Server in a method getToken()
that returns a Promise with the token. Without going into too much detail, this method performs two ajax requests to OpenVidu Server, passing OpenVidu Server secret to authenticate them:
- First ajax request performs a POST to
/openvidu/api/sessions
(we send acustomSessionId
field to name the session with ourmySessionId
value retrieved from HTML input) - Second ajax request performs a POST to
/openvidu/api/sessions/<sessionId>/connection
(the path requires thesessionId
to assign the token to this same session)
You can inspect this method in detail in the GitHub repo.
Finally connect to the session using the token and publish your webcam: 🔗
// --- 4) Connect to the session with a valid user token ---
// 'getToken' method is simulating what your server-side should do.
// 'token' parameter should be retrieved and returned by your own backend
getToken(mySessionId).then(token => {
// First param is the token got from OpenVidu Server. Second param can be retrieved by every user on event
// 'streamCreated' (property Stream.connection.data), and will be appended to DOM as the user's nickname
session.connect(token, { clientData: myUserName })
.then(() => {
// --- 5) Set page layout for active call ---
document.getElementById('session-title').innerText = mySessionId;
document.getElementById('join').style.display = 'none';
document.getElementById('session').style.display = 'block';
// --- 6) Get your own camera stream with the desired properties ---
var publisher = OV.initPublisher('video-container', {
audioSource: undefined, // The source of audio. If undefined default microphone
videoSource: undefined, // The source of video. If undefined default webcam
publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
publishVideo: true, // Whether you want to start publishing with your video enabled or not
resolution: '640x480', // The resolution of your video
frameRate: 30, // The frame rate of your video
insertMode: 'APPEND', // How the video is inserted in the target element 'video-container'
mirror: false // Whether to mirror your local video or not
});
// --- 7) Specify the actions when events take place in our publisher ---
// When our HTML video has been added to DOM...
publisher.on('videoElementCreated', function (event) {
initMainVideo(event.element, myUserName);
appendUserData(event.element, myUserName);
event.element['muted'] = true;
});
// --- 8) Publish your stream ---
session.publish(publisher);
})
.catch(error => {
console.log('There was an error connecting to the session:', error.code, error.message);
});
});
In session.connect
method first param is the recently retrieved user token. Remember videoElementCreated
event for the Subscriber object, when we added the user's nickname to the HTML? Well, second parameter is the actual value every user will receive in Stream.connection.data
property in that event. So in this case it is an object with a property "clientData" with value "myUserName", which has been set in the first step to the value retrieved from HTML input <input class="form-control" type="text" id="userName" required>
(this is filled by the user).
If the method succeeds, we first change our view to the active call (5) and then proceed to publish our webcam to the session. To do so we get a Publisher
object with the desired properties (6). This process will end with the addition of a new HTML video element showing your camera, as a child of element with id 'video-container'. Event videoElementCreated
will be fired by the Publisher object just after this video is added to DOM, so we can subscribe to it and do whatever we want with it. In this case, we init another bigger video element with our video and append our nickname to it, by using auxiliary methods initMainVideo
and appendUserData
(7).
Finally we just have to publish publisher
object through Session.publish
method (8), and the rest of users will begin receiving our webcam ('streamCreated' event will be fired for them).
Leaving the session 🔗
Whenever we want a user to leave the session, we just need to call session.disconnect
method:
function leaveSession() {
// --- 9) Leave the session by calling 'disconnect' method over the Session object ---
session.disconnect();
// Removing all HTML elements with user's nicknames.
// HTML videos are automatically removed when leaving a Session
removeAllUserData();
// Back to 'Join session' page
document.getElementById('join').style.display = 'block';
document.getElementById('session').style.display = 'none';
}
Application specific methods 🔗
Here you have all the auxiliary methods used in this app, which are not directly related to OpenVidu:
/* APPLICATION SPECIFIC METHODS */
window.addEventListener('load', function () {
generateParticipantInfo();
});
window.onbeforeunload = function () {
if (session) session.disconnect();
};
function generateParticipantInfo() {
document.getElementById("sessionId").value = "SessionA";
document.getElementById("userName").value = "Participant" + Math.floor(Math.random() * 100);
}
function appendUserData(videoElement, connection) {
var userData;
var nodeId;
if (typeof connection === "string") {
userData = connection;
nodeId = connection;
} else {
userData = JSON.parse(connection.data).clientData;
nodeId = connection.connectionId;
}
var dataNode = document.createElement('div');
dataNode.className = "data-node";
dataNode.id = "data-" + nodeId;
dataNode.innerHTML = "<p>" + userData + "</p>";
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
addClickListener(videoElement, userData);
}
function removeUserData(connection) {
var dataNode = document.getElementById("data-" + connection.connectionId);
dataNode.parentNode.removeChild(dataNode);
}
function removeAllUserData() {
var nicknameElements = document.getElementsByClassName('data-node');
while (nicknameElements[0]) {
nicknameElements[0].parentNode.removeChild(nicknameElements[0]);
}
}
function addClickListener(videoElement, userData) {
videoElement.addEventListener('click', function () {
var mainVideo = document.querySelector('#main-video video');
var mainUserData = document.querySelector('#main-video p');
if (mainVideo.srcObject !== videoElement.srcObject) {
mainUserData.innerHTML = userData;
mainVideo.srcObject = videoElement.srcObject;
}
});
}
function initMainVideo(videoElement, userData) {
document.querySelector('#main-video video').srcObject = videoElement.srcObject;
document.querySelector('#main-video p').innerHTML = userData;
document.querySelector('#main-video video')['muted'] = true;
}
Deploy openvidu-insecure-js 🔗
Under the root project folder, you can see the openvidu-insecure-js/docker/
directory. Here it is included all the required files yo make it possible the deployment with OpenVidu.
First of all, you will need to create the openvidu-insecure-js docker image.
1) Run create_image.sh
script:
./create_image.sh
This script will create an image named openvidu/openvidu-insecure-js-demo:X.Y.Z
. If you want to create a image with another different name, you can do it change the name here. Once the openvidu-classrom image has been created, you will can deploy it.
2) Redefine the /opt/openvidu/docker-compose.override.yml
Now you will have to redefine the /opt/openvidu/docker-compose.override.yml
in your OpenVidu deployment and you have to take account change the image name by your custom name (openvidu/openvidu-insecure-js-demo
on this sample).
Here it is the docker-compose-override.yml
used by OpenVidu insecure-js application.
version: '3.1'
services:
app:
image: openvidu/openvidu-insecure-js-demo:2.17.0
restart: on-failure
network_mode: host
environment:
- OPENVIDU_URL=http://localhost:5443
- OPENVIDU_SECRET=${OPENVIDU_SECRET}