[MPlayer-dev-eng] [PATCH] vdpau fixes and timing enhancements

Dan Oscarsson Dan.Oscarsson at tietoenator.com
Sat Apr 18 16:02:30 CEST 2009


Hi

Here is a patch for my current version of fixes and enhancements to
using vdpau.

I have been using them for many weeks using the overlay engine for
display without problems. As I only have two systems (of which one is
using a lcd tv for display) to test on, there may be things working
differently on other systems with other graphic cards.

I have not included documentation updates. I will do that if you think
my fixes/enhancements are ok.

The patches include:
  - an option to vdpau: max-delay
    which if set to 0 results in 2 output surfaces and working
    like current code.
    if set will drop frames if a frame would be delayed longer
    then given value. the number of output surfaces will be adjusted to
    match.
    by default will select suitable delay (will be larger if
    de-interlacing is used).

  - an option to vdpau: time-corr
    which measures card time and prints correction value at end of
    movie. To be used with option: card-time-correction

  - a global option: vdpauopts
    that can be used to set max-delay or time-corr for use when
    vo vdpau is selected. This what you can change the default
    for vdpau without having to select -vo vdpau
    Useful to have in your .mplayer/config

  - fixes so resizing window do not flash a lot. Though there are
    some when using blit engine still, none with overlay.

  - a global option: match-vsync-speed
    that will change speed of movie to vsync speed (if nearly the same).
    this allows movies to be shown with less frame dropping.

  - a global option: card-time-correction
    that gives correction that is needed to correct that graphics card
    have a time that have a different speed then system time.

  - on status line, info about dropped frames by vo is added if that
    occur. Like this:
    A:   7.8 V:   7.8 A-V: -0.000 ct:  0.000   0/  0  5%  1%  1.9% 0 0 7
    where the 7 at the end shows frames dropped by vo.

The defaults are:
    max-delay = 1.3 if normal, 1.5 if de-interlacing

    mplayer -vo vdpau:max-delay=0
    gives same handling as before (2 output surfaces, waiting for idle
    surface).

    The default mplayer -vo vdpau:max-delay=1.3
    gives 4 output surfaces and frames will be dropped if more than 1.3
    vsync intervals are needed before the frame will be shown.
    You will probably only notice a difference when running a movie
    at same rate or higher then screen refresh rate.

    To avoid frame drops when rate is nearly the same as display,
    like running a 24hz movie on a lcd tv at 24hz, you can have
    mplayer adjust speed to match display rate.
    mplayer -match-vsync-speed
    In this case, if movie fps is near (about 5%) of vsync rate,
    playback speed will be adjusted to match vsync rate.
    Pressing backspace to adjust speed to 1.0 will change speed
    back to adjusted speed.
    Have not tested this with blit engine, works fine with overlay.
    Measuring vsync interval when blit engine is used is less
    accurate so may not be good enough.
    My tests show that time speed is different on graphics card
    compared to system time. To fix this, you run a full movie
    with -vo vdpau:time-corr and records the value printed
    when movie ends. You then add
    mplayer -card-time-correction printed-value (or in your
    config-file) which corrects the speed matching.
    Using this I can view a movie without frame drops.

Notes on my changes to the code:

In mplayer.c I have changed order of audio decode and video decode
so that video decode is done first. This is due to when starting
decoding of video it takes some time (especially when needing to
measure vsync interval) and that will make audio start out of
sync unless video decode is done first.

In osdep I have added a get_timer call (only for linux for now)
that returns current time in 64-bit value. You cannot use the
GetTimer call for timing over long time, as it is only 32-bit and
will wrap around.

In vo_vdpau.c the code includes code to:
 - time the vsync interval
 - flashing fixes
 - variable number of output surfaces
 - card time correction calculation


Hope they are interesting enough to include now.

    Dan
    

-------------- next part --------------
--- mplayer.c.org	2009-04-18 15:13:38.000000000 +0200
+++ mplayer.c	2009-04-18 15:14:50.000000000 +0200
@@ -216,6 +216,8 @@
 static int output_quality=0;
 
 float playback_speed=1.0;
+float matched_playback_speed=0;
+float card_time_correction=0;
 
 int use_gui=0;
 
@@ -302,6 +304,8 @@
 static int play_n_frames=-1;
 static int play_n_frames_mf=-1;
 
+       int match_vsync_speed=0; // match speed to vsync speed
+
 // screen info:
 char** video_driver_list=NULL;
 char** audio_driver_list=NULL;
@@ -1255,6 +1259,9 @@
   if (sh_video) 
     saddf(line, &pos, width, "%d %d ", drop_frame_cnt, output_quality);
 
+  if (sh_video && vo_drop_frame_cnt)
+    saddf(line, &pos, width, "%d ", vo_drop_frame_cnt);
+
 #ifdef CONFIG_STREAM_CACHE
   // cache stats
   if (stream_cache_size > 0)
