stream.c 62.1 KB
Newer Older
1 2 3
/*****************************************************************************
 * stream.c
 *****************************************************************************
4
 * Copyright (C) 1999-2004 the VideoLAN team
Gildas Bazin's avatar
Gildas Bazin committed
5
 * $Id$
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23
 *****************************************************************************/

24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <vlc_common.h>
29

30 31
#include <assert.h>

32
#include "input_internal.h"
33

34 35
#undef STREAM_DEBUG

36
/* TODO:
37
 *  - tune the 2 methods (block/stream)
38 39 40
 *  - compute cost for seek
 *  - improve stream mode seeking with closest segments
 *  - ...
41
 *  - Maybe remove (block/stream) in favour of immediate
42
 */
43 44 45 46 47 48

/* Two methods:
 *  - using pf_block
 *      One linked list of data read
 *  - using pf_read
 *      More complex scheme using mutliple track to avoid seeking
49 50 51
 *  - using directly the access (only indirection for peeking).
 *      This method is known to introduce much less latency.
 *      It should probably defaulted (instead of the stream method (2)).
52 53
 */

54 55
/* How many tracks we have, currently only used for stream mode */
#ifdef OPTIMIZE_MEMORY
56 57 58 59 60 61 62 63 64 65
#   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 */
66
#define STREAM_CACHE_PREBUFFER_SIZE (32767)
67 68
/* Maximum time we take to pre-buffer */
#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000)
69 70 71 72 73

/* 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
 */
74
#define STREAM_DATA_WAIT 40000       /* Time between before a pf_block retry */
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

/* 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
93
{
94
    int64_t i_date;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
95

96 97 98 99
    int64_t i_start;
    int64_t i_end;

    uint8_t *p_buffer;
100

101 102
} stream_track_t;

103 104 105 106 107 108 109
typedef struct
{
    char     *psz_path;
    int64_t  i_size;

} access_entry_t;

110 111 112 113 114 115 116
typedef enum stream_read_method_t
{
    Immediate,
    Block,
    Stream
} stream_read_method_t;

117 118 119 120
struct stream_sys_t
{
    access_t    *p_access;

121
    stream_read_method_t   method;    /* method to use */
122 123 124 125 126

    int64_t     i_pos;      /* Current reading offset */

    /* Method 1: pf_block */
    struct
127
    {
128
        int64_t i_start;        /* Offset of block for p_first */
129
        int64_t i_offset;       /* Offset for data in p_current */
130 131 132 133 134
        block_t *p_current;     /* Current block */

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

136 137 138 139
    } block;

    /* Method 2: for pf_read */
    struct
140
    {
141
        int i_offset;   /* Buffer offset in the current track */
142 143 144 145 146 147 148 149 150
        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;
151

152 153
    } stream;

154 155 156 157 158 159 160
    /* Method 3: for pf_read */
    struct
    {
        int64_t i_end;
        uint8_t *p_buffer;
    } immediate;

161
    /* Peek temporary buffer */
162
    unsigned int i_peek;
163 164 165 166
    uint8_t *p_peek;

    /* Stat for both method */
    struct
167
    {
168
        bool b_fastseek;  /* From access */
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
169

170 171 172 173
        /* Stat about reading data */
        int64_t i_read_count;
        int64_t i_bytes;
        int64_t i_read_time;
174

175 176 177
        /* Stat about seek */
        int     i_seek_count;
        int64_t i_seek_time;
178

179
    } stat;
180

181 182 183 184 185 186
    /* Streams list */
    int            i_list;
    access_entry_t **list;
    int            i_list_index;
    access_t       *p_list_access;

187
    /* Preparse mode ? */
188
    bool      b_quick;
189
};
190

191
/* Method 1: */
192 193
static int  AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read );
static int  AStreamPeekBlock( stream_t *s, const uint8_t **p_peek, unsigned int i_read );
194
static int  AStreamSeekBlock( stream_t *s, int64_t i_pos );
195
static void AStreamPrebufferBlock( stream_t *s );
196
static block_t *AReadBlock( stream_t *s, bool *pb_eof );
197

198
/* Method 2 */
199 200
static int  AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read );
static int  AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );
201
static int  AStreamSeekStream( stream_t *s, int64_t i_pos );
202
static void AStreamPrebufferStream( stream_t *s );
203
static int  AReadStream( stream_t *s, void *p_read, unsigned int i_read );
204

