我想在我的Android应用程序中使用libspeex,所以我基本上将库从LineageOS 14.1 sources复制到项目中。然后我写了一个简单的jni:
#include <jni.h>
#include <android/log.h>
#include <include/speex/speex_bits.h>
#include <include/speex/speex.h>
#include <stdlib.h>
#include <string.h>
SpeexBits enc_bits, dec_bits;
void* enc_state = NULL;
void* dec_state = NULL;
int frame_size = 0;
JNIEXPORT jint JNICALL
Java_dt_test_Codec_encode(JNIEnv *env, jclass type, jshortArray input_, jbyteArray output_)
{
if(enc_state == NULL)
{
return 0;
}
//check the input size is correct
int input_size = (int)(*env)->GetArrayLength(env, input_);
if(input_size != frame_size)
{
return 0;
}
//encode
jshort* input = (*env)->GetShortArrayElements(env, input_, NULL);
speex_bits_reset(&enc_bits);
speex_encode_int(enc_state, input, &enc_bits);
//write output
int nbBytes = 0;
char* output = (char*)malloc((size_t)frame_size); //output can't be bigger than the input
memset(output, 0, (size_t)frame_size);
nbBytes = speex_bits_write(&enc_bits, output, frame_size);
(*env)->SetByteArrayRegion(env, output_, 0, nbBytes, (jbyte*)output);
//cleanup
free(output);
(*env)->ReleaseShortArrayElements(env, input_, input, 0);
return nbBytes;
}
JNIEXPORT void JNICALL
Java_dt_test_Codec_closeEncoder(JNIEnv *env, jclass type)
{
speex_bits_destroy(&enc_bits);
speex_encoder_destroy(enc_state);
enc_state = NULL;
memset(&enc_bits, 0, sizeof(enc_bits));
frame_size = 0;
}
JNIEXPORT jint JNICALL
Java_dt_test_Codec_getFramesize(JNIEnv *env, jclass type)
{
return frame_size;
}
JNIEXPORT void JNICALL
Java_dt_test_Codec_decode(JNIEnv *env, jclass type, jbyteArray input_, jshortArray output_)
{
if(dec_state == NULL)
{
return;
}
jbyte* input = (*env)->GetByteArrayElements(env, input_, NULL);
int input_size = (int)(*env)->GetArrayLength(env, input_);
speex_bits_read_from(&dec_bits, (char*)input, input_size);
short* output = (short*)malloc((size_t)frame_size);
speex_decode_int(dec_state, &dec_bits, output);
(*env)->SetShortArrayRegion(env, output_, 0, frame_size, output);
(*env)->ReleaseByteArrayElements(env, input_, input, 0);
free(output);
}
JNIEXPORT void JNICALL
Java_dt_test_Codec_closeDecoder(JNIEnv *env, jclass type)
{
speex_bits_destroy(&dec_bits);
speex_decoder_destroy(dec_state);
dec_state = NULL;
memset(&dec_bits, 0, sizeof(dec_bits));
}
JNIEXPORT void JNICALL
Java_dt_test_Codec_initEncoder(JNIEnv *env, jclass type)
{
//cleanup old stuff if necessary
if(enc_state != NULL)
{
Java_dt_test_Codec_closeEncoder(env, NULL);
}
//setup encoder
speex_bits_init(&enc_bits);
enc_state = speex_encoder_init(&speex_uwb_mode);
speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &frame_size);
int quality = 7;
speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &quality);
}
JNIEXPORT void JNICALL
Java_dt_test_Codec_initDecoder(JNIEnv *env, jclass type)
{
//cleanup old stuff if necessary
if(dec_state != NULL)
{
Java_dt_test_Codec_closeDecoder(env, NULL);
}
//setup decoder
speex_bits_init(&dec_bits);
dec_state = speex_decoder_init(&speex_uwb_mode);
}
我只是在未初始化的0数组上调用encode,并在无限循环中解码以下结果以查看jni的运行情况。但是,我似乎总是在渲染线程中发生本机崩溃,并在地址0x60处发生分段错误。地址始终相同,线程也是如此。堆栈跟踪只是一堆未知数。在进一步调查中,如果我只在循环中编码而不是解码,则不会发生这种崩溃。我已经按照说明如何使用speex编解码器完全来自:the speex website我无法想到编解码器如何在Android的渲染线程中导致崩溃的任何好的解释我甚至都没碰过那个区域。
Java部分:
Codec.initEncoder();
Codec.initDecoder();
WAVBUFFERSHORTS = Codec.getFramesize();
while (true)
{
short[] wavshorts = new short[WAVBUFFERSHORTS];
Codec.encode(wavshorts, encbuffer);
short[] reconstruct = new short[WAVBUFFERSHORTS];
Codec.decode(encbuffer, reconstruct);