Android Studio — libmp3lame NDK

В настоящее время я пытаюсь внедрить libmp3lame в свое приложение для Android, чтобы декодировать данные MP3 в PCM.

Чтобы использовать libmp3lame, мне нужно использовать реализацию JNI/NDK, а для декодирования MP3 в PCM мне нужно использовать функцию hip_decode() из libmp3lame.

Эта функция создается в виде файла "lame.h":

int CDECL hip_decode( hip_t           gfp
                , unsigned char * mp3buf
                , size_t          len
                , short           pcm_l[]
                , short           pcm_r[]
                );

Мое приложение работает следующим образом: я получаю один образец монофонического MP3 из WebSocket. Мне нужно декодировать этот образец из MP3 в PCM, а затем записать его в свой AudioTrack, чтобы воспроизвести. Это прямая трансляция, поэтому мне нужна минимально возможная задержка. Я использовал JLayer, и я мог прекрасно понять, что говорил говорящий, но у меня была проблема с «хэшем» / роботизированным голосом. Казалось, что в начале каждого сэмпла у меня было какое-то значение 0, и это вызывало какой-то странный эффект. Теперь мне нужно сделать то же самое с libmp3lame. Итак, что я хочу сделать, это следующее. Каждый раз, когда вызывается мой даже прием WebSocket, мне нужно взять полученный массив байтов (содержащий аудиоданные) и декодировать его в PCM. Мне нужно иметь byte[] или short[] в формате PCM, а затем воспроизвести его в звуковой дорожке. Проблема в том, что я не уверен, как использовать hip_decode для этого. Я действительно не знаком с программированием на C, поэтому может быть очень простой способ сделать это, но я просто не могу этого сделать. Прямо сейчас в моем wrapper.c у меня есть это:

JNIEXPORT void JNICALL Java_com_example_jneb_myapplication_MainActivity_decoderInit(JNIEnv *env, jobject jobj,
    ) {
    hip = hip_decode_init();

}

Я не уверен, какие pcm_l и pcm_r необходимы для использования функции hip_decode. Вот еще немного информации о функции:

/*********************************************************************
* input 1 mp3 frame, output (maybe) pcm data.
*
*  nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r);
*
* input:
*    len          :  number of bytes of mp3 data in mp3buf
*    mp3buf[len]  :  mp3 data to be decoded
*
* output:
*    nout:  -1    : decoding error
*           0    : need more data before we can complete the decode
*           >0    : returned 'nout' samples worth of data in pcm_l,pcm_r
*    pcm_l[nout]  : left channel data
*    pcm_r[nout]  : right channel data
*
*********************************************************************/

РЕДАКТИРОВАТЬ: благодаря ответу bukkojot я смог понять, для чего использовались pcm_l и pcm_r.

Вот обновление моего кода:

JNIEXPORT jshortArray JNICALL Java_com_example_jneb_myapplication_AudioTrackClass_decoderInit(JNIEnv *env, jobject jobj,
                                                                                     jbyteArray data, jint size) {
jsize mp3Len =  (*env)->GetArrayLength(env, data);
// Print the data.length = 96
LOGI("JNI integer: %d", mp3Len);
// mp3 contains all 96 values
jbyte *mp3 = (*env)->GetByteArrayElements(env, data, 0);

// Trying to decode mp3 into PCM
int x = hip_decode(hip, (unsigned char*) mp3, (size_t) mp3Len, pcm_l, pcm_r);

jshortArray pcmBuffer;
pcmBuffer = (*env)->NewShortArray(env, mp3Len);
(*env)->SetShortArrayRegion(env, pcmBuffer, 0, mp3Len, pcm_l);
// Releasing byte array
(*env)->ReleaseByteArrayElements(env, data, mp3, 0);

// Returning
return pcmBuffer;

}

Прямо сейчас pcmBuffer возвращает только значение 0, и hip_decode тоже возвращает только значение 0. В документе говорится, что если hip_decode возвращает 0, то функция hip_decode «требует больше данных, прежде чем мы сможем завершить декодирование». Я уже передаю функции все данные, которые у меня есть. Что я делаю неправильно с функцией hip_decode?


person jineb92    schedule 21.11.2017    source источник


Ответы (1)


Я не уверен, какие pcm_l и pcm_r необходимы для использования функции hip_decode.

Это указатели на буферы, куда будет записан декодированный PCM.

Выделите где-нибудь достаточно памяти для несжатого звука, например:

signed short *pcm_l=malloc(1000000); // make sure it's enough
signed short *pcm_r=malloc(1000000);

А затем просто передать их функции декодирования. Функция вернет количество полезных выборок. Передайте эти данные в часть Java и запишите в AudioTrack.

person bukkojot    schedule 22.11.2017
comment
Хорошо, я попробую это. Должен ли мой буфер быть таким большим? Эта функция будет вызываться для каждого небольшого фрагмента данных mp3, не вызовет ли это проблемы с памятью? - person jineb92; 22.11.2017
comment
Если вы выделяете память только в инициализации, вы будете использовать только 2 МБ памяти. Это очень мало в наши дни. Если вы хотите уточнить, рассчитайте максимальный размер аудио, которое может быть несжатым, например, 5 секунд * 48000 семплрейт * 2 байта на сэмпл = 480000 байт на канал. - person bukkojot; 22.11.2017
comment
Спасибо за ваш ответ, позволил мне понять, что я должен делать. Я отредактировал вопрос, так как все еще сталкиваюсь с проблемой. - person jineb92; 22.11.2017
comment
Во-первых, если вы хотите вернуть указатель, вы должны return pcm_l;, а не return *pcm_l;. Во-вторых, вы должны определить распределение в каком-то другом месте, иначе вы очень скоро достигнете нехватки памяти, делайте это только в функции инициализации. И наконец, прочитайте о копировании данных в мир Java. - person bukkojot; 22.11.2017
comment
Большое спасибо за ваше терпение. Что вы имеете в виду, говоря о копировании данных в мир Java? Я хотел бы прочитать об этом, но я действительно не знаю, что вы имеете в виду. - person jineb92; 22.11.2017
comment
Google для GetShortArrayElements, memcpy и ReleaseShortArrayElements. - person bukkojot; 22.11.2017
comment
Большое спасибо. Я посмотрю на это! - person jineb92; 22.11.2017
comment
Давайте продолжим обсуждение в чате. - person jineb92; 22.11.2017
comment
Я провел достаточно времени, поэтому я не хочу болтать - person bukkojot; 22.11.2017
comment
Без проблем. Спасибо за вашу помощь :). - person jineb92; 22.11.2017