diff mplayer.c @ 746:cd1f0d4de0b8

new audio playback and A-V sync code
author arpi_esp
date Thu, 10 May 2001 03:39:54 +0000
parents c3e0bdb64027
children 717e4677d9ce
line wrap: on
line diff
--- a/mplayer.c	Wed May 09 23:41:01 2001 +0000
+++ b/mplayer.c	Thu May 10 03:39:54 2001 +0000
@@ -27,7 +27,7 @@
 #include "version.h"
 #include "config.h"
 
-#ifndef OUTBURST
+#ifndef MAX_OUTBURST
 #error "============================================="
 #error "Please re-run ./configure and then try again!"
 #error "============================================="
@@ -254,7 +254,6 @@
   return len;
 }
 
-
 //#include "dec_audio.c"
 
 //**************************************************************************//
@@ -304,6 +303,31 @@
 }
 }
 #endif
+
+//**************************************************************************//
+
+int audio_delay_method=2;
+int audio_buffer_size=-1;
+
+int get_audio_delay(int audio_fd){
+  if(audio_delay_method==2){
+      // 
+      int r=0;
+      if(ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &r)!=-1)
+         return r;
+      audio_delay_method=1; // fallback if not supported
+  }
+  if(audio_delay_method==1){
+      // SNDCTL_DSP_GETOSPACE
+      audio_buf_info zz;
+      if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)!=-1)
+         return audio_buffer_size-zz.bytes;
+      audio_delay_method=0; // fallback if not supported
+  }
+  return audio_buffer_size;
+}
+
+
 //**************************************************************************//
 
 // AVI file header reader/parser/writer:
@@ -401,7 +425,7 @@
 extern void mpeg2_allocate_image_buffers(picture_t * picture);
 extern void write_avi_header_1(FILE *f,int fcc,float fps,int width,int height);
 extern int vo_init(void);
-extern int decode_audio(sh_audio_t *sh_audio,unsigned char *buf,int maxlen);
+extern int decode_audio(sh_audio_t *sh_audio,unsigned char *buf,int minlen,int maxlen);
 
 char* filename=NULL; //"MI2-Trailer.avi";
 int i;
@@ -424,7 +448,6 @@
 #else
 int alsa=0;
 #endif
-int audio_buffer_size=-1;
 int audio_id=-1;
 int video_id=-1;
 int dvdsub_id=-1;
@@ -1031,7 +1054,8 @@
     printf("Couldn't initialize audio codec! -> nosound\n");
     has_audio=0;
   } else {
-    printf("AUDIO: samplerate=%d  channels=%d  bps=%d\n",sh_audio->samplerate,sh_audio->channels,sh_audio->samplesize);
+    printf("AUDIO: samplerate=%d  channels=%d  bps=%d  ratio: %d->%d\n",sh_audio->samplerate,sh_audio->channels,sh_audio->samplesize,
+        sh_audio->i_bps,sh_audio->o_bps);
   }
 }
 
