stream.c 57 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 29
#include <vlc/vlc.h>

30
#include "input_internal.h"
31

32 33
#undef STREAM_DEBUG

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

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

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

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

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

90 91 92 93
    int64_t i_start;
    int64_t i_end;

    uint8_t *p_buffer;
94

95 96
} stream_track_t;

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

} access_entry_t;

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

123 124 125 126
    } block;

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

139 140 141 142 143 144 145 146
    } stream;

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

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

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

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

159
    } stat;
160

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

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

171
/* Method 1: */
172
static int  AStreamReadBlock( stream_t *s, void *p_read, int i_read );
173
static int  AStreamPeekBlock( stream_t *s, const uint8_t **p_peek, int i_read );
174
static int  AStreamSeekBlock( stream_t *s, int64_t i_pos );
175 176
static void AStreamPrebufferBlock( stream_t *s );
static block_t *AReadBlock( stream_t *s, vlc_bool_t *pb_eof );
177

178
/* Method 2 */
179
static int  AStreamReadStream( stream_t *s, void *p_read, int i_read );
180
static int  AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, int i_read );
181
static int  AStreamSeekStream( stream_t *s, int64_t i_pos );
182 183
static void AStreamPrebufferStream( stream_t *s );
static int  AReadStream( stream_t *s, void *p_read, int i_read );
184

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

191

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

202 203
    if( !psz_url ) return 0;

204 205
    char psz_dup[strlen (psz_url) + 1];
    strcpy (psz_dup, psz_url);;
206
    MRLSplit( p_parent, psz_dup, &psz_access, &psz_demux, &psz_path );
207

208
    /* Now try a real access */
209
    p_access = access2_New( p_parent, psz_access, psz_demux, psz_path, 0 );
210 211 212 213 214 215

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

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

    p_res->pf_destroy = UStreamDestroy;
    return p_res;
225 226
}

227
stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick )
228
{
229
    stream_t *s = vlc_stream_create( VLC_OBJECT(p_access) );
230
    stream_sys_t *p_sys;
231
    char *psz_list = NULL;
232

233
    if( !s ) return NULL;
234 235 236 237 238 239

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

    s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) );
244 245 246 247 248
    if( p_sys == NULL )
    {
        msg_Err( s, "Out of memory when allocating stream_sys_t" );
        goto error;
    }
249

250 251 252
    /* UTF16 and UTF32 text file conversion */
    s->i_char_width = 1;
    s->b_little_endian = VLC_FALSE;
253
    s->conv = (vlc_iconv_t)(-1);
254

255 256 257 258 259 260 261 262 263 264 265 266 267
    /* 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;

268 269 270 271 272
    p_sys->i_list = 0;
    p_sys->list = 0;
    p_sys->i_list_index = 0;
    p_sys->p_list_access = 0;

273 274
    p_sys->b_quick = b_quick;

275 276 277 278
    /* 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) );
279 280 281 282 283
        if( p_entry == NULL )
        {
            msg_Err( s, "Out of memory when allocating access_entry_t" );
            goto error;
        }
284 285 286 287 288
        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 );
289 290 291 292 293 294
        if( p_entry->psz_path == NULL )
        {
            msg_Err( s, "Out of memory when duplicating p_access->psz_path" );
            free( p_entry );
            goto error;
        }
295 296 297 298 299 300 301 302 303 304 305 306 307
        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,
308
                                               "", psz_name, 0 );
309 310 311 312 313 314 315 316 317 318 319 320

                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) );
321 322 323 324 325
                if( p_entry == NULL )
                {
                    msg_Err( p_access, "Out of memory when allocating access_entry_t" );
                    goto error;
                }
326 327 328 329 330 331 332 333 334 335 336
                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++;
        }
    }
337
    FREENULL( psz_list );
338

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    /* 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
366
    {
367 368 369 370 371 372 373 374 375
        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 );
376 377 378 379 380 381
        if( p_sys->stream.p_buffer == NULL )
        {
            msg_Err( s, "Out of memory when allocating stream cache (%d bytes)",
                        STREAM_CACHE_SIZE );
            goto error;
        }
382
        p_sys->stream.i_used   = 0;
383 384
        access2_Control( p_access, ACCESS_GET_MTU,
                         &p_sys->stream.i_read_size );
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
        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 );
401

402 403 404 405 406
        if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
        {
            msg_Err( s, "cannot pre fill buffer" );
            goto error;
        }
407
    }
408

409
    return s;
410 411 412 413 414 415 416 417 418 419

error:
    if( p_sys->b_block )
    {
        /* Nothing yet */
    }
    else
    {
        free( p_sys->stream.p_buffer );
    }
