stream.c 47.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 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 32 33 34
/* TODO:
 *  - tune the 2 methods
 *  - compute cost for seek
 *  - improve stream mode seeking with closest segments
 *  - ...
35
 */
36 37 38 39 40 41 42 43

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

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

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

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

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

typedef struct
83
{
84
    int64_t i_date;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
85

86 87 88 89
    int64_t i_start;
    int64_t i_end;

    uint8_t *p_buffer;
90

91 92
} stream_track_t;

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

} access_entry_t;

100 101 102 103 104 105 106 107 108 109
struct stream_sys_t
{
    access_t    *p_access;

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

    int64_t     i_pos;      /* Current reading offset */

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

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

119 120 121 122
    } block;

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

        /* Global buffer */
        uint8_t *p_buffer;

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

135 136 137 138 139 140 141 142
    } stream;

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

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

146 147 148 149
        /* Stat about reading data */
        int64_t i_read_count;
        int64_t i_bytes;
        int64_t i_read_time;
150

151 152 153
        /* Stat about seek */
        int     i_seek_count;
        int64_t i_seek_time;
154

155
    } stat;
156

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

163
    /* Preparse mode ? */
164
    vlc_bool_t      b_quick;
165
};
166

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

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

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

187

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

197 198
    if( !psz_url ) return 0;

199 200
    psz_dup = strdup( psz_url );
    MRLSplit( p_parent, psz_dup, &psz_access, &psz_demux, &psz_path );
201

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

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

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

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

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

228
    if( !s ) return NULL;
229 230 231 232 233 234 235 236

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

    s->pf_block  = NULL;
    s->pf_read   = NULL;    /* Set up later */
    s->pf_peek   = NULL;
    s->pf_control= AStreamControl;
237
    s->pf_destroy = AStreamDestroy;
238 239 240

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

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

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

259 260 261 262 263
    p_sys->i_list = 0;
    p_sys->list = 0;
    p_sys->i_list_index = 0;
    p_sys->p_list_access = 0;

264 265
    p_sys->b_quick = b_quick;

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
    /* 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 );

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

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

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

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;
393 394
}

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

    vlc_object_detach( s );

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

419
    free( s->p_sys );
420 421 422
    vlc_object_destroy( s );
}

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

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
/****************************************************************************
 * 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 );
    }
}
475

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

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

    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;
        }
    }
493 494
}

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

503 504 505
    vlc_bool_t *p_bool;
    int64_t    *pi_64, i_64;
    int        i_int;
506 507 508 509

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

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

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

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

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

544
        case STREAM_GET_MTU:
545
            return VLC_EGENERIC;
546

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

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

566 567


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

576 577
    int64_t i_first = 0;
    int64_t i_start;
578

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

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

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

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

            msleep( STREAM_DATA_WAIT );
            continue;
613
        }
614

615 616 617 618 619 620
        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;
621

622 623 624
            p_sys->stat.i_read_count++;
            b = b->p_next;
        }
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639

        if( p_access->info.b_prebuffered ) 
        {
            /* 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");
        }

640 641
    }

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

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

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

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

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

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

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

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

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

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

741
        if( AStreamRefillBlock( s ) ) break;
742 743

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

    /* 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
771
}
772

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

780 781 782 783 784
    /* 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;
785

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

792 793
        p_sys->block.p_current = b;
        p_sys->block.i_offset = i_offset - i_current;
794

795
        p_sys->i_pos = i_pos;
796

797 798
        return VLC_SUCCESS;
    }
799

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

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

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

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

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

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

896 897
        p_sys->block.i_offset = i_pos - p_sys->i_pos;
        p_sys->i_pos = i_pos;
898

899 900
        /* TODO read data */
        return VLC_SUCCESS;
901 902
    }

903
    return VLC_EGENERIC;
904 905
}

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

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

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

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

        if( s->b_die ) return VLC_EGENERIC;
939 940 941


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

944
        if( b_eof ) return VLC_EGENERIC;
945 946

        msleep( STREAM_DATA_WAIT );
947
    }
948

949 950 951
    while( b )
    {
        i_stop = mdate();
952

953 954 955 956
        /* 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;
957

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

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


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

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

984
    uint8_t *p_data = (uint8_t *)p_read;
985
    int      i_data = 0;
986

987
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
988

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

1000 1001 1002 1003 1004 1005
#if 0
    msg_Dbg( s, "AStreamReadStream: %d pos="I64Fd" tk=%d start="I64Fd
             " offset=%d end="I64Fd,
             i_read, p_sys->i_pos, p_sys->stream.i_tk,
             tk->i_start, p_sys->stream.i_offset, tk->i_end );
#endif
1006 1007

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

1016 1017
        if( i_copy <= 0 ) break; /* EOF */

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

        /* Update pos now */
1029 1030
        p_sys->i_pos += i_copy;

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

1044
    return i_data;
1045
}
1046 1047

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

