diff options
-rw-r--r-- | DOCS/man/en/mplayer.1 | 5 | ||||
-rw-r--r-- | TOOLS/vdpau_functions.py | 2 | ||||
-rw-r--r-- | libmenu/vf_menu.c | 2 | ||||
-rw-r--r-- | libvo/vdpau_template.c | 2 | ||||
-rw-r--r-- | libvo/video_out.c | 7 | ||||
-rw-r--r-- | libvo/video_out.h | 3 | ||||
-rw-r--r-- | libvo/vo_vdpau.c | 230 | ||||
-rw-r--r-- | libvo/x11_common.c | 12 | ||||
-rw-r--r-- | libvo/x11_common.h | 1 | ||||
-rw-r--r-- | mencoder.c | 2 | ||||
-rw-r--r-- | mplayer.c | 30 |
11 files changed, 276 insertions, 20 deletions
diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index 29cbff4a4f..6c832c0217 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -3442,6 +3442,11 @@ Use nochroma\-deint to solely use luma and speed up advanced deinterlacing. Useful with slow video memory. .IPs pullup Try to apply inverse telecine, needs motion adaptive temporal deinterlacing. +.IPs fps=<number> +Override autodetected display refresh rate value (the value is needed for framedrop to allow video playback rates higher than display refresh rate, and for vsync-aware frame timing adjustments). +Default 0 means use autodetected value. +A positive value is interpreted as a refresh rate in Hz and overrides the autodetected value. +A negative value disables all timing adjustment and framedrop logic. .RE .PD 1 . diff --git a/TOOLS/vdpau_functions.py b/TOOLS/vdpau_functions.py index e628cb00c3..4a9197e0dd 100644 --- a/TOOLS/vdpau_functions.py +++ b/TOOLS/vdpau_functions.py @@ -25,6 +25,8 @@ presentation_queue_block_until_surface_idle presentation_queue_create presentation_queue_destroy presentation_queue_display +presentation_queue_get_time +presentation_queue_query_surface_status presentation_queue_target_create_x11 presentation_queue_target_destroy video_mixer_create diff --git a/libmenu/vf_menu.c b/libmenu/vf_menu.c index f8ceecc9ca..7f8ae84aca 100644 --- a/libmenu/vf_menu.c +++ b/libmenu/vf_menu.c @@ -62,7 +62,7 @@ void vf_menu_pause_update(struct vf_instance* vf) { put_image(vf,pause_mpi, MP_NOPTS_VALUE); // Don't draw the osd atm //vf->control(vf,VFCTRL_DRAW_OSD,NULL); - vo_flip_page(video_out); + vo_flip_page(video_out, 0, -1); } } diff --git a/libvo/vdpau_template.c b/libvo/vdpau_template.c index ca1a6f6056..1b8d354316 100644 --- a/libvo/vdpau_template.c +++ b/libvo/vdpau_template.c @@ -27,6 +27,8 @@ VDP_FUNCTION(VdpPresentationQueueBlockUntilSurfaceIdle, VDP_FUNC_ID_PRESENTATION VDP_FUNCTION(VdpPresentationQueueCreate, VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, presentation_queue_create) VDP_FUNCTION(VdpPresentationQueueDestroy, VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY, presentation_queue_destroy) VDP_FUNCTION(VdpPresentationQueueDisplay, VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, presentation_queue_display) +VDP_FUNCTION(VdpPresentationQueueGetTime, VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME, presentation_queue_get_time) +VDP_FUNCTION(VdpPresentationQueueQuerySurfaceStatus, VDP_FUNC_ID_PRESENTATION_QUEUE_QUERY_SURFACE_STATUS, presentation_queue_query_surface_status) VDP_FUNCTION(VdpPresentationQueueTargetCreateX11, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11, presentation_queue_target_create_x11) VDP_FUNCTION(VdpPresentationQueueTargetDestroy, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY, presentation_queue_target_destroy) VDP_FUNCTION(VdpVideoMixerCreate, VDP_FUNC_ID_VIDEO_MIXER_CREATE, video_mixer_create) diff --git a/libvo/video_out.c b/libvo/video_out.c index 615cfe57f4..eb3d0183d6 100644 --- a/libvo/video_out.c +++ b/libvo/video_out.c @@ -328,13 +328,16 @@ void vo_draw_osd(struct vo *vo, struct osd_state *osd) vo->driver->draw_osd(vo, osd); } -void vo_flip_page(struct vo *vo) +void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration) { if (!vo->config_ok) return; vo->frame_loaded = false; vo->next_pts = MP_NOPTS_VALUE; - vo->driver->flip_page(vo); + if (vo->driver->flip_page_timed) + vo->driver->flip_page_timed(vo, pts_us, duration); + else + vo->driver->flip_page(vo); } void vo_check_events(struct vo *vo) diff --git a/libvo/video_out.h b/libvo/video_out.h index 9e6de0842e..50602e83a4 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -200,6 +200,7 @@ struct vo_driver { * Blit/Flip buffer to the screen. Must be called after each frame! */ void (*flip_page)(struct vo *vo); + void (*flip_page_timed)(struct vo *vo, unsigned int pts_us, int duration); /* * This func is called after every frames to handle keyboard and @@ -277,7 +278,7 @@ int vo_get_buffered_frame(struct vo *vo, bool eof); int vo_draw_frame(struct vo *vo, uint8_t *src[]); int vo_draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y); void vo_draw_osd(struct vo *vo, struct osd_state *osd); -void vo_flip_page(struct vo *vo); +void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration); void vo_check_events(struct vo *vo); void vo_seek_reset(struct vo *vo); void vo_destroy(struct vo *vo); diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index 2f45003f21..6eef93842f 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -2,6 +2,7 @@ * VDPAU video output driver * * Copyright (C) 2008 NVIDIA + * Copyright (C) 2009 Uoti Urpala * * This file is part of MPlayer. * @@ -32,6 +33,7 @@ #include <dlfcn.h> #include <stdint.h> #include <stdbool.h> +#include <limits.h> #include "config.h" #include "mp_msg.h" @@ -72,7 +74,7 @@ } while (0) /* number of video and output surfaces */ -#define NUM_OUTPUT_SURFACES 2 +#define NUM_OUTPUT_SURFACES 3 #define MAX_VIDEO_SURFACES 50 #define NUM_BUFFERED_VIDEO 4 @@ -105,6 +107,8 @@ struct vdpctx { VdpPresentationQueueTarget flip_target; VdpPresentationQueue flip_queue; + uint64_t last_vdp_time; + unsigned int last_sync_update; void *vdpau_lib_handle; @@ -138,6 +142,13 @@ struct vdpctx { struct vdpau_render_state surface_render[MAX_VIDEO_SURFACES]; int surface_num; + VdpTime recent_vsync_time; + float user_fps; + unsigned int vsync_interval; + uint64_t last_queue_time; + uint64_t last_ideal_time; + bool dropped_frame; + uint64_t dropped_time; uint32_t vid_width, vid_height; uint32_t image_format; VdpChromaType vdp_chroma_type; @@ -172,15 +183,57 @@ struct vdpctx { // Video equalizer VdpProcamp procamp; - bool visible_buf; + int num_shown_frames; bool paused; // These tell what's been initialized and uninit() should free/uninitialize bool mode_switched; }; +static int change_vdptime_sync(struct vdpctx *vc, unsigned int *t) +{ + struct vdp_functions *vdp = vc->vdp; + VdpStatus vdp_st; + VdpTime vdp_time; + vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time); + CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time"); + unsigned int t1 = *t; + unsigned int t2 = GetTimer(); + uint64_t old = vc->last_vdp_time + (t1 - vc->last_sync_update) * 1000ULL; + if (vdp_time > old) + if (vdp_time > old + (t2 - t1) * 1000ULL) + vdp_time -= (t2 - t1) * 1000ULL; + else + vdp_time = old; + mp_msg(MSGT_VO, MSGL_V, "[vdpau] adjusting VdpTime offset by %f µs\n", + (int64_t)(vdp_time - old) / 1000.); + vc->last_vdp_time = vdp_time; + vc->last_sync_update = t1; + *t = t2; + return 0; +} + +static uint64_t sync_vdptime(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + + unsigned int t = GetTimer(); + if (t - vc->last_sync_update > 5000000) + change_vdptime_sync(vc, &t); + uint64_t now = (t - vc->last_sync_update) * 1000ULL + vc->last_vdp_time; + // Make sure nanosecond inaccuracies don't make things inconsistent + now = FFMAX(now, vc->recent_vsync_time); + return now; +} + +static uint64_t convert_to_vdptime(struct vo *vo, unsigned int t) +{ + struct vdpctx *vc = vo->priv; + return (int)(t - vc->last_sync_update) * 1000LL + vc->last_vdp_time; +} + +static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration); -static void flip_page(struct vo *vo); static int video_to_output_surface(struct vo *vo) { struct vdpctx *vc = vo->priv; @@ -292,6 +345,7 @@ static void forget_frames(struct vo *vo) struct vdpctx *vc = vo->priv; vc->deint_queue_pos = -1001; + vc->dropped_frame = false; for (int i = 0; i < NUM_BUFFERED_VIDEO; i++) { struct buffered_video_surface *p = vc->buffered_video + i; if (p->mpi) @@ -329,6 +383,7 @@ static void resize(struct vo *vo) #endif vo_osd_changed(OSDTYPE_OSD); + bool had_frames = vc->num_shown_frames; if (vc->output_surface_width < vo->dwidth || vc->output_surface_height < vo->dheight) { if (vc->output_surface_width < vo->dwidth) { @@ -354,10 +409,11 @@ static void resize(struct vo *vo) mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n", vc->output_surfaces[i]); } + vc->num_shown_frames = 0; } - if (vc->paused && vc->visible_buf) + if (vc->paused && had_frames) if (video_to_output_surface(vo) >= 0) - flip_page(vo); + flip_page_timed(vo, 0, -1); } static void preemption_callback(VdpDevice device, void *context) @@ -448,6 +504,40 @@ static int win_x11_init_vdpau_flip_queue(struct vo *vo) CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create"); } + VdpTime vdp_time; + vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time); + CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time"); + vc->last_vdp_time = vdp_time; + vc->last_sync_update = GetTimer(); + + vc->vsync_interval = 1; + if (vc->user_fps > 0) { + vc->vsync_interval = 1e9 / vc->user_fps; + mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Assuming user-specified display " + "refresh rate of %.3f Hz.\n", vc->user_fps); + } else if (vc->user_fps == 0) { +#ifdef CONFIG_XF86VM + double fps = vo_vm_get_fps(vo); + if (!fps) + mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Failed to get display FPS\n"); + else { + vc->vsync_interval = 1e9 / fps; + // This is verbose, but I'm not yet sure how common wrong values are + mp_msg(MSGT_VO, MSGL_INFO, + "[vdpau] Got display refresh rate %.3f Hz.\n" + "[vdpau] If that value looks wrong give the " + "-vo vdpau:fps=X suboption manually.\n", fps); + } +#else + mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] This binary has been compiled " + "without XF86VidMode support.\n"); + mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Can't use vsync-aware timing " + "without manually provided -vo vdpau:fps=X suboption.\n"); +#endif + } else + mp_msg(MSGT_VO, MSGL_V, "[vdpau] framedrop/timing logic disabled by " + "user.\n"); + return 0; } @@ -631,7 +721,6 @@ static int initialize_vdpau_objects(struct vo *vo) &vc->eosd_surface.max_width, &vc->eosd_surface.max_height); CHECK_ST_WARNING("Query to get max EOSD surface size failed"); - vc->surface_num = 0; forget_frames(vo); resize(vo); return 0; @@ -656,7 +745,7 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo) }; vc->output_surface_width = vc->output_surface_height = -1; vc->eosd_render_count = 0; - vc->visible_buf = false; + vc->num_shown_frames = 0; } static int handle_preemption(struct vo *vo) @@ -775,7 +864,7 @@ static void check_events(struct vo *vo) resize(vo); else if (e & VO_EVENT_EXPOSE && vc->paused) { /* did we already draw a buffer */ - if (vc->visible_buf) { + if (vc->num_shown_frames) { /* redraw the last visible buffer */ VdpStatus vdp_st; int last_surface = (vc->surface_num + NUM_OUTPUT_SURFACES - 1) @@ -1093,23 +1182,135 @@ static void draw_osd(struct vo *vo, struct osd_state *osd) vc->vid_height, draw_osd_I8A8, vo); } -static void flip_page(struct vo *vo) +static void wait_for_previous_frame(struct vo *vo) { struct vdpctx *vc = vo->priv; struct vdp_functions *vdp = vc->vdp; VdpStatus vdp_st; + if (vc->num_shown_frames < 2) + return; + + VdpTime vtime; + VdpOutputSurface visible_s, prev_s; + int base = vc->surface_num + NUM_OUTPUT_SURFACES; + visible_s = vc->output_surfaces[(base - 1) % NUM_OUTPUT_SURFACES]; + prev_s = vc->output_surfaces[(base - 2) % NUM_OUTPUT_SURFACES]; + vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue, + prev_s, &vtime); + CHECK_ST_WARNING("Error calling " + "presentation_queue_block_until_surface_idle"); + VdpPresentationQueueStatus status; + vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue, + visible_s, + &status, &vtime); + CHECK_ST_WARNING("Error calling presentation_queue_query_surface_status"); + vc->recent_vsync_time = vtime; +} + +static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift) +{ + uint64_t offset = ts - vc->recent_vsync_time; + // Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time + offset += (uint64_t)vc->vsync_interval << shift; + offset %= vc->vsync_interval; + return ts - offset; +} + +static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration) +{ + struct vdpctx *vc = vo->priv; + struct vdp_functions *vdp = vc->vdp; + VdpStatus vdp_st; + uint32_t vsync_interval = vc->vsync_interval; + if (handle_preemption(vo) < 0) return; + if (duration > INT_MAX / 1000) + duration = -1; + else + duration *= 1000; + + if (vc->user_fps < 0) + duration = -1; // Make sure drop logic is disabled + + uint64_t now = sync_vdptime(vo); + uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now; + uint64_t ideal_pts = pts; + uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX; + +#define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift) + // Only gives accurate results for ts >= vc->recent_vsync_time +#define PREV_VSYNC(ts) PREV_VS2(ts, 0) + + /* We hope to be here at least one vsync before the frame should be shown. + * If we are running late then don't drop the frame unless there is + * already one queued for the next vsync; even if we _hope_ to show the + * next frame soon enough to mean this one should be dropped we might + * not make the target time in reality. Without this check we could drop + * every frame, freezing the display completely if video lags behind. + */ + if (now > PREV_VSYNC(FFMAX(pts, + vc->last_queue_time + vsync_interval))) + npts = UINT64_MAX; + + /* Allow flipping a frame at a vsync if its presentation time is a + * bit after that vsync and the change makes the flip time delta + * from previous frame better match the target timestamp delta. + * This avoids instability with frame timestamps falling near vsyncs. + * For example if the frame timestamps were (with vsyncs at + * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then + * straightforward timing at next vsync would flip the frames at + * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with + * regular 2-vsync intervals. + * + * Also allow moving the frame forward if it looks like we dropped + * the previous frame incorrectly (now that we know better after + * having final exact timestamp information for this frame) and + * there would unnecessarily be a vsync without a frame change. + */ + uint64_t vsync = PREV_VSYNC(pts); + if (pts < vsync + vsync_interval / 4 + && (vsync - PREV_VS2(vc->last_queue_time, 16) + > pts - vc->last_ideal_time + vsync_interval / 2 + || vc->dropped_frame && vsync > vc->dropped_time)) + pts -= vsync_interval / 2; + + vc->dropped_frame = true; // changed at end if false + vc->dropped_time = ideal_pts; + + pts = FFMAX(pts, vc->last_queue_time + vsync_interval); + pts = FFMAX(pts, now); + if (npts < PREV_VSYNC(pts) + vsync_interval) + return; + + /* At least on my NVIDIA 9500GT with driver versions 185.18.36 and 190.42 + * trying to queue two unshown frames simultaneously caused bad behavior + * (high CPU use in _other_ VDPAU functions called later). Avoiding + * longer queues also makes things simpler. So currently we always + * try to keep exactly one frame queued for the future, queuing the + * current frame immediately after the previous one is shown. + */ + wait_for_previous_frame(vo); + + now = sync_vdptime(vo); + pts = FFMAX(pts, now); + vsync = PREV_VSYNC(pts); + if (npts < vsync + vsync_interval) + return; + pts = vsync + (vsync_interval >> 2); vdp_st = vdp->presentation_queue_display(vc->flip_queue, vc->output_surfaces[vc->surface_num], - vo->dwidth, vo->dheight, 0); + vo->dwidth, vo->dheight, pts); CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display"); + vc->last_queue_time = pts; + vc->last_ideal_time = ideal_pts; + vc->dropped_frame = false; vc->surface_num = (vc->surface_num + 1) % NUM_OUTPUT_SURFACES; - vc->visible_buf = true; + vc->num_shown_frames = FFMIN(vc->num_shown_frames + 1, 1000); } static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w, @@ -1348,6 +1549,7 @@ static int preinit(struct vo *vo, const char *arg) {"pullup", OPT_ARG_BOOL, &vc->pullup, NULL}, {"denoise", OPT_ARG_FLOAT, &vc->denoise, NULL}, {"sharpen", OPT_ARG_FLOAT, &vc->sharpen, NULL}, + {"fps", OPT_ARG_FLOAT, &vc->user_fps, NULL}, {NULL} }; if (subopt_parse(arg, subopts) != 0) { @@ -1478,6 +1680,8 @@ static int control(struct vo *vo, uint32_t request, void *data) } return VO_TRUE; case VOCTRL_PAUSE: + if (vc->dropped_frame) + flip_page_timed(vo, 0, -1); return (vc->paused = true); case VOCTRL_RESUME: return (vc->paused = false); @@ -1538,7 +1742,7 @@ static int control(struct vo *vo, uint32_t request, void *data) video_to_output_surface(vo); draw_eosd(vo); draw_osd(vo, data); - flip_page(vo); + flip_page_timed(vo, 0, -1); return true; case VOCTRL_RESET: forget_frames(vo); @@ -1563,7 +1767,7 @@ const struct vo_driver video_out_vdpau = { .get_buffered_frame = get_buffered_frame, .draw_slice = draw_slice, .draw_osd = draw_osd, - .flip_page = flip_page, + .flip_page_timed = flip_page_timed, .check_events = check_events, .uninit = uninit, }; diff --git a/libvo/x11_common.c b/libvo/x11_common.c index a9de90d350..621880e137 100644 --- a/libvo/x11_common.c +++ b/libvo/x11_common.c @@ -1642,6 +1642,18 @@ void vo_vm_close(struct vo *vo) vidmodes = NULL; } } + +double vo_vm_get_fps(struct vo *vo) +{ + struct vo_x11_state *x11 = vo->x11; + int clock; + XF86VidModeModeLine modeline; + if (!XF86VidModeGetModeLine(x11->display, x11->screen, &clock, &modeline)) + return 0; + if (modeline.privsize) + Xfree(modeline.private); + return 1e3 * clock / modeline.htotal / modeline.vtotal; +} #endif #endif /* X11_FULLSCREEN */ diff --git a/libvo/x11_common.h b/libvo/x11_common.h index a610f813bb..fccad90ced 100644 --- a/libvo/x11_common.h +++ b/libvo/x11_common.h @@ -164,6 +164,7 @@ void vo_x11_putkey(struct vo *vo, int key); #ifdef CONFIG_XF86VM void vo_vm_switch(struct vo *vo); void vo_vm_close(struct vo *vo); +double vo_vm_get_fps(struct vo *vo); #endif void update_xinerama_info(struct vo *vo); diff --git a/mencoder.c b/mencoder.c index 7f4094f663..78c8494966 100644 --- a/mencoder.c +++ b/mencoder.c @@ -208,7 +208,7 @@ int vo_draw_image(struct vo *vo, struct mp_image *mpi, double pts) { abort(); } int vo_draw_frame(struct vo *vo, uint8_t *src[]) { abort(); } int vo_draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y) { abort(); } void vo_draw_osd(struct vo *vo, struct osd_state *osd) { abort(); } -void vo_flip_page(struct vo *vo) { abort(); } +void vo_flip_page(struct vo *vo, uint32_t pts_us, int duration) { abort(); } void vo_check_events(struct vo *vo) { abort(); } // Needed by getch2 @@ -1180,6 +1180,8 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) a_pos = playing_audio_pts(mpctx); if (mpctx->sh_audio && sh_video && at_frame) { mpctx->last_av_difference = a_pos - sh_video->pts - audio_delay; + if (mpctx->time_frame > 0) + mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed; if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50 && !mpctx->drop_message_shown) { mp_tmsg(MSGT_AVSYNC,MSGL_WARN,SystemTooSlow); @@ -2104,9 +2106,13 @@ static int sleep_until_update(struct MPContext *mpctx, float *time_frame, //============================== SLEEP: =================================== + if (mpctx->video_out->driver->flip_page_timed) + *time_frame -= 0.05; // flag 256 means: libvo driver does its timing (dvb card) if (*time_frame > 0.001 && !(mpctx->sh_video->output_flags&256)) *time_frame = timing_sleep(mpctx, *time_frame); + if (mpctx->video_out->driver->flip_page_timed) + *time_frame += 0.05; return frame_time_remaining; } @@ -4044,11 +4050,31 @@ if(!mpctx->sh_video) { current_module="flip_page"; if (!frame_time_remaining && blit_frame) { unsigned int t2=GetTimer(); - - vo_flip_page(mpctx->video_out); + unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; + int duration = -1; + double pts2 = mpctx->video_out->next_pts2; + if (pts2 != MP_NOPTS_VALUE && opts->correct_pts) { + // expected A/V sync correction is ignored + double diff = (pts2 - mpctx->sh_video->pts); + diff /= opts->playback_speed; + if (mpctx->time_frame < 0) + diff += mpctx->time_frame; + if (diff < 0) + diff = 0; + if (diff > 10) + diff = 10; + duration = diff * 1e6; + } + vo_flip_page(mpctx->video_out, pts_us|1, duration); mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001; vout_time_usage += mpctx->last_vo_flip_duration; + if (mpctx->video_out->driver->flip_page_timed) { + // No need to adjust sync based on flip speed + mpctx->last_vo_flip_duration = 0; + // For print_status - VO call finishing early is OK for sync + mpctx->time_frame -= get_relative_time(mpctx); + } print_status(mpctx, MP_NOPTS_VALUE, true); } else |