Out of Memory exception during video playback
I'm seeing some out of memory crashes when playing certain videos using the 2.2.0 Debug APK. One video is a large MP4 (1920 x 1080) with audio, 30 fps, encoded at an abnormally high quality (57 MB for 60 secs). The other is a WEBM that is not obviously "problematic". This is on a Pixel 7a so memory shouldn't be tight.
It's also possible these crashes are the result of creating thumbnails for a folder. As far as I can tell, DroidFS does not extract existing thumbnails from videos or even cache the ones it generates which results in extra processing every time the user enters a folder of videos.
Some stack traces are provided below. The first three are out of memory exceptions, the final is a null pointer exception.
12-22 01:21:04.491 21969 22014 E AndroidRuntime: FATAL EXCEPTION: InsetsAnimations 12-22 01:21:04.491 21969 22014 E AndroidRuntime: Process: sushi.hardcore.droidfs.debug, PID: 21969 12-22 01:21:04.491 21969 22014 E AndroidRuntime: java.lang.OutOfMemoryError: Failed to allocate a 24 byte allocation with 169584 free bytes and 165KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC. 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.graphics.Insets.of(Insets.java:64) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.InsetsController.lambda$static$1(InsetsController.java:333) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.InsetsController$$ExternalSyntheticLambda4.evaluate(D8$$SyntheticClass:0) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.InsetsController$InternalAnimationControlListener.lambda$onReady$0(InsetsController.java:431) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.InsetsController$InternalAnimationControlListener.$r8$lambda$BJTEi7zfHhP2W08t256nzVrDzao(Unknown Source:0) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.InsetsController$InternalAnimationControlListener$$ExternalSyntheticLambda0.onAnimationUpdate(D8$$SyntheticClass:0) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.Animator$AnimatorCaller.lambda$static$4(Animator.java:855) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda6.call(D8$$SyntheticClass:0) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.Animator.callOnList(Animator.java:666) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1645) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.ValueAnimator.animateBasedOnTime(ValueAnimator.java:1405) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1563) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:344) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:87) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1413) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1424) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.Choreographer.doCallbacks(Choreographer.java:1024) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.Choreographer.doFrame(Choreographer.java:949) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1398) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:991) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317) 12-22 01:21:04.491 21969 22014 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:85)12-22 06:31:29.614 4832 11057 E AndroidRuntime: FATAL EXCEPTION: ExoPlayer:Playback
12-22 06:31:29.614 4832 11057 E AndroidRuntime: Process: sushi.hardcore.droidfs.debug, PID: 4832
12-22 06:31:29.614 4832 11057 E AndroidRuntime: java.lang.OutOfMemoryError: Failed to allocate a 64 byte allocation with 103216 free bytes and 100KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC.
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.media.MediaCodec.getBuffer(Native Method)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.media.MediaCodec.getOutputBuffer(MediaCodec.java:4655)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecAdapter.getOutputBuffer(AsynchronousMediaCodecAdapter.java:236)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.drainOutputBuffer(MediaCodecRenderer.java:1963)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:827)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.video.MediaCodecVideoRenderer.render(MediaCodecVideoRenderer.java:940)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1112)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:544)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:105)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:85)`
12-22 06:31:29.614 4832 11057 E AndroidRuntime: FATAL EXCEPTION: ExoPlayer:Playback 12-22 06:31:29.614 4832 11057 E AndroidRuntime: Process: sushi.hardcore.droidfs.debug, PID: 4832 12-22 06:31:29.614 4832 11057 E AndroidRuntime: java.lang.OutOfMemoryError: Failed to allocate a 64 byte allocation with 103216 free bytes and 100KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC. 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.media.MediaCodec.getBuffer(Native Method) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.media.MediaCodec.getOutputBuffer(MediaCodec.java:4655) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecAdapter.getOutputBuffer(AsynchronousMediaCodecAdapter.java:236) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.drainOutputBuffer(MediaCodecRenderer.java:1963) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:827) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.video.MediaCodecVideoRenderer.render(MediaCodecVideoRenderer.java:940) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1112) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:544) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:105) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317) 12-22 06:31:29.614 4832 11057 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:85)
12-22 06:36:00.300 11085 11085 E AndroidRuntime: FATAL EXCEPTION: main 12-22 06:36:00.300 11085 11085 E AndroidRuntime: Process: sushi.hardcore.droidfs.debug, PID: 11085 12-22 06:36:00.300 11085 11085 E AndroidRuntime: java.lang.OutOfMemoryError: Failed to allocate a 24 byte allocation with 32400 free bytes and 31KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC. 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at androidx.media3.common.util.Util.getStringForTime(Util.java:2716) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at androidx.media3.ui.PlayerControlView.updateProgress(PlayerControlView.java:1288) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at androidx.media3.ui.PlayerControlView.$r8$lambda$fA_GCUtqcqNfkXcjodDFOHMvm0o(Unknown Source:0) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at androidx.media3.ui.PlayerControlView$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:991) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8826) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591) 12-22 06:36:00.300 11085 11085 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:887)
12-22 01:21:05.671 4587 4587 E AndroidRuntime: FATAL EXCEPTION: main 12-22 01:21:05.671 4587 4587 E AndroidRuntime: Process: sushi.hardcore.droidfs.debug, PID: 4587 12-22 01:21:05.671 4587 4587 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{sushi.hardcore.droidfs.debug/sushi.hardcore.droidfs.explorers.ExplorerActivity}: java.lang.NullPointerException 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4142) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4329) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:222) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:133) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:103) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2728) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:109) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8826) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:887) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: Caused by: java.lang.NullPointerException 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at sushi.hardcore.droidfs.explorers.BaseExplorerActivity.onCreate(BaseExplorerActivity.kt:98) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:9072) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:9050) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1538) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4124) 12-22 01:21:05.671 4587 4587 E AndroidRuntime: ... 13 more
It's also possible these crashes are the result of creating thumbnails for a folder. As far as I can tell, DroidFS does not extract existing thumbnails from videos or even cache the ones it generates which results in extra processing every time the user enters a folder of videos.
Yeah, the current thumbnails implementation is shitty. This issue looks like #317. Does the application crash or display a "application not responding" dialog? What did you set the thumbnail size limit to? Do these errors still occur when disabling thumbnails in the settings?
I have the thumbnail limit set to 100 MB.
I think I saw an "application not responding" dialog once but I don't see a message to that effect in the logcat.
I usually try to attach a thumbnail to my videos either using atomicparsley (for mp4) or via ffmpeg (for mkv) to give downstream applications a quick way to grab a thumbnail, one that is actually a good representation of the video.
I don't know what strategy DroidFS currently uses. Does it even order the files from smallest to largest and work on the smallest ones first? Or does it spawn multiple threads all at once for each video?
One can extract video thumbnails quickly using ffmpeg, e.g. via:
ffmpeg -i "in.mp4" -map 0:v -map -0:V -c copy -frames:v 1 -update 1 "out.jpg"
The effect of -map 0:v -map -0:V is to ignore anything except a picture stream (if any exist). Some video formats, e.g. mp4, allow multiple picture streams. -frames:v 1 -update 1 says to export only the first one rather than write out say img1.jpg, img2.jpg, etc. If the thumbnail is in a different format (e.g. png), ffmpeg seems to automatically convert it to the format indicated by the extension, i.e. JPEG here.
I've tested this with mp4, mkv (specifically x265), and webm. Seems to work fine. If the file does not contain a thumbnail, ffmpeg errors out with Output file does not contain any stream.
ffmpeg-kit (https://github.com/arthenica/ffmpeg-kit) may be helpful though I'm not an Android developer so I can't say for sure.