diff options
author | Martin Herkt <lachs0r@srsfckn.biz> | 2016-11-20 18:15:08 +0100 |
---|---|---|
committer | Martin Herkt <lachs0r@srsfckn.biz> | 2016-11-20 18:15:08 +0100 |
commit | 8700700de8a4103724796077034f7f254ad974bc (patch) | |
tree | 2fce4dee518a202c45c3f16567db36edc92ed914 /video/out | |
parent | e6b85c91700bee0ddc92e98a30d5021691bd6f65 (diff) | |
parent | eafc273d2c2ae6d247d741202e58ca23dc938cb2 (diff) | |
download | mpv-8700700de8a4103724796077034f7f254ad974bc.tar.bz2 mpv-8700700de8a4103724796077034f7f254ad974bc.tar.xz |
Merge branch 'master' into release/current
Diffstat (limited to 'video/out')
-rw-r--r-- | video/out/cocoa_common.m | 11 | ||||
-rw-r--r-- | video/out/filter_kernels.c | 49 | ||||
-rw-r--r-- | video/out/filter_kernels.h | 1 | ||||
-rw-r--r-- | video/out/opengl/common.h | 3 | ||||
-rw-r--r-- | video/out/opengl/context.c | 2 | ||||
-rw-r--r-- | video/out/opengl/context_rpi.c | 2 | ||||
-rw-r--r-- | video/out/opengl/header_fixes.h | 17 | ||||
-rw-r--r-- | video/out/opengl/video.c | 103 | ||||
-rw-r--r-- | video/out/opengl/video.h | 1 | ||||
-rw-r--r-- | video/out/vo.c | 5 | ||||
-rw-r--r-- | video/out/vo.h | 1 | ||||
-rw-r--r-- | video/out/vo_direct3d.c | 2 | ||||
-rw-r--r-- | video/out/vo_opengl_cb.c | 11 | ||||
-rw-r--r-- | video/out/vo_tct.c | 306 | ||||
-rw-r--r-- | video/out/win_state.c | 2 |
15 files changed, 453 insertions, 63 deletions
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m index ba241f42cc..719169df48 100644 --- a/video/out/cocoa_common.m +++ b/video/out/cocoa_common.m @@ -121,9 +121,14 @@ static void run_on_main_thread(struct vo *vo, void(^block)(void)) static void queue_new_video_size(struct vo *vo, int w, int h) { struct vo_cocoa_state *s = vo->cocoa; + struct mp_vo_opts *opts = vo->opts; if ([s->window conformsToProtocol: @protocol(MpvSizing)]) { id<MpvSizing> win = (id<MpvSizing>) s->window; - [win queueNewVideoSize:NSMakeSize(w, h)]; + NSRect r = NSMakeRect(0, 0, w, h); + if(!opts->hidpi_window_scale) { + r = [s->current_screen convertRectFromBacking:r]; + } + [win queueNewVideoSize:NSMakeSize(r.size.width, r.size.height)]; } } @@ -488,8 +493,10 @@ static void create_ui(struct vo *vo, struct mp_rect *win, int geo_flags) if (s->embedded) { parent = (NSView *) (intptr_t) opts->WinID; } else { - const NSRect wr = + NSRect wr = NSMakeRect(win->x0, win->y0, win->x1 - win->x0, win->y1 - win->y0); + if(!opts->hidpi_window_scale) + wr = [s->current_screen convertRectFromBacking:wr]; s->window = create_window(wr, s->current_screen, opts->border, adapter); parent = [s->window contentView]; } diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c index fe5265c70c..c5a12295f7 100644 --- a/video/out/filter_kernels.c +++ b/video/out/filter_kernels.c @@ -92,34 +92,45 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes, } } -// Sample from the blurred, windowed kernel. Note: The window is always -// stretched to the true radius, regardless of the filter blur/scale. -static double sample_filter(struct filter_kernel *filter, - struct filter_window *window, double x) +// Sample from a blurred and tapered window +static double sample_window(struct filter_window *kernel, double x) { - double bk = filter->f.blur > 0.0 ? filter->f.blur : 1.0; - double bw = window->blur > 0.0 ? window->blur : 1.0; - double c = fabs(x) / (filter->inv_scale * bk); - double w = window->weight ? window->weight(window, x/bw * window->radius - / filter->f.radius) - : 1.0; - double v = c < filter->f.radius ? w * filter->f.weight(&filter->f, c) : 0.0; - return filter->clamp ? fmax(0.0, fmin(1.0, v)) : v; + if (!kernel->weight) + return 1.0; + + // All windows are symmetric, this makes life easier + x = fabs(x); + if (x >= kernel->radius) + return 0.0; + + // Stretch and taper the window size as needed + x = kernel->blur > 0.0 ? x / kernel->blur : x; + x = x <= kernel->taper ? 0.0 : (x - kernel->taper) / (1 - kernel->taper); + + return kernel->weight(kernel, x); +} + +// Evaluate a filter's kernel and window at a given absolute position +static double sample_filter(struct filter_kernel *filter, double x) +{ + // The window is always stretched to the entire kernel + double w = sample_window(&filter->w, x / filter->f.radius * filter->w.radius); + double k = sample_window(&filter->f, x / filter->inv_scale); + return filter->clamp ? fmax(0.0, fmin(1.0, w * k)) : w * k; } // Calculate the 1D filtering kernel for N sample points. // N = number of samples, which is filter->size // The weights will be stored in out_w[0] to out_w[N - 1] // f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1]. -static void mp_compute_weights(struct filter_kernel *filter, - struct filter_window *window, - double f, float *out_w) +static void mp_compute_weights(struct filter_kernel *filter, double f, + float *out_w) { assert(filter->size > 0); double sum = 0; for (int n = 0; n < filter->size; n++) { double x = f - (n - filter->size / 2 + 1); - double w = sample_filter(filter, window, x); + double w = sample_filter(filter, x); out_w[n] = w; sum += w; } @@ -138,17 +149,16 @@ static void mp_compute_weights(struct filter_kernel *filter, // [0.5 / count, 1.0 - 0.5 / count]. void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array) { - struct filter_window *window = &filter->w; if (filter->polar) { // Compute a 1D array indexed by radius for (int x = 0; x < count; x++) { double r = x * filter->f.radius / (count - 1); - out_array[x] = sample_filter(filter, window, r); + out_array[x] = sample_filter(filter, r); } } else { // Compute a 2D array indexed by subpixel position for (int n = 0; n < count; n++) { - mp_compute_weights(filter, window, n / (double)(count - 1), + mp_compute_weights(filter, n / (double)(count - 1), out_array + filter->size * n); } } @@ -321,6 +331,7 @@ const struct filter_window mp_filter_windows[] = { {"triangle", 1, triangle}, {"bartlett", 1, triangle}, {"hanning", 1, hanning}, + {"tukey", 1, hanning, .taper = 0.5}, {"hamming", 1, hamming}, {"quadric", 1.5, quadric}, {"welch", 1, welch}, diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h index 2354ef4d0c..fc90a1cdde 100644 --- a/video/out/filter_kernels.h +++ b/video/out/filter_kernels.h @@ -22,6 +22,7 @@ struct filter_window { double params[2]; // User-defined custom filter parameters. Not used by // all filters double blur; // Blur coefficient (sharpens or widens the filter) + double taper; // Taper coefficient (flattens the filter's center) }; struct filter_kernel { diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h index 5abe839b8d..afb5b61f7e 100644 --- a/video/out/opengl/common.h +++ b/video/out/opengl/common.h @@ -36,6 +36,9 @@ #include <OpenGL/gl.h> #include <OpenGL/gl3.h> #include <OpenGL/glext.h> +#elif HAVE_IOS_GL +#include <OpenGLES/ES2/glext.h> +#include <OpenGLES/ES3/glext.h> #elif HAVE_ANDROID_GL #include <GLES3/gl3.h> #else diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c index 0f5b61e37c..fb3471cd3b 100644 --- a/video/out/opengl/context.c +++ b/video/out/opengl/context.c @@ -125,7 +125,7 @@ int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt, #if HAVE_C11_TLS #define MP_TLS _Thread_local -#elif defined(__GNUC__) +#elif HAVE_GCC_TLS #define MP_TLS __thread #endif diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c index 96c8199ef4..fa19a6c205 100644 --- a/video/out/opengl/context_rpi.c +++ b/video/out/opengl/context_rpi.c @@ -42,7 +42,7 @@ struct priv { EGL_DISPMANX_WINDOW_T egl_window; int x, y, w, h; double display_fps; - atomic_bool reload_display; + atomic_int reload_display; int win_params[4]; }; diff --git a/video/out/opengl/header_fixes.h b/video/out/opengl/header_fixes.h index 9953f7e497..9a7108dcda 100644 --- a/video/out/opengl/header_fixes.h +++ b/video/out/opengl/header_fixes.h @@ -92,6 +92,23 @@ #define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB #endif +#if HAVE_IOS_GL +#define GL_WRITE_ONLY GL_WRITE_ONLY_OES +#define GL_TEXTURE_1D 0x0DE0 +#define GL_R16 0x822A +#define GL_RG16 0x822C +#define GL_RGB10 0x8052 +#define GL_RGB16 0x8054 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_LUMINANCE8 GL_LUMINANCE8_EXT +#define GL_LUMINANCE8_ALPHA8 GL_LUMINANCE8_ALPHA8_EXT +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#endif + // GL_ARB_timer_query and EXT_disjoint_timer_query #ifndef GL_TIME_ELAPSED // Same as GL_TIME_ELAPSED_EXT diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 9461153615..498d89259e 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -104,6 +104,7 @@ struct texplane { struct video_image { struct texplane planes[4]; struct mp_image *mpi; // original input image + uint64_t id; // unique ID identifying mpi contents bool hwdec_mapped; }; @@ -153,6 +154,7 @@ struct tex_hook { struct fbosurface { struct fbotex fbotex; + uint64_t id; double pts; }; @@ -322,6 +324,7 @@ static const struct gl_video_opts gl_video_opts_def = { .target_brightness = 250, .hdr_tone_mapping = TONE_MAPPING_HABLE, .tone_mapping_param = NAN, + .early_flush = -1, }; static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt, @@ -337,7 +340,10 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt, OPT_FLOAT(n"-param1", scaler[i].kernel.params[0], 0), \ OPT_FLOAT(n"-param2", scaler[i].kernel.params[1], 0), \ OPT_FLOAT(n"-blur", scaler[i].kernel.blur, 0), \ + OPT_FLOATRANGE(n"-taper", scaler[i].kernel.taper, 0, 0.0, 1.0), \ OPT_FLOAT(n"-wparam", scaler[i].window.params[0], 0), \ + OPT_FLOAT(n"-wblur", scaler[i].window.blur, 0), \ + OPT_FLOATRANGE(n"-wtaper", scaler[i].window.taper, 0, 0.0, 1.0), \ OPT_FLAG(n"-clamp", scaler[i].clamp, 0), \ OPT_FLOATRANGE(n"-radius", scaler[i].radius, 0, 0.5, 16.0), \ OPT_FLOATRANGE(n"-antiring", scaler[i].antiring, 0, 0.0, 1.0), \ @@ -412,7 +418,8 @@ const struct m_sub_options gl_video_conf = { OPT_INTRANGE("opengl-tex-pad-x", tex_pad_x, 0, 0, 4096), OPT_INTRANGE("opengl-tex-pad-y", tex_pad_y, 0, 0, 4096), OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0), - OPT_FLAG("opengl-early-flush", early_flush, 0), + OPT_CHOICE("opengl-early-flush", early_flush, 0, + ({"no", 0}, {"yes", 1}, {"auto", -1})), {0} }, @@ -515,7 +522,8 @@ static void uninit_scaler(struct gl_video *p, struct scaler *scaler); static void check_gl_features(struct gl_video *p); static bool init_format(struct gl_video *p, int fmt, bool test_only); static void init_image_desc(struct gl_video *p, int fmt); -static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi); +static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi, + uint64_t id); static const char *handle_scaler_opt(const char *name, bool tscale); static void reinit_from_options(struct gl_video *p); static void get_scale_factors(struct gl_video *p, bool transpose_rot, double xy[2]); @@ -563,8 +571,10 @@ void gl_video_set_debug(struct gl_video *p, bool enable) static void gl_video_reset_surfaces(struct gl_video *p) { - for (int i = 0; i < FBOSURFACES_MAX; i++) + for (int i = 0; i < FBOSURFACES_MAX; i++) { + p->surfaces[i].id = 0; p->surfaces[i].pts = MP_NOPTS_VALUE; + } p->surface_idx = 0; p->surface_now = 0; p->frames_drawn = 0; @@ -951,6 +961,7 @@ static void unmap_current_image(struct gl_video *p) p->hwdec->driver->unmap(p->hwdec); memset(vimg->planes, 0, sizeof(vimg->planes)); vimg->hwdec_mapped = false; + vimg->id = 0; // needs to be mapped again } } @@ -958,6 +969,7 @@ static void unref_current_image(struct gl_video *p) { unmap_current_image(p); mp_image_unrefp(&p->image.mpi); + p->image.id = 0; } static void uninit_video(struct gl_video *p) @@ -1349,7 +1361,8 @@ static bool scaler_fun_eq(struct scaler_fun a, struct scaler_fun b) return ((!a.name && !b.name) || strcmp(a.name, b.name) == 0) && double_seq(a.params[0], b.params[0]) && double_seq(a.params[1], b.params[1]) && - a.blur == b.blur; + a.blur == b.blur && + a.taper == b.taper; } static bool scaler_conf_eq(struct scaler_config a, struct scaler_config b) @@ -1410,6 +1423,11 @@ static void reinit_scaler(struct gl_video *p, struct scaler *scaler, if (conf->window.blur > 0.0) scaler->kernel->w.blur = conf->window.blur; + if (conf->kernel.taper > 0.0) + scaler->kernel->f.taper = conf->kernel.taper; + if (conf->window.taper > 0.0) + scaler->kernel->w.taper = conf->window.taper; + if (scaler->kernel->f.resizable && conf->radius > 0.0) scaler->kernel->f.radius = conf->radius; @@ -1995,8 +2013,6 @@ static void pass_convert_yuv(struct gl_video *p) p->components = 3; if (!p->has_alpha || p->opts.alpha_mode == ALPHA_NO) { GLSL(color.a = 1.0;) - } else if (p->opts.alpha_mode == ALPHA_BLEND) { - GLSL(color = vec4(color.rgb * color.a, 1.0);) } else { // alpha present in image p->components = 4; GLSL(color = vec4(color.rgb * color.a, color.a);) @@ -2519,12 +2535,20 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo) pass_colormanage(p, p->image_params.color, false); - // Draw checkerboard pattern to indicate transparency - if (p->has_alpha && p->opts.alpha_mode == ALPHA_BLEND_TILES) { - GLSLF("// transparency checkerboard\n"); - GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy / 32.0), vec2(0.5));) - GLSL(vec3 background = vec3(tile.x == tile.y ? 1.0 : 0.75);) - GLSL(color.rgb = mix(background, color.rgb, color.a);) + if (p->has_alpha){ + if (p->opts.alpha_mode == ALPHA_BLEND_TILES) { + // Draw checkerboard pattern to indicate transparency + GLSLF("// transparency checkerboard\n"); + GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy / 32.0), vec2(0.5));) + GLSL(vec3 background = vec3(tile.x == tile.y ? 1.0 : 0.75);) + GLSL(color.rgb = mix(background, color.rgb, color.a);) + } else if (p->opts.alpha_mode == ALPHA_BLEND) { + // Blend into background color (usually black) + struct m_color c = p->opts.background; + GLSLF("vec4 background = vec4(%f, %f, %f, %f);\n", + c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0); + GLSL(color = mix(background, vec4(color.rgb, 1.0), color.a);) + } } pass_opt_hook_point(p, "OUTPUT", NULL); @@ -2550,22 +2574,23 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, // First of all, figure out if we have a frame available at all, and draw // it manually + reset the queue if not - if (p->surfaces[p->surface_now].pts == MP_NOPTS_VALUE) { - if (!gl_video_upload_image(p, t->current)) + if (p->surfaces[p->surface_now].id == 0) { + if (!gl_video_upload_image(p, t->current, t->frame_id)) return; pass_render_frame(p); finish_pass_fbo(p, &p->surfaces[p->surface_now].fbotex, vp_w, vp_h, FBOTEX_FUZZY); + p->surfaces[p->surface_now].id = p->image.id; p->surfaces[p->surface_now].pts = p->image.mpi->pts; p->surface_idx = p->surface_now; } // Find the right frame for this instant - if (t->current && t->current->pts != MP_NOPTS_VALUE) { + if (t->current) { int next = fbosurface_wrap(p->surface_now + 1); - while (p->surfaces[next].pts != MP_NOPTS_VALUE && - p->surfaces[next].pts > p->surfaces[p->surface_now].pts && - p->surfaces[p->surface_now].pts < t->current->pts) + while (p->surfaces[next].id && + p->surfaces[next].id > p->surfaces[p->surface_now].id && + p->surfaces[p->surface_now].id < t->frame_id) { p->surface_now = next; next = fbosurface_wrap(next + 1); @@ -2607,16 +2632,17 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, break; struct mp_image *f = t->frames[i]; - if (!mp_image_params_equal(&f->params, &p->real_image_params) || - f->pts == MP_NOPTS_VALUE) + uint64_t f_id = t->frame_id + i; + if (!mp_image_params_equal(&f->params, &p->real_image_params)) continue; - if (f->pts > p->surfaces[p->surface_idx].pts) { - if (!gl_video_upload_image(p, f)) + if (f_id > p->surfaces[p->surface_idx].id) { + if (!gl_video_upload_image(p, f, f_id)) return; pass_render_frame(p); finish_pass_fbo(p, &p->surfaces[surface_dst].fbotex, vp_w, vp_h, FBOTEX_FUZZY); + p->surfaces[surface_dst].id = f_id; p->surfaces[surface_dst].pts = f->pts; p->surface_idx = surface_dst; surface_dst = fbosurface_wrap(surface_dst + 1); @@ -2631,11 +2657,9 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, bool valid = true; for (int i = surface_bse, ii; valid && i != surface_end; i = ii) { ii = fbosurface_wrap(i + 1); - if (p->surfaces[i].pts == MP_NOPTS_VALUE || - p->surfaces[ii].pts == MP_NOPTS_VALUE) - { + if (p->surfaces[i].id == 0 || p->surfaces[ii].id == 0) { valid = false; - } else if (p->surfaces[ii].pts < p->surfaces[i].pts) { + } else if (p->surfaces[ii].id < p->surfaces[i].id) { valid = false; MP_DBG(p, "interpolation queue underrun\n"); } @@ -2656,8 +2680,8 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, // (which requires some extra checking to make sure it's valid) if (mix < 0.0) { int prev = fbosurface_wrap(surface_bse - 1); - if (p->surfaces[prev].pts != MP_NOPTS_VALUE && - p->surfaces[prev].pts < p->surfaces[surface_bse].pts) + if (p->surfaces[prev].id != 0 && + p->surfaces[prev].id < p->surfaces[surface_bse].id) { mix += 1.0; surface_bse = prev; @@ -2736,7 +2760,6 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); bool has_frame = !!frame->current; - bool is_new = has_frame && !frame->redraw && !frame->repeat; if (!has_frame || p->dst_rect.x0 > 0 || p->dst_rect.y0 > 0 || p->dst_rect.x1 < p->vp_w || p->dst_rect.y1 < abs(p->vp_h)) @@ -2758,7 +2781,7 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) gl->Disable(GL_SCISSOR_TEST); } - if (is_new || !frame->current) + if (frame->frame_id != p->image.id || !frame->current) p->hwdec->driver->overlay_frame(p->hwdec, frame->current); if (frame->current) @@ -2782,10 +2805,16 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) if (interpolate) { gl_video_interpolate_frame(p, frame, fbo); } else { + bool is_new = frame->frame_id != p->image.id; + + // Redrawing a frame might update subtitles. + if (frame->still && p->opts.blend_subs) + is_new = true; + if (is_new || !p->output_fbo_valid) { p->output_fbo_valid = false; - if (!gl_video_upload_image(p, frame->current)) + if (!gl_video_upload_image(p, frame->current, frame->frame_id)) goto done; pass_render_frame(p); @@ -2849,8 +2878,11 @@ done: // The playloop calls this last before waiting some time until it decides // to call flip_page(). Tell OpenGL to start execution of the GPU commands // while we sleep (this happens asynchronously). - if (p->opts.early_flush) + if ((p->opts.early_flush == -1 && !frame->display_synced) || + p->opts.early_flush == 1) + { gl->Flush(); + } p->frames_rendered++; @@ -2944,11 +2976,15 @@ static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame) } // Returns false on failure. -static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) +static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi, + uint64_t id) { GL *gl = p->gl; struct video_image *vimg = &p->image; + if (vimg->id == id) + return true; + unref_current_image(p); mpi = mp_image_new_ref(mpi); @@ -2956,6 +2992,7 @@ static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) goto error; vimg->mpi = mpi; + vimg->id = id; p->osd_pts = mpi->pts; p->frames_uploaded++; diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h index 5011af83d1..54b7022f27 100644 --- a/video/out/opengl/video.h +++ b/video/out/opengl/video.h @@ -35,6 +35,7 @@ struct scaler_fun { char *name; float params[2]; float blur; + float taper; }; struct scaler_config { diff --git a/video/out/vo.c b/video/out/vo.c index 4b28aadaa1..cbd2ca87c8 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -59,6 +59,7 @@ extern const struct vo_driver video_out_sdl; extern const struct vo_driver video_out_vaapi; extern const struct vo_driver video_out_wayland; extern const struct vo_driver video_out_rpi; +extern const struct vo_driver video_out_tct; const struct vo_driver *const video_out_drivers[] = { @@ -89,6 +90,7 @@ const struct vo_driver *const video_out_drivers[] = &video_out_null, // should not be auto-selected &video_out_image, + &video_out_tct, #if HAVE_CACA &video_out_caca, #endif @@ -399,7 +401,7 @@ static void vsync_skip_detection(struct vo *vo) } int64_t desync = diff / in->num_vsync_samples; if (in->drop_point > window * 2 && - labs(desync - desync_early) >= in->vsync_interval * 3 / 4) + llabs(desync - desync_early) >= in->vsync_interval * 3 / 4) { // Assume a drop. An underflow can technically speaking not be a drop // (it's up to the driver what this is supposed to mean), but no reason @@ -871,6 +873,7 @@ static void do_redraw(struct vo *vo) if (!frame) frame = &dummy; frame->redraw = !full_redraw; // unconditionally redraw if it was dropped + frame->repeat = false; frame->still = true; frame->pts = 0; frame->duration = -1; diff --git a/video/out/vo.h b/video/out/vo.h index d2393f829b..99e6ccabae 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -223,6 +223,7 @@ struct vo_frame { // a frame is guaranteed not to change (instant redraws will use the same // ID). frames[n] has the ID frame_id+n, with the guarantee that frame // drops or reconfigs will keep the guarantee. + // The ID is never 0 (unless num_frames==0). IDs are strictly monotonous. uint64_t frame_id; }; diff --git a/video/out/vo_direct3d.c b/video/out/vo_direct3d.c index 38dde55af2..c99ea372f6 100644 --- a/video/out/vo_direct3d.c +++ b/video/out/vo_direct3d.c @@ -1466,13 +1466,13 @@ static mp_image_t *get_window_screenshot(d3d_priv *priv) POINT pt; D3DLOCKED_RECT locked_rect; int width, height; + IDirect3DSurface9 *surface = NULL; if (FAILED(IDirect3DDevice9_GetDisplayMode(priv->d3d_device, 0, &mode))) { MP_ERR(priv, "GetDisplayMode failed.\n"); goto error_exit; } - IDirect3DSurface9 *surface = NULL; if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(priv->d3d_device, mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, NULL))) diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c index 20e9523cb0..c66f6d434c 100644 --- a/video/out/vo_opengl_cb.c +++ b/video/out/vo_opengl_cb.c @@ -296,7 +296,7 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h) int64_t wait_present_count = ctx->present_count; if (frame) { ctx->next_frame = NULL; - if (frame->redraw || !frame->current) + if (!(frame->redraw || !frame->current)) wait_present_count += 1; pthread_cond_signal(&ctx->wakeup); talloc_free(ctx->cur_frame); @@ -371,8 +371,10 @@ static void flip_page(struct vo *vo) // Wait until frame was rendered while (p->ctx->next_frame) { if (pthread_cond_timedwait(&p->ctx->wakeup, &p->ctx->lock, &ts)) { - MP_VERBOSE(vo, "mpv_opengl_cb_draw() not being called or stuck.\n"); - goto done; + if (p->ctx->next_frame) { + MP_VERBOSE(vo, "mpv_opengl_cb_draw() not being called or stuck.\n"); + goto done; + } } } @@ -399,7 +401,8 @@ done: // Cleanup after the API user is not reacting, or is being unusually slow. if (p->ctx->next_frame) { - talloc_free(p->ctx->next_frame); + talloc_free(p->ctx->cur_frame); + p->ctx->cur_frame = p->ctx->next_frame; p->ctx->next_frame = NULL; p->ctx->present_count += 2; pthread_cond_signal(&p->ctx->wakeup); diff --git a/video/out/vo_tct.c b/video/out/vo_tct.c new file mode 100644 index 0000000000..68b29e9bc8 --- /dev/null +++ b/video/out/vo_tct.c @@ -0,0 +1,306 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <unistd.h> +#include <config.h> + +#if HAVE_POSIX +#include <sys/ioctl.h> +#endif + +#include <libswscale/swscale.h> + +#include "options/m_config.h" +#include "config.h" +#include "vo.h" +#include "sub/osd.h" +#include "video/sws_utils.h" +#include "video/mp_image.h" + +#define IMGFMT IMGFMT_BGR24 + +#define ALGO_PLAIN 1 +#define ALGO_HALF_BLOCKS 2 +#define ESC_HIDE_CURSOR "\e[?25l" +#define ESC_RESTORE_CURSOR "\e[?25h" +#define ESC_CLEAR_SCREEN "\e[2J" +#define ESC_CLEAR_COLORS "\e[0m" +#define ESC_GOTOXY "\e[%d;%df" +#define ESC_COLOR_BG "\e[48;2;%d;%d;%dm" +#define ESC_COLOR_FG "\e[38;2;%d;%d;%dm" +#define ESC_COLOR256_BG "\e[48;5;%dm" +#define ESC_COLOR256_FG "\e[38;5;%dm" +#define DEFAULT_WIDTH 80 +#define DEFAULT_HEIGHT 25 + +struct vo_tct_opts { + int algo; + int width; // 0 -> default + int height; // 0 -> default + int term256; // 0 -> true color +}; + +#define OPT_BASE_STRUCT struct vo_tct_opts +static const struct m_sub_options vo_tct_conf = { + .opts = (const m_option_t[]) { + OPT_CHOICE("vo-tct-algo", algo, 0, + ({"plain", ALGO_PLAIN}, + {"half-blocks", ALGO_HALF_BLOCKS})), + OPT_INT("vo-tct-width", width, 0), + OPT_INT("vo-tct-height", height, 0), + OPT_FLAG("vo-tct-256", term256, 0), + {0} + }, + .defaults = &(const struct vo_tct_opts) { + .algo = ALGO_HALF_BLOCKS, + }, + .size = sizeof(struct vo_tct_opts), +}; + +struct priv { + struct vo_tct_opts *opts; + size_t buffer_size; + char *buffer; + int swidth; + int sheight; + struct mp_image *frame; + struct mp_rect src; + struct mp_rect dst; + struct mp_sws_context *sws; +}; + +// Convert RGB24 to xterm-256 8-bit value +// For simplicity, assume RGB space is perceptually uniform. +// There are 5 places where one of two outputs needs to be chosen when the +// input is the exact middle: +// - The r/g/b channels and the gray value: the higher value output is chosen. +// - If the gray and color have same distance from the input - color is chosen. +static int rgb_to_x256(uint8_t r, uint8_t g, uint8_t b) +{ + // Calculate the nearest 0-based color index at 16 .. 231 +# define v2ci(v) (v < 48 ? 0 : v < 115 ? 1 : (v - 35) / 40) + int ir = v2ci(r), ig = v2ci(g), ib = v2ci(b); // 0..5 each +# define color_index() (36 * ir + 6 * ig + ib) /* 0..215, lazy evaluation */ + + // Calculate the nearest 0-based gray index at 232 .. 255 + int average = (r + g + b) / 3; + int gray_index = average > 238 ? 23 : (average - 3) / 10; // 0..23 + + // Calculate the represented colors back from the index + static const int i2cv[6] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff}; + int cr = i2cv[ir], cg = i2cv[ig], cb = i2cv[ib]; // r/g/b, 0..255 each + int gv = 8 + 10 * gray_index; // same value for r/g/b, 0..255 + + // Return the one which is nearer to the original input rgb value +# define dist_square(A,B,C, a,b,c) ((A-a)*(A-a) + (B-b)*(B-b) + (C-c)*(C-c)) + int color_err = dist_square(cr, cg, cb, r, g, b); + int gray_err = dist_square(gv, gv, gv, r, g, b); + return color_err <= gray_err ? 16 + color_index() : 232 + gray_index; +} + +static void write_plain( + const int dwidth, const int dheight, + const int swidth, const int sheight, + const unsigned char *source, const int source_stride, + bool term256) +{ + assert(source); + const int tx = (dwidth - swidth) / 2; + const int ty = (dheight - sheight) / 2; + for (int y = 0; y < sheight; y++) { + const unsigned char *row = source + y * source_stride; + printf(ESC_GOTOXY, ty + y, tx); + for (int x = 0; x < swidth; x++) { + unsigned char b = *row++; + unsigned char g = *row++; + unsigned char r = *row++; + if (term256) { + printf(ESC_COLOR256_BG, rgb_to_x256(r, g, b)); + } else { + printf(ESC_COLOR_BG, r, g, b); + } + printf(" "); + } + printf(ESC_CLEAR_COLORS); + } + printf("\n"); +} + +static void write_half_blocks( + const int dwidth, const int dheight, + const int swidth, const int sheight, + unsigned char *source, int source_stride, + bool term256) +{ + assert(source); + const int tx = (dwidth - swidth) / 2; + const int ty = (dheight - sheight) / 2; + for (int y = 0; y < sheight * 2; y += 2) { + const unsigned char *row_up = source + y * source_stride; + const unsigned char *row_down = source + (y + 1) * source_stride; + printf(ESC_GOTOXY, ty + y / 2, tx); + for (int x = 0; x < swidth; x++) { + unsigned char b_up = *row_up++; + unsigned char g_up = *row_up++; + unsigned char r_up = *row_up++; + unsigned char b_down = |