@@ -2514,6 +2521,7 @@
 int i;
 
 int gui_no_filename=0;
+int vsynced_speed = 0;
 
   InitTimer();
   srand(GetTimerMS());
@@ -3656,6 +3664,55 @@
   reinit_audio_chain();
 }
 
+if (match_vsync_speed && vo_vsync_interval > 0) {
+    if (!vsynced_speed) {
+        float fps_interval, vo_interval;
+
+        fps_interval = 1000000.0f/vo_fps;
+        vo_interval  = vo_vsync_interval/1000.0f;
+        if (fps_interval*0.95 < vo_interval && vo_interval < fps_interval*1.05) {
+            matched_playback_speed = fps_interval/vo_interval;
+            mp_msg(MSGT_CPLAYER,MSGL_INFO,"Matching speed to vsync %3.10f\n",matched_playback_speed);
+        } else if (fps_interval/2*0.95 < vo_interval && vo_interval < fps_interval/2*1.05) {        
+            matched_playback_speed = fps_interval/2/vo_interval;
+            mp_msg(MSGT_CPLAYER,MSGL_INFO,"Matching speed to double vsync rate %3.10f\n",matched_playback_speed);
+        } else {
+            mp_msg(MSGT_CPLAYER,MSGL_INFO,"Vsync rate to far from video rate, no matching of speed\n");
+        }
+        if (matched_playback_speed) {
+            if (card_time_correction) matched_playback_speed *= card_time_correction;
+            playback_speed = matched_playback_speed;
+            build_afilter_chain(mpctx->sh_audio, &ao_data);
+        }
+        vsynced_speed = 1;
+    }
+}
+
+
+
+/*========================== PLAY VIDEO ============================*/
+
+if(mpctx->sh_video) {
+  vo_pts=mpctx->sh_video->timer*90000.0;
+  vo_fps=mpctx->sh_video->fps;
+
+  if (!mpctx->num_buffered_frames) {
+      double frame_time = update_video(&blit_frame);
+      mp_dbg(MSGT_AVSYNC,MSGL_DBG2,"*** ftime=%5.3f ***\n",frame_time);
+      if (mpctx->sh_video->vf_initialized < 0) {
+	  mp_msg(MSGT_CPLAYER,MSGL_FATAL, MSGTR_NotInitializeVOPorVO);
+	  mpctx->eof = 1; goto goto_next_file;
+      }
+      if (frame_time < 0)
+	  mpctx->eof = 1;
+      else {
+	  // might return with !eof && !blit_frame if !correct_pts
+	  mpctx->num_buffered_frames += blit_frame;
+	  time_frame += frame_time / playback_speed;  // for nosound
+      }
+  }
+}
+
 /*========================== PLAY AUDIO ============================*/
 
 if (mpctx->sh_audio)
