Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 88 additions & 9 deletions docs/rtpengine-recording.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,24 +367,101 @@ sufficient for a standard installation of rtpengine.

- __\-\-notify-uri=__*URI*

Enable HTTP notification about finished recordings to the specified URI, which
must be an HTTP or HTTPS URI. Information about the finished recording is
provided via custom HTTP headers, all of which use a prefix of __X-Recording-__.
Enable HTTP notification about recording lifecycle events to the specified URI,
which must be an HTTP or HTTPS URI. Information is provided via custom HTTP
headers, all of which use a prefix of __X-Recording-__.

By default only the historical __finished__ event is emitted (see
__\-\-notify-events__). When additional events are enabled, the same URI
receives notifications for file open, first write, successful finish,
discard, and failure.

- __\-\-notify-events=__*CSV*

Comma-separated list of lifecycle events to emit. Default is __finished__
for backward compatibility with existing consumers.

Supported tokens:

- __opened__ - after output media file is created and header written
(__recording_file_opened__).
- __started__ - first media packet successfully written
(__recording_started__).
- __finished__ - successful close of a non-empty recording
(__recording_finished__; historical default).
- __discarded__ - closed empty or with discard flag
(__recording_discarded__).
- __failed__ - open/configure failure (__recording_failed__).
- __call-started__ - first mix/single output armed for a call
(__call_recording_started__).
- __call-finished__ - call metafile torn down on success path
(__call_recording_finished__).
- __call-discarded__ - call torn down with discard flag
(__call_recording_discarded__).
- __all__ - enable every token above.

Additional headers on lifecycle notifications:

- __X-Recording-Event__ - event name (e.g. recording_file_opened)
- __X-Recording-Status__ - short status (opened, started, finished, ...)
- __X-Recording-Event-Time__ - unix epoch (float seconds)
- __X-Recording-Output-Id__ - correlates open/start/finish for one stream

Terminal extras when available (finished / discarded / failed):
__X-Recording-Duration-MS__, __X-Recording-File-Size__,
__X-Recording-Sample-Rate__, __X-Recording-Channels__,
__X-Recording-Call-End-Time__, __X-Recording-Stream-End-Time__.
Failure events may also set __X-Recording-Error-Code__ /
__X-Recording-Error-Message__.

Ordering is best-effort asynchronous: opened -> started -> terminal.
Call-level ordering is call-started before stream events and
call-finished / call-discarded after outputs close. Delivery uses the
existing notify thread pool with retries; notify failures never abort
media I/O. Non-terminal queue bound (__\-\-notify-queue-limit__) may drop
excess non-terminal events under load; terminal events are never dropped
by that limit. __\-\-notify-record__ / __output-storage=notify__ and
__\-\-notify-purge__ apply only to finished events.

- __\-\-notify-json__

Send a JSON body with HTTP notifications and force POST. Headers remain
for compatibility. JSON fields include event, event_time, status,
call_id, kind, file names, output_id, db, times, media, tag, metadata,
and error.

- __\-\-notify-no-metadata__

Omit call metadata strings from headers and JSON (default: include
metadata).

- __\-\-notify-post__

Use HTTP POST instead of GET for the HTTP notification requests. The request
body is empty even if POST is used.
body is empty unless __\-\-notify-json__ is also set (or the recording is
attached via __output-storage=notify__ / __\-\-notify-record__ on finished).

- __\-\-notify-no-verify__

Disable TLS peer certificate verification for HTTPS requests.

- __\-\-notify-command=__*PATH*

External command to launch to send a notification about a new recording.
The command will receive two command line arguments: The full path and file
name of the recording, and the ID number from the database entry.
External command to launch for notifications. Argument layout depends on
__\-\-notify-command-format__.

- __\-\-notify-command-format=__*legacy|extended|json-env*

How arguments are passed to __\-\-notify-command__:

- __legacy__ (default): cmd path db_id
- __extended__: cmd event path db_id call_id kind full_filename
- __json-env__: cmd with NOTIFY_PAYLOAD (JSON) and NOTIFY_EVENT env vars

- __\-\-notify-queue-limit=__*INT*

Maximum number of in-flight non-terminal notifications. 0 means unlimited.
Default is 1000. Terminal notifications are always accepted.

- __\-\-notify-concurrency=__*INT*

Expand All @@ -400,12 +477,14 @@ sufficient for a standard installation of rtpengine.

Legacy alias for __output-storage=notify__. If no other output storage is
enabled, then the default __file__ storage remains enabled (unless the
following option is also set).
following option is also set). File attach applies only to finished events
and is not combined with a JSON body.

- __\-\-notify-purge__

Legacy option to disable the default file storage when notification output
is enabled.
is enabled. When used with file attach behaviour, local file removal after
a successful notification applies only to finished events.

- __\-\-output-mixed-per-media__

Expand Down
7 changes: 6 additions & 1 deletion etc/rtpengine-recording.conf
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,18 @@ table = 0
# output-chown = rtpengine
# output-chgrp = rtpengine

### HTTP notifications for finished recordings
### HTTP notifications for recording lifecycle events
# notify-uri = https://example.com/rec/finished
# notify-post = false
# notify-no-verify = false
# notify-purge = false
# notify-concurrency = 5
# notify-retries = 10
# notify-events = finished
# notify-json = false
# notify-no-metadata = false
# notify-command-format = legacy
# notify-queue-limit = 1000

### S3 storage options
# s3-host = examplebucket.s3.amazonaws.com
Expand Down
2 changes: 1 addition & 1 deletion recording-daemon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ CFLAGS += $(CFLAGS_BCG729)
LDLIBS += $(LDLIBS_BCG729)

