summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUoti Urpala <uau@glyph.nonexistent.invalid>2009-11-15 04:39:22 +0200
committerUoti Urpala <uau@glyph.nonexistent.invalid>2009-11-15 12:45:58 +0200
commit201bef7ee150f9d852996b379c926ba0c47320d5 (patch)
treeb0e4e14a73b1d868b1f1a81c8ebdbaa3f0c8f8c9
parentb87ce8bc96657c0b2d9b7fc51ed4bc1661d53270 (diff)
downloadmpv-201bef7ee150f9d852996b379c926ba0c47320d5.tar.bz2
mpv-201bef7ee150f9d852996b379c926ba0c47320d5.tar.xz
Implement vsync-aware frame timing for VDPAU
Main things added are custom frame dropping for VDPAU to work around the display FPS limit, frame timing adjustment to avoid jitter when video frame times keep falling near vsyncs, and use of VDPAU's timing feature to keep one future frame queued in advance. NVIDIA's VDPAU implementation refuses to change the displayed frame more than once per vsync. This set a limit on how much video could be sped up, and caused problems for nearly all videos on low-FPS video projectors (playing 24 FPS video on a 24 FPS projector would not work reliably as MPlayer may need to slightly speed up the video for AV sync). This commit adds a framedrop mechanism that drops some frames so that no more than one is sent for display per vsync. The code tries to select the dropped frames smartly, selecting the best one to show for each vsync. Because of the timing features needed the drop functionality currently does not work if the correct-pts option is disabled. The code also adjusts frame timing slightly to avoid jitter. If you for example play 24 FPS video content on a 72 FPS display then normally a frame would be shown for 3 vsyncs, but if the frame times happen to fall near vsyncs and change between just before and just after then there could be frames alternating between 2 and 4 vsyncs. The code changes frame timing by up to one quarter vsync interval to avoid this. The above functionality depends on having reliable vsync timing information available. The display refresh rate is not directly provided by the VDPAU API. The current code uses information from the XF86VidMode extension if available; I'm not sure how common cases where that is inaccurate are. The refresh rate can be specified manually if necessary. After the changes in this commit MPlayer now always tries to keep one frame queued for future display using VDPAU's internal timing mechanism (however no more than 50 ms to the future). This should make video playback somewhat more robust against timing inaccuracies caused by system load.
-rw-r--r--DOCS/man/en/mplayer.15
-rw-r--r--TOOLS/vdpau_functions.py2
-rw-r--r--libmenu/vf_menu.c2
-rw-r--r--libvo/vdpau_template.c2
-rw-r--r--libvo/video_out.c7
-rw-r--r--libvo/video_out.h3
-rw-r--r--libvo/vo_vdpau.c230
-rw-r--r--libvo/x11_common.c12
-rw-r--r--libvo/x11_common.h1
-rw-r--r--mencoder.c2
-rw-r--r--mplayer.c30
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
diff --git a/mplayer.c b/mplayer.c
index c306c73b4f..b508dc3661 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -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