demux: avi: lazy load indexes if not fastseekable
authorFrancois Cartegnie <fcvlcdev@free.fr>
Thu, 24 Apr 2014 17:21:50 +0000 (19:21 +0200)
committerFrancois Cartegnie <fcvlcdev@free.fr>
Thu, 24 Apr 2014 17:24:38 +0000 (19:24 +0200)
refs #1532

modules/demux/avi/avi.c
modules/demux/avi/libavi.c
modules/demux/avi/libavi.h

index 8c23d1e..b468bb1 100644 (file)
@@ -175,6 +175,8 @@ struct demux_sys_t
 
     bool  b_seekable;
     bool  b_fastseekable;
+    bool  b_indexloaded; /* if we read indexes from end of file before starting */
+    uint32_t i_avih_flags;
     avi_chunk_t ck_root;
 
     bool  b_odml;
@@ -374,6 +376,7 @@ static int Open( vlc_object_t * p_this )
              p_avih->i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"" );
 
     AVI_MetaLoad( p_demux, p_riff, p_avih );
+    p_sys->i_avih_flags = p_avih->i_flags;
 
     /* now read info on each stream and create ES */
     for( i = 0 ; i < i_track; i++ )
@@ -1426,19 +1429,48 @@ static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent )
 
     if( p_sys->b_seekable )
     {
+        int64_t i_pos_backup = stream_Tell( p_demux->s );
+
+        /* Check and lazy load indexes if it was not done (not fastseekable) */
+        if ( !p_sys->b_indexloaded && ( p_sys->i_avih_flags & AVIF_HASINDEX ) )
+        {
+            avi_chunk_t *p_riff = AVI_ChunkFind( &p_sys->ck_root, AVIFOURCC_RIFF, 0 );
+            if (unlikely( !p_riff ))
+                return VLC_EGENERIC;
+
+            int i_ret = AVI_ChunkFetchIndexes( p_demux->s, p_riff );
+            if ( i_ret )
+            {
+                /* Go back to position before index failure */
+                if ( stream_Tell( p_demux->s ) - i_pos_backup )
+                    stream_Seek( p_demux->s, i_pos_backup );
+
+                if ( p_sys->i_avih_flags & AVIF_MUSTUSEINDEX )
+                    return VLC_EGENERIC;
+            }
+            else AVI_IndexLoad( p_demux );
+
+            p_sys->b_indexloaded = true; /* we don't want to try each time */
+        }
+
         if( !p_sys->i_length )
         {
             avi_track_t *p_stream = NULL;
             unsigned i_stream = 0;
             int64_t i_pos;
 
+            if ( !p_sys->i_movi_lastchunk_pos && /* set when index is successfully loaded */
+                 ! ( p_sys->i_avih_flags & AVIF_ISINTERLEAVED ) )
+            {
+                msg_Err( p_demux, "seeking without index at %d%%"
+                         " only works for interleaved files", i_percent );
+                goto failandresetpos;
+            }
             /* use i_percent to create a true i_date */
-            msg_Warn( p_demux, "seeking without index at %d%%"
-                      " only works for interleaved files", i_percent );
             if( i_percent >= 100 )
             {
                 msg_Warn( p_demux, "cannot seek so far !" );
-                return VLC_EGENERIC;
+                goto failandresetpos;
             }
             i_percent = __MAX( i_percent, 0 );
 
@@ -1460,14 +1492,14 @@ static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent )
             if( p_stream == NULL )
             {
                 msg_Warn( p_demux, "cannot find any selected stream" );
-                return VLC_EGENERIC;
+                goto failandresetpos;
             }
 
             /* be sure that the index exist */
             if( AVI_StreamChunkSet( p_demux, i_stream, 0 ) )
             {
                 msg_Warn( p_demux, "cannot seek" );
-                return VLC_EGENERIC;
+                goto failandresetpos;
             }
 
             while( i_pos >= p_stream->idx.p_entry[p_stream->i_idxposc].i_pos +
@@ -1478,7 +1510,7 @@ static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent )
                                         i_stream, p_stream->i_idxposc + 1 ) )
                 {
                     msg_Warn( p_demux, "cannot seek" );
-                    return VLC_EGENERIC;
+                    goto failandresetpos;
                 }
             }
 
