stream.c 49.2 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 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>

28
#include "input_internal.h"
29

30 31
#undef STREAM_DEBUG

32 33 34 35 36
/* TODO:
 *  - tune the 2 methods
 *  - compute cost for seek
 *  - improve stream mode seeking with closest segments
 *  - ...
37
 */
38 39 40 41 42 43 44 45

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

46 47
/* How many tracks we have, currently only used for stream mode */
#ifdef OPTIMIZE_MEMORY
48 49 50 51 52 53 54 55 56 57
#   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 */
58
#define STREAM_CACHE_PREBUFFER_SIZE (32767)
59 60
/* Maximum time we take to pre-buffer */
#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000)
61 62 63 64 65

/* 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
 */
66
#define STREAM_DATA_WAIT 40000       /* Time between before a pf_block retry */
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

/* 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
85
{
86
    int64_t i_date;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
87

88 89 90 91
    int64_t i_start;
    int64_t i_end;

    uint8_t *p_buffer;
92

93 94
} stream_track_t;

95 96 97 98 99 100 101
typedef struct
{
    char     *psz_path;
    int64_t  i_size;

} access_entry_t;

102 103 104 105 106 107 108 109 110 111
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
112
    {
113 114 115 116 117 118 119
        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;
120

121 122 123 124
    } block;

    /* Method 2: for pf_read */
    struct
125
    {
126
        int i_offset;   /* Buffer offset in the current track */
127 128 129 130 131 132 133 134 135
        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;
136

137 138 139 140 141 142 143 144
    } stream;

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

    /* Stat for both method */
    struct
145
    {
146
        vlc_bool_t b_fastseek;  /* From access */
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
147

148 149 150 151
        /* Stat about reading data */
        int64_t i_read_count;
        int64_t i_bytes;
        int64_t i_read_time;
152

153 154 155
        /* Stat about seek */
        int     i_seek_count;
        int64_t i_seek_time;
156

157
    } stat;
158

159 160 161 162 163 164
    /* Streams list */
    int            i_list;
    access_entry_t **list;
    int            i_list_index;
    access_t       *p_list_access;

165
    /* Preparse mode ? */
166
    vlc_bool_t      b_quick;
167
};
168

169
/* Method 1: */
170 171
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 );
172
static int  AStreamSeekBlock( stream_t *s, int64_t i_pos );
173 174
static void AStreamPrebufferBlock( stream_t *s );
static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof );
175

176
/* Method 2 */
177 178
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 );
179
static int  AStreamSeekStream( stream_t *s, int64_t i_pos );
180 181
static void AStreamPrebufferStream( stream_t *s );
static int  AReadStream( stream_t *s, void *p_read, int i_read );
182

183
/* Common */
184
static int AStreamControl( stream_t *s, int i_query, va_list );
185 186
static void AStreamDestroy( stream_t *s );
static void UStreamDestroy( stream_t *s );
187
static int  ASeek( stream_t *s, int64_t i_pos );
188

189

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

199 200
    if( !psz_url ) return 0;

201 202
    psz_dup = strdup( psz_url );
    MRLSplit( p_parent, psz_dup, &psz_access, &psz_demux, &psz_path );
203

204
    /* Now try a real access */
205 206
    p_access = access2_New( p_parent, psz_access, psz_demux, psz_path, 0 );
    free( psz_dup );
207 208 209 210 211 212

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

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

    p_res->pf_destroy = UStreamDestroy;
    return p_res;
222 223
}

224
stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick )
225
{
226 227
    stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM );
    stream_sys_t *p_sys;
228
    char *psz_list;
229

230
    if( !s ) return NULL;
231 232 233 234 235 236 237 238

    /* 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;
239
    s->pf_destroy = AStreamDestroy;
240 241 242

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

243 244 245
    /* UTF16 and UTF32 text file conversion */
    s->i_char_width = 1;
    s->b_little_endian = VLC_FALSE;
246
    s->conv = (vlc_iconv_t)(-1);
247