@@ -3681,28 +3738,6 @@
   update_osd_msg();
 
 } else {
-
-/*========================== PLAY VIDEO ============================*/
-
-  vo_pts=mpctx->sh_video->timer*90000.0;
-  vo_fps=mpctx->sh_video->fps;
-
-  if (!mpctx->num_buffered_frames) {
-      double frame_time = update_video(&blit_frame);
-      mp_dbg(MSGT_AVSYNC,MSGL_DBG2,"*** ftime=%5.3f ***\n",frame_time);
-      if (mpctx->sh_video->vf_initialized < 0) {
-	  mp_msg(MSGT_CPLAYER,MSGL_FATAL, MSGTR_NotInitializeVOPorVO);
-	  mpctx->eof = 1; goto goto_next_file;
-      }
-      if (frame_time < 0)
-	  mpctx->eof = 1;
-      else {
-	  // might return with !eof && !blit_frame if !correct_pts
-	  mpctx->num_buffered_frames += blit_frame;
-	  time_frame += frame_time / playback_speed;  // for nosound
-      }
-  }
-
 // ==========================================================================
     
 //    current_module="draw_osd";
--- libvo/video_out.c.org	2009-04-18 15:13:47.000000000 +0200
+++ libvo/video_out.c	2009-04-18 15:14:50.000000000 +0200
@@ -79,6 +79,11 @@
 int vo_colorkey = 0x0000ff00; // default colorkey is green
                               // (0xff000000 means that colorkey has been disabled)
 
+int vo_drop_frame_cnt = 0;    // frames dropped by vo driver
+unsigned int vo_vsync_offset   = 0;
+unsigned int vo_vsync_interval = 0;
+
+
 //
 // Externally visible list of all vo drivers
 //
--- libvo/video_out.h.org	2009-04-18 15:13:53.000000000 +0200
+++ libvo/video_out.h	2009-04-18 15:14:50.000000000 +0200
@@ -250,6 +250,10 @@
 
 extern int vo_colorkey;
 
+extern int vo_drop_frame_cnt;
+extern unsigned int vo_vsync_offset;
+extern unsigned int vo_vsync_interval;
+
 extern int64_t WinID;
 
 typedef struct {
--- libvo/vo_vdpau.c.org	2009-04-18 15:14:00.000000000 +0200
+++ libvo/vo_vdpau.c	2009-04-18 15:14:50.000000000 +0200
@@ -43,6 +43,7 @@
 #include "aspect.h"
 #include "sub.h"
 #include "subopt-helper.h"
+#include "osdep/timer.h"
 
 #include "libavcodec/vdpau.h"
 
@@ -76,7 +77,8 @@
                message, vdp_get_error_string(vdp_st));
 
 /* number of video and output surfaces */
-#define NUM_OUTPUT_SURFACES                2
+/** number of output surfaces is set in preinit */
+static int num_output_surfaces;
 #define MAX_VIDEO_SURFACES                 50
 
 /* number of palette entries */
@@ -128,6 +130,9 @@
 static VdpPresentationQueueDisplay       *vdp_presentation_queue_display;
 static VdpPresentationQueueBlockUntilSurfaceIdle *vdp_presentation_queue_block_until_surface_idle;
 static VdpPresentationQueueTargetCreateX11       *vdp_presentation_queue_target_create_x11;
+static VdpPresentationQueueQuerySurfaceStatus    *vdp_presentation_queue_query_surface_status;
+static VdpPresentationQueueGetTime               *vdp_presentation_queue_get_time;
+static VdpPresentationQueueSetBackgroundColor    *vdp_presentation_queue_set_background_color;
 
 static VdpOutputSurfaceRenderOutputSurface       *vdp_output_surface_render_output_surface;
 static VdpOutputSurfacePutBitsIndexed            *vdp_output_surface_put_bits_indexed;
@@ -144,9 +149,19 @@
 static VdpGenerateCSCMatrix                      *vdp_generate_csc_matrix;
 
 static void                              *vdpau_lib_handle;
-/* output_surfaces[NUM_OUTPUT_SURFACES] is misused for OSD. */
-#define osd_surface output_surfaces[NUM_OUTPUT_SURFACES]
-static VdpOutputSurface                   output_surfaces[NUM_OUTPUT_SURFACES + 1];
+
+typedef struct {
+    VdpOutputSurface surface;
+    int              width;
+    int              height;
+    int              dwidth;
+    int              dheight;
+    VdpTime          queued;
+} output_surface_t;
+
+/* output_surfaces[num_output_surfaces] is misused for OSD. */
+#define osd_surface output_surfaces[num_output_surfaces].surface
+static output_surface_t                  *output_surfaces;
 static VdpVideoSurface                    deint_surfaces[3];
 static mp_image_t                        *deint_mpi[2];
 static int                                output_surface_width, output_surface_height;
@@ -162,6 +177,10 @@
 static int                                chroma_deint;
 static int                                top_field_first;
 
+static float                              max_delay = -1;
+static unsigned int                       max_delay_interval;
+static int                                time_corr;
+
 static VdpDecoder                         decoder;
 static int                                decoder_max_refs;
 
@@ -205,12 +224,64 @@
 // Video equalizer
 static VdpProcamp procamp;
 
+static u_int64_t reference_system_time;
+static VdpTime   reference_vdpau_time;
+
+#include "m_option.h"
+m_option_t vdpauopts_conf[]={
+        {"max-delay", &max_delay, CONF_TYPE_FLOAT, CONF_RANGE, 0.0, 4.0, NULL},
+        {"time-corr", &time_corr, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+        {NULL, NULL, 0, 0, 0, 0, NULL}
+};
+
+
 /*
  * X11 specific
  */
 static int                                visible_buf;
 static int                                int_pause;
 
+static void create_output_surface(int no) {
+    VdpStatus vdp_st;
+
+    vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8,
+                                       output_surface_width, output_surface_height,
+                                       &output_surfaces[no].surface);
+    CHECK_ST_WARNING("Error when calling vdp_output_surface_create")
+    output_surfaces[no].width  = output_surface_width;
+    output_surfaces[no].height = output_surface_height;
+}
+
+static VdpOutputSurface get_output_surface(void) {
+    VdpStatus vdp_st;
+
+    if (output_surfaces[surface_num].surface == VDP_INVALID_HANDLE) {
+        create_output_surface(surface_num);
+        mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n", output_surfaces[surface_num].surface);
+    } else if (output_surfaces[surface_num].width != output_surface_width ||
+               output_surfaces[surface_num].height != output_surface_height) {
+        vdp_st = vdp_output_surface_destroy(output_surfaces[surface_num].surface);
+        CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy")
+        mp_msg(MSGT_VO, MSGL_DBG2, "OUT DELETE - to small: %u\n", output_surfaces[surface_num].surface);
+        create_output_surface(surface_num);
+        mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE - new size: %u %d %d\n",
+                                   output_surfaces[surface_num].surface,
+                                   output_surfaces[surface_num].width,
+                                   output_surfaces[surface_num].height);
+    }
+    if(!max_delay_interval) {
+        VdpTime visible_time;
+        vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue,
+                                                                 output_surfaces[surface_num].surface,
+                                                                 &visible_time);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_block_until_surface_idle")
+    }
+    output_surfaces[surface_num].dwidth  = vo_dwidth;
+    output_surfaces[surface_num].dheight = vo_dheight;
+    return output_surfaces[surface_num].surface;
+}
+
+
 static void draw_eosd(void);
 
 static void push_deint_surface(VdpVideoSurface surface)
