summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/vo.rst72
-rw-r--r--video/out/gl_video.c221
-rw-r--r--video/out/gl_video.h14
-rw-r--r--video/out/vo_opengl.c10
-rw-r--r--video/out/vo_opengl_cb.c2
5 files changed, 189 insertions, 130 deletions
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index a9a8e6db95..d76c95f987 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -426,11 +426,29 @@ Available video output drivers are:
debug OpenGL context (which does nothing with current graphics drivers
as of this writing).
+ ``interpolation``
+ Reduce stuttering caused by mismatches in the video fps and display
+ refresh rate (also known as judder).
+
+ This essentially attempts to interpolate the missing frames by
+ convoluting the video along the temporal axis. The filter used can be
+ controlled using the ``tscale`` setting.
+
+ Note that this relies on vsync to work, see ``swapinterval`` for more
+ information.
+
``swapinterval=<n>``
Interval in displayed frames between two buffer swaps.
1 is equivalent to enable VSYNC, 0 to disable VSYNC. Defaults to 1 if
not specified.
+ Note that this depends on proper OpenGL vsync support. On some platforms
+ and drivers, this only works reliably when in fullscreen mode. It may
+ also require driver-specific hacks if using multiple monitors, to
+ ensure mpv syncs to the right one. Compositing window managers can
+ also lead to bad results, as can missing or incorrect display FPS
+ information (see ``--display-fps``).
+
``cscale=<filter>``
As ``scale``, but for interpolating chroma information. If the image
is not subsampled, this option is ignored entirely.
@@ -445,6 +463,18 @@ Available video output drivers are:
See ``scale-param1``, ``scale-param2``, ``scale-radius`` and
``scale-antiring``.
+ ``tscale=<filter>``, ``tscale-param1``, ``tscale-param2``, ``tscale-antiring``
+ The filter used for interpolating the temporal axis (frames). This is
+ only used if ``interpolation`` is enabled. The only valid choices
+ for ``tscale`` are separable convolution filters (use ``tscale=help``
+ to get a list). The other options (``tscale-param1`` etc.) are
+ analogous to their ``scale`` counterparts. The default is ``mitchell``.
+
+ Note that the maximum supported filter radius is currently 3, and that
+ using filters with larger radius may introduce isues when pausing or
+ framestepping, proportional to the radius used. It is recommended to
+ stick to a radius of 1 or 2.
+
``linear-scaling``
Scale in linear light. This is automatically enabled if
``target-prim``, ``target-trc``, ``icc-profile`` or
@@ -636,48 +666,6 @@ Available video output drivers are:
Color used to draw parts of the mpv window not covered by video.
See ``--osd-color`` option how colors are defined.
- ``smoothmotion``
- Reduce stuttering caused by mismatches in video fps and display
- refresh rate (also known as judder).
-
- Instead of drawing source frames for variable durations, smoothmotion
- will blend frames that overlap the transition between two frames in
- the source material.
-
- For example, a 24 Hz clip played back on a 60 Hz display would normally
- result in a pattern like this::
-
- A A A B B C C C D D E E E F F
-
- which has different lengths, alternating between 3 and 2. This
- difference in frame duration is what causes judder.
-
- With smoothmotion enabled, the pattern changes to::
-
- A A A+B B B C C C+D D D E E E+F F F
-
- where A+B is a blend of A and B. In this pattern, each frame gets a
- (consistent) duration of roughly 2.5 - resulting in smooth motion.
-
- GPU drivers or compositing window managers overriding vsync behavior
- can lead to bad results. If the framerate is close to or over the
- display refresh rate, results can be bad as well.
-
- .. note:: On systems other than Linux or OS X, you currently must set
- the ``--display-fps`` option, or the results will be bad.
-
- ``smoothmotion-threshold=<0.0-0.5>``
- Mix threshold at which interpolation is skipped (default: 0.0 – never
- skip).
-
- For example, with a ``smoothmotion-threshold`` of 0.1, if the
- smoothmotion algorithm would try to blend two frames by a ratio of
- 95% A + 5% B, it would simply display A instead. (Since the
- distance, 0.05, is lower than the threshold)
-
- Setting this to 0.5 would be similar to disabling smoothmotion
- completely, since it would always just display the nearest frame.
-
``opengl-hq``
Same as ``opengl``, but with default settings for high quality rendering.
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 68611acdaa..5dc362335e 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -49,7 +49,7 @@
// Other texture units are reserved for specific purposes
#define TEXUNIT_SCALERS TEXUNIT_VIDEO_NUM
-#define TEXUNIT_3DLUT (TEXUNIT_SCALERS+2)
+#define TEXUNIT_3DLUT (TEXUNIT_SCALERS+3)
#define TEXUNIT_DITHER (TEXUNIT_3DLUT+1)
// scale/cscale arguments that map directly to shader filter routines.
@@ -63,9 +63,9 @@ static const char *const fixed_scale_filters[] = {
};
// must be sorted, and terminated with 0
-// 2 & 6 are special-cased, the rest can be generated with WEIGHTS_N().
int filter_sizes[] =
{2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 0};
+int tscale_sizes[] = {2, 4, 6, 0}; // limited by TEXUNIT_VIDEO_NUM
struct vertex_pt {
float x, y;
@@ -129,7 +129,7 @@ struct fbosurface {
int64_t pts;
};
-#define FBOSURFACES_MAX 6
+#define FBOSURFACES_MAX 10
struct src_tex {
GLuint gl_tex;
@@ -182,12 +182,12 @@ struct gl_video {
struct fbotex chroma_merge_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
- size_t surface_idx;
- size_t surface_now;
+ int surface_idx;
+ int surface_now;
bool is_interpolated;
- // state for luma (0) and chroma (1) scalers
- struct scaler scalers[2];
+ // state for luma (0), chroma (1) and temporal (2) scalers
+ struct scaler scalers[3];
struct mp_csp_equalizer video_eq;
@@ -320,10 +320,10 @@ const struct gl_video_opts gl_video_opts_def = {
.fbo_format = GL_RGBA,
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
- .scalers = { "bilinear", "bilinear" },
+ .scalers = { "bilinear", "bilinear", "mitchell" },
.dscaler = "bilinear",
- .scaler_params = {{NAN, NAN}, {NAN, NAN}},
- .scaler_radius = {3, 3},
+ .scaler_params = {{NAN, NAN}, {NAN, NAN}, {NAN, NAN}},
+ .scaler_radius = {3, 3, 3},
.alpha_mode = 2,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
@@ -338,10 +338,10 @@ const struct gl_video_opts gl_video_opts_hq_def = {
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
.sigmoid_upscaling = 1,
- .scalers = { "spline36", "bilinear" },
+ .scalers = { "spline36", "bilinear", "mitchell" },
.dscaler = "mitchell",
- .scaler_params = {{NAN, NAN}, {NAN, NAN}},
- .scaler_radius = {3, 3},
+ .scaler_params = {{NAN, NAN}, {NAN, NAN}, {NAN, NAN}},
+ .scaler_radius = {3, 3, 3},
.alpha_mode = 2,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
@@ -372,15 +372,20 @@ const struct m_sub_options gl_video_conf = {
OPT_FLAG("pbo", pbo, 0),
OPT_STRING_VALIDATE("scale", scalers[0], 0, validate_scaler_opt),
OPT_STRING_VALIDATE("cscale", scalers[1], 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("tscale", scalers[2], 0, validate_scaler_opt),
OPT_STRING_VALIDATE("scale-down", dscaler, 0, validate_scaler_opt),
OPT_FLOAT("scale-param1", scaler_params[0][0], 0),
OPT_FLOAT("scale-param2", scaler_params[0][1], 0),
OPT_FLOAT("cscale-param1", scaler_params[1][0], 0),
OPT_FLOAT("cscale-param2", scaler_params[1][1], 0),
+ OPT_FLOAT("tscale-param1", scaler_params[2][0], 0),
+ OPT_FLOAT("tscale-param2", scaler_params[2][1], 0),
OPT_FLOATRANGE("scale-radius", scaler_radius[0], 0, 1.0, 16.0),
OPT_FLOATRANGE("cscale-radius", scaler_radius[1], 0, 1.0, 16.0),
+ OPT_FLOATRANGE("tscale-radius", scaler_radius[2], 0, 1.0, 3.0),
OPT_FLOATRANGE("scale-antiring", scaler_antiring[0], 0, 0.0, 1.0),
OPT_FLOATRANGE("cscale-antiring", scaler_antiring[1], 0, 0.0, 1.0),
+ OPT_FLOATRANGE("tscale-antiring", scaler_antiring[2], 0, 0.0, 1.0),
OPT_FLAG("scaler-resizes-only", scaler_resizes_only, 0),
OPT_FLAG("linear-scaling", linear_scaling, 0),
OPT_FLAG("fancy-downscaling", fancy_downscaling, 0),
@@ -416,13 +421,12 @@ const struct m_sub_options gl_video_conf = {
{"blend", 2})),
OPT_FLAG("rectangle-textures", use_rectangle, 0),
OPT_COLOR("background", background, 0),
- OPT_FLAG("smoothmotion", smoothmotion, 0),
- OPT_FLOAT("smoothmotion-threshold", smoothmotion_threshold,
- CONF_RANGE, .min = 0, .max = 0.5),
+ OPT_FLAG("interpolation", interpolation, 0),
OPT_REMOVED("approx-gamma", "this is always enabled now"),
OPT_REMOVED("cscale-down", "chroma is never downscaled"),
OPT_REMOVED("scale-sep", "this is set automatically whenever sane"),
OPT_REMOVED("indirect", "this is set automatically whenever sane"),
+ OPT_REMOVED("smoothmotion-threshold", "to be readded as a proper scaler"),
OPT_REPLACED("lscale", "scale"),
OPT_REPLACED("lscale-down", "scale-down"),
@@ -435,6 +439,7 @@ const struct m_sub_options gl_video_conf = {
OPT_REPLACED("cradius", "cscale-radius"),
OPT_REPLACED("cantiring", "cscale-antiring"),
OPT_REPLACED("srgb", "target-prim=srgb:target-trc=srgb"),
+ OPT_REPLACED("smoothmotion", "interpolation"),
{0}
},
@@ -489,9 +494,10 @@ static void gl_video_reset_surfaces(struct gl_video *p)
p->surface_now = 0;
}
-static size_t fbosurface_next(size_t id)
+static inline int fbosurface_wrap(int id)
{
- return (id+1) % FBOSURFACES_MAX;
+ id = id % FBOSURFACES_MAX;
+ return id < 0 ? id + FBOSURFACES_MAX : id;
}
static void recreate_osd(struct gl_video *p)
@@ -517,7 +523,7 @@ static void uninit_rendering(struct gl_video *p)
{
GL *gl = p->gl;
- for (int n = 0; n < 2; n++)
+ for (int n = 0; n < 3; n++)
uninit_scaler(p, n);
gl->DeleteTextures(1, &p->dither_texture);
@@ -861,7 +867,7 @@ static void uninit_scaler(struct gl_video *p, int scaler_unit)
}
static void reinit_scaler(struct gl_video *p, int scaler_unit, const char *name,
- double scale_factor)
+ double scale_factor, int sizes[])
{
GL *gl = p->gl;
struct scaler *scaler = &p->scalers[scaler_unit];
@@ -898,8 +904,7 @@ static void reinit_scaler(struct gl_video *p, int scaler_unit, const char *name,
if (scaler->kernel->radius < 0)
scaler->kernel->radius = p->opts.scaler_radius[scaler->index];
- scaler->insufficient = !mp_init_filter(scaler->kernel, filter_sizes,
- scale_factor);
+ scaler->insufficient = !mp_init_filter(scaler->kernel, sizes, scale_factor);
if (scaler->kernel->polar) {
scaler->gl_target = GL_TEXTURE_1D;
@@ -980,18 +985,22 @@ static void pass_sample_separated_get_weights(struct gl_video *p,
}
// Handle a single pass (either vertical or horizontal). The direction is given
-// by the vector (d_x, d_y)
+// by the vector (d_x, d_y). If the vector is 0, then planar interpolation is
+// used instead (samples from texture0 through textureN)
static void pass_sample_separated_gen(struct gl_video *p, struct scaler *scaler,
int d_x, int d_y)
{
int N = scaler->kernel->size;
bool use_ar = scaler->antiring > 0;
+ bool planar = d_x == 0 && d_y == 0;
GLSL(vec4 color = vec4(0.0);)
GLSLF("{\n");
- GLSLF("vec2 dir = vec2(%d, %d);\n", d_x, d_y);
- GLSL(vec2 pt = (vec2(1.0) / sample_size) * dir;)
- GLSL(float fcoord = dot(fract(sample_pos * sample_size - vec2(0.5)), dir);)
- GLSLF("vec2 base = sample_pos - fcoord * pt - pt * vec2(%d);\n", N / 2 - 1);
+ if (!planar) {
+ GLSLF("vec2 dir = vec2(%d, %d);\n", d_x, d_y);
+ GLSL(vec2 pt = (vec2(1.0) / sample_size) * dir;)
+ GLSL(float fcoord = dot(fract(sample_pos * sample_size - vec2(0.5)), dir);)
+ GLSLF("vec2 base = sample_pos - fcoord * pt - pt * vec2(%d);\n", N / 2 - 1);
+ }
GLSL(vec4 c;)
if (use_ar) {
GLSL(vec4 hi = vec4(0.0);)
@@ -1000,7 +1009,11 @@ static void pass_sample_separated_gen(struct gl_video *p, struct scaler *scaler,
pass_sample_separated_get_weights(p, scaler);
GLSLF("// scaler samples\n");
for (int n = 0; n < N; n++) {
- GLSLF("c = texture(sample_tex, base + pt * vec2(%d));\n", n);
+ if (planar) {
+ GLSLF("c = texture(texture%d, texcoord%d);\n", n, n);
+ } else {
+ GLSLF("c = texture(sample_tex, base + pt * vec2(%d));\n", n);
+ }
GLSLF("color += vec4(weights[%d]) * c;\n", n);
if (use_ar && (n == N/2-1 || n == N/2)) {
GLSL(lo = min(lo, c);)
@@ -1163,7 +1176,6 @@ static void pass_sample_sharpen5(struct gl_video *p, struct scaler *scaler)
double param = isnan(scaler->params[0]) ? 0.5 : scaler->params[0];
GLSLF("color = p + t * %f;\n", param);
GLSLF("}\n");
-
}
// Sample. This samples from the texture ID given by src_tex. It's hardcoded to
@@ -1180,7 +1192,7 @@ static void pass_sample(struct gl_video *p, int src_tex,
int w, int h, struct gl_transform transform)
{
struct scaler *scaler = &p->scalers[scaler_unit];
- reinit_scaler(p, scaler_unit, name, scale_factor);
+ reinit_scaler(p, scaler_unit, name, scale_factor, filter_sizes);
// Set up the sample parameters appropriately
GLSLF("#define sample_tex texture%d\n", src_tex);
@@ -1667,7 +1679,6 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
vp_h = p->dst_rect.y1 - p->dst_rect.y0,
fuzz = FBOTEX_FUZZY_W | FBOTEX_FUZZY_H;
- size_t surface_nxt = fbosurface_next(p->surface_now);
// First of all, figure out if we have a frame availble at all, and draw
// it manually + reset the queue if not
@@ -1679,9 +1690,27 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
p->surface_idx = p->surface_now;
}
+ // Figure out the queue size. For illustration, a filter radius of 2 would
+ // look like this: _ A [B] C D _
+ // A is surface_bse, B is surface_now, C is surface_nxt and D is
+ // surface_end.
+ struct scaler *tscale = &p->scalers[2];
+ reinit_scaler(p, 2, p->opts.scalers[2], 1, tscale_sizes);
+ assert(tscale->kernel && !tscale->kernel->polar);
+
+ int size = ceil(tscale->kernel->size);
+ assert(size <= TEXUNIT_VIDEO_NUM);
+ int radius = size/2;
+
+ int surface_now = p->surface_now;
+ int surface_nxt = fbosurface_wrap(surface_now + 1);
+ int surface_bse = fbosurface_wrap(surface_now - (radius-1));
+ int surface_end = fbosurface_wrap(surface_now + radius);
+ assert(fbosurface_wrap(surface_bse + size-1) == surface_end);
+
// Render a new frame if it came in and there's room in the queue
- size_t surface_dst = fbosurface_next(p->surface_idx);
- if (t && surface_dst != p->surface_now &&
+ int surface_dst = fbosurface_wrap(p->surface_idx+1);
+ if (t && surface_dst != surface_bse &&
p->surfaces[p->surface_idx].pts < t->pts) {
MP_STATS(p, "new-pts");
pass_draw_frame(p);
@@ -1691,40 +1720,58 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
p->surface_idx = surface_dst;
}
+ // Figure out whether the queue is "valid". A queue is invalid if the
+ // frames' PTS is not monotonically increasing. Anything else is invalid,
+ // so avoid blending incorrect data and just draw the latest frame as-is.
+ // Possible causes for failure of this condition include seeks, pausing,
+ // end of playback or start of playback.
+ bool valid = true;
+ for (int i = surface_bse; i != surface_end; i = fbosurface_wrap(i+1)) {
+ if (!p->surfaces[i].pts ||
+ p->surfaces[fbosurface_wrap(i+1)].pts < p->surfaces[i].pts) {
+ valid = false;
+ break;
+ }
+ }
+
// Finally, draw the right mix of frames to the screen.
- pass_load_fbotex(p, &p->surfaces[p->surface_now].fbotex, 0, vp_w, vp_h);
- if (!t || p->surfaces[surface_nxt].pts < p->surfaces[p->surface_now].pts) {
- // No next frame available (eg. start of playback, after reconfigure
- // or end of file, so just draw the current frame instead of blending.
- // Also occurs when no timing information is available (eg. paused)
+ if (!t || !valid) {
+ // surface_now is guaranteed to be valid, so we can safely use it.
+ pass_load_fbotex(p, &p->surfaces[surface_now].fbotex, 0, vp_w, vp_h);
GLSL(vec4 color = texture(texture0, texcoord0);)
p->is_interpolated = false;
} else {
- int64_t next_pts = p->surfaces[surface_nxt].pts,
- vsync_interval = t->next_vsync - t->prev_vsync;
- double inter_coeff = (double)(next_pts - t->next_vsync) / vsync_interval,
- threshold = p->opts.smoothmotion_threshold;
- inter_coeff = inter_coeff <= 0.0 + threshold ? 0.0 : inter_coeff;
- inter_coeff = inter_coeff >= 1.0 - threshold ? 1.0 : inter_coeff;
- inter_coeff = 1.0 - inter_coeff;
- gl_sc_uniform_f(p->sc, "inter_coeff", inter_coeff);
- p->is_interpolated = inter_coeff > 0;
+ int64_t pts_now = p->surfaces[surface_now].pts,
+ pts_nxt = p->surfaces[surface_nxt].pts;
+ double fscale = pts_nxt - pts_now;
+ double fcoord = (t->next_vsync - pts_now) / fscale;
+ gl_sc_uniform_f(p->sc, "fcoord", fcoord);
MP_STATS(p, "frame-mix");
MP_DBG(p, "inter frame ppts: %lld, pts: %lld, vsync: %lld, mix: %f\n",
- (long long)p->surfaces[p->surface_now].pts,
- (long long)p->surfaces[surface_nxt].pts,
- (long long)t->next_vsync, inter_coeff);
-
- pass_load_fbotex(p, &p->surfaces[surface_nxt].fbotex, 1, vp_w, vp_h);
- GLSL(vec4 color = mix(texture(texture0, texcoord0),
- texture(texture1, texcoord1),
- inter_coeff);)
- // Dequeue the current frame if it's no longer needed
- if (t->next_vsync + vsync_interval > p->surfaces[surface_nxt].pts)
- p->surface_now = surface_nxt;
+ (long long)pts_now, (long long)pts_nxt,
+ (long long)t->next_vsync, fcoord);
+
+ for (int i = 0; i < size; i++) {
+ pass_load_fbotex(p, &p->surfaces[fbosurface_wrap(surface_bse+i)].fbotex,
+ i, vp_w, vp_h);
+ }
+ pass_sample_separated_gen(p, tscale, 0, 0);
+ p->is_interpolated = true;
+
}
pass_draw_to_screen(p, fbo);
+
+ // Dequeue frames if necessary
+ if (t) {
+ int64_t vsync_interval = t->next_vsync - t->prev_vsync;
+ int64_t vsync_guess = t->next_vsync + vsync_interval;
+ if (p->surfaces[surface_nxt].pts > p->surfaces[p->surface_now].pts
+ && p->surfaces[surface_nxt].pts < vsync_guess) {
+ p->surface_now = surface_nxt;
+ surface_nxt = fbosurface_wrap(p->surface_now+1);
+ }
+ }
}
// (fbo==0 makes BindFramebuffer select the screen backbuffer)
@@ -1748,9 +1795,10 @@ void gl_video_render_frame(struct gl_video *p, int fbo, struct frame_timing *t)
gl_sc_set_vao(p->sc, &p->vao);
- if (p->opts.smoothmotion) {
+ if (p->opts.interpolation) {
gl_video_interpolate_frame(p, fbo, t);
} else {
+ // Skip interpolation if there's nothing to be done
pass_draw_frame(p);
pass_draw_to_screen(p, fbo);
}
@@ -1982,9 +2030,9 @@ static void check_gl_features(struct gl_video *p)
p->use_lut_3d = false;
disabled[n_disabled++] = "color management (FBO)";
}
- if (p->opts.smoothmotion && !test_fbo(p, &have_fbo)) {
- p->opts.smoothmotion = false;
- disabled[n_disabled++] = "smoothmotion (FBO)";
+ if (p->opts.interpolation && !test_fbo(p, &have_fbo)) {
+ p->opts.interpolation = false;
+ disabled[n_disabled++] = "interpolation (FBO)";
}
if (gl->es && p->opts.pbo) {
p->opts.pbo = 0;
@@ -2282,6 +2330,7 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd
.scalers = {
{ .index = 0, .name = "bilinear" },
{ .index = 1, .name = "bilinear" },
+ { .index = 2, .name = "mitchell" },
},
.sc = gl_sc_create(gl, log),
};
@@ -2291,16 +2340,17 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd
return p;
}
-// Get static string for scaler shader.
-static const char *handle_scaler_opt(const char *name)
+// Get static string for scaler shader. If "tscale" is set to true, the
+// scaler must be a separable convolution filter.
+static const char *handle_scaler_opt(const char *name, bool tscale)
{
if (name && name[0]) {
const struct filter_kernel *kernel = mp_find_filter_kernel(name);
- if (kernel)
- return kernel->name;
+ if (kernel && (!tscale || !kernel->polar))
+ return kernel->name;
for (const char *const *filter = fixed_scale_filters; *filter; filter++) {
- if (strcmp(*filter, name) == 0)
+ if (strcmp(*filter, name) == 0 && !tscale)
return *filter;
}
}
@@ -2309,12 +2359,26 @@ static const char *handle_scaler_opt(const char *name)
// Set the options, and possibly update the filter chain too.
// Note: assumes all options are valid and verified by the option parser.
-void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts)
+void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,
+ int *queue_size)
{
p->opts = *opts;
- for (int n = 0; n < 2; n++)
- p->opts.scalers[n] = (char *)handle_scaler_opt(p->opts.scalers[n]);
- p->opts.dscaler = (char *)handle_scaler_opt(p->opts.dscaler);
+ for (int n = 0; n < 3; n++)
+ p->opts.scalers[n] = (char *)handle_scaler_opt(p->opts.scalers[n], n==2);
+ p->opts.dscaler = (char *)handle_scaler_opt(p->opts.dscaler, false);
+
+ // Figure out an adequate size for the interpolation queue. The larger
+ // the radius, the earlier we need to queue frames. This rough heuristic
+ // seems to work for now, but ideally we want to rework the pause/unpause
+ // logic to make larger queue sizes the default.
+ if (queue_size && p->opts.interpolation && p->opts.scalers[2]) {
+ const struct filter_kernel *kernel = mp_find_filter_kernel(p->opts.scalers[2]);
+ if (kernel) {
+ double radius = kernel->radius;
+ radius = radius > 0 ? radius : p->opts.scaler_radius[2];
+ *queue_size = 50e3 * (ceil(radius) - 1);
+ }
+ }
check_gl_features(p);
uninit_rendering(p);
@@ -2340,19 +2404,24 @@ static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
{
char s[20] = {0};
int r = 1;
+ bool tscale = bstr_equals0(name, "tscale");
if (bstr_equals0(param, "help")) {
r = M_OPT_EXIT - 1;
} else {
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
- if (!handle_scaler_opt(s))
+ if (!handle_scaler_opt(s, tscale))
r = M_OPT_INVALID;
}
if (r < 1) {
mp_info(log, "Available scalers:\n");
- for (const char *const *filter = fixed_scale_filters; *filter; filter++)
- mp_info(log, " %s\n", *filter);
- for (int n = 0; mp_filter_kernels[n].name; n++)
- mp_info(log, " %s\n", mp_filter_kernels[n].name);
+ if (!tscale) {
+ for (const char *const *filter = fixed_scale_filters; *filter; filter++)
+ mp_info(log, " %s\n", *filter);
+ }
+ for (int n = 0; mp_filter_kernels[n].name; n++) {
+ if (!tscale || !mp_filter_kernels[n].polar)
+ mp_info(log, " %s\n", mp_filter_kernels[n].name);
+ }
if (s[0])
mp_fatal(log, "No scaler named '%s' found!\n", s);
}
diff --git a/video/out/gl_video.h b/video/out/gl_video.h
index 9c70e9ba82..8ec59895a1 100644
--- a/video/out/gl_video.h
+++ b/video/out/gl_video.h
@@ -29,15 +29,15 @@ struct lut3d {
};
struct gl_video_opts {
- char *scalers[2];
+ char *scalers[3];
char *dscaler;
float gamma;
int gamma_auto;
int target_prim;
int target_trc;
- float scaler_params[2][2];
- float scaler_radius[2];
- float scaler_antiring[2];
+ float scaler_params[3][2];
+ float scaler_radius[3];
+ float scaler_antiring[3];
int linear_scaling;
int fancy_downscaling;
int sigmoid_upscaling;
@@ -55,8 +55,7 @@ struct gl_video_opts {
int chroma_location;
int use_rectangle;
struct m_color background;
- int smoothmotion;
- float smoothmotion_threshold;
+ int interpolation;
};
extern const struct m_sub_options gl_video_conf;
@@ -67,7 +66,8 @@ struct gl_video;
struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd);
void gl_video_uninit(struct gl_video *p);
-void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts);
+void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,
+ int *queue_size);
bool gl_video_check_format(struct gl_video *p, int mp_format);
void gl_video_config(struct gl_video *p, struct mp_image_params *params);
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b);
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index aa3dd0c9e0..3abd1d3320 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -315,8 +315,9 @@ static bool reparse_cmdline(struct gl_priv *p, char *args)
if (r >= 0) {
mpgl_lock(p->glctx);
- gl_video_set_options(p->renderer, opts->renderer_opts);
- vo_set_flip_queue_params(p->vo, 0, opts->renderer_opts->smoothmotion);
+ int queue = 0;
+ gl_video_set_options(p->renderer, opts->renderer_opts, &queue);
+ vo_set_flip_queue_params(p->vo, queue, opts->renderer_opts->interpolation);
p->vo->want_redraw = true;
mpgl_unlock(p->glctx);
}
@@ -459,8 +460,9 @@ static int preinit(struct vo *vo)
goto err_out;
gl_video_set_output_depth(p->renderer, p->glctx->depth_r, p->glctx->depth_g,
p->glctx->depth_b);
- gl_video_set_options(p->renderer, p->renderer_opts);
- vo_set_flip_queue_params(vo, 0, p->renderer_opts->smoothmotion);
+ int queue = 0;
+ gl_video_set_options(p->renderer, p->renderer_opts, &queue);
+ vo_set_flip_queue_params(p->vo, queue, p->renderer_opts->interpolation);
p->cms = gl_lcms_init(p, vo->log, vo->global);
if (!p->cms)
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index e20be8dd67..9b7f650eb2 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -306,7 +306,7 @@ int mpv_opengl_cb_render(struct mpv_opengl_cb_context *ctx, int fbo, int vp[4])
struct vo_priv *p = vo ? vo->priv : NULL;
struct vo_priv *opts = ctx->new_opts ? ctx->new_opts : p;
if (opts) {
- gl_video_set_options(ctx->renderer, opts->renderer_opts);
+ gl_video_set_options(ctx->renderer, opts->renderer_opts, NULL);
ctx->gl->debug_context = opts->use_gl_debug;
gl_video_set_debug(ctx->renderer, opts->use_gl_debug);
frame_queue_shrink(ctx, opts->frame_queue_size);