Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#3531 closed defect (fixed)

ffmpeg hangs converting a possibly broken avi file

Reported by: hxuanyu Owned by:
Priority: important Component: undetermined
Version: git-master Keywords: avi deadlock regression
Cc: donmoir@comcast.net Blocked By:
Blocking: Reproduced by developer: yes
Analyzed by developer: no

Description

Hi,

Here is an avi file, for which latest ffplay couldn't play at all (not even one frame). Nor can ffprobe show any information.

MediaInfo? can show info of this file.

WMP plays a few frames then reports it can't play this type of file.
VLC doesn't play any frames but is responsive.
Quicktime crashes.

Ffprobe and ffplay just hang once command is executed.

https://dl.dropboxusercontent.com/u/89678527/Lake.avi

Change History (9)

comment:1 Changed 5 years ago by cehoyos

  • Keywords avi deadlock regression added
  • Priority changed from normal to important
  • Reproduced by developer set
  • Status changed from new to open
  • Summary changed from ffplay can't play any video frame to ffmpeg hangs converting a possibly broken avi file
  • Version changed from unspecified to git-master

(Works fine here with ffplay.)
Regression since 574dcb5b - possibly related to tickets #1796 and #3208.

(gdb) r -i Lake.avi -qscale 2 out.avi
Starting program: ffmpeg_g -i Lake.avi -qscale 2 out.avi
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
ffmpeg version N-62122-g16509d3 Copyright (c) 2000-2014 the FFmpeg developers
  built on Apr  4 2014 09:59:31 with gcc 4.7 (SUSE Linux)
  configuration: --enable-gpl
  libavutil      52. 73.100 / 52. 73.100
  libavcodec     55. 56.107 / 55. 56.107
  libavformat    55. 36.101 / 55. 36.101
  libavdevice    55. 11.100 / 55. 11.100
  libavfilter     4.  3.100 /  4.  3.100
  libswscale      2.  6.100 /  2.  6.100
  libswresample   0. 18.100 /  0. 18.100
  libpostproc    52.  3.100 / 52.  3.100
Input #0, avi, from 'Lake.avi':
  Duration: 00:00:11.51, start: 0.000000, bitrate: 129297 kb/s
    Stream #0:0: Video: h264 (High) (H264 / 0x34363248), yuv420p, 720x480 [SAR 1:1 DAR 3:2], 29.97 tbr, 1000k tbn, 2000k tbc
    Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 44100 Hz, stereo, s16p, 128 kb/s
Please use -q:a or -q:v, -qscale is ambiguous

Output #0, avi, to 'out.avi':
  Metadata:
    ISFT            : Lavf55.36.101
    Stream #0:0: Video: mpeg4 (FMP4 / 0x34504D46), yuv420p, 720x480 [SAR 1:1 DAR 3:2], q=2-31, 200 kb/s, 29.97 tbn, 29.97 tbc
    Stream #0:1: Audio: ac3 ([0] [0][0] / 0x2000), 44100 Hz, stereo, fltp, 192 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (h264 -> mpeg4)
  Stream #0:1 -> #0:1 (mp3 -> ac3)
Press [q] to stop, [?] for help

hangs here

Program received signal SIGINT, Interrupt.
ff_index_search_timestamp (flags=0, wanted_timestamp=<optimized out>, nb_entries=11511001,
    entries=0x7fffd477d010) at libavformat/utils.c:1822
1822            while (m >= 0 && m < nb_entries &&
(gdb) bt
#0  ff_index_search_timestamp (flags=0, wanted_timestamp=<optimized out>,
    nb_entries=11511001, entries=0x7fffd477d010) at libavformat/utils.c:1822
#1  av_index_search_timestamp (st=st@entry=0x1924140, wanted_timestamp=<optimized out>,
    flags=flags@entry=0) at libavformat/utils.c:1833
#2  0x000000000050fa50 in avi_read_packet (s=0x1923900, pkt=<optimized out>)
    at libavformat/avidec.c:1371
#3  0x00000000005e7666 in ff_read_packet (s=s@entry=0x1923900, pkt=pkt@entry=0x7fffffffd630)
    at libavformat/utils.c:767
#4  0x00000000005e9c30 in read_frame_internal (s=0x1923900, pkt=0x7fffffffda10)
    at libavformat/utils.c:1436
#5  0x00000000005eab06 in av_read_frame (s=0x1923900, pkt=pkt@entry=0x7fffffffda10)
    at libavformat/utils.c:1557
#6  0x0000000000480d85 in get_input_packet (pkt=0x7fffffffda10, f=0x1b1ff20)
    at ffmpeg.c:3184
