+struct spoint
+{
+ spoint(unsigned int tk, mtime_t date, int64_t pos, int64_t cpos):
+ i_track(tk),i_date(date), i_seek_pos(pos),
+ i_cluster_pos(cpos), p_next(NULL){}
+ unsigned int i_track;
+ mtime_t i_date;
+ int64_t i_seek_pos;
+ int64_t i_cluster_pos;
+ spoint * p_next;
+};
+
+void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_global_position )
+{
+ KaxBlock *block;
+ KaxSimpleBlock *simpleblock;
+ int64_t i_block_duration;
+ size_t i_track;
+ int64_t i_seek_position = i_start_pos;
+ int64_t i_seek_time = i_start_time;
+ mtime_t i_pts = 0;
+ spoint *p_first = NULL;
+ spoint *p_last = NULL;
+ int i_cat;
+ bool b_has_key = false;
+
+ if( i_global_position >= 0 )
+ {
+ /* Special case for seeking in files with no cues */
+ EbmlElement *el = NULL;
+
+ /* Start from the last known index instead of the beginning eachtime */
+ if( i_index == 0)
+ es.I_O().setFilePointer( i_start_pos, seek_beginning );
+ else
+ es.I_O().setFilePointer( p_indexes[ i_index - 1 ].i_position,
+ seek_beginning );
+ delete ep;
+ ep = new EbmlParser( &es, segment, &sys.demuxer );
+ cluster = NULL;
+
+ while( ( el = ep->Get() ) != NULL )
+ {
+ if( MKV_IS_ID( el, KaxCluster ) )
+ {
+ cluster = (KaxCluster *)el;
+ i_cluster_pos = cluster->GetElementPosition();
+ if( i_index == 0 ||
+ ( i_index > 0 &&
+ p_indexes[i_index - 1].i_position < (int64_t)cluster->GetElementPosition() ) )
+ {
+ ParseCluster(false);
+ IndexAppendCluster( cluster );
+ }
+ if( es.I_O().getFilePointer() >= (unsigned) i_global_position )
+ break;
+ }
+ }
+ }
+
+#ifndef WIN32
+ /* Don't try complex seek if we seek to 0 */
+ if( i_date == 0 )
+ {
+ es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, 0 );
+ es_out_Control( sys.demuxer.out, ES_OUT_SET_PCR, VLC_TS_0 );
+ es.I_O().setFilePointer( i_start_pos );
+
+ delete ep;
+ ep = new EbmlParser( &es, segment, &sys.demuxer );
+ cluster = NULL;
+ sys.i_start_pts = 0;
+ sys.i_pts = 0;
+ sys.i_pcr = 0;
+ return;
+ }
+#endif
+
+ int i_idx = 0;
+ if ( i_index > 0 )
+ {
+
+ for( ; i_idx < i_index; i_idx++ )
+ if( p_indexes[i_idx].i_time + i_time_offset > i_date )
+ break;
+
+ if( i_idx > 0 )
+ i_idx--;
+
+ i_seek_position = p_indexes[i_idx].i_position;
+ i_seek_time = p_indexes[i_idx].i_time;
+ }
+
+ msg_Dbg( &sys.demuxer, "seek got %"PRId64" (%d%%)",
+ i_seek_time, (int)( 100 * i_seek_position / stream_Size( sys.demuxer.s ) ) );
+
+ es.I_O().setFilePointer( i_seek_position, seek_beginning );
+
+ delete ep;
+ ep = new EbmlParser( &es, segment, &sys.demuxer );
+ cluster = NULL;
+
+ sys.i_start_pts = i_date;
+
+ es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_date );
+
+ /* now parse until key frame */
+ const int es_types[3] = { VIDEO_ES, AUDIO_ES, SPU_ES };
+ i_cat = es_types[0];
+ for( int i = 0; i < 2; i_cat = es_types[++i] )
+ {
+ for( i_track = 0; i_track < tracks.size(); i_track++ )
+ {
+ if( tracks[i_track]->fmt.i_cat == i_cat )
+ {
+ spoint * seekpoint = new spoint(i_track, i_seek_time, i_seek_position, i_seek_position);
+ if( unlikely( !seekpoint ) )
+ {
+ for( spoint * sp = p_first; sp; )
+ {
+ spoint * tmp = sp;
+ sp = sp->p_next;
+ delete tmp;
+ }
+ return;
+ }
+ if( unlikely( !p_first ) )
+ {
+ p_first = seekpoint;
+ p_last = seekpoint;
+ }
+ else
+ {
+ p_last->p_next = seekpoint;
+ p_last = seekpoint;
+ }
+ }
+ }
+ if( likely( p_first ) )
+ break;
+ }
+ /*Neither video nor audio track... no seek further*/
+ if( unlikely( !p_first ) )
+ return;
+
+ for(;;)
+ {
+ while( i_pts < i_date )
+ {
+ bool b_key_picture;
+ bool b_discardable_picture;
+ if( BlockGet( block, simpleblock, &b_key_picture, &b_discardable_picture, &i_block_duration ) )
+ {
+ msg_Warn( &sys.demuxer, "cannot get block EOF?" );
+ return;
+ }
+
+ /* check if block's track is in our list */
+ for( i_track = 0; i_track < tracks.size(); i_track++ )
+ {
+ if( (simpleblock && tracks[i_track]->i_number == simpleblock->TrackNum()) ||
+ (block && tracks[i_track]->i_number == block->TrackNum()) )
+ break;
+ }
+
+ if( simpleblock )
+ i_pts = sys.i_chapter_time + simpleblock->GlobalTimecode() / (mtime_t) 1000;
+ else
+ i_pts = sys.i_chapter_time + block->GlobalTimecode() / (mtime_t) 1000;
+ if( i_track < tracks.size() )
+ {
+ if( tracks[i_track]->fmt.i_cat == i_cat && b_key_picture )
+ {
+ /* get the seekpoint */
+ spoint * sp;
+ for( sp = p_first; sp; sp = sp->p_next )
+ if( sp->i_track == i_track )
+ break;
+
+ sp->i_date = i_pts;
+ if( simpleblock )
+ sp->i_seek_pos = simpleblock->GetElementPosition();
+ else
+ sp->i_seek_pos = i_block_pos;
+ sp->i_cluster_pos = i_cluster_pos;
+ b_has_key = true;
+ }
+ }
+
+ delete block;
+ }
+ if( b_has_key || !i_idx )
+ break;
+
+ /* No key picture was found in the cluster seek to previous seekpoint */
+ i_date = i_time_offset + p_indexes[i_idx].i_time;
+ i_idx--;
+ i_pts = 0;
+ es.I_O().setFilePointer( p_indexes[i_idx].i_position );
+ delete ep;
+ ep = new EbmlParser( &es, segment, &sys.demuxer );
+ cluster = NULL;
+ }
+
+ /* rewind to the last I img */
+ spoint * p_min;
+ for( p_min = p_first, p_last = p_first; p_last; p_last = p_last->p_next )
+ if( p_last->i_date < p_min->i_date )
+ p_min = p_last;
+
+ sys.i_pcr = sys.i_pts = p_min->i_date;
+ es_out_Control( sys.demuxer.out, ES_OUT_SET_PCR, VLC_TS_0 + sys.i_pcr );
+ cluster = (KaxCluster *) ep->UnGet( p_min->i_seek_pos, p_min->i_cluster_pos );
+
+ /* hack use BlockGet to get the cluster then goto the wanted block */
+ if ( !cluster )
+ {
+ bool b_key_picture;
+ bool b_discardable_picture;
+ BlockGet( block, simpleblock, &b_key_picture, &b_discardable_picture, &i_block_duration );
+ delete block;
+ cluster = (KaxCluster *) ep->UnGet( p_min->i_seek_pos, p_min->i_cluster_pos );
+ }
+
+ while( p_first )
+ {
+ p_min = p_first;
+ p_first = p_first->p_next;
+ delete p_min;
+ }
+}
+