JNI byteArray passing help
I have been running into issues with my Android app closing (no errors or anything) when trying to execute the following code:
JNIEXPORT void Java_teamjeff_oggstreamtest_MainTest_audioFunc(JNIEnv* env, jobject obj) {
//<REMOVED VARIABLE INITIALIZATION>
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID writeDataFunc = (*env)->GetMethodID(env, cls, "writeToAudioTrack", "([B)V");
if (!writeDataFunc) return;
jmethodID readDataFunc = (*env)->GetMethodID(env, cls, "readFromBuffer", "([B)I");
if (!readDataFunc) return;
rawDataRead = (*env)->NewByteArray(env, 4096);
bytes = (*env)->CallIntMethod(env, obj,readDataFunc, &rawDataRead);
char* carr = (*env)->GetByteArrayElements(env, rawDataRead, NULL);
memcpy(buffer, carr, bytes);
(*env)->DeleteLocalRef(env, rawDataRead);
//<REMOVED REST OF FUNCTION>
}
I've tracked the "trouble" code to the bytes = (*env)->CallIntMethod(env, obj,readDataFunc, &rawDataRead);
line. If I return before this line, my app doesn't close, but if I return immediately after this line, my app closes randomly without even an error.
Here is the JAVA code:
package teamjeff.oggstreamtest;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.os.Handler;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import com.Ostermiller.util.CircularByteBuffer;
public class MainTest extends Activity {
public static Handler mHandler = new Handler();
private final CircularByteBuffer cbb = new CircularByteBuffer(1024*512, true);
public AudioTrack mAudioTrack;
static {
System.loadLibrary("vorbis-decoder");
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
final Socket test = new Socket(InetAddress.getByName(<HOME SERVER URL>), <PORT>);
new Thread(
new Runnable(){
public void run(){
try {
while(!test.isClosed()) {
byte[] temp = new byte[4096];
int bytes = test.getInputStream().read(temp, 0, 4096);
cbb.getOutputStream().write(temp, 0, bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
).start();
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
44100,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
1024*64,
AudioTrack.MODE_STREAM);
mAudioTrack.play();
new Thread(new Runnable() {
public void run() {
开发者_开发问答 audioFunc();
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
public native void audioFunc();
@SuppressWarnings("unused")
private void writeToAudioTrack(final byte[] media) {
mHandler.post(new Runnable() {
public void run() {
mAudioTrack.write(media, 0, media.length);
}
});
}
@SuppressWarnings("unused")
private int readFromBuffer(byte[] buffer) {
try {
return cbb.getInputStream().read(buffer, 0, buffer.length);
} catch (IOException e) {
e.printStackTrace();
}
return -1;
}
}
I have searched for days here and on Google on how to accomplish what I want to do. My code above is the gluing together of various code snippets I've found around the internet with tweaks to fit my use-case.
What I'm trying to accomplish:
I will read in data from a socket in my Android App, pass this data to my C code for decoding (though the algorithm reads it in piece by piece during the algorithm, meaning I can't pass in the byteArray from Java to C and call my C function multiple times since the decoder sometimes uses data from previous read-in bytes). My C code does the decoding, and passes the PCM data back to my Android app to be played on an AudioTrack.
I am using a circular buffer to buffer in the data from the socket.
When I try to debug my Android App, I set break points at the entries to both the read and write functions and they never seem to be called.
Am I doing something wrong when I pass the byteArray from my C code to JAVA to be filled with data? Should I be doing this an alternative way?
A few notes that might help.
&rawDataRead is wrong. Lose the &. You just pass the ref that you get, not the address of it.
you don't need to delete that local ref. All local refs are deleted when a native function returns. I'm fairly confident that this is true in java, but the android situation may be different.
You don't have to keep retrieving the method ids. You can get them once and hang onto them.
精彩评论