mirror of https://github.com/mpv-player/mpv
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:
parent
115a9b19f1
commit
1b611e38ef
|
@ -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
|
||||||
--------------
|
--------------
|
||||||
|
|
|
@ -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 } },
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue