added a stream_UrlNew to open a general purpos stream_t from a url.
[vlc.git] / src / input / stream.c
1 /*****************************************************************************
2  * stream.c
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #include <stdlib.h>
25 #include <vlc/vlc.h>
26 #include <vlc/input.h>
27
28 #include "input_internal.h"
29
30 /* TODO:
31  *  - tune the 2 methods
32  *  - compute cost for seek
33  *  - improve stream mode seeking with closest segments
34  *  - ...
35  */
36
37 /* Two methods:
38  *  - using pf_block
39  *      One linked list of data read
40  *  - using pf_read
41  *      More complex scheme using mutliple track to avoid seeking
42  */
43
44 /* How many tracks we have, currently only used for stream mode */
45 #ifdef OPTIMIZE_MEMORY
46 #   define STREAM_CACHE_TRACK 1
47     /* Max size of our cache 128Ko per track */
48 #   define STREAM_CACHE_SIZE  (STREAM_CACHE_TRACK*1024*128)
49 #else
50 #   define STREAM_CACHE_TRACK 3
51     /* Max size of our cache 4Mo per track */
52 #   define STREAM_CACHE_SIZE  (4*STREAM_CACHE_TRACK*1024*1024)
53 #endif
54
55 /* How many data we try to prebuffer */
56 #define STREAM_CACHE_PREBUFFER_SIZE (32767)
57 /* Maximum time we take to pre-buffer */
58 #define STREAM_CACHE_PREBUFFER_LENGTH (100*1000)
59
60 /* Method1: Simple, for pf_block.
61  *  We get blocks and put them in the linked list.
62  *  We release blocks once the total size is bigger than CACHE_BLOCK_SIZE
63  */
64 #define STREAM_DATA_WAIT 40000       /* Time between before a pf_block retry */
65
66 /* Method2: A bit more complex, for pf_read
67  *  - We use ring buffers, only one if unseekable, all if seekable
68  *  - Upon seek date current ring, then search if one ring match the pos,
69  *      yes: switch to it, seek the access to match the end of the ring
70  *      no: search the ring with i_end the closer to i_pos,
71  *          if close enough, read data and use this ring
72  *          else use the oldest ring, seek and use it.
73  *
74  *  TODO: - with access non seekable: use all space available for only one ring, but
75  *          we have to support seekable/non-seekable switch on the fly.
76  *        - compute a good value for i_read_size
77  *        - ?
78  */
79 #define STREAM_READ_ATONCE 32767
80 #define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)
81
82 typedef struct
83 {
84     int64_t i_date;
85
86     int64_t i_start;
87     int64_t i_end;
88
89     uint8_t *p_buffer;
90
91 } stream_track_t;
92
93 typedef struct
94 {
95     char     *psz_path;
96     int64_t  i_size;
97
98 } access_entry_t;
99
100 struct stream_sys_t
101 {
102     access_t    *p_access;
103
104     vlc_bool_t  b_block;    /* Block method (1) or stream */
105
106     int64_t     i_pos;      /* Current reading offset */
107
108     /* Method 1: pf_block */
109     struct
110     {
111         int64_t i_start;        /* Offset of block for p_first */
112         int     i_offset;       /* Offset for data in p_current */
113         block_t *p_current;     /* Current block */
114
115         int     i_size;         /* Total amount of data in the list */
116         block_t *p_first;
117         block_t **pp_last;
118
119     } block;
120
121     /* Method 2: for pf_read */
122     struct
123     {
124         int i_offset;   /* Buffer offset in the current track */
125         int i_tk;       /* Current track */
126         stream_track_t tk[STREAM_CACHE_TRACK];
127
128         /* Global buffer */
129         uint8_t *p_buffer;
130
131         /* */
132         int i_used; /* Used since last read */
133         int i_read_size;
134
135     } stream;
136
137     /* Peek temporary buffer */
138     int     i_peek;
139     uint8_t *p_peek;
140
141     /* Stat for both method */
142     struct
143     {
144         vlc_bool_t b_fastseek;  /* From access */
145
146         /* Stat about reading data */
147         int64_t i_read_count;
148         int64_t i_bytes;
149         int64_t i_read_time;
150
151         /* Stat about seek */
152         int     i_seek_count;
153         int64_t i_seek_time;
154
155     } stat;
156
157     /* Streams list */
158     int            i_list;
159     access_entry_t **list;
160     int            i_list_index;
161     access_t       *p_list_access;
162
163     /* Preparse mode ? */
164     vlc_bool_t b_quick;
165 };
166
167 /* Method 1: */
168 static int  AStreamReadBlock( stream_t *s, void *p_read, int i_read );
169 static int  AStreamPeekBlock( stream_t *s, uint8_t **p_peek, int i_read );
170 static int  AStreamSeekBlock( stream_t *s, int64_t i_pos );
171 static void AStreamPrebufferBlock( stream_t *s );
172 static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof );
173
174 /* Method 2 */
175 static int  AStreamReadStream( stream_t *s, void *p_read, int i_read );
176 static int  AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read );
177 static int  AStreamSeekStream( stream_t *s, int64_t i_pos );
178 static void AStreamPrebufferStream( stream_t *s );
179 static int  AReadStream( stream_t *s, void *p_read, int i_read );
180
181 /* Common */
182 static int AStreamControl( stream_t *s, int i_query, va_list );
183 static void AStreamDestroy( stream_t *s );
184 static void UStreamDestroy( stream_t *s );
185 static int  ASeek( stream_t *s, int64_t i_pos );
186
187
188 /****************************************************************************
189  * stream_AccessNew: create a stream from a access
190  ****************************************************************************/
191 stream_t *__stream_UrlNew( vlc_object_t *p_parent, char *psz_url )
192 {
193     char *psz_access, *psz_demux, *psz_path;
194     access_t *p_access;
195     stream_t *p_res;
196     
197     MRLSplit( p_parent, psz_url, &psz_access, &psz_demux, &psz_path );
198     
199     /* Now try a real access */
200     p_access = access2_New( p_parent, psz_access, NULL,
201                             psz_path, VLC_TRUE );
202
203     if( p_access == NULL )
204     {
205         msg_Err( p_parent, "no suitable access module for `%s'", psz_url );
206         return NULL;
207     }
208     p_res = stream_AccessNew( p_access, VLC_TRUE );
209     if( p_res )
210     {
211         p_res->pf_destroy = UStreamDestroy;
212         return p_res;
213     }
214     else
215     {
216         access2_Delete( p_access );
217     }
218 }
219
220 stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick )
221 {
222     stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM );
223     stream_sys_t *p_sys;
224     char *psz_list;
225
226     if( !s ) return NULL;
227
228     /* Attach it now, needed for b_die */
229     vlc_object_attach( s, p_access );
230
231     s->pf_block  = NULL;
232     s->pf_read   = NULL;    /* Set up later */
233     s->pf_peek   = NULL;
234     s->pf_control= AStreamControl;
235     s->pf_destroy = AStreamDestroy;
236
237     s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) );
238
239     /* Common field */
240     p_sys->p_access = p_access;
241     p_sys->b_block = p_access->pf_block ? VLC_TRUE : VLC_FALSE;
242     p_sys->i_pos = p_access->info.i_pos;
243
244     /* Stats */
245     access2_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek );
246     p_sys->stat.i_bytes = 0;
247     p_sys->stat.i_read_time = 0;
248     p_sys->stat.i_read_count = 0;
249     p_sys->stat.i_seek_count = 0;
250     p_sys->stat.i_seek_time = 0;
251
252     p_sys->i_list = 0;
253     p_sys->list = 0;
254     p_sys->i_list_index = 0;
255     p_sys->p_list_access = 0;
256
257     p_sys->b_quick = b_quick;
258
259     /* Get the additional list of inputs if any (for concatenation) */
260     if( (psz_list = var_CreateGetString( s, "input-list" )) && *psz_list )
261     {
262         access_entry_t *p_entry = malloc( sizeof(access_entry_t) );
263         char *psz_name, *psz_parser = psz_name = psz_list;
264
265         p_sys->p_list_access = p_access;
266         p_entry->i_size = p_access->info.i_size;
267         p_entry->psz_path = strdup( p_access->psz_path );
268         TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
269         msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)",
270                  p_entry->psz_path, p_access->info.i_size );
271
272         while( psz_name && *psz_name )
273         {
274             psz_parser = strchr( psz_name, ',' );
275             if( psz_parser ) *psz_parser = 0;
276
277             psz_name = strdup( psz_name );
278             if( psz_name )
279             {
280                 access_t *p_tmp = access2_New( p_access, p_access->psz_access,
281                                                0, psz_name, 0 );
282
283                 if( !p_tmp )
284                 {
285                     psz_name = psz_parser;
286                     if( psz_name ) psz_name++;
287                     continue;
288                 }
289
290                 msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)",
291                          psz_name, p_tmp->info.i_size );
292
293                 p_entry = malloc( sizeof(access_entry_t) );
294                 p_entry->i_size = p_tmp->info.i_size;
295                 p_entry->psz_path = psz_name;
296                 TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
297
298                 access2_Delete( p_tmp );
299             }
300
301             psz_name = psz_parser;
302             if( psz_name ) psz_name++;
303         }
304     }
305     if( psz_list ) free( psz_list );
306
307     /* Peek */
308     p_sys->i_peek = 0;
309     p_sys->p_peek = NULL;
310
311     if( p_sys->b_block )
312     {
313         s->pf_read = AStreamReadBlock;
314         s->pf_peek = AStreamPeekBlock;
315
316         /* Init all fields of p_sys->block */
317         p_sys->block.i_start = p_sys->i_pos;
318         p_sys->block.i_offset = 0;
319         p_sys->block.p_current = NULL;
320         p_sys->block.i_size = 0;
321         p_sys->block.p_first = NULL;
322         p_sys->block.pp_last = &p_sys->block.p_first;
323
324         /* Do the prebuffering */
325         AStreamPrebufferBlock( s );
326
327         if( p_sys->block.i_size <= 0 )
328         {
329             msg_Err( s, "cannot pre fill buffer" );
330             goto error;
331         }
332     }
333     else
334     {
335         int i;
336
337         s->pf_read = AStreamReadStream;
338         s->pf_peek = AStreamPeekStream;
339
340         /* Allocate/Setup our tracks */
341         p_sys->stream.i_offset = 0;
342         p_sys->stream.i_tk     = 0;
343         p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE );
344         p_sys->stream.i_used   = 0;
345         access2_Control( p_access, ACCESS_GET_MTU,
346                          &p_sys->stream.i_read_size );
347         if( p_sys->stream.i_read_size <= 0 )
348             p_sys->stream.i_read_size = STREAM_READ_ATONCE;
349         else if( p_sys->stream.i_read_size <= 256 )
350             p_sys->stream.i_read_size = 256;
351
352         for( i = 0; i < STREAM_CACHE_TRACK; i++ )
353         {
354             p_sys->stream.tk[i].i_date  = 0;
355             p_sys->stream.tk[i].i_start = p_sys->i_pos;
356             p_sys->stream.tk[i].i_end   = p_sys->i_pos;
357             p_sys->stream.tk[i].p_buffer=
358                 &p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE];
359         }
360
361         /* Do the prebuffering */
362         AStreamPrebufferStream( s );
363
364         if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
365         {
366             msg_Err( s, "cannot pre fill buffer" );
367             goto error;
368         }
369     }
370
371     return s;
372
373 error:
374     if( p_sys->b_block )
375     {
376         /* Nothing yet */
377     }
378     else
379     {
380         free( p_sys->stream.p_buffer );
381     }
382     free( s->p_sys );
383     vlc_object_detach( s );
384     vlc_object_destroy( s );
385     return NULL;
386 }
387
388 /****************************************************************************
389  * AStreamDestroy:
390  ****************************************************************************/
391 static void AStreamDestroy( stream_t *s )
392 {
393     stream_sys_t *p_sys = s->p_sys;
394
395     vlc_object_detach( s );
396
397     if( p_sys->b_block ) block_ChainRelease( p_sys->block.p_first );
398     else free( p_sys->stream.p_buffer );
399
400     if( p_sys->p_peek ) free( p_sys->p_peek );
401
402     if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access )
403         access2_Delete( p_sys->p_list_access );
404
405     while( p_sys->i_list-- )
406     {
407         free( p_sys->list[p_sys->i_list]->psz_path );
408         free( p_sys->list[p_sys->i_list] );
409         if( !p_sys->i_list ) free( p_sys->list );
410     }
411
412     free( s->p_sys );
413     vlc_object_destroy( s );
414 }
415
416 static void UStreamDestroy( stream_t *s )
417 {
418     access_t *p_access = (access_t*)vlc_object_find( s, VLC_OBJECT_ACCESS, FIND_PARENT );
419     AStreamDestroy( s );
420     access2_Delete( p_access );
421 }
422
423 /****************************************************************************
424  * stream_AccessReset:
425  ****************************************************************************/
426 void stream_AccessReset( stream_t *s )
427 {
428     stream_sys_t *p_sys = s->p_sys;
429
430     p_sys->i_pos = p_sys->p_access->info.i_pos;
431
432     if( p_sys->b_block )
433     {
434         block_ChainRelease( p_sys->block.p_first );
435
436         /* Init all fields of p_sys->block */
437         p_sys->block.i_start = p_sys->i_pos;
438         p_sys->block.i_offset = 0;
439         p_sys->block.p_current = NULL;
440         p_sys->block.i_size = 0;
441         p_sys->block.p_first = NULL;
442         p_sys->block.pp_last = &p_sys->block.p_first;
443
444         /* Do the prebuffering */
445         AStreamPrebufferBlock( s );
446     }
447     else
448     {
449         int i;
450
451         /* Setup our tracks */
452         p_sys->stream.i_offset = 0;
453         p_sys->stream.i_tk     = 0;
454         p_sys->stream.i_used   = 0;
455
456         for( i = 0; i < STREAM_CACHE_TRACK; i++ )
457         {
458             p_sys->stream.tk[i].i_date  = 0;
459             p_sys->stream.tk[i].i_start = p_sys->i_pos;
460             p_sys->stream.tk[i].i_end   = p_sys->i_pos;
461         }
462
463         /* Do the prebuffering */
464         AStreamPrebufferStream( s );
465     }
466 }
467
468 /****************************************************************************
469  * stream_AccessUpdate:
470  ****************************************************************************/
471 void stream_AccessUpdate( stream_t *s )
472 {
473     stream_sys_t *p_sys = s->p_sys;
474
475     p_sys->i_pos = p_sys->p_access->info.i_pos;
476
477     if( p_sys->i_list )
478     {
479         int i;
480         for( i = 0; i < p_sys->i_list_index; i++ )
481         {
482             p_sys->i_pos += p_sys->list[i]->i_size;
483         }
484     }
485 }
486
487 /****************************************************************************
488  * AStreamControl:
489  ****************************************************************************/
490 static int AStreamControl( stream_t *s, int i_query, va_list args )
491 {
492     stream_sys_t *p_sys = s->p_sys;
493     access_t     *p_access = p_sys->p_access;
494
495     vlc_bool_t *p_bool;
496     int64_t    *pi_64, i_64;
497     int        i_int;
498
499     switch( i_query )
500     {
501         case STREAM_GET_SIZE:
502             pi_64 = (int64_t*)va_arg( args, int64_t * );
503             if( s->p_sys->i_list )
504             {
505                 int i;
506                 *pi_64 = 0;
507                 for( i = 0; i < s->p_sys->i_list; i++ )
508                     *pi_64 += s->p_sys->list[i]->i_size;
509                 break;
510             }
511             *pi_64 = p_access->info.i_size;
512             break;
513
514         case STREAM_CAN_SEEK:
515             p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
516             access2_Control( p_access, ACCESS_CAN_SEEK, p_bool );
517             break;
518
519         case STREAM_CAN_FASTSEEK:
520             p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
521             access2_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
522             break;
523
524         case STREAM_GET_POSITION:
525             pi_64 = (int64_t*)va_arg( args, int64_t * );
526             *pi_64 = p_sys->i_pos;
527             break;
528
529         case STREAM_SET_POSITION:
530             i_64 = (int64_t)va_arg( args, int64_t );
531             if( p_sys->b_block )
532                 return AStreamSeekBlock( s, i_64 );
533             else
534                 return AStreamSeekStream( s, i_64 );
535
536         case STREAM_GET_MTU:
537             return VLC_EGENERIC;
538
539         case STREAM_CONTROL_ACCESS:
540             i_int = (int) va_arg( args, int );
541             if( i_int != ACCESS_SET_PRIVATE_ID_STATE &&
542                 i_int != ACCESS_SET_PRIVATE_ID_CA &&
543                 i_int != ACCESS_GET_PRIVATE_ID_STATE )
544             {
545                 msg_Err( s, "Hey, what are you thinking ?"
546                             "DON'T USE STREAM_CONTROL_ACCESS !!!" );
547                 return VLC_EGENERIC;
548             }
549             return access2_vaControl( p_access, i_int, args );
550
551         default:
552             msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
553             return VLC_EGENERIC;
554     }
555     return VLC_SUCCESS;
556 }
557
558
559
560 /****************************************************************************
561  * Method 1:
562  ****************************************************************************/
563 static void AStreamPrebufferBlock( stream_t *s )
564 {
565     stream_sys_t *p_sys = s->p_sys;
566
567     int64_t i_first = 0;
568     int64_t i_start;
569
570     msg_Dbg( s, "pre buffering" );
571     i_start = mdate();
572     for( ;; )
573     {
574         int64_t i_date = mdate();
575         vlc_bool_t b_eof;
576         block_t *b;
577
578         if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
579             ( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) )
580         {
581             int64_t i_byterate;
582
583             /* Update stat */
584             p_sys->stat.i_bytes = p_sys->block.i_size;
585             p_sys->stat.i_read_time = i_date - i_start;
586             i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) /
587                          (p_sys->stat.i_read_time + 1);
588
589             msg_Dbg( s, "prebuffering done "I64Fd" bytes in "I64Fd"s - "
590                      I64Fd" kbytes/s",
591                      p_sys->stat.i_bytes,
592                      p_sys->stat.i_read_time / I64C(1000000),
593                      i_byterate / 1024 );
594             break;
595         }
596
597         /* Fetch a block */
598         if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
599         {
600             if( b_eof ) break;
601
602             msleep( STREAM_DATA_WAIT );
603             continue;
604         }
605
606         if( i_first == 0 )
607         {
608             i_first = mdate();
609             msg_Dbg( s, "received first data for our buffer");
610         }
611
612         /* Append the block */
613         p_sys->block.i_size += b->i_buffer;
614         *p_sys->block.pp_last = b;
615         p_sys->block.pp_last = &b->p_next;
616
617         p_sys->stat.i_read_count++;
618     }
619
620     p_sys->block.p_current = p_sys->block.p_first;
621 }
622
623 static int AStreamRefillBlock( stream_t *s );
624
625 static int AStreamReadBlock( stream_t *s, void *p_read, int i_read )
626 {
627     stream_sys_t *p_sys = s->p_sys;
628
629     uint8_t *p_data= (uint8_t*)p_read;
630     int     i_data = 0;
631
632     /* It means EOF */
633     if( p_sys->block.p_current == NULL )
634         return 0;
635
636     if( p_read == NULL )
637     {
638         /* seek within this stream if possible, else use plain old read and discard */
639         stream_sys_t *p_sys = s->p_sys;
640         access_t     *p_access = p_sys->p_access;
641         vlc_bool_t   b_aseek;
642         access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
643         if( b_aseek )
644             return AStreamSeekBlock( s, p_sys->i_pos + i_read ) ? 0 : i_read;
645     }
646
647     while( i_data < i_read )
648     {
649         int i_current =
650             p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
651         int i_copy = __MIN( i_current, i_read - i_data);
652
653         /* Copy data */
654         if( p_data )
655         {
656             memcpy( p_data,
657                     &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
658                     i_copy );
659             p_data += i_copy;
660         }
661         i_data += i_copy;
662
663         p_sys->block.i_offset += i_copy;
664         if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer )
665         {
666             /* Current block is now empty, switch to next */
667             if( p_sys->block.p_current )
668             {
669                 p_sys->block.i_offset = 0;
670                 p_sys->block.p_current = p_sys->block.p_current->p_next;
671             }
672             /*Get a new block */
673             if( AStreamRefillBlock( s ) )
674             {
675                 break;
676             }
677         }
678     }
679
680     p_sys->i_pos += i_data;
681     return i_data;
682 }
683
684 static int AStreamPeekBlock( stream_t *s, uint8_t **pp_peek, int i_read )
685 {
686     stream_sys_t *p_sys = s->p_sys;
687     uint8_t *p_data;
688     int      i_data = 0;
689     block_t *b;
690     int      i_offset;
691
692     if( p_sys->block.p_current == NULL ) return 0; /* EOF */
693
694     /* We can directly give a pointer over our buffer */
695     if( i_read <= p_sys->block.p_current->i_buffer - p_sys->block.i_offset )
696     {
697         *pp_peek = &p_sys->block.p_current->p_buffer[p_sys->block.i_offset];
698         return i_read;
699     }
700
701     /* We need to create a local copy */
702     if( p_sys->i_peek < i_read )
703     {
704         p_sys->p_peek = realloc( p_sys->p_peek, i_read );
705         if( !p_sys->p_peek )
706         {
707             p_sys->i_peek = 0;
708             return 0;
709         }
710         p_sys->i_peek = i_read;
711     }
712
713     /* Fill enough data */
714     while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
715            < i_read )
716     {
717         block_t **pp_last = p_sys->block.pp_last;
718
719         if( AStreamRefillBlock( s ) ) break;
720
721         /* Our buffer are probably filled enough, don't try anymore */
722         if( pp_last == p_sys->block.pp_last ) break;
723     }
724
725     /* Copy what we have */
726     b = p_sys->block.p_current;
727     i_offset = p_sys->block.i_offset;
728     p_data = p_sys->p_peek;
729
730     while( b && i_data < i_read )
731     {
732         int i_current = b->i_buffer - i_offset;
733         int i_copy = __MIN( i_current, i_read - i_data );
734
735         memcpy( p_data, &b->p_buffer[i_offset], i_copy );
736         i_data += i_copy;
737         p_data += i_copy;
738         i_offset += i_copy;
739
740         if( i_offset >= b->i_buffer )
741         {
742             i_offset = 0;
743             b = b->p_next;
744         }
745     }
746
747     *pp_peek = p_sys->p_peek;
748     return i_data;
749 }
750
751 static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
752 {
753     stream_sys_t *p_sys = s->p_sys;
754     access_t   *p_access = p_sys->p_access;
755     int64_t    i_offset = i_pos - p_sys->block.i_start;
756     vlc_bool_t b_seek;
757
758     /* We already have thoses data, just update p_current/i_offset */
759     if( i_offset >= 0 && i_offset < p_sys->block.i_size )
760     {
761         block_t *b = p_sys->block.p_first;
762         int i_current = 0;
763
764         while( i_current + b->i_buffer < i_offset )
765         {
766             i_current += b->i_buffer;
767             b = b->p_next;
768         }
769
770         p_sys->block.p_current = b;
771         p_sys->block.i_offset = i_offset - i_current;
772
773         p_sys->i_pos = i_pos;
774
775         return VLC_SUCCESS;
776     }
777
778     /* We may need to seek or to read data */
779     if( i_offset < 0 )
780     {
781         vlc_bool_t b_aseek;
782         access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
783
784         if( !b_aseek )
785         {
786             msg_Err( s, "backward seek impossible (access non seekable)" );
787             return VLC_EGENERIC;
788         }
789
790         b_seek = VLC_TRUE;
791     }
792     else
793     {
794         vlc_bool_t b_aseek, b_aseekfast;
795
796         access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
797         access2_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast );
798
799         if( !b_aseek )
800         {
801             b_seek = VLC_FALSE;
802             msg_Warn( s, I64Fd" bytes need to be skipped "
803                       "(access non seekable)",
804                       i_offset - p_sys->block.i_size );
805         }
806         else
807         {
808             int64_t i_skip = i_offset - p_sys->block.i_size;
809
810             /* Avg bytes per packets */
811             int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count;
812             /* TODO compute a seek cost instead of fixed threshold */
813             int i_th = b_aseekfast ? 1 : 5;
814
815             if( i_skip <= i_th * i_avg &&
816                 i_skip < STREAM_CACHE_SIZE )
817                 b_seek = VLC_FALSE;
818             else
819                 b_seek = VLC_TRUE;
820
821             msg_Dbg( s, "b_seek=%d th*avg=%d skip="I64Fd,
822                      b_seek, i_th*i_avg, i_skip );
823         }
824     }
825
826     if( b_seek )
827     {
828         int64_t i_start, i_end;
829         /* Do the access seek */
830         i_start = mdate();
831         if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
832         i_end = mdate();
833
834         /* Release data */
835         block_ChainRelease( p_sys->block.p_first );
836
837         /* Reinit */
838         p_sys->block.i_start = p_sys->i_pos = i_pos;
839         p_sys->block.i_offset = 0;
840         p_sys->block.p_current = NULL;
841         p_sys->block.i_size = 0;
842         p_sys->block.p_first = NULL;
843         p_sys->block.pp_last = &p_sys->block.p_first;
844
845         /* Refill a block */
846         if( AStreamRefillBlock( s ) )
847         {
848             msg_Err( s, "cannot re fill buffer" );
849             return VLC_EGENERIC;
850         }
851         /* Update stat */
852         p_sys->stat.i_seek_time += i_end - i_start;
853         p_sys->stat.i_seek_count++;
854         return VLC_SUCCESS;
855     }
856     else
857     {
858         /* Read enought data */
859         while( p_sys->block.i_start + p_sys->block.i_size < i_pos )
860         {
861             if( AStreamRefillBlock( s ) )
862             {
863                 msg_Err( s, "can't read enough data in seek" );
864                 return VLC_EGENERIC;
865             }
866             while( p_sys->block.p_current &&
867                    p_sys->i_pos + p_sys->block.p_current->i_buffer < i_pos )
868             {
869                 p_sys->i_pos += p_sys->block.p_current->i_buffer;
870                 p_sys->block.p_current = p_sys->block.p_current->p_next;
871             }
872         }
873
874         p_sys->block.i_offset = i_pos - p_sys->i_pos;
875         p_sys->i_pos = i_pos;
876
877         /* TODO read data */
878         return VLC_SUCCESS;
879     }
880
881     return VLC_EGENERIC;
882 }
883
884 static int AStreamRefillBlock( stream_t *s )
885 {
886     stream_sys_t *p_sys = s->p_sys;
887     int64_t      i_start, i_stop;
888     block_t      *b;
889
890     /* Release data */
891     while( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
892            p_sys->block.p_first != p_sys->block.p_current )
893     {
894         block_t *b = p_sys->block.p_first;
895
896         p_sys->block.i_start += b->i_buffer;
897         p_sys->block.i_size  -= b->i_buffer;
898         p_sys->block.p_first  = b->p_next;
899
900         block_Release( b );
901     }
902     if( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
903         p_sys->block.p_current == p_sys->block.p_first &&
904         p_sys->block.p_current->p_next )    /* At least 2 packets */
905     {
906         /* Enough data, don't read more */
907         return VLC_SUCCESS;
908     }
909
910     /* Now read a new block */
911     i_start = mdate();
912     for( ;; )
913     {
914         vlc_bool_t b_eof;
915
916         if( s->b_die ) return VLC_EGENERIC;
917
918
919         /* Fetch a block */
920         if( ( b = AReadBlock( s, &b_eof ) ) ) break;
921
922         if( b_eof ) return VLC_EGENERIC;
923
924         msleep( STREAM_DATA_WAIT );
925     }
926     i_stop = mdate();
927
928     /* Append the block */
929     p_sys->block.i_size += b->i_buffer;
930     *p_sys->block.pp_last = b;
931     p_sys->block.pp_last = &b->p_next;
932
933     /* Fix p_current */
934     if( p_sys->block.p_current == NULL )
935         p_sys->block.p_current = b;
936
937     /* Update stat */
938     p_sys->stat.i_bytes += b->i_buffer;
939     p_sys->stat.i_read_time += i_stop - i_start;
940     p_sys->stat.i_read_count++;
941
942     return VLC_SUCCESS;
943 }
944
945
946 /****************************************************************************
947  * Method 2:
948  ****************************************************************************/
949 static int AStreamRefillStream( stream_t *s );
950
951 static int AStreamReadStream( stream_t *s, void *p_read, int i_read )
952 {
953     stream_sys_t *p_sys = s->p_sys;
954     stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
955
956     uint8_t *p_data = (uint8_t *)p_read;
957     int      i_data = 0;
958
959     if( tk->i_start >= tk->i_end ) return 0; /* EOF */
960
961     if( p_read == NULL )
962     {
963         /* seek within this stream if possible, else use plain old read and discard */
964         stream_sys_t *p_sys = s->p_sys;
965         access_t     *p_access = p_sys->p_access;
966         vlc_bool_t   b_aseek;
967         access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
968         if( b_aseek )
969             return AStreamSeekStream( s, p_sys->i_pos + i_read ) ? 0 : i_read;
970     }
971
972 #if 0
973     msg_Dbg( s, "AStreamReadStream: %d pos="I64Fd" tk=%d start="I64Fd
974              " offset=%d end="I64Fd,
975              i_read, p_sys->i_pos, p_sys->stream.i_tk,
976              tk->i_start, p_sys->stream.i_offset, tk->i_end );
977 #endif
978
979     while( i_data < i_read )
980     {
981         int i_off = (tk->i_start + p_sys->stream.i_offset) %
982                     STREAM_CACHE_TRACK_SIZE;
983         int i_current =
984             __MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset,
985                    STREAM_CACHE_TRACK_SIZE - i_off );
986         int i_copy = __MIN( i_current, i_read - i_data );
987
988         if( i_copy <= 0 ) break; /* EOF */
989
990         /* Copy data */
991         /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */
992         if( p_data )
993         {
994             memcpy( p_data, &tk->p_buffer[i_off], i_copy );
995             p_data += i_copy;
996         }
997         i_data += i_copy;
998         p_sys->stream.i_offset += i_copy;
999
1000         /* Update pos now */
1001         p_sys->i_pos += i_copy;
1002
1003         /* */
1004         p_sys->stream.i_used += i_copy;
1005         if( tk->i_start + p_sys->stream.i_offset >= tk->i_end ||
1006             p_sys->stream.i_used >= p_sys->stream.i_read_size )
1007         {
1008             if( AStreamRefillStream( s ) )
1009             {
1010                 /* EOF */
1011                 if( tk->i_start >= tk->i_end ) break;
1012             }
1013         }
1014     }
1015
1016     return i_data;
1017 }
1018
1019 static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read )
1020 {
1021     stream_sys_t *p_sys = s->p_sys;
1022     stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1023     int64_t i_off;
1024
1025     if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1026
1027 #if 0
1028     msg_Dbg( s, "AStreamPeekStream: %d pos="I64Fd" tk=%d "
1029              "start="I64Fd" offset=%d end="I64Fd,
1030              i_read, p_sys->i_pos, p_sys->stream.i_tk,
1031              tk->i_start, p_sys->stream.i_offset, tk->i_end );
1032 #endif
1033
1034     /* Avoid problem, but that should *never* happen */
1035     if( i_read > STREAM_CACHE_TRACK_SIZE / 2 )
1036         i_read = STREAM_CACHE_TRACK_SIZE / 2;
1037
1038     while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1039     {
1040         if( p_sys->stream.i_used <= 1 )
1041         {
1042             /* Be sure we will read something */
1043             p_sys->stream.i_used += i_read -
1044                 (tk->i_end - tk->i_start - p_sys->stream.i_offset);
1045         }
1046         if( AStreamRefillStream( s ) ) break;
1047     }
1048
1049     if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1050         i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset;
1051
1052     /* Now, direct pointer or a copy ? */
1053     i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
1054     if( i_off + i_read <= STREAM_CACHE_TRACK_SIZE )
1055     {
1056         *pp_peek = &tk->p_buffer[i_off];
1057         return i_read;
1058     }
1059
1060     if( p_sys->i_peek < i_read )
1061     {
1062         p_sys->p_peek = realloc( p_sys->p_peek, i_read );
1063         if( !p_sys->p_peek )
1064         {
1065             p_sys->i_peek = 0;
1066             return 0;
1067         }
1068         p_sys->i_peek = i_read;
1069     }
1070
1071     memcpy( p_sys->p_peek, &tk->p_buffer[i_off],
1072             STREAM_CACHE_TRACK_SIZE - i_off );
1073     memcpy( &p_sys->p_peek[STREAM_CACHE_TRACK_SIZE - i_off],
1074             &tk->p_buffer[0], i_read - (STREAM_CACHE_TRACK_SIZE - i_off) );
1075
1076     *pp_peek = p_sys->p_peek;
1077     return i_read;
1078 }
1079
1080 static int AStreamSeekStream( stream_t *s, int64_t i_pos )
1081 {
1082     stream_sys_t *p_sys = s->p_sys;
1083     access_t     *p_access = p_sys->p_access;
1084     vlc_bool_t   b_aseek;
1085     vlc_bool_t   b_afastseek;
1086     int i_maxth;
1087     int i_new;
1088     int i;
1089
1090 #if 0
1091     msg_Dbg( s, "AStreamSeekStream: to "I64Fd" pos="I64Fd
1092              "tk=%d start="I64Fd" offset=%d end="I64Fd,
1093              i_pos, p_sys->i_pos, p_sys->stream.i_tk,
1094              p_sys->stream.tk[p_sys->stream.i_tk].i_start,
1095              p_sys->stream.i_offset,
1096              p_sys->stream.tk[p_sys->stream.i_tk].i_end );
1097 #endif
1098
1099
1100     /* Seek in our current track ? */
1101     if( i_pos >= p_sys->stream.tk[p_sys->stream.i_tk].i_start &&
1102         i_pos < p_sys->stream.tk[p_sys->stream.i_tk].i_end )
1103     {
1104         //msg_Dbg( s, "AStreamSeekStream: current track" );
1105         p_sys->i_pos = i_pos;
1106         p_sys->stream.i_offset = i_pos - p_sys->stream.tk[p_sys->stream.i_tk].i_start;
1107         return VLC_SUCCESS;
1108     }
1109
1110     access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
1111     if( !b_aseek )
1112     {
1113         /* We can't do nothing */
1114         msg_Dbg( s, "AStreamSeekStream: can't seek" );
1115         return VLC_EGENERIC;
1116     }
1117
1118     /* Date the current track */
1119     p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1120
1121     /* Try to reuse already read data */
1122     for( i = 0; i < STREAM_CACHE_TRACK; i++ )
1123     {
1124         stream_track_t *tk = &p_sys->stream.tk[i];
1125
1126         if( i_pos >= tk->i_start && i_pos <= tk->i_end )
1127         {
1128 #if 0
1129             msg_Dbg( s, "AStreamSeekStream: reusing %d start="I64Fd
1130                      " end="I64Fd, i, tk->i_start, tk->i_end );
1131 #endif
1132             /* Seek at the end of the buffer */
1133             if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
1134
1135             /* That's it */
1136             p_sys->i_pos = i_pos;
1137             p_sys->stream.i_tk = i;
1138             p_sys->stream.i_offset = i_pos - tk->i_start;
1139
1140             if( p_sys->stream.i_used < 1024 )
1141                 p_sys->stream.i_used = 1024;
1142
1143             if( AStreamRefillStream( s ) && i_pos == tk->i_end )
1144                 return VLC_EGENERIC;
1145
1146             return VLC_SUCCESS;
1147         }
1148     }
1149
1150     access2_Control( p_access, ACCESS_CAN_SEEK, &b_afastseek );
1151     /* FIXME compute seek cost (instead of static 'stupid' value) */
1152     i_maxth = __MIN( p_sys->stream.i_read_size, STREAM_READ_ATONCE / 2 );
1153     if( !b_afastseek )
1154         i_maxth *= 3;
1155
1156     /* FIXME TODO */
1157 #if 0
1158     /* Search closest segment TODO */
1159     for( i = 0; i < STREAM_CACHE_TRACK; i++ )
1160     {
1161         stream_track_t *tk = &p_sys->stream.tk[i];
1162
1163         if( i_pos + i_maxth >= tk->i_start )
1164         {
1165             msg_Dbg( s, "good segment before current pos, TODO" );
1166         }
1167         if( i_pos - i_maxth <= tk->i_end )
1168         {
1169             msg_Dbg( s, "good segment after current pos, TODO" );
1170         }
1171     }
1172 #endif
1173
1174     /* Nothing good, seek and choose oldest segment */
1175     if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
1176     p_sys->i_pos = i_pos;
1177
1178     i_new = 0;
1179     for( i = 1; i < STREAM_CACHE_TRACK; i++ )
1180     {
1181         if( p_sys->stream.tk[i].i_date < p_sys->stream.tk[i_new].i_date )
1182             i_new = i;
1183     }
1184
1185     /* Reset the segment */
1186     p_sys->stream.i_tk     = i_new;
1187     p_sys->stream.i_offset =  0;
1188     p_sys->stream.tk[i_new].i_start = i_pos;
1189     p_sys->stream.tk[i_new].i_end   = i_pos;
1190
1191     /* Read data */
1192     if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 )
1193         p_sys->stream.i_used = STREAM_READ_ATONCE / 2;
1194
1195     if( AStreamRefillStream( s ) )
1196         return VLC_EGENERIC;
1197
1198     return VLC_SUCCESS;
1199 }
1200
1201 static int AStreamRefillStream( stream_t *s )
1202 {
1203     stream_sys_t *p_sys = s->p_sys;
1204     stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1205
1206     /* We read but won't increase i_start after initial start + offset */
1207     int i_toread =
1208         __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE -
1209                (tk->i_end - tk->i_start - p_sys->stream.i_offset) );
1210     vlc_bool_t b_read = VLC_FALSE;
1211     int64_t i_start, i_stop;
1212
1213     if( i_toread <= 0 ) return VLC_EGENERIC; /* EOF */
1214
1215     /* msg_Dbg( s, "AStreamRefillStream: toread=%d", i_toread ); */
1216
1217     i_start = mdate();
1218     while( i_toread > 0 )
1219     {
1220         int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE;
1221         int i_read;
1222
1223         if( s->b_die )
1224             return VLC_EGENERIC;
1225
1226         i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off );
1227         i_read = AReadStream( s, &tk->p_buffer[i_off], i_read );
1228
1229         /* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */
1230         if( i_read <  0 )
1231         {
1232             msleep( STREAM_DATA_WAIT );
1233             continue;
1234         }
1235         else if( i_read == 0 )
1236         {
1237             if( !b_read ) return VLC_EGENERIC;
1238             return VLC_SUCCESS;
1239         }
1240         b_read = VLC_TRUE;
1241
1242         /* Update end */
1243         tk->i_end += i_read;
1244
1245         /* Windows of STREAM_CACHE_TRACK_SIZE */
1246         if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE )
1247         {
1248             int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
1249
1250             tk->i_start += i_invalid;
1251             p_sys->stream.i_offset -= i_invalid;
1252         }
1253
1254         i_toread -= i_read;
1255         p_sys->stream.i_used -= i_read;
1256
1257         p_sys->stat.i_bytes += i_read;
1258         p_sys->stat.i_read_count++;
1259     }
1260     i_stop = mdate();
1261
1262     p_sys->stat.i_read_time += i_stop - i_start;
1263
1264     return VLC_SUCCESS;
1265 }
1266
1267 static void AStreamPrebufferStream( stream_t *s )
1268 {
1269     stream_sys_t *p_sys = s->p_sys;
1270     access_t     *p_access = p_sys->p_access;
1271
1272     int64_t i_first = 0;
1273     int64_t i_start;
1274     int64_t i_prebuffer = p_sys->b_quick ? STREAM_CACHE_TRACK_SIZE /100 :
1275         ( (p_access->info.i_title > 1 || p_access->info.i_seekpoint > 1) ?
1276           STREAM_CACHE_PREBUFFER_SIZE : STREAM_CACHE_TRACK_SIZE / 3 );
1277
1278     msg_Dbg( s, "pre buffering" );
1279     i_start = mdate();
1280     for( ;; )
1281     {
1282         stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1283
1284         int64_t i_date = mdate();
1285         int i_read;
1286
1287         if( s->b_die || tk->i_end >= i_prebuffer ||
1288             (i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date) )
1289         {
1290             int64_t i_byterate;
1291
1292             /* Update stat */
1293             p_sys->stat.i_bytes = tk->i_end - tk->i_start;
1294             p_sys->stat.i_read_time = i_date - i_start;
1295             i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) /
1296                          (p_sys->stat.i_read_time+1);
1297
1298             msg_Dbg( s, "prebuffering done "I64Fd" bytes in "I64Fd"s - "
1299                      I64Fd" kbytes/s",
1300                      p_sys->stat.i_bytes,
1301                      p_sys->stat.i_read_time / I64C(1000000),
1302                      i_byterate / 1024 );
1303             break;
1304         }
1305
1306         /* */
1307         i_read = STREAM_CACHE_TRACK_SIZE - tk->i_end;
1308         i_read = __MIN( p_sys->stream.i_read_size, i_read );
1309         i_read = AReadStream( s, &tk->p_buffer[tk->i_end], i_read );
1310         if( i_read <  0 )
1311         {
1312             msleep( STREAM_DATA_WAIT );
1313             continue;
1314         }
1315         else if( i_read == 0 )
1316         {
1317             /* EOF */
1318             break;
1319         }
1320
1321         if( i_first == 0 )
1322         {
1323             i_first = mdate();
1324             msg_Dbg( s, "received first data for our buffer");
1325         }
1326
1327         tk->i_end += i_read;
1328
1329         p_sys->stat.i_read_count++;
1330     }
1331 }
1332
1333
1334 /****************************************************************************
1335  * stream_ReadLine:
1336  ****************************************************************************/
1337 /**
1338  * Read from the stream untill first newline.
1339  * \param s Stream handle to read from
1340  * \return A null-terminated string. This must be freed,
1341  */
1342 #define STREAM_PROBE_LINE 2048
1343 #define STREAM_LINE_MAX (2048*100)
1344 char *stream_ReadLine( stream_t *s )
1345 {
1346     char *p_line = NULL;
1347     int i_line = 0, i_read = 0;
1348
1349     while( i_read < STREAM_LINE_MAX )
1350     {
1351         char *psz_eol;
1352         uint8_t *p_data;
1353         int i_data;
1354
1355         /* Probe new data */
1356         i_data = stream_Peek( s, &p_data, STREAM_PROBE_LINE );
1357         if( i_data <= 0 ) break; /* No more data */
1358
1359         /* Check if there is an EOL */
1360         if( ( psz_eol = memchr( p_data, '\n', i_data ) ) )
1361         {
1362             i_data = (psz_eol - (char *)p_data) + 1;
1363             p_line = realloc( p_line, i_line + i_data + 1 );
1364             i_data = stream_Read( s, &p_line[i_line], i_data );
1365             if( i_data <= 0 ) break; /* Hmmm */
1366             i_line += (i_data - 1);
1367             i_read += i_data;
1368
1369             /* We have our line */
1370             break;
1371         }
1372
1373         /* Read data (+1 for easy \0 append) */
1374         p_line = realloc( p_line, i_line + STREAM_PROBE_LINE + 1 );
1375         i_data = stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE );
1376         if( i_data <= 0 ) break; /* Hmmm */
1377         i_line += i_data;
1378         i_read += i_data;
1379     }
1380
1381     /* Remove trailing LF/CR */
1382     while( i_line > 0 && ( p_line[i_line-1] == '\r' ||
1383            p_line[i_line-1] == '\n') ) i_line--;
1384
1385     if( i_read > 0 )
1386     {
1387         p_line[i_line] = '\0';
1388         return p_line;
1389     }
1390
1391     /* We failed to read any data, probably EOF */
1392     if( p_line ) free( p_line );
1393     return NULL;
1394 }
1395
1396 /****************************************************************************
1397  * Access reading/seeking wrappers to handle concatenated streams.
1398  ****************************************************************************/
1399 static int AReadStream( stream_t *s, void *p_read, int i_read )
1400 {
1401     stream_sys_t *p_sys = s->p_sys;
1402     access_t *p_access = p_sys->p_access;
1403     int i_read_orig = i_read;
1404
1405     if( !p_sys->i_list )
1406     {
1407         i_read = p_access->pf_read( p_access, p_read, i_read );
1408         return i_read;
1409     }
1410
1411     i_read = p_sys->p_list_access->pf_read( p_sys->p_list_access, p_read,
1412                                             i_read );
1413
1414     /* If we reached an EOF then switch to the next stream in the list */
1415     if( i_read == 0 && p_sys->i_list_index + 1 < p_sys->i_list )
1416     {
1417         char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path;
1418         access_t *p_list_access;
1419
1420         msg_Dbg( s, "opening input `%s'", psz_name );
1421
1422         p_list_access = access2_New( s, p_access->psz_access, 0, psz_name, 0 );
1423
1424         if( !p_list_access ) return 0;
1425
1426         if( p_sys->p_list_access != p_access )
1427             access2_Delete( p_sys->p_list_access );
1428
1429         p_sys->p_list_access = p_list_access;
1430
1431         /* We have to read some data */
1432         return AReadStream( s, p_read, i_read_orig );
1433     }
1434
1435     return i_read;
1436 }
1437
1438 static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof )
1439 {
1440     stream_sys_t *p_sys = s->p_sys;
1441     access_t *p_access = p_sys->p_access;
1442     block_t *p_block;
1443     vlc_bool_t b_eof;
1444
1445     if( !p_sys->i_list )
1446     {
1447         p_block = p_access->pf_block( p_access );
1448         if( pb_eof ) *pb_eof = p_access->info.b_eof;
1449         return p_block;
1450     }
1451
1452     p_block = p_sys->p_list_access->pf_block( p_access );
1453     b_eof = p_sys->p_list_access->info.b_eof;
1454     if( pb_eof ) *pb_eof = b_eof;
1455
1456     /* If we reached an EOF then switch to the next stream in the list */
1457     if( !p_block && b_eof && p_sys->i_list_index + 1 < p_sys->i_list )
1458     {
1459         char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path;
1460         access_t *p_list_access;
1461
1462         msg_Dbg( s, "opening input `%s'", psz_name );
1463
1464         p_list_access = access2_New( s, p_access->psz_access, 0, psz_name, 0 );
1465
1466         if( !p_list_access ) return 0;
1467
1468         if( p_sys->p_list_access != p_access )
1469             access2_Delete( p_sys->p_list_access );
1470
1471         p_sys->p_list_access = p_list_access;
1472
1473         /* We have to read some data */
1474         return AReadBlock( s, pb_eof );
1475     }
1476
1477     return p_block;
1478 }
1479
1480 static int ASeek( stream_t *s, int64_t i_pos )
1481 {
1482     stream_sys_t *p_sys = s->p_sys;
1483     access_t *p_access = p_sys->p_access;
1484
1485     /* Check which stream we need to access */
1486     if( p_sys->i_list )
1487     {
1488         int i;
1489         char *psz_name;
1490         int64_t i_size = 0;
1491         access_t *p_list_access = 0;
1492
1493         for( i = 0; i < p_sys->i_list - 1; i++ )
1494         {
1495             if( i_pos < p_sys->list[i]->i_size + i_size ) break;
1496             i_size += p_sys->list[i]->i_size;
1497         }
1498         psz_name = p_sys->list[i]->psz_path;
1499
1500         if( i != p_sys->i_list_index )
1501             msg_Dbg( s, "opening input `%s'", psz_name );
1502
1503         if( i != p_sys->i_list_index && i != 0 )
1504         {
1505             p_list_access =
1506                 access2_New( s, p_access->psz_access, 0, psz_name, 0 );
1507         }
1508         else if( i != p_sys->i_list_index )
1509         {
1510             p_list_access = p_access;
1511         }
1512
1513         if( p_list_access )
1514         {
1515             if( p_sys->p_list_access != p_access )
1516                 access2_Delete( p_sys->p_list_access );
1517
1518             p_sys->p_list_access = p_list_access;
1519         }
1520
1521         p_sys->i_list_index = i;
1522         return p_sys->p_list_access->pf_seek( p_sys->p_list_access,
1523                                               i_pos - i_size );
1524     }
1525
1526     return p_access->pf_seek( p_access, i_pos );
1527 }