Fork me on GitHub
Record&Play plugin documentation

This is a simple application that implements two different features: it allows you to record a message you send with WebRTC in the format defined in recorded.c (MJR recording) and subsequently replay this recording (or other previously recorded) through WebRTC as well.

This application aims at showing how easy recording frames sent by a peer is, and how this recording can be re-used directly, without necessarily involving a post-processing process (e.g., through the tool we provide in janus-pp-rec.c). Notice that only audio and video can be recorded and replayed in this plugin: if you're interested in recording data channel messages (which Janus and the .mjr format do support), you should use a different plugin instead.

The configuration process is quite easy: just choose where the recordings should be saved. The same folder will also be used to list the available recordings that can be replayed.

Note
The application creates a special file in INI format with .nfo extension for each recording that is saved. This is necessary to map a specific audio .mjr file to a different video .mjr one, as they always get saved in different files. If you want to replay recordings you took in a different application (e.g., the streaming or videoroom plugins) just copy the related files in the folder you configured this plugin to use and create a .nfo file in the same folder to create a mapping, e.g.:
         [12345678]
         name = My videoroom recording
         date = 2014-10-14 17:11:26
         audio = mcu-audio.mjr
         video = mcu-video.mjr

Record&Play API

The Record&Play API supports several requests, some of which are synchronous and some asynchronous. There are some situations, though, (invalid JSON, invalid request) which will always result in a synchronous error response even for asynchronous requests.

list and update are synchronous requests, which means you'll get a response directly within the context of the transaction. list lists all the available recordings, while update forces the plugin to scan the folder of recordings again in case some were added manually and not indexed in the meanwhile.

The record , play , start and stop requests instead are all asynchronous, which means you'll get a notification about their success or failure in an event. record asks the plugin to start recording a session; play asks the plugin to prepare the playout of one of the previously recorded sessions; start starts the actual playout, and stop stops whatever the session was for, i.e., recording or replaying.

The list request has to be formatted as follows:

{
        "request" : "list"
}

A successful request will result in an array of recordings:

{
        "recordplay" : "list",
        "list": [       // Array of recording objects
                {                       // Recording #1
                        "id": <numeric ID>,
                        "name": "<Name of the recording>",
                        "date": "<Date of the recording>",
                        "audio": "<Audio rec file, if any; optional>",
                        "video": "<Video rec file, if any; optional>",
                        "audio_codec": "<Audio codec, if any; optional>",
                        "video_codec": "<Video codec, if any; optional>"
                },
                <other recordings>
        ]
}

An error instead (and the same applies to all other requests, so this won't be repeated) would provide both an error code and a more verbose description of the cause of the issue:

{
        "recordplay" : "event",
        "error_code" : <numeric ID, check Macros below>,
        "error" : "<error description as a string>"
}

The update request instead has to be formatted as follows:

{
        "request" : "update"
}

which will always result in an immediate ack ( ok ):

{
        "recordplay" : "ok",
}

Coming to the asynchronous requests, record has to be attached to a JSEP offer (failure to do so will result in an error) and has to be formatted as follows:

{
        "request" : "record",
        "id" : <unique numeric ID for the recording; optional, will be chosen by the server if missing>
        "name" : "<Pretty name for the recording>"
}

A successful management of this request will result in a recording event which will include the unique ID of the recording and a JSEP answer to complete the setup of the associated PeerConnection to record:

{
        "recordplay" : "event",
        "result": {
                "status" : "recording",
                "id" : <unique numeric ID>
        }
}

A stop request can interrupt the recording process and tear the associated PeerConnection down:

{
        "request" : "stop",
}

This will result in a stopped status:

{
        "recordplay" : "event",
        "result": {
                "status" : "stopped",
                "id" : <unique numeric ID of the interrupted recording>
        }
}

For what concerns the playout, instead, the process is slightly different: you first choose a recording to replay, using play , and then start its playout using a start request. Just as before, a stop request will interrupt the playout and tear the PeerConnection down. It's very important to point out that no JSEP offer must be sent for replaying a recording: in this case, it will always be the plugin to generate a JSON offer (in response to a play request), which means you'll then have to provide a JSEP answer within the context of the following start request which will close the circle.

A play request has to be formatted as follows:

{
        "request" : "play",
        "id" : <unique numeric ID of the recording to replay>
}

This will result in a preparing status notification which will be attached to the JSEP offer originated by the plugin in order to match the media available in the recording:

{
        "recordplay" : "event",
        "result": {
                "status" : "preparing",
                "id" : <unique numeric ID of the recording>
        }
}

A start request, which as anticipated must be attached to the JSEP answer to the previous offer sent by the plugin, has to be formatted as follows:

{
        "request" : "start",
}

This will result in a playing status notification:

{
        "recordplay" : "event",
        "result": {
                "status" : "playing"
        }
}

Just as before, a stop request can interrupt the playout process at any time, and tear the associated PeerConnection down:

{
        "request" : "stop",
}

This will result in a stopped status:

{
        "recordplay" : "event",
        "result": {
                "status" : "stopped"
        }
}

If the plugin detects a loss of the associated PeerConnection, whether as a result of a stop request or because the 10 seconds passed, a done result notification is triggered to inform the application the recording/playout session is over:

{
        "recordplay" : "event",
        "result": "done"
}