#7  process_input (file_index=0) at ffmpeg.c:3221
#8  0x0000000000468db0 in transcode_step () at ffmpeg.c:3507
#9  transcode () at ffmpeg.c:3559
#10 main (argc=<optimized out>, argv=<optimized out>) at ffmpeg.c:3739
(gdb) disass $pc-32,$pc+32
Dump of assembler code from 0x5eae80 to 0x5eaec0:
   0x00000000005eae80 <av_index_search_timestamp+160>:  lea    (%r9,%rdx,8),%rdx
   0x00000000005eae84 <av_index_search_timestamp+164>:  jmp    0x5eae9d <av_index_search_timestamp+189>
   0x00000000005eae86 <av_index_search_timestamp+166>:  nopw   %cs:0x0(%rax,%rax,1)
   0x00000000005eae90 <av_index_search_timestamp+176>:  movzbl 0x10(%rdx),%ecx
   0x00000000005eae94 <av_index_search_timestamp+180>:  add    $0x18,%rdx
   0x00000000005eae98 <av_index_search_timestamp+184>:  and    $0x1,%ecx
   0x00000000005eae9b <av_index_search_timestamp+187>:  jne    0x5eaf00 <av_index_search_timestamp+288>
   0x00000000005eae9d <av_index_search_timestamp+189>:  add    $0x1,%eax
=> 0x00000000005eaea0 <av_index_search_timestamp+192>:  cmp    %eax,%r10d
   0x00000000005eaea3 <av_index_search_timestamp+195>:  jg     0x5eae90 <av_index_search_timestamp+176>
   0x00000000005eaea5 <av_index_search_timestamp+197>:  nopl   (%rax)
   0x00000000005eaea8 <av_index_search_timestamp+200>:  cmp    %eax,%r10d
   0x00000000005eaeab <av_index_search_timestamp+203>:  mov    $0xffffffff,%edx
   0x00000000005eaeb0 <av_index_search_timestamp+208>:  cmove  %edx,%eax
   0x00000000005eaeb3 <av_index_search_timestamp+211>:  repz retq
   0x00000000005eaeb5 <av_index_search_timestamp+213>:  nopl   (%rax)
   0x00000000005eaeb8 <av_index_search_timestamp+216>:  add    %r10d,%r8d
   0x00000000005eaebb <av_index_search_timestamp+219>:  jmpq   0x5eae18 <av_index_search_timestamp+56>
End of assembler dump.
(gdb) info register
rax            0x3f7876 4159606
rbx            0x19249c0        26364352
rcx            0x0      0
rdx            0x7fffda6b1b20   140736857840416
rsi            0xf554e8 16078056
rdi            0x1eaa9d 2009757
rbp            0x0      0x0
rsp            0x7fffffffd428   0x7fffffffd428
r8             0xa38df  669919
r9             0x7fffd477d010   140736758009872
r10            0xafa4d9 11511001
r11            0x0      0
r12            0x1924130        26362160
r13            0x1924e80        26365568
r14            0x1924130        26362160
r15            0x7fffffffd630   140737488344624
rip            0x5eaea0 0x5eaea0 <av_index_search_timestamp+192>
eflags         0x202    [ IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

comment:2 Changed 5 years ago by DonMoir

  • Cc donmoir@comcast.net added

comment:3 Changed 5 years ago by michael

  • Resolution set to fixed
  • Status changed from open to closed

comment:4 Changed 5 years ago by DonMoir

Could someone double check to see if this is actually fixed. For me, it seems to be hung up in read_braindead_odml_indx (avidec.c) which keeps building indexes recursively until it dies.

Comment out where read_braindead_odml_indx is called and it works.

Last edited 5 years ago by DonMoir (previous) (diff)

comment:5 Changed 5 years ago by DonMoir

Hangs up when trying to build index for me.

static int avi_read_header(AVFormatContext *s)
{
    ...
        case MKTAG('i', 'n', 'd', 'x'):
            i = avio_tell(pb);
            if (pb->seekable && !(s->flags & AVFMT_FLAG_IGNIDX) &&
                avi->use_odml &&
                read_braindead_odml_indx(s, 0) < 0 &&
                (s->error_recognition & AV_EF_EXPLODE))
                goto fail;
            avio_seek(pb, i + size, SEEK_SET);
            break;
    ...
}

This allows it to at least play. Seeking not so good :)

        case MKTAG('i', 'n', 'd', 'x'):
            i = avio_tell(pb);
/*
            if (pb->seekable && !(s->flags & AVFMT_FLAG_IGNIDX) &&
                avi->use_odml &&
                read_braindead_odml_indx(s, 0) < 0 &&
                (s->error_recognition & AV_EF_EXPLODE))
                goto fail;
*/
            avio_seek(pb, i + size, SEEK_SET);
            break;

I am not trying to convert, just playback.

Last edited 5 years ago by DonMoir (previous) (diff)

comment:6 Changed 5 years ago by michael

The index is big, it needs time to be read
on ssd its less than 10sec it might be more on a slow hdd

comment:7 Changed 5 years ago by DonMoir

It seems to settle somewhere around 11,200,000 entries. I stopped it at that anyway. If you take the size of the file which is 186,042,456 bytes divided by 11,200,000 then thats about an entry for every 16 bytes.