420 421 422 423
    while( p_sys->i_list > 0 )
        free( p_sys->list[--(p_sys->i_list)] );
    free( p_sys->list );
    free( psz_list );
424 425 426 427
    free( s->p_sys );
    vlc_object_detach( s );
    vlc_object_destroy( s );
    return NULL;
428 429
}

430
/****************************************************************************
431
 * AStreamDestroy:
432
 ****************************************************************************/
433
static void AStreamDestroy( stream_t *s )
434
{
435 436 437 438
    stream_sys_t *p_sys = s->p_sys;

    vlc_object_detach( s );

439 440 441 442 443 444 445 446 447
    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-- )
448
    {
449 450 451
        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 );
452 453
    }

454
    free( s->p_sys );
455 456 457
    vlc_object_destroy( s );
}

458 459 460 461
static void UStreamDestroy( stream_t *s )
{
    access_t *p_access = (access_t*)vlc_object_find( s, VLC_OBJECT_ACCESS, FIND_PARENT );
    AStreamDestroy( s );
462
    vlc_object_release( p_access );
463 464 465
    access2_Delete( p_access );
}

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
/****************************************************************************
 * 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 );
    }
}
510

511 512 513 514 515 516
/****************************************************************************
 * stream_AccessUpdate:
 ****************************************************************************/
void stream_AccessUpdate( stream_t *s )
{
    stream_sys_t *p_sys = s->p_sys;
517

518
    p_sys->i_pos = p_sys->p_access->info.i_pos;
519 520 521 522 523 524 525 526 527

    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;
        }
    }
528 529
}

530
/****************************************************************************
531
 * AStreamControl:
532
 ****************************************************************************/
533
static int AStreamControl( stream_t *s, int i_query, va_list args )
534
{
535 536
    stream_sys_t *p_sys = s->p_sys;
    access_t     *p_access = p_sys->p_access;
537

538 539 540
    vlc_bool_t *p_bool;
    int64_t    *pi_64, i_64;
    int        i_int;
541 542 543 544

    switch( i_query )
    {
        case STREAM_GET_SIZE:
545
            pi_64 = (int64_t*)va_arg( args, int64_t * );
546 547 548 549 550 551 552 553
            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;
            }
554 555
            *pi_64 = p_access->info.i_size;
            break;
556 557

        case STREAM_CAN_SEEK:
558 559 560
            p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            access2_Control( p_access, ACCESS_CAN_SEEK, p_bool );
            break;
561 562

        case STREAM_CAN_FASTSEEK:
563 564 565
            p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            access2_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
            break;
566 567

        case STREAM_GET_POSITION:
568 569 570
            pi_64 = (int64_t*)va_arg( args, int64_t * );
            *pi_64 = p_sys->i_pos;
            break;
571 572

        case STREAM_SET_POSITION:
573 574 575 576 577
            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 );
578

579
        case STREAM_GET_MTU:
580
            return VLC_EGENERIC;
581

582
        case STREAM_CONTROL_ACCESS:
583
            i_int = (int) va_arg( args, int );