SRCS := epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \
decoder.c output.c mix.c db.c log.c forward.c tag.c custom_poller.c notify.c tls_send.c s3.c \
decoder.c output.c mix.c db.c log.c forward.c tag.c custom_poller.c notify.c notify_events.c tls_send.c s3.c \
gcs.c
LIBSRCS := loglib.c auxlib.c rtplib.c codeclib.c resample.c str.c socket.c streambuf.c ssllib.c \
dtmflib.c bufferpool.c bencode.c http.c s3utils.c oauth.c cchain.c
Expand Down
29 changes: 29 additions & 0 deletions recording-daemon/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "socket.h"
#include "ssllib.h"
#include "notify.h"
#include "notify_events.h"
#include "gcs.h"


Expand Down Expand Up @@ -68,6 +69,11 @@ gboolean notify_nverify;
int notify_threads = 5;
int notify_retries = 10;
char *notify_command;
unsigned int notify_events_mask = NOTIFY_MASK_DEFAULT;
gboolean notify_json = FALSE;
gboolean notify_no_metadata = FALSE;
int notify_command_format = 0; /* NOTIFY_CMD_LEGACY */
int notify_queue_limit = 1000;
gboolean mix_output_per_media = 0;
gboolean flush_packets = 0;
int resample_audio;
Expand Down Expand Up @@ -213,6 +219,8 @@ static void options(int *argc, char ***argv) {
gboolean notify_record = FALSE;
bool no_output_allowed = false;
gboolean notify_purge = false;
g_autoptr(char) notify_events_str = NULL;
g_autoptr(char) notify_cmd_fmt_str = NULL;

GOptionEntry e[] = {
{ "table", 't', 0, G_OPTION_ARG_INT, &ktable, "Kernel table rtpengine uses", "INT" },
Expand Down Expand Up @@ -251,6 +259,11 @@ static void options(int *argc, char ***argv) {
{ "notify-concurrency", 0, 0, G_OPTION_ARG_INT, &notify_threads,"How many simultaneous requests", "INT" },
{ "notify-retries", 0, 0, G_OPTION_ARG_INT, &notify_retries,"How many times to retry failed requests","INT" },
{ "notify-command", 0, 0, G_OPTION_ARG_STRING, &notify_command,"External command to execute for notifications","PATH" },
{ "notify-events", 0, 0, G_OPTION_ARG_STRING, &notify_events_str,"CSV of lifecycle events to emit","opened,started,finished,..." },
{ "notify-json", 0, 0, G_OPTION_ARG_NONE, &notify_json,"Send JSON body with HTTP notifications", NULL },
{ "notify-no-metadata", 0, 0, G_OPTION_ARG_NONE, &notify_no_metadata,"Omit call metadata from notify payloads", NULL },
{ "notify-command-format",0, 0, G_OPTION_ARG_STRING, &notify_cmd_fmt_str,"Command argv format","legacy|extended|json-env" },
{ "notify-queue-limit", 0, 0, G_OPTION_ARG_INT, &notify_queue_limit,"Max queued non-terminal notifies (0=unlimited)","INT" },
{ "output-mixed-per-media",0,0, G_OPTION_ARG_NONE, &mix_output_per_media,"Mix participating sources into a single output", NULL },
#if CURL_AT_LEAST_VERSION(7,56,0)
{ "notify-record", 0, 0, G_OPTION_ARG_NONE, &notify_record, "Also attach recorded file to request", NULL },
Expand All @@ -276,6 +289,22 @@ static void options(int *argc, char ***argv) {
config_load(argc, argv, e, " - rtpengine recording daemon",
"/etc/rtpengine/rtpengine-recording.conf", "rtpengine-recording", &rtpe_common_config);

/* notify-events CSV (default finished) */
{
char *bad = NULL;
if (!notify_events_parse(notify_events_str, &notify_events_mask, &bad))
die("Invalid notify-events token '%s'", bad ? bad : "?");
g_free(bad);
}
{
enum notify_command_format fmt = NOTIFY_CMD_LEGACY;
if (!notify_command_format_parse(notify_cmd_fmt_str, &fmt))
die("Invalid notify-command-format (use legacy|extended|json-env)");
notify_command_format = (int) fmt;
}
if (notify_queue_limit < 0)
die("notify-queue-limit must be >= 0");

// default config, if not configured
if (spool_dir == NULL)
spool_dir = g_strdup("/var/spool/rtpengine");
Expand Down
5 changes: 5 additions & 0 deletions recording-daemon/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ extern gboolean notify_nverify;
extern int notify_threads;
extern int notify_retries;
extern char *notify_command;
extern unsigned int notify_events_mask;
extern gboolean notify_json;
extern gboolean notify_no_metadata;
extern int notify_command_format;
extern int notify_queue_limit;
extern gboolean mix_output_per_media;
extern volatile int shutdown_flag;
extern gboolean flush_packets;
Expand Down
9 changes: 9 additions & 0 deletions recording-daemon/metafile.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "recaux.h"
#include "packet.h"
#include "output.h"
#include "notify.h"
#include "mix.h"
#include "db.h"
#include "forward.h"
Expand Down Expand Up @@ -89,6 +90,9 @@ static void meta_destroy(metafile_t *mf) {
output_close(mf, mf->mix_out, NULL, mf->discard);
mf->mix_out = NULL;
mix_close(mf->mix);

/* Call-level terminal notify after stream/mix outputs close. */
notify_push_call(mf);
}


Expand All @@ -112,6 +116,11 @@ static void meta_mix_file_output(metafile_t *mf) {
}

db_do_stream(mf, mf->mix_out, NULL, 0);

if (mf->mix_out && !mf->notify_call_started) {
mf->notify_call_started = 1;
notify_push_call_event(NOTIFY_EVT_CALL_STARTED, mf);
}
}


Expand Down
Loading
Loading