player: rewrite deinterlace filter auto-insertion

Instead of using the "vf" command code (which changes filters at runtime
on user input), use the general filter-insertion code. The latter was
added later, and is more suitable for automatically inserted filters.

The old code failed in particular when using watch-later saving, which
stored the filter list in the resume config file. If a user changed the
hardware decoding mode via command line, the stored filter chain was out
of date and could cause failure due to not working with hardware or
software decoding mode. Storing the deinterlace filter in the filter
list was unavoidable, because it was part of the user state. (The new
code only edits the actually instantiated filters.)
This commit is contained in:
wm4 2016-07-05 21:10:26 +02:00
parent 329a7147d0
commit d72bcc8041
4 changed files with 84 additions and 107 deletions

View File

@ -2258,88 +2258,6 @@ static int mp_property_detected_hwdec(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
#define VF_DEINTERLACE_LABEL "deinterlace"
static bool probe_deint_filter(struct MPContext *mpctx, const char *filt)
{
char filter[80];
// add a label so that removing the filter is easier
snprintf(filter, sizeof(filter), "@%s:%s", VF_DEINTERLACE_LABEL, filt);
return edit_filters(mpctx, mp_null_log, STREAM_VIDEO, "pre", filter) >= 0;
}
static bool check_output_format(struct MPContext *mpctx, int imgfmt)
{
struct vo_chain *vo_c = mpctx->vo_chain;
if (!vo_c)
return false;
return vo_c->vf->allowed_output_formats[imgfmt - IMGFMT_START];
}
static int probe_deint_filters(struct MPContext *mpctx)
{
if (check_output_format(mpctx, IMGFMT_VDPAU)) {
char filter[80] = "vdpaupp:deint=yes";
int pref = 0;
vo_control(mpctx->video_out, VOCTRL_GET_PREF_DEINT, &pref);
pref = pref < 0 ? -pref : pref;
if (pref > 0 && pref <= 4) {
const char *types[] =
{"", "first-field", "bob", "temporal", "temporal-spatial"};
mp_snprintf_cat(filter, sizeof(filter), ":deint-mode=%s",
types[pref]);
}
probe_deint_filter(mpctx, filter);
return 0;
}
if (check_output_format(mpctx, IMGFMT_VAAPI) &&
probe_deint_filter(mpctx, "vavpp"))
return 0;
if ((check_output_format(mpctx, IMGFMT_D3D11VA) ||
check_output_format(mpctx, IMGFMT_D3D11NV12)) &&
probe_deint_filter(mpctx, "d3d11vpp"))
return 0;
if (probe_deint_filter(mpctx, "yadif"))
return 0;
return -1;
}
static int get_deinterlacing(struct MPContext *mpctx)
{
struct vo_chain *vo_c = mpctx->vo_chain;
int enabled = 0;
if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
enabled = -1;
if (enabled < 0) {
// vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE
if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL))
enabled = 1;
}
return enabled;
}
void remove_deint_filter(struct MPContext *mpctx)
{
edit_filters(mpctx, mp_null_log, STREAM_VIDEO, "del", "@" VF_DEINTERLACE_LABEL);
}
void set_deinterlacing(struct MPContext *mpctx, bool enable)
{
struct vo_chain *vo_c = mpctx->vo_chain;
if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL)) {
if (!enable)
remove_deint_filter(mpctx);
} else {
if ((get_deinterlacing(mpctx) > 0) != enable) {
int arg = enable;
if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK)
probe_deint_filters(mpctx);
}
}
mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
}
static int mp_property_deinterlace(void *ctx, struct m_property *prop,
int action, void *arg)
{

View File

@ -59,7 +59,4 @@ void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
void handle_ab_loop(struct MPContext *mpctx);
void remove_deint_filter(struct MPContext *mpctx);
void set_deinterlacing(struct MPContext *mpctx, bool enable);
#endif /* MPLAYER_COMMAND_H */

View File

@ -555,5 +555,7 @@ void uninit_video_out(struct MPContext *mpctx);
void uninit_video_chain(struct MPContext *mpctx);
double calc_average_frame_duration(struct MPContext *mpctx);
int init_video_decoder(struct MPContext *mpctx, struct track *track);
int get_deinterlacing(struct MPContext *mpctx);
void set_deinterlacing(struct MPContext *mpctx, bool enable);
#endif /* MPLAYER_MP_CORE_H */

View File

@ -49,6 +49,8 @@
#include "command.h"
#include "screenshot.h"
#define VF_DEINTERLACE_LABEL "deinterlace"
enum {
// update_video() - code also uses: <0 error, 0 eof, >0 progress
VD_ERROR = -1,
@ -153,8 +155,37 @@ static int try_filter(struct vo_chain *vo_c, char *name, char *label, char **arg
return 0;
}
static bool check_output_format(struct vo_chain *vo_c, int imgfmt)
{
return vo_c->vf->output_params.imgfmt == imgfmt;
}
static int probe_deint_filters(struct vo_chain *vo_c)
{
if (check_output_format(vo_c, IMGFMT_VDPAU)) {
char *args[5] = {"deint", "yes"};
int pref = 0;
vo_control(vo_c->vo, VOCTRL_GET_PREF_DEINT, &pref);
pref = pref < 0 ? -pref : pref;
if (pref > 0 && pref <= 4) {
const char *types[] =
{"", "first-field", "bob", "temporal", "temporal-spatial"};
args[2] = "deint-mode";
args[3] = (char *)types[pref];
}
return try_filter(vo_c, "vdpaupp", VF_DEINTERLACE_LABEL, args);
}
if (check_output_format(vo_c, IMGFMT_VAAPI))
return try_filter(vo_c, "vavpp", VF_DEINTERLACE_LABEL, NULL);
if (check_output_format(vo_c, IMGFMT_D3D11VA) ||
check_output_format(vo_c, IMGFMT_D3D11NV12))
return try_filter(vo_c, "d3d11vpp", VF_DEINTERLACE_LABEL, NULL);
return try_filter(vo_c, "yadif", VF_DEINTERLACE_LABEL, NULL);
}
// Reconfigure the filter chain according to the new input format.
static void filter_reconfig(struct vo_chain *vo_c)
static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
{
struct mp_image_params params = vo_c->input_format;
if (!params.imgfmt)
@ -165,7 +196,7 @@ static void filter_reconfig(struct vo_chain *vo_c)
if (vf_reconfig(vo_c->vf, &params) < 0)
return;
char *filters[] = {"autorotate", "autostereo3d", NULL};
char *filters[] = {"autorotate", "autostereo3d", "deinterlace", NULL};
for (int n = 0; filters[n]; n++) {
struct vf_instance *vf = vf_find_by_label(vo_c->vf, filters[n]);
if (vf) {
@ -194,6 +225,53 @@ static void filter_reconfig(struct vo_chain *vo_c)
MP_ERR(vo_c, "Can't insert 3D conversion filter.\n");
}
}
if (mpctx->opts->deinterlace == 1)
probe_deint_filters(vo_c);
}
static void recreate_auto_filters(struct MPContext *mpctx)
{
filter_reconfig(mpctx, mpctx->vo_chain);
mp_force_video_refresh(mpctx);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
int get_deinterlacing(struct MPContext *mpctx)
{
struct vo_chain *vo_c = mpctx->vo_chain;
int enabled = 0;
if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
enabled = -1;
if (enabled < 0) {
// vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE
if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL))
enabled = 1;
}
return enabled;
}
void set_deinterlacing(struct MPContext *mpctx, bool enable)
{
struct vo_chain *vo_c = mpctx->vo_chain;
if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL)) {
if (!enable) {
mpctx->opts->deinterlace = 0;
recreate_auto_filters(mpctx);
}
} else {
if ((get_deinterlacing(mpctx) > 0) != enable) {
int arg = enable;
if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK)
{
mpctx->opts->deinterlace = 1;
recreate_auto_filters(mpctx);
}
}
}
mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
}
static void recreate_video_filters(struct MPContext *mpctx)
@ -230,7 +308,7 @@ int reinit_video_filters(struct MPContext *mpctx)
recreate_video_filters(mpctx);
if (need_reconfig)
filter_reconfig(vo_c);
filter_reconfig(mpctx, vo_c);
mp_force_video_refresh(mpctx);
@ -314,7 +392,6 @@ void uninit_video_chain(struct MPContext *mpctx)
mpctx->video_status = STATUS_EOF;
remove_deint_filter(mpctx);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
}
@ -522,22 +599,6 @@ static int decode_image(struct MPContext *mpctx)
}
}
// Called after video reinit. This can be generally used to try to insert more
// filters using the filter chain edit functionality in command.c.
static void init_filter_params(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
// Note that the filter chain is already initialized. This code might
// recreate the chain a second time, which is not very elegant, but allows
// us to test whether enabling deinterlacing works with the current video
// format and other filters.
if (opts->deinterlace >= 0) {
remove_deint_filter(mpctx);
set_deinterlacing(mpctx, opts->deinterlace != 0);
}
}
// Feed newly decoded frames to the filter, take care of format changes.
// If eof=true, drain the filter chain, and return VD_EOF if empty.
static int video_filter(struct MPContext *mpctx, bool eof)
@ -564,7 +625,7 @@ static int video_filter(struct MPContext *mpctx, bool eof)
return VD_PROGRESS;
// The filter chain is drained; execute the filter format change.
filter_reconfig(mpctx->vo_chain);
filter_reconfig(mpctx, mpctx->vo_chain);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
@ -586,7 +647,6 @@ static int video_filter(struct MPContext *mpctx, bool eof)
MP_FATAL(mpctx, "Cannot initialize video filters.\n");
return VD_ERROR;
}
init_filter_params(mpctx);
return VD_RECONFIG;
}