stream.c 43.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * stream.c
 *****************************************************************************
4
 * Copyright (C) 1999-2004 VideoLAN
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 164
    /* Preparse mode ? */
    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_AccessNew: 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

    psz_dup = strdup( psz_url );
    MRLSplit( p_parent, psz_dup, &psz_access, &psz_demux, &psz_path );
199 200 201
    
    /* Now try a real access */
    p_access = access2_New( p_parent, psz_access, NULL,
202
                            psz_path, VLC_FALSE );
203 204 205 206

    if( p_access == NULL )
    {
        msg_Err( p_parent, "no suitable access module for `%s'", psz_url );
207
        free( psz_dup );
208 209 210 211 212 213
        return NULL;
    }
    p_res = stream_AccessNew( p_access, VLC_TRUE );
    if( p_res )
    {
        p_res->pf_destroy = UStreamDestroy;
214
        free( psz_dup );
215 216 217 218 219 220
        return p_res;
    }
    else
    {
        access2_Delete( p_access );
    }
221 222
    free( psz_dup );
    return NULL;
223 224
}

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

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

    /* 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;
240
    s->pf_destroy = AStreamDestroy;
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

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

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

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

262 263
    p_sys->b_quick = b_quick;

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

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

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

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

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

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

    vlc_object_detach( s );

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

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

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

428 429 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
/****************************************************************************
 * 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 );
    }
}
472

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

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

    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;
        }
    }
490 491
}

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

500 501 502
    vlc_bool_t *p_bool;
    int64_t    *pi_64, i_64;
    int        i_int;
503 504 505 506

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

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

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

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

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

541
        case STREAM_GET_MTU:
542
            return VLC_EGENERIC;
543

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

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

563 564


565
/****************************************************************************
566
 * Method 1:
567
 ****************************************************************************/
568
static void AStreamPrebufferBlock( stream_t *s )
569
{
570
    stream_sys_t *p_sys = s->p_sys;
571

572 573
    int64_t i_first = 0;
    int64_t i_start;
574

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

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

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

602
        /* Fetch a block */
603
        if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
604
        {
605
            if( b_eof ) break;
606 607 608

            msleep( STREAM_DATA_WAIT );
            continue;
609
        }
610 611

        if( i_first == 0 )
612
        {
613
            i_first = mdate();
614 615
            msg_Dbg( s, "received first data for our buffer");
        }
616 617 618 619 620 621 622

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

        p_sys->stat.i_read_count++;
623 624
    }

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
    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;

641
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
642
    {
643
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
644 645 646 647 648 649 650
        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;
    }
651

652
    while( i_data < i_read )
653
    {
654 655
        int i_current =
            p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
656
        int i_copy = __MIN( i_current, i_read - i_data);
Gildas Bazin's avatar
 
Gildas Bazin committed
657

658 659
        /* Copy data */
        if( p_data )
660
        {
661 662 663
            memcpy( p_data,
                    &p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
                    i_copy );
664 665 666
            p_data += i_copy;
        }
        i_data += i_copy;
667

668 669 670 671 672 673 674 675 676 677 678
        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
679 680 681
            {
                break;
            }
682 683 684
        }
    }

685 686
    p_sys->i_pos += i_data;
    return i_data;
687
}
Gildas Bazin's avatar
Gildas Bazin committed
688

689
static int AStreamPeekBlock( stream_t *s, uint8_t **pp_peek, int i_read )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
690
{
691 692 693 694 695 696
    stream_sys_t *p_sys = s->p_sys;
    uint8_t *p_data;
    int      i_data = 0;
    block_t *b;
    int      i_offset;

697
    if( p_sys->block.p_current == NULL ) return 0; /* EOF */
698 699 700 701 702 703 704 705 706 707 708

    /* 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 )
    {
709 710 711 712 713 714
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
715 716 717 718
        p_sys->i_peek = i_read;
    }

    /* Fill enough data */
719 720
    while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
           < i_read )
721 722 723
    {
        block_t **pp_last = p_sys->block.pp_last;

724
        if( AStreamRefillBlock( s ) ) break;
725 726

        /* Our buffer are probably filled enough, don't try anymore */
727
        if( pp_last == p_sys->block.pp_last ) break;
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
    }

    /* 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
754
}
755

756
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
757
{
758 759 760 761
    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;
762

763 764 765 766 767
    /* 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;
768

769 770 771 772 773
        while( i_current + b->i_buffer < i_offset )
        {
            i_current += b->i_buffer;
            b = b->p_next;
        }
774

775 776
        p_sys->block.p_current = b;
        p_sys->block.i_offset = i_offset - i_current;
777

778
        p_sys->i_pos = i_pos;
779

780 781
        return VLC_SUCCESS;
    }
782

783 784
    /* We may need to seek or to read data */
    if( i_offset < 0 )
