* mkv: avoid seeking at the same place (was sooooo slow 'cause of
[vlc.git] / modules / demux / mkv.cpp
index 2bcb82b..46e20b9 100644 (file)
@@ -2,7 +2,7 @@
  * mkv.cpp : matroska demuxer
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: mkv.cpp,v 1.1 2003/06/22 07:39:39 fenrir Exp $
+ * $Id: mkv.cpp,v 1.7 2003/06/24 00:33:39 fenrir Exp $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *
@@ -80,6 +80,10 @@ static int  Demux     ( input_thread_t * );
  * Module descriptor
  *****************************************************************************/
 vlc_module_begin();
+    add_category_hint( N_("mkv-demuxer"), NULL, VLC_TRUE );
+        add_bool( "mkv-index", 0, NULL,
+                  N_("Create index if no cues found"),
+                  N_("Create index if no cues found"), VLC_TRUE );
     set_description( _("mka/mkv stream demuxer" ) );
     set_capability( "demux", 50 );
     set_callbacks( Activate, Deactivate );
@@ -148,6 +152,9 @@ uint32_t vlc_stream_io_callback::read( void *p_buffer, size_t i_size )
 void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
 {
     int64_t i_pos;
+    int64_t i_last;
+
+    i_last = getFilePointer();
 
     vlc_mutex_lock( &p_input->stream.stream_lock );
     switch( mode )
@@ -159,10 +166,12 @@ void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
             i_pos = p_input->stream.p_selected_area->i_size - i_offset;
             break;
         default:
-            i_pos= p_input->stream.p_selected_area->i_tell + i_offset;
+            i_pos= i_last + i_offset;
             break;
     }
-    if( i_pos < 0 || i_pos > p_input->stream.p_selected_area->i_size )
+
+    if( i_pos < 0 ||
+        ( i_pos > p_input->stream.p_selected_area->i_size && p_input->stream.p_selected_area->i_size != 0 ) )
     {
         msg_Err( p_input, "seeking to wrong place (i_pos=%lld)", i_pos );
         vlc_mutex_unlock( &p_input->stream.stream_lock );
@@ -170,8 +179,54 @@ void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
     }
     vlc_mutex_unlock( &p_input->stream.stream_lock );
 
-    input_AccessReinit( p_input );
-    p_input->pf_seek( p_input, i_pos );
+    if( i_pos == i_last )
+    {
+        return;
+    }
+
+    msg_Dbg( p_input, "####################seek new=%lld old=%lld", i_pos, getFilePointer() );
+
+    if( p_input->stream.b_seekable &&
+        ( /*p_input->stream.i_method == INPUT_METHOD_FILE ||*/ i_pos < i_last || i_pos - i_last > p_input->i_bufsize / 4 ) )
+    {
+        input_AccessReinit( p_input );
+        p_input->pf_seek( p_input, i_pos );
+    }
+    else if( i_pos > i_last )
+    {
+        data_packet_t   *p_data;
+        int             i_skip = i_pos - i_last;
+
+        if( i_skip > 1024 )
+        {
+            msg_Warn( p_input, "will skip %d bytes, slow", i_skip );
+        }
+
+        while (i_skip > 0 )
+        {
+            int i_read;
+
+            i_read = input_SplitBuffer( p_input, &p_data,
+                                        __MIN( 4096, i_skip ) );
+            if( i_read < 0 )
+            {
+                msg_Err( p_input, "seek failed" );
+                return;
+            }
+            i_skip -= i_read;
+
+            input_DeletePacket( p_input->p_method_data, p_data );
+            if( i_read == 0 && i_skip > 0 )
+            {
+                msg_Err( p_input, "seek failed" );
+                return;
+            }
+        }
+    }
+    else
+    {
+        msg_Err( p_input, "cannot seek or emulate seek to %lld from %lld", i_pos, i_last );
+    }
 }
 
 size_t vlc_stream_io_callback::write( const void *p_buffer, size_t i_size )
@@ -203,6 +258,9 @@ class EbmlParser
     public:
         EbmlParser( EbmlStream *es, EbmlElement *el_start );
         ~EbmlParser( void );
+
+        int  SetNext( const EbmlCallbacks & ClassInfos );
+
         void Up( void );
         void Down( void );
         EbmlElement *Get( void );
@@ -214,7 +272,7 @@ class EbmlParser
 
     EbmlStream  *m_es;
     int         mi_level;
-    EbmlElement *m_el[5];
+    EbmlElement *m_el[6];
 
     EbmlElement *m_got;
 
@@ -231,7 +289,7 @@ EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start )
     m_got = NULL;
     m_el[0] = el_start;
 
-    for( i = 1; i < 5; i++ )
+    for( i = 1; i < 6; i++ )
     {
         m_el[i] = NULL;
     }
@@ -354,8 +412,9 @@ static vlc_fourcc_t __GetFOURCC( uint8_t *p )
     return VLC_FOURCC( p[0], p[1], p[2], p[3] );
 }
 
+
 /*****************************************************************************
- * Definitions of structures and functions used by this plugins
+ * definitions of structures and functions used by this plugins
  *****************************************************************************/
 typedef struct
 {
@@ -394,32 +453,76 @@ typedef struct
     int             i_data_init;
     uint8_t         *p_data_init;
 
+    /* hack : it's for seek */
+    vlc_bool_t      b_search_keyframe;
 } mkv_track_t;
 
