/*****************************************************************************
* EbmlParser for the matroska demuxer
*****************************************************************************
* Copyright (C) 2003-2004 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 "Ebml_parser.hpp"
#include "stream_io_callback.hpp"
namespace mkv {
/*****************************************************************************
* Ebml Stream parser
*****************************************************************************/
EbmlParser::EbmlParser( matroska_iostream_c *es, EbmlElement *el_start, demux_t *p_demux ) :
p_demux( p_demux ),
m_es( es ),
mi_level( 1 ),
mi_user_level( 1 ),
mb_keep( false ),
mb_dummy( var_InheritBool( p_demux, "mkv-use-dummy" ) )
{
memset( m_el, 0, sizeof( *m_el ) * M_EL_MAXSIZE);
m_el[0] = el_start;
}
EbmlParser::~EbmlParser( void )
{
if( !mi_level )
{
assert( !mb_keep );
delete m_el[1];
return;
}
for( size_t i = 1; i <= mi_level; i++ )
{
if( !mb_keep )
{
delete m_el[i];
}
mb_keep = false;
}
}
void EbmlParser::reconstruct( matroska_iostream_c* es, EbmlElement* el_start, demux_t* p_demux )
{
this->~EbmlParser();
new( static_cast<void*>( this ) ) EbmlParser( es, el_start, p_demux );
}
void EbmlParser::Up( void )
{
if( mi_user_level == mi_level && m_el[mi_level] )
{
msg_Warn( p_demux, "MKV/Ebml Parser: Up cannot escape itself" );
}
assert(mi_user_level != 0);
mi_user_level--;
}
void EbmlParser::Down( void )
{
assert(m_el[mi_level] != nullptr);
mi_user_level++;
mi_level++;
}
void EbmlParser::Keep( void )
{
mb_keep = true;
}
void EbmlParser::Unkeep()
{
mb_keep = false;
}
int EbmlParser::GetLevel( void ) const
{
return mi_user_level;
}
void EbmlParser::Reset( demux_t *p_demux )
{
while ( mi_level > 0)
{
delete m_el[mi_level];
m_el[mi_level] = NULL;
mi_level--;
}
this->p_demux = p_demux;
mi_user_level = mi_level = 1;
return_previous_parent = false;
// a little faster and cleaner
m_es->I_O().setFilePointer( static_cast<EbmlMaster*>(m_el[0])->GetDataStart() );
}
static const EbmlSemanticContext & GetEbmlNoGlobal_Context();
static const EbmlSemanticContext EbmlNoGlobal_Context = EbmlSemanticContext(0, NULL, NULL, *GetEbmlNoGlobal_Context, NULL);
static const EbmlSemanticContext & GetEbmlNoGlobal_Context()
{
return EbmlNoGlobal_Context;
}
// the Segment Context should not allow Void or CRC32 elements to avoid lookup false alarm
DEFINE_START_SEMANTIC(KaxSegmentVLC)
DEFINE_SEMANTIC_ITEM(true, true, EbmlHead)
DEFINE_SEMANTIC_ITEM(false, false, KaxSegment)
DEFINE_END_SEMANTIC(KaxSegmentVLC)
DEFINE_xxx_CONTEXT(KaxSegmentVLC,GetEbmlNoGlobal_Context)
EbmlElement *EbmlParser::Get( bool allow_overshoot )
{
int i_ulev = 0;
int n_call = 0;
EbmlElement *p_prev = NULL;
bool do_read = true;
if( mi_user_level != mi_level )
{
return NULL;
}
if( return_previous_parent )
{
EbmlElement *ret = m_el[mi_level];
return_previous_parent = false;
if( mi_level > 0 && m_el[mi_level-1]->IsFiniteSize() )
{
if( ret->IsFiniteSize() &&
ret->GetEndPosition() > m_el[mi_level-1]->GetEndPosition() )
{
msg_Err( p_demux, "EBML element at %" PRIu64 " extends beyond parent boundary (%" PRIu64 " beyond %" PRIu64 ")",
ret->GetElementPosition(), ret->GetEndPosition(), m_el[mi_level-1]->GetEndPosition() );
delete ret;
m_el[mi_level] = NULL;
return NULL;
}
if( !ret->IsFiniteSize() )
{
msg_Dbg( p_demux, "Infinite EBML element %s at %" PRIu64 " inside finite parent",
EBML_NAME(ret), ret->GetElementPosition() );
}
}
return ret;
}
next:
p_prev = m_el[mi_level];
if( p_prev && p_prev->IsFiniteSize() )
p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
uint64_t i_max_read;
if (mi_level == 0)
i_max_read = UINT64_MAX;
else if (!m_el[mi_level-1]->IsFiniteSize())
i_max_read = UINT64_MAX;
else if (!p_prev)
{
i_max_read = m_el[mi_level-1]->GetSize();
if (i_max_read == 0)
{
/* check if the parent still has data to read */
if ( mi_level > 1 && m_el[mi_level-2]->IsFiniteSize() &&
m_el[mi_level-1]->GetEndPosition() < m_el[mi_level-2]->GetEndPosition() )
{
uint64_t top = m_el[mi_level-2]->GetEndPosition();
uint64_t bom = m_el[mi_level-1]->GetEndPosition();
i_max_read = top - bom;
}
}
}
else {
size_t size_lvl = mi_level;
while ( size_lvl && m_el[size_lvl-1]->IsFiniteSize() && m_el[size_lvl]->IsFiniteSize() &&
m_el[size_lvl-1]->GetEndPosition() == m_el[size_lvl]->GetEndPosition() )
size_lvl--;
if (size_lvl == 0 && !allow_overshoot)
{
i_ulev = mi_level; // trick to go all the way up
m_el[mi_level] = NULL;
do_read = false;
}
else if (size_lvl == 0 || !m_el[size_lvl-1]->IsFiniteSize() || !m_el[size_lvl]->IsFiniteSize() )
i_max_read = UINT64_MAX;
else {
uint64_t top = m_el[size_lvl-1]->GetEndPosition();
uint64_t bom = m_el[mi_level]->GetEndPosition();
i_max_read = top - bom;
}
}
if (do_read)
{
// avoid leaking previous element at the level we're reading
if(m_el[mi_level] != nullptr && !mb_keep)
{
assert(m_el[mi_level] == p_prev);
delete m_el[mi_level];
p_prev = nullptr;
}
// If the parent is a segment, use the segment context when creating children
// (to prolong their lifetime), otherwise just continue as normal
EbmlSemanticContext e_context =
(mi_level == 0 || EBML_CTX_MASTER( EBML_CONTEXT(m_el[mi_level - 1]) ) == EBML_CTX_MASTER( Context_KaxSegmentVLC ))
? Context_KaxSegmentVLC
: EBML_CONTEXT(m_el[mi_level - 1]);
/* Ignore unknown level 0 or 1 elements */
m_el[mi_level] = unlikely(!i_max_read) ? NULL :
m_es->FindNextElement( e_context,
i_ulev, i_max_read,
( mb_dummy | (mi_level > 1) ), 1 );
if( m_el[mi_level] == NULL )
{
if ( i_max_read != UINT64_MAX )
{
msg_Dbg(p_demux, "found nothing, go up");
i_ulev = 1;
}
}
}
if( i_ulev > 0 )
{
if( p_prev )
{
if( !mb_keep )
{
delete p_prev;
p_prev = NULL;
}
mb_keep = false;
}
while( i_ulev > 0 )
{
if( mi_level == 1 )
{
mi_level = 0;
return NULL;
}
delete m_el[mi_level - 1];
m_el[mi_level -1] = m_el[mi_level];
m_el[mi_level] = NULL;
return_previous_parent = m_el[mi_level - 1] != NULL;
mi_level--;
i_ulev--;
}
return NULL;
}
if( m_el[mi_level] == NULL )
{
msg_Dbg( p_demux,"MKV/Ebml Parser: m_el[mi_level] == NULL" );
/* go back to the end of the parent */
if( p_prev && p_prev->IsFiniteSize() )
p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
}
else if( m_el[mi_level]->IsDummy() && !mb_dummy )
{
bool b_bad_position = false;
/* We got a dummy element but don't want those...
* perform a sanity check */
if( !mi_level )
{
msg_Err(p_demux, "Got invalid lvl 0 element... Aborting");
return NULL;
}
if( mi_level > 1 &&
p_prev && p_prev->IsFiniteSize() &&
p_prev->GetEndPosition() != m_el[mi_level]->GetElementPosition() )
{
msg_Err( p_demux, "Dummy Element at unexpected position (%" PRIu64 " instead of %" PRIu64 ")... corrupted file?",
m_el[mi_level]->GetElementPosition(), p_prev->GetEndPosition() );
b_bad_position = true;
}
if( p_prev )
{
if( !mb_keep )
{
delete p_prev;
p_prev = NULL;
}
mb_keep = false;
}
if( n_call < M_EL_MAXSIZE && !b_bad_position && m_el[mi_level]->IsFiniteSize() &&
( !m_el[mi_level-1]->IsFiniteSize() ||
m_el[mi_level]->GetEndPosition() <= m_el[mi_level-1]->GetEndPosition() ) )
{
/* The element fits inside its upper element */
msg_Warn( p_demux, "Dummy element found %" PRIu64 "... skipping it",
m_el[mi_level]->GetElementPosition() );
n_call++;
}
else
{
/* Too large, misplaced or M_EL_MAXSIZE successive dummy elements */
msg_Err( p_demux,
"Dummy element too large or misplaced at %" PRIu64 "... skipping to next upper element",
m_el[mi_level]->GetElementPosition() );
if( m_el[mi_level]->IsFiniteSize() && m_el[mi_level-1]->IsFiniteSize() &&
m_el[mi_level]->GetElementPosition() >= m_el[mi_level-1]->GetEndPosition() )
{
msg_Err(p_demux, "This element is outside its known parent... upping level");
delete m_el[mi_level - 1];
m_el[mi_level -1] = m_el[mi_level];
m_el[mi_level] = NULL;
return_previous_parent = m_el[mi_level - 1] != NULL;
mi_level--;
return NULL;
}
}
goto next;
}
if( p_prev )
{
if( !mb_keep )
{
delete p_prev;
}
mb_keep = false;
}
return m_el[mi_level];
}
bool EbmlParser::IsTopPresent( EbmlElement *el ) const
{
for( size_t i = 0; i < mi_level; i++ )
{
if( m_el[i] && m_el[i] == el )
return true;
}
return false;
}
} // namespace
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland
|
|