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=`` 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=`` 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=``, ``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); + (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_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; } 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);