Commit 291c6479 authored by Steve Lhomme's avatar Steve Lhomme
Browse files

mkv.cpp: prepare the code to handle seemless segment switching

parent cb48de28
......@@ -362,13 +362,11 @@ public:
bool b_ordered;
};
class demux_sys_t
class matroska_segment_t
{
public:
demux_sys_t()
:in(NULL)
,es(NULL)
,ep(NULL)
matroska_segment_t()
:segment(NULL)
,i_timescale(0)
,f_duration(0.0)
,i_track(0)
......@@ -376,11 +374,7 @@ public:
,i_cues_position(0)
,i_chapters_position(0)
,i_tags_position(0)
,segment(NULL)
,cluster(NULL)
,i_pts(0)
,i_start_pts(0)
,i_chapter_time(0)
,b_cues(false)
,i_index(0)
,i_index_max(0)
......@@ -390,15 +384,11 @@ public:
,psz_segment_filename(NULL)
,psz_title(NULL)
,psz_date_utc(NULL)
,meta(NULL)
,title(NULL)
,i_current_edition(0)
,psz_current_chapter(NULL)
{}
vlc_stream_io_callback *in;
EbmlStream *es;
EbmlParser *ep;
KaxSegment *segment;
/* time scale */
uint64_t i_timescale;
......@@ -415,15 +405,9 @@ public:
int64_t i_chapters_position;
int64_t i_tags_position;
/* current data */
KaxSegment *segment;
KaxCluster *cluster;
KaxSegmentUID segment_uid;
mtime_t i_pts;
mtime_t i_start_pts;
mtime_t i_chapter_time;
vlc_bool_t b_cues;
int i_index;
int i_index_max;
......@@ -436,16 +420,93 @@ public:
char *psz_title;
char *psz_date_utc;
std::vector<chapter_edition_t> editions;
int i_current_edition;
const chapter_item_t *psz_current_chapter;
std::vector<KaxSegmentFamily> families;
chapter_edition_t *Edition()
{
if ( i_current_edition != -1 && i_current_edition < editions.size() )
return &editions[i_current_edition];
return NULL;
}
};
class matroska_stream_t
{
public:
matroska_stream_t()
:in(NULL)
,es(NULL)
,ep(NULL)
,i_current_segment(-1)
{}
~matroska_stream_t()
{
for ( int i=0; i<segments.size(); i++ )
delete segments[i];
if ( in )
delete in;
if ( es )
delete es;
if ( ep )
delete ep;
}
vlc_stream_io_callback *in;
EbmlStream *es;
EbmlParser *ep;
std::vector<matroska_segment_t*> segments;
int i_current_segment;
matroska_segment_t *Segment()
{
if ( i_current_segment != -1 && i_current_segment < segments.size() )
return segments[i_current_segment];
return NULL;
}
};
class demux_sys_t
{
public:
demux_sys_t()
:i_pts(0)
,i_start_pts(0)
,i_chapter_time(0)
,meta(NULL)
,title(NULL)
,i_current_stream(-1)
{}
~demux_sys_t()
{
for (int i=0; i<streams.size(); i++)
delete streams[i];
}
/* current data */
mtime_t i_pts;
mtime_t i_start_pts;
mtime_t i_chapter_time;
vlc_meta_t *meta;
input_title_t *title;
std::vector<KaxSegmentFamily> families;
std::vector<KaxSegment*> family_members;
std::vector<matroska_stream_t*> streams;
int i_current_stream;
std::vector<chapter_edition_t> editions;
int i_current_edition;
const chapter_item_t *psz_current_chapter;
matroska_stream_t *Stream()
{
if ( i_current_stream != -1 && i_current_stream < streams.size() )
return streams[i_current_stream];
return NULL;
}
};
static int Demux ( demux_t * );
......@@ -473,6 +534,8 @@ static int Open( vlc_object_t * p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys;
matroska_stream_t *p_stream;
matroska_segment_t *p_segment;
uint8_t *p_peek;
std::string s_path, s_filename;
int i_upper_lvl;
......@@ -494,40 +557,48 @@ static int Open( vlc_object_t * p_this )
p_demux->pf_control = Control;
p_demux->p_sys = p_sys = new demux_sys_t;
p_sys->in = new vlc_stream_io_callback( p_demux->s );
p_sys->es = new EbmlStream( *p_sys->in );
p_sys->f_duration = -1;
p_sys->i_timescale = MKVD_TIMECODESCALE;
p_sys->i_track = 0;
p_sys->track = (mkv_track_t*)malloc( sizeof( mkv_track_t ) );
p_stream = new matroska_stream_t;
p_segment = new matroska_segment_t;
p_sys->streams.push_back( p_stream );
p_sys->i_current_stream = 0;
p_stream->segments.push_back( p_segment );
p_stream->i_current_segment = 0;
p_stream->in = new vlc_stream_io_callback( p_demux->s );
p_stream->es = new EbmlStream( *p_stream->in );
p_segment->f_duration = -1;
p_segment->i_timescale = MKVD_TIMECODESCALE;
p_segment->i_track = 0;
p_segment->track = (mkv_track_t*)malloc( sizeof( mkv_track_t ) );
p_sys->i_pts = 0;
p_sys->i_cues_position = -1;
p_sys->i_chapters_position = -1;
p_sys->i_tags_position = -1;
p_sys->b_cues = VLC_FALSE;
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 );
p_sys->psz_muxing_application = NULL;
p_sys->psz_writing_application = NULL;
p_sys->psz_segment_filename = NULL;
p_sys->psz_title = NULL;
p_sys->psz_date_utc = NULL;;
p_segment->i_cues_position = -1;
p_segment->i_chapters_position = -1;
p_segment->i_tags_position = -1;
p_segment->b_cues = VLC_FALSE;
p_segment->i_index = 0;
p_segment->i_index_max = 1024;
p_segment->index = (mkv_index_t*)malloc( sizeof( mkv_index_t ) *
p_segment->i_index_max );
p_segment->psz_muxing_application = NULL;
p_segment->psz_writing_application = NULL;
p_segment->psz_segment_filename = NULL;
p_segment->psz_title = NULL;
p_segment->psz_date_utc = NULL;;
p_sys->meta = NULL;
p_sys->title = NULL;
if( p_sys->es == NULL )
if( p_stream->es == NULL )
{
msg_Err( p_demux, "failed to create EbmlStream" );
delete p_sys->in;
delete p_sys;
return VLC_EGENERIC;
}
/* Find the EbmlHead element */
el = p_sys->es->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL);
el = p_stream->es->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL);
if( el == NULL )
{
msg_Err( p_demux, "cannot find EbmlHead" );
......@@ -535,23 +606,23 @@ static int Open( vlc_object_t * p_this )
}
msg_Dbg( p_demux, "EbmlHead" );
/* skip it */
el->SkipData( *p_sys->es, el->Generic().Context );
el->SkipData( *p_stream->es, el->Generic().Context );
delete el;
/* Find a segment */
el = p_sys->es->FindNextID( KaxSegment::ClassInfos, 0xFFFFFFFFL);
el = p_stream->es->FindNextID( KaxSegment::ClassInfos, 0xFFFFFFFFL);
if( el == NULL )
{
msg_Err( p_demux, "cannot find KaxSegment" );
goto error;
}
MkvTree( p_demux, 0, "Segment" );
p_sys->segment = (KaxSegment*)el;
p_sys->cluster = NULL;
p_segment->segment = (KaxSegment*)el;
p_segment->cluster = NULL;
p_sys->ep = new EbmlParser( p_sys->es, el );
p_stream->ep = new EbmlParser( p_stream->es, el );
while( ( el1 = p_sys->ep->Get() ) != NULL )
while( ( el1 = p_stream->ep->Get() ) != NULL )
{
if( MKV_IS_ID( el1, KaxInfo ) )
{
......@@ -573,9 +644,9 @@ static int Open( vlc_object_t * p_this )
{
msg_Dbg( p_demux, "| + Cluster" );
p_sys->cluster = (KaxCluster*)el1;
p_segment->cluster = (KaxCluster*)el1;
p_sys->ep->Down();
p_stream->ep->Down();
/* stop parsing the stream */
break;
}
......@@ -692,15 +763,15 @@ static int Open( vlc_object_t * p_this )
if( MKV_IS_ID( l, KaxSegmentUID ) )
{
p_uid = static_cast<KaxSegmentUID*>(l);
if (p_sys->segment_uid == *p_uid)
if (p_segment->segment_uid == *p_uid)
break;
}
else if( MKV_IS_ID( l, KaxSegmentFamily ) )
{
KaxSegmentFamily *p_fam = static_cast<KaxSegmentFamily*>(l);
std::vector<KaxSegmentFamily>::iterator iter;
for( iter = p_sys->families.begin();
iter != p_sys->families.end();
for( iter = p_segment->families.begin();
iter != p_segment->families.end();
iter++ )
{
if( *iter == *p_fam )
......@@ -739,14 +810,14 @@ static int Open( vlc_object_t * p_this )
}
if( p_sys->cluster == NULL )
if( p_segment->cluster == NULL )
{
msg_Err( p_demux, "cannot find any cluster, damaged file ?" );
goto error;
}
/* *** Load the cue if found *** */
if( p_sys->i_cues_position >= 0 )
if( p_segment->i_cues_position >= 0 )
{
vlc_bool_t b_seekable;
......@@ -757,20 +828,20 @@ static int Open( vlc_object_t * p_this )
}
}
if( !p_sys->b_cues || p_sys->i_index <= 0 )
if( !p_segment->b_cues || p_segment->i_index <= 0 )
{
msg_Warn( p_demux, "no cues/empty cues found->seek won't be precise" );
IndexAppendCluster( p_demux, p_sys->cluster );
IndexAppendCluster( p_demux, p_segment->cluster );
p_sys->b_cues = VLC_FALSE;
p_segment->b_cues = VLC_FALSE;
}
/* add all es */
msg_Dbg( p_demux, "found %d es", p_sys->i_track );
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
msg_Dbg( p_demux, "found %d es", p_segment->i_track );
for( i_track = 0; i_track < p_segment->i_track; i_track++ )
{
#define tk p_sys->track[i_track]
#define tk p_segment->track[i_track]
if( tk.fmt.i_cat == UNKNOWN_ES )
{
msg_Warn( p_demux, "invalid track[%d, n=%d]", i_track, tk.i_number );
......@@ -1060,8 +1131,6 @@ static int Open( vlc_object_t * p_this )
return VLC_SUCCESS;
error:
delete p_sys->es;
delete p_sys->in;
delete p_sys;
return VLC_EGENERIC;
}
......@@ -1073,11 +1142,13 @@ static void Close( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream();
matroska_segment_t *p_segment = p_stream->Segment();
int i_track;
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
for( i_track = 0; i_track < p_segment->i_track; i_track++ )
{
#define tk p_sys->track[i_track]
#define tk p_segment->track[i_track]
if( tk.fmt.psz_description )
{
free( tk.fmt.psz_description );
......@@ -1092,22 +1163,19 @@ static void Close( vlc_object_t *p_this )
}
#undef tk
}
free( p_sys->track );
free( p_segment->track );
if( p_sys->psz_writing_application )
if( p_segment->psz_writing_application )
{
free( p_sys->psz_writing_application );
free( p_segment->psz_writing_application );
}
if( p_sys->psz_muxing_application )
if( p_segment->psz_muxing_application )
{
free( p_sys->psz_muxing_application );
free( p_segment->psz_muxing_application );
}
delete p_sys->segment;
delete p_segment->segment;
delete p_sys->ep;
delete p_sys->es;
delete p_sys->in;
delete p_sys;
}
......@@ -1117,10 +1185,11 @@ static void Close( vlc_object_t *p_this )
static int Control( demux_t *p_demux, int i_query, va_list args )
{
demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream();
matroska_segment_t *p_segment = p_stream->Segment();
int64_t *pi64;
double *pf, f;
int i_skp;
mtime_t *i_sk_time;
vlc_meta_t **pp_meta;
......@@ -1133,19 +1202,16 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
case DEMUX_GET_LENGTH:
pi64 = (int64_t*)va_arg( args, int64_t * );
if( p_sys->f_duration > 0.0 )
if( p_segment->f_duration > 0.0 )
{
*pi64 = (int64_t)(p_sys->f_duration * 1000);
*pi64 = (int64_t)(p_segment->f_duration * 1000);
return VLC_SUCCESS;
}
return VLC_EGENERIC;
case DEMUX_GET_POSITION:
pf = (double*)va_arg( args, double * );
/* if (p_sys->i_pts < p_sys->i_start_pts)
*pf = (double)p_sys->i_start_pts / (1000.0 * p_sys->f_duration);
else*/
*pf = (double)p_sys->i_pts / (1000.0 * p_sys->f_duration);
*pf = (double)p_sys->i_pts / (1000.0 * p_segment->f_duration);
return VLC_SUCCESS;
case DEMUX_SET_POSITION:
......@@ -1155,9 +1221,6 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
case DEMUX_GET_TIME:
pi64 = (int64_t*)va_arg( args, int64_t * );
/* if (p_sys->i_pts < p_sys->i_start_pts)
*pi64 = p_sys->i_start_pts;
else*/
*pi64 = p_sys->i_pts;
return VLC_SUCCESS;
......@@ -1207,6 +1270,8 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
static int BlockGet( demux_t *p_demux, KaxBlock **pp_block, int64_t *pi_ref1, int64_t *pi_ref2, int64_t *pi_duration )
{
demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream();
matroska_segment_t *p_segment = p_stream->Segment();
*pp_block = NULL;
*pi_ref1 = -1;
......@@ -1222,14 +1287,14 @@ static int BlockGet( demux_t *p_demux, KaxBlock **pp_block, int64_t *pi_ref1, in
return VLC_EGENERIC;
}
el = p_sys->ep->Get();
i_level = p_sys->ep->GetLevel();
el = p_stream->ep->Get();
i_level = p_stream->ep->GetLevel();
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 )
#define idx p_segment->index[p_segment->i_index - 1]
if( p_segment->i_index > 0 && idx.i_time == -1 )
{
idx.i_time = (*pp_block)->GlobalTimecode() / (mtime_t)1000;
idx.b_key = *pi_ref1 == -1 ? VLC_TRUE : VLC_FALSE;
......@@ -1240,9 +1305,9 @@ static int BlockGet( demux_t *p_demux, KaxBlock **pp_block, int64_t *pi_ref1, in
if( el == NULL )
{
if( p_sys->ep->GetLevel() > 1 )
if( p_stream->ep->GetLevel() > 1 )
{
p_sys->ep->Up();
p_stream->ep->Up();
continue;
}
msg_Warn( p_demux, "EOF" );
......@@ -1254,16 +1319,16 @@ static int BlockGet( demux_t *p_demux, KaxBlock **pp_block, int64_t *pi_ref1, in
{
if( MKV_IS_ID( el, KaxCluster ) )
{
p_sys->cluster = (KaxCluster*)el;
p_segment->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 < (int64_t)p_sys->cluster->GetElementPosition() ) )
if( p_segment->i_index == 0 ||
( p_segment->i_index > 0 && p_segment->index[p_segment->i_index - 1].i_position < (int64_t)p_segment->cluster->GetElementPosition() ) )
{
IndexAppendCluster( p_demux, p_sys->cluster );
IndexAppendCluster( p_demux, p_segment->cluster );
}
p_sys->ep->Down();
p_stream->ep->Down();
}
else if( MKV_IS_ID( el, KaxCues ) )
{
......@@ -1281,12 +1346,12 @@ static int BlockGet( demux_t *p_demux, KaxBlock **pp_block, int64_t *pi_ref1, in
{
KaxClusterTimecode &ctc = *(KaxClusterTimecode*)el;
ctc.ReadData( p_sys->es->I_O(), SCOPE_ALL_DATA );
p_sys->cluster->InitTimecode( uint64( ctc ), p_sys->i_timescale );
ctc.ReadData( p_stream->es->I_O(), SCOPE_ALL_DATA );
p_segment->cluster->InitTimecode( uint64( ctc ), p_segment->i_timescale );
}
else if( MKV_IS_ID( el, KaxBlockGroup ) )
{
p_sys->ep->Down();
p_stream->ep->Down();
}
}
else if( i_level == 3 )
......@@ -1295,23 +1360,23 @@ static int BlockGet( demux_t *p_demux, KaxBlock **pp_block, int64_t *pi_ref1, in
{
*pp_block = (KaxBlock*)el;
(*pp_block)->ReadData( p_sys->es->I_O() );
(*pp_block)->SetParent( *p_sys->cluster );
(*pp_block)->ReadData( p_stream->es->I_O() );
(*pp_block)->SetParent( *p_segment->cluster );
p_sys->ep->Keep();
p_stream->ep->Keep();
}
else if( MKV_IS_ID( el, KaxBlockDuration ) )
{
KaxBlockDuration &dur = *(KaxBlockDuration*)el;
dur.ReadData( p_sys->es->I_O() );
dur.ReadData( p_stream->es->I_O() );
*pi_duration = uint64( dur );
}
else if( MKV_IS_ID( el, KaxReferenceBlock ) )
{
KaxReferenceBlock &ref = *(KaxReferenceBlock*)el;
ref.ReadData( p_sys->es->I_O() );
ref.ReadData( p_stream->es->I_O() );
if( *pi_ref1 == -1 )
{
*pi_ref1 = int64( ref );
......@@ -1343,13 +1408,15 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
mtime_t i_duration )
{
demux_sys_t *p_sys = p_demux->p_sys;
matroska_stream_t *p_stream = p_sys->Stream();
matroska_segment_t *p_segment = p_stream->Segment();
int i_track;
unsigned int i;
vlc_bool_t b;
#define tk p_sys->track[i_track]
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
#define tk p_segment->track[i_track]
for( i_track = 0; i_track < p_segment->i_track; i_track++ )
{
if( tk.i_number == block->TrackNum() )
{
......@@ -1357,7 +1424,7 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
}
}
if( i_track >= p_sys->i_track )
if( i_track >= p_segment->i_track )
{
msg_Err( p_demux, "invalid track number=%d", block->TrackNum() );
return;
......@@ -1437,42 +1504,46 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
static void UpdateCurrentToChapter( demux_t & demux )
{
demux_sys_t & sys = *demux.p_sys;
matroska_stream_t *p_stream = sys.Stream();
matroska_segment_t *p_segment = p_stream->Segment();
const chapter_item_t *psz_curr_chapter;
/* update current chapter/seekpoint */
if ( sys.editions.size())
if ( p_segment->editions.size())
{
/* 1st, we need to know in which chapter we are */
psz_curr_chapter = sys.editions[sys.i_current_edition].FindTimecode( sys.i_pts );
psz_curr_chapter = p_segment->editions[p_segment->i_current_edition].FindTimecode( sys.i_pts );
/* we have moved to a new chapter */
if (sys.psz_current_chapter != NULL && psz_curr_chapter != NULL && sys.psz_current_chapter != psz_curr_chapter)
if (p_segment->psz_current_chapter != NULL && psz_curr_chapter != NULL && p_segment->psz_current_chapter != psz_curr_chapter)
{
if (sys.psz_current_chapter->i_seekpoint_num != psz_curr_chapter->i_seekpoint_num && psz_curr_chapter->i_seekpoint_num > 0)
if (p_segment->psz_current_chapter->i_seekpoint_num != psz_curr_chapter->i_seekpoint_num && psz_curr_chapter->i_seekpoint_num > 0)
{
demux.info.i_update |= INPUT_UPDATE_SEEKPOINT;
demux.info.i_seekpoint = psz_curr_chapter->i_seekpoint_num - 1;
}
if (sys.editions[sys.i_current_edition].b_ordered )
if (p_segment->editions[p_segment->i_current_edition].b_ordered )
{
/* TODO check if we need to silently seek to a new location in the stream (switch to another chapter) */
if (sys.psz_current_chapter->i_end_time != psz_curr_chapter->i_start_time)
if (p_segment->psz_current_chapter->i_end_time != psz_curr_chapter->i_start_time)
Seek(&demux, sys.i_pts, -1, psz_curr_chapter);
/* count the last duration time found for each track in a table (-1 not found, -2 silent) */
/* only seek after each duration >= end timecode of the current chapter */
}
// sys.i_user_time = psz_curr_chapter->i_user_start_time - psz_curr_chapter->i_start_time;
// sys.i_start_pts = psz_curr_chapter->i_user_start_time;
// p_segment->i_user_time = psz_curr_chapter->i_user_start_time - psz_curr_chapter->i_start_time;
// p_segment->i_start_pts = psz_curr_chapter->i_user_start_time;
}
sys.psz_current_chapter = psz_curr_chapter;
p_segment->psz_current_chapter = psz_curr_chapter;