Skip to content

Commit

Permalink
Added a basic recording functionality plugins can use
Browse files Browse the repository at this point in the history
Added a new helper to allow plugins to record RTP media frames in a structured way;
Added a simple external utility to post-process those recordings to a webm or opus file;
Integrated the recording functionality in the Video MCU plugin, and added a new configuration value to selectively enable/disable the feature
  • Loading branch information
meetecho committed Jul 4, 2014
1 parent f6a402e commit 9d11ac5
Show file tree
Hide file tree
Showing 44 changed files with 1,588 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ STUFF = $(shell pkg-config --cflags glib-2.0 nice libmicrohttpd jansson libssl l
LIBS = $(shell pkg-config --libs glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config) -ldl -lsrtp $(SCTP_LIB) $(WS_LIB) -D_GNU_SOURCE $(HAVE_PORTRANGE) $(HAVE_SCTP) $(HAVE_WS)
OPTS = -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused -Wno-format-security #-Werror #-O2
GDB = -fstack-protector-all -g -ggdb -rdynamic #-gstabs
OBJS=janus.o cmdline.o config.o apierror.o rtcp.o dtls.o sctp.o ice.o sdp.o utils.o
OBJS=janus.o cmdline.o config.o apierror.o rtcp.o dtls.o sctp.o ice.o sdp.o record.o utils.o

all: cmdline janus plugins

Expand Down
2 changes: 1 addition & 1 deletion apierror.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file apierror.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus API errors definition
* \details Definition of all the API errors that may occur when invoking
* the Janus web-based JSON API.
Expand Down
4 changes: 4 additions & 0 deletions conf/janus.plugin.videoroom.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
; conference or 1 for a webinar)
; bitrate = <max video bitrate for senders> (e.g., 128000)
; fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
; record = true|false (whether this room should be recorded, default=false)
; rec_dir = <folder where recordings should be stored, when enabled>

[1234]
description = Demo Room
secret = adminpwd
publishers = 6
bitrate = 128000
fir_freq = 10
record = false
;rec_dir = /tmp/janus-videoroom
1 change: 1 addition & 0 deletions config.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*! \file config.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU General Public License v3
* \brief Configuration files parsing
* \details Implementation of a parser of INI configuration files (based on libini-config).
*
Expand Down
1 change: 1 addition & 0 deletions config.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*! \file config.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU General Public License v3
* \brief Configuration files parsing (headers)
* \details Implementation of a parser of INI configuration files (based on libini-config).
*
Expand Down
1 change: 1 addition & 0 deletions debug.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*! \file debug.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU General Public License v3
* \brief Logging and Debugging
* \details Implementation of a wrapper on printf (or g_print) to either log or debug.
* \todo Improve this wrappers to optionally save logs on file
Expand Down
2 changes: 1 addition & 1 deletion docs/janus-doxygen.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.

INPUT = .. ../plugins
INPUT = .. ../plugins ../postprocessing

# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
Expand Down
2 changes: 1 addition & 1 deletion dtls.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file dtls.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief DTLS/SRTP processing
* \details Implementation (based on OpenSSL and libsrtp) of the DTLS/SRTP
* transport. The code takes care of the DTLS handshake between peers and
Expand Down
2 changes: 1 addition & 1 deletion dtls.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file dtls.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief DTLS/SRTP processing (headers)
* \details Implementation (based on OpenSSL and libsrtp) of the DTLS/SRTP
* transport. The code takes care of the DTLS handshake between peers and
Expand Down
2 changes: 1 addition & 1 deletion ice.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file ice.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief ICE/STUN/TURN processing
* \details Implementation (based on libnice) of the ICE process. The
* code handles the whole ICE process, from the gathering of candidates
Expand Down
2 changes: 1 addition & 1 deletion ice.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file ice.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief ICE/STUN/TURN processing (headers)
* \details Implementation (based on libnice) of the ICE process. The
* code handles the whole ICE process, from the gathering of candidates
Expand Down
8 changes: 7 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,10 @@ fi

