Android, как использовать библиотеку libjpeg-turbo через пример JNi
Я успешно собираю libjpeg-turbo с помощью ndk-build благодаря этому сообщению: libjpeg-turbo для android
Я хотел бы получить встроенную функцию, такую как read_JPEG_file, в файле example.c libjpeg-turbo для вызова из кода Java и использования ее для приложения Android.
Может ли кто-нибудь дать мне пример, как это сделать? Как написать собственный метод для Java, который использует библиотеку libjpeg-turbo ndk?
Я могу загрузить библиотеку через
System.loadLibrary("libjpeg");
Но что дальше? Библиотека не имеет собственных методов для вызова из Java.
Я пытался написать класс JNI c согласно документам JNI, но безуспешно. Пример кода был бы полезен, чтобы узнать, как это сделать.
РЕДАКТИРОВАТЬ:
Я создал тестовый класс 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.c
а также com_test_app_NativeMethods.h
находятся в project/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
Так как же использовать нативный код с библиотекой libjpeg-turbo, встроенной в ndk?
4 ответа
Вы можете создать свою собственную обертку для библиотеки на нативной стороне, если вам нужно выполнить все это из нативного кода. Шаги примерно такие:
В классе, куда вы звоните:
System.loadLibrary ("libjpeg");
Вы объявляете нативные методы, которые вам нужно вызывать на стороне Java, без предоставления какой-либо реализации:
public static native readJPEGFile();
Затем вы можете использовать команду javah для создания 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, чтобы увидеть, не потрудился ли кто-то еще перед тем, как вы заработаете ту же библиотеку, или просто посмотреть более продвинутые примеры, чем официальные примеры.
Поскольку libjpeg_turbo скомпилировано как библиотека C, а ваш собственный код Android скомпилирован как программа C++, вы должны включить библиотеку C через директиву linkage.
Попробуй это,
extern "C" {
#include "headers of libjpeg_turbo"
}
Я разместил это как комментарий под одним из существующих ответов, но я думаю, что на самом деле это лучший ответ, чем "вы должны написать свою собственную оболочку JNI lib":
Согласно комментарию @AlexCohn, вы можете включить интерфейс JNI-оболочки в сборку, добавив turbojpeg-jni.c
к списку файлов для встраивания Android.mk
Таким образом, вы можете использовать предоставляемые ими Java-классы напрямую, вместо того, чтобы создавать собственный нативный код для вызова библиотеки.
Попробуйте изменить Android.mk, включив в него заголовок libjpeg, а также попробуйте найти некоторую информацию о dlopen()
функция для использования вашей скомпилированной руки libjpeg.so.