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]
