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