Fork me on GitHub
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Data Structures | Macros | Typedefs | Functions
janus_recordplay.c File Reference

Janus Record&Play plugin. More...

#include "plugin.h"
#include <dirent.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <jansson.h>
#include "../debug.h"
#include "../apierror.h"
#include "../config.h"
#include "../mutex.h"
#include "../record.h"
#include "../rtp.h"
#include "../rtcp.h"
#include "../utils.h"
Include dependency graph for janus_recordplay.c:

Data Structures

struct  janus_recordplay_message
struct  janus_recordplay_rtp_header_extension
struct  janus_recordplay_frame_packet
struct  janus_recordplay_recording
struct  janus_recordplay_session

Macros

#define JANUS_RECORDPLAY_VERSION   3
#define JANUS_RECORDPLAY_VERSION_STRING   "0.0.3"
#define JANUS_RECORDPLAY_DESCRIPTION   "This is a trivial Record&Play plugin for Janus, to record WebRTC sessions and replay them."
#define JANUS_RECORDPLAY_NAME   "JANUS Record&Play plugin"
#define JANUS_RECORDPLAY_AUTHOR   "Meetecho s.r.l."
#define JANUS_RECORDPLAY_PACKAGE   "janus.plugin.recordplay"
#define OPUS_PT   111
#define VP8_PT   100
#define sdp_template
#define sdp_a_template
#define sdp_v_template
#define JANUS_RECORDPLAY_ERROR_NO_MESSAGE   411
#define JANUS_RECORDPLAY_ERROR_INVALID_JSON   412
#define JANUS_RECORDPLAY_ERROR_INVALID_REQUEST   413
#define JANUS_RECORDPLAY_ERROR_INVALID_ELEMENT   414
#define JANUS_RECORDPLAY_ERROR_MISSING_ELEMENT   415
#define JANUS_RECORDPLAY_ERROR_NOT_FOUND   416
#define JANUS_RECORDPLAY_ERROR_INVALID_RECORDING   417
#define JANUS_RECORDPLAY_ERROR_INVALID_STATE   418
#define JANUS_RECORDPLAY_ERROR_UNKNOWN_ERROR   499

Typedefs

typedef struct
janus_recordplay_message 
janus_recordplay_message
typedef struct
janus_recordplay_rtp_header_extension 
janus_recordplay_rtp_header_extension
typedef struct
janus_recordplay_frame_packet 
janus_recordplay_frame_packet
typedef struct
janus_recordplay_recording 
janus_recordplay_recording
typedef struct
janus_recordplay_session 
janus_recordplay_session

Functions

janus_plugincreate (void)
int janus_recordplay_init (janus_callbacks *callback, const char *onfig_path)
void janus_recordplay_destroy (void)
int janus_recordplay_get_api_compatibility (void)
int janus_recordplay_get_version (void)
const char * janus_recordplay_get_version_string (void)
const char * janus_recordplay_get_description (void)
const char * janus_recordplay_get_name (void)
const char * janus_recordplay_get_author (void)
const char * janus_recordplay_get_package (void)
void janus_recordplay_create_session (janus_plugin_session *handle, int *error)
struct janus_plugin_resultjanus_recordplay_handle_message (janus_plugin_session *handle, char *transaction, char *message, char *sdp_type, char *sdp)
void janus_recordplay_setup_media (janus_plugin_session *handle)
void janus_recordplay_incoming_rtp (janus_plugin_session *handle, int video, char *buf, int len)
void janus_recordplay_incoming_rtcp (janus_plugin_session *handle, int video, char *buf, int len)
void janus_recordplay_incoming_data (janus_plugin_session *handle, char *buf, int len)
void janus_recordplay_slow_link (janus_plugin_session *handle, int uplink, int video)
void janus_recordplay_hangup_media (janus_plugin_session *handle)
void janus_recordplay_destroy_session (janus_plugin_session *handle, int *error)
char * janus_recordplay_query_session (janus_plugin_session *handle)
janus_recordplay_frame_packetjanus_recordplay_get_frames (const char *dir, const char *filename)
void janus_recordplay_update_recordings_list (void)
void janus_recordplay_send_rtcp_feedback (janus_plugin_session *handle, int video, char *buf, int len)
void janus_recordplay_message_free (janus_recordplay_message *msg)
void * janus_recordplay_watchdog (void *data)

Detailed Description

Janus Record&Play plugin.

Author
Lorenzo Miniero loren.nosp@m.zo@m.nosp@m.eetec.nosp@m.ho.c.nosp@m.om

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

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

Plugins

Macro Definition Documentation

