0

現在、iSightカメラから画像データを取得しています。Javaに渡して処理したいと思います。私はもともとデータをjbyteArrayに入れて、jbyteArrayを返そうとしました。これは、プロセスごとに1回機能します。ネイティブ関数を2回呼び出すと、無効なメモリアクセスが発生します。

私はobjective-cとCocoaを使用しているので、JNF_COCOA_ENTER(...)関数とJNF_COCOA_EXIT(...)関数を使用する必要があります。残念ながら、jbyteArrayを返すことができない場合、そうするとJNF_COCOA_EXIT(...)が呼び出されなくなります。JNIランドからjavaランドにデータを渡すために直接ByteBufferを使用することが提案されました。残念ながら、私が使用しているすべてのリソースとリファレンスは、私の脳が理解できるほど簡単にこれを概説していません。これが「当たり前の」瞬間なのか、すでに質問されているのか(運が悪か​​ったので検索しました)、深くお詫び申し上げますが...

1)この画像データをJavaに取り込む最も効率的な方法は何ですか?

2)これを実現するには、ByteBufferクラスをどのように使用する必要がありますか?(該当する場合)

ありがとう!

4

1 に答える 1

2

ここでコードが役立つ場合があります。これらのマクロが何をするのか、そしてJavaとココアの懸念を互いに分離する方法を理解したら、想像するほど難しくはありません。

覚えておく必要があるのは、JNF_COCOA_ENTERとJNF_COCOA_EXITが2つの主要なことを行うということです。

それらはローカルスコープを確立し(したがって、内部で定義された変数は外部では利用できません-これは、触れてはいけない変数で「ばかげた」ことをしないようにするためです)、自動解放プールを設定するため、ココアオブジェクトはそのスコープ内の「自動解放」は、スコープが消えると消えます。(これは、ローカルスコープが役立つ/役立つ理由の一部です)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オブジェクトの使用は複雑ですが、実行不可能ではありません。それでも、メソッド呼び出しの数は重要であり、ジャンプバックと4番目のジャンプには時間がかかるため、このメソッドを頻繁に呼び出す場合、または時間が重要な場合は、可能な限り基本タイプを使用してください。

デリゲートから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);

アプリケーションを実行したら、機器を使用して、参照を適切に破棄していることを確認してください。

于 2011-08-18T03:31:19.020 に答える