@@ -222,7 +293,6 @@
 
 static void video_to_output_surface(void)
 {
-    VdpTime dummy;
     VdpStatus vdp_st;
     int i;
     if (vid_surface_num < 0)
@@ -243,12 +313,7 @@
             field = (top_field_first == i) ^ (deint > 1) ?
                     VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
                     VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
-        output_surface = output_surfaces[surface_num];
-        vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue,
-                                                                 output_surface,
-                                                                 &dummy);
-        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_block_until_surface_idle")
-
+        output_surface = get_output_surface();
         vdp_st = vdp_video_mixer_render(video_mixer, VDP_INVALID_HANDLE, 0,
                                         field, 2, deint_surfaces + 1,
                                         deint_surfaces[0],
@@ -264,7 +329,6 @@
 static void resize(void)
 {
     VdpStatus vdp_st;
-    int i;
     struct vo_rect src_rect;
     struct vo_rect dst_rect;
     struct vo_rect borders;
@@ -288,26 +352,21 @@
     if (output_surface_width < vo_dwidth || output_surface_height < vo_dheight) {
         if (output_surface_width < vo_dwidth) {
             output_surface_width += output_surface_width >> 1;
-            output_surface_width = FFMAX(output_surface_width, vo_dwidth);
+            output_surface_width = FFMIN(vo_screenwidth,FFMAX(output_surface_width, vo_dwidth));
         }
         if (output_surface_height < vo_dheight) {
             output_surface_height += output_surface_height >> 1;
-            output_surface_height = FFMAX(output_surface_height, vo_dheight);
-        }
-        // Creation of output_surfaces
-        for (i = 0; i <= NUM_OUTPUT_SURFACES; i++) {
-            if (output_surfaces[i] != VDP_INVALID_HANDLE)
-                vdp_output_surface_destroy(output_surfaces[i]);
-            vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8,
-                                               output_surface_width, output_surface_height,
-                                               &output_surfaces[i]);
-            CHECK_ST_WARNING("Error when calling vdp_output_surface_create")
-            mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n", output_surfaces[i]);
+            output_surface_height = FFMIN(vo_screenheight,FFMAX(output_surface_height, vo_dheight));
         }
+        // Creation of osd surface
+        if (osd_surface != VDP_INVALID_HANDLE)
+            vdp_output_surface_destroy(osd_surface);
+        vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8,
+                                           output_surface_width, output_surface_height,
+                                           &osd_surface);
+        CHECK_ST_WARNING("Error when calling vdp_output_surface_create")
+        mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE OSD: %u\n", osd_surface);
     }
-    video_to_output_surface();
-    if (visible_buf)
-        flip_page();
 }
 
 /* Initialize vdp_get_proc_address, called from preinit() */
@@ -351,6 +410,12 @@
                         &vdp_presentation_queue_block_until_surface_idle},
         {VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11,
                         &vdp_presentation_queue_target_create_x11},
+        {VDP_FUNC_ID_PRESENTATION_QUEUE_QUERY_SURFACE_STATUS,
+                        &vdp_presentation_queue_query_surface_status},
+        {VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME,
+                        &vdp_presentation_queue_get_time},
+        {VDP_FUNC_ID_PRESENTATION_QUEUE_SET_BACKGROUND_COLOR,
+                        &vdp_presentation_queue_set_background_color},
         {VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE,
                         &vdp_output_surface_render_output_surface},
         {VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED,
@@ -390,6 +455,7 @@
 static int win_x11_init_vdpau_flip_queue(void)
 {
     VdpStatus vdp_st;
+    VdpColor  vdp_black = { 0, 0, 0, 0 };
 
     vdp_st = vdp_presentation_queue_target_create_x11(vdp_device, vo_window,
                                                       &vdp_flip_target);
@@ -399,6 +465,9 @@
                                            &vdp_flip_queue);
     CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create")
 
+    vdp_st = vdp_presentation_queue_set_background_color(vdp_flip_queue, &vdp_black);
+    CHECK_ST_ERROR("Error when calling vdp_presentation_queue_set_background_color")
+
     return 0;
 }
 
