diff options
author | Anton Kindestam <antonki@kth.se> | 2018-12-05 19:02:03 +0100 |
---|---|---|
committer | Anton Kindestam <antonki@kth.se> | 2018-12-05 19:19:24 +0100 |
commit | 8b83c8996686072bc743b112ae5cb3bf93aa33ed (patch) | |
tree | b09ce6a7ff470b05006622f19914b3d39d2f7d9f /player/loadfile.c | |
parent | 5bcac8580df6fc62323136f756a3a6d1e754fe9c (diff) | |
parent | 559a400ac36e75a8d73ba263fd7fa6736df1c2da (diff) | |
download | mpv-8b83c8996686072bc743b112ae5cb3bf93aa33ed.tar.bz2 mpv-8b83c8996686072bc743b112ae5cb3bf93aa33ed.tar.xz |
Merge commit '559a400ac36e75a8d73ba263fd7fa6736df1c2da' into wm4-commits--merge-edition
This bumps libmpv version to 1.103
Diffstat (limited to 'player/loadfile.c')
-rw-r--r-- | player/loadfile.c | 355 |
1 files changed, 280 insertions, 75 deletions
diff --git a/player/loadfile.c b/player/loadfile.c index 4e764dc403..9d37c2ca21 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -26,6 +26,8 @@ #include "config.h" #include "mpv_talloc.h" +#include "misc/thread_pool.h" +#include "misc/thread_tools.h" #include "osdep/io.h" #include "osdep/terminal.h" #include "osdep/threads.h" @@ -59,15 +61,132 @@ #include "command.h" #include "libmpv/client.h" +// Called from the demuxer thread if a new packet is available, or other changes. +static void wakeup_demux(void *pctx) +{ + struct MPContext *mpctx = pctx; + mp_wakeup_core(mpctx); +} + // Called by foreign threads when playback should be stopped and such. void mp_abort_playback_async(struct MPContext *mpctx) { mp_cancel_trigger(mpctx->playback_abort); - pthread_mutex_lock(&mpctx->lock); - if (mpctx->demuxer_cancel) - mp_cancel_trigger(mpctx->demuxer_cancel); - pthread_mutex_unlock(&mpctx->lock); + pthread_mutex_lock(&mpctx->abort_lock); + + for (int n = 0; n < mpctx->num_abort_list; n++) { + struct mp_abort_entry *abort = mpctx->abort_list[n]; + if (abort->coupled_to_playback) + mp_abort_trigger_locked(mpctx, abort); + } + + pthread_mutex_unlock(&mpctx->abort_lock); +} + +// Add it to the global list, and allocate required data structures. +void mp_abort_add(struct MPContext *mpctx, struct mp_abort_entry *abort) +{ + pthread_mutex_lock(&mpctx->abort_lock); + assert(!abort->cancel); + abort->cancel = mp_cancel_new(NULL); + MP_TARRAY_APPEND(NULL, mpctx->abort_list, mpctx->num_abort_list, abort); + mp_abort_recheck_locked(mpctx, abort); + pthread_mutex_unlock(&mpctx->abort_lock); +} + +// Remove Add it to the global list, and free/clear required data structures. +// Does not deallocate the abort value itself. +void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort) +{ + pthread_mutex_lock(&mpctx->abort_lock); + for (int n = 0; n < mpctx->num_abort_list; n++) { + if (mpctx->abort_list[n] == abort) { + MP_TARRAY_REMOVE_AT(mpctx->abort_list, mpctx->num_abort_list, n); + TA_FREEP(&abort->cancel); + abort = NULL; // it's not free'd, just clear for the assert below + break; + } + } + assert(!abort); // should have been in the list + pthread_mutex_unlock(&mpctx->abort_lock); +} + +// Verify whether the abort needs to be signaled after changing certain fields +// in abort. +void mp_abort_recheck_locked(struct MPContext *mpctx, + struct mp_abort_entry *abort) +{ + if ((abort->coupled_to_playback && mp_cancel_test(mpctx->playback_abort)) || + mpctx->abort_all) + { + mp_abort_trigger_locked(mpctx, abort); + } +} + +void mp_abort_trigger_locked(struct MPContext *mpctx, + struct mp_abort_entry *abort) +{ + mp_cancel_trigger(abort->cancel); +} + +static void kill_demuxers_reentrant(struct MPContext *mpctx, + struct demuxer **demuxers, int num_demuxers) +{ + struct demux_free_async_state **items = NULL; + int num_items = 0; + + for (int n = 0; n < num_demuxers; n++) { + struct demuxer *d = demuxers[n]; + + if (!demux_cancel_test(d)) { + // Make sure it is set if it wasn't yet. + demux_set_wakeup_cb(d, wakeup_demux, mpctx); + + struct demux_free_async_state *item = demux_free_async(d); + if (item) { + MP_TARRAY_APPEND(NULL, items, num_items, item); + d = NULL; + } + } + + demux_cancel_and_free(d); + } + + if (!num_items) + return; + + MP_DBG(mpctx, "Terminating demuxers...\n"); + + double end = mp_time_sec() + mpctx->opts->demux_termination_timeout; + bool force = false; + while (num_items) { + double wait = end - mp_time_sec(); + + for (int n = 0; n < num_items; n++) { + struct demux_free_async_state *item = items[n]; + if (demux_free_async_finish(item)) { + items[n] = items[num_items - 1]; + num_items -= 1; + n--; + goto repeat; + } else if (wait < 0) { + demux_free_async_force(item); + if (!force) + MP_VERBOSE(mpctx, "Forcefully terminating demuxers...\n"); + force = true; + } + } + + if (wait >= 0) + mp_set_timeout(mpctx, wait); + mp_idle(mpctx); + repeat:; + } + + talloc_free(items); + + MP_DBG(mpctx, "Done terminating demuxers.\n"); } static void uninit_demuxer(struct MPContext *mpctx) @@ -76,28 +195,44 @@ static void uninit_demuxer(struct MPContext *mpctx) for (int t = 0; t < STREAM_TYPE_COUNT; t++) mpctx->current_track[r][t] = NULL; } + mpctx->seek_slave = NULL; + talloc_free(mpctx->chapters); mpctx->chapters = NULL; mpctx->num_chapters = 0; - // close demuxers for external tracks - for (int n = mpctx->num_tracks - 1; n >= 0; n--) { - mpctx->tracks[n]->selected = false; - mp_remove_track(mpctx, mpctx->tracks[n]); - } + struct demuxer **demuxers = NULL; + int num_demuxers = 0; + + if (mpctx->demuxer) + MP_TARRAY_APPEND(NULL, demuxers, num_demuxers, mpctx->demuxer); + mpctx->demuxer = NULL; + for (int i = 0; i < mpctx->num_tracks; i++) { - sub_destroy(mpctx->tracks[i]->d_sub); - talloc_free(mpctx->tracks[i]); + struct track *track = mpctx->tracks[i]; + + assert(!track->dec && !track->d_sub); + assert(!track->vo_c && !track->ao_c); + assert(!track->sink); + assert(!track->remux_sink); + + // Demuxers can be added in any order (if they appear mid-stream), and + // we can't know which tracks uses which, so here's some O(n^2) trash. + for (int n = 0; n < num_demuxers; n++) { + if (demuxers[n] == track->demuxer) { + track->demuxer = NULL; + break; + } + } + if (track->demuxer) + MP_TARRAY_APPEND(NULL, demuxers, num_demuxers, track->demuxer); + + talloc_free(track); } mpctx->num_tracks = 0; - free_demuxer_and_stream(mpctx->demuxer); - mpctx->demuxer = NULL; - - pthread_mutex_lock(&mpctx->lock); - talloc_free(mpctx->demuxer_cancel); - mpctx->demuxer_cancel = NULL; - pthread_mutex_unlock(&mpctx->lock); + kill_demuxers_reentrant(mpctx, demuxers, num_demuxers); + talloc_free(demuxers); } #define APPEND(s, ...) mp_snprintf_cat(s, sizeof(s), __VA_ARGS__) @@ -227,20 +362,16 @@ void reselect_demux_stream(struct MPContext *mpctx, struct track *track) if (!track->stream) return; double pts = get_current_time(mpctx); - if (pts != MP_NOPTS_VALUE) + if (pts != MP_NOPTS_VALUE) { pts += get_track_seek_offset(mpctx, track); + if (track->type == STREAM_SUB) + pts -= 10.0; + } demuxer_select_track(track->demuxer, track->stream, pts, track->selected); if (track == mpctx->seek_slave) mpctx->seek_slave = NULL; } -// Called from the demuxer thread if a new packet is available. -static void wakeup_demux(void *pctx) -{ - struct MPContext *mpctx = pctx; - mp_wakeup_core(mpctx); -} - static void enable_demux_thread(struct MPContext *mpctx, struct demuxer *demux) { if (mpctx->opts->demuxer_thread && !demux->fully_read) { @@ -554,8 +685,6 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track) struct demuxer *d = track->demuxer; - sub_destroy(track->d_sub); - if (mpctx->seek_slave == track) mpctx->seek_slave = NULL; @@ -572,7 +701,7 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track) in_use |= mpctx->tracks[n]->demuxer == d; if (!in_use) - free_demuxer_and_stream(d); + demux_cancel_and_free(d); mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); @@ -581,11 +710,14 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track) // Add the given file as additional track. The filter argument controls how or // if tracks are auto-selected at any point. +// To be run on a worker thread, locked (temporarily unlocks core). +// cancel will generally be used to abort the loading process, but on success +// the demuxer is changed to be slaved to mpctx->playback_abort instead. int mp_add_external_file(struct MPContext *mpctx, char *filename, - enum stream_type filter) + enum stream_type filter, struct mp_cancel *cancel) { struct MPOpts *opts = mpctx->opts; - if (!filename) + if (!filename || mp_cancel_test(cancel)) return -1; char *disp_filename = filename; @@ -603,11 +735,22 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename, break; } + mp_core_unlock(mpctx); + struct demuxer *demuxer = - demux_open_url(filename, ¶ms, mpctx->playback_abort, mpctx->global); + demux_open_url(filename, ¶ms, cancel, mpctx->global); + if (demuxer) + enable_demux_thread(mpctx, demuxer); + + 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->stop_play) + goto err_out; + if (!demuxer) goto err_out; - enable_demux_thread(mpctx, demuxer); if (opts->rebase_start_time) demux_set_ts_offset(demuxer, -demuxer->start_time); @@ -622,12 +765,11 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename, } if (!has_any) { - free_demuxer_and_stream(demuxer); char *tname = mp_tprintf(20, "%s ", stream_type_name(filter)); if (filter == STREAM_TYPE_COUNT) tname = ""; MP_ERR(mpctx, "No %sstreams in file %s.\n", tname, disp_filename); - return -1; + goto err_out; } int first_num = -1; @@ -643,22 +785,33 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename, first_num = mpctx->num_tracks - 1; } + mp_cancel_set_parent(demuxer->cancel, mpctx->playback_abort); + return first_num; err_out: - if (!mp_cancel_test(mpctx->playback_abort)) + demux_cancel_and_free(demuxer); + if (!mp_cancel_test(cancel)) MP_ERR(mpctx, "Can not open external file %s.\n", disp_filename); return -1; } +// to be run on a worker thread, locked (temporarily unlocks core) static void open_external_files(struct MPContext *mpctx, char **files, 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++) - mp_add_external_file(mpctx, files[n], filter); + mp_add_external_file(mpctx, files[n], filter, mpctx->playback_abort); + + talloc_free(tmp); } -void autoload_external_files(struct MPContext *mpctx) +// See mp_add_external_file() for meaning of cancel parameter. +void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel) { if (mpctx->opts->sub_auto < 0 && mpctx->opts->audiofile_auto < 0) return; @@ -673,7 +826,8 @@ void autoload_external_files(struct MPContext *mpctx) &stream_filename) > 0) base_filename = talloc_steal(tmp, stream_filename); } - struct subfn *list = find_external_files(mpctx->global, base_filename); + struct subfn *list = find_external_files(mpctx->global, base_filename, + mpctx->opts); talloc_steal(tmp, list); int sc[STREAM_TYPE_COUNT] = {0}; @@ -694,7 +848,7 @@ void autoload_external_files(struct MPContext *mpctx) goto skip; if (list[i].type == STREAM_AUDIO && !sc[STREAM_VIDEO]) goto skip; - int first = mp_add_external_file(mpctx, filename, list[i].type); + int first = mp_add_external_file(mpctx, filename, list[i].type, cancel); if (first < 0) goto skip; @@ -758,24 +912,35 @@ static void process_hooks(struct MPContext *mpctx, char *name) { mp_hook_start(mpctx, name); - while (!mp_hook_test_completion(mpctx, name)) + while (!mp_hook_test_completion(mpctx, name)) { mp_idle(mpctx); + + // We have no idea what blocks a hook, so just do a full abort. + if (mpctx->stop_play) + mp_abort_playback_async(mpctx); + } } +// to be run on a worker thread, locked (temporarily unlocks core) static void load_chapters(struct MPContext *mpctx) { struct demuxer *src = mpctx->demuxer; bool free_src = false; char *chapter_file = mpctx->opts->chapter_file; 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, - mpctx->playback_abort, mpctx->global); + mpctx->playback_abort, + mpctx->global); + mp_core_lock(mpctx); if (demux) { src = demux; free_src = true; } talloc_free(mpctx->chapters); mpctx->chapters = NULL; + talloc_free(chapter_file); } if (src && !mpctx->chapters) { talloc_free(mpctx->chapters); @@ -787,7 +952,7 @@ static void load_chapters(struct MPContext *mpctx) } } if (free_src) - free_demuxer_and_stream(src); + demux_cancel_and_free(src); } static void load_per_file_options(m_config_t *conf, @@ -809,7 +974,6 @@ static void *open_demux_thread(void *ctx) struct demuxer_params p = { .force_format = mpctx->open_format, .stream_flags = mpctx->open_url_flags, - .initial_readahead = true, }; mpctx->open_res_demuxer = demux_open_url(mpctx->open_url, &p, mpctx->open_cancel, mpctx->global); @@ -840,14 +1004,14 @@ static void cancel_open(struct MPContext *mpctx) pthread_join(mpctx->open_thread, NULL); mpctx->open_active = false; + if (mpctx->open_res_demuxer) + demux_cancel_and_free(mpctx->open_res_demuxer); + mpctx->open_res_demuxer = NULL; + 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); } @@ -904,9 +1068,7 @@ static void open_demux_reentrant(struct MPContext *mpctx) start_open(mpctx, url, mpctx->playing->stream_flags); // User abort should cancel the opener now. - pthread_mutex_lock(&mpctx->lock); - mpctx->demuxer_cancel = mpctx->open_cancel; - pthread_mutex_unlock(&mpctx->lock); + mp_cancel_set_parent(mpctx->open_cancel, mpctx->playback_abort); while (!atomic_load(&mpctx->open_done)) { mp_idle(mpctx); @@ -916,15 +1078,11 @@ static void open_demux_reentrant(struct MPContext *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; + mp_cancel_set_parent(mpctx->demuxer->cancel, mpctx->playback_abort); } else { mpctx->error_playing = mpctx->open_res_error; - pthread_mutex_lock(&mpctx->lock); - mpctx->demuxer_cancel = NULL; - pthread_mutex_unlock(&mpctx->lock); } cancel_open(mpctx); // cleanup @@ -1134,6 +1292,48 @@ 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, mpctx->playback_abort); + + 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); + + if (mpctx->stop_play) + mp_abort_playback_async(mpctx); + } + + mp_waiter_wait(&wait); +} + // Start playing the current playlist entry. // Handle initialization and deinitialization. static void play_current_file(struct MPContext *mpctx) @@ -1141,6 +1341,8 @@ static void play_current_file(struct MPContext *mpctx) struct MPOpts *opts = mpctx->opts; double playback_start = -1e100; + assert(mpctx->stop_play); + mp_notify(mpctx, MPV_EVENT_START_FILE, NULL); mp_cancel_reset(mpctx->playback_abort); @@ -1161,15 +1363,14 @@ static void play_current_file(struct MPContext *mpctx) mpctx->speed_factor_a = mpctx->speed_factor_v = 1.0; mpctx->display_sync_error = 0.0; mpctx->display_sync_active = false; + // let get_current_time() show 0 as start time (before playback_pts is set) + mpctx->last_seek_pts = 0.0; mpctx->seek = (struct seek_params){ 0 }; mpctx->filter_root = mp_filter_create_root(mpctx->global); mp_filter_root_set_wakeup_cb(mpctx->filter_root, mp_wakeup_core_cb, mpctx); reset_playback_state(mpctx); - // let get_current_time() show 0 as start time (before playback_pts is set) - mpctx->last_seek_pts = 0.0; - mpctx->playing = mpctx->playlist->current; if (!mpctx->playing || !mpctx->playing->filename) goto terminate_playback; @@ -1251,13 +1452,11 @@ static void play_current_file(struct MPContext *mpctx) demux_set_ts_offset(mpctx->demuxer, -mpctx->demuxer->start_time); enable_demux_thread(mpctx, mpctx->demuxer); - load_chapters(mpctx); add_demuxer_tracks(mpctx, mpctx->demuxer); - open_external_files(mpctx, opts->audio_files, STREAM_AUDIO); - open_external_files(mpctx, opts->sub_name, STREAM_SUB); - open_external_files(mpctx, opts->external_files, STREAM_TYPE_COUNT); - autoload_external_files(mpctx); + load_external_opts(mpctx); + if (mpctx->stop_play) + goto terminate_playback; check_previous_track_selection(mpctx); @@ -1369,21 +1568,19 @@ static void play_current_file(struct MPContext *mpctx) terminate_playback: - update_core_idle_state(mpctx); - - process_hooks(mpctx, "on_unload"); - - if (mpctx->stop_play == KEEP_PLAYING) - mpctx->stop_play = AT_END_OF_FILE; + if (!mpctx->stop_play) + mpctx->stop_play = PT_ERROR; if (mpctx->stop_play != AT_END_OF_FILE) clear_audio_output_buffers(mpctx); + update_core_idle_state(mpctx); + + process_hooks(mpctx, "on_unload"); + if (mpctx->step_frames) opts->pause = 1; - mp_abort_playback_async(mpctx); - close_recorder(mpctx); // time to uninit all, except global stuff: @@ -1391,12 +1588,16 @@ terminate_playback: uninit_audio_chain(mpctx); uninit_video_chain(mpctx); uninit_sub_all(mpctx); - uninit_demuxer(mpctx); if (!opts->gapless_audio && !mpctx->encode_lavc_ctx) uninit_audio_out(mpctx); mpctx->playback_initialized = false; + uninit_demuxer(mpctx); + + // Possibly stop ongoing async commands. + mp_abort_playback_async(mpctx); + m_config_restore_backups(mpctx->mconfig); TA_FREEP(&mpctx->filter_root); @@ -1458,6 +1659,8 @@ terminate_playback: } else { mpctx->files_played++; } + + assert(mpctx->stop_play); } // Determine the next file to play. Note that if this function returns non-NULL, @@ -1523,6 +1726,7 @@ void mp_play_files(struct MPContext *mpctx) prepare_playlist(mpctx, mpctx->playlist); for (;;) { + assert(mpctx->stop_play); idle_loop(mpctx); if (mpctx->stop_play == PT_QUIT) break; @@ -1533,14 +1737,14 @@ void mp_play_files(struct MPContext *mpctx) struct playlist_entry *new_entry = mpctx->playlist->current; if (mpctx->stop_play == PT_NEXT_ENTRY || mpctx->stop_play == PT_ERROR || - mpctx->stop_play == AT_END_OF_FILE || !mpctx->stop_play) + mpctx->stop_play == AT_END_OF_FILE || mpctx->stop_play == PT_STOP) { new_entry = mp_next_file(mpctx, +1, false, true); } mpctx->playlist->current = new_entry; mpctx->playlist->current_was_replaced = false; - mpctx->stop_play = 0; + mpctx->stop_play = PT_STOP; if (!mpctx->playlist->current && mpctx->opts->player_idle_mode < 2) break; @@ -1567,6 +1771,7 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e) assert(!e || playlist_entry_to_index(mpctx->playlist, e) >= 0); mpctx->playlist->current = e; mpctx->playlist->current_was_replaced = false; + // Make it pick up the new entry. if (!mpctx->stop_play) mpctx->stop_play = PT_CURRENT_ENTRY; mp_wakeup_core(mpctx); |