aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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