From: Michael Niedermayer <[email protected]>

Additional fixes by Nigel Touati-Evans <[email protected]>.
The method guess_ni_flag needs to divide timestamps in the index by
sample_size, if it is set in order to compare different streams correctly.

Bug-Id: 666
CC: [email protected]
Sample-Id: yet-another-broken-interleaved-avi.avi
Signed-off-by: Vittorio Giovara <[email protected]>
---
No better bug number for the issue at hand.

This fixes desync for badly interleaved files, such as the one provided, in mpv 
and possibly other players. Thanks to wm4 for testing this patch and confirming 
it fixes the bug.

Vittorio

 libavformat/avidec.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 54 insertions(+), 1 deletion(-)

diff --git a/libavformat/avidec.c b/libavformat/avidec.c
index f48c441..55ef9d5 100644
--- a/libavformat/avidec.c
+++ b/libavformat/avidec.c
@@ -1313,6 +1313,8 @@ static int guess_ni_flag(AVFormatContext *s)
     int64_t last_start = 0;
     int64_t first_end  = INT64_MAX;
     int64_t oldpos     = avio_tell(s->pb);
+    int *idx;
+    int64_t min_pos, pos;
 
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st = s->streams[i];
@@ -1336,7 +1338,58 @@ static int guess_ni_flag(AVFormatContext *s)
             first_end = st->index_entries[n - 1].pos;
     }
     avio_seek(s->pb, oldpos, SEEK_SET);
-    return last_start > first_end;
+
+    if (last_start > first_end)
+        return 1;
+
+    idx = av_mallocz_array(s->nb_streams, sizeof(*idx));
+    if (!idx)
+        return 0;
+
+    for (min_pos = pos = 0; min_pos != INT64_MAX; pos = min_pos + 1LU) {
+        int64_t max_dts = INT64_MIN / 2, min_dts= INT64_MAX / 2;
+        int64_t max_buffer = 0;
+        min_pos = INT64_MAX;
+
+        for (i = 0; i < s->nb_streams; i++) {
+            AVStream *st = s->streams[i];
+            AVIStream *ast = st->priv_data;
+            int n = st->nb_index_entries;
+            while (idx[i] < n && st->index_entries[idx[i]].pos < pos)
+                idx[i]++;
+            if (idx[i] < n) {
+                min_dts = FFMIN(min_dts,
+                                
av_rescale_q(st->index_entries[idx[i]].timestamp /
+                                             FFMAX(ast->sample_size, 1),
+                                             st->time_base, AV_TIME_BASE_Q));
+                min_pos = FFMIN(min_pos, st->index_entries[idx[i]].pos);
+            }
+        }
+        for (i = 0; i < s->nb_streams; i++) {
+            AVStream *st = s->streams[i];
+            AVIStream *ast = st->priv_data;
+
+            if (idx[i] && min_dts != INT64_MAX / 2) {
+                int64_t dts;
+                dts = av_rescale_q(st->index_entries[idx[i] - 1].timestamp /
+                                   FFMAX(ast->sample_size, 1),
+                                   st->time_base, AV_TIME_BASE_Q);
+                max_dts = FFMAX(max_dts, dts);
+                max_buffer = FFMAX(max_buffer,
+                                   av_rescale(dts - min_dts,
+                                              st->codec->bit_rate,
+                                              AV_TIME_BASE));
+            }
+        }
+        if (max_dts - min_dts > 2 * AV_TIME_BASE ||
+            max_buffer > 1024 * 1024 * 8 * 8) {
+            av_free(idx);
+            return 1;
+        }
+    }
+    av_free(idx);
+    return 0;
+
 }
 
 static int avi_load_index(AVFormatContext *s)
-- 
1.8.3.4 (Apple Git-47)

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to