From b440f6dfb3d29651d8dcb7abfeb8ed18e3f2b995 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 6 May 2018 18:27:18 +0200 Subject: command: add infrastructure for async commands This enables two types of command behavior: 1. Plain async behavior, like "loadfile" not completing until the file is fully loaded. 2. Running parts of the command on worker threads, e.g. for I/O, such as "sub-add" doing network accesses on a thread while the core continues. Both have no implementation yet, and most new code is actually inactive. The plan is to implement a number of useful cases in the following commits. The most tricky part is handling internal keybindings (input.conf) and the multi-command feature (concatenating commands with ";"). It requires a bunch of roundabout code to make it do the expected thing in combination with async commands. There is the question how commands should be handled that come in at a higher rate than what can be handled by the core. Currently, it will simply queue up input.conf commands as long as memory lasts. The client API is limited by the size of the reply queue per client. For commands which require a worker thread, the thread pool is limited to 30 threads, and then will queue up work in memory. The number is completely arbitrary. --- player/core.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 71c39dcaa5..0840a7836d 100644 --- a/player/core.h +++ b/player/core.h @@ -243,6 +243,8 @@ typedef struct MPContext { // mp_dispatch_lock must be called to change it. int64_t outstanding_async; + struct mp_thread_pool *thread_pool; // for coarse I/O, often during loading + struct mp_log *statusline; struct osd_state *osd; char *term_osd_text; @@ -551,6 +553,8 @@ void mp_wait_events(struct MPContext *mpctx); void mp_set_timeout(struct MPContext *mpctx, double sleeptime); void mp_wakeup_core(struct MPContext *mpctx); void mp_wakeup_core_cb(void *ctx); +void mp_core_lock(struct MPContext *mpctx); +void mp_core_unlock(struct MPContext *mpctx); void mp_process_input(struct MPContext *mpctx); double get_relative_time(struct MPContext *mpctx); void reset_playback_state(struct MPContext *mpctx); -- cgit v1.2.3 From c349e2f337693c53687b0bd5e4d8669363e2d79d Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 6 May 2018 21:23:28 +0200 Subject: command: make sub-add and audio-add commands async Pretty trivial, since commands can be async now, and the common code even provides convenience like running commands on a worker thread. The only ugly thing is that mp_add_external_file() needs an extra flag for locking. This is because there's still some code which calls this synchronously from the main thread, and unlocking the core makes no sense there. --- player/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 0840a7836d..5ca227a8fa 100644 --- a/player/core.h +++ b/player/core.h @@ -488,7 +488,7 @@ struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx, void mp_abort_playback_async(struct MPContext *mpctx); void uninit_player(struct MPContext *mpctx, unsigned int mask); int mp_add_external_file(struct MPContext *mpctx, char *filename, - enum stream_type filter); + enum stream_type filter, bool unlock); #define FLAG_MARK_SELECTION 1 void mp_switch_track(struct MPContext *mpctx, enum stream_type type, struct track *track, int flags); -- cgit v1.2.3 From 1b611e38ef9291c309e97379ef432fd301605033 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 7 May 2018 20:36:17 +0200 Subject: 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. --- player/core.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 5ca227a8fa..9a9eb81852 100644 --- a/player/core.h +++ b/player/core.h @@ -296,6 +296,8 @@ typedef struct MPContext { struct track **tracks; int num_tracks; + int64_t death_hack; // don't fucking ask, just don't + char *track_layout_hash; // 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 uninit_player(struct MPContext *mpctx, unsigned int mask); 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 void mp_switch_track(struct MPContext *mpctx, enum stream_type type, struct track *track, int flags); -- cgit v1.2.3 From ce1f5e78c2b10e24c78d7ee65d7196093709b8ce Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 May 2018 16:51:53 +0200 Subject: player: rename "lock" to "abort_lock" If a struct as large as MPContext contains a field named "lock", it creates the impression that it is the primary lock for MPContext. This is wrong, the lock just protects a single field. --- player/core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 9a9eb81852..0434c5cb64 100644 --- a/player/core.h +++ b/player/core.h @@ -438,9 +438,9 @@ typedef struct MPContext { struct mp_ipc_ctx *ipc_ctx; - pthread_mutex_t lock; + pthread_mutex_t abort_lock; - // --- The following fields are protected by lock + // --- The following fields are protected by abort_lock struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer // --- Owned by MPContext -- cgit v1.2.3 From e4fb23ed7de874bb2d05824d7edb84cfd1b21101 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 May 2018 18:46:37 +0200 Subject: command: add a way to abort asynchronous commands Many asynchronous commands are potentially long running operations, such as loading something from network or running a foreign process. Obviously it shouldn't just be possible for them to freeze the player if they don't terminate as expected. Also, there will be situations where you want to explicitly stop some of those operations explicitly. So add an infrastructure for this. Commands have to support this explicitly. The next commit uses this to actually add support to a command. --- player/core.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 0434c5cb64..a42b1252b8 100644 --- a/player/core.h +++ b/player/core.h @@ -442,6 +442,8 @@ typedef struct MPContext { // --- The following fields are protected by abort_lock struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer + struct mp_abort_entry **abort_list; + int num_abort_list; // --- Owned by MPContext pthread_t open_thread; @@ -459,6 +461,20 @@ typedef struct MPContext { int open_res_error; } MPContext; +// Contains information about an asynchronous work item, how it can be aborted, +// and when. All fields are protected by MPContext.abort_lock. +struct mp_abort_entry { + // General conditions. + bool coupled_to_playback; // trigger when playback is terminated + // Actual trigger to abort the work. + struct mp_cancel *cancel; + // For client API. + struct mpv_handle *client; // non-NULL if done by a client API user + int client_work_type; // client API type, e.h. MPV_EVENT_COMMAND_REPLY + uint64_t client_work_id; // client API user reply_userdata value + // (only valid if client_work_type set) +}; + // audio.c void reset_audio_state(struct MPContext *mpctx); void reinit_audio_chain(struct MPContext *mpctx); @@ -488,6 +504,12 @@ struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx, // loadfile.c void mp_abort_playback_async(struct MPContext *mpctx); +void mp_abort_add(struct MPContext *mpctx, struct mp_abort_entry *abort); +void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort); +void mp_abort_recheck_locked(struct MPContext *mpctx, + struct mp_abort_entry *abort); +void mp_abort_trigger_locked(struct MPContext *mpctx, + struct mp_abort_entry *abort); void uninit_player(struct MPContext *mpctx, unsigned int mask); int mp_add_external_file(struct MPContext *mpctx, char *filename, enum stream_type filter); -- cgit v1.2.3 From 7428cc51496ca8e56600fdc4034b8f55720f09f9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 13 May 2018 13:48:47 +0200 Subject: client API: kill async commands on termination This affects async commands started by client API, commands with async capability run in a sync way by client API (think mpv_command_node() with "subprocess"), and detached async work. Since scripts might want to do some cleanup work (that might involve launching processes, don't ask), we don't unconditionally kill everything on exit, but apply an arbitrary timeout of 2 seconds until async commands are aborted. --- player/core.h | 1 + 1 file changed, 1 insertion(+) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index a42b1252b8..b0d9b2a5ea 100644 --- a/player/core.h +++ b/player/core.h @@ -444,6 +444,7 @@ typedef struct MPContext { struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer struct mp_abort_entry **abort_list; int num_abort_list; + bool abort_all; // during final termination // --- Owned by MPContext pthread_t open_thread; -- cgit v1.2.3 From 12d1404b04e90f5357882e5c1048d92305248cb9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 18 May 2018 21:38:17 +0200 Subject: player: make various commands for managing external tracks abortable Until now, they could be aborted only by ending playback, and calling mpv_abort_async_command didn't do anything. This requires furthering the mess how playback abort is done. The main reason why mp_cancel exists at all is to avoid that a "frozen" demuxer (blocked on network I/O or whatever) cannot freeze the core. The core should always get its way. Previously, there was a single mp_cancel handle, that could be signaled, and all demuxers would unfreeze. With external files, we might want to abort loading of a certain external file, which automatically means they need a separate mp_cancel. So give every demuxer its own mp_cancel, and "slave" it to whatever parent mp_cancel handles aborting. Since the mpv demuxer API conflates creating the demuxer and reading the file headers, mp_cancel strictly need to be created before the demuxer is created (or we couldn't abort loading). Although we give every demuxer its own mp_cancel (as "enforced" by cancel_and_free_demuxer), it's still rather messy to create/destroy it along with the demuxer. --- player/core.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index b0d9b2a5ea..43fc824e4f 100644 --- a/player/core.h +++ b/player/core.h @@ -441,7 +441,6 @@ typedef struct MPContext { pthread_mutex_t abort_lock; // --- The following fields are protected by abort_lock - struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer struct mp_abort_entry **abort_list; int num_abort_list; bool abort_all; // during final termination @@ -513,7 +512,7 @@ void mp_abort_trigger_locked(struct MPContext *mpctx, struct mp_abort_entry *abort); void uninit_player(struct MPContext *mpctx, unsigned int mask); int mp_add_external_file(struct MPContext *mpctx, char *filename, - enum stream_type filter); + enum stream_type filter, struct mp_cancel *cancel); #define FLAG_MARK_SELECTION 1 void mp_switch_track(struct MPContext *mpctx, enum stream_type type, struct track *track, int flags); @@ -532,7 +531,7 @@ void update_demuxer_properties(struct MPContext *mpctx); void print_track_list(struct MPContext *mpctx, const char *msg); void reselect_demux_stream(struct MPContext *mpctx, struct track *track); void prepare_playlist(struct MPContext *mpctx, struct playlist *pl); -void autoload_external_files(struct MPContext *mpctx); +void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel); struct track *select_default_track(struct MPContext *mpctx, int order, enum stream_type type); void prefetch_next(struct MPContext *mpctx); -- cgit v1.2.3 From 562d8e6d3236022a77e49a17948e25e493538f04 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 19 May 2018 12:37:37 +0200 Subject: player: simplify edition switching The player fully restarts playback when the edition or disk title is changed. Before this, the player tried to reinitialized playback partially. For example, it did not print a new "Playing: " message, and did not send playback end to libmpv users (scripts or applications). This playback restart code was a bit messy and could have unforeseen interactions with various state. There have been bugs before. Since it's a mostly cosmetic thing for an obscure feature, just change it to a full restart. This works well, though since it may have consequences for scripts or client API users, mention it in interface-changes.rst. --- player/core.h | 1 - 1 file changed, 1 deletion(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 43fc824e4f..39ea2fe07e 100644 --- a/player/core.h +++ b/player/core.h @@ -43,7 +43,6 @@ enum stop_play_reason { PT_NEXT_ENTRY, // prepare to play next entry in playlist PT_CURRENT_ENTRY, // prepare to play mpctx->playlist->current PT_STOP, // stop playback, clear playlist - PT_RELOAD_FILE, // restart playback PT_QUIT, // stop playback, quit player PT_ERROR, // play next playlist entry (due to an error) }; -- cgit v1.2.3 From 8816e1117ee65039dbb5700219ba3537d3e5290e Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 19 May 2018 18:25:54 +0200 Subject: player: change the role of the "stop_play" and "playing" variable Before this, mpctx->playing was often used to determine whether certain new state could be added to the playback state. In particular this affected external files (which added tracks and demuxers). The variable was checked to prevent that they were added before the corresponding uninit code. We want to make a small part of uninit asynchronous, but mpctx->playing needs to stay in the place where it is. It can't be used for this purpose anymore. Use mpctx->stop_play instead. Make it never have the value 0 outside of loading/playback. On unloading, it obviously has to be non-0. Change some other code in playloop.c to use this, because it seems slightly more correct. But mostly this is preparation for the following commit. --- player/core.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 39ea2fe07e..193537652b 100644 --- a/player/core.h +++ b/player/core.h @@ -37,12 +37,13 @@ // definitions used internally by the core player code enum stop_play_reason { - KEEP_PLAYING = 0, // must be 0, numeric values of others do not matter + KEEP_PLAYING = 0, // playback of a file is actually going on + // must be 0, numeric values of others do not matter AT_END_OF_FILE, // file has ended, prepare to play next // also returned on unrecoverable playback errors PT_NEXT_ENTRY, // prepare to play next entry in playlist PT_CURRENT_ENTRY, // prepare to play mpctx->playlist->current - PT_STOP, // stop playback, clear playlist + PT_STOP, // stop playback, or transient state when going to next PT_QUIT, // stop playback, quit player PT_ERROR, // play next playlist entry (due to an error) }; -- cgit v1.2.3 From 559a400ac36e75a8d73ba263fd7fa6736df1c2da Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 31 Aug 2018 12:48:36 +0200 Subject: demux, stream: rip out the classic stream cache The demuxer cache is the only cache now. Might need another change to combat seeking failures in mp4 etc. The only bad thing is the loss of cache-speed, which was sort of nice to have. --- player/core.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'player/core.h') diff --git a/player/core.h b/player/core.h index 193537652b..7fab8776d7 100644 --- a/player/core.h +++ b/player/core.h @@ -554,8 +554,6 @@ double get_play_end_pts(struct MPContext *mpctx); double get_play_start_pts(struct MPContext *mpctx); double get_ab_loop_start_time(struct MPContext *mpctx); void merge_playlist_files(struct playlist *pl); -float mp_get_cache_percent(struct MPContext *mpctx); -bool mp_get_cache_idle(struct MPContext *mpctx); void update_vo_playback_state(struct MPContext *mpctx); void update_window_title(struct MPContext *mpctx, bool force); void error_on_track(struct MPContext *mpctx, struct track *track); -- cgit v1.2.3