Commit 6a74f9fb authored by Christophe Massiot's avatar Christophe Massiot

Next Generation Buffer Manager, for PS plug-in.

parent 279f805a
......@@ -175,6 +175,9 @@
/* Maximum length of a hostname or source name */
#define INPUT_MAX_SOURCE_LENGTH 100
/* Maximum memory the input is allowed to use (20 MB) */
#define INPUT_MAX_ALLOCATION 20971520
/* Default network protocol */
#define INPUT_NETWORK_PROTOCOL_VAR "vlc_network_protocol"
#define INPUT_NETWORK_PROTOCOL_DEFAULT "ts"
......
......@@ -2,7 +2,7 @@
* input_ext-dec.h: structures exported to the VideoLAN decoders
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
* $Id: input_ext-dec.h,v 1.42 2001/12/10 04:53:10 sam Exp $
* $Id: input_ext-dec.h,v 1.43 2001/12/12 11:18:38 massiot Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Michel Kaempf <maxx@via.ecp.fr>
......@@ -44,6 +44,7 @@ typedef struct data_packet_s
{
/* Nothing before this line, the code relies on that */
byte_t * p_buffer; /* raw data packet */
byte_t * p_buffer_end;
long l_size; /* buffer size */
/* Decoders information */
......@@ -83,6 +84,9 @@ typedef struct pes_packet_s
p_next fields of the data_packet_t struct) */
data_packet_t * p_first; /* The first packet contained by this
* PES (used by decoders). */
/* Chained list used by the input buffers manager */
struct pes_packet_s * p_next;
} pes_packet_t;
/*****************************************************************************
......
......@@ -3,7 +3,7 @@
* but exported to plug-ins
*****************************************************************************
* Copyright (C) 1999, 2000, 2001 VideoLAN
* $Id: input_ext-plugins.h,v 1.8 2001/12/10 04:53:10 sam Exp $
* $Id: input_ext-plugins.h,v 1.9 2001/12/12 11:18:38 massiot Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
......@@ -197,6 +197,359 @@ void input_NetlistDeletePES( void *,
void input_NetlistEnd( struct input_thread_s * );
/*
* Optional Next Generation buffer manager
*
* Either buffers can only be used in one data packet (PS case), or buffers
* contain several data packets (DVD case). In the first case, buffers are
* embedded into data packets, otherwise they are allocated separately and
* shared with a refcount. --Meuuh
*/
/* Number of buffers for the calculation of the mean */
#define INPUT_BRESENHAM_NB 50
/* Flags */
#define BUFFERS_NOFLAGS 0
#define BUFFERS_SHARED 1
#define BUFFERS_UNIQUE_SIZE 2
/*****************************************************************************
* input_buffers_t: defines a LIFO per data type to keep
*****************************************************************************/
#define PACKETS_LIFO( TYPE, NAME ) \
struct \
{ \
TYPE * p_stack; \
unsigned int i_depth; \
} NAME;
#define BUFFERS_LIFO( TYPE, NAME ) \
struct \
{ \
TYPE * p_stack; /* First item in the LIFO */ \
unsigned int i_depth; /* Number of items in the LIFO */ \
unsigned int i_average_size; /* Average size of the items (Bresenham) */\
} NAME;
#define DECLARE_BUFFERS_EMBEDDED( FLAGS, NB_LIFO ) \
typedef struct input_buffers_s \
{ \
vlc_mutex_t lock; \
PACKETS_LIFO( pes_packet_t, pes ) \
BUFFERS_LIFO( data_packet_t, data[NB_LIFO] ) \
size_t i_allocated; \
} input_buffers_t;
#define DECLARE_BUFFERS_SHARED( FLAGS, NB_LIFO ) \
typedef struct data_buffer_s \
{ \
int i_refcount; \
int i_size; \
struct data_buffers_s * p_next; \
byte_t payload_start; \
} data_buffer_t; \
\
typedef struct input_buffers_s \
{ \
vlc_mutex_t lock; \
PACKETS_LIFO( pes_packet_t, pes ) \
PACKETS_LIFO( data_packet_t, data ) \
BUFFERS_LIFO( data_buffers_t, buffers[NB_LIFO] ) \
size_t i_allocated; \
} input_buffers_t;
/*****************************************************************************
* input_BuffersInit: initialize the cache structures, return a pointer to it
*****************************************************************************/
#define DECLARE_BUFFERS_INIT( FLAGS, NB_LIFO ) \
static void * input_BuffersInit( void ) \
{ \
input_buffers_t * p_buffers = malloc( sizeof( input_buffers_t ) ); \
\
if( p_buffers == NULL ) \
{ \
return( NULL ); \
} \
\
memset( p_buffers, 0, sizeof( input_buffers_t ) ); \
vlc_mutex_init( &p_buffers->lock ); \
\
return (void *)p_buffers; \
}
/*****************************************************************************
* input_BuffersEnd: free all cached structures
*****************************************************************************/
#define DECLARE_BUFFERS_END( FLAGS, NB_LIFO ) \
static void input_BuffersEnd( void * _p_buffers ) \
{ \
input_buffers_t * p_buffers = (input_buffers_t *)_p_buffers; \
\
if( _p_buffers != NULL ) \
{ \
pes_packet_t * p_pes = p_buffers->pes.p_stack; \
int i; \
\
if( p_main->b_stats ) \
{ \
int i; \
for( i = 0; i < NB_LIFO; i++ ) \
{ \
intf_StatMsg( \
"input buffers stats: data[%d]: %d bytes, %d packets", \
i, p_buffers->data[i].i_average_size, \
p_buffers->data[i].i_depth ); \
} \
} \
\
/* Free PES */ \
while( p_pes != NULL ) \
{ \
pes_packet_t * p_next = p_pes->p_next; \
free( p_pes ); \
p_pes = p_next; \
} \
\
for( i = 0; i < NB_LIFO; i++ ) \
{ \
data_packet_t * p_data = p_buffers->data[i].p_stack; \
\
/* Free data packets */ \
while( p_data != NULL ) \
{ \
data_packet_t * p_next = p_data->p_next; \
p_buffers->i_allocated -= p_data->p_buffer_end \
- p_data->p_buffer; \
free( p_data ); \
p_data = p_next; \
} \
} \
\
if( p_buffers->i_allocated ) \
{ \
intf_ErrMsg( "input buffers error: %d bytes have not been" \
" freed, expect memory leak", \
p_buffers->i_allocated ); \
} \
\
vlc_mutex_destroy( &p_buffers->lock ); \
free( _p_buffers ); \
} \
}
/*****************************************************************************
* input_NewPacket: return a pointer to a data packet of the appropriate size
*****************************************************************************/
#define DECLARE_BUFFERS_NEWPACKET( FLAGS, NB_LIFO ) \
static data_packet_t * input_NewPacket( void * _p_buffers, size_t i_size ) \
{ \
input_buffers_t * p_buffers = (input_buffers_t *)_p_buffers; \
int i_select; \
data_packet_t * p_data; \
\
/* Safety checks */ \
if( i_size > INPUT_MAX_PACKET_SIZE ) \
{ \
intf_ErrMsg( "Packet too big (%d)", i_size ); \
return NULL; \
} \
\
vlc_mutex_lock( &p_buffers->lock ); \
\
if( p_buffers->i_allocated > INPUT_MAX_ALLOCATION ) \
{ \
vlc_mutex_unlock( &p_buffers->lock ); \
intf_ErrMsg( "INPUT_MAX_ALLOCATION reached (%d)", \
p_buffers->i_allocated ); \
return NULL; \
} \
\
for( i_select = 0; i_select < NB_LIFO - 1; i_select++ ) \
{ \
if( i_size <= (2 * p_buffers->data[i_select].i_average_size \
+ p_buffers->data[i_select + 1].i_average_size) / 3 ) \
{ \
break; \
} \
} \
\
if( p_buffers->data[i_select].p_stack != NULL ) \
{ \
/* Take the packet from the cache */ \
p_data = p_buffers->data[i_select].p_stack; \
p_buffers->data[i_select].p_stack = p_data->p_next; \
p_buffers->data[i_select].i_depth--; \
\
/* Reallocate the packet if it is too small or too large */ \
if( p_data->p_buffer_end - p_data->p_buffer < i_size || \
p_data->p_buffer_end - p_data->p_buffer > 3 * i_size ) \
{ \
p_buffers->i_allocated -= p_data->p_buffer_end \
- p_data->p_buffer; \
p_data = realloc( p_data, sizeof( data_packet_t ) + i_size ); \
if( p_data == NULL ) \
{ \
vlc_mutex_unlock( &p_buffers->lock ); \
intf_ErrMsg( "Out of memory" ); \
return NULL; \
} \
p_data->p_buffer = (byte_t *)p_data + sizeof( data_packet_t ); \
p_data->p_buffer_end = p_data->p_buffer + i_size; \
p_buffers->i_allocated += i_size; \
} \
} \
else \
{ \
/* Allocate a new packet */ \
p_data = malloc( sizeof( data_packet_t ) + i_size ); \
if( p_data == NULL ) \
{ \
vlc_mutex_unlock( &p_buffers->lock ); \
intf_ErrMsg( "Out of memory" ); \
return NULL; \
} \
p_data->p_buffer = (byte_t *)p_data + sizeof( data_packet_t ); \
p_data->p_buffer_end = p_data->p_buffer + i_size; \
p_buffers->i_allocated += i_size; \
} \
\
vlc_mutex_unlock( &p_buffers->lock ); \
\
/* Initialize data */ \
p_data->p_next = NULL; \
p_data->b_discard_payload = 0; \
p_data->p_payload_start = p_data->p_buffer; \
p_data->p_payload_end = p_data->p_buffer + i_size; \
\
return( p_data ); \
}
/*****************************************************************************
* input_DeletePacket: put a packet back into the cache
*****************************************************************************/
#define DECLARE_BUFFERS_DELETEPACKET( FLAGS, NB_LIFO, DATA_CACHE_SIZE ) \
static __inline__ void _input_DeletePacket( void * _p_buffers, \
data_packet_t * p_data ) \
{ \
input_buffers_t * p_buffers = (input_buffers_t *)_p_buffers; \
int i_select, i_size; \
\
i_size = p_data->p_buffer_end - p_data->p_buffer; \
for( i_select = 0; i_select < NB_LIFO - 1; i_select++ ) \
{ \
if( i_size <= (2 * p_buffers->data[i_select].i_average_size \
+ p_buffers->data[i_select + 1].i_average_size) / 3 ) \
{ \
break; \
} \
} \
\
if( p_buffers->data[i_select].i_depth < DATA_CACHE_SIZE ) \
{ \
/* Cache not full : store the packet in it */ \
p_data->p_next = p_buffers->data[i_select].p_stack; \
p_buffers->data[i_select].p_stack = p_data; \
p_buffers->data[i_select].i_depth++; \
\
/* Update Bresenham mean (very approximative) */ \
p_buffers->data[i_select].i_average_size = ( i_size \
+ p_buffers->data[i_select].i_average_size \
* (INPUT_BRESENHAM_NB - 1) ) \
/ INPUT_BRESENHAM_NB; \
} \
else \
{ \
p_buffers->i_allocated -= p_data->p_buffer_end - p_data->p_buffer; \
free( p_data ); \
} \
} \
\
static void input_DeletePacket( void * _p_buffers, data_packet_t * p_data ) \
{ \
input_buffers_t * p_buffers = (input_buffers_t *)_p_buffers; \
\
vlc_mutex_lock( &p_buffers->lock ); \
_input_DeletePacket( _p_buffers, p_data ); \
vlc_mutex_unlock( &p_buffers->lock ); \
}
/*****************************************************************************
* input_NewPES: return a pointer to a new PES packet
*****************************************************************************/
#define DECLARE_BUFFERS_NEWPES( FLAGS, NB_LIFO ) \
static pes_packet_t * input_NewPES( void * _p_buffers ) \
{ \
input_buffers_t * p_buffers = (input_buffers_t *)_p_buffers; \
pes_packet_t * p_pes; \
\
vlc_mutex_lock( &p_buffers->lock ); \
\
if( p_buffers->pes.p_stack != NULL ) \
{ \
p_pes = p_buffers->pes.p_stack; \
p_buffers->pes.p_stack = p_pes->p_next; \
p_buffers->pes.i_depth--; \
} \
else \
{ \
p_pes = malloc( sizeof( pes_packet_t ) ); \
if( p_pes == NULL ) \
{ \
intf_ErrMsg( "Out of memory" ); \
vlc_mutex_unlock( &p_buffers->lock ); \
return( NULL ); \
} \
} \
\
vlc_mutex_unlock( &p_buffers->lock ); \
\
/* Initialize data */ \
p_pes->p_next = NULL; \
p_pes->b_data_alignment = p_pes->b_discontinuity = \
p_pes->i_pts = p_pes->i_dts = 0; \
p_pes->i_pes_size = 0; \
p_pes->p_first = NULL; \
\
return( p_pes ); \
}
/*****************************************************************************
* input_DeletePES: put a pes and all data packets back into the cache
*****************************************************************************/
#define DECLARE_BUFFERS_DELETEPES( FLAGS, NB_LIFO, PES_CACHE_SIZE ) \
static void input_DeletePES( void * _p_buffers, pes_packet_t * p_pes ) \
{ \
input_buffers_t * p_buffers = (input_buffers_t *)_p_buffers; \
data_packet_t * p_data; \
\
vlc_mutex_lock( &p_buffers->lock ); \
\
p_data = p_pes->p_first; \
while( p_data != NULL ) \
{ \
data_packet_t * p_next = p_data->p_next; \
_input_DeletePacket( _p_buffers, p_data ); \
p_data = p_next; \
} \
\
if( p_buffers->pes.i_depth < PES_CACHE_SIZE ) \
{ \
/* Cache not full : store the packet in it */ \
p_pes->p_next = p_buffers->pes.p_stack; \
p_buffers->pes.p_stack = p_pes; \
p_buffers->pes.i_depth++; \
} \
else \
{ \
free( p_pes ); \
} \
\
vlc_mutex_unlock( &p_buffers->lock ); \
}
/*
* Optional MPEG demultiplexing
*/
......
......@@ -2,7 +2,7 @@
* input_ps.c: PS demux and packet management
*****************************************************************************
* Copyright (C) 1998-2001 VideoLAN
* $Id: input_ps.c,v 1.2 2001/12/10 04:53:11 sam Exp $
* $Id: input_ps.c,v 1.3 2001/12/12 11:18:38 massiot Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Cyril Deguet <asmax@via.ecp.fr>
......@@ -91,10 +91,20 @@ static void PSInit ( struct input_thread_s * );
static void PSEnd ( struct input_thread_s * );
static int PSSetProgram ( struct input_thread_s * , pgrm_descriptor_t * );
static void PSSeek ( struct input_thread_s *, off_t );
static struct pes_packet_s * NewPES ( void * );
static struct data_packet_s * NewPacket ( void *, size_t );
static void DeletePacket ( void *, struct data_packet_s * );
static void DeletePES ( void *, struct pes_packet_s * );
/*****************************************************************************
* Declare a buffer manager
*****************************************************************************/
#define FLAGS BUFFERS_NOFLAGS
#define NB_LIFO 2
DECLARE_BUFFERS_EMBEDDED( FLAGS, NB_LIFO );
DECLARE_BUFFERS_INIT( FLAGS, NB_LIFO );
DECLARE_BUFFERS_END( FLAGS, NB_LIFO );
DECLARE_BUFFERS_NEWPACKET( FLAGS, NB_LIFO );
DECLARE_BUFFERS_DELETEPACKET( FLAGS, NB_LIFO, 150 );
DECLARE_BUFFERS_NEWPES( FLAGS, NB_LIFO );
DECLARE_BUFFERS_DELETEPES( FLAGS, NB_LIFO, 150 );
/*****************************************************************************
* Functions exported as capabilities. They are declared as static so that
......@@ -113,10 +123,10 @@ void _M( input_getfunctions )( function_list_t * p_function_list )
input.pf_set_program = PSSetProgram;
input.pf_read = PSRead;
input.pf_demux = input_DemuxPS;
input.pf_new_packet = NewPacket;
input.pf_new_pes = NewPES;
input.pf_delete_packet = DeletePacket;
input.pf_delete_pes = DeletePES;
input.pf_new_packet = input_NewPacket;
input.pf_new_pes = input_NewPES;
input.pf_delete_packet = input_DeletePacket;
input.pf_delete_pes = input_DeletePES;
input.pf_rewind = NULL;
input.pf_seek = PSSeek;
#undef input
......@@ -158,65 +168,12 @@ static int PSProbe( probedata_t *p_data )
*****************************************************************************/
static void PSInit( input_thread_t * p_input )
{
packet_cache_t * p_packet_cache;
/* creates the packet cache structure */
p_packet_cache = malloc( sizeof(packet_cache_t) );
if ( p_packet_cache == NULL )
if( (p_input->p_method_data = input_BuffersInit()) == NULL )
{
intf_ErrMsg( "Out of memory" );
p_input->b_error = 1;
return;
}
p_input->p_method_data = (void *)p_packet_cache;
/* Initialize packet cache mutex */
vlc_mutex_init( &p_packet_cache->lock );
/* allocates the data cache */
p_packet_cache->data.p_stack = malloc( DATA_CACHE_SIZE *
sizeof(data_packet_t*) );
if ( p_packet_cache->data.p_stack == NULL )
{
intf_ErrMsg( "Out of memory" );
p_input->b_error = 1;
return;
}
p_packet_cache->data.l_index = 0;
/* allocates the PES cache */
p_packet_cache->pes.p_stack = malloc( PES_CACHE_SIZE *
sizeof(pes_packet_t*) );
if ( p_packet_cache->pes.p_stack == NULL )
{
intf_ErrMsg( "Out of memory" );
p_input->b_error = 1;
return;
}
p_packet_cache->pes.l_index = 0;
/* allocates the small buffer cache */
p_packet_cache->smallbuffer.p_stack = malloc( SMALL_CACHE_SIZE *
sizeof(packet_buffer_t) );
if ( p_packet_cache->smallbuffer.p_stack == NULL )
{
intf_ErrMsg( "Out of memory" );
p_input->b_error = 1;
return;
}
p_packet_cache->smallbuffer.l_index = 0;
/* allocates the large buffer cache */
p_packet_cache->largebuffer.p_stack = malloc( LARGE_CACHE_SIZE *
sizeof(packet_buffer_t) );
if ( p_packet_cache->largebuffer.p_stack == NULL )
{
intf_ErrMsg( "Out of memory" );
p_input->b_error = 1;
return;
}
p_packet_cache->largebuffer.l_index = 0;
if( p_input->p_stream == NULL )
{
/* Re-open the socket as a buffered FILE stream */
......@@ -275,7 +232,7 @@ static void PSInit( input_thread_t * p_input )
{
/* FIXME: use i_p_config_t */
input_ParsePS( p_input, pp_packets[i] );
DeletePacket( p_input->p_method_data, pp_packets[i] );
p_input->pf_delete_packet( p_input->p_method_data, pp_packets[i] );
}
/* File too big. */
......@@ -388,22 +345,7 @@ static void PSInit( input_thread_t * p_input )
*****************************************************************************/
static void PSEnd( input_thread_t * p_input )
{
#define p_packet_cache ((packet_cache_t *)p_input->p_method_data)
vlc_mutex_destroy( &p_packet_cache->lock );
if( p_packet_cache->data.p_stack )
free( p_packet_cache->data.p_stack );
if( p_packet_cache->pes.p_stack )
free( p_packet_cache->pes.p_stack );
if( p_packet_cache->smallbuffer.p_stack )
free( p_packet_cache->smallbuffer.p_stack );
if( p_packet_cache->largebuffer.p_stack )
free( p_packet_cache->largebuffer.p_stack );
#undef p_packet_cache
free( p_input->p_method_data );
input_BuffersEnd( p_input->p_method_data );
}
/*****************************************************************************
......@@ -528,7 +470,8 @@ static int PSRead( input_thread_t * p_input,
}
/* Fetch a packet of the appropriate size. */
p_data = NewPacket( p_input->p_method_data, i_packet_size + 6 );
p_data = p_input->pf_new_packet( p_input->p_method_data,
i_packet_size + 6 );
if( p_data == NULL )
{
intf_ErrMsg( "Out of memory" );
......@@ -598,358 +541,3 @@ static void PSSeek( input_thread_t * p_input, off_t i_position )
p_input->stream.p_selected_area->i_tell = i_position;
}
/*
* Packet management utilities
*/
/*****************************************************************************
* NewPacket: allocates a data packet
*****************************************************************************/
static struct data_packet_s * NewPacket( void * p_packet_cache,
size_t l_size )
{
packet_cache_t * p_cache;
data_packet_t * p_data;
long l_index;
p_cache = (packet_cache_t *)p_packet_cache;
#ifdef DEBUG
if ( p_cache == NULL )
{
intf_ErrMsg( "PPacket cache not initialized" );
return NULL;
}
#endif
/* Safety check */
if( l_size > INPUT_MAX_PACKET_SIZE )
{
intf_ErrMsg( "Packet too big (%d)", l_size );
return NULL;
}
vlc_mutex_lock( &p_cache->lock );
/* Checks whether the data cache is empty */
if( p_cache->data.l_index == 0 )
{
/* Allocates a new packet */
p_data = malloc( sizeof(data_packet_t) );
if( p_data == NULL )
{
intf_ErrMsg( "Out of memory" );
vlc_mutex_unlock( &p_cache->lock );
return NULL;
}
#ifdef TRACE_INPUT
intf_DbgMsg( "PS input: data packet allocated" );
#endif
}
else
{
/* Takes the packet out from the cache */
if( (p_data = p_cache->data.p_stack[ -- p_cache->data.l_index ])
== NULL )
{
intf_ErrMsg( "NULL packet in the data cache" );
vlc_mutex_unlock( &p_cache->lock );
return NULL;
}
}
if( l_size < MAX_SMALL_SIZE )
{
/* Small buffer */
/* Checks whether the buffer cache is empty */
if( p_cache->smallbuffer.l_index == 0 )
{
/* Allocates a new packet */
p_data->p_buffer = malloc( l_size );
if( p_data->p_buffer == NULL )
{
intf_DbgMsg( "Out of memory" );
free( p_data );
vlc_mutex_unlock( &p_cache->lock );
return NULL;
}
#ifdef TRACE_INPUT
intf_DbgMsg( "PS input: small buffer allocated" );
#endif
p_data->l_size = l_size;
}
else
{
/* Takes the packet out from the cache */
l_index = -- p_cache->smallbuffer.l_index;
if( (p_data->p_buffer = p_cache->smallbuffer.p_stack[l_index].p_data)
== NULL )
{
intf_ErrMsg( "NULL packet in the small buffer cache" );
free( p_data );
vlc_mutex_unlock( &p_cache->lock );
return NULL;
}
/* Reallocates the packet if it is too small or too large */
if( p_cache->smallbuffer.p_stack[l_index].l_size < l_size ||
p_cache->smallbuffer.p_stack[l_index].l_size > 2*l_size )
{
p_data->p_buffer = realloc( p_data->p_buffer, l_size );
p_data->l_size = l_size;
}
else
{
p_data->l_size = p_cache->smallbuffer.p_stack[l_index].l_size;
}
}
}
else
{
/* Large buffer */
/* Checks whether the buffer cache is empty */
if( p_cache->largebuffer.l_index == 0 )
{
/* Allocates a new packet */
p_data->p_buffer = malloc( l_size );
if ( p_data->p_buffer == NULL )
{
intf_ErrMsg( "Out of memory" );
free( p_data );
vlc_mutex_unlock( &p_cache->lock );
return NULL;
}
#ifdef TRACE_INPUT
intf_DbgMsg( "PS input: large buffer allocated" );
#endif
p_data->l_size = l_size;
}
else