【问题标题】:Conversion of Java code to C -> call by JNI -> program stops runningJava 代码到 C 的转换 -> 由 JNI 调用 -> 程序停止运行
【发布时间】:2013-05-06 20:23:59
【问题描述】:

我尝试转换此 Java 代码:

// http://www.stanford.edu/class/ee368/Android/index.html  
// Source: http://www.stanford.edu/class/ee368/Android/HelloViewfinder/Project.zip
private void decodeYUV420RGB(int[] rgb, byte[] yuv420sp, int width, int height) {
     Convert YUV to RGB
    final int frameSize = width * height;
    for (int j = 0, yp = 0; j < height; j++) {
        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
        for (int i = 0; i < width; i++, yp++) {
            int y = (0xff & ((int) yuv420sp[yp])) - 16;
            if (y < 0) y = 0;
            if ((i & 1) == 0) {
                v = (0xff & yuv420sp[uvp++]) - 128;
                u = (0xff & yuv420sp[uvp++]) - 128;
            }

            int y1192 = 1192 * y;
            int r = (y1192 + 1634 * v);
            int g = (y1192 - 833 * v - 400 * u);
            int b = (y1192 + 2066 * u);

            if (r < 0) r = 0; else if (r > 262143) r = 262143;
            if (g < 0) g = 0; else if (g > 262143) g = 262143;
            if (b < 0) b = 0; else if (b > 262143) b = 262143;

            rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
        }
    }
}

这样称呼的:

//byte[] mYUVData; int[] mRGBData;
decodeYUV420RGB(mRGBData, mYUVData, mImageWidth, mImageHeight);

到这个 C 代码:

#include <string.h>
#include <jni.h>