785
    {
786 787 788 789 790 791 792 793 794 795
        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;
796
    }
797 798 799
    else
    {
        vlc_bool_t b_aseek, b_aseekfast;
800

801 802 803 804 805 806
        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;
807 808
            msg_Warn( s, I64Fd" bytes need to be skipped "
                      "(access non seekable)",
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
                      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;

826
            msg_Dbg( s, "b_seek=%d th*avg=%d skip="I64Fd,
827 828
                     b_seek, i_th*i_avg, i_skip );
        }
829 830
    }

831 832 833 834 835
    if( b_seek )
    {
        int64_t i_start, i_end;
        /* Do the access seek */
        i_start = mdate();
836
        if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
837
        i_end = mdate();
838

839 840 841 842 843 844 845 846 847 848
        /* 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;
849

850 851 852 853 854 855 856 857 858 859 860 861
        /* 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
862
    {
863 864
        /* Read enought data */
        while( p_sys->block.i_start + p_sys->block.i_size < i_pos )
865
        {
866 867 868 869 870 871 872
            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 )
873
            {
874 875
                p_sys->i_pos += p_sys->block.p_current->i_buffer;
                p_sys->block.p_current = p_sys->block.p_current->p_next;
876 877 878
            }
        }

879 880
        p_sys->block.i_offset = i_pos - p_sys->i_pos;
        p_sys->i_pos = i_pos;
881

882 883
        /* TODO read data */
        return VLC_SUCCESS;
884 885
    }

886
    return VLC_EGENERIC;
887 888
}

889
static int AStreamRefillBlock( stream_t *s )
890
{
891 892 893 894 895 896 897 898 899
    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;
900

901 902 903
        p_sys->block.i_start += b->i_buffer;
        p_sys->block.i_size  -= b->i_buffer;
        p_sys->block.p_first  = b->p_next;
904

905 906 907 908 909
        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 */
910
    {
911 912
        /* Enough data, don't read more */
        return VLC_SUCCESS;
913 914
    }

915 916 917
    /* Now read a new block */
    i_start = mdate();
    for( ;; )
918
    {
919 920 921
        vlc_bool_t b_eof;

        if( s->b_die ) return VLC_EGENERIC;
922 923 924


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

927
        if( b_eof ) return VLC_EGENERIC;
928 929

        msleep( STREAM_DATA_WAIT );
930
    }
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
    i_stop = mdate();

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

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

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

    return VLC_SUCCESS;
948 949 950
}


951 952 953 954 955 956
/****************************************************************************
 * Method 2:
 ****************************************************************************/
static int AStreamRefillStream( stream_t *s );

static int AStreamReadStream( stream_t *s, void *p_read, int i_read )
957
{
958 959
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
960

961
    uint8_t *p_data = (uint8_t *)p_read;
962
    int      i_data = 0;
963

964
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
965

966
    if( p_read == NULL )
Damien Fouilleul's avatar
 
Damien Fouilleul committed
967
    {
968
        /* seek within this stream if possible, else use plain old read and discard */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
969 970 971 972 973 974 975
        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;
    }
976

977 978 979 980 981 982
#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
983 984

    while( i_data < i_read )
985
    {
986 987 988 989 990
        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 );
991 992
        int i_copy = __MIN( i_current, i_read - i_data );

993 994
        if( i_copy <= 0 ) break; /* EOF */

995
        /* Copy data */
996
        /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */
997
        if( p_data )
998
        {
999 1000
            memcpy( p_data, &tk->p_buffer[i_off], i_copy );
            p_data += i_copy;
1001
        }
1002 1003 1004 1005
        i_data += i_copy;
        p_sys->stream.i_offset += i_copy;

        /* Update pos now */
1006 1007
        p_sys->i_pos += i_copy;

1008 1009 1010 1011
        /* */
        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 )
1012
        {
1013 1014
            if( AStreamRefillStream( s ) )
            {
1015 1016
                /* EOF */
                if( tk->i_start >= tk->i_end ) break;
1017
            }
1018 1019 1020
        }
    }

1021
    return i_data;
1022
}
1023 1024

static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read )
1025
{
1026 1027 1028
    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;
1029

1030
    if( tk->i_start >= tk->i_end ) return 0; /* EOF */
1031

1032 1033 1034 1035 1036 1037
#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
1038 1039 1040 1041 1042 1043

    /* 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 )
1044
    {
1045 1046 1047
        if( p_sys->stream.i_used <= 1 )
        {
            /* Be sure we will read something */
1048 1049
            p_sys->stream.i_used += i_read -
                (tk->i_end - tk->i_start - p_sys->stream.i_offset);
1050
        }
1051
        if( AStreamRefillStream( s ) ) break;
1052 1053
    }

1054 1055
    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;
1056

