summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--player/core.h6
-rw-r--r--player/discnav.c5
-rw-r--r--player/loadfile.c67
-rw-r--r--player/misc.c64
-rw-r--r--player/playloop.c17
-rw-r--r--player/scripting.c6
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)