sannies / mp4parser

A Java API to read, write and create MP4 files
Apache License 2.0
2.76k stars 566 forks source link

OutOfMemory error on opening file via IsoFile #284

Closed alexeyvasilyev closed 6 years ago

alexeyvasilyev commented 6 years ago

Constantly getting OutOfMemory error on any MP4 file written by Android device when opening file via IsoFile. Tested on MP4 files created by default camera apps Samsung S5 Android 6.0.1 and Nexus 6P Android 8.0.

STEPS TO REPRODUCE

new IsoFile("mp4parser_isofile_outofmemory.mp4");

File to test https://www.dropbox.com/s/3jdd5dii1in0o5u/mp4parser_isofile_outofmemory.mp4?dl=0

Using org.mp4parser:isoparser:1.9.31 on Android. ISO Viewer app v2.0.2 also has this issue.

Caused by: java.lang.OutOfMemoryError: Failed to allocate a 1751411832 byte allocation with 4016136 free bytes and 508MB until OOM, max allowed footprint 8032272, growth limit 536870912
                                                                         at java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:54)
                                                                         at java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:49)
                                                                         at java.nio.ByteBuffer.allocate(ByteBuffer.java:261)
                                                                         at org.mp4parser.support.AbstractBox.parse(AbstractBox.java:96)
                                                                         at org.mp4parser.AbstractBoxParser.parseBox(AbstractBoxParser.java:104)
                                                                         at org.mp4parser.BasicContainer.initContainer(BasicContainer.java:107)
                                                                         at org.mp4parser.boxes.iso14496.part12.MetaBox.parse(MetaBox.java:82)
                                                                         at org.mp4parser.AbstractBoxParser.parseBox(AbstractBoxParser.java:104)
                                                                         at org.mp4parser.BasicContainer.initContainer(BasicContainer.java:107)
                                                                         at org.mp4parser.support.AbstractContainerBox.parse(AbstractContainerBox.java:75)
                                                                         at org.mp4parser.AbstractBoxParser.parseBox(AbstractBoxParser.java:104)
                                                                         at org.mp4parser.BasicContainer.initContainer(BasicContainer.java:107)
                                                                         at org.mp4parser.IsoFile.<init>(IsoFile.java:57)
                                                                         at org.mp4parser.IsoFile.<init>(IsoFile.java:44)
alexeyvasilyev commented 6 years ago

Looks like this is the same issue https://github.com/sannies/mp4parser/issues/240

andreikap commented 6 years ago

mp4parser is parsing the files according to ISO14496-12 spec. However, the file is formatted according to Quick Time File Format Specification. The key difference is that in ISO format, the 'meta' box contains two additional fields that precede the 'hdlr' box, namely, 8-bit version and 24-bit flag values. As the result, the data is shifted by 4 bytes.

The crash happens when the code tries to parse metadata ('meta') box. According to the ISO14496-12 format, the first metadata sub-box is the handler reference box ('hdlr'), which (as all boxes do) should start with 4-bytes representing the length. However, because of the 4-byte shift, the parser points to 'hldr' ASCII bytes (which equal to 1751411826) and interprets them as length. That is the reason why you see: java.lang.OutOfMemoryError: Failed to allocate a 1751411832 byte.

The solution could be to determine the type of the metadata format and parse it accordingly.

bbrown-extensis commented 6 years ago

TinyCam, thank you for the analysis. This problem has been frustrating me for a long time. I believe this https://github.com/sannies/mp4parser/issues/139 is the same problem. I wonder how I can tell which format. Perhaps I can get some time to work on this and submit a patch.

andreikap commented 6 years ago

It should be possible to reset the ByteBuffer position back 4 bytes in MetaBox.parseVersionAndFlags() if the next 4 bytes represent 'hdlr' after reading the version and flags. For extra verification, the 9th to 12th bytes after 'hdlr' should contain 'mdta' (see Metadata Handler Atom].

The write methods should be change accordingly.

r2el commented 6 years ago

Having the same issue on Android 8.1.0 from a file recorded on device from camera using android.media.MediaRecorder:

Caused by java.lang.OutOfMemoryError

  | java.nio.HeapByteBuffer. (HeapByteBuffer.java:54)   | java.nio.ByteBuffer.allocate (ByteBuffer.java:261)   | org.mp4parser.support.AbstractBox.parse (AbstractBox.java:96)   | org.mp4parser.AbstractBoxParser.parseBox (AbstractBoxParser.java:104)   | org.mp4parser.BasicContainer.initContainer (BasicContainer.java:107)   | org.mp4parser.boxes.iso14496.part12.MetaBox.parse (MetaBox.java:82)   | org.mp4parser.AbstractBoxParser.parseBox (AbstractBoxParser.java:104)   | org.mp4parser.BasicContainer.initContainer (BasicContainer.java:107)   | org.mp4parser.support.AbstractContainerBox.parse (AbstractContainerBox.java:75)   | org.mp4parser.AbstractBoxParser.parseBox (AbstractBoxParser.java:104)   | org.mp4parser.BasicContainer.initContainer (BasicContainer.java:107)   | org.mp4parser.IsoFile. (IsoFile.java:57)   | org.mp4parser.IsoFile. (IsoFile.java:40)

I'm not seeing the issue on older versions of Android.

TonyXu09 commented 6 years ago

I just ran in to this issue today. Is there anyway for us to merge in the pull request with the fix?

HBiSoft commented 5 years ago

@YitianTonyXu can you please confirm if 1.9.37 - #288 fixed this issue?