diff options
Diffstat (limited to 'player/loadfile.c')
-rw-r--r-- | player/loadfile.c | 287 |
1 files changed, 245 insertions, 42 deletions
diff --git a/player/loadfile.c b/player/loadfile.c index b94ce8af43..dba02ee828 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -40,6 +40,7 @@ #include "options/m_property.h" #include "common/common.h" #include "common/encode.h" +#include "common/recorder.h" #include "input/input.h" #include "audio/audio.h" @@ -57,6 +58,17 @@ #include "command.h" #include "libmpv/client.h" +// 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); +} + static void uninit_demuxer(struct MPContext *mpctx) { for (int r = 0; r < NUM_PTRACKS; r++) { @@ -80,6 +92,11 @@ static void uninit_demuxer(struct MPContext *mpctx) 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); } #define APPEND(s, ...) mp_snprintf_cat(s, sizeof(s), __VA_ARGS__) @@ -453,6 +470,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type uninit_sub(mpctx, current); if (current) { + if (current->remux_sink) + close_recorder_and_error(mpctx); current->selected = false; reselect_demux_stream(mpctx, current); } @@ -774,57 +793,144 @@ 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, - .allow_capture = true, - .stream_flags = args->stream_flags, + .force_format = mpctx->open_format, + .stream_flags = mpctx->open_url_flags, + .initial_readahead = true, }; - 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; } } - if (args->demux && global->opts->rebase_start_time) - demux_set_ts_offset(args->demux, -args->demux->start_time); + + atomic_store(&mpctx->open_done, true); + mp_wakeup_core(mpctx); + return NULL; } -static void open_demux_reentrant(struct MPContext *mpctx) +static void cancel_open(struct MPContext *mpctx) { - struct demux_open_args args = { - .global = mpctx->global, - .cancel = mpctx->playback_abort, - .log = mpctx->log, - .stream_flags = mpctx->playing->stream_flags, - .url = talloc_strdup(NULL, mpctx->stream_open_filename), - }; + 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) - args.stream_flags = 0; - mpctx_run_reentrant(mpctx, open_demux_thread, &args); - if (args.demux) { - mpctx->demuxer = args.demux; - enable_demux_thread(mpctx, mpctx->demuxer); + 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) +{ + 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, "Dropping finished prefetch of wrong URL.\n"); + } else { + 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 = mpctx->open_cancel; + pthread_mutex_unlock(&mpctx->lock); + + 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); + mpctx->demuxer_cancel = NULL; + pthread_mutex_unlock(&mpctx->lock); + } + + 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); } - talloc_free(args.url); } static bool init_complex_filters(struct MPContext *mpctx) @@ -977,7 +1083,7 @@ static void play_current_file(struct MPContext *mpctx) mpctx->filename = talloc_strdup(NULL, mpctx->playing->filename); mpctx->stream_open_filename = mpctx->filename; - mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION | OSD_SEEK_INFO_CURRENT_FILE; + mpctx->add_osd_seek_info &= OSD_SEEK_INFO_CURRENT_FILE; if (opts->reset_options) { for (int n = 0; opts->reset_options[n]; n++) { @@ -1038,6 +1144,10 @@ reopen_file: goto terminate_playback; } + if (mpctx->opts->rebase_start_time) + 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); @@ -1149,6 +1259,8 @@ reopen_file: if (mpctx->opts->pause) pause_player(mpctx); + open_recorder(mpctx, true); + playback_start = mp_time_sec(); mpctx->error_playing = 0; while (!mpctx->stop_play) @@ -1169,7 +1281,9 @@ terminate_playback: if (mpctx->step_frames) opts->pause = 1; - mp_cancel_trigger(mpctx->playback_abort); + mp_abort_playback_async(mpctx); + + close_recorder(mpctx); // time to uninit all, except global stuff: uninit_complex_filters(mpctx); @@ -1255,8 +1369,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) { @@ -1316,7 +1431,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; @@ -1326,6 +1441,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. @@ -1339,3 +1456,89 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e) mpctx->stop_play = PT_CURRENT_ENTRY; mp_wakeup_core(mpctx); } + +static void set_track_recorder_sink(struct track *track, + struct mp_recorder_sink *sink) +{ + if (track->d_sub) + sub_set_recorder_sink(track->d_sub, sink); + if (track->d_video) + track->d_video->recorder_sink = sink; + if (track->d_audio) + track->d_audio->recorder_sink = sink; + track->remux_sink = sink; +} + +void close_recorder(struct MPContext *mpctx) +{ + if (!mpctx->recorder) + return; + + for (int n = 0; n < mpctx->num_tracks; n++) + set_track_recorder_sink(mpctx->tracks[n], NULL); + + mp_recorder_destroy(mpctx->recorder); + mpctx->recorder = NULL; +} + +// Like close_recorder(), but also unset the option. Intended for use on errors. +void close_recorder_and_error(struct MPContext *mpctx) +{ + close_recorder(mpctx); + talloc_free(mpctx->opts->record_file); + mpctx->opts->record_file = NULL; + mp_notify_property(mpctx, "record-file"); + MP_ERR(mpctx, "Disabling stream recording.\n"); +} + +void open_recorder(struct MPContext *mpctx, bool on_init) +{ + if (!mpctx->playback_initialized) + return; + + close_recorder(mpctx); + + char *target = mpctx->opts->record_file; + if (!target || !target[0]) + return; + + struct sh_stream **streams = NULL; + int num_streams = 0; + + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + if (track->stream && track->selected && + (track->d_sub || track->d_video || track->d_audio)) + { + MP_TARRAY_APPEND(NULL, streams, num_streams, track->stream); + } + } + + mpctx->recorder = mp_recorder_create(mpctx->global, mpctx->opts->record_file, + streams, num_streams); + + if (!mpctx->recorder) { + talloc_free(streams); + close_recorder_and_error(mpctx); + return; + } + + if (!on_init) + mp_recorder_mark_discontinuity(mpctx->recorder); + + int n_stream = 0; + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + if (n_stream >= num_streams) + break; + // (We expect track->stream not to be reused on other tracks.) + if (track->stream == streams[n_stream]) { + set_track_recorder_sink(track, + mp_recorder_get_sink(mpctx->recorder, n_stream)); + n_stream++; + } + } + + talloc_free(streams); +} + |