[MPlayer-dev-eng] [PATCH] audio file truncation

Alan Curry pacman at theworld.com
Tue Jun 6 03:45:53 CEST 2006


Here are 3 separate bugs with the same symptom: audio files are truncated
when played by mplayer. It is most noticeable with low quality sound files
because the lower bitrate increases the amount of time that is lost for a
given number of bytes.

The first one is a bug in demux_lavf, where it detects EOF too soon because
it's looking at the wrong layer. Near the end of the file, there comes a
point where the stream layer has read the whole file and emptied its buffer,
so stream_eof() is true, but there is still data in the lavf buffer. The fix
is just to delete the stream_eof test in demux_lavf_fill_buffer, since
av_read_frame already indicates EOF at the proper time.

The second bug is in ao_oss (and probably several other ao's) and requires a
change to the libao2 interface. The ->play() method rounds the buffer length
downward to a multiple of the chunk size, so the final chunk never gets
played because its size is rounded down to 0. So my second patch adds a
"final chunk" flag to the play method. When an ao sees this flag, it should
play the chunk even if it's small. I've made the necessary change to ao_oss;
other ao's will continue to behave as they did before, and if they have the
same problem they should be fixed later.

The third bug is that the mplayer main loop exits too soon, when there is
still data in the sh_audio output buffer.
-------------- next part --------------
Index: libao2/audio_out.h
===================================================================
--- libao2/audio_out.h	(revision 18570)
+++ libao2/audio_out.h	(working copy)
@@ -65,6 +65,9 @@
 #define AOCONTROL_SET_PLUGIN_DRIVER 6
 #define AOCONTROL_SET_PLUGIN_LIST 7
 
+/* Flags for the ->play function */
+#define AOPLAY_FINAL_CHUNK (1<<0)
+
 typedef struct ao_control_vol_s {
 	float left;
 	float right;
Index: libao2/ao_oss.c
===================================================================
--- libao2/ao_oss.c	(revision 18570)
+++ libao2/ao_oss.c	(working copy)
@@ -495,8 +495,13 @@
 // it should round it down to outburst*n
 // return: number of bytes played
 static int play(void* data,int len,int flags){
-    len/=ao_data.outburst;
-    len=write(audio_fd,data,len*ao_data.outburst);
+    if(len==0)
+        return len;
+    if(len>ao_data.outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
+        len/=ao_data.outburst;
+        len*=ao_data.outburst;
+    }
+    len=write(audio_fd,data,len);
     return len;
 }
 
Index: mplayer.c
===================================================================
--- mplayer.c	(revision 18570)
+++ mplayer.c	(working copy)
@@ -3618,7 +3618,8 @@
 while(sh_audio){
   unsigned int t;
   double tt;
-  int playsize;
+  int playsize, playsize_attempted;
+  int playflags;
 
   current_module="play_audio";
   
@@ -3650,12 +3651,23 @@
   }
   t=GetTimer()-t;
   tt = t*0.000001f; audio_time_usage+=tt;
-  if(playsize>sh_audio->a_out_buffer_len) playsize=sh_audio->a_out_buffer_len;
+  playflags=0;
+  if(playsize>sh_audio->a_out_buffer_len) {
+    playsize=sh_audio->a_out_buffer_len;
+    if(sh_audio->a_in_buffer_len==0 && sh_audio->a_buffer_len==0)
+      playflags |= AOPLAY_FINAL_CHUNK;
+  }
 
   // play audio:  
   current_module="play_audio";
-  playsize=audio_out->play(sh_audio->a_out_buffer,playsize,0);
+  playsize_attempted=playsize;
+  playsize=audio_out->play(sh_audio->a_out_buffer,playsize,playflags);
 
+  if(playsize==0 && (playflags & AOPLAY_FINAL_CHUNK)) {
+    mp_msg(MSGT_CPLAYER, MSGL_INFO, "Audio stream truncated!\n");
+    playsize=playsize_attempted;
+  }
+
   if(playsize>0){
       sh_audio->a_out_buffer_len-=playsize;
       memmove(sh_audio->a_out_buffer,&sh_audio->a_out_buffer[playsize],sh_audio->a_out_buffer_len);
Index: DOCS/tech/libao2.txt
===================================================================
--- DOCS/tech/libao2.txt	(revision 18570)
+++ DOCS/tech/libao2.txt	(working copy)
@@ -29,10 +29,11 @@
 
 static int play(void* data,int len,int flags);
   Plays a bit of audio, which is received throught the "data" memory area, with
-  a size of "len". The "flags" isn't used yet. It has to copy the data, because
-  they can be overwritten after the call is made. Doesn't really have to use
-  all the bytes, it has to give back how many have been used (copied to
-  buffer).
+  a size of "len". It has to copy the data, because they can be overwritten
+  after the call is made. If flags & AOPLAY_FINAL_CHUNK, the end of the audio
+  stream is being played, so it should try to play the full "len" amount.
+  Otherwise it doesn't really have to use all the bytes, it has to give back
+  how many have been used (copied to buffer).
 
 static float get_delay(); 
   Returns how long time it will take to play the data currently in the
-------------- next part --------------
Index: mplayer.c
===================================================================
--- mplayer.c	(revision 18570)
+++ mplayer.c	(working copy)
@@ -3683,7 +3683,7 @@
     float a_pos = sh_audio->delay - audio_out->get_delay() * playback_speed;
     print_status(a_pos, 0, 0);
   }
-  if(d_audio->eof && sh_audio->a_in_buffer_len <= 0 && sh_audio->a_buffer_len <= 0) eof = PT_NEXT_ENTRY;
+  if(d_audio->eof && sh_audio->a_in_buffer_len <= 0 && sh_audio->a_buffer_len <= 0 && sh_audio->a_out_buffer_len <= 0) eof = PT_NEXT_ENTRY;
 
 } else {
 
-------------- next part --------------
Index: libmpdemux/demux_lavf.c
===================================================================
--- libmpdemux/demux_lavf.c	(revision 18570)
+++ libmpdemux/demux_lavf.c	(working copy)
@@ -329,11 +329,6 @@
 
     demux->filepos=stream_tell(demux->stream);
 
-    if(stream_eof(demux->stream)){
-//        demuxre->stream->eof=1;
-        return 0;
-    }
-
     if(av_read_frame(priv->avfc, &pkt) < 0)
         return 0;
         


More information about the MPlayer-dev-eng mailing list