player: make all external file loading actions async

Still missing: not freezing when removing a track (i.e. closing demuxer)
with the sub-remove/audio-remove/rescan-external-files commands.
This commit is contained in:
wm4 2018-05-07 20:36:17 +02:00
parent 115a9b19f1
commit 1b611e38ef
4 changed files with 93 additions and 23 deletions

View File

@ -910,7 +910,8 @@ which will be fixed later. Using the ``async`` prefix makes them run the file
saving code in a detached manner.
Currently the following commands have different waiting characteristics with
sync vs. async: sub-add, audio-add
sync vs. async: sub-add, audio-add, sub-reload, audio-reload,
rescan-external-files.
Input Sections
--------------

View File

@ -5520,7 +5520,7 @@ static void cmd_track_add(void *p)
return;
}
}
int first = mp_add_external_file(mpctx, cmd->args[0].v.s, type, true);
int first = mp_add_external_file(mpctx, cmd->args[0].v.s, type);
if (first < 0) {
cmd->success = false;
return;
@ -5584,7 +5584,7 @@ static void cmd_track_reload(void *p)
if (t && t->is_external && t->external_filename) {
char *filename = talloc_strdup(NULL, t->external_filename);
mp_remove_track(mpctx, t);
nt_num = mp_add_external_file(mpctx, filename, type, false);
nt_num = mp_add_external_file(mpctx, filename, type);
talloc_free(filename);
}
@ -6044,7 +6044,9 @@ const struct mp_cmd_def mp_cmds[] = {
{ "sub-remove", cmd_track_remove, { OARG_INT(-1) },
.priv = &(const int){STREAM_SUB}, },
{ "sub-reload", cmd_track_reload, { OARG_INT(-1) },
.priv = &(const int){STREAM_SUB}, },
.priv = &(const int){STREAM_SUB},
.spawn_thread = true,
},
{ "tv-last-channel", cmd_tv_last_channel, },
@ -6175,12 +6177,16 @@ const struct mp_cmd_def mp_cmds[] = {
{ "audio-remove", cmd_track_remove, { OARG_INT(-1) },
.priv = &(const int){STREAM_AUDIO}, },
{ "audio-reload", cmd_track_reload, { OARG_INT(-1) },
.priv = &(const int){STREAM_AUDIO}, },
.priv = &(const int){STREAM_AUDIO},
.spawn_thread = true,
},
{ "rescan-external-files", cmd_rescan_external_files, {
OARG_CHOICE(1, ({"keep-selection", 0},
{"reselect", 1})),
}},
},
.spawn_thread = true,
},
{ "apply-profile", cmd_apply_profile, {ARG_STRING } },

View File

@ -296,6 +296,8 @@ typedef struct MPContext {
struct track **tracks;
int num_tracks;
int64_t death_hack; // don't fucking ask, just don't
char *track_layout_hash;
// Selected tracks. NULL if no track selected.
@ -488,7 +490,7 @@ struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx,
void mp_abort_playback_async(struct MPContext *mpctx);
void uninit_player(struct MPContext *mpctx, unsigned int mask);
int mp_add_external_file(struct MPContext *mpctx, char *filename,
enum stream_type filter, bool unlock);
enum stream_type filter);
#define FLAG_MARK_SELECTION 1
void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
struct track *track, int flags);

View File

