summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/vf.rst48
-rw-r--r--filters/f_autoconvert.c45
-rw-r--r--filters/f_autoconvert.h9
-rw-r--r--filters/f_swscale.c13
-rw-r--r--filters/f_swscale.h5
-rw-r--r--video/filter/vf_format.c88
6 files changed, 165 insertions, 43 deletions
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index ac87cb26fc..350f1b7182 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -112,15 +112,55 @@ With filters that support it, you can access parameters by their name.
Available mpv-only filters are:
``format=fmt=<value>:colormatrix=<value>:...``
- Restricts the color space for the next filter without doing any conversion.
- Use together with the scale filter for a real conversion.
+ Applies video parameter overrides, with optional conversion. By default,
+ this
.. note::
- For a list of available formats, see ``format=fmt=help``.
+ For a list of available formats, use ``--vf=format=fmt=help``.
``<fmt>``
- Format name, e.g. rgb15, bgr24, 420p, etc. (default: don't change).
+ Image format name, e.g. rgb15, bgr24, 420p, etc. (default: don't change).
+
+ This filter always performs conversion to the given format.
+
+ ``<convert=yes|no>``
+ Force conversion of color parameters (default: no).
+
+ If this is disabled (the default), the only conversion that is possibly
+ performed is format conversion if ``<fmt>`` is set. All other parameters
+ (like ``<colormatrix>``) are forced without conversion. This mode is
+ typically useful when files have been incorrectly tagged.
+
+ If this is enabled, libswscale or zimg is used if any of the parameters
+ mismatch. zimg is used of the input/output image formats are supported
+ by mpv's zimg wrapper, and if ``--sws-allow-zimg=yes`` is used. Both
+ libraries may not support all kinds of conversions. This typically
+ results in silent incorrect conversion. zimg has in many cases a better
+ chance of performing the conversion correctly.
+
+ In both cases, the color parameters are set on the output stage of the
+ image format conversion (if ``fmt`` was set). The difference is that
+ with ``convert=no``, the color parameters are not passed on to the
+ converter.
+
+ If input and output video parameters are the same, conversion is always
+ skipped.
+
+ .. admonition:: Examples
+
+ ``mpv test.mkv --vf=format:colormatrix=ycgco``
+ Results in incorrect colors (if test.mkv was tagged correctly).
+
+ ``mpv test.mkv --vf=format:colormatrix=ycgco:convert=yes --sws-allow-zimg``
+ Results in true conversion to ``ycgco``, assuming the renderer
+ supports it (``--vo=gpu`` normally does). You can add ``--vo=xv``
+ to force a VO which definitely does not support it, which should
+ show incorrect colors as confirmation.
+
+ Using ``--sws-allow-zimg=no`` (or disabling zimg at build time)
+ will use libswscale, which cannot perform this conversion as
+ of this writing.
``<colormatrix>``
Controls the YUV to RGB color space conversion when playing video. There
diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c
index 065092ac64..20c083de54 100644
--- a/filters/f_autoconvert.c
+++ b/filters/f_autoconvert.c
@@ -27,6 +27,8 @@ struct priv {
int *imgfmts;
int *subfmts;
int num_imgfmts;
+ struct mp_image_params imgparams;
+ bool imgparams_set;
// Enable special conversion for the final stage before the VO.
bool vo_convert;
@@ -73,6 +75,7 @@ void mp_autoconvert_clear(struct mp_autoconvert *c)
struct priv *p = c->f->priv;
p->num_imgfmts = 0;
+ p->imgparams_set = false;
p->num_afmts = 0;
p->num_srates = 0;
p->chmaps = (struct mp_chmap_sel){0};
@@ -93,6 +96,23 @@ void mp_autoconvert_add_imgfmt(struct mp_autoconvert *c, int imgfmt, int subfmt)
p->force_update = true;
}
+void mp_autoconvert_set_target_image_params(struct mp_autoconvert *c,
+ struct mp_image_params *par)
+{
+ struct priv *p = c->f->priv;
+
+ if (p->imgparams_set && mp_image_params_equal(&p->imgparams, par) &&
+ p->num_imgfmts == 1 && p->imgfmts[0] == par->imgfmt &&
+ p->subfmts[0] == par->hw_subfmt)
+ return;
+
+ p->imgparams = *par;
+ p->imgparams_set = true;
+
+ p->num_imgfmts = 0;
+ mp_autoconvert_add_imgfmt(c, par->imgfmt, par->hw_subfmt);
+}
+
void mp_autoconvert_add_all_sw_imgfmts(struct mp_autoconvert *c)
{
for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
@@ -185,8 +205,13 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
bool samesubffmt = img->params.hw_subfmt == p->subfmts[n];
if (samefmt && !samesubffmt)
different_subfmt = true;
- if (samefmt && (samesubffmt || !p->subfmts[n]))
+ if (samefmt && (samesubffmt || !p->subfmts[n])) {
+ if (p->imgparams_set) {
+ if (!mp_image_params_equal(&p->imgparams, &img->params))
+ break;
+ }
return true;
+ }
}
struct mp_stream_info *info = mp_filter_find_stream_info(f);
@@ -200,6 +225,8 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
// 2: sw->hw upload
struct mp_filter *filters[3] = {0};
bool need_sws = true;
+ bool force_sws_params = false;
+ struct mp_image_params imgpar = img->params;
int *fmts = p->imgfmts;
int num_fmts = p->num_imgfmts;
@@ -259,9 +286,21 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
if (hwd) {
filters[0] = hwd->f;
src_fmt = res_fmt;
+ // Downloading from hw will obviously change the parameters. We
+ // stupidly don't know the result parameters, but if it's
+ // sufficiently sane, it will only do the following.
+ imgpar.imgfmt = src_fmt;
+ imgpar.hw_subfmt = 0;
+ // Try to compensate for in-sane cases.
+ mp_image_params_guess_csp(&imgpar);
}
}
+ if (p->imgparams_set) {
+ force_sws_params |= !mp_image_params_equal(&imgpar, &p->imgparams);
+ need_sws |= force_sws_params;
+ }
+
if (need_sws) {
// Create a new conversion filter.
struct mp_sws_filter *sws = mp_sws_filter_create(conv);
@@ -277,11 +316,13 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
goto fail;
}
- if (out == src_fmt) {
+ if (out == src_fmt && !force_sws_params) {
// Can happen if hwupload goes to same format.
talloc_free(sws->f);
} else {
sws->out_format = out;
+ sws->out_params = p->imgparams;
+ sws->use_out_params = force_sws_params;
mp_info(log, "Converting %s -> %s\n", mp_imgfmt_to_name(src_fmt),
mp_imgfmt_to_name(sws->out_format));
filters[1] = sws->f;
diff --git a/filters/f_autoconvert.h b/filters/f_autoconvert.h
index e8c5a44134..ae08fd4e51 100644
--- a/filters/f_autoconvert.h
+++ b/filters/f_autoconvert.h
@@ -3,6 +3,7 @@
#include "filter.h"
struct mp_image;
+struct mp_image_params;
// A filter which automatically creates and uses a conversion filter based on
// the filter settings, or passes through data unchanged if no conversion is
@@ -22,6 +23,14 @@ struct mp_autoconvert {
// (to free this, free the filter itself, mp_autoconvert.f)
struct mp_autoconvert *mp_autoconvert_create(struct mp_filter *parent);
+// Require that output frames have the following params set.
+// This implicitly clears the image format list, and calls
+// mp_autoconvert_add_imgfmt() with the values in *p.
+// Idempotent on subsequent calls (no reinit forced if parameters don't change).
+// Mixing this with other format-altering calls has undefined effects.
+void mp_autoconvert_set_target_image_params(struct mp_autoconvert *c,
+ struct mp_image_params *p);
+
// Add the imgfmt as allowed video image format, and error on non-video frames.
// Each call adds to the list of allowed formats. Before the first call, all
// formats are allowed (even non-video).
diff --git a/filters/f_swscale.c b/filters/f_swscale.c
index 07198b1377..1ff25ab909 100644
--- a/filters/f_swscale.c
+++ b/filters/f_swscale.c
@@ -88,9 +88,18 @@ static void process(struct mp_filter *f)
}
struct mp_image *src = frame.data;
+
int dstfmt = s->out_format ? s->out_format : src->imgfmt;
+ int w = src->w;
+ int h = src->h;
+
+ if (s->use_out_params) {
+ w = s->out_params.w;
+ h = s->out_params.h;
+ dstfmt = s->out_params.imgfmt;
+ }
- struct mp_image *dst = mp_image_pool_get(s->pool, dstfmt, src->w, src->h);
+ struct mp_image *dst = mp_image_pool_get(s->pool, dstfmt, w, h);
if (!dst)
goto error;
@@ -102,6 +111,8 @@ static void process(struct mp_filter *f)
{
dst->params.color.levels = MP_CSP_LEVELS_TV;
}
+ if (s->use_out_params)
+ dst->params = s->out_params;
mp_image_params_guess_csp(&dst->params);
bool ok = mp_sws_scale(s->sws, dst, src) >= 0;
diff --git a/filters/f_swscale.h b/filters/f_swscale.h
index fd5aa11f5e..6e26aef1ac 100644
--- a/filters/f_swscale.h
+++ b/filters/f_swscale.h
@@ -2,10 +2,15 @@
#include <stdbool.h>
+#include "video/mp_image.h"
+
struct mp_sws_filter {
struct mp_filter *f;
// Desired output imgfmt. If 0, uses the input format.
int out_format;
+ // If set, force all image params; ignores out_format.
+ bool use_out_params;
+ struct mp_image_params out_params;
// private state
struct mp_sws_context *sws;
struct mp_image_pool *pool;
diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c
index 1700f002ab..942fa27a52 100644
--- a/video/filter/vf_format.c
+++ b/video/filter/vf_format.c
@@ -36,12 +36,11 @@
struct priv {
struct vf_format_opts *opts;
- struct mp_pin *in_pin;
+ struct mp_autoconvert *conv;
};
struct vf_format_opts {
int fmt;
- int outfmt;
int colormatrix;
int colorlevels;
int primaries;
@@ -53,34 +52,11 @@ struct vf_format_opts {
int rotate;
int dw, dh;
double dar;
+ int convert;
};
-static void vf_format_process(struct mp_filter *f)
+static void set_params(struct vf_format_opts *p, struct mp_image_params *out)
{
- struct priv *priv = f->priv;
- struct vf_format_opts *p = priv->opts;
-
- if (!mp_pin_can_transfer_data(f->ppins[1], priv->in_pin))
- return;
-
- struct mp_frame frame = mp_pin_out_read(priv->in_pin);
-
- if (mp_frame_is_signaling(frame)) {
- mp_pin_in_write(f->ppins[1], frame);
- return;
- }
- if (frame.type != MP_FRAME_VIDEO) {
- MP_ERR(f, "unsupported frame type\n");
- mp_frame_unref(&frame);
- mp_filter_internal_mark_failed(f);
- return;
- }
-
- struct mp_image *img = frame.data;
- struct mp_image_params *out = &img->params;
-
- if (p->outfmt)
- out->imgfmt = p->outfmt;
if (p->colormatrix)
out->color.space = p->colormatrix;
if (p->colorlevels)
@@ -118,11 +94,53 @@ static void vf_format_process(struct mp_filter *f)
if (p->dar > 0)
dsize = av_d2q(p->dar, INT_MAX);
mp_image_params_set_dsize(out, dsize.num, dsize.den);
+}
+
+static void vf_format_process(struct mp_filter *f)
+{
+ struct priv *priv = f->priv;
- // Make sure the user-overrides are consistent (no RGB csp for YUV, etc.).
- mp_image_params_guess_csp(out);
+ if (mp_pin_can_transfer_data(priv->conv->f->pins[0], f->ppins[0])) {
+ struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
- mp_pin_in_write(f->ppins[1], frame);
+ if (priv->opts->convert && frame.type == MP_FRAME_VIDEO) {
+ struct mp_image *img = frame.data;
+ struct mp_image_params par = img->params;
+ int outfmt = priv->opts->fmt;
+
+ // If we convert from RGB to YUV, default to limited range.
+ if (mp_imgfmt_get_forced_csp(img->imgfmt) == MP_CSP_RGB &&
+ outfmt && mp_imgfmt_get_forced_csp(outfmt) == MP_CSP_AUTO)
+ {
+ par.color.levels = MP_CSP_LEVELS_TV;
+ }
+
+ set_params(priv->opts, &par);
+
+ if (par.imgfmt != outfmt) {
+ par.imgfmt = outfmt;
+ par.hw_subfmt = 0;
+ }
+ mp_image_params_guess_csp(&par);
+
+ mp_autoconvert_set_target_image_params(priv->conv, &par);
+ }
+
+ mp_pin_in_write(priv->conv->f->pins[0], frame);
+ }
+
+ if (mp_pin_can_transfer_data(f->ppins[1], priv->conv->f->pins[1])) {
+ struct mp_frame frame = mp_pin_out_read(priv->conv->f->pins[1]);
+
+ if (!priv->opts->convert && frame.type == MP_FRAME_VIDEO) {
+ struct mp_image *img = frame.data;
+
+ set_params(priv->opts, &img->params);
+ mp_image_params_guess_csp(&img->params);
+ }
+
+ mp_pin_in_write(f->ppins[1], frame);
+ }
}
static const struct mp_filter_info vf_format_filter = {
@@ -145,17 +163,14 @@ static struct mp_filter *vf_format_create(struct mp_filter *parent, void *option
mp_filter_add_pin(f, MP_PIN_IN, "in");
mp_filter_add_pin(f, MP_PIN_OUT, "out");
- struct mp_autoconvert *conv = mp_autoconvert_create(f);
- if (!conv) {
+ priv->conv = mp_autoconvert_create(f);
+ if (!priv->conv) {
talloc_free(f);
return NULL;
}
if (priv->opts->fmt)
- mp_autoconvert_add_imgfmt(conv, priv->opts->fmt, 0);
-
- priv->in_pin = conv->f->pins[1];
- mp_pin_connect(conv->f->pins[0], f->ppins[0]);
+ mp_autoconvert_add_imgfmt(priv->conv, priv->opts->fmt, 0);
return f;
}
@@ -175,6 +190,7 @@ static const m_option_t vf_opts_fields[] = {
OPT_INT("dw", dw, 0),
OPT_INT("dh", dh, 0),
OPT_DOUBLE("dar", dar, 0),
+ OPT_FLAG("convert", convert, 0),
OPT_REMOVED("outputlevels", "use the --video-output-levels global option"),
OPT_REMOVED("peak", "use sig-peak instead (changed value scale!)"),
{0}