]> git.sesse.net Git - vlc/blobdiff - modules/demux/mkv.cpp
mkv.cpp: verify that the matroska version needed for playback matches (support only...
[vlc] / modules / demux / mkv.cpp
index 7a8ec5b9a8a636d30d54b2aa9705cf8a96e4ecf9..0848a8b073df8dedad5ec78ab169a0bd04edcced 100644 (file)
 
 #include "ebml/StdIOCallback.h"
 
+#if LIBMATROSKA_VERSION < 0x000706
+START_LIBMATROSKA_NAMESPACE
+extern const EbmlSemanticContext MATROSKA_DLL_API KaxMatroska_Context;
+END_LIBMATROSKA_NAMESPACE
+#endif
+
 #include "vlc_keys.h"
 
 extern "C" {
@@ -396,6 +402,10 @@ vlc_module_begin();
             N_("Chapter codecs"),
             N_("Use chapter codecs found in the segment."), VLC_TRUE );
 
+    add_bool( "mkv-preload-local-dir", 1, NULL,
+            N_("Preload Directory"),
+            N_("Preload matroska files from the same family in the same directory (not good for broken files)."), VLC_TRUE );
+
     add_bool( "mkv-seek-percent", 0, NULL,
             N_("Seek based on percent not time"),
             N_("Seek based on percent not time."), VLC_TRUE );
@@ -567,6 +577,7 @@ typedef struct
 
     uint64_t     i_default_duration;
     float        f_timecodescale;
+    mtime_t      i_last_dts;
 
     /* video */
     es_format_t fmt;
@@ -1056,6 +1067,7 @@ public:
             free( p_indexes );
 
         delete ep;
+        delete segment;
         delete p_segment_uid;
         delete p_prev_segment_uid;
         delete p_next_segment_uid;
@@ -1072,6 +1084,12 @@ public:
             delete (*indext);
             indext++;
         }
+        std::vector<KaxSegmentFamily*>::iterator indexf = families.begin();
+        while ( indexf != families.end() )
+        {
+            delete (*indexf);
+            indexf++;
+        }
     }
 
     KaxSegment              *segment;
@@ -1119,7 +1137,7 @@ public:
     int                             i_default_edition;
 
     std::vector<chapter_translation_c*> translations;
-    std::vector<KaxSegmentFamily>  families;
+    std::vector<KaxSegmentFamily*>  families;
     
     demux_sys_t                    & sys;
     EbmlParser                     *ep;
@@ -1274,7 +1292,6 @@ public:
     demux_sys_t( demux_t & demux )
         :demuxer(demux)
         ,i_pts(0)
-        ,i_last_dts(0)
         ,i_start_pts(0)
         ,i_chapter_time(0)
         ,meta(NULL)
@@ -1307,7 +1324,6 @@ public:
     demux_t                 & demuxer;
 
     mtime_t                 i_pts;
-    mtime_t                 i_last_dts;
     mtime_t                 i_start_pts;
     mtime_t                 i_chapter_time;
 
@@ -1337,7 +1353,7 @@ public:
     void PreloadFamily( const matroska_segment_c & of_segment );
     void PreloadLinked( matroska_segment_c *p_segment );
     bool PreparePlayback( virtual_segment_c *p_new_segment );
-    matroska_stream_c *AnalyseAllSegmentsFound( EbmlStream *p_estream, bool b_initial = false );
+    matroska_stream_c *AnalyseAllSegmentsFound( demux_t *p_demux, EbmlStream *p_estream, bool b_initial = false );
     void JumpTo( virtual_segment_c & p_segment, chapter_item_c * p_chapter );
 
     void StartUiThread();
@@ -1415,7 +1431,7 @@ static int Open( vlc_object_t * p_this )
         return VLC_EGENERIC;
     }
 
