stream.c 43.3 KB
Newer Older
1
2
3
/*****************************************************************************
 * stream.c
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 1999-2004 VideoLAN
gbazin's avatar
gbazin committed
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

#include <stdlib.h>
#include <vlc/vlc.h>
#include <vlc/input.h>

Laurent Aimar's avatar
Laurent Aimar committed
28
#include "input_internal.h"
29

Laurent Aimar's avatar
Laurent Aimar committed
30
31
32
33
34
/* TODO:
 *  - tune the 2 methods
 *  - compute cost for seek
 *  - improve stream mode seeking with closest segments
 *  - ...
35
 */
Laurent Aimar's avatar
Laurent Aimar committed
36
37
38
39
40
41
42
43

/* Two methods:
 *  - using pf_block
 *      One linked list of data read
 *  - using pf_read
 *      More complex scheme using mutliple track to avoid seeking
 */

44
45
/* How many tracks we have, currently only used for stream mode */
#ifdef OPTIMIZE_MEMORY
46
47
48
49
50
51
52
53
54
55
#   define STREAM_CACHE_TRACK 1
    /* Max size of our cache 128Ko per track */
#   define STREAM_CACHE_SIZE  (STREAM_CACHE_TRACK*1024*128)
#else
#   define STREAM_CACHE_TRACK 3
    /* Max size of our cache 4Mo per track */
#   define STREAM_CACHE_SIZE  (4*STREAM_CACHE_TRACK*1024*1024)
#endif

/* How many data we try to prebuffer */
Laurent Aimar's avatar
Laurent Aimar committed
56
#define STREAM_CACHE_PREBUFFER_SIZE (32767)
57
58
/* Maximum time we take to pre-buffer */
#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000)
Laurent Aimar's avatar
Laurent Aimar committed
59
60
61
62
63

/* Method1: Simple, for pf_block.
 *  We get blocks and put them in the linked list.
 *  We release blocks once the total size is bigger than CACHE_BLOCK_SIZE
 */
64
#define STREAM_DATA_WAIT 40000       /* Time between before a pf_block retry */
Laurent Aimar's avatar
Laurent Aimar committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

/* Method2: A bit more complex, for pf_read
 *  - We use ring buffers, only one if unseekable, all if seekable
 *  - Upon seek date current ring, then search if one ring match the pos,
 *      yes: switch to it, seek the access to match the end of the ring
 *      no: search the ring with i_end the closer to i_pos,
 *          if close enough, read data and use this ring
 *          else use the oldest ring, seek and use it.
 *
 *  TODO: - with access non seekable: use all space available for only one ring, but
 *          we have to support seekable/non-seekable switch on the fly.
 *        - compute a good value for i_read_size
 *        - ?
 */
#define STREAM_READ_ATONCE 32767
#define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)

typedef struct
83
{
Laurent Aimar's avatar
Laurent Aimar committed
84
    int64_t i_date;
hartman's avatar
hartman committed
85

Laurent Aimar's avatar
Laurent Aimar committed
86
87
88
89
    int64_t i_start;
    int64_t i_end;

    uint8_t *p_buffer;
90

Laurent Aimar's avatar
Laurent Aimar committed
91
92
} stream_track_t;

93
94
95
96
97
98
99
typedef struct
{
    char     *psz_path;
    int64_t  i_size;

} access_entry_t;

Laurent Aimar's avatar
Laurent Aimar committed
100
101
102
103
104
105
106
107
108
109
struct stream_sys_t
{
    access_t    *p_access;

    vlc_bool_t  b_block;    /* Block method (1) or stream */

    int64_t     i_pos;      /* Current reading offset */

    /* Method 1: pf_block */
    struct
110
    {
Laurent Aimar's avatar
Laurent Aimar committed
111
112
113
114
115
116
117
        int64_t i_start;        /* Offset of block for p_first */
        int     i_offset;       /* Offset for data in p_current */
        block_t *p_current;     /* Current block */

        int     i_size;         /* Total amount of data in the list */
        block_t *p_first;
        block_t **pp_last;
118

Laurent Aimar's avatar
Laurent Aimar committed
119
120
121
122
    } block;

    /* Method 2: for pf_read */
    struct
123
    {
124
        int i_offset;   /* Buffer offset in the current track */
Laurent Aimar's avatar
Laurent Aimar committed
125
126
127
128
129
130
131
132
133
        int i_tk;       /* Current track */
        stream_track_t tk[STREAM_CACHE_TRACK];

        /* Global buffer */
        uint8_t *p_buffer;

        /* */
        int i_used; /* Used since last read */
        int i_read_size;
134

Laurent Aimar's avatar
Laurent Aimar committed
135
136
137
138
139
140
141
142
    } stream;

    /* Peek temporary buffer */
    int     i_peek;
    uint8_t *p_peek;

    /* Stat for both method */
    struct
143
    {
Laurent Aimar's avatar
Laurent Aimar committed
144
        vlc_bool_t b_fastseek;  /* From access */
hartman's avatar
hartman committed
145

Laurent Aimar's avatar
Laurent Aimar committed
146
147
148
149
        /* Stat about reading data */
        int64_t i_read_count;
        int64_t i_bytes;
        int64_t i_read_time;
150

Laurent Aimar's avatar
Laurent Aimar committed
151
152
153
        /* Stat about seek */
        int     i_seek_count;
        int64_t i_seek_time;
154

Laurent Aimar's avatar
Laurent Aimar committed
155
    } stat;
156

157
158
159
160
161
162
    /* Streams list */
    int            i_list;
    access_entry_t **list;
    int            i_list_index;
    access_t       *p_list_access;

163
164
    /* Preparse mode ? */
    vlc_bool_t b_quick;
Laurent Aimar's avatar
Laurent Aimar committed
165
};
166

Laurent Aimar's avatar
Laurent Aimar committed
167
/* Method 1: */
168
169
static int  AStreamReadBlock( stream_t *s, void *p_read, int i_read );
static int  AStreamPeekBlock( stream_t *s, uint8_t **p_peek, int i_read );
Laurent Aimar's avatar
Laurent Aimar committed
170
static int  AStreamSeekBlock( stream_t *s, int64_t i_pos );
171
172
static void AStreamPrebufferBlock( stream_t *s );
static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof );
173

Laurent Aimar's avatar
Laurent Aimar committed
174
/* Method 2 */
175
176
static int  AStreamReadStream( stream_t *s, void *p_read, int i_read );
static int  AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read );
Laurent Aimar's avatar
Laurent Aimar committed
177
static int  AStreamSeekStream( stream_t *s, int64_t i_pos );
178
179
static void AStreamPrebufferStream( stream_t *s );
static int  AReadStream( stream_t *s, void *p_read, int i_read );
180

Laurent Aimar's avatar
Laurent Aimar committed
181
/* Common */
182
static int AStreamControl( stream_t *s, int i_query, va_list );
183
184
static void AStreamDestroy( stream_t *s );
static void UStreamDestroy( stream_t *s );
185
static int  ASeek( stream_t *s, int64_t i_pos );
186

187

188
/****************************************************************************
189
 * stream_UrlNew: create a stream from a access
190
 ****************************************************************************/
191
stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url )
192
{
193
    char *psz_access, *psz_demux, *psz_path, *psz_dup;
194
195
    access_t *p_access;
    stream_t *p_res;
196

197
198
    if( !psz_url ) return 0;

199
200
    psz_dup = strdup( psz_url );
    MRLSplit( p_parent, psz_dup, &psz_access, &psz_demux, &psz_path );
201
202
    
    /* Now try a real access */
203
204
    p_access = access2_New( p_parent, psz_access, psz_demux, psz_path, 0 );
    free( psz_dup );
205
206
207
208
209
210

    if( p_access == NULL )
    {
        msg_Err( p_parent, "no suitable access module for `%s'", psz_url );
        return NULL;
    }
211
212

    if( !( p_res = stream_AccessNew( p_access, VLC_TRUE ) ) )
213
214
    {
        access2_Delete( p_access );
215
        return NULL;
216
    }
217
218
219

    p_res->pf_destroy = UStreamDestroy;
    return p_res;
220
221
}

222
stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick )
223
{
Laurent Aimar's avatar
Laurent Aimar committed
224
225
    stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM );
    stream_sys_t *p_sys;
226
    char *psz_list;
Laurent Aimar's avatar
Laurent Aimar committed
227

228
    if( !s ) return NULL;
Laurent Aimar's avatar
Laurent Aimar committed
229
230
231
232
233
234
235
236

    /* Attach it now, needed for b_die */
    vlc_object_attach( s, p_access );

    s->pf_block  = NULL;
    s->pf_read   = NULL;    /* Set up later */
    s->pf_peek   = NULL;
    s->pf_control= AStreamControl;
237
    s->pf_destroy = AStreamDestroy;
Laurent Aimar's avatar
Laurent Aimar committed
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

    s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) );

    /* Common field */
    p_sys->p_access = p_access;
    p_sys->b_block = p_access->pf_block ? VLC_TRUE : VLC_FALSE;
    p_sys->i_pos = p_access->info.i_pos;

    /* Stats */
    access2_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek );
    p_sys->stat.i_bytes = 0;
    p_sys->stat.i_read_time = 0;
    p_sys->stat.i_read_count = 0;
    p_sys->stat.i_seek_count = 0;
    p_sys->stat.i_seek_time = 0;

254
255
256
257
258
    p_sys->i_list = 0;
    p_sys->list = 0;
    p_sys->i_list_index = 0;
    p_sys->p_list_access = 0;

259
260
    p_sys->b_quick = b_quick;

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
    /* Get the additional list of inputs if any (for concatenation) */
    if( (psz_list = var_CreateGetString( s, "input-list" )) && *psz_list )
    {
        access_entry_t *p_entry = malloc( sizeof(access_entry_t) );
        char *psz_name, *psz_parser = psz_name = psz_list;

        p_sys->p_list_access = p_access;
        p_entry->i_size = p_access->info.i_size;
        p_entry->psz_path = strdup( p_access->psz_path );
        TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
        msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)",
                 p_entry->psz_path, p_access->info.i_size );

        while( psz_name && *psz_name )
        {
            psz_parser = strchr( psz_name, ',' );
            if( psz_parser ) *psz_parser = 0;

            psz_name = strdup( psz_name );
            if( psz_name )
            {
                access_t *p_tmp = access2_New( p_access, p_access->psz_access,
                                               0, psz_name, 0 );

                if( !p_tmp )
                {
                    psz_name = psz_parser;
                    if( psz_name ) psz_name++;
                    continue;
                }

                msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)",
                         psz_name, p_tmp->info.i_size );

                p_entry = malloc( sizeof(access_entry_t) );
                p_entry->i_size = p_tmp->info.i_size;
                p_entry->psz_path = psz_name;
                TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );

                access2_Delete( p_tmp );
            }

            psz_name = psz_parser;
            if( psz_name ) psz_name++;
        }
    }
    if( psz_list ) free( psz_list );

Laurent Aimar's avatar
Laurent Aimar committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    /* Peek */
    p_sys->i_peek = 0;
    p_sys->p_peek = NULL;

    if( p_sys->b_block )
    {
        s->pf_read = AStreamReadBlock;
        s->pf_peek = AStreamPeekBlock;

        /* Init all fields of p_sys->block */
        p_sys->block.i_start = p_sys->i_pos;
        p_sys->block.i_offset = 0;
        p_sys->block.p_current = NULL;
        p_sys->block.i_size = 0;
        p_sys->block.p_first = NULL;
        p_sys->block.pp_last = &p_sys->block.p_first;

        /* Do the prebuffering */
        AStreamPrebufferBlock( s );

        if( p_sys->block.i_size <= 0 )
        {
            msg_Err( s, "cannot pre fill buffer" );
            goto error;
        }
    }
    else
