+ free( p_line );
+ if( s->conv != (vlc_iconv_t)(-1) ) vlc_iconv_close( s->conv );
+ 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;
+ input_thread_t *p_input = NULL;
+ int i_read_orig = i_read;
+ int i_total = 0;
+
+ if( s->p_parent && s->p_parent->p_parent &&
+ s->p_parent->p_parent->i_object_type == VLC_OBJECT_INPUT )
+ p_input = (input_thread_t *)s->p_parent->p_parent;
+
+ if( !p_sys->i_list )
+ {
+ i_read = p_access->pf_read( p_access, p_read, i_read );
+ if( p_access->b_die )
+ vlc_object_kill( s );
+ if( p_input )
+ {
+ vlc_mutex_lock( &p_input->p->counters.counters_lock );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, i_read,
+ &i_total );
+ stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
+ (float)i_total, NULL );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL );
+ vlc_mutex_unlock( &p_input->p->counters.counters_lock );
+ }
+ return i_read;
+ }
+
+ i_read = p_sys->p_list_access->pf_read( p_sys->p_list_access, p_read,
+ i_read );
+ if( p_access->b_die )
+ vlc_object_kill( s );
+
+ /* 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 = access_New( s, p_access->psz_access, "", psz_name );
+
+ if( !p_list_access ) return 0;
+
+ if( p_sys->p_list_access != p_access )
+ access_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 );
+ }
+
+ /* Update read bytes in input */
+ if( p_input )
+ {
+ vlc_mutex_lock( &p_input->p->counters.counters_lock );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, i_read, &i_total );
+ stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
+ (float)i_total, NULL );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL );
+ vlc_mutex_unlock( &p_input->p->counters.counters_lock );
+ }
+ return i_read;
+}
+
+static block_t *AReadBlock( stream_t *s, bool *pb_eof )
+{
+ stream_sys_t *p_sys = s->p_sys;
+ access_t *p_access = p_sys->p_access;
+ input_thread_t *p_input = NULL;
+ block_t *p_block;
+ bool b_eof;
+ int i_total = 0;
+
+ if( s->p_parent && s->p_parent->p_parent &&
+ s->p_parent->p_parent->i_object_type == VLC_OBJECT_INPUT )
+ p_input = (input_thread_t *)s->p_parent->p_parent;
+
+ if( !p_sys->i_list )
+ {
+ p_block = p_access->pf_block( p_access );
+ if( p_access->b_die )
+ vlc_object_kill( s );
+ if( pb_eof ) *pb_eof = p_access->info.b_eof;
+ if( p_input && p_block && libvlc_stats (p_access) )
+ {
+ vlc_mutex_lock( &p_input->p->counters.counters_lock );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_bytes,
+ p_block->i_buffer, &i_total );
+ stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
+ (float)i_total, NULL );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL );
+ vlc_mutex_unlock( &p_input->p->counters.counters_lock );
+ }
+ return p_block;
+ }
+
+ p_block = p_sys->p_list_access->pf_block( p_access );
+ if( p_access->b_die )
+ vlc_object_kill( s );
+ 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 = access_New( s, p_access->psz_access, "", psz_name );
+
+ if( !p_list_access ) return 0;
+
+ if( p_sys->p_list_access != p_access )
+ access_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 );
+ }
+ if( p_block )
+ {
+ if( p_input )
+ {
+ vlc_mutex_lock( &p_input->p->counters.counters_lock );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_bytes,
+ p_block->i_buffer, &i_total );
+ stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
+ (float)i_total, NULL );
+ stats_UpdateInteger( s, p_input->p->counters.p_read_packets,
+ 1 , NULL);
+ vlc_mutex_unlock( &p_input->p->counters.counters_lock );
+ }
+ }
+ 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 =
+ access_New( s, p_access->psz_access, "", psz_name );
+ }
+ 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 )
+ access_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 );
+}
+
+
+/**
+ * Try to read "i_read" bytes into a buffer pointed by "p_read". If
+ * "p_read" is NULL then data are skipped instead of read. The return
+ * value is the real numbers of bytes read/skip. If this value is less
+ * than i_read that means that it's the end of the stream.
+ */
+int stream_Read( stream_t *s, void *p_read, int i_read )
+{
+ return s->pf_read( s, p_read, i_read );
+}
+
+/**
+ * Store in pp_peek a pointer to the next "i_peek" bytes in the stream
+ * \return The real numbers of valid bytes, if it's less
+ * or equal to 0, *pp_peek is invalid.
+ * \note pp_peek is a pointer to internal buffer and it will be invalid as
+ * soons as other stream_* functions are called.
+ * \note Due to input limitation, it could be less than i_peek without meaning
+ * the end of the stream (but only when you have i_peek >=
+ * p_input->i_bufsize)
+ */
+int stream_Peek( stream_t *s, const uint8_t **pp_peek, int i_peek )
+{
+ return s->pf_peek( s, pp_peek, i_peek );
+}
+
+/**
+ * Use to control the "stream_t *". Look at #stream_query_e for
+ * possible "i_query" value and format arguments. Return VLC_SUCCESS
+ * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
+ */
+int stream_vaControl( stream_t *s, int i_query, va_list args )
+{
+ return s->pf_control( s, i_query, args );
+}
+
+/**
+ * Destroy a stream
+ */
+void stream_Delete( stream_t *s )
+{
+ s->pf_destroy( s );
+}
+
+int stream_Control( stream_t *s, int i_query, ... )
+{
+ va_list args;
+ int i_result;
+
+ if ( s == NULL )
+ return VLC_EGENERIC;
+
+ va_start( args, i_query );
+ i_result = s->pf_control( s, i_query, args );
+ va_end( args );
+ return i_result;
+}
+
+/**
+ * Read "i_size" bytes and store them in a block_t.
+ * It always read i_size bytes unless you are at the end of the stream
+ * where it return what is available.
+ */
+block_t *stream_Block( stream_t *s, int i_size )
+{
+ if( i_size <= 0 ) return NULL;
+
+ /* emulate block read */
+ block_t *p_bk = block_New( s, i_size );
+ if( p_bk )
+ {
+ p_bk->i_buffer = stream_Read( s, p_bk->p_buffer, i_size );
+ if( p_bk->i_buffer > 0 )
+ {
+ return p_bk;
+ }
+ }
+ if( p_bk ) block_Release( p_bk );