@@ -1282,7 +1306,10 @@
 //================== MAIN: ==========================
 {
 int audio_fd=-1;
-float buffer_delay=0;
+int outburst=OUTBURST;
+float audio_buffer_delay=0;
+
+//float buffer_delay=0;
 float frame_correction=0; // A-V timestamp kulonbseg atlagolas
 int frame_corr_num=0;   //
 float a_frame=0;    // Audio
@@ -1362,24 +1389,38 @@
 #ifdef USE_XMMP_AUDIO
   if(audio_buffer_size==-1){
     // Measuring buffer size:
-    buffer_delay=pSound->QueryDelay(pSound, 0);
+    audio_buffer_delay=pSound->QueryDelay(pSound, 0);
   } else {
     // -abs commandline option
-    buffer_delay=audio_buffer_size/(float)(sh_audio->o_bps);
+    audio_buffer_delay=audio_buffer_size/(float)(sh_audio->o_bps);
   }
 #else
   int r;
+  audio_buf_info zz;
+
   r=(sh_audio->samplesize==2)?AFMT_S16_LE:AFMT_U8;ioctl (audio_fd, SNDCTL_DSP_SETFMT, &r);
   r=sh_audio->channels-1; ioctl (audio_fd, SNDCTL_DSP_STEREO, &r);
-  r=sh_audio->samplerate; if(ioctl (audio_fd, SNDCTL_DSP_SPEED, &r)==-1)
-      printf("audio_setup: your card doesn't support %d Hz samplerate\n",r);
+  r=sh_audio->samplerate; if(ioctl (audio_fd, SNDCTL_DSP_SPEED, &r)==-1){
+      printf("audio_setup: your card doesn't support %d Hz samplerate => nosound\n",r);
+      has_audio=0;
+  } else
+      printf("audio_setup: using %d Hz samplerate (requested: %d)\n",r,sh_audio->samplerate);
 
-#if 0
-//  r = (64 << 16) + 1024;
-  r = (65536 << 16) + 512;
-    if(ioctl (audio_fd, SNDCTL_DSP_SETFRAGMENT, &r)==-1)
-      printf("audio_setup: your card doesn't support setting fragments\n",r);
-#endif
+  if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)==-1){
+      printf("audio_setup: driver doesn't support SNDCTL_DSP_GETOSPACE :-(\n");
+      r=0;
+      if(ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &r)==-1){
+          printf("audio_setup: %d bytes/frag (config.h)\n",outburst);
+      } else {
+          outburst=r;
+          printf("audio_setup: %d bytes/frag (GETBLKSIZE)\n",outburst);
+      }
+  } else {
+      printf("audio_setup: frags: %3d/%d  (%d bytes/frag)  free: %6d\n",
+          zz.fragments, zz.fragstotal, zz.fragsize, zz.bytes);
+      if(audio_buffer_size==-1) audio_buffer_size=zz.bytes;
+      outburst=zz.fragsize;
+  }
 
   if(audio_buffer_size==-1){
     // Measuring buffer size:
@@ -1391,8 +1432,8 @@
       FD_ZERO(&rfds); FD_SET(audio_fd,&rfds);
       tv.tv_sec=0; tv.tv_usec = 0;
       if(!select(audio_fd+1, NULL, &rfds, NULL, &tv)) break;
-      write(audio_fd,&sh_audio->a_buffer[sh_audio->a_buffer_len],OUTBURST);
-      audio_buffer_size+=OUTBURST;
+      write(audio_fd,&sh_audio->a_buffer[sh_audio->a_buffer_len],outburst);
+      audio_buffer_size+=outburst;
     }
     if(audio_buffer_size==0){
         printf("\n   ***  Your audio driver DOES NOT support select()  ***\n");
@@ -1401,10 +1442,12 @@
     }
 #endif
   }
-  buffer_delay=audio_buffer_size/(float)(sh_audio->o_bps);
+  audio_buffer_delay=audio_buffer_size/(float)(sh_audio->o_bps);
 #endif
-  a_frame=-(buffer_delay);
-  printf("Audio buffer size: %d bytes, delay: %5.3fs\n",audio_buffer_size,buffer_delay);
+  printf("Audio buffer size: %d bytes, delay: %5.3fs\n",audio_buffer_size,audio_buffer_delay);
+
+//  a_frame=-(audio_buffer_delay);
+  a_frame=0;
 //  RESET_AUDIO(audio_fd);
 }
 
@@ -1428,13 +1471,13 @@
 //==================== START PLAYING =======================
 
 if(file_format==DEMUXER_TYPE_AVI){
-  a_pts=d_audio->pts-(buffer_delay+audio_delay);
+  a_pts=d_audio->pts;
   audio_delay-=(float)(sh_audio->audio.dwInitialFrames-sh_video->video.dwInitialFrames)*sh_video->frametime;
 //  audio_delay-=(float)(sh_audio->audio.dwInitialFrames-sh_video->video.dwInitialFrames)/default_fps;
   if(verbose){
     printf("AVI Initial frame delay: %5.3f\n",(float)(sh_audio->audio.dwInitialFrames-sh_video->video.dwInitialFrames)*sh_video->frametime);
     printf("v: audio_delay=%5.3f  buffer_delay=%5.3f  a_pts=%5.3f  a_frame=%5.3f\n",
-             audio_delay,buffer_delay,a_pts,a_frame);
+             audio_delay,audio_buffer_delay,a_pts,a_frame);
     printf("START:  a_pts=%5.3f  v_pts=%5.3f  \n",d_audio->pts,d_video->pts);
   }
   delay_corrected=0; // has to correct PTS diffs
