[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