From f1d161d55f458cf47e63d1ab9ddf08859019dd8a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 Aug 2017 23:08:48 +0200 Subject: player: make --lavfi-complex changeable at runtime Tends to be somewhat glitchy if subtitles are enabled, and you enable and disable tracks. On error, this will disable --lavfi-complex, which will result in whatever behavior. --- DOCS/interface-changes.rst | 3 + options/m_option.h | 3 +- options/options.c | 2 +- player/audio.c | 34 ++++----- player/command.c | 3 + player/core.h | 9 ++- player/lavfi.c | 13 +++- player/lavfi.h | 4 +- player/loadfile.c | 177 +++++++++++++++++++++++++++++++++------------ player/playloop.c | 4 + player/video.c | 35 ++++----- 11 files changed, 188 insertions(+), 99 deletions(-) diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 1927c8b7b1..74eb079322 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -35,6 +35,9 @@ Interface changes framebuffer that was ever passed to the renderer. Having GL framebuffers with a size larger than 8 bit per component is quite rare. If you need it, set the --dither-depth option instead. + - --lavfi-complex can now be set during runtime. If you set this in + expectation it would be applied only after a reload, you might observe + weird behavior. --- mpv 0.26.0 --- - remove remaining deprecated audio device options, like --alsa-device Some of them were removed in earlier releases. diff --git a/options/m_option.h b/options/m_option.h index 6935c93594..5feed14542 100644 --- a/options/m_option.h +++ b/options/m_option.h @@ -408,7 +408,8 @@ struct m_option { #define UPDATE_PRIORITY (1 << 15) // --priority (Windows-only) #define UPDATE_SCREENSAVER (1 << 16) // --stop-screensaver #define UPDATE_VOL (1 << 17) // softvol related options -#define UPDATE_OPT_LAST (1 << 17) +#define UPDATE_LAVFI_COMPLEX (1 << 18) // --lavfi-complex +#define UPDATE_OPT_LAST (1 << 18) // All bits between _FIRST and _LAST (inclusive) #define UPDATE_OPTS_MASK \ diff --git a/options/options.c b/options/options.c index b13d9e5803..30249467aa 100644 --- a/options/options.c +++ b/options/options.c @@ -362,7 +362,7 @@ const m_option_t mp_opts[] = { OPT_STRINGLIST("alang", stream_lang[STREAM_AUDIO], 0), OPT_STRINGLIST("slang", stream_lang[STREAM_SUB], 0), - OPT_STRING("lavfi-complex", lavfi_complex, 0), + OPT_STRING("lavfi-complex", lavfi_complex, UPDATE_LAVFI_COMPLEX), OPT_CHOICE("audio-display", audio_display, 0, ({"no", 0}, {"attachment", 1})), diff --git a/player/audio.c b/player/audio.c index 7891116c7e..7fef377dac 100644 --- a/player/audio.c +++ b/player/audio.c @@ -557,25 +557,19 @@ init_error: void reinit_audio_chain(struct MPContext *mpctx) { - reinit_audio_chain_src(mpctx, NULL); + struct track *track = NULL; + track = mpctx->current_track[0][STREAM_AUDIO]; + if (!track || !track->stream) { + uninit_audio_out(mpctx); + error_on_track(mpctx, track); + return; + } + reinit_audio_chain_src(mpctx, track); } -void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) +// (track=NULL creates a blank chain, used for lavfi-complex) +void reinit_audio_chain_src(struct MPContext *mpctx, struct track *track) { - struct track *track = NULL; - struct sh_stream *sh = NULL; - if (!src) { - track = mpctx->current_track[0][STREAM_AUDIO]; - if (!track) { - uninit_audio_out(mpctx); - return; - } - sh = track->stream; - if (!sh) { - uninit_audio_out(mpctx); - goto no_audio; - } - } assert(!mpctx->ao_chain); mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL); @@ -584,15 +578,14 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) mpctx->ao_chain = ao_c; ao_c->log = mpctx->log; ao_c->af = af_new(mpctx->global); - if (sh) - ao_c->af->replaygain_data = sh->codec->replaygain_data; + if (track && track->stream) + ao_c->af->replaygain_data = track->stream->codec->replaygain_data; ao_c->spdif_passthrough = true; ao_c->pts = MP_NOPTS_VALUE; ao_c->ao_buffer = mp_audio_buffer_create(NULL); ao_c->ao = mpctx->ao; - ao_c->filter_src = src; - if (!ao_c->filter_src) { + if (track) { ao_c->track = track; track->ao_c = ao_c; if (!init_audio_decoder(mpctx, track)) @@ -614,7 +607,6 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) init_error: uninit_audio_chain(mpctx); uninit_audio_out(mpctx); -no_audio: error_on_track(mpctx, track); } diff --git a/player/command.c b/player/command.c index c282c5349f..b023e350c9 100644 --- a/player/command.c +++ b/player/command.c @@ -5901,6 +5901,9 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags) if (flags & UPDATE_VOL) audio_update_volume(mpctx); + + if (flags & UPDATE_LAVFI_COMPLEX) + update_lavfi_complex(mpctx); } void mp_notify_property(struct MPContext *mpctx, const char *property) diff --git a/player/core.h b/player/core.h index fbc4b1ec7a..d341aeb529 100644 --- a/player/core.h +++ b/player/core.h @@ -367,6 +367,8 @@ typedef struct MPContext { double last_frame_duration; // Video PTS, or audio PTS if video has ended. double playback_pts; + // Last known "good" PTS + double canonical_pts; // audio stats only int64_t audio_stat_start; double written_audio; @@ -478,7 +480,7 @@ void update_playback_speed(struct MPContext *mpctx); void uninit_audio_out(struct MPContext *mpctx); void uninit_audio_chain(struct MPContext *mpctx); int init_audio_decoder(struct MPContext *mpctx, struct track *track); -void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src); +void reinit_audio_chain_src(struct MPContext *mpctx, struct track *track); void audio_update_volume(struct MPContext *mpctx); void audio_update_balance(struct MPContext *mpctx); void reload_audio_output(struct MPContext *mpctx); @@ -522,6 +524,7 @@ void prefetch_next(struct MPContext *mpctx); void close_recorder(struct MPContext *mpctx); void close_recorder_and_error(struct MPContext *mpctx); void open_recorder(struct MPContext *mpctx, bool on_init); +void update_lavfi_complex(struct MPContext *mpctx); // main.c int mp_initialize(struct MPContext *mpctx, char **argv); @@ -611,8 +614,8 @@ int video_set_colors(struct vo_chain *vo_c, const char *item, int value); int video_vf_vo_control(struct vo_chain *vo_c, int vf_cmd, void *data); void reset_video_state(struct MPContext *mpctx); int init_video_decoder(struct MPContext *mpctx, struct track *track); -int reinit_video_chain(struct MPContext *mpctx); -int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src); +void reinit_video_chain(struct MPContext *mpctx); +void reinit_video_chain_src(struct MPContext *mpctx, struct track *track); int reinit_video_filters(struct MPContext *mpctx); void write_video(struct MPContext *mpctx); void mp_force_video_refresh(struct MPContext *mpctx); diff --git a/player/lavfi.c b/player/lavfi.c index c597387937..ee0ca281e4 100644 --- a/player/lavfi.c +++ b/player/lavfi.c @@ -249,12 +249,19 @@ struct lavfi *lavfi_create(struct mp_log *log, char *graph_string) void lavfi_destroy(struct lavfi *c) { + if (!c) + return; free_graph(c); clear_data(c); av_frame_free(&c->tmp_frame); talloc_free(c); } +const char *lavfi_get_graph(struct lavfi *c) +{ + return c->graph_string; +} + struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name) { for (int n = 0; n < c->num_pads; n++) { @@ -484,9 +491,11 @@ static void dump_graph(struct lavfi *c) #endif } -void lavfi_set_hwdec_devs(struct lavfi *c, struct mp_hwdec_devices *hwdevs) +void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad, + struct mp_hwdec_devices *hwdevs) { - c->hwdec_devs = hwdevs; + // We don't actually treat this per-pad. + pad->main->hwdec_devs = hwdevs; } // Initialize the graph if all inputs have formats set. If it's already diff --git a/player/lavfi.h b/player/lavfi.h index 0f2ae7705f..ef19a14179 100644 --- a/player/lavfi.h +++ b/player/lavfi.h @@ -13,6 +13,7 @@ enum lavfi_direction { }; struct lavfi *lavfi_create(struct mp_log *log, char *graph_string); +const char *lavfi_get_graph(struct lavfi *c); void lavfi_destroy(struct lavfi *c); struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name); enum lavfi_direction lavfi_pad_direction(struct lavfi_pad *pad); @@ -22,7 +23,8 @@ bool lavfi_get_connected(struct lavfi_pad *pad); bool lavfi_process(struct lavfi *c); bool lavfi_has_failed(struct lavfi *c); void lavfi_seek_reset(struct lavfi *c); -void lavfi_set_hwdec_devs(struct lavfi *c, struct mp_hwdec_devices *hwdevs); +void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad, + struct mp_hwdec_devices *hwdevs); int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe); int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe); bool lavfi_needs_input(struct lavfi_pad *pad); diff --git a/player/loadfile.c b/player/loadfile.c index a2ef851617..f0bb582b4f 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -945,21 +945,83 @@ void prefetch_next(struct MPContext *mpctx) } } -static bool init_complex_filters(struct MPContext *mpctx) +// Destroy the complex filter, and remove the references to the filter pads. +// (Call cleanup_deassociated_complex_filters() to close decoders/VO/AO +// that are not connected anymore due to this.) +static void deassociate_complex_filters(struct MPContext *mpctx) { - assert(!mpctx->lavfi); + for (int n = 0; n < mpctx->num_tracks; n++) + mpctx->tracks[n]->sink = NULL; + if (mpctx->vo_chain) + mpctx->vo_chain->filter_src = NULL; + if (mpctx->ao_chain) + mpctx->ao_chain->filter_src = NULL; + lavfi_destroy(mpctx->lavfi); + mpctx->lavfi = NULL; +} - char *graph = mpctx->opts->lavfi_complex; +// Close all decoders and sinks (AO/VO) that are not connected to either +// a track or a filter pad. +static void cleanup_deassociated_complex_filters(struct MPContext *mpctx) +{ + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + if (!(track->sink || track->vo_c || track->ao_c)) { + if (track->d_video && !track->vo_c) { + video_uninit(track->d_video); + track->d_video = NULL; + } + if (track->d_audio && !track->ao_c) { + audio_uninit(track->d_audio); + track->d_audio = NULL; + } + track->selected = false; + } + } - if (!graph || !graph[0]) - return true; + if (mpctx->vo_chain && !mpctx->vo_chain->video_src && + !mpctx->vo_chain->filter_src) + { + uninit_video_chain(mpctx); + } + if (mpctx->ao_chain && !mpctx->ao_chain->audio_src && + !mpctx->ao_chain->filter_src) + { + uninit_audio_chain(mpctx); + } +} + +// >0: changed, 0: no change, -1: error +static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) +{ + char *graph = mpctx->opts->lavfi_complex; + bool have_graph = graph && graph[0] && !force_uninit; + if (have_graph && mpctx->lavfi && + strcmp(graph, lavfi_get_graph(mpctx->lavfi)) == 0 && + !lavfi_has_failed(mpctx->lavfi)) + return 0; + if (!mpctx->lavfi && !have_graph) + return 0; + + // Deassociate the old filter pads. We leave both sources (tracks) and + // sinks (AO/VO) "dangling", connected to neither track or filter pad. + // Later, we either reassociate them with new pads, or uninit them if + // they are still dangling. This avoids too interruptive actions like + // recreating the VO. + deassociate_complex_filters(mpctx); + + bool success = false; + if (!have_graph) { + success = true; // normal full removal of graph + goto done; + } mpctx->lavfi = lavfi_create(mpctx->log, graph); if (!mpctx->lavfi) - return false; + goto done; if (lavfi_has_failed(mpctx->lavfi)) - return false; + goto done; for (int n = 0; n < mpctx->num_tracks; n++) { struct track *track = mpctx->tracks[n]; @@ -983,6 +1045,12 @@ static bool init_complex_filters(struct MPContext *mpctx) if (lavfi_get_connected(pad)) continue; + assert(!track->sink); + if (track->vo_c || track->ao_c) { + MP_ERR(mpctx, "Pad %s tries to connect to already selected track.\n", + label); + goto done; + } track->sink = pad; lavfi_set_connected(pad, true); track->selected = true; @@ -992,66 +1060,81 @@ static bool init_complex_filters(struct MPContext *mpctx) if (pad && lavfi_pad_type(pad) == STREAM_VIDEO && lavfi_pad_direction(pad) == LAVFI_OUT) { + if (mpctx->vo_chain) { + if (mpctx->vo_chain->video_src) { + MP_ERR(mpctx, "Pad vo tries to connected to already used VO.\n"); + goto done; + } + } else { + reinit_video_chain_src(mpctx, NULL); + if (!mpctx->vo_chain) + goto done; + } lavfi_set_connected(pad, true); - reinit_video_chain_src(mpctx, pad); + struct vo_chain *vo_c = mpctx->vo_chain; + vo_c->filter_src = pad; + lavfi_pad_set_hwdec_devs(vo_c->filter_src, vo_c->hwdec_devs); } pad = lavfi_find_pad(mpctx->lavfi, "ao"); if (pad && lavfi_pad_type(pad) == STREAM_AUDIO && lavfi_pad_direction(pad) == LAVFI_OUT) { + if (mpctx->ao_chain) { + if (mpctx->ao_chain->audio_src) { + MP_ERR(mpctx, "Pad ao tries to connected to already used AO.\n"); + goto done; + } + } else { + reinit_audio_chain_src(mpctx, NULL); + if (!mpctx->ao_chain) + goto done; + } lavfi_set_connected(pad, true); - reinit_audio_chain_src(mpctx, pad); + mpctx->ao_chain->filter_src = pad; } - return true; -} - -static bool init_complex_filter_decoders(struct MPContext *mpctx) -{ - if (!mpctx->lavfi) - return true; - for (int n = 0; n < mpctx->num_tracks; n++) { struct track *track = mpctx->tracks[n]; if (track->sink && track->type == STREAM_VIDEO) { - if (!init_video_decoder(mpctx, track)) - return false; + if (!track->d_video && !init_video_decoder(mpctx, track)) + goto done; } if (track->sink && track->type == STREAM_AUDIO) { - if (!init_audio_decoder(mpctx, track)) - return false; + if (!track->d_audio && !init_audio_decoder(mpctx, track)) + goto done; } } - return true; -} + success = true; +done: -static void uninit_complex_filters(struct MPContext *mpctx) -{ - if (!mpctx->lavfi) - return; + if (!success) + deassociate_complex_filters(mpctx); - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *track = mpctx->tracks[n]; + cleanup_deassociated_complex_filters(mpctx); - if (track->d_video && !track->vo_c) { - video_uninit(track->d_video); - track->d_video = NULL; - } - if (track->d_audio && !track->ao_c) { - audio_uninit(track->d_audio); - track->d_audio = NULL; - } + if (mpctx->playback_initialized) { + for (int n = 0; n < mpctx->num_tracks; n++) + reselect_demux_stream(mpctx, mpctx->tracks[n]); } - if (mpctx->vo_chain && mpctx->vo_chain->filter_src) - uninit_video_chain(mpctx); - if (mpctx->ao_chain && mpctx->ao_chain->filter_src) - uninit_audio_chain(mpctx); + mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); - lavfi_destroy(mpctx->lavfi); - mpctx->lavfi = NULL; + return success ? 1 : -1; +} + +void update_lavfi_complex(struct MPContext *mpctx) +{ + if (mpctx->playback_initialized) { + if (reinit_complex_filters(mpctx, false) != 0 && + mpctx->canonical_pts != MP_NOPTS_VALUE) + { + // Refresh seek to avoid weird situations. + queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->canonical_pts, + MPSEEK_EXACT, 0); + } + } } // Start playing the current playlist entry. @@ -1083,6 +1166,7 @@ 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; + mpctx->canonical_pts = MP_NOPTS_VALUE; mpctx->seek = (struct seek_params){ 0 }; reset_playback_state(mpctx); @@ -1176,7 +1260,7 @@ reopen_file: if (process_preloaded_hooks(mpctx)) goto terminate_playback; - if (!init_complex_filters(mpctx)) + if (reinit_complex_filters(mpctx, false) < 0) goto terminate_playback; assert(NUM_PTRACKS == 2); // opts->stream_id is hardcoded to 2 @@ -1223,9 +1307,6 @@ reopen_file: update_playback_speed(mpctx); - if (!init_complex_filter_decoders(mpctx)) - goto terminate_playback; - reinit_video_chain(mpctx); reinit_audio_chain(mpctx); reinit_sub_all(mpctx); @@ -1307,7 +1388,7 @@ terminate_playback: close_recorder(mpctx); // time to uninit all, except global stuff: - uninit_complex_filters(mpctx); + reinit_complex_filters(mpctx, true); uninit_audio_chain(mpctx); uninit_video_chain(mpctx); uninit_sub_all(mpctx); diff --git a/player/playloop.c b/player/playloop.c index 736a8f5951..0d3409065b 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -376,6 +376,8 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) !hr_seek && !(demux_flags & SEEK_FORWARD); mpctx->ab_loop_clip = mpctx->last_seek_pts < opts->ab_loop[1]; + + mpctx->canonical_pts = mpctx->last_seek_pts; } // This combines consecutive seek requests. @@ -956,6 +958,8 @@ static void handle_playback_time(struct MPContext *mpctx) { mpctx->playback_pts = playing_audio_pts(mpctx); } + if (mpctx->playback_pts != MP_NOPTS_VALUE) + mpctx->canonical_pts = mpctx->playback_pts; } // We always make sure audio and video buffers are filled before actually diff --git a/player/video.c b/player/video.c index 414629c3c2..5d5f7f1fac 100644 --- a/player/video.c +++ b/player/video.c @@ -456,23 +456,20 @@ err_out: return 0; } -int reinit_video_chain(struct MPContext *mpctx) +void reinit_video_chain(struct MPContext *mpctx) { - return reinit_video_chain_src(mpctx, NULL); + struct track *track = mpctx->current_track[0][STREAM_VIDEO]; + if (!track || !track->stream) { + error_on_track(mpctx, track); + handle_force_window(mpctx, true); + return; + } + reinit_video_chain_src(mpctx, track); } -int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) +// (track=NULL creates a blank chain, used for lavfi-complex) +void reinit_video_chain_src(struct MPContext *mpctx, struct track *track) { - struct track *track = NULL; - struct sh_stream *sh = NULL; - if (!src) { - track = mpctx->current_track[0][STREAM_VIDEO]; - if (!track) - return 0; - sh = track->stream; - if (!sh) - goto no_video; - } assert(!mpctx->vo_chain); if (!mpctx->video_out) { @@ -504,11 +501,7 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) vo_c->hwdec_devs = vo_c->vo->hwdec_devs; - if (mpctx->lavfi) - lavfi_set_hwdec_devs(mpctx->lavfi, vo_c->hwdec_devs); - - vo_c->filter_src = src; - if (!vo_c->filter_src) { + if (track) { vo_c->track = track; track->vo_c = vo_c; if (!init_video_decoder(mpctx, track)) @@ -516,7 +509,7 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) vo_c->video_src = track->d_video; vo_c->container_fps = vo_c->video_src->fps; - vo_c->is_coverart = !!sh->attached_picture; + vo_c->is_coverart = !!track->stream->attached_picture; track->vo_c = vo_c; vo_c->track = track; @@ -540,14 +533,12 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src) reset_video_state(mpctx); reset_subtitle_state(mpctx); - return 1; + return; err_out: -no_video: uninit_video_chain(mpctx); error_on_track(mpctx, track); handle_force_window(mpctx, true); - return 0; } // Try to refresh the video by doing a precise seek to the currently displayed -- cgit v1.2.3