1053
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1054

1055 1056 1057 1058 1059 1060
#if 0
    msg_Dbg( s, "AStreamPeekStream: %d pos="I64Fd" tk=%d "
             "start="I64Fd" offset=%d end="I64Fd,
             i_read, p_sys->i_pos, p_sys->stream.i_tk,
             tk->i_start, p_sys->stream.i_offset, tk->i_end );
#endif
1061 1062 1063 1064 1065 1066

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

1077 1078
    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;
1079

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

1099 1100 1101 1102
    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) );
1103 1104 1105

    *pp_peek = p_sys->p_peek;
    return i_read;
1106 1107
}

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

1118 1119 1120 1121 1122 1123 1124 1125
#if 0
    msg_Dbg( s, "AStreamSeekStream: to "I64Fd" pos="I64Fd
             "tk=%d start="I64Fd" offset=%d end="I64Fd,
             i_pos, p_sys->i_pos, p_sys->stream.i_tk,
             p_sys->stream.tk[p_sys->stream.i_tk].i_start,
             p_sys->stream.i_offset,
             p_sys->stream.tk[p_sys->stream.i_tk].i_end );
#endif
1126 1127 1128 1129 1130


    /* 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 )
1131
    {
1132 1133 1134 1135 1136
        //msg_Dbg( s, "AStreamSeekStream: current track" );
        p_sys->i_pos = i_pos;
        p_sys->stream.i_offset = i_pos - p_sys->stream.tk[p_sys->stream.i_tk].i_start;
        return VLC_SUCCESS;
    }
1137

1138 1139 1140 1141
    access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
    if( !b_aseek )
    {
        /* We can't do nothing */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
1142
        msg_Dbg( s, "AStreamSeekStream: can't seek" );
1143 1144
        return VLC_EGENERIC;
    }
1145

1146 1147
    /* Date the current track */
    p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1148

1149 1150 1151 1152
    /* Try to reuse already read data */
    for( i = 0; i < STREAM_CACHE_TRACK; i++ )
    {
        stream_track_t *tk = &p_sys->stream.tk[i];
1153

1154 1155
        if( i_pos >= tk->i_start && i_pos <= tk->i_end )
        {
1156 1157 1158 1159
#if 0
            msg_Dbg( s, "AStreamSeekStream: reusing %d start="I64Fd
                     " end="I64Fd, i, tk->i_start, tk->i_end );
#endif
1160
            /* Seek at the end of the buffer */
1161
            if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
1162 1163 1164 1165 1166 1167 1168 1169 1170

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

1171
            if( AStreamRefillStream( s ) && i_pos == tk->i_end )
1172
                return VLC_EGENERIC;
1173 1174

            return VLC_SUCCESS;
1175 1176
        }
    }
1177

1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
    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" );
        }
1199
    }
1200 1201 1202
#endif

    /* Nothing good, seek and choose oldest segment */
1203
    if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
    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;
1227 1228
}

1229
static int AStreamRefillStream( stream_t *s )
1230
{
1231 1232
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1233 1234

    /* We read but won't increase i_start after initial start + offset */
1235 1236 1237
    int i_toread =
        __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE -
               (tk->i_end - tk->i_start - p_sys->stream.i_offset) );
1238
    vlc_bool_t b_read = VLC_FALSE;
1239
    int64_t i_start, i_stop;
1240

1241 1242 1243
    if( i_toread <= 0 ) return VLC_EGENERIC; /* EOF */

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

1245 1246
    i_start = mdate();
    while( i_toread > 0 )
1247
    {
1248 1249 1250 1251 1252 1253 1254
        int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE;
        int i_read;

        if( s->b_die )
            return VLC_EGENERIC;

        i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off );
1255
        i_read = AReadStream( s, &tk->p_buffer[i_off], i_read );
1256 1257

        /* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */
1258 1259 1260 1261 1262 1263 1264
        if( i_read <  0 )
        {
            msleep( STREAM_DATA_WAIT );
            continue;
        }
        else if( i_read == 0 )
        {
1265
            if( !b_read ) return VLC_EGENERIC;