248 249 250 251 252 253 254 255 256 257 258 259 260
    /* 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;

261 262 263 264 265
    p_sys->i_list = 0;
    p_sys->list = 0;
    p_sys->i_list_index = 0;
    p_sys->p_list_access = 0;

266 267
    p_sys->b_quick = b_quick;

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 309 310 311 312 313 314 315
    /* 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 );

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    /* 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
343
    {
344 345 346 347 348 349 350 351 352 353
        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;
354 355
        access2_Control( p_access, ACCESS_GET_MTU,
                         &p_sys->stream.i_read_size );
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
        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 );
372

373 374 375 376 377
        if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
        {
            msg_Err( s, "cannot pre fill buffer" );
            goto error;
        }
378
    }
379

380
    return s;
381 382 383 384 385 386 387 388 389 390 391 392 393 394

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;
395 396
}

397
/****************************************************************************
398
 * AStreamDestroy:
399
 ****************************************************************************/
400
static void AStreamDestroy( stream_t *s )
401
{
402 403 404 405
    stream_sys_t *p_sys = s->p_sys;

    vlc_object_detach( s );

406 407 408 409 410 411 412 413 414
    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-- )
415
    {
416 417 418
        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 );
419 420
    }

421
    free( s->p_sys );
422 423 424
    vlc_object_destroy( s );
}

425 426 427 428
static void UStreamDestroy( stream_t *s )
{
    access_t *p_access = (access_t*)vlc_object_find( s, VLC_OBJECT_ACCESS, FIND_PARENT );
    AStreamDestroy( s );
429
    vlc_object_release( p_access );
430 431 432
    access2_Delete( p_access );
}

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 470 471 472 473 474 475 476
/****************************************************************************
 * 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 );
    }
}
477

478 479 480 481 482 483
/****************************************************************************
 * stream_AccessUpdate:
 ****************************************************************************/
void stream_AccessUpdate( stream_t *s )
{
    stream_sys_t *p_sys = s->p_sys;
484

485
    p_sys->i_pos = p_sys->p_access->info.i_pos;
486 487 488 489 490 491 492 493 494

    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;
        }
    }
495 496
}

497
/****************************************************************************
498
 * AStreamControl:
499
 ****************************************************************************/
500
static int AStreamControl( stream_t *s, int i_query, va_list args )
501
{
502 503
    stream_sys_t *p_sys = s->p_sys;
    access_t     *p_access = p_sys->p_access;
504

505 506 507
    vlc_bool_t *p_bool;
    int64_t    *pi_64, i_64;
    int        i_int;
508 509 510 511

    switch( i_query )
    {
        case STREAM_GET_SIZE:
512
            pi_64 = (int64_t*)va_arg( args, int64_t * );
513 514 515 516 517 518 519 520
            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;
            }
521 522
            *pi_64 = p_access->info.i_size;
            break;
523 524

        case STREAM_CAN_SEEK:
525 526 527
            p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            access2_Control( p_access, ACCESS_CAN_SEEK, p_bool );
            break;
528 529

        case STREAM_CAN_FASTSEEK:
530 531 532
            p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            access2_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
            break;
533 534

        case STREAM_GET_POSITION:
535 536 537
            pi_64 = (int64_t*)va_arg( args, int64_t * );
            *pi_64 = p_sys->i_pos;
            break;
538 539

        case STREAM_SET_POSITION:
540 541 542 543 544
            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 );
545

546
        case STREAM_GET_MTU:
547
            return VLC_EGENERIC;
548

549
        case STREAM_CONTROL_ACCESS:
550
            i_int = (int) va_arg( args, int );
551 552 553
            if( i_int != ACCESS_SET_PRIVATE_ID_STATE &&
                i_int != ACCESS_SET_PRIVATE_ID_CA &&
                i_int != ACCESS_GET_PRIVATE_ID_STATE )
554 555 556 557 558
            {
                msg_Err( s, "Hey, what are you thinking ?"
                            "DON'T USE STREAM_CONTROL_ACCESS !!!" );
                return VLC_EGENERIC;
            }
559
            return access2_vaControl( p_access, i_int, args );
