#include "mpg123lib_intern.h"
#include "../common/debug.h"
static int stream_init(mpg123_handle *fr);
static int64_t get_fileinfo(mpg123_handle *);
static ptrdiff_t plain_fullread(mpg123_handle *fr,unsigned char *buf, ptrdiff_t count);
#ifndef NO_FEEDER
static void bc_init(struct bufferchain *bc);
static void bc_reset(struct bufferchain *bc);
static int bc_append(struct bufferchain *bc, ptrdiff_t size);
#if 0
static void bc_drop(struct bufferchain *bc);
#endif
static int bc_add(struct bufferchain *bc, const unsigned char *data, ptrdiff_t size);
static ptrdiff_t bc_give(struct bufferchain *bc, unsigned char *out, ptrdiff_t size);
static ptrdiff_t bc_skip(struct bufferchain *bc, ptrdiff_t count);
static ptrdiff_t bc_seekback(struct bufferchain *bc, ptrdiff_t count);
static void bc_forget(struct bufferchain *bc);
#endif
static ptrdiff_t fdread(mpg123_handle *fr, void *buf, size_t count)
{
if((fr->rdat.flags & READER_HANDLEIO) && fr->rdat.r_read64)
{
size_t got = 0;
int ret = fr->rdat.r_read64(fr->rdat.iohandle, buf, count, &got);
if(ret<0)
{
if(NOQUIET) merror("error reading %zu bytes", count);
return -1;
}
if(VERBOSE3) mdebug("read %zu bytes of %zu", got, count);
return (ptrdiff_t)(got > PTRDIFF_MAX ? PTRDIFF_MAX : got);
}
if(NOQUIET) error("no reader setup");
return -1;
}
static int64_t fdseek(mpg123_handle *fr, int64_t offset, int whence)
{
if((fr->rdat.flags & READER_HANDLEIO) && fr->rdat.r_lseek64)
return (fr->rdat.flags & READER_NOSEEK)
? -1
: fr->rdat.r_lseek64(fr->rdat.iohandle, offset, whence);
if(NOQUIET) error("no reader setup");
return -1;
}
#ifndef NO_ICY
static ptrdiff_t icy_fullread(mpg123_handle *fr, unsigned char *buf, ptrdiff_t count)
{
ptrdiff_t ret,cnt;
cnt = 0;
if(fr->rdat.flags & READER_SEEKABLE)
{
if(NOQUIET) error("mpg123 programmer error: I don't do ICY on seekable streams.");
return -1;
}
while(cnt < count)
{
if(fr->icy.next < count-cnt)
{
unsigned char temp_buff;
size_t meta_size;
ptrdiff_t cut_pos;
if(fr->icy.next > 0)
{
cut_pos = fr->icy.next;
ret = fdread(fr,buf+cnt,cut_pos);
if(ret < 1)
{
if(ret == 0) break;
if(NOQUIET) error("icy boundary read");
return READER_ERROR;
}
if(!(fr->rdat.flags & READER_BUFFERED))
SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX);
cnt += ret;
fr->icy.next -= ret;
if(fr->icy.next > 0)
{
debug1("another try... still %li left", (long)fr->icy.next);
continue;
}
}
ret = fdread(fr,&temp_buff,1);
if(ret < 0){ if(NOQUIET) error("reading icy size"); return READER_ERROR; }
if(ret == 0) break;
debug2("got meta-size byte: %u, at filepos %li", temp_buff, (long)fr->rdat.filepos );
if(!(fr->rdat.flags & READER_BUFFERED))
SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX);
if((meta_size = ((size_t) temp_buff) * 16))
{
char *meta_buff;
meta_buff = malloc(meta_size+1);
if(meta_buff != NULL)
{
ptrdiff_t left = meta_size;
while(left > 0)
{
ret = fdread(fr,meta_buff+meta_size-left,left);
if(ret < 1){ if(NOQUIET) error("reading icy-meta"); return READER_ERROR; }
left -= ret;
}
meta_buff[meta_size] = 0;
if(!(fr->rdat.flags & READER_BUFFERED))
SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX);
if(fr->icy.data) free(fr->icy.data);
fr->icy.data = meta_buff;
fr->metaflags |= MPG123_NEW_ICY;
debug2("icy-meta: %s size: %d bytes", fr->icy.data, (int)meta_size);
}
else
{
if(NOQUIET) error1("cannot allocate memory for meta_buff (%lu bytes) ... trying to skip the metadata!", (unsigned long)meta_size);
fr->rd->skip_bytes(fr, meta_size);
}
}
fr->icy.next = fr->icy.interval;
}
else
{
ret = plain_fullread(fr, buf+cnt, count-cnt);
if(ret < 0){ if(NOQUIET) error1("reading the rest of %li", (long)(count-cnt)); return READER_ERROR; }
if(ret == 0) break;
cnt += ret;
fr->icy.next -= ret;
}
}
return cnt;
}
#else
#define icy_fullread NULL
#endif
static ptrdiff_t plain_fullread(mpg123_handle *fr,unsigned char *buf, ptrdiff_t count)
{
ptrdiff_t ret,cnt=0;
#ifdef EXTRA_DEBUG
mdebug("plain fullread of %td", count);
#endif
while(cnt < count)
{
ret = fdread(fr,buf+cnt,count-cnt);
if(ret < 0) return READER_ERROR;
if(ret == 0) break;
if(!(fr->rdat.flags & READER_BUFFERED))
SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX);
cnt += ret;
}
return cnt;
}
static int64_t stream_lseek(mpg123_handle *fr, int64_t pos, int whence)
{
int64_t ret = fdseek(fr, pos, whence);
if (ret >= 0)
fr->rdat.filepos = ret;
else
{
fr->err = MPG123_LSEEK_FAILED;
ret = READER_ERROR;
}
return ret;
}
static void stream_close(mpg123_handle *fr)
{
#ifndef NO_FEEDER
if(fr->rdat.flags & READER_BUFFERED) bc_reset(&fr->rdat.buffer);
#endif
if(fr->rdat.flags & READER_HANDLEIO)
{
if(fr->rdat.cleanup_handle != NULL)
fr->rdat.cleanup_handle(fr->rdat.iohandle);
fr->rdat.iohandle = NULL;
}
}
static int stream_seek_frame(mpg123_handle *fr, int64_t newframe)
{
debug2("seek_frame to %"PRIi64" (from %"PRIi64")", newframe, fr->num);
if((fr->rdat.flags & READER_SEEKABLE) || (newframe >= fr->num))
{
int64_t preframe;
int64_t seek_to;
int64_t to_skip;
seek_to = INT123_frame_index_find(fr, newframe, &preframe);
if(fr->num >= newframe || fr->num < preframe)
{
to_skip = seek_to - fr->rd->tell(fr);
if(fr->rd->skip_bytes(fr, to_skip) != seek_to)
return READER_ERROR;
debug2("going to %lu; just got %lu", (long unsigned)newframe, (long unsigned)preframe);
fr->num = preframe-1;
}
while(fr->num < newframe)
{
if(!INT123_read_frame(fr)) break;
}
debug1("arrived at %"PRIi64, fr->num);
return MPG123_OK;
}
else
{
fr->err = MPG123_NO_SEEK;
return READER_ERROR;
}
}
static int generic_head_read(mpg123_handle *fr,unsigned long *newhead)
{
unsigned char hbuf[4];
int ret = fr->rd->fullread(fr,hbuf,4);
if(ret == READER_MORE) return ret;
if(ret != 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 generic_head_shift(mpg123_handle *fr,unsigned long *head)
{
unsigned char hbuf;
int ret = fr->rd->fullread(fr,&hbuf,1);
if(ret == READER_MORE) return ret;
if(ret != 1) return FALSE;
*head <<= 8;
*head |= hbuf;
*head &= 0xffffffff;
return TRUE;
}
static int64_t stream_skip_bytes(mpg123_handle *fr, int64_t len)
{
if(fr->rdat.flags & READER_SEEKABLE)
{
int64_t ret = stream_lseek(fr, len, SEEK_CUR);
return (ret < 0) ? READER_ERROR : ret;
}
else if(len >= 0)
{
unsigned char buf[1024];
ptrdiff_t ret;
while (len > 0)
{
ptrdiff_t num = len < (ptrdiff_t)sizeof(buf) ? (ptrdiff_t)len : (ptrdiff_t)sizeof(buf);
ret = fr->rd->fullread(fr, buf, num);
if (ret < 0) return ret;
else if(ret == 0) break;
len -= ret;
}
return fr->rd->tell(fr);
}
#ifndef NO_FEEDER
else if(fr->rdat.flags & READER_BUFFERED)
{
if(fr->rdat.buffer.pos >= -len)
{
fr->rdat.buffer.pos += len;
return fr->rd->tell(fr);
}
else
{
fr->err = MPG123_NO_SEEK;
return READER_ERROR;
}
}
#endif
else
{
fr->err = MPG123_NO_SEEK;
return READER_ERROR;
}
}
static int stream_back_bytes(mpg123_handle *fr, int64_t bytes)
{
int64_t want = fr->rd->tell(fr)-bytes;
if(want < 0) return READER_ERROR;
if(stream_skip_bytes(fr,-bytes) != want) return READER_ERROR;
return 0;
}
static int generic_read_frame_body(mpg123_handle *fr,unsigned char *buf, int size)
{
ptrdiff_t l;
l=fr->rd->fullread(fr,buf,size);
return (l >= 0 && l<size) ? READER_ERROR : (int)l;
}
static int64_t generic_tell(mpg123_handle *fr)
{
#ifndef NO_FEEDER
if(fr->rdat.flags & READER_BUFFERED)
{
fr->rdat.filepos = fr->rdat.buffer.fileoff;
SATURATE_ADD(fr->rdat.filepos, fr->rdat.buffer.pos, INT64_MAX);
}
#endif
return fr->rdat.filepos;
}
static void stream_rewind(mpg123_handle *fr)
{
if(fr->rdat.flags & READER_SEEKABLE)
{
fr->rdat.filepos = stream_lseek(fr,0,SEEK_SET);
#ifndef NO_FEEDER
fr->rdat.buffer.fileoff = fr->rdat.filepos;
#endif
}
#ifndef NO_FEEDER
if(fr->rdat.flags & READER_BUFFERED)
{
fr->rdat.buffer.pos = 0;
fr->rdat.buffer.firstpos = 0;
fr->rdat.filepos = fr->rdat.buffer.fileoff;
}
#endif
}
static int64_t get_fileinfo(mpg123_handle *fr)
{
int64_t len;
if((len=fdseek(fr,0,SEEK_END)) < 0)
{
debug("cannot seek to end");
return -1;
} else if(len >= 128)
{
if(fdseek(fr,-128,SEEK_END) < 0)
{
debug("cannot seek to END-128");
return -1;
}
if(fr->rd->fullread(fr,(unsigned char *)fr->id3buf,128) != 128)
{
debug("cannot read ID3v1?!");
return -1;
}
if(!strncmp((char*)fr->id3buf,"TAG",3)) len -= 128;
} else
{
debug("stream too short for ID3");
}
if(fdseek(fr,0,SEEK_SET) < 0)
{
debug("cannot seek back");
return -1;
}
fr->rdat.filepos = 0;
debug1("returning length: %"PRIi64, len);
return len;
}
#ifndef NO_FEEDER
static struct buffy* buffy_new(ptrdiff_t size, ptrdiff_t minsize)
{
struct buffy *newbuf;
if(size > PTRDIFF_MAX)
return NULL;
newbuf = malloc(sizeof(struct buffy));
if(newbuf == NULL) return NULL;
newbuf->realsize = size > minsize ? size : minsize;
newbuf->data = malloc((size_t)newbuf->realsize);
if(newbuf->data == NULL)
{
free(newbuf);
return NULL;
}
newbuf->size = 0;
newbuf->next = NULL;
return newbuf;
}
static void buffy_del(struct buffy* buf)
{
if(buf)
{
free(buf->data);
free(buf);
}
}
static void buffy_del_chain(struct buffy* buf)
{
while(buf)
{
struct buffy* next = buf->next;
buffy_del(buf);
buf = next;
}
}
void INT123_bc_prepare(struct bufferchain *bc, size_t pool_size, size_t bufblock)
{
INT123_bc_poolsize(bc, pool_size, bufblock);
bc->pool = NULL;
bc->pool_fill = 0;
bc_init(bc);
}
size_t INT123_bc_fill(struct bufferchain *bc)
{
return (size_t)(bc->size - bc->pos);
}
void INT123_bc_poolsize(struct bufferchain *bc, size_t pool_size, size_t bufblock)
{
bc->pool_size = pool_size;
bc->bufblock = bufblock;
}
void INT123_bc_cleanup(struct bufferchain *bc)
{
buffy_del_chain(bc->pool);
bc->pool = NULL;
bc->pool_fill = 0;
}
static struct buffy* bc_alloc(struct bufferchain *bc, size_t size)
{
if(bc->pool)
{
struct buffy *buf = bc->pool;
bc->pool = buf->next;
buf->next = NULL;
buf->size = 0;
--bc->pool_fill;
debug2("bc_alloc: picked %p from pool (fill now %zu)", (void*)buf, bc->pool_fill);
return buf;
}
else return buffy_new(size, bc->bufblock);
}
static void bc_free(struct bufferchain *bc, struct buffy* buf)
{
if(!buf) return;
if(bc->pool_fill < bc->pool_size)
{
buf->next = bc->pool;
bc->pool = buf;
++bc->pool_fill;
}
else buffy_del(buf);
}
static int bc_fill_pool(struct bufferchain *bc)
{
while(bc->pool_fill > bc->pool_size)
{
struct buffy* buf = bc->pool;
bc->pool = buf->next;
buffy_del(buf);
--bc->pool_fill;
}
while(bc->pool_fill < bc->pool_size)
{
struct buffy* buf;
buf = buffy_new(0, bc->bufblock);
if(!buf) return -1;
buf->next = bc->pool;
bc->pool = buf;
++bc->pool_fill;
}
return 0;
}
static void bc_init(struct bufferchain *bc)
{
bc->first = NULL;
bc->last = bc->first;
bc->size = 0;
bc->pos = 0;
bc->firstpos = 0;
bc->fileoff = 0;
}
static void bc_reset(struct bufferchain *bc)
{
while(bc->first)
{
struct buffy* buf = bc->first;
bc->first = buf->next;
bc_free(bc, buf);
}
bc_fill_pool(bc);
bc_init(bc);
}
static int bc_append(struct bufferchain *bc, ptrdiff_t size)
{
struct buffy *newbuf;
if(size < 1) return -1;
newbuf = bc_alloc(bc, size);
if(newbuf == NULL) return -2;
if(bc->last != NULL) bc->last->next = newbuf;
else if(bc->first == NULL) bc->first = newbuf;
bc->last = newbuf;
debug3("bc_append: new last buffer %p with %td B (really %td)", (void*)bc->last, bc->last->size, bc->last->realsize);
return 0;
}
static int bc_add(struct bufferchain *bc, const unsigned char *data, ptrdiff_t size)
{
int ret = 0;
ptrdiff_t part = 0;
if((size_t)(PTRDIFF_MAX - bc->size) < size)
return -1;
debug2("bc_add: adding %zu bytes at %"PRIi64, size, (int64_t)(bc->fileoff+bc->size));
if(size >=4) debug4("first bytes: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]);
while(size > 0)
{
if(bc->last != NULL && bc->last->size < bc->last->realsize)
{
part = bc->last->realsize - bc->last->size;
if(part > size) part = size;
debug2("bc_add: adding %td B to existing block %p", part, (void*)bc->last);
memcpy(bc->last->data+bc->last->size, data, part);
bc->last->size += part;
size -= part;
bc->size += part;
data += part;
}
if(size > 0 && (ret = bc_append(bc, size)) != 0)
break;
}
return ret;
}
static ptrdiff_t bc_need_more(struct bufferchain *bc, ptrdiff_t size)
{
debug3("hit end, back to beginning (%td - %td < %td)", bc->size, bc->pos, size);
bc->pos = bc->firstpos;
return READER_MORE;
}
static ptrdiff_t bc_give(struct bufferchain *bc, unsigned char *out, ptrdiff_t size)
{
struct buffy *b = bc->first;
ptrdiff_t gotcount = 0;
ptrdiff_t offset = 0;
if(bc->size - bc->pos < size) return bc_need_more(bc, size);
while(b != NULL && (offset + b->size) <= bc->pos)
{
offset += b->size;
b = b->next;
}
while(gotcount < size && (b != NULL))
{
ptrdiff_t loff = bc->pos - offset;
ptrdiff_t chunk = size - gotcount;
if(chunk > b->size - loff) chunk = b->size - loff;
#ifdef EXTRA_DEBUG
debug3("copying %liB from %p+%li",(long)chunk, b->data, (long)loff);
#endif
memcpy(out+gotcount, b->data+loff, chunk);
gotcount += chunk;
bc->pos += chunk;
offset += b->size;
b = b->next;
}
#ifdef EXTRA_DEBUG
debug2("got %li bytes, pos advanced to %li", (long)gotcount, (long)bc->pos);
#endif
return gotcount;
}
static ptrdiff_t bc_skip(struct bufferchain *bc, ptrdiff_t count)
{
if(count >= 0)
{
if(bc->size - bc->pos < count) return bc_need_more(bc, count);
else return bc->pos += count;
}
else return READER_ERROR;
}
static ptrdiff_t bc_seekback(struct bufferchain *bc, ptrdiff_t count)
{
if(count >= 0 && count <= bc->pos) return bc->pos -= count;
else return READER_ERROR;
}
static void bc_forget(struct bufferchain *bc)
{
struct buffy *b = bc->first;
if(b) debug2("bc_forget: block %td pos %td", b->size, bc->pos);
else debug("forget with nothing there!");
while(b != NULL && bc->pos >= b->size)
{
struct buffy *n = b->next;
if(n == NULL) bc->last = NULL;
bc->fileoff += b->size;
bc->pos -= b->size;
bc->size -= b->size;
debug5("bc_forget: forgot %p with %td, pos=%td, size=%td, fileoff=%"PRIi64
, (void*)b->data, b->size, bc->pos, bc->size, bc->fileoff);
bc_free(bc, b);
b = n;
}
bc->first = b;
bc->firstpos = bc->pos;
}
static int feed_init(mpg123_handle *fr)
{
bc_init(&fr->rdat.buffer);
bc_fill_pool(&fr->rdat.buffer);
fr->rdat.filelen = 0;
fr->rdat.filepos = 0;
fr->rdat.flags |= READER_BUFFERED;
return 0;
}
int INT123_feed_more(mpg123_handle *fr, const unsigned char *in, size_t count)
{
int ret = 0;
if(VERBOSE3) debug("INT123_feed_more");
if(count > PTRDIFF_MAX)
return READER_ERROR;
if((ret = bc_add(&fr->rdat.buffer, in, (ptrdiff_t)count)) != 0)
{
ret = READER_ERROR;
if(NOQUIET) error1("Failed to add buffer, return: %i", ret);
}
else
if(VERBOSE3) debug3("INT123_feed_more: %p %luB bufsize=%lu", fr->rdat.buffer.last->data,
(unsigned long)fr->rdat.buffer.last->size, (unsigned long)fr->rdat.buffer.size);
return ret;
}
static ptrdiff_t feed_read(mpg123_handle *fr, unsigned char *out, ptrdiff_t count)
{
ptrdiff_t gotcount = bc_give(&fr->rdat.buffer, out, count);
if(gotcount >= 0 && gotcount != count) return READER_ERROR;
else return gotcount;
}
static int64_t feed_skip_bytes(mpg123_handle *fr, int64_t len)
{
int64_t res = bc_skip(&fr->rdat.buffer, (ptrdiff_t)len);
if(res < 0) return res;
return fr->rdat.buffer.fileoff+res;
}
static int feed_back_bytes(mpg123_handle *fr, int64_t bytes)
{
if(bytes >=0)
return bc_seekback(&fr->rdat.buffer, (ptrdiff_t)bytes) >= 0 ? 0 : READER_ERROR;
else
return feed_skip_bytes(fr, -bytes) >= 0 ? 0 : READER_ERROR;
}
static int feed_seek_frame(mpg123_handle *fr, int64_t num){ return READER_ERROR; }
static void buffered_forget(mpg123_handle *fr)
{
bc_forget(&fr->rdat.buffer);
fr->rdat.filepos = fr->rdat.buffer.fileoff;
SATURATE_ADD(fr->rdat.filepos, fr->rdat.buffer.pos, INT64_MAX);
}
int64_t INT123_feed_set_pos(mpg123_handle *fr, int64_t pos)
{
struct bufferchain *bc = &fr->rdat.buffer;
if(pos >= bc->fileoff && pos-bc->fileoff < bc->size)
{
bc->pos = (ptrdiff_t)(pos - bc->fileoff);
debug1("INT123_feed_set_pos inside, next feed from %"PRIi64, (int64_t)(bc->fileoff+bc->size));
return bc->fileoff+bc->size;
}
else
{
bc_reset(bc);
bc->fileoff = pos;
debug1("INT123_feed_set_pos outside, buffer reset, next feed from %"PRIi64, pos);
return pos;
}
}
static ptrdiff_t buffered_fullread(mpg123_handle *fr, unsigned char *out, ptrdiff_t count
, ptrdiff_t (*fullread)(mpg123_handle *, unsigned char *, ptrdiff_t))
{
struct bufferchain *bc = &fr->rdat.buffer;
ptrdiff_t gotcount;
if(VERBOSE3)
mdebug("buffered_fullread: want %zd", count);
if(bc->size - bc->pos < count)
{
unsigned char readbuf[4096];
ptrdiff_t need = count - (bc->size-bc->pos);
while(need>0)
{
int ret;
ptrdiff_t got = fullread(fr, readbuf, sizeof(readbuf));
if(got < 0)
{
if(NOQUIET) error("buffer reading");
return READER_ERROR;
}
if(VERBOSE3)
debug1("buffered_fullread: buffering %td bytes from stream (if > 0)", got);
if(got > 0 && (ret=bc_add(bc, readbuf, got)) != 0)
{
if(NOQUIET) error1("unable to add to chain, return: %i", ret);
return READER_ERROR;
}
need -= got;
if(got < sizeof(readbuf))
{
if(VERBOSE3) fprintf(stderr, "Note: Input data end.\n");
break;
}
}
if(bc->size - bc->pos < count)
count = bc->size - bc->pos;
}
gotcount = bc_give(bc, out, count);
if(VERBOSE3)
mdebug("buffered_fullread: got %td", gotcount);
if(gotcount != count){ if(NOQUIET) error("gotcount != count"); return READER_ERROR; }
else return gotcount;
}
static ptrdiff_t buffered_plain_fullread(mpg123_handle *fr, unsigned char *out, ptrdiff_t count)
{
return buffered_fullread(fr, out, count, plain_fullread);
}
static ptrdiff_t buffered_icy_fullread(mpg123_handle *fr, unsigned char *out, ptrdiff_t count)
{
return buffered_fullread(fr, out, count, icy_fullread);
}
#else
int INT123_feed_more(mpg123_handle *fr, const unsigned char *in, size_t count)
{
fr->err = MPG123_MISSING_FEATURE;
return -1;
}
int64_t INT123_feed_set_pos(mpg123_handle *fr, int64_t pos)
{
fr->err = MPG123_MISSING_FEATURE;
return -1;
}
#endif
#define bugger_off { mh->err = MPG123_NO_READER; return MPG123_ERR; }
static int bad_init(mpg123_handle *mh) bugger_off
static void bad_close(mpg123_handle *mh){}
static ptrdiff_t bad_fullread(mpg123_handle *mh, unsigned char *data, ptrdiff_t count) bugger_off
static int bad_head_read(mpg123_handle *mh, unsigned long *newhead) bugger_off
static int bad_head_shift(mpg123_handle *mh, unsigned long *head) bugger_off
static int64_t bad_skip_bytes(mpg123_handle *mh, int64_t len) bugger_off
static int bad_read_frame_body(mpg123_handle *mh, unsigned char *data, int size) bugger_off
static int bad_back_bytes(mpg123_handle *mh, int64_t bytes) bugger_off
static int bad_seek_frame(mpg123_handle *mh, int64_t num) bugger_off
static int64_t bad_tell(mpg123_handle *mh) bugger_off
static void bad_rewind(mpg123_handle *mh){}
#undef bugger_off
#define READER_STREAM 0
#define READER_ICY_STREAM 1
#define READER_FEED 2
#define READER_BUF_STREAM 3
#define READER_BUF_ICY_STREAM 4
static struct reader readers[] =
{
{
stream_init,
stream_close,
plain_fullread,
generic_head_read,
generic_head_shift,
stream_skip_bytes,
generic_read_frame_body,
stream_back_bytes,
stream_seek_frame,
generic_tell,
stream_rewind,
NULL
} ,
{
stream_init,
stream_close,
icy_fullread,
generic_head_read,
generic_head_shift,
stream_skip_bytes,
generic_read_frame_body,
stream_back_bytes,
stream_seek_frame,
generic_tell,
stream_rewind,
NULL
},
#ifdef NO_FEEDER
#define feed_init NULL
#define feed_read NULL
#define buffered_fullread NULL
#define buffered_plain_fullread NULL
#define buffered_icy_fullread NULL
#define feed_seek_frame NULL
#define feed_back_bytes NULL
#define feed_skip_bytes NULL
#define buffered_forget NULL
#endif
{
feed_init,
stream_close,
feed_read,
generic_head_read,
generic_head_shift,
feed_skip_bytes,
generic_read_frame_body,
feed_back_bytes,
feed_seek_frame,
generic_tell,
stream_rewind,
buffered_forget
},
{
stream_init,
stream_close,
buffered_plain_fullread,
generic_head_read,
generic_head_shift,
stream_skip_bytes,
generic_read_frame_body,
stream_back_bytes,
stream_seek_frame,
generic_tell,
stream_rewind,
buffered_forget
} ,
{
stream_init,
stream_close,
buffered_icy_fullread,
generic_head_read,
generic_head_shift,
stream_skip_bytes,
generic_read_frame_body,
stream_back_bytes,
stream_seek_frame,
generic_tell,
stream_rewind,
buffered_forget
},
};
static struct reader bad_reader =
{
bad_init,
bad_close,
bad_fullread,
bad_head_read,
bad_head_shift,
bad_skip_bytes,
bad_read_frame_body,
bad_back_bytes,
bad_seek_frame,
bad_tell,
bad_rewind,
NULL
};
static int stream_init(mpg123_handle *fr)
{
#ifndef NO_ICY
if(fr->p.icy_interval > 0) fr->rdat.flags |= READER_NOSEEK;
#endif
fr->rdat.filepos = 0;
fr->rdat.filelen = fr->p.flags & MPG123_NO_PEEK_END ? -1 : get_fileinfo(fr);
if(fr->p.flags & MPG123_FORCE_SEEKABLE)
{
fr->rdat.flags |= READER_SEEKABLE;
fr->rdat.flags &= ~READER_NOSEEK;
}
if(fr->rdat.filelen >= 0)
{
debug("seekable stream");
fr->rdat.flags |= READER_SEEKABLE;
if(!strncmp((char*)fr->id3buf,"TAG",3))
{
fr->rdat.flags |= READER_ID3TAG;
fr->metaflags |= MPG123_NEW_ID3;
}
}
else if(fr->p.flags & MPG123_SEEKBUFFER)
{
#ifdef NO_FEEDER
if(NOQUIET)
error("Buffered readers not supported in this build.");
fr->err = MPG123_MISSING_FEATURE;
return -1;
#else
if (fr->rd == &readers[READER_STREAM])
{
debug("switching to buffered stream reader");
fr->rd = &readers[READER_BUF_STREAM];
}
#ifndef NO_ICY
else if(fr->rd == &readers[READER_ICY_STREAM])
{
debug("switching to buffered ICY stream reader");
fr->rd = &readers[READER_BUF_ICY_STREAM];
}
#endif
else
{
if(NOQUIET) error("mpg123 Programmer's fault: invalid reader");
return -1;
}
bc_init(&fr->rdat.buffer);
fr->rdat.filelen = 0;
fr->rdat.flags |= READER_BUFFERED;
#endif
}
return 0;
}
void INT123_open_bad(mpg123_handle *mh)
{
debug("INT123_open_bad");
#ifndef NO_ICY
INT123_clear_icy(&mh->icy);
#endif
mh->rd = &bad_reader;
mh->rdat.flags = 0;
#ifndef NO_FEEDER
bc_init(&mh->rdat.buffer);
#endif
mh->rdat.filelen = -1;
}
int INT123_open_feed(mpg123_handle *fr)
{
debug("feed reader");
#ifdef NO_FEEDER
if(NOQUIET)
error("Buffered readers not supported in this build.");
fr->err = MPG123_MISSING_FEATURE;
return -1;
#else
#ifndef NO_ICY
if(fr->p.icy_interval > 0)
{
if(NOQUIET) error("Feed reader cannot do ICY parsing!");
return -1;
}
INT123_clear_icy(&fr->icy);
#endif
fr->rd = &readers[READER_FEED];
fr->rdat.flags = 0;
if(fr->rd->init(fr) < 0) return -1;
debug("feed reader init successful");
return 0;
#endif
}
int INT123_open_stream_handle(mpg123_handle *fr, void *iohandle)
{
INT123_clear_icy(&fr->icy);
fr->rdat.filelen = -1;
fr->rdat.iohandle = iohandle;
fr->rdat.flags = 0;
fr->rdat.flags |= READER_HANDLEIO;
#ifndef NO_ICY
if(fr->p.icy_interval > 0)
{
debug("ICY reader");
fr->icy.interval = fr->p.icy_interval;
fr->icy.next = fr->icy.interval;
fr->rd = &readers[READER_ICY_STREAM];
}
else
#endif
{
fr->rd = &readers[READER_STREAM];
debug("stream reader");
}
if(fr->rd->init(fr) < 0) return -1;
return MPG123_OK;
}