Das Lesen von TagLib verlangsamt sich nach ca. 1600 ElementenAndroid

Forum für diejenigen, die für Android programmieren
Anonymous
 Das Lesen von TagLib verlangsamt sich nach ca. 1600 Elementen

Post by Anonymous »

Ich habe eine App, die mithilfe von TagLib Metadaten aus einer Liste von Songs abruft.
Sie ruft zuerst die Song-ID über MediaStore ab und sendet fd an TagLib, das sie dupliziert und zum Öffnen eines Streams verwendet, um die Metadaten und Audioeigenschaften abzurufen.
Dies gibt eine Zuordnung von und einem IntArray der Größe 4 zurück.
Dies wird für jede von der zurückgegebene ID aufgerufen MediaStore-Abfrage.
Die ersten 1700 Songs dauern ca. 700 ms pro 100 Songs, aber alle 100 danach dauern 23 Sekunden und erhöhen sich alle 100 auf ca. 60 Sekunden.
Diese Methode wird auf Dispatchers IO ausgeführt.
Was kann ich tun, um dies zu reduzieren?
Dieser vollständige Scan wird beim ersten Start der App durchgeführt Nicht wirklich besorgniserregend.
Allerdings dauert ein vollständiger Scan bei über 7900 Songs etwa 18 Minuten.
MediaStore-Scan

Code: Select all

fun getAudioFilesViaMediaStore(): List {
//.nomedia affected
val musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Audio.Media._ID)

val audioList = mutableListOf()

//TODO replace placeholder path
val audioCursor = contentResolver.query(
musicUri,
projection,
"${MediaStore.Audio.Media.RELATIVE_PATH} LIKE ?",
arrayOf("%Music%"),
null
) ?: return emptyList()

audioCursor.use { cursor ->
val idColumn: Int = audioCursor.getColumnIndex(MediaStore.Audio.AudioColumns._ID)

cursor.apply {
if (count == 0) Log.d("Cursor", "get cursor data: Cursor is empty.")
else {
while (cursor.moveToNext()) {
try {
val iD = cursor.getLong(idColumn)

val song = getSongDetailsTagLib(iD)

if (song != null) audioList += song
} catch (e: Exception) {
Log.e("Cursor read", "ERR", e)
}
}
}
}
}
return audioList
}
GetSongDetails

Code: Select all

fun getSongDetailsTagLib(id: Long): Song? {
val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
val pfd = contentResolver.openFileDescriptor(uri, "r") ?: return null
val fd = pfd.detachFd()

val prop: IntArray = TagLib.getAudioProperties(fd)
val metadata: HashMap = TagLib.getMetadata(fd)

val title = metadata["TITLE"]?.firstOrNull()?.takeIf { it.isNotBlank() } ?: ""
val artist = metadata["ARTIST"]?.firstOrNull()?.takeIf { it.isNotBlank() } ?: ""
val album = metadata["ALBUM"]?.firstOrNull()?.takeIf { it.isNotBlank() } ?: ""

//etcetc

pfd.close()

return Song(
title = title,
artist = artist,
iD = id
)
}
Hauptaktivität

Code: Select all

lifecycleScope.launch(Dispatchers.IO) {
val audioList = getAudioFilesViaMediaStore()
}

Code: Select all

#include 
#include "taglib/taglib/fileref.h"
#include "taglib/taglib/tag.h"
#include 
#include 
#include "toolkit/tfilestream.h"
#include "toolkit/tstringlist.h"
#include "tpropertymap.h"

jclass g_stringClass = nullptr;

jclass g_hashMapClass = nullptr;
jmethodID g_hashMapInit = nullptr;
jmethodID g_hashMapPut = nullptr;

extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;

auto cacheClass = [env](const char *name) {
jclass tmp = env->FindClass(name);
jclass global = (jclass)env->NewGlobalRef(tmp);
env->DeleteLocalRef(tmp);
return global;
};

g_stringClass = cacheClass("java/lang/String");
g_hashMapClass = cacheClass("java/util/HashMap");

g_hashMapInit = env->GetMethodID(g_hashMapClass, "", "(I)V");
g_hashMapPut = env->GetMethodID(g_hashMapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

return JNI_VERSION_1_6;
}

jobjectArray strListToJniArray(JNIEnv *env, const TagLib::StringList &stringList) {
jobjectArray array = env->NewObjectArray(stringList.size(),g_stringClass, nullptr);

for (size_t i = 0; i < stringList.size();  ++i) {
jstring str = env->NewStringUTF(stringList[i].toCString(true));
env->SetObjectArrayElement(array, i, str);
env->DeleteLocalRef(str);
}
return array;
}

jobject propertyMapToHashMap(JNIEnv *env, const TagLib::PropertyMap &propertyMap) {
jobject map = env->NewObject(g_hashMapClass, g_hashMapInit, static_cast(propertyMap.size()));

for (const auto& [key, values]: propertyMap) {
jobjectArray valueArray = strListToJniArray(env, values);
jstring keyStr = env->NewStringUTF(key.toCString(true));

env->CallObjectMethod(map, g_hashMapPut, keyStr, valueArray);

env->DeleteLocalRef(keyStr);
env->DeleteLocalRef(valueArray);
}

return map;
}

extern "C"
JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return;

env->DeleteGlobalRef(g_stringClass);
env->DeleteGlobalRef(g_hashMapClass);
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_taglib_TagLib_getMetadata(JNIEnv *env,jobject thiz,jint fd) {
fd = dup(fd);
auto stream = std::make_unique(fd, true);
TagLib::FileRef file(stream.get(), true);

jobject propertiesMap = propertyMapToHashMap(env, file.properties());
return propertiesMap;
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_taglib_TagLib_getAudioProperties(JNIEnv* env,jobject thiz, jint fd) {
fd = dup(fd);
auto stream = std::make_unique(fd, true);
TagLib::FileRef file(stream.get(), true);

jint values[4] = {0, 0, 0, 0};

if (!file.isNull()) {
auto props = file.audioProperties();
if (props) {
values[0] = props->lengthInMilliseconds();
values[1] = props->bitrate();
values[2] = props->sampleRate();
values[3] = props->channels();
}
}

jintArray result = env->NewIntArray(4);
env->SetIntArrayRegion(result, 0, 4, values);
return result;
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post