Changeset 5df2e973d079c0a43572cb9b248d226911b4cfbc

Show
Ignore:
Timestamp:
06/05/08 19:18:50 (3 months ago)
Author:
Pierre d'Herbemont <pdherbemont@videolan.org>
git-committer:
Pierre d'Herbemont <pdherbemont@videolan.org> 1212686330 +0200
git-parent:

[446bfcd5f40d640097b4e765ae234c47b9831d12]

git-author:
Pierre d'Herbemont <pdherbemont@videolan.org> 1211542116 +0200
Message:

stream: Add a new method for buffering access: A*Immediate method.

It is much more efficient regarding latency as it doesn't bufferize more than needed, and let the module access take care of that eventually.

Enable with --use-stream-immediate. We may want to default it.

Note: --use-stream-immediate will be only effective on access that don't provide pf_block() for now. This is because I didn't benchmark against the Block method.

Here the gain that I did measure is about 200ms (less latency) when using the http access on a loopback.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • src/input/stream.c

    rd666030 r5df2e97  
    2828#include <vlc_common.h> 
    2929 
     30#include <assert.h> 
     31 
    3032#include "input_internal.h" 
    3133 
     
    3335 
    3436/* TODO: 
    35  *  - tune the 2 methods 
     37 *  - tune the 2 methods (block/stream) 
    3638 *  - compute cost for seek 
    3739 *  - improve stream mode seeking with closest segments 
    3840 *  - ... 
     41 *  - Maybe remove (block/stream) in favour of immediate 
    3942 */ 
    4043 
     
    4447 *  - using pf_read 
    4548 *      More complex scheme using mutliple track to avoid seeking 
     49 *  - using directly the access (only indirection for peeking). 
     50 *      This method is known to introduce much less latency. 
     51 *      It should probably defaulted (instead of the stream method (2)). 
    4652 */ 
    4753 
     
    102108} access_entry_t; 
    103109 
     110typedef enum stream_read_method_t 
     111{ 
     112    Immediate, 
     113    Block, 
     114    Stream 
     115} stream_read_method_t; 
     116 
    104117struct stream_sys_t 
    105118{ 
    106119    access_t    *p_access; 
    107120 
    108     bool  b_block;    /* Block method (1) or stream */ 
     121    stream_read_method_t   method;    /* method to use */ 
    109122 
    110123    int64_t     i_pos;      /* Current reading offset */ 
     
    138151 
    139152    } stream; 
     153 
     154    /* Method 3: for pf_read */ 
     155    struct 
     156    { 
     157        int64_t i_end; 
     158        uint8_t *p_buffer; 
     159    } immediate; 
    140160 
    141161    /* Peek temporary buffer */ 
     
    183203static int  AReadStream( stream_t *s, void *p_read, int i_read ); 
    184204 
     205/* Method 3 */ 
     206static int  AStreamReadImmediate( stream_t *s, void *p_read, int i_read ); 
     207static int  AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, int i_read ); 
     208static int  AStreamSeekImmediate( stream_t *s, int64_t i_pos ); 
     209 
    185210/* Common */ 
    186211static int AStreamControl( stream_t *s, int i_query, va_list ); 
     
    189214static int  ASeek( stream_t *s, int64_t i_pos ); 
    190215 
     216/**************************************************************************** 
     217 * Method 3 helpers: 
     218 ****************************************************************************/ 
     219 
     220static inline int64_t stream_buffered_size( stream_t *s ) 
     221{ 
     222    return s->p_sys->immediate.i_end; 
     223} 
     224 
     225static inline void stream_buffer_empty( stream_t *s, int length ) 
     226{ 
     227    length = __MAX( stream_buffered_size( s ), length ); 
     228    if( length ) 
     229    { 
     230        memmove( s->p_sys->immediate.p_buffer, 
     231                 s->p_sys->immediate.p_buffer + length, 
     232                 stream_buffered_size( s ) - length ); 
     233    } 
     234    s->p_sys->immediate.i_end -= length; 
     235} 
     236 
     237static inline void stream_buffer_fill( stream_t *s, int length ) 
     238{ 
     239    s->p_sys->immediate.i_end += length; 
     240} 
     241 
     242static inline uint8_t * stream_buffer( stream_t *s ) 
     243{ 
     244    return s->p_sys->immediate.p_buffer; 
     245} 
    191246 
    192247/**************************************************************************** 
     
    255310    /* Common field */ 
    256311    p_sys->p_access = p_access; 
    257     p_sys->b_block = p_access->pf_block ? true : false; 
     312    if( p_access->pf_block ) 
     313        p_sys->method = Block; 
     314    else if (var_CreateGetBool( s, "use-stream-immediate")) 
     315        p_sys->method = Immediate; 
     316    else 
     317        p_sys->method = Stream; 
     318 
    258319    p_sys->i_pos = p_access->info.i_pos; 
    259320 
     
    341402    p_sys->p_peek = NULL; 
    342403 
    343     if( p_sys->b_block ) 
    344     { 
     404    if( p_sys->method == Block ) 
     405    { 
     406        msg_Dbg( s, "Using AStream*Block" ); 
    345407        s->pf_read = AStreamReadBlock; 
    346408        s->pf_peek = AStreamPeekBlock; 
     
    363425        } 
    364426    } 
    365     else 
     427    else if (p_sys->method == Immediate) 
     428    { 
     429        msg_Dbg( s, "Using AStream*Immediate" ); 
     430 
     431        s->pf_read = AStreamReadImmediate; 
     432        s->pf_peek = AStreamPeekImmediate; 
     433 
     434        /* Allocate/Setup our tracks (useful to peek)*/ 
     435        p_sys->immediate.i_end = 0; 
     436        p_sys->immediate.p_buffer = malloc( STREAM_CACHE_SIZE ); 
     437 
     438        msg_Dbg( s, "p_buffer %p-%p", p_sys->immediate.p_buffer, 
     439                p_sys->immediate.p_buffer + STREAM_CACHE_SIZE ); 
     440 
     441        if( p_sys->immediate.p_buffer == NULL ) 
     442        { 
     443            msg_Err( s, "Out of memory when allocating stream cache (%d bytes)", 
     444                        STREAM_CACHE_SIZE ); 
     445            goto error; 
     446        } 
     447    } 
     448    else /* ( p_sys->method == Stream ) */ 
    366449    { 
    367450        int i; 
     451 
     452        msg_Dbg( s, "Using AStream*Stream" ); 
    368453 
    369454        s->pf_read = AStreamReadStream; 
     
    410495 
    411496error: 
    412     if( p_sys->b_block ) 
     497    if( p_sys->method == Block ) 
    413498    { 
    414499        /* Nothing yet */ 
     
    437522    vlc_object_detach( s ); 
    438523 
    439     if( p_sys->b_block ) block_ChainRelease( p_sys->block.p_first ); 
     524    if( p_sys->method == Block ) block_ChainRelease( p_sys->block.p_first ); 
    440525    else free( p_sys->stream.p_buffer ); 
    441526 
     
    474559    p_sys->i_pos = p_sys->p_access->info.i_pos; 
    475560 
    476     if( p_sys->b_block ) 
     561    if( p_sys->method == Block ) 
    477562    { 
    478563        block_ChainRelease( p_sys->block.p_first ); 
     
    489574        AStreamPrebufferBlock( s ); 
    490575    } 
    491     else 
     576    else if( p_sys->method == Immediate ) 
     577    { 
     578        stream_buffer_empty( s, stream_buffered_size( s ) ); 
     579    } 
     580    else /* ( p_sys->method == Stream ) */ 
    492581    { 
    493582        int i; 
     
    573662        case STREAM_SET_POSITION: 
    574663            i_64 = (int64_t)va_arg( args, int64_t ); 
    575             if( p_sys->b_block ) 
     664            if( p_sys->method == Block ) 
    576665                return AStreamSeekBlock( s, i_64 ); 
    577             else 
     666            else if( p_sys->method == Immediate ) 
     667                return AStreamSeekImmediate( s, i_64 ); 
     668            else /* ( p_sys->method == Stream ) */ 
    578669                return AStreamSeekStream( s, i_64 ); 
    579670 
     
    14211512} 
    14221513 
     1514/**************************************************************************** 
     1515 * Method 3: 
     1516 ****************************************************************************/ 
     1517 
     1518static int AStreamReadImmediate( stream_t *s, void *p_read, int i_read ) 
     1519{ 
     1520    stream_sys_t *p_sys = s->p_sys; 
     1521 
     1522#ifdef STREAM_DEBUG 
     1523    msg_Dbg( s, "AStreamReadImmediate p_read=%p i_read=%d", 
     1524             p_read, i_read ); 
     1525#endif 
     1526 
     1527    if( p_read == NULL ) 
     1528    { 
     1529        /* seek within this stream if possible, else use plain old read and discard */ 
     1530        stream_sys_t *p_sys = s->p_sys; 
     1531        access_t     *p_access = p_sys->p_access; 
     1532        bool   b_aseek; 
     1533        access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); 
     1534        if( b_aseek ) 
     1535            return AStreamSeekStream( s, p_sys->i_pos + i_read ) ? 0 : i_read; 
     1536    } 
     1537 
     1538    /* First, check if we already have some data in the buffer, 
     1539     * that we could copy directly */ 
     1540    int i_copy = __MIN( stream_buffered_size( s ), i_read ); 
     1541    if( i_copy ) 
     1542    { 
     1543#ifdef STREAM_DEBUG 
     1544        msg_Dbg( s, "AStreamReadImmediate: copy %d from %p", i_copy, stream_buffer( s ) ); 
     1545#endif 
     1546 
     1547        assert( i_copy <= STREAM_CACHE_SIZE ); 
     1548 
     1549        if( p_read ) 
     1550        { 
     1551            memcpy( p_read, stream_buffer( s ), i_copy ); 
     1552            p_read = (uint8_t *)p_read + i_copy; 
     1553        } 
     1554    } 
     1555 
     1556    /* Now that we've read our buffer we don't need its i_copy bytes */ 
     1557    stream_buffer_empty( s, i_copy ); 
     1558 
     1559    /* Now check if we have still to really read some data */ 
     1560    int i_to_read = i_read - i_copy; 
     1561    if( i_to_read ) 
     1562    { 
     1563        i_to_read = AReadStream( s, p_read, i_to_read ); 
     1564    } 
     1565 
     1566    p_sys->i_pos += i_to_read; 
     1567 
     1568    return i_to_read + i_copy; 
     1569} 
     1570 
     1571static int AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, int i_read ) 
     1572{ 
     1573#ifdef STREAM_DEBUG 
     1574    msg_Dbg( s, "AStreamPeekImmediate: %d  size=%"PRId64, 
     1575             i_read, size_buffered_size( s ) ); 
     1576#endif 
     1577 
     1578    /* Avoid problem, but that shouldn't happen */ 
     1579    if( i_read > STREAM_CACHE_SIZE / 2 ) 
     1580        i_read = STREAM_CACHE_SIZE / 2; 
     1581 
     1582    int i_to_read = i_read - stream_buffered_size( s ); 
     1583    if( i_to_read > 0 ) 
     1584    { 
     1585#ifdef STREAM_DEBUG 
     1586        msg_Dbg( s, "AStreamPeekImmediate: Reading %d", 
     1587             i_to_read ); 
     1588#endif 
     1589        i_to_read = AReadStream( s, stream_buffer( s ) + stream_buffered_size( s ), 
     1590                                 i_to_read ); 
     1591 
     1592        if( i_to_read > 0 ) 
     1593            stream_buffer_fill( s, i_to_read ); 
     1594    } 
     1595 
     1596    *pp_peek = stream_buffer( s ); 
     1597 
     1598    return __MIN(stream_buffered_size( s ), i_read); 
     1599} 
     1600 
     1601static int AStreamSeekImmediate( stream_t *s, int64_t i_pos ) 
     1602{ 
     1603    stream_sys_t *p_sys = s->p_sys; 
     1604    access_t     *p_access = p_sys->p_access; 
     1605    bool   b_aseek; 
     1606 
     1607#ifdef STREAM_DEBUG 
     1608    msg_Dbg( s, "AStreamSeekImmediate to %"PRId64" pos=%"PRId64 
     1609             i_pos, p_sys->i_pos ); 
     1610#endif 
     1611 
     1612    access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); 
     1613    if( !b_aseek ) 
     1614    { 
     1615        /* We can't do nothing */ 
     1616        msg_Dbg( s, "AStreamSeekImmediate: can't seek" ); 
     1617        return VLC_EGENERIC; 
     1618    } 
     1619 
     1620    /* Just reset our buffer */ 
     1621    stream_buffer_empty( s, stream_buffered_size( s ) ); 
     1622 
     1623    if( ASeek( s, i_pos ) ) return VLC_EGENERIC; 
     1624 
     1625    return VLC_SUCCESS; 
     1626} 
    14231627 
    14241628/**************************************************************************** 
  • src/libvlc-module.c

    r04f2e3a r5df2e97  
    994994     "This option minimizes the number of threads needed to run VLC.") 
    995995 
     996#define USE_STREAM_IMMEDIATE N_("(Experimental) Use the StreamImmediate " \ 
     997    "method and minimize the caching done at the access level.") 
     998#define USE_STREAM_IMMEDIATE_LONGTEXT N_( \ 
     999     "This option is useful if you want to lower the latency when " \ 
     1000     "reading a stream") 
     1001 
    9961002#define PLUGIN_PATH_TEXT N_("Modules search path") 
    9971003#define PLUGIN_PATH_LONGTEXT N_( \ 
     
    18141820              MINIMIZE_THREADS_LONGTEXT, true ); 
    18151821        change_need_restart(); 
     1822 
     1823    add_bool( "use-stream-immediate", false, NULL, 
     1824               USE_STREAM_IMMEDIATE, USE_STREAM_IMMEDIATE_LONGTEXT, false ); 
    18161825 
    18171826#if !defined(__APPLE__) && !defined(SYS_BEOS) && defined(LIBVLC_USE_PTHREAD)