336
    {
Laurent Aimar's avatar
Laurent Aimar committed
337
338
339
340
341
342
343
344
345
346
        int i;

        s->pf_read = AStreamReadStream;
        s->pf_peek = AStreamPeekStream;

        /* Allocate/Setup our tracks */
        p_sys->stream.i_offset = 0;
        p_sys->stream.i_tk     = 0;
        p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE );
        p_sys->stream.i_used   = 0;
347
348
        access2_Control( p_access, ACCESS_GET_MTU,
                         &p_sys->stream.i_read_size );
Laurent Aimar's avatar
Laurent Aimar committed
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
        if( p_sys->stream.i_read_size <= 0 )
            p_sys->stream.i_read_size = STREAM_READ_ATONCE;
        else if( p_sys->stream.i_read_size <= 256 )
            p_sys->stream.i_read_size = 256;

        for( i = 0; i < STREAM_CACHE_TRACK; i++ )
        {
            p_sys->stream.tk[i].i_date  = 0;
            p_sys->stream.tk[i].i_start = p_sys->i_pos;
            p_sys->stream.tk[i].i_end   = p_sys->i_pos;
            p_sys->stream.tk[i].p_buffer=
                &p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE];
        }

        /* Do the prebuffering */
        AStreamPrebufferStream( s );
365

Laurent Aimar's avatar
Laurent Aimar committed
366
367
368
369
370
        if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
        {
            msg_Err( s, "cannot pre fill buffer" );
            goto error;
        }
371
    }
Laurent Aimar's avatar
Laurent Aimar committed
372

373
    return s;
Laurent Aimar's avatar
Laurent Aimar committed
374
375
376
377
378
379
380
381
382
383
384
385
386
387

error:
    if( p_sys->b_block )
    {
        /* Nothing yet */
    }
    else
    {
        free( p_sys->stream.p_buffer );
    }
    free( s->p_sys );
    vlc_object_detach( s );
    vlc_object_destroy( s );
    return NULL;
388
389
}

390
/****************************************************************************
391
 * AStreamDestroy:
392
 ****************************************************************************/
393
static void AStreamDestroy( stream_t *s )
394
{
Laurent Aimar's avatar
Laurent Aimar committed
395
396
397
398
    stream_sys_t *p_sys = s->p_sys;

    vlc_object_detach( s );

399
400
401
402
403
404
405
406
407
    if( p_sys->b_block ) block_ChainRelease( p_sys->block.p_first );
    else free( p_sys->stream.p_buffer );

    if( p_sys->p_peek ) free( p_sys->p_peek );

    if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access )
        access2_Delete( p_sys->p_list_access );

    while( p_sys->i_list-- )
Laurent Aimar's avatar
Laurent Aimar committed
408
    {
409
410
411
        free( p_sys->list[p_sys->i_list]->psz_path );
        free( p_sys->list[p_sys->i_list] );
        if( !p_sys->i_list ) free( p_sys->list );
Laurent Aimar's avatar
Laurent Aimar committed
412
413
    }

414
    free( s->p_sys );
415
416
417
    vlc_object_destroy( s );
}

418
419
420
421
static void UStreamDestroy( stream_t *s )
{
    access_t *p_access = (access_t*)vlc_object_find( s, VLC_OBJECT_ACCESS, FIND_PARENT );
    AStreamDestroy( s );
422
    vlc_object_release( p_access );
423
424
425
    access2_Delete( p_access );
}

Laurent Aimar's avatar
Laurent Aimar committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/****************************************************************************
 * stream_AccessReset:
 ****************************************************************************/
void stream_AccessReset( stream_t *s )
{
    stream_sys_t *p_sys = s->p_sys;

    p_sys->i_pos = p_sys->p_access->info.i_pos;

    if( p_sys->b_block )
    {
        block_ChainRelease( p_sys->block.p_first );

        /* Init all fields of p_sys->block */
        p_sys->block.i_start = p_sys->i_pos;
        p_sys->block.i_offset = 0;
        p_sys->block.p_current = NULL;
        p_sys->block.i_size = 0;
        p_sys->block.p_first = NULL;
        p_sys->block.pp_last = &p_sys->block.p_first;

        /* Do the prebuffering */
        AStreamPrebufferBlock( s );
    }
    else
    {
        int i;

        /* Setup our tracks */
        p_sys->stream.i_offset = 0;
        p_sys->stream.i_tk     = 0;
        p_sys->stream.i_used   = 0;

        for( i = 0; i < STREAM_CACHE_TRACK; i++ )
        {
            p_sys->stream.tk[i].i_date  = 0;
            p_sys->stream.tk[i].i_start = p_sys->i_pos;
            p_sys->stream.tk[i].i_end   = p_sys->i_pos;
        }

        /* Do the prebuffering */
        AStreamPrebufferStream( s );
    }
}
470

471
472
473
474
475
476
/****************************************************************************
 * stream_AccessUpdate:
 ****************************************************************************/
void stream_AccessUpdate( stream_t *s )
{
    stream_sys_t *p_sys = s->p_sys;
477

478
    p_sys->i_pos = p_sys->p_access->info.i_pos;
479
480
481
482
483
484
485
486
487

    if( p_sys->i_list )
    {
        int i;
        for( i = 0; i < p_sys->i_list_index; i++ )
        {
            p_sys->i_pos += p_sys->list[i]->i_size;
        }
    }
488
489
}

