]> git.sesse.net Git - vlc/blobdiff - modules/demux/mkv/Ebml_parser.cpp
Regression fix for non-SPU tracks in MKV containers.
[vlc] / modules / demux / mkv / Ebml_parser.cpp
index 8abe76c138cbcb9b0cc664b5d6ed7aae2cf2090c..6890f418d7cad14aa151e393a2b8a33d4b9b06f2 100644 (file)
@@ -2,46 +2,44 @@
 /*****************************************************************************
  * EbmlParser for the matroska demuxer
  *****************************************************************************
- * Copyright (C) 2003-2004 the VideoLAN team
+ * Copyright (C) 2003-2004 VLC authors and VideoLAN
  * $Id$
  *
  * 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 General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * 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 General Public License for more details.
+ * 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 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.
+ * 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"
 
 /*****************************************************************************
  * Ebml Stream parser
  *****************************************************************************/
-EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start, demux_t *p_demux )
+EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start, demux_t *p_demux ) :
+    p_demux( p_demux ),
+    m_es( es ),
+    mi_level( 1 ),
+    m_got( NULL ),
+    mi_user_level( 1 ),
+    mb_keep( false )
 {
-    m_es = es;
-    m_got = NULL;
-    m_el[0] = el_start;
     mi_remain_size[0] = el_start->GetSize();
-
-    for( int i = 1; i < 6; i++ )
-    {
-        m_el[i] = NULL;
-    }
-    mi_level = 1;
-    mi_user_level = 1;
-    mb_keep = false;
+    memset( m_el, 0, 6 * sizeof( *m_el ) );
+    m_el[0] = el_start;
     mb_dummy = var_InheritBool( p_demux, "mkv-use-dummy" );
 }
 
@@ -75,21 +73,29 @@ EbmlElement* EbmlParser::UnGet( uint64 i_block_pos, uint64 i_cluster_pos )
             mi_user_level--;
         }
     }
+
+    /* Avoid data skip in BlockGet */
+    delete m_el[mi_level];
+    m_el[mi_level] = NULL;
+
     m_got = NULL;
     mb_keep = false;
-    if ( m_el[1]->GetElementPosition() == i_cluster_pos )
+    if ( m_el[1] && m_el[1]->GetElementPosition() == i_cluster_pos )
     {
         m_es->I_O().setFilePointer( i_block_pos, seek_beginning );
-        return (EbmlMaster*) m_el[1];
+        return m_el[1];
     }
     else
     {
         // seek to the previous Cluster
         m_es->I_O().setFilePointer( i_cluster_pos, seek_beginning );
-        mi_level--;
-        mi_user_level--;
-        delete m_el[mi_level];
-        m_el[mi_level] = NULL;
+        while(mi_level > 1)
+        {
+            mi_level--;
+            mi_user_level--;
+            delete m_el[mi_level];
+            m_el[mi_level] = NULL;
+        }
         return NULL;
     }
 }
@@ -98,7 +104,7 @@ void EbmlParser::Up( void )
 {
     if( mi_user_level == mi_level )
     {
-        fprintf( stderr,"MKV/Ebml Parser: Up cannot escape itself\n" );
+        msg_Warn( p_demux, "MKV/Ebml Parser: Up cannot escape itself" );
     }
 
     mi_user_level--;
@@ -115,7 +121,12 @@ void EbmlParser::Keep( void )
     mb_keep = true;
 }
 
-int EbmlParser::GetLevel( void )
+void EbmlParser::Unkeep()
+{
+    mb_keep = false;
+}
+
+int EbmlParser::GetLevel( void ) const
 {
     return mi_user_level;
 }
@@ -128,15 +139,17 @@ void EbmlParser::Reset( demux_t *p_demux )
         m_el[mi_level] = NULL;
         mi_level--;
     }
+    this->p_demux = p_demux;
     mi_user_level = mi_level = 1;
     // a little faster and cleaner
     m_es->I_O().setFilePointer( static_cast<KaxSegment*>(m_el[0])->GetGlobalPosition(0) );
     mb_dummy = var_InheritBool( p_demux, "mkv-use-dummy" );
 }
 
