]> git.sesse.net Git - vlc/blob - src/input/stream.c
Simplifications
[vlc] / src / input / stream.c
1 /*****************************************************************************
2  * stream.c
3  *****************************************************************************
4  * Copyright (C) 1999-2004 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <dirent.h>
29
30 #include <vlc_common.h>
31 #include <vlc_strings.h>
32 #include <vlc_osd.h>
33 #include <vlc_charset.h>
34
35 #include <assert.h>
36
37 #include "input_internal.h"
38
39 #undef STREAM_DEBUG
40
41 /* TODO:
42  *  - tune the 2 methods (block/stream)
43  *  - compute cost for seek
44  *  - improve stream mode seeking with closest segments
45  *  - ...
46  *  - Maybe remove (block/stream) in favour of immediate
47  */
48
49 /* Two methods:
50  *  - using pf_block
51  *      One linked list of data read
52  *  - using pf_read
53  *      More complex scheme using mutliple track to avoid seeking
54  *  - using directly the access (only indirection for peeking).
55  *      This method is known to introduce much less latency.
56  *      It should probably defaulted (instead of the stream method (2)).
57  */
58
59 /* How many tracks we have, currently only used for stream mode */
60 #ifdef OPTIMIZE_MEMORY
61 #   define STREAM_CACHE_TRACK 1
62     /* Max size of our cache 128Ko per track */
63 #   define STREAM_CACHE_SIZE  (STREAM_CACHE_TRACK*1024*128)
64 #else
65 #   define STREAM_CACHE_TRACK 3
66     /* Max size of our cache 4Mo per track */
67 #   define STREAM_CACHE_SIZE  (4*STREAM_CACHE_TRACK*1024*1024)
68 #endif
69
70 /* How many data we try to prebuffer */
71 #define STREAM_CACHE_PREBUFFER_SIZE (32767)
72 /* Maximum time we take to pre-buffer */
73 #define STREAM_CACHE_PREBUFFER_LENGTH (100*1000)
74
75 /* Method1: Simple, for pf_block.
76  *  We get blocks and put them in the linked list.
77  *  We release blocks once the total size is bigger than CACHE_BLOCK_SIZE
78  */
79 #define STREAM_DATA_WAIT 40000       /* Time between before a pf_block retry */
80
81 /* Method2: A bit more complex, for pf_read
82  *  - We use ring buffers, only one if unseekable, all if seekable
83  *  - Upon seek date current ring, then search if one ring match the pos,
84  *      yes: switch to it, seek the access to match the end of the ring
85  *      no: search the ring with i_end the closer to i_pos,
86  *          if close enough, read data and use this ring
87  *          else use the oldest ring, seek and use it.
88  *
89  *  TODO: - with access non seekable: use all space available for only one ring, but
90  *          we have to support seekable/non-seekable switch on the fly.
91  *        - compute a good value for i_read_size
92  *        - ?
93  */
94 #define STREAM_READ_ATONCE 32767
95 #define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)
96
97 typedef struct
98 {
99     int64_t i_date;
100
101     int64_t i_start;
102     int64_t i_end;
103
104     uint8_t *p_buffer;
105
106 } stream_track_t;
107
108 typedef struct
109 {
110     char     *psz_path;
111     int64_t  i_size;
112
113 } access_entry_t;
114
115 typedef enum stream_read_method_t
116 {
117     STREAM_METHOD_IMMEDIATE,
118     STREAM_METHOD_BLOCK,
119     STREAM_METHOD_STREAM
120 } stream_read_method_t;
121
122 struct stream_sys_t
123 {
124     access_t    *p_access;
125
126     stream_read_method_t   method;    /* method to use */
127
128     int64_t     i_pos;      /* Current reading offset */
129
130     /* Method 1: pf_block */
131     struct
132     {
133         int64_t i_start;        /* Offset of block for p_first */
134         int64_t i_offset;       /* Offset for data in p_current */
135         block_t *p_current;     /* Current block */
136
137         int     i_size;         /* Total amount of data in the list */
138         block_t *p_first;
139         block_t **pp_last;
140
141     } block;
142
143     /* Method 2: for pf_read */
144     struct
145     {
146         int i_offset;   /* Buffer offset in the current track */
147         int i_tk;       /* Current track */
148         stream_track_t tk[STREAM_CACHE_TRACK];
149
150         /* Global buffer */
151         uint8_t *p_buffer;
152
153         /* */
154         int i_used; /* Used since last read */
155         int i_read_size;
156
157     } stream;
158
159     /* Method 3: for pf_read */
160     struct
161     {
162         int64_t i_end;
163         uint8_t *p_buffer;
164     } immediate;
165
166     /* Peek temporary buffer */
167     unsigned int i_peek;
168     uint8_t *p_peek;
169
170     /* Stat for both method */
171     struct
172     {
173         bool b_fastseek;  /* From access */
174
175         /* Stat about reading data */
176         int64_t i_read_count;
177         int64_t i_bytes;
178         int64_t i_read_time;
179
180         /* Stat about seek */
181         int     i_seek_count;
182         int64_t i_seek_time;
183
184     } stat;
185
186     /* Streams list */
187     int            i_list;
188     access_entry_t **list;
189     int            i_list_index;
190     access_t       *p_list_access;
191
192     /* Preparse mode ? */
193     bool      b_quick;
194
195     /* */
196     struct
197     {
198         bool b_active;
199
200         FILE *f;        /* TODO it could be replaced by access_output_t one day */
201         bool b_error;
202     } record;
203 };
204
205 /* Method 1: */
206 static int  AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read );
207 static int  AStreamPeekBlock( stream_t *s, const uint8_t **p_peek, unsigned int i_read );
208 static int  AStreamSeekBlock( stream_t *s, int64_t i_pos );
209 static void AStreamPrebufferBlock( stream_t *s );
210 static block_t *AReadBlock( stream_t *s, bool *pb_eof );
211
212 /* Method 2 */
213 static int  AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read );
214 static int  AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );
215 static int  AStreamSeekStream( stream_t *s, int64_t i_pos );
216 static void AStreamPrebufferStream( stream_t *s );
217 static int  AReadStream( stream_t *s, void *p_read, unsigned int i_read );
218
219 /* Method 3 */
220 static int  AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read );
221 static int  AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );
222 static int  AStreamSeekImmediate( stream_t *s, int64_t i_pos );
223
224 /* Common */
225 static int AStreamControl( stream_t *s, int i_query, va_list );
226 static void AStreamDestroy( stream_t *s );
227 static void UStreamDestroy( stream_t *s );
228 static int  ASeek( stream_t *s, int64_t i_pos );
229 static int  ARecordSetState( stream_t *s, bool b_record, const char *psz_extension );
230 static void ARecordWrite( stream_t *s, const uint8_t *p_buffer, size_t i_buffer );
231
232 /****************************************************************************
233  * Method 3 helpers:
234  ****************************************************************************/
235
236 static inline int64_t stream_buffered_size( stream_t *s )
237 {
238     return s->p_sys->immediate.i_end;
239 }
240
241 static inline void stream_buffer_empty( stream_t *s, int length )
242 {
243     length = __MAX( stream_buffered_size( s ), length );
244     if( length )
245     {
246         memmove( s->p_sys->immediate.p_buffer,
247                  s->p_sys->immediate.p_buffer + length,
248                  stream_buffered_size( s ) - length );
249     }
250     s->p_sys->immediate.i_end -= length;
251 }
252
253 static inline void stream_buffer_fill( stream_t *s, int length )
254 {
255     s->p_sys->immediate.i_end += length;
256 }
257
258 static inline uint8_t * stream_buffer( stream_t *s )
259 {
260     return s->p_sys->immediate.p_buffer;
261 }
262
263 /****************************************************************************
264  * stream_UrlNew: create a stream from a access
265  ****************************************************************************/
266 stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url )
267 {
268     const char *psz_access, *psz_demux;
269     char *psz_path;
270     access_t *p_access;
271     stream_t *p_res;
272
273     if( !psz_url )
274         return NULL;
275
276     char psz_dup[strlen( psz_url ) + 1];
277     strcpy( psz_dup, psz_url );
278     input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup );
279
280     /* Now try a real access */
281     p_access = access_New( p_parent, psz_access, psz_demux, psz_path );
282
283     if( p_access == NULL )
284     {
285         msg_Err( p_parent, "no suitable access module for `%s'", psz_url );
286         return NULL;
287     }
288
289     if( !( p_res = stream_AccessNew( p_access, true ) ) )
290     {
291         access_Delete( p_access );
292         return NULL;
293     }
294
295     p_res->pf_destroy = UStreamDestroy;
296     return p_res;
297 }
298
299 stream_t *stream_AccessNew( access_t *p_access, bool b_quick )
300 {
301     stream_t *s = vlc_stream_create( VLC_OBJECT(p_access) );
302     stream_sys_t *p_sys;
303     char *psz_list = NULL;
304
305     if( !s ) return NULL;
306
307     /* Attach it now, needed for b_die */
308     vlc_object_attach( s, p_access );
309
310     s->pf_read   = NULL;    /* Set up later */
311     s->pf_peek   = NULL;
312     s->pf_control = AStreamControl;
313     s->pf_destroy = AStreamDestroy;
314
315     s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) );
316     if( p_sys == NULL )
317         goto error;
318
319     /* UTF16 and UTF32 text file conversion */
320     s->i_char_width = 1;
321     s->b_little_endian = false;
322     s->conv = (vlc_iconv_t)(-1);
323
324     /* Common field */
325     p_sys->p_access = p_access;
326     if( p_access->pf_block )
327         p_sys->method = STREAM_METHOD_BLOCK;
328     else if( var_CreateGetBool( s, "use-stream-immediate" ) )
329         p_sys->method = STREAM_METHOD_IMMEDIATE;
330     else
331         p_sys->method = STREAM_METHOD_STREAM;
332
333     p_sys->record.b_active = false;
334
335     p_sys->i_pos = p_access->info.i_pos;
336
337     /* Stats */
338     access_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek );
339     p_sys->stat.i_bytes = 0;
340     p_sys->stat.i_read_time = 0;
341     p_sys->stat.i_read_count = 0;
342     p_sys->stat.i_seek_count = 0;
343     p_sys->stat.i_seek_time = 0;
344
345     p_sys->i_list = 0;
346     p_sys->list = 0;
347     p_sys->i_list_index = 0;
348     p_sys->p_list_access = 0;
349
350     p_sys->b_quick = b_quick;
351
352     /* Get the additional list of inputs if any (for concatenation) */
353     if( (psz_list = var_CreateGetString( s, "input-list" )) && *psz_list )
354     {
355         access_entry_t *p_entry = malloc( sizeof(access_entry_t) );
356         if( p_entry == NULL )
357             goto error;
358         char *psz_name, *psz_parser = psz_name = psz_list;
359
360         p_sys->p_list_access = p_access;
361         p_entry->i_size = p_access->info.i_size;
362         p_entry->psz_path = strdup( p_access->psz_path );
363         if( p_entry->psz_path == NULL )
364         {
365             free( p_entry );
366             goto error;
367         }
368         TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
369         msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)",
370                  p_entry->psz_path, p_access->info.i_size );
371
372         while( psz_name && *psz_name )
373         {
374             psz_parser = strchr( psz_name, ',' );
375             if( psz_parser ) *psz_parser = 0;
376
377             psz_name = strdup( psz_name );
378             if( psz_name )
379             {
380                 access_t *p_tmp = access_New( p_access, p_access->psz_access,
381                                                "", psz_name );
382
383                 if( !p_tmp )
384                 {
385                     psz_name = psz_parser;
386                     if( psz_name ) psz_name++;
387                     continue;
388                 }
389
390                 msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)",
391                          psz_name, p_tmp->info.i_size );
392
393                 p_entry = malloc( sizeof(access_entry_t) );
394                 if( p_entry == NULL )
395                     goto error;
396                 p_entry->i_size = p_tmp->info.i_size;
397                 p_entry->psz_path = psz_name;
398                 TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
399
400                 access_Delete( p_tmp );
401             }
402
403             psz_name = psz_parser;
404             if( psz_name ) psz_name++;
405         }
406     }
407     FREENULL( psz_list );
408
409     /* Peek */
410     p_sys->i_peek = 0;
411     p_sys->p_peek = NULL;
412
413     if( p_sys->method == STREAM_METHOD_BLOCK )
414     {
415         msg_Dbg( s, "Using AStream*Block" );
416         s->pf_read = AStreamReadBlock;
417         s->pf_peek = AStreamPeekBlock;
418
419         /* Init all fields of p_sys->block */
420         p_sys->block.i_start = p_sys->i_pos;
421         p_sys->block.i_offset = 0;
422         p_sys->block.p_current = NULL;
423         p_sys->block.i_size = 0;
424         p_sys->block.p_first = NULL;
425         p_sys->block.pp_last = &p_sys->block.p_first;
426
427         /* Do the prebuffering */
428         AStreamPrebufferBlock( s );
429
430         if( p_sys->block.i_size <= 0 )
431         {
432             msg_Err( s, "cannot pre fill buffer" );
433             goto error;
434         }
435     }
436     else if( p_sys->method == STREAM_METHOD_IMMEDIATE )
437     {
438         msg_Dbg( s, "Using AStream*Immediate" );
439
440         s->pf_read = AStreamReadImmediate;
441         s->pf_peek = AStreamPeekImmediate;
442
443         /* Allocate/Setup our tracks (useful to peek)*/
444         p_sys->immediate.i_end = 0;
445         p_sys->immediate.p_buffer = malloc( STREAM_CACHE_SIZE );
446
447         msg_Dbg( s, "p_buffer %p-%p",
448                  p_sys->immediate.p_buffer,
449                  &p_sys->immediate.p_buffer[STREAM_CACHE_SIZE] );
450
451         if( p_sys->immediate.p_buffer == NULL )
452         {
453             msg_Err( s, "Out of memory when allocating stream cache (%d bytes)",
454                         STREAM_CACHE_SIZE );
455             goto error;
456         }
457     }
458     else
459     {
460         int i;
461
462         assert( p_sys->method == STREAM_METHOD_STREAM );
463
464         msg_Dbg( s, "Using AStream*Stream" );
465
466         s->pf_read = AStreamReadStream;
467         s->pf_peek = AStreamPeekStream;
468
469         /* Allocate/Setup our tracks */
470         p_sys->stream.i_offset = 0;
471         p_sys->stream.i_tk     = 0;
472         p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE );
473         if( p_sys->stream.p_buffer == NULL )
474         {
475             msg_Err( s, "Out of memory when allocating stream cache (%d bytes)",
476                         STREAM_CACHE_SIZE );
477             goto error;
478         }
479         p_sys->stream.i_used   = 0;
480         access_Control( p_access, ACCESS_GET_MTU,
481                          &p_sys->stream.i_read_size );
482         if( p_sys->stream.i_read_size <= 0 )
483             p_sys->stream.i_read_size = STREAM_READ_ATONCE;
484         else if( p_sys->stream.i_read_size <= 256 )
485             p_sys->stream.i_read_size = 256;
486
487         for( i = 0; i < STREAM_CACHE_TRACK; i++ )
488         {
489             p_sys->stream.tk[i].i_date  = 0;
490             p_sys->stream.tk[i].i_start = p_sys->i_pos;
491             p_sys->stream.tk[i].i_end   = p_sys->i_pos;
492             p_sys->stream.tk[i].p_buffer=
493                 &p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE];
494         }
495
496         /* Do the prebuffering */
497         AStreamPrebufferStream( s );
498
499         if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
500         {
501             msg_Err( s, "cannot pre fill buffer" );
502             goto error;
503         }
504     }
505
506     return s;
507
508 error:
509     if( p_sys->method == STREAM_METHOD_BLOCK )
510     {
511         /* Nothing yet */
512     }
513     else
514     {
515         free( p_sys->stream.p_buffer );
516     }
517     while( p_sys->i_list > 0 )
518         free( p_sys->list[--(p_sys->i_list)] );
519     free( p_sys->list );
520     free( psz_list );
521     free( s->p_sys );
522     vlc_object_detach( s );
523     vlc_object_release( s );
524     return NULL;
525 }
526
527 /****************************************************************************
528  * AStreamDestroy:
529  ****************************************************************************/
530 static void AStreamDestroy( stream_t *s )
531 {
532     stream_sys_t *p_sys = s->p_sys;
533
534     vlc_object_detach( s );
535
536     if( p_sys->record.b_active )
537         ARecordSetState( s, false, NULL );
538
539     if( p_sys->method == STREAM_METHOD_BLOCK )
540         block_ChainRelease( p_sys->block.p_first );
541     else if( p_sys->method == STREAM_METHOD_IMMEDIATE )
542         free( p_sys->immediate.p_buffer );
543     else
544         free( p_sys->stream.p_buffer );
545
546     free( p_sys->p_peek );
547
548     if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access )
549         access_Delete( p_sys->p_list_access );
550
551     while( p_sys->i_list-- )
552     {
553         free( p_sys->list[p_sys->i_list]->psz_path );
554         free( p_sys->list[p_sys->i_list] );
555     }
556
557     free( p_sys->list );
558     free( p_sys );
559
560     vlc_object_release( s );
561 }
562
563 static void UStreamDestroy( stream_t *s )
564 {
565     access_t *p_access = (access_t *)s->p_parent;
566     AStreamDestroy( s );
567     access_Delete( p_access );
568 }
569
570 /****************************************************************************
571  * stream_AccessReset:
572  ****************************************************************************/
573 void stream_AccessReset( stream_t *s )
574 {
575     stream_sys_t *p_sys = s->p_sys;
576
577     p_sys->i_pos = p_sys->p_access->info.i_pos;
578
579     if( p_sys->method == STREAM_METHOD_BLOCK )
580     {
581         block_ChainRelease( p_sys->block.p_first );
582
583         /* Init all fields of p_sys->block */
584         p_sys->block.i_start = p_sys->i_pos;
585         p_sys->block.i_offset = 0;
586         p_sys->block.p_current = NULL;
587         p_sys->block.i_size = 0;
588         p_sys->block.p_first = NULL;
589         p_sys->block.pp_last = &p_sys->block.p_first;
590
591         /* Do the prebuffering */
592         AStreamPrebufferBlock( s );
593     }
594     else if( p_sys->method == STREAM_METHOD_IMMEDIATE )
595     {
596         stream_buffer_empty( s, stream_buffered_size( s ) );
597     }
598     else
599     {
600         int i;
601
602         assert( p_sys->method == STREAM_METHOD_STREAM );
603
604         /* Setup our tracks */
605         p_sys->stream.i_offset = 0;
606         p_sys->stream.i_tk     = 0;
607         p_sys->stream.i_used   = 0;
608
609         for( i = 0; i < STREAM_CACHE_TRACK; i++ )
610         {
611             p_sys->stream.tk[i].i_date  = 0;
612             p_sys->stream.tk[i].i_start = p_sys->i_pos;
613             p_sys->stream.tk[i].i_end   = p_sys->i_pos;
614         }
615
616         /* Do the prebuffering */
617         AStreamPrebufferStream( s );
618     }
619 }
620
621 /****************************************************************************
622  * stream_AccessUpdate:
623  ****************************************************************************/
624 void stream_AccessUpdate( stream_t *s )
625 {
626     stream_sys_t *p_sys = s->p_sys;
627
628     p_sys->i_pos = p_sys->p_access->info.i_pos;
629
630     if( p_sys->i_list )
631     {
632         int i;
633         for( i = 0; i < p_sys->i_list_index; i++ )
634         {
635             p_sys->i_pos += p_sys->list[i]->i_size;
636         }
637     }
638 }
639
640 /****************************************************************************
641  * AStreamControl:
642  ****************************************************************************/
643 static int AStreamControl( stream_t *s, int i_query, va_list args )
644 {
645     stream_sys_t *p_sys = s->p_sys;
646     access_t     *p_access = p_sys->p_access;
647
648     bool *p_bool;
649     bool b_bool;
650     const char *psz_string;
651     int64_t    *pi_64, i_64;
652     int        i_int;
653
654     switch( i_query )
655     {
656         case STREAM_GET_SIZE:
657             pi_64 = (int64_t*)va_arg( args, int64_t * );
658             if( s->p_sys->i_list )
659             {
660                 int i;
661                 *pi_64 = 0;
662                 for( i = 0; i < s->p_sys->i_list; i++ )
663                     *pi_64 += s->p_sys->list[i]->i_size;
664                 break;
665             }
666             *pi_64 = p_access->info.i_size;
667             break;
668
669         case STREAM_CAN_SEEK:
670             p_bool = (bool*)va_arg( args, bool * );
671             access_Control( p_access, ACCESS_CAN_SEEK, p_bool );
672             break;
673
674         case STREAM_CAN_FASTSEEK:
675             p_bool = (bool*)va_arg( args, bool * );
676             access_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
677             break;
678
679         case STREAM_GET_POSITION:
680             pi_64 = (int64_t*)va_arg( args, int64_t * );
681             *pi_64 = p_sys->i_pos;
682             break;
683
684         case STREAM_SET_POSITION:
685             i_64 = (int64_t)va_arg( args, int64_t );
686             switch( p_sys->method )
687             {
688             case STREAM_METHOD_BLOCK:
689                 return AStreamSeekBlock( s, i_64 );
690             case STREAM_METHOD_IMMEDIATE:
691                 return AStreamSeekImmediate( s, i_64 );
692             case STREAM_METHOD_STREAM:
693                 return AStreamSeekStream( s, i_64 );
694             default:
695                 assert(0);
696                 return VLC_EGENERIC;
697             }
698
699         case STREAM_GET_MTU:
700             return VLC_EGENERIC;
701
702         case STREAM_CONTROL_ACCESS:
703             i_int = (int) va_arg( args, int );
704             if( i_int != ACCESS_SET_PRIVATE_ID_STATE &&
705                 i_int != ACCESS_SET_PRIVATE_ID_CA &&
706                 i_int != ACCESS_GET_PRIVATE_ID_STATE )
707             {
708                 msg_Err( s, "Hey, what are you thinking ?"
709                             "DON'T USE STREAM_CONTROL_ACCESS !!!" );
710                 return VLC_EGENERIC;
711             }
712             return access_vaControl( p_access, i_int, args );
713
714         case STREAM_GET_CONTENT_TYPE:
715             return access_Control( p_access, ACCESS_GET_CONTENT_TYPE,
716                                     va_arg( args, char ** ) );
717         case STREAM_SET_RECORD_STATE:
718             b_bool = (bool)va_arg( args, int );
719             psz_string = NULL;
720             if( b_bool )
721                 psz_string = (const char*)va_arg( args, const char* );
722             return ARecordSetState( s, b_bool, psz_string );
723
724         default:
725             msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
726             return VLC_EGENERIC;
727     }
728     return VLC_SUCCESS;
729 }
730
731 /****************************************************************************
732  * ARecord*: record stream functions
733  ****************************************************************************/
734 static int  ARecordStart( stream_t *s, const char *psz_extension )
735 {
736     stream_sys_t *p_sys = s->p_sys;
737
738     char *psz_file;
739     FILE *f;
740
741     /* */
742     if( !psz_extension )
743         psz_extension = "dat";
744
745     /* Retreive path */
746     char *psz_path = var_CreateGetString( s, "input-record-path" );
747     if( !psz_path || *psz_path == '\0' )
748     {
749         free( psz_path );
750         psz_path = strdup( config_GetHomeDir() );
751     }
752
753     if( !psz_path )
754         return VLC_ENOMEM;
755
756     /* Create file name
757      * TODO allow prefix configuration */
758     psz_file = input_CreateFilename( VLC_OBJECT(s), psz_path, INPUT_RECORD_PREFIX, psz_extension );
759
760     free( psz_path );
761
762     if( !psz_file )
763         return VLC_ENOMEM;
764
765     f = utf8_fopen( psz_file, "wb" );
766     if( !f )
767     {
768         free( psz_file );
769         return VLC_EGENERIC;
770     }
771     msg_Dbg( s, "Recording into %s", psz_file );
772     free( psz_file );
773
774     /* */
775     p_sys->record.f = f;
776     p_sys->record.b_active = true;
777     p_sys->record.b_error = false;
778     return VLC_SUCCESS;
779 }
780 static int  ARecordStop( stream_t *s )
781 {
782     stream_sys_t *p_sys = s->p_sys;
783
784     assert( p_sys->record.b_active );
785
786     msg_Dbg( s, "Recording completed" );
787     fclose( p_sys->record.f );
788     p_sys->record.b_active = false;
789     return VLC_SUCCESS;
790 }
791
792 static int  ARecordSetState( stream_t *s, bool b_record, const char *psz_extension )
793 {
794     stream_sys_t *p_sys = s->p_sys;
795
796     if( !!p_sys->record.b_active == !!b_record )
797         return VLC_SUCCESS;
798
799     if( b_record )
800         return ARecordStart( s, psz_extension );
801     else
802         return ARecordStop( s );
803 }
804 static void ARecordWrite( stream_t *s, const uint8_t *p_buffer, size_t i_buffer )
805 {
806     stream_sys_t *p_sys = s->p_sys;
807
808     assert( p_sys->record.b_active );
809
810     if( i_buffer > 0 )
811     {
812         const bool b_previous_error = p_sys->record.b_error;
813         const size_t i_written = fwrite( p_buffer, 1, i_buffer, p_sys->record.f );
814
815         p_sys->record.b_error = i_written != i_buffer;
816
817         /* TODO maybe a intf_UserError or something like that ? */
818         if( p_sys->record.b_error && !b_previous_error )
819             msg_Err( s, "Failed to record data (begin)" );
820         else if( !p_sys->record.b_error && b_previous_error )
821             msg_Err( s, "Failed to record data (end)" );
822     }
823 }
824
825 /****************************************************************************
826  * Method 1:
827  ****************************************************************************/
828 static void AStreamPrebufferBlock( stream_t *s )
829 {
830     stream_sys_t *p_sys = s->p_sys;
831     access_t     *p_access = p_sys->p_access;
832
833     int64_t i_first = 0;
834     int64_t i_start;
835
836     msg_Dbg( s, "pre buffering" );
837     i_start = mdate();
838     for( ;; )
839     {
840         int64_t i_date = mdate();
841         bool b_eof;
842         block_t *b;
843
844         if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
845             ( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) )
846         {
847             int64_t i_byterate;
848
849             /* Update stat */
850             p_sys->stat.i_bytes = p_sys->block.i_size;
851             p_sys->stat.i_read_time = i_date - i_start;
852             i_byterate = ( INT64_C(1000000) * p_sys->stat.i_bytes ) /
853                          (p_sys->stat.i_read_time + 1);
854
855             msg_Dbg( s, "prebuffering done %"PRId64" bytes in %"PRId64"s - "
856                      "%"PRId64" kbytes/s",
857                      p_sys->stat.i_bytes,
858                      p_sys->stat.i_read_time / INT64_C(1000000),
859                      i_byterate / 1024 );
860             break;
861         }
862
863         /* Fetch a block */
864         if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
865         {
866             if( b_eof ) break;
867
868             msleep( STREAM_DATA_WAIT );
869             continue;
870         }
871
872         while( b )
873         {
874             /* Append the block */
875             p_sys->block.i_size += b->i_buffer;
876             *p_sys->block.pp_last = b;
877             p_sys->block.pp_last = &b->p_next;
878
879             p_sys->stat.i_read_count++;
880             b = b->p_next;
881         }
882
883         if( p_access->info.b_prebuffered )
884         {
885             /* Access has already prebufferred - update stats and exit */
886             p_sys->stat.i_bytes = p_sys->block.i_size;
887             p_sys->stat.i_read_time = mdate() - i_start;
888             break;
889         }
890
891         if( i_first == 0 )
892         {
893             i_first = mdate();
894             msg_Dbg( s, "received first data for our buffer");
895         }
896
897     }
898
899     p_sys->block.p_current = p_sys->block.p_first;
900 }
901
902 static int AStreamRefillBlock( stream_t *s );
903
904 static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read )
905 {
906     stream_sys_t *p_sys = s->p_sys;
907
908     uint8_t *p_data = p_read;
909     uint8_t *p_record = p_data;
910     unsigned int i_data = 0;
911
912     /* It means EOF */
913     if( p_sys->block.p_current == NULL )
914         return 0;
915
916     if( p_sys->record.b_active && !p_data )
917         p_record = p_data = malloc( i_read );
918
919     if( p_data == NULL )
920     {
921         /* seek within this stream if possible, else use plain old read and discard */
922         stream_sys_t *p_sys = s->p_sys;
923         access_t     *p_access = p_sys->p_access;
924         bool   b_aseek;
925         access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
926         if( b_aseek )
927             return AStreamSeekBlock( s, p_sys->i_pos + i_read ) ? 0 : i_read;
928     }
929
930     while( i_data < i_read )
931     {
932         int i_current =
933             p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
934         unsigned int i_copy = __MIN( (unsigned int)__MAX(i_current,0), i_read - i_data);
935
936         /* Copy data */
937         if( p_data )
938         {
939             memcpy( p_data,
940                     &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
941                     i_copy );
942             p_data += i_copy;
943         }
944         i_data += i_copy;
945
946         p_sys->block.i_offset += i_copy;
947         if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer )
948         {
949             /* Current block is now empty, switch to next */
950             if( p_sys->block.p_current )
951             {
952                 p_sys->block.i_offset = 0;
953                 p_sys->block.p_current = p_sys->block.p_current->p_next;
954             }
955             /*Get a new block if needed */
956             if( !p_sys->block.p_current && AStreamRefillBlock( s ) )
957             {
958                 break;
959             }
960         }
961     }
962
963     if( p_sys->record.b_active )
964     {
965         if( i_data > 0 && p_record != NULL)
966             ARecordWrite( s, p_record, i_data );
967         if( !p_read )
968             free( p_record );
969     }
970
971     p_sys->i_pos += i_data;
972     return i_data;
973 }
974
975 static int AStreamPeekBlock( stream_t *s, const uint8_t **pp_peek, unsigned int i_read )
976 {
977     stream_sys_t *p_sys = s->p_sys;
978     uint8_t *p_data;
979     unsigned int i_data = 0;
980     block_t *b;
981     unsigned int i_offset;
982
983     if( p_sys->block.p_current == NULL ) return 0; /* EOF */
984
985     /* We can directly give a pointer over our buffer */
986     if( i_read <= p_sys->block.p_current->i_buffer - p_sys->block.i_offset )
987     {
988         *pp_peek = &p_sys->block.p_current->p_buffer[p_sys->block.i_offset];
989         return i_read;
990     }
991
992     /* We need to create a local copy */
993     if( p_sys->i_peek < i_read )
994     {
995         p_sys->p_peek = realloc( p_sys->p_peek, i_read );
996         if( !p_sys->p_peek )
997         {
998             p_sys->i_peek = 0;
999             return 0;
1000         }
1001         p_sys->i_peek = i_read;
1002     }
1003
1004     /* Fill enough data */
1005     while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
1006            < i_read )
1007     {
1008         block_t **pp_last = p_sys->block.pp_last;
1009
1010         if( AStreamRefillBlock( s ) ) break;
1011
1012         /* Our buffer are probably filled enough, don't try anymore */
1013         if( pp_last == p_sys->block.pp_last ) break;
1014     }
1015
1016     /* Copy what we have */
1017     b = p_sys->block.p_current;
1018     i_offset = p_sys->block.i_offset;
1019     p_data = p_sys->p_peek;
1020
1021     while( b && i_data < i_read )
1022     {
1023         unsigned int i_current = __MAX(b->i_buffer - i_offset,0);
1024         int i_copy = __MIN( i_current, i_read - i_data );
1025
1026         memcpy( p_data, &b->p_buffer[i_offset], i_copy );
1027         i_data += i_copy;
1028         p_data += i_copy;
1029         i_offset += i_copy;
1030
1031         if( i_offset >= b->i_buffer )
1032         {
1033             i_offset = 0;
1034             b = b->p_next;
1035         }
1036     }
1037
1038     *pp_peek = p_sys->p_peek;
1039     return i_data;
1040 }
1041
1042 static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
1043 {
1044     stream_sys_t *p_sys = s->p_sys;
1045     access_t   *p_access = p_sys->p_access;
1046     int64_t    i_offset = i_pos - p_sys->block.i_start;
1047     bool b_seek;
1048
1049     /* We already have thoses data, just update p_current/i_offset */
1050     if( i_offset >= 0 && i_offset < p_sys->block.i_size )
1051     {
1052         block_t *b = p_sys->block.p_first;
1053         int i_current = 0;
1054
1055         while( i_current + b->i_buffer < i_offset )
1056         {
1057             i_current += b->i_buffer;
1058             b = b->p_next;
1059         }
1060
1061         p_sys->block.p_current = b;
1062         p_sys->block.i_offset = i_offset - i_current;
1063
1064         p_sys->i_pos = i_pos;
1065
1066         return VLC_SUCCESS;
1067     }
1068
1069     /* We may need to seek or to read data */
1070     if( i_offset < 0 )
1071     {
1072         bool b_aseek;
1073         access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
1074
1075         if( !b_aseek )
1076         {
1077             msg_Err( s, "backward seeking impossible (access not seekable)" );
1078             return VLC_EGENERIC;
1079         }
1080
1081         b_seek = true;
1082     }
1083     else
1084     {
1085         bool b_aseek, b_aseekfast;
1086
1087         access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
1088         access_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast );
1089
1090         if( !b_aseek )
1091         {
1092             b_seek = false;
1093             msg_Warn( s, "%"PRId64" bytes need to be skipped "
1094                       "(access non seekable)",
1095                       i_offset - p_sys->block.i_size );
1096         }
1097         else
1098         {
1099             int64_t i_skip = i_offset - p_sys->block.i_size;
1100
1101             /* Avg bytes per packets */
1102             int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count;
1103             /* TODO compute a seek cost instead of fixed threshold */
1104             int i_th = b_aseekfast ? 1 : 5;
1105
1106             if( i_skip <= i_th * i_avg &&
1107                 i_skip < STREAM_CACHE_SIZE )
1108                 b_seek = false;
1109             else
1110                 b_seek = true;
1111
1112             msg_Dbg( s, "b_seek=%d th*avg=%d skip=%"PRId64,
1113                      b_seek, i_th*i_avg, i_skip );
1114         }
1115     }
1116
1117     if( b_seek )
1118     {
1119         int64_t i_start, i_end;
1120         /* Do the access seek */
1121         i_start = mdate();
1122         if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
1123         i_end = mdate();
1124
1125         /* Release data */
1126         block_ChainRelease( p_sys->block.p_first );
1127
1128         /* Reinit */
1129         p_sys->block.i_start = p_sys->i_pos = i_pos;
1130         p_sys->block.i_offset = 0;
1131         p_sys->block.p_current = NULL;
1132         p_sys->block.i_size = 0;
1133         p_sys->block.p_first = NULL;
1134         p_sys->block.pp_last = &p_sys->block.p_first;
1135
1136         /* Refill a block */
1137         if( AStreamRefillBlock( s ) )
1138             return VLC_EGENERIC;
1139
1140         /* Update stat */
1141         p_sys->stat.i_seek_time += i_end - i_start;
1142         p_sys->stat.i_seek_count++;
1143         return VLC_SUCCESS;
1144     }
1145     else
1146     {
1147         do
1148         {
1149             /* Read and skip enough data */
1150             if( AStreamRefillBlock( s ) )
1151                 return VLC_EGENERIC;
1152
1153             while( p_sys->block.p_current &&
1154                    p_sys->i_pos + p_sys->block.p_current->i_buffer - p_sys->block.i_offset < i_pos )
1155             {
1156                 p_sys->i_pos += p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
1157                 p_sys->block.p_current = p_sys->block.p_current->p_next;
1158                 p_sys->block.i_offset = 0;
1159             }
1160         }
1161         while( p_sys->block.i_start + p_sys->block.i_size < i_pos );
1162
1163         p_sys->block.i_offset = i_pos - p_sys->i_pos;
1164         p_sys->i_pos = i_pos;
1165
1166         return VLC_SUCCESS;
1167     }
1168
1169     return VLC_EGENERIC;
1170 }
1171
1172 static int AStreamRefillBlock( stream_t *s )
1173 {
1174     stream_sys_t *p_sys = s->p_sys;
1175     int64_t      i_start, i_stop;
1176     block_t      *b;
1177
1178     /* Release data */
1179     while( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
1180            p_sys->block.p_first != p_sys->block.p_current )
1181     {
1182         block_t *b = p_sys->block.p_first;
1183
1184         p_sys->block.i_start += b->i_buffer;
1185         p_sys->block.i_size  -= b->i_buffer;
1186         p_sys->block.p_first  = b->p_next;
1187
1188         block_Release( b );
1189     }
1190     if( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
1191         p_sys->block.p_current == p_sys->block.p_first &&
1192         p_sys->block.p_current->p_next )    /* At least 2 packets */
1193     {
1194         /* Enough data, don't read more */
1195         return VLC_SUCCESS;
1196     }
1197
1198     /* Now read a new block */
1199     i_start = mdate();
1200     for( ;; )
1201     {
1202         bool b_eof;
1203
1204         if( s->b_die ) return VLC_EGENERIC;
1205
1206
1207         /* Fetch a block */
1208         if( ( b = AReadBlock( s, &b_eof ) ) ) break;
1209
1210         if( b_eof ) return VLC_EGENERIC;
1211
1212         msleep( STREAM_DATA_WAIT );
1213     }
1214
1215     while( b )
1216     {
1217         i_stop = mdate();
1218
1219         /* Append the block */
1220         p_sys->block.i_size += b->i_buffer;
1221         *p_sys->block.pp_last = b;
1222         p_sys->block.pp_last = &b->p_next;
1223
1224         /* Fix p_current */
1225         if( p_sys->block.p_current == NULL )
1226             p_sys->block.p_current = b;
1227
1228         /* Update stat */
1229         p_sys->stat.i_bytes += b->i_buffer;
1230         p_sys->stat.i_read_time += i_stop - i_start;
1231         p_sys->stat.i_read_count++;
1232
1233         b = b->p_next;
1234         i_start = mdate();
1235     }
1236     return VLC_SUCCESS;
1237 }
1238
1239
1240 /****************************************************************************
1241  * Method 2:
1242  ****************************************************************************/
1243 static int AStreamRefillStream( stream_t *s );
1244
1245 static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read )
1246 {
1247     stream_sys_t *p_sys = s->p_sys;
1248     stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1249
1250     uint8_t *p_data = (uint8_t *)p_read;
1251     uint8_t *p_record = p_data;
1252     unsigned int i_data = 0;
1253
1254     if( tk->i_start >= tk->i_end )
1255         return 0; /* EOF */
1256
1257     if( p_sys->record.b_active && !p_data )
1258         p_record = p_data = malloc( i_read );
1259
1260     if( p_data == NULL )
1261     {
1262         /* seek within this stream if possible, else use plain old read and discard */
1263         stream_sys_t *p_sys = s->p_sys;
1264         access_t     *p_access = p_sys->p_access;
1265
1266         /* seeking after EOF is not what we want */
1267         if( !( p_access->info.b_eof ) )
1268         {
1269             bool   b_aseek;
1270             access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
1271             if( b_aseek )
1272                 return AStreamSeekStream( s, p_sys->i_pos + i_read ) ? 0 : i_read;
1273         }
1274     }
1275
1276 #ifdef STREAM_DEBUG
1277     msg_Dbg( s, "AStreamReadStream: %d pos=%"PRId64" tk=%d start=%"PRId64
1278              " offset=%d end=%"PRId64,
1279              i_read, p_sys->i_pos, p_sys->stream.i_tk,
1280              tk->i_start, p_sys->stream.i_offset, tk->i_end );
1281 #endif
1282
1283     while( i_data < i_read )
1284     {
1285         int i_off = (tk->i_start + p_sys->stream.i_offset) %
1286                     STREAM_CACHE_TRACK_SIZE;
1287         unsigned int i_current =
1288             __MAX(0,__MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset,
1289                    STREAM_CACHE_TRACK_SIZE - i_off ));
1290         int i_copy = __MIN( i_current, i_read - i_data );
1291
1292         if( i_copy <= 0 ) break; /* EOF */
1293
1294         /* Copy data */
1295         /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */
1296         if( p_data )
1297         {
1298             memcpy( p_data, &tk->p_buffer[i_off], i_copy );
1299             p_data += i_copy;
1300         }
1301         i_data += i_copy;
1302         p_sys->stream.i_offset += i_copy;
1303
1304         /* Update pos now */
1305         p_sys->i_pos += i_copy;
1306
1307         /* */
1308         p_sys->stream.i_used += i_copy;
1309         if( tk->i_start + p_sys->stream.i_offset >= tk->i_end ||
1310             p_sys->stream.i_used >= p_sys->stream.i_read_size )
1311         {
1312             if( AStreamRefillStream( s ) )
1313             {
1314                 /* EOF */
1315                 if( tk->i_start >= tk->i_end ) break;
1316             }
1317         }
1318     }
1319
1320     if( p_sys->record.b_active )
1321     {
1322         if( i_data > 0 && p_record != NULL)
1323             ARecordWrite( s, p_record, i_data );
1324         if( !p_read )
1325             free( p_record );
1326     }
1327
1328     return i_data;
1329 }
1330
1331 static int AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read )
1332 {
1333     stream_sys_t *p_sys = s->p_sys;
1334     stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1335     int64_t i_off;
1336
1337     if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1338
1339 #ifdef STREAM_DEBUG
1340     msg_Dbg( s, "AStreamPeekStream: %d pos=%"PRId64" tk=%d "
1341              "start=%"PRId64" offset=%d end=%"PRId64,
1342              i_read, p_sys->i_pos, p_sys->stream.i_tk,
1343              tk->i_start, p_sys->stream.i_offset, tk->i_end );
1344 #endif
1345
1346     /* Avoid problem, but that should *never* happen */
1347     if( i_read > STREAM_CACHE_TRACK_SIZE / 2 )
1348         i_read = STREAM_CACHE_TRACK_SIZE / 2;
1349
1350     while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1351     {
1352         if( p_sys->stream.i_used <= 1 )
1353         {
1354             /* Be sure we will read something */
1355             p_sys->stream.i_used += i_read -
1356                 (tk->i_end - tk->i_start - p_sys->stream.i_offset);
1357         }
1358         if( AStreamRefillStream( s ) ) break;
1359     }
1360
1361     if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1362         i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset;
1363
1364     /* Now, direct pointer or a copy ? */
1365     i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
1366     if( i_off + i_read <= STREAM_CACHE_TRACK_SIZE )
1367     {
1368         *pp_peek = &tk->p_buffer[i_off];
1369         return i_read;
1370     }
1371
1372     if( p_sys->i_peek < i_read )
1373     {
1374         p_sys->p_peek = realloc( p_sys->p_peek, i_read );
1375         if( !p_sys->p_peek )
1376         {
1377             p_sys->i_peek = 0;
1378             return 0;
1379         }
1380         p_sys->i_peek = i_read;
1381     }
1382
1383     memcpy( p_sys->p_peek, &tk->p_buffer[i_off],
1384             STREAM_CACHE_TRACK_SIZE - i_off );
1385     memcpy( &p_sys->p_peek[STREAM_CACHE_TRACK_SIZE - i_off],
1386             &tk->p_buffer[0], i_read - (STREAM_CACHE_TRACK_SIZE - i_off) );
1387
1388     *pp_peek = p_sys->p_peek;
1389     return i_read;
1390 }
1391
1392 static int AStreamSeekStream( stream_t *s, int64_t i_pos )
1393 {
1394     stream_sys_t *p_sys = s->p_sys;
1395     access_t     *p_access = p_sys->p_access;
1396     bool   b_aseek;
1397     bool   b_afastseek;
1398     int i_maxth;
1399     int i_new;
1400     int i;
1401
1402 #ifdef STREAM_DEBUG
1403     msg_Dbg( s, "AStreamSeekStream: to %"PRId64" pos=%"PRId64
1404              " tk=%d start=%"PRId64" offset=%d end=%"PRId64,
1405              i_pos, p_sys->i_pos, p_sys->stream.i_tk,
1406              p_sys->stream.tk[p_sys->stream.i_tk].i_start,
1407              p_sys->stream.i_offset,
1408              p_sys->stream.tk[p_sys->stream.i_tk].i_end );
1409 #endif
1410
1411
1412     /* Seek in our current track ? */
1413     if( i_pos >= p_sys->stream.tk[p_sys->stream.i_tk].i_start &&
1414         i_pos < p_sys->stream.tk[p_sys->stream.i_tk].i_end )
1415     {
1416         stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1417 #ifdef STREAM_DEBUG
1418         msg_Dbg( s, "AStreamSeekStream: current track" );
1419 #endif
1420         p_sys->i_pos = i_pos;
1421         p_sys->stream.i_offset = i_pos - tk->i_start;
1422
1423         /* If there is not enough data left in the track, refill  */
1424         /* \todo How to get a correct value for
1425          *    - refilling threshold
1426          *    - how much to refill
1427          */
1428         if( (tk->i_end - tk->i_start ) - p_sys->stream.i_offset <
1429                                              p_sys->stream.i_read_size )
1430         {
1431             if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2  )
1432             {
1433                 p_sys->stream.i_used = STREAM_READ_ATONCE / 2 ;
1434                 AStreamRefillStream( s );
1435             }
1436         }
1437         return VLC_SUCCESS;
1438     }
1439
1440     access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
1441     if( !b_aseek )
1442     {
1443         /* We can't do nothing */
1444         msg_Dbg( s, "AStreamSeekStream: can't seek" );
1445         return VLC_EGENERIC;
1446     }
1447
1448     /* Date the current track */
1449     p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1450
1451     /* Try to reuse already read data */
1452     for( i = 0; i < STREAM_CACHE_TRACK; i++ )
1453     {
1454         stream_track_t *tk = &p_sys->stream.tk[i];
1455
1456         if( i_pos >= tk->i_start && i_pos <= tk->i_end )
1457         {
1458 #ifdef STREAM_DEBUG
1459             msg_Dbg( s, "AStreamSeekStream: reusing %d start=%"PRId64
1460                      " end=%"PRId64, i, tk->i_start, tk->i_end );
1461 #endif
1462
1463             /* Seek at the end of the buffer */
1464             if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
1465
1466             /* That's it */
1467             p_sys->i_pos = i_pos;
1468             p_sys->stream.i_tk = i;
1469             p_sys->stream.i_offset = i_pos - tk->i_start;
1470
1471             if( p_sys->stream.i_used < 1024 )
1472                 p_sys->stream.i_used = 1024;
1473
1474             if( AStreamRefillStream( s ) && i_pos == tk->i_end )
1475                 return VLC_EGENERIC;
1476
1477             return VLC_SUCCESS;
1478         }
1479     }
1480
1481     access_Control( p_access, ACCESS_CAN_SEEK, &b_afastseek );
1482     /* FIXME compute seek cost (instead of static 'stupid' value) */
1483     i_maxth = __MIN( p_sys->stream.i_read_size, STREAM_READ_ATONCE / 2 );
1484     if( !b_afastseek )
1485         i_maxth *= 3;
1486
1487     /* FIXME TODO */
1488 #if 0
1489     /* Search closest segment TODO */
1490     for( i = 0; i < STREAM_CACHE_TRACK; i++ )
1491     {
1492         stream_track_t *tk = &p_sys->stream.tk[i];
1493
1494         if( i_pos + i_maxth >= tk->i_start )
1495         {
1496             msg_Dbg( s, "good segment before current pos, TODO" );
1497         }
1498         if( i_pos - i_maxth <= tk->i_end )
1499         {
1500             msg_Dbg( s, "good segment after current pos, TODO" );
1501         }
1502     }
1503 #endif
1504
1505     /* Nothing good, seek and choose oldest segment */
1506     if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
1507     p_sys->i_pos = i_pos;
1508
1509     i_new = 0;
1510     for( i = 1; i < STREAM_CACHE_TRACK; i++ )
1511     {
1512         if( p_sys->stream.tk[i].i_date < p_sys->stream.tk[i_new].i_date )
1513             i_new = i;
1514     }
1515
1516     /* Reset the segment */
1517     p_sys->stream.i_tk     = i_new;
1518     p_sys->stream.i_offset =  0;
1519     p_sys->stream.tk[i_new].i_start = i_pos;
1520     p_sys->stream.tk[i_new].i_end   = i_pos;
1521
1522     /* Read data */
1523     if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 )
1524         p_sys->stream.i_used = STREAM_READ_ATONCE / 2;
1525
1526     if( AStreamRefillStream( s ) )
1527         return VLC_EGENERIC;
1528
1529     return VLC_SUCCESS;
1530 }
1531
1532 static int AStreamRefillStream( stream_t *s )
1533 {
1534     stream_sys_t *p_sys = s->p_sys;
1535     stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1536
1537     /* We read but won't increase i_start after initial start + offset */
1538     int i_toread =
1539         __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE -
1540                (tk->i_end - tk->i_start - p_sys->stream.i_offset) );
1541     bool b_read = false;
1542     int64_t i_start, i_stop;
1543
1544     if( i_toread <= 0 ) return VLC_EGENERIC; /* EOF */
1545
1546 #ifdef STREAM_DEBUG
1547     msg_Dbg( s, "AStreamRefillStream: used=%d toread=%d",
1548                  p_sys->stream.i_used, i_toread );
1549 #endif
1550
1551     i_start = mdate();
1552     while( i_toread > 0 )
1553     {
1554         int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE;
1555         int i_read;
1556
1557         if( s->b_die )
1558             return VLC_EGENERIC;
1559
1560         i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off );
1561         i_read = AReadStream( s, &tk->p_buffer[i_off], i_read );
1562
1563         /* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */
1564         if( i_read <  0 )
1565         {
1566             msleep( STREAM_DATA_WAIT );
1567             continue;
1568         }
1569         else if( i_read == 0 )
1570         {
1571             if( !b_read ) return VLC_EGENERIC;
1572             return VLC_SUCCESS;
1573         }
1574         b_read = true;
1575
1576         /* Update end */
1577         tk->i_end += i_read;
1578
1579         /* Windows of STREAM_CACHE_TRACK_SIZE */
1580         if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE )
1581         {
1582             int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
1583
1584             tk->i_start += i_invalid;
1585             p_sys->stream.i_offset -= i_invalid;
1586         }
1587
1588         i_toread -= i_read;
1589         p_sys->stream.i_used -= i_read;
1590
1591         p_sys->stat.i_bytes += i_read;
1592         p_sys->stat.i_read_count++;
1593     }
1594     i_stop = mdate();
1595
1596     p_sys->stat.i_read_time += i_stop - i_start;
1597
1598     return VLC_SUCCESS;
1599 }
1600
1601 static void AStreamPrebufferStream( stream_t *s )
1602 {
1603     stream_sys_t *p_sys = s->p_sys;
1604     access_t     *p_access = p_sys->p_access;
1605
1606     int64_t i_first = 0;
1607     int64_t i_start;
1608     int64_t i_prebuffer = p_sys->b_quick ? STREAM_CACHE_TRACK_SIZE /100 :
1609         ( (p_access->info.i_title > 1 || p_access->info.i_seekpoint > 1) ?
1610           STREAM_CACHE_PREBUFFER_SIZE : STREAM_CACHE_TRACK_SIZE / 3 );
1611
1612     msg_Dbg( s, "pre-buffering..." );
1613     i_start = mdate();
1614     for( ;; )
1615     {
1616         stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1617
1618         int64_t i_date = mdate();
1619         int i_read;
1620
1621         if( s->b_die || tk->i_end >= i_prebuffer ||
1622             (i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date) )
1623         {
1624             int64_t i_byterate;
1625
1626             /* Update stat */
1627             p_sys->stat.i_bytes = tk->i_end - tk->i_start;
1628             p_sys->stat.i_read_time = i_date - i_start;
1629             i_byterate = ( INT64_C(1000000) * p_sys->stat.i_bytes ) /
1630                          (p_sys->stat.i_read_time+1);
1631
1632             msg_Dbg( s, "pre-buffering done %"PRId64" bytes in %"PRId64"s - "
1633                      "%"PRId64" kbytes/s",
1634                      p_sys->stat.i_bytes,
1635                      p_sys->stat.i_read_time / INT64_C(1000000),
1636                      i_byterate / 1024 );
1637             break;
1638         }
1639
1640         /* */
1641         i_read = STREAM_CACHE_TRACK_SIZE - tk->i_end;
1642         i_read = __MIN( p_sys->stream.i_read_size, i_read );
1643         i_read = AReadStream( s, &tk->p_buffer[tk->i_end], i_read );
1644         if( i_read <  0 )
1645         {
1646             msleep( STREAM_DATA_WAIT );
1647             continue;
1648         }
1649         else if( i_read == 0 )
1650         {
1651             /* EOF */
1652             break;
1653         }
1654
1655         if( i_first == 0 )
1656         {
1657             i_first = mdate();
1658             msg_Dbg( s, "received first data for our buffer");
1659         }
1660
1661         tk->i_end += i_read;
1662
1663         p_sys->stat.i_read_count++;
1664     }
1665 }
1666
1667 /****************************************************************************
1668  * Method 3:
1669  ****************************************************************************/
1670
1671 static int AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read )
1672 {
1673     stream_sys_t *p_sys = s->p_sys;
1674     uint8_t *p_data= (uint8_t*)p_read;
1675     uint8_t *p_record = p_data;
1676     unsigned int i_data;
1677
1678 #ifdef STREAM_DEBUG
1679     msg_Dbg( s, "AStreamReadImmediate p_read=%p i_read=%d",
1680              p_read, i_read );
1681 #endif
1682
1683     if( p_sys->record.b_active && !p_data )
1684         p_record = p_data = malloc( i_read );
1685
1686     /* First, check if we already have some data in the buffer,
1687      * that we could copy directly */
1688     i_data = __MIN( stream_buffered_size( s ), i_read );
1689     if( i_data > 0 )
1690     {
1691 #ifdef STREAM_DEBUG
1692         msg_Dbg( s, "AStreamReadImmediate: copy %u from %p", i_data, stream_buffer( s ) );
1693 #endif
1694
1695         assert( i_data <= STREAM_CACHE_SIZE );
1696
1697         if( p_data )
1698         {
1699             memcpy( p_data, stream_buffer( s ), i_data );
1700             p_data += i_data;
1701         }
1702     }
1703
1704     /* Now that we've read our buffer we don't need its i_copy bytes */
1705     stream_buffer_empty( s, i_data );
1706
1707     /* Now check if we have still to really read some data */
1708     while( i_data < i_read )
1709     {
1710         const unsigned int i_to_read = i_read - i_data;
1711         int i_result;
1712
1713         if( p_data )
1714         {
1715             i_result = AReadStream( s, p_data, i_to_read );
1716         }
1717         else
1718         {
1719             void *p_dummy = malloc( i_to_read );
1720
1721             if( !p_dummy )
1722                 break;
1723
1724             i_result = AReadStream( s, p_dummy, i_to_read );
1725
1726             free( p_dummy );
1727         }
1728         if( i_result <= 0 )
1729             break;
1730
1731         p_sys->i_pos += i_data;
1732         if( p_data )
1733             p_data += i_result;
1734         i_data += i_result;
1735     }
1736
1737     if( p_sys->record.b_active )
1738     {
1739         if( i_data > 0 && p_record != NULL)
1740             ARecordWrite( s, p_record, i_data );
1741         if( !p_read )
1742             free( p_record );
1743     }
1744     return i_data;
1745 }
1746
1747 static int AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, unsigned int i_read )
1748 {
1749 #ifdef STREAM_DEBUG
1750     msg_Dbg( s, "AStreamPeekImmediate: %d  size=%"PRId64,
1751              i_read, size_buffered_size( s ) );
1752 #endif
1753
1754     /* Avoid problem, but that shouldn't happen
1755      * FIXME yes it can */
1756     if( i_read > STREAM_CACHE_SIZE / 2 )
1757         i_read = STREAM_CACHE_SIZE / 2;
1758
1759     int i_to_read = i_read - stream_buffered_size( s );
1760     if( i_to_read > 0 )
1761     {
1762 #ifdef STREAM_DEBUG
1763         msg_Dbg( s, "AStreamPeekImmediate: Reading %d",
1764              i_to_read );
1765 #endif
1766         i_to_read = AReadStream( s, stream_buffer( s ) + stream_buffered_size( s ),
1767                                  i_to_read );
1768
1769         if( i_to_read > 0 )
1770             stream_buffer_fill( s, i_to_read );
1771     }
1772
1773     *pp_peek = stream_buffer( s );
1774
1775     return __MIN( stream_buffered_size( s ), i_read );
1776 }
1777
1778 static int AStreamSeekImmediate( stream_t *s, int64_t i_pos )
1779 {
1780     stream_sys_t *p_sys = s->p_sys;
1781     access_t     *p_access = p_sys->p_access;
1782     bool   b_aseek;
1783
1784 #ifdef STREAM_DEBUG
1785     msg_Dbg( s, "AStreamSeekImmediate to %"PRId64" pos=%"PRId64
1786              i_pos, p_sys->i_pos );
1787 #endif
1788
1789     access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
1790     if( !b_aseek )
1791     {
1792         /* We can't do nothing */
1793         msg_Dbg( s, "AStreamSeekImmediate: can't seek" );
1794         return VLC_EGENERIC;
1795     }
1796
1797     /* Just reset our buffer */
1798     stream_buffer_empty( s, stream_buffered_size( s ) );
1799
1800     if( ASeek( s, i_pos ) )
1801         return VLC_EGENERIC;
1802
1803     return VLC_SUCCESS;
1804 }
1805
1806 /****************************************************************************
1807  * stream_ReadLine:
1808  ****************************************************************************/
1809 /**
1810  * Read from the stream untill first newline.
1811  * \param s Stream handle to read from
1812  * \return A pointer to the allocated output string. You need to free this when you are done.
1813  */
1814 #define STREAM_PROBE_LINE 2048
1815 #define STREAM_LINE_MAX (2048*100)
1816 char *stream_ReadLine( stream_t *s )
1817 {
1818     char *p_line = NULL;
1819     int i_line = 0, i_read = 0;
1820
1821     while( i_read < STREAM_LINE_MAX )
1822     {
1823         char *psz_eol;
1824         const uint8_t *p_data;
1825         int i_data;
1826         int64_t i_pos;
1827
1828         /* Probe new data */
1829         i_data = stream_Peek( s, &p_data, STREAM_PROBE_LINE );
1830         if( i_data <= 0 ) break; /* No more data */
1831
1832         /* BOM detection */
1833         i_pos = stream_Tell( s );
1834         if( i_pos == 0 && i_data >= 3 )
1835         {
1836             int i_bom_size = 0;
1837             const char *psz_encoding = NULL;
1838
1839             if( !memcmp( p_data, "\xEF\xBB\xBF", 3 ) )
1840             {
1841                 psz_encoding = "UTF-8";
1842                 i_bom_size = 3;
1843             }
1844             else if( !memcmp( p_data, "\xFF\xFE", 2 ) )
1845             {
1846                 psz_encoding = "UTF-16LE";
1847                 s->b_little_endian = true;
1848                 s->i_char_width = 2;
1849                 i_bom_size = 2;
1850             }
1851             else if( !memcmp( p_data, "\xFE\xFF", 2 ) )
1852             {
1853                 psz_encoding = "UTF-16BE";
1854                 s->i_char_width = 2;
1855                 i_bom_size = 2;
1856             }
1857
1858             /* Seek past the BOM */
1859             if( i_bom_size )
1860             {
1861                 stream_Seek( s, i_bom_size );
1862                 p_data += i_bom_size;
1863                 i_data -= i_bom_size;
1864             }
1865
1866             /* Open the converter if we need it */
1867             if( psz_encoding != NULL )
1868             {
1869                 input_thread_t *p_input;
1870                 msg_Dbg( s, "%s BOM detected", psz_encoding );
1871                 p_input = (input_thread_t *)vlc_object_find( s, VLC_OBJECT_INPUT, FIND_PARENT );
1872                 if( s->i_char_width > 1 )
1873                 {
1874                     s->conv = vlc_iconv_open( "UTF-8", psz_encoding );
1875                     if( s->conv == (vlc_iconv_t)-1 )
1876                     {
1877                         msg_Err( s, "iconv_open failed" );
1878                     }
1879                 }
1880                 if( p_input != NULL)
1881                 {
1882                     var_Create( p_input, "subsdec-encoding", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
1883                     var_SetString( p_input, "subsdec-encoding", "UTF-8" );
1884                     vlc_object_release( p_input );
1885                 }
1886             }
1887         }
1888
1889         if( i_data % s->i_char_width )
1890         {
1891             /* keep i_char_width boundary */
1892             i_data = i_data - ( i_data % s->i_char_width );
1893             msg_Warn( s, "the read is not i_char_width compatible");
1894         }
1895
1896         if( i_data == 0 )
1897             break;
1898
1899         /* Check if there is an EOL */
1900         if( s->i_char_width == 1 )
1901         {
1902             /* UTF-8: 0A <LF> */
1903             psz_eol = memchr( p_data, '\n', i_data );
1904         }
1905         else
1906         {
1907             const uint8_t *p = p_data;
1908             const uint8_t *p_last = p + i_data - s->i_char_width;
1909
1910             if( s->i_char_width == 2 )
1911             {
1912                 if( s->b_little_endian == true)
1913                 {
1914                     /* UTF-16LE: 0A 00 <LF> */
1915                     while( p <= p_last && ( p[0] != 0x0A || p[1] != 0x00 ) )
1916                         p += 2;
1917                 }
1918                 else
1919                 {
1920                     /* UTF-16BE: 00 0A <LF> */
1921                     while( p <= p_last && ( p[1] != 0x0A || p[0] != 0x00 ) )
1922                         p += 2;
1923                 }
1924             }
1925
1926             if( p > p_last )
1927             {
1928                 psz_eol = NULL;
1929             }
1930             else
1931             {
1932                 psz_eol = (char *)p + ( s->i_char_width - 1 );
1933             }
1934         }
1935
1936         if(psz_eol)
1937         {
1938             i_data = (psz_eol - (char *)p_data) + 1;
1939             p_line = realloc( p_line, i_line + i_data + s->i_char_width ); /* add \0 */
1940             if( !p_line )
1941                 goto error;
1942             i_data = stream_Read( s, &p_line[i_line], i_data );
1943             if( i_data <= 0 ) break; /* Hmmm */
1944             i_line += i_data - s->i_char_width; /* skip \n */;
1945             i_read += i_data;
1946
1947             /* We have our line */
1948             break;
1949         }
1950
1951         /* Read data (+1 for easy \0 append) */
1952         p_line = realloc( p_line, i_line + STREAM_PROBE_LINE + s->i_char_width );
1953         if( !p_line )
1954             goto error;
1955         i_data = stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE );
1956         if( i_data <= 0 ) break; /* Hmmm */
1957         i_line += i_data;
1958         i_read += i_data;
1959     }
1960
1961     if( i_read > 0 )
1962     {
1963         int j;
1964         for( j = 0; j < s->i_char_width; j++ )
1965         {
1966             p_line[i_line + j] = '\0';
1967         }
1968         i_line += s->i_char_width; /* the added \0 */
1969         if( s->i_char_width > 1 )
1970         {
1971             size_t i_in = 0, i_out = 0;
1972             const char * p_in = NULL;
1973             char * p_out = NULL;
1974             char * psz_new_line = NULL;
1975
1976             /* iconv */
1977             psz_new_line = malloc( i_line );
1978             if( psz_new_line == NULL )
1979                 goto error;
1980             i_in = i_out = (size_t)i_line;
1981             p_in = p_line;
1982             p_out = psz_new_line;
1983
1984             if( vlc_iconv( s->conv, &p_in, &i_in, &p_out, &i_out ) == (size_t)-1 )
1985             {
1986                 msg_Err( s, "iconv failed" );
1987                 msg_Dbg( s, "original: %d, in %d, out %d", i_line, (int)i_in, (int)i_out );
1988             }
1989             free( p_line );
1990             p_line = psz_new_line;
1991             i_line = (size_t)i_line - i_out; /* does not include \0 */
1992         }
1993
1994         /* Remove trailing LF/CR */
1995         while( i_line >= 2 && ( p_line[i_line-2] == '\r' ||
1996             p_line[i_line-2] == '\n') ) i_line--;
1997
1998         /* Make sure the \0 is there */
1999         p_line[i_line-1] = '\0';
2000
2001         return p_line;
2002     }
2003
2004 error:
2005
2006     /* We failed to read any data, probably EOF */
2007     free( p_line );
2008     if( s->conv != (vlc_iconv_t)(-1) ) vlc_iconv_close( s->conv );
2009     return NULL;
2010 }
2011
2012 /****************************************************************************
2013  * Access reading/seeking wrappers to handle concatenated streams.
2014  ****************************************************************************/
2015 static int AReadStream( stream_t *s, void *p_read, unsigned int i_read )
2016 {
2017     stream_sys_t *p_sys = s->p_sys;
2018     access_t *p_access = p_sys->p_access;
2019     input_thread_t *p_input = NULL;
2020     int i_read_orig = i_read;
2021     int i_total = 0;
2022
2023     if( s->p_parent && s->p_parent->p_parent &&
2024         s->p_parent->p_parent->i_object_type == VLC_OBJECT_INPUT )
2025         p_input = (input_thread_t *)s->p_parent->p_parent;
2026
2027     if( !p_sys->i_list )
2028     {
2029         i_read = p_access->pf_read( p_access, p_read, i_read );
2030         if( p_access->b_die )
2031             vlc_object_kill( s );
2032         if( p_input )
2033         {
2034             vlc_mutex_lock( &p_input->p->counters.counters_lock );
2035             stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, i_read,
2036                              &i_total );
2037             stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
2038                            (float)i_total, NULL );
2039             stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL );
2040             vlc_mutex_unlock( &p_input->p->counters.counters_lock );
2041         }
2042         return i_read;
2043     }
2044
2045     i_read = p_sys->p_list_access->pf_read( p_sys->p_list_access, p_read,
2046                                             i_read );
2047     if( p_access->b_die )
2048         vlc_object_kill( s );
2049
2050     /* If we reached an EOF then switch to the next stream in the list */
2051     if( i_read == 0 && p_sys->i_list_index + 1 < p_sys->i_list )
2052     {
2053         char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path;
2054         access_t *p_list_access;
2055
2056         msg_Dbg( s, "opening input `%s'", psz_name );
2057
2058         p_list_access = access_New( s, p_access->psz_access, "", psz_name );
2059
2060         if( !p_list_access ) return 0;
2061
2062         if( p_sys->p_list_access != p_access )
2063             access_Delete( p_sys->p_list_access );
2064
2065         p_sys->p_list_access = p_list_access;
2066
2067         /* We have to read some data */
2068         return AReadStream( s, p_read, i_read_orig );
2069     }
2070
2071     /* Update read bytes in input */
2072     if( p_input )
2073     {
2074         vlc_mutex_lock( &p_input->p->counters.counters_lock );
2075         stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, i_read, &i_total );
2076         stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
2077                        (float)i_total, NULL );
2078         stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL );
2079         vlc_mutex_unlock( &p_input->p->counters.counters_lock );
2080     }
2081     return i_read;
2082 }
2083
2084 static block_t *AReadBlock( stream_t *s, bool *pb_eof )
2085 {
2086     stream_sys_t *p_sys = s->p_sys;
2087     access_t *p_access = p_sys->p_access;
2088     input_thread_t *p_input = NULL;
2089     block_t *p_block;
2090     bool b_eof;
2091     int i_total = 0;
2092
2093     if( s->p_parent && s->p_parent->p_parent &&
2094         s->p_parent->p_parent->i_object_type == VLC_OBJECT_INPUT )
2095         p_input = (input_thread_t *)s->p_parent->p_parent;
2096
2097     if( !p_sys->i_list )
2098     {
2099         p_block = p_access->pf_block( p_access );
2100         if( p_access->b_die )
2101             vlc_object_kill( s );
2102         if( pb_eof ) *pb_eof = p_access->info.b_eof;
2103         if( p_input && p_block && libvlc_stats (p_access) )
2104         {
2105             vlc_mutex_lock( &p_input->p->counters.counters_lock );
2106             stats_UpdateInteger( s, p_input->p->counters.p_read_bytes,
2107                                  p_block->i_buffer, &i_total );
2108             stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
2109                               (float)i_total, NULL );
2110             stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL );
2111             vlc_mutex_unlock( &p_input->p->counters.counters_lock );
2112         }
2113         return p_block;
2114     }
2115
2116     p_block = p_sys->p_list_access->pf_block( p_access );
2117     if( p_access->b_die )
2118         vlc_object_kill( s );
2119     b_eof = p_sys->p_list_access->info.b_eof;
2120     if( pb_eof ) *pb_eof = b_eof;
2121
2122     /* If we reached an EOF then switch to the next stream in the list */
2123     if( !p_block && b_eof && p_sys->i_list_index + 1 < p_sys->i_list )
2124     {
2125         char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path;
2126         access_t *p_list_access;
2127
2128         msg_Dbg( s, "opening input `%s'", psz_name );
2129
2130         p_list_access = access_New( s, p_access->psz_access, "", psz_name );
2131
2132         if( !p_list_access ) return 0;
2133
2134         if( p_sys->p_list_access != p_access )
2135             access_Delete( p_sys->p_list_access );
2136
2137         p_sys->p_list_access = p_list_access;
2138
2139         /* We have to read some data */
2140         return AReadBlock( s, pb_eof );
2141     }
2142     if( p_block )
2143     {
2144         if( p_input )
2145         {
2146             vlc_mutex_lock( &p_input->p->counters.counters_lock );
2147             stats_UpdateInteger( s, p_input->p->counters.p_read_bytes,
2148                                  p_block->i_buffer, &i_total );
2149             stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate,
2150                               (float)i_total, NULL );
2151             stats_UpdateInteger( s, p_input->p->counters.p_read_packets,
2152                                  1 , NULL);
2153             vlc_mutex_unlock( &p_input->p->counters.counters_lock );
2154         }
2155     }
2156     return p_block;
2157 }
2158
2159 static int ASeek( stream_t *s, int64_t i_pos )
2160 {
2161     stream_sys_t *p_sys = s->p_sys;
2162     access_t *p_access = p_sys->p_access;
2163
2164     /* Check which stream we need to access */
2165     if( p_sys->i_list )
2166     {
2167         int i;
2168         char *psz_name;
2169         int64_t i_size = 0;
2170         access_t *p_list_access = 0;
2171
2172         for( i = 0; i < p_sys->i_list - 1; i++ )
2173         {
2174             if( i_pos < p_sys->list[i]->i_size + i_size ) break;
2175             i_size += p_sys->list[i]->i_size;
2176         }
2177         psz_name = p_sys->list[i]->psz_path;
2178
2179         if( i != p_sys->i_list_index )
2180             msg_Dbg( s, "opening input `%s'", psz_name );
2181
2182         if( i != p_sys->i_list_index && i != 0 )
2183         {
2184             p_list_access =
2185                 access_New( s, p_access->psz_access, "", psz_name );
2186         }
2187         else if( i != p_sys->i_list_index )
2188         {
2189             p_list_access = p_access;
2190         }
2191
2192         if( p_list_access )
2193         {
2194             if( p_sys->p_list_access != p_access )
2195                 access_Delete( p_sys->p_list_access );
2196
2197             p_sys->p_list_access = p_list_access;
2198         }
2199
2200         p_sys->i_list_index = i;
2201         return p_sys->p_list_access->pf_seek( p_sys->p_list_access,
2202                                               i_pos - i_size );
2203     }
2204
2205     return p_access->pf_seek( p_access, i_pos );
2206 }
2207
2208
2209 /**
2210  * Try to read "i_read" bytes into a buffer pointed by "p_read".  If
2211  * "p_read" is NULL then data are skipped instead of read.  The return
2212  * value is the real numbers of bytes read/skip. If this value is less
2213  * than i_read that means that it's the end of the stream.
2214  */
2215 int stream_Read( stream_t *s, void *p_read, int i_read )
2216 {
2217     return s->pf_read( s, p_read, i_read );
2218 }
2219
2220 /**
2221  * Store in pp_peek a pointer to the next "i_peek" bytes in the stream
2222  * \return The real numbers of valid bytes, if it's less
2223  * or equal to 0, *pp_peek is invalid.
2224  * \note pp_peek is a pointer to internal buffer and it will be invalid as
2225  * soons as other stream_* functions are called.
2226  * \note Due to input limitation, it could be less than i_peek without meaning
2227  * the end of the stream (but only when you have i_peek >=
2228  * p_input->i_bufsize)
2229  */
2230 int stream_Peek( stream_t *s, const uint8_t **pp_peek, int i_peek )
2231 {
2232     return s->pf_peek( s, pp_peek, i_peek );
2233 }
2234
2235 /**
2236  * Use to control the "stream_t *". Look at #stream_query_e for
2237  * possible "i_query" value and format arguments.  Return VLC_SUCCESS
2238  * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
2239  */
2240 int stream_vaControl( stream_t *s, int i_query, va_list args )
2241 {
2242     return s->pf_control( s, i_query, args );
2243 }
2244
2245 /**
2246  * Destroy a stream
2247  */
2248 void stream_Delete( stream_t *s )
2249 {
2250     s->pf_destroy( s );
2251 }
2252
2253 int stream_Control( stream_t *s, int i_query, ... )
2254 {
2255     va_list args;
2256     int     i_result;
2257
2258     if( s == NULL )
2259         return VLC_EGENERIC;
2260
2261     va_start( args, i_query );
2262     i_result = s->pf_control( s, i_query, args );
2263     va_end( args );
2264     return i_result;
2265 }
2266
2267 /**
2268  * Read "i_size" bytes and store them in a block_t.
2269  * It always read i_size bytes unless you are at the end of the stream
2270  * where it return what is available.
2271  */
2272 block_t *stream_Block( stream_t *s, int i_size )
2273 {
2274     if( i_size <= 0 ) return NULL;
2275
2276     /* emulate block read */
2277     block_t *p_bk = block_New( s, i_size );
2278     if( p_bk )
2279     {
2280         int i_read = stream_Read( s, p_bk->p_buffer, i_size );
2281         if( i_read > 0 )
2282         {
2283             p_bk->i_buffer = i_read;
2284             return p_bk;
2285         }
2286         block_Release( p_bk );
2287     }
2288     return NULL;
2289 }