490
/****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
491
 * AStreamControl:
492
 ****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
493
static int AStreamControl( stream_t *s, int i_query, va_list args )
494
{
Laurent Aimar's avatar
Laurent Aimar committed
495
496
    stream_sys_t *p_sys = s->p_sys;
    access_t     *p_access = p_sys->p_access;
497

Laurent Aimar's avatar
Laurent Aimar committed
498
499
500
    vlc_bool_t *p_bool;
    int64_t    *pi_64, i_64;
    int        i_int;
501
502
503
504

    switch( i_query )
    {
        case STREAM_GET_SIZE:
Laurent Aimar's avatar
Laurent Aimar committed
505
            pi_64 = (int64_t*)va_arg( args, int64_t * );
506
507
508
509
510
511
512
513
            if( s->p_sys->i_list )
            {
                int i;
                *pi_64 = 0;
                for( i = 0; i < s->p_sys->i_list; i++ )
                    *pi_64 += s->p_sys->list[i]->i_size;
                break;
            }
Laurent Aimar's avatar
Laurent Aimar committed
514
515
            *pi_64 = p_access->info.i_size;
            break;
516
517

        case STREAM_CAN_SEEK:
Laurent Aimar's avatar
Laurent Aimar committed
518
519
520
            p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            access2_Control( p_access, ACCESS_CAN_SEEK, p_bool );
            break;
521
522

        case STREAM_CAN_FASTSEEK:
Laurent Aimar's avatar
Laurent Aimar committed
523
524
525
            p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            access2_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
            break;
526
527

        case STREAM_GET_POSITION:
Laurent Aimar's avatar
Laurent Aimar committed
528
529
530
            pi_64 = (int64_t*)va_arg( args, int64_t * );
            *pi_64 = p_sys->i_pos;
            break;
531
532

        case STREAM_SET_POSITION:
Laurent Aimar's avatar
Laurent Aimar committed
533
534
535
536
537
            i_64 = (int64_t)va_arg( args, int64_t );
            if( p_sys->b_block )
                return AStreamSeekBlock( s, i_64 );
            else
                return AStreamSeekStream( s, i_64 );
538

539
        case STREAM_GET_MTU:
Laurent Aimar's avatar
Laurent Aimar committed
540
            return VLC_EGENERIC;
541

542
        case STREAM_CONTROL_ACCESS:
Laurent Aimar's avatar
Laurent Aimar committed
543
            i_int = (int) va_arg( args, int );
544
545
546
            if( i_int != ACCESS_SET_PRIVATE_ID_STATE &&
                i_int != ACCESS_SET_PRIVATE_ID_CA &&
                i_int != ACCESS_GET_PRIVATE_ID_STATE )
547
548
549
550
551
            {
                msg_Err( s, "Hey, what are you thinking ?"
                            "DON'T USE STREAM_CONTROL_ACCESS !!!" );
                return VLC_EGENERIC;
            }
552
            return access2_vaControl( p_access, i_int, args );
553

554
555
556
557
        default:
            msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
            return VLC_EGENERIC;
    }
Laurent Aimar's avatar
Laurent Aimar committed
558
    return VLC_SUCCESS;
559
560
}

Laurent Aimar's avatar
Laurent Aimar committed
561
562


563
/****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
564
 * Method 1:
565
 ****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
566
static void AStreamPrebufferBlock( stream_t *s )
567
{
Laurent Aimar's avatar
Laurent Aimar committed
568
    stream_sys_t *p_sys = s->p_sys;
569

Laurent Aimar's avatar
Laurent Aimar committed
570
571
    int64_t i_first = 0;
    int64_t i_start;
572

Laurent Aimar's avatar
Laurent Aimar committed
573
574
575
    msg_Dbg( s, "pre buffering" );
    i_start = mdate();
    for( ;; )
576
    {
Laurent Aimar's avatar
Laurent Aimar committed
577
        int64_t i_date = mdate();
578
        vlc_bool_t b_eof;
Laurent Aimar's avatar
Laurent Aimar committed
579
        block_t *b;
580

581
        if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
Laurent Aimar's avatar
Laurent Aimar committed
582
583
584
585
586
587
588
589
            ( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) )
        {
            int64_t i_byterate;

            /* Update stat */
            p_sys->stat.i_bytes = p_sys->block.i_size;
            p_sys->stat.i_read_time = i_date - i_start;
            i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) /
590
                         (p_sys->stat.i_read_time + 1);
Laurent Aimar's avatar
Laurent Aimar committed
591

592
593
            msg_Dbg( s, "prebuffering done "I64Fd" bytes in "I64Fd"s - "
                     I64Fd" kbytes/s",
Laurent Aimar's avatar
Laurent Aimar committed
594
595
596
597
598
                     p_sys->stat.i_bytes,
                     p_sys->stat.i_read_time / I64C(1000000),
                     i_byterate / 1024 );
            break;
        }
599

Laurent Aimar's avatar
Laurent Aimar committed
600
        /* Fetch a block */
601
        if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
602
        {
603
            if( b_eof ) break;
Laurent Aimar's avatar
Laurent Aimar committed
604
605
606

            msleep( STREAM_DATA_WAIT );
            continue;
607
        }
Laurent Aimar's avatar
Laurent Aimar committed
608
609

        if( i_first == 0 )
610
        {
Laurent Aimar's avatar
Laurent Aimar committed
611
            i_first = mdate();
612
613
            msg_Dbg( s, "received first data for our buffer");
        }
Laurent Aimar's avatar
Laurent Aimar committed
614
615
616
617
618
619
620

        /* Append the block */
        p_sys->block.i_size += b->i_buffer;
        *p_sys->block.pp_last = b;
        p_sys->block.pp_last = &b->p_next;

        p_sys->stat.i_read_count++;
621
622
    }

Laurent Aimar's avatar
Laurent Aimar committed
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
    p_sys->block.p_current = p_sys->block.p_first;
}

static int AStreamRefillBlock( stream_t *s );

