繁体   English   中英

将(objective-c)JNI中的jbyteArray传递给Java的最佳方法是什么?

[英]What is the best way to pass a jbyteArray from (objective-c) JNI to Java?

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

由于我正在使用objective-c和Cocoa,我必须使用JNF_COCOA_ENTER(...)和JNF_COCOA_EXIT(...)函数。 可悲的是,如果我不能返回jbyteArray,因为这样做会导致JNF_COCOA_EXIT(...)没有被调用。 有人建议使用直接ByteBuffer将数据从JNI land传递给java land。 不幸的是,我一直在使用的所有资源和参考资料都没有直截了当地概括我的大脑来理解。 如果这是一个“呃”的时刻或者已经被问到(我没有运气的搜索),我深表歉意,但......

1)将此图像数据引入Java的最有效方法是什么?

2)我应该如何使用ByteBuffer类来实现这一目标? (如果相关)

谢谢!

代码在这里可能会有帮助。 一旦你了解这些宏做了什么,以及如何将java和cocoa问题彼此分开,它就不像你想象的那么困难。

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

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

也就是说,以下代码是法律的,您只需要非常小心地管理内存访问和数据所有权。 尽可能避免混合使用Java所有权和Objective-C所有权,或者让对象管理所有权,并在使用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);

一旦你有一个正在运行的应用程序,使用仪器,以确保你正确处理参考!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM