diff --git a/daemon/main.c b/daemon/main.c index a5ea823d6..a0e2c9a45 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -677,6 +677,7 @@ static void options(int *argc, char ***argv, GHashTable *templates) { { "media-files-reload",0,0,G_OPTION_ARG_INT, &rtpe_config.media_refresh,"Refresh/reload preloaded media files at a certain interval","SECONDS"}, { "preload-db-media",0,0,G_OPTION_ARG_STRING_ARRAY,&rtpe_config.preload_db_media,"Preload media from database for playback into memory","INT"}, { "db-media-reload",0,0,G_OPTION_ARG_INT, &rtpe_config.db_refresh,"Reload preloaded media from DB at a certain interval","SECONDS"}, + { "db-media-cache",0,0, G_OPTION_ARG_FILENAME, &rtpe_config.db_media_cache,"Directory to store media loaded from database","PATH"}, { "audio-buffer-length",0,0, G_OPTION_ARG_INT,&rtpe_config.audio_buffer_length,"Length in milliseconds of audio buffer","INT"}, { "audio-buffer-delay",0,0, G_OPTION_ARG_INT,&rtpe_config.audio_buffer_delay,"Initial delay in milliseconds for buffered audio","INT"}, { "audio-player",0,0, G_OPTION_ARG_STRING, &use_audio_player, "When to enable the internal audio player","on-demand|play-media|transcoding|always"}, @@ -1489,6 +1490,10 @@ static void create_everything(void) { if (!media_player_preload_db(rtpe_config.preload_db_media)) die("Failed to preload media from database"); + + if (rtpe_config.db_media_cache) + if (g_mkdir_with_parents(rtpe_config.db_media_cache, 0700)) + die("Failed to create cache directory for media loaded from DB: %s", strerror(errno)); } diff --git a/daemon/media_player.c b/daemon/media_player.c index 3076838bc..981b0059a 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -1755,6 +1755,23 @@ err: return -1; } +static char *media_player_make_cache_entry_name(unsigned long long id) { + return g_strdup_printf("%s/%llu.blob", rtpe_config.db_media_cache, id); +} + +static void media_player_add_cache_file(const char *s, size_t len, unsigned long long id) { + if (!rtpe_config.db_media_cache) + return; + + g_autoptr(char) fn = media_player_make_cache_entry_name(id); + GError *err = NULL; + gboolean ok = g_file_set_contents(fn, s, len, &err); + if (!ok) { + ilog(LOG_WARN, "Failed to write to cache file '%s': %s", fn, err->message); + g_error_free(err); + } +} + static const char *media_player_get_db_id(str *out, unsigned long long id, str (*dup_fn)(const char *, size_t)) { @@ -1802,6 +1819,8 @@ success:; goto err; } + media_player_add_cache_file(row[0], lengths[0], id); + *out = dup_fn(row[0], lengths[0]); return NULL; @@ -1828,6 +1847,24 @@ static mp_cached_code __media_player_add_db(struct media_player *mp, return media_player_set_media_file(mp, opts, dst_pt, fo); } + // or maybe we have a cache file for it + if (rtpe_config.db_media_cache) { + g_autoptr(char) fn = media_player_make_cache_entry_name(opts.db_id); + gchar *buf = NULL; + gsize len = -1; + gboolean ret = g_file_get_contents(fn, &buf, &len, NULL); + if (ret && len > 0) { + // use this as blob and play it + ilog(LOG_DEBUG, "Using cached file of DB media for playback"); + // use a `media_player_media_file` object to hold a reference on the g_malloc'd + // data to avoid having to memcpy it + fo = media_player_media_file_new(STR_LEN(buf, len)); + return media_player_set_media_file(mp, opts, dst_pt, fo); + } + if (ret) // zero-length file + g_free(buf); + } + err = media_player_get_db_id(&opts.blob, opts.db_id, call_str_cpy_len); if (err) return MPC_ERR; diff --git a/debian/ngcp-rtpengine-daemon.dirs b/debian/ngcp-rtpengine-daemon.dirs index 09f36ded2..e3d3459a8 100644 --- a/debian/ngcp-rtpengine-daemon.dirs +++ b/debian/ngcp-rtpengine-daemon.dirs @@ -1,2 +1,3 @@ etc/modprobe.d +var/cache/rtpengine var/spool/rtpengine diff --git a/debian/ngcp-rtpengine-daemon.postinst b/debian/ngcp-rtpengine-daemon.postinst index 6055b235d..fbb2c628c 100644 --- a/debian/ngcp-rtpengine-daemon.postinst +++ b/debian/ngcp-rtpengine-daemon.postinst @@ -43,6 +43,12 @@ if [ "$1" = configure ]; then chmod 0770 "$rtpdir" fi + cachedir='/var/cache/rtpengine' + if ! dpkg-statoverride --list "$cachedir" > /dev/null 2>&1; then + chown rtpengine:rtpengine "$cachedir" + chmod 0770 "$cachedir" + fi + # set up modprobe.d fragment for auto-load usage if ! [ -f /etc/modprobe.d/rtpengine.conf ] || grep -q DPKG-GENERATED /etc/modprobe.d/rtpengine.conf; then OPTIONS="options xt_RTPENGINE proc_mask=0x7" diff --git a/debian/ngcp-rtpengine-daemon.service b/debian/ngcp-rtpengine-daemon.service index 267f7a3a3..c6cae9aad 100644 --- a/debian/ngcp-rtpengine-daemon.service +++ b/debian/ngcp-rtpengine-daemon.service @@ -42,7 +42,7 @@ ProtectKernelTunables=true ProtectSystem=strict # Allow write access -ReadWritePaths=/var/spool/rtpengine +ReadWritePaths=/var/cache/rtpengine /var/spool/rtpengine # Access to the kernel log ring buffer will be denied ProtectKernelLogs=true diff --git a/docs/rtpengine.md b/docs/rtpengine.md index 93157b1c1..52f86569f 100644 --- a/docs/rtpengine.md +++ b/docs/rtpengine.md @@ -1187,6 +1187,18 @@ call to inject-DTMF won't be sent to __\-\-dtmf-log-dest=__ or __\-\-listen-tcp- timestamp, which means that all media will always be reloaded from the database in the given interval. +- __\-\-db-media-cache=__*PATH* + + Enables filesystem-backed caching of media entries from the database. The + given path must be readable and writeable by *rtpengine*. + + Whenever playback of media from the database is requested, *rtpengine* + first checks if a corresponding cached file within the given path exists. + If it exists, media will be read from that file instead of from the + database. If it doesn't exist, media will be read from the database, and + then *rtpengine* will create the cache file for the next time the same + media is requested. + - __\-\-audio-buffer-length=__*INT* Set the buffer length used by the audio player (see below) in milliseconds. The diff --git a/etc/rtpengine.conf b/etc/rtpengine.conf index c73ed4589..bd5a9575f 100644 --- a/etc/rtpengine.conf +++ b/etc/rtpengine.conf @@ -169,6 +169,7 @@ recording-method = proc # media-files-reload = 60 # preload-db-media = 1; 2; 3; 4; on-demand # db-media-reload = 3600 +# db-media-cache = /var/cache/rtpengine # signalling templates (see key `templates` above) [templates] diff --git a/include/main.h b/include/main.h index 8862b9a42..e12715ebc 100644 --- a/include/main.h +++ b/include/main.h @@ -154,7 +154,8 @@ enum endpoint_learning { X(mqtt_certfile) \ X(mqtt_keyfile) \ X(mqtt_publish_topic) \ - X(janus_secret) + X(janus_secret) \ + X(db_media_cache) \ #define RTPE_CONFIG_ENDPOINT_PARAMS \ X(graphite_ep) \