[vlc-devel] [PATCH] demux: ogg: seek to keyframes

Denis Charmet typx at dinauz.org
Thu Oct 10 10:58:43 CEST 2013


Hi,

Since j-b already stated the fact that the DemuxDebug macro is fugly,
and it's not used in release I shamelessly ignored it :)

Some comments inlined.

> @@ -442,8 +478,12 @@ static int Demux( demux_t * p_demux )
>              p_sys->i_pcr = p_stream->i_interpolated_pcr;
>      }
>  
> -    if( p_sys->i_pcr >= 0 && ! b_skipping )
> -        es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
> +    if( p_sys->i_pcr < 0 ) return -1;
> +
> +    es_out_Control( p_demux->out,
> +                    ( b_skipping ) ? ES_OUT_SET_NEXT_DISPLAY_TIME
> +                                   : ES_OUT_SET_PCR,
> +                    VLC_TS_0 + p_sys->i_pcr );

To me, you shouldn't do that. Call ES_OUT_SET_NEXT_DISPLAY_TIME in your
seek function and always call ES_OUT_SET_PCR. Let the core actually
handle the preroll.

>  
>      return 1;
>  }
> @@ -464,6 +504,34 @@ static void Ogg_ResetStreamHelper( demux_sys_t *p_sys )
>      ogg_sync_reset( &p_sys->oy );
>  }
>  
> +static logical_stream_t * Ogg_GetSelectedStream( demux_t *p_demux )
> +{
> +    demux_sys_t *p_sys = p_demux->p_sys;
> +    logical_stream_t *p_stream = NULL;
> +    for( int i=0; i<p_sys->i_streams; i++ )
> +    {
> +        logical_stream_t *p_candidate = p_sys->pp_stream[i];
> +
> +        bool b_selected = false;
> +        es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE,
> +                        p_candidate->p_es, &b_selected );
> +        if ( !b_selected ) continue;
> +
> +        if ( !p_stream && p_candidate->fmt.i_cat == AUDIO_ES )
> +        {
> +            p_stream = p_candidate;
> +            continue; /* Try to find video anyway */
> +        }
> +
> +        if ( p_candidate->fmt.i_cat == VIDEO_ES )
> +        {
> +            p_stream = p_candidate;
> +            break;
> +        }
> +    }
BEWARE! the core allow you to spawn several vout if you have several
video tracks. It's even its basic behaviour so what about that? Not to
mention that Opus also needs preroll.