560

561 562 563 564
        default:
            msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
            return VLC_EGENERIC;
    }
565
    return VLC_SUCCESS;
566 567
}

568 569


570
/****************************************************************************
571
 * Method 1:
572
 ****************************************************************************/
573
static void AStreamPrebufferBlock( stream_t *s )
574
{
575
    stream_sys_t *p_sys = s->p_sys;
576
    access_t     *p_access = p_sys->p_access;
577

578 579
    int64_t i_first = 0;
    int64_t i_start;
580

581 582 583
    msg_Dbg( s, "pre buffering" );
    i_start = mdate();
    for( ;; )
584
    {
585
        int64_t i_date = mdate();
586
        vlc_bool_t b_eof;
587
        block_t *b;
588

589
        if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
590 591 592 593 594 595 596 597
            ( 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 ) /
598
                         (p_sys->stat.i_read_time + 1);
599

600 601
            msg_Dbg( s, "prebuffering done "I64Fd" bytes in "I64Fd"s - "
                     I64Fd" kbytes/s",
602 603 604 605 606
                     p_sys->stat.i_bytes,
                     p_sys->stat.i_read_time / I64C(1000000),
                     i_byterate / 1024 );
            break;
        }
607

608
        /* Fetch a block */
609
        if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
610
        {
611
            if( b_eof ) break;
612 613 614

            msleep( STREAM_DATA_WAIT );
            continue;
615
        }
616

617 618 619 620 621 622
        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;
623

624 625 626
            p_sys->stat.i_read_count++;
            b = b->p_next;
        }
627

628
        if( p_access->info.b_prebuffered )
629 630 631 632 633 634 635 636 637 638 639 640 641
        {
            /* 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");
        }

642 643
    }

644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    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;

660
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
661
    {
662
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
663 664 665 666 667 668 669
        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;
    }
670

671
    while( i_data < i_read )
672
    {
673 674
        int i_current =
            p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
675
        int i_copy = __MIN( i_current, i_read - i_data);
Gildas Bazin's avatar
 
Gildas Bazin committed
676

677 678
        /* Copy data */
        if( p_data )
679
        {
680 681 682
            memcpy( p_data,
                    &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
                    i_copy );
683 684 685
            p_data += i_copy;
        }
        i_data += i_copy;
686

687 688 689 690 691 692 693 694 695 696 697
        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 ) )
Gildas Bazin's avatar
Gildas Bazin committed
698 699 700
            {
                break;
            }
701 702 703
        }
    }

704 705
    p_sys->i_pos += i_data;
    return i_data;
706
}
Gildas Bazin's avatar
Gildas Bazin committed
707

708
static int AStreamPeekBlock( stream_t *s, uint8_t **pp_peek, int i_read )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
709
{
710 711 712 713 714 715
    stream_sys_t *p_sys = s->p_sys;
    uint8_t *p_data;
    int      i_data = 0;
    block_t *b;
    int      i_offset;

716
    if( p_sys->block.p_current == NULL ) return 0; /* EOF */
717 718 719 720 721 722 723 724 725 726 727

    /* 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 )
    {
728 729 730 731 732 733
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
734 735 736 737
        p_sys->i_peek = i_read;
    }

    /* Fill enough data */
738 739
    while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
           < i_read )
740 741 742
    {
        block_t **pp_last = p_sys->block.pp_last;

743
        if( AStreamRefillBlock( s ) ) break;
744 745

        /* Our buffer are probably filled enough, don't try anymore */
746
        if( pp_last == p_sys->block.pp_last ) break;
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    }

    /* 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;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
773
}
774

775
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
776
{
777 778 779 780
    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;
781

782 783 784 785 786
    /* 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;
787

788 789 790 791 792
        while( i_current + b->i_buffer < i_offset )
        {
            i_current += b->i_buffer;
            b = b->p_next;
        }
793

794 795
        p_sys->block.p_current = b;
        p_sys->block.i_offset = i_offset - i_current;
796

797
        p_sys->i_pos = i_pos;
798

799 800
        return VLC_SUCCESS;
    }
801

802 803
    /* We may need to seek or to read data */
    if( i_offset < 0 )
