+
+/*****************************************************************************
+ * Stream managment
+ *****************************************************************************/
+vlc_stream_io_callback::vlc_stream_io_callback( stream_t *s_ )
+{
+ s = s_;
+ mb_eof = VLC_FALSE;
+}
+
+uint32_t vlc_stream_io_callback::read( void *p_buffer, size_t i_size )
+{
+ if( i_size <= 0 || mb_eof )
+ {
+ return 0;
+ }
+
+ return stream_Read( s, p_buffer, i_size );
+}
+void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
+{
+ int64_t i_pos;
+
+ switch( mode )
+ {
+ case seek_beginning:
+ i_pos = i_offset;
+ break;
+ case seek_end:
+ i_pos = stream_Size( s ) - i_offset;
+ break;
+ default:
+ i_pos= stream_Tell( s ) + i_offset;
+ break;
+ }
+
+ if( i_pos < 0 || i_pos >= stream_Size( s ) )
+ {
+ mb_eof = VLC_TRUE;
+ return;
+ }
+
+ mb_eof = VLC_FALSE;
+ if( stream_Seek( s, i_pos ) )
+ {
+ mb_eof = VLC_TRUE;
+ }
+ return;
+}
+size_t vlc_stream_io_callback::write( const void *p_buffer, size_t i_size )
+{
+ return 0;
+}
+uint64_t vlc_stream_io_callback::getFilePointer( void )
+{
+ return stream_Tell( s );
+}
+void vlc_stream_io_callback::close( void )
+{
+ return;
+}
+
+
+/*****************************************************************************
+ * Ebml Stream parser
+ *****************************************************************************/
+EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start )
+{
+ int i;
+
+ m_es = es;
+ m_got = NULL;
+ m_el[0] = el_start;
+
+ for( i = 1; i < 6; i++ )
+ {
+ m_el[i] = NULL;
+ }
+ mi_level = 1;
+ mi_user_level = 1;
+ mb_keep = VLC_FALSE;
+}
+
+EbmlParser::~EbmlParser( void )
+{
+ int i;
+
+ for( i = 1; i < mi_level; i++ )
+ {
+ if( !mb_keep )
+ {
+ delete m_el[i];
+ }
+ mb_keep = VLC_FALSE;
+ }
+}
+
+void EbmlParser::Up( void )
+{
+ if( mi_user_level == mi_level )
+ {
+ fprintf( stderr," arrrrrrrrrrrrrg Up cannot escape itself\n" );
+ }
+
+ mi_user_level--;
+}
+
+void EbmlParser::Down( void )
+{
+ mi_user_level++;
+ mi_level++;
+}
+
+void EbmlParser::Keep( void )
+{
+ mb_keep = VLC_TRUE;
+}
+
+int EbmlParser::GetLevel( void )
+{
+ return mi_user_level;
+}
+
+EbmlElement *EbmlParser::Get( void )
+{
+ int i_ulev = 0;
+
+ if( mi_user_level != mi_level )
+ {
+ return NULL;
+ }
+ if( m_got )
+ {
+ EbmlElement *ret = m_got;
+ m_got = NULL;
+
+ return ret;
+ }
+
+ if( m_el[mi_level] )
+ {
+ m_el[mi_level]->SkipData( *m_es, m_el[mi_level]->Generic().Context );
+ if( !mb_keep )
+ {
+ delete m_el[mi_level];
+ }
+ mb_keep = VLC_FALSE;
+ }
+
+ m_el[mi_level] = m_es->FindNextElement( m_el[mi_level - 1]->Generic().Context, i_ulev, 0xFFFFFFFFL, true, 1 );
+ if( i_ulev > 0 )
+ {
+ while( i_ulev > 0 )
+ {
+ if( mi_level == 1 )
+ {
+ mi_level = 0;
+ return NULL;
+ }
+
+ delete m_el[mi_level - 1];
+ m_got = m_el[mi_level -1] = m_el[mi_level];
+ m_el[mi_level] = NULL;
+
+ mi_level--;
+ i_ulev--;
+ }
+ return NULL;
+ }
+ else if( m_el[mi_level] == NULL )
+ {
+ fprintf( stderr," m_el[mi_level] == NULL\n" );
+ }
+
+ return m_el[mi_level];
+}
+
+
+/*****************************************************************************
+ * Tools
+ * * LoadCues : load the cues element and update index
+ *
+ * * LoadTags : load ... the tags element
+ *
+ * * InformationsCreate : create all informations, load tags if present
+ *
+ *****************************************************************************/
+static void LoadCues( input_thread_t *p_input )
+{
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ int64_t i_sav_position = p_sys->in->getFilePointer();
+ EbmlParser *ep;
+ EbmlElement *el, *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);
+
+ if( cues == NULL )
+ {
+ msg_Err( p_input, "cannot load cues (broken seekhead or file)" );
+ return;
+ }
+
+ 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, " * Unknown (%s)", typeid(*el).name() );
+ }
+ }
+ ep->Up();
+ }
+ else
+ {
+ msg_Dbg( p_input, " * Unknown (%s)", typeid(*el).name() );
+ }
+ }
+ ep->Up();
+
+ msg_Dbg( p_input, " * added time="I64Fd" pos="I64Fd
+ " 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, " * Unknown (%s)", typeid(*el).name() );
+ }
+ }
+ delete ep;
+ delete cues;
+
+ p_sys->b_cues = VLC_TRUE;
+
+ msg_Dbg( p_input, "loading cues done." );
+ p_sys->in->setFilePointer( i_sav_position, seek_beginning );
+}
+
+static void LoadTags( input_thread_t *p_input )
+{
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ int64_t i_sav_position = p_sys->in->getFilePointer();
+ EbmlParser *ep;
+ EbmlElement *el, *tags;
+
+ msg_Dbg( p_input, "loading tags" );
+ p_sys->in->setFilePointer( p_sys->i_tags_position, seek_beginning );
+ tags = p_sys->es->FindNextID( KaxTags::ClassInfos, 0xFFFFFFFFL);
+
+ if( tags == NULL )
+ {
+ msg_Err( p_input, "cannot load tags (broken seekhead or file)" );
+ return;
+ }
+
+ msg_Dbg( p_input, "Tags" );
+ ep = new EbmlParser( p_sys->es, tags );
+ while( ( el = ep->Get() ) != NULL )
+ {
+ if( EbmlId( *el ) == KaxTag::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "+ Tag" );
+ ep->Down();
+ while( ( el = ep->Get() ) != NULL )
+ {
+ if( EbmlId( *el ) == KaxTagTargets::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Targets" );
+ ep->Down();
+ while( ( el = ep->Get() ) != NULL )
+ {
+ msg_Dbg( p_input, "| | + Unknown (%s)", typeid( *el ).name() );
+ }
+ ep->Up();
+ }
+ else if( EbmlId( *el ) == KaxTagGeneral::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + General" );
+ ep->Down();
+ while( ( el = ep->Get() ) != NULL )
+ {
+ msg_Dbg( p_input, "| | + Unknown (%s)", typeid( *el ).name() );
+ }
+ ep->Up();
+ }
+ else if( EbmlId( *el ) == KaxTagGenres::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Genres" );
+ ep->Down();
+ while( ( el = ep->Get() ) != NULL )
+ {
+ msg_Dbg( p_input, "| | + Unknown (%s)", typeid( *el ).name() );
+ }
+ ep->Up();
+ }
+ else if( EbmlId( *el ) == KaxTagAudioSpecific::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Audio Specific" );
+ ep->Down();
+ while( ( el = ep->Get() ) != NULL )
+ {
+ msg_Dbg( p_input, "| | + Unknown (%s)", typeid( *el ).name() );
+ }
+ ep->Up();
+ }
+ else if( EbmlId( *el ) == KaxTagImageSpecific::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Images Specific" );
+ ep->Down();
+ while( ( el = ep->Get() ) != NULL )
+ {
+ msg_Dbg( p_input, "| | + Unknown (%s)", typeid( *el ).name() );
+ }
+ ep->Up();
+ }
+ else if( EbmlId( *el ) == KaxTagMultiComment::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Comment" );
+ }
+ else if( EbmlId( *el ) == KaxTagMultiCommercial::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Commercial" );
+ }
+ else if( EbmlId( *el ) == KaxTagMultiDate::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Date" );
+ }
+ else if( EbmlId( *el ) == KaxTagMultiEntity::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Entity" );
+ }
+ else if( EbmlId( *el ) == KaxTagMultiIdentifier::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Identifier" );
+ }
+ else if( EbmlId( *el ) == KaxTagMultiLegal::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Legal" );
+ }
+ else if( EbmlId( *el ) == KaxTagMultiTitle::ClassInfos.GlobalId )
+ {
+ msg_Dbg( p_input, "| + Multi Title" );
+ }
+ else
+ {
+ msg_Dbg( p_input, "| + Unknown (%s)", typeid( *el ).name() );
+ }
+ }
+ ep->Up();
+ }
+ else
+ {
+ msg_Dbg( p_input, "+ Unknown (%s)", typeid( *el ).name() );
+ }
+ }
+ delete ep;
+ delete tags;
+
+ msg_Dbg( p_input, "loading tags done." );
+ p_sys->in->setFilePointer( i_sav_position, seek_beginning );
+}
+
+static void InformationsCreate( input_thread_t *p_input )
+{
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ input_info_category_t *p_cat;
+ playlist_t *p_playlist;
+ int i_track;
+
+ p_cat = input_InfoCategory( p_input, "Matroska" );
+ p_playlist = (playlist_t*)vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
+ if( p_sys->f_duration > 1000.1 )
+ {
+ char psz_buffer[MSTRTIME_MAX_SIZE];
+ input_AddInfo( p_cat, _("Duration"),
+ msecstotimestr( psz_buffer, (int)p_sys->f_duration ) );
+ }
+
+ if( p_sys->psz_title )
+ {
+ input_AddInfo( p_cat, _("Title"), "%s" ,p_sys->psz_title );
+ }
+ if( p_sys->psz_date_utc )
+ {
+ input_AddInfo( p_cat, _("UTC date"), "%s" ,p_sys->psz_date_utc );
+ }
+ if( p_sys->psz_segment_filename )
+ {
+ input_AddInfo( p_cat, _("Segment filename"), "%s" ,p_sys->psz_segment_filename );
+ }
+ if( p_sys->psz_muxing_application )
+ {
+ input_AddInfo( p_cat, _("Muxing application"), "%s" ,p_sys->psz_muxing_application );
+ }
+ if( p_sys->psz_writing_application )
+ {
+ input_AddInfo( p_cat, _("Writing application"), "%s" ,p_sys->psz_writing_application );
+ }
+ input_AddInfo( p_cat, _("Number of streams"), "%d" , p_sys->i_track );
+
+ for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+ {
+ char psz_cat[strlen( "Stream " ) + 10];
+#define tk p_sys->track[i_track]
+
+ sprintf( psz_cat, "Stream %d", i_track );
+ p_cat = input_InfoCategory( p_input, psz_cat);
+ if( tk.fmt.psz_description )
+ {
+ input_AddInfo( p_cat, _("Name"), "%s", tk.fmt.psz_description );
+ }
+ if( tk.fmt.psz_language )
+ {
+ input_AddInfo( p_cat, _("Language"), "%s", tk.fmt.psz_language );
+ }
+ if( tk.psz_codec_name )
+ {
+ input_AddInfo( p_cat, _("Codec name"), "%s", tk.psz_codec_name );
+ }
+ if( tk.psz_codec_settings )
+ {
+ input_AddInfo( p_cat, _("Codec setting"), "%s", tk.psz_codec_settings );
+ }
+ if( tk.psz_codec_info_url )
+ {
+ input_AddInfo( p_cat, _("Codec info"), "%s", tk.psz_codec_info_url );
+ }
+ if( tk.psz_codec_download_url )
+ {
+ input_AddInfo( p_cat, _("Codec download"), "%s", tk.psz_codec_download_url );
+ }
+#undef tk
+ }
+
+ if( p_sys->i_tags_position >= 0 )
+ {
+ vlc_bool_t b_seekable;
+
+ stream_Control( p_input->s, STREAM_CAN_FASTSEEK, &b_seekable );
+ if( b_seekable )
+ {
+ LoadTags( p_input );
+ }
+ }
+}
+
+
+/*****************************************************************************
+ * Divers
+ *****************************************************************************/
+
+static void IndexAppendCluster( input_thread_t *p_input, KaxCluster *cluster )
+{
+ demux_sys_t *p_sys = p_input->p_demux_data;
+
+#define idx p_sys->index[p_sys->i_index]
+ idx.i_track = -1;
+ idx.i_block_number= -1;
+ idx.i_position = cluster->GetElementPosition();
+ idx.i_time = -1;
+ idx.b_key = VLC_TRUE;
+
+ 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
+}
+
+static char * UTF8ToStr( const UTFstring &u )
+{
+ int i_src;
+ const wchar_t *src;
+ char *dst, *p;
+
+ i_src = u.length();
+ src = u.c_str();
+
+ p = dst = (char*)malloc( i_src + 1);
+ while( i_src > 0 )
+ {
+ if( *src < 255 )
+ {
+ *p++ = (char)*src;
+ }
+ else
+ {
+ *p++ = '?';
+ }
+ src++;
+ i_src--;
+ }
+ *p++= '\0';
+
+ return dst;
+}
+
+static char *LanguageGetName ( const char *psz_code )
+{
+ const iso639_lang_t *pl;
+
+ if( strlen( psz_code ) == 2 )
+ {
+ pl = GetLang_1( psz_code );
+ }
+ else if( strlen( psz_code ) == 3 )
+ {
+ pl = GetLang_2B( psz_code );
+ if( !strcmp( pl->psz_iso639_1, "??" ) )
+ {
+ pl = GetLang_2T( psz_code );
+ }
+ }
+ else
+ {
+ return strdup( psz_code );
+ }
+
+ if( !strcmp( pl->psz_iso639_1, "??" ) )
+ {
+ return strdup( psz_code );
+ }
+ else
+ {
+ if( *pl->psz_native_name )
+ {
+ return strdup( pl->psz_native_name );
+ }
+ return strdup( pl->psz_eng_name );
+ }
+}
+