+typedef struct
+{
+    int     i_track;
+    int     i_block_number;
+
+    int64_t i_position;
+    int64_t i_time;
+
+    vlc_bool_t b_key;
+} mkv_index_t;
+
 struct demux_sys_t
 {
     vlc_stream_io_callback  *in;
     EbmlStream              *es;
     EbmlParser              *ep;
 
-    KaxSegment              *segment;
-    KaxCluster              *cluster;
+    /* time scale */
+    uint64_t                i_timescale;
 
-    uint64_t                tc_scale;
+    /* duration of the segment */
     float                   f_duration;
 
+    /* all tracks */
     int                     i_track;
     mkv_track_t             *track;
 
-    mtime_t                 i_pcr;
+    /* from seekhead */
+    int64_t                 i_cues_position;
+    int64_t                 i_chapters_position;
+
+    /* current data */
+    KaxSegment              *segment;
+    KaxCluster              *cluster;
 
-    mtime_t                 i_last_pts;
+    mtime_t                 i_pts;
 
-    uint64_t                i_cluster_tc;
+    vlc_bool_t              b_cues;
+    int                     i_index;
+    int                     i_index_max;
+    mkv_index_t             *index;
 };
 
 #define MKVD_TIMECODESCALE 1000000
 
+static void IndexAppendCluster( input_thread_t *p_input, KaxCluster *cluster )
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+
+#define idx p_sys->index[p_sys->i_index]
+    idx.i_track       = -1;
+    idx.i_block_number= -1;
+    idx.i_position    = cluster->GetElementPosition();
+    idx.i_time        = -1;
+    idx.b_key         = VLC_TRUE;
+
+    p_sys->i_index++;
+    if( p_sys->i_index >= p_sys->i_index_max )
+    {
+        p_sys->i_index_max += 1024;
+        p_sys->index = (mkv_index_t*)realloc( p_sys->index, sizeof( mkv_index_t ) * p_sys->i_index_max );
+    }
+#undef idx
+}
+
+
 /*****************************************************************************
  * Activate: initializes matroska demux structures
  *****************************************************************************/
@@ -459,27 +562,23 @@ static int Activate( vlc_object_t * p_this )
         return VLC_EGENERIC;
     }
 
-    if( p_input->stream.i_method != INPUT_METHOD_FILE || !p_input->stream.b_seekable )
-    {
-        msg_Err( p_input, "can only read matroska over seekable file yet" );
-        return VLC_EGENERIC;
-    }
-    /* FIXME FIXME FIXME */
-    p_input->stream.b_seekable = VLC_FALSE;
-    /* FIXME FIXME FIXME */
-
-
     p_input->p_demux_data = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
     memset( p_sys, 0, sizeof( demux_sys_t ) );
 
     p_sys->in = new vlc_stream_io_callback( p_input );
     p_sys->es = new EbmlStream( *p_sys->in );
     p_sys->f_duration   = -1;
-    p_sys->tc_scale     = MKVD_TIMECODESCALE;
+    p_sys->i_timescale     = MKVD_TIMECODESCALE;
     p_sys->i_track      = 0;
     p_sys->track        = (mkv_track_t*)malloc( sizeof( mkv_track_t ) );