804
    {
805 806 807 808 809 810 811 812 813 814
        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;
815
    }
816 817 818
    else
    {
        vlc_bool_t b_aseek, b_aseekfast;
819

820 821 822 823 824 825
        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;
826 827
            msg_Warn( s, I64Fd" bytes need to be skipped "
                      "(access non seekable)",
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
                      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;

845
            msg_Dbg( s, "b_seek=%d th*avg=%d skip="I64Fd,
846 847
                     b_seek, i_th*i_avg, i_skip );
        }
848 849
    }

850 851 852 853 854
    if( b_seek )
    {
        int64_t i_start, i_end;
        /* Do the access seek */
        i_start = mdate();
855
        if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
856
        i_end = mdate();
857

858 859 860 861 862 863 864 865 866 867
        /* 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;
868

869 870 871 872 873 874 875 876 877 878 879 880
        /* 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
881
    {
882
        /* Read enough data */
883
        while( p_sys->block.i_start + p_sys->block.i_size < i_pos )
884
        {
885 886 887 888 889 890 891
            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 )
892
            {
893 894
                p_sys->i_pos += p_sys->block.p_current->i_buffer;
                p_sys->block.p_current = p_sys->block.p_current->p_next;
895 896 897
            }
        }

898 899
        p_sys->block.i_offset = i_pos - p_sys->i_pos;
        p_sys->i_pos = i_pos;
900

901 902
        /* TODO read data */
        return VLC_SUCCESS;
903 904
    }

905
    return VLC_EGENERIC;
906 907
}

908
static int AStreamRefillBlock( stream_t *s )
909
{
910 911 912 913 914 915 916 917 918
    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;
919

920 921 922
        p_sys->block.i_start += b->i_buffer;
        p_sys->block.i_size  -= b->i_buffer;
        p_sys->block.p_first  = b->p_next;
923

924 925 926 927 928
        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 */
929
    {
930 931
        /* Enough data, don't read more */
        return VLC_SUCCESS;
932 933
    }

934 935 936
    /* Now read a new block */
    i_start = mdate();
    for( ;; )
937
    {
938 939 940
        vlc_bool_t b_eof;

        if( s->b_die ) return VLC_EGENERIC;
941 942 943


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

946
        if( b_eof ) return VLC_EGENERIC;
947 948

        msleep( STREAM_DATA_WAIT );
949
    }
950

951 952 953
    while( b )
    {
        i_stop = mdate();
954

955 956 957 958
        /* 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;
959

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

964 965 966 967 968 969 970 971
        /* 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();
    }
972
    return VLC_SUCCESS;
973 974 975
}


976 977 978 979 980 981
/****************************************************************************
 * Method 2:
 ****************************************************************************/
static int AStreamRefillStream( stream_t *s );

static int AStreamReadStream( stream_t *s, void *p_read, int i_read )
982
{
983 984
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
985

986
    uint8_t *p_data = (uint8_t *)p_read;
987
    int      i_data = 0;
988

989
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
990

991
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
992
    {
993
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
994 995 996 997 998 999 1000
        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;
    }
1001

1002
#ifdef STREAM_DEBUG
1003 1004 1005 1006 1007
    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
1008 1009

    while( i_data < i_read )
1010
    {
1011 1012 1013 1014 1015
        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 );
1016 1017
        int i_copy = __MIN( i_current, i_read - i_data );

1018 1019
        if( i_copy <= 0 ) break; /* EOF */

1020
        /* Copy data */
1021
        /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */
1022
        if( p_data )
1023
        {
1024 1025
            memcpy( p_data, &tk->p_buffer[i_off], i_copy );
            p_data += i_copy;
1026
        }
1027 1028 1029 1030
        i_data += i_copy;
        p_sys->stream.i_offset += i_copy;

        /* Update pos now */
1031 1032
        p_sys->i_pos += i_copy;

1033 1034 1035 1036
        /* */
        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 )
1037
        {
1038 1039
            if( AStreamRefillStream( s ) )
            {
1040 1041
                /* EOF */
                if( tk->i_start >= tk->i_end ) break;
1042
            }
1043 1044 1045
        }
    }

1046
    return i_data;
1047
}
1048 1049