While it's doing this, it's eating the CPU big time plus memory overhead. It's just not going to work for most people. The only way out is to trap interrupt_callback and just fail, but wish there was some other solution.

I am testing this on my slower machine and there I can't let it complete since this computer will overheat and shutdown. Have not tested it on fast machine but whatever the case I can't let this happen.

comment:8 Changed 5 years ago by DonMoir

I changed read_braindead_odml_indx so it only adds an index entry if it's a keyframe. You end up with only 2 index entries. This allowed to open in about 1 to 3 seconds on my slower machine. Could not open on this machine before without it dying due to overheating. When adding index entries in read_braindead_odml_indx, it adds keyframe or non-keyframe entries and it's the non-keyframe entries that are a problem since there are many millions of them. Seems memory is thrashing to disk etc., as it attempts to build the index since it becomes a large consumer of memory and CPU.

With the above change it worked normally but I don't know what other ramifications it might have.

comment:9 Changed 5 years ago by DonMoir

I ended up changing the following. I tested several avi files and it seems to be ok. With the change, lake.avi will open in about 2 seconds with about 350 index entries. Need someone to verify. Also, it could be last_pos should only be set if an index is added. Does new ticket need to be opened?

static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
{
    AVIContext *avi     = s->priv_data;
    AVIOContext *pb     = s->pb;
    int longs_pre_entry = avio_rl16(pb);
    int index_sub_type  = avio_r8(pb);
    int index_type      = avio_r8(pb);
    int entries_in_use  = avio_rl32(pb);
    int chunk_id        = avio_rl32(pb);
    int64_t base        = avio_rl64(pb);
    int stream_id       = ((chunk_id      & 0xFF) - '0') * 10 +
                          ((chunk_id >> 8 & 0xFF) - '0');
    AVStream *st;
    AVIStream *ast;
    int i;
    int64_t last_pos = -1;
    int64_t filesize = avi->fsize;

    av_dlog(s,
            "longs_pre_entry:%d index_type:%d entries_in_use:%d "
            "chunk_id:%X base:%16"PRIX64"\n",
            longs_pre_entry,
            index_type,
            entries_in_use,
            chunk_id,
            base);

    if (stream_id >= s->nb_streams || stream_id < 0)
        return AVERROR_INVALIDDATA;
    st  = s->streams[stream_id];
    ast = st->priv_data;

    if (index_sub_type)
        return AVERROR_INVALIDDATA;

    avio_rl32(pb);

    if (index_type && longs_pre_entry != 2)
        return AVERROR_INVALIDDATA;
    if (index_type > 1)
        return AVERROR_INVALIDDATA;

    if (filesize > 0 && base >= filesize) {
        av_log(s, AV_LOG_ERROR, "ODML index invalid\n");
        if (base >> 32 == (base & 0xFFFFFFFF) &&
            (base & 0xFFFFFFFF) < filesize    &&
            filesize <= 0xFFFFFFFF)
            base &= 0xFFFFFFFF;
        else
            return AVERROR_INVALIDDATA;
    }

    for (i = 0; i < entries_in_use; i++) {
        if (index_type) {
            int64_t pos = avio_rl32(pb) + base - 8;
            int len     = avio_rl32(pb);
            int key     = len >= 0;
            len &= 0x7FFFFFFF;

#ifdef DEBUG_SEEK
            av_log(s, AV_LOG_ERROR, "pos:%"PRId64", len:%X\n", pos, len);
#endif
            if (url_feof(pb))
                return AVERROR_INVALIDDATA;

            if (last_pos == pos || pos == base - 8)
                avi->non_interleaved = 1;
-            if (last_pos != pos && (len || !ast->sample_size))
+            if (len && last_pos != pos)
                av_add_index_entry(st, pos, ast->cum_len, len, 0,
                                   key ? AVINDEX_KEYFRAME : 0);

            ast->cum_len += get_duration(ast, len);
            last_pos      = pos;
        } else {
            int64_t offset, pos;
            int duration;
            offset = avio_rl64(pb);
            avio_rl32(pb);       /* size */
            duration = avio_rl32(pb);

            if (url_feof(pb))
                return AVERROR_INVALIDDATA;

            pos = avio_tell(pb);

            if (avi->odml_depth > MAX_ODML_DEPTH) {
                av_log(s, AV_LOG_ERROR, "Too deeply nested ODML indexes\n");
                return AVERROR_INVALIDDATA;
            }

            if (avio_seek(pb, offset + 8, SEEK_SET) < 0)
                return -1;
            avi->odml_depth++;
            read_braindead_odml_indx(s, frame_num);
            avi->odml_depth--;
            frame_num += duration;

            if (avio_seek(pb, pos, SEEK_SET) < 0) {
                av_log(s, AV_LOG_ERROR, "Failed to restore position after reading index\n");
                return -1;
            }

        }
    }
    avi->index_loaded = 2;
    return 0;
}
Note: See TracTickets for help on using tickets.