You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 20:00:56