You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1468 lines
49 KiB
1468 lines
49 KiB
/*****************************************************************************
|
|
* matroska_segment.cpp : matroska demuxer
|
|
*****************************************************************************
|
|
* Copyright (C) 2003-2010 VLC authors and VideoLAN
|
|
*
|
|
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
|
|
* Steve Lhomme <steve.lhomme@free.fr>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#include "matroska_segment.hpp"
|
|
#include "chapters.hpp"
|
|
#include "demux.hpp"
|
|
#include "util.hpp"
|
|
#include "Ebml_parser.hpp"
|
|
#include "Ebml_dispatcher.hpp"
|
|
|
|
#include <new>
|
|
#include <iterator>
|
|
#include <limits>
|
|
|
|
namespace mkv {
|
|
|
|
matroska_segment_c::matroska_segment_c( demux_sys_t & demuxer, EbmlStream & estream, KaxSegment *p_seg )
|
|
:segment(p_seg)
|
|
,es(estream)
|
|
,i_timescale(MKVD_TIMECODESCALE)
|
|
,i_duration(-1)
|
|
,i_mk_start_time(0)
|
|
,i_seekhead_count(0)
|
|
,i_seekhead_position(-1)
|
|
,i_cues_position(-1)
|
|
,i_tracks_position(-1)
|
|
,i_info_position(-1)
|
|
,i_chapters_position(-1)
|
|
,i_attachments_position(-1)
|
|
,cluster(NULL)
|
|
,i_block_pos(0)
|
|
,p_segment_uid(NULL)
|
|
,p_prev_segment_uid(NULL)
|
|
,p_next_segment_uid(NULL)
|
|
,b_cues(false)
|
|
,psz_muxing_application(NULL)
|
|
,psz_writing_application(NULL)
|
|
,psz_segment_filename(NULL)
|
|
,psz_title(NULL)
|
|
,psz_date_utc(NULL)
|
|
,i_default_edition(0)
|
|
,sys(demuxer)
|
|
,ep( EbmlParser(&estream, p_seg, &demuxer.demuxer ))
|
|
,b_preloaded(false)
|
|
,b_ref_external_segments(false)
|
|
{
|
|
}
|
|
|
|
matroska_segment_c::~matroska_segment_c()
|
|
{
|
|
free( psz_writing_application );
|
|
free( psz_muxing_application );
|
|
free( psz_segment_filename );
|
|
free( psz_title );
|
|
free( psz_date_utc );
|
|
|
|
delete segment;
|
|
delete p_segment_uid;
|
|
delete p_prev_segment_uid;
|
|
delete p_next_segment_uid;
|
|
|
|
vlc_delete_all( stored_editions );
|
|
vlc_delete_all( translations );
|
|
vlc_delete_all( families );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Tools *
|
|
*****************************************************************************
|
|
* * LoadCues : load the cues element and update index
|
|
* * LoadTags : load ... the tags element
|
|
* * InformationCreate : create all information, load tags if present
|
|
*****************************************************************************/
|
|
void matroska_segment_c::LoadCues( KaxCues *cues )
|
|
{
|
|
EbmlElement *el;
|
|
|
|
if( b_cues )
|
|
{
|
|
msg_Warn( &sys.demuxer, "There can be only 1 Cues per section." );
|
|
return;
|
|
}
|
|
|
|
EbmlParser eparser (&es, cues, &sys.demuxer );
|
|
while( ( el = eparser.Get() ) != NULL )
|
|
{
|
|
if( MKV_IS_ID( el, KaxCuePoint ) )
|
|
{
|
|
uint64_t cue_position = -1;
|
|
vlc_tick_t cue_mk_time = -1;
|
|
|
|
unsigned int track_id = 0;
|
|
bool b_invalid_cue = false;
|
|
|
|
eparser.Down();
|
|
while( ( el = eparser.Get() ) != NULL )
|
|
{
|
|
if ( MKV_CHECKED_PTR_DECL( cuetime, KaxCueTime, el ) )
|
|
{
|
|
try
|
|
{
|
|
if( unlikely( !cuetime->ValidateSize() ) )
|
|
{
|
|
msg_Err( &sys.demuxer, "CueTime size too big");
|
|
b_invalid_cue = true;
|
|
break;
|
|
}
|
|
cuetime->ReadData( es.I_O() );
|
|
}
|
|
catch(...)
|
|
{
|
|
msg_Err( &sys.demuxer, "Error while reading CueTime" );
|
|
b_invalid_cue = true;
|
|
break;
|
|
}
|
|
cue_mk_time = VLC_TICK_FROM_NS(static_cast<uint64>( *cuetime ) * i_timescale);
|
|
}
|
|
else if( MKV_IS_ID( el, KaxCueTrackPositions ) )
|
|
{
|
|
eparser.Down();
|
|
try
|
|
{
|
|
while( ( el = eparser.Get() ) != NULL )
|
|
{
|
|
if( unlikely( !el->ValidateSize() ) )
|
|
{
|
|
eparser.Up();
|
|
msg_Err( &sys.demuxer, "Error %s too big, aborting", typeid(*el).name() );
|
|
b_invalid_cue = true;
|
|
break;
|
|
}
|
|
|
|
if( MKV_CHECKED_PTR_DECL ( kct_ptr, KaxCueTrack, el ) )
|
|
{
|
|
kct_ptr->ReadData( es.I_O() );
|
|
track_id = static_cast<uint16>( *kct_ptr );
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( kccp_ptr, KaxCueClusterPosition, el ) )
|
|
{
|
|
kccp_ptr->ReadData( es.I_O() );
|
|
cue_position = segment->GetGlobalPosition( static_cast<uint64>( *kccp_ptr ) );
|
|
|
|
_seeker.add_cluster_position( cue_position );
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( kcbn_ptr, KaxCueBlockNumber, el ) )
|
|
{
|
|
VLC_UNUSED( kcbn_ptr );
|
|
}
|
|
#if LIBMATROSKA_VERSION >= 0x010401
|
|
else if( MKV_CHECKED_PTR_DECL( cuerelative, KaxCueRelativePosition, el ) )
|
|
{
|
|
// IGNORE
|
|
cuerelative->ReadData( es.I_O() );
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL( cueblock, KaxCueBlockNumber, el ) )
|
|
{
|
|
// IGNORE
|
|
cueblock->ReadData( es.I_O() );
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL( cueref, KaxCueReference, el ) )
|
|
{
|
|
// IGNORE
|
|
cueref->ReadData( es.I_O(), SCOPE_ALL_DATA );
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL( cueduration, KaxCueDuration, el ) )
|
|
{
|
|
/* For future use */
|
|
cueduration->ReadData( es.I_O() );
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
msg_Dbg( &sys.demuxer, " * Unknown (%s)", typeid(*el).name() );
|
|
}
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
eparser.Up();
|
|
msg_Err( &sys.demuxer, "Error while reading %s", typeid(*el).name() );
|
|
b_invalid_cue = true;
|
|
break;
|
|
}
|
|
eparser.Up();
|
|
}
|
|
else
|
|
{
|
|
msg_Dbg( &sys.demuxer, " * Unknown (%s)", typeid(*el).name() );
|
|
}
|
|
}
|
|
eparser.Up();
|
|
|
|
if( track_id != 0 && cue_mk_time != -1 && cue_position != static_cast<uint64_t>( -1 ) ) {
|
|
|
|
SegmentSeeker::Seekpoint::TrustLevel level = SegmentSeeker::Seekpoint::DISABLED;
|
|
|
|
if( ! b_invalid_cue && tracks.find( track_id ) != tracks.end() )
|
|
{
|
|
level = sys.trust_cues ?
|
|
SegmentSeeker::Seekpoint::TRUSTED :
|
|
SegmentSeeker::Seekpoint::QUESTIONABLE;
|
|
}
|
|
|
|
_seeker.add_seekpoint( track_id,
|
|
SegmentSeeker::Seekpoint( cue_position, cue_mk_time, level ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
msg_Dbg( &sys.demuxer, " * Unknown (%s)", typeid(*el).name() );
|
|
}
|
|
}
|
|
b_cues = true;
|
|
msg_Dbg( &sys.demuxer, "| - loading cues done." );
|
|
}
|
|
|
|
|
|
static const struct {
|
|
vlc_meta_type_t type;
|
|
const char *key;
|
|
int target_type; /* 0 is valid for all target_type */
|
|
} metadata_map[] = {
|
|
{vlc_meta_ShowName, "TITLE", 70},
|
|
{vlc_meta_Album, "TITLE", 50},
|
|
{vlc_meta_Title, "TITLE", 30},
|
|
{vlc_meta_DiscNumber, "PART_NUMBER", 60},
|
|
{vlc_meta_Season, "PART_NUMBER", 60},
|
|
{vlc_meta_Episode, "PART_NUMBER", 50},
|
|
{vlc_meta_TrackNumber, "PART_NUMBER", 30},
|
|
{vlc_meta_DiscTotal, "TOTAL_PARTS", 70},
|
|
{vlc_meta_TrackTotal, "TOTAL_PARTS", 30},
|
|
{vlc_meta_Setting, "ENCODER_SETTINGS", 0},
|
|
/* TODO read TagLanguage {vlc_meta_Language} */
|
|
/* TODO read tags targeting attachments {vlc_meta_ArtworkURL, */
|
|
{vlc_meta_AlbumArtist, "ARTIST", 50},
|
|
{vlc_meta_Artist, "ARTIST", 0},
|
|
{vlc_meta_Director, "DIRECTOR", 0},
|
|
{vlc_meta_Actors, "ACTOR", 0},
|
|
{vlc_meta_Genre, "GENRE", 0},
|
|
{vlc_meta_Copyright, "COPYRIGHT", 0},
|
|
{vlc_meta_Description, "DESCRIPTION", 0},
|
|
{vlc_meta_Description, "COMMENT", 0},
|
|
{vlc_meta_Rating, "RATING", 0},
|
|
{vlc_meta_Date, "DATE_RELEASED", 0},
|
|
{vlc_meta_Date, "DATE_RELEASE", 0},
|
|
{vlc_meta_Date, "DATE_RECORDED", 0},
|
|
{vlc_meta_URL, "URL", 0},
|
|
{vlc_meta_Publisher, "PUBLISHER", 0},
|
|
{vlc_meta_EncodedBy, "ENCODED_BY", 0},
|
|
{vlc_meta_Album, "ALBUM", 0}, /* invalid tag */
|
|
{vlc_meta_Title, NULL, 0},
|
|
};
|
|
|
|
bool matroska_segment_c::ParseSimpleTags( SimpleTag* pout_simple, KaxTagSimple *tag, int target_type )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Simple Tag ");
|
|
struct SimpleTagHandlerPayload
|
|
{
|
|
matroska_segment_c * const obj;
|
|
EbmlParser * const ep;
|
|
demux_sys_t & sys;
|
|
SimpleTag & out;
|
|
int target_type;
|
|
} payload = { this, &ep, sys, *pout_simple, 50 };
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, SimpleTagHandler, SimpleTagHandlerPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
E_CASE( KaxTagName, entry )
|
|
{
|
|
vars.out.tag_name = UTFstring( entry ).GetUTF8().c_str();
|
|
}
|
|
E_CASE( KaxTagString, entry )
|
|
{
|
|
vars.out.value = UTFstring( entry ).GetUTF8().c_str();
|
|
}
|
|
E_CASE( KaxTagLangue, entry )
|
|
{
|
|
vars.out.lang = entry;
|
|
}
|
|
E_CASE( KaxTagDefault, unused )
|
|
{
|
|
VLC_UNUSED(unused);
|
|
VLC_UNUSED(vars);
|
|
}
|
|
E_CASE( KaxTagSimple, simple )
|
|
{
|
|
SimpleTag st; // ParseSimpleTags will write to this variable
|
|
// the SimpleTag is valid if ParseSimpleTags returns `true`
|
|
|
|
if (vars.obj->ParseSimpleTags( &st, &simple, vars.target_type ))
|
|
vars.out.sub_tags.push_back( st );
|
|
}
|
|
};
|
|
SimpleTagHandler::Dispatcher().iterate( tag->begin(), tag->end(), &payload );
|
|
|
|
if( pout_simple->tag_name.empty() )
|
|
{
|
|
msg_Warn( &sys.demuxer, "Invalid MKV SimpleTag found.");
|
|
return false;
|
|
}
|
|
if( !sys.meta )
|
|
sys.meta = vlc_meta_New();
|
|
for( int i = 0; metadata_map[i].key; i++ )
|
|
{
|
|
if( pout_simple->tag_name == metadata_map[i].key &&
|
|
(metadata_map[i].target_type == 0 || target_type == metadata_map[i].target_type ) )
|
|
{
|
|
vlc_meta_Set( sys.meta, metadata_map[i].type, pout_simple->value.c_str () );
|
|
msg_Dbg( &sys.demuxer, "| | + Meta %s: %s", pout_simple->tag_name.c_str (), pout_simple->value.c_str ());
|
|
goto done;
|
|
}
|
|
}
|
|
msg_Dbg( &sys.demuxer, "| | + Meta %s: %s", pout_simple->tag_name.c_str (), pout_simple->value.c_str ());
|
|
vlc_meta_SetExtra( sys.meta, pout_simple->tag_name.c_str (), pout_simple->value.c_str ());
|
|
done:
|
|
return true;
|
|
}
|
|
|
|
void matroska_segment_c::LoadTags( KaxTags *tags_ )
|
|
{
|
|
/* Master elements */
|
|
if( unlikely( tags_->IsFiniteSize() && tags_->GetSize() >= SIZE_MAX ) )
|
|
{
|
|
msg_Err( &sys.demuxer, "Tags too big, aborting" );
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
EbmlElement *el;
|
|
int i_upper_level = 0;
|
|
tags_->Read( es, EBML_CONTEXT(tags_), i_upper_level, el, true );
|
|
}
|
|
catch(...)
|
|
{
|
|
msg_Err( &sys.demuxer, "Couldn't read tags" );
|
|
return;
|
|
}
|
|
|
|
struct TagsHandlerPayload
|
|
{
|
|
matroska_segment_c * const obj;
|
|
EbmlParser * const ep;
|
|
demux_sys_t & sys;
|
|
int target_type;
|
|
} payload = { this, &ep, sys, 50 };
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, KaxTagsHandler, TagsHandlerPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
E_CASE( KaxTag, entry )
|
|
{
|
|
msg_Dbg( &vars.sys.demuxer, "+ Tag" );
|
|
Tag tag;
|
|
struct TagHandlerPayload
|
|
{
|
|
matroska_segment_c * const obj;
|
|
EbmlParser * const ep;
|
|
demux_sys_t & sys;
|
|
Tag & tag;
|
|
int target_type;
|
|
} payload = { vars.obj, vars.ep, vars.sys, tag, 50 };
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, TagHandler, TagHandlerPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
E_CASE( KaxTagTargets, targets )
|
|
{
|
|
msg_Dbg( &vars.sys.demuxer, "| + Targets" );
|
|
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, TargetsHandler, TagHandlerPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
E_CASE( KaxTagTargetTypeValue, entry )
|
|
{
|
|
vars.target_type = static_cast<uint32>( entry );
|
|
msg_Dbg( &vars.sys.demuxer, "| | + TargetTypeValue: %u", vars.target_type);
|
|
}
|
|
E_CASE( KaxTagTrackUID, entry )
|
|
{
|
|
vars.tag.i_tag_type = TRACK_UID;
|
|
vars.tag.i_uid = static_cast<uint64>( entry );
|
|
msg_Dbg( &vars.sys.demuxer, "| | + TrackUID: %" PRIu64, vars.tag.i_uid);
|
|
}
|
|
E_CASE( KaxTagEditionUID, entry )
|
|
{
|
|
vars.tag.i_tag_type = EDITION_UID;
|
|
vars.tag.i_uid = static_cast<uint64>( entry );
|
|
msg_Dbg( &vars.sys.demuxer, "| | + EditionUID: %" PRIu64, vars.tag.i_uid);
|
|
}
|
|
E_CASE( KaxTagChapterUID, entry )
|
|
{
|
|
vars.tag.i_tag_type = CHAPTER_UID;
|
|
vars.tag.i_uid = static_cast<uint64>( entry );
|
|
msg_Dbg( &vars.sys.demuxer, "| | + ChapterUID: %" PRIu64, vars.tag.i_uid);
|
|
}
|
|
E_CASE( KaxTagAttachmentUID, entry )
|
|
{
|
|
vars.tag.i_tag_type = ATTACHMENT_UID;
|
|
vars.tag.i_uid = static_cast<uint64>( entry );
|
|
msg_Dbg( &vars.sys.demuxer, "| | + AttachmentUID: %" PRIu64, vars.tag.i_uid);
|
|
}
|
|
E_CASE( KaxTagTargetType, entry )
|
|
{
|
|
msg_Dbg( &vars.sys.demuxer, "| | + TargetType: %s", entry.GetValue().c_str());
|
|
}
|
|
E_CASE_DEFAULT( el )
|
|
{
|
|
msg_Dbg( &vars.sys.demuxer, "| | + Unknown (%s)", typeid(el).name() );
|
|
}
|
|
};
|
|
|
|
TargetsHandler::Dispatcher().iterate( targets.begin(), targets.end(), &vars );
|
|
}
|
|
E_CASE( KaxTagSimple, entry )
|
|
{
|
|
SimpleTag simple;
|
|
|
|
if (vars.obj->ParseSimpleTags( &simple, &entry, vars.target_type ))
|
|
vars.tag.simple_tags.push_back( simple );
|
|
}
|
|
E_CASE_DEFAULT( el )
|
|
{
|
|
msg_Dbg( &vars.sys.demuxer, "| | + Unknown (%s)", typeid(el).name() );
|
|
}
|
|
};
|
|
|
|
TagHandler::Dispatcher().iterate( entry.begin(), entry.end(), &payload );
|
|
vars.obj->tags.push_back(tag);
|
|
}
|
|
E_CASE_DEFAULT( el )
|
|
{
|
|
msg_Dbg( &vars.sys.demuxer, "| + LoadTag Unknown (%s)", typeid(el).name() );
|
|
}
|
|
};
|
|
|
|
KaxTagsHandler::Dispatcher().iterate( tags_->begin(), tags_->end(), &payload );
|
|
msg_Dbg( &sys.demuxer, "loading tags done." );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* InformationCreate:
|
|
*****************************************************************************/
|
|
void matroska_segment_c::InformationCreate( )
|
|
{
|
|
if( !sys.meta )
|
|
sys.meta = vlc_meta_New();
|
|
|
|
if( psz_title )
|
|
{
|
|
vlc_meta_SetTitle( sys.meta, psz_title );
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Misc
|
|
*****************************************************************************/
|
|
|
|
bool matroska_segment_c::PreloadClusters(uint64 i_cluster_pos)
|
|
{
|
|
struct ClusterHandlerPayload
|
|
{
|
|
matroska_segment_c * const obj;
|
|
bool stop_parsing;
|
|
|
|
} payload = { this, false };
|
|
|
|
MKV_SWITCH_CREATE(EbmlTypeDispatcher, ClusterHandler, ClusterHandlerPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
|
|
E_CASE( KaxCluster, kcluster )
|
|
{
|
|
vars.obj->ParseCluster( &kcluster, false );
|
|
}
|
|
|
|
E_CASE_DEFAULT( el )
|
|
{
|
|
VLC_UNUSED( el );
|
|
vars.stop_parsing = true;
|
|
}
|
|
};
|
|
|
|
{
|
|
es.I_O().setFilePointer( i_cluster_pos );
|
|
|
|
while (payload.stop_parsing == false)
|
|
{
|
|
EbmlParser parser ( &es, segment, &sys.demuxer );
|
|
EbmlElement* el = parser.Get();
|
|
|
|
if( el == NULL )
|
|
break;
|
|
|
|
ClusterHandler::Dispatcher().send( el, &payload );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool matroska_segment_c::PreloadFamily( const matroska_segment_c & of_segment )
|
|
{
|
|
if ( b_preloaded )
|
|
return false;
|
|
|
|
if ( SameFamily( of_segment ) )
|
|
return Preload( );
|
|
|
|
return false;
|
|
}
|
|
|
|
bool matroska_segment_c::CompareSegmentUIDs( const matroska_segment_c * p_item_a, const matroska_segment_c * p_item_b )
|
|
{
|
|
EbmlBinary *p_tmp;
|
|
|
|
if ( p_item_a == NULL || p_item_b == NULL )
|
|
return false;
|
|
|
|
p_tmp = static_cast<EbmlBinary *>( p_item_a->p_segment_uid );
|
|
if ( !p_tmp )
|
|
return false;
|
|
if ( p_item_b->p_prev_segment_uid != NULL
|
|
&& *p_tmp == *p_item_b->p_prev_segment_uid )
|
|
return true;
|
|
|
|
p_tmp = static_cast<EbmlBinary *>( p_item_a->p_next_segment_uid );
|
|
if ( !p_tmp )
|
|
return false;
|
|
|
|
if ( p_item_b->p_segment_uid != NULL
|
|
&& *p_tmp == *p_item_b->p_segment_uid )
|
|
return true;
|
|
|
|
if ( p_item_b->p_prev_segment_uid != NULL
|
|
&& *p_tmp == *p_item_b->p_prev_segment_uid )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool matroska_segment_c::SameFamily( const matroska_segment_c & of_segment ) const
|
|
{
|
|
for (size_t i=0; i<families.size(); i++)
|
|
{
|
|
for (size_t j=0; j<of_segment.families.size(); j++)
|
|
{
|
|
if ( *(families[i]) == *(of_segment.families[j]) )
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool matroska_segment_c::Preload( )
|
|
{
|
|
if ( b_preloaded )
|
|
return false;
|
|
|
|
EbmlElement *el = NULL;
|
|
|
|
ep.Reset( &sys.demuxer );
|
|
|
|
while( ( el = ep.Get() ) != NULL )
|
|
{
|
|
if( MKV_IS_ID( el, KaxSeekHead ) )
|
|
{
|
|
/* Multiple allowed */
|
|
/* We bail at 10, to prevent possible recursion */
|
|
msg_Dbg( &sys.demuxer, "| + Seek head" );
|
|
if( i_seekhead_count < 10 )
|
|
{
|
|
i_seekhead_position = el->GetElementPosition();
|
|
ParseSeekHead( static_cast<KaxSeekHead*>( el ) );
|
|
}
|
|
}
|
|
else if( MKV_IS_ID( el, KaxInfo ) )
|
|
{
|
|
/* Multiple allowed, mandatory */
|
|
msg_Dbg( &sys.demuxer, "| + Information" );
|
|
if( i_info_position < 0 )
|
|
{
|
|
ParseInfo( static_cast<KaxInfo*>( el ) );
|
|
i_info_position = el->GetElementPosition();
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( kt_ptr, KaxTracks, el ) )
|
|
{
|
|
/* Multiple allowed */
|
|
msg_Dbg( &sys.demuxer, "| + Tracks" );
|
|
if( i_tracks_position < 0 )
|
|
{
|
|
ParseTracks( kt_ptr );
|
|
}
|
|
if ( tracks.size() == 0 )
|
|
{
|
|
msg_Err( &sys.demuxer, "No tracks supported" );
|
|
}
|
|
i_tracks_position = el->GetElementPosition();
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( cues, KaxCues, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Cues" );
|
|
if( i_cues_position < 0 )
|
|
{
|
|
LoadCues( cues );
|
|
i_cues_position = el->GetElementPosition();
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( cluster_, KaxCluster, el ) )
|
|
{
|
|
if( sys.b_seekable &&
|
|
var_InheritBool( &sys.demuxer, "mkv-preload-clusters" ) )
|
|
{
|
|
PreloadClusters ( cluster_->GetElementPosition() );
|
|
es.I_O().setFilePointer( cluster_->GetElementPosition() );
|
|
}
|
|
msg_Dbg( &sys.demuxer, "| + Cluster" );
|
|
|
|
|
|
cluster = cluster_;
|
|
|
|
// add first cluster as trusted seekpoint for all tracks
|
|
for( tracks_map_t::const_iterator it = tracks.begin();
|
|
it != tracks.end(); ++it )
|
|
{
|
|
_seeker.add_seekpoint( it->first,
|
|
SegmentSeeker::Seekpoint( cluster->GetElementPosition(), -1,
|
|
SegmentSeeker::Seekpoint::TrustLevel::QUESTIONABLE ) );
|
|
}
|
|
|
|
/* stop pre-parsing the stream */
|
|
break;
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( ka_ptr, KaxAttachments, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Attachments" );
|
|
if( i_attachments_position < 0 )
|
|
{
|
|
ParseAttachments( ka_ptr );
|
|
i_attachments_position = el->GetElementPosition();
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( chapters, KaxChapters, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Chapters" );
|
|
if( i_chapters_position < 0 )
|
|
{
|
|
ParseChapters( chapters );
|
|
i_chapters_position = el->GetElementPosition();
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( tags_, KaxTags, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Tags" );
|
|
if(tags.empty ())
|
|
{
|
|
LoadTags( tags_ );
|
|
}
|
|
}
|
|
else if( MKV_IS_ID ( el, EbmlVoid ) )
|
|
msg_Dbg( &sys.demuxer, "| + Void" );
|
|
else
|
|
msg_Dbg( &sys.demuxer, "| + Preload Unknown (%s)", typeid(*el).name() );
|
|
}
|
|
|
|
ComputeTrackPriority();
|
|
|
|
b_preloaded = true;
|
|
|
|
if( cluster )
|
|
EnsureDuration();
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Here we try to load elements that were found in Seek Heads, but not yet parsed */
|
|
bool matroska_segment_c::LoadSeekHeadItem( const EbmlCallbacks & ClassInfos, int64_t i_element_position )
|
|
{
|
|
int64_t i_sav_position = static_cast<int64_t>( es.I_O().getFilePointer() );
|
|
EbmlElement *el;
|
|
|
|
es.I_O().setFilePointer( i_element_position, seek_beginning );
|
|
el = es.FindNextID( ClassInfos, 0xFFFFFFFFL);
|
|
|
|
if( el == NULL )
|
|
{
|
|
msg_Err( &sys.demuxer, "cannot load some cues/chapters/tags etc. (broken seekhead or file)" );
|
|
es.I_O().setFilePointer( i_sav_position, seek_beginning );
|
|
return false;
|
|
}
|
|
|
|
if( MKV_CHECKED_PTR_DECL ( ksh_ptr, KaxSeekHead, el ) )
|
|
{
|
|
/* Multiple allowed */
|
|
msg_Dbg( &sys.demuxer, "| + Seek head" );
|
|
if( i_seekhead_count < 10 )
|
|
{
|
|
if ( i_seekhead_position != i_element_position )
|
|
{
|
|
i_seekhead_position = i_element_position;
|
|
ParseSeekHead( ksh_ptr );
|
|
}
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( ki_ptr, KaxInfo, el ) ) // FIXME
|
|
{
|
|
/* Multiple allowed, mandatory */
|
|
msg_Dbg( &sys.demuxer, "| + Information" );
|
|
if( i_info_position < 0 )
|
|
{
|
|
ParseInfo( ki_ptr );
|
|
i_info_position = i_element_position;
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( kt_ptr, KaxTracks, el ) ) // FIXME
|
|
{
|
|
/* Multiple allowed */
|
|
msg_Dbg( &sys.demuxer, "| + Tracks" );
|
|
if( i_tracks_position < 0 )
|
|
ParseTracks( kt_ptr );
|
|
if ( tracks.size() == 0 )
|
|
{
|
|
msg_Err( &sys.demuxer, "No tracks supported" );
|
|
delete el;
|
|
es.I_O().setFilePointer( i_sav_position, seek_beginning );
|
|
return false;
|
|
}
|
|
i_tracks_position = i_element_position;
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( kc_ptr, KaxCues, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Cues" );
|
|
if( i_cues_position < 0 )
|
|
{
|
|
LoadCues( kc_ptr );
|
|
i_cues_position = i_element_position;
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( ka_ptr, KaxAttachments, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Attachments" );
|
|
if( i_attachments_position < 0 )
|
|
{
|
|
ParseAttachments( ka_ptr );
|
|
i_attachments_position = i_element_position;
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( chapters, KaxChapters, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Chapters" );
|
|
if( i_chapters_position < 0 )
|
|
{
|
|
ParseChapters( chapters );
|
|
i_chapters_position = i_element_position;
|
|
}
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( tags_, KaxTags, el ) )
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + Tags" );
|
|
if(tags.empty ())
|
|
{
|
|
LoadTags( tags_ );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
msg_Dbg( &sys.demuxer, "| + LoadSeekHeadItem Unknown (%s)", typeid(*el).name() );
|
|
}
|
|
delete el;
|
|
|
|
es.I_O().setFilePointer( i_sav_position, seek_beginning );
|
|
return true;
|
|
}
|
|
|
|
bool matroska_segment_c::Seek( demux_t &demuxer, vlc_tick_t i_absolute_mk_date, vlc_tick_t i_mk_time_offset, bool b_accurate )
|
|
{
|
|
SegmentSeeker::tracks_seekpoint_t seekpoints;
|
|
|
|
SegmentSeeker::fptr_t i_seek_position = std::numeric_limits<SegmentSeeker::fptr_t>::max();
|
|
vlc_tick_t i_mk_seek_time = -1;
|
|
vlc_tick_t i_mk_date = i_absolute_mk_date - i_mk_time_offset;
|
|
SegmentSeeker::track_ids_t selected_tracks;
|
|
SegmentSeeker::track_ids_t priority;
|
|
|
|
// reset information for all tracks //
|
|
|
|
for( tracks_map_t::iterator it = tracks.begin(); it != tracks.end(); ++it )
|
|
{
|
|
mkv_track_t &track = *it->second;
|
|
|
|
track.i_skip_until_fpos = std::numeric_limits<uint64_t>::max();
|
|
if( track.i_last_dts != VLC_TICK_INVALID )
|
|
track.b_discontinuity = true;
|
|
track.i_last_dts = VLC_TICK_INVALID;
|
|
|
|
bool selected;
|
|
if (track.p_es == NULL)
|
|
selected = false;
|
|
else
|
|
es_out_Control( demuxer.out, ES_OUT_GET_ES_STATE, track.p_es, &selected );
|
|
if ( selected )
|
|
selected_tracks.push_back( track.i_number );
|
|
}
|
|
|
|
if ( selected_tracks.empty() )
|
|
{
|
|
selected_tracks = priority_tracks;
|
|
priority = priority_tracks;
|
|
}
|
|
else
|
|
{
|
|
std::set_intersection(priority_tracks.begin(),priority_tracks.end(),
|
|
selected_tracks.begin(),selected_tracks.end(),
|
|
std::back_inserter(priority));
|
|
if (priority.empty()) // no video selected ?
|
|
priority = selected_tracks;
|
|
}
|
|
|
|
// find appropriate seekpoints //
|
|
|
|
try {
|
|
seekpoints = _seeker.get_seekpoints( *this, i_mk_date, priority, selected_tracks );
|
|
}
|
|
catch( std::exception const& e )
|
|
{
|
|
msg_Err( &sys.demuxer, "error during seek: \"%s\", aborting!", e.what() );
|
|
return false;
|
|
}
|
|
|
|
// initialize seek information in order to set up playback //
|
|
|
|
for( SegmentSeeker::tracks_seekpoint_t::const_iterator it = seekpoints.begin(); it != seekpoints.end(); ++it )
|
|
{
|
|
tracks_map_t::iterator trackit = tracks.find( it->first );
|
|
if ( trackit == tracks.end() )
|
|
continue; // there were blocks with unknown tracks
|
|
|
|
if( i_seek_position > it->second.fpos )
|
|
{
|
|
i_seek_position = it->second.fpos;
|
|
i_mk_seek_time = it->second.pts;
|
|
}
|
|
|
|
// blocks that will be not be read until this fpos
|
|
if ( b_accurate )
|
|
trackit->second->i_skip_until_fpos = it->second.fpos;
|
|
else
|
|
trackit->second->i_skip_until_fpos = std::numeric_limits<uint64_t>::max();
|
|
if (it->second.pts != -1)
|
|
trackit->second->i_last_dts = it->second.pts + i_mk_time_offset;
|
|
|
|
msg_Dbg( &sys.demuxer, "seek: preroll{ track: %u, pts: %" PRId64 ", fpos: %" PRIu64 " skip: %" PRIu64 "} ",
|
|
it->first, it->second.pts, it->second.fpos, trackit->second->i_skip_until_fpos );
|
|
}
|
|
|
|
if ( i_seek_position == std::numeric_limits<SegmentSeeker::fptr_t>::max() )
|
|
return false;
|
|
|
|
// propagate seek information //
|
|
|
|
sys.i_pcr = VLC_TICK_INVALID;
|
|
sys.i_pts = VLC_TICK_0 + i_mk_seek_time + i_mk_time_offset;
|
|
if (b_accurate)
|
|
sys.i_start_pts = VLC_TICK_0 + i_absolute_mk_date;
|
|
else
|
|
sys.i_start_pts = sys.i_pts;
|
|
|
|
// make the jump //
|
|
|
|
_seeker.mkv_jump_to( *this, i_seek_position );
|
|
|
|
// debug diagnostics //
|
|
|
|
msg_Dbg( &sys.demuxer, "seek: preroll{ req: %" PRId64 ", start-pts: %" PRId64 ", start-fpos: %" PRIu64 "} ",
|
|
sys.i_start_pts, sys.i_pts, i_seek_position );
|
|
|
|
// blocks that will be read and decoded but discarded until this pts
|
|
es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, sys.i_start_pts );
|
|
return true;
|
|
}
|
|
|
|
|
|
mkv_track_t * matroska_segment_c::FindTrackByBlock(
|
|
const KaxBlock *p_block, const KaxSimpleBlock *p_simpleblock )
|
|
{
|
|
tracks_map_t::iterator track_it;
|
|
|
|
if (p_block != NULL)
|
|
track_it = tracks.find( p_block->TrackNum() );
|
|
else if( p_simpleblock != NULL)
|
|
track_it = tracks.find( p_simpleblock->TrackNum() );
|
|
else
|
|
track_it = tracks.end();
|
|
|
|
if (track_it == tracks.end())
|
|
return NULL;
|
|
|
|
return track_it->second.get();
|
|
}
|
|
|
|
void matroska_segment_c::ComputeTrackPriority()
|
|
{
|
|
bool b_has_default_video = false;
|
|
bool b_has_default_audio = false;
|
|
/* check for default */
|
|
for( tracks_map_t::const_iterator it = tracks.begin(); it != tracks.end();
|
|
++it )
|
|
{
|
|
mkv_track_t &track = *it->second;
|
|
|
|
bool flag = track.b_enabled && ( track.b_default || track.b_forced );
|
|
|
|
switch( track.fmt.i_cat )
|
|
{
|
|
case VIDEO_ES: b_has_default_video |= flag; break;
|
|
case AUDIO_ES: b_has_default_audio |= flag; break;
|
|
default: break; // ignore
|
|
}
|
|
}
|
|
|
|
for( tracks_map_t::iterator it = tracks.begin(); it != tracks.end(); ++it )
|
|
{
|
|
tracks_map_t::key_type track_id = it->first;
|
|
mkv_track_t & track = *it->second;
|
|
|
|
if( unlikely( track.fmt.i_cat == UNKNOWN_ES || track.codec.empty() ) )
|
|
{
|
|
msg_Warn( &sys.demuxer, "invalid track[%d]", static_cast<int>( track_id ) );
|
|
track.p_es = NULL;
|
|
continue;
|
|
}
|
|
else if( unlikely( !b_has_default_video && track.fmt.i_cat == VIDEO_ES ) )
|
|
{
|
|
track.b_default = true;
|
|
b_has_default_video = true;
|
|
}
|
|
else if( unlikely( !b_has_default_audio && track.fmt.i_cat == AUDIO_ES ) )
|
|
{
|
|
track.b_default = true;
|
|
b_has_default_audio = true;
|
|
}
|
|
if( unlikely( !track.b_enabled ) )
|
|
track.fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
|
|
else if( track.b_forced )
|
|
track.fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 2;
|
|
else if( track.b_default )
|
|
track.fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1;
|
|
else
|
|
track.fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN;
|
|
|
|
/* Avoid multivideo tracks when unnecessary */
|
|
if( track.fmt.i_cat == VIDEO_ES )
|
|
track.fmt.i_priority--;
|
|
}
|
|
|
|
// find track(s) with highest priority //
|
|
{
|
|
int score = -1;
|
|
int es_type = -1;
|
|
|
|
for( tracks_map_t::const_iterator it = this->tracks.begin(); it != this->tracks.end(); ++it )
|
|
{
|
|
int track_score = -1;
|
|
|
|
switch( it->second->fmt.i_cat )
|
|
{
|
|
case VIDEO_ES: ++track_score;
|
|
/* fallthrough */
|
|
case AUDIO_ES: ++track_score;
|
|
/* fallthrough */
|
|
case SPU_ES: ++track_score;
|
|
/* fallthrough */
|
|
default:
|
|
if( score < track_score )
|
|
{
|
|
es_type = it->second->fmt.i_cat;
|
|
score = track_score;
|
|
}
|
|
}
|
|
}
|
|
|
|
for( tracks_map_t::const_iterator it = this->tracks.begin(); it != this->tracks.end(); ++it )
|
|
{
|
|
if( it->second->fmt.i_cat == es_type )
|
|
priority_tracks.push_back( it->first );
|
|
}
|
|
}
|
|
}
|
|
|
|
void matroska_segment_c::EnsureDuration()
|
|
{
|
|
if ( i_duration > 0 )
|
|
return;
|
|
|
|
i_duration = -1;
|
|
|
|
if( !sys.b_fastseekable )
|
|
{
|
|
msg_Warn( &sys.demuxer, "could not look for the segment duration" );
|
|
return;
|
|
}
|
|
|
|
uint64 i_current_position = es.I_O().getFilePointer();
|
|
uint64 i_last_cluster_pos = cluster->GetElementPosition();
|
|
|
|
// find the last Cluster from the Cues
|
|
|
|
if ( b_cues && _seeker._cluster_positions.size() )
|
|
i_last_cluster_pos = *_seeker._cluster_positions.rbegin();
|
|
else if( !cluster->IsFiniteSize() )
|
|
return;
|
|
|
|
es.I_O().setFilePointer( i_last_cluster_pos, seek_beginning );
|
|
|
|
EbmlParser eparser ( &es, segment, &sys.demuxer );
|
|
|
|
// locate the definitely last cluster in the stream
|
|
|
|
while( EbmlElement* el = eparser.Get() )
|
|
{
|
|
if( !el->IsFiniteSize() && el->GetElementPosition() != i_last_cluster_pos )
|
|
{
|
|
es.I_O().setFilePointer( i_current_position, seek_beginning );
|
|
return;
|
|
}
|
|
|
|
if( MKV_IS_ID( el, KaxCluster ) )
|
|
{
|
|
i_last_cluster_pos = el->GetElementPosition();
|
|
if ( i_last_cluster_pos == cluster->GetElementPosition() )
|
|
// make sure our first Cluster has a timestamp
|
|
ParseCluster( cluster, false, SCOPE_PARTIAL_DATA );
|
|
}
|
|
}
|
|
|
|
// find the last timecode in the Cluster
|
|
|
|
eparser.Reset( &sys.demuxer );
|
|
es.I_O().setFilePointer( i_last_cluster_pos, seek_beginning );
|
|
|
|
EbmlElement* el = eparser.Get();
|
|
MKV_CHECKED_PTR_DECL( p_last_cluster, KaxCluster, el );
|
|
|
|
if( p_last_cluster &&
|
|
ParseCluster( p_last_cluster, false, SCOPE_PARTIAL_DATA ) )
|
|
{
|
|
// use the last block + duration
|
|
uint64 i_last_timecode = p_last_cluster->GlobalTimecode();
|
|
for( unsigned int i = 0; i < p_last_cluster->ListSize(); i++ )
|
|
{
|
|
EbmlElement *l = (*p_last_cluster)[i];
|
|
|
|
if( MKV_CHECKED_PTR_DECL ( simpleblock, KaxSimpleBlock, l ) )
|
|
{
|
|
simpleblock->SetParent( *p_last_cluster );
|
|
i_last_timecode = std::max(i_last_timecode, simpleblock->GlobalTimecode());
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( group, KaxBlockGroup, l ) )
|
|
{
|
|
uint64 i_group_timecode = 0;
|
|
for( unsigned int j = 0; j < group->ListSize(); j++ )
|
|
{
|
|
EbmlElement *g = (*group)[j];
|
|
|
|
if( MKV_CHECKED_PTR_DECL ( block, KaxBlock, g ) )
|
|
{
|
|
block->SetParent( *p_last_cluster );
|
|
i_group_timecode += block->GlobalTimecode();
|
|
}
|
|
else if( MKV_CHECKED_PTR_DECL ( kbd_ptr, KaxBlockDuration, g ) )
|
|
{
|
|
i_group_timecode += static_cast<uint64>( *kbd_ptr );
|
|
}
|
|
}
|
|
i_last_timecode = std::max(i_last_timecode, i_group_timecode);
|
|
}
|
|
}
|
|
|
|
i_duration = VLC_TICK_FROM_NS( i_last_timecode - cluster->GlobalTimecode() );
|
|
msg_Dbg( &sys.demuxer, " extracted Duration=%" PRId64, SEC_FROM_VLC_TICK(i_duration) );
|
|
}
|
|
|
|
// get back to the reading position we were at before looking for a duration
|
|
es.I_O().setFilePointer( i_current_position, seek_beginning );
|
|
}
|
|
|
|
bool matroska_segment_c::ESCreate()
|
|
{
|
|
/* add all es */
|
|
msg_Dbg( &sys.demuxer, "found %d es", static_cast<int>( tracks.size() ) );
|
|
|
|
mkv_track_t *default_tracks[ES_CATEGORY_COUNT] = {};
|
|
for( tracks_map_t::iterator it = tracks.begin(); it != tracks.end(); ++it )
|
|
{
|
|
tracks_map_t::key_type track_id = it->first;
|
|
mkv_track_t & track = *it->second;
|
|
|
|
if( unlikely( track.fmt.i_cat == UNKNOWN_ES || track.codec.empty() ) )
|
|
{
|
|
msg_Warn( &sys.demuxer, "invalid track[%d]", static_cast<int>( track_id ) );
|
|
track.p_es = NULL;
|
|
continue;
|
|
}
|
|
track.fmt.i_id = static_cast<int>( track_id );
|
|
|
|
if( !track.p_es )
|
|
{
|
|
track.p_es = es_out_Add( sys.demuxer.out, &track.fmt );
|
|
|
|
if( track.p_es &&
|
|
!sys.ev.AddES( track.p_es, track.fmt.i_cat ) )
|
|
{
|
|
msg_Warn( &sys.demuxer, "Could not register events, interactive menus will not work");
|
|
}
|
|
}
|
|
|
|
/* Turn on a subtitles track if it has been flagged as default -
|
|
* but only do this if no subtitles track has already been engaged,
|
|
* either by an earlier 'default track' (??) or by default
|
|
* language choice behaviour.
|
|
*/
|
|
if( track.b_default || track.b_forced )
|
|
{
|
|
mkv_track_t *&default_track = default_tracks[track.fmt.i_cat];
|
|
if( !default_track || track.b_default )
|
|
default_track = &track;
|
|
}
|
|
}
|
|
|
|
for( mkv_track_t *track : default_tracks )
|
|
{
|
|
if( track )
|
|
es_out_Control( sys.demuxer.out, ES_OUT_SET_ES_DEFAULT, track->p_es );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void matroska_segment_c::ESDestroy( )
|
|
{
|
|
sys.ev.ResetPci();
|
|
|
|
for( tracks_map_t::iterator it = tracks.begin(); it != tracks.end(); ++it )
|
|
{
|
|
mkv_track_t & track = *it->second;
|
|
|
|
if( track.p_es != NULL )
|
|
{
|
|
es_out_Del( sys.demuxer.out, track.p_es );
|
|
sys.ev.DelES( track.p_es );
|
|
track.p_es = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int matroska_segment_c::BlockGet( KaxBlock * & pp_block, KaxSimpleBlock * & pp_simpleblock,
|
|
KaxBlockAdditions * & pp_additions,
|
|
bool *pb_key_picture, bool *pb_discardable_picture,
|
|
int64_t *pi_duration )
|
|
{
|
|
pp_simpleblock = NULL;
|
|
pp_block = NULL;
|
|
pp_additions = NULL;
|
|
|
|
*pb_key_picture = true;
|
|
*pb_discardable_picture = false;
|
|
*pi_duration = 0;
|
|
|
|
struct BlockPayload {
|
|
matroska_segment_c * const obj;
|
|
EbmlParser * const ep;
|
|
demux_t * const p_demuxer;
|
|
KaxBlock *& block;
|
|
KaxSimpleBlock *& simpleblock;
|
|
KaxBlockAdditions *& additions;
|
|
|
|
int64_t & i_duration;
|
|
bool & b_key_picture;
|
|
bool & b_discardable_picture;
|
|
bool b_cluster_timecode;
|
|
|
|
} payload = {
|
|
this, &ep, &sys.demuxer, pp_block, pp_simpleblock, pp_additions,
|
|
*pi_duration, *pb_key_picture, *pb_discardable_picture, true
|
|
};
|
|
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, BlockGetHandler_l1, BlockPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
|
|
E_CASE( KaxCluster, kcluster )
|
|
{
|
|
vars.obj->cluster = &kcluster;
|
|
vars.b_cluster_timecode = false;
|
|
vars.ep->Down ();
|
|
}
|
|
E_CASE( KaxCues, kcue )
|
|
{
|
|
VLC_UNUSED( kcue );
|
|
msg_Warn( vars.p_demuxer, "find KaxCues FIXME" );
|
|
}
|
|
E_CASE_DEFAULT(element)
|
|
{
|
|
msg_Dbg( vars.p_demuxer, "Unknown (%s)", typeid (element).name () );
|
|
}
|
|
};
|
|
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, BlockGetHandler_l2, BlockPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
|
|
E_CASE( KaxClusterTimecode, ktimecode )
|
|
{
|
|
ktimecode.ReadData( vars.obj->es.I_O(), SCOPE_ALL_DATA );
|
|
vars.obj->cluster->InitTimecode( static_cast<uint64>( ktimecode ), vars.obj->i_timescale );
|
|
vars.obj->_seeker.add_cluster( vars.obj->cluster );
|
|
vars.b_cluster_timecode = true;
|
|
}
|
|
E_CASE( KaxClusterSilentTracks, ksilent )
|
|
{
|
|
vars.ep->Down ();
|
|
|
|
VLC_UNUSED( ksilent );
|
|
}
|
|
E_CASE( KaxBlockGroup, kbgroup )
|
|
{
|
|
vars.obj->i_block_pos = kbgroup.GetElementPosition();
|
|
vars.ep->Down ();
|
|
}
|
|
E_CASE( KaxSimpleBlock, ksblock )
|
|
{
|
|
if( vars.b_cluster_timecode == false )
|
|
{
|
|
msg_Warn( vars.p_demuxer, "ignoring SimpleBlock prior to mandatory Timecode" );
|
|
return;
|
|
}
|
|
|
|
vars.simpleblock = &ksblock;
|
|
vars.simpleblock->ReadData( vars.obj->es.I_O() );
|
|
vars.simpleblock->SetParent( *vars.obj->cluster );
|
|
|
|
if( ksblock.IsKeyframe() )
|
|
{
|
|
bool const b_valid_track = vars.obj->FindTrackByBlock( NULL, &ksblock ) != NULL;
|
|
if (b_valid_track)
|
|
vars.obj->_seeker.add_seekpoint( ksblock.TrackNum(),
|
|
SegmentSeeker::Seekpoint( ksblock.GetElementPosition(), VLC_TICK_FROM_NS(ksblock.GlobalTimecode()) ) );
|
|
}
|
|
}
|
|
};
|
|
|
|
MKV_SWITCH_CREATE( EbmlTypeDispatcher, BlockGetHandler_l3, BlockPayload )
|
|
{
|
|
MKV_SWITCH_INIT();
|
|
|
|
E_CASE( KaxBlock, kblock )
|
|
{
|
|
vars.block = &kblock;
|
|
vars.block->ReadData( vars.obj->es.I_O() );
|
|
vars.block->SetParent( *vars.obj->cluster );
|
|
|
|
const mkv_track_t *p_track = vars.obj->FindTrackByBlock( &kblock, NULL );
|
|
if( p_track != NULL && p_track->fmt.i_cat == SPU_ES )
|
|
{
|
|
vars.obj->_seeker.add_seekpoint( kblock.TrackNum(),
|
|
SegmentSeeker::Seekpoint( kblock.GetElementPosition(), VLC_TICK_FROM_NS(kblock.GlobalTimecode()) ) );
|
|
}
|
|
|
|
vars.ep->Keep ();
|
|
}
|
|
E_CASE( KaxBlockAdditions, kadditions )
|
|
{
|
|
EbmlElement *el;
|
|
int i_upper_level = 0;
|
|
try
|
|
{
|
|
kadditions.Read( vars.obj->es, EBML_CONTEXT(&kadditions), i_upper_level, el, false );
|
|
vars.additions = &kadditions;
|
|
vars.ep->Keep ();
|
|
} catch (...) {}
|
|
}
|
|
E_CASE( KaxBlockDuration, kduration )
|
|
{
|
|
kduration.ReadData( vars.obj->es.I_O() );
|
|
vars.i_duration = static_cast<uint64>( kduration );
|
|
}
|
|
E_CASE( KaxReferenceBlock, kreference )
|
|
{
|
|
kreference.ReadData( vars.obj->es.I_O() );
|
|
|
|
if( vars.b_key_picture )
|
|
vars.b_key_picture = false;
|
|
else if( static_cast<int64>( kreference ) )
|
|
vars.b_discardable_picture = true;
|
|
}
|
|
E_CASE( KaxClusterSilentTrackNumber, kstrackn )
|
|
{
|
|
VLC_UNUSED( kstrackn );
|
|
VLC_UNUSED( vars );
|
|
}
|
|
#if LIBMATROSKA_VERSION >= 0x010401
|
|
E_CASE( KaxDiscardPadding, kdiscardp )
|
|
{
|
|
kdiscardp.ReadData( vars.obj->es.I_O() );
|
|
int64 i_duration = static_cast<int64>( kdiscardp );
|
|
|
|
if( vars.i_duration < i_duration )
|
|
vars.i_duration = 0;
|
|
else
|
|
vars.i_duration -= i_duration;
|
|
}
|
|
#endif
|
|
E_CASE_DEFAULT( element )
|
|
{
|
|
VLC_UNUSED(element);
|
|
|
|
msg_Warn( vars.p_demuxer, "unknown element at { fpos: %" PRId64 ", '%s' }",
|
|
element.GetElementPosition(), typeid( element ).name() );
|
|
}
|
|
};
|
|
|
|
static EbmlTypeDispatcher const * const dispatchers[] = {
|
|
&BlockGetHandler_l1::Dispatcher(),
|
|
&BlockGetHandler_l2::Dispatcher(),
|
|
&BlockGetHandler_l3::Dispatcher()
|
|
};
|
|
|
|
for( ;; )
|
|
{
|
|
EbmlElement *el = NULL;
|
|
int i_level;
|
|
|
|
if( pp_simpleblock != NULL || ((el = ep.Get()) == NULL && pp_block != NULL) )
|
|
{
|
|
/* Check blocks validity to protect against broken files */
|
|
const mkv_track_t *p_track = FindTrackByBlock( pp_block , pp_simpleblock );
|
|
if( p_track == NULL )
|
|
{
|
|
ep.Unkeep();
|
|
pp_simpleblock = NULL;
|
|
pp_block = NULL;
|
|
continue;
|
|
}
|
|
if( pp_simpleblock != NULL )
|
|
{
|
|
*pb_key_picture = pp_simpleblock->IsKeyframe();
|
|
*pb_discardable_picture = pp_simpleblock->IsDiscardable();
|
|
}
|
|
/* We have block group let's check if the picture is a keyframe */
|
|
else if( *pb_key_picture )
|
|
{
|
|
if( p_track->fmt.i_codec == VLC_CODEC_THEORA )
|
|
{
|
|
DataBuffer * p_data = &pp_block->GetBuffer(0);
|
|
const uint8_t * p_buff = p_data->Buffer();
|
|
/* if the second bit of a Theora frame is 1
|
|
it's not a keyframe */
|
|
if( p_data->Size() && p_buff )
|
|
{
|
|
if( p_buff[0] & 0x40 )
|
|
*pb_key_picture = false;
|
|
}
|
|
else
|
|
*pb_key_picture = false;
|
|
}
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
i_level = ep.GetLevel();
|
|
|
|
if( el == NULL )
|
|
{
|
|
if( i_level > 1 )
|
|
{
|
|
ep.Up();
|
|
continue;
|
|
}
|
|
msg_Warn( &sys.demuxer, "EOF" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* Verify that we are still inside our cluster
|
|
* It can happens with broken files and when seeking
|
|
* without index */
|
|
if( i_level > 1 )
|
|
{
|
|
if( cluster && !ep.IsTopPresent( cluster ) )
|
|
{
|
|
msg_Warn( &sys.demuxer, "Unexpected escape from current cluster" );
|
|
cluster = NULL;
|
|
}
|
|
if( !cluster )
|
|
continue;
|
|
}
|
|
|
|
/* do parsing */
|
|
|
|
try {
|
|
switch( i_level )
|
|
{
|
|
case 2:
|
|
/* fallthrough */
|
|
case 3:
|
|
if( unlikely( !el->ValidateSize() || ( el->IsFiniteSize() && el->GetSize() >= SIZE_MAX ) ) )
|
|
{
|
|
msg_Err( &sys.demuxer, "Error while reading %s... upping level", typeid(*el).name());
|
|
ep.Up();
|
|
|
|
if ( i_level == 2 )
|
|
break;
|
|
|
|
ep.Unkeep();
|
|
pp_simpleblock = NULL;
|
|
pp_block = NULL;
|
|
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
case 1:
|
|
{
|
|
EbmlTypeDispatcher const * dispatcher = dispatchers[i_level - 1];
|
|
dispatcher->send( el, &payload );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
msg_Err( &sys.demuxer, "invalid level = %d", i_level );
|
|
return VLC_EGENERIC;
|
|
}
|
|
}
|
|
catch (int ret_code)
|
|
{
|
|
return ret_code;
|
|
}
|
|
catch (...)
|
|
{
|
|
msg_Err( &sys.demuxer, "Error while reading %s... upping level", typeid(*el).name());
|
|
ep.Up();
|
|
ep.Unkeep();
|
|
pp_simpleblock = NULL;
|
|
pp_block = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|