summaryrefslogtreecommitdiffstats
path: root/player/loadfile.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-01-18 19:02:50 +0100
committerwm4 <wm4@nowhere>2017-01-18 19:02:50 +0100
commite277fadd60350caad1fc31e92a5076692e61dcc9 (patch)
tree715593fc3cb52fee1daffa7e4fe6712a8de97777 /player/loadfile.c
parentc54c3b6991ac0273e6b7a42dc42c5103f87ff9f1 (diff)
downloadmpv-e277fadd60350caad1fc31e92a5076692e61dcc9.tar.bz2
mpv-e277fadd60350caad1fc31e92a5076692e61dcc9.tar.xz
player: add prefetching of the next playlist entry
Since for mpv CLI, the player state is a singleton, full prefetching is a bit tricky. We do it only on the demuxer layer. The implementation reuses the old "open thread". This means there is significant potential for regressions even if the new option is not used. This is made worse by the fact that I barely tested this code. The generic mpctx_run_reentrant() wrapper is also removed - this was its only user, and its remains become part of the new implementation.
Diffstat (limited to 'player/loadfile.c')
-rw-r--r--player/loadfile.c159
1 files changed, 121 insertions, 38 deletions
diff --git a/player/loadfile.c b/player/loadfile.c
index e59acbc8d4..8023428e75 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -790,61 +790,141 @@ static void load_per_file_options(m_config_t *conf,
}
}
-struct demux_open_args {
- int stream_flags;
- char *url;
- struct mpv_global *global;
- struct mp_cancel *cancel;
- struct mp_log *log;
- // results
- struct demuxer *demux;
- int err;
-};
-
-static void open_demux_thread(void *pctx)
+static void *open_demux_thread(void *ctx)
{
- struct demux_open_args *args = pctx;
- struct mpv_global *global = args->global;
+ struct MPContext *mpctx = ctx;
+
struct demuxer_params p = {
- .force_format = global->opts->demuxer_name,
+ .force_format = mpctx->open_format,
.allow_capture = true,
- .stream_flags = args->stream_flags,
+ .stream_flags = mpctx->open_url_flags,
};
- args->demux = demux_open_url(args->url, &p, args->cancel, global);
- if (!args->demux) {
+ mpctx->open_res_demuxer =
+ demux_open_url(mpctx->open_url, &p, mpctx->open_cancel, mpctx->global);
+
+ if (mpctx->open_res_demuxer) {
+ MP_VERBOSE(mpctx, "Opening done: %s\n", mpctx->open_url);
+ } else {
+ MP_VERBOSE(mpctx, "Opening failed or was aborted: %s\n", mpctx->open_url);
+
if (p.demuxer_failed) {
- args->err = MPV_ERROR_UNKNOWN_FORMAT;
+ mpctx->open_res_error = MPV_ERROR_UNKNOWN_FORMAT;
} else {
- args->err = MPV_ERROR_LOADING_FAILED;
+ mpctx->open_res_error = MPV_ERROR_LOADING_FAILED;
}
}
+
+ atomic_store(&mpctx->open_done, true);
+ mp_wakeup_core(mpctx);
+ return NULL;
+}
+
+static void cancel_open(struct MPContext *mpctx)
+{
+ if (mpctx->open_cancel)
+ mp_cancel_trigger(mpctx->open_cancel);
+
+ if (mpctx->open_active)
+ pthread_join(mpctx->open_thread, NULL);
+ mpctx->open_active = false;
+
+ TA_FREEP(&mpctx->open_cancel);
+ TA_FREEP(&mpctx->open_url);
+ TA_FREEP(&mpctx->open_format);
+
+ if (mpctx->open_res_demuxer)
+ free_demuxer_and_stream(mpctx->open_res_demuxer);
+ mpctx->open_res_demuxer = NULL;
+
+ atomic_store(&mpctx->open_done, false);
+}
+
+// Setup all the field to open this url, and make sure a thread is running.
+static void start_open(struct MPContext *mpctx, char *url, int url_flags)
+{
+ cancel_open(mpctx);
+
+ assert(!mpctx->open_active);
+ assert(!mpctx->open_cancel);
+ assert(!mpctx->open_res_demuxer);
+ assert(!atomic_load(&mpctx->open_done));
+
+ mpctx->open_cancel = mp_cancel_new(NULL);
+ mpctx->open_url = talloc_strdup(NULL, url);
+ mpctx->open_format = talloc_strdup(NULL, mpctx->opts->demuxer_name);
+ mpctx->open_url_flags = url_flags;
+ if (mpctx->opts->load_unsafe_playlists)
+ mpctx->open_url_flags = 0;
+
+ if (pthread_create(&mpctx->open_thread, NULL, open_demux_thread, mpctx)) {
+ cancel_open(mpctx);
+ return;
+ }
+
+ mpctx->open_active = true;
}
static void open_demux_reentrant(struct MPContext *mpctx)
{
- struct demux_open_args args = {
- .global = mpctx->global,
- .cancel = mp_cancel_new(NULL),
- .log = mpctx->log,
- .stream_flags = mpctx->playing->stream_flags,
- .url = talloc_strdup(NULL, mpctx->stream_open_filename),
- };
+ char *url = mpctx->stream_open_filename;
+
+ if (mpctx->open_active) {
+ bool done = atomic_load(&mpctx->open_done);
+ bool failed = done && !mpctx->open_res_demuxer;
+ bool correct_url = strcmp(mpctx->open_url, url) == 0;
+
+ if (correct_url && !failed) {
+ MP_VERBOSE(mpctx, "Using prefetched/prefetching URL.\n");
+ } else if (correct_url && failed) {
+ MP_VERBOSE(mpctx, "Prefetched URL failed, retrying.\n");
+ cancel_open(mpctx);
+ } else {
+ if (!done)
+ MP_VERBOSE(mpctx, "Aborting onging prefetch of wrong URL.\n");
+ cancel_open(mpctx);
+ }
+ }
+
+ if (!mpctx->open_active)
+ start_open(mpctx, url, mpctx->playing->stream_flags);
+
+ // User abort should cancel the opener now.
pthread_mutex_lock(&mpctx->lock);
- mpctx->demuxer_cancel = args.cancel;
+ mpctx->demuxer_cancel = mpctx->open_cancel;
pthread_mutex_unlock(&mpctx->lock);
- if (mpctx->opts->load_unsafe_playlists)
- args.stream_flags = 0;
- mpctx_run_reentrant(mpctx, open_demux_thread, &args);
- if (args.demux) {
- mpctx->demuxer = args.demux;
+
+ while (!atomic_load(&mpctx->open_done)) {
+ mp_idle(mpctx);
+
+ if (mpctx->stop_play)
+ mp_abort_playback_async(mpctx);
+ }
+
+ if (mpctx->open_res_demuxer) {
+ assert(mpctx->demuxer_cancel == mpctx->open_cancel);
+ mpctx->demuxer = mpctx->open_res_demuxer;
+ mpctx->open_res_demuxer = NULL;
+ mpctx->open_cancel = NULL;
} else {
- mpctx->error_playing = args.err;
+ mpctx->error_playing = mpctx->open_res_error;
pthread_mutex_lock(&mpctx->lock);
- talloc_free(mpctx->demuxer_cancel);
mpctx->demuxer_cancel = NULL;
pthread_mutex_unlock(&mpctx->lock);
}
- talloc_free(args.url);
+
+ cancel_open(mpctx); // cleanup
+}
+
+void prefetch_next(struct MPContext *mpctx)
+{
+ if (!mpctx->opts->prefetch_open)
+ return;
+
+ struct playlist_entry *new_entry = mp_next_file(mpctx, +1, false, false);
+ if (new_entry && !mpctx->open_active && new_entry->filename) {
+ MP_VERBOSE(mpctx, "Prefetching: %s\n", new_entry->filename);
+ start_open(mpctx, new_entry->filename, new_entry->stream_flags);
+ }
}
static bool init_complex_filters(struct MPContext *mpctx)
@@ -1279,8 +1359,9 @@ terminate_playback:
// it can have side-effects and mutate mpctx.
// direction: -1 (previous) or +1 (next)
// force: if true, don't skip playlist entries marked as failed
+// mutate: if true, change loop counters
struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
- bool force)
+ bool force, bool mutate)
{
struct playlist_entry *next = playlist_get_next(mpctx->playlist, direction);
if (next && direction < 0 && !force) {
@@ -1340,7 +1421,7 @@ void mp_play_files(struct MPContext *mpctx)
if (mpctx->stop_play == PT_NEXT_ENTRY || mpctx->stop_play == PT_ERROR ||
mpctx->stop_play == AT_END_OF_FILE || !mpctx->stop_play)
{
- new_entry = mp_next_file(mpctx, +1, false);
+ new_entry = mp_next_file(mpctx, +1, false, true);
}
mpctx->playlist->current = new_entry;
@@ -1350,6 +1431,8 @@ void mp_play_files(struct MPContext *mpctx)
if (!mpctx->playlist->current && mpctx->opts->player_idle_mode < 2)
break;
}
+
+ cancel_open(mpctx);
}
// Abort current playback and set the given entry to play next.