Error passing a cv::Mat to JNI using OpenCV on Android - c++

I am developing an Android project with OpenCV and JNI.
Actually I am changing the face-detection sample.
The problem I have is that when I pass a cv::Mat reference it gives some strane output and it is not passed well.
To put you in situation, I have this in my FdActivity.java, which is the main activity of my android app:
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
rgb = inputFrame.rgba();
Mat res = mNativeDetector.process(rgb);
return res;
}
The process function is like this:
public Mat process(Mat rgb) {
Mat n = null;
if(rgb.empty()) {
System.out.println("Empty Image");
}
else {
System.out.println("The image is " + rgb.rows() + "x" + rgb.cols());
n = nativeSkinFilter(mNativeObj, rgb.getNativeObjAddr());
}
return n;
}
Where nativeSkinFilter is a native function with this declaration
private static native Mat nativeSkinFilter(long thiz, long inputImage);
In the C++ side I have the function declaration (DetectionBasedTracker.h):
JNIEXPORT jlong JNICALL Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeSkinFilter (JNIEnv *, jclass, jlong);
The only thing I want to do is return the same image, just passing by the C++ function (more complex implementation will come as soon as I know I can correctly pass a matrix), so the code is just like this (DetectionBasedTracker.cpp):
JNIEXPORT jlong JNICALL Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeSkinFilter (JNIEnv * jenv,jclass,jlong rgb)
{
Mat* rgba = (Mat*) rgb;
if(rgb == 0) {
LOGD("Null matrix");
}
else {
LOGD("The matrix is not null. It has %i rows and %i columns", (*rgba).rows, (*rgba).cols);
}
return (jlong)rgb;
}
The ouptut I have is the following:
07-07 13:00:07.671: I/Choreographer(14980): Skipped 55 frames! The application may be doing too much work on its main thread.
07-07 13:00:07.701: E/BufferQueue(14980): [unnamed-14980-0] dequeueBuffer: min undequeued buffer count (2) exceeded (dequeued=6 undequeudCount=0)
07-07 13:00:07.741: I/JavaCameraView(14980): Preview Frame received. Need to create MAT and deliver it to clients
07-07 13:00:07.741: I/JavaCameraView(14980): Frame size is 576000
07-07 13:00:07.761: I/System.out(14980): The image is 480x800
07-07 13:00:07.761: D/FaceDetection/DetectionBasedTracker(14980): The matrix is not null. It has 1937716000 rows and 0 columns
07-07 13:00:07.761: E/cv::error()(14980): OpenCV Error: Assertion failed (src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols) in void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean), file /home/reports/ci/slave_desktop/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp, line 97
07-07 13:00:07.761: E/org.opencv.android.Utils(14980): nMatToBitmap catched cv::Exception: /home/reports/ci/slave_desktop/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp:97: error: (-215) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean)
07-07 13:00:07.761: A/libc(14980): Fatal signal 11 (SIGSEGV) at 0x0000000a (code=1), thread 15115 (Thread-5379)
07-07 13:00:07.791: E/BufferQueue(14980): [unnamed-14980-0] dequeueBuffer: min undequeued buffer count (2) exceeded (dequeued=5 undequeudCount=1)
07-07 13:00:07.801: I/JavaCameraView(14980): Preview Frame received. Need to create MAT and deliver it to clients
07-07 13:00:07.801: I/JavaCameraView(14980): Frame size is 576000
I think I have tried everything, but it seems the correct way and it's still failing.
Can you please please help me?
Thank you very much for your time! Help will be really appreciated.

this
JNIEXPORT jlong JNICALL Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeSkinFilter (JNIEnv *, jclass, jlong);
should be this
JNIEXPORT jlong JNICALL Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeSkinFilter (JNIEnv *, jclass, jobject, jlong);
because a call like this in java
n = nativeSkinFilter(mNativeObj, rgb.getNativeObjAddr());
expects 4 parameters:
JNEnv (obvious)
jclass is the class. Or if you are invoking the method from an instance it will be jobject
jobject as you are passing in some object
jlong now this is the actual matrix you are looking for
in the c++ side, after the second parameter, the parameters you pass in on the Java side are passed. In other words, every call via Java->C++ (whether instance or static function) on the c++ side the first 2 parameters are mandatory. Then follows your parameters between "(" and ")" in the Java code.

Related

How to get the localized return string of C++ strerror?

My Minecraft server crushed caused by insufficient spaces. About 2 hours before crushed, there were some plugins printing exception stack traces including java.io.ioexception: 设备上没有空间 (means java.io.IOException: No space left on device). After I restart it, I found that all the buildings players built in the 2 hours disappeared, and no any plugins can restore it (include CoreProtect, because of insufficient spaces, it can not submit database transactions).
In order to close server immediately when there's no enough spaces, I decided to develop a plugin. Because there's no way to listen exceptions thrown by all threads, I implemented this function by listen logs.
Any code print log like java.io.ioexception: ${message} or [...] ... [...]: java.io.ioexception: ${message} will trigger the shutdown of server.
Pay attention to the error message: In some platforms, it's 设备上没有空间; In some others platforms, it's No space left on device... So It's necessary to get the localized version of it firstly.
This error message, comes from Operating System, is strerror(28) (C++ function) in Windows or Linux. So I create a native function to invoke it:
// cn.chuanwise.nessc.errno.ErrorMessage;
public class ErrorMessage {
public native static String of(int errno, Locale locale);
}
#include "localed-error-message-native.h"
#include <locale>
#include <string.h>
JNIEXPORT jstring JNICALL Java_cn_chuanwise_nessc_errno_ErrorMessage_of(JNIEnv* env, jclass clz, jint code, jobject locale) {
// find locale class
const jclass locale_class = env->FindClass("java/util/Locale");
const jmethodID to_string_method = env->GetMethodID(locale_class, "toString", "()Ljava/lang/String;");
const jobject locale_string = env->CallObjectMethod(locale, to_string_method);
const char* locale_name = env->GetStringUTFChars(reinterpret_cast<jstring>(locale_string), nullptr);
// set locale
std::setlocale(LC_ALL, locale_name);
const char* strerr = strerror(static_cast<int>(code));
if (strerr == nullptr) {
return nullptr;
} else {
return env->NewStringUTF(strerr);
}
}
ErrorMessage.of(28, Locale.getDefault()) returns No space left on device in my device, but this code throws java.io.IOException: 设备上没有空间 in my device:
final File file = new File("test");
// ...
try (OutputStream outputStream = new FileOutputStream(file)) {
final byte[] buf = new byte[1024];
while (true) {
outputStream.write(buf);
}
}
How to get the localized return string of strerror?

libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xcdb8007c

I have saw many question about Fatal signal 11 (SIGSEGV), code 1 on stackoverflow,but no one has an answer.
my JNI code :
JNIEXPORT jbyteArray JNICALL
Java_com_btc_testtwo_ProxyApplication_decrypt(JNIEnv *env, jobject instance,
jbyteArray srcdata_) {
jbyte *srcdata = env->GetByteArrayElements(srcdata_, NULL);
print_debug("come in");
while (*srcdata) {
*srcdata ^= 0xF;
*srcdata++;
}
print_debug("come out");
env->ReleaseByteArrayElements(srcdata_, srcdata, 0);
}
I wanna know if the bit operation XOR changed the memory size.My suspicion is that the error caused by the memory error.I don't know how the code cause this.
ReleaseByteArrayElements(srcdata_, srcdata, 0) will fail if GetByteArrayElements gave you a copy of the array data, because you've incremented srcdata so that it no longer contains the same address that you got from GetByteArrayElements.
Also, your while-loop can potentially access data past the end of the array, because you don't take the value of GetArrayLength(srcdata_) into account.

Reuse SuperpoweredDecoder for loading audio files

For benchmarking purposes I repeat loading an .wav-file, processing it offline and saving the output by using the SuperpoweredSDK.
But after some iterations (in my case 4) I get the error "A/libc: invalid address or address of corrupt block 0x5e825000 passed to dlfree" when I try to release the shortIntBuffer.
extern "C" JNIEXPORT void JNICALL
Java_com_example_sebas_superpoweredtest_MainActivity_testDecoderReuse(JNIEnv *env, jobject instance,
jstring apkPath_,
jlong fileOffset,
jlong fileLength,
jstring outputFileName_) {
const char *apkPath = env->GetStringUTFChars(apkPath_, 0);
const char *outputFileName = env->GetStringUTFChars(outputFileName_, 0);
SuperpoweredDecoder decoder;
short int* shortIntBuffer;
FILE* fd;
for(int i = 0; i < 100; ++i) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "RUN %d", i+1);
//open InputFile
const char *openError = decoder.open(apkPath, false, fileOffset, fileLength);
if (openError) {
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", openError);
return;
};
shortIntBuffer = new short int[decoder.samplesPerFrame * 4 + 16384] {0};
fd = createWAV(outputFileName, decoder.samplerate, 2);
if (!fd) {
__android_log_print(ANDROID_LOG_ERROR,LOG_TAG, "Failed creating File %s", outputFileName);
return;
};
//process samples
unsigned int samplesDecoded;
while (true) {
// Decode one frame. samplesDecoded will be overwritten with the actual decoded number of samples.
samplesDecoded = decoder.samplesPerFrame;
if (decoder.decode(shortIntBuffer, &samplesDecoded) == SUPERPOWEREDDECODER_ERROR) {
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error while decoding samples.");
break;
}
if (samplesDecoded < 1) break;
// Write the audio to disk.
fwrite(shortIntBuffer, 1, samplesDecoded * 4, fd);
};
//close resources
if(fd) {
closeWAV(fd);
__android_log_print(ANDROID_LOG_ERROR,LOG_TAG, "Closed wav.");
}
delete[](shortIntBuffer); // <- SIGSEGV (signal SIGSEGV: invalid address (fault address: 0xdeadbaad))
__android_log_print(ANDROID_LOG_ERROR,LOG_TAG, "Deleted shortInBuffer");
}
env->ReleaseStringUTFChars(apkPath_, apkPath);
env->ReleaseStringUTFChars(outputFileName_, outputFileName);
}
I don't understand why the code works fine for the first iterations but not for all iterations. I would be grateful to hear from you to solve this problem.
Thanks in advance
This is the LLDB-Frame when I get the message: "Fatal signal 11 (SIGSEGV) at 0xfde0fdec (code=1), thread 9779 (uperpoweredtest)"

