[xiph-commits] r16985 - trunk/ao/src/plugins/esd

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Thu Mar 18 20:04:44 PDT 2010


Author: xiphmont
Date: 2010-03-18 20:04:44 -0700 (Thu, 18 Mar 2010)
New Revision: 16985

Modified:
   trunk/ao/src/plugins/esd/ao_esd.c
Log:
Only ever write 4096 bytes at a time to ESD.

EsounD: It truly made the Buddha's llama cry.



Modified: trunk/ao/src/plugins/esd/ao_esd.c
===================================================================
--- trunk/ao/src/plugins/esd/ao_esd.c	2010-03-19 01:06:20 UTC (rev 16984)
+++ trunk/ao/src/plugins/esd/ao_esd.c	2010-03-19 03:04:44 UTC (rev 16985)
@@ -59,6 +59,9 @@
 {
 	int sock;
 	char *host;
+        char bugbuffer[4096];
+        int bugfill;
+        int bits;
 } ao_esd_internal;
 
 /* An old favorite from the UNIX-hater's handbook.
@@ -125,13 +128,13 @@
 {
 	ao_esd_internal *internal;
 
-	internal = (ao_esd_internal *) malloc(sizeof(ao_esd_internal));
+	internal = (ao_esd_internal *) calloc(1,sizeof(ao_esd_internal));
 
 	if (internal == NULL)
 		return 0; /* Could not initialize device memory */
 
 	internal->host = NULL;
-
+        internal->sock = -1;
 	device->internal = internal;
         device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
         device->output_matrix=strdup("L,R");
@@ -162,13 +165,17 @@
 
 	switch (format->bits)
 	{
-	case 8  : esd_bits = ESD_BITS8;
-		  break;
-	case 16 : esd_bits = ESD_BITS16;
-		  break;
-	default : return 0;
+	case 8  :
+          esd_bits = ESD_BITS8;
+          internal->bits = 8;
+          break;
+	case 16 :
+          esd_bits = ESD_BITS16;
+          internal->bits = 16;
+          break;
+	default :
+          return 0;
 	}
-
 	switch (device->output_channels)
 	{
 	case 1 : esd_channels = ESD_MONO;
@@ -191,46 +198,109 @@
 	return 1;
 }
 
+/* ESD has a longstanding bug where it won't properly handle any input
+   frame size other than ESD_BUF_SIZE, which is defined in esd.h to be
+   4096.  The bug is lines 317/318 in esound/players.c; the lines
+   perform an unneeded/incorrect short-read test that abort playback
+   of any nonblocking read that returns not-4096 bytes.  Simply
+   commenting out these two lines corrects the problem, but even if
+   it's fixed today, ESD is dead and any already deployed daemons are
+   unlilely to ever be fixed.
+
+   So... only ever write 4096 byte chunks.  Ever. */
+
+int write4096(int fd, const char *output_samples){
+  int num_bytes = 4096;
+  while (num_bytes > 0) {
+    /* the loop always makes sure at least a complete block is written
+       out almost always, almost instantly-- there's no other way to
+       deal.  Except to fix esd of course. */
+    ssize_t ret = write(fd, output_samples, num_bytes);
+    if(ret<0){
+      switch(errno){
+      case EAGAIN:
+      case EINTR:
+        break;
+      default:
+        return ret;
+      }
+    }else{
+      output_samples += ret;
+      num_bytes -= ret;
+    }
+  }
+  return 0;
+}
+
 int ao_plugin_play(ao_device *device, const char* output_samples,
-		uint_32 num_bytes)
+                   uint_32 num_bytes)
 {
-	ao_esd_internal *internal = (ao_esd_internal *) device->internal;
+  ao_esd_internal *internal = (ao_esd_internal *) device->internal;
 
+  if(internal->bugfill){
+    int addto = internal->bugfill + num_bytes;
+    if(addto>4096) addto = 4096;
+    addto -= internal->bugfill;
+    if(addto){
+      memcpy(internal->bugbuffer + internal->bugfill, output_samples, addto);
+      output_samples += addto;
+      internal->bugfill += addto;
+      num_bytes -= addto;
+    }
+  }
 
-        while (num_bytes > 0) {
-          ssize_t ret = write(internal->sock, output_samples, num_bytes);
-          if(ret<0){
-            switch(errno){
-            case EAGAIN:
-            case EINTR:
-              break;
-            default:
-              return 0;
-            }
-          }else{
-            output_samples += ret;
-            num_bytes -= ret;
-          }
-        }
+  if(internal->bugfill==4096){
+    if(write4096(internal->sock,internal->bugbuffer))
+      return 0;
+    internal->bugfill=0;
+  }
 
-        return 1;
+  while(num_bytes>=4096){
+    if(write4096(internal->sock,output_samples))
+      return 0;
+    output_samples += 4096;
+    num_bytes -= 4096;
+  }
+
+  if(num_bytes){
+    memcpy(internal->bugbuffer, output_samples, num_bytes);
+    internal->bugfill = num_bytes;
+  }
+  return 1;
 }
 
-
 int ao_plugin_close(ao_device *device)
 {
-	ao_esd_internal *internal = (ao_esd_internal *) device->internal;
+  ao_esd_internal *internal = (ao_esd_internal *) device->internal;
 
-	esd_close(internal->sock);
+  if(internal->bugfill && internal->sock != -1){
 
-	return 1;
+    if(internal->bugfill<4096){
+      switch(internal->bits){
+      case 8:
+        memset(internal->bugbuffer + internal->bugfill, 128, 4096-internal->bugfill);
+        break;
+      default:
+        memset(internal->bugbuffer + internal->bugfill, 0, 4096-internal->bugfill);
+        break;
+      }
+    }
+
+    write4096(internal->sock,internal->bugbuffer);
+    internal->bugfill = 0;
+  }
+
+  if(internal->sock != -1)
+    esd_close(internal->sock);
+  internal->sock=-1;
+  return 1;
 }
 
 
 void ao_plugin_device_clear(ao_device *device)
 {
-	ao_esd_internal *internal = (ao_esd_internal *) device->internal;
+  ao_esd_internal *internal = (ao_esd_internal *) device->internal;
 
-	if(internal->host) free(internal->host);
-	free(internal);
+  if(internal->host) free(internal->host);
+  free(internal);
 }



More information about the commits mailing list