stream.c 47.6 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 244
    /* UTF16 and UTF32 text file conversion */
    s->i_char_width = 1;
    s->b_little_endian = VLC_FALSE;

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

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

263 264
    p_sys->b_quick = b_quick;

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

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

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

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

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

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

    vlc_object_detach( s );

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

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

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

430 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
/****************************************************************************
 * 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 );
    }
}
474

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

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

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

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

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

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

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

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

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

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

543
        case STREAM_GET_MTU:
544
            return VLC_EGENERIC;
545

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

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

565 566


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

575 576
    int64_t i_first = 0;
    int64_t i_start;
577

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

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

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

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

            msleep( STREAM_DATA_WAIT );
            continue;
612
        }
613

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

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

        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");
        }

639 640
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

794
        p_sys->i_pos = i_pos;
795

796 797
        return VLC_SUCCESS;
    }
798

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

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

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

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

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

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

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

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

902
    return VLC_EGENERIC;
903 904
}

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

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

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

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

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


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

943
        if( b_eof ) return VLC_EGENERIC;
944 945

        msleep( STREAM_DATA_WAIT );
946
    }
947

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

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

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

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


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

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

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

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

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

999 1000 1001 1002 1003 1004
#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
1005 1006

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

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

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

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

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

1043
    return i_data;
1044
}
1045 1046

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

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

1054 1055 1056 1057 1058 1059
#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
1060 1061 1062 1063 1064 1065

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

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

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

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

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

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

1117 1118 1119 1120 1121 1122 1123 1124
#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
1125 1126 1127 1128 1129


    /* 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 )
1130
    {
1131 1132 1133 1134 1135
        //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;
    }
1136

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

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

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

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

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

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

            return VLC_SUCCESS;
1174 1175
        }
    }
1176

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

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

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

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

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

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

1244 1245
    i_start = mdate();
    while( i_toread > 0 )
1246
    {
1247 1248 1249 1250 1251 1252 1253
        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 );
1254
        i_read = AReadStream( s, &tk->p_buffer[i_off], i_read );
1255 1256

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