MT#61822 support caching of media files in memory

Change-Id: I4b4e203b03273abf6e745897dea2de663f599a6c
pull/1897/head
Richard Fuchs 4 months ago
parent 2b90581e6c
commit 67dfeef914

@ -673,6 +673,7 @@ static void options(int *argc, char ***argv, GHashTable *templates) {
{ "player-cache",0,0, G_OPTION_ARG_NONE, &rtpe_config.player_cache,"Cache media files for playback in memory",NULL},
{ "kernel-player",0,0, G_OPTION_ARG_INT, &rtpe_config.kernel_player,"Max number of kernel media player streams","INT"},
{ "kernel-player-media",0,0,G_OPTION_ARG_INT, &rtpe_config.kernel_player_media,"Max number of kernel media files","INT"},
{ "preload-media-files",0,0,G_OPTION_ARG_FILENAME_ARRAY,&rtpe_config.preload_media_files,"Preload media file(s) for playback into memory","FILE"},
{ "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"},
@ -1479,6 +1480,9 @@ static void create_everything(void) {
timeval_from_us(&tmp_tv, (long long) rtpe_config.graphite_interval*1000000);
set_graphite_interval_tv(&tmp_tv);
if (!media_player_preload_files(rtpe_config.preload_media_files))
die("Failed to preload media files");
}

@ -83,11 +83,23 @@ struct media_player_cache_packet {
long long duration; // us
long long duration_ts;
};
struct media_player_media_file {
struct obj obj; // must be first
str blob;
};
static mutex_t media_player_cache_lock = MUTEX_STATIC_INIT;
static GHashTable *media_player_cache; // keys and values only ever freed at shutdown
TYPED_GHASHTABLE(media_player_media_files_ht, str, struct media_player_media_file, str_hash, str_equal,
NULL, __obj_put);
static mutex_t media_player_media_files_lock = MUTEX_STATIC_INIT;
static media_player_media_files_ht media_player_media_files;
static bool media_player_read_packet(struct media_player *mp);
static mp_cached_code __media_player_add_blob_id(struct media_player *mp,
media_player_opts_t opts,
const rtp_payload_type *dst_pt);
#endif
static struct timerthread send_timer_thread;
@ -181,6 +193,7 @@ static void __media_player_free(struct media_player *mp) {
mutex_destroy(&mp->lock);
obj_put(mp->call);
av_packet_free(&mp->coder.pkt);
obj_release(mp->media_file);
}
#endif
@ -1154,6 +1167,90 @@ static bool media_player_play_start(struct media_player *mp, const rtp_payload_t
return true;
}
static void media_player_media_file_free(struct media_player_media_file *fo) {
g_free(fo->blob.s);
}
static str media_player_read_file(const char *f) {
gchar *buf = NULL;
gsize len = -1;
GError *err = NULL;
gboolean ret = g_file_get_contents(f, &buf, &len, &err);
if (!ret) {
ilog(LOG_ERR, "Failed to read media file '%s' for caching: %s", f, err->message);
g_error_free(err);
return STR_NULL;
}
if (len <= 0) {
ilog(LOG_ERR, "Media file '%s' appears to be empty", f);
g_free(buf);
return STR_NULL;
}
return STR_LEN(buf, len);
}
static struct media_player_media_file *media_player_media_file_new(str blob) {
__auto_type fo = obj_alloc0(struct media_player_media_file,
media_player_media_file_free);
fo->blob = blob;
fo->blob.dup = call_ref; // string is allocated by reference on `fo`
return fo;
}
static struct media_player_media_file *media_player_media_file_read_c(const char *fn) {
str blob = media_player_read_file(fn);
if (blob.len == 0)
return NULL;
return media_player_media_file_new(blob);
}
static struct media_player_media_file *media_player_media_file_read_str(const str *fn) {
char file_s[PATH_MAX];
snprintf(file_s, sizeof(file_s), STR_FORMAT, STR_FMT(fn));
return media_player_media_file_read_c(file_s);
}
static struct media_player_media_file *media_player_media_files_get(const str *fn) {
struct media_player_media_file *fo;
{
LOCK(&media_player_media_files_lock);
if (!t_hash_table_is_set(media_player_media_files))
return NULL;
fo = t_hash_table_lookup(media_player_media_files, fn);
if (!fo)
return NULL;
obj_hold(fo);
}
return fo;
}
// lock must be held, reference will be taken over
static void media_player_media_files_insert(const str *fn, struct media_player_media_file *fo) {
if (!t_hash_table_is_set(media_player_media_files))
media_player_media_files = media_player_media_files_ht_new();
str *dup = str_dup(fn);
t_hash_table_insert(media_player_media_files, dup, fo);
}
static mp_cached_code media_player_set_media_file(struct media_player *mp,
media_player_opts_t opts,
const rtp_payload_type *dst_pt,
struct media_player_media_file *fo)
{
// release old reference if any and take over this new one
if (mp->media_file)
obj_put(mp->media_file);
mp->media_file = fo;
// switch to blob playing
opts.file = STR_NULL;
opts.blob = fo->blob;
return __media_player_add_blob_id(mp, opts, dst_pt);
}
static void __media_player_set_opts(struct media_player *mp, media_player_opts_t opts) {
mp->opts = opts;
@ -1177,6 +1274,13 @@ static mp_cached_code __media_player_add_file(struct media_player *mp,
if (media_player_cache_get_entry(mp, dst_pt, opts.codec_set))
return MPC_CACHED;
// check if we have it in memory
struct media_player_media_file *fo = media_player_media_files_get(&opts.file);
if (fo) {
ilog(LOG_DEBUG, "Using cached media file for playback");
return media_player_set_media_file(mp, opts, dst_pt, fo);
}
char file_s[PATH_MAX];
snprintf(file_s, sizeof(file_s), STR_FORMAT, STR_FMT(&opts.file));
@ -1771,6 +1875,9 @@ void media_player_free(void) {
if (media_player_cache)
g_hash_table_destroy(media_player_cache);
if (t_hash_table_is_set(media_player_media_files))
t_hash_table_destroy(media_player_media_files);
#endif
timerthread_free(&send_timer_thread);
}
@ -1785,3 +1892,31 @@ void send_timer_launch(void) {
//ilog(LOG_DEBUG, "send_timer_loop");
timerthread_launch(&send_timer_thread, rtpe_config.scheduling, rtpe_config.priority, "media player");
}
bool media_player_preload_files(char **files) {
#ifdef WITH_TRANSCODING
if (!files || !files[0])
return true;
for (char **filep = files; *filep; filep++) {
char *file = *filep;
while (*file == ' ')
file++;
ilog(LOG_DEBUG, "Reading media file '%s' for caching", file);
str f = STR(file);
if (t_hash_table_is_set(media_player_media_files) && t_hash_table_lookup(media_player_media_files, &f)) {
ilog(LOG_CRIT, "Duplicate entry for caching media file '%s'", file);
return false;
}
__auto_type fo = media_player_media_file_read_c(file);
if (!fo)
return false;
media_player_media_files_insert(&f, fo);
}
#endif
return true;
}

@ -1144,6 +1144,21 @@ call to inject-DTMF won't be sent to __\-\-dtmf-log-dest=__ or __\-\-listen-tcp-
*rtpengine* (using different kernel table IDs) running on a system using
the same kernel module. Unused slots use minimal resources.
- __\-\-preload-media-files=__*FILE*
Enables reading of media files at startup and caching them in memory for
playback. Multiple files can be specified. On the command line, the option
must be given multiple times to do so, while in the config file the option
must be given only once, with the list of files separated by semicolons.
All listed files will be read into memory at startup and cached there for
the lifetime of the daemon. When playback of one such media file is
requested, playback will be done from the cached contents instead of
opening and reading the file. The file name given in the `play media`
request must exactly match the file name given in the config option. If the
file name differs (or an entirely different file is requested for playback)
then playback will happen from file as usual.
- __\-\-audio-buffer-length=__*INT*
Set the buffer length used by the audio player (see below) in milliseconds. The

@ -165,6 +165,8 @@ recording-method = proc
# whenever MoH is triggered. If not defined, then not in use.
# moh-attr-name = rtpengine-hold
# preload-media-files = /var/media/file1.wav ; /var/media/file2.wav ; /var/media/file3.wav
# 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

@ -181,7 +181,8 @@ enum endpoint_learning {
#define RTPE_CONFIG_CHARPP_PARAMS \
X(http_ifs) \
X(https_ifs)
X(https_ifs) \
X(preload_media_files) \
// these are not automatically included in rtpe_config due to different types
#define RTPE_CONFIG_ENUM_PARAMS \

@ -35,6 +35,7 @@ typedef struct {
#include <libavcodec/avcodec.h>
struct media_player_cache_entry;
struct media_player_media_file;
struct media_player_content_index {
enum { MP_OTHER = 0, MP_FILE = 1, MP_DB, MP_BLOB } type;
@ -77,6 +78,7 @@ struct media_player {
struct media_player_cache_entry *cache_entry;
unsigned int cache_read_idx;
unsigned int kernel_idx;
struct media_player_media_file *media_file;
struct ssrc_ctx *ssrc_out;
unsigned long seq;
@ -147,6 +149,7 @@ const char * call_check_moh(struct call_monologue *from_ml, struct call_monologu
void media_player_init(void);
void media_player_free(void);
void media_player_launch(void);
bool media_player_preload_files(char **);
struct send_timer *send_timer_new(struct packet_stream *);
void send_timer_push(struct send_timer *, struct codec_packet *);

Loading…
Cancel
Save