> +    return p_stream;
> +}
> +
>  /*****************************************************************************
>   * Control:
>   *****************************************************************************/
> @@ -471,8 +539,9 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
>  {
>      demux_sys_t *p_sys  = p_demux->p_sys;
>      vlc_meta_t *p_meta;
> -    int64_t *pi64;
> -    bool *pb_bool;
> +    int64_t *pi64, i64;
> +    double *pf, f;
> +    bool *pb_bool, b;
>  
>      switch( i_query )
>      {
> @@ -511,6 +580,21 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
>              return VLC_SUCCESS;
>          }
>  
> +        case DEMUX_GET_POSITION:
> +            pf = (double*)va_arg( args, double * );
> +            if( p_sys->i_length > 0 )
> +            {
> +                *pf =  (double) p_sys->i_pcr /
> +                       (double) ( p_sys->i_length * (mtime_t)1000000 );
> +            }
> +            else if( stream_Size( p_demux->s ) > 0 )
> +            {
> +                i64 = stream_Tell( p_demux->s );
> +                *pf = (double) i64 / stream_Size( p_demux->s );
> +            }
> +            else *pf = 0.0;
or maybe return VLC_EGENERIC;
> +            return VLC_SUCCESS;
> +
>          case DEMUX_SET_POSITION:
>              /* forbid seeking if we haven't initialized all logical bitstreams yet;
>                 if we allowed, some headers would not get backed up and decoder init
> @@ -520,9 +604,29 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
>                  return VLC_EGENERIC;
>              }
>  
> +            logical_stream_t *p_stream = Ogg_GetSelectedStream( p_demux );
> +            if ( !p_stream )
> +            {
> +                msg_Err( p_demux, "No selected seekable stream found" );
> +                return VLC_EGENERIC;
> +            }
> +
> +            stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b );
> +
> +            f = (double)va_arg( args, double );
> +            if ( p_sys->i_length <= 0 || !b /* || ! ACCESS_CAN_FASTSEEK */ )
> +            {
> +                Ogg_ResetStreamHelper( p_sys );
> +                Oggseek_BlindSeektoPosition( p_demux, p_stream, f, b );
> +                return VLC_SUCCESS;
> +            }
> +            assert( p_sys->i_length > 0 );
> +            i64 = (mtime_t)(1000000.0 * p_sys->i_length * f );
>              Ogg_ResetStreamHelper( p_sys );
> -            return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate,
> -                                          1, i_query, args );
> +            Oggseek_SeektoAbsolutetime( p_demux, p_stream, i64 );
> +
> +            return VLC_SUCCESS;
> +
>          case DEMUX_GET_LENGTH:
>              if ( p_sys->i_length < 0 )
>                  return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate,
> @@ -577,13 +681,16 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
>                   * we'll need to bisect search from here
>                   * or use skeleton index if any (FIXME)
>                  */
> -                if ( p_sys->pp_stream[0]->fmt.i_codec == VLC_CODEC_OPUS )
> +                logical_stream_t *p_stream = Ogg_GetSelectedStream( p_demux );
> +                if ( !p_stream )
>                  {
> -                    /* Granule = Freq * T + pre-skip */
> -                    oggseek_find_frame ( p_demux, p_sys->pp_stream[0],
> -                        ( p_sys->pp_seekpoints[i_seekpoint]->i_time_offset * 0.048 + p_sys->pp_stream[0]->i_pre_skip ) );
> +                    msg_Err( p_demux, "No selected seekable stream found" );
> +                    return VLC_EGENERIC;
talking about opus preroll/preskip :)
>                  }
> -                else return VLC_EGENERIC;
> +
> +                Oggseek_SeektoAbsolutetime( p_demux, p_stream, p_sys->pp_seekpoints[i_seekpoint]->i_time_offset );
> +
> +                return VLC_SUCCESS;
>              }
>              else
>              {
> @@ -636,25 +743,16 @@ static void Ogg_UpdatePCR( logical_stream_t *p_stream,
>                             ogg_packet *p_oggpacket )
>  {
>      p_stream->i_end_trim = 0;
> +
>      /* Convert the granulepos into a pcr */
>      if( p_oggpacket->granulepos >= 0 )
>      {
>          if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
> -            p_stream->fmt.i_codec == VLC_CODEC_KATE )
> +            p_stream->fmt.i_codec == VLC_CODEC_KATE ||
> +            p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
>          {
> -            ogg_int64_t iframe = p_oggpacket->granulepos >>
> -              p_stream->i_granule_shift;
> -            ogg_int64_t pframe = p_oggpacket->granulepos -
> -              ( iframe << p_stream->i_granule_shift );
> -
> -            p_stream->i_pcr = ( iframe + pframe - p_stream->i_keyframe_offset )
> -              * INT64_C(1000000) / p_stream->f_rate;
> -        }
> -        else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
> -        {
> -            ogg_int64_t i_dts = p_oggpacket->granulepos >> 31;
> -            /* NB, OggDirac granulepos values are in units of 2*picturerate */
> -            p_stream->i_pcr = (i_dts/2) * INT64_C(1000000) / p_stream->f_rate;
> +            p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
> +                                                p_oggpacket->granulepos, true );
>          }
>          else
>          {
> @@ -1305,12 +1403,13 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
>                      Ogg_ReadKateHeader( p_stream, &oggpacket );
>                      msg_Dbg( p_demux, "found kate header" );
>                  }
> +                /* Check for OggDS */
>                  else if( oggpacket.bytes >= 142 &&
>                           !memcmp( &oggpacket.packet[1],
>                                     "Direct Show Samples embedded in Ogg", 35 ))
>                  {
>                      /* Old header type */
> -
> +                    p_stream->b_oggds = true;
>                      /* Check for video header (old format) */
>                      if( GetDWLE((oggpacket.packet+96)) == 0x05589f80 &&
>                          oggpacket.bytes >= 184 )
> @@ -1410,12 +1509,15 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
>                          p_ogg->i_streams--;
>                      }
>                  }
> +                /* Check for OggDS */
>                  else if( oggpacket.bytes >= 44+1 &&
>                           (*oggpacket.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER )
>                  {
>                      stream_header_t tmp;
>                      stream_header_t *st = &tmp;
>  
> +                    p_stream->b_oggds = true;
> +
>                      memcpy( st->streamtype, &oggpacket.packet[1+0], 8 );
>                      memcpy( st->subtype, &oggpacket.packet[1+8], 4 );
>                      st->size = GetDWLE( &oggpacket.packet[1+12] );
> diff --git a/modules/demux/ogg.h b/modules/demux/ogg.h
> index 606282c..d69e389 100644
> --- a/modules/demux/ogg.h
> +++ b/modules/demux/ogg.h
> @@ -26,6 +26,20 @@
>   * Definitions of structures and functions used by this plugin
>   *****************************************************************************/
>  
> +//#define OGG_DEMUX_DEBUG 1
> +#ifdef OGG_DEMUX_DEBUG
> +  #define DemuxDebug(code) code
> +#else
> +  #define DemuxDebug(code)
> +#endif
> +
> +/* Some defines from OggDS http://svn.xiph.org/trunk/oggds/ */
> +#define PACKET_TYPE_HEADER   0x01
> +#define PACKET_TYPE_BITS     0x07
> +#define PACKET_LEN_BITS01    0xc0
> +#define PACKET_LEN_BITS2     0x02
> +#define PACKET_IS_SYNCPOINT  0x08
> +
>  typedef struct oggseek_index_entry demux_index_entry_t;
>  
>  typedef struct logical_stream_s
> @@ -56,6 +70,7 @@ typedef struct logical_stream_s
>  
>      /* Misc */
>      bool b_reinit;
> +    bool b_oggds;
>      int i_granule_shift;
>  
>      /* Opus has a starting offset in the headers. */
> @@ -64,7 +79,7 @@ typedef struct logical_stream_s
>      int i_end_trim;
>  
>      /* offset of first keyframe for theora; can be 0 or 1 depending on version number */
> -    int64_t i_keyframe_offset;
> +    int8_t i_keyframe_offset;
>  
>      /* keyframe index for seeking, created as we discover keyframes */
>      demux_index_entry_t *idx;
> @@ -130,4 +145,6 @@ struct demux_sys_t
>  
>      /* Length, if available. */
>      int64_t i_length;
> +
> +    DemuxDebug( bool b_seeked; )
>  };
> diff --git a/modules/demux/oggseek.c b/modules/demux/oggseek.c
> index a059fa0..227f2a8 100644
> --- a/modules/demux/oggseek.c
> +++ b/modules/demux/oggseek.c
> @@ -34,11 +34,33 @@
>  #include <vlc_demux.h>
>  
>  #include <ogg/ogg.h>
> +#include <limits.h>
>  
>  #include "ogg.h"
>  #include "oggseek.h"
>  
> +/* Theora spec 7.1 */
> +#define THEORA_FTYPE_NOTDATA       0x80
> +#define THEORA_FTYPE_INTERFRAME    0x40
> +
> +#define SEGMENT_NOT_FOUND -1
> +
> +#define MAX_PAGE_SIZE 65307
> +typedef struct packetStartCoordinates
> +{
> +    int64_t i_pos;
> +    int64_t i_pageno;
> +    int64_t i_skip;
> +} packetStartCoordinates;
> +
>  //#define OGG_SEEK_DEBUG 1
> +#ifdef OGG_SEEK_DEBUG
> +  #define OggDebug(code) code
> +  #define OggNoDebug(code)
> +#else
> +  #define OggDebug(code)
> +  #define OggNoDebug(code) code
> +#endif
>  /************************************************************
>  * index entries
>  *************************************************************/
> @@ -158,8 +180,90 @@ const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_
>      return idx;
>  }
>  
> +/* We insert into index, sorting by pagepos (as a page can match multiple
> +   time stamps) */
> +const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream,
> +                                             int64_t i_timestamp,
> +                                             int64_t i_pagepos )
> +{
> +    demux_index_entry_t *idx;
> +    demux_index_entry_t *oidx;
> +    demux_index_entry_t *last_idx = NULL;
> +
> +    if ( p_stream == NULL ) return NULL;
> +
> +    oidx = idx = p_stream->idx;
> +
> +    if ( i_timestamp < 1 || i_pagepos < 1 ) return NULL;
> +
> +    if ( idx == NULL )
> +    {
> +        demux_index_entry_t *ie = index_entry_new();
no check?
> +        ie->i_value = i_timestamp;
> +        ie->i_pagepos = i_pagepos;
> +        p_stream->idx = ie;
> +        return ie;
> +    }
>  
> +    while ( idx != NULL )
> +    {
> +        if ( idx->i_pagepos > i_pagepos ) break;
> +        last_idx = idx;
> +        idx = idx->p_next;
I don't really like the id of going through all the indexes each time,
why not keep a pointer to the last one? or use a "revert" list that just
appends a new index at the beginning (this will change some comparison
operator).
> +    }
>  
> +    /* new entry; insert after last_idx */
> +    idx = index_entry_new();
check?
> +    if ( last_idx != NULL )
> +    {
> +        idx->p_next = last_idx->p_next;
> +        last_idx->p_next = idx;
> +        idx->p_prev = last_idx;
> +    }
> +    else
> +    {
> +        idx->p_next = oidx;
> +        oidx = idx;
> +    }
> +
> +    if ( idx->p_next != NULL )
> +    {
> +        idx->p_next->p_prev = idx;
> +    }
> +
> +    idx->i_value = i_timestamp;
> +    idx->i_pagepos = i_pagepos;
> +
> +    return idx;
> +}
> +
> +static bool OggSeekIndexFind ( logical_stream_t *p_stream, int64_t i_timestamp,
> +                               int64_t *pi_pos_lower, int64_t *pi_pos_upper )
> +{
> +    demux_index_entry_t *idx = p_stream->idx;
> +    *pi_pos_lower = *pi_pos_upper = -1;
> +
> +    while ( idx != NULL )
> +    {
> +        if ( idx->i_value <= i_timestamp )
> +        {
> +            if ( !idx->p_next ) /* found on last index */
> +            {
> +                *pi_pos_lower = idx->i_pagepos;
> +                return true;
> +            }
> +            if ( idx->p_next->i_value > i_timestamp )
> +            {
> +                *pi_pos_lower = idx->i_pagepos;
> +                *pi_pos_upper = idx->p_next->i_pagepos;
> +                return true;
> +            }
> +        }
> +        idx = idx->p_next;
> +    }
> +
> +    return false;
> +}
>  
>  /*********************************************************************
>   * private functions
> @@ -202,6 +306,8 @@ static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
>          }
>      }
>  
> +    i_bytes_to_read = __MIN( i_bytes_to_read, INT_MAX );
> +
>      seek_byte ( p_demux, p_sys->i_input_position );
>  
>      buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read );
> @@ -544,20 +650,14 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
>              }
>              break;
>          }
> -#ifdef OGG_SEEK_DEBUG
> -        msg_Dbg( p_demux, "Bisecting startpos=%ld endpos=%ld kframe=%ld iframe=%ld",
> -                 i_start_pos, i_end_pos, i_kframe, i_frame);
> -#endif
> +
>          if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA || p_stream->fmt.i_codec == VLC_CODEC_OPUS )
>          {
>              i_pagepos = find_first_page( p_demux, i_start_pos, i_end_pos, p_stream,
>                                           &i_kframe, &i_frame );
>          }
>          else return -1;
> -#ifdef OGG_SEEK_DEBUG
> -        msg_Dbg( p_demux, "Page Found startpos=%ld endpos=%ld kframe=%ld iframe=%ld pagepos=%ld",
> -                 i_start_pos, i_end_pos, i_kframe, i_frame, i_pagepos);
> -#endif
> +
>          if ( i_pagepos != -1 && i_kframe != -1 )
>          {
>              /* found a page */
> @@ -611,61 +711,526 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
>      return -1;
>  }
>  
> +static int64_t find_first_page_granule( demux_t *p_demux,
> +                                int64_t i_pos1, int64_t i_pos2,
> +                                logical_stream_t *p_stream,
> +                                int64_t *i_granulepos )
> +{
> +    int64_t i_result;
> +    *i_granulepos = -1;
> +    int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1;
> +    int64_t i_bytes_read;
> +    int64_t i_pages_checked = 0;
> +    int64_t i_packets_checked;
> +
> +    demux_sys_t *p_sys  = p_demux->p_sys;
> +
> +    ogg_packet op;
> +
> +    seek_byte( p_demux, i_pos1 );
> +
> +    if ( i_pos1 == p_stream->i_data_start )
> +    {
> +        p_sys->b_page_waiting = true;
> +        return p_sys->i_input_position;
> +    }
> +
> +    if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
> +
> +    while ( 1 )
> +    {
> +
> +        if ( p_sys->i_input_position >= i_pos2 )
> +        {
> +            /* we reached the end and found no pages */
> +            return -1;
> +        }
> +
> +        /* read next chunk */
> +        if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) )
> +        {
> +            /* EOF */
> +            return -1;
> +        }
> +
> +        i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
> +
> +        i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
> +
> +        if ( i_result < 0 )
> +        {
> +            /* found a page, sync to page start */
> +            p_sys->i_input_position -= i_result;
> +            i_pos1 = p_sys->i_input_position;
> +            continue;
> +        }
> +
> +        if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
> +                               ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
> +        {
> +            i_pos1 = p_sys->i_input_position;
> +            break;
> +        }
>  
> +        p_sys->i_input_position += i_bytes_read;
>  
> +    };
>  
> +    seek_byte( p_demux, p_sys->i_input_position );
> +    ogg_stream_reset( &p_stream->os );
>  
> +    while( 1 )
> +    {
>  
> -/* find upper and lower pagepos for i_tframe; if we find an exact match, we return it */
> +        if ( p_sys->i_input_position >= i_pos2 )
> +        {
> +            /* reached the end of the search region and nothing was found */
> +            return p_sys->i_input_position;
> +        }
> +
> +        p_sys->b_page_waiting = false;
> +
> +        if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
> +        {
> +            /* EOF */
> +            return p_sys->i_input_position;
> +        }
> +
> +        // found a page
> +        if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) )
> +        {
> +            /* page is not for this stream */
> +            p_sys->i_input_position += i_result;
> +            if ( ! i_pages_checked ) i_pos1 = p_sys->i_input_position;
> +            continue;
> +        }
> +
> +        ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
> +
> +        i_pages_checked++;
> +        i_packets_checked = 0;
>  
> -static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe,
> -                                             int64_t *pi_pos_lower, int64_t *pi_pos_upper)
> +        if ( ogg_stream_packetout( &p_stream->os, &op ) > 0 )
> +        {
> +            i_packets_checked++;
> +        }
> +
> +        if ( i_packets_checked )
> +        {
> +            *i_granulepos = ogg_page_granulepos( &p_sys->current_page );
> +            p_sys->b_page_waiting = true;
> +            return i_pos1;
> +
> +        }
> +
> +        /*  -> start of next page */
> +        p_sys->i_input_position += i_result;
> +    }
> +}
> +
> +/* Checks if current packet matches codec keyframe */
> +bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet )
>  {
> -    int64_t i_kframe;
> -    int64_t i_frame;
> -    int64_t i_gpos;
> +    if ( p_stream->b_oggds )
> +    {
> +        return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT );
> +    }
> +    else if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
> +    {
> +        if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA )
> +            return false;
> +        else
> +            return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
could merged in :
               return !(( p_packet->bytes <= 0 || p_packet->packet[0]&THEORA_FTYPE_NOTDATA ) &&
                       ( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME));
