Commit e265882b authored by Laurent Aimar's avatar Laurent Aimar

* mkv: basic seek support (only with file with cues, it doesn't search key

 frame, ...)
        clean up.
parent 5c196547
......@@ -2,7 +2,7 @@
* mkv.cpp : matroska demuxer
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: mkv.cpp,v 1.1 2003/06/22 07:39:39 fenrir Exp $
* $Id: mkv.cpp,v 1.2 2003/06/22 12:27:16 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
......@@ -80,6 +80,10 @@ static int Demux ( input_thread_t * );
* Module descriptor
*****************************************************************************/
vlc_module_begin();
add_category_hint( N_("mkv-demuxer"), NULL, VLC_TRUE );
add_bool( "mkv-index", 0, NULL,
N_("Create index if no cues found"),
N_("Create index if no cues found"), VLC_TRUE );
set_description( _("mka/mkv stream demuxer" ) );
set_capability( "demux", 50 );
set_callbacks( Activate, Deactivate );
......@@ -214,7 +218,7 @@ class EbmlParser
EbmlStream *m_es;
int mi_level;
EbmlElement *m_el[5];
EbmlElement *m_el[6];
EbmlElement *m_got;
......@@ -231,7 +235,7 @@ EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start )
m_got = NULL;
m_el[0] = el_start;
for( i = 1; i < 5; i++ )
for( i = 1; i < 6; i++ )
{
m_el[i] = NULL;
}
......@@ -354,8 +358,9 @@ static vlc_fourcc_t __GetFOURCC( uint8_t *p )
return VLC_FOURCC( p[0], p[1], p[2], p[3] );
}
/*****************************************************************************
* Definitions of structures and functions used by this plugins
* definitions of structures and functions used by this plugins
*****************************************************************************/
typedef struct
{
......@@ -396,26 +401,46 @@ typedef struct
} mkv_track_t;
typedef struct
{
int i_track;
int i_block_number;
int64_t i_position;
int64_t i_time;
vlc_bool_t b_key;
} mkv_index_t;
struct demux_sys_t
{
vlc_stream_io_callback *in;
EbmlStream *es;
EbmlParser *ep;
KaxSegment *segment;
KaxCluster *cluster;
/* time scale */
uint64_t i_timescale;
uint64_t tc_scale;
/* duration of the segment */
float f_duration;
/* all tracks */
int i_track;
mkv_track_t *track;
mtime_t i_pcr;
/* from seekhead */
int64_t i_cues_position;
int64_t i_chapters_position;
/* current data */
KaxSegment *segment;
KaxCluster *cluster;
mtime_t i_last_pts;
mtime_t i_pts;
uint64_t i_cluster_tc;
int i_index;
int i_index_max;
mkv_index_t *index;
};
#define MKVD_TIMECODESCALE 1000000
......@@ -464,10 +489,6 @@ static int Activate( vlc_object_t * p_this )
msg_Err( p_input, "can only read matroska over seekable file yet" );
return VLC_EGENERIC;
}
/* FIXME FIXME FIXME */
p_input->stream.b_seekable = VLC_FALSE;
/* FIXME FIXME FIXME */
p_input->p_demux_data = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
memset( p_sys, 0, sizeof( demux_sys_t ) );
......@@ -475,11 +496,16 @@ static int Activate( vlc_object_t * p_this )
p_sys->in = new vlc_stream_io_callback( p_input );
p_sys->es = new EbmlStream( *p_sys->in );
p_sys->f_duration = -1;
p_sys->tc_scale = MKVD_TIMECODESCALE;
p_sys->i_timescale = MKVD_TIMECODESCALE;
p_sys->i_track = 0;
p_sys->track = (mkv_track_t*)malloc( sizeof( mkv_track_t ) );
p_sys->i_pcr = 0;
p_sys->i_last_pts = 0;
p_sys->i_pts = 0;
p_sys->i_cues_position = -1;
p_sys->i_chapters_position = -1;
p_sys->i_index = 0;
p_sys->i_index_max = 1024;
p_sys->index = (mkv_index_t*)malloc( sizeof( mkv_index_t ) * p_sys->i_index_max );
if( p_sys->es == NULL )
{
......@@ -527,9 +553,9 @@ static int Activate( vlc_object_t * p_this )
KaxTimecodeScale &tcs = *(KaxTimecodeScale*)el2;
tcs.ReadData( p_sys->es->I_O() );
p_sys->tc_scale = uint64(tcs);
p_sys->i_timescale = uint64(tcs);
msg_Dbg( p_input, "| | + TimecodeScale=%lld", p_sys->tc_scale );
msg_Dbg( p_input, "| | + TimecodeScale=%lld", p_sys->i_timescale );
}
else if( EbmlId( *el2 ) == KaxDuration::ClassInfos.GlobalId )
{
......@@ -809,6 +835,61 @@ static int Activate( vlc_object_t * p_this )
else if( EbmlId( *el1 ) == KaxSeekHead::ClassInfos.GlobalId )
{
msg_Dbg( p_input, "| + Seek head" );
p_sys->ep->Down();
while( ( el = p_sys->ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxSeek::ClassInfos.GlobalId )
{
EbmlId id = EbmlVoid::ClassInfos.GlobalId;
int64_t i_pos = -1;
//msg_Dbg( p_input, "| | + Seek" );
p_sys->ep->Down();
while( ( el = p_sys->ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxSeekID::ClassInfos.GlobalId )
{
KaxSeekID &sid = *(KaxSeekID*)el;
sid.ReadData( p_sys->es->I_O() );
id = EbmlId( sid.GetBuffer(), sid.GetSize() );
}
else if( EbmlId( *el ) == KaxSeekPosition::ClassInfos.GlobalId )
{
KaxSeekPosition &spos = *(KaxSeekPosition*)el;
spos.ReadData( p_sys->es->I_O() );
i_pos = uint64( spos );
}
else
{
msg_Dbg( p_input, "| | | + Unknow (%s)", typeid(*el).name() );
}
}
p_sys->ep->Up();
if( i_pos >= 0 )
{
if( id == KaxCues::ClassInfos.GlobalId )
{
msg_Dbg( p_input, "| | | = cues at %lld", i_pos );
p_sys->i_cues_position = p_sys->segment->GetGlobalPosition( i_pos );
}
else if( id == KaxChapters::ClassInfos.GlobalId )
{
msg_Dbg( p_input, "| | | = chapters at %lld", i_pos );
p_sys->i_chapters_position = p_sys->segment->GetGlobalPosition( i_pos );
}
}
}
else
{
msg_Dbg( p_input, "| | + Unknow (%s)", typeid(*el).name() );
}
}
p_sys->ep->Up();
}
else if( EbmlId( *el1 ) == KaxCues::ClassInfos.GlobalId )
{
......@@ -847,6 +928,150 @@ static int Activate( vlc_object_t * p_this )
goto error;
}
if( p_sys->i_chapters_position >= 0 )
{
msg_Warn( p_input, "chapters unsupported" );
}
#if 0
if( p_sys->i_cues_position == -1 && config_GetInt( p_input, "mkv-index" ) != 0 )
{
int64_t i_sav_position = p_sys->in->getFilePointer();
EbmlParser *ep;
/* parse to find cues gathering info on all clusters */
msg_Dbg( p_input, "no cues referenced, looking for one" );
/* ugly but like easier */
p_sys->in->setFilePointer( p_sys->cluster->GetElementPosition(), seek_beginning );
ep = new EbmlParser( p_sys->es, p_sys->segment );
while( ( el = ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
{
int64_t i_pos = el->GetElementPosition();
msg_Dbg( p_input, "found a cluster at %lld", i_pos );
}
else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
{
msg_Dbg( p_input, "found a Cues !" );
}
}
delete ep;
msg_Dbg( p_input, "lookind done." );
p_sys->in->setFilePointer( i_sav_position, seek_beginning );
}
#endif
/* *** Load the cue if found *** */
if( p_sys->i_cues_position >= 0 )
{
int64_t i_sav_position = p_sys->in->getFilePointer();
EbmlParser *ep;
EbmlElement *cues;
msg_Dbg( p_input, "loading cues" );
p_sys->in->setFilePointer( p_sys->i_cues_position, seek_beginning );
cues = p_sys->es->FindNextID( KaxCues::ClassInfos, 0xFFFFFFFFL);
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]
idx.i_track = -1;
idx.i_block_number= -1;
idx.i_position = -1;
idx.i_time = -1;
idx.b_key = VLC_TRUE;
ep->Down();
while( ( el = ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxCueTime::ClassInfos.GlobalId )
{
KaxCueTime &ctime = *(KaxCueTime*)el;
ctime.ReadData( p_sys->es->I_O() );
idx.i_time = uint64( ctime ) * (mtime_t)1000000000 / p_sys->i_timescale;
}
else if( EbmlId( *el ) == KaxCueTrackPositions::ClassInfos.GlobalId )
{
ep->Down();
while( ( el = ep->Get() ) != NULL )
{
if( EbmlId( *el ) == KaxCueTrack::ClassInfos.GlobalId )
{
KaxCueTrack &ctrack = *(KaxCueTrack*)el;
ctrack.ReadData( p_sys->es->I_O() );
idx.i_track = uint16( ctrack );
}
else if( EbmlId( *el ) == KaxCueClusterPosition::ClassInfos.GlobalId )
{
KaxCueClusterPosition &ccpos = *(KaxCueClusterPosition*)el;
ccpos.ReadData( p_sys->es->I_O() );
idx.i_position = p_sys->segment->GetGlobalPosition( uint64( ccpos ) );
}
else if( EbmlId( *el ) == KaxCueBlockNumber::ClassInfos.GlobalId )
{
KaxCueBlockNumber &cbnum = *(KaxCueBlockNumber*)el;
cbnum.ReadData( p_sys->es->I_O() );
idx.i_block_number = uint32( cbnum );
}
else
{
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
}
}
ep->Up();
}
else
{
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
}
}
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 )
{
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
}
else
{
msg_Dbg( p_input, " * Unknow (%s)", typeid(*el).name() );
}
}
delete ep;
delete cues;
msg_Dbg( p_input, "loading cues done." );
p_sys->in->setFilePointer( i_sav_position, seek_beginning );
}
if( p_sys->i_index <= 0 )
{
/* FIXME FIXME FIXME */
msg_Warn( p_input, "no cues found -> unseekable stream for now FIXME" );
p_input->stream.b_seekable = VLC_FALSE;
/* FIXME FIXME FIXME */
}
/* Create one program */
vlc_mutex_lock( &p_input->stream.stream_lock );
if( input_InitStream( p_input, 0 ) == -1)
......@@ -862,7 +1087,15 @@ static int Activate( vlc_object_t * p_this )
goto error;
}
p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
p_input->stream.i_mux_rate = 0;
if( p_sys->f_duration > 1001.0 )
{
mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000.0 );
p_input->stream.i_mux_rate = p_input->stream.p_selected_area->i_size / 50 / i_duration;
}
else
{
p_input->stream.i_mux_rate = 0;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* add all es */
......@@ -883,7 +1116,7 @@ static int Activate( vlc_object_t * p_this )
tk.psz_language, 0 );
if( !strcmp( tk.psz_codec, "V_MS/VFW/FOURCC" ) )
{
if( tk.i_extra_data < sizeof( BITMAPINFOHEADER ) )
if( tk.i_extra_data < (int)sizeof( BITMAPINFOHEADER ) )
{
msg_Err( p_input, "missing/invalid BITMAPINFOHEADER" );
tk.i_codec = VLC_FOURCC( 'u', 'n', 'd', 'f' );
......@@ -938,7 +1171,7 @@ static int Activate( vlc_object_t * p_this )
}
else if( !strcmp( tk.psz_codec, "A_MS/ACM" ) )
{
if( tk.i_extra_data < sizeof( WAVEFORMATEX ) )
if( tk.i_extra_data < (int)sizeof( WAVEFORMATEX ) )
{
msg_Err( p_input, "missing/invalid WAVEFORMATEX" );
tk.i_codec = VLC_FOURCC( 'u', 'n', 'd', 'f' );
......@@ -947,13 +1180,13 @@ static int Activate( vlc_object_t * p_this )
{
WAVEFORMATEX *p_wf = (WAVEFORMATEX*)tk.p_extra_data;
p_wf->wFormatTag = GetWLE( p_wf->wFormatTag );
p_wf->nChannels = GetWLE( p_wf->nChannels );
p_wf->nSamplesPerSec = GetDWLE( p_wf->nSamplesPerSec );
p_wf->nAvgBytesPerSec = GetDWLE( p_wf->nAvgBytesPerSec );
p_wf->nBlockAlign = GetWLE( p_wf->nBlockAlign );
p_wf->wBitsPerSample = GetWLE( p_wf->wBitsPerSample );
p_wf->cbSize = GetWLE( p_wf->cbSize );
p_wf->wFormatTag = GetWLE( &p_wf->wFormatTag );
p_wf->nChannels = GetWLE( &p_wf->nChannels );
p_wf->nSamplesPerSec = GetDWLE( &p_wf->nSamplesPerSec );
p_wf->nAvgBytesPerSec = GetDWLE( &p_wf->nAvgBytesPerSec );
p_wf->nBlockAlign = GetWLE( &p_wf->nBlockAlign );
p_wf->wBitsPerSample = GetWLE( &p_wf->wBitsPerSample );
p_wf->cbSize = GetWLE( &p_wf->cbSize );
switch( p_wf->wFormatTag )
{
......@@ -1210,6 +1443,41 @@ static void Deactivate( vlc_object_t *p_this )
free( p_sys );
}
static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent)
{
demux_sys_t *p_sys = p_input->p_demux_data;
int i_index;
msg_Dbg( p_input, "seek request to %lld (%d%%)", i_date, i_percent );
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 ) );
delete p_sys->ep;
p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
p_sys->ep = new EbmlParser( p_sys->es, p_sys->segment );
// p_sys->cluster = p_sys->es->FindNextElement( p_sys->segment->Generic().Context,
// i_ulev, 0xFFFFFFFFL, true, 1);
}
/*****************************************************************************
* Demux: reads and demuxes data packets
*****************************************************************************
......@@ -1218,7 +1486,7 @@ static void Deactivate( vlc_object_t *p_this )
static int Demux( input_thread_t * p_input )
{
demux_sys_t *p_sys = p_input->p_demux_data;
mtime_t i_start_pts = p_sys->i_last_pts;
mtime_t i_start_pts = p_sys->i_pts;
KaxBlock *block = NULL;
int64_t i_block_duration = -1;
int64_t i_block_ref1 = 0;
......@@ -1226,7 +1494,20 @@ static int Demux( input_thread_t * p_input )
if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
{
msg_Err( p_input, "DO NO SEEK YET" );
mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
mtime_t i_date;
int i_percent;
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() /
p_input->stream.p_selected_area->i_size;
Seek( p_input, i_date, i_percent);
i_start_pts = -1;
}
for( ;; )
......@@ -1251,13 +1532,13 @@ static int Demux( input_thread_t * p_input )
}
}
p_sys->i_last_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->tc_scale;
p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
if( p_sys->i_last_pts > 0 )
if( p_sys->i_pts > 0 )
{
input_ClockManageRef( p_input,
p_input->stream.p_selected_program,
p_sys->i_last_pts * 9 / 100 );
p_sys->i_pts * 9 / 100 );
}
if( i_track >= p_sys->i_track )
......@@ -1267,7 +1548,7 @@ static int Demux( input_thread_t * p_input )
else if( tk.p_es->p_decoder_fifo != NULL )
{
pes_packet_t *p_pes;
int i;
unsigned int i;
if( ( p_pes = input_NewPES( p_input->p_method_data ) ) != NULL )
{
......@@ -1275,7 +1556,7 @@ static int Demux( input_thread_t * p_input )
i_pts = input_ClockGetTS( p_input,
p_input->stream.p_selected_program,
p_sys->i_last_pts * 9 / 100 );
p_sys->i_pts * 9 / 100 );
p_pes->i_pts = i_pts;
p_pes->i_dts = i_pts;
......@@ -1285,7 +1566,7 @@ static int Demux( input_thread_t * p_input )
if( i_block_duration > 0 )
{
/* FIXME not sure about that */
p_pes->i_dts += i_block_duration * 1000;// * (mtime_t) 1000 / p_sys->tc_scale;
p_pes->i_dts += i_block_duration * 1000;// * (mtime_t) 1000 / p_sys->i_timescale;
}
else
{
......@@ -1359,7 +1640,11 @@ static int Demux( input_thread_t * p_input )
delete block;
block = NULL;
if( p_sys->i_last_pts > i_start_pts + (mtime_t)100000)
if( i_start_pts == -1 )
{
i_start_pts = p_sys->i_pts;
}
else if( p_sys->i_pts > i_start_pts + (mtime_t)100000)
{
return 1;
}
......@@ -1401,8 +1686,7 @@ static int Demux( input_thread_t * p_input )
KaxClusterTimecode &ctc = *(KaxClusterTimecode*)el;
ctc.ReadData( p_sys->es->I_O() );
p_sys->i_cluster_tc = uint64( ctc );
p_sys->cluster->InitTimecode( p_sys->i_cluster_tc, p_sys->tc_scale );
p_sys->cluster->InitTimecode( uint64( ctc ), p_sys->i_timescale );
}
else if( EbmlId( *el ) == KaxBlockGroup::ClassInfos.GlobalId )
{
......
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