Error occured when sending message through Google Play Game Service with Cocos2d-x

I'm developing a multiplayer game with Google Play Services and Cocos2d-x. I already set up communication between Java and C++ using JNI, and can run processes like Sign-in, Create room, Invitation... Everything is fine until I need to send some struct to another players. When receive data from other, an error has occurred.
Here's the structs:
typedef enum
{
MESSAGE_TYPE_PING = -1,
MESSAGE_TYPE_PING_BACK = 0,
MESSAGE_TYPE_RTT = 3
} MESSAGE_TYPE;
typedef struct
{
MESSAGE_TYPE messType;
} MESSAGE;
typedef struct
{
MESSAGE mess;
timeval sendTime;
const char* text;
} PING_MESSAGE;
And in this code snippet I convert struct and send it to another players:
void MyClass::sendData()
{
// Send package
PING_MESSAGE ping;
ping.mess.messType = MESSAGE_TYPE_PING;
ping.sendTime = startTime;
ping.text = "Ohhhhhh";
char byte[sizeof(ping)];
memcpy(byte, &ping, sizeof(ping));
GCHelper::sendReliableRealTimeMessage(&byte[0]);
}
// In GCHelper's implementation
void GCHelper::sendReliableRealTimeMessage(const char* byteMess)
{
// Convert char* to jbyteArray
jbyteArray bArray = env->NewByteArray(16); //env is a static JNIEnv* in this class; 16 since my PING_MESSAGE struct is 16 bytes in size;
env->SetByteArrayRegion(bArray,0,16,(jbyte*)byteMess);
// Make Java call
env->CallStaticVoidMethod(classID, methodID, bArray);
methodInfo.env->DeleteLocalRef(bArray);
methodInfo.env->DeleteLocalRef(classID);
}
Now, Java code take responsibility to send the byte array to participants in room. At receiver side, I'm continue send received data to C++. And error occured here when I convert jbyteArray back to struct:
void Java_package_name_TestMulti_nativeOnRealTimeMessageReceived(JNIEnv *env, jobject thiz, jbyteArray receivedMess)
{
CCLOG("Called from java");
jboolean isCopy;
jbyte * pCData = env->GetByteArrayElements(receivedMess, &isCopy);
jsize size = env->GetArrayLength(receivedMess);
const char* sd = (const char*)pCData;
PING_MESSAGE result;
memcpy(&result, sd, sizeof(result));
CCLOG("TEST size: %d", size); // This log out: "TEST size: 16"
CCLOG("TEST type: %d", result.mess.messType); // This log out: "TEST type: -1"
CCLOG("TEST text: %s", result.text); // AND ERROR!!!!!!!
cocos2d::CCByteArray* data = cocos2d::CCByteArray::createWithData(sd);
cocos2d::CCNotificationCenter::sharedNotificationCenter()->postNotification("onRealTimeMessageReceived", data);
if(isCopy)
{
env->ReleaseByteArrayElements(receivedMess,pCData,JNI_ABORT);
}
}
I'm not understand here. If I don't send byte array to another players yet send that array back to C++ by calling nativeOnRealTimeMessageReceived() method from Java side, it runs fine and logs correctly. It's mean that with the same byte[] package converted from char* in C++, if I just pass it back to C++, it's correct, but if I send it through Google Play Game Services, it goes wrong. What does this mean?

How to Release jstring that is sent from Native Code back to Java?

I have the following native routine:
void sendMessage(const char* text)
{
JNIEnv* env;
if(!_jvm)
return;
_jvm->AttachCurrentThread(&env, NULL);
if(!_nativesCls)
_nativesCls = env->FindClass("com/foo/BaseLib");
if(_nativesCls == 0)
return;
jstring message = env->NewStringUTF(text);
if(!_sendStr)
_sendStr = env->GetStaticMethodID(_nativesCls, "onMessage", "(Ljava/lang/String;)V");
if(_sendStr)
env->CallStaticVoidMethod(_nativesCls, _sendStr, message);
//env->ReleaseStringUTFChars(message, text); // <----- * NOT WORKING
}
If I run this as is, it works fine up until memory fills up and I receive:
ReferenceTable overflow (max=512)
I thought adding the commented line above would fix the issue but it just causes the app to bomb out at that point.
Any suggestions?
DeleteLocalRef(). Just like any other Java object that was allocated within JNI. However, it will be automatically garbage-collected once the JNI method returns. Details here: http://download.oracle.com/javase/1.3/docs/guide/jni/spec/design.doc.html#1242