-    p_sys->i_pcr        = 0;
-    p_sys->i_last_pts   = 0;
+    p_sys->i_pts   = 0;
+    p_sys->i_cues_position = -1;
+    p_sys->i_chapters_position = -1;
+
+    p_sys->b_cues       = VLC_FALSE;
+    p_sys->i_index      = 0;
+    p_sys->i_index_max  = 1024;
+    p_sys->index        = (mkv_index_t*)malloc( sizeof( mkv_index_t ) * p_sys->i_index_max );
 
     if( p_sys->es == NULL )
     {
@@ -527,9 +626,9 @@ static int Activate( vlc_object_t * p_this )
                     KaxTimecodeScale &tcs = *(KaxTimecodeScale*)el2;
 
                     tcs.ReadData( p_sys->es->I_O() );
-                    p_sys->tc_scale = uint64(tcs);
+                    p_sys->i_timescale = uint64(tcs);
 
-                    msg_Dbg( p_input, "|   |   + TimecodeScale=%lld", p_sys->tc_scale );
+                    msg_Dbg( p_input, "|   |   + TimecodeScale=%lld", p_sys->i_timescale );
                 }
                 else if( EbmlId( *el2 ) == KaxDuration::ClassInfos.GlobalId )
                 {
@@ -809,6 +908,61 @@ static int Activate( vlc_object_t * p_this )
         else if( EbmlId( *el1 ) == KaxSeekHead::ClassInfos.GlobalId )
         {
             msg_Dbg( p_input, "|   + Seek head" );
+            p_sys->ep->Down();
+            while( ( el = p_sys->ep->Get() ) != NULL )
+            {
+                if( EbmlId( *el ) == KaxSeek::ClassInfos.GlobalId )
+                {
+                    EbmlId id = EbmlVoid::ClassInfos.GlobalId;
+                    int64_t i_pos = -1;
+
+                    //msg_Dbg( p_input, "|   |   + Seek" );
+                    p_sys->ep->Down();
+                    while( ( el = p_sys->ep->Get() ) != NULL )
+                    {
+                        if( EbmlId( *el ) == KaxSeekID::ClassInfos.GlobalId )
+                        {
+                            KaxSeekID &sid = *(KaxSeekID*)el;
+
+                            sid.ReadData( p_sys->es->I_O() );
+
+                            id = EbmlId( sid.GetBuffer(), sid.GetSize() );
+                        }
+                        else  if( EbmlId( *el ) == KaxSeekPosition::ClassInfos.GlobalId )
+                        {
+                            KaxSeekPosition &spos = *(KaxSeekPosition*)el;
+
+                            spos.ReadData( p_sys->es->I_O() );
+
+                            i_pos = uint64( spos );
+                        }
+                        else
+                        {
+                            msg_Dbg( p_input, "|   |   |   + Unknow (%s)", typeid(*el).name() );
+                        }
+                    }
+                    p_sys->ep->Up();
+
+                    if( i_pos >= 0 )
+                    {
+                        if( id == KaxCues::ClassInfos.GlobalId )
+                        {
+                            msg_Dbg( p_input, "|   |   |   = cues at %lld", i_pos );
+                            p_sys->i_cues_position = p_sys->segment->GetGlobalPosition( i_pos );
+                        }
+                        else if( id == KaxChapters::ClassInfos.GlobalId )
+                        {
+                            msg_Dbg( p_input, "|   |   |   = chapters at %lld", i_pos );
+                            p_sys->i_chapters_position = p_sys->segment->GetGlobalPosition( i_pos );
+                        }
+                    }
+                }
+                else
+                {
+                    msg_Dbg( p_input, "|   |   + Unknow (%s)", typeid(*el).name() );
+                }
+            }
+            p_sys->ep->Up();
         }
         else if( EbmlId( *el1 ) == KaxCues::ClassInfos.GlobalId )
         {
@@ -817,9 +971,10 @@ static int Activate( vlc_object_t * p_this )
         else if( EbmlId( *el1 ) == KaxCluster::ClassInfos.GlobalId )
         {
             msg_Dbg( p_input, "|   + Cluster" );
+
             p_sys->cluster = (KaxCluster*)el1;
-            p_sys->ep->Down();
 
+            p_sys->ep->Down();
             /* stop parsing the stream */
             break;
         }
@@ -847,6 +1002,160 @@ static int Activate( vlc_object_t * p_this )
         goto error;
     }
 
+    if( p_sys->i_chapters_position >= 0 )
+    {
+        msg_Warn( p_input, "chapters unsupported" );
+    }
+
+#if 0
+    if( p_sys->i_cues_position == -1 && config_GetInt( p_input, "mkv-index" ) != 0 )
+    {
+        int64_t i_sav_position = p_sys->in->getFilePointer();
+        EbmlParser *ep;
+
+        /* parse to find cues gathering info on all clusters */
+        msg_Dbg( p_input, "no cues referenced, looking for one" );
+
+        /* ugly but like easier */
+        p_sys->in->setFilePointer( p_sys->cluster->GetElementPosition(), seek_beginning );
+
+        ep = new EbmlParser( p_sys->es, p_sys->segment );
+        while( ( el = ep->Get() ) != NULL )
+        {
+            if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
+            {
+                int64_t i_pos = el->GetElementPosition();
+
+                msg_Dbg( p_input, "found a cluster at %lld", i_pos );
+            }
+            else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
+            {
+                msg_Dbg( p_input, "found a Cues !" );
+            }
+        }
+        delete ep;
+        msg_Dbg( p_input, "lookind done." );
+
+        p_sys->in->setFilePointer( i_sav_position, seek_beginning );
+    }
+#endif
+
+    /* *** Load the cue if found *** */
+    if( p_sys->i_cues_position >= 0 && p_input->stream.b_seekable )
+    {
+        int64_t i_sav_position = p_sys->in->getFilePointer();
+        EbmlParser *ep;
+        EbmlElement *cues;
+
+        msg_Dbg( p_input, "loading cues" );
+        p_sys->in->setFilePointer( p_sys->i_cues_position, seek_beginning );
+        cues = p_sys->es->FindNextID( KaxCues::ClassInfos, 0xFFFFFFFFL);
+
+        if( cues == NULL )
+        {
+            msg_Err( p_input, "cannot load cues (broken seekhead or file)" );
+        }
+        else
+        {
+            ep = new EbmlParser( p_sys->es, cues );
+            while( ( el = ep->Get() ) != NULL )
+            {
+                if( EbmlId( *el ) == KaxCuePoint::ClassInfos.GlobalId )
+                {
+#define idx p_sys->index[p_sys->i_index]
+
+                    idx.i_track       = -1;
+                    idx.i_block_number= -1;
+                    idx.i_position    = -1;
+                    idx.i_time        = -1;
+                    idx.b_key         = VLC_TRUE;
+
+                    ep->Down();
+                    while( ( el = ep->Get() ) != NULL )
+                    {
+                        if( EbmlId( *el ) == KaxCueTime::ClassInfos.GlobalId )
+                        {
+                            KaxCueTime &ctime = *(KaxCueTime*)el;
+
+                            ctime.ReadData( p_sys->es->I_O() );
+
+                            idx.i_time = uint64( ctime ) * (mtime_t)1000000000 / p_sys->i_timescale;
+                        }
+                        else if( EbmlId( *el ) == KaxCueTrackPositions::ClassInfos.GlobalId )
+                        {
+                            ep->Down();
+                            while( ( el = ep->Get() ) != NULL )
+                            {
+                                if( EbmlId( *el ) == KaxCueTrack::ClassInfos.GlobalId )
+                                {
+                                    KaxCueTrack &ctrack = *(KaxCueTrack*)el;
+
+                                    ctrack.ReadData( p_sys->es->I_O() );
+                                    idx.i_track = uint16( ctrack );
+                                }
+                                else if( EbmlId( *el ) == KaxCueClusterPosition::ClassInfos.GlobalId )
+                                {
+                                    KaxCueClusterPosition &ccpos = *(KaxCueClusterPosition*)el;
+
+                                    ccpos.ReadData( p_sys->es->I_O() );
+                                    idx.i_position = p_sys->segment->GetGlobalPosition( uint64( ccpos ) );
+                                }
+                                else if( EbmlId( *el ) == KaxCueBlockNumber::ClassInfos.GlobalId )
+                                {
+                                    KaxCueBlockNumber &cbnum = *(KaxCueBlockNumber*)el;
+
+                                    cbnum.ReadData( p_sys->es->I_O() );
+                                    idx.i_block_number = uint32( cbnum );
+                                }
+                                else
+                                {
+                                    msg_Dbg( p_input, "         * Unknow (%s)", typeid(*el).name() );
+                                }
+                            }
+                            ep->Up();
+                        }
+                        else
+                        {
+                            msg_Dbg( p_input, "     * Unknow (%s)", typeid(*el).name() );
+                        }
+                    }
+                    ep->Up();
+
+                    msg_Dbg( p_input, " * added time=%lld pos=%lld track=%d bnum=%d",
+                             idx.i_time, idx.i_position, idx.i_track, idx.i_block_number );
+
+                    p_sys->i_index++;
+                    if( p_sys->i_index >= p_sys->i_index_max )
+                    {
+                        p_sys->i_index_max += 1024;
+                        p_sys->index = (mkv_index_t*)realloc( p_sys->index, sizeof( mkv_index_t ) * p_sys->i_index_max );
+                    }
+#undef idx
+                }
+                else
+                {
+                    msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
+                }
+            }
+            delete ep;
+            delete cues;
+
+            p_sys->b_cues = VLC_TRUE;
+        }
+
+        msg_Dbg( p_input, "loading cues done." );
+        p_sys->in->setFilePointer( i_sav_position, seek_beginning );
+    }
+
+    if( !p_sys->b_cues || p_sys->i_index <= 0 )
+    {
+        msg_Warn( p_input, "no cues/empty cues found -> seek won't be precise" );
+
+        IndexAppendCluster( p_input, p_sys->cluster );
+
+        p_sys->b_cues = VLC_FALSE;
+    }
+
     /* Create one program */
     vlc_mutex_lock( &p_input->stream.stream_lock );
     if( input_InitStream( p_input, 0 ) == -1)
@@ -862,7 +1171,15 @@ static int Activate( vlc_object_t * p_this )
         goto error;
     }
     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
-    p_input->stream.i_mux_rate = 0;
+    if( p_sys->f_duration > 1001.0 )
+    {
+        mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000.0 );
+        p_input->stream.i_mux_rate = p_input->stream.p_selected_area->i_size / 50 / i_duration;
+    }
+    else
+    {
+        p_input->stream.i_mux_rate = 0;
+    }
     vlc_mutex_unlock( &p_input->stream.stream_lock );
 
     /* add all es */
@@ -883,7 +1200,7 @@ static int Activate( vlc_object_t * p_this )
                                tk.psz_language, 0 );
         if( !strcmp( tk.psz_codec, "V_MS/VFW/FOURCC" ) )
         {
-            if( tk.i_extra_data < sizeof( BITMAPINFOHEADER ) )
+            if( tk.i_extra_data < (int)sizeof( BITMAPINFOHEADER ) )
             {
                 msg_Err( p_input, "missing/invalid BITMAPINFOHEADER" );
                 tk.i_codec = VLC_FOURCC( 'u', 'n', 'd', 'f' );
@@ -938,7 +1255,7 @@ static int Activate( vlc_object_t * p_this )
         }
         else if( !strcmp( tk.psz_codec, "A_MS/ACM" ) )
         {
-            if( tk.i_extra_data < sizeof( WAVEFORMATEX ) )
+            if( tk.i_extra_data < (int)sizeof( WAVEFORMATEX ) )
             {
                 msg_Err( p_input, "missing/invalid WAVEFORMATEX" );
                 tk.i_codec = VLC_FOURCC( 'u', 'n', 'd', 'f' );
@@ -947,13 +1264,13 @@ static int Activate( vlc_object_t * p_this )
             {
                 WAVEFORMATEX *p_wf = (WAVEFORMATEX*)tk.p_extra_data;
 
-                p_wf->wFormatTag        = GetWLE( p_wf->wFormatTag );
-                p_wf->nChannels         = GetWLE( p_wf->nChannels );
-                p_wf->nSamplesPerSec    = GetDWLE( p_wf->nSamplesPerSec );
-                p_wf->nAvgBytesPerSec   = GetDWLE( p_wf->nAvgBytesPerSec );
-                p_wf->nBlockAlign       = GetWLE( p_wf->nBlockAlign );
-                p_wf->wBitsPerSample    = GetWLE( p_wf->wBitsPerSample );
-                p_wf->cbSize            = GetWLE( p_wf->cbSize );
+                p_wf->wFormatTag        = GetWLE( &p_wf->wFormatTag );
+                p_wf->nChannels         = GetWLE( &p_wf->nChannels );
+                p_wf->nSamplesPerSec    = GetDWLE( &p_wf->nSamplesPerSec );
+                p_wf->nAvgBytesPerSec   = GetDWLE( &p_wf->nAvgBytesPerSec );
+                p_wf->nBlockAlign       = GetWLE( &p_wf->nBlockAlign );
+                p_wf->wBitsPerSample    = GetWLE( &p_wf->wBitsPerSample );
+                p_wf->cbSize            = GetWLE( &p_wf->cbSize );
 
                 switch( p_wf->wFormatTag )
                 {
@@ -1210,159 +1527,38 @@ static void Deactivate( vlc_object_t *p_this )
     free( p_sys );
 }
 
-/*****************************************************************************
- * Demux: reads and demuxes data packets
- *****************************************************************************
- * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
- *****************************************************************************/
-static int Demux( input_thread_t * p_input )
+static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_ref1, int64_t *pi_ref2, int64_t *pi_duration )
 {
     demux_sys_t    *p_sys   = p_input->p_demux_data;
-    mtime_t        i_start_pts = p_sys->i_last_pts;
-    KaxBlock *block = NULL;
-    int64_t i_block_duration = -1;
-    int64_t i_block_ref1 = 0;
-    int64_t i_block_ref2 = 0;
 
-    if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
-    {
-        msg_Err( p_input, "DO NO SEEK YET" );
-    }
+    *pp_block = NULL;
+    *pi_ref1  = -1;
+    *pi_ref2  = -1;
 
     for( ;; )
     {
         EbmlElement *el;
         int         i_level;
 
+        if( p_input->b_die )
+        {
+            return VLC_EGENERIC;
+        }
+
         el = p_sys->ep->Get();
         i_level = p_sys->ep->GetLevel();
 
-        /* First send last collected blocks (before ep->Up, because of cluster) */
-        if( el == NULL && block != NULL )
+        if( el == NULL && *pp_block != NULL )
         {
-            /* we have data */
-            int i_track;
-#define tk  p_sys->track[i_track]
-            for( i_track = 0; i_track < p_sys->i_track; i_track++ )
-            {
-                if( tk.i_number == block->TrackNum() )
-                {
-                    break;
-                }
-            }
-
-            p_sys->i_last_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->tc_scale;
-
-            if( p_sys->i_last_pts > 0 )
-            {
-                input_ClockManageRef( p_input,
-                                      p_input->stream.p_selected_program,
-                                      p_sys->i_last_pts * 9 / 100 );
-            }
-
-            if( i_track >= p_sys->i_track )
-            {
-                msg_Err( p_input, "invalid track number=%d", block->TrackNum() );
-            }
-            else if( tk.p_es->p_decoder_fifo != NULL )
-            {
-                pes_packet_t  *p_pes;
-                int i;
-
-                if( ( p_pes = input_NewPES( p_input->p_method_data ) ) != NULL )
-                {
-                    mtime_t i_pts;
-
-                    i_pts = input_ClockGetTS( p_input,
-                                              p_input->stream.p_selected_program,
-                                              p_sys->i_last_pts * 9 / 100 );
-
-                    p_pes->i_pts = i_pts;
-                    p_pes->i_dts = i_pts;
-                    if( tk.i_cat == SPU_ES )
-                    {
-                        /* special case : dts mean end of display */
-                        if( i_block_duration > 0 )
-                        {
-                            /* FIXME not sure about that */
-                            p_pes->i_dts += i_block_duration * 1000;// * (mtime_t) 1000 / p_sys->tc_scale;
-                        }
-                        else
-                        {
-                            /* unknown */
-                            p_pes->i_dts = 0;
-                        }
-                    }
-
-                    p_pes->p_first = p_pes->p_last = NULL;
-                    p_pes->i_nb_data = 0;
-                    p_pes->i_pes_size = 0;
-
-                    for( i = 0; i < block->NumberFrames(); i++ )
-                    {
-                        data_packet_t *p_data = NULL;
-                        DataBuffer &data = block->GetBuffer(i);
-
-                        if( ( p_data = input_NewPacket( p_input->p_method_data, data.Size() ) ) != NULL )
-                        {
-                            memcpy( p_data->p_payload_start, data.Buffer(), data.Size() );
-
-                            p_data->p_payload_end = p_data->p_payload_start + data.Size();
-
-                            if( p_pes->p_first == NULL )
-                            {
-                                p_pes->p_first = p_data;
-                            }
-                            else
-                            {
-                                p_pes->p_last->p_next  = p_data;
-                            }
-                            p_pes->i_nb_data++;
-                            p_pes->i_pes_size += data.Size();
-                            p_pes->p_last  = p_data;
-                        }
-                    }
-                    if( tk.i_cat == SPU_ES && p_pes->p_first && p_pes->i_pes_size > 0 )
-                    {
-                        p_pes->p_first->p_payload_end[-1] = '\0';
-                    }
-
-                    if( !tk.b_inited && tk.i_data_init > 0 )
-                    {
-                        pes_packet_t *p_init;
-                        data_packet_t *p_data;
-
-                        msg_Dbg( p_input, "sending header (%d bytes)", tk.i_data_init );
-
-                        p_init = input_NewPES( p_input->p_method_data );
-
-                        p_data = input_NewPacket( p_input->p_method_data, tk.i_data_init );
-                        memcpy( p_data->p_payload_start, tk.p_data_init, tk.i_data_init );
-                        p_data->p_payload_end = p_data->p_payload_start + tk.i_data_init;
-
-                        p_init->p_first = p_init->p_last = p_data;
-                        p_init->i_nb_data = 1;
-                        p_init->i_pes_size = tk.i_data_init;
-
-                        input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
-                    }
-                    tk.b_inited = VLC_TRUE;
-
-                    input_DecodePES( tk.p_es->p_decoder_fifo, p_pes );
-                }
-                else
-                {
-                    tk.b_inited = VLC_FALSE;
-                }
-            }
-#undef tk
-            delete block;
-            block = NULL;
-
-            if( p_sys->i_last_pts > i_start_pts + (mtime_t)100000)
+            /* update the index */
+#define idx p_sys->index[p_sys->i_index - 1]
+            if( p_sys->i_index > 0 && idx.i_time == -1 )
             {
-                return 1;
+                idx.i_time        = (*pp_block)->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
+                idx.b_key         = *pi_ref1 == -1 ? VLC_TRUE : VLC_FALSE;
             }
+#undef idx
+            return VLC_SUCCESS;
         }
 
         if( el == NULL )
@@ -1373,7 +1569,7 @@ static int Demux( input_thread_t * p_input )
                 continue;
             }
             msg_Warn( p_input, "EOF" );
-            return 0;
+            return VLC_EGENERIC;
         }
 
         /* do parsing */
@@ -1382,12 +1578,20 @@ static int Demux( input_thread_t * p_input )
             if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
             {
                 p_sys->cluster = (KaxCluster*)el;
+
+                /* add it to the index */
+                if( p_sys->i_index == 0 ||
+                    ( p_sys->i_index > 0 && p_sys->index[p_sys->i_index - 1].i_position < p_sys->cluster->GetElementPosition() ) )
+                {
+                    IndexAppendCluster( p_input, p_sys->cluster );
+                }
+
                 p_sys->ep->Down();
             }
             else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
             {
                 msg_Warn( p_input, "find KaxCues FIXME" );
-                return 0;
+                return VLC_EGENERIC;
             }
             else
             {
@@ -1401,8 +1605,7 @@ static int Demux( input_thread_t * p_input )
                 KaxClusterTimecode &ctc = *(KaxClusterTimecode*)el;
 
                 ctc.ReadData( p_sys->es->I_O() );
-                p_sys->i_cluster_tc = uint64( ctc );
-                p_sys->cluster->InitTimecode( p_sys->i_cluster_tc, p_sys->tc_scale );
+                p_sys->cluster->InitTimecode( uint64( ctc ), p_sys->i_timescale );
             }
             else if( EbmlId( *el ) == KaxBlockGroup::ClassInfos.GlobalId )
             {
@@ -1413,10 +1616,10 @@ static int Demux( input_thread_t * p_input )
         {
             if( EbmlId( *el ) == KaxBlock::ClassInfos.GlobalId )
             {
-                block = (KaxBlock*)el;
+                *pp_block = (KaxBlock*)el;
 
-                block->ReadData( p_sys->es->I_O() );
-                block->SetParent( *p_sys->cluster );
+                (*pp_block)->ReadData( p_sys->es->I_O() );
+                (*pp_block)->SetParent( *p_sys->cluster );
 
                 p_sys->ep->Keep();
             }
@@ -1425,30 +1628,405 @@ static int Demux( input_thread_t * p_input )
                 KaxBlockDuration &dur = *(KaxBlockDuration*)el;
 
                 dur.ReadData( p_sys->es->I_O() );
-                i_block_duration = uint64( dur );
+                *pi_duration = uint64( dur );
             }
             else if( EbmlId( *el ) == KaxReferenceBlock::ClassInfos.GlobalId )
             {
                 KaxReferenceBlock &ref = *(KaxReferenceBlock*)el;
 
                 ref.ReadData( p_sys->es->I_O() );
-                if( i_block_ref1 == 0 )
+                if( *pi_ref1 == -1 )
                 {
-                    i_block_ref1 = int64( ref );
+                    *pi_ref1 = int64( ref );
                 }
                 else
                 {
-                    i_block_ref2 = int64( ref );
+                    *pi_ref2 = int64( ref );
                 }
             }
         }
         else
         {
             msg_Err( p_input, "invalid level = %d", i_level );
-            return 0;
+            return VLC_EGENERIC;
+        }
+    }
+}
+
+static pes_packet_t *MemToPES( input_thread_t *p_input, uint8_t *p_mem, int i_mem )
+{
+    pes_packet_t *p_pes;
+    data_packet_t *p_data;
+
+    if( ( p_pes = input_NewPES( p_input->p_method_data ) ) == NULL )
+    {
+        return NULL;
+    }
+
+    p_data = input_NewPacket( p_input->p_method_data, i_mem);
+
+    memcpy( p_data->p_payload_start, p_mem, i_mem );
+    p_data->p_payload_end = p_data->p_payload_start + i_mem;
+
+    p_pes->p_first = p_pes->p_last = p_data;
+    p_pes->i_nb_data = 1;
+    p_pes->i_pes_size = i_mem;
+
+    return p_pes;
+}
+
+static void BlockDecode( input_thread_t *p_input, KaxBlock *block, mtime_t i_pts, mtime_t i_duration )
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+
+    int             i_track;
+    unsigned int    i;
+
+#define tk  p_sys->track[i_track]
+    for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+    {
+        if( tk.i_number == block->TrackNum() )
+        {
+            break;
+        }
+    }
+
+    if( i_track >= p_sys->i_track )
+    {
+        msg_Err( p_input, "invalid track number=%d", block->TrackNum() );
+        return;
+    }
+
+    if( tk.p_es->p_decoder_fifo == NULL )
+    {
+        tk.b_inited = VLC_FALSE;
+        return;
+    }
+
+    /* First send init data */
+    if( !tk.b_inited && tk.i_data_init > 0 )
+    {
+        pes_packet_t *p_init;
+
+        msg_Dbg( p_input, "sending header (%d bytes)", tk.i_data_init );
+
+        if( tk.i_codec == VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
+        {
+            int i;
+            int i_offset = 1;
+            int i_size[3];
+
+            /* XXX hack split the 3 headers */
+            if( tk.p_data_init[0] != 0x02 )
+            {
+                msg_Err( p_input, "invalid vorbis header" );
+            }
+
+            for( i = 0; i < 2; i++ )
+            {
+                i_size[i] = 0;
+                while( i_offset < tk.i_data_init )
+                {
+                    i_size[i] += tk.p_data_init[i_offset];
+                    if( tk.p_data_init[i_offset++] != 0xff )
+                    {
+                        break;
+                    }
+                }
+            }
+            i_size[0] = __MIN( i_size[0], tk.i_data_init - i_offset );
+            i_size[1] = __MIN( i_size[1], tk.i_data_init - i_offset - i_size[0] );
+            i_size[2] = tk.i_data_init - i_offset - i_size[0] - i_size[1];
+
+            p_init = MemToPES( p_input, &tk.p_data_init[i_offset], i_size[0] );
+            if( p_init )
+            {
+                input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
+            }
+            p_init = MemToPES( p_input, &tk.p_data_init[i_offset+i_size[0]], i_size[1] );
+            if( p_init )
+            {
+                input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
+            }
+            p_init = MemToPES( p_input, &tk.p_data_init[i_offset+i_size[0]+i_size[1]], i_size[2] );
+            if( p_init )
+            {
+                input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
+            }
+        }
+        else
+        {
+            p_init = MemToPES( p_input, tk.p_data_init, tk.i_data_init );
+            if( p_init )
+            {
+                input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
+            }
+        }
+    }
+    tk.b_inited = VLC_TRUE;
+
+
+    for( i = 0; i < block->NumberFrames(); i++ )
+    {
+        pes_packet_t *p_pes;
+        DataBuffer &data = block->GetBuffer(i);
+
+        p_pes = MemToPES( p_input, data.Buffer(), data.Size() );
+        if( p_pes == NULL )
+        {
+            break;
+        }
+
+        p_pes->i_pts = i_pts;
+        p_pes->i_dts = i_pts;
+
+        if( tk.i_cat == SPU_ES )
+        {
+            if( i_duration > 0 )
+            {
+                /* FIXME not sure about that */
+                p_pes->i_dts += i_duration * 1000;// * (mtime_t) 1000 / p_sys->i_timescale;
+            }
+            else
+            {
+                p_pes->i_dts = 0;
+            }
+            if( p_pes->p_first && p_pes->i_pes_size > 0 )
+            {
+                p_pes->p_first->p_payload_end[-1] = '\0';
+            }
+        }
+
+        input_DecodePES( tk.p_es->p_decoder_fifo, p_pes );
+
+        /* use time stamp only for first block */
+        i_pts = 0;
+    }
+
+#undef tk
+}
+
+static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent)
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+
+    KaxBlock    *block;
+    int64_t     i_block_duration;
+    int64_t     i_block_ref1;
+    int64_t     i_block_ref2;
+
+    int         i_index;
+    int         i_track_skipping;
+    int         i_track;
+
+    msg_Dbg( p_input, "seek request to %lld (%d%%)", i_date, i_percent );
+    if( i_date < 0 && i_percent < 0 )
+    {
+        return;
+    }
+
+    delete p_sys->ep;
+    p_sys->ep = new EbmlParser( p_sys->es, p_sys->segment );
+    p_sys->cluster = NULL;
+
+    /* seek without index or without date */
+    if( !p_sys->b_cues || i_date < 0 )
+    {
+        int64_t i_pos = i_percent * p_input->stream.p_selected_area->i_size / 100;
+
+        msg_Warn( p_input, "imprecise way of seeking" );
+        for( i_index = 0; i_index < p_sys->i_index; i_index++ )
+        {
+            if( p_sys->index[i_index].i_position >= i_pos)
+            {
+                break;
+            }
+        }
+        if( i_index == p_sys->i_index )
+        {
+            i_index--;
+        }
+
+        p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
+
+        if( p_sys->index[i_index].i_position < i_pos )
+        {
+            EbmlElement *el;
+
+            msg_Warn( p_input, "searching for cluster, could take some time" );
+
+            /* search a cluster */
+            while( ( el = p_sys->ep->Get() ) != NULL )
+            {
+                if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
+                {
+                    KaxCluster *cluster = (KaxCluster*)el;
+
+                    /* add it to the index */
+                    IndexAppendCluster( p_input, cluster );
+
+                    if( cluster->GetElementPosition() >= i_pos )
+                    {
+                        p_sys->cluster = cluster;
+                        p_sys->ep->Down();
+                        break;
+                    }
+                }
+            }
         }
     }
