I use opengl es with jni to play YUV video, the video is OK.
But when i close GLSurfaceView and return to main Activity.
The characters and images display in main Activity become messed.
All icons becomes black solid rectangle. All the characters becomes white solid rectangle.
The code look likes come from WebRTC and modified by someone else.
I'm a C programmer and not familiar with OpenGl. So it's a really big problem for me to solve this problem.
I create handle in JNI like this:
bool isAttached = false;
JNIEnv* env = NULL;
if (_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
// try to attach the thread and get the env
// Attach this thread to JVM
jint res = _jvm->AttachCurrentThread(&env, NULL);
// Get the JNI env for this thread
if ((res < 0) || !env) {
LOGE("%s: Could not attach thread to JVM (%d, %p)",
__FUNCTION__, res, env);
return -1;
}
isAttached = true;
}
// get the ViEAndroidGLES20 class
jclass javaRenderClassLocal = reinterpret_cast<jclass> (env->FindClass("com/wg/rgc/library/gl/ViEAndroidGLES20"));
if (!javaRenderClassLocal) {
LOGE("%s: could not find ViEAndroidGLES20", __FUNCTION__);
return -1;
}
_javaRenderClass = reinterpret_cast<jclass> (env->NewGlobalRef(javaRenderClassLocal));
if (!_javaRenderClass) {
LOGE("%s: could not create Java SurfaceHolder class reference",
__FUNCTION__);
return -1;
}
// Delete local class ref, we only use the global ref
env->DeleteLocalRef(javaRenderClassLocal);
jmethodID cidUseOpenGL = env->GetStaticMethodID(_javaRenderClass,
"UseOpenGL2",
"(Ljava/lang/Object;)Z");
if (cidUseOpenGL == NULL) {
LOGE("%s: could not get UseOpenGL ID", __FUNCTION__);
return false;
}
jboolean res = env->CallStaticBooleanMethod(_javaRenderClass,
cidUseOpenGL, (jobject) _ptrWindow);
// create a reference to the object (to tell JNI that we are referencing it
// after this function has returned)
_javaRenderObj = reinterpret_cast<jobject> (env->NewGlobalRef((jobject)_ptrWindow));
if (!_javaRenderObj)
{
LOGE("%s: could not create Java SurfaceRender object reference",
__FUNCTION__);
return -1;
}
// get the method ID for the ReDraw function
_redrawCid = env->GetMethodID(_javaRenderClass, "ReDraw", "()V");
if (_redrawCid == NULL) {
LOGE("%s: could not get ReDraw ID", __FUNCTION__);
return -1;
}
_registerNativeCID = env->GetMethodID(_javaRenderClass,
"RegisterNativeObject", "(J)V");
if (_registerNativeCID == NULL) {
LOGE("%s: could not get RegisterNativeObject ID", __FUNCTION__);
return -1;
}
_deRegisterNativeCID = env->GetMethodID(_javaRenderClass,
"DeRegisterNativeObject", "()V");
if (_deRegisterNativeCID == NULL) {
LOGE("%s: could not get DeRegisterNativeObject ID",
__FUNCTION__);
return -1;
}
JNINativeMethod nativeFunctions[2] = {
{ "DrawNative",
"(J)V",
(void*) &AndroidNativeOpenGl2Channel::DrawNativeStatic, },
{ "CreateOpenGLNative",
"(JII)I",
(void*) &AndroidNativeOpenGl2Channel::CreateOpenGLNativeStatic },
};
if (env->RegisterNatives(_javaRenderClass, nativeFunctions, 2) == 0) {
LOGE("%s: Registered native functions", __FUNCTION__);
}
else {
LOGE("%s: Failed to register native functions", __FUNCTION__);
return -1;
}
env->CallVoidMethod(_javaRenderObj, _registerNativeCID, (jlong) this);
if (isAttached) {
if (_jvm->DetachCurrentThread() < 0) {
LOGE("%s: Could not detach thread from JVM", __FUNCTION__);
}
}
LOGE("%s done", __FUNCTION__);
if (_openGLRenderer.SetCoordinates(0, 1.0, 1.0, 0, 0) != 0) {
return -1;
}
isAlreadyInit = 1; //Init finish
return 0;
And i close this handle in JNI like this:
bool isAttached = false;
JNIEnv* env = NULL;
if (_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
// try to attach the thread and get the env
// Attach this thread to JVM
jint res = _jvm->AttachCurrentThread(&env, NULL);
// Get the JNI env for this thread
if ((res < 0) || !env) {
LOGE("%s: Could not attach thread to JVM (%d, %p)",
__FUNCTION__, res, env);
env = NULL;
} else {
isAttached = true;
}
}
if (env && _deRegisterNativeCID) {
env->CallVoidMethod(_javaRenderObj, _deRegisterNativeCID);
}
env->DeleteGlobalRef(_javaRenderObj);
env->DeleteGlobalRef(_javaRenderClass);
if (isAttached) {
if (_jvm->DetachCurrentThread() < 0) {
LOGE("%s: Could not detach thread from JVM",
__FUNCTION__);
}
}
The method env->CallVoidMethod(_javaRenderObj, _deRegisterNativeCID); called Java method in Java src:
nativeFunctionLock.lock();
nativeFunctionsRegisted = false;
openGLCreated = false;
this.nativeObject = 0;
nativeFunctionLock.unlock();
The code in Java src looks like extends GLSurfaceView and implements GLSurfaceView.Renderer. In JNI the code registered some method as native method in Java.
The code is complicated for me to understand , but my boss request me fix this problem quickly.
I can't figure out why after playing YUV video , every image and characters become rectangles. How did this happen?
you probably delete the texture after rendering, you can try to keep them, and deallocate them when app terminated
Related
I am using the Android NDK on a Motorola G6, running Android 8.0.
I am using JNI to perform periodic callbacks to my Java code in the following:
if ( data_arg && g_vm && g_obj ) {
jint res = (*g_vm)->GetEnv(g_vm, (void**)&g_env, JNI_VERSION_1_6);
if (res != JNI_OK) {
res = (*g_vm)->AttachCurrentThread(g_vm, &g_env, NULL);
if (JNI_OK != res) {
return NULL;
}
jclass interfaceClass = (*g_env)->GetObjectClass(g_env,g_obj);
if (!interfaceClass) {
return NULL;
}
g_mid = (*g_env)->GetMethodID(g_env,interfaceClass, "readCallback", "(I[B)V");
if (!g_mid){
return NULL;
}
jbyteArray dataByteArray = (*g_env) -> NewByteArray(g_env,data_arg->len);
(*g_env)->SetByteArrayRegion(g_env, dataByteArray, 0,data_arg->len,(jbyte*)data_arg->data);
(*g_env)->CallVoidMethod(g_env, g_obj,(jmethodID)g_mid, (jint)data_arg->len, (jbyteArray)dataByteArray);
(*g_env)->DeleteLocalRef(g_env,dataByteArray);
(*g_env)->DeleteLocalRef(g_env,res);
(*g_env)->DeleteLocalRef(g_env,interfaceClass);
}
The g_vm and g_obj variables are initialized when the app starts up with the following:
JNIEXPORT jint JNICALL Java_com_abc_register(JNIEnv *env, jobject obj) {
int returnValueBool = 1;
(*env)->GetJavaVM(env, &g_vm);
g_obj = (*env)->NewGlobalRef(env,obj);
g_env = env;
g_res = JNI_ERR;
return (jint)returnValueBool;
}
The class that calls the above register method is a static instance.
The callbacks occur about about 20 Hz. The callbacks are called from a single thread, which is why the DetachCurrentThread method is not called. The app typically runs ok for a few minutes, until it crashes.
The crash produces the following stack trace:
* thread #65, name = 'JVMTI Agent thr', stop reason = signal SIGSEGV: invalid address (fault address: 0x765630)
* frame #0: 0x92b8d44e libopenjdkjvmti.so`openjdkjvmti::MethodUtil::GetMethodName(_jvmtiEnv*, _jmethodID*, char**, char**, char**) + 410
Before the crash occurs, memory and cpu usage are not excessive, nor is the thread count too high. I do not believe there is a memory leak based on the android profiler view
shown here
Thanks a ton in advance!
I've got a problem similar to the question/answer posed here: https://stackoverflow.com/a/22461014. However, the difference is that the I'm trying to decode from the UnityMain thread (Unity's main loop). Calling from Update(), I pass a byte array and a textureId to MediaCodec's decoder.
public void decodeFrameToTexture(BytePointer pixels, int len, int textureID) {
if ( this.textureID != textureID) {
Log.d(TAG, "TextureID changed: " + textureID);
this.textureID = textureID;
SurfaceTexture surfaceTexture = new SurfaceTexture(textureID);
mSurface = new Surface(surfaceTexture);
outputSurface = new CodecOutputSurface(width, height, textureID);
}
... then we do the decoding (basically a copy of the code at http://bigflake.com/mediacodec/ExtractMpegFramesTest.java.txt but without the while loop, as this is frame-by-frame. Also copied the CodecOutputSurface and supporting classes basically verbatim).
Finally we have this code:
decoder.releaseOutputBuffer(decoderStatus, info.size != 0 && outputSurface != null /*render*/);
if ( outputSurface != null ) {
outputSurface.awaitNewImage();
outputSurface.drawImage(true);
}
The trouble is, awaitNewImage() always times out without getting a frame, leading back to the problem referenced here, that the onFrameAvailable() callback is never getting called.
For reference, UnityMain does not have a Looper component. When running this code:
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(looper);
} else {
mEventHandler = null;
}
the assigned looper from that thread is the MainLooper looper. Any ideas would be appreciated. As stated by #fadden, "the trick is to make sure that frame-available events arrive on a different thread from the one sitting in awaitNewImage()". Given that we're running
mSurfaceTexture.setOnFrameAvailableListener(this);
from UnityMain, I think this satisfies this requirement? The callback should be called from the "main" thread?
I'd like to monitor Informatica ETL Workflows from custom Java program, via informatica Development Platform (LMapi), v9.1
I already have got C program, it works fine, but it would be great port to Java.
We get a lot of C DLLs, with asynchronous functions e.g.: JavaLMApi.dll, INFA_LMMonitorServerA (with detailed event-log possibilities)
In header we can see:
PMLM_API_EXTSPEC INFA_API_STATUS
INFA_LMMonitorServerA
(
INFA_UINT32 connectionId,
struct INFA_LMAPI_MONITOR_SERVER_REQUEST_PARAMS *request,
void *clientContext,
INFA_UINT32 *requestId
);
There is no any documentation for this problem, only with this information should I use for soulution.
The question is: how to call/use INFA_LMMonitorServerA in Java? (Dll-load with JNA/JNI is not problem, only the callback).
static INFA_UINT32 nConnectionId = 0;
/* C-"skeleton": */
void GetSrvDetailcallback(struct INFA_API_REPLY_CONTEXT* GetSrvDetailReplyCtxt)
{
INFA_API_STATUS apiRet;
struct INFA_LMAPI_SERVER_DETAILS *serverDetails = NULL;
char *serverStatus = NULL;
/* Check if the return status is Acknowledgement */
if (GetSrvDetailReplyCtxt->returnStatus == INFA_REQUEST_ACKNOWLEDGED)
{
fprintf(stdout, "\nINFA REQUEST ACKNOWLEDGED \n\n",NULL);
return;
}
apiRet = INFA_LMGetServerDetailsResults(GetSrvDetailReplyCtxt, &serverDetails);
/* Check the return code if if is an error */
if (INFA_SUCCESS != apiRet)
{
fprintf(stderr, "Error: INFA_LMGetServerDetailsResults returns %d\n", apiRet);
return;
}
printResults(serverDetails);
}
static void myServer()
{
struct INFA_LMAPI_CONNECT_SERVER_REQUEST_PARAMS_EX connectParamsex;
INFA_API_STATUS apiRet;
struct INFA_LMAPI_LOGIN_REQUEST_PARAMS loginparams;
apiRet = INFA_LMLogin(nConnectionId, &loginparams, NULL);
if (INFA_SUCCESS != apiRet)
{
fprintf(stderr, "Error: INFA_LMLogin returns %d\n", apiRet);
return;
}
struct INFA_LMAPI_MONITOR_SERVER_REQUEST_PARAMS strMonitorRequestParams;
//Only Running Tasks
strMonitorRequestParams.monitorMode = INFA_LMAPI_MONITOR_RUNNING;
strMonitorRequestParams.continuous = INFA_TRUE;
/* Get Server Details */
INFA_UINT32 GetSrvDetailsrequestId = 0;
/* Register a callback function. */
INFA_LMRegisterCallback(INFA_LMAPI_MONITOR_SERVER, &GetSrvDetailcallback);
apiRet = INFA_LMMonitorServerA(nConnectionId, &strMonitorRequestParams, NULL, &GetSrvDetailsrequestId);
if (INFA_SUCCESS != apiRet && INFA_REQUEST_PENDING != apiRet)
{
fprintf(stderr, "Error: INFA_LMMonitorServerA returns %d\n", apiRet);
return;
}
}
I'm creating a program in Java that will interface with a C library to take images from hardware and display with OpenGL (using JOGL). So the workflow is this:
Hardware -> C -> disk image file -> Java -> JOGL
I have the Java -> JOGL part working fine. The images are displayed fully and I can load multiple images at a time. I also have the Hardware -> C working as well, and a temporary viewer in C is showing that the images are being created just fine.
The crux of the problem is this: I want to be able to launch the main() method of the Java program in C and then display the image using only JNI code in C (using static methods I've created). However, when I do this, the image is truncated, where I only get the top 20 or so rows. I do know that I'm loading the entire image because I can check the pixel values for every pixel in the image. Only the display is truncated. The same number of pixels are shown for every image I load.
This is the C code in a nutshell:
int main () { ...
HMODULE * jvm_dll;
//HWND hWnd = GetConsoleWindow();
//ShowWindow( hWnd, SW_HIDE );
env = create_vm(&jvm, jvm_dll);
if (env == NULL) { return 1; }
//Create a new String array with one blank String
cls = (*env)->FindClass(env,"java/lang/String");
arg = (*env)->NewStringUTF(env,"");
args = (jobjectArray)(*env)->NewObjectArray(env, 1, cls, arg);
//Call the main method with the String array argument
cls = (*env)->FindClass(env, "path/to/package/program");
mID = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
(*env)->CallStaticVoidMethod(env, cls, mID, args);
PrintStackTrace(env);
blockAndClose(jvm, env, jvm_dll);
return ret;
}
int blockAndClose() {...
int ret = 0;
if (jvm == 0 || env == 0) {
FreeLibrary(*jvm_dll);
return 1;
}
ret = (*jvm)->DestroyJavaVM(jvm);
if(jvm_dll) {
FreeLibrary(*jvm_dll);
jvm_dll = 0;
}
env = 0;
jvm = 0;
return ret;
}
I know that I've only posted the C portion, but the Java portion is working when I run it purely in Java. At this point, the C portion is simply a "launcher" of sorts, so I'm wondering why it affects the running of the code. Any suggestions?
EDIT:
Here's the code for loading images. I use JAI to load in the image as a PlanarImage type (TiledImage class is a subclass).
tempI = JAI.create("fileload", path);
DataBufferUShort dbOut = null;
ColorModel CM = PlanarImage.getDefaultColorModel(DataBuffer.TYPE_USHORT, tempI.getNumBands());
SampleModel SM = CM.createCompatibleSampleModel(tempI.getWidth(), tempI.getHeight());
//Ensure that the buffer is in the internal format (USHORT)
if (tempI.getData().getDataBuffer().getDataType() != DataBuffer.TYPE_USHORT) {
DataBuffer dBIn = tempI.getData().getDataBuffer();
short [] dBPixels = new short[dBIn.getSize()];
switch(dBIn.getDataType()) {
case DataBuffer.TYPE_BYTE:
DataBufferByte dBByte = (DataBufferByte)dBIn;
byte [] pByte = dBByte.getData();
for (int b = 0; b < pByte.length; b++)
{ dBPixels[b] = (short)((double)pByte[b] / 0xFF * 0xFFFF); }
dbOut = new DataBufferUShort(dBPixels, dBPixels.length);
break;
case DataBuffer.TYPE_SHORT:
DataBufferShort dBShort = (DataBufferShort)dBIn;
dBPixels = dBShort.getData();
dbOut = new DataBufferUShort(dBPixels, dBPixels.length);
break;
} //SWITCH DATA TYPE --END
WritableRaster rs = Raster.createWritableRaster(SM, dbOut, new Point(0,0));
tempI = new TiledImage(0,0,tempI.getWidth(),tempI.getHeight(),0,0,SM,CM);
((TiledImage)tempI).setData(rs);
}
Can anybody explain to me why can I get a callback when jvm allocates some java objects, but not others? Here is what I am doing:
static jvmtiCapabilities capa;
static jvmtiEnv* jvmti = NULL;
static const char* fileName = "C:\\temp\\ObjectInitCallbackDump.txt";
static ofstream outFileStream;
void JNICALL callbackObjectAllocation ( jvmtiEnv* jvmti_env,
JNIEnv* jni_env,
jthread thread,
jobject object,
jclass object_klass,
jlong size )
{
char* generic_ptr_class;
char* class_name;
jvmtiError error;
error = jvmti_env->GetClassSignature(object_klass, &class_name, &generic_ptr_class);
if (check_jvmti_error(jvmti_env, error, "Failed to get class signature")) {
return;
}
outFileStream << class_name << std::endl;
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
jint result;
jvmtiError error;
jvmtiEventCallbacks callbacks;
outFileStream.open(fileName,ios::trunc);
result = jvm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_1);
if (result != JNI_OK || jvmti == NULL) {
printf("error\n");
return JNI_ERR;
} else {
printf("loaded agent\n");
}
(void) memset(&capa, 0, sizeof(jvmtiCapabilities));
capa.can_generate_vm_object_alloc_events = 1;
error = jvmti->AddCapabilities(&capa);
if (check_jvmti_error(jvmti, error, "Unable to set capabilities") != JNI_OK) {
return JNI_ERR;
}
(void) memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMObjectAlloc = &callbackObjectAllocation;
error = jvmti->SetEventCallbacks(&callbacks, (jint) sizeof(callbacks));
if (check_jvmti_error(jvmti, error, "Unable to set callbacks") != JNI_OK) {
return JNI_ERR;
}
error = jvmti->SetEventNotificationMode( JVMTI_ENABLE,
JVMTI_EVENT_VM_OBJECT_ALLOC,
(jthread) NULL);
if (check_jvmti_error(jvmti, error,
"Unable to set method entry notifications") != JNI_OK) {
return JNI_ERR;
}
return JNI_OK;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
outFileStream.close();
}
When I examine the file that I create, I do not see the classes that I am interested in, although I know they are there and NetBeans tells me there is exactly one instance of that class in the jvm. Any thoughts???
Nikita
For performance reasons, the JVMTI only supports allocation events for objects that cannot be detected through bytecode instrumentation (BCI), as explained in the JVMTI VMObjectAlloc event documentation. This means that the event will not be triggered for most object allocations. I assume that the allocations you are missing fall within that category.
Fortunately, it's not really too difficult to intercept all object allocations using BCI. The HeapTracker demo illustrates precisely how to intercept all object allocations in a JVMTI agent using java_crw_demo in addition to the VMObjectAlloc events.
Maybe you are just not hitting your check?
If this is the code you actually run then you have a bug; you missed the ; at the end of the object
Your compare should be like this:
if (strcmp(class_name,"Ljavax/swing/JFrame;") == 0) {
printf("Got the sucker!!!");
}