> +    }
> +    return true;
> +}
>  
> -    demux_index_entry_t *idx = p_stream->idx;
> +int64_t Ogg_GetKeyframeGranule( logical_stream_t *p_stream, int64_t i_granule )
> +{
> +    if ( p_stream->b_oggds )
> +    {
> +           return -1; /* We have no way to know */
> +    }
> +    else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
> +    {
> +        return ( i_granule >> p_stream->i_granule_shift ) << p_stream->i_granule_shift;
> +    }
> +    else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
> +    {
> +        return ( i_granule >> 31 ) << 31;
> +    }
> +    /* No change, that's keyframe or it can't be shifted out (oggds) */
> +    return i_granule;
> +}
>  
> -    *pi_pos_lower = *pi_pos_upper = -1;
> +static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
> +            int64_t i_granulepos, packetStartCoordinates *p_lastpacketcoords,
> +            bool b_exact )
> +{
> +    ogg_packet op;
> +    demux_sys_t *p_sys  = p_demux->p_sys;
> +    ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
> +    int i=0;
>  
> -    while ( idx != NULL )
> +    int64_t itarget_frame = Ogg_GetKeyframeGranule( p_stream, i_granulepos );
> +    int64_t iframe = Ogg_GetKeyframeGranule( p_stream, ogg_page_granulepos( &p_sys->current_page ) );
> +
> +    if ( ! ogg_page_continued( &p_sys->current_page ) )
> +    {
> +        /* Start of frame, not continued page, but no packet. */
> +        p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page );
> +        p_lastpacketcoords->i_pos = p_sys->i_input_position;
> +        p_lastpacketcoords->i_skip = 0;
> +    }
> +
> +    if ( b_exact && iframe > itarget_frame )
> +    {
> +        while( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) {};
> +        return false;
> +    }
> +
> +    while( ogg_stream_packetpeek( &p_stream->os, &op ) > 0 )
> +    {
> +        if ( ( !b_exact || itarget_frame == iframe ) && Ogg_IsKeyFrame( p_stream, &op ) )
> +        {
> +            OggDebug(
> +                msg_Dbg(p_demux, "** KEYFRAME **" );
> +                msg_Dbg(p_demux, "** KEYFRAME PACKET START pageno %"PRId64" OFFSET %"PRId64" skip %"PRId64" **", p_lastpacketcoords->i_pageno, p_lastpacketcoords->i_pos, p_lastpacketcoords->i_skip );
> +                msg_Dbg(p_demux, "KEYFRAME PACKET IS at pageno %"PRId64" OFFSET %"PRId64" with skip %d packet (%d / %d) ",
> +                    ogg_page_pageno( &p_sys->current_page ), p_sys->i_input_position, i, i+1, ogg_page_packets( &p_sys->current_page ) );
> +                p_sys->b_seeked = true;
> +            );
> +
> +            if ( i != 0 ) /* Not continued packet */
> +            {
> +                /* We need to handle the case when the packet spans onto N
> +                       previous page(s). packetout() will be valid only when
> +                       all segments are assembled.
> +                       Keyframe flag is only available after assembling last part
> +                       (when packetout() becomes valid). We have no way to guess
> +                       keyframe at earlier time.
> +                    */
> +                p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page );
> +                p_lastpacketcoords->i_pos = p_sys->i_input_position;
> +                p_lastpacketcoords->i_skip = i;
> +            }
> +
> +            return true;
> +        }
> +
> +        p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page );
> +        p_lastpacketcoords->i_pos = p_sys->i_input_position;
> +        p_lastpacketcoords->i_skip = i + 1;
> +        i++;
           p_lastpacketcoords->i_skip = ++i;
