Some AVI encoders write size=0 in every movi chunk header but store
correct sizes in the idx1 index. The demuxer reads movi chunk headers,
sees size=0, and discards every frame — producing no output. VLC and
other players handle these files by falling back to idx1 sizes.

Two issues prevent recovery in the current code:

1. In these files the idx1 tag appears before the LIST movi chunk in
   the file. avi_load_index() scans forward from movi_end and never
   finds it, so index entries are never populated.

2. avi_sync() reads the chunk size from the movi data (which is 0)
   and has no fallback to use index entry sizes.

Fix both by:
- Recording the idx1 position and size when encountered during header
  parsing in avi_read_header()
- Adding a fallback in avi_load_index() to try the recorded position
  when idx1 is not found after movi_end
- Looking up the correct chunk size from loaded index entries in
  avi_sync() when the movi chunk header reports size=0

The avi_sync() recovery mirrors the existing pattern in
ni_prepare_read() (line 1460) which already uses index entry sizes
when ast->remaining is 0.

Tested with AVI files from surveillance DVR encoders that exhibit
this exact pattern: valid idx1, valid frame data, but all movi chunk
size fields set to 0.

Signed-off-by: Kirill Bykov <[email protected]>
---
 libavformat/avidec.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/libavformat/avidec.c b/libavformat/avidec.c
index d7f8610628..8a478d8528 100644
--- a/libavformat/avidec.c
+++ b/libavformat/avidec.c
@@ -78,6 +78,8 @@ typedef struct AVIContext {
     int64_t io_fsize;
     int64_t movi_list;
     int64_t last_pkt_pos;
+    int64_t idx1_pos;  /* position of idx1 tag seen during header parsing */
+    int idx1_size;     /* size of idx1 data */
     int index_loaded;
     int is_odml;
     int non_interleaved;
@@ -1056,6 +1058,9 @@ static int avi_read_header(AVFormatContext *s)
             }
         /* Do not fail for very large idx1 tags */
         case MKTAG('i', 'd', 'x', '1'):
+            /* Record idx1 position for avi_load_index fallback */
+            avi->idx1_pos  = avio_tell(pb);
+            avi->idx1_size = size;
             /* skip tag */
             size += (size & 1);
             avio_skip(pb, size);
@@ -1362,6 +1367,18 @@ start_sync:
                     ast->prefix_count = 0;
                 }
 
+                /* Recover zero-size chunks using idx1 index entries */
+                if (!size && !exit_early) {
+                    FFStream *const sti = ffstream(st);
+                    if (sti->nb_index_entries > 0) {
+                        int idx = av_index_search_timestamp(st,
+                            ast->frame_offset, AVSEEK_FLAG_ANY);
+                        if (idx >= 0 && idx < sti->nb_index_entries &&
+                            sti->index_entries[idx].size > 0)
+                            size = sti->index_entries[idx].size;
+                    }
+                }
+
                 if (!avi->dv_demux &&
                     ((st->discard >= AVDISCARD_DEFAULT && size == 0) /* ||
                         // FIXME: needs a little reordering
@@ -1843,6 +1860,18 @@ static int avi_load_index(AVFormatContext *s)
             break; // something is wrong here
     }
 
+    /* Fallback: if idx1 was seen during header parsing (before movi),
+     * but not found after movi_end, try reading it from the recorded position 
*/
+    if (ret < 0 && avi->idx1_pos > 0 && avi->idx1_size > 0) {
+        av_log(s, AV_LOG_INFO,
+               "idx1 not found after movi_end, trying position from header 
parsing\n");
+        avio_seek(pb, avi->idx1_pos, SEEK_SET);
+        if (avi_read_idx1(s, avi->idx1_size) >= 0) {
+            avi->index_loaded = 2;
+            ret = 0;
+        }
+    }
+
 the_end:
     avio_seek(pb, pos, SEEK_SET);
     return ret;
-- 
2.43.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to