205
/* Method 3 */
206 207
static int  AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read );
static int  AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );
208 209
static int  AStreamSeekImmediate( stream_t *s, int64_t i_pos );

210
/* Common */
211
static int AStreamControl( stream_t *s, int i_query, va_list );
212 213
static void AStreamDestroy( stream_t *s );
static void UStreamDestroy( stream_t *s );
214
static int  ASeek( stream_t *s, int64_t i_pos );
215

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
/****************************************************************************
 * Method 3 helpers:
 ****************************************************************************/

static inline int64_t stream_buffered_size( stream_t *s )
{
    return s->p_sys->immediate.i_end;
}

static inline void stream_buffer_empty( stream_t *s, int length )
{
    length = __MAX( stream_buffered_size( s ), length );
    if( length )
    {
        memmove( s->p_sys->immediate.p_buffer,
                 s->p_sys->immediate.p_buffer + length,
                 stream_buffered_size( s ) - length );
    }
    s->p_sys->immediate.i_end -= length;
}

static inline void stream_buffer_fill( stream_t *s, int length )
{
    s->p_sys->immediate.i_end += length;
}

static inline uint8_t * stream_buffer( stream_t *s )
{
    return s->p_sys->immediate.p_buffer;
}
246

247
/****************************************************************************
248
 * stream_UrlNew: create a stream from a access
249
 ****************************************************************************/
250
stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url )
251
{
252 253
    const char *psz_access, *psz_demux;
    char *psz_path;
254 255
    access_t *p_access;
    stream_t *p_res;
256

257 258
    if( !psz_url ) return 0;

259 260
    char psz_dup[strlen (psz_url) + 1];
    strcpy (psz_dup, psz_url);;
261
    MRLSplit( psz_dup, &psz_access, &psz_demux, &psz_path );
262

263
    /* Now try a real access */
264
    p_access = access_New( p_parent, psz_access, psz_demux, psz_path );
265 266 267 268 269 270

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

272
    if( !( p_res = stream_AccessNew( p_access, true ) ) )
273
    {
274
        access_Delete( p_access );
275
        return NULL;
276
    }
277 278 279

    p_res->pf_destroy = UStreamDestroy;
    return p_res;
280 281
}

282
stream_t *stream_AccessNew( access_t *p_access, bool b_quick )
283
{
284
    stream_t *s = vlc_stream_create( VLC_OBJECT(p_access) );
285
    stream_sys_t *p_sys;
286
    char *psz_list = NULL;
287

288
    if( !s ) return NULL;
289 290 291 292 293 294

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

    s->pf_read   = NULL;    /* Set up later */
    s->pf_peek   = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
295
    s->pf_control = AStreamControl;
296
    s->pf_destroy = AStreamDestroy;
297 298

    s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) );
299 300
    if( p_sys == NULL )
        goto error;
301

302 303
    /* UTF16 and UTF32 text file conversion */
    s->i_char_width = 1;
304
    s->b_little_endian = false;
305
    s->conv = (vlc_iconv_t)(-1);
306

307 308
    /* Common field */
    p_sys->p_access = p_access;
309 310 311 312 313 314 315
    if( p_access->pf_block )
        p_sys->method = Block;
    else if (var_CreateGetBool( s, "use-stream-immediate"))
        p_sys->method = Immediate;
    else
        p_sys->method = Stream;

316 317 318
    p_sys->i_pos = p_access->info.i_pos;

    /* Stats */
319
    access_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek );
320 321 322 323 324 325
    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;

326 327 328 329 330
    p_sys->i_list = 0;
    p_sys->list = 0;
    p_sys->i_list_index = 0;
    p_sys->p_list_access = 0;

331 332
    p_sys->b_quick = b_quick;

333 334 335 336
    /* 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) );
337 338
        if( p_entry == NULL )
            goto error;
339 340 341 342 343
        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 );
344 345 346 347 348
        if( p_entry->psz_path == NULL )
        {
            free( p_entry );
            goto error;
        }
349
        TAB_APPEND( p_sys->i_list, p_sys->list, p_entry );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
350
        msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)",
351 352 353 354 355 356 357 358 359 360
                 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 )
            {
361
                access_t *p_tmp = access_New( p_access, p_access->psz_access,
362
                                               "", psz_name );
363 364 365 366 367 368 369 370

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
371
                msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)",
372 373 374
                         psz_name, p_tmp->info.i_size );

                p_entry = malloc( sizeof(access_entry_t) );
375 376
                if( p_entry == NULL )
                    goto error;
377 378 379 380
                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 );

381
                access_Delete( p_tmp );
382 383 384 385 386 387
            }

            psz_name = psz_parser;
            if( psz_name ) psz_name++;
        }
    }
388
    FREENULL( psz_list );
389

390 391 392 393
    /* Peek */
    p_sys->i_peek = 0;
    p_sys->p_peek = NULL;

394
    if( p_sys->method == Block )
395
    {
396
        msg_Dbg( s, "Using AStream*Block" );
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        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;
        }
    }
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    else if (p_sys->method == Immediate)
    {
        msg_Dbg( s, "Using AStream*Immediate" );

        s->pf_read = AStreamReadImmediate;
        s->pf_peek = AStreamPeekImmediate;

        /* Allocate/Setup our tracks (useful to peek)*/
        p_sys->immediate.i_end = 0;
        p_sys->immediate.p_buffer = malloc( STREAM_CACHE_SIZE );

        msg_Dbg( s, "p_buffer %p-%p", p_sys->immediate.p_buffer,
                p_sys->immediate.p_buffer + STREAM_CACHE_SIZE );

        if( p_sys->immediate.p_buffer == NULL )
        {
            msg_Err( s, "Out of memory when allocating stream cache (%d bytes)",
                        STREAM_CACHE_SIZE );
            goto error;
        }
    }
    else /* ( p_sys->method == Stream ) */
439
    {
440 441
        int i;

442 443
        msg_Dbg( s, "Using AStream*Stream" );

444 445 446 447 448 449 450
        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 );
451 452 453 454 455 456
        if( p_sys->stream.p_buffer == NULL )
        {
            msg_Err( s, "Out of memory when allocating stream cache (%d bytes)",
                        STREAM_CACHE_SIZE );
            goto error;
        }
457
        p_sys->stream.i_used   = 0;
458
        access_Control( p_access, ACCESS_GET_MTU,
459
                         &p_sys->stream.i_read_size );
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
        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 );
476

477 478 479 480 481
        if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
        {
            msg_Err( s, "cannot pre fill buffer" );
            goto error;
        }
482
    }
483

484
    return s;
485 486

error:
487
    if( p_sys->method == Block )
488 489 490 491 492 493 494
    {
        /* Nothing yet */
    }
    else
    {
        free( p_sys->stream.p_buffer );
    }
495 496 497 498
    while( p_sys->i_list > 0 )
        free( p_sys->list[--(p_sys->i_list)] );
    free( p_sys->list );
    free( psz_list );
499 500
    free( s->p_sys );
    vlc_object_detach( s );
501
    vlc_object_release( s );
502
    return NULL;
503 504
}

505
/****************************************************************************
506
 * AStreamDestroy:
507
 ****************************************************************************/
508
static void AStreamDestroy( stream_t *s )
509
{
510 511 512 513
    stream_sys_t *p_sys = s->p_sys;

    vlc_object_detach( s );

514
    if( p_sys->method == Block ) block_ChainRelease( p_sys->block.p_first );
515
    else if ( p_sys->method == Immediate ) free( p_sys->immediate.p_buffer );
516 517
    else free( p_sys->stream.p_buffer );

518
    free( p_sys->p_peek );
519 520

    if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access )
521
        access_Delete( p_sys->p_list_access );
522 523

    while( p_sys->i_list-- )
524
    {
525 526
        free( p_sys->list[p_sys->i_list]->psz_path );
        free( p_sys->list[p_sys->i_list] );
527 528
    }

529 530 531
    free( p_sys->list );
    free( p_sys );

532
    vlc_object_release( s );
533 534
}

535 536
static void UStreamDestroy( stream_t *s )
{
537
    access_t *p_access = (access_t *)s->p_parent;
538
    AStreamDestroy( s );
539
    access_Delete( p_access );
540 541
}

