Recording 🔗



The OpenVidu deployment can be configured to record sessions. Two modes of recordings are available:

  • COMPOSED: every publisher stream is composed in the same video file in a grid layout. You can use the default layout, that will evenly distribute each stream in the available space, or you can use your own custom layout implemented with HTML/CSS/JS.

  • INDIVIDUAL: every publisher stream is recorded in its own file, generating a ZIP file containing all videos along with a text file with synchronization data. This recording mode cannot directly produce a single mixed video file of all the streams of the session, but is much more efficient than COMPOSED mode (which is quite demanding in terms of CPU).


For technical information about the media codecs and formats used for recording, check here: Codecs on OpenVidu Recordings.

For tutorials implementing recording capabilities, see openvidu-recording-java or openvidu-recording-node.



How to record sessions 🔗

1. Enable OpenVidu recording module 🔗

For OpenVidu production deployments 🔗

Configure the following property in the .env file at OpenVidu installation path (default to /opt/openvidu)

OPENVIDU_RECORDING=true

There are other environment variables related to recordings configuration that may be set.
Visit OpenVidu configuration to see the full list.

For OpenVidu development docker container 🔗

If your are using the official openvidu/openvidu-dev docker container in your development environment and want to enable the recording module, then run it like this:

docker run -p 4443:4443 --rm \
    -e OPENVIDU_RECORDING=true \
    -e OPENVIDU_RECORDING_PATH=/PATH/TO/VIDEO/FILES \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /PATH/TO/VIDEO/FILES:/PATH/TO/VIDEO/FILES \
openvidu/openvidu-dev:2.29.0

The two configuration properties that must be set are:

  • OPENVIDU_RECORDING: enables OpenVidu recording module. Another Docker image (openvidu/openvidu-recording) will be downloaded only during the first run of the container.
  • OPENVIDU_RECORDING_PATH: where to store the recorded video files on the host machine. The OpenVidu deployment must have write access to this path.

It is also necessary to mount 2 volumes:

  • -v /var/run/docker.sock:/var/run/docker.sock: gives OpenVidu development container access to the Docker daemon.
  • -v /PATH/TO/VIDEO/FILES:/PATH/TO/VIDEO/FILES: gives access to the recorded video files through the container.

IMPORTANT! /PATH/TO/VIDEO/FILES must be the same in property OPENVIDU_RECORDING_PATH=/PATH/TO/VIDEO/FILES and in both sides of volume flag -v /PATH/TO/VIDEO/FILES:/PATH/TO/VIDEO/FILES

There are other environment variables related to recordings configuration that may be set.
Visit OpenVidu configuration to see the full list.



2. Configure your Sessions to be recorded 🔗


Recording can be configured in two ways:

  • ALWAYS: the session will be automatically recorded from the moment the first participant starts publishing.

  • MANUAL: you will have to tell OpenVidu when to start the recording of the session.

In both cases you can stop the recording manually, and every recording will always be automatically stopped if last user leaves the session and certain timeout elapses (see Automatic stop of recordings).

NOTE: be aware that as the ALWAYS recording mode is automatic and asynchronous, the only way to know if there has been an error when starting the recording is to use the recordingStatusChanged webhook event.


You can use REST API or any of the server SDKs (openvidu-java-client, openvidu-node-client) to manage your recorded sessions.

  1. Call OpenVidu.createSession() passing as an optional parameter a SessionProperties object. You may configure default values for recordings started for this session with that object. This way you can pre-configure recordings that will be automatically started (for sessions with RecordingMode.ALWAYS)

    OpenVidu openvidu = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
    RecordingProperties recordingProperties = new RecordingProperties.Builder()
        .outputMode(Recording.OutputMode.COMPOSED)
        .resolution("640x480")
        .frameRate(24)
        .build();
    SessionProperties sessionProperties = new SessionProperties.Builder()
        .recordingMode(RecordingMode.MANUAL) // RecordingMode.ALWAYS for automatic recording
        .defaultRecordingProperties(recordingProperties)
        .build();
    Session session = openVidu.createSession(sessionProperties);
    
  2. If Session is configured with RecordingMode.MANUAL, manually start and stop the recording whenever you want. You may pass a RecordingProperties object when calling OpenVidu.startRecording method to override default values configured in step 1 or to further configure it with other available options

    RecordingProperties properties = new RecordingProperties.Builder()
        .name("MY_RECORDING_NAME")
        .build();
    Recording recording = openVidu.startRecording(session.getSessionId(), properties); // Starts recording
    recording = openVidu.stopRecording(recording.getId()); // Stops recording
    




