Recording



OpenVidu Server can be configured to record sessions. Two types 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.

  • INDIVIDUAL: every publisher stream is recorded in its own file, generating a ZIP file containing all videos along with a text file for synchronization data.



How to record sessions

To start OpenVidu Server properly configured to allow session recording it is necessary to:

1. Have Docker CE installed in the host machine

OpenVidu recording module may use a Docker image that needs to be downloaded from the cloud. The process is 100% automatic, but you will need Docker CE installed in your server. If you enable OpenVidu recording service but there's no Docker installed, OpenVidu Server will fail to init, throwing the following exception:

Exception connecting to Docker daemon: you need Docker installed in this machine to enable OpenVidu recorder service


OpenVidu AWS deployment already includes the Docker image for recording service and is always launched with recording module enabled. You don't need to install anything or wait during the first execution if you use this type of deployment for OpenVidu Server. You can go straight to step 3



2. Launch OpenVidu Server with new properties

For OpenVidu Server JAR

java -jar \
    -Dopenvidu.recording=true \
    -Dopenvidu.recording.path=/PATH/TO/VIDEO/FILES \
openvidu-server.jar
  • openvidu.recording: if true OpenVidu recording service is enabled and sessions can be configured to be recorded. During the first execution of openvidu-server.jar, a Docker image (openvidu/openvidu-recording) will be downloaded.
  • openvidu.recording.path: where to store the recorded video files on the host machine. OpenVidu Server must have write access to this path


There are other environment variables related to recordings configuration that may be set. To see the full list, visit OpenVidu Server configuration parameters

For OpenVidu Server Docker image (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 \
    -e openvidu.recording=true \
    -e openvidu.recording.path=/PATH/TO/VIDEO/FILES \
openvidu/openvidu-server-kms:2.11.0
  • openvidu.recording: same as in OpenVidu Server JAR
  • openvidu.recording.path: same as in OpenVidu Server JAR

It is also necessary to mount 2 volumes:

  • -v /var/run/docker.sock:/var/run/docker.sock: gives openvidu-server container access to the local 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 flag -v /PATH/TO/VIDEO/FILES:/PATH/TO/VIDEO/FILES



3. 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-server 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).


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

  1. Initialize your sessions with this POST method: POST /api/sessions
    You may configure default values for recordings started for this session by sending params such as defaultOutputMode or defaultRecordingLayout. This way you can pre-configure recordings that will be automatically started (for sessions with {"recordingMode": "ALWAYS"}). For these sessions configured with ALWAYS recording mode, no more steps are needed.

  2. If you have configured your session with "recordingMode": "MANUAL"

    • Start the recording with this POST method: POST /api/recordings/start
      You can pass parameters to override default recording configuration values set in step 1 and to further configure it with other available options

    • Stop the recording with this POST method: POST /api/recordings/stop




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:

When starting the recording of a session with method POST /api/recordings/start pass parameters
{"outputMode: "COMPOSED", "recordingLayout": "BEST_FIT"}


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, it is desirable to launch OpenVidu Server in a host with significant CPU power if COMPOSED video recordings are expected. In comparison, INDIVIDUAL stream recording (and COMPOSED audio-only recording) can be 4x up to 10x more efficient

  • You can configure the resolution of the MP4 file for COMPOSED recordings by using resolution property 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



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.

When starting the recording of a session with method POST /api/recordings/start pass parameter {"outputMode:"INDIVIDUAL"}


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

MyRecording.zip
+-- MyRecording.json
+-- tzk08sgffcqqwpor_CAMERA_DFDAE.webm
+-- ugkmpnz4bn6yewbi_CAMERA_PAHHB.webm

And the content of the JSON synchronization file might be:

{
  "createdAt": 1548947712287,
  "id": "zfgmthb8jl9uellk",
  "name": "MyRecording",
  "sessionId": "zfgmthb8jl9uellk",
  "files": [
    {
      "connectionId": "ugkmpnz4bn6yewbi",
      "streamId": "ugkmpnz4bn6yewbi_CAMERA_PAHHB",
      "size": 4006190,
      "clientData": "",
      "serverData": "UserA",
      "hasAudio": true,
      "hasVideo": true,
      "typeOfVideo": "SCREEN",
      "startTimeOffset": 95,
      "endTimeOffset": 56445
    },
    {
      "connectionId": "tzk08sgffcqqwpor",
      "streamId": "tzk08sgffcqqwpor_CAMERA_DFDAE",
      "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
  • 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



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.

When starting the recording of a session with method POST /api/recordings/start simply pass parameters hasAudio or hasVideo with the desired values.


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

  • Recordings started automatically (with recording mode ALWAYS) will record both audio and video



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.

Sessions will always remain opened and the recording active during the timeout. If it 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 automatically closed.


You can always manually stop any recording at any time:



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.hostname + ':4443?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';
    

    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.hostname + ':4443?sessionId=' + SESSION_ID + '&secret=' + SECRET + '&recorder=true';
    var session = OV.initSession();
    session.connect(TOKEN);
    

    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.hostname + ':4443?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);
    


  • 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. Add new properties when launching openvidu-server

You can configure where should OpenVidu Server 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. OpenVidu Server must have read access to that path.


openvidu-server.jar

java -jar \
    -Dopenvidu.recording=true \
    -Dopenvidu.recording.path=/PATH/TO/VIDEO/FILES \
    -Dopenvidu.recording.custom-layout: /PATH/TO/INDEX/CUSTOM/LAYOUT \
openvidu-server.jar


openvidu/openvidu-server-kms (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 MY_UID=$(id -u $USER) \
    -e openvidu.recording=true \
    -e openvidu.recording.path=/PATH/TO/VIDEO/FILES \
    -e openvidu.recording.custom-layout=/PATH/TO/INDEX/CUSTOM/LAYOUT \
openvidu/openvidu-server-kms:2.11.0

WARNING: remember to add the -v option mounting the path defined with openvidu.recording.custom-layout


OpenVidu AWS deployment

You must store your custom layouts in the server under default path /opt/openvidu/custom-layout



3. Configure your recordings to use your custom layout

When starting the recording of a session with method POST /api/recordings/start pass parameters
{"outputMode": "COMPOSED", "recordingLayout": "CUSTOM"}



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:

When starting the recording of a session with method POST /api/recordings/start pass parameters
{"outputMode": "COMPOSED", "recordingLayout": "CUSTOM", "customLayout": "RELATIVE/PATH/TO/INDEX"}


In the snippets above, string RELATIVE/PATH/TO/INDEX is the path from openvidu-server 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 Server 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.



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)

<html>

<head><script src="openvidu-browser-2.8.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");
    var TOKEN = 'wss://' + location.hostname + ':4443?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>




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.