[vlc-devel] [PATCH 27/34] mkv: new way of seeking

Filip Roséen filip at videolabs.io
Fri May 6 19:09:06 CEST 2016


since we require two different ways of seeking depending on
DEMUX_SET_POSITION and DEMUX_SET_TIME has been recieved with precise
seeking or not, we now have two different seek functions within
matroska_segment_c.

The theory is quite simple:

    - matroska_segment_c::FastSeek is for future use

    - matroska_segment_c::Seek will find the lowest correct seekpoint
      for all our tracks combined, but set track.i_skip_until_fpos to
      the first block that needs to be decoded for a certain track.

      This means that decoding will start at the right location for
      every track, without us having to worry about decoding too much
      data for tracks that does not require such.
---
 modules/demux/mkv/matroska_segment.cpp | 227 +++++----------------------------
 modules/demux/mkv/matroska_segment.hpp |   5 +-
 modules/demux/mkv/virtual_segment.cpp  |  13 +-
 3 files changed, 45 insertions(+), 200 deletions(-)

diff --git a/modules/demux/mkv/matroska_segment.cpp b/modules/demux/mkv/matroska_segment.cpp
index ee1809f..732ca61 100644
--- a/modules/demux/mkv/matroska_segment.cpp
+++ b/modules/demux/mkv/matroska_segment.cpp
@@ -794,223 +794,56 @@ bool matroska_segment_c::LoadSeekHeadItem( const EbmlCallbacks & ClassInfos, int
     return true;
 }
 
-struct spoint
+void matroska_segment_c::FastSeek( mtime_t i_mk_date, mtime_t i_mk_time_offset )
 {
-    spoint(unsigned int tk, mtime_t mk_date, int64_t pos, int64_t cpos):
-        i_track(tk),i_mk_date(mk_date), i_seek_pos(pos),
-        i_cluster_pos(cpos){}
-    unsigned int     i_track;
-    mtime_t i_mk_date;
-    int64_t i_seek_pos;
-    int64_t i_cluster_pos;
-};
+    msg_Err( &sys.demuxer, "%s is not implemented in this patch", __func__ );
+    throw std::runtime_error( "" );
+}
 
-void matroska_segment_c::Seek( mtime_t i_mk_date, mtime_t i_mk_time_offset, int64_t i_global_position )
+void matroska_segment_c::Seek( mtime_t i_absolute_mk_date, mtime_t i_mk_time_offset )
 {
-    KaxBlock    *block;
-    KaxSimpleBlock *simpleblock;
-    int64_t     i_block_duration;
-    size_t      i_track;
-    int64_t     i_seek_position = i_start_pos;
-    mtime_t     i_mk_seek_time = i_mk_start_time;
-    mtime_t     i_mk_pts = 0;
-    int i_cat;
-    bool b_has_key = false;
-
-    for( size_t i = 0; i < tracks.size(); i++)
-        tracks[i]->i_last_dts = VLC_TS_INVALID;
-
-    if( i_global_position >= 0 )
-    {
-        /* Special case for seeking in files with no cues */
-        EbmlElement *el = NULL;
+    uint64_t i_seek_position = -1;
+    mtime_t  i_mk_seek_time  = -1;
 
-        /* Start from the last known index instead of the beginning eachtime */
-        if(index_idx() == 0)
-            es.I_O().setFilePointer( i_start_pos, seek_beginning );
-        else
-            es.I_O().setFilePointer( prev_index().i_position,
-                                     seek_beginning );
-
-        ep->reconstruct( &es, segment, &sys.demuxer );
-        cluster = NULL;
-
-        while( ( el = ep->Get() ) != NULL )
-        {
-            if( MKV_CHECKED_PTR_DECL ( kc_ptr, KaxCluster, el ) )
-            {
-                cluster = kc_ptr;
-                i_cluster_pos = cluster->GetElementPosition();
-                if( index_idx() == 0 ||
-                    ( prev_index().i_position < (int64_t)cluster->GetElementPosition() ) )
-                {
-                    ParseCluster( cluster, false, SCOPE_NO_DATA );
-                    IndexAppendCluster( cluster );
-                }
-                if( es.I_O().getFilePointer() >= static_cast<unsigned>( i_global_position ) )
-                    break;
-            }
-        }
+    mtime_t i_mk_date = i_absolute_mk_date - i_mk_time_offset;
 
-        std::sort( indexes_begin(), indexes_end() );
-    }
+    SegmentSeeker::tracks_seekpoint_t seekpoints = _seeker.get_seekpoints_cues( *this, i_mk_date );
 
-    /* Don't try complex seek if we seek to 0 */
-    if( i_mk_date == 0 && i_mk_time_offset == 0 )
+    for( SegmentSeeker::tracks_seekpoint_t::iterator it = seekpoints.begin(); it != seekpoints.end(); ++it )
     {
-        es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME,
-                        INT64_C(0) );
-        es.I_O().setFilePointer( i_start_pos );
-
-        ep->reconstruct( &es, segment, &sys.demuxer );
+        mkv_track_t& track = tracks[ it->first ];
 
-        cluster = NULL;
-        sys.i_start_pts = VLC_TS_0;
-        sys.i_pcr = sys.i_pts = VLC_TS_INVALID;
-        return;
-    }
-
-    indexes_t::const_iterator index_it = indexes_begin();
-
-    if ( index_idx() )
-    {
-        index_it = std::upper_bound (
-          indexes_begin(), indexes_end(), i_mk_date, SeekIndexFinder( i_mk_time_offset )
-        );
-
-        if (index_it != indexes_begin())
-            --index_it;
-
-        i_seek_position = index_it->i_position;
-        i_mk_seek_time  = index_it->i_mk_time;
-    }
-
-    msg_Dbg( &sys.demuxer, "seek got %" PRId64 " - %" PRId64, i_mk_seek_time, i_seek_position );
-
-    es.I_O().setFilePointer( i_seek_position, seek_beginning );
+        if( i_seek_position > it->second.fpos )
+        {
+            i_seek_position = it->second.fpos;
+            i_mk_seek_time  = it->second.pts;
+        }
 
