summaryrefslogtreecommitdiffstats
path: root/video/filter/vf.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/filter/vf.c')
-rw-r--r--video/filter/vf.c250
1 files changed, 128 insertions, 122 deletions
diff --git a/video/filter/vf.c b/video/filter/vf.c
index a548bafbbb..1345826e57 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -116,10 +116,6 @@ static const vf_info_t *const filter_list[] = {
NULL
};
-static void vf_uninit_filter(vf_instance_t *vf);
-static int vf_reconfig_wrapper(struct vf_instance *vf,
- const struct mp_image_params *params, int flags);
-
static bool get_desc(struct m_obj_desc *dst, int index)
{
if (index >= MP_ARRAY_SIZE(filter_list) - 1)
@@ -194,19 +190,12 @@ void vf_make_out_image_writeable(struct vf_instance *vf, struct mp_image *img)
//============================================================================
+// The default callback assumes all formats are passed through.
static int vf_default_query_format(struct vf_instance *vf, unsigned int fmt)
{
return vf_next_query_format(vf, fmt);
}
-
-static struct mp_image *vf_default_filter(struct vf_instance *vf,
- struct mp_image *mpi)
-{
- assert(!vf->filter_ext);
- return mpi;
-}
-
static void print_fmt(int msglevel, struct mp_image_params *p)
{
if (p && p->imgfmt) {
@@ -228,11 +217,7 @@ void vf_print_filter_chain(struct vf_chain *c, int msglevel)
for (vf_instance_t *f = c->first; f; f = f->next) {
mp_msg(MSGT_VFILTER, msglevel, " [%s] ", f->info->name);
- print_fmt(msglevel, &f->fmt_in);
- if (f->next) {
- mp_msg(MSGT_VFILTER, msglevel, " -> ");
- print_fmt(msglevel, &f->fmt_out);
- }
+ print_fmt(msglevel, &f->fmt_out);
mp_msg(MSGT_VFILTER, msglevel, "\n");
}
}
@@ -251,11 +236,8 @@ static struct vf_instance *vf_open(struct vf_chain *c, const char *name,
.info = desc.p,
.opts = c->opts,
.hwdec = c->hwdec,
- .config = vf_next_config,
.query_format = vf_default_query_format,
- .filter = vf_default_filter,
.out_pool = talloc_steal(vf, mp_image_pool_new(16)),
- .chain = c,
};
struct m_config *config = m_config_from_obj_desc(vf, &desc);
if (m_config_apply_defaults(config, name, c->opts->vf_defs) < 0)
@@ -299,7 +281,8 @@ struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name,
struct vf_instance *vf = vf_open_filter(c, name, args);
if (vf) {
// Insert it before the last filter, which is the "vo" filter
- struct vf_instance **pprev = &c->first;
+ // (But after the "in" pseudo-filter)
+ struct vf_instance **pprev = &c->first->next;
while (*pprev && (*pprev)->next)
pprev = &(*pprev)->next;
vf->next = *pprev ? *pprev : NULL;
@@ -351,7 +334,9 @@ static int vf_do_filter(struct vf_instance *vf, struct mp_image *img)
if (vf->filter_ext) {
return vf->filter_ext(vf, img);
} else {
- vf_add_output_frame(vf, vf->filter(vf, img));
+ if (vf->filter)
+ img = vf->filter(vf, img);
+ vf_add_output_frame(vf, img);
return 0;
}
}
@@ -360,17 +345,18 @@ static int vf_do_filter(struct vf_instance *vf, struct mp_image *img)
// Return >= 0 on success, < 0 on failure (even if output frames were produced)
int vf_filter_frame(struct vf_chain *c, struct mp_image *img)
{
- if (c->first) {
- return vf_do_filter(c->first, img);
- } else {
+ if (c->initialized < 1) {
talloc_free(img);
- return 0;
+ return -1;
}
+ return vf_do_filter(c->first, img);
}
// Output the next queued image (if any) from the full filter chain.
struct mp_image *vf_output_queued_frame(struct vf_chain *c)
{
+ if (c->initialized < 1)
+ return NULL;
while (1) {
struct vf_instance *last = NULL;
for (struct vf_instance * cur = c->first; cur; cur = cur->next) {
@@ -395,8 +381,6 @@ static void vf_forget_frames(struct vf_instance *vf)
void vf_seek_reset(struct vf_chain *c)
{
- if (!c->first)
- return;
for (struct vf_instance *cur = c->first; cur; cur = cur->next) {
if (cur->control)
cur->control(cur, VFCTRL_SEEK_RESET, NULL);
@@ -404,62 +388,11 @@ void vf_seek_reset(struct vf_chain *c)
}
}
-static int vf_reconfig_wrapper(struct vf_instance *vf, const struct mp_image_params *p,
- int flags)
-{
- vf_forget_frames(vf);
- mp_image_pool_clear(vf->out_pool);
-
- vf->fmt_in = *p;
- vf->fmt_out = (struct mp_image_params){0};
-
- int r;
- if (vf->reconfig) {
- struct mp_image_params params = *p;
- r = vf->reconfig(vf, &params, flags);
- } else {
- r = vf->config(vf, p->w, p->h, p->d_w, p->d_h, flags, p->imgfmt);
- r = r ? 0 : -1;
- }
- if (r >= 0) {
- if (vf->next)
- vf->fmt_out = vf->next->fmt_in;
- } else {
- vf->fmt_in = (struct mp_image_params){0};
- }
- return r;
-}
-
-int vf_next_reconfig(struct vf_instance *vf, struct mp_image_params *p,
- int outflags)
-{
- int flags = vf->next->query_format(vf->next, p->imgfmt);
- if (!flags) {
- // hmm. colorspace mismatch!!!
- // let's insert the 'scale' filter, it does the job for us:
- vf_instance_t *vf2;
- if (vf->next->info == &vf_info_scale)
- return -1; // scale->scale
- vf2 = vf_open_filter(vf->chain, "scale", NULL);
- if (!vf2)
- return -1; // shouldn't happen!
- vf2->next = vf->next;
- vf->next = vf2;
- flags = vf->next->query_format(vf->next, p->imgfmt);
- if (!flags) {
- mp_tmsg(MSGT_VFILTER, MSGL_ERR, "Cannot find matching colorspace, "
- "even by inserting 'scale' :(\n");
- return -1; // FAIL
- }
- }
- return vf_reconfig_wrapper(vf->next, p, outflags);
-}
-
int vf_next_config(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
unsigned int voflags, unsigned int outfmt)
{
- struct mp_image_params p = {
+ vf->fmt_out = (struct mp_image_params) {
.imgfmt = outfmt,
.w = width,
.h = height,
@@ -470,58 +403,117 @@ int vf_next_config(struct vf_instance *vf,
.chroma_location = vf->fmt_in.chroma_location,
.outputlevels = vf->fmt_in.outputlevels,
};
- // Fix csp in case of pixel format change
- mp_image_params_guess_csp(&p);
- int r = vf_reconfig_wrapper(vf->next, &p, voflags);
- return r < 0 ? 0 : 1;
+ return 1;
}
int vf_next_query_format(struct vf_instance *vf, unsigned int fmt)
{
- return vf->next->query_format(vf->next, fmt);
+ return fmt >= IMGFMT_START && fmt < IMGFMT_END
+ ? vf->last_outfmts[fmt - IMGFMT_START] : 0;
}
-//============================================================================
+// Mark accepted input formats in fmts[]. Note that ->query_format will
+// typically (but not always) call vf_next_query_format() to check whether
+// an output format is supported.
+static void query_formats(uint8_t *fmts, struct vf_instance *vf)
+{
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++)
+ fmts[n - IMGFMT_START] = vf->query_format(vf, n);
+}
-int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params)
+static bool is_conv_filter(struct vf_instance *vf)
{
- // check if libvo and codec has common outfmt (no conversion):
- struct vf_instance *vf = c->first;
- int flags = 0;
- for (;;) {
- mp_msg(MSGT_VFILTER, MSGL_V, "Trying filter chain:\n");
- vf_print_filter_chain(c, MSGL_V);
-
- flags = vf->query_format(vf, params->imgfmt);
- mp_msg(MSGT_CPLAYER, MSGL_DBG2, "vo_debug: query(%s) returned 0x%X \n",
- vo_format_name(params->imgfmt), flags);
- if ((flags & VFCAP_CSP_SUPPORTED_BY_HW)
- || (flags & VFCAP_CSP_SUPPORTED))
- {
- break;
- }
- // TODO: no match - we should use conversion...
- if (strcmp(vf->info->name, "scale")) {
- mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Could not find matching colorspace - retrying with -vf scale...\n");
- vf = vf_open_filter(c, "scale", NULL);
- vf->next = c->first;
- c->first = vf;
- continue;
+ return vf && strcmp(vf->info->name, "scale") == 0;
+}
+
+static void update_formats(struct vf_chain *c, struct vf_instance *vf,
+ uint8_t *fmts)
+{
+ if (vf->next)
+ update_formats(c, vf->next, vf->last_outfmts);
+ query_formats(fmts, vf);
+ bool has_in = false, has_out = false;
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
+ has_in |= !!fmts[n - IMGFMT_START];
+ has_out |= !!vf->last_outfmts[n - IMGFMT_START];
+ }
+ if (has_out && !has_in && !is_conv_filter(vf) &&
+ !is_conv_filter(vf->next))
+ {
+ // If there are output formats, but no input formats (meaning the
+ // filters after vf work, but vf can't output any format the filters
+ // after it accept), try to insert a conversion filter.
+ mp_tmsg(MSGT_VFILTER, MSGL_INFO, "Using conversion filter.\n");
+ struct vf_instance *conv = vf_open(c, "scale", NULL);
+ if (conv) {
+ conv->next = vf->next;
+ vf->next = conv;
+ update_formats(c, conv, vf->last_outfmts);
+ query_formats(fmts, vf);
}
- mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
- "The selected video_out device is incompatible with this codec.\n"\
- "Try appending the scale filter to your filter list,\n"\
- "e.g. -vf filter,scale instead of -vf filter.\n");
- mp_tmsg(MSGT_VFILTER, MSGL_WARN, "Attempted filter chain:\n");
- vf_print_filter_chain(c, MSGL_WARN);
- c->initialized = -1;
- return -1; // failed
}
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++)
+ has_in |= !!fmts[n - IMGFMT_START];
+ if (!has_in) {
+ // Pretend all out formats work. All this does it getting better
+ // error messages in some cases, so we can configure all filter
+ // until it fails, which will be visible in vf_print_filter_chain().
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++)
+ vf->last_outfmts[n - IMGFMT_START] = VFCAP_CSP_SUPPORTED;
+ query_formats(fmts, vf);
+ }
+}
+
+static int vf_reconfig_wrapper(struct vf_instance *vf,
+ const struct mp_image_params *p)
+{
+ vf_forget_frames(vf);
+ if (vf->out_pool)
+ mp_image_pool_clear(vf->out_pool);
+
+ if (!vf->query_format(vf, p->imgfmt))
+ return -2;
+
+ vf->fmt_out = vf->fmt_in = *p;
+
+ int r;
+ if (vf->reconfig) {
+ r = vf->reconfig(vf, &vf->fmt_in, &vf->fmt_out);
+ } else if (vf->config) {
+ r = vf->config(vf, p->w, p->h, p->d_w, p->d_h, 0, p->imgfmt) ? 0 : -1;
+ } else {
+ r = 0;
+ }
+
+ if (!mp_image_params_equals(&vf->fmt_in, p))
+ r = -2;
+
+ // Fix csp in case of pixel format change
+ if (r >= 0)
+ mp_image_params_guess_csp(&vf->fmt_out);
- int r = vf_reconfig_wrapper(c->first, params, 0);
- c->initialized = r >= 0 ? 1 : -1;
- mp_tmsg(MSGT_VFILTER, MSGL_V, "Video filter chain:\n");
- vf_print_filter_chain(c, MSGL_V);
+ return r;
+}
+
+int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params)
+{
+ struct mp_image_params cur = *params;
+ int r = 0;
+ c->first->fmt_in = *params;
+ uint8_t unused[IMGFMT_END - IMGFMT_START];
+ update_formats(c, c->first, unused);
+ for (struct vf_instance *vf = c->first; vf; vf = vf->next) {
+ r = vf_reconfig_wrapper(vf, &cur);
+ if (r < 0)
+ break;
+ cur = vf->fmt_out;
+ }
+ c->initialized = r < 0 ? -1 : 1;
+ int loglevel = r < 0 ? MSGL_WARN : MSGL_V;
+ if (r == -2)
+ mp_tmsg(MSGT_VFILTER, MSGL_ERR, "Image formats incompatible.\n");
+ mp_tmsg(MSGT_VFILTER, loglevel, "Video filter chain:\n");
+ vf_print_filter_chain(c, loglevel);
return r;
}
@@ -536,7 +528,7 @@ struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label)
return NULL;
}
-void vf_uninit_filter(vf_instance_t *vf)
+static void vf_uninit_filter(vf_instance_t *vf)
{
if (vf->uninit)
vf->uninit(vf);
@@ -544,12 +536,26 @@ void vf_uninit_filter(vf_instance_t *vf)
talloc_free(vf);
}
+static int input_query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ // Setting fmt_in is guaranteed by vf_reconfig().
+ if (fmt == vf->fmt_in.imgfmt)
+ return vf_next_query_format(vf, fmt);
+ return 0;
+}
+
struct vf_chain *vf_new(struct MPOpts *opts)
{
struct vf_chain *c = talloc_ptrtype(NULL, c);
*c = (struct vf_chain){
.opts = opts,
};
+ static const struct vf_info in = { .name = "in" };
+ c->first = talloc(c, struct vf_instance);
+ *c->first = (struct vf_instance) {
+ .info = &in,
+ .query_format = input_query_format,
+ };
return c;
}