2011-07-28 56 views
0

我目前正在从iSight相机中检索图像数据,我想将它交给Java进行处理。我最初试图把数据放在jbyteArray中并返回jbyteArray。这在每个过程中都有效。第二次调用本地函数将导致无效的内存访问。将jbyteArray从(objective-c)JNI传递给Java的最佳方式是什么?

因为我正在使用objective-c和Cocoa,我必须使用JNF_COCOA_ENTER(...)和JNF_COCOA_EXIT(...)函数。可悲的是,如果我不能返回jbyteArray,因为这样会导致JNF_COCOA_EXIT(...)没有被调用。有人建议使用直接的ByteBuffer将数据从JNI land传送到java land。不幸的是,我一直使用的所有资源和参考资料都没有简单描述这些足以让我的大脑理解的内容。我深表歉意,如果这是一个“嗯”的时刻,或已经被问到(我没有搜索到运气),但...

1)什么是最有效的方式来将此图像数据到Java?

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

谢谢!

回答

2

此处的代码可能会有帮助。一旦你了解了这些宏的用途,以及如何区分java和可可关注点,这并不像想象的那么困难。

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

他们建立一个本地范围内(所以里面定义的变量不可外 - 这是帮你不要做“哑巴”有变量的东西你不应该触及),并且设置一个自动释放池,所以当范围消失时,在该范围内的“自动释放”的可可对象将会消失。 (这是本地范围为什么有用/有用的原因之一)他们还会执行一些异常分析,以便您可以捕获Java中的Cocoa异常。

也就是说,下面的代码是LEGAL,你只需要非常小心地管理内存访问和数据所有权。如果可能,避免混合使用Java所有权和Objective-C所有权,否则让对象管理所有权,并在java对象被GCed时清理。

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 Object Reference和JNI调用的方法来初始化此对象。一旦初始化,它就会被设置为委托对象,并从相机接收更新。

@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); 

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

相关问题