在android

时间:2016-09-07 16:03:48

标签: android android-ndk speex

目前我正在开发一个类似对讲机的音频实时聊天应用程序。此应用程序通过Android手机和远程服务器之间的tcp套接字传输音频字节。为了节省用户的移动互联网数据成本,我必须先编码从AudioRecord读取的音频字节,然后再将其发送到服务器。因此,我还需要解码从服务器收到的内容。我选择使用的编解码器是speex。虽然speex是用c ++编写的,我几乎没用,也很难理解它的语法和规则,但我找到了一些关于在an​​droid中使用speex的例子,包括jni文件,如何通过ndk build将它编译成.so文件,如何调用它的方法通过jni。感谢这些家伙的贡献,最后我在我的应用程序中使用了speex。我将比特率从16kb / s压缩到2kb / s,这确实是一项重大改进。但是,实施并不完美。小缺陷是编码方法不编码从AudioRecord读取的完整字节数组。例如,     speex.encode(readShortBuffer,0,缓冲液,readShortBuffer.length)。 readShortBuffer是一个320 * 12的短数组,缓冲区是一个字节数组,其长度为80 * 12,压缩比为8(2个字节等于short)。上面的命令应该用960(80 * 12)字节编码数据填充缓冲区。但是,我只有320字节的数据。其余的都是0.所以我必须使readShortBuffer和缓冲区更短来制作缓冲区完全填充。然后将每个小缓冲区组装成一个较长的缓冲区来表示AudioRecord读取的内容。但它效率很低。我要找的是找到一种在任意长度编码短数组的方法。以下是代码:

speex.cpp
#include <jni.h>

#include <string.h>
#include <unistd.h>

#include <include/speex/speex.h>

static int codec_open = 0;

static int dec_frame_size;
static int enc_frame_size;

static SpeexBits ebits, dbits;
void *enc_state;
void *dec_state;

static JavaVM *gJavaVM;

extern "C"
JNIEXPORT jint JNICALL Java_eu_ratikal_helloc_Speex_open(JNIEnv *env,
    jobject obj, jint compression) {
int tmp;

if (codec_open++ != 0)
    return (jint) 0;

speex_bits_init(&ebits);
speex_bits_init(&dbits);

enc_state = speex_encoder_init(&speex_wb_mode);
dec_state = speex_decoder_init(&speex_wb_mode);
 tmp = 1;
    speex_decoder_ctl(dec_state, SPEEX_SET_ENH, &tmp);
    speex_encoder_ctl(enc_state, SPEEX_SET_ENH, &tmp);
speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size);
speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);

return (jint) 0;
}

extern "C"
JNIEXPORT jint Java_eu_ratikal_helloc_Speex_encode(JNIEnv *env,
    jobject obj, jshortArray lin, jint offset, jbyteArray encoded,
    jint size) {

     jshort buffer[enc_frame_size];
        jbyte output_buffer[enc_frame_size];
    int nsamples = (size-1)/enc_frame_size + 1;
    int i, tot_bytes = 0;

    if (!codec_open)
        return 0;

    speex_bits_reset(&ebits);

    for (i = 0; i < nsamples; i++) {
        env->GetShortArrayRegion(lin, offset + i*enc_frame_size,      enc_frame_size, buffer);
        speex_encode_int(enc_state, buffer, &ebits);
    }
    //env->GetShortArrayRegion(lin, offset, enc_frame_size, buffer);
    //speex_encode_int(enc_state, buffer, &ebits);

    tot_bytes = speex_bits_write(&ebits, (char *)output_buffer,
                     enc_frame_size);
    env->SetByteArrayRegion(encoded, 0, tot_bytes,
                output_buffer);

        return (jint)tot_bytes;
}

extern "C"
JNIEXPORT jint JNICALL Java_eu_ratikal_helloc_Speex_decode(
    JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin,
    jint size) {

jbyte buffer[dec_frame_size];
jshort output_buffer[dec_frame_size];
jsize encoded_length = size;

if (!codec_open)
    return 0;

env->GetByteArrayRegion(encoded, 0, encoded_length, buffer);
speex_bits_read_from(&dbits, (char *) buffer, encoded_length);
speex_decode_int(dec_state, &dbits, output_buffer);
env->SetShortArrayRegion(lin, 0, dec_frame_size, output_buffer);

return (jint) dec_frame_size;
}

extern "C"
JNIEXPORT jint JNICALL Java_eu_ratikal_helloc_Speex_getFrameSize(
    JNIEnv *env, jobject obj) {

if (!codec_open)
    return 0;
return (jint) enc_frame_size;

}

Speex.java
public class Speex {

/* quality
 * 1 : 4kbps (very noticeable artifacts, usually intelligible)
 * 2 : 6kbps (very noticeable artifacts, good intelligibility)
 * 4 : 8kbps (noticeable artifacts sometimes)
 * 6 : 11kpbs (artifacts usually only noticeable with headphones)
 * 8 : 15kbps (artifacts not usually noticeable)
 */
private static final int DEFAULT_COMPRESSION = 1;
public Speex() {
}

public int init() {
    load();
    return open(DEFAULT_COMPRESSION);

}

private void load() {
    try {
        System.loadLibrary("speex");
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

public native int open(int compression);
public native int getFrameSize();
public native int decode(byte encoded[], short lin[], int size);
public native int encode(short lin[], int offset, byte encoded[], int size);
public native void close();

}

MainActivity.java
recorder.read(readShortBuffer, 0, readShortBuffer.length);       
int r = speex.encode(readShortBuffer,0,buffer,readShortBuffer.length);
os.write(buffer, 0, buffer.length); 

我的帖子太长了,无法通读。谢谢你们耐心等待,非常感谢任何帮助!

0 个答案:

没有答案