[xiph-cvs] cvs commit: vorbis-tools/ogg123 buffer.c buffer.h Makefile.am ogg123.c ogg123.h

Kenneth C. Arnold kcarnold at xiph.org
Fri Dec 29 21:44:33 PST 2000



kcarnold    00/12/29 21:44:32

  Modified:    ogg123   Makefile.am ogg123.c ogg123.h
  Added:       ogg123   buffer.c buffer.h
  Log:
  Buffering for ogg123. It's not as robust as it could be, but still
  better than nothing.
  
  gcc 2.95.2 20000220 does _not_ compile this right with -O1 or higher.
  gdb cannot properly debug multithreaded apps (it follows the parent
  process even when I tell it to follow the child explicitly), so I have
  not been able to figure out where the culprit is either.
  
  This is a Unix buffer implementation. Non-Unix platforms should replace
  buffer.c with a pass-through in submit_chunk that just calls devices_write.

Revision  Changes    Path
1.9       +2 -2      vorbis-tools/ogg123/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/Makefile.am,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- Makefile.am	2000/12/25 22:20:41	1.8
+++ Makefile.am	2000/12/30 05:44:31	1.9
@@ -12,9 +12,9 @@
 
 ogg123_LDFLAGS = @OGG_LIBS@ @VORBIS_LIBS@ @VORBISFILE_LIBS@ @AO_LIBS@
 
-ogg123_INCLUDES = ogg123.h
+ogg123_INCLUDES = ogg123.h buffer.h
 
-ogg123_SOURCES = ogg123.c ao_interface.c
+ogg123_SOURCES = ogg123.c ao_interface.c buffer.c
 
 EXTRA_DIST = $(man_MANS) $(doc_DATA)
 

1.18      +39 -16    vorbis-tools/ogg123/ogg123.c

Index: ogg123.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- ogg123.c	2000/12/30 04:43:47	1.17
+++ ogg123.c	2000/12/30 05:44:31	1.18
@@ -14,7 +14,7 @@
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: ogg123.c,v 1.17 2000/12/30 04:43:47 msmith Exp $
+ last mod: $Id: ogg123.c,v 1.18 2000/12/30 05:44:31 kcarnold Exp $
 
  ********************************************************************/
 
@@ -28,8 +28,8 @@
 #include <netdb.h>
 #include <netinet/in.h>
 #include <errno.h>
-#include <getopt.h>
 #include <time.h>
+#include <getopt.h>
 
 #include "ogg123.h"
 
@@ -62,6 +62,7 @@
     {"verbose", no_argument, 0, 'v'},
     {"quiet", no_argument, 0, 'q'},
     {"shuffle", no_argument, 0, 'z'},
+    {"buffer", required_argument, 0, 'b'},
     {0, 0, 0, 0}
 };
 
@@ -76,7 +77,7 @@
             "Usage: ogg123 [<options>] <input file> ...\n\n"
             "  -h, --help     this help\n"
             "  -V, --version  display Ogg123 version\n"
-	    "  -d, --device=d uses 'd' as an output device)\n"
+	    "  -d, --device=d uses 'd' as an output device\n"
             "      Possible devices are (some may not be compiled):\n"
             "      null (output nothing), oss (for Linux and *BSD),\n"
             "      irix, solaris, wav (write to a .WAV file)\n"
@@ -84,6 +85,7 @@
             "  -o, --device-option=k:v passes special option k with value\n"
             "      v to previously specified device (with -d).  See\n"
             "      man page for more info.\n"
+	    "  -b n, --buffer n  use a buffer of 'n' chunks (4096 bytes)\n"
             "  -v, --verbose  display progress and other useful stuff\n"
             "  -q, --quiet    don't display anything (no title)\n"
             "  -z, --shuffle  shuffle play\n");
@@ -98,6 +100,7 @@
     int temp_driver_id = -1;
     devices_t *current;
     int bits, rate, channels;
+    buf_t *buffer = NULL;
 
     opt.read_file = NULL;
     opt.shuffle = 0;
@@ -106,18 +109,22 @@
     opt.seekpos = 0;
     opt.instream = NULL;
     opt.outdevices = NULL;
+    opt.buffer_size = 0;
 
     ao_initialize();
 
     temp_driver_id = get_default_device();
 
