【问题标题】:What is the best way to pass a jbyteArray from (objective-c) JNI to Java?将 jbyteArray 从(objective-c)JNI 传递到 Java 的最佳方法是什么?
【发布时间】:2011-10-15 07:52:52
【问题描述】:

我目前正在从 iSight 摄像头检索图像数据,我想将其交给 Java 进行处理。我最初尝试将数据放入 jbyteArray 并返回 jbyteArray。这对每个进程有效。再次调用本机函数将导致无效的内存访问。

由于我使用的是 Objective-c 和 Cocoa,我必须使用 JNF_COCOA_ENTER(...) 和 JNF_COCOA_EXIT(...) 函数。可悲的是,如果我不能返回 jbyteArray,因为这样做会导致 JNF_COCOA_EXIT(...) 不会被调用。建议使用直接 ByteBuffer 将数据从 JNI 域传递到 Java 域。不幸的是,我一直在使用的所有资源和参考资料并不足以让我的大脑理解这一点。如果这是一个“duh”时刻或已经被问到(我已经搜索但没有运气),我深表歉意,但是......

1) 将这些图像数据导入 Java 的最有效方法是什么?

2) 我应该如何使用 ByteBuffer 类来完成这个? (如果相关)

谢谢!

【问题讨论】:

    标签: java objective-c java-native-interface bytebuffer isight


    【解决方案1】:

    代码在这里可能会有所帮助。一旦您了解了这些宏的作用以及如何将 java 和 cocoa 关注点相互分离,这并不像您想象的那么困难。

    您需要记住的是,JNF_COCOA_ENTER 和 JNF_COCOA_EXIT 为您做了两件主要的事情:

    他们建立了一个本地范围(因此内部定义的变量在外部不可用 - 这是为了帮助您不要用您不应该接触的变量做“愚蠢”的事情)并设置一个自动释放池,所以当范围消失时,在该范围内“自动释放”的可可对象将消失。 (这是局部作用域有用/有帮助的部分原因)他们还将进行一些异常解析,以便您可以在 Java 中捕获 Cocoa 异常。

    也就是说,以下代码是合法的,您只需要非常小心地管理内存访问和数据的所有权。尽可能避免混合 Java 所有权和 Objective-C 所有权,否则让您的对象管理所有权,并在 Java 对象被 GC 时清理。

    jbyteArray bytes;
    
    JNF_COCOA_ENTER(env);   
    
    // Assign and full the bytes array here, doing any requisite transformations.
    // Remember to COPY any data out of COCOA objects, as references will be dead soon!
    
    JNF_COCOA_EXIT(env);
    
    return bytes;
    

    从 C 中使用 Java 对象很复杂,但并非不可行。尽管如此,方法调用的次数还是很重要的,并且向后跳转和第四次跳转非常耗时,因此如果您会经常调用此方法,或者时间紧迫,请尽可能坚持使用基本类型。

    如果您需要从委托将数据从 Cocoa 推送到 Java,事情会稍微复杂一些,但并非不可行。这是我管理的一个名为 QTCubed 的项目的一部分,它就是这样做的。 didOutputVideoFrame 是委托方法,该对象必须使用目标 Java 对象引用和来自 JNI 调用方法的 Java 环境进行初始化。初始化后,它会被设置为委托对象,并从相机接收更新。

    @implementation QTKitCaptureDecompressedVideoOutput
    
    - (QTKitCaptureDecompressedVideoOutput *)initWithEnv:(JNIEnv *) env javaObject:(jobject) javaObjectRef {
        [super init];
        // Save a reference to the VM
        (*env)->GetJavaVM(env,&g_vm);
        // Create a global reference to this object so we can access it later
        objectRef = (*env)->NewGlobalRef(env,javaObjectRef);
    
        return self;
    }
    
    - (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection {
        // Move into Java to deliver the data
        JNIEnv *env;
        (*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL);
    
        void * rawData = [sampleBuffer bytesForAllSamples];
        int length = [sampleBuffer lengthForAllSamples];
        QTFormatDescription * formatDescription = [sampleBuffer formatDescription];
        QTTime duration = [sampleBuffer duration];
    
        float frameDuration = duration.timeValue/duration.timeScale;
        float fps = 1/frameDuration;
    
        jint format = [formatDescription formatType];
        NSValue * pixelSize = [formatDescription attributeForKey:QTFormatDescriptionVideoEncodedPixelsSizeAttribute];
        NSSize size = [pixelSize sizeValue];
        jint width = size.width;
        jint height = size.height;
        //NSLog(@"Outputting frame sized %d x %d of length %d with format: %#x",width,height,length,format);
    
        switch (format) {
                // 8 bit codecs
            case kCVPixelFormatType_1Monochrome:
            case kCVPixelFormatType_2Indexed:
            case kCVPixelFormatType_4Indexed:
            case kCVPixelFormatType_8Indexed:
            case kCVPixelFormatType_1IndexedGray_WhiteIsZero:
            case kCVPixelFormatType_2IndexedGray_WhiteIsZero:
            case kCVPixelFormatType_4IndexedGray_WhiteIsZero:
            case kCVPixelFormatType_8IndexedGray_WhiteIsZero:
            case kCVPixelFormatType_422YpCbCr8:
            case kCVPixelFormatType_4444YpCbCrA8:
            case kCVPixelFormatType_4444YpCbCrA8R:
            case kCVPixelFormatType_444YpCbCr8:
            case kCVPixelFormatType_420YpCbCr8Planar:
            case kCVPixelFormatType_422YpCbCr_4A_8BiPlanar:
            case kCVPixelFormatType_24RGB:
            case kCVPixelFormatType_24BGR:
            default:
            {
                // Re-use the existing array if possible
                if (byteFrameData == nil || (*env)->GetArrayLength(env,byteFrameData) < length) {
                    // Clean up the previously allocated global reference
                    if (byteFrameData != nil) {
                        (*env)->DeleteGlobalRef(env,byteFrameData);
                        byteFrameData = nil;
                    }
                    // Create an appropriately sized byte array to hold the data
                    byteFrameData = (*env)->NewGlobalRef(env,(*env)->NewByteArray(env,length));
                }
                if (byteFrameData) {
                    // Copy the raw data into the byteArray
                    (*env)->SetByteArrayRegion(env,byteFrameData,0,length,rawData);
    
                    // Get the class reference for our object
                    jclass classRef = (*env)->GetObjectClass(env,objectRef);
                    // Get the pushFrame methodId
                    jmethodID methodId = (*env)->GetMethodID(env,classRef,"pushFrame","([BIIIF)V");
                    // Call pushFrame with the byte array
                    (*env)->CallVoidMethod(env,objectRef,methodId,byteFrameData,format,width,height,fps);
                }
                break;
            }   
                // 16 bit (short) storage of values
            case kCVPixelFormatType_16BE555:
            case kCVPixelFormatType_16LE555:
            case kCVPixelFormatType_16LE5551:
            case kCVPixelFormatType_16BE565:
            case kCVPixelFormatType_16LE565:
            case kCVPixelFormatType_16Gray:
            case kCVPixelFormatType_422YpCbCr16:
            case kCVPixelFormatType_422YpCbCr10:
            case kCVPixelFormatType_444YpCbCr10:
            {
                // Re-use the existing array if possible
                if (shortFrameData == nil || (*env)->GetArrayLength(env,shortFrameData) < length/2) {
                    // Clean up the previously allocated global reference
                    if (shortFrameData != nil) {
                        (*env)->DeleteGlobalRef(env,shortFrameData);
                        shortFrameData = nil;
                    }
                    // Create an appropriately sized byte array to hold the data
                    shortFrameData = (*env)->NewGlobalRef(env,(*env)->NewShortArray(env,length/2));
                }
                if (shortFrameData) {
                    // Copy the raw data into the byteArray
                    (*env)->SetShortArrayRegion(env,shortFrameData,0,length/2,rawData);
    
                    // Get the class reference for our object
                    jclass classRef = (*env)->GetObjectClass(env,objectRef);
                    // Get the pushFrame methodId
                    jmethodID methodId = (*env)->GetMethodID(env,classRef,"pushFrame","([SIIIF)V");
                    // Call pushFrame with the short array
                    (*env)->CallVoidMethod(env,objectRef,methodId,shortFrameData,format,width,height,fps);          
                }
                break;
            }   
                // 32 bit (int) storage of values
            case kCVPixelFormatType_32ABGR:
            case kCVPixelFormatType_32AlphaGray:
            case kCVPixelFormatType_32ARGB:
            case kCVPixelFormatType_32BGRA:
            case kCVPixelFormatType_32RGBA:
            {
                // Re-use the existing array if possible
                if (intFrameData == nil || (*env)->GetArrayLength(env,intFrameData) < length/4) {
                    // Clean up the previously allocated global reference
                    if (intFrameData != nil) {
                        (*env)->DeleteGlobalRef(env,intFrameData);
                        intFrameData = nil;
                    }
                    // Create an appropriately sized byte array to hold the data
                    intFrameData = (*env)->NewGlobalRef(env,(*env)->NewIntArray(env,length/4));
                }
                if (intFrameData) {
                    // Copy the raw data into the byteArray
                    (*env)->SetByteArrayRegion(env,intFrameData,0,length/4,rawData);
    
                    // Get the class reference for our object
                    jclass classRef = (*env)->GetObjectClass(env,objectRef);
                    // Get the pushFrame methodId
                    jmethodID methodId = (*env)->GetMethodID(env,classRef,"pushFrame","([IIIIF)V");
                    // Call pushFrame with the int array
                    (*env)->CallVoidMethod(env,objectRef,methodId,intFrameData,format,width,height,fps);
                }
                break;
            }
        }
    
        // Detatch from Java
        (*g_vm)->DetachCurrentThread (g_vm);
    }
    
    /* Clean up java references so they can be properly GCed in java */
    - (void) dealloc {
    
        // Attach to java so we can release references
        JNIEnv *env;
        (*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL);
    
        // release the references we hold
    
        if (objectRef != nil) {
            (*env)->DeleteGlobalRef(env,objectRef);
            objectRef = nil;        
        }
        if (byteFrameData != nil) {
            (*env)->DeleteGlobalRef(env,byteFrameData);
            byteFrameData = nil;        
        }
        if (shortFrameData != nil) {
            (*env)->DeleteGlobalRef(env,shortFrameData);
            shortFrameData = nil;       
        }
        if (intFrameData != nil) {
            (*env)->DeleteGlobalRef(env,intFrameData);
            intFrameData = nil;     
        }
    
        // Detatch from Java
        (*g_vm)->DetachCurrentThread (g_vm);
    
        g_vm = nil;
    
        [super dealloc];
    }
    
    @end
    

    这是传入的 java 对象引用在 java 端的方法签名:

    protected void pushFrame(byte[] frameData, int formatInt, int width, int height, float frameRate);
    protected void pushFrame(short[] frameData, int formatInt, int width, int height, float frameRate);
    protected void pushFrame(int[] frameData, int formatInt, int width, int height, float frameRate);
    

    一旦你有一个正在运行的应用程序,使用工具来确保你正确地处理引用!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-06
      • 1970-01-01
      • 2013-12-29
      • 1970-01-01
      • 2011-09-08
      • 2019-01-30
      • 1970-01-01
      • 2010-09-06
      相关资源
      最近更新 更多