Commit ac2a4ae1 authored by Laurent Aimar's avatar Laurent Aimar

* mkv: better seeking support.

parent a4b28e62
...@@ -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.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> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* *
...@@ -399,6 +399,8 @@ typedef struct ...@@ -399,6 +399,8 @@ typedef struct
int i_data_init; int i_data_init;
uint8_t *p_data_init; uint8_t *p_data_init;
/* hack : it's for seek */
vlc_bool_t b_search_keyframe;
} mkv_track_t; } mkv_track_t;
typedef struct typedef struct
...@@ -1443,86 +1445,124 @@ static void Deactivate( vlc_object_t *p_this ) ...@@ -1443,86 +1445,124 @@ static void Deactivate( vlc_object_t *p_this )
free( p_sys ); 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; 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( ;; )
for( i_index = 0; i_index < p_sys->i_index; i_index++ )
{
if( p_sys->index[i_index].i_time >= i_date )
{ {
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%%)", el = p_sys->ep->Get();
p_sys->index[i_index].i_time, (int)(100 * p_sys->index[i_index].i_position /p_input->stream.p_selected_area->i_size ) ); i_level = p_sys->ep->GetLevel();
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, if( el == NULL && *pp_block != NULL )
// i_ulev, 0xFFFFFFFFL, true, 1); {
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;
}
/***************************************************************************** /* do parsing */
* Demux: reads and demuxes data packets if( i_level == 1 )
***************************************************************************** {
* Returns -1 in case of error, 0 in case of EOF, 1 otherwise if( EbmlId( *el ) == KaxCluster::ClassInfos.GlobalId )
*****************************************************************************/ {
static int Demux( input_thread_t * p_input ) p_sys->cluster = (KaxCluster*)el;
{ p_sys->ep->Down();
demux_sys_t *p_sys = p_input->p_demux_data; }
mtime_t i_start_pts = p_sys->i_pts; else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
KaxBlock *block = NULL; {
int64_t i_block_duration = -1; msg_Warn( p_input, "find KaxCues FIXME" );
int64_t i_block_ref1 = 0; return VLC_EGENERIC;
int64_t i_block_ref2 = 0; }
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 ); p_sys->ep->Down();
mtime_t i_date; }
int i_percent; }
else if( i_level == 3 )
{
if( EbmlId( *el ) == KaxBlock::ClassInfos.GlobalId )
{
*pp_block = (KaxBlock*)el;
i_date = (mtime_t)1000000 * (*pp_block)->ReadData( p_sys->es->I_O() );
(mtime_t)i_duration* (*pp_block)->SetParent( *p_sys->cluster );
(mtime_t)p_sys->in->getFilePointer() /
(mtime_t)p_input->stream.p_selected_area->i_size;
i_percent = 100 * p_sys->in->getFilePointer() / p_sys->ep->Keep();
p_input->stream.p_selected_area->i_size; }
else if( EbmlId( *el ) == KaxBlockDuration::ClassInfos.GlobalId )
{
KaxBlockDuration &dur = *(KaxBlockDuration*)el;
Seek( p_input, i_date, i_percent); dur.ReadData( p_sys->es->I_O() );
i_start_pts = -1; *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; *pi_ref1 = int64( ref );
int i_level; }
else
{
*pi_ref2 = int64( ref );
}
}
}
else
{
msg_Err( p_input, "invalid level = %d", i_level );
return VLC_EGENERIC;
}
}
}
el = p_sys->ep->Get(); static void BlockDecode( input_thread_t *p_input, KaxBlock *block, mtime_t i_pts, mtime_t i_duration )
i_level = p_sys->ep->GetLevel(); {
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; int i_track;
pes_packet_t *p_pes;
unsigned int i;
#define tk p_sys->track[i_track] #define tk p_sys->track[i_track]
for( i_track = 0; i_track < p_sys->i_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 ) ...@@ -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 ) if( i_track >= p_sys->i_track )
{ {
msg_Err( p_input, "invalid track number=%d", block->TrackNum() ); 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; delete block;
unsigned int i; 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_pts = i_pts;
p_pes->i_dts = i_pts; p_pes->i_dts = i_pts;
if( tk.i_cat == SPU_ES ) if( tk.i_cat == SPU_ES )
{ {
/* special case : dts mean end of display */ /* special case : dts mean end of display */
if( i_block_duration > 0 ) if( i_duration > 0 )
{ {
/* FIXME not sure about that */ /* 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 else
{ {
...@@ -1630,109 +1664,167 @@ static int Demux( input_thread_t * p_input ) ...@@ -1630,109 +1664,167 @@ static int Demux( input_thread_t * p_input )
tk.b_inited = VLC_TRUE; tk.b_inited = VLC_TRUE;
input_DecodePES( tk.p_es->p_decoder_fifo, p_pes ); input_DecodePES( tk.p_es->p_decoder_fifo, p_pes );
}
else
{
tk.b_inited = VLC_FALSE;
}
}
#undef tk #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; if( p_sys->index[i_index].i_time >= i_date )
}
else if( p_sys->i_pts > i_start_pts + (mtime_t)100000)
{ {
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(); if( tk.i_cat == VIDEO_ES )
continue; {
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; msg_Warn( p_input, "cannot get block EOF?" );
p_sys->ep->Down();
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" ); if( tk.i_number == block->TrackNum() )
return 0;
}
else
{ {
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; tk.b_search_keyframe = VLC_FALSE;
i_track_skipping--;
ctc.ReadData( p_sys->es->I_O() );
p_sys->cluster->InitTimecode( uint64( ctc ), p_sys->i_timescale );
} }
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_date = (mtime_t)1000000 *
i_block_duration = uint64( dur ); (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( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
if( i_block_ref1 == 0 )
{ {
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 1;
return 0;
} }
} }
} }
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