From 286016f8ea7413959fbf1b902a60dba4db193183 Mon Sep 17 00:00:00 2001 From: Nicolas Pennequin Date: Thu, 16 Aug 2007 15:54:14 +0200 Subject: [PATCH] First commit of buffering.[ch], based on testplugin.c These are based on testplugin.c, with the "rb->" API calls replaced by regular calls. The utility and threading code are removed, except for buffering_thread(), which is still highly experimental. --- buffering.c | 808 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ buffering.h | 128 ++++------ 2 files changed, 855 insertions(+), 81 deletions(-) create mode 100644 buffering.c rewrite buffering.h (63%) diff --git a/buffering.c b/buffering.c new file mode 100644 index 0000000..7971002 --- /dev/null +++ b/buffering.c @@ -0,0 +1,808 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include +#include +#include +#include +#include "buffering.h" + +#include "ata.h" +#include "system.h" +#include "thread.h" +#include "file.h" +#include "panic.h" +#include "memory.h" +#include "lcd.h" +#include "font.h" +#include "button.h" +#include "kernel.h" +#include "tree.h" +#include "debug.h" +#include "sprintf.h" +#include "settings.h" +#include "codecs.h" +#include "audio.h" +#include "logf.h" +#include "mp3_playback.h" +#include "usb.h" +#include "status.h" +#include "screens.h" +#include "playlist.h" +#include "playback.h" +#include "pcmbuf.h" +#include "buffer.h" + +#ifdef SIMULATOR +#define ata_disk_is_active() 1 +#endif + +#define GUARD_SIZE (32*1024) + +/* amount of data to read in one read() call */ +#define AUDIO_DEFAULT_FILECHUNK (1024*32) + +/* Ring buffer helper macros */ +/* Buffer pointer (p) plus value (v), wrapped if necessary */ +#define RINGBUF_ADD(p,v) ((p+v)=v) ? p-v : p+buffer_len-v) +/* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ +#define RINGBUF_ADD_CROSS(p1,v,p2) \ +((p1= 0) { + return NULL; + } + buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3; + + size_t len = (data_size ? *data_size : 0) + + sizeof(struct memory_handle); + + /* check that we actually can add the handle and its data */ + int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx); + if (overlap >= 0) { + *data_size -= overlap; + len -= overlap; + } + if (len < sizeof(struct memory_handle)) { + /* There isn't even enough space to write the struct */ + return NULL; + } + + struct memory_handle *new_handle = + (struct memory_handle *)(&buffer[buf_widx]); + + /* only advance the buffer write index of the size of the struct */ + buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle)); + + if (!first_handle) { + /* the new handle is the first one */ + first_handle = new_handle; + } + + if (cur_handle) { + cur_handle->next = new_handle; + } + + cur_handle = new_handle; + cur_handle->id = cur_handle_id++; + cur_handle->next = NULL; + num_handles++; + return cur_handle; +} + +/* Delete a given memory handle from the linked list + and return true for success. Nothing is actually erased from memory. */ +static bool rm_handle(struct memory_handle *h) +{ + if (h == first_handle) { + first_handle = h->next; + if (h == cur_handle) { + /* h was the first and last handle: the buffer is now empty */ + cur_handle = NULL; + buf_ridx = buf_widx; + } else { + /* update buf_ridx to point to the new first handle */ + buf_ridx = (void *)first_handle - (void *)buffer; + } + } else { + struct memory_handle *m = first_handle; + while (m && m->next != h) { + m = m->next; + } + if (h && m && m->next == h) { + m->next = h->next; + if (h == cur_handle) { + cur_handle = m; + } + } else { + return false; + } + } + + num_handles--; + return true; +} + +/* Return a pointer to the memory handle of given ID. + NULL if the handle wasn't found */ +static struct memory_handle *find_handle(int handle_id) +{ + /* simple caching because most of the time the requested handle + will either be the same as the last, or the one after the last */ + if (cached_handle) + { + if (cached_handle_id == handle_id && + cached_handle_id == cached_handle->id) + return cached_handle; + else if (cached_handle->next && (cached_handle->next->id == handle_id)) + { + /* JD's quick testing showd this block was only entered + 2/1971 calls to find_handle. + 8/1971 calls to find_handle resulted in a cache miss */ + cached_handle = cached_handle->next; + cached_handle_id = handle_id; + return cached_handle; + } + } + + struct memory_handle *m = first_handle; + while (m && m->id != handle_id) { + m = m->next; + } + cached_handle_id = handle_id; + cached_handle = m; + return (m && m->id == handle_id) ? m : NULL; +} + +/* Move a memory handle and data_size of its data of delta. + Return a pointer to the new location of the handle. + delta is the value of which to move the struct data. + data_size is the amount of data to move along with the struct. */ +static struct memory_handle *move_handle(struct memory_handle *h, + size_t *delta, size_t data_size) +{ + if (*delta < 4) { + /* aligning backwards would yield a negative result, + and moving the handle of such a small amount is a waste + of time anyway. */ + return NULL; + } + /* make sure delta is 32-bit aligned so that the handle struct is. */ + *delta = (*delta - 3) & ~3; + + size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta); + + struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]); + + /* Invalidate the cache to prevent it from keeping the old location of h */ + if (h == cached_handle) + cached_handle = NULL; + + /* the cur_handle pointer might need updating */ + if (h == cur_handle) { + cur_handle = dest; + } + + if (h == first_handle) { + first_handle = dest; + buf_ridx = newpos; + } else { + struct memory_handle *m = first_handle; + while (m && m->next != h) { + m = m->next; + } + if (h && m && m->next == h) { + m->next = dest; + } else { + return NULL; + } + } + + memmove(dest, h, sizeof(struct memory_handle) + data_size); + + return dest; +} + + +/* +BUFFER SPACE MANAGEMENT +======================= + +buffer_handle : Buffer data for a handle +free_buffer : Free buffer space by moving a handle +fill_buffer : Call buffer_handle for all handles that have data to buffer +can_add_handle : Indicate whether it's safe to add a handle. +data_rem : Total amount of data needing to be buffered +wasted_space : Total amount of space available for freeing + +These functions are used by the buffering thread to manage buffer space. +*/ + +/* Buffer data for the given handle. Return the amount of data buffered + or -1 if the handle wasn't found */ +static ssize_t buffer_handle(int handle_id) +{ + DEBUGF("buffer_handle(%d)\n", handle_id); + struct memory_handle *h = find_handle(handle_id); + if (!h) + return -1; + + if (h->filerem == 0) { + /* nothing left to buffer */ + return 0; + } + + if (h->fd < 0) /* file closed, reopen */ + { + if (*h->path) + h->fd = open(h->path, O_RDONLY); + else + return -1; + + if (h->fd < 0) + return -1; + + if (h->offset) + lseek(h->fd, h->offset, SEEK_SET); + } + + ssize_t ret = 0; + while (h->filerem > 0) + { + /* max amount to copy */ + size_t copy_n = MIN( MIN(h->filerem, conf_filechunk), + buffer_len - h->widx); + + /* stop copying if it would overwrite the reading position + or the next handle */ + if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next && + RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned) + ((void *)h->next - (void *)buffer)) > 0)) + break; + + /* rc is the actual amount read */ + int rc = read(h->fd, &buffer[h->widx], copy_n); + + if (rc < 0) + { + DEBUGF("File ended %ld bytes early\n", (long)h->filerem); + h->filesize -= h->filerem; + h->filerem = 0; + break; + } + + /* Advance buffer */ + h->widx = RINGBUF_ADD(h->widx, rc); + if (h == cur_handle) + buf_widx = h->widx; + h->available += rc; + ret += rc; + h->filerem -= rc; + } + + if (h->filerem == 0) { + /* finished buffering the file */ + close(h->fd); + h->fd = -1; + } + + DEBUGF("buffered %ld bytes (%ld of %ld available, rem: %ld, off: %ld)\n", + ret, h->available, h->filesize, h->filerem, h->offset); + + return ret; +} + +/* Free buffer space by moving the handle struct right before the useful + part of its data buffer or by moving all the data. */ +static void free_buffer(int handle_id) +{ + struct memory_handle *h = find_handle(handle_id); + if (!h) + return; + + size_t delta; + /* The value of delta might change for alignment reasons */ + + if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || + h->type == TYPE_IMAGE) && h->filerem == 0 ) + { + /* metadata handle: we can move all of it */ + delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer), + h->data) - h->available; + h = move_handle(h, &delta, h->available); + if (!h) return; + h->data = RINGBUF_ADD(h->data, delta); + h->ridx = RINGBUF_ADD(h->ridx, delta); + h->widx = RINGBUF_ADD(h->widx, delta); + + /* when moving a struct mp3entry we need to readjust its pointers. */ + if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) { + adjust_mp3entry((struct mp3entry *)&buffer[h->data], + (void *)&buffer[h->data], + (void *)&buffer[RINGBUF_SUB(h->data, delta)]); + } + + DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n", + handle_id, delta); + } + else + { + /* only move the handle struct */ + delta = RINGBUF_SUB(h->ridx, h->data); + h = move_handle(h, &delta, 0); + if (!h) return; + h->data = RINGBUF_ADD(h->data, delta); + h->available -= delta; + h->offset += delta; + DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id, delta); + } +} + +/* Fill the buffer by buffering as much data as possible for handles that still + have data left to buffer */ +static void fill_buffer(void) +{ + DEBUGF("fill buffer()\n"); + struct memory_handle *m = first_handle; + while (m) { + if (m->filerem > 0) { + buffer_handle(m->id); + } + m = m->next; + } +} + +/* Check whether it's safe to add a new handle and reserve space to let the + current one finish buffering its data. Used by bufopen and bufgetdata as + a preliminary check before even trying to physically add the handle. + Returns true if it's ok to add a new handle, false if not. +*/ +static bool can_add_handle(void) +{ + if (cur_handle && cur_handle->filerem > 0) { + /* the current handle hasn't finished buffering. We can only add + a new one if there is already enough free space to finish + the buffering. */ + if (cur_handle->filerem < (buffer_len - BUF_USED)) { + /* Before adding the new handle we reserve some space for the + current one to finish buffering its data. */ + buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem); + } else { + return false; + } + } + + return true; +} + +/* Return the total amount of data left to be buffered for all the handles */ +static size_t data_rem(void) +{ + size_t ret = 0; + + struct memory_handle *m = first_handle; + while (m) { + ret += m->filerem; + m = m->next; + } + + return ret; +} + +/* Return the amount of data we have but don't need anymore. This data can be + safely erased to reclaim buffer space. */ +static size_t wasted_space(void) +{ + size_t ret = 0; + + struct memory_handle *m = first_handle; + while (m) { + ret += RINGBUF_SUB(m->ridx, m->data); + m = m->next; + } + + return ret; +} + + +/* +BUFFERING API FUNCTIONS +======================= + +bufopen : Request the opening of a new handle for a file +bufalloc : Open a new handle for data other than a file. +bufclose : Close an open handle +bufseek : Set the read pointer in a handle +bufadvance : Move the read pointer in a handle +bufread : Copy data from a handle into a given buffer +bufgetdata : Give a pointer to the handle's data + +These functions are exported, to allow interaction with the buffer. +They take care of the content of the structs, and rely on the linked list +management functions for all the actual handle management work. +*/ + + +/* Request a file be buffered + filename: name of the file to open + offset: offset at which to start buffering the file, useful when the first + (offset-1) bytes of the file aren't needed. + return value: <0 if the file cannot be opened, or one file already + queued to be opened, otherwise the handle for the file in the buffer +*/ +int bufopen(char *file, size_t offset, enum data_type type) +{ + if (!can_add_handle()) + return -2; + + int fd = open(file, O_RDONLY); + if (fd < 0) + return -1; + + size_t size = filesize(fd) - offset; + + if (type != TYPE_AUDIO && + size + sizeof(struct memory_handle) > buffer_len - buf_widx) + { + /* for types other than audio, the data can't wrap */ + return -2; + } + + DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n", + file, offset, size); + + struct memory_handle *h = add_handle(&size); + if (!h) + { + DEBUGF("failed to add handle\n"); + close(fd); + return -2; + } + + if (offset) lseek(fd, offset, SEEK_SET); + strncpy(h->path, file, MAX_PATH); + h->fd = fd; + h->filesize = filesize(fd); + h->filerem = h->filesize - offset; + h->offset = offset; + h->ridx = buf_widx; + h->widx = buf_widx; + h->data = buf_widx; + h->available = 0; + h->type = type; + + DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id); + return h->id; +} + +/* Open a new handle from data that isn't in a file. + src is the source buffer from which to copy data. It can be NULL to simply + reserve buffer space. + size is the requested size. The call will only be successful if the + requested amount of data can entirely fit in the buffer without wrapping. + Return value is the handle id for success or <0 for failure. +*/ +int bufalloc(void *src, size_t size, enum data_type type) +{ + if (!can_add_handle()) + return -2; + + if (size + sizeof(struct memory_handle) > buffer_len - buf_widx) + /* The data would need to wrap. */ + return -2; + + size_t allocsize = size; + struct memory_handle *h = add_handle(&allocsize); + + if (!h || allocsize != size) + return -2; + + if (src) { + if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) { + /* specially take care of struct mp3entry */ + copy_mp3entry((struct mp3entry *)&buffer[buf_widx], + (struct mp3entry *)src); + } else { + memcpy(&buffer[buf_widx], src, size); + } + } + + h->fd = -1; + *h->path = 0; + h->filesize = size; + h->filerem = 0; + h->offset = 0; + h->ridx = buf_widx; + h->widx = buf_widx; + h->data = buf_widx; + h->available = size; + h->type = type; + + buf_widx = RINGBUF_ADD(buf_widx, size); + + return h->id; +} + +/* Close the handle. Return 0 for success and < 0 for failure */ +int bufclose(int handle_id) +{ + DEBUGF("bufclose(%d)\n", handle_id); + struct memory_handle *h = find_handle(handle_id); + if (!h) + return -1; + + rm_handle(h); + return 0; +} + +/* Set reading index in handle (relatively to the start of the file). + Access before the available data will trigger a rebuffer. + TODO: Test this + TODO: Maybe force an immediate rebuffer by calling buffer_handle() ? + Return 0 for success and < 0 for failure: + -1 if the handle wasn't found + -2 if there is no data available at the new position + (the reading index is still moved) + -3 if the new requested position was beyond the end of the file +*/ +int bufseek(int handle_id, size_t newpos) +{ + int ret = 0; + struct memory_handle *h = find_handle(handle_id); + if (!h) + return -1; + + if (newpos > h->filesize) { + /* access beyond the end of the file */ + return -3; + } + + else if (newpos < h->offset) { + /* access before what we currently have. A rebuffer is needed. */ + h->offset = newpos; + h->available = 0; + h->filerem = h->filesize - newpos; + /* having changed filerem should be enough to trigger the rebuffer. */ + h->widx = h->data; + ret = -2; + } + + else if (newpos > h->offset + h->available) { + /* data isn't available yet. */ + ret = -2; + } + + h->ridx = RINGBUF_ADD(h->data, newpos); + return ret; +} + +/* Advance the reading index in a handle (relatively to its current position). + Return 0 for success and < 0 for failure + TODO: Add some rebuffering like in bufseek */ +int bufadvance(int handle_id, off_t offset) +{ + struct memory_handle *h = find_handle(handle_id); + if (!h) + return -1; + + if (offset >= 0) + { + /* check for access beyond what's available */ + if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data))) + return -2; + + h->ridx = RINGBUF_ADD(h->ridx, offset); + } + else + { + /* check for access before what's available */ + if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data)) + return -2; + + h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset)); + } + + return 0; +} + +/* Copy data from the given handle to the dest buffer. + Return the number of bytes copied or < 0 for failure. */ +ssize_t bufread(int handle_id, size_t size, char *dest) +{ + struct memory_handle *h = find_handle(handle_id); + size_t buffered_data; + if (!h) + return -1; + + if (h->available < size && h->filerem > 0) /* Data isn't ready */ + return -2; + + if (h->available == 0 && h->filerem == 0) /* File is finished reading */ + return 0; + + buffered_data = MIN(size, h->available - RINGBUF_SUB(h->ridx, h->data)); + + if (h->ridx + buffered_data > buffer_len) + { + /* the data wraps around the end of the buffer */ + size_t read = buffer_len - h->ridx; + memcpy(dest, &buffer[h->ridx], read); + memcpy(dest+read, buffer, buffered_data - read); + } + else memcpy(dest, &buffer[h->ridx], buffered_data); + + return buffered_data; +} + +/* Update the "data" pointer to make the handle's data available to the caller. + Return the length of the available linear data or < 0 for failure. + size is the amount of linear data requested. it can be 0 to get as + much as possible. + The guard buffer may be used to provide the requested size */ +ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data) +{ + struct memory_handle *h = find_handle(handle_id); + if (!h) + return -1; + + if (h->available < size && h->filerem > 0) /* Data isn't ready */ + return -2; + + if (h->available == 0 && h->filerem == 0) /* File is finished reading */ + return 0; + + ssize_t ret; + + if (h->ridx + size > buffer_len && + h->available - RINGBUF_SUB(h->ridx, h->data) >= size) + { + /* the data wraps around the end of the buffer : + use the guard buffer to provide the requested amount of data. */ + size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_SIZE); + memcpy(guard_buffer, (unsigned char *)buffer, copy_n); + ret = buffer_len - h->ridx + copy_n; + DEBUGF("used the guard buffer to complete\n"); + } + else + { + ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data), + buffer_len - h->ridx); + } + + *data = (unsigned char *)&buffer[h->ridx]; + + /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id, + (long)h->ridx, ret); */ + return ret; +} + + +void buffering_thread(void) +{ + while (true) + { + if (data_rem() > 0 && wasted_space() > buffer_len/5) { + DEBUGF("there is %ld bytes of wasted space\n", wasted_space()); + + /* free buffer from outdated audio data */ + struct memory_handle *m = first_handle; + while (m) { + if (m->type == TYPE_AUDIO) + free_buffer(m->id); + m = m->next; + } + + /* free buffer by moving metadata */ + m = first_handle; + while (m) { + if (m->type != TYPE_AUDIO) + free_buffer(m->id); + m = m->next; + } + + } + + if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 && + ata_disk_is_active()) + { + DEBUGF("%ld bytes left to buffer and the buffer is low\n", + data_rem()); + fill_buffer(); + } else { + sleep(HZ/2); + } + } +} diff --git a/buffering.h b/buffering.h dissimilarity index 63% index d48986d..a08c4e1 100644 --- a/buffering.h +++ b/buffering.h @@ -1,81 +1,47 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 Nicolas Pennequin - * - * All files in this archive are subject to the GNU General Public License. - * See the file COPYING in the source tree root for full license agreement. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#ifndef _BUFFERING_H_ -#define _BUFFERING_H_ - -#include - -#define MAX_PATH 256 - -/* Memory allocation should happen in the following order for each track: -TYPE_CODEC, TYPE_(>AUDIO), TYPE_AUDIO. -TYPE_CODEC will be released as soon as it has been closed. -TYPE_(>AUDIO) (the metadata types) will stay valid as long as the - audio buffer straight after them is kept open. -TYPE_AUDIO will be released as needed */ - -#if 0 -JdGordon's thinking aloud... - -The way this all should work is that if first_handle->data_type > TYPE_AUDIO -the LL will be searched for the first TYPE_AUDIO item. -if that item is closed then all the handles between first_handle and the found audio track -will be closed and dumped (or maybe they have to wait for playback to close them...), -either way, they will never be dumped unless the audio item is closed. - -Now, when reading/wiritng the buffer... if (m->next && m->next->id == -1 && m->next->data_type == TYPE_AUDIO (might not need this extra check)) then it is a continuation of m. the guard fuffer will most likely be needed when the buffer is read.. but thats ok. - -Unless im wrong it should only be possible that we have one of these "safe" sections at any one time. - -The only problem with this system is that the MoB has to be allocated before the audio is read. -the id3 data should be a constant size which is ok, cuesheet will be known. image and buffer will not be known, which is where it might get tricky. - -/end babbling -#endif - -enum data_type { - TYPE_CODEC, - TYPE_AUDIO, /* TYPE_VIDEO, TYPE_BUFFERING_FILE, all the same */ - /* the following data_types will not be unallocated unless the - audio next audio handle is closed. When that happens all the data - will be unallocated in one hit */ - TYPE_ID3, - TYPE_CUESHEET, - TYPE_IMAGE, - TYPE_BUFFER, - TYPE_UNKNOWN, -}; - -enum data_state { - STATE_UNKNOWN, - STATE_OUTDATED, - STATE_IN_USE, - STATE_TO_BE_USED -}; - -void buffer_init(void); -int bufopen(char *file, size_t offset); -int bufclose(int handle_id); -int bufseek(int handle_id, size_t offset); -int bufadvance(int handle_id, ssize_t offset); -int bufread(int handle_id, size_t size, char *dest); -long bufgetdata(int handle_id, size_t size, unsigned char **data); - -#endif +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _BUFFERING_H_ +#define _BUFFERING_H_ + +#include + +#define MAX_PATH 256 + +enum data_type { + TYPE_CODEC, + TYPE_AUDIO, + TYPE_STREAM, + TYPE_ID3, + TYPE_CUESHEET, + TYPE_IMAGE, + TYPE_BUFFER, + TYPE_UNKNOWN, +}; + +void buffer_init(void); +int bufopen(char *file, size_t offset, enum data_type type); +int bufalloc(void *src, size_t size, enum data_type type); +int bufclose(int handle_id); +int bufseek(int handle_id, size_t newpos); +int bufadvance(int handle_id, off_t offset); +ssize_t bufread(int handle_id, size_t size, char *dest); +ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data); + +#endif -- 2.11.4.GIT