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.
This commit is contained in:
wm4 2017-08-12 23:08:48 +02:00
parent baead23ea0
commit f1d161d55f
11 changed files with 188 additions and 99 deletions

View File

@ -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.

View File

@ -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 \

View File

@ -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})),

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}
// 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 (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;
if (!graph || !graph[0])
return true;
// 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:
if (!success)
deassociate_complex_filters(mpctx);
cleanup_deassociated_complex_filters(mpctx);
if (mpctx->playback_initialized) {
for (int n = 0; n < mpctx->num_tracks; n++)
reselect_demux_stream(mpctx, mpctx->tracks[n]);
}
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
return success ? 1 : -1;
}
static void uninit_complex_filters(struct MPContext *mpctx)
void update_lavfi_complex(struct MPContext *mpctx)
{
if (!mpctx->lavfi)
return;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
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) {
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);
}
}
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);
lavfi_destroy(mpctx->lavfi);
mpctx->lavfi = NULL;
}
// 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);

View File

@ -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

View File

@ -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