542 543 544 545 546 547 548 549 550
/****************************************************************************
 * 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;

551
    if( p_sys->method == Block )
552 553 554 555 556 557 558 559 560 561 562 563 564 565
    {
        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 );
    }
566 567 568 569 570
    else if( p_sys->method == Immediate )
    {
        stream_buffer_empty( s, stream_buffered_size( s ) );
    }
    else /* ( p_sys->method == Stream ) */
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
    {
        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 );
    }
}
590

591 592 593 594 595 596
/****************************************************************************
 * stream_AccessUpdate:
 ****************************************************************************/
void stream_AccessUpdate( stream_t *s )
{
    stream_sys_t *p_sys = s->p_sys;
597

598
    p_sys->i_pos = p_sys->p_access->info.i_pos;
599 600 601 602 603 604 605 606 607

    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;
        }
    }
608 609
}

610
/****************************************************************************
611
 * AStreamControl:
612
 ****************************************************************************/
613
static int AStreamControl( stream_t *s, int i_query, va_list args )
614
{
615 616
    stream_sys_t *p_sys = s->p_sys;
    access_t     *p_access = p_sys->p_access;
617

618
    bool *p_bool;
619 620
    int64_t    *pi_64, i_64;
    int        i_int;
621 622 623 624

    switch( i_query )
    {
        case STREAM_GET_SIZE:
625
            pi_64 = (int64_t*)va_arg( args, int64_t * );
626 627 628 629 630 631 632 633
            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;
            }
634 635
            *pi_64 = p_access->info.i_size;
            break;
636 637

        case STREAM_CAN_SEEK:
638
            p_bool = (bool*)va_arg( args, bool * );
639
            access_Control( p_access, ACCESS_CAN_SEEK, p_bool );
640
            break;
641 642

        case STREAM_CAN_FASTSEEK:
643
            p_bool = (bool*)va_arg( args, bool * );
644
            access_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
645
            break;
646 647

        case STREAM_GET_POSITION:
648 649 650
            pi_64 = (int64_t*)va_arg( args, int64_t * );
            *pi_64 = p_sys->i_pos;
            break;
651 652

        case STREAM_SET_POSITION:
653
            i_64 = (int64_t)va_arg( args, int64_t );
654
            if( p_sys->method == Block )
655
                return AStreamSeekBlock( s, i_64 );
656 657 658
            else if( p_sys->method == Immediate )
                return AStreamSeekImmediate( s, i_64 );
            else /* ( p_sys->method == Stream ) */
659
                return AStreamSeekStream( s, i_64 );
660

661
        case STREAM_GET_MTU:
662
            return VLC_EGENERIC;
663

664
        case STREAM_CONTROL_ACCESS:
665
            i_int = (int) va_arg( args, int );
666 667 668
            if( i_int != ACCESS_SET_PRIVATE_ID_STATE &&
                i_int != ACCESS_SET_PRIVATE_ID_CA &&
                i_int != ACCESS_GET_PRIVATE_ID_STATE )
669 670 671 672 673
            {
                msg_Err( s, "Hey, what are you thinking ?"
                            "DON'T USE STREAM_CONTROL_ACCESS !!!" );
                return VLC_EGENERIC;
            }
674
            return access_vaControl( p_access, i_int, args );
675

676
        case STREAM_GET_CONTENT_TYPE:
677
            return access_Control( p_access, ACCESS_GET_CONTENT_TYPE,
678 679
                                    va_arg( args, char ** ) );

680 681 682 683
        default:
            msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
            return VLC_EGENERIC;
    }
684
    return VLC_SUCCESS;
685 686
}

687 688


689
/****************************************************************************
690
 * Method 1:
691
 ****************************************************************************/
