非Java应用加载的C库回调Java方法的GStreamer实现方案咨询
跨加载实例共享Java回调函数指针的实现方案
针对你的GStreamer元数据同步场景,这里提供一种基于命名共享内存+辅助桥接库的实现方案,能让未被Java直接加载的外部C库安全获取并调用Java回调方法,且对所有DLL/Lib实例通用:
核心思路
通过一个Java可加载的小型辅助C库,将Java回调的中转函数指针写入系统级命名共享内存区域;外部DLL/Lib在需要触发回调时,从该共享内存读取指针并执行调用。共享内存采用单例初始化逻辑,确保所有加载的实例访问同一套回调数据。
具体实现步骤
1. 编写Java可加载的辅助桥接库
这个库负责创建/维护命名共享内存,并提供接口让Java注册回调逻辑:
JNI端代码(callbackbridge.c)
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <pthread.h> #ifdef _WIN32 #include <windows.h> #else #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #endif // 定义回调函数类型 typedef void (*MetadataCallback)(void*, const char*); // 共享内存存储的结构体,包含回调指针和JavaVM相关引用 typedef struct { MetadataCallback cb; pthread_mutex_t mutex; int initialized; JavaVM* jvm; jclass callbackClass; jmethodID callbackMethod; } SharedCallbackData; SharedCallbackData* sharedData; // 跨平台共享内存名称 #ifdef _WIN32 #define SHARED_MEM_NAME "Global\\GStreamerMetadataCallback" #else #define SHARED_MEM_NAME "/gstreamer_metadata_callback" #endif // 初始化共享内存 void init_shared_memory() { #ifdef _WIN32 HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(SharedCallbackData), SHARED_MEM_NAME); if (hMapFile == NULL) { perror("CreateFileMapping failed"); exit(1); } sharedData = (SharedCallbackData*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SharedCallbackData)); #else int fd = shm_open(SHARED_MEM_NAME, O_CREAT | O_RDWR, 0600); if (fd == -1) { perror("shm_open failed"); exit(1); } ftruncate(fd, sizeof(SharedCallbackData)); sharedData = (SharedCallbackData*)mmap(NULL, sizeof(SharedCallbackData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); #endif // 仅第一次初始化时设置互斥锁 if (!sharedData->initialized) { pthread_mutex_init(&sharedData->mutex, NULL); sharedData->initialized = 1; } } // JNI方法:注册Java回调 JNIEXPORT void JNICALL Java_GStreamerCallback_setNativeCallback(JNIEnv* env, jobject obj) { init_shared_memory(); // 保存JavaVM引用,用于跨线程回调 (*env)->GetJavaVM(env, &sharedData->jvm); // 创建全局类引用(避免GC回收) jclass cls = (*env)->GetObjectClass(env, obj); sharedData->callbackClass = (*env)->NewGlobalRef(env, cls); // 获取Java回调方法的ID sharedData->callbackMethod = (*env)->GetMethodID(env, cls, "onMetadataReceived", "(JLjava/lang/String;)V"); // 定义C层中转回调函数,负责调用Java方法 void c_callback(void* gstInstance, const char* metadata) { JNIEnv* callbackEnv; // 将当前线程绑定到JavaVM (*sharedData->jvm)->AttachCurrentThread(sharedData->jvm, &callbackEnv, NULL); // 转换C参数为Java类型 jstring jMetadata = (*callbackEnv)->NewStringUTF(callbackEnv, metadata); jlong jGstInstance = (jlong)gstInstance; // 调用Java回调方法 (*callbackEnv)->CallVoidMethod(callbackEnv, sharedData->callbackClass, sharedData->callbackMethod, jGstInstance, jMetadata); // 清理本地引用 (*callbackEnv)->DeleteLocalRef(callbackEnv, jMetadata); // 解绑线程 (*sharedData->jvm)->DetachCurrentThread(sharedData->jvm); } // 上锁更新回调指针 pthread_mutex_lock(&sharedData->mutex); sharedData->cb = c_callback; pthread_mutex_unlock(&sharedData->mutex); } // JNI方法:清理资源(Java退出时调用) JNIEXPORT void JNICALL Java_GStreamerCallback_cleanup(JNIEnv* env, jobject obj) { if (sharedData && sharedData->initialized) { pthread_mutex_lock(&sharedData->mutex); // 置空回调指针,防止外部DLL调用无效指针 sharedData->cb = NULL; // 释放全局引用 (*env)->DeleteGlobalRef(env, sharedData->callbackClass); pthread_mutex_unlock(&sharedData->mutex); // 销毁互斥锁 pthread_mutex_destroy(&sharedData->mutex); sharedData->initialized = 0; } }
Java端代码
public class GStreamerCallback { static { // 加载辅助桥接库 System.loadLibrary("callbackbridge"); } // 声明JNI方法 private native void setNativeCallback(); private native void cleanup(); // Java回调方法,接收GStreamer实例指针和元数据 public void onMetadataReceived(long gstInstancePtr, String metadata) { // 这里处理元数据同步逻辑 System.out.println("Received metadata for GStreamer instance: " + gstInstancePtr); System.out.println("Metadata: " + metadata); } // 初始化回调注册 public void initCallback() { setNativeCallback(); } // 程序退出时清理资源 public void onShutdown() { cleanup(); } }
2. 外部DLL/Lib读取共享内存并触发回调
外部深度学习模块的DLL需要包含相同的共享内存结构体定义,在生成元数据时读取回调指针并调用:
#include <stdio.h> #include <pthread.h> #ifdef _WIN32 #include <windows.h> #else #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #endif // 必须与桥接库中的定义完全一致 typedef void (*MetadataCallback)(void*, const char*); typedef struct { MetadataCallback cb; pthread_mutex_t mutex; int initialized; JavaVM* jvm; jclass callbackClass; jmethodID callbackMethod; } SharedCallbackData; #ifdef _WIN32 #define SHARED_MEM_NAME "Global\\GStreamerMetadataCallback" #else #define SHARED_MEM_NAME "/gstreamer_metadata_callback" #endif // 获取共享内存中的回调数据(单例模式) static SharedCallbackData* get_shared_callback_data() { static SharedCallbackData* data = NULL; if (!data) { #ifdef _WIN32 HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_MEM_NAME); if (hMapFile == NULL) { return NULL; } data = (SharedCallbackData*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SharedCallbackData)); #else int fd = shm_open(SHARED_MEM_NAME, O_RDWR, 0600); if (fd == -1) { return NULL; } data = (SharedCallbackData*)mmap(NULL, sizeof(SharedCallbackData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); #endif } return data; } // 深度学习模块生成元数据后调用此函数 void send_metadata_to_java(void* gst_instance, const char* metadata) { SharedCallbackData* data = get_shared_callback_data(); if (data && data->initialized && data->cb) { pthread_mutex_lock(&data->mutex); // 调用Java回调的中转函数 data->cb(gst_instance, metadata); pthread_mutex_unlock(&data->mutex); } }
关键安全与注意事项
- 线程安全:全程用互斥锁保护共享内存中的回调指针,避免并发读写冲突。
- 权限控制:共享内存设置严格权限(Linux下
0600,Windows下限制访问权限),防止恶意进程篡改。 - 生命周期管理:Java退出时必须调用
cleanup方法,释放全局引用并置空回调指针,避免外部DLL调用无效地址。 - 跨线程回调:GStreamer的回调线程并非Java线程,必须通过
AttachCurrentThread绑定到JavaVM,完成后Detach。 - 指针有效性检查:外部DLL调用回调前必须检查指针是否为空,防止空指针崩溃。
内容的提问来源于stack exchange,提问作者Scooter Willis
相关产品推荐
相关产品推荐