static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read )
1050
{
1051 1052 1053
    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;
1054

1055
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1056

1057
#ifdef STREAM_DEBUG
1058 1059 1060 1061 1062
    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
1063 1064 1065 1066 1067 1068

    /* 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 )
1069
    {
1070 1071 1072
        if( p_sys->stream.i_used <= 1 )
        {
            /* Be sure we will read something */
1073 1074
            p_sys->stream.i_used += i_read -
                (tk->i_end - tk->i_start - p_sys->stream.i_offset);
1075
        }
1076
        if( AStreamRefillStream( s ) ) break;
1077 1078
    }

1079 1080
    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;
1081

1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
    /* 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 )
    {
1092 1093 1094 1095 1096 1097
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
1098 1099 1100
        p_sys->i_peek = i_read;
    }

1101 1102 1103 1104
    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) );
1105 1106 1107

    *pp_peek = p_sys->p_peek;
    return i_read;
1108 1109
}

1110
static int AStreamSeekStream( stream_t *s, int64_t i_pos )
1111
{
1112 1113 1114 1115 1116 1117 1118 1119
    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;

1120
#ifdef STREAM_DEBUG
1121
    msg_Dbg( s, "AStreamSeekStream: to "I64Fd" pos="I64Fd
1122
             " tk=%d start="I64Fd" offset=%d end="I64Fd,
1123 1124 1125 1126 1127
             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
1128 1129 1130 1131 1132


    /* 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 )
1133
    {
1134 1135 1136 1137
        stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
#ifdef STREAM_DEBUG
        msg_Dbg( s, "AStreamSeekStream: current track" );
#endif
1138
        p_sys->i_pos = i_pos;
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
        p_sys->stream.i_offset = i_pos - tk->i_start;

        /* If there is not enough data left in the track, refill  */
        /* \todo How to get a correct value for
         *    - refilling threshold
         *    - how much to refill
         */
        if( (tk->i_end - tk->i_start ) - p_sys->stream.i_offset <
                                             p_sys->stream.i_read_size )
        {
            if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2  )
            {
                p_sys->stream.i_used = STREAM_READ_ATONCE / 2 ;
1152
                AStreamRefillStream( s );
1153 1154
            }
        }
1155 1156
        return VLC_SUCCESS;
    }
1157

1158 1159 1160 1161
    access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
    if( !b_aseek )
    {
        /* We can't do nothing */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
1162
        msg_Dbg( s, "AStreamSeekStream: can't seek" );
1163 1164
        return VLC_EGENERIC;
    }
1165

1166 1167
    /* Date the current track */
    p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1168

1169 1170 1171 1172
    /* Try to reuse already read data */
    for( i = 0; i < STREAM_CACHE_TRACK; i++ )
    {
        stream_track_t *tk = &p_sys->stream.tk[i];
1173

1174 1175
        if( i_pos >= tk->i_start && i_pos <= tk->i_end )
        {
1176
#ifdef STREAM_DEBUG
1177 1178 1179
            msg_Dbg( s, "AStreamSeekStream: reusing %d start="I64Fd
                     " end="I64Fd, i, tk->i_start, tk->i_end );
#endif
1180

1181
            /* Seek at the end of the buffer */
1182
            if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
1183 1184 1185 1186 1187 1188 1189 1190 1191

            /* 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;

1192
            if( AStreamRefillStream( s ) && i_pos == tk->i_end )
1193
                return VLC_EGENERIC;
1194 1195

            return VLC_SUCCESS;
1196 1197
        }
    }
1198

1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
    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" );
        }
1220
    }
1221 1222 1223
#endif

    /* Nothing good, seek and choose oldest segment */
1224
    if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
    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;
1248 1249
}

1250
static int AStreamRefillStream( stream_t *s )
1251
{
1252 1253
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk =