gpt4 book ai didi

Android使用没有静态变量的JNI

转载 作者:搜寻专家 更新时间:2023-11-01 09:01:00 24 4
gpt4 key购买 nike

我使用 MediaMetadataRetriever.java 的源代码作为基础创建了我自己的 Android MediaMetadataRetriever 版本。我的版本使用 FFmpeg 检索元数据。这种方法有效,但它依赖于 C 代码中的静态变量来保留 JNI 调用之间的状态。这意味着我一次只能使用此类的一个实例,否则状态可能会损坏。两个Java函数定义如下:

public class MediaMetadataRetriever
{
static {
System.loadLibrary("metadata_retriever_jni");
}

public MediaMetadataRetriever() {

}

public native void setDataSource(String path) throws IllegalArgumentException;
public native String extractMetadata(String key);

对应的C(JNI)代码代码为:

const char *TAG = "Java_com_example_metadataexample_MediaMetadataRetriever";
static AVFormatContext *pFormatCtx = NULL;

JNIEXPORT void JNICALL
Java_com_example_metadataexample_MediaMetadataRetriever_setDataSource(JNIEnv *env, jclass obj, jstring jpath) {

if (pFormatCtx) {
avformat_close_input(&pFormatCtx);
}

char duration[30] = "0";
const char *uri;

uri = (*env)->GetStringUTFChars(env, jpath, NULL);

if (avformat_open_input(&pFormatCtx, uri, NULL, NULL) != 0) {
__android_log_write(ANDROID_LOG_INFO, TAG, "Metadata could not be retrieved");
(*env)->ReleaseStringUTFChars(env, jpath, uri);
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}

if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
__android_log_write(ANDROID_LOG_INFO, TAG, "Metadata could not be retrieved");
avformat_close_input(&pFormatCtx);
(*env)->ReleaseStringUTFChars(env, jpath, uri);
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}

(*env)->ReleaseStringUTFChars(env, jpath, uri);
}

JNIEXPORT jstring JNICALL
Java_com_example_metadataexample_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jclass obj, jstring jkey) {

const char *key;
jstring value = NULL;

key = (*env)->GetStringUTFChars(env, jkey, NULL) ;

if (!pFormatCtx) {
goto fail;
}

if (key) {
if (av_dict_get(pFormatCtx->metadata, key, NULL, AV_DICT_IGNORE_SUFFIX)) {
value = (*env)->NewStringUTF(env, av_dict_get(pFormatCtx->metadata, key, NULL, AV_DICT_IGNORE_SUFFIX)->value);
}
}

fail:
(*env)->ReleaseStringUTFChars(env, jkey, key);

return value;
}

概述我的问题的示例用法是:

MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource("one.mp3");

MediaMetadataRetriever mmr2 = new MediaMetadataRetriever();
// This line resets the data source to two.mp3
mmr2.setDataSource("two.mp3");

// should retrieve the artist from one.mp3 but retrieves it from two.mp3 due to the static
// variable being reset in the previous statement
String artist = mmr.extractMetadata(MediaMetadataRetriever.ARTIST);

有人可以解释一下我将如何构建这段代码,以便我可以使用 MediaMetadataRetriever 的多个实例而不会相互干扰吗?我不想将代码切换到 C++,而且我很确定我不需要修改 MediaMetadataRetriever.java,因为这段代码是从 Android 框架逐行获取的(它允许多个实例,请参见下面的示例).看来我需要重新构造 C 代码,但我不确定如何在不使用静态变量的情况下跨 JNI 调用保留状态。提前致谢。

File file1 = new File(Environment.getExternalStorageDirectory(), "Music/one.mp3");
File file2 = new File(Environment.getExternalStorageDirectory(), "Music/two.mp3");

android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever();
mmr.setDataSource(file1.toString());

android.media.MediaMetadataRetriever mmr2 = new android.media.MediaMetadataRetriever();
mmr2.setDataSource(file2.toString());

// Returns the artist of one.mp3, not two.mp3, as expected. This is the expected behavior
// and confirms that multiple instances of MediaMetadataRetriever can be used simultaneously
mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_ARTIST));

最佳答案

重组代码会非常简单。您无需使用 pFormatCtx,只需扩展 JNI 调用的接口(interface)即可传递之前存储在 pFormatCtx 中的指针。现在最大的问题是如何在 java 不知道这种数据类型的情况下传递指针?最直接的解决方案是使用 ints(对于 32 位系统)或 longs(对于 64 位系统)来传递指针到 Java 环境和从 Java 环境传出。不幸的是,一旦您在库的 64 位和 32 位版本之间切换,您可能会遇到麻烦。

当我几个月前试图解决这个问题时,我偶然发现了一篇 Clebert Suconic 的文章.他指出了一种非常优雅的方法,可以通过 JNI 安全地传递指针,而无需使用类型转换来“破解”。相反,他建议使用 java.nio.ByteBuffer

简而言之,这个概念是:他建议创建一个长度为零的新 ByteBuffer 对象:env->NewDirectByteBuffer(myPointer, 0); 并将生成的 jobject 通过 JNI 传回和

调用 env->NewDirectByteBuffer(myPointer, 0); 创建一个不可变的字节缓冲区对象,指向您想要传递的位置。缓冲区是不可变的这一事实是完美的,因为您不想修改内存位置,您只想存储位置本身。您得到的是一个封装了您的指针的对象,您可以将指针大小问题留给 JVM。

编辑: 只是为了完整性:稍后可以调用 env->GetDirectBufferAddress(myPointer); 检索指针。

关于Android使用没有静态变量的JNI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14968547/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com