/* reader.c - compact version of famous library mpg123 Copyright (C) 2017 Uncle Mike This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "mpg123.h" #ifdef _WIN32 #include <io.h> #else #include <unistd.h> #endif #define READER_STREAM 0 #define READER_FEED 1 #define READER_BUF_STREAM 2 static int default_init( mpg123_handle_t *fr ); static mpg_off_t get_fileinfo( mpg123_handle_t *fr ); // methods for the buffer chain, mainly used for feed reader, but not just that. static buffy_t* buffy_new( size_t size, size_t minsize ) { buffy_t *newbuf = malloc( sizeof( buffy_t )); if( newbuf == NULL ) return NULL; newbuf->realsize = size > minsize ? size : minsize; newbuf->data = malloc( newbuf->realsize ); if( newbuf->data == NULL ) { free( newbuf ); return NULL; } newbuf->size = 0; newbuf->next = NULL; return newbuf; } static void buffy_del( buffy_t *buf ) { if( buf ) { free( buf->data ); free( buf ); } } // delete this buffy and all following buffies. static void buffy_del_chain( buffy_t *buf ) { while( buf ) { buffy_t *next = buf->next; buffy_del( buf ); buf = next; } } // fetch a buffer from the pool (if possible) or create one. static buffy_t* bc_alloc( bufferchain_t *bc, size_t size ) { // Easy route: Just try the first available buffer. // size does not matter, it's only a hint for creation of new buffers. if( bc->pool ) { buffy_t *buf = bc->pool; bc->pool = buf->next; buf->next = NULL; // that shall be set to a sensible value later. buf->size = 0; bc->pool_fill--; return buf; } return buffy_new( size, bc->bufblock ); } // either stuff the buffer back into the pool or free it for good. static void bc_free( bufferchain_t *bc, buffy_t* 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 ); } // make the buffer count in the pool match the pool size. static int bc_fill_pool( bufferchain_t *bc ) { // remove superfluous ones. while( bc->pool_fill > bc->pool_size ) { // lazyness: Just work on the front. buffy_t *buf = bc->pool; bc->pool = buf->next; buffy_del( buf ); bc->pool_fill--; } // add missing ones. while( bc->pool_fill < bc->pool_size ) { // again, just work on the front. buffy_t *buf = buffy_new( 0, bc->bufblock ); // use default block size. if( !buf ) return -1; buf->next = bc->pool; bc->pool = buf; bc->pool_fill++; } return 0; } static void bc_init( bufferchain_t *bc ) { bc->first = NULL; bc->last = bc->first; bc->size = 0; bc->pos = 0; bc->firstpos = 0; bc->fileoff = 0; } static void bc_reset( bufferchain_t *bc ) { // free current chain, possibly stuffing back into the pool. while( bc->first ) { buffy_t *buf = bc->first; bc->first = buf->next; bc_free( bc, buf ); } bc_fill_pool( bc ); // ignoring an error here... bc_init( bc ); } // create a new buffy at the end to be filled. static int bc_append( bufferchain_t *bc, mpg_ssize_t size ) { buffy_t *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; return 0; } void bc_prepare( bufferchain_t *bc, size_t pool_size, size_t bufblock ) { bc_poolsize( bc, pool_size, bufblock ); bc->pool = NULL; bc->pool_fill = 0; bc_init( bc ); // ensure that members are zeroed for read-only use. } size_t bc_fill( bufferchain_t *bc ) { return (size_t)(bc->size - bc->pos); } void bc_poolsize( bufferchain_t *bc, size_t pool_size, size_t bufblock ) { bc->pool_size = pool_size; bc->bufblock = bufblock; } void bc_cleanup( bufferchain_t *bc ) { buffy_del_chain( bc->pool ); bc->pool_fill = 0; bc->pool = NULL; } // append a new buffer and copy content to it. static int bc_add( bufferchain_t *bc, const byte *data, mpg_ssize_t size ) { int ret = 0; mpg_ssize_t part = 0; while( size > 0 ) { // try to fill up the last buffer block. if( bc->last != NULL && bc->last->size < bc->last->realsize ) { part = bc->last->realsize - bc->last->size; if( part > size ) part = size; memcpy( bc->last->data + bc->last->size, data, part ); bc->last->size += part; size -= part; bc->size += part; data += part; } // if there is still data left, put it into a new buffer block. if( size > 0 && ( ret = bc_append( bc, size )) != 0 ) break; } return ret; } // common handler for "You want more than I can give." situation. static mpg_ssize_t bc_need_more( bufferchain_t *bc ) { // go back to firstpos, undo the previous reads bc->pos = bc->firstpos; return MPG123_NEED_MORE; } // give some data, advancing position but not forgetting yet. static mpg_ssize_t bc_give( bufferchain_t *bc, byte *out, mpg_ssize_t size ) { buffy_t *b = bc->first; mpg_ssize_t gotcount = 0; mpg_ssize_t offset = 0; if( bc->size - bc->pos < size ) return bc_need_more( bc ); // find the current buffer while( b != NULL && ( offset + b->size ) <= bc->pos ) { offset += b->size; b = b->next; } // now start copying from there while( gotcount < size && ( b != NULL )) { mpg_ssize_t loff = bc->pos - offset; mpg_ssize_t chunk = size - gotcount; // amount of bytes to get from here... if( chunk > b->size - loff ) chunk = b->size - loff; memcpy( out + gotcount, b->data + loff, chunk ); gotcount += chunk; bc->pos += chunk; offset += b->size; b = b->next; } return gotcount; } // skip some bytes and return the new position. // the buffers are still there, just the read pointer is moved! static mpg_ssize_t bc_skip( bufferchain_t *bc, mpg_ssize_t count ) { if( count >= 0 ) { if( bc->size - bc->pos < count ) return bc_need_more( bc ); return bc->pos += count; } return MPG123_ERR; } static mpg_ssize_t bc_seekback( bufferchain_t *bc, mpg_ssize_t count ) { if( count >= 0 && count <= bc->pos ) return bc->pos -= count; return MPG123_ERR; } // throw away buffies that we passed. static void bc_forget( bufferchain_t *bc ) { buffy_t *b = bc->first; // free all buffers that are def'n'tly outdated // we have buffers until filepos... delete all buffers fully below it while( b != NULL && bc->pos >= b->size ) { buffy_t *n = b->next; // != NULL or this is indeed the end and the last cycle anyway if( n == NULL ) bc->last = NULL; // Going to delete the last buffy... bc->fileoff += b->size; bc->pos -= b->size; bc->size -= b->size; bc_free( bc, b ); b = n; } bc->first = b; bc->firstpos = bc->pos; } // reader for input via manually provided buffers static int feed_init( mpg123_handle_t *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; } // externally called function, returns 0 on success, -1 on error int feed_more( mpg123_handle_t *fr, const byte *in, long count ) { if( bc_add( &fr->rdat.buffer, in, count ) != 0 ) return MPG123_ERR; return MPG123_OK; } static mpg_ssize_t feed_read( mpg123_handle_t *fr, byte *out, mpg_ssize_t count ) { mpg_ssize_t gotcount = bc_give( &fr->rdat.buffer, out, count ); if( gotcount >= 0 && gotcount != count ) return MPG123_ERR; return gotcount; } // returns reached position... negative ones are bad... static mpg_off_t feed_skip_bytes( mpg123_handle_t *fr, mpg_off_t len ) { // this is either the new buffer offset or some negative error value. mpg_off_t res = bc_skip( &fr->rdat.buffer, (mpg_ssize_t)len ); if( res < 0 ) return res; return fr->rdat.buffer.fileoff + res; } static int feed_back_bytes( mpg123_handle_t *fr, mpg_off_t bytes ) { if( bytes >= 0 ) return bc_seekback(&fr->rdat.buffer, (mpg_ssize_t)bytes) >= 0 ? 0 : MPG123_ERR; return feed_skip_bytes( fr, -bytes ) >= 0 ? 0 : MPG123_ERR; } static int feed_seek_frame( mpg123_handle_t *fr, mpg_off_t num ) { return MPG123_ERR; } // not just for feed reader, also for self-feeding buffered reader. static void buffered_forget( mpg123_handle_t *fr ) { bc_forget( &fr->rdat.buffer ); fr->rdat.filepos = fr->rdat.buffer.fileoff + fr->rdat.buffer.pos; } mpg_off_t feed_set_pos( mpg123_handle_t *fr, mpg_off_t pos ) { bufferchain_t *bc = &fr->rdat.buffer; if( pos >= bc->fileoff && pos - bc->fileoff < bc->size ) { // we have the position! bc->pos = (mpg_ssize_t)(pos - bc->fileoff); // next input after end of buffer... return bc->fileoff + bc->size; } else { // i expect to get the specific position on next feed. Forget what I have now. bc_reset( bc ); bc->fileoff = pos; // next input from exactly that position. return pos; } } // the specific stuff for buffered stream reader. static mpg_ssize_t buffered_fullread( mpg123_handle_t *fr, byte *out, mpg_ssize_t count ) { bufferchain_t *bc = &fr->rdat.buffer; mpg_ssize_t gotcount; if( bc->size - bc->pos < count ) { // add more stuff to buffer. If hitting end of file, adjust count. byte readbuf[4096]; mpg_ssize_t need = count - (bc->size - bc->pos); while( need > 0 ) { mpg_ssize_t got = fr->rdat.fullread( fr, readbuf, sizeof( readbuf )); int ret; if( got < 0 ) return MPG123_ERR; if( got > 0 && ( ret = bc_add( bc, readbuf, got )) != 0 ) return MPG123_ERR; need -= got; // may underflow here... if( got < sizeof( readbuf )) // that naturally catches got == 0, too. break; // end. } if( bc->size - bc->pos < count ) count = bc->size - bc->pos; // we want only what we got. } gotcount = bc_give( bc, out, count ); if( gotcount != count ) return MPG123_ERR; return gotcount; } // stream based operation static mpg_ssize_t plain_fullread( mpg123_handle_t *fr, byte *buf, mpg_ssize_t count ) { mpg_ssize_t ret, cnt=0; // there used to be a check for expected file end here (length value or ID3 flag). // this is not needed: // 1. EOF is indicated by fdread returning zero bytes anyway. // 2. We get false positives of EOF for either files that grew or // 3. ... files that have ID3v1 tags in between (stream with intro). while( cnt < count ) { ret = fr->rdat.fdread( fr, buf + cnt, count - cnt ); if( ret < 0 ) return MPG123_ERR; if( ret == 0 ) break; if(!( fr->rdat.flags & READER_BUFFERED )) fr->rdat.filepos += ret; cnt += ret; } return cnt; } // wrappers for actual reading/seeking... I'm full of wrappers here. static mpg_off_t io_seek( reader_data_t *rdat, mpg_off_t offset, int whence ) { if( rdat->flags & READER_HANDLEIO ) { if( rdat->r_lseek_handle != NULL ) return rdat->r_lseek_handle( rdat->iohandle, offset, whence ); return -1; } return rdat->lseek( rdat->filept, offset, whence ); } static mpg_ssize_t io_read( reader_data_t *rdat, void *buf, size_t count ) { if( rdat->flags & READER_HANDLEIO ) { if( rdat->r_read_handle != NULL ) return rdat->r_read_handle( rdat->iohandle, buf, count ); return -1; } return rdat->read( rdat->filept, buf, count ); } // A normal read and a read with timeout. static mpg_ssize_t plain_read( mpg123_handle_t *fr, void *buf, size_t count ) { return io_read( &fr->rdat, buf, count ); } static mpg_off_t stream_lseek( mpg123_handle_t *fr, mpg_off_t pos, int whence ) { mpg_off_t ret; ret = io_seek( &fr->rdat, pos, whence ); if( ret >= 0 ) { fr->rdat.filepos = ret; } else { fr->err = MPG123_LSEEK_FAILED; ret = MPG123_ERR; } return ret; } static void stream_close( mpg123_handle_t *fr ) { if( fr->rdat.flags & READER_FD_OPENED ) close( fr->rdat.filept ); fr->rdat.filept = 0; if( fr->rdat.flags & READER_BUFFERED ) bc_reset( &fr->rdat.buffer ); 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_t *fr, mpg_off_t newframe ) { // seekable streams can go backwards and jump forwards. // non-seekable streams still can go forward, just not jump. if(( fr->rdat.flags & READER_SEEKABLE ) || ( newframe >= fr->num )) { mpg_off_t preframe; // a leading frame we jump to mpg_off_t seek_to; // the byte offset we want to reach mpg_off_t to_skip; // bytes to skip to get there (can be negative) // now seek to nearest leading index position and read from there until newframe is reached. // we use skip_bytes, which handles seekable and non-seekable streams // (the latter only for positive offset, which we ensured before entering here). seek_to = frame_index_find( fr, newframe, &preframe ); // no need to seek to index position if we are closer already. // but I am picky about fr->num == newframe, play safe by reading the frame again. // if you think that's stupid, don't call a seek to the current frame. 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 MPG123_ERR; fr->num = preframe - 1; // watch out! I am going to read preframe... fr->num should indicate the frame before! } while( fr->num < newframe ) { // try to be non-fatal now... frameNum only gets advanced on success anyway if( !read_frame( fr )) break; } // now the wanted frame should be ready for decoding. return MPG123_OK; } else { fr->err = MPG123_NO_SEEK; return MPG123_ERR; // invalid, no seek happened } } // return FALSE on error, TRUE on success, READER_MORE on occasion static int generic_head_read( mpg123_handle_t *fr, ulong *newhead ) { byte hbuf[4]; int ret = fr->rd->fullread( fr, hbuf, 4 ); if( ret == MPG123_NEED_MORE ) return ret; if( ret != 4 ) return FALSE; *newhead = ((ulong) hbuf[0] << 24) | ((ulong) hbuf[1] << 16) | ((ulong) hbuf[2] << 8) | (ulong) hbuf[3]; return TRUE; } // return FALSE on error, TRUE on success, READER_MORE on occasion static int generic_head_shift( mpg123_handle_t *fr, ulong *head ) { byte hbuf; int ret = fr->rd->fullread( fr, &hbuf, 1 ); if( ret == MPG123_NEED_MORE ) return ret; if( ret != 1 ) return FALSE; *head <<= 8; *head |= hbuf; *head &= 0xffffffff; return TRUE; } // returns reached position... negative ones are bad... static mpg_off_t stream_skip_bytes( mpg123_handle_t *fr, mpg_off_t len ) { if( fr->rdat.flags & READER_SEEKABLE ) { mpg_off_t ret = stream_lseek( fr, len, SEEK_CUR ); return (ret < 0) ? MPG123_ERR : ret; } else if( len >= 0 ) { byte buf[1024]; // ThOr: Compaq cxx complained and it makes sense to me... or should one do a cast? What for? mpg_ssize_t ret; while( len > 0 ) { mpg_ssize_t num = len < (mpg_off_t)sizeof( buf ) ? (mpg_ssize_t)len : (mpg_ssize_t)sizeof( buf ); ret = fr->rd->fullread( fr, buf, num ); if( ret < 0 ) return ret; else if( ret == 0 ) break; // EOF... an error? interface defined to tell the actual position... len -= ret; } return fr->rd->tell( fr ); } else if( fr->rdat.flags & READER_BUFFERED ) { // perhaps we _can_ go a bit back. if( fr->rdat.buffer.pos >= -len ) { fr->rdat.buffer.pos += len; return fr->rd->tell( fr ); } else { fr->err = MPG123_NO_SEEK; return MPG123_ERR; } } else { fr->err = MPG123_NO_SEEK; return MPG123_ERR; } } // return 0 on success... static int stream_back_bytes( mpg123_handle_t *fr, mpg_off_t bytes ) { mpg_off_t want = fr->rd->tell( fr ) - bytes; if( want < 0 ) return MPG123_ERR; if( stream_skip_bytes( fr, -bytes ) != want ) return MPG123_ERR; return 0; } // returns size on success... static int generic_read_frame_body( mpg123_handle_t *fr, byte *buf, int size ) { long l; if(( l = fr->rd->fullread( fr, buf, size )) != size ) return MPG123_ERR; return l; } static mpg_off_t generic_tell( mpg123_handle_t *fr ) { if( fr->rdat.flags & READER_BUFFERED ) fr->rdat.filepos = fr->rdat.buffer.fileoff + fr->rdat.buffer.pos; return fr->rdat.filepos; } // this does not (fully) work for non-seekable streams... You have to check for that flag, pal! static void stream_rewind( mpg123_handle_t *fr ) { if( fr->rdat.flags & READER_SEEKABLE ) { fr->rdat.filepos = stream_lseek( fr, 0, SEEK_SET ); fr->rdat.buffer.fileoff = fr->rdat.filepos; } if( fr->rdat.flags & READER_BUFFERED ) { fr->rdat.buffer.pos = 0; fr->rdat.buffer.firstpos = 0; fr->rdat.filepos = fr->rdat.buffer.fileoff; } } // returns length of a file (if filept points to a file) // reads the last 128 bytes information into buffer // ... that is not totally safe... static mpg_off_t get_fileinfo( mpg123_handle_t *fr ) { mpg_off_t len; if(( len = io_seek( &fr->rdat, 0, SEEK_END )) < 0 ) return -1; if( io_seek( &fr->rdat, -128, SEEK_END ) < 0 ) return -1; if( fr->rd->fullread( fr, (byte *)fr->id3buf, 128 ) != 128 ) return -1; if( !strncmp((char *)fr->id3buf, "TAG", 3 )) len -= 128; if( io_seek( &fr->rdat, 0, SEEK_SET ) < 0 ) return -1; if( len <= 0 ) return -1; return len; } static int bad_init( mpg123_handle_t *mh ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static mpg_ssize_t bad_fullread( mpg123_handle_t *mh, byte *data, mpg_ssize_t count ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static int bad_head_read( mpg123_handle_t *mh, ulong *newhead ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static int bad_head_shift( mpg123_handle_t *mh, ulong *head ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static mpg_off_t bad_skip_bytes( mpg123_handle_t *mh, mpg_off_t len ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static int bad_read_frame_body( mpg123_handle_t *mh, byte *data, int size ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static int bad_back_bytes( mpg123_handle_t *mh, mpg_off_t bytes ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static int bad_seek_frame( mpg123_handle_t *mh, mpg_off_t num ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static mpg_off_t bad_tell( mpg123_handle_t *mh ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } static void bad_rewind( mpg123_handle_t *mh ) { } static void bad_close( mpg123_handle_t *mh ) { } static reader_t 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 }; void open_bad( mpg123_handle_t *mh ) { mh->rd = &bad_reader; mh->rdat.flags = 0; bc_init( &mh->rdat.buffer ); mh->rdat.filelen = -1; } static reader_t readers[] = { { // READER_STREAM default_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 }, { // READER_FEED 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 }, { // READER_BUF_STREAM default_init, stream_close, buffered_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 } }; // final code common to open_stream and open_stream_handle. static int open_finish( mpg123_handle_t *fr ) { fr->rd = &readers[READER_STREAM]; if( fr->rd->init( fr ) < 0 ) return -1; return MPG123_OK; } int open_stream_handle( mpg123_handle_t *fr, void *iohandle ) { fr->rdat.filelen = -1; fr->rdat.filept = -1; fr->rdat.iohandle = iohandle; fr->rdat.flags = 0; fr->rdat.flags |= READER_HANDLEIO; return open_finish( fr ); } int open_feed( mpg123_handle_t *fr ) { fr->rd = &readers[READER_FEED]; fr->rdat.flags = 0; if( fr->rd->init( fr ) < 0 ) return -1; return 0; } static int default_init( mpg123_handle_t *fr ) { fr->rdat.fdread = plain_read; fr->rdat.read = fr->rdat.r_read != NULL ? fr->rdat.r_read : read; fr->rdat.lseek = fr->rdat.r_lseek != NULL ? fr->rdat.r_lseek : lseek; fr->rdat.filelen = get_fileinfo( fr ); fr->rdat.filepos = 0; // don't enable seeking on ICY streams, just plain normal files. // this check is necessary since the client can enforce ICY parsing on files that would otherwise be seekable. // it is a task for the future to make the ICY parsing safe with seeks ... or not. if( fr->rdat.filelen >= 0 ) { 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 ) { // switch reader to a buffered one, if allowed. if( fr->rd == &readers[READER_STREAM] ) { fr->rd = &readers[READER_BUF_STREAM]; fr->rdat.fullread = plain_fullread; } else { return -1; } bc_init( &fr->rdat.buffer ); fr->rdat.filelen = 0; // we carry the offset, but never know how big the stream is. fr->rdat.flags |= READER_BUFFERED; } return 0; }