static int AStreamReadBlock( stream_t *s, void *p_read, int i_read )
{
    stream_sys_t *p_sys = s->p_sys;

    uint8_t *p_data= (uint8_t*)p_read;
    int     i_data = 0;

    /* It means EOF */
    if( p_sys->block.p_current == NULL )
        return 0;

639
    if( p_read == NULL )
damienf's avatar
   
damienf committed
640
    {
641
        /* seek within this stream if possible, else use plain old read and discard */
damienf's avatar
   
damienf committed
642
643
644
645
646
647
648
        stream_sys_t *p_sys = s->p_sys;
        access_t     *p_access = p_sys->p_access;
        vlc_bool_t   b_aseek;
        access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
        if( b_aseek )
            return AStreamSeekBlock( s, p_sys->i_pos + i_read ) ? 0 : i_read;
    }
649

Laurent Aimar's avatar
Laurent Aimar committed
650
    while( i_data < i_read )
651
    {
652
653
        int i_current =
            p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
Laurent Aimar's avatar
Laurent Aimar committed
654
        int i_copy = __MIN( i_current, i_read - i_data);
gbazin's avatar
   
gbazin committed
655

Laurent Aimar's avatar
Laurent Aimar committed
656
657
        /* Copy data */
        if( p_data )
658
        {
659
660
661
            memcpy( p_data,
                    &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
                    i_copy );
Laurent Aimar's avatar
Laurent Aimar committed
662
663
664
            p_data += i_copy;
        }
        i_data += i_copy;
665

Laurent Aimar's avatar
Laurent Aimar committed
666
667
668
669
670
671
672
673
674
675
676
        p_sys->block.i_offset += i_copy;
        if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer )
        {
            /* Current block is now empty, switch to next */
            if( p_sys->block.p_current )
            {
                p_sys->block.i_offset = 0;
                p_sys->block.p_current = p_sys->block.p_current->p_next;
            }
            /*Get a new block */
            if( AStreamRefillBlock( s ) )
gbazin's avatar
gbazin committed
677
678
679
            {
                break;
            }
680
681
682
        }
    }

Laurent Aimar's avatar
Laurent Aimar committed
683
684
    p_sys->i_pos += i_data;
    return i_data;
685
}
gbazin's avatar
gbazin committed
686

Laurent Aimar's avatar
Laurent Aimar committed
687
static int AStreamPeekBlock( stream_t *s, uint8_t **pp_peek, int i_read )
sigmunau's avatar
sigmunau committed
688
{
Laurent Aimar's avatar
Laurent Aimar committed
689
690
691
692
693
694
    stream_sys_t *p_sys = s->p_sys;
    uint8_t *p_data;
    int      i_data = 0;
    block_t *b;
    int      i_offset;

695
    if( p_sys->block.p_current == NULL ) return 0; /* EOF */
Laurent Aimar's avatar
Laurent Aimar committed
696
697
698
699
700
701
702
703
704
705
706

    /* We can directly give a pointer over our buffer */
    if( i_read <= p_sys->block.p_current->i_buffer - p_sys->block.i_offset )
    {
        *pp_peek = &p_sys->block.p_current->p_buffer[p_sys->block.i_offset];
        return i_read;
    }

    /* We need to create a local copy */
    if( p_sys->i_peek < i_read )
    {
707
708
709
710
711
712
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
Laurent Aimar's avatar
Laurent Aimar committed
713
714
715
716
        p_sys->i_peek = i_read;
    }

    /* Fill enough data */
717
718
    while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
           < i_read )
Laurent Aimar's avatar
Laurent Aimar committed
719
720
721
    {
        block_t **pp_last = p_sys->block.pp_last;

722
        if( AStreamRefillBlock( s ) ) break;
Laurent Aimar's avatar
Laurent Aimar committed
723
724

        /* Our buffer are probably filled enough, don't try anymore */
725
        if( pp_last == p_sys->block.pp_last ) break;
Laurent Aimar's avatar
Laurent Aimar committed
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
    }

    /* Copy what we have */
    b = p_sys->block.p_current;
    i_offset = p_sys->block.i_offset;
    p_data = p_sys->p_peek;

    while( b && i_data < i_read )
    {
        int i_current = b->i_buffer - i_offset;
        int i_copy = __MIN( i_current, i_read - i_data );

        memcpy( p_data, &b->p_buffer[i_offset], i_copy );
        i_data += i_copy;
        p_data += i_copy;
        i_offset += i_copy;

        if( i_offset >= b->i_buffer )
        {
            i_offset = 0;
            b = b->p_next;
        }
    }

    *pp_peek = p_sys->p_peek;
    return i_data;
sigmunau's avatar
sigmunau committed
752
}
753

Laurent Aimar's avatar
Laurent Aimar committed
754
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
755
{
Laurent Aimar's avatar
Laurent Aimar committed
756
757
758
759
    stream_sys_t *p_sys = s->p_sys;
    access_t   *p_access = p_sys->p_access;
    int64_t    i_offset = i_pos - p_sys->block.i_start;
    vlc_bool_t b_seek;
760

Laurent Aimar's avatar
Laurent Aimar committed
761
762
763
764
765
    /* We already have thoses data, just update p_current/i_offset */
    if( i_offset >= 0 && i_offset < p_sys->block.i_size )
    {
        block_t *b = p_sys->block.p_first;
        int i_current = 0;
766

Laurent Aimar's avatar
Laurent Aimar committed
767
768
769
770
771
        while( i_current + b->i_buffer < i_offset )
        {
            i_current += b->i_buffer;
            b = b->p_next;
        }
772

Laurent Aimar's avatar
Laurent Aimar committed
773
774
        p_sys->block.p_current = b;
        p_sys->block.i_offset = i_offset - i_current;
775

Laurent Aimar's avatar
Laurent Aimar committed
776
        p_sys->i_pos = i_pos;
777

Laurent Aimar's avatar
Laurent Aimar committed
778
779
        return VLC_SUCCESS;
    }
780

Laurent Aimar's avatar
Laurent Aimar committed
781
782
    /* We may need to seek or to read data */
    if( i_offset < 0 )
783
    {
Laurent Aimar's avatar
Laurent Aimar committed
784
785
786
787
788
789
790
791
792
793
        vlc_bool_t b_aseek;
        access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );

        if( !b_aseek )
        {
            msg_Err( s, "backward seek impossible (access non seekable)" );
            return VLC_EGENERIC;
        }

        b_seek = VLC_TRUE;
794
    }
