/* * Copyright (C) 2002-2006 Niels Elburg * * 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 2 * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /* Cache frames from file to memory */ /** \defgroup elcache Efficient Frame Caching * * Veejay stores each retrieved frame (from the EDL) in a temporary cache. * The cache has at most N slots, in each slot an image buffer is placed * together with a minimum of bookkeeping (frame number and decoder ID) * * The caching mechanism will trash when loading a new series of frames. * As unknown frames are loaded, old frames will be discarded. * A very long sample will thus beat the caching mechanism if the sample only * loops forward. * * This cache does *not* read-ahead, instead it keeps already played frames for repated usage. * The caching mechanism is intended for sample playback usage. It is * only used when looping samples that typically access compressed image data repeatedly. * The price for such a situation is the time needed to memory-to-memory copy the image buffer. * * Veejay estimates the number of slots it can claim by taking a percentage * of the available system memory. * The caching mechanism is disabled by default. * */ #include #include #include #include #include #include #include #include #include #ifdef STRICT_CHECKING #include #endif //! \typedef cache_slot_t Cache Slot for holding an image buffer typedef struct { int size; int fmt; long num; void *buffer; } cache_slot_t; //! \typedef cache_t Cache Container for holding N cache slots typedef struct { cache_slot_t **cache; int len; long *index; } cache_t; //! Find an available slot /** \param v Cache \return Index */ static int cache_free_slot(cache_t *v) { int i; for( i = 0; i < v->len; i ++ ) if( v->index[i] == -1 ) return i; return -1; } static long total_mem_used_ = 0; //! Push an image buffer to a slot /** \param v Cache \param free_slot Cache Index \param linbuf Image buffer \param frame_num Frame Number \param buf_len Length of Image Buffer \param decoder_id Decoder ID */ static void cache_claim_slot(cache_t *v, int free_slot, uint8_t *linbuf, long frame_num,int buf_len, int decoder_id) { // create new node cache_slot_t *data = (cache_slot_t*) vj_malloc(sizeof(cache_slot_t)); data->size = buf_len; data->num = frame_num; data->fmt = decoder_id; data->buffer = vj_malloc( buf_len ); #ifdef STRICT_CHECKING assert( v->index[free_slot] != frame_num ); #endif // clear old buffer if( v->index[free_slot] >= 0 ) { cache_slot_t *del_slot = v->cache[free_slot]; total_mem_used_ -= del_slot->size; free( del_slot->buffer ); free( del_slot ); v->cache[free_slot] = NULL; } veejay_memcpy( data->buffer, linbuf, buf_len ); v->index[ free_slot ] = frame_num; v->cache[ free_slot ] = data; total_mem_used_ += buf_len; } //! Find a cached frame /** \param v Cache \param frame_num Frame Number \return Index of cached frame */ static int cache_find_slot( cache_t *v, long frame_num ) { int i; int k = 0; long n = 0; for( i = 0; i < v->len ; i ++ ) { long d = abs( v->index[i] - frame_num ); if( d > n ) { n = d; k = i ; } } return k; } //! Find a Frame in the Cache /** \param v Cache \param frame_num Frame Number \return Error or Index */ static int cache_locate_slot( cache_t *v, long frame_num) { int i; for( i = 0; i < v->len ; i ++ ) if( v->index[i] == frame_num ) return i; return -1; } //! Initialize the Caching System /** \param n_slots Maximum slots for this Cache \return New Cache */ void *init_cache( unsigned int n_slots ) { if(n_slots <= 0) return NULL; cache_t *v = (cache_t*) vj_malloc(sizeof(cache_t)); v->len = n_slots; v->cache = (cache_slot_t**) vj_malloc(sizeof(cache_slot_t*) * v->len ); memset( v->cache, 0, sizeof(cache_slot_t*) * v->len ); v->index = (long*) malloc(sizeof(long) * v->len ); memset( v->index, -1, sizeof(long) * v->len ); return (void*) v; } //! Clear all slots in Cache /** \param cache Cache */ void reset_cache(void *cache) { int i = 0; cache_t *v = (cache_t*) cache; memset( v->index, -1, sizeof(long) * v->len ); for( i = 0; i < v->len; i ++ ) { if( v->cache[i] ) { total_mem_used_ -= v->cache[i]->size; if(v->cache[i]->buffer) free(v->cache[i]->buffer); free(v->cache[i]); v->cache[i] = NULL; }; } } int cache_avail_mb() { return ( total_mem_used_ == 0 ? 0 : total_mem_used_ / (1024 * 1024 )); } //! Destroy Cache /** \param cache Cache */ void free_cache(void *cache) { cache_t *v = (cache_t*) cache; reset_cache( cache ); free(v->cache); free(v->index); free(v); } int cache_frame( void *cache, uint8_t *linbuf, int buflen, long frame_num , int decoder_id) { cache_t *v = (cache_t*) cache; #ifdef STRICT_CHECKING assert( cache != NULL ); assert( linbuf != NULL ); assert( buflen > 0 ); assert( frame_num >= 0 ); #endif int slot_num = cache_free_slot( cache ); if( slot_num == -1 ) slot_num = cache_find_slot( v, frame_num ); #ifdef STRICT_CHECKING assert(slot_num >= 0 ); #endif cache_claim_slot(v, slot_num, linbuf, frame_num, buflen, decoder_id); return 1; } //! Get a Frame from the Cache /** \param cache Cache \param frame_num Frame Number \param buf_len Buffer Length \param decoder_id Decoder ID \return Pointer to Image buffer or NULL */ uint8_t *get_cached_frame( void *cache, long frame_num, int *buf_len, int *decoder_id ) { cache_t *v = (cache_t*) cache; int slot = cache_locate_slot( v, frame_num ); if( slot == -1 ) return NULL; cache_slot_t *data = v->cache[ slot ]; *buf_len = data->size; *decoder_id = data->fmt; #ifdef STRICT_CHECKING assert( data->num == frame_num ); #endif return (uint8_t*) data->buffer; }