Подтвердить что ты не робот

Можно ли получить байтовый буфер непосредственно из аудиообъекта в OpenSL ES (для Android)?

Я хотел бы получить байт-буфер из аудио-ресурса с помощью объекта OpenSL ES FileDescriptor, поэтому я могу повторно установить его в SimpleBufferQueue, вместо того, чтобы использовать SL-интерфейсы для воспроизведения/остановки/поиска файла.

Существуют три основные причины, по которым я хотел бы напрямую обращаться с образцами байтов:

  • OpenSL использует слой AudioTrack для воспроизведения/остановки/etc для объектов Player. Это не только приводит к нежелательным накладным расходам, но также имеет несколько ошибок, а быстрые запуски/остановки игрока вызывают множество проблем.
  • Мне нужно манипулировать байтовым буфером непосредственно для пользовательских эффектов DSP.
  • Клипы, которые я собираюсь играть, являются небольшими, и все они могут быть загружены в память, чтобы избежать накладных расходов ввода-вывода. Кроме того, включение собственных буферов позволит мне уменьшить задержку, записав 0 в выходной приемник и просто переключившись на выборки байтов при их воспроизведении, а не на STOPPING, PAUSING и PLAYING AudioTrack.

Хорошо, поэтому оправдания завершены - вот что я пробовал - у меня есть структура Sample, которая содержит, по существу, входную и выходную дорожки и массив байтов для хранения образцов. Ввод - мой проигрыватель FileDescriptor, а выход - объект SimpleBufferQueue. Здесь моя структура:

typedef struct Sample_ {
    // buffer to hold all samples
    short *buffer;      
    int totalSamples;

    SLObjectItf fdPlayerObject;
    // file descriptor player interfaces
    SLPlayItf fdPlayerPlay;
    SLSeekItf fdPlayerSeek;
    SLMuteSoloItf fdPlayerMuteSolo;
    SLVolumeItf fdPlayerVolume;
    SLAndroidSimpleBufferQueueItf fdBufferQueue;

    SLObjectItf outputPlayerObject; 
    SLPlayItf outputPlayerPlay; 
    // output buffer interfaces
    SLAndroidSimpleBufferQueueItf outputBufferQueue;        
} Sample;

после инициализации файлового проигрывателя fdPlayerObject и памяти malloc-ing для моего байтового буфера с помощью

sample->buffer = malloc(sizeof(short)*sample->totalSamples);

Я получаю его интерфейс BufferQueue с

// get the buffer queue interface
result = (*(sample->fdPlayerObject))->GetInterface(sample->fdPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(sample->fdBufferQueue));

Затем я создаю выходной проигрыватель:

// create audio player for output buffer queue
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(sample->outputPlayerObject), &outputAudioSrc, &audioSnk,
                                               1, ids1, req1);

// realize the output player
result = (*(sample->outputPlayerObject))->Realize(sample->outputPlayerObject, SL_BOOLEAN_FALSE);
assert(result == SL_RESULT_SUCCESS);

// get the play interface
result = (*(sample->outputPlayerObject))->GetInterface(sample->outputPlayerObject, SL_IID_PLAY, &(sample->outputPlayerPlay));
assert(result == SL_RESULT_SUCCESS);

// get the buffer queue interface for output
result = (*(sample->outputPlayerObject))->GetInterface(sample->outputPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                                   &(sample->outputBufferQueue));
assert(result == SL_RESULT_SUCCESS);    

  // set the player state to playing
result = (*(sample->outputPlayerPlay))->SetPlayState(sample->outputPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(result == SL_RESULT_SUCCESS);

Когда я хочу воспроизвести образец, я использую:

Sample *sample = &samples[sampleNum];
// THIS WORKS FOR SIMPLY PLAYING THE SAMPLE, BUT I WANT THE BUFFER DIRECTLY 
//    if (sample->fdPlayerPlay != NULL) {
//        // set the player state to playing
//        (*(sample->fdPlayerPlay))->SetPlayState(sample->fdPlayerPlay, SL_PLAYSTATE_PLAYING);
//    }

// fill buffer with the samples from the file descriptor
(*(sample->fdBufferQueue))->Enqueue(sample->fdBufferQueue, sample->buffer,sample->totalSamples*sizeof(short));
// write the buffer to the outputBufferQueue, which is already playing
(*(sample->outputBufferQueue))->Enqueue(sample->outputBufferQueue, sample->buffer, sample->totalSamples*sizeof(short));

Однако это заставляет мое приложение замораживать и закрывать. Что-то здесь не так. Также, я бы предпочел не получать образцы из дескриптора файла BufferQueue каждый раз. Вместо этого я хотел бы сохранить его в байтовом массиве и Enqueue на выходе, когда захочу.

4b9b3361

Ответ 1

Декодирование на PCM доступно на уровне API 14 и выше.

Когда вы создаете проигрыватель декодера, вам нужна установка простой очереди буферов Android в качестве приемника данных:

// For init use something like this:
SLDataLocator_AndroidFD locatorIn = {SL_DATALOCATOR_ANDROIDFD, decriptor, start, length};
SLDataFormat_MIME dataFormat = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
SLDataSource audioSrc = {&locatorIn, &dataFormat};

SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataSink audioSnk = { &loc_bq, NULL };

const SLInterfaceID ids[2] = {SL_IID_PLAY, SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(sample->fdPlayerObject), &outputAudioSrc, &audioSnk, 2, ids1, req1);

Для очереди декодера вам необходимо ввести в очередь набор пустых буферов в обычную очередь буферов Android, которая будет заполнена данными PCM.

Также вам нужно зарегистрировать обработчик обратного вызова с очередью декодера, который будет вызываться, когда данные PCM будут готовы. Обработчик обратного вызова должен обработать данные PCM, повторно поставить в очередь пустой буфер и затем вернуться. Приложение отвечает за отслеживание декодированных буферов; список параметров обратного вызова не содержит достаточной информации, чтобы указать, какой буфер был заполнен, или какой буфер для очереди в очереди.

Декодирование на PCM поддерживает приостановку и первоначальный поиск. Регулировка громкости, эффекты, цикличность и скорость воспроизведения не поддерживаются.

Прочтите Декодировать аудио в PCM из OpenSL ES для Android для более подробной информации.