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
# hide the original source file name.
#-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");
inputStreamSeekFromEndMethod = env->GetMethodID(inputStreamClass,
"seekFromEnd", "(J)V");
inputStreamClearMethod = env->GetMethodID(inputStreamClass, "clear", "()V");
inputStreamTellMethod = env->GetMethodID(inputStreamClass, "tell", "()J");
inputStreamLengthMethod = env->GetMethodID(inputStreamClass, "length",
"()J");
@ -107,7 +106,7 @@ void JVMInputStream::seek(TagLib::offset_t offset, Position p) {
}
void JVMInputStream::clear() {
env->CallVoidMethod(inputStream, inputStreamClearMethod);
// Nothing to do
}
TagLib::offset_t JVMInputStream::tell() const {

View file

@ -100,13 +100,11 @@ public:
private:
JNIEnv *env;
jobject inputStream;
jmethodID inputStreamNameMethod;
jmethodID inputStreamReadBlockMethod;
jmethodID inputStreamIsOpenMethod;
jmethodID inputStreamSeekFromBeginningMethod;
jmethodID inputStreamSeekFromCurrentMethod;
jmethodID inputStreamSeekFromEndMethod;
jmethodID inputStreamClearMethod;
jmethodID inputStreamTellMethod;
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?
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) =
withContext(Dispatchers.IO) {
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
/**
* Java interface for the read-only methods in TagLib's IOStream API.
*
* The vast majority of IO shim between Taglib/KTaglib should occur here to minimize JNI calls.
*/
internal interface NativeInputStream {
fun readBlock(length: Long): ByteArray
import java.io.FileInputStream
import java.nio.ByteBuffer
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.
*/
fun open(context: Context, fis: FileInputStream): Metadata? {
val inputStream = AndroidInputStream(context, fis)
fun open(fis: FileInputStream): Metadata? {
val inputStream = NativeInputStream(fis)
val tag = openNative(inputStream)
inputStream.close()
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 =
ExtractStepImpl(
context,
MetadataExtractor.from(context),
MetadataExtractor.new(),
TagParser.new(),
storage.cache,
storage.storedCovers)