Laurent Aimar's avatar
Laurent Aimar committed
795
796
797
    else
    {
        vlc_bool_t b_aseek, b_aseekfast;
798

Laurent Aimar's avatar
Laurent Aimar committed
799
800
801
802
803
804
        access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
        access2_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast );

        if( !b_aseek )
        {
            b_seek = VLC_FALSE;
805
806
            msg_Warn( s, I64Fd" bytes need to be skipped "
                      "(access non seekable)",
Laurent Aimar's avatar
Laurent Aimar committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
                      i_offset - p_sys->block.i_size );
        }
        else
        {
            int64_t i_skip = i_offset - p_sys->block.i_size;

            /* Avg bytes per packets */
            int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count;
            /* TODO compute a seek cost instead of fixed threshold */
            int i_th = b_aseekfast ? 1 : 5;

            if( i_skip <= i_th * i_avg &&
                i_skip < STREAM_CACHE_SIZE )
                b_seek = VLC_FALSE;
            else
                b_seek = VLC_TRUE;

824
            msg_Dbg( s, "b_seek=%d th*avg=%d skip="I64Fd,
Laurent Aimar's avatar
Laurent Aimar committed
825
826
                     b_seek, i_th*i_avg, i_skip );
        }
827
828
    }

Laurent Aimar's avatar
Laurent Aimar committed
829
830
831
832
833
    if( b_seek )
    {
        int64_t i_start, i_end;
        /* Do the access seek */
        i_start = mdate();
834
        if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
835
        i_end = mdate();
836

Laurent Aimar's avatar
Laurent Aimar committed
837
838
839
840
841
842
843
844
845
846
        /* Release data */
        block_ChainRelease( p_sys->block.p_first );

        /* Reinit */
        p_sys->block.i_start = p_sys->i_pos = i_pos;
        p_sys->block.i_offset = 0;
        p_sys->block.p_current = NULL;
        p_sys->block.i_size = 0;
        p_sys->block.p_first = NULL;
        p_sys->block.pp_last = &p_sys->block.p_first;
847

Laurent Aimar's avatar
Laurent Aimar committed
848
849
850
851
852
853
854
855
856
857
858
859
        /* Refill a block */
        if( AStreamRefillBlock( s ) )
        {
            msg_Err( s, "cannot re fill buffer" );
            return VLC_EGENERIC;
        }
        /* Update stat */
        p_sys->stat.i_seek_time += i_end - i_start;
        p_sys->stat.i_seek_count++;
        return VLC_SUCCESS;
    }
    else
860
    {
Laurent Aimar's avatar
Laurent Aimar committed
861
862
        /* Read enought data */
        while( p_sys->block.i_start + p_sys->block.i_size < i_pos )
863
        {
Laurent Aimar's avatar
Laurent Aimar committed
864
865
866
867
868
869
870
            if( AStreamRefillBlock( s ) )
            {
                msg_Err( s, "can't read enough data in seek" );
                return VLC_EGENERIC;
            }
            while( p_sys->block.p_current &&
                   p_sys->i_pos + p_sys->block.p_current->i_buffer < i_pos )
871
            {
Laurent Aimar's avatar
Laurent Aimar committed
872
873
                p_sys->i_pos += p_sys->block.p_current->i_buffer;
                p_sys->block.p_current = p_sys->block.p_current->p_next;
874
875
876
            }
        }

Laurent Aimar's avatar
Laurent Aimar committed
877
878
        p_sys->block.i_offset = i_pos - p_sys->i_pos;
        p_sys->i_pos = i_pos;
879

Laurent Aimar's avatar
Laurent Aimar committed
880
881
        /* TODO read data */
        return VLC_SUCCESS;
882
883
    }

Laurent Aimar's avatar
Laurent Aimar committed
884
    return VLC_EGENERIC;
885
886
}

Laurent Aimar's avatar
Laurent Aimar committed
887
static int AStreamRefillBlock( stream_t *s )
888
{
Laurent Aimar's avatar
Laurent Aimar committed
889
890
891
892
893
894
895
896
897
    stream_sys_t *p_sys = s->p_sys;
    int64_t      i_start, i_stop;
    block_t      *b;

    /* Release data */
    while( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
           p_sys->block.p_first != p_sys->block.p_current )
    {
        block_t *b = p_sys->block.p_first;
898

Laurent Aimar's avatar
Laurent Aimar committed
899
900
901
        p_sys->block.i_start += b->i_buffer;
        p_sys->block.i_size  -= b->i_buffer;
        p_sys->block.p_first  = b->p_next;
902

Laurent Aimar's avatar
Laurent Aimar committed
903
904
905
906
907
        block_Release( b );
    }
    if( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
        p_sys->block.p_current == p_sys->block.p_first &&
        p_sys->block.p_current->p_next )    /* At least 2 packets */
908
    {
Laurent Aimar's avatar
Laurent Aimar committed
909
910
        /* Enough data, don't read more */
        return VLC_SUCCESS;
911
912
    }

Laurent Aimar's avatar
Laurent Aimar committed
913
914
915
    /* Now read a new block */
    i_start = mdate();
    for( ;; )
916
    {
917
918
919
        vlc_bool_t b_eof;

        if( s->b_die ) return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
920
921
922


        /* Fetch a block */
923
        if( ( b = AReadBlock( s, &b_eof ) ) ) break;
Laurent Aimar's avatar
Laurent Aimar committed
924

925
        if( b_eof ) return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
926
927

        msleep( STREAM_DATA_WAIT );
928
    }
Laurent Aimar's avatar
Laurent Aimar committed
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
    i_stop = mdate();

    /* Append the block */
    p_sys->block.i_size += b->i_buffer;
    *p_sys->block.pp_last = b;
    p_sys->block.pp_last = &b->p_next;

    /* Fix p_current */
    if( p_sys->block.p_current == NULL )
        p_sys->block.p_current = b;

    /* Update stat */
    p_sys->stat.i_bytes += b->i_buffer;
    p_sys->stat.i_read_time += i_stop - i_start;
    p_sys->stat.i_read_count++;

    return VLC_SUCCESS;
946
947
948
}


Laurent Aimar's avatar
Laurent Aimar committed
949
950
951
952
953
954
/****************************************************************************
 * Method 2:
 ****************************************************************************/
static int AStreamRefillStream( stream_t *s );

