Commit ac2a4ae1 authored by Laurent Aimar's avatar Laurent Aimar

* mkv: better seeking support.

parent a4b28e62
......@@ -2,7 +2,7 @@
* mkv.cpp : matroska demuxer
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: mkv.cpp,v 1.2 2003/06/22 12:27:16 fenrir Exp $
* $Id: mkv.cpp,v 1.3 2003/06/22 14:36:34 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
......@@ -399,6 +399,8 @@ typedef struct
int i_data_init;
uint8_t *p_data_init;
/* hack : it's for seek */
vlc_bool_t b_search_keyframe;
} mkv_track_t;
typedef struct
......@@ -1443,86 +1445,124 @@ 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)
static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_ref1, int64_t *pi_ref2, int64_t *pi_duration )
{
demux_sys_t *p_sys = p_input->p_demux_data;
int i_index;
*pp_block = NULL;
*pi_ref1 = -1;
*pi_ref2 = -1;
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 )
for( ;; )
{
break;
}
}
EbmlElement *el;
int i_level;
if( i_index > 0 )
if( p_input->b_die )
{
i_index--;
return VLC_EGENERIC;
}
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 );
el = p_sys->ep->Get();
i_level = p_sys->ep->GetLevel();
// p_sys->cluster = p_sys->es->FindNextElement( p_sys->segment->Generic().Context,
// i_ulev, 0xFFFFFFFFL, true, 1);
if( el == NULL && *pp_block != NULL )
{
return VLC_SUCCESS;
}
}
if( el == NULL )
{
if( p_sys->ep->GetLevel() > 1 )
{
p_sys->ep->Up();
continue;
}
msg_Warn( p_input, "EOF" );
return VLC_EGENERIC;
}
/*****************************************************************************
* Demux: reads and demuxes data packets
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, 1 otherwise
*****************************************************************************/
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_pts;
KaxBlock *block = NULL;
int64_t i_block_duration = -1;
int64_t i_block_ref1 = 0;
int64_t i_block_ref2 = 0;
/* do parsing */
if( i_level == 1 )
{
if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
{
p_sys->cluster = (KaxCluster*)el;
p_sys->ep->Down();
}
else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
{
msg_Warn( p_input, "find KaxCues FIXME" );
return VLC_EGENERIC;
}
else
{
msg_Dbg( p_input, "unknown (%s)", typeid( el ).name() );
}
}
else if( i_level == 2 )
{
if( EbmlId( *el ) == KaxClusterTimecode::ClassInfos.GlobalId )
{
KaxClusterTimecode &ctc = *(KaxClusterTimecode*)el;
if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
ctc.ReadData( p_sys->es->I_O() );
p_sys->cluster->InitTimecode( uint64( ctc ), p_sys->i_timescale );
}
else if( EbmlId( *el ) == KaxBlockGroup::ClassInfos.GlobalId )
{
mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
mtime_t i_date;
int i_percent;
p_sys->ep->Down();
}
}
else if( i_level == 3 )
{
if( EbmlId( *el ) == KaxBlock::ClassInfos.GlobalId )
{
*pp_block = (KaxBlock*)el;
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;
(*pp_block)->ReadData( p_sys->es->I_O() );
(*pp_block)->SetParent( *p_sys->cluster );
i_percent = 100 * p_sys->in->getFilePointer() /
p_input->stream.p_selected_area->i_size;
p_sys->ep->Keep();
}
else if( EbmlId( *el ) == KaxBlockDuration::ClassInfos.GlobalId )
{
KaxBlockDuration &dur = *(KaxBlockDuration*)el;
Seek( p_input, i_date, i_percent);
i_start_pts = -1;
dur.ReadData( p_sys->es->I_O() );
*pi_duration = uint64( dur );
}
else if( EbmlId( *el ) == KaxReferenceBlock::ClassInfos.GlobalId )
{
KaxReferenceBlock &ref = *(KaxReferenceBlock*)el;
for( ;; )
ref.ReadData( p_sys->es->I_O() );
if( *pi_ref1 == -1 )
{
EbmlElement *el;
int i_level;
*pi_ref1 = int64( ref );
}
else
{
*pi_ref2 = int64( ref );
}
}
}
else
{
msg_Err( p_input, "invalid level = %d", i_level );
return VLC_EGENERIC;
}
}
}
el = p_sys->ep->Get();
i_level = p_sys->ep->GetLevel();
static void BlockDecode( input_thread_t *p_input, KaxBlock *block, mtime_t i_pts, mtime_t i_duration )
{
demux_sys_t *p_sys = p_input->p_demux_data;
/* First send last collected blocks (before ep->Up, because of cluster) */
if( el == NULL && block != NULL )
{
/* we have data */
int i_track;
pes_packet_t *p_pes;
unsigned int i;
#define tk p_sys->track[i_track]
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
{
......@@ -1532,41 +1572,35 @@ static int Demux( input_thread_t * p_input )
}
}
p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
if( p_sys->i_pts > 0 )
{
input_ClockManageRef( p_input,
p_input->stream.p_selected_program,
p_sys->i_pts * 9 / 100 );
}
if( i_track >= p_sys->i_track )
{
msg_Err( p_input, "invalid track number=%d", block->TrackNum() );
delete block;
return;
}
else if( tk.p_es->p_decoder_fifo != NULL )
if( tk.p_es->p_decoder_fifo == NULL )
{
pes_packet_t *p_pes;
unsigned int i;
delete block;
return;
}
if( ( p_pes = input_NewPES( p_input->p_method_data ) ) != NULL )
if( ( p_pes = input_NewPES( p_input->p_method_data ) ) == NULL )
{
mtime_t i_pts;
msg_Err( p_input, "cannot allocate PES" );
}
i_pts = input_ClockGetTS( p_input,
p_input->stream.p_selected_program,
p_sys->i_pts * 9 / 100 );
p_pes->i_pts = i_pts;
p_pes->i_dts = i_pts;
if( tk.i_cat == SPU_ES )
{
/* special case : dts mean end of display */
if( i_block_duration > 0 )
if( i_duration > 0 )
{
/* FIXME not sure about that */
p_pes->i_dts += i_block_duration * 1000;// * (mtime_t) 1000 / p_sys->i_timescale;
p_pes->i_dts += i_duration * 1000;// * (mtime_t) 1000 / p_sys->i_timescale;
}
else
{
......@@ -1630,109 +1664,167 @@ static int Demux( input_thread_t * p_input )
tk.b_inited = VLC_TRUE;
input_DecodePES( tk.p_es->p_decoder_fifo, p_pes );
}
else
{
tk.b_inited = VLC_FALSE;
}
}
#undef tk
delete block;
block = NULL;
}
if( i_start_pts == -1 )
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;
KaxBlock *block;
int64_t i_block_duration;
int64_t i_block_ref1;
int64_t i_block_ref2;
int i_index;
int i_track_skipping;
int i_track;
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++ )
{
i_start_pts = p_sys->i_pts;
}
else if( p_sys->i_pts > i_start_pts + (mtime_t)100000)
if( p_sys->index[i_index].i_time >= i_date )
{
return 1;
break;
}
}
if( el == NULL )
if( i_index > 0 )
{
if( p_sys->ep->GetLevel() > 1 )
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 );
/* now parse until key frame */
#define tk p_sys->track[i_track]
i_track_skipping = 0;
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
{
p_sys->ep->Up();
continue;
if( tk.i_cat == VIDEO_ES )
{
tk.b_search_keyframe = VLC_TRUE;
i_track_skipping++;
}
msg_Warn( p_input, "EOF" );
return 0;
}
/* do parsing */
if( i_level == 1 )
while( i_track_skipping > 0 )
{
if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
{
p_sys->cluster = (KaxCluster*)el;
p_sys->ep->Down();
msg_Warn( p_input, "cannot get block EOF?" );
return;
}
else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
{
msg_Warn( p_input, "find KaxCues FIXME" );
return 0;
}
else
if( tk.i_number == block->TrackNum() )
{
msg_Dbg( p_input, "unknown (%s)", typeid( el ).name() );
break;
}
}
else if( i_level == 2 )
if( i_track < p_sys->i_track )
{
if( EbmlId( *el ) == KaxClusterTimecode::ClassInfos.GlobalId )
if( tk.i_cat == VIDEO_ES && i_block_ref1 == -1 && tk.b_search_keyframe )
{
KaxClusterTimecode &ctc = *(KaxClusterTimecode*)el;
ctc.ReadData( p_sys->es->I_O() );
p_sys->cluster->InitTimecode( uint64( ctc ), p_sys->i_timescale );
tk.b_search_keyframe = VLC_FALSE;
i_track_skipping--;
}
else if( EbmlId( *el ) == KaxBlockGroup::ClassInfos.GlobalId )
if( tk.i_cat == VIDEO_ES && !tk.b_search_keyframe )
{
p_sys->ep->Down();
BlockDecode( p_input, block, 0, 0 );
}
}
else if( i_level == 3 )
{
if( EbmlId( *el ) == KaxBlock::ClassInfos.GlobalId )
{
block = (KaxBlock*)el;
block->ReadData( p_sys->es->I_O() );
block->SetParent( *p_sys->cluster );
p_sys->ep->Keep();
delete block;
}
else if( EbmlId( *el ) == KaxBlockDuration::ClassInfos.GlobalId )
#undef tk
}
/*****************************************************************************
* Demux: reads and demuxes data packets
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, 1 otherwise
*****************************************************************************/
static int Demux( input_thread_t * p_input )
{
demux_sys_t *p_sys = p_input->p_demux_data;
mtime_t i_start_pts;
KaxBlock *block;
int64_t i_block_duration;
int64_t i_block_ref1;
int64_t i_block_ref2;
if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
{
KaxBlockDuration &dur = *(KaxBlockDuration*)el;
mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
mtime_t i_date;
int i_percent;
dur.ReadData( p_sys->es->I_O() );
i_block_duration = uint64( dur );
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);
}
else if( EbmlId( *el ) == KaxReferenceBlock::ClassInfos.GlobalId )
i_start_pts = p_sys->i_pts;
for( ;; )
{
KaxReferenceBlock &ref = *(KaxReferenceBlock*)el;
mtime_t i_pts;
ref.ReadData( p_sys->es->I_O() );
if( i_block_ref1 == 0 )
if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
{
i_block_ref1 = int64( ref );
msg_Warn( p_input, "cannot get block EOF?" );
return 0;
}
else
p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
if( p_sys->i_pts > 0 )
{
i_block_ref2 = int64( ref );
}
input_ClockManageRef( p_input,
p_input->stream.p_selected_program,
p_sys->i_pts * 9 / 100 );
}
i_pts = input_ClockGetTS( p_input,
p_input->stream.p_selected_program,
p_sys->i_pts * 9 / 100 );
BlockDecode( p_input, block, i_pts, i_block_duration );
delete block;
if( i_start_pts == -1 )
{
i_start_pts = p_sys->i_pts;
}
else
else if( p_sys->i_pts > i_start_pts + (mtime_t)100000)
{
msg_Err( p_input, "invalid level = %d", i_level );
return 0;
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