692
static void AStreamPrebufferBlock( stream_t *s )
693
{
694
    stream_sys_t *p_sys = s->p_sys;
695
    access_t     *p_access = p_sys->p_access;
696

697 698
    int64_t i_first = 0;
    int64_t i_start;
699

700 701 702
    msg_Dbg( s, "pre buffering" );
    i_start = mdate();
    for( ;; )
703
    {
704
        int64_t i_date = mdate();
705
        bool b_eof;
706
        block_t *b;
707

708
        if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
709 710 711 712 713 714 715
            ( 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;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
716
            i_byterate = ( INT64_C(1000000) * p_sys->stat.i_bytes ) /
717
                         (p_sys->stat.i_read_time + 1);
718

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
719
            msg_Dbg( s, "prebuffering done %"PRId64" bytes in %"PRId64"s - "
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
720
                     "%"PRId64" kbytes/s",
721
                     p_sys->stat.i_bytes,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
722
                     p_sys->stat.i_read_time / INT64_C(1000000),
723 724 725
                     i_byterate / 1024 );
            break;
        }
726

727
        /* Fetch a block */
728
        if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
729
        {
730
            if( b_eof ) break;
731 732 733

            msleep( STREAM_DATA_WAIT );
            continue;
734
        }
735

736 737 738 739 740 741
        while( b )
        {
            /* 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;
742

743 744 745
            p_sys->stat.i_read_count++;
            b = b->p_next;
        }
746

747
        if( p_access->info.b_prebuffered )
748 749 750 751 752 753 754 755 756 757 758 759 760
        {
            /* Access has already prebufferred - update stats and exit */
            p_sys->stat.i_bytes = p_sys->block.i_size;
            p_sys->stat.i_read_time = mdate() - i_start;
            break;
        }

        if( i_first == 0 )
        {
            i_first = mdate();
            msg_Dbg( s, "received first data for our buffer");
        }

761 762
    }

763 764 765 766 767
    p_sys->block.p_current = p_sys->block.p_first;
}

static int AStreamRefillBlock( stream_t *s );

768
static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read )
769 770 771 772
{
    stream_sys_t *p_sys = s->p_sys;

    uint8_t *p_data= (uint8_t*)p_read;
773
    unsigned int i_data = 0;
774 775 776 777 778

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

779
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
780
    {
781
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
782 783
        stream_sys_t *p_sys = s->p_sys;
        access_t     *p_access = p_sys->p_access;
784
        bool   b_aseek;
785
        access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
Damien Fouilleul's avatar
 
Damien Fouilleul committed
786 787 788
        if( b_aseek )
            return AStreamSeekBlock( s, p_sys->i_pos + i_read ) ? 0 : i_read;
    }
789

790
    while( i_data < i_read )
791
    {
792 793
        int i_current =
            p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
794
        unsigned int i_copy = __MIN( (unsigned int)__MAX(i_current,0), i_read - i_data);
Gildas Bazin's avatar
 
Gildas Bazin committed
795

796
        /* Copy data */
797 798 799
        if( p_data )
        {
            memcpy( p_data,
800 801
                    &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
                    i_copy );
802 803
            p_data += i_copy;
        }
804
        i_data += i_copy;
805

806 807 808 809 810 811 812 813 814
        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;
            }
Rafaël Carré's avatar
Rafaël Carré committed
815 816
            /*Get a new block if needed */
            if( !p_sys->block.p_current && AStreamRefillBlock( s ) )
Gildas Bazin's avatar
Gildas Bazin committed
817 818 819
            {
                break;
            }
820 821 822
        }
    }

823 824
    p_sys->i_pos += i_data;
    return i_data;
825
}
Gildas Bazin's avatar
Gildas Bazin committed
826

827
static int AStreamPeekBlock( stream_t *s, const uint8_t **pp_peek, unsigned int i_read )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
828
{
829 830
    stream_sys_t *p_sys = s->p_sys;
    uint8_t *p_data;
831
    unsigned int i_data = 0;
832
    block_t *b;
833
    unsigned int i_offset;
834

835
    if( p_sys->block.p_current == NULL ) return 0; /* EOF */
836 837 838 839 840 841 842 843 844 845 846

    /* 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 )
    {
847 848 849 850 851 852
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
853 854 855 856
        p_sys->i_peek = i_read;
    }

    /* Fill enough data */
857 858
    while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
           < i_read )
859 860 861
    {
        block_t **pp_last = p_sys->block.pp_last;

862
        if( AStreamRefillBlock( s ) ) break;
863 864

        /* Our buffer are probably filled enough, don't try anymore */
865
        if( pp_last == p_sys->block.pp_last ) break;
866 867 868 869 870 871 872 873 874
    }

    /* 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 )
    {
875
        unsigned int i_current = __MAX(b->i_buffer - i_offset,0);
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
        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;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
892
}
893

894
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
895
{
896 897 898
    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;
899
    bool b_seek;
900

901 902 903 904 905
    /* 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;
906

907 908 909 910 911
        while( i_current + b->i_buffer < i_offset )
        {
            i_current += b->i_buffer;
            b = b->p_next;
        }
912

913 914
        p_sys->block.p_current = b;
        p_sys->block.i_offset = i_offset - i_current;
915

916
        p_sys->i_pos = i_pos;
917

918 919
        return VLC_SUCCESS;
    }
920

921 922
    /* We may need to seek or to read data */
    if( i_offset < 0 )
923
    {
924
        bool b_aseek;
925
        access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
926 927 928

        if( !b_aseek )
        {
929
            msg_Err( s, "backward seeking impossible (access not seekable)" );
930 931 932
            return VLC_EGENERIC;
        }

933
        b_seek = true;
934
    }
935 936
    else
    {
937
        bool b_aseek, b_aseekfast;
938

939 940
        access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
        access_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast );
941 942 943

        if( !b_aseek )
        {
944
            b_seek = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
945
            msg_Warn( s, "%"PRId64" bytes need to be skipped "
946
                      "(access non seekable)",
947 948 949 950 951 952 953 954 955 956 957 958 959
                      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 )
960
                b_seek = false;
961
            else
962
                b_seek = true;
963

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
964
            msg_Dbg( s, "b_seek=%d th*avg=%d skip=%"PRId64,
965 966
                     b_seek, i_th*i_avg, i_skip );
        }
967 968
    }

969 970 971 972 973
    if( b_seek )
    {
        int64_t i_start, i_end;
        /* Do the access seek */
        i_start = mdate();
974
        if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
975
        i_end = mdate();
976

977 978 979 980 981 982 983 984 985 986
        /* 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;
987

988 989 990
        /* Refill a block */
        if( AStreamRefillBlock( s ) )
            return VLC_EGENERIC;
991

992 993 994 995 996 997
        /* Update stat */
        p_sys->stat.i_seek_time += i_end - i_start;
        p_sys->stat.i_seek_count++;
        return VLC_SUCCESS;
    }
    else
998
    {
999
        do
1000
        {
1001
            /* Read and skip enough data */
1002 1003
            if( AStreamRefillBlock( s ) )
                return VLC_EGENERIC;
1004

1005
            while( p_sys->block.p_current &&
1006
                   p_sys->i_pos + p_sys->block.p_current->i_buffer - p_sys->block.i_offset < i_pos )
1007
            {
1008
                p_sys->i_pos += p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
1009
                p_sys->block.p_current = p_sys->block.p_current->p_next;
1010
                p_sys->block.i_offset = 0;
1011 1012
            }
        }
1013
        while( p_sys->block.i_start + p_sys->block.i_size < i_pos );
1014

1015 1016
        p_sys->block.i_offset = i_pos - p_sys->i_pos;
        p_sys->i_pos = i_pos;
1017

1018
        return VLC_SUCCESS;
1019 1020
    }

1021
    return VLC_EGENERIC;
1022 1023
}

1024
static int AStreamRefillBlock( stream_t *s )
1025
{
1026 1027 1028 1029 1030 1031 1032 1033 1034
    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;
1035

1036 1037 1038
        p_sys->block.i_start += b->i_buffer;
        p_sys->block.i_size  -= b->i_buffer;
        p_sys->block.p_first  = b->p_next;
1039

1040 1041 1042 1043 1044
        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 */
1045
    {
1046 1047
        /* Enough data, don't read more */
        return VLC_SUCCESS;
1048 1049
    }

1050 1051 1052
    /* Now read a new block */
    i_start = mdate();
    for( ;; )
1053
    {
1054
        bool b_eof;
1055 1056

        if( s->b_die ) return VLC_EGENERIC;
1057 1058 1059


        /* Fetch a block */
1060
        if( ( b = AReadBlock( s, &b_eof ) ) ) break;
1061

1062
        if( b_eof ) return VLC_EGENERIC;
1063 1064

        msleep( STREAM_DATA_WAIT );
1065
    }
1066

1067 1068 1069
    while( b )
    {
        i_stop = mdate();
1070

1071 1072 1073 1074
        /* 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;
1075

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

1080 1081 1082 1083 1084 1085 1086 1087
        /* 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++;

        b = b->p_next;
        i_start = mdate();
    }
1088
    return VLC_SUCCESS;
1089 1090 1091
}


1092 1093 1094 1095 1096
/****************************************************************************
 * Method 2:
 ****************************************************************************/
static int AStreamRefillStream( stream_t *s );