]> git.sesse.net Git - vlc/blobdiff - modules/demux/avi/avi.c
Fix the subtitles loss at MKV segment changes
[vlc] / modules / demux / avi / avi.c
index 66b8428b6fe33f7f41f1964981da39ac27f88ffe..5e86df89f44ed13efd384f17fe4c097cb87e710d 100644 (file)
@@ -28,6 +28,7 @@
 # include "config.h"
 #endif
 #include <assert.h>
+#include <ctype.h>
 
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 static int  Open ( vlc_object_t * );
 static void Close( vlc_object_t * );
 
-static const int pi_index[] = {0,1,2};
+static const int pi_index[] = {0,1,2,3};
 
 static const char *const ppsz_indexes[] = { N_("Ask for action"),
                                             N_("Always fix"),
-                                            N_("Never fix") };
+                                            N_("Never fix"),
+                                            N_("Fix when necessary")};
 
 vlc_module_begin ()
     set_shortname( "AVI" )
@@ -95,6 +97,8 @@ static char *FromACP( const char *str )
     return FromCharset(vlc_pgettext("GetACP", "CP1252"), str, strlen(str));
 }
 
+#define IGNORE_ES NAV_ES
+
 typedef struct
 {
     vlc_fourcc_t i_fourcc;
@@ -663,29 +667,38 @@ aviindex:
 
         msg_Warn( p_demux, "broken or missing index, 'seek' will be "
                            "approximative or will exhibit strange behavior" );
-        if( i_do_index == 0 && !b_index )
+        if( (i_do_index == 0 || i_do_index == 3) && !b_index )
         {
             if( !p_sys->b_seekable ) {
                 b_index = true;
                 goto aviindex;
             }
-            switch( dialog_Question( p_demux, _("Broken or missing AVI Index") ,
-               _( "Because this AVI file index is broken or missing, "
-                  "seeking will not work correctly.\n"
-                  "VLC won't repair your file but can temporary fix this "
-                  "problem by building an index in memory.\n"
-                  "This step might take a long time on a large file.\n"
-                  "What do you want to do ?" ),
-                  _( "Build index then play" ), _( "Play as is" ), _( "Do not play") ) )
+            if( i_do_index == 0 )
+            {
+                switch( dialog_Question( p_demux, _("Broken or missing AVI Index") ,
+                   _( "Because this AVI file index is broken or missing, "
+                      "seeking will not work correctly.\n"
+                      "VLC won't repair your file but can temporary fix this "
+                      "problem by building an index in memory.\n"
+                      "This step might take a long time on a large file.\n"
+                      "What do you want to do?" ),
+                      _( "Build index then play" ), _( "Play as is" ), _( "Do not play") ) )
+                {
+                    case 1:
+                        b_index = true;
+                        msg_Dbg( p_demux, "Fixing AVI index" );
+                        goto aviindex;
+                    case 3:
+                        /* Kill input */
+                        vlc_object_kill( p_demux->p_parent );
+                        goto error;
+                }
+            }
+            else
             {
-                case 1:
-                    b_index = true;
-                    msg_Dbg( p_demux, "Fixing AVI index" );
-                    goto aviindex;
-                case 3:
-                    /* Kill input */
-                    vlc_object_kill( p_demux->p_parent );
-                    goto error;
+                b_index = true;
+                msg_Dbg( p_demux, "Fixing AVI index" );
+                goto aviindex;
             }
         }
     }
@@ -1303,15 +1316,16 @@ static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent )
 {
 
     demux_sys_t *p_sys = p_demux->p_sys;
-    unsigned int i_stream;
     msg_Dbg( p_demux, "seek requested: %"PRId64" seconds %d%%",
              i_date / 1000000, i_percent );
 
     if( p_sys->b_seekable )
     {
+        unsigned i_stream;
+
         if( !p_sys->i_length )
         {
-            avi_track_t *p_stream;
+            avi_track_t *p_stream = NULL;
             int64_t i_pos;
 
             /* use i_percent to create a true i_date */
@@ -1327,17 +1341,19 @@ static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent )
             /* try to find chunk that is at i_percent or the file */
             i_pos = __MAX( i_percent * stream_Size( p_demux->s ) / 100,
                            p_sys->i_movi_begin );
-            /* search first selected stream (and prefer non eof ones) */
-            for( i_stream = 0, p_stream = NULL;
-                        i_stream < p_sys->i_track; i_stream++ )
+            /* search first selected stream (and prefer non-EOF ones) */
+            for( unsigned i = 0; i < p_sys->i_track; i++ )
             {
-                if( !p_stream || p_stream->b_eof )
-                    p_stream = p_sys->track[i_stream];
+                avi_track_t *p_track = p_sys->track[i];
+                if( !p_track->b_activated )
+                    continue;
 
-                if( p_stream->b_activated && !p_stream->b_eof )
+                p_stream = p_track;
+                i_stream = i;
+                if( !p_track->b_eof )
                     break;
             }
-            if( !p_stream || !p_stream->b_activated )
+            if( p_stream == NULL )
             {
                 msg_Warn( p_demux, "cannot find any selected stream" );
                 return VLC_EGENERIC;
@@ -1948,6 +1964,9 @@ static void AVI_ParseStreamHeader( vlc_fourcc_t i_id,
             case AVITWOCC_sb:
                 SET_PTR( pi_type, SPU_ES );
                 break;
+            case AVITWOCC_pc:
+                SET_PTR( pi_type, IGNORE_ES );
+                break;
             default:
                 SET_PTR( pi_type, UNKNOWN_ES );
                 break;
@@ -2143,18 +2162,35 @@ static int AVI_IndexFind_idx1( demux_t *p_demux,
     }
     *pp_idx1 = p_idx1;
 
-    /* *** calculate offset *** */
-    /* Well, avi is __SHIT__ so test more than one entry
-     * (needed for some avi files) */
+    /* The offset in the index should be from the start of the movi content,
+     * but some broken files use offset from the start of the file. Just
+     * checking the offset of the first packet is not enough as some files
+     * has unused chunk at the beginning of the movi content.
+     */
     avi_chunk_list_t *p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0);
-    *pi_offset = 0;
-    for( unsigned i = 0; i < __MIN( p_idx1->i_entry_count, 10 ); i++ )
+    uint64_t i_first_pos = UINT64_MAX;
+    for( unsigned i = 0; i < __MIN( p_idx1->i_entry_count, 100 ); i++ )
+        i_first_pos = __MIN( i_first_pos, p_idx1->entry[i].i_pos );
+
+    const uint64_t i_movi_content = p_movi->i_chunk_pos + 8;
+    if( i_first_pos < i_movi_content )
     {
-        if( p_idx1->entry[i].i_pos < p_movi->i_chunk_pos )
-        {
-            *pi_offset = p_movi->i_chunk_pos + 8;
-            break;
-        }
+        *pi_offset = i_movi_content;
+    }
+    else if( p_sys->b_seekable && i_first_pos < UINT64_MAX )
+    {
+        const uint8_t *p_peek;
+        if( !stream_Seek( p_demux->s, i_movi_content + i_first_pos ) &&
+            stream_Peek( p_demux->s, &p_peek, 4 ) >= 4 &&
+            ( !isdigit( p_peek[0] ) || !isdigit( p_peek[1] ) ||
+              !isalpha( p_peek[2] ) || !isalpha( p_peek[3] ) ) )
+            *pi_offset = 0;
+        else
+            *pi_offset = i_movi_content;
+    }
+    else
+    {
+        *pi_offset = 0;
     }
     return VLC_SUCCESS;
 }
@@ -2178,7 +2214,7 @@ static int AVI_IndexLoad_idx1( demux_t *p_demux,
                                &i_stream,
                                &i_cat );
         if( i_stream < p_sys->i_track &&
-            i_cat == p_sys->track[i_stream]->i_cat )
+            (i_cat == p_sys->track[i_stream]->i_cat || i_cat == UNKNOWN_ES ) )
         {
             avi_entry_t index;
             index.i_id     = p_idx1->entry[i_index].i_fourcc;