@@ -528,6 +597,108 @@
     return 1;
 }
 
+static void get_vdpau_timing(void) {
+    VdpTime time1;
+    VdpTime time2;
+    VdpTime time3;
+    VdpTime td1,td2,td3;
+    VdpPresentationQueueStatus out_status;
+
+    VdpOutputSurface surface1;
+    VdpOutputSurface surface2;
+    VdpOutputSurface surface3;
+    VdpStatus vdp_st;
+
+    if (vo_vsync_interval) return;
+
+    if (time_corr) {
+        // get relation between vdpau time and system time
+        vdp_st = vdp_presentation_queue_get_time(vdp_flip_queue, &reference_vdpau_time);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_get_time")
+        reference_system_time = get_time();
+    }
+
+    // create timing surfaces
+    vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, 16, 16, &surface1);
+    CHECK_ST_WARNING("Error when calling vdp_output_surface_create")
+    vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, 16, 16, &surface2);
+    CHECK_ST_WARNING("Error when calling vdp_output_surface_create")
+    vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, 16, 16, &surface3);
+    CHECK_ST_WARNING("Error when calling vdp_output_surface_create")
+
+    // queue surfaces and check time for display
+    vdp_st = vdp_presentation_queue_display(vdp_flip_queue, surface1, 1, 1,0);
+    CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
+    vdp_st = vdp_presentation_queue_display(vdp_flip_queue, surface2, 1, 1,0);
+    CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
+    vdp_st = vdp_presentation_queue_display(vdp_flip_queue, surface3, 1, 1,0);
+    CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
+
+    vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue, surface1, &time1);
+    CHECK_ST_WARNING("Error when calling vdp_presentation_queue_block_until_surface_idle")
+    vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue, surface2, &time2);
+    CHECK_ST_WARNING("Error when calling vdp_presentation_queue_block_until_surface_idle")
+
+    vdp_st = vdp_presentation_queue_query_surface_status(vdp_flip_queue, surface3, &out_status, &time3);
+    CHECK_ST_WARNING("Error when calling vdp_presentation_queue_query_surface_status")
+
+    if (out_status != 2) {
+        mp_msg(MSGT_VO, MSGL_V, "[vdpau] Surface not visible as it should\n");
+        return;
+    }
+    td1 = time2-time1;
+    td2 = time3-time2;
+    if (td1 != td2) {
+        // time one frame more to get more accurate data
+        vdp_st = vdp_presentation_queue_display(vdp_flip_queue, surface2, 1, 1, 0);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
+        vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue, surface3, &time2);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_block_until_surface_idle")
+        vdp_st = vdp_presentation_queue_query_surface_status(vdp_flip_queue, surface2, &out_status, &time2);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_query_surface_status")
+        if (out_status != 2) {
+            mp_msg(MSGT_VO, MSGL_V, "[vdpau] Surface not visible as it should (accuracy test)\n");
+            return;
+        }
+        td3 = time2-time3;
+        if (td3 == td2) {
+            td1 = td3;
+        } else if (td3 != td1) {
+            VdpTime tdv;
+            VdpTime tdmax,tdmin;
+            
+            tdmax = FFMAX(td1,FFMAX(td2,td3));
+            tdmin = FFMIN(td1,FFMIN(td2,td3));
+            td1 = (time2-time1)/3;
+            tdv = td1/10000;
+            if (tdv != td2/10000 || tdv != td3/10000) {
+                mp_msg(MSGT_VO, MSGL_V, "[vdpau] Timing unaccurate at 10 microsecond level - assume blit engine\n");
+                if (tdmax-tdmin < 3000000) {  // 3 ms  (about 3 ms have been seen)
+                    mp_msg(MSGT_VO, MSGL_V, "[vdpau] Timing unaccurate at millisecond level - acceptible for blit engine\n");
+                } else {
+                    mp_msg(MSGT_VO, MSGL_V, "[vdpau] Timing very unaccurate at millisecond level - assume assume no vsync alignment\n");
+                    return;
+                }
+            }
+        } else { // td1 == td3
+        }
+    }
+    if (td1 < 5000000) {
+        mp_msg(MSGT_VO, MSGL_V, "[vdpau] To small vsync value - assume no vsync alignment\n");
+        return;
+    }
+    vo_vsync_interval = td1;
+
+    vdp_st = vdp_output_surface_destroy(surface1);
+    vdp_st = vdp_output_surface_destroy(surface2);
+    vdp_st = vdp_output_surface_destroy(surface3);
+
+    if (max_delay) {
+        max_delay_interval = vo_vsync_interval*max_delay;
+    }
+}
+
+
 /*
  * connect to X server, create and map window, initialize all
  * VDPAU objects, create different surfaces etc.
@@ -625,6 +796,8 @@
     vid_surface_num = -1;
     resize();
 
+    if (max_delay) get_vdpau_timing();
+
     return 0;
 }
 
@@ -636,23 +809,16 @@
         resize();
 
     if ((e & VO_EVENT_EXPOSE || e & VO_EVENT_RESIZE) && int_pause) {
-        /* did we already draw a buffer */
-        if (visible_buf) {
-            /* redraw the last visible buffer */
-            VdpStatus vdp_st;
-            vdp_st = vdp_presentation_queue_display(vdp_flip_queue,
-                                                    output_surfaces[surface_num],
-                                                    vo_dwidth, vo_dheight,
-                                                    0);
-            CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
-        }
+        video_to_output_surface();
+        if (visible_buf)
+            flip_page();
     }
 }
 
 static void draw_osd_I8A8(int x0,int y0, int w,int h, unsigned char *src,
                           unsigned char *srca, int stride)
 {
-    VdpOutputSurface output_surface = output_surfaces[surface_num];
+    VdpOutputSurface output_surface = get_output_surface();
     VdpStatus vdp_st;
     int i, j;
     int pitch;
@@ -713,7 +879,7 @@
 
 static void draw_eosd(void) {
     VdpStatus vdp_st;
-    VdpOutputSurface output_surface = output_surfaces[surface_num];
+    VdpOutputSurface output_surface = get_output_surface();
     VdpOutputSurfaceRenderBlendState blend_state;
     int i;
 
@@ -836,15 +1002,67 @@
 static void flip_page(void)
 {
     VdpStatus vdp_st;
+    VdpPresentationQueueStatus next_status = VDP_PRESENTATION_QUEUE_STATUS_IDLE;
+    VdpTime show_time;
+    VdpTime next_show,now;
+    int q,s;
+    VdpTime time_to_next_vsync;
+    int next_surface = (surface_num + 1) % num_output_surfaces;
+
+    if (output_surfaces[next_surface].surface == VDP_INVALID_HANDLE) {
+        create_output_surface(next_surface);
+        mp_msg(MSGT_VO, MSGL_DBG2, "OUT at flip_page CREATE: %u\n", output_surfaces[next_surface].surface);
+    } else {
+        vdp_st = vdp_presentation_queue_query_surface_status(vdp_flip_queue,
+                    output_surfaces[next_surface].surface, &next_status, &show_time);
+        if (num_output_surfaces > 2 && vdp_st == VDP_STATUS_OK &&
+            next_status != VDP_PRESENTATION_QUEUE_STATUS_IDLE) {
+            mp_msg(MSGT_VO, MSGL_V, "[vdpau] dropping frame\n");
+            vo_drop_frame_cnt++;
+            return;
+        }
+    }
+    vo_vsync_offset = (show_time-output_surfaces[next_surface].queued)/1000;
+
+    if (max_delay_interval && vo_vsync_interval) {
+        // find queued surfaces and the visible one
+        for (q=0,s = surface_num-1 < 0 ? num_output_surfaces-1 : surface_num-1;
+             s != surface_num; s = s-1 < 0 ? num_output_surfaces-1 : s-1) {
+            vdp_st = vdp_presentation_queue_query_surface_status(vdp_flip_queue,
+                                   output_surfaces[s].surface, &next_status, &show_time);
+            if (next_status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED) q++;
+            if (next_status == VDP_PRESENTATION_QUEUE_STATUS_VISIBLE) break;
+        }
+        vdp_st = vdp_presentation_queue_get_time(vdp_flip_queue, &now);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_get_time")
+
+        if (next_status == VDP_PRESENTATION_QUEUE_STATUS_VISIBLE) {
+            q++;
+            next_show = show_time+q*vo_vsync_interval;
+            if (next_show > now) {
+                time_to_next_vsync = next_show-now;
+                if (time_to_next_vsync > max_delay_interval) {
+                     mp_msg(MSGT_VO, MSGL_V, "[vdpau] dropping frame - delay to large\n");
+                     vo_drop_frame_cnt++;
+                     return;
+                }
+            }
+        }
+        output_surfaces[surface_num].queued = now;
+    } else {
+        vdp_st = vdp_presentation_queue_get_time(vdp_flip_queue, &output_surfaces[surface_num].queued);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_get_time")
+    }
+
     mp_msg(MSGT_VO, MSGL_DBG2, "\nFLIP_PAGE VID:%u -> OUT:%u\n",
-           surface_render[vid_surface_num].surface, output_surfaces[surface_num]);
+           surface_render[vid_surface_num].surface, output_surfaces[surface_num].surface);
 
-    vdp_st = vdp_presentation_queue_display(vdp_flip_queue, output_surfaces[surface_num],
-                                            vo_dwidth, vo_dheight,
+    vdp_st = vdp_presentation_queue_display(vdp_flip_queue, output_surfaces[surface_num].surface,
+                                            output_surfaces[surface_num].dwidth, output_surfaces[surface_num].dheight,
                                             0);
     CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
 
-    surface_num = (surface_num + 1) % NUM_OUTPUT_SURFACES;
+    surface_num = next_surface;
     visible_buf = 1;
 }
 
@@ -973,6 +1191,22 @@
     int i;
     VdpStatus vdp_st;
 
+    if (time_corr) {
+        VdpTime   now;
+        u_int64_t systime;
+        VdpTime   nvtime;
+        double    corr;
+
+        systime = get_time();
+        vdp_st = vdp_presentation_queue_get_time(vdp_flip_queue, &now);
+        CHECK_ST_WARNING("Error when calling vdp_presentation_queue_get_time")
+
+        nvtime = now-reference_vdpau_time;
+        systime = systime-reference_system_time;
+        corr = systime/(nvtime/1000.0f);
+        mp_msg(MSGT_VO,MSGL_INFO,"[vdpau] Card time correction=%2.10f\n",corr);
+    }
+
     free_video_specific();
 
     vdp_st = vdp_presentation_queue_destroy(vdp_flip_queue);
@@ -981,11 +1215,15 @@
     vdp_st = vdp_presentation_queue_target_destroy(vdp_flip_target);
     CHECK_ST_WARNING("Error when calling vdp_presentation_queue_target_destroy")
 
-    for (i = 0; i <= NUM_OUTPUT_SURFACES; i++) {
-        vdp_st = vdp_output_surface_destroy(output_surfaces[i]);
-        output_surfaces[i] = VDP_INVALID_HANDLE;
-        CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy")
+    for (i = 0; i <= num_output_surfaces; i++) {
+        if (output_surfaces[i].surface != VDP_INVALID_HANDLE) {
+            vdp_st = vdp_output_surface_destroy(output_surfaces[i].surface);
+            output_surfaces[i].surface = VDP_INVALID_HANDLE;
+            CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy")
+        }
     }
+    free(output_surfaces);
+    output_surfaces = NULL;
 
     for (i = 0; i<eosd_surface_count; i++) {
         if (eosd_surfaces[i].surface != VDP_INVALID_HANDLE) {
@@ -1030,6 +1268,8 @@
     {"pullup",  OPT_ARG_BOOL,  &pullup,  NULL},
     {"denoise", OPT_ARG_FLOAT, &denoise, NULL},
     {"sharpen", OPT_ARG_FLOAT, &sharpen, NULL},
+    {"max-delay",OPT_ARG_FLOAT,&max_delay, NULL},
+    {"time-corr",OPT_ARG_BOOL, &time_corr, NULL},
     {NULL}
 };
 
@@ -1052,6 +1292,14 @@
     "    Apply denoising, argument is strength from 0.0 to 1.0\n"
     "  sharpen\n"
     "    Apply sharpening or softening, argument is strength from -1.0 to 1.0\n"
+    "  max-delay=delay\n"
+    "    Maximum delay before frames are dropped, argument is in vsyncs. Do not use\n"
+    "    an integer value to avoid jitter (default is 1.3 (1.5 with deint)),\n"
+    "    set to 0 for no dropping\n"
+    "  time-corr\n"
+    "    Turns on card time correction calculation (unless max_delay=0).\n"
+    "    Printed at exit, to get best value, and can be used as input to\n"
+    "    the -card-time-correction option\n"
     ;
 
 static int preinit(const char *arg)
@@ -1077,6 +1325,19 @@
         deint_type = deint;
     if (deint > 1)
         deint_buffer_past_frames = 1;
+    if (max_delay < 0) {
+        if (deint) {
+            max_delay = 1.5;
+        } else {
+            max_delay = 1.3;
+        }
+    }
+    if (max_delay) {
+        num_output_surfaces = 3+((int)(max_delay+0.05));
+    } else {
+        num_output_surfaces = 2;
+    }
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] max delay %f, output surfaces: %d\n", max_delay, num_output_surfaces);
 
     vdpau_lib_handle = dlopen(vdpaulibrary, RTLD_LAZY);
     if (!vdpau_lib_handle) {
@@ -1097,8 +1358,9 @@
     for (i = 0; i < MAX_VIDEO_SURFACES; i++)
         surface_render[i].surface = VDP_INVALID_HANDLE;
     video_mixer = VDP_INVALID_HANDLE;
-    for (i = 0; i <= NUM_OUTPUT_SURFACES; i++)
-        output_surfaces[i] = VDP_INVALID_HANDLE;
+    output_surfaces = (output_surface_t *)calloc(num_output_surfaces+1,sizeof(output_surface_t));
+    for (i = 0; i <= num_output_surfaces; i++)
+        output_surfaces[i].surface = VDP_INVALID_HANDLE;
     vdp_flip_queue = VDP_INVALID_HANDLE;
     output_surface_width = output_surface_height = -1;
 
--- osdep/timer.h.org	2009-04-18 15:14:05.000000000 +0200
+++ osdep/timer.h	2009-04-18 15:14:50.000000000 +0200
@@ -21,6 +21,8 @@
 
 extern const char *timer_name;
 
+u_int64_t get_time(void);
+
 void InitTimer(void);
 unsigned int GetTimer(void);
 unsigned int GetTimerMS(void);
--- osdep/timer-linux.c.org	2009-04-18 15:14:12.000000000 +0200
+++ osdep/timer-linux.c	2009-04-18 15:14:50.000000000 +0200
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #include "config.h"
 
 const char *timer_name =
@@ -47,6 +48,13 @@
 #endif
 }
 
+// Returns current time in microseconds in 64 bit quantity so it does not wrap
+u_int64_t get_time(void){
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+  return (u_int64_t)(tv.tv_sec)*1000000+tv.tv_usec;
+}  
+
 // Returns current time in microseconds
 unsigned int GetTimer(void){
   struct timeval tv;
--- mp_core.h.org	2009-04-18 15:14:18.000000000 +0200
+++ mp_core.h	2009-04-18 15:14:50.000000000 +0200
@@ -124,6 +124,10 @@
 extern int fixed_vo;
 extern int forced_subs_only;
 
+extern float matched_playback_speed; // matched with vsync speed, use instead of 1.0
+extern int   match_vsync_speed;      // enable speed matching
+extern float card_time_correction;   // speed correction due to different clock on card
+
 
 int build_afilter_chain(sh_audio_t *sh_audio, ao_data_t *ao_data);
 void uninit_player(unsigned int mask);
--- cfg-mplayer.h.org	2009-04-18 15:14:23.000000000 +0200
+++ cfg-mplayer.h	2009-04-18 15:14:50.000000000 +0200
@@ -71,6 +71,11 @@
 	{NULL, NULL, 0, 0, 0, 0, NULL}
 };
 #endif
+
+#ifdef CONFIG_VDPAU
+extern m_option_t vdpauopts_conf[];
+#endif
+
 /*
  * CONF_TYPE_FUNC_FULL :
  * allows own implementations for passing the params
@@ -241,6 +246,9 @@
 	{"dxr2", &dxr2_opts, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
 #endif
 
+#ifdef CONFIG_VDPAU
+	{"vdpauopts", vdpauopts_conf, CONF_TYPE_SUBCONFIG, CONF_GLOBAL, 0, 0, NULL},
+#endif
 
 //---------------------- mplayer-only options ------------------------
 
@@ -326,6 +334,8 @@
 	{"autosync", &autosync, CONF_TYPE_INT, CONF_RANGE, 0, 10000, NULL},
 //	{"dapsync", &dapsync, CONF_TYPE_FLAG, 0, 0, 1, NULL},
 //	{"nodapsync", &dapsync, CONF_TYPE_FLAG, 0, 1, 0, NULL},
+        {"match-vsync-speed", &match_vsync_speed, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+        {"card-time-correction", &card_time_correction, CONF_TYPE_FLOAT, CONF_RANGE, 0.8, 1.2, NULL},
 
 	{"softsleep", &softsleep, CONF_TYPE_FLAG, 0, 0, 1, NULL},
 #ifdef HAVE_RTC
--- command.c.org	2009-04-18 15:14:29.000000000 +0200
+++ command.c	2009-04-18 15:14:50.000000000 +0200
@@ -198,6 +198,9 @@
 	    return M_PROPERTY_ERROR;
 	M_PROPERTY_CLAMP(prop, *(float *) arg);
 	playback_speed = *(float *) arg;
+        if (playback_speed == 1.0 && matched_playback_speed) {
+            playback_speed = matched_playback_speed;
+        }
 	build_afilter_chain(mpctx->sh_audio, &ao_data);
 	return M_PROPERTY_OK;
     case M_PROPERTY_STEP_UP:
@@ -2491,6 +2494,9 @@
 	case MP_CMD_SPEED_SET:{
 		float v = cmd->args[0].v.f;
 		playback_speed = v;
+                if (playback_speed == 1.0 && matched_playback_speed) {
+                    playback_speed = matched_playback_speed;
+                }
 		build_afilter_chain(sh_audio, &ao_data);
 		set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDSpeed,
 			    playback_speed);


More information about the MPlayer-dev-eng mailing list