From 1b611e38ef9291c309e97379ef432fd301605033 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 7 May 2018 20:36:17 +0200 Subject: 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. --- DOCS/man/input.rst | 3 +- player/command.c | 16 +++++++--- player/core.h | 4 ++- player/loadfile.c | 93 ++++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 93 insertions(+), 23 deletions(-) diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 205c732781..94e1ea0b1f 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -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 -------------- diff --git a/player/command.c b/player/command.c index 9aa3a9e6d8..12c232640a 100644 --- a/player/command.c +++ b/player/command.c @@ -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 } }, diff --git a/player/core.h b/player/core.h index 5ca227a8fa..9a9eb81852 100644 --- a/player/core.h +++ b/player/core.h @@ -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); diff --git a/player/loadfile.c b/player/loadfile.c index 7acac24f72..766f86a77b 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -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, ¶ms, 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; -- cgit v1.2.3