2

由于这篇文章,我使用 ndk-build 成功构建了 libjpeg-turbo: libjpeg-turbo for android

我想在 libjpeg-turbo 的 example.c 中获得一个像 read_JPEG_file 这样的本机函数,以便从 Java 代码调用以将其用于 Android 应用程序。

有人可以给我一个例子吗?如何为使用 libjpeg-turbo ndk 构建库的 Java 编写本机方法?

我可以通过加载库

System.loadLibrary("libjpeg");

但接下来呢?该库没有任何可从 Java 调用的本机方法。

我试图根据 JNI 文档编写一个 JNI c 类,但没有成功。示例代码非常适合学习如何操作。

编辑:

我创建了一个测试类 NativeMethods:

package com.test.app;

public class NativeMethods {

    private String filename = null;

    static {
        System.loadLibrary("jpeg"); // this represents compiled libjpeg-turbo under ndk
    }

    public NativeMethods(String jpegFilename) {
        this.filename = jpegFilename;
    }

    public native int computeNumberOfDCTS(String filename);
}

然后我使用 javah 生成本地方法的 C 头文件,结果是com_test_app_NativeMethods.h包含以下内容的文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_app_NativeMethods */

#ifndef _Included_com_test_app_NativeMethods
#define _Included_com_test_app_NativeMethods
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_test_app_NativeMethods
 * Method:    computeNumberOfDCTS
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

然后我创建了一个名为的文件JPEGProcessing.c,我将本机函数的 c 实现放置在如下位置:

#include "com_test_app_NativeMethods.h"

//that came from jpeglib example
struct my_error_mgr
{
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;

//routine that will replace the standard error_exit method
static void
my_error_exit (j_common_ptr cinfo)
{
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    my_error_ptr myerr = (my_error_ptr) cinfo->err;

    (*cinfo->err->output_message) (cinfo);

    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}

JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS
    (JNIEnv *env, jobject obj, jstring javaString)
{
    jint toReturn = 0;
    // struct representing jpeg image
    struct jpeg_decompress_struct  cinfo;
    // struct representing error manager; defined above
    struct my_error_mgr jerr;

    const char *filename = (*env)->GetStringUTFChars(env, javaString, 0);

    FILE * infile;

    if ((infile = fopen(filename, "rb")) == NULL) {
        fprintf(stderr, "can't open %s\n", filename);
        return -1;
    }

    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return -1;
    }

    jpeg_create_decompress(&cinfo);

    jpeg_stdio_src(&cinfo, infile);

    (void) jpeg_read_header(&cinfo, TRUE);

    // declare virtual arrays for DCT coefficients
    jvirt_barray_ptr* coeffs_array;
    // read DCT coefficients from jpeg
    coeffs_array = jpeg_read_coefficients(&cinfo);

    // fill virtual arrays.
    // ci represents component color
    // this cycle prints all dct coefficient of the jpeg in 8x8 blocks
    for (int ci = 0; ci < 3; ci++)
    {
        JBLOCKARRAY buffer_one;
        JCOEFPTR blockptr_one;
        jpeg_component_info* compptr_one;
        compptr_one = cinfo.comp_info + ci;

        for (int by = 1; by < (compptr_one->height_in_blocks - 1); by++) // we don't want to use the edges of the images
        {
            buffer_one = (cinfo.mem->access_virt_barray)((j_common_ptr)&cinfo, coeffs_array[ci], by, (JDIMENSION)1, FALSE);

            for (int bx = 1; bx < (compptr_one->width_in_blocks - 1); bx++) // we don't want to use the edges of the images
            {
                blockptr_one = buffer_one[0][bx];

                for (int bi = 1; bi < 64; bi++) // we don't want to use AC
                {
                    toReturn++;
                }
            }
        }
    }

    jpeg_destroy_decompress(&cinfo);
    fclose(infile);

    return toReturn;
}

文件JPEGProcessing.ccom_test_app_NativeMethods.hproject/jni/文件夹中。在同一个 jni 文件夹Android.mk中,我创建了放置这些行的文件:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE    := JPEGProcessing
LOCAL_CFLAGS    := -Werror
LOCAL_SRC_FILES := JPEGProcessing.c

LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_LDLIBS    := -lm -llog -landroid
LOCAL_STATIC_LIBRARIES := libjpeg
include $(BUILD_SHARED_LIBRARY)

从我的项目目录运行 ndk-build 后,出现了许多错误:

jni/JPEGProcessing.c:7:27: error: field 'pub' has incomplete type
jni/JPEGProcessing.c:8:5: error: unknown type name 'jmp_buf'
jni/JPEGProcessing.c:14:16: error: unknown type name 'j_common_ptr'
jni/JPEGProcessing.c: In function 'Java_com_test_app_NativeMethods_computeNumberOfDCTS':
jni/JPEGProcessing.c:30:36: error: storage size of 'cinfo' isn't known
jni/JPEGProcessing.c:36:5: error: unknown type name 'FILE'
jni/JPEGProcessing.c:38:17: error: assignment makes pointer from integer without a cast [-Werror]
jni/JPEGProcessing.c:38:45: error: 'NULL' undeclared (first use in this function)
...

我不明白。如何将代码与 libjpeg-turbo 函数结合以获得工作库并在 java 中使用它?

我阅读了 NDK 说明和示例,但仍然不明白。

编辑:

带有简单本机方法的 NDK 和 JNI 工作得很好。作为测试,我使用了以下效果很好的简单方法:

#include "com_test_app_NativeMethods.h"

JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS
    (JNIEnv *env, jobject obj, jstring javaString)
{
    jint toReturn = 0;

    const char *filename = (*env)->GetStringUTFChars(env, javaString, 0);
    const char *test = "test";

    if ( filename == test )
    {
        toReturn++;
    }

    return toReturn;
}

我用 libjpeg-turbo 的东西试了一下,如下所示:

#include "com_test_app_NativeMethods.h"
#include <stdio.h>
#include <setjmp.h>
#include <libjpeg-turbo/jpeglib.h>
#include <libjpeg-turbo/turbojpeg.h>
#include <libjpeg-turbo/jconfig.h>
#include <libjpeg-turbo/jmorecfg.h>
#include <libjpeg-turbo/jerror.h>

//that came from jpeglib example
struct my_error_mgr
{
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;

//routine that will replace the standard error_exit method
static void
my_error_exit (j_common_ptr cinfo)
{
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    my_error_ptr myerr = (my_error_ptr) cinfo->err;

    (*cinfo->err->output_message) (cinfo);

    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}

JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS
    (JNIEnv *env, jobject obj, jstring javaString)
{
    jint toReturn = 0;
    // struct representing jpeg image
    struct jpeg_decompress_struct  cinfo;
    // struct representing error manager; defined above
    struct my_error_mgr jerr;

    const char *filename = (*env)->GetStringUTFChars(env, javaString, 0);

    FILE * infile;

    if ((infile = fopen(filename, "rb")) == NULL) {
        // cannot open file
        return -1;
    }

    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return -1;
    }

    jpeg_create_decompress(&cinfo);

    jpeg_destroy_decompress(&cinfo);
    fclose(infile);

    return toReturn;
}

我得到了错误......:

Compile thumb  : JPEGProcessing <= JPEGProcessing.c
SharedLibrary  : libJPEGProcessing.so
/usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:50: error: undefined reference to 'jpeg_std_error'
/usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:59: error: undefined reference to 'jpeg_CreateDecompress'
/usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:61: error: undefined reference to 'jpeg_destroy_decompress'
/usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:54: error: undefined reference to 'jpeg_destroy_decompress'
collect2: ld returned 1 exit status
make: *** [obj/local/armeabi/libJPEGProcessing.so] Error 1

那么如何将本机代码与 ndk 构建的 libjpeg-turbo 库一起使用?

4

4 回答 4

2

由于 libjpeg_turbo 被编译为 C 库,而您的 Android 本机代码被编译为 C++ 程序,因此您应该通过链接指令包含 C 库。

试试这个,

extern "C" {
#include "headers of libjpeg_turbo"
}
于 2013-03-05T09:19:38.767 回答
1

如果您需要从本机代码执行所有操作,您可以在本机端为库创建自己的包装器。步骤大致是这样的:

  • 在你打电话的班级中:

    System.loadLibrary("libjpeg");

您声明需要在 Java 端调用的本机方法,而不提供任何实现:

public static native readJPEGFile();
  • 然后,您可以使用 javah 命令创建一个 C 文件,其中包含在本机端执行本机功能的 C 文件。当您在 Java 端调用本机函数时,将调用该函数。注意函数和参数的完整路径,它们将由 javah 命令自动生成,不应更改,否则将不起作用。结果应该是这样的:

     extern "C" {
         JNIEXPORT void JNICALL Java_com_android_packagename_GL2JNILib_readJPEGFile(JNIEnv * env, jobject obj);
     };
    
    JNIEXPORT void JNICALL Java_com_android_packagename_GL2JNILib_readJPEGFile(JNIEnv * env, jobject obj) {
        // Call your library's function
     }
    
  • 之后,您需要在 jni 文件夹中创建一个包含您的包装函数的共享库(如果它不存在则创建它)。您可以创建一个 Android.mk 文件并运行 ndk-build 来生成共享库(.so 文件)。一个可能的 Android.mk 可能是这样的,但您应该检查名称和依赖项:

      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE    := jpgturbo-wrapper
      LOCAL_CFLAGS    := -Werror
      LOCAL_SRC_FILES := jpgturboWrapper.cpp
      LOCAL_C_INCLUDES := $(LOCAL_PATH)
      LOCAL_LDLIBS    := -lm -llog -landroid
      LOCAL_STATIC_LIBRARIES := jpegturbo # write all the libraries your project depend on here, separated by white spaces
      include $(BUILD_SHARED_LIBRARY)
    
  • 现在您可以运行 ndk-build 并生成 .so 文件并将其复制到其对应的文件夹中。

您可以查看 NDK 文档以开始,尤其是他们提供的示例:http: //developer.android.com/tools/sdk/ndk/index.html

有时在 github 上进行快速搜索也是一个好主意,看看其他人是否在您使用同一个库之前付出了痛苦,或者只是为了查看一些比官方示例更高级的示例。

于 2013-01-14T22:12:37.097 回答
0

尝试修改您的 Android.mk 以包含您的 libjpeg 标头,还尝试查找有关dlopen()使用已编译 arm libjpeg.so 的函数的一些信息。

于 2013-07-21T02:41:26.247 回答
0

我将此作为评论发布在现有答案之一下,但我认为这实际上是一个更好的答案,然后“您必须编写自己的包装器 JNI lib”:

As per the comment from @AlexCohn, you can include the wrapper JNI interface in the build by adding the turbojpeg-jni.c to the list of files to build in Android.mk, this way you may use the Java classes they provide directly instead of having to create your own native code to invoke the lib.

于 2015-05-05T19:38:08.337 回答