From d855be5ec1615e77a48ee56f987147890459de45 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 21 Jan 2015 18:33:47 +0100 Subject: vf_scale: replace ancient fallback image format selection If video output and VO don't support the same format, a conversion filter needs to be insert. Since a VO can support multiple formats, and the filter chain also can deal with multiple formats, you basically have to pick from a huge matrix of possible conversions. The old MPlayer code had a quite naive algorithm: it first checked whether any conversion from the list of preferred conversions matched, and if not, it was falling back on checking a hardcoded list of output formats (more or less sorted by quality). This had some unintended side- effects, like not using obvious "replacement" formats, selecting the wrong colorspace, selecting a bit depth that is too high or too low, and more. Use avcodec_find_best_pix_fmt_of_list() provided by FFmpeg instead. This function was made for this purpose, and should select the "best" format. Libav provides a similar function, but with a different name - there is a function with the same name in FFmpeg, but it has different semantics (I'm not sure if Libav or FFmpeg fucked up here). This also removes handling of VFCAP_CSP_SUPPORTED vs. VFCAP_CSP_SUPPORTED_BY_HW, which has no meaning anymore, except possibly for filter chains with multiple scale filters. Fixes #1494. --- video/filter/vf_scale.c | 157 +++++------------------------------------------- video/img_format.c | 18 ++++++ video/img_format.h | 2 + 3 files changed, 36 insertions(+), 141 deletions(-) diff --git a/video/filter/vf_scale.c b/video/filter/vf_scale.c index 42c8c23df7..d9506f31bc 100644 --- a/video/filter/vf_scale.c +++ b/video/filter/vf_scale.c @@ -55,144 +55,20 @@ static struct vf_priv_s { {SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT}, }; -//===========================================================================// - -static const unsigned int outfmt_list[] = { -// YUV: - IMGFMT_444P, - IMGFMT_444P16, - IMGFMT_444P14, - IMGFMT_444P12, - IMGFMT_444P10, - IMGFMT_444P9, - IMGFMT_422P, - IMGFMT_422P16, - IMGFMT_422P14, - IMGFMT_422P12, - IMGFMT_422P10, - IMGFMT_422P9, - IMGFMT_420P, - IMGFMT_420P16, - IMGFMT_420P14, - IMGFMT_420P12, - IMGFMT_420P10, - IMGFMT_420P9, - IMGFMT_420AP, - IMGFMT_410P, - IMGFMT_411P, - IMGFMT_NV12, - IMGFMT_NV21, - IMGFMT_YUYV, - IMGFMT_UYVY, - IMGFMT_440P, -// RGB and grayscale (Y8 and Y800): - IMGFMT_BGR32, - IMGFMT_RGB32, - IMGFMT_ABGR, - IMGFMT_ARGB, - IMGFMT_BGRA, - IMGFMT_RGBA, - IMGFMT_BGR24, - IMGFMT_RGB24, - IMGFMT_GBRP, - IMGFMT_RGB48, - IMGFMT_BGR565, - IMGFMT_RGB565, - IMGFMT_BGR555, - IMGFMT_RGB555, - IMGFMT_BGR444, - IMGFMT_RGB444, - IMGFMT_Y8, - IMGFMT_BGR8, - IMGFMT_RGB8, - IMGFMT_BGR4, - IMGFMT_RGB4, - IMGFMT_RGB4_BYTE, - IMGFMT_BGR4_BYTE, - IMGFMT_MONO, - IMGFMT_MONO_W, - 0 -}; - -/** - * A list of preferred conversions, in order of preference. - * This should be used for conversions that e.g. involve no scaling - * or to stop vf_scale from choosing a conversion that has no - * fast assembler implementation. - */ -static const int preferred_conversions[][2] = { - {IMGFMT_YUYV, IMGFMT_UYVY}, - {IMGFMT_YUYV, IMGFMT_422P}, - {IMGFMT_UYVY, IMGFMT_YUYV}, - {IMGFMT_UYVY, IMGFMT_422P}, - {IMGFMT_422P, IMGFMT_YUYV}, - {IMGFMT_422P, IMGFMT_UYVY}, - {IMGFMT_420P10, IMGFMT_420P}, - {IMGFMT_GBRP, IMGFMT_BGR24}, - {IMGFMT_GBRP, IMGFMT_RGB24}, - {IMGFMT_GBRP, IMGFMT_BGR32}, - {IMGFMT_GBRP, IMGFMT_RGB32}, - {IMGFMT_PAL8, IMGFMT_BGR32}, - {IMGFMT_XYZ12, IMGFMT_RGB48}, - {0, 0} -}; - -static int check_outfmt(vf_instance_t *vf, int outfmt) -{ - enum AVPixelFormat pixfmt = imgfmt2pixfmt(outfmt); - if (pixfmt == AV_PIX_FMT_NONE || sws_isSupportedOutput(pixfmt) < 1) - return 0; - return vf_next_query_format(vf, outfmt); -} - -static unsigned int find_best_out(vf_instance_t *vf, int in_format) +static int find_best_out(vf_instance_t *vf, int in_format) { - unsigned int best = 0; - int i = -1; - int j = -1; - int format = 0; - - // find the best outfmt: - while (1) { - int ret; - if (j < 0) { - format = in_format; - j = 0; - } else if (i < 0) { - while (preferred_conversions[j][0] && - preferred_conversions[j][0] != in_format) - j++; - format = preferred_conversions[j++][1]; - // switch to standard list - if (!format) - i = 0; - } - if (i >= 0) - format = outfmt_list[i++]; - if (!format) - break; - - ret = check_outfmt(vf, format); - - MP_DBG(vf, "scale: query(%s) -> %d\n", vo_format_name(format), ret & 3); - if (ret & VFCAP_CSP_SUPPORTED_BY_HW) { - best = format; // no conversion -> bingo! - break; - } - if (ret & VFCAP_CSP_SUPPORTED && !best) - best = format; // best with conversion - } - if (!best) { - // Try anything else. outfmt_list is just a list of preferred formats. - for (int cur = IMGFMT_START; cur < IMGFMT_END; cur++) { - int ret = check_outfmt(vf, cur); - - if (ret & VFCAP_CSP_SUPPORTED_BY_HW) { - best = cur; // no conversion -> bingo! - break; - } - if (ret & VFCAP_CSP_SUPPORTED && !best) - best = cur; // best with conversion + int best = 0; + for (int out_format = IMGFMT_START; out_format < IMGFMT_END; out_format++) { + if (!vf_next_query_format(vf, out_format)) + continue; + if (sws_isSupportedOutput(imgfmt2pixfmt(out_format)) < 1) + continue; + if (best) { + int candidate = mp_imgfmt_select_best(best, out_format, in_format); + if (candidate) + best = candidate; + } else { + best = out_format; } } return best; @@ -202,8 +78,7 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in, struct mp_image_params *out) { 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); + unsigned int best = find_best_out(vf, in->imgfmt); int round_w = 0, round_h = 0; if (!best) { @@ -269,7 +144,7 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in, } MP_DBG(vf, "SwScale: scaling %dx%d %s to %dx%d %s \n", - width, height, vo_format_name(outfmt), vf->priv->w, vf->priv->h, + width, height, vo_format_name(in->imgfmt), vf->priv->w, vf->priv->h, vo_format_name(best)); // Compute new d_width and d_height, preserving aspect @@ -352,7 +227,7 @@ static int query_format(struct vf_instance *vf, unsigned int fmt) if (!IMGFMT_IS_HWACCEL(fmt) && imgfmt2pixfmt(fmt) != AV_PIX_FMT_NONE) { if (sws_isSupportedInput(imgfmt2pixfmt(fmt)) < 1) return 0; - unsigned int best = find_best_out(vf, fmt); + int best = find_best_out(vf, fmt); int flags; if (!best) return 0; // no matching out-fmt diff --git a/video/img_format.c b/video/img_format.c index 7df0923cfe..5b8e2bb089 100644 --- a/video/img_format.c +++ b/video/img_format.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -261,6 +262,23 @@ int mp_imgfmt_find_yuv_planar(int xs, int ys, int planes, int component_bits) return 0; } +#if LIBAVUTIL_VERSION_MICRO < 100 +#define avcodec_find_best_pix_fmt_of_list avcodec_find_best_pix_fmt2 +#endif + +// Compare the dst image formats, and return the one which can carry more data +// (e.g. higher depth, more color components, lower chroma subsampling, etc.), +// with respect to what is required to keep most of the src format. +// Returns the imgfmt, or 0 on error. +int mp_imgfmt_select_best(int dst1, int dst2, int src) +{ + enum AVPixelFormat dst1pxf = imgfmt2pixfmt(dst1); + enum AVPixelFormat dst2pxf = imgfmt2pixfmt(dst2); + enum AVPixelFormat srcpxf = imgfmt2pixfmt(src); + enum AVPixelFormat dstlist[] = {dst1pxf, dst2pxf, AV_PIX_FMT_NONE}; + return pixfmt2imgfmt(avcodec_find_best_pix_fmt_of_list(dstlist, srcpxf, 0, 0)); +} + #if 0 #include diff --git a/video/img_format.h b/video/img_format.h index d9da81f2f9..b93e0fe974 100644 --- a/video/img_format.h +++ b/video/img_format.h @@ -239,4 +239,6 @@ char **mp_imgfmt_name_list(void); int mp_imgfmt_find_yuv_planar(int xs, int ys, int planes, int component_bits); +int mp_imgfmt_select_best(int dst1, int dst2, int src); + #endif /* MPLAYER_IMG_FORMAT_H */ -- cgit v1.2.3