Composed recording 🔗

Every publisher stream is composed in the same video file in a grid layout. This is the default recording mode, and it will generate as output an MP4 file.

You can use the default layout, that will evenly distribute each stream in the available space, or you can use your own custom layout.
To use the default layout:

RecordingProperties properties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.COMPOSED)
    .recordingLayout(RecordingLayout.BEST_FIT)
    .resolution("640x480")
    .frameRate(24)
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);


For example, for a session with two publishers the video file will look like this when using output mode COMPOSED and recording layout BEST_FIT


Notes on COMPOSED recordings

  • If a COMPOSED recording is configured to record video (that is, not being an audio-only recording), this type of grid recording can be a pretty heavy consuming process. A maximum number of 4 publishers is recommended, and starting more than 2 recordings of this type at the same time can overload server CPUs. For these reasons, when using OpenVidu CE it is desirable to launch the OpenVidu deployment in a host with significant CPU power if COMPOSED video recordings are expected. In OpenVidu Pro, the same applies to the Media Node hosting the recording process. In comparison, INDIVIDUAL stream recording (and COMPOSED audio-only recording) can be 4x up to 10x more efficient

  • You can configure the resolution and frame rate of the MP4 file for COMPOSED recordings by using resolution and frameRate properties when starting the recording

  • A thumbnail got from the middle of the video will be generated for COMPOSED recordings that have video. They will be stored next to the MP4 file and named [RECORDING_ID].jpg



Composed quick start recording 🔗

There is an extra recording output mode which is a variation of COMPOSED recording. The resulting recorded file will be exactly the same, but in this case the lifespan of the recording module will be attached to the lifecycle of the session, not to the lifecycle of the recording. This means that:

  • If you configure a Session with this composed quick start recording mode, a new recording module especially dedicated to this session will be instantiated even before you start to record it. All of the session streams will be rendered by the recording module all the time, even when not being recorded.
  • When starting the recording, the process will be as fast as physically possible for composed recordings: no need to launch the recording module and to establish the inner media connections, as this has already been done in the background.
  • When stopping the recording, the recording module of this session won't be terminated. This way the next recording of the same session will also start as quickly as possible. Only when closing the session this particular recording module will be terminated and its resources freed up.

When should you consider using this mode? When response time starting a composed recording is key in your use case. If you are going to start and stop multiple short composed recordings for the same session over time, then this mode can also be helpful. But take into account that each one of the sessions initialized with this recording mode will require considerable CPU power in your server (at least 1 dedicated CPU).

To initialize a Session with this recording output mode, just use outputMode = COMPOSED_QUICK_START when configuring your sessions to be recorded:

OpenVidu openvidu = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
RecordingProperties recordingProperties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.COMPOSED_QUICK_START)
    .build();
SessionProperties sessionProperties = new SessionProperties.Builder()
    .defaultRecordingProperties(recordingProperties)
    .build();
Session session = openVidu.createSession(sessionProperties);


Then you can initialize your recording as usual:

  • If you have configured the session with recording mode ALWAYS, then the recording will be automatically started in COMPOSED_QUICK_START output mode when the first user publishes.
  • If you have configured the session with recording mode MANUAL, then you can start recordings with COMPOSED_QUICK_START or COMPOSED output modes (both will end up being set to COMPOSED_QUICK_START), but also with INDIVIDUAL output mode if you need so.

The default recording output mode of the session will determine the output mode of its recordings. If the session is configured with COMPOSED, starting a recording with COMPOSED or COMPOSED_QUICK_START will always end up with the recording set to COMPOSED. If the session is configured with COMPOSED_QUICK_START, starting a recording with COMPOSED or COMPOSED_QUICK_START will always end up with the recording set to COMPOSED_QUICK_START.