jint*
Java_com_camera_DrawOnTop_decodeYUV420RGB565(JNIEnv* env, jobject  thiz, jintArray rgb, jbyteArray yuv420sp, jint width, jint height)
{
 jbyte* yuv420spc = (*env)->GetByteArrayElements(env, yuv420sp, NULL);
 jint*  rgbc      = (*env)->GetIntArrayElements(env, rgb, NULL);

    int frameSize = width * height;
    int j;
    int i;
    int yp;
    for (j = 0, yp = 0; j < height; j++) {
        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
        for (i = 0; i < width; i++, yp++) {
            int y = (0xff & ((int) yuv420spc[yp])) - 16;
            if (y < 0) y = 0;
            if ((i & 1) == 0) {
                v = (0xff & yuv420spc[uvp++]) - 128;
                u = (0xff & yuv420spc[uvp++]) - 128;
            }

            int y1192 = 1192 * y;
            int r = (y1192 + 1634 * v);
            int g = (y1192 - 833 * v - 400 * u);
            int b = (y1192 + 2066 * u);

            if (r < 0) r = 0; else if (r > 262143) r = 262143;
            if (g < 0) g = 0; else if (g > 262143) g = 262143;
            if (b < 0) b = 0; else if (b > 262143) b = 262143;

            rgbc[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
        }
    }

(*env)->ReleaseByteArrayElements(env, yuv420sp, yuv420spc, 0 );
(*env)->ReleaseIntArrayElements(env, rgb, rgbc, 0 );
return rgbc;

}

并通过 JNI 调用它:

//int[] mRGBData; int [] tmpData = {1,2,3};
mRGBData = decodeYUV420RGB565(tmpData, mYUVData, mImageWidth, mImageHeight);

但程序在上述调用后中断运行。 我现在不知道如何使用 JNI 进行引用调用,所以我使用了 tmpData 只是有数据,但通过等号将真实数据返回给mRGBData。

我的 C 代码出了什么问题,所以它在运行时中断?

我有什么要改变的,以便它与参考一起工作 原代码(不带等号)?

【问题讨论】:

    标签: java c java-native-interface converter


    【解决方案1】:

    您应该为您的 JNI 函数提供一个大小正确的 RGB 数组(即 w*h),以及一个大小和结构正确的 YUV 数组(w*h*3 /2w*h luma 字节 (Y),后跟 (w/2)*( h/2)chromaUV)字节。如果您提供 rgb,调用将崩溃 大小为 3 的数组,如您的 sn-p 中。

    另请注意,您正在构建一个 rgb565 数组。它的元素可能是 short 类型(16 位)而不是 int 类型(32 位)。

    【讨论】:

    • Android 的原始代码在 onPreviewFrame(byte[] data, Camera camera) 中获取相机预览并设置 mYUVData = new byte[data.length] 和 mRGBData = new int[previewWidth * previewHeight] 和作品。所以我目前只想将 Java 更改为 C 方法,因为我不想“更改正在运行的系统”(只是让它更快)。
    • @KWT:我真的不确定您将 Java 代码直接翻译成 C 是否会使代码更快。 Dlavik 有足够的 JIT 优化来使 Java 代码高效执行,而 JNI 引入了一些开销。因此,我建议您衡量两种方法的性能,并做出明智的选择。
    • @KWT:您可能会寻找非常高效的 yuv2rgb 汇编器实现,特别是如果您的目标设备支持 NEON 指令:wss.co.uk/pinknoise/yuv2rgb
    【解决方案2】:

    GetByteArrayElements() 在 C 端创建一个数组,您填充该数组,然后释放,然后返回到 Java,它获取指向已释放内存的指针。如果您想真正将数据传送到 Java 端,您需要创建一个新的 Java 数组对象并将其返回。或者,在 Java 端创建新对象并将其传递给 C 代码进行修改。我发现后者通常更容易。我一般会在Java端创建一个DirectByteBuffer,然后传给native函数,让native函数调用GetDirectBufferAddress并写入。

    例如,看我的 PD ojrandlib JNI 代码:https://github.com/lcrocker/ojrandlib/tree/master/source/java/com/onejoker/randlib

    【讨论】:

    • 你错了。 GetByteArrayElements() 将现有的 Java 数组固定到 C 端的数组就可以了。代码还有其他问题。
    • @Alex Cohn/Lee Daniel Crocker:GetByteArrayElements() 是按引用调用还是按值调用似乎取决于 JVM,而您无法预见:publib.boulder.ibm.com/infocenter/javasdk/v1r4m2/…
    • @KWT: 不,你不能预见 GetByteArrayElements() 会在原地使用数组还是生成一个副本。但在这两种情况下,使用 ReleaseByteArrayElements(...., 0) 将确保 Java 数组获得您对从 GetByteArrayElements() 获得的字节所做的所有更改。如果两个 memcpy 操作的开销是可以接受的,取决于场景。
    • @KWT: ... 但 Android 虚拟机的实现细节是它总是为您提供 int 数组。
    【解决方案3】:

    我把代码改成了这样:

    Java:

    native void decodeYUV420RGB565(ByteBuffer rgb, byte[] yuv420sp, int width, int height); 
    //There's no allocateDirect for IntBuffer so I have to use ByteBuffer
    //although mRGBData is an int array
    //allocateDirect(mRGBData.length * 4) because mRGBData is an int array
    ByteBuffer mTempData = ByteBuffer.allocateDirect(mRGBData.length*4);
    decodeYUV420RGB565(mTempData, mYUVData, mImageWidth, mImageHeight);
    mRGBData = mTempData.asIntBuffer().array();
    mTempData.clear();
    

    C:

    void Java_com_camera_DrawOnTop_decodeYUV420RGB565(JNIEnv* env, jobject thiz, jintArray rgb, jbyteArray yuv420sp, jint width, jint height)
    {
     jbyte* yuv420spc = (*env)->GetByteArrayElements(env, yuv420sp, NULL);
     jint*  rgbc      = (*env)->GetDirectBufferAddress(env, thiz);
    
     //...conversion code as above...
    
     (*env)->ReleaseByteArrayElements(env, yuv420sp, yuv420spc, 0 );
     (*env)->ReleaseIntArrayElements(env, rgb, rgbc, 0 );
    

    但我有同样的问题:当我调用 decodeYUV420RGB565(mTempData, mYUVData, mImageWidth, mImageHeight) 时代码崩溃。我要在代码中更改什么?

    【讨论】:

    • meta 可以在 stackoverflow 上发布您自己的问题的答案,但这不是答案:它仍然会崩溃。恕我直言,它更适合作为您原始问题的更新。
    • 您切换到直接字节缓冲区。但是您的本机函数仍然调用 ReleaseIntArrayElements()。这行不通。
    • 在你的 Java 代码中,你所有的 array() 方法都用于直接字节缓冲区。此调用崩溃。 ByteBuffer 类有一个 hasArray() 方法来指示是否可以使用 array() 方法。 Android VM 的一个实现细节是,对 isDirect() 响应为肯定的缓冲区对 hasArray() 响应为否定,反之亦然。
    • 一般来说,使用直接字节缓冲区在 Java 和本机之间“共享”内存可能会提高性能。但不是你的情况。您需要 int 数组形式的结果,这需要复制直接字节缓冲区的所有元素。使用 GetIntArrayElements() / ReleaseIntArrayElements() 更便宜。使用 GetPrimitiveArrayCritical() / ReleasePrimitiveArrayCritical() 可能更便宜。请注意,对于 yuv420sp,您可以致电 ReleaseByteArrayElements(env, yuv420sp, yuv420spc, JNI_ABORT)
    猜你喜欢
    • 2013-01-22
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-02
    • 2012-04-14
    • 2014-04-05
    相关资源
    最近更新 更多