gpt4 book ai didi

java - SWIG (Java) : How do I pass a struct with callback functions to C++ from an Android application?

转载 作者:行者123 更新时间:2023-12-02 10:27:42 27 4
gpt4 key购买 nike

我正在使用基于 WebRTC 的 C++ 代码库为 Android 开发实时通信应用程序(视频和音频)。我使用 SWIG 生成了一个 JNI 桥,以从 Java 访问 native 代码。调用的行为是通过在应用程序层中定义并在结构中传递给库代码的许多回调函数确定的。传递这些回调的函数如下所示:void registerCallbacks(CALL_CALLBACKS* callbacks);在哪里 CALL_CALLBACKS是一个包含许多回调函数的结构,如下所示:

struct CALL_CALLBACKS {
// CALL_STATE is an Enum
// Called whenever the state of the call changes
void (*statusCallback)(CALL_STATE);

// FRAME is a struct representing a video frame
// Called whenever the other participant sends a video frame, typically at 30 fps
void (*frameCallback)(const FRAME*);

// ...
}
问题是当我让 SWIG 默认做它的事情时,结果是不可用的。它生成一个java类型 CALL_CALLBACKS其中包含每个回调的 setter 和 getter,它们也是 SWIG 生成的类型。但是,这些类型的命名方式为 SWIGTYPE_p_f_ENUM_CALL_STATUS__void。并且只是 C 指针的包装器。
如何编写我的 SWIG 接口(interface)文件以便将回调(最好使用较少荒谬的名称)传递给 C++ 库?我认为可以以某种方式使用类型映射,但我无法理解如何做到这一点。

最佳答案

我认为 SWIG 自己无法做到这一点。以下是我在普通 JNI 中的做法:
首先,创建一个可以在 Java 端实现的 Java 接口(interface)。

interface Callbacks {
void statusCallback(int status);
void frameCallback(Frame frame);
static native void registerCallbacks(Callbacks cb);
}
接下来,创建将 C++ 参数转发到 jobject g_receiver 的函数。实现接口(interface):
jobject g_receiver;
void my_statusCallback(CALL_STATE s) {
if (!g_receiver) {
// Print a warning?
return;
}

JNIEnv *env = getEnv();
env->PushLocalFrame(10);

jclass cls_Callbacks = env->GetObjectClass(g_receiver);
jmethodID mid_Callbacks_statusCallback = env->GetMethodID(cls_Callbacks, "statusCallback", "(I)V");
env->CallVoidMethod(g_receiver, mid_Callbacks_statusCallback, s);
env->PopLocalFrame(nullptr);
}

void my_frameCallback(const FRAME* frame) {
if (!g_receiver) {
// Print a warning?
return;
}

JNIEnv *env = getEnv();
env->PushLocalFrame(10);

// Create a Frame object from the C++ pointer.
// See Proxy classes at http://swig.org/Doc4.0/Java.html#Java_imclass
jclass cls_Frame = env->FindClass("Frame");
jmethodID ctr_Frame = env->GetMethodID(cls_Frame, "<init>", "(JZ)V");
jobject jFrame = env->NewObject(cls_Frame, ctr_Frame, (jlong) frame, (jboolean)false);
jmethodID mid_Frame_delete = env->GetMethodID(cls_Frame, "delete", "(LFrame;)V");

jclass cls_Callbacks = env->GetObjectClass(g_receiver);
jmethodID mid_Callbacks_frameCallback = env->GetMethodID(cls_Callbacks, "frameCallback", "(LFrame;)V");
env->CallVoidMethod(g_receiver, mid_Callbacks_frameCallback, jFrame);

env->CallVoidMethod(jFrame, mid_Frame_delete); // Disconnect the Java Frame object from the C++ FRAME object.
env->PopLocalFrame(nullptr);
}

CALL_CALLBACKS global_callbacks = { my_statusCallback, my_frameCallback };
最后可以实现 Callbacks#registerCallbacks如下。这有点棘手,因为您必须确保 g_receiver 是 nullptr 或有效的全局引用:
JNIEXPORT void Java_Callbacks_registerCallbacks(JNIEnv *env, jclass cls_Callbacks, jobject receiver) {
if (g_receiver) {
env->DeleteGlobalRef(g_receiver);
}

g_receiver = receiver ? env->NewGlobalRef(receiver) : nullptr;
registerCallbacks(global_callbacks);
}
我做了一些假设:
  • 生成代码时您没有使用任何包。这将影响 JNI 方法名称和对签名中类的任何引用。
  • 我假设您的 native 代码在不同的线程上运行,所以 getEnv函数应该使用 JNI 调用 API 来附加当前线程 as a daemon thread .您可以将指针存储在另一个全局变量中。
  • 由于您使用的是Android,您只能调用FindClass从主线程。您可以通过在 JNI_Onload 中创建对类的全局引用来解决此问题。方法或通过添加 Class<Frame> getFrameClass() 来绕过它Callbacks 的方法界面。或者您可以通过执行 g_receiver.getClass().getClassLoader().findClass("Frame") .
  • 您没有指定 frameCallback需要释放 FRAME 对象本身。我的代码假定它没有并断开 Java 对象,因此您不会在回调结束后意外使用它。
  • 您没有指定是否为原生 registerCallbacks可以多次调用,所以我认为确实如此。您也可以调用registerCallbacks作为JNI_Onload 的一部分.
  • 关于java - SWIG (Java) : How do I pass a struct with callback functions to C++ from an Android application?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63581654/

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