1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
    /* 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 )
    {
1067 1068 1069 1070 1071 1072
        p_sys->p_peek = realloc( p_sys->p_peek, i_read );
        if( !p_sys->p_peek )
        {
            p_sys->i_peek = 0;
            return 0;
        }
1073 1074 1075
        p_sys->i_peek = i_read;
    }

1076 1077 1078 1079
    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) );
1080 1081 1082

    *pp_peek = p_sys->p_peek;
    return i_read;
1083 1084
}

1085
static int AStreamSeekStream( stream_t *s, int64_t i_pos )
1086
{
1087 1088 1089 1090 1091 1092 1093 1094
    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;

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


    /* 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 )
1108
    {
1109 1110 1111 1112 1113
        //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;
    }
1114

1115 1116 1117 1118
    access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
    if( !b_aseek )
    {
        /* We can't do nothing */
Damien Fouilleul's avatar
 
Damien Fouilleul committed
1119
        msg_Dbg( s, "AStreamSeekStream: can't seek" );
1120 1121
        return VLC_EGENERIC;
    }
1122

1123 1124
    /* Date the current track */
    p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
1125

1126 1127 1128 1129
    /* Try to reuse already read data */
    for( i = 0; i < STREAM_CACHE_TRACK; i++ )
    {
        stream_track_t *tk = &p_sys->stream.tk[i];
1130

1131 1132
        if( i_pos >= tk->i_start && i_pos <= tk->i_end )
        {
1133 1134 1135 1136
#if 0
            msg_Dbg( s, "AStreamSeekStream: reusing %d start="I64Fd
                     " end="I64Fd, i, tk->i_start, tk->i_end );
#endif
1137
            /* Seek at the end of the buffer */
1138
            if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC;
1139 1140 1141 1142 1143 1144 1145 1146 1147

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

1148
            if( AStreamRefillStream( s ) && i_pos == tk->i_end )
1149
                return VLC_EGENERIC;
1150 1151

            return VLC_SUCCESS;
1152 1153
        }
    }
1154

1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
    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" );
        }
1176
    }
1177 1178 1179
#endif

    /* Nothing good, seek and choose oldest segment */
1180
    if( ASeek( s, i_pos ) ) return VLC_EGENERIC;
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
    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;
1204 1205
}

1206
static int AStreamRefillStream( stream_t *s )
1207
{
1208 1209
    stream_sys_t *p_sys = s->p_sys;
    stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
1210 1211

    /* We read but won't increase i_start after initial start + offset */
1212 1213 1214
    int i_toread =
        __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE -
               (tk->i_end - tk->i_start - p_sys->stream.i_offset) );
1215
    vlc_bool_t b_read = VLC_FALSE;
1216
    int64_t i_start, i_stop;
1217

1218 1219 1220
    if( i_toread <= 0 ) return VLC_EGENERIC; /* EOF */

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

1222 1223
    i_start = mdate();
    while( i_toread > 0 )
1224
    {
1225 1226 1227 1228 1229 1230 1231
        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 );
1232
        i_read = AReadStream( s, &tk->p_buffer[i_off], i_read );
1233 1234

        /* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */
1235 1236 1237 1238 1239 1240 1241
        if( i_read <  0 )
        {
            msleep( STREAM_DATA_WAIT );
            continue;
        }
        else if( i_read == 0 )
        {
1242
            if( !b_read ) return VLC_EGENERIC;
1243
            return VLC_SUCCESS;
1244
        }
1245
        b_read = VLC_TRUE;
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263

        /* Update end */
        tk->i_end += i_read;

        /* Windows of STREAM_CACHE_TRACK_SIZE */
        if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE )
        {
            int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;

            tk->i_start += i_invalid;
            p_sys->stream.i_offset -= i_invalid;
        }

        i_toread -= i_read;
        p_sys->stream.i_used -= i_read;

        p_sys->stat.i_bytes += i_read;
        p_sys->stat.i_read_count++;
1264
    }
1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
    i_stop = mdate();

    p_sys->stat.i_read_time += i_stop - i_start;

    return VLC_SUCCESS;
}

static void AStreamPrebufferStream( stream_t *s )
{
    stream_sys_t *p_sys = s->p_sys;
    access_t     *p_access = p_sys->p_access;
1276

1277 1278
    int64_t i_first = 0;
    int64_t i_start;
1279
    int64_t i_prebuffer = p_sys->b_quick ? STREAM_CACHE_TRACK_SIZE /100 :
1280 1281
        ( (p_access->info.i_title > 1 || p_access->info.i_seekpoint > 1) ?
          STREAM_CACHE_PREBUFFER_SIZE : STREAM_CACHE_TRACK_SIZE / 3 );
1282

1283 1284 1285
    msg_Dbg( s, "pre buffering" );
    i_start = mdate();
    for( ;; )
1286
    {
1287 1288 1289 1290 1291
        stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];

        int64_t i_date = mdate();
        int i_read;

Laurent Aimar's avatar