是否有可能直接从OpenSL ES(Android版)音频资产得到一个字节的缓冲区?(Is it p

2019-06-24 20:52发布

我想摆脱使用OpenSL ES FileDescriptor对象音频资产一个字节的缓冲区,这样我就可以反复排队到一个SimpleBufferQueue,而不是使用SL接口来播放/停止/搜索文件。

有几个原因,我想直接管理的样本字节主要原因有三:

  1. OpenSL使用一个AudioTrack层播放/停止/等供玩家对象。 这不仅引入不必要的开销,但同时也存在一些bug,以及快速启动/播放器的停止导致很多问题。
  2. 我需要直接操纵字节的缓冲区定制的DSP效果。
  3. 我会被打的剪辑是小,都可以加载到内存中,避免文件I / O开销。 另外,入队我自己的缓冲区会允许我写0对输出水槽,并简单地切换时,他们是在玩,而不是停止,暂停采样字节,打AudioTrack以减少延迟。

好了,所以理完成-这里就是我试过-我有一个样品结构包含本质上,输入和输出的轨道,和一个字节数组来保存样品。 输入是我的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's 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's 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每次拿到样品。 相反,我想永久将其存储在一个字节数组,排队,要输出时,我喜欢。

Answer 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的更多细节。



文章来源: Is it possible to get a byte buffer directly from an audio asset in OpenSL ES (for Android)?