Commit 69a96ade authored by Denis Charmet's avatar Denis Charmet Committed by Jean-Baptiste Kempf

MKV: Virtual segment rewrite

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: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent 2ee48753
...@@ -172,7 +172,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si ...@@ -172,7 +172,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
return false; return false;
virtual_segment_c *p_segment = NULL; virtual_segment_c *p_segment = NULL;
chapter_item_c *p_chapter = NULL; virtual_chapter_c *p_chapter = NULL;
bool f_result = false; bool f_result = false;
uint16 i_command = ( p_command[0] << 8 ) + p_command[1]; 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 ...@@ -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 ); p_chapter = sys.p_current_segment->BrowseCodecPrivate( 1, MatchIsDomain, NULL, 0 );
if ( p_chapter != NULL ) 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 ) if ( i_curr_title > 0 )
{ {
p_chapter = sys.BrowseCodecPrivate( 1, MatchVTSNumber, &i_curr_title, sizeof(i_curr_title), p_segment ); 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 ...@@ -531,7 +531,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
{ {
if ( !p_chapter->Enter( true ) ) if ( !p_chapter->Enter( true ) )
// jump to the location in the found segment // 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; f_result = true;
} }
...@@ -549,7 +549,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si ...@@ -549,7 +549,7 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
{ {
if ( !p_chapter->Enter( true ) ) if ( !p_chapter->Enter( true ) )
// jump to the location in the found segment // 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; f_result = true;
} }
...@@ -742,14 +742,14 @@ bool matroska_script_interpretor_c::Interpret( const binary * p_command, size_t ...@@ -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() ); int64_t i_chapter_uid = atoi( st.c_str() );
virtual_segment_c *p_segment; 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 ) if ( p_chapter == NULL )
msg_Dbg( &sys.demuxer, "Chapter %"PRId64" not found", i_chapter_uid); msg_Dbg( &sys.demuxer, "Chapter %"PRId64" not found", i_chapter_uid);
else else
{ {
if ( !p_chapter->EnterAndLeave( sys.p_current_segment->CurrentChapter() ) ) 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; b_result = true;
} }
} }
......
...@@ -28,53 +28,20 @@ ...@@ -28,53 +28,20 @@
chapter_item_c::~chapter_item_c() 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( codecs );
vlc_delete_all( sub_chapters ); 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, 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 ), bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
const void *p_cookie, const void *p_cookie,
size_t i_cookie_size ) size_t i_cookie_size )
{ {
VLC_UNUSED( codec_id );
// this chapter // this chapter
std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin(); std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
while ( index != codecs.end() ) while ( index != codecs.end() )
...@@ -83,19 +50,7 @@ chapter_item_c *chapter_item_c::BrowseCodecPrivate( unsigned int codec_id, ...@@ -83,19 +50,7 @@ chapter_item_c *chapter_item_c::BrowseCodecPrivate( unsigned int codec_id,
return this; return this;
++index; ++index;
} }
return NULL;
// 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;
} }
void chapter_item_c::Append( const chapter_item_c & chapter ) void chapter_item_c::Append( const chapter_item_c & chapter )
...@@ -116,9 +71,6 @@ 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] ); 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 ) chapter_item_c * chapter_item_c::FindChapter( int64_t i_find_uid )
...@@ -170,79 +122,6 @@ int16 chapter_item_c::GetTitleNumber( ) const ...@@ -170,79 +122,6 @@ int16 chapter_item_c::GetTitleNumber( ) const
return result; 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 bool chapter_item_c::ParentOf( const chapter_item_c & item ) const
{ {
if ( &item == this ) if ( &item == this )
...@@ -360,32 +239,3 @@ std::string chapter_edition_c::GetMainName() const ...@@ -360,32 +239,3 @@ std::string chapter_edition_c::GetMainName() const
return ""; 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 );
}
...@@ -53,9 +53,8 @@ public: ...@@ -53,9 +53,8 @@ public:
chapter_item_c() chapter_item_c()
:i_start_time(0) :i_start_time(0)
,i_end_time(-1) ,i_end_time(-1)
,i_user_start_time(-1) ,p_segment_uid(NULL)
,i_user_end_time(-1) ,p_segment_edition_uid(NULL)
,i_seekpoint_num(-1)
,b_display_seekpoint(true) ,b_display_seekpoint(true)
,b_user_display(false) ,b_user_display(false)
,p_parent(NULL) ,p_parent(NULL)
...@@ -63,13 +62,8 @@ public: ...@@ -63,13 +62,8 @@ public:
{} {}
virtual ~chapter_item_c(); 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 ); void Append( const chapter_item_c & edition );
chapter_item_c * FindChapter( int64_t i_find_uid ); chapter_item_c * FindChapter( int64_t i_find_uid );
virtual chapter_item_c *BrowseCodecPrivate( unsigned int codec_id, 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 ), bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
const void *p_cookie, const void *p_cookie,
...@@ -79,9 +73,9 @@ public: ...@@ -79,9 +73,9 @@ public:
int16 GetTitleNumber( ) const; int16 GetTitleNumber( ) const;
int64_t i_start_time, i_end_time; 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; std::vector<chapter_item_c*> sub_chapters;
int i_seekpoint_num; KaxChapterSegmentUID *p_segment_uid;
KaxChapterSegmentEditionUID *p_segment_edition_uid;
int64_t i_uid; int64_t i_uid;
bool b_display_seekpoint; bool b_display_seekpoint;
bool b_user_display; bool b_user_display;
...@@ -91,12 +85,6 @@ public: ...@@ -91,12 +85,6 @@ public:
std::vector<chapter_codec_cmds_c*> codecs; 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 Enter( bool b_do_subchapters );
bool Leave( bool b_do_subchapters ); bool Leave( bool b_do_subchapters );
bool EnterAndLeave( chapter_item_c *p_item, bool b_enter = true ); bool EnterAndLeave( chapter_item_c *p_item, bool b_enter = true );
...@@ -105,16 +93,14 @@ public: ...@@ -105,16 +93,14 @@ public:
class chapter_edition_c : public chapter_item_c class chapter_edition_c : public chapter_item_c
{ {
public: public:
chapter_edition_c() chapter_edition_c(): b_ordered(false), b_default(false), b_hidden(false)
:b_ordered(false)
{} {}
void RefreshChapters( );
mtime_t Duration() const;
std::string GetMainName() const; std::string GetMainName() const;
chapter_item_c * FindTimecode( mtime_t i_timecode, const chapter_item_c * p_current );
bool b_ordered; bool b_ordered;
bool b_default;
/* TODO handle hidden chapters */
bool b_hidden;
}; };
#endif #endif
...@@ -529,7 +529,6 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS ...@@ -529,7 +529,6 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
b_keep_segment = (FindSegment( *p_uid ) == NULL); b_keep_segment = (FindSegment( *p_uid ) == NULL);
if ( !b_keep_segment ) if ( !b_keep_segment )
break; // this segment is already known break; // this segment is already known
opened_segments.push_back( p_segment1 );
delete p_segment1->p_segment_uid; delete p_segment1->p_segment_uid;
p_segment1->p_segment_uid = new KaxSegmentUID(*p_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 ...@@ -547,6 +546,8 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
p_segment1->families.push_back( p_fam ); p_segment1->families.push_back( p_fam );
} }
} }
if( b_keep_segment || !p_segment1->p_segment_uid )
opened_segments.push_back( p_segment1 );
break; break;
} }
} }
...@@ -633,29 +634,15 @@ void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment ) ...@@ -633,29 +634,15 @@ void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment )
} }
// preload all the linked segments for all preloaded segments // 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; virtual_segment_c *p_seg;
p_current_segment = VirtualFromSegments( p_segment ); p_current_segment = VirtualFromSegments( &opened_segments );
used_segments.push_back( p_current_segment ); 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 // publish all editions of all usable segment
for ( i=0; i< used_segments.size(); i++ ) for ( i=0; i< used_segments.size(); i++ )
{ {
...@@ -676,10 +663,8 @@ void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment ) ...@@ -676,10 +663,8 @@ void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment )
p_title->psz_name = strdup( psz_tmp ); p_title->psz_name = strdup( psz_tmp );
} }
chapter_edition_c *p_edition = (*p_seg->Editions())[j];
i_chapters = 0; 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 // create a name if there is none
...@@ -696,21 +681,9 @@ void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment ) ...@@ -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) // 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++ ) virtual_segment_c *p_result = new virtual_segment_c( p_segments );
{
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 );
return p_result; return p_result;
} }
...@@ -736,7 +709,7 @@ bool demux_sys_t::PreparePlayback( virtual_segment_c *p_new_segment ) ...@@ -736,7 +709,7 @@ bool demux_sys_t::PreparePlayback( virtual_segment_c *p_new_segment )
return true; 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 the segment is not part of the current segment, select the new one
if ( &vsegment != p_current_segment ) if ( &vsegment != p_current_segment )
...@@ -744,12 +717,12 @@ void demux_sys_t::JumpTo( virtual_segment_c & vsegment, chapter_item_c * p_chapt ...@@ -744,12 +717,12 @@ void demux_sys_t::JumpTo( virtual_segment_c & vsegment, chapter_item_c * p_chapt
PreparePlayback( &vsegment ); 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 // 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 ...@@ -765,13 +738,13 @@ matroska_segment_c *demux_sys_t::FindSegment( const EbmlBinary & uid ) const
return NULL; 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 ), bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
const void *p_cookie, const void *p_cookie,
size_t i_cookie_size, size_t i_cookie_size,
virtual_segment_c * &p_segment_found ) 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++) for (size_t i=0; i<used_segments.size(); i++)
{ {
p_result = used_segments[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size ); 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, ...@@ -784,9 +757,9 @@ chapter_item_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
return p_result; 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++) for (size_t i=0; i<used_segments.size(); i++)
{ {
p_result = used_segments[i]->FindChapter( i_find_uid ); 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 ...@@ -799,4 +772,3 @@ chapter_item_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c
return p_result; return p_result;
} }
...@@ -371,18 +371,18 @@ public: ...@@ -371,18 +371,18 @@ public:
float f_duration; float f_duration;
matroska_segment_c *FindSegment( const EbmlBinary & uid ) const; 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 ), bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
const void *p_cookie, const void *p_cookie,
size_t i_cookie_size, size_t i_cookie_size,
virtual_segment_c * & p_segment_found ); 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 PreloadFamily( const matroska_segment_c & of_segment );
void PreloadLinked( matroska_segment_c *p_segment ); void PreloadLinked();
bool PreparePlayback( virtual_segment_c *p_new_segment ); bool PreparePlayback( virtual_segment_c *p_new_segment );
matroska_stream_c *AnalyseAllSegmentsFound( demux_t *p_demux, EbmlStream *p_estream, bool b_initial = false ); 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 InitUi();
void CleanUi(); void CleanUi();
...@@ -396,8 +396,7 @@ public: ...@@ -396,8 +396,7 @@ public:
event_thread_t *p_ev; event_thread_t *p_ev;
protected: protected:
virtual_segment_c *VirtualFromSegments( matroska_segment_c *p_segment ) const; virtual_segment_c *VirtualFromSegments( std::vector<matroska_segment_c*> *p_segments ) const;
bool IsUsedSegment( matroska_segment_c &p_segment ) const;
}; };
......
...@@ -692,7 +692,7 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_ ...@@ -692,7 +692,7 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_
{ {
IndexAppendCluster( cluster ); IndexAppendCluster( cluster );
} }
if( es.I_O().getFilePointer() >= i_global_position ) if( es.I_O().getFilePointer() >= (unsigned) i_global_position )
{ {
ParseCluster(); ParseCluster();
msg_Dbg( &sys.demuxer, "we found a cluster that is in the neighbourhood" ); 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_ ...@@ -769,9 +769,9 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_
} }
if( simpleblock ) 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 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() ) if( i_track < tracks.size() )
{ {
......
...@@ -855,6 +855,16 @@ void matroska_segment_c::ParseChapterAtom( int i_level, KaxChapterAtom *ca, chap ...@@ -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" ); 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 ) ) else if( MKV_IS_ID( l, KaxChapterTimeStart ) )
{ {
KaxChapterTimeStart &start =*(KaxChapterTimeStart*)l; KaxChapterTimeStart &start =*(KaxChapterTimeStart*)l;
...@@ -998,7 +1008,6 @@ void matroska_segment_c::ParseChapters( KaxChapters *chapters ) ...@@ -998,7 +1008,6 @@ void matroska_segment_c::ParseChapters( KaxChapters *chapters )
{ {
EbmlElement *el; EbmlElement *el;
int i_upper_level = 0; int i_upper_level = 0;
mtime_t i_dur;
/* Master elements */ /* Master elements */
chapters->Read( es, EBML_CONTEXT(chapters), i_upper_level, el, true ); chapters->Read( es, EBML_CONTEXT(chapters), i_upper_level, el, true );
...@@ -1052,19 +1061,6 @@ void matroska_segment_c::ParseChapters( KaxChapters *chapters ) ...@@ -1052,19 +1061,6 @@ void matroska_segment_c::ParseChapters( KaxChapters *chapters )
msg_Dbg( &sys.demuxer, "| | + Unknown (%s)", typeid(*l).name() ); 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( ) void matroska_segment_c::ParseCluster( )
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "stream_io_callback.hpp" #include "stream_io_callback.hpp"
#include <vlc_fs.h> #include <vlc_fs.h>
#include <vlc_url.h>
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
...@@ -76,7 +77,7 @@ class demux_sys_t; ...@@ -76,7 +77,7 @@ class demux_sys_t;
static int Demux ( demux_t * ); static int Demux ( demux_t * );
static int Control( demux_t *, int, va_list ); 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 * Open: initializes matroska demux structures
...@@ -141,7 +142,7 @@ static int Open( vlc_object_t * p_this ) ...@@ -141,7 +142,7 @@ static int Open( vlc_object_t * p_this )
if (var_InheritBool( p_demux, "mkv-preload-local-dir" )) 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) */ /* 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 // assume it's a regular file
// get the directory path // get the directory path
...@@ -185,9 +186,10 @@ static int Open( vlc_object_t * p_this ) ...@@ -185,9 +186,10 @@ static int Open( vlc_object_t * p_this )
// test wether this file belongs to our family // test wether this file belongs to our family
const uint8_t *p_peek; const uint8_t *p_peek;
bool file_ok = false; bool file_ok = false;
std::string s_url = make_URI( s_filename.c_str(), "file" );
stream_t *p_file_stream = stream_UrlNew( stream_t *p_file_stream = stream_UrlNew(
p_demux, p_demux,
s_filename.c_str()); s_url.c_str() );
/* peek the begining */ /* peek the begining */
if( p_file_stream && if( p_file_stream &&
stream_Peek( p_file_stream, &p_peek, 4 ) >= 4 stream_Peek( p_file_stream, &p_peek, 4 ) >= 4
...@@ -232,7 +234,7 @@ static int Open( vlc_object_t * p_this ) ...@@ -232,7 +234,7 @@ static int Open( vlc_object_t * p_this )
p_sys->PreloadFamily( *p_segment ); p_sys->PreloadFamily( *p_segment );
} }
p_sys->PreloadLinked( p_segment ); p_sys->PreloadLinked();
if ( !p_sys->PreparePlayback( NULL ) ) if ( !p_sys->PreparePlayback( NULL ) )
{ {
...@@ -266,7 +268,7 @@ static void Close( vlc_object_t *p_this ) ...@@ -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 ) static int Control( demux_t *p_demux, int i_query, va_list args )
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
int64_t *pi64; int64_t *pi64, i64;
double *pf, f; double *pf, f;
int i_skp; int i_skp;
size_t i_idx; size_t i_idx;
...@@ -386,13 +388,17 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -386,13 +388,17 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
return VLC_SUCCESS; return VLC_SUCCESS;
case DEMUX_SET_TIME: 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: default:
return VLC_EGENERIC; return VLC_EGENERIC;
} }
} }
/* Seek */ /* 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; demux_sys_t *p_sys = p_demux->p_sys;
virtual_segment_c *p_vsegment = p_sys->p_current_segment; virtual_segment_c *p_vsegment = p_sys->p_current_segment;
...@@ -516,7 +522,7 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock ...@@ -516,7 +522,7 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
for( unsigned int i = 0; 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++ ) i++ )
{ {
block_t *p_block; block_t *p_block;
...@@ -643,24 +649,11 @@ static int Demux( demux_t *p_demux) ...@@ -643,24 +649,11 @@ static int Demux( demux_t *p_demux)
break; 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 */ /* nothing left to read in this ordered edition */
if ( !p_vsegment->SelectNext() )
break; 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;
}
KaxBlock *block; KaxBlock *block;
KaxSimpleBlock *simpleblock; KaxSimpleBlock *simpleblock;
...@@ -671,15 +664,13 @@ static int Demux( demux_t *p_demux) ...@@ -671,15 +664,13 @@ static int Demux( demux_t *p_demux)
{ {
if ( p_vsegment->CurrentEdition() && p_vsegment->CurrentEdition()->b_ordered ) 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 // check if there are more chapters to read
if ( p_chap != NULL ) if ( p_chap != NULL )
{ {
/* TODO handle successive chapters with the same user_start_time/user_end_time /* 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 p_sys->i_pts++; // trick to avoid staying on segments with no duration and no content
i_return = 1; i_return = 1;
...@@ -690,37 +681,25 @@ static int Demux( demux_t *p_demux) ...@@ -690,37 +681,25 @@ static int Demux( demux_t *p_demux)
else else
{ {
msg_Warn( p_demux, "cannot get block EOF?" ); msg_Warn( p_demux, "cannot get block EOF?" );
p_segment->UnSelect( ); p_segment->UnSelect();
es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); 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; break;
} }
continue;
}
} }
if( simpleblock != NULL ) 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 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 */ /* 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 ); 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_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; i_return = 1;
delete block; delete block;
...@@ -728,29 +707,14 @@ static int Demux( demux_t *p_demux) ...@@ -728,29 +707,14 @@ 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 */ /* 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; delete block;
break; break;
} }
delete block;
continue;
}
BlockDecode( p_demux, block, simpleblock, p_sys->i_pts, i_block_duration, b_key_picture || b_discardable_picture ); BlockDecode( p_demux, block, simpleblock, p_sys->i_pts, i_block_duration, b_key_picture || b_discardable_picture );
......
...@@ -21,261 +21,570 @@ ...@@ -21,261 +21,570 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <vector>
#include "virtual_segment.hpp"
#include "demux.hpp" #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 ) for( size_t i = 0; i < (*segments).size(); i++ )
return; {
if( *p_uid == *((*segments)[i]->p_segment_uid) )
// !!! should be called only once !!! return (*segments)[i];
matroska_segment_c *p_segment; }
return NULL;
}
// copy editions from the first segment virtual_chapter_c * virtual_chapter_c::CreateVirtualChapter( chapter_item_c * p_chap,
p_segment = linked_segments[0]; matroska_segment_c * p_main_segment,
p_editions = &p_segment->stored_editions; std::vector<matroska_segment_c*> * segments,
int64_t * usertime_offset, bool b_ordered)
{
matroska_segment_c * p_segment = p_main_segment;
for ( size_t i=1 ; i<linked_segments.size(); i++ ) if( !p_chap )
{ {
p_segment = linked_segments[i]; /* Dummy chapter use the whole segment */
// FIXME assume we have the same editions in all segments return new virtual_chapter_c( p_segment, NULL, 0, p_segment->i_duration*1000 );
for ( size_t j=0; j<p_segment->stored_editions.size(); j++) }
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;
if( p_chap->p_segment_uid &&
( !( p_segment = getSegmentbyUID( (KaxSegmentUID*) p_chap->p_segment_uid,segments ) ) || !b_ordered ) )
{ {
if( j >= p_editions->size() ) /* Protect against broken files (?) */ msg_Warn( &p_main_segment->sys.demuxer,
break; "Couldn't find segment 0x%x or not ordered... - ignoring chapter %s",
(*p_editions)[j]->Append( *p_segment->stored_editions[j] ); *( (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; for( size_t i = 0 ; i < sub_chapters.size(); i++ )
chapter_item_c *p_curr_chapter; delete sub_chapters[i];
bool b_has_seeked = false; }
/* 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 */ virtual_edition_c::virtual_edition_c( chapter_edition_c * p_edit, std::vector<matroska_segment_c*> *opened_segments)
if (p_curr_chapter != NULL && p_current_chapter != p_curr_chapter) {
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 )
{ {
if ( (*p_editions)[i_current_edition]->b_ordered ) b_ordered = true;
for( size_t i = 0; i < p_edition->sub_chapters.size(); i++ )
{ {
// Leave/Enter up to the link point virtual_chapter_c * p_vchap = virtual_chapter_c::CreateVirtualChapter( p_edition->sub_chapters[i],
b_has_seeked = p_curr_chapter->EnterAndLeave( p_current_chapter ); p_main_segment, opened_segments,
if ( !b_has_seeked ) &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 */
{ {
// only physically seek if necessary b_ordered = false;
if ( p_current_chapter == NULL || (p_current_chapter->i_end_time != p_curr_chapter->i_start_time) ) matroska_segment_c * p_cur = p_main_segment;
Seek( demux, sys.i_pts, 0, p_curr_chapter, -1 ); 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 ) ) )
{
tmp = 0;
msg_Dbg( &p_main_segment->sys.demuxer, "Prev segment 0x%x found\n",
*(int32_t*)p_cur->p_prev_segment_uid->GetBuffer() );
/* 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 );
p_cur = p_prev;
} }
else /* segment not found */
break;
} }
if ( !b_has_seeked ) 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++ )
{ {
p_current_chapter = p_curr_chapter; matroska_segment_c * p_next = NULL;
if ( p_curr_chapter->i_seekpoint_num > 0 ) if( ( p_next = getSegmentbyUID( p_cur->p_next_segment_uid, opened_segments ) ) )
{ {
demux.info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT; tmp = 0;
demux.info.i_title = sys.i_current_title = i_sys_title; msg_Dbg( &p_main_segment->sys.demuxer, "Next segment 0x%x found\n",
demux.info.i_seekpoint = p_curr_chapter->i_seekpoint_num - 1; *(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;
} }
return true; /* Retime chapters */
retimeChapters();
} }
else if (p_curr_chapter == NULL)
#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; )
{ {
// out of the scope of the data described by chapters, leave the edition virtual_chapter_c * p_vsubchap = p_vchap->sub_chapters[i];
if ( (*p_editions)[i_current_edition]->b_ordered && p_current_chapter != NULL ) 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++ )
{ {
if ( !(*p_editions)[i_current_edition]->EnterAndLeave( p_current_chapter, false ) ) virtual_chapter_c * p_vchap = chapters[i];
p_current_chapter = NULL;
else p_vchap->i_virtual_start_time = i_duration;
return true; 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 );
} }
return false;
/* Set current chapter */
p_current_chapter = editions[i_current_edition]->getChapterbyTimecode(0);
} }
chapter_item_c *virtual_segment_c::BrowseCodecPrivate( unsigned int codec_id, 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 ), bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
const void *p_cookie, const void *p_cookie,
size_t i_cookie_size ) size_t i_cookie_size )
{ {
// FIXME don't assume it is the first edition virtual_edition_c * p_ved = CurrentEdition();
std::vector<chapter_edition_c*>::iterator index = p_editions->begin(); if( p_ved )
if ( index != p_editions->end() ) return p_ved->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
return NULL;
}
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 )
{
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 ); virtual_chapter_c * p_result = chapters[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
if ( p_result != NULL ) if( p_result )
return p_result; return p_result;
} }
return NULL; 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 if( !p_chapter )
matroska_segment_c *p_segment = linked_segments[i_current_segment]; 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++) for( size_t i = 0; i < sub_chapters.size(); i++ )
if ( linked_segments[i_current_segment] == p_segment ) {
break; 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; for( size_t i = 0; i < sub_chapters.size(); i++ )
// check if it's not already in here
for ( i=0; i<linked_segments.size(); i++ )
{ {
if ( linked_segments[i]->p_segment_uid != NULL if( time >= sub_chapters[i]->i_virtual_start_time && time < sub_chapters[i]->i_virtual_stop_time )
&& p_segment->p_segment_uid != NULL return sub_chapters[i]->getSubChapterbyTimecode( time );
&& *p_segment->p_segment_uid == *linked_segments[i]->p_segment_uid )
return 0;
} }
// find possible mates return this;
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;
} }
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; demux_sys_t & sys = *demux.p_sys;
if ( linked_segments.size() == 0 ) virtual_chapter_c *p_cur_chapter;
i_duration = 0; virtual_edition_c * p_cur_edition = editions[ i_current_edition ];
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; 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;
}
}
} }
return i_duration;
}
void virtual_segment_c::AppendUID( const EbmlBinary * p_UID ) p_current_chapter = p_cur_chapter;
{ if ( p_cur_chapter->i_seekpoint_num > 0 )
if ( p_UID == NULL ) {
return; demux.info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
if ( p_UID->GetBuffer() == NULL ) demux.info.i_title = sys.i_current_title = i_sys_title;
return; demux.info.i_seekpoint = p_cur_chapter->i_seekpoint_num - 1;
}
for (size_t i=0; i<linked_uids.size(); i++) return b_has_seeked;
}
else if ( p_cur_chapter == NULL )
{ {
if ( *p_UID == linked_uids[i] ) /* out of the scope of the data described by chapters, leave the edition */
return; 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;
}
} }
linked_uids.push_back( *(KaxSegmentUID*)(p_UID) ); return false;
}
bool virtual_chapter_c::EnterAndLeave( virtual_chapter_c *p_item, bool b_enter )
{
if( !p_chapter )
return false;
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; 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 ( p_chapter == NULL )
{
if ( CurrentEdition() && CurrentEdition()->b_ordered )
{
/* 1st, we need to know in which chapter we are */ /* 1st, we need to know in which chapter we are */
p_chapter = (*p_editions)[i_current_edition]->FindTimecode( i_date, p_current_chapter ); p_chapter = editions[ i_current_edition ]->getChapterbyTimecode( i_date );
}
}
if ( p_chapter != NULL ) if ( p_chapter != NULL )
{ {
p_current_chapter = p_chapter; p_sys->i_chapter_time =
p_sys->i_chapter_time = i_time_offset = p_chapter->i_user_start_time - p_chapter->i_start_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->i_seekpoint_num > 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_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
demuxer.info.i_title = p_sys->i_current_title = i_sys_title; demuxer.info.i_title = p_sys->i_current_title = i_sys_title;
demuxer.info.i_seekpoint = p_chapter->i_seekpoint_num - 1; demuxer.info.i_seekpoint = p_chapter->i_seekpoint_num - 1;
} }
}
// find the best matching segment if( p_current_chapter->p_segment != p_chapter->p_segment )
for ( i=0; i<linked_segments.size(); i++ )
{ {
if ( i_date < linked_segments[i]->i_start_time ) p_current_chapter->p_segment->UnSelect();
break; es_out_Control( demuxer.out, ES_OUT_RESET_PCR );
p_chapter->p_segment->Select( i_date );
} }
p_current_chapter = p_chapter;
if ( i > 0 ) p_chapter->p_segment->Seek( i_date, i_time_offset, i_global_position );
i--; }
}
if ( i_current_segment != i ) 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++ )
{ {
linked_segments[i_current_segment]->UnSelect(); virtual_chapter_c * p_res = sub_chapters[i]->FindChapter( i_find_uid );
linked_segments[i]->Select( i_date ); if( p_res )
i_current_segment = i; return p_res;
} }
linked_segments[i]->Seek( i_date, i_time_offset, i_global_position ); return NULL;
} }
chapter_item_c *virtual_segment_c::FindChapter( int64_t i_find_uid ) virtual_chapter_c * virtual_segment_c::FindChapter( int64_t i_find_uid )
{ {
// FIXME don't assume it is the first edition virtual_edition_c * p_edition = editions[i_current_edition];
std::vector<chapter_edition_c*>::iterator index = p_editions->begin();
if ( index != p_editions->end() ) for( size_t i = 0; p_edition->chapters.size(); i++ )
{ {
chapter_item_c *p_result = (*index)->FindChapter( i_find_uid ); virtual_chapter_c * p_chapter = p_edition->chapters[i]->FindChapter( i_find_uid );
if ( p_result != NULL ) if( p_chapter )
return p_result; return p_chapter;
} }
return NULL; return NULL;
} }
void virtual_segment_c::AddSegments( const std::vector<matroska_segment_c *> &segments) 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 ( ( 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 )
{
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 );
return i_user_chapters;
}
int virtual_edition_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
{ {
// fill our current virtual segment with all hard linked segments
size_t i_preloaded; /* HACK for now don't expose edition as a seekpoint if its start time is the same than it's first chapter */
do { if( chapters.size() > 0 && chapters[0]->i_virtual_start_time )
i_preloaded = 0;
for ( size_t i=0; i < segments.size(); i++ )
{ {
i_preloaded += AddSegment( segments[i] ); 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;
} }
} while ( i_preloaded ); // worst case: will stop when all segments are found as linked
Sort(); for( size_t i = 0; i < chapters.size(); i++ )
PreloadLinked( ); chapters[i]->PublishChapters( title, i_user_chapters, i_level );
PrepareChapters( );
return i_user_chapters;
}
std::string virtual_edition_c::GetMainName()
{
if( p_edition )
return p_edition->GetMainName();
return std::string("");
}
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
/***************************************************************************** /*****************************************************************************
* mkv.cpp : matroska demuxer * mkv.cpp : matroska demuxer
***************************************************************************** *****************************************************************************
...@@ -31,95 +30,134 @@ ...@@ -31,95 +30,134 @@
#include "matroska_segment.hpp" #include "matroska_segment.hpp"
#include "chapters.hpp" #include "chapters.hpp"
// class holding hard-linked segment together in the playback order /* virtual classes don't own anything but virtual elements so they shouldn't have to delete anything */
class virtual_segment_c
class virtual_chapter_c
{ {
public: public:
virtual_segment_c( matroska_segment_c *p_segment ) virtual_chapter_c( matroska_segment_c *p_seg, chapter_item_c *p_chap, int64_t start, int64_t stop ):
:i_sys_title(0) p_segment(p_seg), p_chapter(p_chap),
,i_current_segment(0) i_virtual_start_time(start), i_virtual_stop_time(stop)
,i_current_edition(-1) {}
,p_current_chapter(NULL) ~virtual_chapter_c();
,p_editions(NULL)
{ static virtual_chapter_c * CreateVirtualChapter( chapter_item_c * p_chap,
linked_segments.push_back( p_segment ); 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 );
AppendUID( p_segment->p_segment_uid ); static bool CompareTimecode( const virtual_chapter_c * itemA, const virtual_chapter_c * itemB )
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 );
void Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset, chapter_item_c *p_chapter, int64_t i_global_position ); 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
mtime_t Duration() const; };
inline chapter_edition_c *CurrentEdition() // 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;
inline virtual_edition_c * CurrentEdition()
{ {
if ( i_current_edition >= 0 && size_t(i_current_edition) < p_editions->size() ) if( i_current_edition >= 0 && (size_t) i_current_edition < editions.size() )
return (*p_editions)[i_current_edition]; return editions[i_current_edition];
return NULL; 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 p_current_chapter;
return NULL;
return linked_segments[i_current_segment];
} }
chapter_item_c *CurrentChapter() { return p_current_chapter; } matroska_segment_c * CurrentSegment() const
bool SelectNext()
{
if ( i_current_segment < linked_segments.size()-1 )
{ {
i_current_segment++; if ( !p_current_chapter )
return true; return NULL;
} return p_current_chapter->p_segment;
return false;
} }
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 editions[i_current_edition]->i_duration / 1000;
return true;
}
return false;
} }
chapter_item_c *FindChapter( int64_t i_find_uid ); inline std::vector<virtual_edition_c*>* Editions() { return &editions; }
bool UpdateCurrentToChapter( demux_t & demux ); virtual_chapter_c *BrowseCodecPrivate( unsigned int codec_id,
bool (*match)( const chapter_codec_cmds_c &data,
chapter_item_c *BrowseCodecPrivate( unsigned int codec_id, const void *p_cookie,
bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ), size_t i_cookie_size ),
const void *p_cookie, const void *p_cookie,
size_t i_cookie_size ); size_t i_cookie_size );
int i_sys_title; virtual_chapter_c * FindChapter( int64_t i_find_uid );
protected:
std::vector<matroska_segment_c*> linked_segments;
std::vector<KaxSegmentUID> linked_uids;
size_t i_current_segment;
int i_current_edition; bool UpdateCurrentToChapter( demux_t & demux );
chapter_item_c *p_current_chapter; void Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset,
virtual_chapter_c *p_chapter, int64_t i_global_position );
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( );
}; };
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment