Commit e617b434 authored by Steve Lhomme's avatar Steve Lhomme

mkv.cpp: major rewrite to handle hard-linked segments easily though a virtual segment

parent 6c7dc61b
...@@ -508,7 +508,6 @@ public: ...@@ -508,7 +508,6 @@ public:
bool Preload( ); bool Preload( );
bool PreloadFamily( const matroska_segment_t & segment ); bool PreloadFamily( const matroska_segment_t & segment );
size_t PreloadLinked( const demux_sys_t & of_sys, std::vector<matroska_segment_t*> & segments );
void ParseInfo( EbmlElement *info ); void ParseInfo( EbmlElement *info );
void ParseChapters( EbmlElement *chapters ); void ParseChapters( EbmlElement *chapters );
void ParseSeekHead( EbmlElement *seekhead ); void ParseSeekHead( EbmlElement *seekhead );
...@@ -525,15 +524,48 @@ public: ...@@ -525,15 +524,48 @@ public:
static bool CompareSegmentUIDs( const matroska_segment_t * item_a, const matroska_segment_t * item_b ); static bool CompareSegmentUIDs( const matroska_segment_t * item_a, const matroska_segment_t * item_b );
}; };
// class holding hard-linked segment together in the playback order
class virtual_segment_t
{
public:
virtual_segment_t()
:i_current_segment(0)
{}
std::vector<matroska_segment_t*> linked_segments;
size_t i_current_segment;
void Sort();
size_t AddSegment( matroska_segment_t *p_segment );
void PreloadLinked( );
float Duration( ) const;
void LoadCues( );
matroska_segment_t * Segment() const
{
if ( linked_segments.size() == 0 || i_current_segment >= linked_segments.size() )
return NULL;
return linked_segments[i_current_segment];
}
bool SelectNext()
{
if ( i_current_segment < linked_segments.size()-1 )
{
i_current_segment++;
return true;
}
return false;
}
};
class matroska_stream_t class matroska_stream_t
{ {
public: public:
matroska_stream_t( demux_sys_t & demuxer ) matroska_stream_t( demux_sys_t & demuxer )
:p_in(NULL) :p_in(NULL)
,p_es(NULL) ,p_es(NULL)
,i_current_segment(-1)
,sys(demuxer) ,sys(demuxer)
,f_duration(-1.0)
{} {}
~matroska_stream_t() ~matroska_stream_t()
...@@ -546,25 +578,10 @@ public: ...@@ -546,25 +578,10 @@ public:
EbmlStream *p_es; EbmlStream *p_es;
std::vector<matroska_segment_t*> segments; std::vector<matroska_segment_t*> segments;
size_t i_current_segment;
demux_sys_t & sys; demux_sys_t & sys;
/* duration of the stream */
float f_duration;
inline matroska_segment_t *Segment()
{
if ( i_current_segment >= 0 && size_t(i_current_segment) < segments.size() )
return segments[i_current_segment];
return NULL;
}
matroska_segment_t *FindSegment( const EbmlBinary & uid ) const;
void PreloadFamily( const matroska_segment_t & segment ); void PreloadFamily( const matroska_segment_t & segment );
size_t PreloadLinked( const demux_sys_t & of_sys );
void PreparePlayback( );
}; };
class demux_sys_t class demux_sys_t
...@@ -577,7 +594,8 @@ public: ...@@ -577,7 +594,8 @@ public:
,i_chapter_time(0) ,i_chapter_time(0)
,meta(NULL) ,meta(NULL)
,title(NULL) ,title(NULL)
,i_current_stream(-1) ,p_current_segment(NULL)
,f_duration(-1.0)
{} {}
~demux_sys_t() ~demux_sys_t()
...@@ -600,19 +618,15 @@ public: ...@@ -600,19 +618,15 @@ public:
input_title_t *title; input_title_t *title;
std::vector<matroska_stream_t*> streams; std::vector<matroska_stream_t*> streams;
int i_current_stream;
std::vector<matroska_segment_t*> opened_segments; std::vector<matroska_segment_t*> opened_segments;
virtual_segment_t *p_current_segment;
inline matroska_stream_t *Stream() /* duration of the stream */
{ float f_duration;
if ( i_current_stream >= 0 && size_t(i_current_stream) < streams.size() )
return streams[i_current_stream];
return NULL;
}
matroska_segment_t *FindSegment( const EbmlBinary & uid ) const; matroska_segment_t *FindSegment( const EbmlBinary & uid ) const;
void PreloadFamily( ); void PreloadFamily( );
void PreloadLinked( ); void PreloadLinked( matroska_segment_t *p_segment );
void PreparePlayback( ); void PreparePlayback( );
matroska_stream_t *AnalyseAllSegmentsFound( EbmlStream *p_estream ); matroska_stream_t *AnalyseAllSegmentsFound( EbmlStream *p_estream );
}; };
...@@ -669,7 +683,6 @@ static int Open( vlc_object_t * p_this ) ...@@ -669,7 +683,6 @@ static int Open( vlc_object_t * p_this )
goto error; goto error;
} }
p_sys->streams.push_back( p_stream ); p_sys->streams.push_back( p_stream );
p_sys->i_current_stream = 0;
p_stream->p_in = p_io_callback; p_stream->p_in = p_io_callback;
p_stream->p_es = p_io_stream; p_stream->p_es = p_io_stream;
...@@ -678,9 +691,8 @@ static int Open( vlc_object_t * p_this ) ...@@ -678,9 +691,8 @@ static int Open( vlc_object_t * p_this )
{ {
p_stream->segments[i]->Preload(); p_stream->segments[i]->Preload();
} }
p_stream->i_current_segment = 0;
p_segment = p_stream->Segment(); p_segment = p_stream->segments[0];
if( p_segment->cluster == NULL ) if( p_segment->cluster == NULL )
{ {
msg_Err( p_demux, "cannot find any cluster, damaged file ?" ); msg_Err( p_demux, "cannot find any cluster, damaged file ?" );
...@@ -755,7 +767,7 @@ static int Open( vlc_object_t * p_this ) ...@@ -755,7 +767,7 @@ static int Open( vlc_object_t * p_this )
} }
p_sys->PreloadFamily( ); p_sys->PreloadFamily( );
p_sys->PreloadLinked( ); p_sys->PreloadLinked( p_segment );
p_sys->PreparePlayback( ); p_sys->PreparePlayback( );
if( !p_segment->b_cues || p_segment->i_index <= 0 ) if( !p_segment->b_cues || p_segment->i_index <= 0 )
...@@ -800,9 +812,7 @@ static void Close( vlc_object_t *p_this ) ...@@ -800,9 +812,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;
matroska_stream_t *p_stream = p_sys->Stream(); matroska_segment_t *p_segment = p_sys->p_current_segment->Segment();
if ( p_stream == NULL ) return VLC_EGENERIC;
matroska_segment_t *p_segment = p_stream->Segment();
int64_t *pi64; int64_t *pi64;
double *pf, f; double *pf, f;
int i_skp; int i_skp;
...@@ -818,17 +828,17 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -818,17 +828,17 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
case DEMUX_GET_LENGTH: case DEMUX_GET_LENGTH:
pi64 = (int64_t*)va_arg( args, int64_t * ); pi64 = (int64_t*)va_arg( args, int64_t * );
if( p_stream->f_duration > 0.0 ) if( p_sys->f_duration > 0.0 )
{ {
*pi64 = (int64_t)(p_stream->f_duration * 1000); *pi64 = (int64_t)(p_sys->f_duration * 1000);
return VLC_SUCCESS; return VLC_SUCCESS;
} }
return VLC_EGENERIC; return VLC_EGENERIC;
case DEMUX_GET_POSITION: case DEMUX_GET_POSITION:
pf = (double*)va_arg( args, double * ); pf = (double*)va_arg( args, double * );
if ( p_stream->f_duration > 0.0 ) if ( p_sys->f_duration > 0.0 )
*pf = (double)p_sys->i_pts / (1000.0 * p_stream->f_duration); *pf = (double)p_sys->i_pts / (1000.0 * p_sys->f_duration);
return VLC_SUCCESS; return VLC_SUCCESS;
case DEMUX_SET_POSITION: case DEMUX_SET_POSITION:
...@@ -1045,8 +1055,7 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts, ...@@ -1045,8 +1055,7 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
mtime_t i_duration ) mtime_t i_duration )
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream(); matroska_segment_t *p_segment = p_sys->p_current_segment->Segment();
matroska_segment_t *p_segment = p_stream->Segment();
size_t i_track; size_t i_track;
unsigned int i; unsigned int i;
...@@ -1559,8 +1568,7 @@ void matroska_segment_t::UnSelect( ) ...@@ -1559,8 +1568,7 @@ void matroska_segment_t::UnSelect( )
static void UpdateCurrentToChapter( demux_t & demux ) static void UpdateCurrentToChapter( demux_t & demux )
{ {
demux_sys_t & sys = *demux.p_sys; demux_sys_t & sys = *demux.p_sys;
matroska_stream_t *p_stream = sys.Stream(); matroska_segment_t *p_segment = sys.p_current_segment->Segment();
matroska_segment_t *p_segment = p_stream->Segment();
const chapter_item_t *psz_curr_chapter; const chapter_item_t *psz_curr_chapter;
/* update current chapter/seekpoint */ /* update current chapter/seekpoint */
...@@ -1597,8 +1605,7 @@ static void UpdateCurrentToChapter( demux_t & demux ) ...@@ -1597,8 +1605,7 @@ static void UpdateCurrentToChapter( demux_t & demux )
static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chapter_item_t *psz_chapter) static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chapter_item_t *psz_chapter)
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream(); matroska_segment_t *p_segment = p_sys->p_current_segment->Segment();
matroska_segment_t *p_segment = p_stream->Segment();
mtime_t i_time_offset = 0; mtime_t i_time_offset = 0;
KaxBlock *block; KaxBlock *block;
...@@ -1623,15 +1630,15 @@ static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chap ...@@ -1623,15 +1630,15 @@ static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chap
} }
delete p_segment->ep; delete p_segment->ep;
p_segment->ep = new EbmlParser( p_stream->p_es, p_segment->segment ); p_segment->ep = new EbmlParser( &p_segment->es, p_segment->segment );
p_segment->cluster = NULL; p_segment->cluster = NULL;
/* seek without index or without date */ /* seek without index or without date */
if( f_percent >= 0 && (config_GetInt( p_demux, "mkv-seek-percent" ) || !p_segment->b_cues || i_date < 0 )) if( f_percent >= 0 && (config_GetInt( p_demux, "mkv-seek-percent" ) || !p_segment->b_cues || i_date < 0 ))
{ {
if (p_stream->f_duration >= 0) if (p_sys->f_duration >= 0)
{ {
i_date = int64_t( f_percent * p_stream->f_duration * 1000.0 ); i_date = int64_t( f_percent * p_sys->f_duration * 1000.0 );
} }
else else
{ {
...@@ -1718,7 +1725,7 @@ static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chap ...@@ -1718,7 +1725,7 @@ static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chap
(int)( 100 * p_segment->index[i_index].i_position / (int)( 100 * p_segment->index[i_index].i_position /
stream_Size( p_demux->s ) ) ); stream_Size( p_demux->s ) ) );
p_stream->p_in->setFilePointer( p_segment->index[i_index].i_position, p_segment->es.I_O().setFilePointer( p_segment->index[i_index].i_position,
seek_beginning ); seek_beginning );
p_sys->i_start_pts = i_date; p_sys->i_start_pts = i_date;
...@@ -1792,8 +1799,7 @@ static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chap ...@@ -1792,8 +1799,7 @@ static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chap
static int Demux( demux_t *p_demux) static int Demux( demux_t *p_demux)
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream(); matroska_segment_t *p_segment = p_sys->p_current_segment->Segment();
matroska_segment_t *p_segment = p_stream->Segment();
if ( p_segment == NULL ) return 0; if ( p_segment == NULL ) return 0;
int i_block_count = 0; int i_block_count = 0;
...@@ -1810,17 +1816,19 @@ static int Demux( demux_t *p_demux) ...@@ -1810,17 +1816,19 @@ static int Demux( demux_t *p_demux)
if ( p_segment->editions.size() && p_segment->editions[p_segment->i_current_edition].b_ordered && p_segment->psz_current_chapter == NULL ) if ( p_segment->editions.size() && p_segment->editions[p_segment->i_current_edition].b_ordered && p_segment->psz_current_chapter == NULL )
{ {
/* nothing left to read in this ordered edition */ /* nothing left to read in this ordered edition */
if ( p_stream->i_current_segment == p_stream->segments.size() - 1) if ( !p_sys->p_current_segment->SelectNext() )
return 0; return 0;
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 (TODO update the duration) */ /* switch to the next segment */
p_stream->i_current_segment++; p_segment = p_sys->p_current_segment->Segment();
p_segment = p_stream->Segment(); if ( !p_segment->Select( 0 ) )
if ( !p_segment || !p_segment->Select( 0 ) ) {
msg_Err( p_demux, "Failed to select new segment" );
return 0; return 0;
}
continue; continue;
} }
...@@ -1843,11 +1851,15 @@ static int Demux( demux_t *p_demux) ...@@ -1843,11 +1851,15 @@ static int Demux( demux_t *p_demux)
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 */ /* switch to the next segment */
p_stream->i_current_segment++; if ( !p_sys->p_current_segment->SelectNext() )
p_segment = p_stream->Segment();
if ( !p_segment || !p_segment->Select( 0 ) )
// no more segments in this stream // no more segments in this stream
return 0; return 0;
p_segment = p_sys->p_current_segment->Segment();
if ( !p_segment->Select( 0 ) )
{
msg_Err( p_demux, "Failed to select new segment" );
return 0;
}
continue; continue;
} }
...@@ -3364,6 +3376,7 @@ const chapter_item_t *chapter_edition_t::FindTimecode( mtime_t i_user_timecode ) ...@@ -3364,6 +3376,7 @@ const chapter_item_t *chapter_edition_t::FindTimecode( mtime_t i_user_timecode )
void demux_sys_t::PreloadFamily( ) void demux_sys_t::PreloadFamily( )
{ {
/* family handling disabled for the moment
matroska_stream_t *p_stream = Stream(); matroska_stream_t *p_stream = Stream();
if ( p_stream ) if ( p_stream )
{ {
...@@ -3376,6 +3389,7 @@ void demux_sys_t::PreloadFamily( ) ...@@ -3376,6 +3389,7 @@ void demux_sys_t::PreloadFamily( )
} }
} }
} }
*/
} }
void matroska_stream_t::PreloadFamily( const matroska_segment_t & of_segment ) void matroska_stream_t::PreloadFamily( const matroska_segment_t & of_segment )
...@@ -3404,83 +3418,34 @@ bool matroska_segment_t::PreloadFamily( const matroska_segment_t & of_segment ) ...@@ -3404,83 +3418,34 @@ bool matroska_segment_t::PreloadFamily( const matroska_segment_t & 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( ) void demux_sys_t::PreloadLinked( matroska_segment_t *p_segment )
{ {
size_t i_prealoaded; size_t i_preloaded, i;
do {
i_prealoaded = 0;
for (size_t i=0; i<streams.size(); i++)
{
i_prealoaded += streams[i]->PreloadLinked( *this );
}
} while ( i_prealoaded ); // worst case: will stop when all segments are preloaded
}
size_t matroska_stream_t::PreloadLinked( const demux_sys_t & of_sys ) delete p_current_segment;
{ p_current_segment = new virtual_segment_t();
size_t i_result = 0;
for (size_t i=0; i<segments.size(); i++)
{
i_result += segments[i]->PreloadLinked( of_sys, segments );
}
return i_result; // fill our current virtual segment with the used segment from the current stream
} p_current_segment->linked_segments.push_back( p_segment );
size_t matroska_segment_t::PreloadLinked( const demux_sys_t & of_sys, std::vector<matroska_segment_t*> & segments ) // fill our current virtual segment with all hard linked segments
{ do {
size_t i_result = 0; i_preloaded = 0;
if ( prev_segment_uid.GetBuffer() ) for ( i=0; i< opened_segments.size(); i++ )
{
matroska_segment_t *p_segment = of_sys.FindSegment( prev_segment_uid );
if ( p_segment )
{
if ( p_segment->Preload( ) )
{
segments.push_back( p_segment );
i_result++;
}
}
}
if ( next_segment_uid.GetBuffer() )
{
matroska_segment_t *p_segment = of_sys.FindSegment( next_segment_uid );
if ( p_segment )
{ {
if ( p_segment->Preload( ) ) i_preloaded += p_current_segment->AddSegment( opened_segments[i] );
{
segments.push_back( p_segment );
i_result++;
}
} }
} } while ( i_preloaded ); // worst case: will stop when all segments are found as linked
return i_result;
}
void demux_sys_t::PreparePlayback( ) p_current_segment->Sort( );
{
matroska_stream_t *p_stream = Stream(); p_current_segment->PreloadLinked( );
if ( p_stream )
{
p_stream->PreparePlayback( );
}
} }
void matroska_stream_t::PreparePlayback( ) void demux_sys_t::PreparePlayback( )
{ {
size_t i; f_duration = p_current_segment->Duration();
p_current_segment->LoadCues();
// update duration
f_duration = 0.0;
for (i=0; i<segments.size(); i++)
{
f_duration += segments[i]->f_duration;
segments[i]->LoadCues( );
}
// sort segment order
std::sort( segments.begin(), segments.end(), matroska_segment_t::CompareSegmentUIDs );
} }
bool matroska_segment_t::CompareSegmentUIDs( const matroska_segment_t * p_item_a, const matroska_segment_t * p_item_b ) bool matroska_segment_t::CompareSegmentUIDs( const matroska_segment_t * p_item_a, const matroska_segment_t * p_item_b )
...@@ -3561,20 +3526,75 @@ bool matroska_segment_t::Preload( ) ...@@ -3561,20 +3526,75 @@ bool matroska_segment_t::Preload( )
matroska_segment_t *demux_sys_t::FindSegment( const EbmlBinary & uid ) const matroska_segment_t *demux_sys_t::FindSegment( const EbmlBinary & uid ) const
{ {
matroska_segment_t *p_segment = NULL; for (size_t i=0; i<opened_segments.size(); i++)
for (size_t i=0; i<streams.size() && p_segment == NULL; i++)
{ {
p_segment = streams[i]->FindSegment( uid ); if ( opened_segments[i]->segment_uid == uid )
return opened_segments[i];
} }
return p_segment; return NULL;
} }
matroska_segment_t *matroska_stream_t::FindSegment( const EbmlBinary & uid ) const void virtual_segment_t::Sort()
{ {
for (size_t i=0; i<segments.size(); i++) // keep the current segment index
matroska_segment_t *p_segment = linked_segments[i_current_segment];
std::sort( linked_segments.begin(), linked_segments.end(), matroska_segment_t::CompareSegmentUIDs );
for ( i_current_segment=0; i_current_segment<linked_segments.size(); i_current_segment++)
if ( linked_segments[i_current_segment] == p_segment )
break;
}
size_t virtual_segment_t::AddSegment( matroska_segment_t *p_segment )
{
size_t i;
// check if it's not already in here
for ( i=0; i<linked_segments.size(); i++ )
{ {
if ( segments[i]->segment_uid == uid ) if ( p_segment->segment_uid == linked_segments[i]->segment_uid )
return segments[i]; return 0;
} }
return NULL;
// find possible mates
for ( i=0; i<linked_segments.size(); i++ )
{
if ( p_segment->segment_uid == linked_segments[i]->prev_segment_uid )
{
linked_segments.push_back( p_segment );
return 1;
}
if ( p_segment->segment_uid == linked_segments[i]->next_segment_uid )
{
linked_segments.push_back( p_segment );
return 1;
}
}
return 0;
} }
void virtual_segment_t::PreloadLinked( )
{
for ( size_t i=0; i<linked_segments.size(); i++ )
{
linked_segments[i]->Preload( );
}
}
float virtual_segment_t::Duration() const
{
float f_duration = 0.0;
for ( size_t i=0; i<linked_segments.size(); i++ )
{
f_duration += linked_segments[i]->f_duration;
}
return f_duration;
}
void virtual_segment_t::LoadCues( )
{
for ( size_t i=0; i<linked_segments.size(); i++ )
{
linked_segments[i]->LoadCues();
}
}
\ No newline at end of file
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