584 585 586
            if( i_int != ACCESS_SET_PRIVATE_ID_STATE &&
                i_int != ACCESS_SET_PRIVATE_ID_CA &&
                i_int != ACCESS_GET_PRIVATE_ID_STATE )
587 588 589 590 591
            {
                msg_Err( s, "Hey, what are you thinking ?"
                            "DON'T USE STREAM_CONTROL_ACCESS !!!" );
                return VLC_EGENERIC;
            }
592
            return access2_vaControl( p_access, i_int, args );
593

594 595 596 597
        case STREAM_GET_CONTENT_TYPE:
            return access2_Control( p_access, ACCESS_GET_CONTENT_TYPE,
                                    va_arg( args, char ** ) );

598 599 600 601
        default:
            msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
            return VLC_EGENERIC;
    }
602
    return VLC_SUCCESS;
603 604
}

605 606


607
/****************************************************************************
608
 * Method 1:
609
 ****************************************************************************/
610
static void AStreamPrebufferBlock( stream_t *s )
611
{
612
    stream_sys_t *p_sys = s->p_sys;
613
    access_t     *p_access = p_sys->p_access;
614

615 616
    int64_t i_first = 0;
    int64_t i_start;
617

618 619 620
    msg_Dbg( s, "pre buffering" );
    i_start = mdate();
    for( ;; )
621
    {
622
        int64_t i_date = mdate();
623
        vlc_bool_t b_eof;
624
        block_t *b;
625

626
        if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
627 628 629 630 631 632 633 634
            ( 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 ) /
635
                         (p_sys->stat.i_read_time + 1);
636

637 638
            msg_Dbg( s, "prebuffering done "I64Fd" bytes in "I64Fd"s - "
                     I64Fd" kbytes/s",
639 640 641 642 643
                     p_sys->stat.i_bytes,
                     p_sys->stat.i_read_time / I64C(1000000),
                     i_byterate / 1024 );
            break;
        }
644

645
        /* Fetch a block */
646
        if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
647
        {
648
            if( b_eof ) break;
649 650 651

            msleep( STREAM_DATA_WAIT );
            continue;
652
        }
653

654 655 656 657 658 659
        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;
660

661 662 663
            p_sys->stat.i_read_count++;
            b = b->p_next;
        }
664

665
        if( p_access->info.b_prebuffered )
666 667 668 669 670 671 672 673 674 675 676 677 678
        {
            /* 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");
        }

679 680
    }

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
    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;

697
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
698
    {
699
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
700 701 702 703 704 705 706
        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;
    }
707

708
    while( i_data < i_read )
709
    {
710 711
        int i_current =
            p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
712
        int i_copy = __MIN( i_current, i_read - i_data);
Gildas Bazin's avatar
 
Gildas Bazin committed
713

714 715
        /* Copy data */
        if( p_data )
716
        {
717 718 719
            memcpy( p_data,
                    &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
                    i_copy );
720 721 722
            p_data += i_copy;
        }
        i_data += i_copy;
723

724 725 726 727 728 729 730 731 732 733 734
        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
735 736 737
            {
                break;
            }
738 739 740
        }
    }

741 742
    p_sys->i_pos += i_data;
    return i_data;
743
}
Gildas Bazin's avatar
Gildas Bazin committed
744

745
static int AStreamPeekBlock( stream_t *s, const uint8_t **pp_peek, int i_read )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
746
{
747 748 749 750 751 752
    stream_sys_t *p_sys = s->p_sys;
    uint8_t *p_data;
    int      i_data = 0;
    block_t *b;
    int      i_offset;

753
    if( p_sys->block.p_current == NULL ) return 0; /* EOF */
754 755 756 757 758 759 760 761 762 763 764

    /* 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 )
    {
765 766 767 768 769 770
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
771 772 773 774
        p_sys->i_peek = i_read;
    }

    /* Fill enough data */
775 776
    while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
           < i_read )
