TT#111357 add support for configurable output file name patterns

Change-Id: I8662a2ee93700329ee2641c536be9ea8306f6b89
pull/1218/head
Richard Fuchs 5 years ago
parent 7c25d39f70
commit c8d6a45777

@ -42,6 +42,7 @@ int output_enabled = 1;
mode_t output_chmod;
uid_t output_chown = -1;
gid_t output_chgrp = -1;
char *output_pattern = NULL;
int decoding_enabled;
char *c_mysql_host,
*c_mysql_user,
@ -83,14 +84,8 @@ static void setup(void) {
socket_init();
if (decoding_enabled)
codeclib_init(0);
if (output_enabled) {
if (output_enabled)
output_init(output_format);
if (!g_file_test(output_dir, G_FILE_TEST_IS_DIR)) {
ilog(LOG_INFO, "Creating output dir '%s'", output_dir);
if (mkdir(output_dir, 0700))
die_errno("Failed to create output dir '%s'", output_dir);
}
}
mysql_library_init(0, NULL, NULL);
signals();
metafile_setup();
@ -174,6 +169,7 @@ static void options(int *argc, char ***argv) {
{ "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-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" },
{ "resample-to", 0, 0, G_OPTION_ARG_INT, &resample_audio,"Resample all output audio", "INT" },
{ "mp3-bitrate", 0, 0, G_OPTION_ARG_INT, &mp3_bitrate, "Bits per second for MP3 encoding", "INT" },
@ -275,6 +271,13 @@ static void options(int *argc, char ***argv) {
if (num_threads <= 0)
num_threads = num_cpu_cores(8);
if (!output_pattern)
output_pattern = g_strdup("%c-%t");
if (!strstr(output_pattern, "%c"))
die("Invalid output pattern '%s' (no '%%c' format present)", output_pattern);
if (!strstr(output_pattern, "%t"))
die("Invalid output pattern '%s' (no '%%t' format present)", output_pattern);
}
static void options_free(void) {
@ -288,6 +291,7 @@ static void options_free(void) {
g_free(c_mysql_db);
g_free(forward_to);
g_free(tls_send_to);
g_free(output_pattern);
// free common config options
config_load_free(&rtpe_common_config);

@ -24,6 +24,7 @@ extern int output_enabled;
extern mode_t output_chmod;
extern uid_t output_chown;
extern gid_t output_chgrp;
extern char *output_pattern;
extern int decoding_enabled;
extern char *c_mysql_host,
*c_mysql_user,

@ -73,9 +73,7 @@ static void meta_stream_interface(metafile_t *mf, unsigned long snum, char *cont
if (output_enabled && output_mixed) {
pthread_mutex_lock(&mf->mix_lock);
if (!mf->mix) {
char buf[256];
snprintf(buf, sizeof(buf), "%s-mix", mf->parent);
mf->mix_out = output_new(output_dir, buf);
mf->mix_out = output_new(output_dir, mf->parent, "mix");
mf->mix = mix_new();
db_do_stream(mf, mf->mix_out, "mixed", NULL, 0);
}

@ -6,6 +6,7 @@
#include <glib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "log.h"
#include "db.h"
#include "main.h"
@ -46,13 +47,98 @@ int output_add(output_t *output, AVFrame *frame) {
}
output_t *output_new(const char *path, const char *filename) {
output_t *output_new(const char *path, const char *call, const char *type) {
// construct output file name
time_t now = time(NULL);
struct tm tm;
localtime_r(&now, &tm);
const char *ax = call;
GString *f = g_string_new("");
for (const char *p = output_pattern; *p; p++) {
if (*p != '%') {
g_string_append_c(f, *p);
continue;
}
p++;
switch (*p) {
case '\0':
ilog(LOG_ERR, "Invalid output pattern (trailing %%)");
goto done;
case '%':
g_string_append_c(f, '%');
break;
case 'c':
g_string_append(f, call);
break;
case 't':
g_string_append(f, type);
break;
case 'Y':
g_string_append_printf(f, "%04i", tm.tm_year + 1900);
break;
case 'm':
g_string_append_printf(f, "%02i", tm.tm_mon + 1);
break;
case 'd':
g_string_append_printf(f, "%02i", tm.tm_mday);
break;
case 'H':
g_string_append_printf(f, "%02i", tm.tm_hour);
break;
case 'M':
g_string_append_printf(f, "%02i", tm.tm_min);
break;
case 'S':
g_string_append_printf(f, "%02i", tm.tm_sec);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':;
char *end;
long len = strtol(p, &end, 10);
if (len <= 0 || len == LONG_MAX || end == p) {
ilog(LOG_ERR, "Invalid output pattern (invalid number at '%%%s')", p);
break;
}
while (*ax && len--)
g_string_append_c(f, *ax++);
p = end - 1; // will be advanced +1 in the next loop
break;
default:
ilog(LOG_ERR, "Invalid output pattern (unknown format character '%c')", *p);
break;
}
}
done:;
output_t *ret = g_slice_alloc0(sizeof(*ret));
ret->file_path = g_strdup(path);
ret->file_name = g_strdup(filename);
ret->full_filename = g_strdup_printf("%s/%s", path, filename);
ret->file_name = f->str; // stealing the content
ret->full_filename = g_strdup_printf("%s/%s", path, f->str);
ret->file_format = output_file_format;
ret->encoder = encoder_new();
// create parent directories if needed
char *last_sep = strrchr(ret->full_filename, G_DIR_SEPARATOR);
if (last_sep) {
*last_sep = '\0';
if (g_mkdir_with_parents(ret->full_filename, 0700))
ilog(LOG_WARN, "Failed to create (parent) directory for '%s': %s",
ret->full_filename, strerror(errno));
*last_sep = G_DIR_SEPARATOR;
}
g_string_free(f, FALSE);
return ret;
}

@ -10,7 +10,7 @@ extern int mp3_bitrate;
void output_init(const char *format);
output_t *output_new(const char *path, const char *filename);
output_t *output_new(const char *path, const char *call, const char *type);
void output_close(output_t *);
int output_config(output_t *output, const format_t *requested_format, format_t *actual_format);

@ -177,9 +177,9 @@ out:
dbg("Init for SSRC %s%lx%s of stream #%lu", FMT_M(ret->ssrc), stream->id);
if (mf->recording_on && !ret->output && output_single) {
char buf[256];
snprintf(buf, sizeof(buf), "%s-%08lx", mf->parent, ssrc);
ret->output = output_new(output_dir, buf);
char buf[16];
snprintf(buf, sizeof(buf), "%08lx", ssrc);
ret->output = output_new(output_dir, mf->parent, buf);
db_do_stream(mf, ret->output, "single", stream, ssrc);
}
if ((stream->forwarding_on || mf->forwarding_on) && !ret->tls_fwd_stream) {

@ -140,6 +140,58 @@ Path for media files to be written to if file output is enabled. Defaults to
F</var/lib/rtpengine-recording>. The path must not be the same as used for the
B<spool-dir>.
=item B<--output-pattern=>I<STRING>
File name pattern to be used for recording files. The pattern can reference
sub-directories. Parent directories will be created on demand. The default
setting is B<%c-%t>.
The pattern must include B<printf>-style format sequences. Supported format
sequences are:
=over
=item B<%%>
A literal percent sign.
=item B<%c>
The call ID. It is mandatory for the output pattern to include this format
sequence.
=item B<%t>
The stream type. For B<single> streams this is the SSRC written as hexadecimal;
for B<mix> stream this is the string B<mix>. It is mandatory for the output
pattern to include this format sequence.
=item B<%Y>
=item B<%m>
=item B<%d>
=item B<%H>
=item B<%M>
=item B<%S>
These format sequence reference the current system time (when the output file
was created) and are the same as the format sequences supported by L<date(1)>
or L<strftime(3)> (year, month, day, hours, minutes, and seconds,
respectively).
=item B<%>I<INT>
References a prefix from the call ID of the given length. If this format
sequence is present more than once, then the prefixes are cumulative. For
example, if the call ID is B<abcdefgh> and the output pattern is configured as
B<%2/%3/%c>, then the resulting output file name would be B<ab/cde/abcdefgh>.
=back
=item B<--output-format=>B<wav>|B<mp3>|B<none>
File format to be used for media files that are produced. Defaults to PCM WAV

Loading…
Cancel
Save