-    while (-1 != (ret = getopt_long(argc, argv, "d:hqk:o:vV:z",
+    while (-1 != (ret = getopt_long(argc, argv, "b:d:hk:o:qvV:z",
                                     long_options, &option_index))) {
         switch (ret) {
         case 0:
             fprintf(stderr,
                     "Internal error: long option given when none expected.\n");
             exit(1);
+	case 'b':
+	  opt.buffer_size = atoi (optarg);
+	  break;
         case 'd':
             /* Need to store previous device before gathering options for
                this device */
@@ -216,6 +223,9 @@
         current = current->next_device;
     }
 
+    if (opt.buffer_size)
+      buffer = fork_writer (opt.buffer_size, opt.outdevices);
+    
     if (opt.shuffle) {
         /* Messy code that I didn't write -ken */
         int i;
@@ -233,21 +243,23 @@
         }
         for (i = 0; i < nb; i++) {
             opt.read_file = argv[p[i] + optind];
-	    play_file(opt);
+	    play_file(opt, buffer);
         }
     } else {
         while (optind < argc) {
             opt.read_file = argv[optind];
-	    play_file(opt);
+	    play_file(opt, buffer);
             optind++;
         }
     }
 
+    buffer_shutdown (buffer);
+
     while (opt.outdevices != NULL) {
-	ao_close(opt.outdevices->device);
-	current = opt.outdevices->next_device;
-	free(opt.outdevices);
-	opt.outdevices = current;
+      ao_close(opt.outdevices->device);
+      current = opt.outdevices->next_device;
+      free(opt.outdevices);
+      opt.outdevices = current;
     }
 
     ao_shutdown();
@@ -255,7 +267,7 @@
     return (0);
 }
 
-void play_file(ogg123_options_t opt)
+void play_file(ogg123_options_t opt, buf_t *buffer)
 {
     /* Oh my gosh this is disgusting. Big cleanups here will include an
        almost complete rewrite of the hacked-out HTTP streaming, a shift
@@ -322,12 +334,13 @@
                 char last = 0, in = 0;
                 int eol = 0;
 
-		/* Need to 'quiet' this header dump */
-		fprintf(stderr, "HTTP Headers:\n");
+		if (opt.verbose > 0)
+		  fprintf(stderr, "HTTP Headers:\n");
                 for (;;) {
                     last = in;
                     in = getc(opt.instream);
-		    putc(in, stderr);
+		    if (opt.verbose > 0)
+		      putc(in, stderr);
                     if (last == 13 && in == 10) {
                         if (eol)
                             break;
@@ -372,7 +385,7 @@
                 int i;
 
                 for (i = 0; ogg_comment_keys[i].key != NULL; i++)
-		    if (!strncmp
+		    if (!strncasecmp
                         (ogg_comment_keys[i].key, cc,
                          strlen(ogg_comment_keys[i].key))) {
                         fprintf(stderr, ogg_comment_keys[i].formatstr,
@@ -421,7 +434,17 @@
                 if (old_section != current_section && old_section != -1)
                     eos = 1;
 
-		devices_write(convbuffer, ret, opt.outdevices);
+		if (buffer)
+		  {
+		    chunk_t chunk;
+		    chunk.len = ret;
+		    memcpy (chunk.data, convbuffer, ret);
+		    
+		    submit_chunk (buffer, chunk);
+		  }
+		else
+		  devices_write(convbuffer, ret, opt.outdevices);
+		
                 if (opt.verbose > 0) {
                     u_pos = ov_time_tell(&vf);
                     c_min = (long) u_pos / (long) 60;

1.3       +5 -1      vorbis-tools/ogg123/ogg123.h

Index: ogg123.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- ogg123.h	2000/12/25 22:20:41	1.2
+++ ogg123.h	2000/12/30 05:44:31	1.3
@@ -25,15 +25,19 @@
   double seekpos;             /* Amount to seek by */
   FILE *instream;             /* Stream to read from. */
   devices_t *outdevices;      /* Streams to write to. */
+  int buffer_size;            /* Size of the buffer in chunks. */
 } ogg123_options_t;           /* Changed in 0.6 to be non-static */
 
+/* This goes here because it relies on some of the above. */
+#include "buffer.h"
+
 devices_t *append_device(devices_t * devices_list, int driver_id,
                          ao_option_t * options);
 void devices_write(void *ptr, size_t size, devices_t * d);
 void usage(void);
 int add_option(ao_option_t ** op_h, const char *optstring);
 int get_default_device(void);
-void play_file(ogg123_options_t opt);
+void play_file(ogg123_options_t opt, buf_t *buffer);
 int get_tcp_socket(void); /* Will be going soon. */
 FILE *http_open(char *server, int port, char *path); /* ditto */
 

1.1                  vorbis-tools/ogg123/buffer.c

Index: buffer.c
===================================================================
/* buffer.c
 *  buffering code for ogg123. This is Unix-specific. Other OSes anyone?
 *
 * Thanks to Lee McLouchlin's buffer(1) for inspiration; no code from
 * that program is used in this buffer.
 */

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h> /* for fork and pipe*/
#include <fcntl.h>
#include <malloc.h>

#include "ogg123.h"
#include "buffer.h"

/* Initialize the buffer structure. */
void buffer_init (buf_t *buf, long size)
{
  buf->status = 0;
  buf->reader = buf->writer = buf->buffer;
  buf->end = buf->buffer + (size - 1);
}

/* Main write loop. No semaphores. No deadlock. No problem. I hope. */
void writer_main (buf_t *buf, devices_t *d)
{
  devices_t *d1;
  while (! (buf->status & STAT_SHUTDOWN && buf->reader == buf->writer))
    {
      /* Writer just waits on reader to be done with buf_write.
       * Reader must ensure that we don't deadlock. */

      write (buf->fds[1], "1", 1); /* This identifier could hold a lot
                                    * more detail in the future. */

      while (buf->reader == buf->writer && !(buf->status & STAT_SHUTDOWN));

      if (buf->reader == buf->writer) break;

      /* devices_write (buf->writer->data, buf->writer->len, d); */
      {
        d1 = d;
        while (d1 != NULL) {
          ao_play(d1->device, buf->writer->data, buf->writer->len);
          d1 = d1->next_device;
        }
      }

      if (buf->writer == buf->end)
        buf->writer = buf->buffer;
      else
        buf->writer++;
   }
  buf->status = 0;
  write (buf->fds[1], "2", 1);
  _exit(0);
}

/* fork_writer is called to create the writer process. This creates
 * the shared memory segment of 'size' chunks, and returns a pointer
 * to the buffer structure that is shared. Just pass this straight to
 * submit_chunk and all will be happy. */

buf_t *fork_writer (long size, devices_t *d)
{
  int childpid;
  buf_t *buf;

  /* Get the shared memory segment. */
  int shmid = shmget (IPC_PRIVATE,
                          sizeof(buf_t) + sizeof (chunk_t) * (size - 1),
                          IPC_CREAT|S_IREAD|S_IWRITE);

  if (shmid == -1)
    {
      perror ("shmget");
      exit (1);
    }
  
  /* Attach the segment to us (and our kids). Get and store the pointer. */
  buf = (buf_t *) shmat (shmid, 0, 0);
  
  if (buf == NULL)
    {
      perror ("shmat");
      exit (1);
    }
  
  buffer_init (buf, size);
  
  /* Create a pipe for communication between the two processes. Unlike
   * the first incarnation of an ogg123 buffer, the data is not transferred
   * over this channel, only occasional "WAKE UP!"'s. */

  if (pipe (buf->fds))
    {
      perror ("pipe");
      exit (1);
    }

  fcntl (buf->fds[1], F_SETFL, O_NONBLOCK);
  /* write should never block; read should always block. */

  fflush (stdout);
  /* buffer flushes stderr, but stderr is unbuffered (*duh*!) */
  
  childpid = fork();
  
  if (childpid == -1)
    {
      perror ("fork");
      exit (1);
    }

  if (childpid == 0)
    {
      writer_main (buf, d);
      return NULL;
    }
  else
    return buf;
}

void submit_chunk (buf_t *buf, chunk_t chunk)
{
  struct timeval tv;
  static fd_set set;

  FD_ZERO(&set);
  FD_SET(buf->fds[0], &set);

  /* Wait wait, don't step on my sample! */
  while (!((buf->reader != buf->end && buf->reader + 1 != buf->writer) ||
           (buf->reader == buf->end && buf->writer != buf->buffer)))
    {
      /* buffer overflow (yikes! no actually it's a GOOD thing) */
      int ret;
      char t;
      
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      ret = select (buf->fds[0]+1, &set, NULL, NULL, &tv);
      
      while (ret-- > 0)
        read (buf->fds[0], &t, 1);
    }
              
  *(buf->reader) = chunk;
  /* do this atomically */
  if (buf->reader == buf->end)
    buf->reader = buf->buffer;
  else
    buf->reader++;
}

void buffer_shutdown (buf_t *buf)
{
  struct timeval tv;

  buf->status |= STAT_SHUTDOWN;
  while (buf->status != 0)
    {
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      select (0, NULL, NULL, NULL, &tv);
    } 
}
     

1.1                  vorbis-tools/ogg123/buffer.h

Index: buffer.h
===================================================================
/* Common things between reader and writer threads */

#ifndef __BUFFER_H
#define __BUFFER_H

#include "ogg123.h"

typedef struct chunk_s
{
  long len; /* Length of the chunk (for if we only got partial data) */
  char data[4096]; /* Data. 4096 is the chunk size we request from libvorbis. */
} chunk_t;

typedef struct buf_s
{
  char status;       /* Status. See STAT_* below. */
  int fds[2];        /* Pipe file descriptors. */
  chunk_t *reader;   /* Chunk the reader is busy with */
  chunk_t *writer;   /* Chunk the writer is busy with */
  chunk_t *end;      /* Last chunk in the buffer (for convenience) */
  chunk_t buffer[1]; /* The buffer itself. It's more than one chunk. */
} buf_t;

buf_t *fork_writer (long size, devices_t *d);
void submit_chunk (buf_t *buf, chunk_t chunk);
void buffer_shutdown (buf_t *buf);

#define STAT_FLUSH 1
#define STAT_SHUTDOWN 2

#endif /* !defined (__BUFFER_H) */

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list