static int AStreamReadStream( stream_t *s, void *p_read, int i_read )
955
{
Laurent Aimar's avatar
Laurent Aimar committed
956
957
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
958

959
    uint8_t *p_data = (uint8_t *)p_read;
Laurent Aimar's avatar
Laurent Aimar committed
960
    int      i_data = 0;
961

962
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
963

964
    if( p_read == NULL )
damienf's avatar
   
damienf committed
965
    {
966
        /* seek within this stream if possible, else use plain old read and discard */
damienf's avatar
   
damienf committed
967
968
969
970
971
972
973
        stream_sys_t *p_sys = s->p_sys;
        access_t     *p_access = p_sys->p_access;
        vlc_bool_t   b_aseek;
        access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
        if( b_aseek )
            return AStreamSeekStream( s, p_sys->i_pos + i_read ) ? 0 : i_read;
    }
974

975
976
977
978
979
980
#if 0
    msg_Dbg( s, "AStreamReadStream: %d pos="I64Fd" tk=%d start="I64Fd
             " offset=%d end="I64Fd,
             i_read, p_sys->i_pos, p_sys->stream.i_tk,
             tk->i_start, p_sys->stream.i_offset, tk->i_end );
#endif
Laurent Aimar's avatar
Laurent Aimar committed
981
982

    while( i_data < i_read )
983
    {
984
985
986
987
988
        int i_off = (tk->i_start + p_sys->stream.i_offset) %
                    STREAM_CACHE_TRACK_SIZE;
        int i_current =
            __MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset,
                   STREAM_CACHE_TRACK_SIZE - i_off );
Laurent Aimar's avatar
Laurent Aimar committed
989
990
        int i_copy = __MIN( i_current, i_read - i_data );

991
992
        if( i_copy <= 0 ) break; /* EOF */

Laurent Aimar's avatar
Laurent Aimar committed
993
        /* Copy data */
994
        /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */
Laurent Aimar's avatar
Laurent Aimar committed
995
        if( p_data )
996
        {
Laurent Aimar's avatar
Laurent Aimar committed
997
998
            memcpy( p_data, &tk->p_buffer[i_off], i_copy );
            p_data += i_copy;
999
        }
Laurent Aimar's avatar
Laurent Aimar committed
1000
1001
1002
1003
        i_data += i_copy;
        p_sys->stream.i_offset += i_copy;

        /* Update pos now */
1004
1005
        p_sys->i_pos += i_copy;

Laurent Aimar's avatar
Laurent Aimar committed
1006
1007
1008
1009
        /* */
        p_sys->stream.i_used += i_copy;
        if( tk->i_start + p_sys->stream.i_offset >= tk->i_end ||
            p_sys->stream.i_used >= p_sys->stream.i_read_size )
1010
        {
Laurent Aimar's avatar
Laurent Aimar committed
1011
1012
            if( AStreamRefillStream( s ) )
            {
1013
1014
                /* EOF */
                if( tk->i_start >= tk->i_end ) break;
Laurent Aimar's avatar
Laurent Aimar committed
1015
            }
1016
1017
1018
        }
    }

Laurent Aimar's avatar
Laurent Aimar committed
1019
    return i_data;
1020
}
Laurent Aimar's avatar
Laurent Aimar committed
1021
1022

static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read )
1023
{
Laurent Aimar's avatar
Laurent Aimar committed
1024
1025
1026
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
    int64_t i_off;
1027

1028
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
Laurent Aimar's avatar
Laurent Aimar committed
1029

1030
1031
1032
1033
1034
1035
#if 0
    msg_Dbg( s, "AStreamPeekStream: %d pos="I64Fd" tk=%d "
             "start="I64Fd" offset=%d end="I64Fd,
             i_read, p_sys->i_pos, p_sys->stream.i_tk,
             tk->i_start, p_sys->stream.i_offset, tk->i_end );
#endif
Laurent Aimar's avatar
Laurent Aimar committed
1036
1037
1038
1039
1040
1041

    /* Avoid problem, but that should *never* happen */
    if( i_read > STREAM_CACHE_TRACK_SIZE / 2 )
        i_read = STREAM_CACHE_TRACK_SIZE / 2;

    while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1042
    {
1043
1044
1045
        if( p_sys->stream.i_used <= 1 )
        {
            /* Be sure we will read something */
1046
1047
            p_sys->stream.i_used += i_read -
                (tk->i_end - tk->i_start - p_sys->stream.i_offset);
1048
        }
1049
        if( AStreamRefillStream( s ) ) break;
1050
1051
    }

Laurent Aimar's avatar
Laurent Aimar committed
1052
1053
    if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
        i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset;
1054

Laurent Aimar's avatar
Laurent Aimar committed
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
    /* Now, direct pointer or a copy ? */
    i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
    if( i_off + i_read <= STREAM_CACHE_TRACK_SIZE )
    {
        *pp_peek = &tk->p_buffer[i_off];
        return i_read;
    }

    if( p_sys->i_peek < i_read )
    {
1065
1066
1067
1068
1069
1070
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
Laurent Aimar's avatar
Laurent Aimar committed
1071
1072
1073
        p_sys->i_peek = i_read;
    }

1074
1075
1076
1077
    memcpy( p_sys->p_peek, &tk->p_buffer[i_off],
            STREAM_CACHE_TRACK_SIZE - i_off );
    memcpy( &p_sys->p_peek[STREAM_CACHE_TRACK_SIZE - i_off],
            &tk->p_buffer[0], i_read - (STREAM_CACHE_TRACK_SIZE - i_off) );
Laurent Aimar's avatar
Laurent Aimar committed
1078
1079
1080

    *pp_peek = p_sys->p_peek;
    return i_read;
1081
1082
}