> +        /* remove that packet and go sync to next */
> +        ogg_stream_packetout( &p_stream->os, &op );
> +    }
> +    return false;
> +}
> +
> +static int64_t OggForwardSeekToFrame( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
> +                logical_stream_t *p_stream, int64_t i_granulepos, bool b_fastseek )
> +{
> +    int64_t i_result;
> +    int64_t i_bytes_to_read;
> +    int64_t i_bytes_read;
> +
> +    demux_sys_t *p_sys  = p_demux->p_sys;
> +
> +    i_bytes_to_read = i_pos2 - i_pos1 + 1;
> +    seek_byte( p_demux, i_pos1 );
> +    if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
> +
> +    OggDebug(
> +        msg_Dbg( p_demux, "Probing Fwd %"PRId64" %"PRId64" for granule %"PRId64,
> +        i_pos1, i_pos2, i_granulepos );
> +    );
> +
> +    while ( 1 )
>      {
> -        if ( idx-> i_pagepos < 0 )
> +
> +        if ( p_sys->i_input_position >= i_pos2 ) return -1;
> +        /* read next chunk */
> +        if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) ) return -1;
> +
> +        i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
> +
> +        i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
> +
> +        if ( i_result < 0 )
>          {
> -            /* kframe was found to be invalid */
> -            idx = idx->p_next;
> +            /* found a page, sync to page start */
> +            p_sys->i_input_position -= i_result;
> +            i_pos1 = p_sys->i_input_position;
>              continue;
>          }
>  
> -        if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||  p_stream->fmt.i_codec == VLC_CODEC_OPUS )
> +        if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
> +                               ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
>          {
> -            i_gpos = idx->i_value;
> -            i_kframe = i_gpos >> p_stream->i_granule_shift;
> -            i_frame = i_kframe + i_gpos - ( i_kframe << p_stream->i_granule_shift );
> +            i_pos1 = p_sys->i_input_position;
> +            break;
>          }
> -        else return NULL;
>  
> +        p_sys->i_input_position += i_bytes_read;
> +    };
> +
> +    seek_byte( p_demux, p_sys->i_input_position );
> +    ogg_stream_reset( &p_stream->os );
> +
> +    packetStartCoordinates lastpacket = { -1, -1, -1 };
> +
> +    while( 1 )
> +    {
>  
> -        if ( i_kframe > i_tframe )
> +        if ( p_sys->i_input_position >= i_pos2 )
>          {
> -            *pi_pos_upper = idx->i_pagepos;
> -            return NULL;
> +            /* reached the end of the search region and nothing was found */
> +            break;
>          }
>  
> -        if ( i_frame < i_tframe )
> +        p_sys->b_page_waiting = false;
> +
> +        if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
>          {
> -            *pi_pos_lower = idx->i_pagepos;
> -            idx = idx->p_next;
> +            /* EOF */
> +            break;
> +        }
> +
> +        // found a page
> +        if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) )
> +        {
> +            /* page is not for this stream */
> +            p_sys->i_input_position += i_result;
>              continue;
>          }
>  
> -        return idx;
> +        if ( OggSeekToPacket( p_demux, p_stream, i_granulepos, &lastpacket, b_fastseek ) )
> +        {
> +            p_sys->i_input_position = lastpacket.i_pos;
> +            p_stream->i_skip_frames = 0;
> +            p_sys->b_page_waiting = true;
> +            return p_sys->i_input_position;
> +        }
> +
> +        /*  -> start of next page */
> +        p_sys->i_input_position += i_result;
> +    }
> +
> +    return SEGMENT_NOT_FOUND;
> +}
> +
> +static int64_t OggBackwardSeekToFrame( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
> +                               logical_stream_t *p_stream, int64_t i_granulepos )
> +{
> +    int64_t i_result;
> +    int64_t i_offset = __MAX( 1 + ( (i_pos2 - i_pos1) >> 1 ), OGGSEEK_BYTES_TO_READ );
> +
> +restart:
> +
> +    OggDebug(
> +        msg_Dbg( p_demux, "Probing Back %"PRId64" %"PRId64" for granule %"PRId64,
> +        i_pos1, i_pos2, i_granulepos );
> +    );
> +
> +    i_result = OggForwardSeekToFrame( p_demux, i_pos1, i_pos2, p_stream,
> +                                      i_granulepos, true );
> +
> +    if ( i_result == SEGMENT_NOT_FOUND && i_pos1 > p_stream->i_data_start )
> +    {
> +        i_pos1 = __MAX( p_stream->i_data_start, i_pos1 - i_offset );
> +        goto restart;
looping with goto :(
> +    }
> +
> +    return i_result;
> +}
> +
> +/* Dont use b_presentation with frames granules ! */
> +int64_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream,
> +                                       int64_t i_granule, bool b_presentation )
> +{
> +    int64_t i_timestamp = -1;
> +
> +    if ( p_stream->b_oggds )
> +    {
> +        i_timestamp = i_granule * INT64_C(1000000) / p_stream->f_rate;
> +    }
> +    else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
> +        p_stream->fmt.i_codec == VLC_CODEC_KATE )
> +    {
> +        ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift;
> +        ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift );
> +        /* See Theora A.2.3 */
> +        if ( b_presentation ) pframe -= p_stream->i_keyframe_offset;
> +        i_timestamp = ( iframe + pframe ) * INT64_C(1000000) / p_stream->f_rate;
> +    }
> +    else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
> +    {
> +        ogg_int64_t i_dts = i_granule >> 31;
> +        /* NB, OggDirac granulepos values are in units of 2*picturerate */
> +        i_timestamp = (i_dts/2) * INT64_C(1000000) / p_stream->f_rate;
> +    }
> +    else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
> +    {
> +        i_timestamp = p_stream->i_pre_skip + i_granule * INT64_C(1000000) / 48000;
> +    }
> +    else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS )
> +    {
> +        i_timestamp = i_granule * INT64_C(1000000) / p_stream->f_rate;
I hope that there is somewhere a check that makes sure that
p_stream->f_rate isn't null.

>      }
>  
> -    return NULL;
> +    return i_timestamp;
>  }
>  
> +bool Oggseek_PacketPCRFixup( logical_stream_t *p_stream, ogg_page *p_page,
> +                             ogg_packet *p_packet )
> +{
> +    if ( p_packet->granulepos != -1 )
> +        return false;
> +    else
> +    if ( p_stream->b_oggds )
> +    {
> +        p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
> +                ogg_page_granulepos( p_page ), true );
> +        return true;
> +    }
> +    else if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
> +    {
> +        p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
> +                                        ogg_page_granulepos( p_page ), false );
> +        /* Computes the presentation time of the first packet on page */
> +        p_stream->i_pcr -= INT64_C(1000000) *
> +                ogg_page_packets( p_page ) / p_stream->f_rate;
> +        return true;
> +    }
> +
> +    return false;
> +}
> +
> +/* returns pos */
> +static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stream,
> +            int64_t i_targettime, int64_t i_pos_lower, int64_t i_pos_upper)
> +{
> +    int64_t i_start_pos;
> +    int64_t i_end_pos;
> +    int64_t i_segsize;
> +
> +    struct
> +    {
> +        int64_t i_pos;
> +        int64_t i_timestamp;
> +        int64_t i_granule;
> +    } bestlower = { p_stream->i_data_start, 0, p_stream->i_keyframe_offset },
> +      current = { -1, -1, -1 };
> +
> +    demux_sys_t *p_sys  = p_demux->p_sys;
> +
> +    i_pos_lower = __MAX( i_pos_lower, p_stream->i_data_start );
> +    i_pos_upper = __MIN( i_pos_upper, p_sys->i_total_length );
> +    if ( i_pos_upper < 0 ) i_pos_upper = p_sys->i_total_length;
> +
> +    i_start_pos = i_pos_lower;
> +    i_end_pos = i_pos_upper;
> +
> +    i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
> +    i_start_pos += i_segsize;
> +
> +    OggDebug( msg_Dbg(p_demux, "Bisecting for time=%"PRId64" between %"PRId64" and %"PRId64,
> +            i_targettime, i_pos_lower, i_pos_upper ) );
> +
> +    do
> +    {
> +        /* see if the frame lies in current segment */
> +        i_start_pos = __MAX( i_start_pos, i_pos_lower );
> +        i_end_pos = __MIN( i_end_pos, i_pos_upper );
> +
> +        if ( i_start_pos >= i_end_pos )
> +        {
> +            if ( i_start_pos == i_pos_lower)
> +            {
> +                return i_start_pos;
> +            }
> +            return -1;
> +        }
> +
> +
> +        current.i_pos = find_first_page_granule( p_demux,
> +                                                 i_start_pos, i_end_pos,
> +                                                 p_stream,
> +                                                 &current.i_granule );
> +
> +        current.i_timestamp = Oggseek_GranuleToAbsTimestamp( p_stream,
> +                                                             current.i_granule, false );
> +
> +        if ( current.i_timestamp == -1 )
> +        {
> +            msg_Err( p_demux, "Unmatched granule. New codec ?" );
> +            return -1;
> +        }
> +
> +        if ( current.i_pos != -1 && current.i_granule != -1 )
> +        {
> +            /* found a page */
> +
> +            if ( current.i_timestamp <= i_targettime )
> +            {
> +                /* set our lower bound */
> +                if ( current.i_timestamp > bestlower.i_timestamp )
> +                    bestlower = current;
> +                i_start_pos = current.i_pos;
> +            }
> +            else if ( current.i_timestamp > i_targettime )
> +            {
> +                /* check lower half of segment */
> +                i_start_pos -= i_segsize;
> +                i_end_pos -= i_segsize;
> +            }
> +        }
> +        else
> +        {
> +            /* no keyframe found, check lower segment */
> +            i_end_pos -= i_segsize;
> +            i_start_pos -= i_segsize;
> +        }
> +
> +        OggDebug( msg_Dbg(p_demux, "Bisect restart i_segsize=%"PRId64" between %"PRId64" and %"PRId64,
> +                i_segsize, i_start_pos, i_end_pos ) );
> +
> +        i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
> +        i_start_pos += i_segsize;
> +
> +    } while ( i_segsize > 64 && current.i_granule != -1 );
> +
> +
> +    if ( p_stream->b_oggds )
> +    {
> +        int64_t a = OggBackwardSeekToFrame( p_demux,
> +                __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ),
> +                bestlower.i_pos,
> +                p_stream, bestlower.i_granule /* unused */ );
> +        return a;
> +    }
> +    /* If not each packet is usable as keyframe, query the codec for keyframe */
> +    else if ( Ogg_GetKeyframeGranule( p_stream, bestlower.i_granule ) != bestlower.i_granule )
> +    {
> +        int64_t i_keyframegranule = Ogg_GetKeyframeGranule( p_stream, bestlower.i_granule );
> +
> +        OggDebug( msg_Dbg( p_demux, "Need to reseek to keyframe (%"PRId64") granule (%"PRId64"!=%"PRId64") to t=%"PRId64,
> +                           i_keyframegranule >> p_stream->i_granule_shift,
> +                           bestlower.i_granule,
> +                           i_pos_upper,
> +                           Oggseek_GranuleToAbsTimestamp( p_stream, i_keyframegranule, true ) ) );
> +
> +        OggDebug( msg_Dbg( p_demux, "Seeking back to %"PRId64, __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ) ) );
> +
> +        int64_t a = OggBackwardSeekToFrame( p_demux,
> +            __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ),
> +            stream_Size( p_demux->s ), p_stream, i_keyframegranule );
> +        return a;
> +    }
> +
> +    return bestlower.i_pos;
> +}
>  
>  /* get highest frame in theora and opus streams */
>  
> @@ -728,129 +1293,73 @@ int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
>      return -1;
>  }
>  
> -
> -
> -
> -
> -
> -/* seek to target frame in p_stream; actually we will probably end up just before it
> - *   (so we set skip)
> - *
> - * range for i_tframe is 0 -> p_sys->i_total_frames - 1
> - */
> -
> -int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe )
> +int Oggseek_BlindSeektoPosition( demux_t *p_demux, logical_stream_t *p_stream,
> +                                 double f, bool b_canfastseek )
>  {
> -
> -    const demux_index_entry_t *fidx;
> -
> -    /* lower and upper bounds for search domain */
> -    int64_t i_pos_lower;
> -    int64_t i_pos_upper;
> -
> -    int64_t i_granulepos;
> +    OggDebug( msg_Dbg( p_demux, "=================== Seeking To Blind Pos" ) );
> +    int64_t i_size = stream_Size( p_demux->s );
> +    int64_t i_granule;
>      int64_t i_pagepos;
>  
> -    /* keyframe for i_tframe ( <= i_tframe ) */
> -    int64_t i_kframe;
> -
> -    /* keyframe for i_kframe ( <= i_kframe ) */
> -    int64_t i_xkframe;
> +    i_size = find_first_page_granule( p_demux,
> +                                             i_size * f, i_size,
> +                                             p_stream,
> +                                             &i_granule );
>  
> -    /* next frame to be decoded ( >= i_xkframe ) */
> -    int64_t i_cframe;
> +    OggDebug( msg_Dbg( p_demux, "Seek start pos is %"PRId64" granule %"PRId64, i_size, i_granule ) );
>  
> -    demux_sys_t *p_sys  = p_demux->p_sys;
> -
> -    i_tframe += p_stream->i_keyframe_offset;
> -
> -    i_cframe = i_tframe;
> -    /* For Opus, seek back 80 ms before the target playback position. */
> -    if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
> -    {
> -        if ( i_tframe <= p_stream->i_pre_skip )
> -            i_cframe = 0;
> -        else if ( i_tframe < 80*48 )
> -            i_cframe = 0;
> -        else
> -            i_cframe = i_tframe - 80*48;
> -    }
> -
> -    /* reduce the search domain */
> -    fidx = get_bounds_for( p_stream, i_cframe, &i_pos_lower, &i_pos_upper );
> -
> -    if ( fidx == NULL )
> -    {
> -        /* no exact match found; search the domain for highest keyframe <= i_cframe */
> -#ifdef OGG_SEEK_DEBUG
> -        msg_Dbg( p_demux, "Not found in index, searching");
> -#endif
> -        i_granulepos = ogg_seek ( p_demux, p_stream, i_cframe, i_pos_lower, i_pos_upper,
> -                                  &i_pagepos, true );
> -        if ( i_granulepos == -1 )
> -        {
> -#ifdef OGG_SEEK_DEBUG
> -        msg_Err( p_demux, "Unable to find the requested frame");
> -#endif
> -            return VLC_EGENERIC;
> -        }
> -    }
> -    else {
> -        i_granulepos = fidx->i_value;
> -    }
> +    i_granule = Ogg_GetKeyframeGranule( p_stream, i_granule );
>  
> -    if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
> +    if ( b_canfastseek )
>      {
> -#ifdef OGG_SEEK_DEBUG
> -        msg_Dbg( p_demux, "OPUS seeks to granulepos %ld (%ld frames)", i_granulepos,  i_tframe - i_cframe );
> -#endif
> -        oggseek_theora_index_entry_add( p_stream, i_granulepos, p_sys->i_input_position );
> +        /* Peek back until we meet a keyframe to start our decoding up to our
> +         * final seek time */
> +        i_pagepos = OggBackwardSeekToFrame( p_demux,
> +                __MAX ( i_size - MAX_PAGE_SIZE, p_stream->i_data_start ),
> +                __MIN ( i_size + MAX_PAGE_SIZE, p_demux->p_sys->i_total_length ),
> +                p_stream, i_granule );
>      }
>      else
> -    if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
>      {
> -        i_kframe = i_granulepos >> p_stream->i_granule_shift;
> -        if ( i_kframe < p_stream->i_keyframe_offset )
> -        {
> -            i_kframe = p_stream->i_keyframe_offset;
> -        }
> -
> -        /* we found a keyframe, but we don't know where its packet starts, so search for a
> -           frame just before it */
> -
> -        /* reduce search domain */
> -        get_bounds_for( p_stream, i_kframe-1, &i_pos_lower, &i_pos_upper );
> -
> -        i_granulepos = ogg_seek( p_demux, p_stream, i_kframe-1, i_pos_lower, i_pos_upper,
> -                                 &i_pagepos, false );
> -
> -        /* i_cframe will be the next frame we decode */
> -        i_xkframe = i_granulepos >> p_stream->i_granule_shift;
> -        i_cframe = i_xkframe + i_granulepos - ( i_xkframe << p_stream->i_granule_shift) + 1;
> -
> -        if ( p_sys->i_input_position == p_stream->i_data_start )
> -        {
> -            i_cframe = i_kframe = p_stream->i_keyframe_offset;
> -        }
> -        else
> -        {
> -            oggseek_theora_index_entry_add( p_stream, i_granulepos, p_sys->i_input_position );
> -        }
> -
> +        /* Otherwise, we just sync to the next keyframe we meet */
> +        i_pagepos = OggForwardSeekToFrame( p_demux,
> +                __MAX ( i_size - MAX_PAGE_SIZE, p_stream->i_data_start ),
> +                stream_Size( p_demux->s ),
> +                p_stream, i_granule, false );
>      }
> -    else return VLC_EGENERIC;
>  
> -    p_stream->i_skip_frames = i_tframe - i_cframe;
> +    OggDebug( msg_Dbg( p_demux, "=================== Seeked To %"PRId64" granule %"PRId64, i_pagepos, i_granule ) );
> +    return i_pagepos;
> +}
>  
> -    ogg_stream_reset( &p_stream->os );
> +int Oggseek_SeektoAbsolutetime( demux_t *p_demux, logical_stream_t *p_stream,
> +                                int64_t i_time )
> +{
> +    demux_sys_t *p_sys  = p_demux->p_sys;
>  
> -    return VLC_SUCCESS;
> -}
> +    OggDebug( msg_Dbg( p_demux, "=================== Seeking To Absolute Time %"PRId64, i_time ) );
> +    int64_t i_offset_lower = p_stream->i_data_start;
> +    int64_t i_offset_upper = p_sys->i_total_length;
>  
> +    OggNoDebug(
> +        OggSeekIndexFind( p_stream, i_time, &i_offset_lower, &i_offset_upper )
> +    );
>  
> +    i_offset_lower = __MAX( i_offset_lower, p_stream->i_data_start );
> +    i_offset_upper = __MIN( i_offset_upper, p_sys->i_total_length );
>  
> +    int64_t i_pagepos = OggBisectSearchByTime( p_demux, p_stream, i_time,
> +                                       i_offset_lower, i_offset_upper);
>  
> +    /* Insert keyframe position into index */
> +    OggNoDebug(
> +    if ( i_pagepos >= p_stream->i_data_start )
> +        OggSeek_IndexAdd( p_stream, i_time, i_pagepos )
> +    );
>  
> +    OggDebug( msg_Dbg( p_demux, "=================== Seeked To %"PRId64" time %"PRId64, i_pagepos, i_time ) );
> +    return i_pagepos;
> +}
>  
>  /****************************************************************************
>   * oggseek_read_page: Read a full Ogg page from the physical bitstream.
> @@ -859,6 +1368,7 @@ int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
>   * unless we are at the end of stream.
>   *
>   ****************************************************************************/
> +
>  int64_t oggseek_read_page( demux_t *p_demux )
>  {
>      demux_sys_t *p_ogg = p_demux->p_sys  ;
> diff --git a/modules/demux/oggseek.h b/modules/demux/oggseek.h
> index 88d22fa..5920379 100644
> --- a/modules/demux/oggseek.h
> +++ b/modules/demux/oggseek.h
> @@ -49,6 +49,16 @@ struct oggseek_index_entry
>      int64_t i_pagepos_end;
>  };
>  
> +int64_t Ogg_GetKeyframeGranule ( logical_stream_t *p_stream, int64_t i_granule );
> +bool    Ogg_IsKeyFrame ( logical_stream_t *, ogg_packet * );
> +
> +int64_t Oggseek_GranuleToAbsTimestamp ( logical_stream_t *p_stream, int64_t i_granule,
> +                                       bool b_presentation );
> +bool    Oggseek_PacketPCRFixup ( logical_stream_t *p_stream, ogg_page *, ogg_packet * );
> +int     Oggseek_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool );
> +int     Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, int64_t i_granulepos );
> +const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *, int64_t, int64_t );
> +
>  const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *,
>                                                              int64_t i_granule,
>                                                              int64_t i_pagepos );

Regards,

-- 
Denis Charmet - TypX
Le mauvais esprit est un art de vivre





More information about the vlc-devel mailing list