OpenVidu is a platform to facilitate the addition of video calls in your web or mobile application, either group or one-to-one calls. In fact, any combination you come up with is easy to implement with OpenVidu.
It is based on Kurento, the WebRTC platform for multimedia applications. Openvidu was forked from KurentoRoom project.
OpenVidu and Kurento are licensed under Apache License v2.
- Running a videocall demo
- Building a simple app
- Securization
- Sharing data between users
- API Reference
- Deploying on AWS
- Developing OpenVidu
- Acknowledgments
We have implemented a very basic demo application to see OpenVidu in action. To ease the installation, we have packaged it as a docker image.
-
Please be sure that you have docker CE installed
-
Run this Docker container
docker run -p 5000:5000 -p 4040:4040 -e KMS_STUN_IP=193.147.51.12 -e KMS_STUN_PORT=3478 -e openvidu.security=false openvidu/openvidu-plainjs-demo
-
Wait until you see a public URL ended with
.ngrok.io
. You can connect locally inlocalhost:5000
or by using the ngrok public URL. You can also share this URL with anyone you want to test the app over the Internet!
OpenVidu has a traditional Client - Server architecture built on three modules that are shown in the image above. To run openvidu-server and Kurento Media Server you can execute the following container:
docker run -p 8443:8443 --rm -e KMS_STUN_IP=193.147.51.12 -e KMS_STUN_PORT=3478 -e openvidu.security=false openvidu/openvidu-server-kms
Then, you have to use the library openvidu-browser in your JavaScript browser application (frontend). This library is packaged in [OpenVidu.js] file that you can download from https://github.com/OpenVidu/openvidu/blob/master/openvidu-browser/src/main/resources/static/js/OpenVidu.js. Then add the file in your HTML with <script src="OpenVidu.js"></script>
.
With the openvidu-browser library you can handle all available operations straight away from your client, as creating video calls, joining users to them or publishing/unpublishing video and audio
Once you have up and running Kurento Media Server and openvidu-server, you just need to add a few lines of code in your frontend to make your first video call with OpenVidu. You can take a look to the simplest sample application in GitHub https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-insecure-js.
You can clone the repo and serve the app locally with your favourite tool (we recommend http-server: npm install -g http-server
)
git clone https://github.com/OpenVidu/openvidu-tutorials.git
cd openvidu-tutorials/openvidu-insecure-js/web
http-server
You can now start editing HTML, JS and CSS files. Just reload your browser to see your changes (mind the browser's cache!).
-
Get an OpenVidu object and initialize a session with a sessionId. Have in mind that this is the parameter that defines which video call to connect.
var OV = new OpenVidu("wss://" + OPENVIDU_SERVER_IP + ":8443/"); var session = OV.initSession(sessionId);
-
Set the events to be listened by your session. For example, this snippet below will automatically append the new participants videos to HTML element with 'subscriber' id. Available events for the Session object are detailed in API section.
session.on('streamCreated', function (event) { session.subscribe(event.stream, 'subscriber'); });
-
Connect to the session. For a non-secure approach, the value of token parameter is irrelevant. You can pass as second parameter a callback to be executed after connection is stablished. A common use-case for users that want to stream their own video is the following one: if the connection to the session has been succesful, get a Publisher object (appended to HTML element with id 'publisher') and publish it. The rest of participants will receive the stream.
session.connect(token, function (error) { // If connection successful, get a publisher and publish to the session if (!error) { var publisher = OV.initPublisher('publisher', { audio: true, video: true, quality: 'MEDIUM' //'LOW','MEDIUM','HIGH' }); session.publish(publisher); } else { console.log('Error while connecting to the session'); } });
-
Finally, whenever you want to leave the video call...
session.disconnect();
With these few lines of code you will already have a functional video-call capability in your app. Check Securization section to learn how to easily make your app ready for production.
If you prefer, there's an Angular version of the sample app that uses openvidu-browser npm package. Check it out here.
In a production environment probably you don't want unauthorized users swamping your video calls. It's not possible to control access to them with the first approach we have seen in the sections above: anyone who knows the sessionId could connect to your video call, and if it turns out that the sessionId doesn't belong to an existing session, a new one would be created.
In addition, a secure version also means you can choose the role each user has in your video calls (see OpenViduRole section).
Thus, a non-secure version of OpenVidu is only intended for development environments. Don't worry, adding securization is not a difficult task.
In the image above you can see the main difference with the non-secure version of OpenVidu. Your backend will now have to call two HTTP REST operations in openvidu-server to get the two parameters needed in the securization process:
- sessionId: just as in the non-secure version, it identifies each specific video-call
- token: any user joining a specific video call will need to pass a valid token as a parameter
For the moment you have two options available for getting sessionIds and tokens from openvidu-server:
As stated in the former point, two REST operations are provided: /getSessionId and /newToken. Both operations have in common the header referred to authorization. It is implemented via Basic Auth, and it is as simple as applying Base64 encoding to the username (always "OPENVIDUAPP") and the password (your secret shared with openvidu-server). An example is shown below:
For secret "MY_SECRET", the final header would be
Authorization:Basic T1BFTlZJRFVBUFA6TVlfU0VDUkVU
NEW SESSIONID | PARAMETERS |
---|---|
Operation | POST |
URL | https://[YOUR_OPENVIDUSERVER_IP]/api/sessions |
Headers | Authorization:Basic EncodeBase64(OPENVIDUAPP:[YOUR_SECRET]) |
Returns | {"id": "SESSIONID"} |
NEW TOKEN | PARAMETERS |
---|---|
Operation | POST |
URL | https://[YOUR_OPENVIDUSERVER_IP]/api/tokens |
Headers | Authorization:Basic EncodeBase64(OPENVIDUAPP:[YOUR_SECRET]) Content-Type:application/json |
Body | {"session": "SESSIONID", "role": "ROLE", "data": "DATA"} |
Returns | {"token": "TOKEN", "session": "SESSIONID", "role": "ROLE", "data": "DATA", "id": "TOKEN"} |
ROLE value in Body field of POST to "/api/tokens" can be:
- SUBSCRIBER
- PUBLISHER
- MODERATOR
(See OpenViduRole section)
A Java package that wraps the HTTP REST operations for making them even easier
-
Maven dependency
<dependency> <groupId>io.openvidu</groupId> <artifactId>openvidu-java-client</artifactId> <version>...</version> </dependency>
-
Jar
The usage is quite simple: import OpenVidu package and get an OpenVidu object. You need to provide to the constructor the IP of your OpenVidu Server and the secret shared with it (initialized by openvidu.secret=MY_SECRET
property). Then just call the following methods to get a shiny new sessionId or token to be returned to your frontend.
import io.openvidu.java.client.OpenVidu;
OpenVidu openVidu = new OpenVidu(OPENVIDU_SERVER_IP, YOUR_SECRET);
Session session = this.openVidu.createSession();
String sessionId = session.getSessionId();
String token = session.generateToken();
// Send sessionId and token to frontend
- Identify your user and listen to a request for joining a video call (represented by [LOGIN OPERATION] and [JOIN VIDEO CALL] in the diagram). This process is entirely up to you.
- You must get a sessionId: a new one if the video call is being created or an existing one for an active video call. In the first case you need to ask openvidu-server for it (as shown in the diagram), in the second case you must retrieve it from wherever you stored it when it was created (a data-base or maybe your backend itself).
- You also need a new valid token for this session. Ask openvidu-server for it passing the sessionId.
- Finally return both parameters to your frontend, where using openvidu-browser you may initilize your session with sessionId and then connect to it with token. Good news: the code is exactly the same as explained before in Code description section
Communication between Your Back and openvidu-server modules is outlined in the diagram, but it does not correspond to the real methods. Remember you can handle this from your backend by consuming the REST API or by using openvidu-backend-client package.
We have implemented a very basic demo application to see the secure version of OpenVidu in action. It has a Java backend to manage the user sessions and the securization process with OpenVidu Server.
-
Please be sure that you have docker CE installed
-
Run this Docker container
docker run -p 5000:5000 -p 3000:3000 -p 4040:4040 -e KMS_STUN_IP=193.147.51.12 -e KMS_STUN_PORT=3478 openvidu/openvidu-sample-secure
-
Wait until you see a public URL ended with
.ngrok.io
. You can connect locally inlocalhost:3000
or by using the ngrok public URL. You can also share this URL with anyone you want to test the app over the Internet!
Wanna try a real sample application that makes use of everything we have talked about? Take a look at this app. It wraps a frontend built with Angular, a backend built with Spring and a MySQL database:
-
Please be sure that you have docker-compose (
sudo apt-get install docker-compose
) -
Download the
docker-compose.yml
file and run it:wget -O docker-compose.yml https://raw.githubusercontent.com/OpenVidu/openvidu-docker/master/openvidu-sample-app/docker-compose.yml docker-compose up
-
Wait until you see an output like
Started App in XXX seconds (JVM running for XXX)
-
Go to
https://localhost:5000
and accept the self-signed certificate. Here you have a couple registered users (use a standard window and an incognito window to test both of them at the same time):user password teacher@gmail.com pass student1@gmail.com pass
Whatever app you are developing, chances are you will need to pass some data for each user, at least a nickname. You can do it in two different places:
-
openvidu-browser: when calling
session.connect
methodsession.connect(token, DATA, function (error) { ... });
-
API REST: when asking for a token to /api/tokens, you can pass data as third parameter in the BODY of the POST request
{“session”: “sessionId”, “role”: “role”, “data”: "DATA"}
Java and Node clients (openvidu-java-client and openvidu-node-client) allow you to pass data when creating a Token object:
tokenOptions = new TokenOptions.Builder().data("DATA").build();
The result will be that in all clients, Connection objects will have in their data property the pertinent value you have provided for each user. So, an easy way to get the data associated to any user would be:
session.on('streamCreated', function (event) {
session.subscribe(event.stream, 'subscriber');
console.log('USER DATA: ' + event.stream.connection.data);
});
Some clarifications:
-
Connection.data will be a simple string if you have provided data only with one of the methods, and will be a string with the following format if you provide data both from openvidu-browser and your backend: "OPENVIDUBROWSER_DATA%/%APIREST_DATA"
-
Using only first option is not secure, as clients could modify the value of the second parameter. It is intended only in development environments. If you want total control over shared data, please use the second way.
-
You can choose whatever format you like for the data string, but if you are planning to share more than a simple field, maybe a standard format as JSON would be a wise choice.
NOTE: all input parameters ("Parameters" columns) are listed in strict order, optional ones in italics
Class | Description |
---|---|
OpenVidu | Use it to initialize your sessions and publishers |
Session | Represents a video call. It can also be seen as a room where multiple users can connect. Participants who publish their videos to a session will be seen by the rest of users connected to that specific session |
Publisher | Packs local media streams. Users can publish it to a session |
Subscriber | Packs remote media streams. Users automatically receive them when others publish their streams |
Stream | Represents each one of the videos send and receive by a user in a session. Therefore each Publisher and Subscriber has an attribute of type Stream |
Connection | Represents each one of the user's connection to the session (the local one and other user's connections). Therefore each Session and Stream object has an attribute of type Connection |
Method | Returns | Parameters | Description |
---|---|---|---|
initSession |
Session | apikey:string sessionId:string |
Returns a session with id sessionId |
initPublisher |
Publisher | parentId:string cameraOptions:any callback:function |
Starts local video stream, appending it to parentId HTML element, with the specific cameraOptions settings and executing callback function in the end. cameraOptions must be an object with three properties: {audio:boolean, video:boolean, quality:string}, being audio/video false if you want to initialize them muted (Publisher.publishAudio(true) and Publisher.publishVideo(true) can unmute them later) and quality must be 'LOW', 'MEDIUM' or 'HIGH' |
checkSystemRequirements |
Number | Returns 1 if the browser supports WebRTC, 0 otherwise | |
getDevices |
Promise | callback(error, deviceInfo):function |
Collects information about the media input and output devices available on the system, returned in deviceInfo array |
Method | Returns | Parameters | Description |
---|---|---|---|
connect |
token:string metadata:string callback(error):function |
Connects to the session using token and executes callback in the end (error parameter null if success). metadata parameter allows you to pass a string as extra data to share with other users when they receive participantJoined event. You can also add metadata through openvidu-backend-client when generating tokens (see TokenOptions). The structure of this string is up to you (maybe some standarized format as JSON or XML is a good idea), the only restriction is a maximum length of 1000 chars | |
disconnect |
Leaves the session, destroying all streams and deleting the user as a participant | ||
publish |
publisher:Publisher |
Publishes the specific user's local stream contained in publisher object to the session | |
unpublish |
publisher:Publisher |
Unpublishes the specific user's local stream contained in publisher object | |
on |
eventName:string callback:function |
callback function will be triggered each time eventName event is recieved | |
once |
eventName:string callback:function |
callback function will be triggered once when eventName event is recieved. The listener is removed immediately | |
off |
eventName:string eventHandler:any |
Removes eventHandler handler for eventName event | |
subscribe |
Subscriber | stream:Stream htmlId:string videoOptions:any |
Subscribes to stream, appending a new HTML Video element to DOM element of htmlId id, with videoOptions settings. This method is usually called in the callback of streamCreated event |
unsubscribe |
subscriber:Subscriber |
Unsubscribes from subscriber, automatically removing its HTML Video element |
Property | Type | Description |
---|---|---|
sessionId |
string | The unique id of the session |
Event | Properties | Description |
---|---|---|
streamCreated |
stream:Stream | Triggered by Session object when a new Stream has been created and added to it |
streamDestroyed |
stream:Stream preventDefault():Function |
Triggered by Session object when an existing Stream has been destroyed. The default behaviour is the deletion of the HTML video element associated to it. To prevent it, call preventDefault() method on the event object |
connectionCreated |
connection:Connection | Triggered by Session object whenever any user has joined the session. This includes dispatching one event for each user that joins the session when you are already connected to it, one for each existing participant the first time you connect to the session and once for your own local connection |
connectionDestroyed |
connection:Connection | Triggered by Session object whenever a user leaves the session. This event can also mean that streamDestroyed events could be dispatched, depending on the streams associated to it |
sessionDisconnected |
preventDefault():Function | Triggered by Session object when the user disconnects from the Session. Default behaviour is the deletion of all HTML video elements. Call preventDefault() on event object to prevent it and delete them by yourself |
Method | Returns | Parameters | Description |
---|---|---|---|
publishAudio |
value:boolean |
Enable or disable the audio track depending on whether value is true or false | |
publishVideo |
value:boolean |
Enable or disable the video track depending on whether value is true or false | |
destroy |
Publisher | Delets the publisher object and removes it from DOM. The rest of users will trigger a streamDestroyed event |
Property | Type | Description |
---|---|---|
accessAllowed |
boolean | true if the user has granted access to the camera, false otherwise |
element |
Element | The parent HTML Element which contains the publisher |
id |
string | The id of the HTML Video element of the publisher |
stream |
Stream | The stream object of the publisher |
session |
Session | The session to which the publisher belongs |
Event | Properties | Description |
---|---|---|
accessAllowed |
Triggered by Publisher object when the user has granted access to the camera/microphone | |
accessDenied |
Triggered by Publisher object when the user has rejected access to the camera/microphone | |
videoElementCreated |
element:HTMLVideoElement | Triggered by Publisher object inmediately after a new video element has been added to DOM |
Method | Returns | Parameters | Description |
---|---|---|---|
Property | Type | Description |
---|---|---|
element |
Element | The parent HTML Element which contains the subscriber |
id |
string | The id of the HTML Video element of the subscriber |
stream |
Stream | The stream object of the subscriber |
Event | Properties | Description |
---|---|---|
videoElementCreated |
element:HTMLVideoElement | Triggered by Subscriber object inmediately after a new video element has been added to DOM |
Property | Type | Description |
---|---|---|
connectionId |
string | Unique identifier of the connection |
data |
string | Data associated to this connection (and therefore to certain user). This is an important field: it allows you to broadcast all the information you want for each user (a username, for example) |
creationTime |
number | Time when this connection was created |
Class | Description |
---|---|
OpenVidu | Use it to create all the sessions you need |
Session | Allows for the creation of tokens |
OpenViduRole | Enum that defines the values accepted by TokenOptions.Builder.role(OpenViduRole role) method |
TokenOptions | Customize each token with this class when generating them |
Method | Returns | Parameters | Description |
---|---|---|---|
OpenVidu() | String:urlOpenViduServer String:secret |
The constructor receives the URL of your OpenVidu Server and the secret shared with it | |
createSession() | Session | Get a Session object by calling this method. You can then store it as you want |
Method | Returns | Parameters | Description |
---|---|---|---|
getSessionId() | String | Returns the unique identifier of the session. You will need to return this parameter to the client side to pass it during the connection process to the session | |
generateToken() | String | TokenOptions:tokenOptions |
The value returned is required in the client side just as the sessionId in order to connect to a session |
Enum | Description |
---|---|
SUBSCRIBER | They can subscribe to published streams of other users |
PUBLISHER | They can subscribe to published streams of other users and publish their own streams |
MODERATOR | They can subscribe to published streams of other users, publish their own streams and force unpublish() and disconnect() over a third-party stream or user |
Method | Returns | Parameters | Description |
---|---|---|---|
getData() | String | Returns the metadata associated to the token | |
getRole() | OpenViduRole | Returns the role associated to the token |
Method | Returns | Parameters | Description |
---|---|---|---|
TokenOptions.Builder() | Constructor | ||
build() | TokenOptions | Returns a new TokenOptions object with the stablished properties. Default values if methods data() and role() are not called are an empty string and OpenViduRole.PUBLISHER, respectively | |
data() | TokenOptions.Builder | String:data |
Some extra metadata to be associated to the user through its token. The structure of this string is up to you (maybe some standarized format as JSON or XML is a good idea), the only restriction is a maximum length of 1000 chars |
role() | TokenOptions.Builder | OpenViduRole:role |
The role associated to this token |
Here you have a step by step guide to deploy a production version of OpenVidu in an Ubuntu machine. In this case, KMS and openvidu-server run in the same machine, the first one as a native service and the second one in a Docker container.
-
Install KMS (in first command: xenial for 16.04, trusty for 14.04)
echo "deb http://ubuntu.kurento.org xenial kms6" | sudo tee /etc/apt/sources.list.d/kurento.list wget -O - http://ubuntu.kurento.org/kurento.gpg.key | sudo apt-key add - sudo apt-get update sudo apt-get install kurento-media-server-6.0
-
Install COTURN
sudo apt-get install coturn
-
File
/etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
stunServerAddress=STUN_IP stunServerPort=STUN_PORT turnURL=USER:PASS@YOUR_MACHINES'S_PUBLIC_IP:3478
-
File
/etc/turnserver.conf
external-ip=YOUR_MACHINES'S_PUBLIC_IP fingerprint user=USER:PASS lt-cred-mech realm=kurento.org log-file=/var/log/turnserver/turnserver.log simple-log
-
File
/etc/default/coturn
TURNSERVER_ENABLED=1
-
Init services
sudo service coturn restart sudo service kurento-media-server-6.0 restart
-
Init openvidu-server Docker container (securization enabled)
sudo docker run -d -p 8443:8443 -e openvidu.security=true -e openvidu.secret=YOUR_SECRET -e kms.uris=[\"ws://YOUR_MACHINE'S_INTERNAL_IP:8888/kurento\"] openvidu/openvidu-server
Packages required:
sudo apt-get update
Dependecy | Check version | Install |
---|---|---|
node | nodejs -v |
sudo apt-get install -g nodejs |
npm | npm -v |
sudo apt-get install -g npm |
maven | mvn -v |
sudo apt-get install -g maven |
angular-cli | ng -v |
sudo npm install -g @angular/cli |
typescript | tsc -v |
sudo npm install -g typescript |
How to install and run KMS in your development machine:
Ubuntu 14.04 LTS Trusty (64 bits)
echo "deb http://ubuntu.kurento.org trusty kms6" | sudo tee /etc/apt/sources.list.d/kurento.list
wget -O - http://ubuntu.kurento.org/kurento.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install kurento-media-server-6.0
Ubuntu 16.04 LTS Xenial (64 bits)
echo "deb http://ubuntu.kurento.org xenial kms6" | sudo tee /etc/apt/sources.list.d/kurento.list
wget -O - http://ubuntu.kurento.org/kurento.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install kurento-media-server-6.0
Start and stop the service
sudo service kurento-media-server-6.0 start
sudo service kurento-media-server-6.0 stop
Here you can check Kurento's official documentation.
Here we show how to develop an Angular app with OpenVidu having all packages linked in your local machine, so you can modify them and check the final result. After installing Kurento Media Server and forking or downloading the repo, these are the necessary steps to start developing openvidu-ng-testapp:
sudo service kurento-media-server-6.0 start
/openvidu/openvidu-browser/src/main/resources
npm install
sudo npm link
/openvidu/openvidu-ng-testapp
npm install
npm link openvidu-browser
ng serve
/openvidu
mvn compile -DskipTests=true
mvn install -DskipTests=true
/openvidu/openvidu-server
mvn clean compile package exec:java
(or if you prefer you can just run the Java application in your favourite IDE)
At these point, you can start modifying openvidu-ng-testapp, openvidu-browser or openvidu-server.
- openvidu-ng-testapp: the previous "ng serve" command will take care of refreshing the browser's page whenever any change takes place.
- openvidu-browser: after modifying any typescript file, you will need to run the following command to update your changes (typescript package is necessary):
/openvidu/openvidu-browser/src/main/resources
npm run updatetsc
- openvidu-server: after modifying any file, there is no other alternative but to re-launch the java application if you want to update your changes.
/openvidu/openvidu-server
mvn clean compile package exec:java
(or re-launch the Java application in your IDE. Some IDE's support automatic re-launch in response to changes)
You can also use different machines in the same network to build a more advanced development environment, so you can test the application in different devices at the same time. It's very similar to the process outlined above:
You will need a server for the built app (if you don't have any, we recommend http-server):
npm install -g http-server
Then...
sudo service kurento-media-server-6.0 start
/openvidu/openvidu-browser/src/main/resources
npm install
sudo npm link
/openvidu/openvidu-ng-testapp
npm install
npm link openvidu-browser
/openvidu
mvn compile -DskipTests=true
mvn install -DskipTests=true
/openvidu/openvidu-server
mvn clean compile package exec:java
(or if you prefer you can just run the Java application in your favourite IDE)
The following commands will be the ones which you should relaunch to update your changes: /openvidu/openvidu-ng-testapp
ng build
cd dist
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem [ACCEPT ALL FIELDS]
http-server -S
These commands build the Angular project, generate a self-signed certificate (which unfortunately is a mandatory requirement for http-server SSL) and serves the content in http-server.
Finally, to launch the app connect to https://127.0.0.1:8080 in the machine running the http-server and to https://[HOST]:8080 in other devices of the same network ([HOST] the IP of the machine running the http-server).
Don't forget to accept the certificate at https://[HOST]:8443 !
OpenVidu platform has been supported under project LERNIM (RTC-2016-4674-7) confunded by the Ministry of Economy, Finance and Competitiveness of Spain, as well as by the European Union FEDER, whose main goal with this funds is to promote technological development, innovation and high-quality research.