#include <typeinfo>
#include <string>
#include <vector>
+#include <algorithm>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
-static int Demux ( demux_t * );
-static int Control( demux_t *, int, va_list );
-static void Seek ( demux_t *, mtime_t i_date, double f_percent );
-
#ifdef HAVE_ZLIB_H
block_t *block_zlib_decompress( vlc_object_t *p_this, block_t *p_in_block ) {
int result, dstsize, n;
class chapter_item_t
{
public:
- chapter_item_t()
- :i_start_time(0)
- ,i_end_time(-1)
- ,i_user_start_time(-1)
- ,i_user_end_time(-1)
- ,i_current_sub_chapter(-1)
- ,i_seekpoint_num(-1)
- {}
-
- int64_t RefreshChapters( bool b_ordered, int64_t i_prev_user_time );
-
+ chapter_item_t()
+ :i_start_time(0)
+ ,i_end_time(-1)
+ ,i_user_start_time(-1)
+ ,i_user_end_time(-1)
+ ,i_seekpoint_num(-1)
+ ,b_display_seekpoint(true)
+ ,psz_parent(NULL)
+ {}
+
+ int64_t RefreshChapters( bool b_ordered, int64_t i_prev_user_time, input_title_t & title );
+ const chapter_item_t * FindTimecode( mtime_t i_timecode ) const;
+
int64_t i_start_time, i_end_time;
int64_t i_user_start_time, i_user_end_time; /* the time in the stream when an edition is ordered */
std::vector<chapter_item_t> sub_chapters;
- int i_current_sub_chapter;
int i_seekpoint_num;
+ int64_t i_uid;
+ bool b_display_seekpoint;
+ std::string psz_name;
+ chapter_item_t *psz_parent;
+
+ bool operator<( const chapter_item_t & item ) const
+ {
+ return ( i_user_start_time < item.i_user_start_time || (i_user_start_time == item.i_user_start_time && i_user_end_time < item.i_user_end_time) );
+ }
+
+protected:
+ bool Enter();
+ bool Leave();
};
class chapter_edition_t
:i_uid(-1)
,b_ordered(false)
{}
-
- void RefreshChapters();
+
+ void RefreshChapters( input_title_t & title );
+ double Duration() const;
+ const chapter_item_t * FindTimecode( mtime_t i_timecode ) const;
std::vector<chapter_item_t> chapters;
int64_t i_uid;
,cluster(NULL)
,i_pts(0)
,i_start_pts(0)
+ ,i_chapter_time(0)
,b_cues(false)
,i_index(0)
,i_index_max(0)
,meta(NULL)
,title(NULL)
,i_current_edition(0)
+ ,psz_current_chapter(NULL)
{}
vlc_stream_io_callback *in;
mtime_t i_pts;
mtime_t i_start_pts;
+ mtime_t i_chapter_time;
vlc_bool_t b_cues;
int i_index;
std::vector<chapter_edition_t> editions;
int i_current_edition;
+ const chapter_item_t *psz_current_chapter;
};
+static int Demux ( demux_t * );
+static int Control( demux_t *, int, va_list );
+static void Seek ( demux_t *, mtime_t i_date, double f_percent, const chapter_item_t *psz_chapter );
+
#define MKVD_TIMECODESCALE 1000000
#define MKV_IS_ID( el, C ) ( EbmlId( (*el) ) == C::ClassInfos.GlobalId )
EbmlElement *el = NULL, *el1 = NULL;
/* peek the begining */
- if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 )
- {
- msg_Warn( p_demux, "cannot peek" );
- return VLC_EGENERIC;
- }
+ if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC;
/* is a valid file */
if( p_peek[0] != 0x1a || p_peek[1] != 0x45 ||
- p_peek[2] != 0xdf || p_peek[3] != 0xa3 )
- {
- msg_Warn( p_demux, "matroska module discarded "
- "(invalid header 0x%.2x%.2x%.2x%.2x)",
- p_peek[0], p_peek[1], p_peek[2], p_peek[3] );
- return VLC_EGENERIC;
- }
+ p_peek[2] != 0xdf || p_peek[3] != 0xa3 ) return VLC_EGENERIC;
/* Set the demux function */
p_demux->pf_demux = Demux;
int64_t *pi64;
double *pf, f;
int i_skp;
+ mtime_t *i_sk_time;
vlc_meta_t **pp_meta;
case DEMUX_SET_POSITION:
f = (double)va_arg( args, double );
- Seek( p_demux, -1, f );
+ Seek( p_demux, -1, f, NULL );
return VLC_SUCCESS;
case DEMUX_GET_TIME:
return VLC_EGENERIC;
case DEMUX_SET_TITLE:
+ /* TODO handle editions as titles & DVD titles as well */
if( p_sys->title && p_sys->title->i_seekpoint > 0 )
{
return VLC_SUCCESS;
if( p_sys->title && i_skp < p_sys->title->i_seekpoint)
{
- Seek( p_demux, (int64_t)p_sys->title->seekpoint[i_skp]->i_time_offset, -1);
+ Seek( p_demux, (int64_t)p_sys->title->seekpoint[i_skp]->i_time_offset, -1, NULL);
p_demux->info.i_seekpoint |= INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_seekpoint = i_skp;
-/* p_sys->i_current_chapter = i_skp;*/
return VLC_SUCCESS;
}
return VLC_EGENERIC;
+ case DEMUX_GET_SEEKPOINT_TIME:
+ i_skp = (int)va_arg( args, int );
+ i_sk_time = (mtime_t *)va_arg( args, mtime_t * );
+ if( p_sys->title && i_skp < p_sys->title->i_seekpoint)
+ {
+ *i_sk_time = p_sys->title->seekpoint[i_skp]->i_time_offset;
+ return VLC_SUCCESS;
+ }
+ return VLC_EGENERIC;
case DEMUX_SET_TIME:
case DEMUX_GET_FPS:
}
#endif
- if (p_sys->i_start_pts > i_pts)
- {
- p_block->i_dts = 0;
- p_block->i_pts = -1;
- }
- else
+ // TODO implement correct timestamping when B frames are used
if( tk.fmt.i_cat != VIDEO_ES )
{
p_block->i_dts = p_block->i_pts = i_pts;
#undef tk
}
-static void UpdateCurrentChapter()
+static void UpdateCurrentToChapter( demux_t & demux )
{
+ demux_sys_t & sys = *demux.p_sys;
+ const chapter_item_t *psz_curr_chapter;
+
/* update current chapter/seekpoint */
-#ifdef TODO
- i_chapter
- if (p_sys->title->seekpoint[i_skp]->i_time_offset)
+ if ( sys.editions.size())
{
- p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
- p_demux->info.i_seekpoint = i_chapter;
+ /* 1st, we need to know in which chapter we are */
+ psz_curr_chapter = sys.editions[sys.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 (sys.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 )
+ {
+ /* 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)
+ 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;
+ }
+ sys.psz_current_chapter = psz_curr_chapter;
}
-#endif
}
-static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent)
+static void Seek( demux_t *p_demux, mtime_t i_date, double f_percent, const chapter_item_t *psz_chapter)
{
demux_sys_t *p_sys = p_demux->p_sys;
+ mtime_t i_time_offset = 0;
KaxBlock *block;
int64_t i_block_duration;
{
if (p_sys->f_duration >= 0)
{
- i_date = f_percent * p_sys->f_duration * 1000;
+ i_date = int64_t( f_percent * p_sys->f_duration * 1000.0 );
}
else
{
- int64_t i_pos = f_percent * stream_Size( p_demux->s );
+ int64_t i_pos = int64_t( f_percent * stream_Size( p_demux->s ) );
msg_Dbg( p_demux, "inacurate way of seeking" );
for( i_index = 0; i_index < p_sys->i_index; i_index++ )
}
}
+ // find the actual time for an ordered edition
+ if ( psz_chapter == NULL )
+ {
+ if ( p_sys->editions.size() && p_sys->editions[p_sys->i_current_edition].b_ordered )
+ {
+ /* 1st, we need to know in which chapter we are */
+ psz_chapter = p_sys->editions[p_sys->i_current_edition].FindTimecode( i_date );
+ }
+ }
+
+ if ( psz_chapter != NULL )
+ {
+ p_sys->psz_current_chapter = psz_chapter;
+ p_sys->i_chapter_time = i_time_offset = psz_chapter->i_user_start_time - psz_chapter->i_start_time;
+ p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
+ p_demux->info.i_seekpoint = psz_chapter->i_seekpoint_num - 1;
+ }
+
for( ; i_index < p_sys->i_index; i_index++ )
{
- if( p_sys->index[i_index].i_time > i_date )
+ if( p_sys->index[i_index].i_time + i_time_offset > i_date )
{
break;
}
p_sys->in->setFilePointer( p_sys->index[i_index].i_position,
seek_beginning );
+ p_sys->i_start_pts = i_date;
+
+ es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
/* now parse until key frame */
#define tk p_sys->track[i_track]
tk.b_search_keyframe = VLC_TRUE;
i_track_skipping++;
}
+ es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, tk.p_es, i_date );
}
- p_sys->i_start_pts = i_date;
-
- es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
while( i_track_skipping > 0 )
{
return;
}
- p_sys->i_pts = block->GlobalTimecode() / (mtime_t) 1000;
-
for( i_track = 0; i_track < p_sys->i_track; i_track++ )
{
if( tk.i_number == block->TrackNum() )
}
}
+ p_sys->i_pts = p_sys->i_chapter_time + block->GlobalTimecode() / (mtime_t) 1000;
+
if( i_track < p_sys->i_track )
{
if( tk.fmt.i_cat == VIDEO_ES )
}
if( !tk.b_search_keyframe )
{
- BlockDecode( p_demux, block, 0, 0 );
+ BlockDecode( p_demux, block, p_sys->i_pts, 0 );
}
}
}
static int Demux( demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
- mtime_t i_start_pts;
int i_block_count = 0;
KaxBlock *block;
int64_t i_block_ref1;
int64_t i_block_ref2;
- i_start_pts = -1;
-
for( ;; )
{
- if ( p_sys->editions[p_sys->i_current_edition].b_ordered )
+ if( p_sys->i_pts >= p_sys->i_start_pts )
+ UpdateCurrentToChapter( *p_demux );
+
+ if ( p_sys->editions.size() && p_sys->editions[p_sys->i_current_edition].b_ordered && p_sys->psz_current_chapter == NULL )
{
- /* 1st, we need to know in which chapter we are */
-
- /* check if we need to silently seek to a new location in the stream */
- /* 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 */
+ /* nothing left to read in this ordered edition */
+ return 0;
}
-
+
if( BlockGet( p_demux, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
{
+ if ( p_sys->editions.size() && p_sys->editions[p_sys->i_current_edition].b_ordered )
+ {
+ // check if there are more chapters to read
+ if ( p_sys->psz_current_chapter != NULL )
+ {
+ p_sys->i_pts = p_sys->psz_current_chapter->i_user_end_time;
+ return 1;
+ }
+
+ return 0;
+ }
msg_Warn( p_demux, "cannot get block EOF?" );
return 0;
}
- p_sys->i_pts = block->GlobalTimecode() / (mtime_t) 1000;
+ p_sys->i_pts = p_sys->i_chapter_time + block->GlobalTimecode() / (mtime_t) 1000;
if( p_sys->i_pts >= p_sys->i_start_pts )
{
BlockDecode( p_demux, block, p_sys->i_pts, i_block_duration );
- UpdateCurrentChapter();
-
delete block;
i_block_count++;
- if( p_sys->i_pts > i_start_pts + (mtime_t)100000 || i_block_count > 5 )
+ // TODO optimize when there is need to leave or when seeking has been called
+ if( i_block_count > 5 )
{
return 1;
}
{
demux_sys_t *p_sys = p_demux->p_sys;
unsigned int i;
- seekpoint_t *sk;
- bool b_display_seekpoint = true;
if( p_sys->title == NULL )
{
p_sys->title = vlc_input_title_New();
}
- sk = vlc_seekpoint_New();
-
- sk->i_level = i_level;
msg_Dbg( p_demux, "| | | + ChapterAtom (level=%d)", i_level );
for( i = 0; i < ca->ListSize(); i++ )
if( MKV_IS_ID( l, KaxChapterUID ) )
{
- KaxChapterUID &uid = *(KaxChapterUID*)l;
- uint32_t i_uid = uint32( uid );
- msg_Dbg( p_demux, "| | | | + ChapterUID: 0x%x", i_uid );
+ chapters.i_uid = uint64_t(*(KaxChapterUID*)l);
+ msg_Dbg( p_demux, "| | | | + ChapterUID: %lld", chapters.i_uid );
}
else if( MKV_IS_ID( l, KaxChapterFlagHidden ) )
{
KaxChapterFlagHidden &flag =*(KaxChapterFlagHidden*)l;
- b_display_seekpoint = uint8( flag ) == 0;
+ chapters.b_display_seekpoint = uint8( flag ) == 0;
- msg_Dbg( p_demux, "| | | | + ChapterFlagHidden: %s", b_display_seekpoint ? "no":"yes" );
+ msg_Dbg( p_demux, "| | | | + ChapterFlagHidden: %s", chapters.b_display_seekpoint ? "no":"yes" );
}
else if( MKV_IS_ID( l, KaxChapterTimeStart ) )
{
KaxChapterTimeStart &start =*(KaxChapterTimeStart*)l;
- sk->i_time_offset = uint64( start ) / I64C(1000);
+ chapters.i_start_time = uint64( start ) / I64C(1000);
- msg_Dbg( p_demux, "| | | | + ChapterTimeStart: %lld", sk->i_time_offset );
+ msg_Dbg( p_demux, "| | | | + ChapterTimeStart: %lld", chapters.i_start_time );
}
else if( MKV_IS_ID( l, KaxChapterTimeEnd ) )
{
KaxChapterTimeEnd &end =*(KaxChapterTimeEnd*)l;
- int64_t i_end = uint64( end );
+ chapters.i_end_time = uint64( end ) / I64C(1000);
- msg_Dbg( p_demux, "| | | | + ChapterTimeEnd: %lld", i_end );
+ msg_Dbg( p_demux, "| | | | + ChapterTimeEnd: %lld", chapters.i_end_time );
}
else if( MKV_IS_ID( l, KaxChapterDisplay ) )
{
if( MKV_IS_ID( l, KaxChapterString ) )
{
- std::string psz;
int k;
KaxChapterString &name =*(KaxChapterString*)l;
for (k = 0; k < i_level; k++)
- psz += '+';
- psz += ' ';
- psz += UTF8ToStr( UTFstring( name ) );
- sk->psz_name = strdup( psz.c_str() );
+ chapters.psz_name += '+';
+ chapters.psz_name += ' ';
+ chapters.psz_name += UTF8ToStr( UTFstring( name ) );
+
msg_Dbg( p_demux, "| | | | | + ChapterString '%s'", UTF8ToStr(UTFstring(name)) );
}
else if( MKV_IS_ID( l, KaxChapterLanguage ) )
{
chapter_item_t new_sub_chapter;
ParseChapterAtom( p_demux, i_level+1, static_cast<EbmlMaster *>(l), new_sub_chapter );
+ new_sub_chapter.psz_parent = &chapters;
chapters.sub_chapters.push_back( new_sub_chapter );
}
}
-
- if (b_display_seekpoint)
- {
- // A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value.
- p_sys->title->i_seekpoint++;
- p_sys->title->seekpoint = (seekpoint_t**)realloc( p_sys->title->seekpoint, p_sys->title->i_seekpoint * sizeof( seekpoint_t* ) );
- p_sys->title->seekpoint[p_sys->title->i_seekpoint-1] = sk;
- }
- else
- {
- vlc_seekpoint_Delete(sk);
- }
}
/*****************************************************************************
unsigned int i;
int i_upper_level = 0;
int i_default_edition = 0;
+ float f_duration;
/* Master elements */
m = static_cast<EbmlMaster *>(chapters);
else if( MKV_IS_ID( l, KaxEditionFlagDefault ) )
{
if (uint8(*static_cast<KaxEditionFlagDefault *>( l )) != 0)
- p_sys->i_current_edition = p_sys->editions.size();
+ i_default_edition = p_sys->editions.size();
}
else
{
for( i = 0; i < p_sys->editions.size(); i++ )
{
- p_sys->editions[i].RefreshChapters();
- }
-
+ p_sys->editions[i].RefreshChapters( *p_sys->title );
+ }
+
p_sys->i_current_edition = i_default_edition;
if ( p_sys->editions[i_default_edition].b_ordered )
{
/* update the duration of the segment according to the sum of all sub chapters */
+ f_duration = p_sys->editions[i_default_edition].Duration() / I64C(1000);
+ if (f_duration > 0.0)
+ p_sys->f_duration = f_duration;
}
}
return dst;
}
-void chapter_edition_t::RefreshChapters()
+void chapter_edition_t::RefreshChapters( input_title_t & title )
+{
+ int64_t i_prev_user_time = 0;
+ std::vector<chapter_item_t>::iterator index = chapters.begin();
+
+ while ( index != chapters.end() )
+ {
+ i_prev_user_time = (*index).RefreshChapters( b_ordered, i_prev_user_time, title );
+ index++;
+ }
+}
+
+int64_t chapter_item_t::RefreshChapters( bool b_ordered, int64_t i_prev_user_time, input_title_t & title )
+{
+ int64_t i_user_time = i_prev_user_time;
+
+ // first the sub-chapters, and then ourself
+ std::vector<chapter_item_t>::iterator index = sub_chapters.begin();
+ while ( index != sub_chapters.end() )
+ {
+ i_user_time = (*index).RefreshChapters( b_ordered, i_user_time, title );
+ index++;
+ }
+
+ if ( b_ordered )
+ {
+ i_user_start_time = i_prev_user_time;
+ if ( i_end_time != -1 && i_user_time == i_prev_user_time )
+ {
+ i_user_end_time = i_user_start_time - i_start_time + i_end_time;
+ }
+ else
+ {
+ i_user_end_time = i_user_time;
+ }
+ }
+ else
+ {
+ std::sort( sub_chapters.begin(), sub_chapters.end() );
+ i_user_start_time = i_start_time;
+ i_user_end_time = i_end_time;
+ }
+
+ if (b_display_seekpoint)
+ {
+ seekpoint_t *sk = vlc_seekpoint_New();
+
+// sk->i_level = i_level;
+ sk->i_time_offset = i_start_time;
+ sk->psz_name = strdup( psz_name.c_str() );
+
+ // A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value.
+ title.i_seekpoint++;
+ title.seekpoint = (seekpoint_t**)realloc( title.seekpoint, title.i_seekpoint * sizeof( seekpoint_t* ) );
+ title.seekpoint[title.i_seekpoint-1] = sk;
+ }
+
+ i_seekpoint_num = title.i_seekpoint;
+
+ return i_user_end_time;
+}
+
+double chapter_edition_t::Duration() const
+{
+ double f_result = 0.0;
+
+ if ( chapters.size() )
+ {
+ std::vector<chapter_item_t>::const_iterator index = chapters.end();
+ index--;
+ f_result = (*index).i_user_end_time;
+ }
+
+ return f_result;
+}
+
+const chapter_item_t *chapter_item_t::FindTimecode( mtime_t i_user_timecode ) const
{
- int64_t i_prev_user_time = 0;
- std::vector<chapter_item_t>::iterator index = chapters.begin();
- while ( index != chapters.end() )
- {
- i_prev_user_time = (*index).RefreshChapters( b_ordered, i_prev_user_time );
- index++;
- }
+ const chapter_item_t *psz_result = NULL;
+
+ if (i_user_timecode >= i_user_start_time && i_user_timecode < i_user_end_time)
+ {
+ std::vector<chapter_item_t>::const_iterator index = sub_chapters.begin();
+ while ( index != sub_chapters.end() && psz_result == NULL )
+ {
+ psz_result = (*index).FindTimecode( i_user_timecode );
+ index++;
+ }
+
+ if ( psz_result == NULL )
+ psz_result = this;
+ }
+
+ return psz_result;
}
-int64_t chapter_item_t::RefreshChapters( bool b_ordered, int64_t i_prev_user_time )
+const chapter_item_t *chapter_edition_t::FindTimecode( mtime_t i_user_timecode ) const
{
- int64_t i_user_time = i_prev_user_time;
-
- // first the sub-chapters, and then ourself
- std::vector<chapter_item_t>::iterator index = sub_chapters.begin();
- while ( index != sub_chapters.end() )
- {
- i_user_time = (*index).RefreshChapters( b_ordered, i_user_time );
- index++;
- }
-
- if ( b_ordered )
- {
- i_user_start_time = i_prev_user_time;
- if ( i_end_time != -1 && i_user_time == i_prev_user_time )
- {
- i_user_end_time = i_user_start_time - i_start_time + i_end_time;
- }
- else
- {
- i_user_end_time = i_user_start_time;
- }
- }
- else
- {
- i_user_start_time = i_start_time;
- i_user_end_time = i_end_time;
- }
-
- return i_user_end_time;
+ const chapter_item_t *psz_result = NULL;
+
+ std::vector<chapter_item_t>::const_iterator index = chapters.begin();
+ while ( index != chapters.end() && psz_result == NULL )
+ {
+ psz_result = (*index).FindTimecode( i_user_timecode );
+ index++;
+ }
+
+ return psz_result;
}