Scalable composed recording 🔗

This feature is part of OpenVidu PRO and ENTERPRISE editions.

For an OpenVidu Pro cluster, by default composed recordings with video take place in the same Media Node hosting the recorded Session (see OpenVidu Pro architecture). By transferring the recording process to Media Nodes, the Master Node avoids being overloaded even with multiple composed video recordings in progress. Hosting the recording in the same Media Node as its session is the optimal and default choice, as the media doesn't need to be sent across different Media Nodes, saving network traffic. But you can decide to start the composed video recording of a session in a different Media Node, if your specific use case can take advantage of it:

RecordingProperties properties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.COMPOSED)
    .hasVideo(true)
    .mediaNode("media_i-1234567890abcdef0") // The string being the unique ID of an existing Media Node
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);


If the provided Media Node does not exist or its status is not valid for starting a recording, then a 400 BAD_REQUEST response is returned. The active recordings that are hosted by a Media Node at any given time are available in the Media Node object of the REST API, in attribute recordingIds.

For another perspective on this matter, visit Scalable recording.



Individual stream recording 🔗

Every publisher stream is recorded in its own file. The final result is a ZIP file containing one WEBM file for each published stream during the recording (named after each stream identifier), along with a text file with synchronization information.

RecordingProperties properties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.INDIVIDUAL)
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);


For example, for a session with 2 publishers the final content of the ZIP file could be:

MyRecording.zip
+-- MyRecording.json
+-- str_CAM_WyJt_con_HZPIDsWXBx.webm
+-- str_CAM_CPQ7_con_PHBKMPOlLb.webm

And the content of the JSON synchronization file (MyRecording.json in the example above) might be:

{
  "createdAt": 1548947712287,
  "id": "ses_MEx72i7vFd",
  "name": "MyRecording",
  "sessionId": "ses_MEx72i7vFd",
  "files": [
    {
      "connectionId": "con_HZPIDsWXBx",
      "streamId": "str_CAM_WyJt_con_HZPIDsWXBx",
      "size": 4006190,
      "clientData": "",
      "serverData": "UserA",
      "hasAudio": true,
      "hasVideo": true,
      "typeOfVideo": "SCREEN",
      "startTimeOffset": 95,
      "endTimeOffset": 56445
    },
    {
      "connectionId": "con_PHBKMPOlLb",
      "streamId": "str_CAM_CPQ7_con_PHBKMPOlLb",
      "size": 2404760,
      "clientData": "",
      "serverData": "UserB",
      "hasAudio": false,
      "hasVideo": true,
      "typeOfVideo": "CAMERA",
      "startTimeOffset": 148,
      "endTimeOffset": 56398
    }
  ]
}


These are the properties in the JSON file

  • createdAt: time when the recording was started in UTC milliseconds
  • id: unique identifier of the recording. It is built from the session identifier
  • name: custom name of the recording. You can set this parameter when starting the recording, and the final ZIP file will be named after it
  • sessionId: unique identifier of the session that was recorded
  • files: array containing one JSON object for each one of the WEBM videos inside the ZIP file
    • connectionId: unique identifier of the connection that published the stream
    • streamId: unique identifier of the recorded stream
    • size: size in bytes of this particular recorded file
    • clientData: data associated to the connection that published the stream, in the client side. You can use this field to identify the user that published this particular recorded stream
    • serverData: data associated to the connection that published the stream, in the server side. You can use this field to identify the user that published this particular recorded stream
    • hasAudio: whether this recorded stream has an audio track or not
    • hasVideo: whether this recorded stream has a video track or not
    • typeOfVideo: type of video ("CAMERA" or "SCREEN"). Only defined if hasVideo is true
    • startTimeOffset: the offset in milliseconds for when this particular stream started being recorded, from the createdAt property of the root element
    • endTimeOffset: the offset in milliseconds for when this particular stream stopped being recorded, from the createdAt property of the root element



Selecting streams to be recorded 🔗