-EbmlElement *EbmlParser::Get( void )
+EbmlElement *EbmlParser::Get( int n_call )
 {
     int i_ulev = 0;
+    EbmlElement *p_prev = NULL;
 
     if( mi_user_level != mi_level )
     {
@@ -150,22 +163,29 @@ EbmlElement *EbmlParser::Get( void )
         return ret;
     }
 
+    p_prev = m_el[mi_level];
     if( m_el[mi_level] )
     {
         m_el[mi_level]->SkipData( *m_es, EBML_CONTEXT(m_el[mi_level]) );
-        if( !mb_keep )
-        {
-            if( MKV_IS_ID( m_el[mi_level], KaxBlockVirtual ) )
-                static_cast<KaxBlockVirtualWorkaround*>(m_el[mi_level])->Fix();
-            delete m_el[mi_level];
-        }
-        mb_keep = false;
+
     }
 
-    m_el[mi_level] = m_es->FindNextElement( EBML_CONTEXT(m_el[mi_level - 1]), i_ulev, 0xFFFFFFFFL, mb_dummy != 0, 1 );
-//    mi_remain_size[mi_level] = m_el[mi_level]->GetSize();
+    /* Ignore unknown level 0 or 1 elements */
+    m_el[mi_level] = m_es->FindNextElement( EBML_CONTEXT(m_el[mi_level - 1]),
+                                            i_ulev, UINT64_MAX,
+                                            (  mb_dummy | (mi_level > 1) ), 1 );
     if( i_ulev > 0 )
     {
+        if( p_prev )
+        {
+            if( !mb_keep )
+            {
+                if( MKV_IS_ID( p_prev, KaxBlockVirtual ) )
+                    static_cast<KaxBlockVirtualWorkaround*>(p_prev)->Fix();
+                delete p_prev;
+            }
+            mb_keep = false;
+        }
         while( i_ulev > 0 )
         {
             if( mi_level == 1 )
@@ -185,13 +205,76 @@ EbmlElement *EbmlParser::Get( void )
     }
     else if( m_el[mi_level] == NULL )
     {
-        fprintf( stderr,"MKV/Ebml Parser: m_el[mi_level] == NULL\n" );
+        msg_Warn( p_demux,"MKV/Ebml Parser: m_el[mi_level] == NULL\n" );
     }
+    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( p_prev && p_prev->IsFiniteSize() &&
+            p_prev->GetEndPosition() != m_el[mi_level]->GetElementPosition() &&
+            mi_level > 1 )
+        {
+            msg_Err( p_demux, "Dummy Element at unexpected position... corrupted file?" );
+            b_bad_position = true;
+        }
+
+        if( n_call < 10 && !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() );
+            return Get( ++n_call );
+        }
+        else
+        {
+            /* Too large, misplaced or 10 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( mi_level >= 1 &&
+                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_got = m_el[mi_level -1] = m_el[mi_level];
+                m_el[mi_level] = NULL;
 
+                mi_level--;
+                return NULL;
+            }
+
+            delete m_el[mi_level];
+            m_el[mi_level] = NULL;
+            m_el[mi_level - 1]->SkipData( *m_es, EBML_CONTEXT(m_el[mi_level - 1]) );
+            return Get();
+        }
+    }
+
+    if( p_prev )
+    {
+        if( !mb_keep )
+        {
+            if( MKV_IS_ID( p_prev, KaxBlockVirtual ) )
+                static_cast<KaxBlockVirtualWorkaround*>(p_prev)->Fix();
+            delete p_prev;
+        }
+        mb_keep = false;
+    }
     return m_el[mi_level];
 }
 
-bool EbmlParser::IsTopPresent( EbmlElement *el )
+bool EbmlParser::IsTopPresent( EbmlElement *el ) const
 {
     for( int i = 0; i < mi_level; i++ )
     {