diff options
-rw-r--r-- | player/core.h | 6 | ||||
-rw-r--r-- | player/discnav.c | 5 | ||||
-rw-r--r-- | player/loadfile.c | 67 | ||||
-rw-r--r-- | player/misc.c | 64 | ||||
-rw-r--r-- | player/playloop.c | 17 | ||||
-rw-r--r-- | player/scripting.c | 6 |
6 files changed, 146 insertions, 19 deletions
diff --git a/player/core.h b/player/core.h index cf35df64b2..a0c9db2cc5 100644 --- a/player/core.h +++ b/player/core.h @@ -178,7 +178,7 @@ typedef struct MPContext { struct playlist *playlist; struct playlist_entry *playing; // currently playing file - char *filename; // always the same as playing->filename (or NULL) + char *filename; // immutable copy of playing->filename (or NULL) struct mp_resolve_result *resolve_result; enum stop_play_reason stop_play; bool playback_initialized; // playloop can be run/is running @@ -417,6 +417,9 @@ float mp_get_cache_percent(struct MPContext *mpctx); bool mp_get_cache_idle(struct MPContext *mpctx); void update_window_title(struct MPContext *mpctx, bool force); void stream_dump(struct MPContext *mpctx); +int mpctx_run_non_blocking(struct MPContext *mpctx, void (*thread_fn)(void *arg), + void *thread_arg); +struct mpv_global *create_sub_global(struct MPContext *mpctx); // osd.c void set_osd_bar(struct MPContext *mpctx, int type, @@ -449,6 +452,7 @@ double chapter_start_time(struct MPContext *mpctx, int chapter); int get_chapter_count(struct MPContext *mpctx); void execute_queued_seek(struct MPContext *mpctx); void run_playloop(struct MPContext *mpctx); +void mp_idle(struct MPContext *mpctx); void idle_loop(struct MPContext *mpctx); void handle_force_window(struct MPContext *mpctx, bool reconfig); void add_frame_pts(struct MPContext *mpctx, double pts); diff --git a/player/discnav.c b/player/discnav.c index 1511dec468..ceb38bc3f4 100644 --- a/player/discnav.c +++ b/player/discnav.c @@ -167,6 +167,11 @@ void mp_nav_user_input(struct MPContext *mpctx, char *command) struct mp_nav_state *nav = mpctx->nav_state; if (!nav) return; + // In the short time while the demuxer is opened (running in a different + // thread) we can't access the stream directly. Once the demuxer is opened, + // we can access the stream via demux_stream_control() though. + if (!mpctx->demuxer) + return; if (strcmp(command, "mouse_move") == 0) { struct mp_image_params vid = {0}; if (mpctx->d_video) diff --git a/player/loadfile.c b/player/loadfile.c index e015b867e7..23ffa80684 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -32,6 +32,7 @@ #include "osdep/timer.h" #include "common/msg.h" +#include "common/global.h" #include "options/path.h" #include "options/m_config.h" #include "options/parse_configfile.h" @@ -844,6 +845,60 @@ static void load_per_file_options(m_config_t *conf, } } +struct stream_open_args { + struct mp_cancel *cancel; + struct mpv_global *global; // contains copy of global options + char *filename; + int stream_flags; + struct stream *stream; // result +}; + +static void open_stream_thread(void *pctx) +{ + struct stream_open_args *args = pctx; + args->stream = stream_create(args->filename, args->stream_flags, + args->cancel, args->global); +} + +static struct stream *open_stream_async(struct MPContext *mpctx, + char *filename, int stream_flags) +{ + struct stream_open_args args = { + .cancel = mpctx->playback_abort, + .global = create_sub_global(mpctx), + .filename = filename, + .stream_flags = stream_flags, + }; + mpctx_run_non_blocking(mpctx, open_stream_thread, &args); + if (args.stream) { + talloc_steal(args.stream, args.global); + } else { + talloc_free(args.global); + } + return args.stream; +} + +struct demux_open_args { + struct stream *stream; + struct demuxer *demux; // result +}; + +static void open_demux_thread(void *pctx) +{ + struct demux_open_args *args = pctx; + struct stream *s = args->stream; + struct mpv_global *global = s->global; // they run in the same thread anyway + args->demux = demux_open(s, global->opts->demuxer_name, NULL, global); +} + +static struct demuxer *open_demux_async(struct MPContext *mpctx, + struct stream *stream) +{ + struct demux_open_args args = {stream}; + mpctx_run_non_blocking(mpctx, open_demux_thread, &args); + return args.demux; +} + // Start playing the current playlist entry. // Handle initialization and deinitialization. static void play_current_file(struct MPContext *mpctx) @@ -880,7 +935,7 @@ static void play_current_file(struct MPContext *mpctx) goto terminate_playback; mpctx->playing->reserved += 1; - mpctx->filename = mpctx->playing->filename; + mpctx->filename = talloc_strdup(tmp, mpctx->playing->filename); mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION; @@ -931,8 +986,7 @@ static void play_current_file(struct MPContext *mpctx) int stream_flags = STREAM_READ; if (!opts->load_unsafe_playlists) stream_flags |= mpctx->playing->stream_flags; - mpctx->stream = stream_create(stream_filename, stream_flags, - mpctx->playback_abort, mpctx->global); + mpctx->stream = open_stream_async(mpctx, stream_filename, stream_flags); if (!mpctx->stream) { // error... mp_process_input(mpctx); goto terminate_playback; @@ -958,15 +1012,12 @@ goto_reopen_demuxer: ; mp_nav_reset(mpctx); - //============ Open DEMUXERS --- DETECT file type ======================= - - mpctx->demuxer = demux_open(mpctx->stream, opts->demuxer_name, NULL, - mpctx->global); - mpctx->master_demuxer = mpctx->demuxer; + mpctx->demuxer = open_demux_async(mpctx, mpctx->stream); if (!mpctx->demuxer) { MP_ERR(mpctx, "Failed to recognize file format.\n"); goto terminate_playback; } + mpctx->master_demuxer = mpctx->demuxer; MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, mpctx->demuxer); diff --git a/player/misc.c b/player/misc.c index d5e27f9584..21e9bcbba3 100644 --- a/player/misc.c +++ b/player/misc.c @@ -18,6 +18,7 @@ #include <stddef.h> #include <stdbool.h> +#include <pthread.h> #include <assert.h> #include "config.h" @@ -29,7 +30,9 @@ #include "common/msg.h" #include "options/options.h" #include "options/m_property.h" +#include "options/m_config.h" #include "common/common.h" +#include "common/global.h" #include "common/encode.h" #include "common/playlist.h" #include "input/input.h" @@ -219,3 +222,64 @@ void merge_playlist_files(struct playlist *pl) playlist_add_file(pl, edl); talloc_free(edl); } + +// Create a talloc'ed copy of mpctx->global. It contains a copy of the global +// option struct. It still just references some things though, like mp_log. +// The main purpose is letting threads access the option struct without the +// need for additional synchronization. +struct mpv_global *create_sub_global(struct MPContext *mpctx) +{ + struct mpv_global *new = talloc_ptrtype(NULL, new); + struct m_config *new_config = m_config_dup(new, mpctx->mconfig); + *new = (struct mpv_global){ + .log = mpctx->global->log, + .opts = new_config->optstruct, + }; + return new; +} + +struct wrapper_args { + struct MPContext *mpctx; + void (*thread_fn)(void *); + void *thread_arg; + pthread_mutex_t mutex; + bool done; +}; + +static void *thread_wrapper(void *pctx) +{ + struct wrapper_args *args = pctx; + args->thread_fn(args->thread_arg); + pthread_mutex_lock(&args->mutex); + args->done = true; + pthread_mutex_unlock(&args->mutex); + mp_input_wakeup(args->mpctx->input); // this interrupts mp_idle() + return NULL; +} + +// Run the thread_fn in a new thread. Wait until the thread returns, but while +// waiting, process input and input commands. +int mpctx_run_non_blocking(struct MPContext *mpctx, void (*thread_fn)(void *arg), + void *thread_arg) +{ + struct wrapper_args args = {mpctx, thread_fn, thread_arg}; + pthread_mutex_init(&args.mutex, NULL); + bool success = false; + pthread_t thread; + if (pthread_create(&thread, NULL, thread_wrapper, &args)) + goto done; + while (!success) { + mp_idle(mpctx); + + if (mpctx->stop_play) + mp_cancel_trigger(mpctx->playback_abort); + + pthread_mutex_lock(&args.mutex); + success |= args.done; + pthread_mutex_unlock(&args.mutex); + } + pthread_join(thread, NULL); +done: + pthread_mutex_destroy(&args.mutex); + return success ? 0 : -1; +} diff --git a/player/playloop.c b/player/playloop.c index 79d3d14655..5d9abf923d 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -968,6 +968,16 @@ void run_playloop(struct MPContext *mpctx) execute_queued_seek(mpctx); } +void mp_idle(struct MPContext *mpctx) +{ + handle_dummy_ticks(mpctx); + mp_wait_events(mpctx, mpctx->sleeptime); + mpctx->sleeptime = 100.0; + mp_process_input(mpctx); + update_osd_msg(mpctx); + handle_osd_redraw(mpctx); +} + // Waiting for the slave master to send us a new file to play. void idle_loop(struct MPContext *mpctx) { @@ -983,11 +993,6 @@ void idle_loop(struct MPContext *mpctx) mpctx->sleeptime = 0; need_reinit = false; } - handle_dummy_ticks(mpctx); - mp_wait_events(mpctx, mpctx->sleeptime); - mpctx->sleeptime = 100.0; - mp_process_input(mpctx); - update_osd_msg(mpctx); - handle_osd_redraw(mpctx); + mp_idle(mpctx); } } diff --git a/player/scripting.c b/player/scripting.c index 0b5993c8e5..6e2a5a4e2d 100644 --- a/player/scripting.c +++ b/player/scripting.c @@ -90,10 +90,8 @@ static void *script_thread(void *p) static void wait_loaded(struct MPContext *mpctx) { - while (!mp_clients_all_initialized(mpctx)) { - mp_wait_events(mpctx, 1e9); - mp_process_input(mpctx); - } + while (!mp_clients_all_initialized(mpctx)) + mp_idle(mpctx); } static void mp_load_script(struct MPContext *mpctx, const char *fname) |