This feature is part of OpenVidu PRO and ENTERPRISE editions.

In OpenVidu CE all of the streams published to a session being recorded with INDIVIDUAL mode will always be stored to disk. In OpenVidu Pro you have greater control: you can configure in detail which streams are to be recorded, and even activate and deactivate the recording of a specific stream during the very same recording process.

This applies to INDIVIDUAL recording. You can specify the streams that should or shouldn't be recorded in a session when creating the Connection for a participant. The default option when creating a Connection is to record all of the streams published by it. Below there are examples of Connections being created that will make their published streams NOT to be recorded when recording the session in INDIVIDUAL mode.

ConnectionProperties connectionProperties = new ConnectionProperties.Builder()
    .record(false)
    .build();
Connection connection = session.createConnection(connectionProperties);
String token = connection.getToken(); // Send this string to the client side

See JavaDoc


You can also change on the fly whether the streams of a Connection must be recorded or not. By using the capability of dynamically updating the options of a Connection you can start or stop the individual recording of the Connection's stream at any moment, even while the recording is active:

ConnectionProperties connectionProperties = new ConnectionProperties.Builder()
    .record(false) // false to stop the recording, true to start it
    .build();
// connectionId being Connection.getConnectionId()
session.updateConnection(connectionId, connectionProperties);

See JavaDoc


If the same stream of some user is recorded multiple times during one INDIVIDUAL session recording, then the resulting ZIP file described in INDIVIDUAL recording may have this content:

MyRecording.zip
+-- MyRecording.json
+-- str_CAM_WyJt_con_HZPIDsWXBx.webm
+-- str_CAM_CPQ7_con_PHBKMPOlLb.webm
+-- str_CAM_CPQ7_con_PHBKMPOlLb-1.webm
+-- str_CAM_CPQ7_con_PHBKMPOlLb-2.webm

In this case the recording process for stream str_CAM_CPQ7_con_PHBKMPOlLb has been started and stopped 3 times, generating 3 separate files for the same recording named MyRecording.



Audio-only and video-only recordings 🔗

By default recordings will be generated with both audio and video, but you can configure them to record audio-only or video-only files.

RecordingProperties properties = new RecordingProperties.Builder()
    .hasAudio(true)
    .hasVideo(false)
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);

Notes on audio/video only recordings

  • Recordings configured to not record neither audio nor video will fail to start, returning a status error of 422

  • COMPOSED video-only recordings will generate an MP4 file. COMPOSED audio-only recordings will generate a WEBM file. INDIVIDUAL recordings will always generate a ZIP file containing one WEBM file for each recorded stream

  • Streams published during a video-only recording that are audio-only won't be recorded: they won't be included in the grid layout for COMPOSED recordings and won't generate a WEBM file in INDIVIDUAL recordings. Same for audio-only recordings with video-only streams

  • COMPOSED audio-only recording is not available when using mediasoup in OpenVidu Enterprise



Automatic stop of recordings 🔗

Any started recording will automatically be stopped when any of the following situations occur and certain timeout elapses. This timeout is by default 120 seconds, but you can configure it with system property OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT. The automatic recording stop timout will start:

  • For any recorded session, if last user disconnects from the session.

  • For sessions with recording mode MANUAL, if the recording is started and no user is publishing a stream.

The only condition to abort the timeout is to have any user publishing a stream to the session within the timeout.

During the timeout sessions will always remain opened and the recording active. If the timeout elapses and no stream is being published to the session, the recording will be stopped. If in addition there's no user connected to the session, the session will also be immediately closed.

You can always manually stop any recording at any time, even during the automatic stop timeout:

Recording stoppedRecording = openVidu.stopRecording(recordingId);



Custom recording layouts 🔗

You can create your own layouts for the session recording process. They are implemented with HTML/CSS/JS files, just as your OpenVidu application client-side.

1. Create your layout with HTML/CSS/JS files 🔗

Put them in a path accessible to openvidu-server. There must be an index.html file as entrypoint for your custom layout:


  • WHAT SHOULD YOUR JS CODE DO: by making use of openvidu-browser.js library, you need to connect a recorder participant to the session. This means:

    1) Your layout must connect to the session using a token like this:

    'wss://' + location.host + '?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';
    

    WARNING! Use ws:// as protocol instead of wss:// if you are using the OpenVidu dev container through localhost

    Being SESSION_ID and SECRET two parameters that will be url-encoded under ids sessionId and secret respectively. So, for example:

    var url = new URL(window.location.href);
    var SESSION_ID = url.searchParams.get("sessionId");
    var SECRET = url.searchParams.get("secret");
    var TOKEN = 'wss://' + location.host + '?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';
    var session = OV.initSession();
    session.connect(TOKEN);
    

    WARNING! Use ws:// as protocol instead of wss:// if you are using the OpenVidu dev container through localhost

    2) You will need to subscribe to, at least, one event: streamCreated of Session object. That way you can subscribe your recorder to every stream when any user starts publishing (by default, the video element will be automatically removed on every streamDestroyed event). To sum up, this would be the simplest code you need to properly start your recorder participant:

    var OV = new OpenVidu();
    
    var url = new URL(window.location.href);
    var SESSION_ID = url.searchParams.get("sessionId");
    var SECRET = url.searchParams.get("secret");
    var TOKEN = 'wss://' + location.host + '?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';
    var session = OV.initSession();
    
    session.on("streamCreated", (event) => {
        session.subscribe(event.stream, 'html-id-where-insert-video');
    });
    
    session.connect(TOKEN);
    

    WARNING! Use ws:// as protocol instead of wss:// if you are using the OpenVidu dev container through localhost


  • HOW TO IDENTIFY YOUR USERS: you can identify them by making use of property Stream.connection.data of the Stream object retrieved in Session event "streamCreated". That way you may know which particular user should be displayed in which particular HTML element of your layout. For example:
    session.on("streamCreated", (event) => {
        var stream = event.stream;
        if (stream.connection.data === 'userBigVideo') {
            session.subscribe(stream, 'big-video-div');
        } else if (stream.connection.data === 'userSmallVideo') {
            session.subscribe(stream, 'small-video-div');
        }
    });
    



2. Configure custom layouts in the OpenVidu deployment 🔗

You can configure where should the OpenVidu deployment look for your custom layout in the system.
Default path is /opt/openvidu/custom-layout, but you can configure it with system property OPENVIDU_RECORDING_CUSTOM_LAYOUT. The OpenVidu deployment must have read access to that path, where you must have stored the index.html file of your layout.


OpenVidu production deployments

Default path /opt/openvidu/custom-layout is the recommended one. But if for any reason you want to change it, then set the following property in the .env configuration file:

OPENVIDU_RECORDING_CUSTOM_LAYOUT=/PATH/TO/INDEX/CUSTOM/LAYOUT


OpenVidu development docker container (development environment)

docker run -p 4443:4443 --rm \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /PATH/TO/VIDEO/FILES:/PATH/TO/VIDEO/FILES \
    -v /PATH/TO/INDEX/CUSTOM/LAYOUT:/PATH/TO/INDEX/CUSTOM/LAYOUT \
    -e OPENVIDU_RECORDING=true \
    -e OPENVIDU_RECORDING_PATH=/PATH/TO/VIDEO/FILES \
    -e OPENVIDU_RECORDING_CUSTOM_LAYOUT=/PATH/TO/INDEX/CUSTOM/LAYOUT \
openvidu/openvidu-dev:2.29.0

WARNING: remember to add the -v option mounting the path defined with OPENVIDU_RECORDING_CUSTOM_LAYOUT



3. Configure your recordings to use your custom layout 🔗

RecordingProperties properties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.COMPOSED)
    .recordingLayout(RecordingLayout.CUSTOM)
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);



Configuring multiple custom layouts 🔗

You can implement as many custom recording layouts as you want. Simply store each one of them (each one with its own index.html entrypoint file) in a subfolder under path defined with system property OPENVIDU_RECORDING_CUSTOM_LAYOUT (default value /opt/openvidu/custom-layout). Then, when configuring your sessions as stated above in point 3, just add a new parameter:

