From 5c52772183bbb6d44d56fa000b3c26a4bf987b3a Mon Sep 17 00:00:00 2001 From: Donat Zenichev Date: Tue, 31 Dec 2024 15:41:22 +0100 Subject: [PATCH] MT#61630 moh: add duration limit Add duration limit for the moh functionality. This is required due to a possibility to get monologues and hence packet streams stuck forever and sending audio to recipients which already don't exist. A configuration option that controls that: `moh-max-duration` - gets a value in milliseconds. By default is set to 1800000 (half an hour). Change-Id: Id50a0a10ce5b52b3876a3122fb16a71accec90ff --- daemon/call_interfaces.c | 4 ++-- daemon/main.c | 2 ++ daemon/media_player.c | 35 ++++++++++++++++++++++++++--------- etc/rtpengine.conf | 3 +++ include/main.h | 1 + include/media_player.h | 1 + 6 files changed, 35 insertions(+), 11 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 6ea9d9513..cf6c4b1cb 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -2389,9 +2389,9 @@ static const char *call_offer_answer_ng(ng_command_ctx_t *ctx, const char* addr, * to be provided with moh playbacks */ if (call_ml_wants_moh(from_ml, flags.opmode)) { - /* TODO: should be fine tuned? */ media_player_opts_t opts = MPO( - .repeat = 999, /* TODO: maybe there is a better way to loop it */ + .repeat = 999, + .duration_spent = rtpe_config.moh_max_duration, .start_pos = 0, .block_egress = 1, .codec_set = flags.codec_set, diff --git a/daemon/main.c b/daemon/main.c index bc2bd9aa7..3982df710 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -112,6 +112,7 @@ struct rtpengine_config rtpe_config = { .mqtt_publish_interval = 5000, .dtmf_digit_delay = 2500, .rtcp_interval = 5000, + .moh_max_duration = 1800000, // in ms .common = { .log_levels = { [log_level_index_internals] = -1, @@ -696,6 +697,7 @@ static void options(int *argc, char ***argv, GHashTable *templates) { #endif { "janus-secret", 0,0, G_OPTION_ARG_STRING, &rtpe_config.janus_secret,"Admin secret for Janus protocol","STRING"}, { "rtcp-interval", 0,0, G_OPTION_ARG_INT, &rtpe_config.rtcp_interval,"Delay in milliseconds between RTCP packets when generate-rtcp flag is on, where random dispersion < 1 sec is added on top","INT"}, + { "moh-max-duration", 0,0, G_OPTION_ARG_INT, &rtpe_config.moh_max_duration, "Music-on-hold max possible duration (in milliseconds). If set to 0 then will be ignored.", "INT"}, { "max-recv-iters", 0, 0, G_OPTION_ARG_INT, &rtpe_config.max_recv_iters, "Maximum continuous reading cycles in UDP poller loop.", "INT"}, { "vsc-start-rec",0,0, G_OPTION_ARG_STRING, &rtpe_config.vsc_start_rec.s,"DTMF VSC to start recording.", "STRING"}, { "vsc-stop-rec",0,0, G_OPTION_ARG_STRING, &rtpe_config.vsc_stop_rec.s,"DTMF VSC to stop recording.", "STRING"}, diff --git a/daemon/media_player.c b/daemon/media_player.c index f96a682d6..e3eb70fa1 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -990,6 +990,19 @@ void media_player_add_packet(struct media_player *mp, char *buf, size_t len, timerthread_obj_schedule_abs(&mp->tt_obj, &mp->next_run); } +static int media_player_find_file_begin(struct media_player *mp) { + int ret = 0; + + int64_t ret64 = avio_seek(mp->coder.fmtctx->pb, 0, SEEK_SET); + if (ret64 != 0) + ilog(LOG_ERR, "Failed to seek to beginning of media file"); + ret = av_seek_frame(mp->coder.fmtctx, -1, 0, 0); + if (ret < 0) + ilog(LOG_ERR, "Failed to seek to beginning of media file"); + ret = av_read_frame(mp->coder.fmtctx, mp->coder.pkt); + + return ret; +} // appropriate lock must be held static bool media_player_read_packet(struct media_player *mp) { @@ -999,17 +1012,21 @@ static bool media_player_read_packet(struct media_player *mp) { int ret = av_read_frame(mp->coder.fmtctx, mp->coder.pkt); if (ret < 0) { if (ret == AVERROR_EOF) { - if (mp->opts.repeat > 1) { - ilog(LOG_DEBUG, "EOF reading from media stream but will repeat %i time", + /* for moh: count based on duration */ + if (mp->moh && mp->opts.duration_spent > 0) { + ilog(LOG_DEBUG, "EOF reading from media stream but will be played further for duration of '%lld' ms", + mp->opts.duration_spent); + /* moh counter for the max spent duration (in milliseconds) */ + mp->opts.duration_spent = mp->opts.duration_spent - mp->coder.duration; + ret = media_player_find_file_begin(mp); + + /* for play media: count based on repeats */ + } else if (!mp->moh && mp->opts.repeat > 1) { + ilog(LOG_DEBUG, "EOF reading from media stream but will repeat '%i' time", mp->opts.repeat); mp->opts.repeat = mp->opts.repeat - 1; - int64_t ret64 = avio_seek(mp->coder.fmtctx->pb, 0, SEEK_SET); - if (ret64 != 0) - ilog(LOG_ERR, "Failed to seek to beginning of media file"); - ret = av_seek_frame(mp->coder.fmtctx, -1, 0, 0); - if (ret < 0) - ilog(LOG_ERR, "Failed to seek to beginning of media file"); - ret = av_read_frame(mp->coder.fmtctx, mp->coder.pkt); + ret = media_player_find_file_begin(mp); + } else { ilog(LOG_DEBUG, "EOF reading from media stream"); return true; diff --git a/etc/rtpengine.conf b/etc/rtpengine.conf index e7015bbe0..6e4adfe77 100644 --- a/etc/rtpengine.conf +++ b/etc/rtpengine.conf @@ -152,6 +152,9 @@ recording-method = proc # socket-cpu-affinity = -1 # rtcp-interval = 5000 +# music-on-hold max possible duration (in ms), if set 0 then will be ignored +# moh-max-duration = 1800000 + # signalling templates (see key `templates` above) [templates] WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid diff --git a/include/main.h b/include/main.h index 7e0d77c0e..87c5351cc 100644 --- a/include/main.h +++ b/include/main.h @@ -39,6 +39,7 @@ enum endpoint_learning { X(silent_timeout) \ X(final_timeout) \ X(offer_timeout) \ + X(moh_max_duration) \ X(delete_delay) \ X(redis_expires_secs) \ X(default_tos) \ diff --git a/include/media_player.h b/include/media_player.h index 34eb03504..279a4461e 100644 --- a/include/media_player.h +++ b/include/media_player.h @@ -21,6 +21,7 @@ struct media_player; typedef struct { long long start_pos; int repeat; + long long duration_spent; /* in milliseconds */ str_case_value_ht codec_set; unsigned int block_egress:1; str file, blob;