Laurent Aimar's avatar
Laurent Aimar committed
1083
static int AStreamSeekStream( stream_t *s, int64_t i_pos )
1084
{
Laurent Aimar's avatar
Laurent Aimar committed
1085
1086
1087
1088
1089
1090
1091
1092
    stream_sys_t *p_sys = s->p_sys;
    access_t     *p_access = p_sys->p_access;
    vlc_bool_t   b_aseek;
    vlc_bool_t   b_afastseek;
    int i_maxth;
    int i_new;
    int i;

1093
1094
1095
1096
1097
1098
1099
1100
#if 0
    msg_Dbg( s, "AStreamSeekStream: to "I64Fd" pos="I64Fd
             "tk=%d start="I64Fd" offset=%d end="I64Fd,
             i_pos, p_sys->i_pos, p_sys->stream.i_tk,
             p_sys->stream.tk[p_sys->stream.i_tk].i_start,
             p_sys->stream.i_offset,
             p_sys->stream.tk[p_sys->stream.i_tk].i_end );
#endif
Laurent Aimar's avatar
Laurent Aimar committed
1101
1102
1103
1104
1105


    /* Seek in our current track ? */
    if( i_pos >= p_sys->stream.tk[p_sys->stream.i_tk].i_start &&
        i_pos < p_sys->stream.tk[p_sys->stream.i_tk].i_end )
1106
    {
Laurent Aimar's avatar
Laurent Aimar committed
1107
1108
1109
1110
1111
        //msg_Dbg( s, "AStreamSeekStream: current track" );
        p_sys->i_pos = i_pos;
        p_sys->stream.i_offset = i_pos - p_sys->stream.tk[p_sys->stream.i_tk].i_start;
        return VLC_SUCCESS;
    }
1112

Laurent Aimar's avatar
Laurent Aimar committed
1113
1114
1115
1116
    access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
    if( !b_aseek )
    {
        /* We can't do nothing */
damienf's avatar
   
damienf committed
1117
        msg_Dbg( s, "AStreamSeekStream: can't seek" );
Laurent Aimar's avatar
Laurent Aimar committed
1118
1119
        return VLC_EGENERIC;
    }
1120

Laurent Aimar's avatar
Laurent Aimar committed
1121
1122
    /* Date the current track */
    p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1123

Laurent Aimar's avatar
Laurent Aimar committed
1124
1125
1126
1127
    /* Try to reuse already read data */
    for( i = 0; i < STREAM_CACHE_TRACK; i++ )
    {
        stream_track_t *tk = &p_sys->stream.tk[i];
1128

Laurent Aimar's avatar
Laurent Aimar committed
1129
1130
        if( i_pos >= tk->i_start && i_pos <= tk->i_end )
        {
1131
1132
1133
1134
#if 0
            msg_Dbg( s, "AStreamSeekStream: reusing %d start="I64Fd
                     " end="I64Fd, i, tk->i_start, tk->i_end );
#endif
Laurent Aimar's avatar
Laurent Aimar committed
1135
            /* Seek at the end of the buffer */
1136
            if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
1137
1138
1139
1140
1141
1142
1143
1144
1145

            /* That's it */
            p_sys->i_pos = i_pos;
            p_sys->stream.i_tk = i;
            p_sys->stream.i_offset = i_pos - tk->i_start;

            if( p_sys->stream.i_used < 1024 )
                p_sys->stream.i_used = 1024;

1146
            if( AStreamRefillStream( s ) && i_pos == tk->i_end )
Laurent Aimar's avatar
Laurent Aimar committed
1147
                return VLC_EGENERIC;
1148
1149

            return VLC_SUCCESS;
Laurent Aimar's avatar
Laurent Aimar committed
1150
1151
        }
    }
1152

Laurent Aimar's avatar
Laurent Aimar committed
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
    access2_Control( p_access, ACCESS_CAN_SEEK, &b_afastseek );
    /* FIXME compute seek cost (instead of static 'stupid' value) */
    i_maxth = __MIN( p_sys->stream.i_read_size, STREAM_READ_ATONCE / 2 );
    if( !b_afastseek )
        i_maxth *= 3;

    /* FIXME TODO */
#if 0
    /* Search closest segment TODO */
    for( i = 0; i < STREAM_CACHE_TRACK; i++ )
    {
        stream_track_t *tk = &p_sys->stream.tk[i];

        if( i_pos + i_maxth >= tk->i_start )
        {
            msg_Dbg( s, "good segment before current pos, TODO" );
        }
        if( i_pos - i_maxth <= tk->i_end )
        {
            msg_Dbg( s, "good segment after current pos, TODO" );
        }
1174
    }
Laurent Aimar's avatar
Laurent Aimar committed
1175
1176
1177
#endif

    /* Nothing good, seek and choose oldest segment */
1178
    if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
    p_sys->i_pos = i_pos;

    i_new = 0;
    for( i = 1; i < STREAM_CACHE_TRACK; i++ )
    {
        if( p_sys->stream.tk[i].i_date < p_sys->stream.tk[i_new].i_date )
            i_new = i;
    }

    /* Reset the segment */
    p_sys->stream.i_tk     = i_new;
    p_sys->stream.i_offset =  0;
    p_sys->stream.tk[i_new].i_start = i_pos;
    p_sys->stream.tk[i_new].i_end   = i_pos;

    /* Read data */
    if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 )
        p_sys->stream.i_used = STREAM_READ_ATONCE / 2;

    if( AStreamRefillStream( s ) )
        return VLC_EGENERIC;

    return VLC_SUCCESS;
1202
1203
}

Laurent Aimar's avatar
Laurent Aimar committed
1204
static int AStreamRefillStream( stream_t *s )
1205
{
Laurent Aimar's avatar
Laurent Aimar committed
1206
1207
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1208
1209

    /* We read but won't increase i_start after initial start + offset */
1210
1211
1212
    int i_toread =
        __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE -
               (tk->i_end - tk->i_start - p_sys->stream.i_offset) );
1213
    vlc_bool_t b_read = VLC_FALSE;
Laurent Aimar's avatar
Laurent Aimar committed
1214
    int64_t i_start, i_stop;
1215

1216
1217
1218
    if( i_toread <= 0 ) return VLC_EGENERIC; /* EOF */

    /* msg_Dbg( s, "AStreamRefillStream: toread=%d", i_toread ); */
1219

Laurent Aimar's avatar