-    ep->reconstruct( &es, segment, &sys.demuxer );
+        track.i_skip_until_fpos = it->second.fpos;
+        track.i_last_dts        = it->second.pts;
 
-    cluster = NULL;
 
-    sys.i_start_pts = i_mk_date + VLC_TS_0;
+        bool is_active;
 
-    /* now parse until key frame */
-    std::vector<spoint> spoints;
-    const int es_types[3] = { VIDEO_ES, AUDIO_ES, SPU_ES };
-    i_cat = es_types[0];
-    mtime_t i_seek_preroll = 0;
-    for( int i = 0; i < 2; i_cat = es_types[++i] )
-    {
-        for( i_track = 0; i_track < tracks.size(); i_track++ )
+        if( es_out_Control( sys.demuxer.out, ES_OUT_GET_ES_STATE, track.p_es, &is_active ) )
         {
-            if( tracks[i_track]->i_seek_preroll )
-            {
-                bool b_enabled;
-                if( es_out_Control( sys.demuxer.out,
-                                    ES_OUT_GET_ES_STATE,
-                                    tracks[i_track]->p_es,
-                                    &b_enabled ) == VLC_SUCCESS &&
-                    b_enabled )
-                    i_seek_preroll = __MAX( i_seek_preroll,
-                                            tracks[i_track]->i_seek_preroll );
-            }
-            if( tracks[i_track]->fmt.i_cat == i_cat )
-            {
-                spoints.push_back (
-                  spoint (i_track, i_mk_seek_time, i_seek_position, i_seek_position)
-                );
-            }
+            msg_Err( &sys.demuxer, "Unable to query track %u for ES_OUT_GET_ES_STATE", it->first );
         }
-        if ( likely( !spoints.empty() ) )
-            break;
-    }
-    /*Neither video nor audio track... no seek further*/
-    if( unlikely( spoints.empty() ) )
-    {
-        es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_mk_date );
-        return;
-    }
-    i_mk_date -= i_seek_preroll;
-    for(;;)
-    {
-        do
+        else if( !is_active )
         {
-            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;
-            }
-
-            if( simpleblock )
-                i_mk_pts = sys.i_mk_chapter_time + simpleblock->GlobalTimecode() / INT64_C(1000);
-            else
-                i_mk_pts = sys.i_mk_chapter_time + block->GlobalTimecode() / INT64_C(1000);
-
-            if( BlockFindTrackIndex( &i_track, block, simpleblock ) == VLC_SUCCESS )
-            {
-                if( tracks[i_track]->fmt.i_cat == i_cat && b_key_picture )
-                {
-                    /* get the seekpoint */
-                    std::vector<spoint>::iterator it;
-
-                    for ( it = spoints.begin (); it != spoints.end (); ++it )
-                        if (it->i_track == i_track)
-                            break;
-
-                    if (unlikely (it == spoints.end ()) ) {
-                        msg_Err( &sys.demuxer, "Unable to locate seekpoint using i_track = %zu!", i_track);
-                        return;
-                    }
-
-                    it->i_mk_date = i_mk_pts;
-                    if( simpleblock )
-                        it->i_seek_pos = simpleblock->GetElementPosition();
-                    else
-                        it->i_seek_pos = i_block_pos;
-                    it->i_cluster_pos = i_cluster_pos;
-                    b_has_key = true;
-                }
-            }
-
-            delete block;
-        } while( i_mk_pts < i_mk_date );
-        if( b_has_key || !index_idx())
-            break;
-
-        /* No key picture was found in the cluster seek to previous seekpoint */
-        i_mk_date = i_mk_time_offset + index_it->i_mk_time;
-        index_it--;
-        i_mk_pts = 0;
-        es.I_O().setFilePointer( index_it->i_position );
-        ep->reconstruct( &es, segment, &sys.demuxer );
-        cluster = NULL;
+            track.i_last_dts = VLC_TS_INVALID;
+        }
     }
 
