Commit 26039bc4 authored by Gildas Bazin's avatar Gildas Bazin

* src/libvlc.h, src/input/stream.c: new --input-list option which allows you...

* src/libvlc.h, src/input/stream.c: new --input-list option which allows you to specify a list of inputs which will be concatenated to the main MRL during playback (for splitted streams). Note that this deprecates the --file-cat option which was specific to the file access plugin while the new option should be generic.
parent 7b6d3391
...@@ -91,8 +91,16 @@ typedef struct ...@@ -91,8 +91,16 @@ typedef struct
int64_t i_end; int64_t i_end;
uint8_t *p_buffer; uint8_t *p_buffer;
} stream_track_t; } stream_track_t;
typedef struct
{
char *psz_path;
int64_t i_size;
} access_entry_t;
struct stream_sys_t struct stream_sys_t
{ {
access_t *p_access; access_t *p_access;
...@@ -111,6 +119,7 @@ struct stream_sys_t ...@@ -111,6 +119,7 @@ struct stream_sys_t
int i_size; /* Total amount of data in the list */ int i_size; /* Total amount of data in the list */
block_t *p_first; block_t *p_first;
block_t **pp_last; block_t **pp_last;
} block; } block;
/* Method 2: for pf_read */ /* Method 2: for pf_read */
...@@ -126,6 +135,7 @@ struct stream_sys_t ...@@ -126,6 +135,7 @@ struct stream_sys_t
/* */ /* */
int i_used; /* Used since last read */ int i_used; /* Used since last read */
int i_read_size; int i_read_size;
} stream; } stream;
/* Peek temporary buffer */ /* Peek temporary buffer */
...@@ -145,26 +155,36 @@ struct stream_sys_t ...@@ -145,26 +155,36 @@ struct stream_sys_t
/* Stat about seek */ /* Stat about seek */
int i_seek_count; int i_seek_count;
int64_t i_seek_time; int64_t i_seek_time;
} stat; } stat;
/* Streams list */
int i_list;
access_entry_t **list;
int i_list_index;
access_t *p_list_access;
/* Preparse mode ? */ /* Preparse mode ? */
vlc_bool_t b_quick; vlc_bool_t b_quick;
}; };
/* Method 1: */ /* Method 1: */
static int AStreamReadBlock( stream_t *, void *p_read, int i_read ); static int AStreamReadBlock( stream_t *s, void *p_read, int i_read );
static int AStreamPeekBlock( stream_t *, uint8_t **p_peek, int i_read ); static int AStreamPeekBlock( stream_t *s, uint8_t **p_peek, int i_read );
static int AStreamSeekBlock( stream_t *s, int64_t i_pos ); static int AStreamSeekBlock( stream_t *s, int64_t i_pos );
static void AStreamPrebufferBlock( stream_t * ); static void AStreamPrebufferBlock( stream_t *s );
static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof );
/* Method 2 */ /* Method 2 */
static int AStreamReadStream( stream_t *, void *p_read, int i_read ); static int AStreamReadStream( stream_t *s, void *p_read, int i_read );
static int AStreamPeekStream( stream_t *, uint8_t **pp_peek, int i_read ); static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read );
static int AStreamSeekStream( stream_t *s, int64_t i_pos ); static int AStreamSeekStream( stream_t *s, int64_t i_pos );
static void AStreamPrebufferStream( stream_t * ); static void AStreamPrebufferStream( stream_t *s );
static int AReadStream( stream_t *s, void *p_read, int i_read );
/* Common */ /* Common */
static int AStreamControl( stream_t *, int i_query, va_list ); static int AStreamControl( stream_t *s, int i_query, va_list );
static int ASeek( stream_t *s, int64_t i_pos );
/**************************************************************************** /****************************************************************************
...@@ -174,9 +194,9 @@ stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick ) ...@@ -174,9 +194,9 @@ stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick )
{ {
stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM ); stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM );
stream_sys_t *p_sys; stream_sys_t *p_sys;
char *psz_list;
if( !s ) if( !s ) return NULL;
return NULL;
/* Attach it now, needed for b_die */ /* Attach it now, needed for b_die */
vlc_object_attach( s, p_access ); vlc_object_attach( s, p_access );
...@@ -201,8 +221,61 @@ stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick ) ...@@ -201,8 +221,61 @@ stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick )
p_sys->stat.i_seek_count = 0; p_sys->stat.i_seek_count = 0;
p_sys->stat.i_seek_time = 0; p_sys->stat.i_seek_time = 0;
p_sys->i_list = 0;
p_sys->list = 0;
p_sys->i_list_index = 0;
p_sys->p_list_access = 0;
p_sys->b_quick = b_quick; p_sys->b_quick = b_quick;
/* Get the additional list of inputs if any (for concatenation) */
if( (psz_list = var_CreateGetString( s, "input-list" )) && *psz_list )
{
access_entry_t *p_entry = malloc( sizeof(access_entry_t) );
char *psz_name, *psz_parser = psz_name = psz_list;
p_sys->p_list_access = p_access;
p_entry->i_size = p_access->info.i_size;
p_entry->psz_path = strdup( p_access->psz_path );
TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)",
p_entry->psz_path, p_access->info.i_size );
while( psz_name && *psz_name )
{
psz_parser = strchr( psz_name, ',' );
if( psz_parser ) *psz_parser = 0;
psz_name = strdup( psz_name );
if( psz_name )
{
access_t *p_tmp = access2_New( p_access, p_access->psz_access,
0, psz_name, 0 );
if( !p_tmp )
{
psz_name = psz_parser;
if( psz_name ) psz_name++;
continue;
}
msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)",
psz_name, p_tmp->info.i_size );
p_entry = malloc( sizeof(access_entry_t) );
p_entry->i_size = p_tmp->info.i_size;
p_entry->psz_path = psz_name;
TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
access2_Delete( p_tmp );
}
psz_name = psz_parser;
if( psz_name ) psz_name++;
}
}
if( psz_list ) free( psz_list );
/* Peek */ /* Peek */
p_sys->i_peek = 0; p_sys->i_peek = 0;
p_sys->p_peek = NULL; p_sys->p_peek = NULL;
...@@ -293,18 +366,21 @@ void stream_AccessDelete( stream_t *s ) ...@@ -293,18 +366,21 @@ void stream_AccessDelete( stream_t *s )
vlc_object_detach( s ); vlc_object_detach( s );
if( p_sys->b_block ) if( p_sys->b_block ) block_ChainRelease( p_sys->block.p_first );
{ else free( p_sys->stream.p_buffer );
block_ChainRelease( p_sys->block.p_first );
} if( p_sys->p_peek ) free( p_sys->p_peek );
else
if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access )
access2_Delete( p_sys->p_list_access );
while( p_sys->i_list-- )
{ {
free( p_sys->stream.p_buffer ); free( p_sys->list[p_sys->i_list]->psz_path );
free( p_sys->list[p_sys->i_list] );
if( !p_sys->i_list ) free( p_sys->list );
} }
if( p_sys->p_peek )
free( p_sys->p_peek );
free( s->p_sys ); free( s->p_sys );
vlc_object_destroy( s ); vlc_object_destroy( s );
} }
...@@ -360,7 +436,17 @@ void stream_AccessReset( stream_t *s ) ...@@ -360,7 +436,17 @@ void stream_AccessReset( stream_t *s )
void stream_AccessUpdate( stream_t *s ) void stream_AccessUpdate( stream_t *s )
{ {
stream_sys_t *p_sys = s->p_sys; stream_sys_t *p_sys = s->p_sys;
p_sys->i_pos = p_sys->p_access->info.i_pos; p_sys->i_pos = p_sys->p_access->info.i_pos;
if( p_sys->i_list )
{
int i;
for( i = 0; i < p_sys->i_list_index; i++ )
{
p_sys->i_pos += p_sys->list[i]->i_size;
}
}
} }
/**************************************************************************** /****************************************************************************
...@@ -379,6 +465,14 @@ static int AStreamControl( stream_t *s, int i_query, va_list args ) ...@@ -379,6 +465,14 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
{ {
case STREAM_GET_SIZE: case STREAM_GET_SIZE:
pi_64 = (int64_t*)va_arg( args, int64_t * ); pi_64 = (int64_t*)va_arg( args, int64_t * );
if( s->p_sys->i_list )
{
int i;
*pi_64 = 0;
for( i = 0; i < s->p_sys->i_list; i++ )
*pi_64 += s->p_sys->list[i]->i_size;
break;
}
*pi_64 = p_access->info.i_size; *pi_64 = p_access->info.i_size;
break; break;
...@@ -433,7 +527,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list args ) ...@@ -433,7 +527,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
static void AStreamPrebufferBlock( stream_t *s ) static void AStreamPrebufferBlock( stream_t *s )
{ {
stream_sys_t *p_sys = s->p_sys; stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
int64_t i_first = 0; int64_t i_first = 0;
int64_t i_start; int64_t i_start;
...@@ -443,6 +536,7 @@ static void AStreamPrebufferBlock( stream_t *s ) ...@@ -443,6 +536,7 @@ static void AStreamPrebufferBlock( stream_t *s )
for( ;; ) for( ;; )
{ {
int64_t i_date = mdate(); int64_t i_date = mdate();
vlc_bool_t b_eof;
block_t *b; block_t *b;
if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE || if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
...@@ -465,10 +559,9 @@ static void AStreamPrebufferBlock( stream_t *s ) ...@@ -465,10 +559,9 @@ static void AStreamPrebufferBlock( stream_t *s )
} }
/* Fetch a block */ /* Fetch a block */
if( ( b = p_access->pf_block( p_access ) ) == NULL ) if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
{ {
if( p_access->info.b_eof ) if( b_eof ) break;
break;
msleep( STREAM_DATA_WAIT ); msleep( STREAM_DATA_WAIT );
continue; continue;
...@@ -698,8 +791,7 @@ static int AStreamSeekBlock( stream_t *s, int64_t i_pos ) ...@@ -698,8 +791,7 @@ static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
int64_t i_start, i_end; int64_t i_start, i_end;
/* Do the access seek */ /* Do the access seek */
i_start = mdate(); i_start = mdate();
if( p_access->pf_seek( p_access, i_pos ) ) if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
return VLC_EGENERIC;
i_end = mdate(); i_end = mdate();
/* Release data */ /* Release data */
...@@ -755,7 +847,6 @@ static int AStreamSeekBlock( stream_t *s, int64_t i_pos ) ...@@ -755,7 +847,6 @@ static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
static int AStreamRefillBlock( stream_t *s ) static int AStreamRefillBlock( stream_t *s )
{ {
stream_sys_t *p_sys = s->p_sys; stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
int64_t i_start, i_stop; int64_t i_start, i_stop;
block_t *b; block_t *b;
...@@ -783,16 +874,15 @@ static int AStreamRefillBlock( stream_t *s ) ...@@ -783,16 +874,15 @@ static int AStreamRefillBlock( stream_t *s )
i_start = mdate(); i_start = mdate();
for( ;; ) for( ;; )
{ {
if( s->b_die ) vlc_bool_t b_eof;
return VLC_EGENERIC;
if( s->b_die ) return VLC_EGENERIC;
/* Fetch a block */ /* Fetch a block */
if( ( b = p_access->pf_block( p_access ) ) ) if( ( b = AReadBlock( s, &b_eof ) ) ) break;
break;
if( p_access->info.b_eof ) if( b_eof ) return VLC_EGENERIC;
return VLC_EGENERIC;
msleep( STREAM_DATA_WAIT ); msleep( STREAM_DATA_WAIT );
} }
...@@ -999,8 +1089,7 @@ static int AStreamSeekStream( stream_t *s, int64_t i_pos ) ...@@ -999,8 +1089,7 @@ static int AStreamSeekStream( stream_t *s, int64_t i_pos )
" end="I64Fd, i, tk->i_start, tk->i_end ); " end="I64Fd, i, tk->i_start, tk->i_end );
#endif #endif
/* Seek at the end of the buffer */ /* Seek at the end of the buffer */
if( p_access->pf_seek( p_access, tk->i_end ) ) if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
return VLC_EGENERIC;
/* That's it */ /* That's it */
p_sys->i_pos = i_pos; p_sys->i_pos = i_pos;
...@@ -1042,8 +1131,7 @@ static int AStreamSeekStream( stream_t *s, int64_t i_pos ) ...@@ -1042,8 +1131,7 @@ static int AStreamSeekStream( stream_t *s, int64_t i_pos )
#endif #endif
/* Nothing good, seek and choose oldest segment */ /* Nothing good, seek and choose oldest segment */
if( p_access->pf_seek( p_access, i_pos ) ) if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
return VLC_EGENERIC;
p_sys->i_pos = i_pos; p_sys->i_pos = i_pos;
i_new = 0; i_new = 0;
...@@ -1072,7 +1160,6 @@ static int AStreamSeekStream( stream_t *s, int64_t i_pos ) ...@@ -1072,7 +1160,6 @@ static int AStreamSeekStream( stream_t *s, int64_t i_pos )
static int AStreamRefillStream( stream_t *s ) static int AStreamRefillStream( stream_t *s )
{ {
stream_sys_t *p_sys = s->p_sys; stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
/* We read but won't increase i_start after initial start + offset */ /* We read but won't increase i_start after initial start + offset */
...@@ -1096,7 +1183,7 @@ static int AStreamRefillStream( stream_t *s ) ...@@ -1096,7 +1183,7 @@ static int AStreamRefillStream( stream_t *s )
return VLC_EGENERIC; return VLC_EGENERIC;
i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off ); i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off );
i_read = p_access->pf_read( p_access, &tk->p_buffer[i_off], i_read ); i_read = AReadStream( s, &tk->p_buffer[i_off], i_read );
/* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */ /* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */
if( i_read < 0 ) if( i_read < 0 )
...@@ -1106,8 +1193,7 @@ static int AStreamRefillStream( stream_t *s ) ...@@ -1106,8 +1193,7 @@ static int AStreamRefillStream( stream_t *s )
} }
else if( i_read == 0 ) else if( i_read == 0 )
{ {
if( !b_read ) if( !b_read ) return VLC_EGENERIC;
return VLC_EGENERIC;
return VLC_SUCCESS; return VLC_SUCCESS;
} }
b_read = VLC_TRUE; b_read = VLC_TRUE;
...@@ -1145,8 +1231,8 @@ static void AStreamPrebufferStream( stream_t *s ) ...@@ -1145,8 +1231,8 @@ static void AStreamPrebufferStream( stream_t *s )
int64_t i_first = 0; int64_t i_first = 0;
int64_t i_start; int64_t i_start;
int64_t i_prebuffer = p_sys->b_quick ? STREAM_CACHE_TRACK_SIZE /100 : int64_t i_prebuffer = p_sys->b_quick ? STREAM_CACHE_TRACK_SIZE /100 :
((s->p_sys->p_access->info.i_title > 1 || ( (p_access->info.i_title > 1 || p_access->info.i_seekpoint > 1) ?
s->p_sys->p_access->info.i_seekpoint > 1) ? STREAM_CACHE_PREBUFFER_SIZE : STREAM_CACHE_TRACK_SIZE / 3); STREAM_CACHE_PREBUFFER_SIZE : STREAM_CACHE_TRACK_SIZE / 3 );
msg_Dbg( s, "pre buffering" ); msg_Dbg( s, "pre buffering" );
i_start = mdate(); i_start = mdate();
...@@ -1179,8 +1265,7 @@ static void AStreamPrebufferStream( stream_t *s ) ...@@ -1179,8 +1265,7 @@ static void AStreamPrebufferStream( stream_t *s )
/* */ /* */
i_read = STREAM_CACHE_TRACK_SIZE - tk->i_end; i_read = STREAM_CACHE_TRACK_SIZE - tk->i_end;
i_read = __MIN( p_sys->stream.i_read_size, i_read ); i_read = __MIN( p_sys->stream.i_read_size, i_read );
i_read = p_access->pf_read( p_access, &tk->p_buffer[tk->i_end], i_read = AReadStream( s, &tk->p_buffer[tk->i_end], i_read );
i_read );
if( i_read < 0 ) if( i_read < 0 )
{ {
msleep( STREAM_DATA_WAIT ); msleep( STREAM_DATA_WAIT );
...@@ -1266,3 +1351,136 @@ char *stream_ReadLine( stream_t *s ) ...@@ -1266,3 +1351,136 @@ char *stream_ReadLine( stream_t *s )
if( p_line ) free( p_line ); if( p_line ) free( p_line );
return NULL; return NULL;
} }
/****************************************************************************
* Access reading/seeking wrappers to handle concatenated streams.
****************************************************************************/
static int AReadStream( stream_t *s, void *p_read, int i_read )
{
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
int i_read_orig = i_read;
if( !p_sys->i_list )
{
i_read = p_access->pf_read( p_access, p_read, i_read );
return i_read;
}
i_read = p_sys->p_list_access->pf_read( p_sys->p_list_access, p_read,
i_read );
/* If we reached an EOF then switch to the next stream in the list */
if( i_read == 0 && p_sys->i_list_index + 1 < p_sys->i_list )
{
char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path;
access_t *p_list_access;
msg_Dbg( s, "opening input `%s'", psz_name );
p_list_access = access2_New( s, p_access->psz_access, 0, psz_name, 0 );
if( !p_list_access ) return 0;
if( p_sys->p_list_access != p_access )
access2_Delete( p_sys->p_list_access );
p_sys->p_list_access = p_list_access;
/* We have to read some data */
return AReadStream( s, p_read, i_read_orig );
}
return i_read;
}
static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof )
{
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
block_t *p_block;
vlc_bool_t b_eof;
if( !p_sys->i_list )
{
p_block = p_access->pf_block( p_access );
if( pb_eof ) *pb_eof = p_access->info.b_eof;
return p_block;
}
p_block = p_sys->p_list_access->pf_block( p_access );
b_eof = p_sys->p_list_access->info.b_eof;
if( pb_eof ) *pb_eof = b_eof;
/* If we reached an EOF then switch to the next stream in the list */
if( !p_block && b_eof && p_sys->i_list_index + 1 < p_sys->i_list )
{
char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path;
access_t *p_list_access;
msg_Dbg( s, "opening input `%s'", psz_name );
p_list_access = access2_New( s, p_access->psz_access, 0, psz_name, 0 );
if( !p_list_access ) return 0;
if( p_sys->p_list_access != p_access )
access2_Delete( p_sys->p_list_access );
p_sys->p_list_access = p_list_access;
/* We have to read some data */
return AReadBlock( s, pb_eof );
}
return p_block;
}
static int ASeek( stream_t *s, int64_t i_pos )
{
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
/* Check which stream we need to access */
if( p_sys->i_list )
{
int i;
char *psz_name;
int64_t i_size = 0;
access_t *p_list_access = 0;
for( i = 0; i < p_sys->i_list - 1; i++ )
{
if( i_pos < p_sys->list[i]->i_size + i_size ) break;
i_size += p_sys->list[i]->i_size;
}
psz_name = p_sys->list[i]->psz_path;
if( i != p_sys->i_list_index )
msg_Dbg( s, "opening input `%s'", psz_name );
if( i != p_sys->i_list_index && i != 0 )
{
p_list_access =
access2_New( s, p_access->psz_access, 0, psz_name, 0 );
}
else if( i != p_sys->i_list_index )
{
p_list_access = p_access;
}
if( p_list_access )
{
if( p_sys->p_list_access != p_access )
access2_Delete( p_sys->p_list_access );
p_sys->p_list_access = p_list_access;
}
p_sys->i_list_index = i;
return p_sys->p_list_access->pf_seek( p_sys->p_list_access,
i_pos - i_size );
}
return p_access->pf_seek( p_access, i_pos );
}
...@@ -336,6 +336,10 @@ static char *ppsz_align_descriptions[] = ...@@ -336,6 +336,10 @@ static char *ppsz_align_descriptions[] =
#define STOP_TIME_TEXT N_("Input stop time (seconds)") #define STOP_TIME_TEXT N_("Input stop time (seconds)")
#define STOP_TIME_LONGTEXT N_("Input stop time (seconds)") #define STOP_TIME_LONGTEXT N_("Input stop time (seconds)")
#define INPUT_LIST_TEXT N_("Input list")
#define INPUT_LIST_LONGTEXT N_("Allows you to specify a coma separated list " \
"of inputs that will be concatenated to the main MRL when playing.")
#define INPUT_SLAVE_TEXT N_("Input slave (experimental)") #define INPUT_SLAVE_TEXT N_("Input slave (experimental)")
#define INPUT_SLAVE_LONGTEXT N_("Input slave (experimental)") #define INPUT_SLAVE_LONGTEXT N_("Input slave (experimental)")
...@@ -981,6 +985,8 @@ vlc_module_begin(); ...@@ -981,6 +985,8 @@ vlc_module_begin();
START_TIME_TEXT, START_TIME_LONGTEXT, VLC_TRUE ); START_TIME_TEXT, START_TIME_LONGTEXT, VLC_TRUE );
add_integer( "stop-time", 0, NULL, add_integer( "stop-time", 0, NULL,
STOP_TIME_TEXT, STOP_TIME_LONGTEXT, VLC_TRUE ); STOP_TIME_TEXT, STOP_TIME_LONGTEXT, VLC_TRUE );
add_string( "input-list", NULL, NULL,
INPUT_LIST_TEXT, INPUT_LIST_LONGTEXT, VLC_TRUE );
add_string( "input-slave", NULL, NULL, add_string( "input-slave", NULL, NULL,
INPUT_SLAVE_TEXT, INPUT_SLAVE_LONGTEXT, VLC_TRUE ); INPUT_SLAVE_TEXT, INPUT_SLAVE_LONGTEXT, VLC_TRUE );
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment