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. saving code in a detached manner.
Currently the following commands have different waiting characteristics with 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 Input Sections
-------------- --------------

View File

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

View File

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

View File

@ -26,6 +26,8 @@
#include "config.h" #include "config.h"
#include "mpv_talloc.h" #include "mpv_talloc.h"
#include "misc/thread_pool.h"
#include "misc/thread_tools.h"
#include "osdep/io.h" #include "osdep/io.h"
#include "osdep/terminal.h" #include "osdep/terminal.h"
#include "osdep/threads.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 // Add the given file as additional track. The filter argument controls how or
// if tracks are auto-selected at any point. // 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, 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; struct MPOpts *opts = mpctx->opts;
if (!filename || mp_cancel_test(mpctx->playback_abort)) if (!filename || mp_cancel_test(mpctx->playback_abort))
@ -600,7 +603,6 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
break; break;
} }
if (unlock)
mp_core_unlock(mpctx); mp_core_unlock(mpctx);
struct demuxer *demuxer = struct demuxer *demuxer =
@ -608,9 +610,17 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
if (demuxer) if (demuxer)
enable_demux_thread(mpctx, demuxer); enable_demux_thread(mpctx, demuxer);
if (unlock)
mp_core_lock(mpctx); 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) if (!demuxer)
goto err_out; goto err_out;
@ -627,10 +637,8 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
} }
if (!has_any) { if (!has_any) {
if (unlock)
mp_core_unlock(mpctx); mp_core_unlock(mpctx);
free_demuxer_and_stream(demuxer); 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)); char *tname = mp_tprintf(20, "%s ", stream_type_name(filter));
if (filter == STREAM_TYPE_COUNT) if (filter == STREAM_TYPE_COUNT)
@ -660,11 +668,18 @@ err_out:
return -1; return -1;
} }
// to be run on a worker thread, locked (temporarily unlocks core)
static void open_external_files(struct MPContext *mpctx, char **files, static void open_external_files(struct MPContext *mpctx, char **files,
enum stream_type filter) 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++) 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) void autoload_external_files(struct MPContext *mpctx)
@ -703,7 +718,7 @@ void autoload_external_files(struct MPContext *mpctx)
goto skip; goto skip;
if (list[i].type == STREAM_AUDIO && !sc[STREAM_VIDEO]) if (list[i].type == STREAM_AUDIO && !sc[STREAM_VIDEO])
goto skip; 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) if (first < 0)
goto skip; goto skip;
@ -771,20 +786,25 @@ static void process_hooks(struct MPContext *mpctx, char *name)
mp_idle(mpctx); mp_idle(mpctx);
} }
// to be run on a worker thread, locked (temporarily unlocks core)
static void load_chapters(struct MPContext *mpctx) static void load_chapters(struct MPContext *mpctx)
{ {
struct demuxer *src = mpctx->demuxer; struct demuxer *src = mpctx->demuxer;
bool free_src = false; bool free_src = false;
char *chapter_file = mpctx->opts->chapter_file; char *chapter_file = mpctx->opts->chapter_file;
if (chapter_file && chapter_file[0]) { 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, struct demuxer *demux = demux_open_url(chapter_file, NULL,
mpctx->playback_abort, mpctx->global); mpctx->playback_abort, mpctx->global);
mp_core_lock(mpctx);
if (demux) { if (demux) {
src = demux; src = demux;
free_src = true; free_src = true;
} }
talloc_free(mpctx->chapters); talloc_free(mpctx->chapters);
mpctx->chapters = NULL; mpctx->chapters = NULL;
talloc_free(chapter_file);
} }
if (src && !mpctx->chapters) { if (src && !mpctx->chapters) {
talloc_free(mpctx->chapters); talloc_free(mpctx->chapters);
@ -795,8 +815,11 @@ static void load_chapters(struct MPContext *mpctx)
mpctx->chapters[n].pts -= src->start_time; mpctx->chapters[n].pts -= src->start_time;
} }
} }
if (free_src) if (free_src) {
mp_core_unlock(mpctx);
free_demuxer_and_stream(src); free_demuxer_and_stream(src);
mp_core_lock(mpctx);
}
} }
static void load_per_file_options(m_config_t *conf, 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. // Start playing the current playlist entry.
// Handle initialization and deinitialization. // Handle initialization and deinitialization.
static void play_current_file(struct MPContext *mpctx) static void play_current_file(struct MPContext *mpctx)
@ -1264,13 +1325,11 @@ reopen_file:
demux_set_ts_offset(mpctx->demuxer, -mpctx->demuxer->start_time); demux_set_ts_offset(mpctx->demuxer, -mpctx->demuxer->start_time);
enable_demux_thread(mpctx, mpctx->demuxer); enable_demux_thread(mpctx, mpctx->demuxer);
load_chapters(mpctx);
add_demuxer_tracks(mpctx, mpctx->demuxer); add_demuxer_tracks(mpctx, mpctx->demuxer);
open_external_files(mpctx, opts->audio_files, STREAM_AUDIO); load_external_opts(mpctx);
open_external_files(mpctx, opts->sub_name, STREAM_SUB); if (mpctx->stop_play)
open_external_files(mpctx, opts->external_files, STREAM_TYPE_COUNT); goto terminate_playback;
autoload_external_files(mpctx);
check_previous_track_selection(mpctx); check_previous_track_selection(mpctx);
@ -1465,6 +1524,8 @@ terminate_playback:
if (mpctx->playing) if (mpctx->playing)
playlist_entry_unref(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; mpctx->playing = NULL;
talloc_free(mpctx->filename); talloc_free(mpctx->filename);
mpctx->filename = NULL; mpctx->filename = NULL;