mirror of
https://github.com/mpv-player/mpv
synced 2024-12-26 09:02:38 +00:00
core: allow changing filter filters at runtime
Add the "vf" command, which allows changing the video filter chain at runtime. For example, the 'y' key could be bound to toggle deinterlacing by adding 'y vf toggle yadif' to the input.conf. Reconfiguring the video filter chain normally resets the VO, so that it will be "stuck" until a new video frame is rendered. To mitigate this, a seek to the current position is issued when the filter chain is changed. This is done only if playback is paused, because normal playback will show an actual new frame quickly enough. If vdpau hardware decoding is used, filter insertion (whether it fails or not) will break the video for a while. This is because vo_vdpau resets decoding related things on vo_config().
This commit is contained in:
parent
d1b37aff32
commit
f569d245ba
@ -236,6 +236,37 @@ af_clr
|
|||||||
Remove all audio filters. (Conversion filters will be re-added
|
Remove all audio filters. (Conversion filters will be re-added
|
||||||
automatically if needed.)
|
automatically if needed.)
|
||||||
|
|
||||||
|
vf set|add|toggle|del "filter1=params,filter2,..."
|
||||||
|
Change video filter chain.
|
||||||
|
|
||||||
|
The first argument decides what happens:
|
||||||
|
|
||||||
|
set
|
||||||
|
Overwrite the previous filter chain with the new one.
|
||||||
|
|
||||||
|
add
|
||||||
|
Append the new filter chain to the previous one.
|
||||||
|
|
||||||
|
toggle
|
||||||
|
Check if the given filter (with the exact parameters) is already
|
||||||
|
in the video chain. If yes, remove the filter. If no, add the filter.
|
||||||
|
(If several filters are passed to the command, this is done for
|
||||||
|
each filter.)
|
||||||
|
|
||||||
|
del
|
||||||
|
Remove the given filters from the video chain. Unlike in the other
|
||||||
|
cases, the second parameter is a comma separated list of filter names
|
||||||
|
or integer indexes. ``0`` would denote the first filter. Negative
|
||||||
|
indexes start from the last filter, and ``-1`` denotes the last
|
||||||
|
filter.
|
||||||
|
|
||||||
|
*EXAMPLE for input.conf*:
|
||||||
|
|
||||||
|
- ``a vf set flip`` turn video upside-down on the ``a`` key
|
||||||
|
- ``b vf set ""`` remove all video filters on ``b``
|
||||||
|
- ``c vf toggle lavfi=gradfun`` toggle debanding on ``c``
|
||||||
|
|
||||||
|
|
||||||
Undocumented commands: tv_start_scan, tv_step_channel, tv_step_norm,
|
Undocumented commands: tv_start_scan, tv_step_channel, tv_step_norm,
|
||||||
tv_step_chanlist, tv_set_channel, tv_last_channel, tv_set_freq, tv_step_freq,
|
tv_step_chanlist, tv_set_channel, tv_last_channel, tv_set_freq, tv_step_freq,
|
||||||
tv_set_norm, dvb_set_channel, radio_step_channel, radio_set_channel,
|
tv_set_norm, dvb_set_channel, radio_step_channel, radio_set_channel,
|
||||||
|
@ -1775,6 +1775,42 @@ static void show_playlist_on_osd(MPContext *mpctx)
|
|||||||
talloc_free(res);
|
talloc_free(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void change_video_filters(MPContext *mpctx, const char *cmd,
|
||||||
|
const char *arg)
|
||||||
|
{
|
||||||
|
struct MPOpts *opts = &mpctx->opts;
|
||||||
|
struct m_config *conf = mpctx->mconfig;
|
||||||
|
struct m_obj_settings *old_vf_settings = NULL;
|
||||||
|
bool success = false;
|
||||||
|
bool need_refresh = false;
|
||||||
|
double refresh_pts = mpctx->last_vo_pts;
|
||||||
|
|
||||||
|
// The option parser is used to modify the filter list itself.
|
||||||
|
char optname[20];
|
||||||
|
snprintf(optname, sizeof(optname), "vf-%s", cmd);
|
||||||
|
const struct m_option *type = m_config_get_option(conf, bstr0(optname));
|
||||||
|
|
||||||
|
// Backup old settings, in case it fails
|
||||||
|
m_option_copy(type, &old_vf_settings, &opts->vf_settings);
|
||||||
|
|
||||||
|
if (m_config_set_option0(conf, optname, arg) >= 0) {
|
||||||
|
need_refresh = true;
|
||||||
|
success = reinit_video_filters(mpctx) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
m_option_copy(type, &opts->vf_settings, &old_vf_settings);
|
||||||
|
if (need_refresh)
|
||||||
|
reinit_video_filters(mpctx);
|
||||||
|
}
|
||||||
|
m_option_free(type, &old_vf_settings);
|
||||||
|
|
||||||
|
// Try to refresh the video by doing a precise seek to the currently
|
||||||
|
// displayed frame.
|
||||||
|
if (need_refresh && opts->pause)
|
||||||
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, refresh_pts, 1);
|
||||||
|
}
|
||||||
|
|
||||||
void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||||
{
|
{
|
||||||
struct MPOpts *opts = &mpctx->opts;
|
struct MPOpts *opts = &mpctx->opts;
|
||||||
@ -2289,6 +2325,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
|||||||
af_uninit(mpctx->mixer.afilter);
|
af_uninit(mpctx->mixer.afilter);
|
||||||
af_init(mpctx->mixer.afilter);
|
af_init(mpctx->mixer.afilter);
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case MP_CMD_AF_ADD:
|
case MP_CMD_AF_ADD:
|
||||||
case MP_CMD_AF_DEL: {
|
case MP_CMD_AF_DEL: {
|
||||||
if (!sh_audio)
|
if (!sh_audio)
|
||||||
@ -2331,6 +2368,9 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
|||||||
af_reinit(sh_audio->afilter);
|
af_reinit(sh_audio->afilter);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MP_CMD_VF:
|
||||||
|
change_video_filters(mpctx, cmd->args[0].v.s, cmd->args[1].v.s);
|
||||||
|
break;
|
||||||
case MP_CMD_SHOW_CHAPTERS:
|
case MP_CMD_SHOW_CHAPTERS:
|
||||||
show_chapters_on_osd(mpctx);
|
show_chapters_on_osd(mpctx);
|
||||||
break;
|
break;
|
||||||
|
@ -198,6 +198,8 @@ static const mp_cmd_t mp_cmds[] = {
|
|||||||
{ MP_CMD_AF_CLR, "af_clr", },
|
{ MP_CMD_AF_CLR, "af_clr", },
|
||||||
{ MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } },
|
{ MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } },
|
||||||
|
|
||||||
|
{ MP_CMD_VF, "vf", { ARG_STRING, ARG_STRING } },
|
||||||
|
|
||||||
{ MP_CMD_SHOW_CHAPTERS, "show_chapters", },
|
{ MP_CMD_SHOW_CHAPTERS, "show_chapters", },
|
||||||
{ MP_CMD_SHOW_TRACKS, "show_tracks", },
|
{ MP_CMD_SHOW_TRACKS, "show_tracks", },
|
||||||
{ MP_CMD_SHOW_PLAYLIST, "show_playlist", },
|
{ MP_CMD_SHOW_PLAYLIST, "show_playlist", },
|
||||||
|
@ -78,6 +78,9 @@ enum mp_command_type {
|
|||||||
MP_CMD_AF_CLR,
|
MP_CMD_AF_CLR,
|
||||||
MP_CMD_AF_CMDLINE,
|
MP_CMD_AF_CMDLINE,
|
||||||
|
|
||||||
|
/// Video filter commands
|
||||||
|
MP_CMD_VF,
|
||||||
|
|
||||||
MP_CMD_SHOW_CHAPTERS,
|
MP_CMD_SHOW_CHAPTERS,
|
||||||
MP_CMD_SHOW_TRACKS,
|
MP_CMD_SHOW_TRACKS,
|
||||||
MP_CMD_SHOW_PLAYLIST,
|
MP_CMD_SHOW_PLAYLIST,
|
||||||
|
@ -298,6 +298,7 @@ double playing_audio_pts(struct MPContext *mpctx);
|
|||||||
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
|
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
|
||||||
float fps, int noerr);
|
float fps, int noerr);
|
||||||
int reinit_video_chain(struct MPContext *mpctx);
|
int reinit_video_chain(struct MPContext *mpctx);
|
||||||
|
int reinit_video_filters(struct MPContext *mpctx);
|
||||||
void pause_player(struct MPContext *mpctx);
|
void pause_player(struct MPContext *mpctx);
|
||||||
void unpause_player(struct MPContext *mpctx);
|
void unpause_player(struct MPContext *mpctx);
|
||||||
void add_step_frame(struct MPContext *mpctx, int dir);
|
void add_step_frame(struct MPContext *mpctx, int dir);
|
||||||
|
@ -2466,6 +2466,39 @@ static void update_fps(struct MPContext *mpctx)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void recreate_video_filters(struct MPContext *mpctx)
|
||||||
|
{
|
||||||
|
struct MPOpts *opts = &mpctx->opts;
|
||||||
|
struct sh_video *sh_video = mpctx->sh_video;
|
||||||
|
assert(sh_video);
|
||||||
|
|
||||||
|
vf_uninit_filter_chain(sh_video->vfilter);
|
||||||
|
|
||||||
|
char *vf_arg[] = {
|
||||||
|
"_oldargs_", (char *)mpctx->video_out, NULL
|
||||||
|
};
|
||||||
|
sh_video->vfilter = vf_open_filter(opts, NULL, "vo", vf_arg);
|
||||||
|
|
||||||
|
sh_video->vfilter = append_filters(sh_video->vfilter, opts->vf_settings);
|
||||||
|
|
||||||
|
struct vf_instance *vf = sh_video->vfilter;
|
||||||
|
mpctx->osd->render_subs_in_filter
|
||||||
|
= vf->control(vf, VFCTRL_INIT_OSD, NULL) == VO_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int reinit_video_filters(struct MPContext *mpctx)
|
||||||
|
{
|
||||||
|
struct sh_video *sh_video = mpctx->sh_video;
|
||||||
|
|
||||||
|
if (!sh_video)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
recreate_video_filters(mpctx);
|
||||||
|
video_reinit_vo(sh_video);
|
||||||
|
|
||||||
|
return sh_video->vf_initialized > 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
int reinit_video_chain(struct MPContext *mpctx)
|
int reinit_video_chain(struct MPContext *mpctx)
|
||||||
{
|
{
|
||||||
struct MPOpts *opts = &mpctx->opts;
|
struct MPOpts *opts = &mpctx->opts;
|
||||||
@ -2518,18 +2551,7 @@ int reinit_video_chain(struct MPContext *mpctx)
|
|||||||
STREAM_CTRL_GET_ASPECT_RATIO, &ar) != STREAM_UNSUPPORTED)
|
STREAM_CTRL_GET_ASPECT_RATIO, &ar) != STREAM_UNSUPPORTED)
|
||||||
mpctx->sh_video->stream_aspect = ar;
|
mpctx->sh_video->stream_aspect = ar;
|
||||||
|
|
||||||
{
|
recreate_video_filters(mpctx);
|
||||||
char *vf_arg[] = {
|
|
||||||
"_oldargs_", (char *)mpctx->video_out, NULL
|
|
||||||
};
|
|
||||||
sh_video->vfilter = vf_open_filter(opts, NULL, "vo", vf_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
sh_video->vfilter = append_filters(sh_video->vfilter, opts->vf_settings);
|
|
||||||
|
|
||||||
struct vf_instance *vf = sh_video->vfilter;
|
|
||||||
mpctx->osd->render_subs_in_filter
|
|
||||||
= vf->control(vf, VFCTRL_INIT_OSD, NULL) == VO_TRUE;
|
|
||||||
|
|
||||||
init_best_video_codec(sh_video, opts->video_decoders);
|
init_best_video_codec(sh_video, opts->video_decoders);
|
||||||
|
|
||||||
|
@ -184,6 +184,7 @@ void uninit_video(sh_video_t *sh_video)
|
|||||||
mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Uninit video.\n");
|
mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Uninit video.\n");
|
||||||
sh_video->vd_driver->uninit(sh_video);
|
sh_video->vd_driver->uninit(sh_video);
|
||||||
vf_uninit_filter_chain(sh_video->vfilter);
|
vf_uninit_filter_chain(sh_video->vfilter);
|
||||||
|
sh_video->vfilter = NULL;
|
||||||
talloc_free(sh_video->gsh->decoder_desc);
|
talloc_free(sh_video->gsh->decoder_desc);
|
||||||
sh_video->gsh->decoder_desc = NULL;
|
sh_video->gsh->decoder_desc = NULL;
|
||||||
sh_video->initialized = 0;
|
sh_video->initialized = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user