Commit 7e1d4d17 authored by Laurent Aimar's avatar Laurent Aimar

* mkv: avoid seeking at the same place (was sooooo slow 'cause of

          buffer filling)
        can seek in file without cues (slow)
        can play file over http and unseekable input.
parent 72bb01ef
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* mkv.cpp : matroska demuxer * mkv.cpp : matroska demuxer
***************************************************************************** *****************************************************************************
* Copyright (C) 2001 VideoLAN * Copyright (C) 2001 VideoLAN
* $Id: mkv.cpp,v 1.6 2003/06/23 00:30:41 fenrir Exp $ * $Id: mkv.cpp,v 1.7 2003/06/24 00:33:39 fenrir Exp $
* *
* Authors: Laurent Aimar <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* *
...@@ -152,6 +152,9 @@ uint32_t vlc_stream_io_callback::read( void *p_buffer, size_t i_size ) ...@@ -152,6 +152,9 @@ uint32_t vlc_stream_io_callback::read( void *p_buffer, size_t i_size )
void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode ) void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
{ {
int64_t i_pos; int64_t i_pos;
int64_t i_last;
i_last = getFilePointer();
vlc_mutex_lock( &p_input->stream.stream_lock ); vlc_mutex_lock( &p_input->stream.stream_lock );
switch( mode ) switch( mode )
...@@ -163,10 +166,12 @@ void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode ) ...@@ -163,10 +166,12 @@ void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
i_pos = p_input->stream.p_selected_area->i_size - i_offset; i_pos = p_input->stream.p_selected_area->i_size - i_offset;
break; break;
default: default:
i_pos= p_input->stream.p_selected_area->i_tell + i_offset; i_pos= i_last + i_offset;
break; break;
} }
if( i_pos < 0 || i_pos > p_input->stream.p_selected_area->i_size )
if( i_pos < 0 ||
( i_pos > p_input->stream.p_selected_area->i_size && p_input->stream.p_selected_area->i_size != 0 ) )
{ {
msg_Err( p_input, "seeking to wrong place (i_pos=%lld)", i_pos ); msg_Err( p_input, "seeking to wrong place (i_pos=%lld)", i_pos );
vlc_mutex_unlock( &p_input->stream.stream_lock ); vlc_mutex_unlock( &p_input->stream.stream_lock );
...@@ -174,8 +179,54 @@ void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode ) ...@@ -174,8 +179,54 @@ void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
} }
vlc_mutex_unlock( &p_input->stream.stream_lock ); vlc_mutex_unlock( &p_input->stream.stream_lock );
input_AccessReinit( p_input ); if( i_pos == i_last )
p_input->pf_seek( p_input, i_pos ); {
return;
}
msg_Dbg( p_input, "####################seek new=%lld old=%lld", i_pos, getFilePointer() );
if( p_input->stream.b_seekable &&
( /*p_input->stream.i_method == INPUT_METHOD_FILE ||*/ i_pos < i_last || i_pos - i_last > p_input->i_bufsize / 4 ) )
{
input_AccessReinit( p_input );
p_input->pf_seek( p_input, i_pos );
}
else if( i_pos > i_last )
{
data_packet_t *p_data;
int i_skip = i_pos - i_last;
if( i_skip > 1024 )
{
msg_Warn( p_input, "will skip %d bytes, slow", i_skip );
}
while (i_skip > 0 )
{
int i_read;
i_read = input_SplitBuffer( p_input, &p_data,
__MIN( 4096, i_skip ) );
if( i_read < 0 )
{
msg_Err( p_input, "seek failed" );
return;
}
i_skip -= i_read;
input_DeletePacket( p_input->p_method_data, p_data );
if( i_read == 0 && i_skip > 0 )
{
msg_Err( p_input, "seek failed" );
return;
}
}
}
else
{
msg_Err( p_input, "cannot seek or emulate seek to %lld from %lld", i_pos, i_last );
}
} }
size_t vlc_stream_io_callback::write( const void *p_buffer, size_t i_size ) size_t vlc_stream_io_callback::write( const void *p_buffer, size_t i_size )
...@@ -207,6 +258,9 @@ class EbmlParser ...@@ -207,6 +258,9 @@ class EbmlParser
public: public:
EbmlParser( EbmlStream *es, EbmlElement *el_start ); EbmlParser( EbmlStream *es, EbmlElement *el_start );
~EbmlParser( void ); ~EbmlParser( void );
int SetNext( const EbmlCallbacks & ClassInfos );
void Up( void ); void Up( void );
void Down( void ); void Down( void );
EbmlElement *Get( void ); EbmlElement *Get( void );
...@@ -440,6 +494,7 @@ struct demux_sys_t ...@@ -440,6 +494,7 @@ struct demux_sys_t
mtime_t i_pts; mtime_t i_pts;
vlc_bool_t b_cues;
int i_index; int i_index;
int i_index_max; int i_index_max;
mkv_index_t *index; mkv_index_t *index;
...@@ -447,6 +502,27 @@ struct demux_sys_t ...@@ -447,6 +502,27 @@ struct demux_sys_t
#define MKVD_TIMECODESCALE 1000000 #define MKVD_TIMECODESCALE 1000000
static void IndexAppendCluster( input_thread_t *p_input, KaxCluster *cluster )
{
demux_sys_t *p_sys = p_input->p_demux_data;
#define idx p_sys->index[p_sys->i_index]
idx.i_track = -1;
idx.i_block_number= -1;
idx.i_position = cluster->GetElementPosition();
idx.i_time = -1;
idx.b_key = VLC_TRUE;
p_sys->i_index++;
if( p_sys->i_index >= p_sys->i_index_max )
{
p_sys->i_index_max += 1024;
p_sys->index = (mkv_index_t*)realloc( p_sys->index, sizeof( mkv_index_t ) * p_sys->i_index_max );
}
#undef idx
}
/***************************************************************************** /*****************************************************************************
* Activate: initializes matroska demux structures * Activate: initializes matroska demux structures
*****************************************************************************/ *****************************************************************************/
...@@ -486,12 +562,6 @@ static int Activate( vlc_object_t * p_this ) ...@@ -486,12 +562,6 @@ static int Activate( vlc_object_t * p_this )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( p_input->stream.i_method != INPUT_METHOD_FILE || !p_input->stream.b_seekable )
{
msg_Err( p_input, "can only read matroska over seekable file yet" );
return VLC_EGENERIC;
}
p_input->p_demux_data = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) ); p_input->p_demux_data = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
memset( p_sys, 0, sizeof( demux_sys_t ) ); memset( p_sys, 0, sizeof( demux_sys_t ) );
...@@ -505,6 +575,7 @@ static int Activate( vlc_object_t * p_this ) ...@@ -505,6 +575,7 @@ static int Activate( vlc_object_t * p_this )
p_sys->i_cues_position = -1; p_sys->i_cues_position = -1;
p_sys->i_chapters_position = -1; p_sys->i_chapters_position = -1;
p_sys->b_cues = VLC_FALSE;
p_sys->i_index = 0; p_sys->i_index = 0;
p_sys->i_index_max = 1024; p_sys->i_index_max = 1024;
p_sys->index = (mkv_index_t*)malloc( sizeof( mkv_index_t ) * p_sys->i_index_max ); p_sys->index = (mkv_index_t*)malloc( sizeof( mkv_index_t ) * p_sys->i_index_max );
...@@ -900,9 +971,10 @@ static int Activate( vlc_object_t * p_this ) ...@@ -900,9 +971,10 @@ static int Activate( vlc_object_t * p_this )
else if( EbmlId( *el1 ) == KaxCluster::ClassInfos.GlobalId ) else if( EbmlId( *el1 ) == KaxCluster::ClassInfos.GlobalId )
{ {
msg_Dbg( p_input, "| + Cluster" ); msg_Dbg( p_input, "| + Cluster" );
p_sys->cluster = (KaxCluster*)el1; p_sys->cluster = (KaxCluster*)el1;
p_sys->ep->Down();
p_sys->ep->Down();
/* stop parsing the stream */ /* stop parsing the stream */
break; break;
} }
...@@ -969,7 +1041,7 @@ static int Activate( vlc_object_t * p_this ) ...@@ -969,7 +1041,7 @@ static int Activate( vlc_object_t * p_this )
#endif #endif
/* *** Load the cue if found *** */ /* *** Load the cue if found *** */
if( p_sys->i_cues_position >= 0 ) if( p_sys->i_cues_position >= 0 && p_input->stream.b_seekable )
{ {
int64_t i_sav_position = p_sys->in->getFilePointer(); int64_t i_sav_position = p_sys->in->getFilePointer();
EbmlParser *ep; EbmlParser *ep;
...@@ -979,99 +1051,109 @@ static int Activate( vlc_object_t * p_this ) ...@@ -979,99 +1051,109 @@ static int Activate( vlc_object_t * p_this )
p_sys->in->setFilePointer( p_sys->i_cues_position, seek_beginning ); p_sys->in->setFilePointer( p_sys->i_cues_position, seek_beginning );
cues = p_sys->es->FindNextID( KaxCues::ClassInfos, 0xFFFFFFFFL); cues = p_sys->es->FindNextID( KaxCues::ClassInfos, 0xFFFFFFFFL);
ep = new EbmlParser( p_sys->es, cues ); if( cues == NULL )
while( ( el = ep->Get() ) != NULL )
{ {
if( EbmlId( *el ) == KaxCuePoint::ClassInfos.GlobalId ) msg_Err( p_input, "cannot load cues (broken seekhead or file)" );
}
else
{
ep = new EbmlParser( p_sys->es, cues );
while( ( el = ep->Get() ) != NULL )
{ {
if( EbmlId( *el ) == KaxCuePoint::ClassInfos.GlobalId )
{
#define idx p_sys->index[p_sys->i_index] #define idx p_sys->index[p_sys->i_index]
idx.i_track = -1; idx.i_track = -1;
idx.i_block_number= -1; idx.i_block_number= -1;
idx.i_position = -1; idx.i_position = -1;
idx.i_time = -1; idx.i_time = -1;
idx.b_key = VLC_TRUE; idx.b_key = VLC_TRUE;
ep->Down(); ep->Down();
while( ( el = ep->Get() ) != NULL ) while( ( el = ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxCueTime::ClassInfos.GlobalId )
{ {
KaxCueTime &ctime = *(KaxCueTime*)el; if( EbmlId( *el ) == KaxCueTime::ClassInfos.GlobalId )
{
KaxCueTime &ctime = *(KaxCueTime*)el;
ctime.ReadData( p_sys->es->I_O() ); ctime.ReadData( p_sys->es->I_O() );
idx.i_time = uint64( ctime ) * (mtime_t)1000000000 / p_sys->i_timescale; idx.i_time = uint64( ctime ) * (mtime_t)1000000000 / p_sys->i_timescale;
} }
else if( EbmlId( *el ) == KaxCueTrackPositions::ClassInfos.GlobalId ) else if( EbmlId( *el ) == KaxCueTrackPositions::ClassInfos.GlobalId )
{
ep->Down();
while( ( el = ep->Get() ) != NULL )
{ {
if( EbmlId( *el ) == KaxCueTrack::ClassInfos.GlobalId ) ep->Down();
while( ( el = ep->Get() ) != NULL )
{ {
KaxCueTrack &ctrack = *(KaxCueTrack*)el; if( EbmlId( *el ) == KaxCueTrack::ClassInfos.GlobalId )
{
KaxCueTrack &ctrack = *(KaxCueTrack*)el;
ctrack.ReadData( p_sys->es->I_O() ); ctrack.ReadData( p_sys->es->I_O() );
idx.i_track = uint16( ctrack ); idx.i_track = uint16( ctrack );
} }
else if( EbmlId( *el ) == KaxCueClusterPosition::ClassInfos.GlobalId ) else if( EbmlId( *el ) == KaxCueClusterPosition::ClassInfos.GlobalId )
{ {
KaxCueClusterPosition &ccpos = *(KaxCueClusterPosition*)el; KaxCueClusterPosition &ccpos = *(KaxCueClusterPosition*)el;
ccpos.ReadData( p_sys->es->I_O() ); ccpos.ReadData( p_sys->es->I_O() );
idx.i_position = p_sys->segment->GetGlobalPosition( uint64( ccpos ) ); idx.i_position = p_sys->segment->GetGlobalPosition( uint64( ccpos ) );
} }
else if( EbmlId( *el ) == KaxCueBlockNumber::ClassInfos.GlobalId ) else if( EbmlId( *el ) == KaxCueBlockNumber::ClassInfos.GlobalId )
{ {
KaxCueBlockNumber &cbnum = *(KaxCueBlockNumber*)el; KaxCueBlockNumber &cbnum = *(KaxCueBlockNumber*)el;
cbnum.ReadData( p_sys->es->I_O() ); cbnum.ReadData( p_sys->es->I_O() );
idx.i_block_number = uint32( cbnum ); idx.i_block_number = uint32( cbnum );
} }
else else
{ {
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() ); msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
}
} }
ep->Up();
}
else
{
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
} }
ep->Up();
} }
else ep->Up();
msg_Dbg( p_input, " * added time=%lld pos=%lld track=%d bnum=%d",
idx.i_time, idx.i_position, idx.i_track, idx.i_block_number );
p_sys->i_index++;
if( p_sys->i_index >= p_sys->i_index_max )
{ {
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() ); p_sys->i_index_max += 1024;
p_sys->index = (mkv_index_t*)realloc( p_sys->index, sizeof( mkv_index_t ) * p_sys->i_index_max );
} }
#undef idx
} }
ep->Up(); else
msg_Dbg( p_input, " * added time=%lld pos=%lld track=%d bnum=%d",
idx.i_time, idx.i_position, idx.i_track, idx.i_block_number );
p_sys->i_index++;
if( p_sys->i_index >= p_sys->i_index_max )
{ {
p_sys->i_index_max += 1024; msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
p_sys->index = (mkv_index_t*)realloc( p_sys->index, sizeof( mkv_index_t ) * p_sys->i_index_max );
} }
#undef idx
}
else
{
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
} }
delete ep;
delete cues;
p_sys->b_cues = VLC_TRUE;
} }
delete ep;
delete cues;
msg_Dbg( p_input, "loading cues done." ); msg_Dbg( p_input, "loading cues done." );
p_sys->in->setFilePointer( i_sav_position, seek_beginning ); p_sys->in->setFilePointer( i_sav_position, seek_beginning );
} }
if( p_sys->i_index <= 0 ) if( !p_sys->b_cues || p_sys->i_index <= 0 )
{ {
/* FIXME FIXME FIXME */ msg_Warn( p_input, "no cues/empty cues found -> seek won't be precise" );
msg_Warn( p_input, "no cues found -> unseekable stream for now FIXME" );
p_input->stream.b_seekable = VLC_FALSE; IndexAppendCluster( p_input, p_sys->cluster );
/* FIXME FIXME FIXME */
p_sys->b_cues = VLC_FALSE;
} }
/* Create one program */ /* Create one program */
...@@ -1468,6 +1550,14 @@ static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_r ...@@ -1468,6 +1550,14 @@ static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_r
if( el == NULL && *pp_block != NULL ) if( el == NULL && *pp_block != NULL )
{ {
/* update the index */
#define idx p_sys->index[p_sys->i_index - 1]
if( p_sys->i_index > 0 && idx.i_time == -1 )
{
idx.i_time = (*pp_block)->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
idx.b_key = *pi_ref1 == -1 ? VLC_TRUE : VLC_FALSE;
}
#undef idx
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -1488,6 +1578,14 @@ static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_r ...@@ -1488,6 +1578,14 @@ static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_r
if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId ) if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
{ {
p_sys->cluster = (KaxCluster*)el; p_sys->cluster = (KaxCluster*)el;
/* add it to the index */
if( p_sys->i_index == 0 ||
( p_sys->i_index > 0 && p_sys->index[p_sys->i_index - 1].i_position < p_sys->cluster->GetElementPosition() ) )
{
IndexAppendCluster( p_input, p_sys->cluster );
}
p_sys->ep->Down(); p_sys->ep->Down();
} }
else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId ) else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
...@@ -1722,28 +1820,81 @@ static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent) ...@@ -1722,28 +1820,81 @@ static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent)
int i_track; int i_track;
msg_Dbg( p_input, "seek request to %lld (%d%%)", i_date, i_percent ); msg_Dbg( p_input, "seek request to %lld (%d%%)", i_date, i_percent );
if( i_date < 0 && i_percent < 0 )
{
return;
}
delete p_sys->ep;
p_sys->ep = new EbmlParser( p_sys->es, p_sys->segment );
p_sys->cluster = NULL;
for( i_index = 0; i_index < p_sys->i_index; i_index++ ) /* seek without index or without date */
if( !p_sys->b_cues || i_date < 0 )
{ {
if( p_sys->index[i_index].i_time >= i_date ) int64_t i_pos = i_percent * p_input->stream.p_selected_area->i_size / 100;
msg_Warn( p_input, "imprecise way of seeking" );
for( i_index = 0; i_index < p_sys->i_index; i_index++ )
{ {
break; if( p_sys->index[i_index].i_position >= i_pos)
{
break;
}
}
if( i_index == p_sys->i_index )
{
i_index--;
} }
}
if( i_index > 0 ) p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
{
i_index--;
}
msg_Dbg( p_input, "seek got %lld (%d%%)", if( p_sys->index[i_index].i_position < i_pos )
p_sys->index[i_index].i_time, (int)(100 * p_sys->index[i_index].i_position /p_input->stream.p_selected_area->i_size ) ); {
EbmlElement *el;
delete p_sys->ep; msg_Warn( p_input, "searching for cluster, could take some time" );
p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning ); /* search a cluster */
while( ( el = p_sys->ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
{
KaxCluster *cluster = (KaxCluster*)el;
p_sys->ep = new EbmlParser( p_sys->es, p_sys->segment ); /* add it to the index */
IndexAppendCluster( p_input, cluster );
if( cluster->GetElementPosition() >= i_pos )
{
p_sys->cluster = cluster;
p_sys->ep->Down();
break;
}
}
}
}
}
else
{
for( i_index = 0; i_index < p_sys->i_index; i_index++ )
{
if( p_sys->index[i_index].i_time >= i_date )
{
break;
}
}
if( i_index > 0 )
{
i_index--;
}
msg_Dbg( p_input, "seek got %lld (%d%%)",
p_sys->index[i_index].i_time, (int)(100 * p_sys->index[i_index].i_position /p_input->stream.p_selected_area->i_size ) );
p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
}
/* now parse until key frame */ /* now parse until key frame */
#define tk p_sys->track[i_track] #define tk p_sys->track[i_track]
...@@ -1757,7 +1908,6 @@ static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent) ...@@ -1757,7 +1908,6 @@ static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent)
} }
} }
while( i_track_skipping > 0 ) while( i_track_skipping > 0 )
{ {
if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) ) if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
...@@ -1804,6 +1954,8 @@ static int Demux( input_thread_t * p_input ) ...@@ -1804,6 +1954,8 @@ static int Demux( input_thread_t * p_input )
{ {
demux_sys_t *p_sys = p_input->p_demux_data; demux_sys_t *p_sys = p_input->p_demux_data;
mtime_t i_start_pts; mtime_t i_start_pts;
int i_block_count = 0;
KaxBlock *block; KaxBlock *block;
int64_t i_block_duration; int64_t i_block_duration;
int64_t i_block_ref1; int64_t i_block_ref1;
...@@ -1812,21 +1964,28 @@ static int Demux( input_thread_t * p_input ) ...@@ -1812,21 +1964,28 @@ static int Demux( input_thread_t * p_input )
if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT ) if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
{ {
mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 ); mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
mtime_t i_date; mtime_t i_date = -1;
int i_percent; int i_percent = -1;
i_date = (mtime_t)1000000 *
(mtime_t)i_duration*
(mtime_t)p_sys->in->getFilePointer() /
(mtime_t)p_input->stream.p_selected_area->i_size;
i_percent = 100 * p_sys->in->getFilePointer() / if( i_duration > 0 )
p_input->stream.p_selected_area->i_size; {
i_date = (mtime_t)1000000 *
(mtime_t)i_duration*
(mtime_t)p_sys->in->getFilePointer() /
(mtime_t)p_input->stream.p_selected_area->i_size;
}
if( p_input->stream.p_selected_area->i_size > 0 )
{
i_percent = 100 * p_sys->in->getFilePointer() /
p_input->stream.p_selected_area->i_size;
}
msg_Dbg( p_input, "call Seek" );
Seek( p_input, i_date, i_percent); Seek( p_input, i_date, i_percent);
msg_Dbg( p_input, "Seek end" );
} }
i_start_pts = p_sys->i_pts; i_start_pts = -1;
for( ;; ) for( ;; )
{ {
...@@ -1857,12 +2016,13 @@ static int Demux( input_thread_t * p_input ) ...@@ -1857,12 +2016,13 @@ static int Demux( input_thread_t * p_input )
BlockDecode( p_input, block, i_pts, i_block_duration ); BlockDecode( p_input, block, i_pts, i_block_duration );
delete block; delete block;
i_block_count++;
if( i_start_pts == -1 ) if( i_start_pts == -1 )
{ {
i_start_pts = p_sys->i_pts; i_start_pts = p_sys->i_pts;
} }
else if( p_sys->i_pts > i_start_pts + (mtime_t)100000) else if( p_sys->i_pts > i_start_pts + (mtime_t)100000 || i_block_count > 5 )
{ {
return 1; return 1;
} }
......
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