MKV: Virtual segment rewrite
authorDenis Charmet <typx@dinauz.org>
Tue, 20 Sep 2011 22:33:58 +0000 (00:33 +0200)
committerJean-Baptiste Kempf <jb@videolan.org>
Wed, 21 Sep 2011 00:14:17 +0000 (02:14 +0200)
This should handle better ordered chapters and multi-file mkvs
(with --mkv-preload-local-dir)

Should close #3942 #4074 #3810 #4031 #4336 #4501.

Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
modules/demux/mkv/chapter_command.cpp
modules/demux/mkv/chapters.cpp
modules/demux/mkv/chapters.hpp
modules/demux/mkv/demux.cpp
modules/demux/mkv/demux.hpp
modules/demux/mkv/matroska_segment.cpp
modules/demux/mkv/matroska_segment_parse.cpp
modules/demux/mkv/mkv.cpp
modules/demux/mkv/virtual_segment.cpp
modules/demux/mkv/virtual_segment.hpp

index a220aee..67faad1 100644 (file)
@@ -172,7 +172,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
         return false;
 
     virtual_segment_c *p_segment = NULL;
-    chapter_item_c *p_chapter = NULL;
+    virtual_chapter_c *p_chapter = NULL;
     bool f_result = false;
     uint16 i_command = ( p_command[0] << 8 ) + p_command[1];
 
@@ -481,7 +481,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
             p_chapter = sys.p_current_segment->BrowseCodecPrivate( 1, MatchIsDomain, NULL, 0 );
             if ( p_chapter != NULL )
             {
-                int16 i_curr_title = p_chapter->GetTitleNumber( );
+                int16 i_curr_title = ( p_chapter->p_chapter )? p_chapter->p_chapter->GetTitleNumber() : 0;
                 if ( i_curr_title > 0 )
                 {
                     p_chapter = sys.BrowseCodecPrivate( 1, MatchVTSNumber, &i_curr_title, sizeof(i_curr_title), p_segment );
@@ -531,7 +531,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
             {
                 if ( !p_chapter->Enter( true ) )
                     // jump to the location in the found segment
-                    sys.p_current_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter, -1 );
+                    sys.p_current_segment->Seek( sys.demuxer, p_chapter->i_virtual_start_time, -1, p_chapter, -1 );
 
                 f_result = true;
             }
@@ -549,7 +549,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
             {
                 if ( !p_chapter->Enter( true ) )
                     // jump to the location in the found segment
-                    sys.p_current_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter, -1 );
+                    sys.p_current_segment->Seek( sys.demuxer, p_chapter->i_virtual_start_time, -1, p_chapter, -1 );
 
                 f_result = true;
             }
@@ -742,14 +742,14 @@ bool matroska_script_interpretor_c::Interpret( const binary * p_command, size_t
         int64_t i_chapter_uid = atoi( st.c_str() );
 
         virtual_segment_c *p_segment;
-        chapter_item_c *p_chapter = sys.FindChapter( i_chapter_uid, p_segment );
+        virtual_chapter_c *p_chapter = sys.FindChapter( i_chapter_uid, p_segment );
 
         if ( p_chapter == NULL )
             msg_Dbg( &sys.demuxer, "Chapter %"PRId64" not found", i_chapter_uid);
         else
         {
             if ( !p_chapter->EnterAndLeave( sys.p_current_segment->CurrentChapter() ) )
-                p_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter, -1 );
+                p_segment->Seek( sys.demuxer, p_chapter->i_virtual_start_time, -1, p_chapter, -1 );
             b_result = true;
         }
     }
index 6e5e091..0e14632 100644 (file)
 
 chapter_item_c::~chapter_item_c()
 {
+    if( p_segment_uid )
+        delete p_segment_uid;
+    if( p_segment_edition_uid )
+        delete p_segment_edition_uid;
     vlc_delete_all( codecs );
     vlc_delete_all( sub_chapters );
 }
 
-int chapter_item_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
-{
-    // add support for meta-elements from codec like DVD Titles
-    if ( !b_display_seekpoint || psz_name == "" )
-    {
-        psz_name = GetCodecName();
-        if ( psz_name != "" )
-            b_display_seekpoint = true;
-    }
-
-    if (b_display_seekpoint)
-    {
-        seekpoint_t *sk = vlc_seekpoint_New();
-
-        sk->i_level = i_level;
-        sk->i_time_offset = i_start_time;
-        sk->psz_name = strdup( psz_name.c_str() );
-
-        // A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value.
-        title.i_seekpoint++;
-        title.seekpoint = (seekpoint_t**)xrealloc( title.seekpoint,
-                                 title.i_seekpoint * sizeof( seekpoint_t* ) );
-        title.seekpoint[title.i_seekpoint-1] = sk;
-
-        if ( b_user_display )
-            i_user_chapters++;
-    }
-
-    for ( size_t i=0; i<sub_chapters.size() ; i++)
-    {
-        sub_chapters[i]->PublishChapters( title, i_user_chapters, i_level+1 );
-    }
-
-    i_seekpoint_num = i_user_chapters;
-
-    return i_user_chapters;
-}
-
 chapter_item_c *chapter_item_c::BrowseCodecPrivate( unsigned int codec_id,
                                     bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
                                     const void *p_cookie,
                                     size_t i_cookie_size )
 {
+    VLC_UNUSED( codec_id );
     // this chapter
     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
     while ( index != codecs.end() )
@@ -83,19 +50,7 @@ chapter_item_c *chapter_item_c::BrowseCodecPrivate( unsigned int codec_id,
             return this;
         ++index;
     }
-
-    // sub-chapters
-    chapter_item_c *p_result = NULL;
-    std::vector<chapter_item_c*>::const_iterator index2 = sub_chapters.begin();
-    while ( index2 != sub_chapters.end() )
-    {
-        p_result = (*index2)->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
-        if ( p_result != NULL )
-            return p_result;
-        ++index2;
-    }
-
-    return p_result;
+    return NULL;
 }
 
 void chapter_item_c::Append( const chapter_item_c & chapter )
@@ -116,9 +71,6 @@ void chapter_item_c::Append( const chapter_item_c & chapter )
             sub_chapters.push_back( chapter.sub_chapters[i] );
         }
     }
-
-    i_user_start_time = min( i_user_start_time, chapter.i_user_start_time );
-    i_user_end_time = max( i_user_end_time, chapter.i_user_end_time );
 }
 
 chapter_item_c * chapter_item_c::FindChapter( int64_t i_find_uid )
@@ -170,79 +122,6 @@ int16 chapter_item_c::GetTitleNumber( ) const
     return result;
 }
 
-int64_t chapter_item_c::RefreshChapters( bool b_ordered, int64_t i_prev_user_time )
-{
-    int64_t i_user_time = i_prev_user_time;
-
-    // first the sub-chapters, and then ourself
-    std::vector<chapter_item_c*>::iterator index = sub_chapters.begin();
-    while ( index != sub_chapters.end() )
-    {
-        i_user_time = (*index)->RefreshChapters( b_ordered, i_user_time );
-        ++index;
-    }
-
-    if ( b_ordered )
-    {
-        // the ordered chapters always start at zero
-        if ( i_prev_user_time == -1 )
-        {
-            if ( i_user_time == -1 )
-                i_user_time = 0;
-            i_prev_user_time = 0;
-        }
-
-        i_user_start_time = i_prev_user_time;
-        if ( i_end_time != -1 && i_user_time == i_prev_user_time )
-        {
-            i_user_end_time = i_user_start_time - i_start_time + i_end_time;
-        }
-        else
-        {
-            i_user_end_time = i_user_time;
-        }
-    }
-    else
-    {
-        if ( sub_chapters.begin() != sub_chapters.end() )
-            std::sort( sub_chapters.begin(), sub_chapters.end(), chapter_item_c::CompareTimecode );
-        i_user_start_time = i_start_time;
-        if ( i_end_time != -1 )
-            i_user_end_time = i_end_time;
-        else if ( i_user_time != -1 )
-            i_user_end_time = i_user_time;
-        else
-            i_user_end_time = i_user_start_time;
-    }
-
-    return i_user_end_time;
-}
-
-chapter_item_c *chapter_item_c::FindTimecode( mtime_t i_user_timecode, const chapter_item_c * p_current, bool & b_found )
-{
-    chapter_item_c *psz_result = NULL;
-
-    if ( p_current == this )
-        b_found = true;
-
-    if ( i_user_timecode >= i_user_start_time &&
-        ( i_user_timecode < i_user_end_time ||
-          ( i_user_start_time == i_user_end_time && i_user_timecode == i_user_end_time )))
-    {
-        std::vector<chapter_item_c*>::iterator index = sub_chapters.begin();
-        while ( index != sub_chapters.end() && ((p_current == NULL && psz_result == NULL) || (p_current != NULL && (!b_found || psz_result == NULL))))
-        {
-            psz_result = (*index)->FindTimecode( i_user_timecode, p_current, b_found );
-            ++index;
-        }
-
-        if ( psz_result == NULL )
-            psz_result = this;
-    }
-
-    return psz_result;
-}
-
 bool chapter_item_c::ParentOf( const chapter_item_c & item ) const
 {
     if ( &item == this )
@@ -360,32 +239,3 @@ std::string chapter_edition_c::GetMainName() const
     return "";
 }
 
-void chapter_edition_c::RefreshChapters( )
-{
-    chapter_item_c::RefreshChapters( b_ordered, -1 );
-    b_display_seekpoint = false;
-}
-
-mtime_t chapter_edition_c::Duration() const
-{
-    mtime_t i_result = 0;
-
-    if ( sub_chapters.size() )
-    {
-        std::vector<chapter_item_c*>::const_iterator index = sub_chapters.end();
-        --index;
-        i_result = (*index)->i_user_end_time;
-    }
-
-    return i_result;
-}
-
-chapter_item_c * chapter_edition_c::FindTimecode( mtime_t i_timecode, const chapter_item_c * p_current )
-{
-    if ( !b_ordered )
-        p_current = NULL;
-    bool b_found_current = false;
-    return chapter_item_c::FindTimecode( i_timecode, p_current, b_found_current );
-}
-
-
index b515b47..84ee4fa 100644 (file)
@@ -53,9 +53,8 @@ public:
     chapter_item_c()
     :i_start_time(0)
     ,i_end_time(-1)
-    ,i_user_start_time(-1)
-    ,i_user_end_time(-1)
-    ,i_seekpoint_num(-1)
+    ,p_segment_uid(NULL)
+    ,p_segment_edition_uid(NULL)
     ,b_display_seekpoint(true)
     ,b_user_display(false)
     ,p_parent(NULL)
@@ -63,13 +62,8 @@ public:
     {}
 
     virtual ~chapter_item_c();
-
-    int64_t RefreshChapters( bool b_ordered, int64_t i_prev_user_time );
-    int PublishChapters( input_title_t & title, int & i_user_chapters, int i_level );
-    virtual chapter_item_c * FindTimecode( mtime_t i_timecode, const chapter_item_c * p_current, bool & b_found );
     void Append( const chapter_item_c & edition );
     chapter_item_c * FindChapter( int64_t i_find_uid );
-
     virtual chapter_item_c *BrowseCodecPrivate( unsigned int codec_id,
                                     bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
                                     const void *p_cookie,
@@ -79,9 +73,9 @@ public:
     int16                       GetTitleNumber( ) const;
 
     int64_t                     i_start_time, i_end_time;
-    int64_t                     i_user_start_time, i_user_end_time; /* the time in the stream when an edition is ordered */
     std::vector<chapter_item_c*> sub_chapters;
-    int                         i_seekpoint_num;
+    KaxChapterSegmentUID        *p_segment_uid;
+    KaxChapterSegmentEditionUID *p_segment_edition_uid;
     int64_t                     i_uid;
     bool                        b_display_seekpoint;
     bool                        b_user_display;
@@ -91,12 +85,6 @@ public:
 
     std::vector<chapter_codec_cmds_c*> codecs;
 
-    static bool CompareTimecode( const chapter_item_c * itemA, const chapter_item_c * itemB )
-    {
-        return ( itemA->i_user_start_time < itemB->i_user_start_time ||
-                (itemA->i_user_start_time == itemB->i_user_start_time && itemA->i_user_end_time < itemB->i_user_end_time) );
-    }
-
     bool Enter( bool b_do_subchapters );
     bool Leave( bool b_do_subchapters );
     bool EnterAndLeave( chapter_item_c *p_item, bool b_enter = true );
@@ -105,16 +93,14 @@ public:
 class chapter_edition_c : public chapter_item_c
 {
 public:
-    chapter_edition_c()
-    :b_ordered(false)
+    chapter_edition_c(): b_ordered(false), b_default(false), b_hidden(false)
     {}
 
-    void RefreshChapters( );
-    mtime_t Duration() const;
     std::string GetMainName() const;
-    chapter_item_c * FindTimecode( mtime_t i_timecode, const chapter_item_c * p_current );
-
     bool                        b_ordered;
+    bool                        b_default;
+    /* TODO handle hidden chapters */
+    bool                        b_hidden;
 };
 
 #endif
index c79b788..ac399d4 100644 (file)
@@ -529,7 +529,6 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
                             b_keep_segment = (FindSegment( *p_uid ) == NULL);
                             if ( !b_keep_segment )
                                 break; // this segment is already known
-                            opened_segments.push_back( p_segment1 );
                             delete p_segment1->p_segment_uid;
                             p_segment1->p_segment_uid = new KaxSegmentUID(*p_uid);
                         }
@@ -547,6 +546,8 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
                             p_segment1->families.push_back( p_fam );
                         }
                     }
+                    if( b_keep_segment || !p_segment1->p_segment_uid )
+                        opened_segments.push_back( p_segment1 );
                     break;
                 }
             }
@@ -633,29 +634,15 @@ void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment )
 }
 
 // preload all the linked segments for all preloaded segments
-void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment )
+void demux_sys_t::PreloadLinked()
 {
-    size_t i_preloaded, i, j;
+    size_t i, j;
     virtual_segment_c *p_seg;
 
-    p_current_segment = VirtualFromSegments( p_segment );
+    p_current_segment = VirtualFromSegments( &opened_segments );
 
     used_segments.push_back( p_current_segment );
 
-    // create all the other virtual segments of the family
-    do {
-        i_preloaded = 0;
-        for ( i=0; i< opened_segments.size(); i++ )
-        {
-            if ( opened_segments[i]->b_preloaded && !IsUsedSegment( *opened_segments[i] ) )
-            {
-                p_seg = VirtualFromSegments( opened_segments[i] );
-                used_segments.push_back( p_seg );
-                i_preloaded++;
-            }
-        }
-    } while ( i_preloaded ); // worst case: will stop when all segments are found as family related
-
     // publish all editions of all usable segment
     for ( i=0; i< used_segments.size(); i++ )
     {
@@ -676,10 +663,8 @@ void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment )
                         p_title->psz_name = strdup( psz_tmp );
                 }
 
-                chapter_edition_c *p_edition = (*p_seg->Editions())[j];
-
                 i_chapters = 0;
-                p_edition->PublishChapters( *p_title, i_chapters, 0 );
+                ( *p_seg->Editions() )[j]->PublishChapters( *p_title, i_chapters, 0 );
             }
 
             // create a name if there is none
@@ -696,21 +681,9 @@ void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment )
     // TODO decide which segment should be first used (VMG for DVD)
 }
 
-bool demux_sys_t::IsUsedSegment( matroska_segment_c &segment ) const
+virtual_segment_c *demux_sys_t::VirtualFromSegments( std::vector<matroska_segment_c*> *p_segments ) const
 {
-    for ( size_t i=0; i< used_segments.size(); i++ )
-    {
-        if ( used_segments[i]->FindUID( *segment.p_segment_uid ) )
-            return true;
-    }
-    return false;
-}
-
-virtual_segment_c *demux_sys_t::VirtualFromSegments( matroska_segment_c *p_segment ) const
-{
-    virtual_segment_c *p_result = new virtual_segment_c( p_segment );
-    p_result->AddSegments( opened_segments );
-
+    virtual_segment_c *p_result = new virtual_segment_c( p_segments );
     return p_result;
 }
 
@@ -736,7 +709,7 @@ bool demux_sys_t::PreparePlayback( virtual_segment_c *p_new_segment )
     return true;
 }
 
-void demux_sys_t::JumpTo( virtual_segment_c & vsegment, chapter_item_c * p_chapter )
+void demux_sys_t::JumpTo( virtual_segment_c & vsegment, virtual_chapter_c * p_chapter )
 {
     // if the segment is not part of the current segment, select the new one
     if ( &vsegment != p_current_segment )
@@ -744,12 +717,12 @@ void demux_sys_t::JumpTo( virtual_segment_c & vsegment, chapter_item_c * p_chapt
         PreparePlayback( &vsegment );
     }
 
-    if ( p_chapter != NULL )
+    if ( p_chapter )
     {
-        if ( !p_chapter->Enter( true ) )
+        if ( !p_chapter->p_chapter || !p_chapter->p_chapter->Enter( true ) )
         {
             // jump to the location in the found segment
-            vsegment.Seek( demuxer, p_chapter->i_user_start_time, -1, p_chapter, -1 );
+            vsegment.Seek( demuxer, p_chapter->i_virtual_start_time, -1, p_chapter, -1 );
         }
     }
 
@@ -765,13 +738,13 @@ matroska_segment_c *demux_sys_t::FindSegment( const EbmlBinary & uid ) const
     return NULL;
 }
 
-chapter_item_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
+virtual_chapter_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
                                         bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
                                         const void *p_cookie,
                                         size_t i_cookie_size,
                                         virtual_segment_c * &p_segment_found )
 {
-    chapter_item_c *p_result = NULL;
+    virtual_chapter_c *p_result = NULL;
     for (size_t i=0; i<used_segments.size(); i++)
     {
         p_result = used_segments[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
@@ -784,9 +757,9 @@ chapter_item_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
     return p_result;
 }
 
-chapter_item_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c * & p_segment_found )
+virtual_chapter_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c * & p_segment_found )
 {
-    chapter_item_c *p_result = NULL;
+    virtual_chapter_c *p_result = NULL;
     for (size_t i=0; i<used_segments.size(); i++)
     {
         p_result = used_segments[i]->FindChapter( i_find_uid );
@@ -799,4 +772,3 @@ chapter_item_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c
     return p_result;
 }
 
-
index 39588ea..0497089 100644 (file)
@@ -371,18 +371,18 @@ public:
     float                   f_duration;
 
     matroska_segment_c *FindSegment( const EbmlBinary & uid ) const;
-    chapter_item_c *BrowseCodecPrivate( unsigned int codec_id,
+    virtual_chapter_c *BrowseCodecPrivate( unsigned int codec_id,
                                         bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
                                         const void *p_cookie,
                                         size_t i_cookie_size,
                                         virtual_segment_c * & p_segment_found );
-    chapter_item_c *FindChapter( int64_t i_find_uid, virtual_segment_c * & p_segment_found );
+    virtual_chapter_c *FindChapter( int64_t i_find_uid, virtual_segment_c * & p_segment_found );
 
     void PreloadFamily( const matroska_segment_c & of_segment );
-    void PreloadLinked( matroska_segment_c *p_segment );
+    void PreloadLinked();
     bool PreparePlayback( virtual_segment_c *p_new_segment );
     matroska_stream_c *AnalyseAllSegmentsFound( demux_t *p_demux, EbmlStream *p_estream, bool b_initial = false );
-    void JumpTo( virtual_segment_c & p_segment, chapter_item_c * p_chapter );
+    void JumpTo( virtual_segment_c & p_segment, virtual_chapter_c * p_chapter );
 
     void InitUi();
     void CleanUi();
@@ -396,8 +396,7 @@ public:
     event_thread_t *p_ev;
 
 protected:
-    virtual_segment_c *VirtualFromSegments( matroska_segment_c *p_segment ) const;
-    bool IsUsedSegment( matroska_segment_c &p_segment ) const;
+    virtual_segment_c *VirtualFromSegments( std::vector<matroska_segment_c*> *p_segments ) const;
 };
 
 
index efa2c55..c4cd2b7 100644 (file)
@@ -692,7 +692,7 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_
                 {
                     IndexAppendCluster( cluster );
                 }
-                if( es.I_O().getFilePointer() >= i_global_position )
+                if( es.I_O().getFilePointer() >= (unsigned) i_global_position )
                 {
                     ParseCluster();
                     msg_Dbg( &sys.demuxer, "we found a cluster that is in the neighbourhood" );
@@ -769,9 +769,9 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_
         }
 
         if( simpleblock )
-            sys.i_pts = (sys.i_chapter_time + simpleblock->GlobalTimecode()) / (mtime_t) 1000;
+            sys.i_pts = sys.i_chapter_time + simpleblock->GlobalTimecode() / (mtime_t) 1000;
         else
-            sys.i_pts = (sys.i_chapter_time + block->GlobalTimecode()) / (mtime_t) 1000;
+            sys.i_pts = sys.i_chapter_time + block->GlobalTimecode() / (mtime_t) 1000;
 
         if( i_track < tracks.size() )
         {
index 1a024ea..616c312 100644 (file)
@@ -855,6 +855,16 @@ void matroska_segment_c::ParseChapterAtom( int i_level, KaxChapterAtom *ca, chap
 
             msg_Dbg( &sys.demuxer, "|   |   |   |   + ChapterFlagHidden: %s", chapters.b_display_seekpoint ? "no":"yes" );
         }
+        else if( MKV_IS_ID( l, KaxChapterSegmentUID ) )
+        {
+            chapters.p_segment_uid = new KaxChapterSegmentUID( *static_cast<KaxChapterSegmentUID*>(l) );
+            msg_Dbg( &sys.demuxer, "|   |   |   |   + ChapterSegmentUID= %u", *(uint32*)chapters.p_segment_uid->GetBuffer() );
+        }
+        else if( MKV_IS_ID( l, KaxChapterSegmentEditionUID ) )
+        {
+            chapters.p_segment_edition_uid = new KaxChapterSegmentEditionUID( *static_cast<KaxChapterSegmentEditionUID*>(l) );
+            msg_Dbg( &sys.demuxer, "|   |   |   |   + ChapterSegmentEditionUID= %u", *(uint32*)chapters.p_segment_edition_uid->GetBuffer() );
+        }
         else if( MKV_IS_ID( l, KaxChapterTimeStart ) )
         {
             KaxChapterTimeStart &start =*(KaxChapterTimeStart*)l;
@@ -998,7 +1008,6 @@ void matroska_segment_c::ParseChapters( KaxChapters *chapters )
 {
     EbmlElement *el;
     int i_upper_level = 0;
-    mtime_t i_dur;
 
     /* Master elements */
     chapters->Read( es, EBML_CONTEXT(chapters), i_upper_level, el, true );
@@ -1052,19 +1061,6 @@ void matroska_segment_c::ParseChapters( KaxChapters *chapters )
             msg_Dbg( &sys.demuxer, "|   |   + Unknown (%s)", typeid(*l).name() );
         }
     }
-
-    for( size_t i = 0; i < stored_editions.size(); i++ )
-    {
-        stored_editions[i]->RefreshChapters( );
-    }
-
-    if ( stored_editions.size() != 0 && stored_editions[i_default_edition]->b_ordered )
-    {
-        /* update the duration of the segment according to the sum of all sub chapters */
-        i_dur = stored_editions[i_default_edition]->Duration() / INT64_C(1000);
-        if (i_dur > 0)
-            i_duration = i_dur;
-    }
 }
 
 void matroska_segment_c::ParseCluster( )
index 8b238b9..0d6e3a0 100644 (file)
@@ -34,6 +34,7 @@
 #include "stream_io_callback.hpp"
 
 #include <vlc_fs.h>
+#include <vlc_url.h>
 
 /*****************************************************************************
  * Module descriptor
@@ -76,7 +77,7 @@ class demux_sys_t;
 
 static int  Demux  ( demux_t * );
 static int  Control( demux_t *, int, va_list );
-static void Seek   ( demux_t *, mtime_t i_date, double f_percent, chapter_item_c *p_chapter );
+static void Seek   ( demux_t *, mtime_t i_date, double f_percent, virtual_chapter_c *p_chapter );
 
 /*****************************************************************************
  * Open: initializes matroska demux structures
@@ -141,7 +142,7 @@ static int Open( vlc_object_t * p_this )
     if (var_InheritBool( p_demux, "mkv-preload-local-dir" ))
     {
         /* get the files from the same dir from the same family (based on p_demux->psz_path) */
-        if (p_demux->psz_file && !strcmp(p_demux->psz_access, ""))
+        if ( p_demux->psz_file && !strcmp( p_demux->psz_access, "file" ) )
         {
             // assume it's a regular file
             // get the directory path
@@ -185,9 +186,10 @@ static int Open( vlc_object_t * p_this )
                             // test wether this file belongs to our family
                             const uint8_t *p_peek;
                             bool          file_ok = false;
+                            std::string   s_url = make_URI( s_filename.c_str(), "file" );
                             stream_t      *p_file_stream = stream_UrlNew(
                                                             p_demux,
-                                                            s_filename.c_str());
+                                                            s_url.c_str() );
                             /* peek the begining */
                             if( p_file_stream &&
                                 stream_Peek( p_file_stream, &p_peek, 4 ) >= 4
@@ -232,7 +234,7 @@ static int Open( vlc_object_t * p_this )
         p_sys->PreloadFamily( *p_segment );
     }
 
-    p_sys->PreloadLinked( p_segment );
+    p_sys->PreloadLinked();
 
     if ( !p_sys->PreparePlayback( NULL ) )
     {
@@ -266,7 +268,7 @@ static void Close( vlc_object_t *p_this )
 static int Control( demux_t *p_demux, int i_query, va_list args )
 {
     demux_sys_t        *p_sys = p_demux->p_sys;
-    int64_t     *pi64;
+    int64_t     *pi64, i64;
     double      *pf, f;
     int         i_skp;
     size_t      i_idx;
@@ -386,13 +388,17 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             return VLC_SUCCESS;
 
         case DEMUX_SET_TIME:
+            i64 = (int64_t) va_arg( args, int64_t );
+            msg_Dbg(p_demux,"SET_TIME to %"PRId64, i64 );
+            Seek( p_demux, i64, -1, NULL );
+            return VLC_SUCCESS;
         default:
             return VLC_EGENERIC;
     }
 }
 
 /* Seek */
-static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, chapter_item_c *p_chapter )
+static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, virtual_chapter_c *p_chapter )
 {
     demux_sys_t        *p_sys = p_demux->p_sys;
     virtual_segment_c  *p_vsegment = p_sys->p_current_segment;
@@ -516,7 +522,7 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
 
 
     for( unsigned int i = 0;
-         (block != NULL && i < block->NumberFrames()) || (simpleblock != NULL && i < simpleblock->NumberFrames());
+         ( block != NULL && i < block->NumberFrames()) || ( simpleblock != NULL && i < simpleblock->NumberFrames() );
          i++ )
     {
         block_t *p_block;
@@ -643,24 +649,11 @@ static int Demux( demux_t *p_demux)
                 break;
             }
 
-        if ( p_vsegment->CurrentEdition() && p_vsegment->CurrentEdition()->b_ordered && p_vsegment->CurrentChapter() == NULL )
-        {
+        if ( p_vsegment->CurrentEdition() &&
+             p_vsegment->CurrentEdition()->b_ordered &&
+             p_vsegment->CurrentChapter() == NULL )
             /* nothing left to read in this ordered edition */
-            if ( !p_vsegment->SelectNext() )
-                break;
-            p_segment->UnSelect( );
-
-            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
-
-            /* switch to the next segment */
-            p_segment = p_vsegment->CurrentSegment();
-            if ( !p_segment->Select( 0 ) )
-            {
-                msg_Err( p_demux, "Failed to select new segment" );
-                break;
-            }
-            continue;
-        }
+            break;
 
         KaxBlock *block;
         KaxSimpleBlock *simpleblock;
@@ -671,15 +664,13 @@ static int Demux( demux_t *p_demux)
         {
             if ( p_vsegment->CurrentEdition() && p_vsegment->CurrentEdition()->b_ordered )
             {
-                const chapter_item_c *p_chap = p_vsegment->CurrentChapter();
+                const virtual_chapter_c *p_chap = p_vsegment->CurrentChapter();
                 // check if there are more chapters to read
                 if ( p_chap != NULL )
                 {
                     /* TODO handle successive chapters with the same user_start_time/user_end_time
-                    if ( p_chap->i_user_start_time == p_chap->i_user_start_time )
-                        p_vsegment->SelectNext();
                     */
-                    p_sys->i_pts = p_chap->i_user_end_time;
+                    p_sys->i_pts = p_chap->i_virtual_stop_time;
                     p_sys->i_pts++; // trick to avoid staying on segments with no duration and no content
 
                     i_return = 1;
@@ -690,37 +681,25 @@ static int Demux( demux_t *p_demux)
             else
             {
                 msg_Warn( p_demux, "cannot get block EOF?" );
-                p_segment->UnSelect( );
+                p_segment->UnSelect();
 
                 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
-
-                /* switch to the next segment */
-                if ( !p_vsegment->SelectNext() )
-                    // no more segments in this stream
-                    break;
-                p_segment = p_vsegment->CurrentSegment();
-                if ( !p_segment->Select( 0 ) )
-                {
-                    msg_Err( p_demux, "Failed to select new segment" );
-                    break;
-                }
-
-                continue;
+                break;
             }
         }
 
         if( simpleblock != NULL )
-            p_sys->i_pts = (p_sys->i_chapter_time + simpleblock->GlobalTimecode()) / (mtime_t) 1000;
+            p_sys->i_pts = p_sys->i_chapter_time + ( simpleblock->GlobalTimecode() / (mtime_t) 1000 );
         else
-            p_sys->i_pts = (p_sys->i_chapter_time + block->GlobalTimecode()) / (mtime_t) 1000;
+            p_sys->i_pts = p_sys->i_chapter_time + ( block->GlobalTimecode() / (mtime_t) 1000 );
 
         /* The blocks are in coding order so we can safely consider that only references are in chronological order */
-        if( b_key_picture )
+        if( simpleblock == NULL || b_key_picture )
             es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pts );
 
         if( p_sys->i_pts >= p_sys->i_start_pts  )
         {
-            if ( p_vsegment->UpdateCurrentToChapter( *p_demux ) && p_vsegment->CurrentEdition() && p_vsegment->CurrentEdition()->b_ordered )
+            if ( p_vsegment->UpdateCurrentToChapter( *p_demux ) )
             {
                 i_return = 1;
                 delete block;
@@ -728,28 +707,13 @@ static int Demux( demux_t *p_demux)
             }
         }
 
-        if ( p_vsegment->CurrentEdition() && p_vsegment->CurrentEdition()->b_ordered && p_vsegment->CurrentChapter() == NULL )
+        if ( p_vsegment->CurrentEdition() &&
+             p_vsegment->CurrentEdition()->b_ordered &&
+             p_vsegment->CurrentChapter() == NULL )
         {
             /* nothing left to read in this ordered edition */
-            if ( !p_vsegment->SelectNext() )
-            {
-                delete block;
-                break;
-            }
-            p_segment->UnSelect( );
-
-            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
-
-            /* switch to the next segment */
-            p_segment = p_vsegment->CurrentSegment();
-            if ( !p_segment->Select( 0 ) )
-            {
-                msg_Err( p_demux, "Failed to select new segment" );
-                delete block;
-                break;
-            }
             delete block;
-            continue;
+            break;
         }
 
         BlockDecode( p_demux, block, simpleblock, p_sys->i_pts, i_block_duration, b_key_picture || b_discardable_picture );
index da65f18..5bf29a7 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
-
-#include "virtual_segment.hpp"
+#include <vector>
 
 #include "demux.hpp"
 
-void virtual_segment_c::PrepareChapters( )
+/* FIXME move this */
+matroska_segment_c * getSegmentbyUID( KaxSegmentUID * p_uid, std::vector<matroska_segment_c*> *segments )
 {
-    if ( linked_segments.size() == 0 )
-        return;
+    for( size_t i = 0; i < (*segments).size(); i++ )
+    {
+        if( *p_uid == *((*segments)[i]->p_segment_uid) )
+            return (*segments)[i];
+    }
+    return NULL;
+}
+
+virtual_chapter_c * virtual_chapter_c::CreateVirtualChapter( chapter_item_c * p_chap,
+                                                             matroska_segment_c * p_main_segment,
+                                                             std::vector<matroska_segment_c*> * segments,
+                                                             int64_t * usertime_offset, bool b_ordered)
+{
+    matroska_segment_c * p_segment = p_main_segment;
 
-    // !!! should be called only once !!!
-    matroska_segment_c *p_segment;
+    if( !p_chap )
+    {
+        /* Dummy chapter use the whole segment */
+        return new virtual_chapter_c( p_segment, NULL, 0, p_segment->i_duration*1000 );
+    }
 
-    // copy editions from the first segment
-    p_segment = linked_segments[0];
-    p_editions = &p_segment->stored_editions;
+    int64_t start = ( b_ordered )? *usertime_offset : p_chap->i_start_time;
+    int64_t stop = ( b_ordered )? ( *usertime_offset + p_chap->i_end_time - p_chap->i_start_time ) : p_chap->i_end_time;
 
-    for ( size_t i=1 ; i<linked_segments.size(); i++ )
+    if( p_chap->p_segment_uid && 
+       ( !( p_segment = getSegmentbyUID( (KaxSegmentUID*) p_chap->p_segment_uid,segments ) ) || !b_ordered ) )
     {
-        p_segment = linked_segments[i];
-        // FIXME assume we have the same editions in all segments
-        for ( size_t j=0; j<p_segment->stored_editions.size(); j++)
-        {
-            if( j >= p_editions->size() ) /* Protect against broken files (?) */
-                break;
-            (*p_editions)[j]->Append( *p_segment->stored_editions[j] );
-        }
+        msg_Warn( &p_main_segment->sys.demuxer,
+                  "Couldn't find segment 0x%x or not ordered... - ignoring chapter %s",
+                  *( (uint32_t *) p_chap->p_segment_uid->GetBuffer() ),p_chap->psz_name.c_str() );
+        return NULL;
     }
+
+    /* Preload segment */
+    if ( !p_segment->b_preloaded )
+        p_segment->Preload();
+
+    virtual_chapter_c * p_vchap = new virtual_chapter_c( p_segment, p_chap, start, stop );
+
+    if( !p_vchap )
+        return NULL;
+
+    int64_t tmp = *usertime_offset;
+
+    for( size_t i = 0; i < p_chap->sub_chapters.size(); i++ )
+    {
+        virtual_chapter_c * p_vsubchap = CreateVirtualChapter( p_chap->sub_chapters[i], p_segment, segments, &tmp, b_ordered );
+
+        if( p_vsubchap )
+            p_vchap->sub_chapters.push_back( p_vsubchap );
+    }
+
+    if( tmp == *usertime_offset )
+        *usertime_offset += p_chap->i_end_time - p_chap->i_start_time;
+    else
+        *usertime_offset = tmp;
+
+    msg_Dbg( &p_main_segment->sys.demuxer,
+             "Virtual chapter %s from %"PRId64" to %"PRId64" - segment 0x%x",
+             p_chap->psz_name.c_str(), p_vchap->i_virtual_start_time, p_vchap->i_virtual_stop_time,
+             *(uint32_t*)p_vchap->p_segment->p_segment_uid->GetBuffer() );
+
+    return p_vchap;
 }
 
-bool virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
+virtual_chapter_c::~virtual_chapter_c()
 {
-    demux_sys_t & sys = *demux.p_sys;
-    chapter_item_c *p_curr_chapter;
-    bool b_has_seeked = false;
+    for( size_t i = 0 ; i < sub_chapters.size(); i++ )
+        delete sub_chapters[i];
+}
 
-    /* update current chapter/seekpoint */
-    if ( p_editions->size() )
-    {
-        /* 1st, we need to know in which chapter we are */
-        p_curr_chapter = (*p_editions)[i_current_edition]->FindTimecode( sys.i_pts, p_current_chapter );
 
-        /* we have moved to a new chapter */
-        if (p_curr_chapter != NULL && p_current_chapter != p_curr_chapter)
+virtual_edition_c::virtual_edition_c( chapter_edition_c * p_edit, std::vector<matroska_segment_c*> *opened_segments)
+{
+    matroska_segment_c *p_main_segment = (*opened_segments)[0];
+    p_edition = p_edit;
+
+    int64_t usertime_offset = 0;
+
+    /* ordered chapters */
+    if( p_edition && p_edition->b_ordered )
+    {
+        b_ordered = true;
+        for( size_t i = 0; i < p_edition->sub_chapters.size(); i++ )
         {
-            if ( (*p_editions)[i_current_edition]->b_ordered )
+            virtual_chapter_c * p_vchap = virtual_chapter_c::CreateVirtualChapter( p_edition->sub_chapters[i],
+                                                                                   p_main_segment, opened_segments,
+                                                                                   &usertime_offset, b_ordered );
+            if( p_vchap )
+                chapters.push_back( p_vchap );
+        }
+        i_duration = chapters[ chapters.size() - 1 ]->i_virtual_stop_time;
+    }
+    else /* Not ordered or no edition at all */
+    {
+        b_ordered = false;
+        matroska_segment_c * p_cur = p_main_segment;
+        virtual_chapter_c * p_vchap = NULL;
+        int64_t tmp = 0;
+
+        /* check for prev linked segments */
+        /* FIXME to avoid infinite recursion we limit to 5 prev sould be better as parameter */
+        for( int limit = 0; limit < 5 && p_cur->p_prev_segment_uid ; limit++ )
+        {
+            matroska_segment_c * p_prev = NULL;
+            if( ( p_prev = getSegmentbyUID( p_cur->p_prev_segment_uid, opened_segments ) ) )
             {
-                // Leave/Enter up to the link point
-                b_has_seeked = p_curr_chapter->EnterAndLeave( p_current_chapter );
-                if ( !b_has_seeked )
-                {
-                    // only physically seek if necessary
-                    if ( p_current_chapter == NULL || (p_current_chapter->i_end_time != p_curr_chapter->i_start_time) )
-                        Seek( demux, sys.i_pts, 0, p_curr_chapter, -1 );
-                }
-            }
+                tmp = 0;
+                msg_Dbg( &p_main_segment->sys.demuxer, "Prev segment 0x%x found\n",
+                         *(int32_t*)p_cur->p_prev_segment_uid->GetBuffer() );
 
-            if ( !b_has_seeked )
-            {
-                p_current_chapter = p_curr_chapter;
-                if ( p_curr_chapter->i_seekpoint_num > 0 )
-                {
-                    demux.info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
-                    demux.info.i_title = sys.i_current_title = i_sys_title;
-                    demux.info.i_seekpoint = p_curr_chapter->i_seekpoint_num - 1;
-                }
-            }
+                /* Create virtual_chapter from the first edition if any */
+                chapter_item_c * p_chap = ( p_prev->stored_editions.size() > 0 )? ((chapter_item_c *)p_prev->stored_editions[0]) : NULL;
+
+                p_vchap = virtual_chapter_c::CreateVirtualChapter( p_chap, p_prev, opened_segments, &tmp, b_ordered );
+
+                if( p_vchap )
+                    chapters.insert( chapters.begin(), p_vchap );
 
-            return true;
+                p_cur = p_prev;
+            }
+            else /* segment not found */
+                break;
         }
-        else if (p_curr_chapter == NULL)
+
+        tmp = 0;
+
+        /* Append the main segment */
+        p_vchap = virtual_chapter_c::CreateVirtualChapter( (chapter_item_c*) p_edit, p_main_segment,
+                                                           opened_segments, &tmp, b_ordered );
+        if( p_vchap )
+            chapters.push_back( p_vchap );
+
+        /* Append next linked segments */
+        for( int limit = 0; limit < 5 && p_cur->p_next_segment_uid; limit++ )
         {
-            // out of the scope of the data described by chapters, leave the edition
-            if ( (*p_editions)[i_current_edition]->b_ordered && p_current_chapter != NULL )
+            matroska_segment_c * p_next = NULL;
+            if( ( p_next = getSegmentbyUID( p_cur->p_next_segment_uid, opened_segments ) ) )
             {
-                if ( !(*p_editions)[i_current_edition]->EnterAndLeave( p_current_chapter, false ) )
-                    p_current_chapter = NULL;
-                else
-                    return true;
+                tmp = 0;
+                msg_Dbg( &p_main_segment->sys.demuxer, "Next segment 0x%x found\n",
+                         *(int32_t*) p_cur->p_next_segment_uid->GetBuffer() );
+
+                /* Create virtual_chapter from the first edition if any */
+                chapter_item_c * p_chap = ( p_next->stored_editions.size() > 0 )?( (chapter_item_c *)p_next->stored_editions[0] ) : NULL;
+
+                 p_vchap = virtual_chapter_c::CreateVirtualChapter( p_chap, p_next, opened_segments, &tmp, b_ordered );
+
+                if( p_vchap )
+                    chapters.push_back( p_vchap );
+
+
+                p_cur = p_next;
             }
+            else /* segment not found */
+                break;
         }
+
+        /* Retime chapters */
+        retimeChapters();
     }
-    return false;
+
+#if MKV_DEBUG
+    msg_Dbg( &p_main_segment->sys.demuxer, "-- RECAP-BEGIN --" );
+    print();
+    msg_Dbg( &p_main_segment->sys.demuxer, "-- RECAP-END --" );
+#endif
+}
+
+virtual_edition_c::~virtual_edition_c()
+{
+    for( size_t i = 0; i < chapters.size(); i++ )
+        delete chapters[i];
+}
+
+void virtual_edition_c::retimeSubChapters( virtual_chapter_c * p_vchap )
+{
+    int64_t stop_time = p_vchap->i_virtual_stop_time;
+    for( size_t i = p_vchap->sub_chapters.size(); i-- > 0; )
+    {
+        virtual_chapter_c * p_vsubchap = p_vchap->sub_chapters[i];
+        p_vsubchap->i_virtual_start_time += p_vchap->i_virtual_start_time;
+
+        /*FIXME we artificially extend stop time if they were there before...*/
+        /* Just for comfort*/
+        p_vsubchap->i_virtual_stop_time = stop_time;
+        stop_time = p_vsubchap->i_virtual_start_time;
+
+        retimeSubChapters( p_vsubchap );
+    }
+}
+
+void virtual_edition_c::retimeChapters()
+{
+    /* This function is just meant to be used on unordered chapters */
+    if( b_ordered )
+        return;
+
+    i_duration = 0;
+
+    /* Sort by start time */
+    if( chapters.size() > 1 )
+        std::sort( chapters.begin(), chapters.end(), virtual_chapter_c::CompareTimecode );
+
+    /* On non ordered editions we have one top chapter == one segment */
+    for( size_t i = 0; i < chapters.size(); i++ )
+    {
+        virtual_chapter_c * p_vchap = chapters[i];
+
+        p_vchap->i_virtual_start_time = i_duration;
+        i_duration += p_vchap->p_segment->i_duration * 1000;
+        p_vchap->i_virtual_stop_time = i_duration;
+
+        retimeSubChapters( p_vchap );
+    }
+}
+
+virtual_segment_c::virtual_segment_c( std::vector<matroska_segment_c*> * p_opened_segments )
+{
+    /* Main segment */
+    matroska_segment_c *p_segment = (*p_opened_segments)[0];
+    i_current_edition = 0;
+    i_sys_title = 0;
+    p_current_chapter = NULL;
+
+    for( size_t i = 0; i < p_segment->stored_editions.size(); i++ )
+    {
+        /* Get the default edition, if non use the first one */
+        if( p_segment->stored_editions[i]->b_default )
+            i_current_edition = i;
+
+        /* Create a virtual edition from opened */
+        virtual_edition_c * p_vedition = new virtual_edition_c( p_segment->stored_editions[i], p_opened_segments );
+
+        /*FIXME if p_vedition failed...*/
+
+        editions.push_back( p_vedition );
+    }
+    /*if we don't have edition create a dummy one*/
+    if( !p_segment->stored_editions.size() )
+    {
+        virtual_edition_c * p_vedition = new virtual_edition_c( NULL, p_opened_segments );
+        editions.push_back( p_vedition );
+    }
+
+    /* Set current chapter */
+    p_current_chapter = editions[i_current_edition]->getChapterbyTimecode(0);
+
+}
+
+virtual_segment_c::~virtual_segment_c()
+{
+    for( size_t i = 0; i < editions.size(); i++ )
+        delete editions[i];
+}
+
+virtual_chapter_c *virtual_segment_c::BrowseCodecPrivate( unsigned int codec_id,
+                                    bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
+                                    const void *p_cookie,
+                                    size_t i_cookie_size )
+{
+    virtual_edition_c * p_ved = CurrentEdition();
+    if( p_ved )
+        return p_ved->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
+
+    return NULL;
 }
 
-chapter_item_c *virtual_segment_c::BrowseCodecPrivate( unsigned int codec_id,
+
+virtual_chapter_c * virtual_edition_c::BrowseCodecPrivate( unsigned int codec_id,
                                     bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
                                     const void *p_cookie,
                                     size_t i_cookie_size )
 {
-    // FIXME don't assume it is the first edition
-    std::vector<chapter_edition_c*>::iterator index = p_editions->begin();
-    if ( index != p_editions->end() )
+    if( !p_edition )
+        return NULL;
+
+    for( size_t i = 0; i < chapters.size(); i++ )
     {
-        chapter_item_c *p_result = (*index)->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
-        if ( p_result != NULL )
+        virtual_chapter_c * p_result = chapters[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
+        if( p_result )
             return p_result;
     }
     return NULL;
 }
 
 
-void virtual_segment_c::Sort()
+
+virtual_chapter_c * virtual_chapter_c::BrowseCodecPrivate( unsigned int codec_id,
+                                    bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
+                                    const void *p_cookie,
+                                    size_t i_cookie_size )
 {
-    // keep the current segment index
-    matroska_segment_c *p_segment = linked_segments[i_current_segment];
+    if( !p_chapter )
+        return NULL;
 
-    std::sort( linked_segments.begin(), linked_segments.end(), matroska_segment_c::CompareSegmentUIDs );
+    if( p_chapter->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size ) )
+        return this;
 
-    for ( i_current_segment=0; i_current_segment<linked_segments.size(); i_current_segment++)
-        if ( linked_segments[i_current_segment] == p_segment )
-            break;
+    for( size_t i = 0; i < sub_chapters.size(); i++ )
+    {
+        virtual_chapter_c * p_result = sub_chapters[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
+        if( p_result )
+            return p_result;
+    }
+    return NULL;
 }
 
-size_t virtual_segment_c::AddSegment( matroska_segment_c *p_segment )
+virtual_chapter_c* virtual_chapter_c::getSubChapterbyTimecode( int64_t time )
 {
-    size_t i;
-    // check if it's not already in here
-    for ( i=0; i<linked_segments.size(); i++ )
+    for( size_t i = 0; i < sub_chapters.size(); i++ )
     {
-        if ( linked_segments[i]->p_segment_uid != NULL
-            && p_segment->p_segment_uid != NULL
-            && *p_segment->p_segment_uid == *linked_segments[i]->p_segment_uid )
-            return 0;
+        if( time >= sub_chapters[i]->i_virtual_start_time && time < sub_chapters[i]->i_virtual_stop_time )
+            return sub_chapters[i]->getSubChapterbyTimecode( time );
     }
 
-    // find possible mates
-    for ( i=0; i<linked_uids.size(); i++ )
-    {
-        if (   (p_segment->p_segment_uid != NULL && *p_segment->p_segment_uid == linked_uids[i])
-            || (p_segment->p_prev_segment_uid != NULL && *p_segment->p_prev_segment_uid == linked_uids[i])
-            || (p_segment->p_next_segment_uid !=NULL && *p_segment->p_next_segment_uid == linked_uids[i]) )
-        {
-            linked_segments.push_back( p_segment );
-
-            AppendUID( p_segment->p_prev_segment_uid );
-            AppendUID( p_segment->p_next_segment_uid );
-
-            return 1;
-        }
-    }
-    return 0;
+    return this;
 }
 
-void virtual_segment_c::PreloadLinked( )
+virtual_chapter_c* virtual_edition_c::getChapterbyTimecode( int64_t time )
 {
-    for ( size_t i=0; i<linked_segments.size(); i++ )
+    for( size_t i = 0; i < chapters.size(); i++ )
     {
-        linked_segments[i]->Preload( );
+        if( time >= chapters[i]->i_virtual_start_time && time < chapters[i]->i_virtual_stop_time )
+            return chapters[i]->getSubChapterbyTimecode( time );
     }
-    i_current_edition = linked_segments[0]->i_default_edition;
+
+    return NULL;
 }
 
-mtime_t virtual_segment_c::Duration() const
+bool virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
 {
-    mtime_t i_duration;
-    if ( linked_segments.size() == 0 )
-        i_duration = 0;
-    else {
-        matroska_segment_c *p_last_segment = linked_segments[linked_segments.size()-1];
-//        p_last_segment->ParseCluster( );
-
-        i_duration = p_last_segment->i_start_time / 1000 + p_last_segment->i_duration;
-    }
-    return i_duration;
+    demux_sys_t & sys = *demux.p_sys;
+    virtual_chapter_c *p_cur_chapter;
+    virtual_edition_c * p_cur_edition = editions[ i_current_edition ];
+
+    bool b_has_seeked = false;
+
+    p_cur_chapter = p_cur_edition->getChapterbyTimecode( sys.i_pts );
+
+    /* we have moved to a new chapter */
+    if ( p_cur_chapter != NULL && p_current_chapter != p_cur_chapter )
+        {
+            msg_Dbg( &demux, "NEW CHAPTER %"PRId64, sys.i_pts );
+            if ( p_cur_edition->b_ordered )
+            {
+                /* FIXME EnterAndLeave has probably been broken for a long time */
+                // Leave/Enter up to the link point
+                b_has_seeked = p_cur_chapter->EnterAndLeave( p_current_chapter );
+                if ( !b_has_seeked )
+                {
+                    // only physically seek if necessary
+                    if ( p_current_chapter == NULL ||
+                        ( p_current_chapter->p_chapter->i_end_time != p_cur_chapter->p_chapter->i_start_time ) ||
+                        ( p_current_chapter && p_current_chapter->p_segment != p_cur_chapter->p_segment ) )
+                    {
+                        /* hack : we have to use input to seek in order to clean buffers */
+                        var_SetTime( demux.p_sys->p_input, "time", p_cur_chapter->i_virtual_start_time );
+                        return true;
+                    }
+                }
+            }
+
+            p_current_chapter = p_cur_chapter;
+            if ( p_cur_chapter->i_seekpoint_num > 0 )
+            {
+                demux.info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
+                demux.info.i_title = sys.i_current_title = i_sys_title;
+                demux.info.i_seekpoint = p_cur_chapter->i_seekpoint_num - 1;
+            }
+
+            return b_has_seeked;
+        }
+        else if ( p_cur_chapter == NULL )
+        {
+            /* out of the scope of the data described by chapters, leave the edition */
+            if ( p_cur_edition->b_ordered && p_current_chapter != NULL )
+            {
+                /* TODO */
+                if ( !p_cur_edition->p_edition->EnterAndLeave( p_current_chapter->p_chapter, false ) )
+                    p_current_chapter = NULL;
+                else
+                    return true;
+            }
+        }
+    return false;
 }
 
-void virtual_segment_c::AppendUID( const EbmlBinary * p_UID )
+bool virtual_chapter_c::EnterAndLeave( virtual_chapter_c *p_item, bool b_enter )
 {
-    if ( p_UID == NULL )
-        return;
-    if ( p_UID->GetBuffer() == NULL )
-        return;
+    if( !p_chapter )
+        return false;
 
-    for (size_t i=0; i<linked_uids.size(); i++)
-    {
-        if ( *p_UID == linked_uids[i] )
-            return;
-    }
-    linked_uids.push_back( *(KaxSegmentUID*)(p_UID) );
+    return p_chapter->EnterAndLeave( p_item->p_chapter, b_enter );
 }
 
-void virtual_segment_c::Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset, chapter_item_c *p_chapter, int64_t i_global_position )
+void virtual_segment_c::Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset, 
+                              virtual_chapter_c *p_chapter, int64_t i_global_position )
 {
     demux_sys_t *p_sys = demuxer.p_sys;
-    size_t i;
 
-    // find the actual time for an ordered edition
+
+    /* find the actual time for an ordered edition */
     if ( p_chapter == NULL )
-    {
-        if ( CurrentEdition() && CurrentEdition()->b_ordered )
-        {
-            /* 1st, we need to know in which chapter we are */
-            p_chapter = (*p_editions)[i_current_edition]->FindTimecode( i_date, p_current_chapter );
-        }
-    }
+        /* 1st, we need to know in which chapter we are */
+        p_chapter = editions[ i_current_edition ]->getChapterbyTimecode( i_date );
 
     if ( p_chapter != NULL )
     {
-        p_current_chapter = p_chapter;
-        p_sys->i_chapter_time = i_time_offset = p_chapter->i_user_start_time - p_chapter->i_start_time;
-        if ( p_chapter->i_seekpoint_num > 0 )
+        p_sys->i_chapter_time =
+            i_time_offset = p_chapter->i_virtual_start_time - ( ( p_chapter->p_chapter )? p_chapter->p_chapter->i_start_time : 0 );
+        if ( p_chapter->p_chapter && p_chapter->i_seekpoint_num > 0 )
         {
             demuxer.info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
             demuxer.info.i_title = p_sys->i_current_title = i_sys_title;
             demuxer.info.i_seekpoint = p_chapter->i_seekpoint_num - 1;
         }
+
+        if( p_current_chapter->p_segment != p_chapter->p_segment )
+        {
+            p_current_chapter->p_segment->UnSelect();
+            es_out_Control( demuxer.out, ES_OUT_RESET_PCR );
+            p_chapter->p_segment->Select( i_date );
+        }
+        p_current_chapter = p_chapter;
+
+        p_chapter->p_segment->Seek( i_date, i_time_offset, i_global_position );
     }
+}
+
+virtual_chapter_c * virtual_chapter_c::FindChapter( int64_t i_find_uid )
+{
+    if( p_chapter && ( p_chapter->i_uid == i_find_uid ) )
+        return this;
+
+    for( size_t i = 0; i < sub_chapters.size(); i++ )
+    {
+        virtual_chapter_c * p_res = sub_chapters[i]->FindChapter( i_find_uid );
+        if( p_res )
+            return p_res;
+    }
+
+    return NULL;
+}
+
+virtual_chapter_c * virtual_segment_c::FindChapter( int64_t i_find_uid )
+{
+    virtual_edition_c * p_edition = editions[i_current_edition];
 
-    // find the best matching segment
-    for ( i=0; i<linked_segments.size(); i++ )
+    for( size_t i = 0; p_edition->chapters.size(); i++ )
     {
-        if ( i_date < linked_segments[i]->i_start_time )
-            break;
+        virtual_chapter_c * p_chapter = p_edition->chapters[i]->FindChapter( i_find_uid );
+        if( p_chapter )
+            return p_chapter;
     }
+    return NULL;
+}
 
-    if ( i > 0 )
-        i--;
+int virtual_chapter_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
+{
+    if ( p_chapter && ( !p_chapter->b_display_seekpoint || p_chapter->psz_name == "" ) )
+    {
+        p_chapter->psz_name = p_chapter->GetCodecName();
+        if ( p_chapter->psz_name != "" )
+            p_chapter->b_display_seekpoint = true;
+    }
 
-    if ( i_current_segment != i  )
+    if ( ( p_chapter && p_chapter->b_display_seekpoint &&
+         ( ( sub_chapters.size() > 0 && i_virtual_start_time != sub_chapters[0]->i_virtual_start_time) ||
+           sub_chapters.size() == 0 ) ) || !p_chapter )
     {
-        linked_segments[i_current_segment]->UnSelect();
-        linked_segments[i]->Select( i_date );
-        i_current_segment = i;
+        seekpoint_t *sk = vlc_seekpoint_New();
+
+        sk->i_level = i_level;
+        sk->i_time_offset = i_virtual_start_time;
+        if( p_chapter )
+            sk->psz_name = strdup( p_chapter->psz_name.c_str() );
+        else
+            sk->psz_name = strdup("dummy chapter");
+
+        /* A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value. */
+        title.i_seekpoint++;
+        title.seekpoint = (seekpoint_t**)xrealloc( title.seekpoint,
+                                 title.i_seekpoint * sizeof( seekpoint_t* ) );
+        title.seekpoint[title.i_seekpoint-1] = sk;
+
+        if ( (p_chapter && p_chapter->b_user_display ) ||  !p_chapter )
+            i_user_chapters++;
     }
+    i_seekpoint_num = i_user_chapters;
+
+    for( size_t i = 0; i < sub_chapters.size(); i++ )
+        sub_chapters[i]->PublishChapters( title, i_user_chapters, i_level + 1 );
 
-    linked_segments[i]->Seek( i_date, i_time_offset, i_global_position );
+    return i_user_chapters;
 }
 
-chapter_item_c *virtual_segment_c::FindChapter( int64_t i_find_uid )
+
+int virtual_edition_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
 {
-    // FIXME don't assume it is the first edition
-    std::vector<chapter_edition_c*>::iterator index = p_editions->begin();
-    if ( index != p_editions->end() )
+
+    /* HACK for now don't expose edition as a seekpoint if its start time is the same than it's first chapter */
+    if( chapters.size() > 0 && chapters[0]->i_virtual_start_time )
     {
-        chapter_item_c *p_result = (*index)->FindChapter( i_find_uid );
-        if ( p_result != NULL )
-            return p_result;
+        seekpoint_t *sk = vlc_seekpoint_New();
+
+        sk->i_level = i_level;
+        sk->i_time_offset = 0;
+        if( p_edition )
+            sk->psz_name = strdup( p_edition->psz_name.c_str() );
+        else
+            sk->psz_name = strdup( "Dummy edition" );
+
+        title.i_seekpoint++;
+        title.seekpoint = (seekpoint_t**)xrealloc( title.seekpoint,
+                             title.i_seekpoint * sizeof( seekpoint_t* ) );
+        title.seekpoint[title.i_seekpoint - 1] = sk;
+        i_level++;
+
+        i_user_chapters++;
+        i_seekpoint_num = i_user_chapters;
     }
-    return NULL;
+
+    for( size_t i = 0; i < chapters.size(); i++ )
+        chapters[i]->PublishChapters( title, i_user_chapters, i_level );
+
+    return i_user_chapters;
 }
 
-void virtual_segment_c::AddSegments( const std::vector<matroska_segment_c *> &segments)
+std::string virtual_edition_c::GetMainName()
 {
-    // fill our current virtual segment with all hard linked segments
-    size_t i_preloaded;
-    do {
-        i_preloaded = 0;
-        for ( size_t i=0; i < segments.size(); i++ )
-        {
-            i_preloaded += AddSegment( segments[i] );
-        }
-    } while ( i_preloaded ); // worst case: will stop when all segments are found as linked
+    if( p_edition )
+        return p_edition->GetMainName();
+
+    return std::string("");
+}
 
-    Sort();
-    PreloadLinked( );
-    PrepareChapters( );
+bool virtual_chapter_c::Enter( bool b_do_subs )
+{
+    if( p_chapter )
+        return p_chapter->Enter( b_do_subs );
+    return false;
+}
+
+bool virtual_chapter_c::Leave( bool b_do_subs )
+{
+    if( p_chapter )
+        return p_chapter->Leave( b_do_subs );
+    return false;
+}
+
+#if MKV_DEBUG
+void virtual_chapter_c::print() 
+{
+    msg_Dbg( &p_segment->sys.demuxer, "*** chapter %"PRId64" - %"PRId64" (%u)",
+             i_virtual_start_time, i_virtual_stop_time, sub_chapters.size() );
+    for( size_t i = 0; i < sub_chapters.size(); i++ )
+        sub_chapters[i]->print();
 }
+#endif
index a8c6100..bbdc2fa 100644 (file)
@@ -1,4 +1,3 @@
-
 /*****************************************************************************
  * mkv.cpp : matroska demuxer
  *****************************************************************************
 #include "matroska_segment.hpp"
 #include "chapters.hpp"
 
-// class holding hard-linked segment together in the playback order
-class virtual_segment_c
+/* virtual classes don't own anything but virtual elements so they shouldn't have to delete anything */
+
+class virtual_chapter_c
 {
 public:
-    virtual_segment_c( matroska_segment_c *p_segment )
-        :i_sys_title(0)
-        ,i_current_segment(0)
-        ,i_current_edition(-1)
-        ,p_current_chapter(NULL)
-        ,p_editions(NULL)
+    virtual_chapter_c( matroska_segment_c *p_seg, chapter_item_c *p_chap, int64_t start, int64_t stop ):
+        p_segment(p_seg), p_chapter(p_chap),
+        i_virtual_start_time(start), i_virtual_stop_time(stop)
+    {}
+    ~virtual_chapter_c();
+
+    static virtual_chapter_c * CreateVirtualChapter( chapter_item_c * p_chap,
+                                                     matroska_segment_c * p_main_segment,
+                                                     std::vector<matroska_segment_c*> * segments,
+                                                     int64_t * usertime_offset, bool b_ordered );
+
+    virtual_chapter_c* getSubChapterbyTimecode( int64_t time );
+    bool EnterAndLeave( virtual_chapter_c *p_item, bool b_enter = true );
+    virtual_chapter_c * FindChapter( int64_t i_find_uid );
+    int PublishChapters( input_title_t & title, int & i_user_chapters, int i_level );
+
+    virtual_chapter_c * BrowseCodecPrivate( unsigned int codec_id,
+                                            bool (*match)( const chapter_codec_cmds_c &data,
+                                                           const void *p_cookie,
+                                                           size_t i_cookie_size ),
+                                            const void *p_cookie,
+                                            size_t i_cookie_size );
+    bool Enter( bool b_do_subs );
+    bool Leave( bool b_do_subs );
+
+    static bool CompareTimecode( const virtual_chapter_c * itemA, const virtual_chapter_c * itemB )
     {
-        linked_segments.push_back( p_segment );
-
-        AppendUID( p_segment->p_segment_uid );
-        AppendUID( p_segment->p_prev_segment_uid );
-        AppendUID( p_segment->p_next_segment_uid );
+        return ( itemA->i_virtual_start_time < itemB->i_virtual_start_time ||
+                ( itemA->i_virtual_start_time == itemB->i_virtual_start_time &&
+                  itemA->i_virtual_stop_time < itemB->i_virtual_stop_time ) );
     }
 
-    void AddSegments( const std::vector<matroska_segment_c*> &segments );
+    matroska_segment_c  *p_segment;
+    chapter_item_c      *p_chapter;
+    int64_t             i_virtual_start_time;
+    int64_t             i_virtual_stop_time;
+    int                 i_seekpoint_num;
+    std::vector<virtual_chapter_c *> sub_chapters;
+#if MKV_DEBUG
+    void print();
+#endif
+};
+
+class virtual_edition_c
+{
+public:
+    virtual_edition_c( chapter_edition_c * p_edition, std::vector<matroska_segment_c*> *opened_segments );
+    ~virtual_edition_c();
+    std::vector<virtual_chapter_c*> chapters;
+
+    virtual_chapter_c* getChapterbyTimecode( int64_t time );
+    std::string GetMainName();
+    int PublishChapters( input_title_t & title, int & i_user_chapters, int i_level );
+    virtual_chapter_c * BrowseCodecPrivate( unsigned int codec_id,
+                                            bool (*match)( const chapter_codec_cmds_c &data,
+                                                           const void *p_cookie,
+                                                           size_t i_cookie_size ),
+                                             const void *p_cookie, size_t i_cookie_size );
+
+    bool                b_ordered;
+    int64_t             i_duration;
+    chapter_edition_c   *p_edition;
+    int                 i_seekpoint_num;
+
+private:
+    void retimeChapters();
+    void retimeSubChapters( virtual_chapter_c * p_vchap );
+#if MKV_DEBUG
+    void print(){ for( size_t i = 0; i<chapters.size(); i++ ) chapters[i]->print(); }
+#endif
+
+};
 
-    void Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset, chapter_item_c *p_chapter, int64_t i_global_position );
+// class holding hard-linked segment together in the playback order
+class virtual_segment_c
+{
+public:
+    virtual_segment_c( std::vector<matroska_segment_c*> * opened_segments );
+    ~virtual_segment_c();
+    std::vector<virtual_edition_c*> editions;
+    int                             i_current_edition;
+    virtual_chapter_c               *p_current_chapter;
+    int                             i_sys_title;
 
-    mtime_t Duration() const;
 
-    inline chapter_edition_c *CurrentEdition()
+    inline virtual_edition_c * CurrentEdition()
     {
-        if ( i_current_edition >= 0 && size_t(i_current_edition) < p_editions->size() )
-            return (*p_editions)[i_current_edition];
+        if( i_current_edition >= 0 && (size_t) i_current_edition < editions.size() )
+            return editions[i_current_edition];
         return NULL;
     }
-    std::vector<chapter_edition_c*>*  Editions() const { return p_editions; };
 
-    matroska_segment_c * CurrentSegment() const
+    virtual_chapter_c * CurrentChapter() const
     {
-        if ( linked_segments.size() == 0 || i_current_segment >= linked_segments.size() )
-            return NULL;
-        return linked_segments[i_current_segment];
+        return p_current_chapter;
     }
 
-    chapter_item_c *CurrentChapter() { return p_current_chapter; }
-
-    bool SelectNext()
+    matroska_segment_c * CurrentSegment() const
     {
-        if ( i_current_segment < linked_segments.size()-1 )
-        {
-            i_current_segment++;
-            return true;
-        }
-        return false;
+        if ( !p_current_chapter )
+            return NULL;
+        return p_current_chapter->p_segment;
     }
 
-    bool FindUID( KaxSegmentUID & uid ) const
+    inline int64_t Duration()
     {
-        for ( size_t i=0; i<linked_uids.size(); i++ )
-        {
-            if ( linked_uids[i] == uid )
-                return true;
-        }
-        return false;
+        return editions[i_current_edition]->i_duration / 1000;
     }
 
-    chapter_item_c *FindChapter( int64_t i_find_uid );
+    inline std::vector<virtual_edition_c*>* Editions() { return &editions; }
 
-    bool UpdateCurrentToChapter( demux_t & demux );
-
-    chapter_item_c *BrowseCodecPrivate( unsigned int codec_id,
-                                        bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
-                                        const void *p_cookie,
-                                        size_t i_cookie_size );
-
-    int                              i_sys_title;
-
-protected:
-    std::vector<matroska_segment_c*> linked_segments;
-    std::vector<KaxSegmentUID>       linked_uids;
-    size_t                           i_current_segment;
+    virtual_chapter_c *BrowseCodecPrivate( unsigned int codec_id,
+                                           bool (*match)( const chapter_codec_cmds_c &data,
+                                                          const void *p_cookie,
+                                                          size_t i_cookie_size ),
+                                           const void *p_cookie,
+                                           size_t i_cookie_size );
 
-    int                              i_current_edition;
-    chapter_item_c                   *p_current_chapter;
+    virtual_chapter_c * FindChapter( int64_t i_find_uid );
 
-    std::vector<chapter_edition_c*>  *p_editions;
-
-    void                             AppendUID( const EbmlBinary * UID );
-
-private:
-    void Sort();
-    size_t AddSegment( matroska_segment_c *p_segment );
-    void PreloadLinked( );
-    void PrepareChapters( );
+    bool UpdateCurrentToChapter( demux_t & demux );
+    void Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset,
+               virtual_chapter_c *p_chapter, int64_t i_global_position );
 };
 
 #endif