Я хочу использовать libspeex в своем приложении для Android, поэтому я по сути скопировал библиотеку в проект из источников LineageOS 14.1 . Затем я написал простой 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 с и декодирую следующий результат в бесконечном цикле, чтобы увидеть jni в действии. Тем не менее я, кажется, всегда получаю собственный сбой в потоке рендеринга с ошибкой сегментации по адресу 0x60. Адрес всегда один и тот же, как и поток. Трассировка стека - это просто стэк неизвестных. При дальнейшем расследовании этого сбоя не произойдет, если я только кодирую в цикле, а не декодирую. Я следовал инструкциям о том, как использовать кодек speex точно из: веб-сайта speex Я не могу придумать никакого хорошего объяснения того, как кодек вызывает сбой в потоке рендеринга 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);