echo
echo "Done! Check the configuration files for both the gateway and the plugins in the 'conf' folder."
./janus -h
echo
echo "If you're also interested in compiling the Janus recordings post-processing utility, launch the install.sh script in the postprocessing folder:"
echo " cd postprocessing"
echo " ./install.sh"
echo
echo "Type './janus -h' for help on Janus"
echo
2 changes: 1 addition & 1 deletion janus.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus core
* \details Implementation of the gateway core. This code takes care of
* the gateway initialization (command line/configuration) and setup,
Expand Down
2 changes: 1 addition & 1 deletion janus.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus core (headers)
* \details Implementation of the gateway core. This code takes care of
* the gateway initialization (command line/configuration) and setup,
Expand Down
14 changes: 14 additions & 0 deletions mainpage.dox
Original file line number Diff line number Diff line change
Expand Up @@ -1589,3 +1589,17 @@ var server = "ws://www.example.com:8188/";
* exposed to browsers.
* \ingroup plugins
*/

/*! \defgroup tools Tools and utilities
* \brief Tools and utilities
* \details Set of simple tools and utilities that may be of help when
* used in conjunction with Janus.
*/

/*! \defgroup postprocessing Recordings post-processing utility
* \brief Recordings post-processing utility
* \details This simple utility (janus-pp-rec.c) allows you to
* post-process recordings generated by the janus_recorder helper (e.g.,
* in the Video MCU plugin).
* \ingroup tools
*/
2 changes: 1 addition & 1 deletion plugins/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ LIBS = $(shell pkg-config --libs glib-2.0 jansson sofia-sip-ua opus ogg ini_conf
OPTS = -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused -Wno-format-security #-Werror #-O2
GDB = -g -ggdb #-gstabs
OBJS = janus_echotest.so janus_streaming.so janus_sip.so janus_videocall.so janus_videoroom.so
UTILS = ../apierror.o ../config.o ../rtcp.o ../utils.o
UTILS = ../apierror.o ../config.o ../rtcp.o ../record.o ../utils.o
ifeq ($(HAVE_OPUS),1)
OBJS += janus_audiobridge.so
endif
Expand Down
2 changes: 1 addition & 1 deletion plugins/janus_audiobridge.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_audiobridge.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus AudioBridge plugin
* \details This is a plugin implementing an audio conference bridge for
* Janus, specifically mixing Opus streams. This means that it replies
Expand Down
6 changes: 3 additions & 3 deletions plugins/janus_echotest.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_echotest.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus EchoTest plugin
* \details This is a trivial EchoTest plugin for Janus, just used to
* showcase the plugin interface. A peer attaching to this plugin will
Expand Down Expand Up @@ -34,8 +34,8 @@


/* Plugin information */
#define JANUS_ECHOTEST_VERSION 2
#define JANUS_ECHOTEST_VERSION_STRING "0.0.2"
#define JANUS_ECHOTEST_VERSION 3
#define JANUS_ECHOTEST_VERSION_STRING "0.0.3"
#define JANUS_ECHOTEST_DESCRIPTION "This is a trivial EchoTest plugin for Janus, just used to showcase the plugin interface."
#define JANUS_ECHOTEST_NAME "JANUS EchoTest plugin"
#define JANUS_ECHOTEST_AUTHOR "Meetecho s.r.l."
Expand Down
2 changes: 1 addition & 1 deletion plugins/janus_sip.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_sip.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus SIP plugin
* \details This is a simple SIP plugin for Janus, allowing WebRTC peers
* to register at a SIP server (e.g., Asterisk) and call SIP user agents
Expand Down
2 changes: 1 addition & 1 deletion plugins/janus_streaming.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_streaming.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus Streaming plugin
* \details This is a streaming plugin for Janus, allowing WebRTC peers
* to watch/listen to pre-recorded files or media generated by another tool.
Expand Down
2 changes: 1 addition & 1 deletion plugins/janus_videocall.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_videocall.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus VideoCall plugin
* \details This is a simple video call plugin for Janus, allowing two
* WebRTC peers to call each other through the gateway. The idea is to
Expand Down
84 changes: 83 additions & 1 deletion plugins/janus_videoroom.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_videoroom.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus VideoRoom plugin
* \details This is a plugin implementing a videoconferencing MCU for Janus.
* This means that the plugin implements a virtual conferencing room peers
Expand Down Expand Up @@ -43,6 +43,8 @@ publishers = <max number of concurrent senders> (e.g., 6 for a video
conference or 1 for a webinar)
bitrate = <max video bitrate for senders> (e.g., 128000)
fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
record = true|false (whether this room should be recorded, default=false)
rec_dir = <folder where recordings should be stored, when enabled>
\endverbatim
*
* \ingroup plugins
Expand All @@ -57,6 +59,7 @@ fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
#include "../config.h"
#include "../mutex.h"
#include "../rtcp.h"
#include "../record.h"
#include "../utils.h"


Expand Down Expand Up @@ -167,6 +170,8 @@ typedef struct janus_videoroom {
int max_publishers; /* Maximum number of concurrent publishers */
uint64_t bitrate; /* Global bitrate limit */
uint16_t fir_freq; /* Regular FIR frequency (0=disabled) */
gboolean record; /* Whether the feeds from publishers in this room should be recorded */
char *rec_dir; /* Where to save the recordings of this room, if enabled */
gboolean destroy; /* Value to flag the room for destruction */
GHashTable *participants; /* Map of potential publishers (we get listeners from them) */
} janus_videoroom;
Expand Down Expand Up @@ -196,6 +201,8 @@ typedef struct janus_videoroom_participant {
uint64_t bitrate;
gint64 fir_latest; /* Time of latest sent FIR (to avoid flooding) */
gint fir_seq; /* FIR sequence number */
janus_recorder *arc; /* The Janus recorder instance for this publisher's audio, if enabled */
janus_recorder *vrc; /* The Janus recorder instance for this publisher's video, if enabled */
GSList *listeners;
janus_mutex listeners_mutex;
} janus_videoroom_participant;
Expand Down Expand Up @@ -296,6 +303,8 @@ int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
janus_config_item *bitrate = janus_config_get_item(cat, "bitrate");
janus_config_item *maxp = janus_config_get_item(cat, "publishers");
janus_config_item *firfreq = janus_config_get_item(cat, "fir_freq");
janus_config_item *record = janus_config_get_item(cat, "record");
janus_config_item *rec_dir = janus_config_get_item(cat, "rec_dir");
/* Create the video mcu room */
janus_videoroom *videoroom = calloc(1, sizeof(janus_videoroom));
if(videoroom == NULL) {
Expand Down Expand Up @@ -329,12 +338,28 @@ int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
videoroom->fir_freq = 0;
if(firfreq != NULL && firfreq->value != NULL)
videoroom->fir_freq = atol(firfreq->value);
if(record && record->value) {
if(!strcasecmp(record->value, "true"))
videoroom->record = TRUE;
else if(!strcasecmp(record->value, "false"))
videoroom->record = FALSE;
else {
JANUS_LOG(LOG_WARN, "Invalid value '%s' for 'record', recording disabled\n", record->value);
videoroom->record = FALSE;
}
if(rec_dir && rec_dir->value) {
videoroom->rec_dir = g_strdup(rec_dir->value);
}
}
videoroom->destroy = 0;
videoroom->participants = g_hash_table_new(NULL, NULL);
janus_mutex_lock(&rooms_mutex);
g_hash_table_insert(rooms, GUINT_TO_POINTER(videoroom->room_id), videoroom);
janus_mutex_unlock(&rooms_mutex);
JANUS_LOG(LOG_VERB, "Created videoroom: %"SCNu64" (%s, secret: %s)\n", videoroom->room_id, videoroom->room_name, videoroom->room_secret ? videoroom->room_secret : "no secret");
if(videoroom->record) {
JANUS_LOG(LOG_VERB, " -- Room is going to be recorded in %s\n", videoroom->rec_dir ? videoroom->rec_dir : "the current folder");
}
cat = cat->next;
}
/* Done */
Expand Down Expand Up @@ -484,6 +509,15 @@ void janus_videoroom_destroy_session(janus_plugin_session *handle, int *error) {
}
g_free(leaving_text);
g_list_free(participants_list);
/* Get rid of the recorders, if available */
if(participant->arc) {
janus_recorder_close(participant->arc);
JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", participant->arc->filename ? participant->arc->filename : "??");
}
if(participant->vrc) {
janus_recorder_close(participant->vrc);
JANUS_LOG(LOG_INFO, "Closed video recording %s\n", participant->vrc->filename ? participant->vrc->filename : "??");
}
} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
/* Detaching this listener from its subscriber is already done by hangup_media */
}
Expand Down Expand Up @@ -556,6 +590,13 @@ void janus_videoroom_incoming_rtp(janus_plugin_session *handle, int video, char
return;
janus_videoroom_participant *participant = (janus_videoroom_participant *)session->participant;
if((!video && participant->audio_active) || (video && participant->video_active)) {
/* Save the frame if we're recording */
if(participant->room->record) {
if(video && participant->vrc)
janus_recorder_save_frame(participant->vrc, buf, len);
else if(!video && participant->arc)
janus_recorder_save_frame(participant->arc, buf, len);
}
janus_videoroom_rtp_relay_packet packet;
packet.data = buf;
packet.length = len;
Expand Down Expand Up @@ -792,6 +833,20 @@ static void *janus_videoroom_handler(void *data) {
sprintf(error_cause, "Invalid element (publishers should be an integer)");
goto error;
}
json_t *record = json_object_get(root, "record");
if(record && !json_is_boolean(record)) {
JANUS_LOG(LOG_ERR, "Invalid element (record should be a boolean)\n");
error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
sprintf(error_cause, "Invalid element (record should be a boolean)");
goto error;
}
json_t *rec_dir = json_object_get(root, "rec_dir");
if(rec_dir && !json_is_string(rec_dir)) {
JANUS_LOG(LOG_ERR, "Invalid element (rec_dir should be a string)\n");
error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
sprintf(error_cause, "Invalid element (rec_dir should be a string)");
goto error;
}
/* Create the audio bridge room */
janus_videoroom *videoroom = calloc(1, sizeof(janus_videoroom));
if(videoroom == NULL) {
Expand Down Expand Up @@ -840,10 +895,19 @@ static void *janus_videoroom_handler(void *data) {
videoroom->fir_freq = 0;
if(fir_freq)
videoroom->fir_freq = json_integer_value(fir_freq);
if(record) {
videoroom->record = json_is_true(record);
if(videoroom->record && rec_dir) {
videoroom->rec_dir = g_strdup(json_string_value(rec_dir));
}
}
videoroom->destroy = 0;
videoroom->participants = g_hash_table_new(NULL, NULL);
g_hash_table_insert(rooms, GUINT_TO_POINTER(videoroom->room_id), videoroom);
JANUS_LOG(LOG_VERB, "Created videoroom: %"SCNu64" (%s, secret: %s)\n", videoroom->room_id, videoroom->room_name, videoroom->room_secret ? videoroom->room_secret : "no secret");
if(videoroom->record) {
JANUS_LOG(LOG_VERB, " -- Room is going to be recorded in %s\n", videoroom->rec_dir ? videoroom->rec_dir : "the current folder");
}
/* Show updated rooms list */
GList *rooms_list = g_hash_table_get_values(rooms);
GList *r = rooms_list;
Expand Down Expand Up @@ -1411,6 +1475,24 @@ static void *janus_videoroom_handler(void *data) {
newsdp = tempsdp;
}
}
/* Is this room recorded? */
if(videoroom->record) {
char filename[255];
memset(filename, 0, 255);
sprintf(filename, "videoroom-%"SCNu64"-user-%"SCNu64"-%"SCNi64"-audio",
videoroom->room_id, participant->user_id, janus_get_monotonic_time());
participant->arc = janus_recorder_create(videoroom->rec_dir, 0, filename);
if(participant->arc == NULL) {
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this publisher!\n");
}
memset(filename, 0, 255);
sprintf(filename, "videoroom-%"SCNu64"-user-%"SCNu64"-%"SCNi64"-video",
videoroom->room_id, participant->user_id, janus_get_monotonic_time());
participant->vrc = janus_recorder_create(videoroom->rec_dir, 1, filename);
if(participant->vrc == NULL) {
JANUS_LOG(LOG_ERR, "Couldn't open a video recording file for this publisher!\n");
}
}

JANUS_LOG(LOG_VERB, "Handling publisher: turned this into an '%s':\n%s\n", type, newsdp);
/* How long will the gateway take to push the event? */
Expand Down
4 changes: 2 additions & 2 deletions plugins/janus_voicemail.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! \file janus_voicemail.c
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU Affero General Public License v3
* \copyright GNU General Public License v3
* \brief Janus VoiceMail plugin
* \details This is a plugin implementing a very simple VoiceMail service
* for Janus, specifically recording Opus streams. This means that it replies
Expand Down Expand Up @@ -724,7 +724,7 @@ ogg_packet *op_opushead() {
/* Manufacture a generic OpusTags packet */
ogg_packet *op_opustags() {
char *identifier = "OpusTags";
char *vendor = "opus rtp packet dump";
char *vendor = "Janus VoiceMail plugin";
int size = strlen(identifier) + 4 + strlen(vendor) + 4;
unsigned char *data = malloc(size);
ogg_packet *op = malloc(sizeof(*op));
Expand Down
Loading

0 comments on commit 9d11ac5

Please sign in to comment.