demux:mkv: put the lighter tests first
[vlc.git] / modules / demux / mkv / Ebml_parser.cpp
blob1e3099cfac752c7f3fc1ded85fe30393c91512f2
2 /*****************************************************************************
3 * EbmlParser for the matroska demuxer
4 *****************************************************************************
5 * Copyright (C) 2003-2004 VLC authors and VideoLAN
6 * $Id$
8 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * Steve Lhomme <steve.lhomme@free.fr>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 #include "Ebml_parser.hpp"
27 #include "stream_io_callback.hpp"
29 /*****************************************************************************
30 * Ebml Stream parser
31 *****************************************************************************/
32 EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start, demux_t *p_demux,
33 bool b_with_dummy ) :
34 p_demux( p_demux ),
35 m_es( es ),
36 mi_level( 1 ),
37 m_got( NULL ),
38 mi_user_level( 1 ),
39 mb_keep( false ),
40 mb_dummy( b_with_dummy )
42 memset( m_el, 0, sizeof( *m_el ) * M_EL_MAXSIZE);
43 m_el[0] = el_start;
46 EbmlParser::~EbmlParser( void )
48 if( !mi_level )
50 assert( !mb_keep );
51 delete m_el[1];
52 return;
55 for( int i = 1; i <= mi_level; i++ )
57 if( !mb_keep )
59 delete m_el[i];
61 mb_keep = false;
65 void EbmlParser::reconstruct( EbmlStream* es, EbmlElement* el_start, demux_t* p_demux )
67 this->reconstruct( es, el_start, p_demux, var_InheritBool( p_demux, "mkv-use-dummy" ) );
70 void EbmlParser::reconstruct( EbmlStream* es, EbmlElement* el_start, demux_t* p_demux,
71 bool b_with_dummy)
73 this->~EbmlParser();
75 new( static_cast<void*>( this ) ) EbmlParser(
76 es, el_start, p_demux, b_with_dummy
80 void EbmlParser::Up( void )
82 if( mi_user_level == mi_level && m_el[mi_level] )
84 msg_Warn( p_demux, "MKV/Ebml Parser: Up cannot escape itself" );
87 mi_user_level--;
90 void EbmlParser::Down( void )
92 mi_user_level++;
93 mi_level++;
96 void EbmlParser::Keep( void )
98 mb_keep = true;
101 void EbmlParser::Unkeep()
103 mb_keep = false;
106 int EbmlParser::GetLevel( void ) const
108 return mi_user_level;
111 void EbmlParser::Reset( demux_t *p_demux )
113 while ( mi_level > 0)
115 delete m_el[mi_level];
116 m_el[mi_level] = NULL;
117 mi_level--;
119 this->p_demux = p_demux;
120 mi_user_level = mi_level = 1;
121 // a little faster and cleaner
122 m_es->I_O().setFilePointer( static_cast<KaxSegment*>(m_el[0])->GetGlobalPosition(0) );
126 static const EbmlSemanticContext & GetEbmlNoGlobal_Context();
127 static const EbmlSemanticContext EbmlNoGlobal_Context = EbmlSemanticContext(0, NULL, NULL, *GetEbmlNoGlobal_Context, NULL);
128 static const EbmlSemanticContext & GetEbmlNoGlobal_Context()
130 return EbmlNoGlobal_Context;
133 // the Segment Context should not allow Void or CRC32 elements to avoid lookup false alarm
134 const EbmlSemanticContext Context_KaxSegmentVLC = EbmlSemanticContext(KaxSegment_Context.GetSize(),
135 KaxSegment_Context.MyTable,
136 KaxSegment_Context.Parent(),
137 GetEbmlNoGlobal_Context,
138 KaxSegment_Context.GetMaster());
140 EbmlElement *EbmlParser::Get( int n_call )
142 int i_ulev = 0;
143 EbmlElement *p_prev = NULL;
145 if( mi_user_level != mi_level )
147 return NULL;
149 if( m_got )
151 EbmlElement *ret = m_got;
152 m_got = NULL;
154 return ret;
157 p_prev = m_el[mi_level];
158 if( p_prev )
159 p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
161 // If the parent is a segment, use the segment context when creating children
162 // (to prolong their lifetime), otherwise just continue as normal
163 EbmlSemanticContext e_context =
164 EBML_CTX_MASTER( EBML_CONTEXT(m_el[mi_level - 1]) ) == EBML_CTX_MASTER( Context_KaxSegmentVLC )
165 ? Context_KaxSegmentVLC
166 : EBML_CONTEXT(m_el[mi_level - 1]);
168 /* Ignore unknown level 0 or 1 elements */
169 m_el[mi_level] = m_es->FindNextElement( e_context,
170 i_ulev, UINT64_MAX,
171 ( mb_dummy | (mi_level > 1) ), 1 );
172 if( i_ulev > 0 )
174 if( p_prev )
176 if( !mb_keep )
178 if( MKV_IS_ID( p_prev, KaxBlockVirtual ) )
179 static_cast<KaxBlockVirtualWorkaround*>(p_prev)->Fix(); // !! WARNING : TODO !! this is undefined-behavior
180 delete p_prev;
182 mb_keep = false;
184 while( i_ulev > 0 )
186 if( mi_level == 1 )
188 mi_level = 0;
189 return NULL;
192 delete m_el[mi_level - 1];
193 m_got = m_el[mi_level -1] = m_el[mi_level];
194 m_el[mi_level] = NULL;
196 mi_level--;
197 i_ulev--;
199 return NULL;
201 else if( m_el[mi_level] == NULL )
203 msg_Dbg( p_demux,"MKV/Ebml Parser: m_el[mi_level] == NULL" );
205 else if( m_el[mi_level]->IsDummy() && !mb_dummy )
207 bool b_bad_position = false;
208 /* We got a dummy element but don't want those...
209 * perform a sanity check */
210 if( !mi_level )
212 msg_Err(p_demux, "Got invalid lvl 0 element... Aborting");
213 return NULL;
216 if( mi_level > 1 &&
217 p_prev && p_prev->IsFiniteSize() &&
218 p_prev->GetEndPosition() != m_el[mi_level]->GetElementPosition() )
220 msg_Err( p_demux, "Dummy Element at unexpected position... corrupted file?" );
221 b_bad_position = true;
224 if( n_call < M_EL_MAXSIZE && !b_bad_position && m_el[mi_level]->IsFiniteSize() &&
225 ( !m_el[mi_level-1]->IsFiniteSize() ||
226 m_el[mi_level]->GetEndPosition() <= m_el[mi_level-1]->GetEndPosition() ) )
228 /* The element fits inside its upper element */
229 msg_Warn( p_demux, "Dummy element found %" PRIu64 "... skipping it",
230 m_el[mi_level]->GetElementPosition() );
231 return Get( ++n_call );
233 else
235 /* Too large, misplaced or M_EL_MAXSIZE successive dummy elements */
236 msg_Err( p_demux,
237 "Dummy element too large or misplaced at %" PRIu64 "... skipping to next upper element",
238 m_el[mi_level]->GetElementPosition() );
240 if( mi_level >= 1 &&
241 m_el[mi_level]->GetElementPosition() >= m_el[mi_level-1]->GetEndPosition() )
243 msg_Err(p_demux, "This element is outside its known parent... upping level");
244 delete m_el[mi_level - 1];
245 m_got = m_el[mi_level -1] = m_el[mi_level];
246 m_el[mi_level] = NULL;
248 mi_level--;
249 return NULL;
252 delete m_el[mi_level];
253 delete p_prev;
255 m_el[mi_level] = NULL;
256 m_el[mi_level - 1]->SkipData( *m_es, EBML_CONTEXT(m_el[mi_level - 1]) );
257 return Get();
261 if( p_prev )
263 if( !mb_keep )
265 if( MKV_IS_ID( p_prev, KaxBlockVirtual ) )
266 static_cast<KaxBlockVirtualWorkaround*>(p_prev)->Fix();
267 delete p_prev;
269 mb_keep = false;
271 return m_el[mi_level];
274 bool EbmlParser::IsTopPresent( EbmlElement *el ) const
276 for( int i = 0; i < mi_level; i++ )
278 if( m_el[i] && m_el[i] == el )
279 return true;
281 return false;