-    /* rewind to the last I img */
-    std::vector<spoint>::const_iterator it;
-    std::vector<spoint>::const_iterator it_min = spoints.begin ();
+    _seeker.mkv_jump_to( *this, i_seek_position );
 
-    for (it = spoints.begin () + 1; it != spoints.end (); ++it)
-        if ( it->i_mk_date < it_min->i_mk_date )
-            it_min = it;
+    sys.i_pcr       = VLC_TS_INVALID;
+    sys.i_pts       = VLC_TS_0 + i_mk_seek_time + i_mk_time_offset;
+    sys.i_start_pts = VLC_TS_0 + i_absolute_mk_date;
 
-    sys.i_pts = it_min->i_mk_date + VLC_TS_0;
-    sys.i_pcr = VLC_TS_INVALID;
-    es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_mk_date );
-    cluster = static_cast<KaxCluster*>( ep->UnGet( it_min->i_seek_pos, it_min->i_cluster_pos ) );
+    es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, sys.i_start_pts );
 
-    /* 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 = static_cast<KaxCluster*>( ep->UnGet( it_min->i_seek_pos, it_min->i_cluster_pos ) );
-    }
+    msg_Dbg( &sys.demuxer, "seek got i_mk_date = % " PRId64 ", i_mk_seek_time = %" PRId64 ", i_seek_position = %" PRId64 ", i_absolute_mk_date = %" PRId64 ", i_mk_time_offset = %" PRId64, i_mk_date, i_mk_seek_time, i_seek_position, i_absolute_mk_date,  i_mk_time_offset );
 }
 
 
diff --git a/modules/demux/mkv/matroska_segment.hpp b/modules/demux/mkv/matroska_segment.hpp
index 5b44a47..4ee159a 100644
--- a/modules/demux/mkv/matroska_segment.hpp
+++ b/modules/demux/mkv/matroska_segment.hpp
@@ -138,10 +138,11 @@ public:
     bool PreloadFamily( const matroska_segment_c & segment );
     bool PreloadClusters( uint64 i_cluster_position );
     void InformationCreate();
-    void Seek( mtime_t i_mk_date, mtime_t i_mk_time_offset );
-    int BlockGet( KaxBlock * &, KaxSimpleBlock * &, bool *, bool *, int64_t *);
 
+    void FastSeek( mtime_t i_mk_date, mtime_t i_mk_time_offset );
+    void Seek( mtime_t i_mk_date, mtime_t i_mk_time_offset );
 
+    int BlockGet( KaxBlock * &, KaxSimpleBlock * &, bool *, bool *, int64_t *);
 
     int FindTrackByBlock(tracks_map_t::iterator* track_it, const KaxBlock *, const KaxSimpleBlock * );
 
diff --git a/modules/demux/mkv/virtual_segment.cpp b/modules/demux/mkv/virtual_segment.cpp
index 7b3d0f7..5038e0d 100644
--- a/modules/demux/mkv/virtual_segment.cpp
+++ b/modules/demux/mkv/virtual_segment.cpp
@@ -543,8 +543,19 @@ void virtual_segment_c::Seek( demux_t & demuxer, mtime_t i_mk_date,
         }
         else
         {
+            typedef void( matroska_segment_c::* seek_callback_t )( mtime_t, mtime_t );
+
+            seek_callback_t pf_seek = &matroska_segment_c::Seek;
+
+#if 0
+            /* disabled due to non-existing implementation */
+            if( ! b_precise )
+                pf_seek = &matroska_segment_c::FastSeek;
+#endif
+
             p_current_vchapter = p_vchapter;
-            p_current_vchapter->segment.Seek( i_mk_date, i_mk_time_offset, i_global_position );
+
+            ( p_current_vchapter->segment.*pf_seek )( i_mk_date, i_mk_time_offset );
         }
     }
 }
-- 
2.8.2



More information about the vlc-devel mailing list