Кодирование и декодирование аудио потока с Speex в Android

В настоящее время я занимаюсь разработкой приложения для живого чата, которое похоже на рацию. Это приложение передает аудиобайты через сокеты TCP между телефонами Android и удаленным сервером. Чтобы сохранить стоимость данных мобильного интернета для моих пользователей, я должен кодировать аудиобайты, считанные из AudioRecord, прежде чем отправлять их на сервер. В результате мне также нужно декодировать то, что я получил с сервера, конечно. Кодек, который я выбрал для использования, - speex. Хотя speex написан на C++, который я почти не использую и плохо понимаю его грамматику и правила, я нашел несколько примеров использования speex в android, включая файл jni, как скомпилировать его в.so файлы с помощью ndk build, как вызвать его метод через JNI. Спасибо за вклад этих ребят, наконец-то я заставил speex работать в моем приложении. Я сжал битрейт до 2 кбит / с с 16 кбит / с, что действительно является значительным улучшением. Однако реализация не идеальна. Небольшой недостаток заключается в том, что метод кодирования не кодирует полный массив байтов, считанный из AudioRecord. Например, speex.encode(readShortBuffer,0, буфер,readShortBuffer.length). ReadShortBuffer - это короткий массив 320*12, буфер - это байтовый массив, его длина - 80*12, степень сжатия - 8(2 байта равны короткому). Предполагается, что приведенная выше команда заполняет буфер 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 ответов

Другие вопросы по тегам