-    p_stream = p_sys->AnalyseAllSegmentsFound( p_io_stream, true );
+    p_stream = p_sys->AnalyseAllSegmentsFound( p_demux, p_io_stream, true );
     if( p_stream == NULL )
     {
         msg_Err( p_demux, "cannot find KaxSegment" );
@@ -1440,81 +1456,86 @@ static int Open( vlc_object_t * p_this )
         p_stream->p_in->setFilePointer( p_segment->cluster->GetElementPosition() );
     }
 
-    /* get the files from the same dir from the same family (based on p_demux->psz_path) */
-    if (p_demux->psz_path[0] != '\0' && !strcmp(p_demux->psz_access, ""))
+    if (config_GetInt( p_demux, "mkv-preload-local-dir" ))
     {
-        // assume it's a regular file
-        // get the directory path
-        s_path = p_demux->psz_path;
-        if (s_path.at(s_path.length() - 1) == DIRECTORY_SEPARATOR)
-        {
-            s_path = s_path.substr(0,s_path.length()-1);
-        }
-        else
+        /* get the files from the same dir from the same family (based on p_demux->psz_path) */
+        if (p_demux->psz_path[0] != '\0' && !strcmp(p_demux->psz_access, ""))
         {
-            if (s_path.find_last_of(DIRECTORY_SEPARATOR) > 0) 
+            // assume it's a regular file
+            // get the directory path
+            s_path = p_demux->psz_path;
+            if (s_path.at(s_path.length() - 1) == DIRECTORY_SEPARATOR)
             {
-                s_path = s_path.substr(0,s_path.find_last_of(DIRECTORY_SEPARATOR));
+                s_path = s_path.substr(0,s_path.length()-1);
+            }
+            else
+            {
+                if (s_path.find_last_of(DIRECTORY_SEPARATOR) > 0) 
+                {
+                    s_path = s_path.substr(0,s_path.find_last_of(DIRECTORY_SEPARATOR));
+                }
             }
-        }
 
-        struct dirent *p_file_item;
-        DIR *p_src_dir = opendir(s_path.c_str());
+            struct dirent *p_file_item;
+            DIR *p_src_dir = opendir(s_path.c_str());
 
-        if (p_src_dir != NULL)
-        {
-            while ((p_file_item = (dirent *) readdir(p_src_dir)))
+            if (p_src_dir != NULL)
             {
-                if (strlen(p_file_item->d_name) > 4)
+                while ((p_file_item = (dirent *) readdir(p_src_dir)))
                 {
-                    s_filename = s_path + DIRECTORY_SEPARATOR + p_file_item->d_name;
+                    if (strlen(p_file_item->d_name) > 4)
+                    {
+                        s_filename = s_path + DIRECTORY_SEPARATOR + p_file_item->d_name;
 
-                    if (!s_filename.compare(p_demux->psz_path))
-                        continue; // don't reuse the original opened file
+                        if (!s_filename.compare(p_demux->psz_path))
+                            continue; // don't reuse the original opened file
 
 #if defined(__GNUC__) && (__GNUC__ < 3)
-                    if (!s_filename.compare("mkv", s_filename.length() - 3, 3) || 
-                        !s_filename.compare("mka", s_filename.length() - 3, 3))
+                        if (!s_filename.compare("mkv", s_filename.length() - 3, 3) || 
+                            !s_filename.compare("mka", s_filename.length() - 3, 3))
 #else
-                    if (!s_filename.compare(s_filename.length() - 3, 3, "mkv") || 
-                        !s_filename.compare(s_filename.length() - 3, 3, "mka"))
+                        if (!s_filename.compare(s_filename.length() - 3, 3, "mkv") || 
+                            !s_filename.compare(s_filename.length() - 3, 3, "mka"))
 #endif
-                    {
-                        // test wether this file belongs to our family
-                        stream_t *p_file_stream = stream_UrlNew( p_demux, s_filename.c_str());
-                        if ( p_file_stream != NULL )
                         {
-                            vlc_stream_io_callback *p_file_io = new vlc_stream_io_callback( p_file_stream, VLC_TRUE );
-                            EbmlStream *p_estream = new EbmlStream(*p_file_io);
+                            // test wether this file belongs to our family
+                            stream_t *p_file_stream = stream_UrlNew( p_demux, s_filename.c_str());
+                            if ( p_file_stream != NULL )
+                            {
+                                vlc_stream_io_callback *p_file_io = new vlc_stream_io_callback( p_file_stream, VLC_TRUE );
+                                EbmlStream *p_estream = new EbmlStream(*p_file_io);
 
-                            p_stream = p_sys->AnalyseAllSegmentsFound( p_estream );
+                                p_stream = p_sys->AnalyseAllSegmentsFound( p_demux, p_estream );
 
-                            if ( p_stream == NULL )
-                            {
-                                msg_Dbg( p_demux, "the file '%s' will not be used", s_filename.c_str() );
-                                delete p_estream;
-                                delete p_file_io;
+                                if ( p_stream == NULL )
+                                {
+                                    msg_Dbg( p_demux, "the file '%s' will not be used", s_filename.c_str() );
+                                    delete p_estream;
+                                    delete p_file_io;
+                                }
+                                else
+                                {
+                                    p_stream->p_in = p_file_io;
+                                    p_stream->p_es = p_estream;
+                                    p_sys->streams.push_back( p_stream );
+                                }
                             }
                             else
                             {
-                                p_stream->p_in = p_file_io;
-                                p_stream->p_es = p_estream;
-                                p_sys->streams.push_back( p_stream );
+                                msg_Dbg( p_demux, "the file '%s' cannot be opened", s_filename.c_str() );
                             }
                         }
-                        else
-                        {
-                            msg_Dbg( p_demux, "the file '%s' cannot be opened", s_filename.c_str() );
-                        }
                     }
                 }
+                closedir( p_src_dir );
             }
-            closedir( p_src_dir );
         }
+
+        p_sys->PreloadFamily( *p_segment );
     }
 
-    p_sys->PreloadFamily( *p_segment );
     p_sys->PreloadLinked( p_segment );
+
     if ( !p_sys->PreparePlayback( NULL ) )
     {
         msg_Err( p_demux, "cannot use the segment" );
@@ -1790,7 +1811,7 @@ static block_t *MemToBlock( demux_t *p_demux, uint8_t *p_mem, int i_mem)
 }
 
 static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
-                         mtime_t i_duration )
+                         mtime_t i_duration, bool f_unreferenced )
 {
     demux_sys_t        *p_sys = p_demux->p_sys;
     matroska_segment_c *p_segment = p_sys->p_current_segment->Segment();
@@ -1889,16 +1910,23 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
         {
             if( !strcmp( tk->psz_codec, "V_MS/VFW/FOURCC" ) )
             {
+                // in VFW we have no idea about B frames
                 p_block->i_pts = 0;
+                p_block->i_dts = i_pts;
             }
             else
             {
                 p_block->i_pts = i_pts;
+                if ( f_unreferenced )
+                    p_block->i_dts = p_block->i_pts;
+                else
+                    p_block->i_dts = min( i_pts, tk->i_last_dts + (tk->i_default_duration >> 10));
+                p_sys->i_pts = p_block->i_dts;
             }
-            p_block->i_dts = p_sys->i_pts;
         }
+        tk->i_last_dts = p_block->i_dts;
 
-#if 0
+#if 1
 msg_Dbg( p_demux, "block i_dts: "I64Fd" / i_pts: "I64Fd, p_block->i_dts, p_block->i_pts);
 #endif
         if( strcmp( tk->psz_codec, "S_VOBSUB" ) )
@@ -1915,7 +1943,7 @@ msg_Dbg( p_demux, "block i_dts: "I64Fd" / i_pts: "I64Fd, p_block->i_dts, p_block
 #undef tk
 }
 
-matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( EbmlStream *p_estream, bool b_initial )
+matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlStream *p_estream, bool b_initial )
 {
     int i_upper_lvl = 0;
     size_t i;
@@ -1928,9 +1956,27 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( EbmlStream *p_estream,
     {
         return NULL;
     }
-    p_l0->SkipData(*p_estream, KaxMatroska_Context);
+
+    // verify we can read this Segment, we only support Matroska version 1 for now
+    p_l0->Read(*p_estream, EbmlHead::ClassInfos.Context, i_upper_lvl, p_l0, true);
+
+    EDocType doc_type = GetChild<EDocType>(*static_cast<EbmlHead*>(p_l0));
+    if (std::string(doc_type) != "matroska")
+    {
+        msg_Dbg( p_demux, "Not a Matroska file : %s ", std::string(doc_type).c_str());
+        return NULL;
+    }
+
+    EDocTypeReadVersion doc_read_version = GetChild<EDocTypeReadVersion>(*static_cast<EbmlHead*>(p_l0));
+    if (uint64(doc_read_version) != 1)
+    {
+        msg_Dbg( p_demux, "Matroska Read version not matching : "I64Fd, uint64(doc_read_version));
+        return NULL;
+    }
+
     delete p_l0;
 
+
     // find all segments in this file
     p_l0 = p_estream->FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFFLL);
     if (p_l0 == NULL)
@@ -1985,7 +2031,7 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( EbmlStream *p_estream,
                         else if( MKV_IS_ID( l, KaxSegmentFamily ) )
                         {
                             KaxSegmentFamily *p_fam = new KaxSegmentFamily( *static_cast<KaxSegmentFamily*>(l) );
-                            p_segment1->families.push_back( *p_fam );
+                            p_segment1->families.push_back( p_fam );
                         }
                     }
                     break;
@@ -2381,7 +2427,10 @@ int demux_sys_t::EventMouse( vlc_object_t *p_this, char const *psz_var,
     event_thread_t *p_ev = (event_thread_t *) p_data;
     vlc_mutex_lock( &p_ev->lock );
     if( psz_var[6] == 'c' )
+    {
         p_ev->b_clicked = VLC_TRUE;
+        msg_Dbg( p_this, "Event Mouse: clicked");
+    }
     else if( psz_var[6] == 'm' )
         p_ev->b_moved = VLC_TRUE;
     vlc_mutex_unlock( &p_ev->lock );
@@ -2396,6 +2445,7 @@ int demux_sys_t::EventKey( vlc_object_t *p_this, char const *psz_var,
     vlc_mutex_lock( &p_ev->lock );
     p_ev->b_key = VLC_TRUE;
     vlc_mutex_unlock( &p_ev->lock );
+    msg_Dbg( p_this, "Event Key");
 
     return VLC_SUCCESS;
 }
@@ -2432,6 +2482,8 @@ int demux_sys_t::EventThread( vlc_object_t *p_this )
             struct vlc_t::hotkey *p_hotkeys = p_ev->p_vlc->p_hotkeys;
             int i, i_action = -1;
 
+            msg_Dbg( p_ev->p_demux, "Handle Key Event");
+
             vlc_mutex_lock( &p_ev->lock );
 
             pci_t *pci = (pci_t *) &p_sys->pci_packet;
@@ -2581,6 +2633,8 @@ int demux_sys_t::EventThread( vlc_object_t *p_this )
                 int32_t best,dist,d;
                 int32_t mx,my,dx,dy;
 
+                msg_Dbg( p_ev->p_demux, "Handle Mouse Event: Mouse clicked x(%d)*y(%d)", (unsigned)valx.i_int, (unsigned)valy.i_int);
+
                 b_activated = VLC_TRUE;
                 // get current button
                 best = 0;
@@ -3184,7 +3238,7 @@ static int Demux( demux_t *p_demux)
                     if ( p_chap->i_user_start_time == p_chap->i_user_start_time )
                         p_vsegment->SelectNext();
                     */
-                    p_sys->i_last_dts = p_sys->i_pts = p_chap->i_user_end_time;
+                    p_sys->i_pts = p_chap->i_user_end_time;
                     p_sys->i_pts++; // trick to avoid staying on segments with no duration and no content
 
                     i_return = 1;
@@ -3214,41 +3268,28 @@ static int Demux( demux_t *p_demux)
             }
         }
 
-        mtime_t i_pts, i_dts;
-
-        mtime_t i_time = block->GlobalTimecode();
-        if ( i_block_ref1 != 0 )
-            i_time += min( 0, i_block_ref1 );
-        if ( i_block_ref2 != 0 )
-            i_time += min( 0, i_block_ref2 );
-
-        i_pts = (p_sys->i_chapter_time + block->GlobalTimecode()) / (mtime_t) 1000;
-        i_dts = (p_sys->i_chapter_time + i_time) / (mtime_t) 1000;
-
-        if ( i_dts != i_pts && p_sys->i_last_dts >= i_dts )
-            p_sys->i_last_dts += 200;
-        else
-            p_sys->i_last_dts = i_dts;
-
-        p_sys->i_pts = i_pts;
+        p_sys->i_pts = (p_sys->i_chapter_time + block->GlobalTimecode()) / (mtime_t) 1000;
 
         if( p_sys->i_pts >= p_sys->i_start_pts  )
         {
             es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pts );
-        }
 
-        if( i_pts >= p_sys->i_start_pts  )
             if ( p_vsegment->UpdateCurrentToChapter( *p_demux ) )
             {
                 i_return = 1;
+                delete block;
                 break;
             }
+        }
         
         if ( p_vsegment->Edition() && p_vsegment->Edition()->b_ordered && p_vsegment->CurrentChapter() == NULL )
         {
             /* nothing left to read in this ordered edition */
             if ( !p_vsegment->SelectNext() )
+            {
+                delete block;
                 break;
+            }
             p_segment->UnSelect( );
             
             es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
@@ -3258,12 +3299,14 @@ static int Demux( demux_t *p_demux)
             if ( !p_segment->Select( 0 ) )
             {
                 msg_Err( p_demux, "Failed to select new segment" );
+                delete block;
                 break;
             }
+            delete block;
             continue;
         }
 
