|
|
|
@ -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;
|
|
|
|
|
}
|
|
|
|
|