From d658b115a0fcb9b313b2eb77ed860649f83257b0 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 7 Dec 2013 19:35:55 +0100 Subject: vf: redo conversion filter insertion/format negotiation Remove the inconsistent, duplicated, and insufficient scale filter insertion code, and do it in one place instead. This also compensates for the earlier removal of vf_match_csp() (which was in fact duplicated code). The algorithm to determine where to insert a filter etc. is probably the same, though it also comes with some changes that should make debugging easier when trying to figure out why a chain is failing to configure. Add an "in" pseudo filter, which makes insertion of conversion filters easier. Also change the vf->reconfig signature. At a later point, I'll probably change format negotiation such that the generic filter code will choose the output format, so having separate in and out params will be useful. --- video/filter/vf.c | 250 ++++++++++++++++++++++++----------------------- video/filter/vf.h | 18 ++-- video/filter/vf_rotate.c | 28 +++--- video/filter/vf_scale.c | 42 ++++---- video/filter/vf_vavpp.c | 11 ++- video/filter/vf_vo.c | 13 ++- 6 files changed, 187 insertions(+), 175 deletions(-) (limited to 'video') 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, ¶ms, 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; } diff --git a/video/filter/vf.h b/video/filter/vf.h index ebe7637c0e..a852a245f3 100644 --- a/video/filter/vf.h +++ b/video/filter/vf.h @@ -44,9 +44,14 @@ typedef struct vf_info { typedef struct vf_instance { const vf_info_t *info; - // Note: the callee is allowed to write *params. - int (*reconfig)(struct vf_instance *vf, struct mp_image_params *params, - int flags); + // Initialize the filter. The filter must set *out to the same image + // params as the images the filter functions will return for the given + // *in format. + // Note that by default, only formats reported as supported by query_format + // will be allowed for *in. + // Returns >= 0 on success, < 0 on error. + int (*reconfig)(struct vf_instance *vf, struct mp_image_params *in, + struct mp_image_params *out); // Legacy variant, use reconfig instead. int (*config)(struct vf_instance *vf, @@ -80,8 +85,9 @@ typedef struct vf_instance { struct mp_image **out_queued; int num_out_queued; - // Temporary - struct vf_chain *chain; + // Caches valid output formats. + uint8_t last_outfmts[IMGFMT_END - IMGFMT_START]; + struct vf_instance *next; } vf_instance_t; @@ -138,8 +144,6 @@ int vf_next_config(struct vf_instance *vf, unsigned int flags, unsigned int outfmt); int vf_next_query_format(struct vf_instance *vf, unsigned int fmt); -int vf_next_reconfig(struct vf_instance *vf, struct mp_image_params *params, - int flags); // Helpers diff --git a/video/filter/vf_rotate.c b/video/filter/vf_rotate.c index 0a23071cdc..0105cb2c1d 100644 --- a/video/filter/vf_rotate.c +++ b/video/filter/vf_rotate.c @@ -66,24 +66,26 @@ static void rotate(unsigned char* dst,unsigned char* src,int dststride,int srcst } } -static int reconfig(struct vf_instance *vf, struct mp_image_params *p, int flags) +static int reconfig(struct vf_instance *vf, struct mp_image_params *in, + struct mp_image_params *out) { + *out = *in; if (vf->priv->direction & 4) { - if (p->w < p->h) + if (in->w < in->h) vf->priv->direction &= 3; } if (vf->priv->direction & 4) - return vf_next_reconfig(vf, p, flags); - struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p->imgfmt); - int a_w = MP_ALIGN_DOWN(p->w, desc.align_x); - int a_h = MP_ALIGN_DOWN(p->h, desc.align_y); - vf_rescale_dsize(&p->d_w, &p->d_h, p->w, p->h, a_w, a_h); - p->w = a_h; - p->h = a_w; - int t = p->d_w; - p->d_w = p->d_h; - p->d_h = t; - return vf_next_reconfig(vf, p, flags); + return 0; + struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(in->imgfmt); + int a_w = MP_ALIGN_DOWN(in->w, desc.align_x); + int a_h = MP_ALIGN_DOWN(in->h, desc.align_y); + vf_rescale_dsize(&out->d_w, &out->d_h, in->w, in->h, a_w, a_h); + out->w = a_h; + out->h = a_w; + int t = out->d_w; + out->d_w = out->d_h; + out->d_h = t; + return 0; } static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) diff --git a/video/filter/vf_scale.c b/video/filter/vf_scale.c index c466c5d39a..268e65ae59 100644 --- a/video/filter/vf_scale.c +++ b/video/filter/vf_scale.c @@ -217,13 +217,13 @@ static unsigned int find_best_out(vf_instance_t *vf, int in_format) return best; } -static int reconfig(struct vf_instance *vf, struct mp_image_params *p, int flags) +static int reconfig(struct vf_instance *vf, struct mp_image_params *in, + struct mp_image_params *out) { - int width = p->w, height = p->h, d_width = p->d_w, d_height = p->d_h; - unsigned int outfmt = p->imgfmt; + int width = in->w, height = in->h, d_width = in->d_w, d_height = in->d_h; + unsigned int outfmt = in->imgfmt; unsigned int best = find_best_out(vf, outfmt); int round_w = 0, round_h = 0; - struct mp_image_params input = *p; if (!best) { mp_msg(MSGT_VFILTER, MSGL_WARN, @@ -302,30 +302,30 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *p, int flags d_height = vf->priv->w * d_height / d_width; d_width = vf->priv->w; } - //d_width=d_width*vf->priv->w/width; - //d_height=d_height*vf->priv->h/height; - p->w = vf->priv->w; - p->h = vf->priv->h; - p->d_w = d_width; - p->d_h = d_height; - p->imgfmt = best; + + *out = *in; + out->w = vf->priv->w; + out->h = vf->priv->h; + out->d_w = d_width; + out->d_h = d_height; + out->imgfmt = best; // Second-guess what libswscale is going to output and what not. // It depends what libswscale supports for in/output, and what makes sense. - struct mp_imgfmt_desc s_fmt = mp_imgfmt_get_desc(input.imgfmt); - struct mp_imgfmt_desc d_fmt = mp_imgfmt_get_desc(p->imgfmt); + struct mp_imgfmt_desc s_fmt = mp_imgfmt_get_desc(in->imgfmt); + struct mp_imgfmt_desc d_fmt = mp_imgfmt_get_desc(out->imgfmt); // keep colorspace settings if the data stays in yuv if (!(s_fmt.flags & MP_IMGFLAG_YUV) || !(d_fmt.flags & MP_IMGFLAG_YUV)) { - p->colorspace = MP_CSP_AUTO; - p->colorlevels = MP_CSP_LEVELS_AUTO; + out->colorspace = MP_CSP_AUTO; + out->colorlevels = MP_CSP_LEVELS_AUTO; } - mp_image_params_guess_csp(p); + mp_image_params_guess_csp(out); mp_sws_set_from_cmdline(vf->priv->sws); vf->priv->sws->flags |= vf->priv->v_chr_drop << SWS_SRC_V_CHR_DROP_SHIFT; vf->priv->sws->flags |= vf->priv->accurate_rnd * SWS_ACCURATE_RND; - vf->priv->sws->src = input; - vf->priv->sws->dst = *p; + vf->priv->sws->src = *in; + vf->priv->sws->dst = *out; if (mp_sws_reinit(vf->priv->sws) < 0) { // error... @@ -333,11 +333,7 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *p, int flags "Couldn't init libswscale for this setup\n"); return -1; } - - // In particular, fix up colorspace/levels if YUV<->RGB conversion is - // performed. - - return vf_next_reconfig(vf, p, flags); + return 0; } static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c index 94666c13bd..dfd99e8a3c 100644 --- a/video/filter/vf_vavpp.c +++ b/video/filter/vf_vavpp.c @@ -240,15 +240,16 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *in) return 0; } -static int reconfig(struct vf_instance *vf, struct mp_image_params *params, - int flags) +static int reconfig(struct vf_instance *vf, struct mp_image_params *in, + struct mp_image_params *out) { struct vf_priv_s *p = vf->priv; p->prev_pts = MP_NOPTS_VALUE; - p->params = *params; - params->imgfmt = IMGFMT_VAAPI; - return vf_next_reconfig(vf, params, flags); + p->params = *in; + *out = *in; + out->imgfmt = IMGFMT_VAAPI; + return 0; } static void uninit(struct vf_instance *vf) diff --git a/video/filter/vf_vo.c b/video/filter/vf_vo.c index 87cdb781f7..3655b72141 100644 --- a/video/filter/vf_vo.c +++ b/video/filter/vf_vo.c @@ -35,25 +35,28 @@ struct vf_priv_s { }; #define video_out (vf->priv->vo) -static int reconfig(struct vf_instance *vf, struct mp_image_params *p, int flags) +static int reconfig(struct vf_instance *vf, struct mp_image_params *in, + struct mp_image_params *out) { if (!video_out) return -1; + struct mp_image_params *p = in; + *out = *in; + if (p->w <= 0 || p->h <= 0 || p->d_w <= 0 || p->d_h <= 0) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "VO: invalid dimensions!\n"); return -1; } const struct vo_driver *info = video_out->driver; - mp_msg(MSGT_CPLAYER, MSGL_INFO, "VO: [%s] %dx%d => %dx%d %s %s\n", + mp_msg(MSGT_CPLAYER, MSGL_INFO, "VO: [%s] %dx%d => %dx%d %s\n", info->name, p->w, p->h, p->d_w, p->d_h, - vo_format_name(p->imgfmt), - (flags & VOFLAG_FLIPPING) ? " [flip]" : ""); + vo_format_name(p->imgfmt)); mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Description: %s\n", info->description); - return vo_reconfig(video_out, p, flags); + return vo_reconfig(video_out, p, 0); } static int control(struct vf_instance *vf, int request, void *data) -- cgit v1.2.3