diff --git a/docs/rtpengine-recording.md b/docs/rtpengine-recording.md index 9e836fa1b..4df310e48 100644 --- a/docs/rtpengine-recording.md +++ b/docs/rtpengine-recording.md @@ -138,15 +138,27 @@ sufficient for a standard installation of rtpengine. Points to the shared object file (__.so__) containing the reference implementation for the EVS codec. See the `README` for more details. -- __\-\-output-storage=file__\|__db__\|__db-mem__\|__both__ - - Where to store media files. By default, media files are written directly to the - file system (see __output-dir__). They can also be stored as a __BLOB__ in a - MySQL database, either instead of, or in addition to, being written to the file - system. Database storage can either be facilitated using temporary files - (__db__) which are then read back and deleted, or without temporary files - (__db-mem__) by spooling the audio data in memory until the recording is - complete. +- __\-\-output-storage=file__\|__db__\|__memory__ + + Where to store media files. This option can be given multiple times (or, in + the config file, using a comma-separated list) to enable multiple storage + modes. By default only the __file__ storage is enabled. + + The __file__ storage writes media files directly to the file system (see + __output-dir__). + + The __db__ storage writes media files as a __BLOB__ in a MySQL database. + + The string __both__ is recognised as legacy alternative to enabling both + __file__ and __db__ storage. + + The string __memory__ acts as a modifier and can be used if __file__ + storage is not enabled. Without the __memory__ modifier, media is first written + to a temporary file before being placed into its destination storage when + recording is finished. With __memory__ set, use of temporary files is disabled + and media is kept in memory only until recording is complete. + + __db-mem__ can be used as a shortcut to setting both __db__ and __memory__. - __\-\-output-dir=__*PATH* diff --git a/etc/rtpengine-recording.conf b/etc/rtpengine-recording.conf index 02dc8199f..a3dd277bb 100644 --- a/etc/rtpengine-recording.conf +++ b/etc/rtpengine-recording.conf @@ -8,8 +8,8 @@ table = 0 ### where to forward to (unix socket) # forward-to = /run/rtpengine/sock -### where to store recordings: file (default), db, both -# output-storage = db +### where to store recordings: file (default), db, memory +# output-storage = db;memory ### format of stored recordings: wav (default), mp3 # output-format = mp3 diff --git a/recording-daemon/main.c b/recording-daemon/main.c index e1c653e3a..b80640ac5 100644 --- a/recording-daemon/main.c +++ b/recording-daemon/main.c @@ -35,7 +35,7 @@ int ktable = 0; int num_threads; -enum output_storage_enum output_storage = OUTPUT_STORAGE_FILE; +enum output_storage_enum output_storage; char *spool_dir = NULL; char *output_dir = NULL; static char *output_format = NULL; @@ -190,7 +190,7 @@ static mode_t chmod_parse(const char *s) { static void options(int *argc, char ***argv) { - g_autoptr(char) os_str = NULL; + g_autoptr(char_p) os_a = NULL; g_autoptr(char) chmod_mode = NULL; g_autoptr(char) chmod_dir_mode = NULL; g_autoptr(char) user_uid = NULL; @@ -202,7 +202,7 @@ static void options(int *argc, char ***argv) { { "table", 't', 0, G_OPTION_ARG_INT, &ktable, "Kernel table rtpengine uses", "INT" }, { "spool-dir", 0, 0, G_OPTION_ARG_FILENAME, &spool_dir, "Directory containing rtpengine metadata files", "PATH" }, { "num-threads", 0, 0, G_OPTION_ARG_INT, &num_threads, "Number of worker threads", "INT" }, - { "output-storage", 0, 0, G_OPTION_ARG_STRING, &os_str, "Where to store audio streams", "file|db|both" }, + { "output-storage", 0, 0, G_OPTION_ARG_STRING_ARRAY,&os_a, "Where to store audio streams", "file|db|memory"}, { "output-dir", 0, 0, G_OPTION_ARG_STRING, &output_dir, "Where to write media files to", "PATH" }, { "output-pattern", 0, 0, G_OPTION_ARG_STRING, &output_pattern,"File name pattern for recordings", "STRING" }, { "output-format", 0, 0, G_OPTION_ARG_STRING, &output_format, "Write audio files of this type", "wav|mp3|none" }, @@ -289,16 +289,31 @@ static void options(int *argc, char ***argv) { if (!tls_send_to && !tcp_send_to) tls_mixed = false; - if (!os_str || !strcmp(os_str, "file")) + // output config + for (char *const *iter = os_a; iter && *iter; iter++) { + if (!strcmp(*iter, "file")) + output_storage |= OUTPUT_STORAGE_FILE; + else if (!strcmp(*iter, "db")) + output_storage |= OUTPUT_STORAGE_DB; + else if (!strcmp(*iter, "db-mem")) + output_storage |= OUTPUT_STORAGE_DB | OUTPUT_STORAGE_MEMORY; + else if (!strcmp(*iter, "mem") || !strcmp(*iter, "memory")) + output_storage |= OUTPUT_STORAGE_MEMORY; + else if (!strcmp(*iter, "both")) + output_storage |= OUTPUT_STORAGE_FILE | OUTPUT_STORAGE_DB; + else + die("Invalid 'output-storage' option '%s'", *iter); + } + + // default: + if (output_storage == 0) output_storage = OUTPUT_STORAGE_FILE; - else if (!strcmp(os_str, "db")) - output_storage = OUTPUT_STORAGE_DB; - else if (!strcmp(os_str, "db-mem")) - output_storage = OUTPUT_STORAGE_DB | OUTPUT_STORAGE_DB_MEMORY; - else if (!strcmp(os_str, "both")) - output_storage = OUTPUT_STORAGE_BOTH; - else - die("Invalid 'output-storage' option"); + + // sane config? + if ((output_storage & OUTPUT_STORAGE_MASK) == 0) + die("No output storage configured"); + if ((output_storage & OUTPUT_STORAGE_DB) && (!c_mysql_host || !c_mysql_db)) + die("DB output storage is enabled but no DB is configured"); if (!mix_method_str || !mix_method_str[0] || !strcmp(mix_method_str, "direct")) mix_method = MM_DIRECT; @@ -312,6 +327,8 @@ static void options(int *argc, char ***argv) { if ((output_storage & OUTPUT_STORAGE_FILE) && !strcmp(output_dir, spool_dir)) die("The spool-dir cannot be the same as the output-dir"); + if ((output_storage & OUTPUT_STORAGE_FILE) && (output_storage & OUTPUT_STORAGE_MEMORY)) + die("Memory storage and file storage are mutually exclusive"); // no threads here, so safe to use the non-_r versions of these lookups if (user_uid && *user_uid) { diff --git a/recording-daemon/main.h b/recording-daemon/main.h index 232a9abef..81b13eafb 100644 --- a/recording-daemon/main.h +++ b/recording-daemon/main.h @@ -10,8 +10,11 @@ enum output_storage_enum { OUTPUT_STORAGE_FILE = 0x1, OUTPUT_STORAGE_DB = 0x2, - OUTPUT_STORAGE_BOTH = 0x3, - OUTPUT_STORAGE_DB_MEMORY = 0x4, + + OUTPUT_STORAGE_MASK = 0xff, + + // modifier flags below + OUTPUT_STORAGE_MEMORY = 0x100, }; enum mix_method { MM_DIRECT = 0, diff --git a/recording-daemon/output.c b/recording-daemon/output.c index 71aa29f7b..070f04966 100644 --- a/recording-daemon/output.c +++ b/recording-daemon/output.c @@ -416,7 +416,7 @@ static bool output_config_(sink_t *sink, output_t *output, const format_t *reque char *full_fn = NULL; - if ((output_storage & OUTPUT_STORAGE_DB_MEMORY)) + if ((output_storage & OUTPUT_STORAGE_MEMORY)) goto no_output_file; char suff[16] = ""; @@ -441,7 +441,7 @@ got_fn: output->filename = full_fn; err = "failed to open output file"; - if (!(output_storage & OUTPUT_STORAGE_DB_MEMORY)) { + if (!(output_storage & OUTPUT_STORAGE_MEMORY)) { output->fp = fopen(full_fn, (output_storage & OUTPUT_STORAGE_DB) ? "wb+" : "wb"); if (!output->fp) goto err; @@ -469,7 +469,7 @@ no_output_file: if (!avio_buf) goto err; - if (!(output_storage & OUTPUT_STORAGE_DB_MEMORY)) + if (!(output_storage & OUTPUT_STORAGE_MEMORY)) output->avioctx = avio_alloc_context(avio_buf, DEFAULT_AVIO_BUFSIZE, 1, output, NULL, output_avio_write, output_avio_seek); else {