[MPlayer-dev-eng] [PATCH] smooth playback with NAS and all audio drivers

Sidik Isani lksi at cfht.hawaii.edu
Mon Sep 23 11:13:45 CEST 2002


Hello -

  A frame timing patch.  A few comments about this patch ...

  o Most importantly, this patch should result in video as smooth as
    you get with -nosound, even if your sound driver is broken (or you
    have network audio and it is impossible to get accurate instantaneous
    get_delay() measurements.)  I'm confident that this does not penalize
    the good sound drivers, but a flag can be added to cfg*.h to turn it
    on/off if you want to make sure.  The variable to toggle is "avgsync".
    (Tell me exactly how you'd want it and I can make a patch too.)

  o It also has 2 important fixes to the RTC sleep.  Just the minimal
    changes were done this time, with everything left in mplayer.c.
    The RTC returns a 32-bit number, but the read() was for a long long.
    This had the effect of cutting the frequency in half (?), so now it
    would probably work as well at 512Hz, causing half the number of
    hardware interrupts.  I set it at 1024 anyway since that rate didn't
    seem to be bringing anyone's machine to its knees.

  o Definitely no changes to setuid behavior this time.  The new call
    to seteuid(0) before /dev/rtc is opened should not break anything
    and will allow the RTC code to continue to work even if the line
    "seteuid(getuid())" is added some day at the start of main().

  o The patch removes the dapsync code.  But I can generate a version
    which leaves it in if you prefer?

  o A new variable "delay_avg" stores either the last delay measurement
    from audio_out->get_delay(), or a corrected one calculated by my
    averaging code, if "avgsync" is set.  With network audio's jittery
    get_delay() measurement, both frame dropping and pts_correction had
    some problems too.  Since both are long-term calculations, they
    can use delay_avg instead of calling get_delay().  If avgsync is OFF,
    the result is they just see the last value of get_delay obtained by
    the A/V-sync code.  Please double-check where delay_avg is used.

  There were several ways to write this patch, but I chose one which
  makes it clear that all it does is adjust the delay that Arpi's sync
  uses, and it is guaranteed to settle on the same A/V offset as before.
  It just applies a little "damping" to the frame timing, and the result
  looks very good (or the no different if you had a good sound driver
  to begin with.)  Thanks again for all the effort on mplayer!

Be seeing you,

- Sidik
-------------- next part --------------
diff -ru MPlayer-before-avgsync/mplayer.c MPlayer-isani/mplayer.c
--- MPlayer-before-avgsync/mplayer.c	Sun Sep 22 15:38:36 2002
+++ MPlayer-isani/mplayer.c	Sun Sep 22 20:30:27 2002
@@ -216,9 +216,11 @@
 static float default_max_pts_correction=-1;//0.01f;
 static float max_pts_correction=0;//default_max_pts_correction;
 static float c_total=0;
+static float delay_avg=0;
        float audio_delay=0;
 
 static int dapsync=0;
+static int avgsync=1; /* Average (smooth out) frame timing corrections */
 static int softsleep=0;
 
 static float force_fps=0;
@@ -693,15 +695,16 @@
 #ifdef HAVE_RTC
   if(!nortc)
   {
+    seteuid(0); /* Can't hurt to try to get root here */
     if ((rtc_fd = open("/dev/rtc", O_RDONLY)) < 0)
-	mp_msg(MSGT_CPLAYER, MSGL_ERR, "Linux RTC init error: %s\n", strerror(errno));
-    else {
-	unsigned long irqp;
-
-	/* if (ioctl(rtc_fd, RTC_IRQP_SET, _) < 0) { */
-	if (ioctl(rtc_fd, RTC_IRQP_READ, &irqp) < 0) {
-    	    mp_msg(MSGT_CPLAYER, MSGL_ERR, "Linux RTC init error in ioctl (rtc_irqp_read): %s\n", strerror(errno));
-    	    close (rtc_fd);
+	mp_msg(MSGT_CPLAYER, MSGL_ERR, "Failed to open /dev/rtc: %s (mplayer should be setuid root or /dev/rtc should be readable by the user.)\n", strerror(errno));
+     else {
+	unsigned long irqp = 1024; /* 512 seemed OK. 128 is jerky. */
+
+	if (ioctl(rtc_fd, RTC_IRQP_SET, irqp) < 0) {
+    	    mp_msg(MSGT_CPLAYER, MSGL_ERR, "Linux RTC init error in ioctl (rtc_irqp_set %lu): %s\n", irqp, strerror(errno));
+	    mp_msg(MSGT_CPLAYER, MSGL_ERR, "Try adding \"echo %lu > /proc/sys/dev/rtc/max-user-freq\" to your system startup scripts.\n", irqp);
+   	    close (rtc_fd);
     	    rtc_fd = -1;
 	} else if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) {
 	    /* variable only by the root */
@@ -1549,9 +1552,8 @@
 	time_frame+=frame_time;  // for nosound
 	// check for frame-drop:
 	current_module="check_framedrop";
-	if(sh_audio && !d_audio->eof){
-	    float delay=audio_out->get_delay();
-	    float d=(sh_video->timer)-(sh_audio->timer-delay);
+	if(sh_audio && !d_audio->eof && delay_avg!=0){
+	    float d=(sh_video->timer)-(sh_audio->timer-delay_avg);
 	    // we should avoid dropping to many frames in sequence unless we
 	    // are too late. and we allow 100ms A-V delay here:
 	    if(d<-dropped_frames*frame_time-0.100){
@@ -1618,53 +1620,42 @@
 	  float delay=audio_out->get_delay();
 	  mp_dbg(MSGT_AVSYNC,MSGL_DBG2,"delay=%f\n",delay);
 
-	if(!dapsync){
+	if(avgsync){
+	  static float corr_avg = 0.;	/* Running average of corrections */
+	  float corr_desired;		/* From single delay measurement */
+	  float corr_applied;		/* Average correction to time_frame */
+
+	  /* Calculate the correction that Arpi's sync would apply */
+
+	  corr_desired = sh_audio->timer - delay;
+	  corr_desired -= sh_video->timer - time_frame;
+	  corr_avg = (corr_desired + corr_avg*99.)/100.; /* Average it */
+
+	  /*
+	   * A linear response to correction is more likely to oscillate.
+	   * The avg^2 times 20 below means correction approaches 100%
+	   * of corr_avg per frame as it reaches 1/20 of a second.
+	   */
+	  corr_applied = corr_avg * corr_avg * 20.; /* Now have abs value */
+	  if (corr_applied > 0.002) { /* Make correction if > 0.002 sec */
+	    if (corr_avg < 0.) corr_applied *= -1.; /* Get sign back */
+	    corr_avg -= corr_applied; /* Apply correction to average too! */
+	    mp_dbg(MSGT_AVSYNC,MSGL_DBG2, "frame time correction:"
+		   " desired %7.4f avg %7.4f applied %9.6f tf=%7.5f\n",
+		   corr_desired, corr_avg, corr_applied, time_frame);
+	  }
+	  else corr_applied = 0.; /* Don't correct until it's significant */
+	  delay += corr_desired - corr_applied; /* Adjust the delay */
+	  mp_dbg(MSGT_AVSYNC,MSGL_DBG2,"delay=%f (averaged)\n");
+	}
+
+	  delay_avg = delay; /* Save it for frame drop calculations. */
 
 	      /* Arpi's AV-sync */
 
           time_frame=sh_video->timer;
           time_frame-=sh_audio->timer-delay;
 
-	} else {  // if(!dapsync)
-
-	      /* DaP's AV-sync */
-
-    	      float SH_AV_delay;
-	      /* SH_AV_delay = sh_video->timer - (sh_audio->timer - (float)((float)delay + sh_audio->a_buffer_len) / (float)sh_audio->o_bps); */
-	      SH_AV_delay = sh_video->timer - (sh_audio->timer - (float)((float)delay) / (float)sh_audio->o_bps);
-	      // printf ("audio slp req: %.3f TF: %.3f delta: %.3f (v: %.3f a: %.3f) | ", i, time_frame,
-	      //	    i - time_frame, sh_video->timer, sh_audio->timer - (float)((float)delay / (float)sh_audio->o_bps));
-	      if(SH_AV_delay<-2*frame_time){
-	          drop_frame=frame_dropping; // tricky!
-	          ++drop_frame_cnt;
-		mp_msg(MSGT_AVSYNC,MSGL_INFO,"A-V SYNC: FRAMEDROP (SH_AV_delay=%.3f)!\n", SH_AV_delay);
-	        mp_msg(MSGT_AVSYNC,MSGL_DBG2,"\nframe drop %d, %.2f\n", drop_frame, time_frame);
-	        /* go into unlimited-TF cycle */
-    		time_frame = SH_AV_delay;
-	      } else {
-#define	SL_CORR_AVG_LEN	125
-	        /* don't adjust under framedropping */
-	        time_frame_corr_avg = (time_frame_corr_avg * (SL_CORR_AVG_LEN - 1) +
-	    				(SH_AV_delay - time_frame)) / SL_CORR_AVG_LEN;
-#define	UNEXP_CORR_MAX	0.1	/* limit of unexpected correction between two frames (percentage) */
-#define	UNEXP_CORR_WARN	1.0	/* warn limit of A-V lag (percentage) */
-	        time_frame += time_frame_corr_avg;
-	        // printf ("{%.3f - %.3f}\n", i - time_frame, frame_time + frame_time_corr_avg);
-	        if (SH_AV_delay - time_frame < (frame_time + time_frame_corr_avg) * UNEXP_CORR_MAX &&
-		    SH_AV_delay - time_frame > (frame_time + time_frame_corr_avg) * -UNEXP_CORR_MAX)
-		    time_frame = SH_AV_delay;
-	        else {
-		    if (SH_AV_delay - time_frame > (frame_time + time_frame_corr_avg) * UNEXP_CORR_WARN ||
-		        SH_AV_delay - time_frame < (frame_time + time_frame_corr_avg) * -UNEXP_CORR_WARN)
-		        mp_msg(MSGT_AVSYNC, MSGL_WARN, "WARNING: A-V SYNC LAG TOO LARGE: %.3f {%.3f - %.3f} (too little UNEXP_CORR_MAX?)\n",
-		  	    SH_AV_delay - time_frame, SH_AV_delay, time_frame);
-		        time_frame += (frame_time + time_frame_corr_avg) * ((SH_AV_delay > time_frame) ?
-		    		      UNEXP_CORR_MAX : -UNEXP_CORR_MAX);
-	        }
-	      }	/* /start dropframe */
-
-	} // if(dapsync)
-
 	if(delay>0.25) delay=0.25; else
 	if(delay<0.10) delay=0.10;
 	if(time_frame>delay*0.6){
@@ -1697,7 +1688,7 @@
 	// -------- RTC -----------
 	current_module="sleep_rtc";
         while (time_frame > 0.000) {
-	    unsigned long long rtc_ts;
+	    unsigned long rtc_ts;
 	    if (read (rtc_fd, &rtc_ts, sizeof(rtc_ts)) <= 0)
 		    mp_msg(MSGT_CPLAYER, MSGL_ERR, "Linux RTC read error: %s\n", strerror(errno));
     	    time_frame-=GetRelativeTime();
@@ -1762,7 +1753,7 @@
     float v_pts=0;
 
     // unplayed bytes in our and soundcard/dma buffer:
-    float delay=audio_out->get_delay()+(float)sh_audio->a_buffer_len/(float)sh_audio->o_bps;
+    float delay=delay_avg+(float)sh_audio->a_buffer_len/(float)sh_audio->o_bps;
 
     if(pts_from_bps){
 	// PTS = sample_no / samplerate
@@ -1797,7 +1788,17 @@
 	  ++drop_message;
 	  mp_msg(MSGT_AVSYNC,MSGL_ERR,MSGTR_SystemTooSlow);
 	}
-        x=AV_delay*0.1f;
+	if (avgsync){
+	  /*
+	   * Only make an audio time correction when a significant error
+	   * in the delay exists.  This makes a more stable playback rate
+	   * in general, and is important to keep this logic from competing
+	   * with avgsync and causing things to oscillate otherwise.  (The
+	   * case of !avgsync is left unchanged, always correcting 10%.)
+	   */
+	  if (AV_delay > -0.01 && AV_delay < 0.01) x=0.;
+	  else x=AV_delay; /* Make full correction since avgsync smooths it */
+	} else x=AV_delay*0.1f;
         if(x<-max_pts_correction) x=-max_pts_correction; else
         if(x> max_pts_correction) x= max_pts_correction;
         if(default_max_pts_correction>=0)


More information about the MPlayer-dev-eng mailing list