@ -26,6 +26,8 @@
#include "config.h"
#include "mpv_talloc.h"
#include "misc/thread_pool.h"
#include "misc/thread_tools.h"
#include "osdep/io.h"
#include "osdep/terminal.h"
#include "osdep/threads.h"
@ -578,8 +580,9 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
// Add the given file as additional track. The filter argument controls how or
// if tracks are auto-selected at any point.
// To be run on a worker thread, locked (temporarily unlocks core).
int mp_add_external_file(struct MPContext *mpctx, char *filename,
enum stream_type filter, bool unlock)
enum stream_type filter)
{
struct MPOpts *opts = mpctx->opts;
if (!filename || mp_cancel_test(mpctx->playback_abort))
@ -600,16 +603,23 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
break;
}
if (unlock)
mp_core_unlock(mpctx);
mp_core_unlock(mpctx);
struct demuxer *demuxer =
demux_open_url(filename, &params, mpctx->playback_abort, mpctx->global);
if (demuxer)
enable_demux_thread(mpctx, demuxer);
if (unlock)
mp_core_lock(mpctx);
// The command could have overlapped with playback exiting. (We don't care
// if playback has started again meanwhile - weird, but not a problem.)
if (!mpctx->playing) {
mp_core_unlock(mpctx);
free_demuxer_and_stream(demuxer);
mp_core_lock(mpctx);
return -1;
}
if (!demuxer)
goto err_out;
@ -627,11 +637,9 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
}
if (!has_any) {
if (unlock)
mp_core_unlock(mpctx);
mp_core_unlock(mpctx);
free_demuxer_and_stream(demuxer);
if (unlock)
mp_core_lock(mpctx);
mp_core_lock(mpctx);
char *tname = mp_tprintf(20, "%s ", stream_type_name(filter));
if (filter == STREAM_TYPE_COUNT)
tname = "";
@ -660,11 +668,18 @@ err_out:
return -1;
}
// to be run on a worker thread, locked (temporarily unlocks core)
static void open_external_files(struct MPContext *mpctx, char **files,
enum stream_type filter)
{
// Need a copy, because the option value could be mutated during iteration.
void *tmp = talloc_new(NULL);
files = mp_dup_str_array(tmp, files);
for (int n = 0; files && files[n]; n++)
mp_add_external_file(mpctx, files[n], filter, false);
mp_add_external_file(mpctx, files[n], filter);
talloc_free(tmp);
}
void autoload_external_files(struct MPContext *mpctx)
@ -703,7 +718,7 @@ void autoload_external_files(struct MPContext *mpctx)
goto skip;
if (list[i].type == STREAM_AUDIO && !sc[STREAM_VIDEO])
goto skip;
int first = mp_add_external_file(mpctx, filename, list[i].type, false);
int first = mp_add_external_file(mpctx, filename, list[i].type);
if (first < 0)
goto skip;
@ -771,20 +786,25 @@ static void process_hooks(struct MPContext *mpctx, char *name)
mp_idle(mpctx);
}
// to be run on a worker thread, locked (temporarily unlocks core)
static void load_chapters(struct MPContext *mpctx)
{
struct demuxer *src = mpctx->demuxer;
bool free_src = false;
char *chapter_file = mpctx->opts->chapter_file;
if (chapter_file && chapter_file[0]) {
chapter_file = talloc_strdup(NULL, chapter_file);
mp_core_unlock(mpctx);
struct demuxer *demux = demux_open_url(chapter_file, NULL,
mpctx->playback_abort, mpctx->global);
mp_core_lock(mpctx);
if (demux) {
src = demux;
free_src = true;
}
talloc_free(mpctx->chapters);
mpctx->chapters = NULL;
talloc_free(chapter_file);
}
if (src && !mpctx->chapters) {
talloc_free(mpctx->chapters);
@ -795,8 +815,11 @@ static void load_chapters(struct MPContext *mpctx)
mpctx->chapters[n].pts -= src->start_time;
}
}
if (free_src)
if (free_src) {
mp_core_unlock(mpctx);
free_demuxer_and_stream(src);
mp_core_lock(mpctx);
}
}
static void load_per_file_options(m_config_t *conf,
@ -1143,6 +1166,44 @@ void update_lavfi_complex(struct MPContext *mpctx)
}
}
// Worker thread for loading external files and such. This is needed to avoid
// freezing the core when waiting for network while loading these.
static void load_external_opts_thread(void *p)
{
void **a = p;
struct MPContext *mpctx = a[0];
struct mp_waiter *waiter = a[1];
mp_core_lock(mpctx);
load_chapters(mpctx);
open_external_files(mpctx, mpctx->opts->audio_files, STREAM_AUDIO);
open_external_files(mpctx, mpctx->opts->sub_name, STREAM_SUB);
open_external_files(mpctx, mpctx->opts->external_files, STREAM_TYPE_COUNT);
autoload_external_files(mpctx);
mp_waiter_wakeup(waiter, 0);
mp_wakeup_core(mpctx);
mp_core_unlock(mpctx);
}
static void load_external_opts(struct MPContext *mpctx)
{
struct mp_waiter wait = MP_WAITER_INITIALIZER;
void *a[] = {mpctx, &wait};
if (!mp_thread_pool_queue(mpctx->thread_pool, load_external_opts_thread, a)) {
mpctx->stop_play = PT_ERROR;
return;
}
while (!mp_waiter_poll(&wait))
mp_idle(mpctx);
mp_waiter_wait(&wait);
}
// Start playing the current playlist entry.
// Handle initialization and deinitialization.
static void play_current_file(struct MPContext *mpctx)
@ -1264,13 +1325,11 @@ reopen_file:
demux_set_ts_offset(mpctx->demuxer, -mpctx->demuxer->start_time);
enable_demux_thread(mpctx, mpctx->demuxer);
load_chapters(mpctx);
add_demuxer_tracks(mpctx, mpctx->demuxer);
open_external_files(mpctx, opts->audio_files, STREAM_AUDIO);
open_external_files(mpctx, opts->sub_name, STREAM_SUB);
open_external_files(mpctx, opts->external_files, STREAM_TYPE_COUNT);
autoload_external_files(mpctx);
load_external_opts(mpctx);
if (mpctx->stop_play)
goto terminate_playback;
check_previous_track_selection(mpctx);
@ -1465,6 +1524,8 @@ terminate_playback:
if (mpctx->playing)
playlist_entry_unref(mpctx->playing);
// Note: a lot of things assume that the core won't be unlocked between
// uninitializing various playback-only resources (such as tracks).
mpctx->playing = NULL;
talloc_free(mpctx->filename);
mpctx->filename = NULL;