musikr: fix broken iostream jni integration
This commit is contained in:
parent
6f8a960ee1
commit
6fd0bd411b
9 changed files with 40 additions and 87 deletions
|
@ -0,0 +1 @@
|
||||||
|
-keep class org.oxycblt.musikr.metadata.NativeInputStream { *; }
|
4
musikr/proguard-rules.pro
vendored
4
musikr/proguard-rules.pro
vendored
|
@ -18,4 +18,6 @@
|
||||||
|
|
||||||
# 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 { *; }
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue