diff --git a/panda/src/mpg123/mpgbuffer.c b/panda/src/mpg123/mpgbuffer.c new file mode 100644 index 0000000000..e528afa5ac --- /dev/null +++ b/panda/src/mpg123/mpgbuffer.c @@ -0,0 +1,231 @@ +/* + * buffer.c + * + * Oliver Fromme + * Mon Apr 14 03:53:18 MET DST 1997 + */ + +#include +#include + +#include "mpg123.h" + +int outburst = MAXOUTBURST; +int preload; + +static int intflag = FALSE; +static int usr1flag = FALSE; + +static void catch_interrupt (void) +{ + intflag = TRUE; +} + +static void catch_usr1 (void) +{ + usr1flag = TRUE; +} + +/* Interfaces to writer process */ + +extern void buffer_sig(int signal, int block); + +void buffer_ignore_lowmem(void) +{ +#ifndef NOXFERMEM + if(buffermem->wakeme[XF_READER]) + xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP); +#endif +} + +void buffer_end(void) +{ +#ifndef NOXFERMEM + xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_TERMINATE); +#endif +} + +void buffer_resync(void) +{ + buffer_sig(SIGINT, TRUE); +} + +void buffer_reset(void) +{ + buffer_sig(SIGUSR1, TRUE); +} + +void buffer_start(void) +{ + buffer_sig(SIGCONT, FALSE); +} + +void buffer_stop(void) +{ + buffer_sig(SIGSTOP, FALSE); +} + +extern int buffer_pid; + +void buffer_sig(int signal, int block) +{ + +#ifndef NOXFERMEM + + kill(buffer_pid, signal); + + if (!buffermem || !block) + return; + + if(xfermem_block(XF_WRITER, buffermem) != XF_CMD_WAKEUP) + perror("Could not resync/reset buffers"); +#endif + + return; +} + +#ifndef NOXFERMEM + +void buffer_loop(struct audio_info_struct *ai, sigset_t *oldsigset) +{ + int bytes; + int my_fd = buffermem->fd[XF_READER]; + txfermem *xf = buffermem; + int done = FALSE; + + catchsignal (SIGINT, catch_interrupt); + catchsignal (SIGUSR1, catch_usr1); + sigprocmask (SIG_SETMASK, oldsigset, NULL); + if (param.outmode == DECODE_AUDIO) { + if (audio_open(ai) < 0) { + perror("audio"); + exit(1); + } + } + + for (;;) { + if (intflag) { + intflag = FALSE; + if (param.outmode == DECODE_AUDIO) + audio_queueflush (ai); + xf->readindex = xf->freeindex; + if (xf->wakeme[XF_WRITER]) + xfermem_putcmd(my_fd, XF_CMD_WAKEUP); + } + if (usr1flag) { + usr1flag = FALSE; + /* close and re-open in order to flush + * the device's internal buffer before + * changing the sample rate. [OF] + */ + /* writer must block when sending SIGUSR1 + * or we will lose all data processed + * in the meantime! [dk] + */ + xf->readindex = xf->freeindex; + /* We've nailed down the new starting location - + * writer is now safe to go on. [dk] + */ + if (xf->wakeme[XF_WRITER]) + xfermem_putcmd(my_fd, XF_CMD_WAKEUP); + if (param.outmode == DECODE_AUDIO) { + audio_close (ai); + ai->rate = xf->buf[0]; + ai->channels = xf->buf[1]; + ai->format = xf->buf[2]; + if (audio_open(ai) < 0) { + perror("audio"); + exit(1); + } + } + } + if ( (bytes = xfermem_get_usedspace(xf)) < outburst ) { + /* if we got a buffer underrun we first + * fill 1/8 of the buffer before continue/start + * playing */ + preload = xf->size>>3; + if(preload < outburst) + preload = outburst; + } + if(bytes < preload) { + int cmd; + if (done && !bytes) { + break; + } + + if(!done) { + + cmd = xfermem_block(XF_READER, xf); + + switch(cmd) { + + /* More input pending. */ + case XF_CMD_WAKEUP_INFO: + continue; + /* Yes, we know buffer is low but + * know we don't care. + */ + case XF_CMD_WAKEUP: + break; /* Proceed playing. */ + case XF_CMD_TERMINATE: + /* Proceed playing without + * blocking any further. + */ + done=TRUE; + break; + case -1: + if(errno==EINTR) + continue; + perror("Yuck! Error in buffer handling..."); + done = TRUE; + xf->readindex = xf->freeindex; + xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE); + break; + default: + fprintf(stderr, "\nEh!? Received unknown command 0x%x in buffer process. Tell Daniel!\n", cmd); + } + } + } + preload = outburst; /* set preload to lower mark */ + if (bytes > xf->size - xf->readindex) + bytes = xf->size - xf->readindex; + if (bytes > outburst) + bytes = outburst; + + if (param.outmode == DECODE_FILE) + bytes = write(OutputDescriptor, xf->data + xf->readindex, bytes); + else if (param.outmode == DECODE_AUDIO) + bytes = audio_play_samples(ai, + (unsigned char *) (xf->data + xf->readindex), bytes); + + if(bytes < 0) { + bytes = 0; + if(errno != EINTR) { + perror("Ouch ... error while writing audio data: "); + /* + * done==TRUE tells writer process to stop + * sending data. There might be some latency + * involved when resetting readindex to + * freeindex so we might need more than one + * cycle to terminate. (The number of cycles + * should be finite unless I managed to mess + * up something. ;-) [dk] + */ + done = TRUE; + xf->readindex = xf->freeindex; + xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE); + } + } + + xf->readindex = (xf->readindex + bytes) % xf->size; + if (xf->wakeme[XF_WRITER]) + xfermem_putcmd(my_fd, XF_CMD_WAKEUP); + } + + if (param.outmode == DECODE_AUDIO) + audio_close (ai); +} + +#endif + +/* EOF */ diff --git a/panda/src/mpg123/mpgbuffer.h b/panda/src/mpg123/mpgbuffer.h new file mode 100644 index 0000000000..c1c771d4de --- /dev/null +++ b/panda/src/mpg123/mpgbuffer.h @@ -0,0 +1,13 @@ +/* + * Application specific interaction between main and buffer + * process. This is much less generic than the functions in + * xfermem so I chose to put it in buffer.[hc]. + * 01/28/99 [dk] + */ + +void buffer_ignore_lowmem(void); +void buffer_end(void); +void buffer_resync(void); +void buffer_reset(void); +void buffer_start(void); +void buffer_stop(void); diff --git a/panda/src/mpg123/readers.c b/panda/src/mpg123/readers.c new file mode 100644 index 0000000000..90f6aa12e9 --- /dev/null +++ b/panda/src/mpg123/readers.c @@ -0,0 +1,499 @@ +#include + +#include +#include +#include + +#include "mpg123.h" +#include "buffer.h" +#include "common.h" + +#ifdef READ_MMAP +#include +#ifndef MAP_FAILED +#define MAP_FAILED ( (void *) -1 ) +#endif +#endif + +static int get_fileinfo(struct reader *,char *buf); + + +/******************************************************************* + * stream based operation + */ +static int fullread(int fd,unsigned char *buf,int count) +{ + int ret,cnt=0; + while(cnt < count) { + ret = read(fd,buf+cnt,count-cnt); + if(ret < 0) + return ret; + if(ret == 0) + break; + cnt += ret; + } + + return cnt; +} + +static int default_init(struct reader *rds) +{ + char buf[128]; + + rds->filepos = 0; + rds->filelen = get_fileinfo(rds,buf); + + if(rds->filelen > 0) { + if(!strncmp(buf,"TAG",3)) { + rds->flags |= READER_ID3TAG; + memcpy(rds->id3buf,buf,128); + } + } + return 0; +} + +void stream_close(struct reader *rds) +{ + if (rds->flags & READER_FD_OPENED) + close(rds->filept); +} + +/**************************************** + * HACK,HACK,HACK: step back frames + * can only work if the 'stream' isn't a real stream but a file + */ +static int stream_back_bytes(struct reader *rds,int bytes) +{ + if(lseek(rds->filept,-bytes,SEEK_CUR) < 0) + return -1; + if(param.usebuffer) + buffer_resync(); + return 0; +} + +static int stream_back_frame(struct reader *rds,struct frame *fr,int num) +{ + long bytes; + unsigned char buf[4]; + unsigned long newhead; + + if(!firsthead) + return 0; + + bytes = (fr->framesize+8)*(num+2); + + /* Skipping back/forth requires a bit more work in buffered mode. + * See mapped_back_frame(). + */ + if(param.usebuffer) + bytes += (long)(xfermem_get_usedspace(buffermem) / + (buffermem->buf[0] * buffermem->buf[1] + * (buffermem->buf[2] & AUDIO_FORMAT_MASK ? + 16.0 : 8.0 )) + * (tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index] << 10)); + /* + bytes += (long)(compute_buffer_offset(fr)*compute_bpf(fr)); + */ + if(lseek(rds->filept,-bytes,SEEK_CUR) < 0) + return -1; + + if(fullread(rds->filept,buf,4) != 4) + return -1; + + newhead = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3]; + + while( (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK) ) { + if(fullread(rds->filept,buf,1) != 1) + return -1; + newhead <<= 8; + newhead |= buf[0]; + newhead &= 0xffffffff; + } + + if( lseek(rds->filept,-4,SEEK_CUR) < 0) + return -1; + + read_frame(fr); + read_frame(fr); + + if(fr->lay == 3) { + set_pointer(512); + } + + if(param.usebuffer) + buffer_resync(); + + return 0; +} + +static int stream_head_read(struct reader *rds,unsigned long *newhead) +{ + unsigned char hbuf[4]; + + if(fullread(rds->filept,hbuf,4) != 4) + return FALSE; + + *newhead = ((unsigned long) hbuf[0] << 24) | + ((unsigned long) hbuf[1] << 16) | + ((unsigned long) hbuf[2] << 8) | + (unsigned long) hbuf[3]; + + return TRUE; +} + +static int stream_head_shift(struct reader *rds,unsigned long *head) +{ + unsigned char hbuf; + + if(fullread(rds->filept,&hbuf,1) != 1) + return 0; + *head <<= 8; + *head |= hbuf; + *head &= 0xffffffff; + return 1; +} + +static int stream_skip_bytes(struct reader *rds,int len) +{ + if (!param.usebuffer) + return lseek(rds->filept,len,SEEK_CUR); + + else { + + int ret = lseek(rds->filept,len,SEEK_CUR); + buffer_resync(); + return ret; + + } +} + +static int stream_read_frame_body(struct reader *rds,unsigned char *buf, + int size) +{ + long l; + + if( (l=fullread(rds->filept,buf,size)) != size) + { + if(l <= 0) + return 0; + memset(buf+l,0,size-l); + } + + return 1; +} + +static long stream_tell(struct reader *rds) +{ + return lseek(rds->filept,0,SEEK_CUR); +} + +static void stream_rewind(struct reader *rds) +{ + lseek(rds->filept,0,SEEK_SET); + if(param.usebuffer) + buffer_resync(); +} + +/* + * returns length of a file (if filept points to a file) + * reads the last 128 bytes information into buffer + */ +static int get_fileinfo(struct reader *rds,char *buf) +{ + int len; + + if((len=lseek(rds->filept,0,SEEK_END)) < 0) { + return -1; + } + if(lseek(rds->filept,-128,SEEK_END) < 0) + return -1; + if(fullread(rds->filept,(unsigned char *)buf,128) != 128) { + return -1; + } + if(!strncmp(buf,"TAG",3)) { + len -= 128; + } + if(lseek(rds->filept,0,SEEK_SET) < 0) + return -1; + if(len <= 0) + return -1; + return len; +} + + +#ifdef READ_MMAP +/*********************************************************+ + * memory mapped operation + * + */ +static unsigned char *mapbuf; +static unsigned char *mappnt; +static unsigned char *mapend; + +static int mapped_init(struct reader *rds) +{ + long len; + char buf[128]; + + len = get_fileinfo(rds,buf); + if(len < 0) + return -1; + + if(!strncmp(buf,"TAG",3)) { + rds->flags |= READER_ID3TAG; + memcpy(rds->id3buf,buf,128); + } + + mappnt = mapbuf = (unsigned char *) + mmap(NULL, len, PROT_READ, MAP_SHARED , rds->filept, 0); + if(!mapbuf || mapbuf == MAP_FAILED) + return -1; + + mapend = mapbuf + len; + + if(param.verbose > 1) + fprintf(stderr,"Using memory mapped IO for this stream.\n"); + + rds->filelen = len; + return 0; +} + +static void mapped_rewind(struct reader *rds) +{ + mappnt = mapbuf; + if (param.usebuffer) + buffer_resync(); +} + +static void mapped_close(struct reader *rds) +{ + munmap((void *)mapbuf,mapend-mapbuf); + if (rds->flags & READER_FD_OPENED) + close(rds->filept); +} + +static int mapped_head_read(struct reader *rds,unsigned long *newhead) +{ + unsigned long nh; + + if(mappnt + 4 > mapend) + return FALSE; + + nh = (*mappnt++) << 24; + nh |= (*mappnt++) << 16; + nh |= (*mappnt++) << 8; + nh |= (*mappnt++) ; + + *newhead = nh; + return TRUE; +} + +static int mapped_head_shift(struct reader *rds,unsigned long *head) +{ + if(mappnt + 1 > mapend) + return FALSE; + *head <<= 8; + *head |= *mappnt++; + *head &= 0xffffffff; + return TRUE; +} + +static int mapped_skip_bytes(struct reader *rds,int len) +{ + if(mappnt + len > mapend) + return FALSE; + mappnt += len; + if (param.usebuffer) + buffer_resync(); + return TRUE; +} + +static int mapped_read_frame_body(struct reader *rds,unsigned char *buf, + int size) +{ + if(size <= 0) { + fprintf(stderr,"Ouch. Read_frame called with size <= 0\n"); + return FALSE; + } + if(mappnt + size > mapend) + return FALSE; + memcpy(buf,mappnt,size); + mappnt += size; + + return TRUE; +} + +static int mapped_back_bytes(struct reader *rds,int bytes) +{ + if( (mappnt - bytes) < mapbuf || (mappnt - bytes + 4) > mapend) + return -1; + mappnt -= bytes; + if(param.usebuffer) + buffer_resync(); + return 0; +} + +static int mapped_back_frame(struct reader *rds,struct frame *fr,int num) +{ + long bytes; + unsigned long newhead; + + + if(!firsthead) + return 0; + + bytes = (fr->framesize+8)*(num+2); + + /* Buffered mode is a bit trickier. From the size of the buffered + * output audio stream we have to make a guess at the number of frames + * this corresponds to. + */ + if(param.usebuffer) + bytes += (long)(xfermem_get_usedspace(buffermem) / + (buffermem->buf[0] * buffermem->buf[1] + * (buffermem->buf[2] & AUDIO_FORMAT_MASK ? + 16.0 : 8.0 )) + * (tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index] << 10)); + /* + bytes += (long)(compute_buffer_offset(fr)*compute_bpf(fr)); + */ + + if( (mappnt - bytes) < mapbuf || (mappnt - bytes + 4) > mapend) + return -1; + mappnt -= bytes; + + newhead = (mappnt[0]<<24) + (mappnt[1]<<16) + (mappnt[2]<<8) + mappnt[3]; + mappnt += 4; + + while( (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK) ) { + if(mappnt + 1 > mapend) + return -1; + newhead <<= 8; + newhead |= *mappnt++; + newhead &= 0xffffffff; + } + mappnt -= 4; + + read_frame(fr); + read_frame(fr); + + if(fr->lay == 3) + set_pointer(512); + + if(param.usebuffer) + buffer_resync(); + + return 0; +} + +static long mapped_tell(struct reader *rds) +{ + return mappnt - mapbuf; +} + +#endif + +/***************************************************************** + * read frame helper + */ + +struct reader *rd; +struct reader readers[] = { +#ifdef READ_SYSTEM + { system_init, + NULL, /* filled in by system_init() */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL } , +#endif +#ifdef READ_MMAP + { mapped_init, + mapped_close, + mapped_head_read, + mapped_head_shift, + mapped_skip_bytes, + mapped_read_frame_body, + mapped_back_bytes, + mapped_back_frame, + mapped_tell, + mapped_rewind } , +#endif + { default_init, + stream_close, + stream_head_read, + stream_head_shift, + stream_skip_bytes, + stream_read_frame_body, + stream_back_bytes, + stream_back_frame, + stream_tell, + stream_rewind } , + { NULL, } +}; + + +/* open the device to read the bit stream from it */ + +struct reader *open_stream(char *bs_filenam,int fd) +{ + int i; + int filept_opened = 1; + int filept; + + if (!bs_filenam) { + if(fd < 0) { + filept = 0; + filept_opened = 0; + } + else + filept = fd; + } + else if (!strncmp(bs_filenam, "http://", 7)) + filept = http_open(bs_filenam); +#ifndef O_BINARY +#define O_BINARY (0) +#endif + else if ( (filept = open(bs_filenam, O_RDONLY|O_BINARY)) < 0) { + perror (bs_filenam); + return NULL; + } + + rd = NULL; + for(i=0;;i++) { + readers[i].filelen = -1; + readers[i].filept = filept; + readers[i].flags = 0; + if(filept_opened) + readers[i].flags |= READER_FD_OPENED; + if(!readers[i].init) { + fprintf(stderr,"Fatal error!\n"); + exit(1); + } + if(readers[i].init(readers+i) >= 0) { + rd = &readers[i]; + break; + } + } + + if(rd && rd->flags & READER_ID3TAG) { + print_id3_tag(rd->id3buf); + } + + return rd; +} + + + + + + + + + + + +