@@ -1452,39 +1495,69 @@
 while(!eof){
 
 /*========================== PLAY AUDIO ============================*/
+if(!has_audio){
+  int playsize=512;
+  a_frame+=playsize/(float)(sh_audio->o_bps);
+  a_pts+=playsize/(float)(sh_audio->o_bps);
+  //time_frame+=playsize/(float)(sh_audio->o_bps);
 
+} else
 while(has_audio){
+  unsigned int t;
+  int playsize=outburst;
+  audio_buf_info zz;
+  int use_select=1;
+  
+  if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)!=-1){
+      // calculate exact buffer space:
+      playsize=zz.fragments*zz.fragsize;
+      if(!playsize) break; // buffer is full, do not block here!!!
+      use_select=0;
+  }
+  
+  if(playsize>MAX_OUTBURST) playsize=MAX_OUTBURST; // we shouldn't exceed it!
+  //if(playsize>outburst) playsize=outburst;
 
   // Update buffer if needed
-  unsigned int t=GetTimer();
+  t=GetTimer();
   current_module="decode_audio";   // Enter AUDIO decoder module
-  //sh_audio->codec->driver=has_audio; // FIXME!
-  while(sh_audio->a_buffer_len<OUTBURST && !d_audio->eof){
-    int ret=decode_audio(sh_audio,&sh_audio->a_buffer[sh_audio->a_buffer_len],sh_audio->a_buffer_size-sh_audio->a_buffer_len);
+  while(sh_audio->a_buffer_len<playsize && !d_audio->eof){
+    int ret=decode_audio(sh_audio,&sh_audio->a_buffer[sh_audio->a_buffer_len],
+        playsize-sh_audio->a_buffer_len,sh_audio->a_buffer_size-sh_audio->a_buffer_len);
     if(ret>0) sh_audio->a_buffer_len+=ret; else break;
   }
   current_module=NULL;   // Leave AUDIO decoder module
   t=GetTimer()-t;audio_time_usage+=t*0.000001;
-
+  
+  if(playsize>sh_audio->a_buffer_len) playsize=sh_audio->a_buffer_len;
+  playsize/=outburst; playsize*=outburst; // rounding to fragment boundary
+  
+//  printf("play %d bytes of %d [max: %d]\n",playsize,sh_audio->a_buffer_len,sh_audio->a_buffer_size);
 
   // Play sound from the buffer:
-  if(sh_audio->a_buffer_len>=OUTBURST){ // if not EOF
+  if(playsize>0){ // if not EOF
 #ifdef USE_XMMP_AUDIO
-    pSound->Write( pSound, sh_audio->a_buffer, OUTBURST );
+    pSound->Write( pSound, sh_audio->a_buffer, playsize );
 #else
 #ifdef SIMULATE_ALSA
-    fake_ALSA_write(audio_fd,sh_audio->a_buffer,OUTBURST); // for testing purposes
+    fake_ALSA_write(audio_fd,sh_audio->a_buffer,playsize); // for testing purposes
 #else
-    write(audio_fd,sh_audio->a_buffer,OUTBURST);
+    playsize=write(audio_fd,sh_audio->a_buffer,playsize);
 #endif
 #endif
-    sh_audio->a_buffer_len-=OUTBURST;
-    memcpy(sh_audio->a_buffer,&sh_audio->a_buffer[OUTBURST],sh_audio->a_buffer_len);
+    if(playsize>0){
+      sh_audio->a_buffer_len-=playsize;
+      memcpy(sh_audio->a_buffer,&sh_audio->a_buffer[playsize],sh_audio->a_buffer_len);
+      a_frame+=playsize/(float)(sh_audio->o_bps);
+      //a_pts+=playsize/(float)(sh_audio->o_bps);
+//      time_frame+=playsize/(float)(sh_audio->o_bps);
+    }
 #ifndef USE_XMMP_AUDIO
 #ifndef SIMULATE_ALSA
     // check buffer
 #ifdef HAVE_AUDIO_SELECT
-    {  fd_set rfds;
+    if(use_select){  // do not use this code if SNDCTL_DSP_GETOSPACE works
+       fd_set rfds;
        struct timeval tv;
        FD_ZERO(&rfds);
        FD_SET(audio_fd, &rfds);
@@ -1492,7 +1565,7 @@
        tv.tv_usec = 0;
        if(select(audio_fd+1, NULL, &rfds, NULL, &tv)){
          a_frame+=OUTBURST/(float)(sh_audio->o_bps);
-         a_pts+=OUTBURST/(float)(sh_audio->o_bps);
+//         a_pts+=OUTBURST/(float)(sh_audio->o_bps);
 //         printf("Filling audio buffer...\n");
          continue;
 //       } else {
@@ -1508,13 +1581,9 @@
 } // if(has_audio)
 
 /*========================== UPDATE TIMERS ============================*/
-
-  a_frame+=OUTBURST/(float)(sh_audio->o_bps);
-  a_pts+=OUTBURST/(float)(sh_audio->o_bps);
-
+#if 0
   if(alsa){
     // Use system timer for sync, not audio card/driver
-    time_frame+=OUTBURST/(float)(sh_audio->o_bps);
     time_frame-=GetRelativeTime();
     if(time_frame<-0.1 || time_frame>0.1){
       time_frame=0;
@@ -1529,19 +1598,19 @@
         }
     }
   }
-
+#endif
 
 /*========================== PLAY VIDEO ============================*/
 
 if(1)
-  while(v_frame<a_frame || force_redraw){
+  while(1){
   
     float frame_time=1;
     float pts1=d_video->pts;
 
     current_module="decode_video";
     
-    if(!force_redraw && v_frame+0.1<a_frame) drop_frame=1; else drop_frame=0;
+//    if(!force_redraw && v_frame+0.1<a_frame) drop_frame=1; else drop_frame=0;
     drop_frame_cnt+=drop_frame;
 
   //--------------------  Decode a frame: -----------------------
@@ -1714,6 +1783,29 @@
     }
     v_frame+=frame_time;
     v_pts+=frame_time;
+    time_frame+=frame_time;  // for nosound
+
+    // It's time to sleep...
+    time_frame-=GetRelativeTime(); // reset timer
+
+    if(has_audio){
+          int delay=get_audio_delay(audio_fd);
+          if(verbose)printf("delay=%d\n",delay);
+          time_frame=v_frame;
+          time_frame-=a_frame-(float)delay/(float)sh_audio->o_bps;
+    } else {
+          if(time_frame<-0.1 || time_frame>0.1) time_frame=0;
+    }
+    
+      if(verbose)printf("sleep: %5.3f  a:%6.3f  v:%6.3f  \n",stime,a_frame,v_frame);
+      
+      while(time_frame>0.005){
+          if(time_frame<=0.020)
+             usleep(0); // sleep 10ms
+          else
+             usleep(1000000*(time_frame-0.002));
+          time_frame-=GetRelativeTime();
+      }
 
     if(!drop_frame){
         current_module="flip_page";
@@ -1726,6 +1818,7 @@
     if(force_redraw){
       --force_redraw;
       if(!force_redraw) osd_function=OSD_PLAY;
+      continue;
     }
 
 //    printf("A:%6.1f  V:%6.1f  A-V:%7.3f  frame=%5.2f   \r",d_audio->pts,d_video->pts,d_audio->pts-d_video->pts,a_frame);
@@ -1734,34 +1827,45 @@
 #if 1
 /*================ A-V TIMESTAMP CORRECTION: =========================*/
   if(has_audio){
+    // unplayed bytes in our and soundcard/dma buffer:
+    int delay_bytes=get_audio_delay(audio_fd)+sh_audio->a_buffer_len;
+    float delay=(float)delay_bytes/(float)sh_audio->o_bps;
+
     if(pts_from_bps && (file_format==DEMUXER_TYPE_AVI)){
 //      a_pts=(float)ds_tell(d_audio)/sh_audio->wf.nAvgBytesPerSec-(buffer_delay+audio_delay);
-      a_pts=(float)ds_tell(d_audio)/sh_audio->wf->nAvgBytesPerSec-(buffer_delay);
+      a_pts=(float)ds_tell(d_audio)/sh_audio->wf->nAvgBytesPerSec;
       delay_corrected=1; // hack
     } else
     if(d_audio->pts){
 //      printf("\n=== APTS  a_pts=%5.3f  v_pts=%5.3f ===  \n",d_audio->pts,d_video->pts);
 #if 1
       if(!delay_corrected){
-        float x=d_audio->pts-d_video->pts-(buffer_delay+audio_delay);
-        float y=-(buffer_delay+audio_delay);
+        float x=d_audio->pts-d_video->pts-(delay+audio_delay);
+        float y=-(delay+audio_delay);
         printf("Initial PTS delay: %5.3f sec  (calculated: %5.3f)\n",x,y);
         audio_delay+=x;
         //a_pts-=x;
         delay_corrected=1;
         if(verbose)
         printf("v: audio_delay=%5.3f  buffer_delay=%5.3f  a.pts=%5.3f  v.pts=%5.3f\n",
-               audio_delay,buffer_delay,d_audio->pts,d_video->pts);
+               audio_delay,delay,d_audio->pts,d_video->pts);
       }
 #endif
-      a_pts=d_audio->pts-(buffer_delay+audio_delay);
-      d_audio->pts=0;
+//      a_pts=(ds_tell_pts(d_audio)+sh_audio->a_in_buffer_len)/(float)sh_audio->i_bps;
+//      printf("a_pts+=%6.3f    \n",a_pts);
+//      a_pts=d_audio->pts-a_pts;
+
+      a_pts=d_audio->pts;
+      a_pts+=(ds_tell_pts(d_audio)-sh_audio->a_in_buffer_len)/(float)sh_audio->i_bps;
+
+      //a_pts=d_audio->pts; d_audio->pts=0;
     }
     if(d_video->pts) v_pts=d_video->pts;
     if(frame_corr_num==5){
       float x=(frame_correction/5.0f);
       if(delay_corrected){
-        printf("A:%6.1f  V:%6.1f  A-V:%7.3f",a_pts,v_pts,x);
+//        printf("A:%6.1f  V:%6.1f  A-V:%7.3f",a_pts-audio_delay-delay,v_pts,x);
+        printf("A:%6.1f (%6.1f)  V:%6.1f  A-V:%7.3f",a_pts,a_pts-audio_delay-delay,v_pts,x);
         x*=0.5f;
         if(x<-max_pts_correction) x=-max_pts_correction; else
         if(x> max_pts_correction) x= max_pts_correction;
@@ -1779,7 +1883,7 @@
       }
       frame_corr_num=0; frame_correction=0;
     }
-    if(frame_corr_num>=0) frame_correction+=a_pts-v_pts;
+    if(frame_corr_num>=0) frame_correction+=(a_pts-delay-audio_delay)-v_pts;
   } else {
     // No audio:
     if(d_video->pts) v_pts=d_video->pts;
@@ -1825,6 +1929,8 @@
 #endif
   }
 
+
+    if(!force_redraw) break;
   } //  while(v_frame<a_frame || force_redraw)
 
 
@@ -1857,11 +1963,11 @@
       rel_seek_secs-=600;break;
     // delay correction:
     case '+':
-      buffer_delay+=0.1;  // increase audio buffer delay
+      audio_delay+=0.1;  // increase audio buffer delay
       a_frame-=0.1;
       break;
     case '-':
-      buffer_delay-=0.1;  // decrease audio buffer delay
+      audio_delay-=0.1;  // decrease audio buffer delay
       a_frame+=0.1;
       break;
     // quit
@@ -1986,7 +2092,7 @@
 #else
       avi_video_pts=v_pts;
 #endif
-      a_pts=avi_video_pts-(buffer_delay);
+      a_pts=avi_video_pts;
       //a_pts=v_pts; //-(buffer_delay+audio_delay);
 
       if(has_audio){
@@ -2189,7 +2295,7 @@
       max_pts_correction=0.1;
       frame_corr_num=-5; frame_correction=0;
       force_redraw=5;
-      a_frame=-buffer_delay-skip_audio_secs;
+      a_frame=-skip_audio_secs;
 //      a_frame=-audio_delay-buffer_delay-skip_audio_secs;
       v_frame=0; // !!!!!!
       audio_time_usage=0; video_time_usage=0; vout_time_usage=0;