RecordingProperties properties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.COMPOSED)
    .recordingLayout(RecordingLayout.CUSTOM)
    .customLayout("RELATIVE/PATH/TO/INDEX")
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);


In the snippets above, string RELATIVE/PATH/TO/INDEX is the path from OpenVidu configuration property OPENVIDU_RECORDING_CUSTOM_LAYOUT to the specific index.html you want to use for a particular recording. So, if you have the following folder tree structure in your OpenVidu deployment host:

/opt
+-- /openvidu
|   +-- /my_custom_layouts
|       +-- index.html
|       +-- /layout1
|           +-- index.html
|       +-- /layout2
|           +-- index.html
/etc
    ...

You should start openvidu-server with property OPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/my_custom_layouts and you can use any of the 3 index.html files for recording any of your sessions. To use the outer layout in a recording, just configure in recording properties recordingLayout to CUSTOM. To use any of the inner layouts, also configure customLayout to layout1 or layout2.



Using an external custom layout 🔗

OpenVidu allows you to configure a recording to use a custom layout deployed outside OpenVidu host. This is useful if, for whatever reason, you have your layout being served in a different server. To achieve this, you just have to configure the complete URL where your layout is served in property customLayout:

RecordingProperties properties = new RecordingProperties.Builder()
    .outputMode(Recording.OutputMode.COMPOSED)
    .recordingLayout(RecordingLayout.CUSTOM)
    .customLayout("https://USER:PASS@my.domain.com:8888/path?myParam=123")
    .build();
Recording recording = openVidu.startRecording(session.getSessionId(), properties);


As you can see, this URL may have credentials and any query parameter or token you may need in your custom layout.
For example, in the snippets above the layout files would be protected by Basic Auth with "USER" ans "PASS" as username and password, and you could access value 123 in your layout JS code just by calling
new URL(window.location.href).searchParams.get("myParam");



Debugging your custom layouts 🔗

To debug your custom layout, you just need to store it in the path declared with property OPENVIDU_RECORDING_CUSTOM_LAYOUT, as explained in section Configure custom layouts in the OpenVidu deployment.

Then, by using your OpenVidu application, start a session and as many publishers as you expect to be recorded in your custom layout. Finally you just have to connect to your layout through Chrome by entering url:

https://OPENVIDUAPP:SECRET@OPENVIDU_IP:OPENVIDU_PORT/openvidu/layouts/index.html?sessionId=SESSION_ID&secret=SECRET

Being:

  • SECRET: parameter OPENVIDU_SECRET configured when launching openvidu-server
  • OPENVIDU_IP: the IP where openvidu-server is accessible in your development machine. You will be probably using openvidu-dev docker container in your development environment, so this parameter is localhost if you are in Mac or Linux, and the docker IP of the container if you are in Windows (see this FAQ)
  • OPENVIDU_PORT: port where openvidu-server is listening. In OpenVidu production deployments this is by default 443 and if using the development container it is by default 4443.
  • SESSION_ID: the session ID you have initialized for the debugging process. Here's a little tip: you can initialize the session in openvidu-server (REST API, openvidu-java-client, openvidu-node-client) configuring parameter customSessionId to fix this session ID and avoid having to change it every time you restart your session.

By connecting with Chrome to the above URL you will see the exact result obtained when recording a session with your custom layout. You can open the browsers console to debug any error, and you can also change the HTML/CSS/JS files of your layout until you are happy with the outcome. Refresh the browser's tab after any change in the HTML/JS/CSS files to see the changes.



Sample custom layout 🔗

This is literally the simplest HTML for a custom recording layout. Use it as a template for building more complex ones (you will need latest openvidu-browser-VERSION.min.js file to be in the same folder. Be sure to use the same version number as your openvidu-server!)

<html>

<head><script src="openvidu-browser-2.29.0.min.js"></script></head>

<body>
    <div id="videos"></div>
</body>

