mirror of
https://github.com/game-stop/veejay.git
synced 2025-12-18 22:00:00 +01:00
268 lines
6.4 KiB
C
268 lines
6.4 KiB
C
/*
|
|
* Copyright (C) 2002-2006 Niels Elburg <nelburg@looze.net>
|
|
*
|
|
* 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 <config.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <libvjmem/vjmem.h>
|
|
#include <libvjmsg/vj-common.h>
|
|
#include <libel/elcache.h>
|
|
#ifdef STRICT_CHECKING
|
|
#include <assert.h>
|
|
#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;
|
|
}
|