最简单的OpenSL播放PCM实时音频

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37677536/article/details/79013969

这里是c语言写的给android用的,可以拿到其他平台使用。既然是最简单的,肯定使用起来就是超级简单如回调方法就一句代码。这里简单说一下使用要注意的地方:
1.如果想要使用opensl的一些功能如音量控制:
这里写图片描述
只是这样是不可以的,拿到的bqPlayerVolume为空值,还需要在这个地方打开一下:
这里写图片描述
这是我碰到的坑,帮助大家直接跳过。
2. opensl播放音频速率是一定的,那么给opensl更新数据的速率也是一定的,opensl跟openal一样也是有播放缓存的,每播放完毕缓存中一个pcm音频数据包就会有回调,回调方法就是:

// this callback handler is called every time a buffer finishes playing
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
    p->inputDataCount --;
}

我这里处理超简单,就是一个数据包计数,传进去一个pcm数据包就计数加一,这个回调就计数减一,这个计数就表示音频播放器里面将要播放的pcm音频数据包的数量。就根据这个计数来控制pcm数据的更新速率就可以了。如果缓存中播放完毕再更新数据会断片,就是计数为零时。
3.这里处理不好会有内存泄漏,就是传给opensl的数据包地址要用数组或者链表存起来,播放完毕再释放掉。

完整播放器demo:
http://blog.csdn.net/m0_37677536/article/details/78775007

Opensl_io.h

//
// Created by huizai on 2017/12/4.
//

#ifndef FFMPEG_DEMO_OPENSL_IO_H
#define FFMPEG_DEMO_OPENSL_IO_H

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

typedef struct opensl_stream {

    // engine interfaces
    SLObjectItf engineObject;
    SLEngineItf engineEngine;

    // output mix interfaces
    SLObjectItf outputMixObject;

    // buffer queue player interfaces
    SLObjectItf bqPlayerObject;
    SLPlayItf bqPlayerPlay;
    SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;

    SLObjectItf pitchObject;
    SLPitchItf bqPitchEngne;
    SLPlaybackRateItf bqPlayerRate;
    SLPitchItf bqPlayerVolume;


    // buffer indexes
    int      inputDataCount;
    double time;
    uint32_t outchannels; //输出的声道数量
    uint32_t sampleRate; //采样率

} OPENSL_STREAM;

int android_SetPlayRate(OPENSL_STREAM *p,int playRate);

/*
  Open the audio device with a given sampling rate (sr), input and output channels and IO buffer size
  in frames. Returns a handle to the OpenSL stream
*/
OPENSL_STREAM* android_OpenAudioDevice(uint32_t sr, uint32_t inchannels, uint32_t outchannels, uint32_t bufferframes);

/*
  Close the audio device
*/
void android_CloseAudioDevice(OPENSL_STREAM *p);

/*
  Write a buffer to the OpenSL stream *p, of size samples. Returns the number of samples written.
*/
uint32_t android_AudioOut(OPENSL_STREAM *p, uint16_t *buffer,uint32_t size);

#endif //FFMPEG_DEMO_OPENSL_IO_H

Opensl_io.c

//
// Created by huizai on 2017/12/4.
//

#include "Opensl_io.h"

static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context);

// creates the OpenSL ES audio engine
static SLresult openSLCreateEngine(OPENSL_STREAM *p)
{
    SLresult result;
    // create engine
    result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
    if(result != SL_RESULT_SUCCESS) goto  engine_end;

    // realize the engine
    result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
    if(result != SL_RESULT_SUCCESS) goto engine_end;

    // get the engine interface, which is needed in order to create other objects
    result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
    if(result != SL_RESULT_SUCCESS) goto  engine_end;

    engine_end:
    return result;
}

// opens the OpenSL ES device for output
static SLresult openSLPlayOpen(OPENSL_STREAM *p)
{
    SLresult result;
    SLuint32 sr = p->sampleRate;
    SLuint32  channels = p->outchannels;

    if(channels) {
        // configure audio source
        SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};

        switch(sr){
            case 8000:
                sr = SL_SAMPLINGRATE_8;
                break;
            case 11025:
                sr = SL_SAMPLINGRATE_11_025;
                break;
            case 16000:
                sr = SL_SAMPLINGRATE_16;
                break;
            case 22050:
                sr = SL_SAMPLINGRATE_22_05;
                break;
            case 24000:
                sr = SL_SAMPLINGRATE_24;
                break;
            case 32000:
                sr = SL_SAMPLINGRATE_32;
                break;
            case 44100:
                sr = SL_SAMPLINGRATE_44_1;
                break;
            case 48000:
                sr = SL_SAMPLINGRATE_48;
                break;
            case 64000:
                sr = SL_SAMPLINGRATE_64;
                break;
            case 88200:
                sr = SL_SAMPLINGRATE_88_2;
                break;
            case 96000:
                sr = SL_SAMPLINGRATE_96;
                break;
            case 192000:
                sr = SL_SAMPLINGRATE_192;
                break;
            default:
                break;
                return -1;
        }

        const SLInterfaceID ids[] = {SL_IID_VOLUME};
        const SLboolean req[] = {SL_BOOLEAN_FALSE};
        result = (*p->engineEngine)->CreateOutputMix(p->engineEngine, &(p->outputMixObject), 1, ids, req);
        if(result != SL_RESULT_SUCCESS) return result;

        // realize the output mix
        result = (*p->outputMixObject)->Realize(p->outputMixObject, SL_BOOLEAN_FALSE);

        int speakers;
        if(channels > 1)
            speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
        else speakers = SL_SPEAKER_FRONT_CENTER;

        SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,channels, sr,
                                       SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
                                       (SLuint32)speakers, SL_BYTEORDER_LITTLEENDIAN};

        SLDataSource audioSrc = {&loc_bufq, &format_pcm};

        // configure audio sink
        SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, p->outputMixObject};
        SLDataSink audioSnk = {&loc_outmix, NULL};

        // create audio player
        const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE,SL_IID_PLAYBACKRATE,SL_IID_VOLUME};
        const SLboolean req1[] = {SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE};
        result = (*p->engineEngine)->CreateAudioPlayer(p->engineEngine, &(p->bqPlayerObject), &audioSrc, &audioSnk,
                                                       3, ids1, req1);
        if(result != SL_RESULT_SUCCESS) return result;

        // realize the player
        result = (*p->bqPlayerObject)->Realize(p->bqPlayerObject, SL_BOOLEAN_FALSE);
        if(result != SL_RESULT_SUCCESS) return result;

        // get the play interface
        result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_PLAY, &(p->bqPlayerPlay));
        if(result != SL_RESULT_SUCCESS) return result;

        // get the play rate
        result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_VOLUME, &(p->bqPlayerVolume));
        if(result != SL_RESULT_SUCCESS) return result;

        // get the play volume
        result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_PLAYBACKRATE, &(p->bqPlayerRate));
        if(result != SL_RESULT_SUCCESS) return result;

        // get the buffer queue interface
        result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                                    &(p->bqPlayerBufferQueue));
        if(result != SL_RESULT_SUCCESS) return result;

        // register callback on the buffer queue
        result = (*p->bqPlayerBufferQueue)->RegisterCallback(p->bqPlayerBufferQueue, bqPlayerCallback, p);
        if(result != SL_RESULT_SUCCESS) return result;

        // set the player's state to playing
        result = (*p->bqPlayerPlay)->SetPlayState(p->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
        return result;

    }

    return SL_RESULT_SUCCESS;
}

// close the OpenSL IO and destroy the audio engine
static void openSLDestroyEngine(OPENSL_STREAM *p)
{
    // destroy buffer queue audio player object, and invalidate all associated interfaces
    if (p->bqPlayerObject != NULL) {
        (*p->bqPlayerObject)->Destroy(p->bqPlayerObject);
        p->bqPlayerObject = NULL;
        p->bqPlayerPlay = NULL;
        p->bqPlayerBufferQueue = NULL;
    }

    // destroy output mix object, and invalidate all associated interfaces
    if (p->outputMixObject != NULL) {
        (*p->outputMixObject)->Destroy(p->outputMixObject);
        p->outputMixObject = NULL;
    }

    // destroy engine object, and invalidate all associated interfaces
    if (p->engineObject != NULL) {
        (*p->engineObject)->Destroy(p->engineObject);
        p->engineObject = NULL;
        p->engineEngine = NULL;
    }
}

// open the android audio device for input and/or output
OPENSL_STREAM *android_OpenAudioDevice(uint32_t sr, uint32_t inchannels, uint32_t outchannels, uint32_t bufferframes)
{
    OPENSL_STREAM *p;
    //分配内存空间并初始化
    p = (OPENSL_STREAM *) calloc(1,sizeof(OPENSL_STREAM));
    //采样率
    p->sampleRate = sr;

    //创建引擎对象及接口
    if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) {
        android_CloseAudioDevice(p);
        return NULL;
    }
    p->inputDataCount = 0;
    //输出声道数
    p->outchannels = outchannels;
    if(openSLPlayOpen(p) != SL_RESULT_SUCCESS) {
        android_CloseAudioDevice(p);
        return NULL;
    }
    return p;
}

// close the android audio device
void android_CloseAudioDevice(OPENSL_STREAM *p)
{
    if (p == NULL)
        return;
    openSLDestroyEngine(p);
    free(p);
}

// returns timestamp of the processed stream
double android_GetTimestamp(OPENSL_STREAM *p)
{
    return p->time;
}

// this callback handler is called every time a buffer finishes playing
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
    OPENSL_STREAM *p = (OPENSL_STREAM *) context;
    p->inputDataCount --;
   // free(p->inputBuffer);
}

// puts a buffer of size samples to the device
uint32_t android_AudioOut(OPENSL_STREAM *p, uint16_t *buffer,uint32_t size)
{
    (*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue,
                                       buffer, size);
    p->inputDataCount ++;
    return 0;
}

int android_SetPlayRate(OPENSL_STREAM *p,int rateChange){
    SLmillibel value;
    SLresult result;
    if (!p) return -1;
    if (!p->bqPlayerRate) return -1;
    result = (*p->bqPlayerRate)->GetRate(p->bqPlayerRate,&value);
    if (result != SL_RESULT_SUCCESS)return -1;
    if (rateChange<0){
        value -= 100;
    } else
        value += 100;
    if (value < 500) value = 500;
    if (value > 2000) value = 2000;
    result = (*p->bqPlayerRate)->SetRate(p->bqPlayerRate,value);
    if (result == SL_RESULT_SUCCESS){
        return 0;
    } else
        return -1;
}

猜你喜欢

转载自blog.csdn.net/m0_37677536/article/details/79013969