-        BlockDecode( p_demux, block, i_pts, i_block_duration );
+        BlockDecode( p_demux, block, p_sys->i_pts, i_block_duration, i_block_ref1 >= 0 || i_block_ref2 > 0 );
 
         delete block;
         i_block_count++;
@@ -3988,6 +4031,8 @@ void matroska_segment_c::ParseTrackEntry( KaxTrackEntry *m )
         {
             KaxTrackLanguage &lang = *(KaxTrackLanguage*)l;
 
+            if ( tk->fmt.psz_language != NULL )
+                free( tk->fmt.psz_language );
             tk->fmt.psz_language = strdup( string( lang ).c_str() );
             msg_Dbg( &sys.demuxer,
                      "|   |   |   + Track Language=`%s'", tk->fmt.psz_language );
@@ -4303,19 +4348,22 @@ void matroska_segment_c::ParseInfo( KaxInfo *info )
 
         if( MKV_IS_ID( l, KaxSegmentUID ) )
         {
-            p_segment_uid = new KaxSegmentUID(*static_cast<KaxSegmentUID*>(l));
+            if ( p_segment_uid == NULL )
+                p_segment_uid = new KaxSegmentUID(*static_cast<KaxSegmentUID*>(l));
 
             msg_Dbg( &sys.demuxer, "|   |   + UID=%d", *(uint32*)p_segment_uid->GetBuffer() );
         }
         else if( MKV_IS_ID( l, KaxPrevUID ) )
         {
-            p_prev_segment_uid = new KaxPrevUID(*static_cast<KaxPrevUID*>(l));
+            if ( p_prev_segment_uid == NULL )
+                p_prev_segment_uid = new KaxPrevUID(*static_cast<KaxPrevUID*>(l));
 
             msg_Dbg( &sys.demuxer, "|   |   + PrevUID=%d", *(uint32*)p_prev_segment_uid->GetBuffer() );
         }
         else if( MKV_IS_ID( l, KaxNextUID ) )
         {
-            p_next_segment_uid = new KaxNextUID(*static_cast<KaxNextUID*>(l));
+            if ( p_next_segment_uid == NULL )
+                p_next_segment_uid = new KaxNextUID(*static_cast<KaxNextUID*>(l));
 
             msg_Dbg( &sys.demuxer, "|   |   + NextUID=%d", *(uint32*)p_next_segment_uid->GetBuffer() );
         }
@@ -4376,7 +4424,7 @@ void matroska_segment_c::ParseInfo( KaxInfo *info )
         {
             KaxSegmentFamily *uid = static_cast<KaxSegmentFamily*>(l);
 
-            families.push_back(*uid);
+            families.push_back( new KaxSegmentFamily(*uid) );
 
             msg_Dbg( &sys.demuxer, "|   |   + family=%d", *(uint32*)uid->GetBuffer() );
         }
@@ -4493,10 +4541,12 @@ void matroska_segment_c::ParseChapterAtom( int i_level, KaxChapterAtom *ca, chap
                     for (k = 0; k < i_level; k++)
                         chapters.psz_name += '+';
                     chapters.psz_name += ' ';
-                    chapters.psz_name += ToUTF8( UTFstring( name ) );
+                    char *psz_tmp_utf8 = ToUTF8( UTFstring( name ) );
+                    chapters.psz_name += psz_tmp_utf8;
                     chapters.b_user_display = true;
 
-                    msg_Dbg( &sys.demuxer, "|   |   |   |   |    + ChapterString '%s'", ToUTF8(UTFstring(name)) );
+                    msg_Dbg( &sys.demuxer, "|   |   |   |   |    + ChapterString '%s'", psz_tmp_utf8 );
+                    free( psz_tmp_utf8 );
                 }
                 else if( MKV_IS_ID( l, KaxChapterLanguage ) )
                 {
@@ -4895,7 +4945,7 @@ bool matroska_segment_c::PreloadFamily( const matroska_segment_c & of_segment )
     {
         for (size_t j=0; j<of_segment.families.size(); j++)
         {
-            if ( families[i] == of_segment.families[j] )
+            if ( *(families[i]) == *(of_segment.families[j]) )
                 return Preload( );
         }
     }
@@ -5191,7 +5241,9 @@ size_t virtual_segment_c::AddSegment( matroska_segment_c *p_segment )
     // check if it's not already in here
     for ( i=0; i<linked_segments.size(); i++ )
     {
-        if ( *p_segment->p_segment_uid == *linked_segments[i]->p_segment_uid )
+        if ( linked_segments[i]->p_segment_uid != NULL 
+            && p_segment->p_segment_uid != NULL
+            && *p_segment->p_segment_uid == *linked_segments[i]->p_segment_uid )
             return 0;
     }
 
@@ -5335,7 +5387,7 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset )
             }
         }
 
-        sys.i_last_dts = sys.i_pts = (sys.i_chapter_time + block->GlobalTimecode()) / (mtime_t) 1000;
+        sys.i_pts = (sys.i_chapter_time + block->GlobalTimecode()) / (mtime_t) 1000;
 
         if( i_track < tracks.size() )
         {
@@ -5353,7 +5405,7 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset )
                 }
                 if( !tracks[i_track]->b_search_keyframe )
                 {
-                    BlockDecode( &sys.demuxer, block, sys.i_pts, 0 );
+                    BlockDecode( &sys.demuxer, block, sys.i_pts, 0, i_block_ref1 >= 0 || i_block_ref2 > 0 );
                 }
             } 
         }