From 67bfa5a5df2d987dd53926e4919dfb595c610707 Mon Sep 17 00:00:00 2001 From: Donat Zenichev Date: Fri, 3 Jan 2025 12:08:25 +0100 Subject: [PATCH] MT#61630 Generalize `moh-max-duration`, add `repeat-duration` Make `moh-max-duration` available for both music-on-hold functionality as well as for the media player. For that to work, do the following: - keep `moh-max-duration` config option only for MoH, if not set (so 0) by default is 1800000ms (half an hour) - for the play media functionality introduce flag option `repeat-duration`, by default is disabled Policy changes: - duration counter can be used in common with repeats counter, but then takes a precedence over it. Hence if first a duration is underflown, then EOF triggered. Otherwise if the duration counter is still positive, but repeats are negative, then do EOF based on repeats. - the repeats counter will always count down during each iteration, even when used together with the duration counter For MoH to survive, the repeats counter is simple set to 999 to let the duration counter always win over repeats one - MoH cannot take duration disabled, since otherwise would make no sense for it. Hence always takes internally defined value 1800000ms (half an hour) if not defined by the configuration option Backwards compatibility: - is kept in regards of repeats counter - is kept in regards of the play media functionality Change-Id: I48ff3c17c9bed31f80c3106b275b703a9ccb4b26 --- daemon/call.c | 1 + daemon/call_interfaces.c | 5 +++++ daemon/main.c | 8 +++++-- daemon/media_player.c | 45 ++++++++++++++++++++++++------------- docs/ng_control_protocol.md | 11 +++++++++ etc/rtpengine.conf | 3 ++- include/call_interfaces.h | 1 + include/media_player.h | 1 - 8 files changed, 55 insertions(+), 20 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index a99c54d28..559eece1d 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2765,6 +2765,7 @@ static void __call_monologue_init_from_flags(struct call_monologue *ml, struct c media_player_new(&ml->rec_player, ml); media_player_opts_t opts = MPO( .repeat = flags->repeat_times, + .duration_spent = flags->repeat_duration, .start_pos = flags->start_pos, .block_egress = !!flags->block_egress, .codec_set = flags->codec_set, diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 33e62eaf6..a8738cfab 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1351,6 +1351,7 @@ void call_ng_flags_init(sdp_ng_flags *out, enum ng_opmode opmode) { out->delete_delay = -1; out->volume = 9999; out->digit = -1; + out->repeat_duration = -1; out->frequencies = g_array_new(false, false, sizeof(int)); for (int i = 0; i < __MT_MAX; ++i) out->sdp_media_remove[i] = false; @@ -1929,6 +1930,9 @@ void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, h case CSH_LOOKUP("repeat-times"): out->repeat_times = parser->get_int_str(value, out->repeat_times); break; + case CSH_LOOKUP("repeat-duration"): + out->repeat_duration = parser->get_int_str(value, out->repeat_duration); + break; case CSH_LOOKUP("replace"): call_ng_flags_str_list(parser, value, call_ng_flags_replace, out); break; @@ -3634,6 +3638,7 @@ const char *call_play_media_ng(ng_command_ctx_t *ctx) { media_player_opts_t opts = MPO( .repeat = flags.repeat_times, + .duration_spent = flags.repeat_duration, .start_pos = flags.start_pos, .block_egress = !!flags.block_egress, .codec_set = flags.codec_set, diff --git a/daemon/main.c b/daemon/main.c index 3982df710..5d193d73e 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -112,7 +112,7 @@ struct rtpengine_config rtpe_config = { .mqtt_publish_interval = 5000, .dtmf_digit_delay = 2500, .rtcp_interval = 5000, - .moh_max_duration = 1800000, // in ms + .moh_max_duration = -1, // disabled by default .common = { .log_levels = { [log_level_index_internals] = -1, @@ -697,7 +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"}, + { "moh-max-duration", 0,0, G_OPTION_ARG_INT, &rtpe_config.moh_max_duration, "Max possible duration (in milliseconds) that can be spent on playing a file. 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"}, @@ -867,6 +867,10 @@ static void options(int *argc, char ***argv, GHashTable *templates) { if (rtpe_config.max_recv_iters < 1) die("Invalid max-recv-iters value"); + /* if not set, define by default to half an hour */ + if (rtpe_config.moh_max_duration <= 0) + rtpe_config.moh_max_duration = 1800000; + if (rtpe_config.timeout <= 0) rtpe_config.timeout = 60; diff --git a/daemon/media_player.c b/daemon/media_player.c index 0d3f6d255..3271d16c7 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -1013,27 +1013,39 @@ 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) { - /* 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; - ret = media_player_find_file_begin(mp); - - } else { + /* Duration counter cannot underflow and is always aligned to 0 when used. + * By default is -1. + * If either a duration or repeats counter are done, then the reading process + * is considered EOF. + */ + if (mp->opts.duration_spent == 0 || + mp->opts.repeat <= 1) + { ilog(LOG_DEBUG, "EOF reading from media stream"); return true; + } + + ret = media_player_find_file_begin(mp); + /* counter for the max spent duration (in milliseconds) + * duration takes precedence over repeats, if used together + */ + if (mp->opts.duration_spent > 0) { + ilog(LOG_DEBUG, "EOF reading from stream but will be played further due to available duration '%lld'", + mp->opts.duration_spent); + mp->opts.duration_spent = mp->opts.duration_spent - mp->coder.duration; + /* don't let the duration counter to underflow */ + if (mp->opts.duration_spent < 0) + mp->opts.duration_spent = 0; } + /* counter for the max repeats + * still count down each time, even if we are based on max duration in milliseconds */ + if (mp->opts.repeat > 1) { + ilog(LOG_DEBUG, "EOF reading from stream but will be played further due to available repeats '%d'", + mp->opts.repeat); + mp->opts.repeat--; + } } if (ret < 0 && ret != AVERROR_EOF) { ilog(LOG_ERR, "Error while reading from media stream"); @@ -1282,6 +1294,7 @@ const char * call_check_moh(struct call_monologue *from_ml, struct call_monologu const char *errstr = NULL; media_player_opts_t opts = MPO( .repeat = 999, + /* MoH always has duration set (even if not defined) */ .duration_spent = rtpe_config.moh_max_duration, .start_pos = 0, .block_egress = 1, diff --git a/docs/ng_control_protocol.md b/docs/ng_control_protocol.md index 75f0fe06a..3489db4e9 100644 --- a/docs/ng_control_protocol.md +++ b/docs/ng_control_protocol.md @@ -2095,6 +2095,17 @@ Media files can be provided through one of these keys: Contains an integer. How many times to repeat playback of the media. Default is 1. +* `repeat-duration` + + Contains an integer. How much time in milliseconds is a playback of the media to be minimally iterated. + E.g. if set to 10000ms and the playback's length is 1000ms, then this playback will be iterated + 10 times due to limitation set to 10000ms. + If used together with `repeat-times` then the following logic takes place: + if `repeat-duration` hits the trigger earlier, then this playback will be stopped, + otherwise if the `repeat-duration` is still positive, but the `repeat-times` counter went down to 1, + then still the playback is to be stopped. + By default is disabled. + * `start-pos` Contains an integer. The start frame position to begin the playback from. diff --git a/etc/rtpengine.conf b/etc/rtpengine.conf index 6e4adfe77..9556f6296 100644 --- a/etc/rtpengine.conf +++ b/etc/rtpengine.conf @@ -152,7 +152,8 @@ 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 +# music-on-hold max possible duration (in ms). +# When not defined (set to 0), it takes 1800000ms default value. # moh-max-duration = 1800000 # signalling templates (see key `templates` above) diff --git a/include/call_interfaces.h b/include/call_interfaces.h index c6246bd53..14aa06b1d 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -133,6 +133,7 @@ struct sdp_ng_flags { int media_rec_slot_answer; int media_rec_slots; int repeat_times; + long long repeat_duration; int delete_delay; str file; str moh_file; diff --git a/include/media_player.h b/include/media_player.h index 1b91cd889..36fd77434 100644 --- a/include/media_player.h +++ b/include/media_player.h @@ -34,7 +34,6 @@ typedef struct { #include #include - struct media_player_cache_entry; struct media_player_content_index {