musikr: fix broken iostream jni integration

This commit is contained in:
Alexander Capehart 2024-12-23 09:59:23 -05:00
parent 6f8a960ee1
commit 6fd0bd411b
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 40 additions and 87 deletions

View file

@ -0,0 +1 @@
-keep class org.oxycblt.musikr.metadata.NativeInputStream { *; }

View file

@ -19,3 +19,5 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keep class org.oxycblt.musikr.metadata.NativeInputStream { *; }

View file

@ -40,7 +40,6 @@ JVMInputStream::JVMInputStream(JNIEnv *env, jobject inputStream) : env(env), inp
"seekFromCurrent", "(J)V"); "seekFromCurrent", "(J)V");
inputStreamSeekFromEndMethod = env->GetMethodID(inputStreamClass, inputStreamSeekFromEndMethod = env->GetMethodID(inputStreamClass,
"seekFromEnd", "(J)V"); "seekFromEnd", "(J)V");
inputStreamClearMethod = env->GetMethodID(inputStreamClass, "clear", "()V");
inputStreamTellMethod = env->GetMethodID(inputStreamClass, "tell", "()J"); inputStreamTellMethod = env->GetMethodID(inputStreamClass, "tell", "()J");
inputStreamLengthMethod = env->GetMethodID(inputStreamClass, "length", inputStreamLengthMethod = env->GetMethodID(inputStreamClass, "length",
"()J"); "()J");
@ -107,7 +106,7 @@ void JVMInputStream::seek(TagLib::offset_t offset, Position p) {
} }
void JVMInputStream::clear() { void JVMInputStream::clear() {
env->CallVoidMethod(inputStream, inputStreamClearMethod); // Nothing to do
} }
TagLib::offset_t JVMInputStream::tell() const { TagLib::offset_t JVMInputStream::tell() const {

View file

@ -100,13 +100,11 @@ public:
private: private:
JNIEnv *env; JNIEnv *env;
jobject inputStream; jobject inputStream;
jmethodID inputStreamNameMethod;
jmethodID inputStreamReadBlockMethod; jmethodID inputStreamReadBlockMethod;
jmethodID inputStreamIsOpenMethod; jmethodID inputStreamIsOpenMethod;
jmethodID inputStreamSeekFromBeginningMethod; jmethodID inputStreamSeekFromBeginningMethod;
jmethodID inputStreamSeekFromCurrentMethod; jmethodID inputStreamSeekFromCurrentMethod;
jmethodID inputStreamSeekFromEndMethod; jmethodID inputStreamSeekFromEndMethod;
jmethodID inputStreamClearMethod;
jmethodID inputStreamTellMethod; jmethodID inputStreamTellMethod;
jmethodID inputStreamLengthMethod; jmethodID inputStreamLengthMethod;

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2024 Auxio Project
* AndroidInputStream.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.metadata
import android.content.Context
import java.io.FileInputStream
import java.nio.ByteBuffer
internal class AndroidInputStream(context: Context, fis: FileInputStream) : NativeInputStream {
private val channel = fis.channel
override fun readBlock(length: Long): ByteArray {
val buffer = ByteBuffer.allocate(length.toInt())
channel.read(buffer)
return buffer.array()
}
override fun isOpen(): Boolean {
return channel.isOpen
}
override fun seekFromBeginning(offset: Long) {
channel.position(offset)
}
override fun seekFromCurrent(offset: Long) {
channel.position(channel.position() + offset)
}
override fun seekFromEnd(offset: Long) {
channel.position(channel.size() - offset)
}
override fun clear() {
// Nothing to clear
}
override fun tell() = channel.position()
override fun length() = channel.size()
fun close() {
channel.close()
}
}

View file

@ -28,14 +28,14 @@ internal interface MetadataExtractor {
suspend fun extract(fd: ParcelFileDescriptor): Metadata? suspend fun extract(fd: ParcelFileDescriptor): Metadata?
companion object { companion object {
fun from(context: Context): MetadataExtractor = MetadataExtractorImpl(context) fun new(): MetadataExtractor = MetadataExtractorImpl
} }
} }
private class MetadataExtractorImpl(private val context: Context) : MetadataExtractor { private object MetadataExtractorImpl : MetadataExtractor {
override suspend fun extract(fd: ParcelFileDescriptor) = override suspend fun extract(fd: ParcelFileDescriptor) =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val fis = FileInputStream(fd.fileDescriptor) val fis = FileInputStream(fd.fileDescriptor)
TagLibJNI.open(context, fis).also { fis.close() } TagLibJNI.open(fis).also { fis.close() }
} }
} }

View file

@ -18,25 +18,39 @@
package org.oxycblt.musikr.metadata package org.oxycblt.musikr.metadata
/** import java.io.FileInputStream
* Java interface for the read-only methods in TagLib's IOStream API. import java.nio.ByteBuffer
*
* The vast majority of IO shim between Taglib/KTaglib should occur here to minimize JNI calls.
*/
internal interface NativeInputStream {
fun readBlock(length: Long): ByteArray
fun isOpen(): Boolean class NativeInputStream(fis: FileInputStream) {
private val channel = fis.channel
fun seekFromBeginning(offset: Long) fun readBlock(length: Long): ByteArray {
val buffer = ByteBuffer.allocate(length.toInt())
channel.read(buffer)
return buffer.array()
}
fun seekFromCurrent(offset: Long) fun isOpen(): Boolean {
return channel.isOpen
}
fun seekFromEnd(offset: Long) fun seekFromBeginning(offset: Long) {
channel.position(offset)
}
fun clear() fun seekFromCurrent(offset: Long) {
channel.position(channel.position() + offset)
}
fun tell(): Long fun seekFromEnd(offset: Long) {
channel.position(channel.size() - offset)
}
fun length(): Long fun tell() = channel.position()
fun length() = channel.size()
fun close() {
channel.close()
}
} }

View file

@ -31,12 +31,12 @@ internal object TagLibJNI {
* *
* Note: This method is blocking and should be handled as such if calling from a coroutine. * Note: This method is blocking and should be handled as such if calling from a coroutine.
*/ */
fun open(context: Context, fis: FileInputStream): Metadata? { fun open(fis: FileInputStream): Metadata? {
val inputStream = AndroidInputStream(context, fis) val inputStream = NativeInputStream(fis)
val tag = openNative(inputStream) val tag = openNative(inputStream)
inputStream.close() inputStream.close()
return tag return tag
} }
private external fun openNative(ioStream: AndroidInputStream): Metadata? private external fun openNative(ioStream: NativeInputStream): Metadata?
} }

View file

@ -48,7 +48,7 @@ internal interface ExtractStep {
fun from(context: Context, storage: Storage): ExtractStep = fun from(context: Context, storage: Storage): ExtractStep =
ExtractStepImpl( ExtractStepImpl(
context, context,
MetadataExtractor.from(context), MetadataExtractor.new(),
TagParser.new(), TagParser.new(),
storage.cache, storage.cache,
storage.storedCovers) storage.storedCovers)