MediaMetadataRetriever fails after 102 requests with exception 0x80000000


I want to check if each of my mp3 files has a valid picture that the Android system can read.

For this I iterate over all the items in the MediaStore.Audio.Media and then ask the MediaMetadataRetriever to .setDataSource() to .getEmbeddedPicture()

This works fine for the first 102 files on my Pixel 8 device. Thereafter .setDataSource() fails with this exception:

java.lang.RuntimeException: setDataSource failed: status = 0x80000000 at android.media.MediaMetadataRetriever._setDataSource(Native Method) at android.media.MediaMetadataRetriever.setDataSource(MediaMetadataRetriever.java:310) at android.media.MediaMetadataRetriever.setDataSource(MediaMetadataRetriever.java:253) at org.richinet.checkmusicimages.MainActivity.checkMetadata(MainActivity.kt:108) at org.richinet.checkmusicimages.MainActivity.scanMusic(MainActivity.kt:100) at org.richinet.checkmusicimages.MainActivity.checkPermissionsAndScan(MainActivity.kt:72) at org.richinet.checkmusicimages.MainActivity.onCreate(MainActivity.kt:59) at android.app.Activity.performCreate(Activity.java:9290) at android.app.Activity.performCreate(Activity.java:9268) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1543) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4503) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4723) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:224) at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:133) at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:103) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2987) at android.os.Handler.dispatchMessageImpl(Handler.java:142) at android.os.Handler.dispatchMessage(Handler.java:125) at android.os.Looper.loopOnce(Looper.java:269) at android.os.Looper.loop(Looper.java:367) at android.app.ActivityThread.main(ActivityThread.java:9333) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)

I have tried

  • to slow things down to 1 request per second so that resources can be reclaimed with .release()

  • chunking requests and waiting 20 seconds after each chunk

  • spawning chunks off into a separate process.

  • reusing the MediaMetadataRetriever

  • creating a new MediaMetadataRetriever for each request

  • calling GC after every request

Only stopping the app and starting it fresh gets me me a new window of 102 requests again.

How to solve this issue?

I want to use the MediaMetadataReceiver and not an external library as I want to be sure the raw Android device can deal with the images and not something a skilled 3rd party library can exclusively handle.

Gemini says there are Binders and native C++ functions that may not be releasing resources. If this is true, how can we get the native process to restart after say 80 tests? Here is my test code (generated by Gemini)

private fun scanMusic() {
    val projection = arrayOf(
        MediaStore.Audio.Media._ID,
        MediaStore.Audio.Media.DISPLAY_NAME,
        MediaStore.Audio.Media.DATA
    )

    val cursor = contentResolver.query(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        projection,
        null,
        null,
        null
    )

    cursor?.use {
        val dataColumn = it.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)
        val nameColumn = it.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)

        while (it.moveToNext()) {
            val path = it.getString(dataColumn)
            val name = it.getString(nameColumn)
            checkMetadata(path, name)
        }
    }
}

private fun checkMetadata(path: String, name: String) {
    val retriever = MediaMetadataRetriever()
    try {
        retriever.setDataSource(path)
        val art = retriever.embeddedPicture
        if (art != null) {
            musicResults.add("OK: $name (Size: ${art.size} bytes)")
            okCount++
            if (okCount > 0 && okCount % 100 == 0) logCounts()
        } else {
            musicResults.add("NO ART: $name")
            noArtCount++
            if (noArtCount > 0 && noArtCount % 100 == 0) logCounts()
        }
    } catch (e: Exception) {
        musicResults.add("ERROR: $name - ${e.message}")
        errorCount++
        if (errorCount > 0 && errorCount % 100 == 0) logCounts()
        //Log.e("MusicScanner", "Error reading $path", e)
    } finally {
        retriever.release()
    }
}

And here a sample log message:

OK: 102, No Art: 42, Errors: 900
0
Apr 13 at 10:50 AM
User Avatarrichardeigenmann
#android#kotlin#mediametadataretriever

No answer found for this question yet.