777 778 779
    {
        block_t **pp_last = p_sys->block.pp_last;

780
        if( AStreamRefillBlock( s ) ) break;
781 782

        /* Our buffer are probably filled enough, don't try anymore */
783
        if( pp_last == p_sys->block.pp_last ) break;
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
    }

    /* 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
810
}
811

812
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
813
{
814 815 816 817
    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;
818

819 820 821 822 823
    /* 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;
824

825 826 827 828 829
        while( i_current + b->i_buffer < i_offset )
        {
            i_current += b->i_buffer;
            b = b->p_next;
        }
830

831 832
        p_sys->block.p_current = b;
        p_sys->block.i_offset = i_offset - i_current;
833

834
        p_sys->i_pos = i_pos;
835

836 837
        return VLC_SUCCESS;
    }
838

839 840
    /* We may need to seek or to read data */
    if( i_offset < 0 )
841
    {
842 843 844 845 846
        vlc_bool_t b_aseek;
        access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );

        if( !b_aseek )
        {
847
            msg_Err( s, "backward seeking impossible (access not seekable)" );
848 849 850 851
            return VLC_EGENERIC;
        }

        b_seek = VLC_TRUE;
852
    }
853 854 855
    else
    {
        vlc_bool_t b_aseek, b_aseekfast;
856

857 858 859 860 861 862
        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;
863 864
            msg_Warn( s, I64Fd" bytes need to be skipped "
                      "(access non seekable)",
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
                      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;

882
            msg_Dbg( s, "b_seek=%d th*avg=%d skip="I64Fd,
883 884
                     b_seek, i_th*i_avg, i_skip );
        }
885 886
    }

887 888 889 890 891
    if( b_seek )
    {
        int64_t i_start, i_end;
        /* Do the access seek */
        i_start = mdate();
892
        if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
893
        i_end = mdate();
894

895 896 897 898 899 900 901 902 903 904
        /* 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;
905

906 907 908
        /* Refill a block */
        if( AStreamRefillBlock( s ) )
        {
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
909
            msg_Err( s, "cannot pre fill buffer" );
910 911 912 913 914 915 916 917
            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
918
    {
919
        /* Read enough data */
920
        while( p_sys->block.i_start + p_sys->block.i_size < i_pos )
921
        {
922 923 924 925 926 927 928
            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 )
929
            {
930 931
                p_sys->i_pos += p_sys->block.p_current->i_buffer;
                p_sys->block.p_current = p_sys->block.p_current->p_next;
932 933 934
            }
        }

935 936
        p_sys->block.i_offset = i_pos - p_sys->i_pos;
        p_sys->i_pos = i_pos;
937

938 939
        /* TODO read data */
        return VLC_SUCCESS;
940 941
    }

942
    return VLC_EGENERIC;
943 944
}

