I only have 1 .so file from old project.
How can I use this file without creating the same package in project or module ?
Actually, you don't need to change function name in .so file. You can use dlopen to load your .so library dynamically at runtime and dlsym to get pointer for you YOUR_FUNCTION_NAME() and then call YOUR_FUNCTION_NAME() by pointer. For do that in your current project you can create "wrapper" like that:
public class OldSoHelper {
public static native void loadOldSo();
public static native <TYPE_OF_RESULT> runFunFromOldSo(<PARAMETERS>);
public static native void unloadOldSo();
}
and in corresponding .c/.cpp file of current project (e.g. native-lib.cpp by default):
void *handle;
<TYPE_OF_OLD_FUNCTION> (*old_fun_wrapper)(<PARAMETERS_OF_OLD_FUNCTION>);
extern "C"
JNIEXPORT void JNICALL
Java_<YOUR_PACKAGE_NAME>_OldSoHelper_loadOldSo(JNIEnv *env, jclass type) {
handle = dlopen("<YOUR_OLD_SO>.so", RTLD_NOW);
old_fun_wrapper = (<TYPE_OF_OLD_FUNCTION> (*)(<PARAMETERS_OF_OLD_FUNCTION>))(dlsym(handle, "<OLD_FUNCTION_NAME_e.g._Java_com_abc_dee_Native_appInit>"));
}
extern "C"
JNIEXPORT jobject JNICALL
Java_<YOUR_PACKAGE_NAME>_OldSoHelper_runFunFromOldSo(JNIEnv *env, jclass type,
<PARAMETERS_FOR_OLD_FUNCTION>)
{
jclass ResultClass = env->FindClass("YOUR/PACKAGE/NAME/RESULT_CLASS");
jobject result = ...
jfieldID fieldId = env->GetFieldID(ResultClass, "<FIELD_NAME>", "<FILED_TYPE_LETTER>");
<TYPE_OF_OLD_FUNCTION> res = old_fun_wrapper(<PARAMETERS_FOR_OLD_FUNCTION>);
env->Set<TYPE>Field(result, fieldId , res.filed);
return result;
}
extern "C"
JNIEXPORT void JNICALL
Java_<YOUR_PACKAGE_NAME>_OldSoHelper_unloadOldSo(JNIEnv *env, jclass type) {
if (handle) {
dlclose(handle);
}
}
and from java code you can call:
...
// when you need old .so e.g. in onCreate()
OldSoHelper.loadOldSo();
...
// when you no need to call function from old .so
<TYPE_OF_RESULT> result = OldSoHelper.runFunFromOldSo(<PARAMETERS>);
...
// when you no need old .so e.g. in onDestroy()
OldSoHelper.unloadOldSo();
...
I am trying to call a method in my java class from within a thread in the native code but not having any success. These are the global vars:
JavaVM* javaVM = NULL;
jclass activityClass;
jobject activityObj;
Code called on initialisation of the native code:
extern "C" {
JNIEXPORT jint JNICALL
naInit(JNIEnv *pEnv, jobject pObj, jstring pFileName, jstring, defaultStorageDirectory) {
pEnv->GetJavaVM(&javaVM);
jclass cls = pEnv->GetObjectClass(pObj);
activityClass = reinterpret_cast<jclass>((jclass) pEnv->NewGlobalRef(cls));
activityObj = pEnv->NewGlobalRef(pObj);
}
}
Code used within the thread function:
void *decodeAndRender(void * args) {
JNIEnv *env;
javaVM->AttachCurrentThread(&env, NULL);
jmethodID retryStartVideoMethodID = env->GetMethodID(activityClass, "retryStartVideo", "()V");
env->CallVoidMethod(activityObj, retryStartVideoMethodID);
javaVM->DetachCurrentThread();
return 0;
}
Java code :
public void retryStartVideo() {
Log.d(TAG, "METHOD CALLED FROM CPP ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
Error in logcat:
JNI DETECTED ERROR IN APPLICATION: JNI CallVoidMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Ljava/lang/Class;.retryStartVideo()V"
I have set the instances of the calling class to be a global ref and used that when calling from within the thread function but it is still failing to find the method. I can use similar code to call a static method no problem but I need to be able to call a non static one.
I tried implementing direct convolution of two audio files using JNI on Android.. So far, I ve done this:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output)
{
jdouble *sig1, *sig2, *out;
// jboolean isCopy1, isCopy2, isCopy3;
int i,j;
jsize n,m;
sig1=(*env)->NewDoubleArray(env,n);
sig2=(*env)->NewDoubleArray(env,m);
out=(*env)->NewDoubleArray(env,m);
n=(*env)->GetArrayLength(env, sig1);
m=(*env)->GetArrayLength(env,sig2);
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
out=(*env)->GetDoubleArrayElements(env, output, NULL);
if (sig1 != NULL || sig2!=NULL) {
memcpy(signal1,sig1,n);
memcpy(signal2,sig2,m);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
}
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->ReleaseDoubleArrayElements(env,output,out,0);
return 1;}
And on the java side:
public class MainActivity extends Activity {
static
{
System.loadLibrary("DirectConv");
}
File externalDir1=Environment.getExternalStorageDirectory();
File externalDir2=Environment.getExternalStorageDirectory();
File f1=new File(externalDir1.getAbsolutePath()+"/Test"+File.separator+"wav2.wav");
File f2=new File(externalDir2.getAbsolutePath()+"/Test"+File.separator+"wav2.wav");
int res;
public native int convolve(double[]signal1, double[]signal2, double[]output);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
//---------------------------------------------read files
double[]raw1=read(f1);
double[]raw2=read(f2);
double[]out=new double[raw1.length];
res=convolve(raw1,raw2,out);
for(int i=0;i<out.length;i++)
Log.i("out", "out "+ out[i]);
}
The resulting array (out) is all zeros. I don't see what I did wrong. Any help?
Your handling of JNI arrays is all wrong. First off, there's no reason for you to be creating any new arrays here. YOur inputs and outputs are passed down, what you want to do is convert the inputs to C arrays, work on them in pure C, then convert your output array to a Java array.
Secondly, you don't need the memcpys. Calling getDoubleArrayElements does it for you.
Third, you need to put the values in out back into output via setDoubleArrayRegion
Fourth, you need to clean up your memory usage. If you don't, you're going to memory leak and eventually die as there's a limit of 256 (or is it 512? Been too long I forget) Java objects pinned to C.
Your code should look more like:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output)
{
double *sig1, *sig2, *out;
int i,j;
jsize n,m;
n=(*env)->GetArrayLength(env, sig1);
m=(*env)->GetArrayLength(env,sig2);
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->SetDoubleArrayRegion(env, output, 0, n, out);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
return 1;}
I'm going to assume your convolution math is right, its way too late for me to start remembering that stuff :)
I figured out the problem. I added files sizes as parameters like this:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output, jint size1, jint size2)
{
jdouble *sig1, *sig2, *out;
int i,j,k;
jint n,m;
n=size1;
m=size2;
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
out=(*env)->GetDoubleArrayElements(env,output,NULL);
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
if(i-j>=0)
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->SetDoubleArrayRegion(env, output, 0, n, out);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
return 1;
}
I a Library in C that I'm leveraging for an Android application. This library has an audio stream that it occasionally flushes. When this happens it calls a write callback function of my design.
My intent is to have that C callback call a method on a specific Java Object which will handle stuff with the strem.
Currently I have code like so:
methodID compressionHandler=0;
jobject compressionHandlerClass;
int audioBufferChunkSize;
static JavaVM *gJavaVM;
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
gJavaVM = vm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_com_my_code_init(JNIEnv* env, jobject obj, /*classpath of the class we want to call against*/jstring compressedAudioHandlerPath, /*class instance we want to call against*/jobject callbackClass) {
......
// this is a global ref as per:
//http://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
compressionHandlerClass = (*env)->NewGlobalRef(env,callbackClass);
// name of the class
const char *classLocation;
// convert jString to c String
classLocation = (*env)->GetStringUTFChars( env, compressedAudioHandlerPath , NULL ) ;
// tmp variable for holding the class location, relates to the above issue with garbage collection
jclass clazz = (*env)->FindClass(env, classLocation);
// the actual method that we want to call, this gets used in the writeCallback
compressionHandler = (*env)->GetMethodID(env, clazz, "handleCompressedAudio", "([B)V");
......
}
The callback method looks like so:
void writeCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {
JNIEnv *env;
int isAttached = 0;
if ((status = (*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
if ((status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
isAttached = 1;
}
if(*env!=0 && compressionHandler!=0){
jbyteArray arr = (*env)->NewByteArray(env,bytes);
(*env)->SetByteArrayRegion(env,arr, 0, bytes, (jbyte*)buffer);
(*env)->CallVoidMethod(env,compressionHandlerClass, compressionHandler,arr);
free(arr);
free(env);
free(isAttached);
}
}
I'm getting crashes at the CallVoidMethod, that signature of which is an interface implemented by whatever object I pass in:
public interface CompressedAudioHandler {
void handleCompressedAudio(byte[] buff);
}
I suspect that I am improperly attaining/keep references to these objects, but I haven't found a great way to handle that. Any advice on how I can more correctly handle this?
I have a package named com.self.tryffmpeg.
There's a file MainActivity.java inside that package which declares two native functions from the C inside the JNI folder
static {
System.loadLibrary("ffmpeg");
System.loadLibrary("ffmpeg-test-jni");
}
private native int createEngine();
private native String loadFile(String file, byte[] array);
}
Inside the JNI folder, there's a C file which exports the functions needed by the MainActivity.java.
Inside the C , I exports the functions
NIEXPORT jint JNICALL Java_com_self_tryffmpeg_MainActivity_createEngine(JNIEnv* env, jclass clazz)
{
}
JNIEXPORT jintArray JNICALL Java_com_self_tryffmpeg_MainActivity_loadFile(JNIEnv* env, jobject obj,jstring file,jbyteArray array) {
}
But the error is the functions of loadFile and createEngine cannot be resolved. UnsatisfiedLinkError. Did i do anything wrong with that. I thought I export the functions correctly.