+    else
+    {
+        for( i_index = 0; i_index < p_sys->i_index; i_index++ )
+        {
+            if( p_sys->index[i_index].i_time >= i_date )
+            {
+                break;
+            }
+        }
+
+        if( i_index > 0 )
+        {
+            i_index--;
+        }
+
+        msg_Dbg( p_input, "seek got %lld (%d%%)",
+                 p_sys->index[i_index].i_time, (int)(100 * p_sys->index[i_index].i_position /p_input->stream.p_selected_area->i_size ) );
+
+        p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
+    }
+
+    /* now parse until key frame */
+#define tk  p_sys->track[i_track]
+    i_track_skipping = 0;
+    for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+    {
+        if( tk.i_cat == VIDEO_ES )
+        {
+            tk.b_search_keyframe = VLC_TRUE;
+            i_track_skipping++;
+        }
+    }
+
+    while( i_track_skipping > 0 )
+    {
+        if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
+        {
+            msg_Warn( p_input, "cannot get block EOF?" );
 
+            return;
+        }
+
+        p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
+
+        for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+        {
+            if( tk.i_number == block->TrackNum() )
+            {
+                break;
+            }
+        }
+
+        if( i_track < p_sys->i_track )
+        {
+            if( tk.i_cat == VIDEO_ES && i_block_ref1 == -1 && tk.b_search_keyframe )
+            {
+                tk.b_search_keyframe = VLC_FALSE;
+                i_track_skipping--;
+            }
+            if( tk.i_cat == VIDEO_ES && !tk.b_search_keyframe )
+            {
+                BlockDecode( p_input, block, 0, 0 );
+            }
+        }
+
+        delete block;
+    }
+#undef tk
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux( input_thread_t * p_input )
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+    mtime_t        i_start_pts;
+    int            i_block_count = 0;
+
+    KaxBlock *block;
+    int64_t i_block_duration;
+    int64_t i_block_ref1;
+    int64_t i_block_ref2;
+
+    if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
+    {
+        mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
+        mtime_t i_date = -1;
+        int i_percent  = -1;
+
+        if( i_duration > 0 )
+        {
+            i_date = (mtime_t)1000000 *
+                     (mtime_t)i_duration*
+                     (mtime_t)p_sys->in->getFilePointer() /
+                     (mtime_t)p_input->stream.p_selected_area->i_size;
+        }
+        if( p_input->stream.p_selected_area->i_size > 0 )
+        {
+            i_percent = 100 * p_sys->in->getFilePointer() /
+                            p_input->stream.p_selected_area->i_size;
+        }
+
+        msg_Dbg( p_input, "call Seek" );
+        Seek( p_input, i_date, i_percent);
+        msg_Dbg( p_input, "Seek end" );
+    }
+
+    i_start_pts = -1;
+
+    for( ;; )
+    {
+        mtime_t i_pts;
+
+        if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
+        {
+            msg_Warn( p_input, "cannot get block EOF?" );
+
+            return 0;
+        }
+
+        p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
+
+        if( p_sys->i_pts > 0 )
+        {
+            input_ClockManageRef( p_input,
+                                  p_input->stream.p_selected_program,
+                                  p_sys->i_pts * 9 / 100 );
+        }
+
+        i_pts = input_ClockGetTS( p_input,
+                                  p_input->stream.p_selected_program,
+                                  p_sys->i_pts * 9 / 100 );
+
+
+
+        BlockDecode( p_input, block, i_pts, i_block_duration );
+
+        delete block;
+        i_block_count++;
+
+        if( i_start_pts == -1 )
+        {
+            i_start_pts = p_sys->i_pts;
+        }
+        else if( p_sys->i_pts > i_start_pts + (mtime_t)100000 || i_block_count > 5 )
+        {
+            return 1;
+        }
+    }
 }