945
static int AStreamRefillBlock( stream_t *s )
946
{
947 948 949 950 951 952 953 954 955
    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;
956

957 958 959
        p_sys->block.i_start += b->i_buffer;
        p_sys->block.i_size  -= b->i_buffer;
        p_sys->block.p_first  = b->p_next;
960

961 962 963 964 965
        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 */
966
    {
967 968
        /* Enough data, don't read more */
        return VLC_SUCCESS;
969 970
    }

971 972 973
    /* Now read a new block */
    i_start = mdate();
    for( ;; )
974
    {
975 976 977
        vlc_bool_t b_eof;

        if( s->b_die ) return VLC_EGENERIC;
978 979 980


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

983
        if( b_eof ) return VLC_EGENERIC;
984 985

        msleep( STREAM_DATA_WAIT );
986
    }
987

988 989 990
    while( b )
    {
        i_stop = mdate();
991

992 993 994 995
        /* 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;
996

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

1001 1002 1003 1004 1005 1006 1007 1008
        /* 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();
    }
1009
    return VLC_SUCCESS;
1010 1011 1012
}


1013 1014 1015 1016 1017 1018
/****************************************************************************
 * Method 2:
 ****************************************************************************/
static int AStreamRefillStream( stream_t *s );

static int AStreamReadStream( stream_t *s, void *p_read, int i_read )
1019
{
1020 1021
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1022

1023
    uint8_t *p_data = (uint8_t *)p_read;
1024
    int      i_data = 0;
1025

1026
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1027

1028
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
1029
    {
1030
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
1031 1032 1033 1034 1035 1036 1037
        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;
    }
1038

1039
#ifdef STREAM_DEBUG
1040 1041 1042 1043 1044
    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
1045 1046

    while( i_data < i_read )
1047
    {
1048 1049 1050 1051 1052
        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 );
1053 1054
        int i_copy = __MIN( i_current, i_read - i_data );

1055 1056
        if( i_copy <= 0 ) break; /* EOF */

1057
        /* Copy data */
1058
        /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */
1059
        if( p_data )
1060
        {
1061 1062
            memcpy( p_data, &tk->p_buffer[i_off], i_copy );
            p_data += i_copy;
1063
        }
1064 1065 1066 1067
        i_data += i_copy;
        p_sys->stream.i_offset += i_copy;

        /* Update pos now */
1068 1069
        p_sys->i_pos += i_copy;

1070 1071 1072 1073
        /* */
        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 )
1074
        {
1075 1076
            if( AStreamRefillStream( s ) )
            {
1077 1078
                /* EOF */
                if( tk->i_start >= tk->i_end ) break;
1079
            }
1080 1081 1082
        }
    }

1083
    return i_data;
1084
}
1085

1086
static int AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, int i_read )
1087
{
1088 1089 1090
    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;
1091

1092
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1093

1094
#ifdef STREAM_DEBUG
1095 1096 1097 1098 1099
    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
1100 1101 1102 1103 1104 1105

    /* 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 )
1106
    {
1107 1108 1109
        if( p_sys->stream.i_used <= 1 )
        {
            /* Be sure we will read something */
1110 1111
            p_sys->stream.i_used += i_read -
                (tk->i_end - tk->i_start - p_sys->stream.i_offset);
1112
        }
1113
        if( AStreamRefillStream( s ) ) break;
1114 1115
    }

1116 1117
    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;
1118

1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
    /* 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 )
    {
1129 1130 1131 1132 1133 1134
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
1135 1136 1137
        p_sys->i_peek = i_read;
    }

1138 1139 1140 1141
    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) );
1142 1143 1144

    *pp_peek = p_sys->p_peek;
    return i_read;
1145 1146
}

1147
static int AStreamSeekStream( stream_t *s, int64_t i_pos )
1148
{
1149 1150 1151 1152 1153 1154 1155 1156
    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;

1157
#ifdef STREAM_DEBUG
1158
    msg_Dbg( s, "AStreamSeekStream: to "I64Fd" pos="I64Fd
1159
             " tk=%d start="I64Fd" offset=%d end="I64Fd,
1160 1161 1162 1163 1164
             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
1165 1166 1167 1168 1169


    /* 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 )
1170
    {
1171 1172 1173 1174
        stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
#ifdef STREAM_DEBUG
        msg_Dbg( s, "AStreamSeekStream: current track" );
#endif
1175
        p_sys->i_pos = i_pos;
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
        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 ;
1189
                AStreamRefillStream( s );
1190 1191
            }
        }
1192 1193
        return VLC_SUCCESS;
    }
1194

1195 1196 1197 1198
    access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
    if( !b_aseek )
    {
        /* We can't do nothing */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
1199
        msg_Dbg( s, "AStreamSeekStream: can't seek" );
1200 1201
        return VLC_EGENERIC;
    }
1202

1203 1204
    /* Date the current track */
    p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1205

1206 1207 1208 1209
    /* Try to reuse already read data */
    for( i = 0; i < STREAM_CACHE_TRACK; i++ )
    {
        stream_track_t *tk = &p_sys->stream.tk[i];