Android Camera2 JNI 图像处理 - Surface 未从 JNI 解锁?

Android Camera2 JNI Image Processing - Surface not being unlocked from JNI?

我遵循了 Camera2Video 示例,还实现了一些 JNI 代码以在属于 SurfaceTexture 的 Surface 上进行一些图像处理,以便在屏幕上进行预览。这工作正常但是当我点击记录按钮时它无法记录因为我从 CameraCaptureSession.StateCallback().

得到一个 onConfigureFailed()

我的主要JNI函数在这里-

extern "C"
jstring
Java_com_example_android_camera2video_Camera2VideoFragment_someImageProcessingJNI(
        JNIEnv *env,
        jobject, /* this */
        jint srcWidth,
        jint srcHeight,
        jobject y_srcBuffer,
        jobject u_srcBuffer,
        jobject v_srcBuffer,
        jobject dstSurface) {

    // Get pointers to the 3 planes (YUV) of the source (Image from ImageReader).

    uint8_t *srcLumaPtr = reinterpret_cast<uint8_t *>(env->GetDirectBufferAddress(y_srcBuffer));
    if (srcLumaPtr == NULL) {
        return NULL;
    }

    uint8_t *srcUPtr = reinterpret_cast<uint8_t *>(env->GetDirectBufferAddress(u_srcBuffer));
    if (srcUPtr == NULL) {
        return NULL;
    }

    uint8_t *srcVPtr = reinterpret_cast<uint8_t *>(env->GetDirectBufferAddress(v_srcBuffer));
    if (srcVPtr == NULL) {
        return NULL;
    }

    // Destination setup.
    // YUV - NV21

    int dstWidth;
    int dstHeight;

    // Get a ptr to the destination window - don't forget to release it.
    ANativeWindow *dstWin = ANativeWindow_fromSurface(env, dstSurface);

    // Acquire a reference on the given ANativeWindow object. This prevents the object from being
    // deleted until the reference is removed.
    ANativeWindow_acquire(dstWin);

    // Swapping due to the rotation.
    dstWidth = srcHeight;
    dstHeight = srcWidth;

    // Change the format and size of the window buffers.
    int32_t setBufferGeometryRet = ANativeWindow_setBuffersGeometry(dstWin, dstWidth, dstHeight,
                                                                    0 /*format unchanged*/);

    // Struct that represents a windows buffer.
    ANativeWindow_Buffer dstBuf;

    // Lock the window's next drawing surface for writing.
    if (int32_t err = ANativeWindow_lock(dstWin, &dstBuf, NULL)) {
        ANativeWindow_release(dstWin);
        return NULL;
    }

    // uint8_t* dstLumaPtr = reinterpret_cast<uint8_t*>(dstBuf.bits);
    // If we want to write as 32-bit.
    uint32_t *dstLumaPtr = reinterpret_cast<uint32_t *>(dstBuf.bits);
    uint32_t *dstLumaPtr_orig = dstLumaPtr;

    // Buffer
    int32_t ANativeWindow_Buffer_Width = dstBuf.width;
    int32_t ANativeWindow_Buffer_Height = dstBuf.height;
    int32_t ANativeWindow_Buffer_Stride = dstBuf.stride;

    // Window
    int32_t dstNativeWinWidth = ANativeWindow_getWidth(dstWin);
    int32_t dstNativeWinHeight = ANativeWindow_getHeight(dstWin);
    int32_t dstNativeWinFormat = ANativeWindow_getFormat(dstWin);

    // Read from the YUV source which needs the 90 deg clockwise rotation.
    for (int srcCol = 0; srcCol < srcWidth; srcCol++) {
        for (int srcRow = srcHeight - 1; srcRow >= 0; srcRow--) {

            // packRGBA() just converts YUV to RGB.
            *dstLumaPtr = packRGBA(srcLumaPtr[srcRow * srcWidth + srcCol],
                                   srcUPtr[((srcRow / 2) * srcWidth) + (srcCol - (srcCol % 2))],
                                   srcVPtr[((srcRow / 2) * srcWidth) + (srcCol - (srcCol % 2))]);
            dstLumaPtr++;

            // We cannot simple write to destination pixels sequentially because of the
            // stride. Stride is the actual memory buffer width, while the image width is only
            // the wdith of valid pixels.
            // If we reach the end of a source row, we need to advance our destination
            // pointer to skip the padding cells.
            if (srcRow == 0)
                dstLumaPtr += (ANativeWindow_Buffer_Stride - ANativeWindow_Buffer_Width);

        }
    }

    // ----------------------------------------------------------------------
    // Some image processing done here...
    // --------------------------------------------------------------------

    ANativeWindow_unlockAndPost(dstWin);
    ANativeWindow_release(dstWin);

    return env->NewStringUTF("Return from JNI");
}

现在,如果我删除对 JNI 函数的调用,录制按钮就会起作用。这让我相信我没有在此 JNI 代码中正确发布 Surface,但我不确定如何继续解决这个问题。

非常感谢您的任何建议。


编辑 - 我在日志中收到以下消息。

10-11 10:15:41.749 8736-8736/com.example.android.camera2video E/BufferQueueProducer: [SurfaceTexture-0-8736-0] 连接:已连接(cur=2 req =4)

好吧,我发现预览界面还在用。我通过删除在 onStartRecording().

的 Java 侧添加的另一个预览表面变量来解决此问题。