Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions filters/f_auto_filters.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,15 @@ static void deint_process(struct mp_filter *f)
bool filter_needed = opts->deinterlace == 1 ||
(opts->deinterlace == -1 && (p->interlaced_frame || p->sub.filter));

// If the image format changed, or if we no longer need a filter,
// destroy any existing filter.
if (img->imgfmt != p->prev_imgfmt || (p->sub.filter && !filter_needed)) {
// If the image format changed, destroy any existing filter immediately since
// it may not support the new format. If we no longer need a filter, drain
// and destroy it gracefully.
if (img->imgfmt != p->prev_imgfmt) {
mp_subfilter_destroy(&p->sub);
p->prev_imgfmt = img->imgfmt;
} else if (p->sub.filter && !filter_needed) {
if (!mp_subfilter_drain_destroy(&p->sub))
return;
p->prev_imgfmt = img->imgfmt;
}

// If no filter is needed or if the filter is already inserted and we reach
Expand All @@ -92,6 +95,7 @@ static void deint_process(struct mp_filter *f)
field_parity = "auto";
}

struct mp_stream_info *info = mp_filter_find_stream_info(f);
bool has_filter = true;
if (img->imgfmt == IMGFMT_VDPAU) {
char *args[] = {"deint", "yes",
Expand Down Expand Up @@ -123,6 +127,11 @@ static void deint_process(struct mp_filter *f)
"parity", field_parity, NULL};
p->sub.filter =
mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "vavpp", args);
} else if (info && info->deinterlace && !IMGFMT_IS_HWACCEL(img->imgfmt)) {
char *args[] = {"interlaced-only", opts->deinterlace == 1 ? "no" : "yes",
"parity", field_parity, NULL};
p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO,
"fieldrate", args);
} else {
has_filter = false;
}
Expand Down
1 change: 1 addition & 0 deletions filters/f_output_chain.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ void mp_output_chain_set_vo(struct mp_output_chain *c, struct vo *vo)
p->stream_info.osd = vo ? vo->osd : NULL;
p->stream_info.vflip = vo ? vo->driver->caps & VO_CAP_VFLIP : false;
p->stream_info.rotate90 = vo ? vo->driver->caps & VO_CAP_ROTATE90 : false;
p->stream_info.deinterlace = vo ? vo->driver->caps & VO_CAP_DEINTERLACE : false;
p->stream_info.dr_vo = vo;
p->vo = vo;
update_output_caps(p);
Expand Down
1 change: 1 addition & 0 deletions filters/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ struct mp_stream_info {
bool vflip;
bool rotate90;
bool force_swdec;
bool deinterlace;
struct vo *dr_vo; // for calling vo_get_image()
};

Expand Down
1 change: 1 addition & 0 deletions filters/user_filters.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const struct mp_user_filter_entry *vf_list[] = {
#if (HAVE_GL && HAVE_EGL) || HAVE_VULKAN
&vf_gpu,
#endif
&vf_fieldrate,
};

static bool get_vf_desc(struct m_obj_desc *dst, int index)
Expand Down
1 change: 1 addition & 0 deletions filters/user_filters.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ extern const struct mp_user_filter_entry vf_d3d11vpp;
extern const struct mp_user_filter_entry vf_amf_frc;
extern const struct mp_user_filter_entry vf_fingerprint;
extern const struct mp_user_filter_entry vf_gpu;
extern const struct mp_user_filter_entry vf_fieldrate;
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ sources = files(
'video/filter/refqueue.c',
'video/filter/vf_format.c',
'video/filter/vf_sub.c',
'video/filter/vf_fieldrate.c',
'video/fmt-conversion.c',
'video/hwdec.c',
'video/image_loader.c',
Expand Down
107 changes: 107 additions & 0 deletions video/filter/vf_fieldrate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include "filters/filter_internal.h"
#include "filters/user_filters.h"
#include "refqueue.h"

struct opts {
int field_parity;
bool interlaced_only;
};

struct priv {
struct opts *opts;
struct mp_refqueue *queue;
};

static void vf_field_process(struct mp_filter *f)
{
struct priv *p = f->priv;
mp_refqueue_execute_reinit(p->queue);

if (!mp_refqueue_can_output(p->queue))
return;

struct mp_image *in = mp_refqueue_get(p->queue, 0);
struct mp_image *out = mp_image_new_ref(in);
if (!out) {
mp_refqueue_write_out_pin(p->queue, NULL);
return;
}
// This filter does not deinterlace. It only emits one output per field so
// the VO gets called at fieldrate cadence.
if (mp_refqueue_should_deint(p->queue)) {
out->field_tick = mp_refqueue_is_second_field(p->queue) ?
MP_FIELD_TICK_SECOND : MP_FIELD_TICK_FIRST;
out->fields |= MP_IMGFIELD_INTERLACED;
if (mp_refqueue_top_field_first(p->queue)) {
out->fields |= MP_IMGFIELD_TOP_FIRST;
} else {
out->fields &= ~MP_IMGFIELD_TOP_FIRST;
}
} else {
out->field_tick = MP_FIELD_TICK_NONE;
}
mp_refqueue_write_out_pin(p->queue, out);
}

static void vf_field_reset(struct mp_filter *f)
{
struct priv *p = f->priv;
mp_refqueue_flush(p->queue);
}

static void vf_field_destroy(struct mp_filter *f)
{
struct priv *p = f->priv;
mp_refqueue_flush(p->queue);
talloc_free(p->queue);
}

static const struct mp_filter_info vf_field_filter = {
.name = "fieldrate",
.process = vf_field_process,
.reset = vf_field_reset,
.destroy = vf_field_destroy,
.priv_size = sizeof(struct priv),
};

static struct mp_filter *vf_field_create(struct mp_filter *parent, void *options)
{
struct mp_filter *f = mp_filter_create(parent, &vf_field_filter);
if (!f)
return NULL;
struct priv *p = f->priv;
p->opts = talloc_steal(p, options);

mp_filter_add_pin(f, MP_PIN_IN, "in");
mp_filter_add_pin(f, MP_PIN_OUT, "out");

p->queue = mp_refqueue_alloc(f);

mp_refqueue_set_refs(p->queue, 0, 0);
mp_refqueue_set_mode(p->queue,
MP_MODE_DEINT |
MP_MODE_OUTPUT_FIELDS |
(p->opts->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0));
mp_refqueue_set_parity(p->queue, p->opts->field_parity);

return f;
}
#define OPT_BASE_STRUCT struct opts
static const m_option_t vf_opts_fields[] = {
{"interlaced-only", OPT_BOOL(interlaced_only)},
{"parity", OPT_CHOICE(field_parity,
{"tff", MP_FIELD_PARITY_TFF},
{"bff", MP_FIELD_PARITY_BFF},
{"auto", MP_FIELD_PARITY_AUTO})},
{0}
};

const struct mp_user_filter_entry vf_fieldrate = {
.desc = {
.description = "Emit one frame per field",
.name = "fieldrate",
.priv_size = sizeof(OPT_BASE_STRUCT),
.options = vf_opts_fields,
},
.create = vf_field_create,
};
8 changes: 8 additions & 0 deletions video/mp_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
#define MP_IMGFIELD_REPEAT_FIRST 0x04
#define MP_IMGFIELD_INTERLACED 0x20

enum mp_image_field_tick {
MP_FIELD_TICK_NONE = 0,
MP_FIELD_TICK_FIRST,
MP_FIELD_TICK_SECOND,
};

// Describes image parameters that usually stay constant.
// New fields can be added in the future. Code changing the parameters should
// usually copy the whole struct, so that fields added later will be preserved.
Expand Down Expand Up @@ -97,6 +103,8 @@ typedef struct mp_image {
int pict_type; // 0->unknown, 1->I, 2->P, 3->B
int fields;

enum mp_image_field_tick field_tick;

/* only inside filter chain */
double pts;
/* only after decoder */
Expand Down
2 changes: 2 additions & 0 deletions video/out/vo.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ enum {
VO_CAP_FRAMEOWNER = 1 << 5,
// VO does handle mp_image_params.vflip
VO_CAP_VFLIP = 1 << 6,
// VO supports deinterlacing
VO_CAP_DEINTERLACE = 1 << 7,
};

enum {
Expand Down
26 changes: 25 additions & 1 deletion video/out/vo_gpu_next.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct priv {
pl_options pars;
struct m_config_cache *opts_cache;
struct m_config_cache *next_opts_cache;
struct m_config_cache *filter_opts_cache;
struct gl_next_opts *next_opts;
struct cache shader_cache, icc_cache;
struct mp_csp_equalizer_state *video_eq;
Expand Down Expand Up @@ -890,6 +891,7 @@ static void update_options(struct vo *vo)
pl_options pars = p->pars;
bool changed = m_config_cache_update(p->opts_cache);
changed = m_config_cache_update(p->next_opts_cache) || changed;
changed = m_config_cache_update(p->filter_opts_cache) || changed;
if (changed)
update_render_options(vo);

Expand Down Expand Up @@ -1143,13 +1145,29 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
mpi->priv = fp;
fp->vo = vo;

bool use_fields = params.deinterlace_params &&
mpi->fields & MP_IMGFIELD_INTERLACED &&
!IMGFMT_IS_HWACCEL(mpi->imgfmt);
// vf_fieldrate emits a frame for each field only to make mpv render
// at the second field PTS. But don't actually push it to pl_queue,
// because it already derives it internally from first_field.
if (use_fields && mpi->field_tick == MP_FIELD_TICK_SECOND) {
talloc_free(mpi);
p->last_id = id;
continue;
}
int first_field = !use_fields ? PL_FIELD_NONE :
(mpi->fields & MP_IMGFIELD_TOP_FIRST) ? PL_FIELD_TOP : PL_FIELD_BOTTOM;

pl_queue_push(p->queue, &(struct pl_source_frame) {
.pts = mpi->pts,
.duration = can_interpolate ? frame->approx_duration : 0,
.duration = can_interpolate ? frame->approx_duration :
use_fields ? mpi->pkt_duration : 0,
.frame_data = mpi,
.map = map_frame,
.unmap = unmap_frame,
.discard = discard_frame,
.first_field = first_field,
});

p->last_id = id;
Expand Down Expand Up @@ -2229,6 +2247,7 @@ static int preinit(struct vo *vo)
{
struct priv *p = vo->priv;
p->opts_cache = m_config_cache_alloc(p, vo->global, &gl_video_conf);
p->filter_opts_cache = m_config_cache_alloc(p, vo->global, &filter_conf);
p->next_opts_cache = m_config_cache_alloc(p, vo->global, &gl_next_conf);
p->next_opts = p->next_opts_cache->opts;
p->video_eq = mp_csp_equalizer_create(p, vo->global);
Expand Down Expand Up @@ -2559,6 +2578,7 @@ static void update_render_options(struct vo *vo)
struct priv *p = vo->priv;
pl_options pars = p->pars;
const struct gl_video_opts *opts = p->opts_cache->opts;
const struct filter_opts *fopts = p->filter_opts_cache->opts;
pars->params.background_color[0] = opts->background_color.r / 255.0;
pars->params.background_color[1] = opts->background_color.g / 255.0;
pars->params.background_color[2] = opts->background_color.b / 255.0;
Expand Down Expand Up @@ -2592,6 +2612,9 @@ static void update_render_options(struct vo *vo)
pars->params.plane_upscaler = map_scaler(p, SCALER_CSCALE);
pars->params.frame_mixer = opts->interpolation ? map_scaler(p, SCALER_TSCALE) : NULL;

pars->params.deinterlace_params = fopts->deinterlace != 0 ? &pars->deinterlace_params : NULL;
pars->deinterlace_params.algo = PL_DEINTERLACE_BWDIF;

// Request as many frames as required from the decoder, depending on the
// speed VPS/FPS ratio libplacebo may need more frames. Request frames up to
// ratio of 1/2, but only if anti aliasing is enabled.
Expand Down Expand Up @@ -2709,6 +2732,7 @@ const struct vo_driver video_out_gpu_next = {
.caps = VO_CAP_ROTATE90 |
VO_CAP_FILM_GRAIN |
VO_CAP_VFLIP |
VO_CAP_DEINTERLACE |
0x0,
.preinit = preinit,
.query_format = query_format,
Expand Down
Loading