Loading...
Searching...
No Matches
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. For more information on how Janus implements recordings natively and the MJR format, refer to the Recordings documentation.

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 = videoroom-audio.mjr
               video = videoroom-video.mjr

  Data channel recordings are supported via a `data` attribute as well.

# 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>",
                            "data": "<Data 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>",
            "filename" : "<Base path/name for the file (media type and extension added by the plugin); optional>",
            "audiocodec" : "<name of the audio codec we prefer for the recording; optional>",
            "videocodec" : "<name of the video codec we prefer for the recording; optional>",
            "videoprofile" : "<in case the video codec supports, profile to use (e.g., "2" for VP9, or "42e01f" for H.264); optional>",
            "opusred" : <true|false, whether RED should be negotiated for audio, if offered; optional (default=false)>,
            "textdata" : "<in case data channels have to be recorded, whether the data will be text (default) or binary; optional>"
    }

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"
    }