@@ -1501,6 +1533,13 @@ static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent )
         p_sys->i_time = i_date;
         msg_Dbg( p_demux, "seek: %"PRId64" seconds", p_sys->i_time /1000000 );
         return VLC_SUCCESS;
+
+failandresetpos:
+        /* Go back to position before index failure */
+        if ( stream_Tell( p_demux->s ) - i_pos_backup )
+            stream_Seek( p_demux->s, i_pos_backup );
+
+        return VLC_EGENERIC;
     }
     else
     {
@@ -2296,6 +2335,8 @@ static int AVI_IndexLoad_idx1( demux_t *p_demux,
     if( AVI_IndexFind_idx1( p_demux, &p_idx1, &i_offset ) )
         return VLC_EGENERIC;
 
+    p_sys->b_indexloaded = true;
+
     for( unsigned i_index = 0; i_index < p_idx1->i_entry_count; i_index++ )
     {
         unsigned i_cat;
@@ -2325,6 +2366,8 @@ static void __Parse_indx( demux_t *p_demux, avi_index_t *p_index, off_t *pi_max_
 {
     avi_entry_t index;
 
+    p_demux->p_sys->b_indexloaded = true;
+
     msg_Dbg( p_demux, "loading subindex(0x%x) %d entries", p_indx->i_indextype, p_indx->i_entriesinuse );
     if( p_indx->i_indexsubtype == 0 )
     {
index d237cfa..c8a1eff 100644 (file)
@@ -202,6 +202,56 @@ static int AVI_ChunkRead_list( stream_t *s, avi_chunk_t *p_container )
     return VLC_SUCCESS;
 }
 
+/* Allow to append indexes after starting playback */
+int AVI_ChunkFetchIndexes( stream_t *s, avi_chunk_t *p_riff )
+{
+    avi_chunk_t *p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0 );
+    if ( !p_movi )
+        return VLC_EGENERIC;
+
+    avi_chunk_t *p_chk;
+    uint64_t i_indexpos = 8 + p_movi->common.i_chunk_pos + p_movi->common.i_chunk_size;
+    bool b_seekable = false;
+    int i_ret = VLC_SUCCESS;
+
+    stream_Control( s, STREAM_CAN_SEEK, &b_seekable );
+    if ( !b_seekable || stream_Seek( s, i_indexpos ) )
+        return VLC_EGENERIC;
+
+    for( ; ; )
+    {
+        p_chk = xmalloc( sizeof( avi_chunk_t ) );
+        memset( p_chk, 0, sizeof( avi_chunk_t ) );
+        if (unlikely( !p_riff->common.p_first ))
+            p_riff->common.p_first = p_chk;
+        else
+            p_riff->common.p_last->common.p_next = p_chk;
+        p_riff->common.p_last = p_chk;
+
+        i_ret = AVI_ChunkRead( s, p_chk, p_riff );
+        if( i_ret )
+            break;
+
+        if( p_chk->common.p_father->common.i_chunk_size > 0 &&
+           ( stream_Tell( s ) >
+              (off_t)p_chk->common.p_father->common.i_chunk_pos +
+               (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) )
+        {
+            break;
+        }
+
+        /* If we can't seek then stop when we 've found any index */
+        if( p_chk->common.i_chunk_fourcc == AVIFOURCC_indx ||
+            p_chk->common.i_chunk_fourcc == AVIFOURCC_idx1 )
+        {
+            break;
+        }
+
+    }
+
+    return i_ret;
+}
+
 #define AVI_READCHUNK_ENTER \
     int64_t i_read = __EVEN(p_chk->common.i_chunk_size ) + 8; \
     if( i_read > 100000000 ) \
index 2149223..83f004d 100644 (file)
@@ -259,6 +259,7 @@ void   *_AVI_ChunkFind ( avi_chunk_t *, vlc_fourcc_t, int );
 
 int     AVI_ChunkReadRoot( stream_t *, avi_chunk_t *p_root );
 void    AVI_ChunkFreeRoot( stream_t *, avi_chunk_t *p_chk  );
+int     AVI_ChunkFetchIndexes( stream_t *, avi_chunk_t *p_riff );
 
 #define AVI_ChunkCount( p_chk, i_fourcc ) \
     _AVI_ChunkCount( AVI_CHUNK(p_chk), i_fourcc )