#define JANUS_RECORDPLAY_AUTHOR   "Meetecho s.r.l."
#define JANUS_RECORDPLAY_DESCRIPTION   "This is a trivial Record&Play plugin for Janus, to record WebRTC sessions and replay them."
#define JANUS_RECORDPLAY_ERROR_INVALID_ELEMENT   414
#define JANUS_RECORDPLAY_ERROR_INVALID_JSON   412
#define JANUS_RECORDPLAY_ERROR_INVALID_RECORDING   417
#define JANUS_RECORDPLAY_ERROR_INVALID_REQUEST   413
#define JANUS_RECORDPLAY_ERROR_INVALID_STATE   418
#define JANUS_RECORDPLAY_ERROR_MISSING_ELEMENT   415
#define JANUS_RECORDPLAY_ERROR_NO_MESSAGE   411
#define JANUS_RECORDPLAY_ERROR_NOT_FOUND   416
#define JANUS_RECORDPLAY_ERROR_UNKNOWN_ERROR   499
#define JANUS_RECORDPLAY_NAME   "JANUS Record&Play plugin"
#define JANUS_RECORDPLAY_PACKAGE   "janus.plugin.recordplay"
#define JANUS_RECORDPLAY_VERSION   3
#define JANUS_RECORDPLAY_VERSION_STRING   "0.0.3"
#define OPUS_PT   111
#define sdp_a_template
Value:
"m=audio 1 RTP/SAVPF %d\r\n" /* Opus payload type */ \
"c=IN IP4 1.1.1.1\r\n" \
"a=%s\r\n" /* Media direction */ \
"a=rtpmap:%d opus/48000/2\r\n" /* Opus payload type */
#define sdp_template
Value:
"v=0\r\n" \
"o=- %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n" /* We need current time here */ \
"s=%s\r\n" /* Recording playout id */ \
"t=0 0\r\n" \
"%s%s" /* Audio and/or video m-lines */
#define sdp_v_template
Value:
"m=video 1 RTP/SAVPF %d\r\n" /* VP8 payload type */ \
"c=IN IP4 1.1.1.1\r\n" \
"a=%s\r\n" /* Media direction */ \
"a=rtpmap:%d VP8/90000\r\n" /* VP8 payload type */ \
"a=rtcp-fb:%d ccm fir\r\n" /* VP8 payload type */ \
"a=rtcp-fb:%d nack\r\n" /* VP8 payload type */ \
"a=rtcp-fb:%d nack pli\r\n" /* VP8 payload type */ \
"a=rtcp-fb:%d goog-remb\r\n" /* VP8 payload type */
#define VP8_PT   100

Typedef Documentation

Function Documentation

janus_plugin* create ( void  )
void janus_recordplay_create_session ( janus_plugin_session handle,
int *  error 
)
void janus_recordplay_destroy ( void  )
void janus_recordplay_destroy_session ( janus_plugin_session handle,
int *  error 
)
int janus_recordplay_get_api_compatibility ( void  )
const char * janus_recordplay_get_author ( void  )
const char * janus_recordplay_get_description ( void  )
janus_recordplay_frame_packet * janus_recordplay_get_frames ( const char *  dir,
const char *  filename 
)
const char * janus_recordplay_get_name ( void  )
const char * janus_recordplay_get_package ( void  )
int janus_recordplay_get_version ( void  )
const char * janus_recordplay_get_version_string ( void  )
struct janus_plugin_result * janus_recordplay_handle_message ( janus_plugin_session handle,
char *  transaction,
char *  message,
char *  sdp_type,
char *  sdp 
)
read
void janus_recordplay_hangup_media ( janus_plugin_session handle)
void janus_recordplay_incoming_data ( janus_plugin_session handle,
char *  buf,
int  len 
)
void janus_recordplay_incoming_rtcp ( janus_plugin_session handle,
int  video,
char *  buf,
int  len 
)
void janus_recordplay_incoming_rtp ( janus_plugin_session handle,
int  video,
char *  buf,
int  len 
)
int janus_recordplay_init ( janus_callbacks callback,
const char *  onfig_path 
)
void janus_recordplay_message_free ( janus_recordplay_message msg)
char * janus_recordplay_query_session ( janus_plugin_session handle)
void janus_recordplay_send_rtcp_feedback ( janus_plugin_session handle,
int  video,
char *  buf,
int  len 
)
void janus_recordplay_setup_media ( janus_plugin_session handle)
void janus_recordplay_slow_link ( janus_plugin_session handle,
int  uplink,
int  video 
)
void janus_recordplay_update_recordings_list ( void  )
void * janus_recordplay_watchdog ( void *  data)