stream.c 51.7 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
 *****************************************************************************/

#include <stdlib.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
27
#include <assert.h>
28

29
#include "input_internal.h"
30

31 32
#undef STREAM_DEBUG

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

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

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

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

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

89 90 91 92
    int64_t i_start;
    int64_t i_end;

    uint8_t *p_buffer;
93

94 95
} stream_track_t;

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

} access_entry_t;

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

122 123 124 125
    } block;

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

138 139 140 141 142 143 144 145
    } stream;

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

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

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

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

158
    } stat;
159

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

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

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

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

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

190

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

200 201
    if( !psz_url ) return 0;

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

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

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

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

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

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

231
    if( !s ) return NULL;
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;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
239
    s->pf_control = AStreamControl;
240
    s->pf_destroy = AStreamDestroy;
241 242 243

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

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

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

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

267 268
    p_sys->b_quick = b_quick;

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

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 343
    /* 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
344
    {
345 346 347 348 349 350 351 352 353 354
        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;
355 356
        access2_Control( p_access, ACCESS_GET_MTU,
                         &p_sys->stream.i_read_size );
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
        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 );
373

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

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

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

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

    vlc_object_detach( s );

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

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

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

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 477
/****************************************************************************
 * 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 );
    }
}
478

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

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

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

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

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

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

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

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

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

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

547
        case STREAM_GET_MTU:
548
            return VLC_EGENERIC;
549

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

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

569 570


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

579 580
    int64_t i_first = 0;
    int64_t i_start;
581

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

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

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

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

            msleep( STREAM_DATA_WAIT );
            continue;
616
        }
617

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

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

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

643 644
    }

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

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

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

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

688 689 690 691 692 693 694 695 696 697 698
        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
699 700 701
            {
                break;
            }
702 703 704
        }
    }

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

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

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

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

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

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

        /* Our buffer are probably filled enough, don't try anymore */
747
        if( pp_last == p_sys->block.pp_last ) break;
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 773
    }

    /* 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
774
}
775

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

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

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

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

798
        p_sys->i_pos = i_pos;
799

800 801
        return VLC_SUCCESS;
    }
802

803 804
    /* We may need to seek or to read data */
    if( i_offset < 0 )
805
    {
806 807 808 809 810
        vlc_bool_t b_aseek;
        access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );

        if( !b_aseek )
        {
811
            msg_Err( s, "backward seeking impossible (access not seekable)" );
812 813 814 815
            return VLC_EGENERIC;
        }

        b_seek = VLC_TRUE;
816
    }
817 818 819
    else
    {
        vlc_bool_t b_aseek, b_aseekfast;
820

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

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

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

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

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

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

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

906
    return VLC_EGENERIC;
907 908
}

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

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

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

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

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


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

947
        if( b_eof ) return VLC_EGENERIC;
948 949

        msleep( STREAM_DATA_WAIT );
950
    }
951

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

1047
    return i_data;
1048
}
1049 1050

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

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

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

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

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

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

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

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

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

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


    /* 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 )
1134
    {
1135 1136 1137 1138
        stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
#ifdef STREAM_DEBUG
        msg_Dbg( s, "AStreamSeekStream: current track" );
#endif
1139
        p_sys->i_pos = i_pos;
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
        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 ;
1153
                AStreamRefillStream( s );
1154 1155
            }
        }
1156 1157
        return VLC_SUCCESS;
    }
1158

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

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

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

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

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

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

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

            return VLC_SUCCESS;
1197 1198
        }
    }
1199

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

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