<script>
    var url = new URL(window.location.href);
    var SESSION_ID = url.searchParams.get("sessionId");
    var SECRET = url.searchParams.get("secret");
    // WARNING! Use "ws://" as protocol instead of "wss://" if you are using
    // the OpenVidu dev container (openvidu/openvidu-dev) through localhost
    var TOKEN = 'wss://' + location.host + '?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';

    var OV = new OpenVidu();
    var session = OV.initSession();

    session.on("streamCreated", (event) => {
        session.subscribe(event.stream, 'videos');
    });
    session.connect(TOKEN)
        .then(() => { console.log('Recorder participant connected') })
        .catch(error => { console.error(error) });
</script>

</html>

You also can use an additional layout library for improving the layout and get a more compact and professional look for your recordings. This example is using the opentok-layout-js (you will need latest openvidu-browser-VERSION.min.js file and the opentok-layout.min.js static file to be in the same folder.

<html>

<head>
    <script src="openvidu-browser-2.29.0.min.js"></script>
    <script src="opentok-layout.min.js"></script>
</head>

<body>
    <div id="layout" style="height: 100%;"></div>
</body>

<script>

    var layoutContainer = document.getElementById("layout");

    // Initialize the layout container and get a reference to the layout method
    var layout = initLayoutContainer(layoutContainer);
    layout.layout();
    var resizeTimeout;
    window.onresize = function() {
        clearTimeout(resizeTimeout);
        resizeTimeout = setTimeout(function () {
            layout.layout();
        }, 20);
    };


    var url = new URL(window.location.href);
    var SESSION_ID = url.searchParams.get("sessionId");
    var SECRET = url.searchParams.get("secret");
    // WARNING! Use "ws://" as protocol instead of "wss://" if you are using
    // the OpenVidu dev container (openvidu/openvidu-dev) through localhost
    var TOKEN = 'wss://localhost:4443' + '?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';

    var OV = new OpenVidu();
    var session = OV.initSession();

    session.on("streamCreated", (event) => {
        session.subscribe(event.stream, 'layout');
        layout.layout();
    });
    session.connect(TOKEN)
        .then(() => {
            console.log('Recorder participant connected');
            layout.layout();
    }).catch(error => { console.error(error) });


</script>

</html>




Uploading recordings to S3 🔗

This feature is part of OpenVidu PRO and ENTERPRISE editions.

OpenVidu Pro can be configured to upload recordings to any S3 compatible bucket instead of storing them in local storage.

S3 provides persistance for recording data in OpenVidu Pro clusters. It brings multiple advantages:

  • You can terminate clusters without worrying losing your recordings, as long as they are properly uploaded to the bucket.
  • Launching an OpenVidu Pro cluster configured to use an already populated S3 bucket will make the existing recordings accessible and manageable from the new cluster.
  • You can upload to the same S3 bucket from different OpenVidu Pro clusters.

Bare in mind that the upload process is not performed in real time while the recording is active. Recordings must be first stopped before they are automatically uploaded to S3, and terminating a cluster with an active recording will result in losing that entire recording. Listen to recordingStatusChanged event to know when a recording has been successfully uploaded to S3.

To enable S3 recording storage configure the following properties in the .env file at Master Node installation path (default to /opt/openvidu)

OPENVIDU_PRO_RECORDING_STORAGE=s3
OPENVIDU_PRO_AWS_S3_BUCKET=your-bucket-name
OPENVIDU_PRO_AWS_S3_HEADERS=
OPENVIDU_PRO_AWS_S3_SERVICE_ENDPOINT=
OPENVIDU_PRO_AWS_S3_WITH_PATH_STYLE_ACCESS=false
OPENVIDU_PRO_AWS_ACCESS_KEY=your-access-key
OPENVIDU_PRO_AWS_SECRET_KEY=your-secret-key
OPENVIDU_PRO_AWS_REGION=eu-west-1

There is a complete description of these properties at OpenVidu Pro configuration. Take into account the following points:

  • Property OPENVIDU_PRO_AWS_S3_BUCKET can have a folder structure if you want OpenVidu Pro to upload recordings to a specific folder of your bucket.
  • Property OPENVIDU_PRO_AWS_S3_HEADERS allows further configuring the internal S3 client of OpenVidu Pro with the HTTP headers used when uploading the recordings. The property is a key-value map of strings, following the format of a JSON object. For example, according to AWS documentation, for applying server-side encryption with AES-256, this header is mandatory: {"x-amz-server-side-encryption":"AES256"}. The list of available headers can be found here.
  • Property OPENVIDU_PRO_AWS_S3_SERVICE_ENDPOINT allows you to configure any S3 compatible endpoint. If blank, then AWS S3 will be assumed. Must be an URL. For example: https://s3.us-west-002.backblazeb2.com
  • Property OPENVIDU_PRO_AWS_S3_WITH_PATH_STYLE_ACCESS may be important when using non-AWS S3 buckets. It must be set to true in case the OPENVIDU_PRO_AWS_S3_SERVICE_ENDPOINT is a domain name and not an IP to avoid Access Denied errors. In this way the internal S3 client will always use path-style access for all requests sent to the bucket, which will solve the problem.
  • Properties OPENVIDU_PRO_AWS_ACCESS_KEY and OPENVIDU_PRO_AWS_SECRET_KEY refer only to long-lived AWS credentials with read and write access to the specified bucket. They can be omitted. If so, the internal S3 client will try to use the default AWS credentials of the machine, if available (see the credentials search order in the Java Doc). For short-lived credentials, the internal S3 client will do its best to automatically refresh them when expired.
  • Property OPENVIDU_PRO_AWS_REGION may not be necessary. S3 buckets are not tied to a specific world region, and theoretically the internal S3 client should be able to autodiscover the region from the bucket's name only. But this has been proven to may not be possible in some occasions, and the property must be specified explicitly in these cases.



Local recording in the browser 🔗

OpenVidu Browser offers an extremely simple API to record Streams directly in the client's browser. Check it out here.



Troubleshooting 🔗

The video files of COMPOSED or COMPOSED_QUICK_START recordings are showing an error message 🔗

If the resulting video file of a COMPOSED or COMPOSED_QUICK_START recording with video (not audio-only) is a blank page showing an error message, then it is very likely that your host does not support hairpinning. Hairpinning is the ability of a host to access one of its own internal services using its own public domain/IP.

COMPOSED and COMPOSED_QUICK_START recordings with video uses a special module that connects to the recorded session using the public domain of your OpenVidu deployment. This is only true if you are not using an external custom layout. This means:

  • For OpenVidu CE and its single-node environment, this means that the recording module is not able to connect to the OpenVidu deployment from inside the same machine.
  • For OpenVidu PRO and its multi-node environment, this means that Media Nodes are not able to reach Master Node using its public IP (the recording module is hosted in the Media Nodes).

There are 2 possible solutions to this problem:

  1. RECOMMENDED ONE: change your firewall/proxy/security to allow hairpinning from the affected host.
  2. HACKY ONE: allow the recording module to connect to the session using the internal private IP of the OpenVidu host. To do so set configuration property OPENVIDU_RECORDING_COMPOSED_URL to a specific value.

    • For OpenVidu CE: https://HOST:PORT/dashboard being HOST the internal IP of the host and PORT the public port of the deployment (configuration property HTTPS_PORT)
    • For OpenVidu PRO: https://HOST:PORT/inspector being HOST the internal IP of the Master Node and PORT the public port of the deployment (configuration property HTTPS_PORT)

Enable debug mode of COMPOSED or COMPOSED_QUICK_START recordings 🔗

COMPOSED and COMPOSED_QUICK_START recordings with video (not audio-only) use a special module that can be initialized in a debug mode that will log much more information. Set configuration property OPENVIDU_RECORDING_DEBUG to true to enable the recording debug mode.

First time launching a COMPOSED or COMPOSED_QUICK_START recording is taking too long or throwing an error 🔗

There is a known bug affecting the first time a COMPOSED or COMPOSED_QUICK_START recording is launched that may prevent the recording process to properly start. After the error is received, second attempt should work just fine. Take into account that for OpenVidu Pro, this may happen for every new Media Node hosting a COMPOSED or COMPOSED_QUICK_START recording for the first time.