diff --git a/README.md b/README.md index 4e6acd671..81b63d249 100644 --- a/README.md +++ b/README.md @@ -1753,6 +1753,9 @@ call legs, therefore all keys other than `call-id` are currently ignored. If the chosen recording method doesn't support in-kernel packet forwarding, enabling call recording via this messages will force packet forwarding to happen in userspace only. +If the optional 'output-destination' key is set, then its value will be used +as an output file. Note that a filename extension will not be added. + `stop recording` Message ------------------------- diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 85c0337b0..e44649493 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1445,7 +1445,7 @@ static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t detect_setup_recording(call, &flags.record_call_str, &flags.metadata); if (flags.record_call) { call->recording_on = 1; - recording_start(call, NULL, &flags.metadata); + recording_start(call, NULL, &flags.metadata, NULL); } if (flags.drop_traffic_start) { @@ -1914,16 +1914,18 @@ const char *call_start_recording_ng(bencode_item_t *input, bencode_item_t *outpu str callid; struct call *call; str metadata; + str output_dest; if (!bencode_dictionary_get_str(input, "call-id", &callid)) return "No call-id in message"; bencode_dictionary_get_str(input, "metadata", &metadata); + bencode_dictionary_get_str(input, "output-destination", &output_dest); call = call_get_opmode(&callid, OP_OTHER); if (!call) return "Unknown call-id"; call->recording_on = 1; - recording_start(call, NULL, &metadata); + recording_start(call, NULL, &metadata, &output_dest); rwlock_unlock_w(&call->master_lock); obj_put(call); @@ -2034,7 +2036,7 @@ const char *call_start_forwarding_ng(bencode_item_t *input, bencode_item_t *outp call->rec_forwarding = 1; } - recording_start(call, NULL, &flags.metadata); + recording_start(call, NULL, &flags.metadata, NULL); errstr = NULL; out: if (call) { diff --git a/daemon/recording.c b/daemon/recording.c index bb178f790..aff28c73c 100644 --- a/daemon/recording.c +++ b/daemon/recording.c @@ -244,6 +244,12 @@ static void update_metadata(struct call *call, str *metadata) { } } +static void update_output_dest(struct call *call, str *output_dest) { + if (!output_dest || !output_dest->s || !call->recording) + return; + recording_meta_chunk(call->recording, "OUTPUT_DESTINATION", output_dest); +} + // lock must be held static void update_flags_proc(struct call *call) { append_meta_chunk_null(call->recording, "RECORDING %u", call->recording_on ? 1 : 0); @@ -259,9 +265,11 @@ static void recording_update_flags(struct call *call) { } // lock must be held -void recording_start(struct call *call, const char *prefix, str *metadata) { +void recording_start(struct call *call, const char *prefix, str *metadata, str *output_dest) { update_metadata(call, metadata); + update_output_dest(call, output_dest); + if (call->recording) { // already active recording_update_flags(call); @@ -350,7 +358,7 @@ void detect_setup_recording(struct call *call, const str *recordcall, str *metad if (!str_cmp(recordcall, "yes") || !str_cmp(recordcall, "on")) { call->recording_on = 1; - recording_start(call, NULL, NULL); + recording_start(call, NULL, NULL, NULL); } else if (!str_cmp(recordcall, "no") || !str_cmp(recordcall, "off")) { call->recording_on = 0; diff --git a/daemon/redis.c b/daemon/redis.c index c903c971c..af5afac89 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1923,7 +1923,7 @@ static void json_restore_call(struct redis *r, const str *callid, int foreign) { if (!redis_hash_get_str(&s, &call, "recording_meta_prefix")) { // coverity[check_return : FALSE] redis_hash_get_str(&meta, &call, "recording_metadata"); - recording_start(c, s.s, &meta); + recording_start(c, s.s, &meta, NULL); } err = NULL; diff --git a/include/recording.h b/include/recording.h index c24d1504b..4662ede92 100644 --- a/include/recording.h +++ b/include/recording.h @@ -117,7 +117,7 @@ void recording_fs_free(void); */ void detect_setup_recording(struct call *call, const str *recordcall, str *metadata); -void recording_start(struct call *call, const char *prefix, str *metadata); +void recording_start(struct call *call, const char *prefix, str *metadata, str *output_dest); void recording_stop(struct call *call, str *metadata); diff --git a/recording-daemon/metafile.c b/recording-daemon/metafile.c index 86e570753..6b7853761 100644 --- a/recording-daemon/metafile.c +++ b/recording-daemon/metafile.c @@ -149,7 +149,6 @@ static void meta_ptime(metafile_t *mf, unsigned long mnum, int ptime) mf->media_ptimes[mnum] = ptime; } - // mf is locked static void meta_metadata(metafile_t *mf, char *content) { mf->metadata = g_string_chunk_insert(mf->gsc, content); @@ -192,6 +191,8 @@ static void meta_section(metafile_t *mf, char *section, char *content, unsigned mf->forwarding_on = u ? 1 : 0; else if (sscanf_match(section, "STREAM %lu FORWARDING %u", &lu, &u) == 2) stream_forwarding_on(mf, lu, u); + else if (!strcmp(section, "OUTPUT_DESTINATION")) + mf->output_dest = g_string_chunk_insert(mf->gsc, content); } diff --git a/recording-daemon/output.c b/recording-daemon/output.c index 166c09e89..da67a879b 100644 --- a/recording-daemon/output.c +++ b/recording-daemon/output.c @@ -81,6 +81,19 @@ static void create_parent_dirs(char *dir) { } } +static output_t *output_alloc(const char *path, const char *name) { + output_t *ret = g_slice_alloc0(sizeof(*ret)); + ret->file_path = g_strdup(path); + ret->file_name = g_strdup(name); + ret->full_filename = g_strdup_printf("%s/%s", path, name); + ret->file_format = output_file_format; + ret->encoder = encoder_new(); + ret->channel_mult = 1; + ret->requested_format.format = -1; + ret->actual_format.format = -1; + + return ret; +} output_t *output_new(const char *path, const char *call, const char *type) { // construct output file name @@ -159,23 +172,20 @@ output_t *output_new(const char *path, const char *call, const char *type) { } done:; - output_t *ret = g_slice_alloc0(sizeof(*ret)); - ret->file_path = g_strdup(path); - 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(); - ret->channel_mult = 1; - ret->requested_format.format = -1; - ret->actual_format.format = -1; - + output_t *ret = output_alloc(path, f->str); create_parent_dirs(ret->full_filename); - g_string_free(f, FALSE); + g_string_free(f, TRUE); return ret; } +output_t *output_new_from_full_path(const char *path, char *name) { + output_t *ret = output_alloc(path, name); + create_parent_dirs(ret->full_filename); + + return ret; +} int output_config(output_t *output, const format_t *requested_format, format_t *actual_format) { const char *err; @@ -238,9 +248,15 @@ int output_config(output_t *output, const format_t *requested_format, format_t * char *full_fn = NULL; char suff[16] = ""; for (int i = 1; i < 20; i++) { - full_fn = g_strdup_printf("%s%s.%s", output->full_filename, suff, output->file_format); + if (!output->skip_filename_extension) { + full_fn = g_strdup_printf("%s%s.%s", output->full_filename, suff, output->file_format); + } + else { + full_fn = g_strdup_printf("%s%s", output->full_filename, suff); + } if (!g_file_test(full_fn, G_FILE_TEST_EXISTS)) goto got_fn; + ilog(LOG_INFO, "Storing record in %s", full_fn); snprintf(suff, sizeof(suff), "-%i", i); g_free(full_fn); } diff --git a/recording-daemon/output.h b/recording-daemon/output.h index a65e9a322..6ff30322e 100644 --- a/recording-daemon/output.h +++ b/recording-daemon/output.h @@ -11,6 +11,7 @@ extern int mp3_bitrate; void output_init(const char *format); output_t *output_new(const char *path, const char *call, const char *type); +output_t *output_new_from_full_path(const char *path, char *name); void output_close(output_t *); int output_config(output_t *output, const format_t *requested_format, format_t *actual_format); diff --git a/recording-daemon/packet.c b/recording-daemon/packet.c index 9ba50901f..6a4cb1fb7 100644 --- a/recording-daemon/packet.c +++ b/recording-daemon/packet.c @@ -152,7 +152,6 @@ void ssrc_free(void *p) { g_slice_free1(sizeof(*s), s); } - // mf must be unlocked; returns ssrc locked static ssrc_t *ssrc_get(stream_t *stream, unsigned long ssrc) { metafile_t *mf = stream->metafile; @@ -177,9 +176,26 @@ 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[16]; - snprintf(buf, sizeof(buf), "%08lx", ssrc); - ret->output = output_new(output_dir, mf->parent, buf); + dbg("Metadata %s, output destination %s", mf->metadata, mf->output_dest); + if (mf->output_dest) { + char path[256]; + strncpy(path, mf->output_dest, sizeof(path)); + char *sep = strrchr(path, '/'); + if (sep) { + char *filename = sep + 1; + *sep = 0; + ret->output = output_new_from_full_path(path, filename); + ret->output->skip_filename_extension = TRUE; + } + else { + ret->output = output_new_from_full_path(output_dir, path); + } + } + else { + 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) { diff --git a/recording-daemon/types.h b/recording-daemon/types.h index 9fa6ab0f4..b4e8c09a5 100644 --- a/recording-daemon/types.h +++ b/recording-daemon/types.h @@ -111,6 +111,7 @@ struct metafile_s { char *call_id; char *metadata; char *metadata_db; + char *output_dest; off_t pos; unsigned long long db_id; @@ -146,6 +147,7 @@ struct output_s { *filename; // path + filename + suffix const char *file_format; unsigned long long db_id; + gboolean skip_filename_extension; unsigned int channel_mult; AVFormatContext *fmtctx;