2011-07-28 57 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!

相關問題