OpenSL ES: 利用OpenSL ES播放一個存在於SDcard上的PCM文件

native-lib.cppandroid

#include <jni.h>
#include <string>

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

#include <android/log.h>

#define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"xp.chen",FORMAT,##__VA_ARGS__);


void bufferQueueCallback(SLAndroidSimpleBufferQueueItf bufferQueue, void *pContext) {
    static FILE *fp = NULL;
    static char *buf = NULL;
    if (!buf) {
        buf = new char[1024*1024];
    }
    if (!fp) {
        fp = fopen("/sdcard/test.pcm", "rb");
    }
    if (!fp) return;
    if (feof(fp) == 0) {
        int len = fread(buf, 1, 1024, fp);
        if (len > 0)
            (*bufferQueue)->Enqueue(bufferQueue, buf, len);
    }
}


extern "C" JNIEXPORT jstring JNICALL
Java_com_yongdaimi_android_androidapitest_OpenSLESApiUseDemoActivity_stringFromJNI(JNIEnv* env, jobject object)
{
    std::string hello = "Hello from C++";

    SLresult re;
    SLObjectItf engineObject;
    SLEngineItf slAudioEngine;

    // 1. Create and init audio engine
    re = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("slCreateEngine() failed");
    }
    re = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("engineObject Realize failed");
    }
    re = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &slAudioEngine);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("engineObject GetInterface SL_IID_ENGINE failed");
    }

    // 2. Set output mix
    SLObjectItf outputMix;
    re = (*slAudioEngine)->CreateOutputMix(slAudioEngine, &outputMix, 0, NULL, NULL);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("CreateOutputMix() failed");
    }
    re = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("outputMix Realize failed");
    }

    // 3. Configuring the input data source
    SLDataLocator_AndroidSimpleBufferQueue inputBuffQueueLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};
    SLDataFormat_PCM input_format_pcm = {
            SL_DATAFORMAT_PCM,                              // <<< 輸入的音頻格式,PCM
            2,                                              // <<< 輸入的聲道數,2(立體聲)
            SL_SAMPLINGRATE_44_1,                           // <<< 輸入的採樣率,44100hz
            SL_PCMSAMPLEFORMAT_FIXED_16,                    // <<< 輸入的採樣位數,16bit
            SL_PCMSAMPLEFORMAT_FIXED_16,                    // <<< 容器大小,同上
            SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,   // <<< 聲道標記,這裏使用左前聲道和右前聲道
            SL_BYTEORDER_LITTLEENDIAN                       // <<< 輸入的字節序,小端
    };
    SLDataSource dataSource = {&inputBuffQueueLocator, &input_format_pcm};

    SLDataLocator_OutputMix outputMixLocator = {SL_DATALOCATOR_OUTPUTMIX, outputMix};
    SLDataSink dataSink = {&outputMixLocator, 0};

    // 4. Create Audio Player
    SLObjectItf audioPlayer;
    SLAndroidSimpleBufferQueueItf pcmBufferQueue;
    SLPlayItf playInterface;
    SLInterfaceID audioPlayerInterfaceIDs[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
    SLboolean audioPlayerInterfaceRequired[] = {SL_BOOLEAN_TRUE};

    re = (*slAudioEngine)->CreateAudioPlayer(slAudioEngine, &audioPlayer, &dataSource, &dataSink, 1, audioPlayerInterfaceIDs, audioPlayerInterfaceRequired);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("CreateAudioPlayer() failed");
    }
    re = (*audioPlayer)->Realize(audioPlayer, SL_BOOLEAN_FALSE);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("AudioPlayer Realize failed");
    }
    re = (*audioPlayer)->GetInterface(audioPlayer, SL_IID_PLAY, &playInterface);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("AudioPlayer GetInterface SL_IID_PLAY failed");
    }
    re = (*audioPlayer)->GetInterface(audioPlayer, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
    if (re != SL_RESULT_SUCCESS) {
        LOGD("AudioPlayer GetInterface SL_IID_BUFFERQUEUE failed");
    }

    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, bufferQueueCallback, NULL);
    (*playInterface)->SetPlayState(playInterface, SL_PLAYSTATE_PLAYING);

    // Start queue callback
    (*pcmBufferQueue)->Enqueue(pcmBufferQueue, "", 1);
    return env->NewStringUTF(hello.c_str());
}

代碼中的一些概念補充:api

1.結構體中的 numInterfaces , pInterfaceIds , pInterfaceRequired ,這裏以建立播放器所調用的 CreateAudioPlayer 函數爲例說明:數組

SLresult (*CreateAudioPlayer) (
        SLEngineItf self,
        SLObjectItf * pPlayer,
        SLDataSource *pAudioSrc,
        SLDataSink *pAudioSnk,
        SLuint32 numInterfaces,
        const SLInterfaceID * pInterfaceIds,
        const SLboolean * pInterfaceRequired
    );

各參數含義以下:函數

  • SLEngineItf C語言不像C++,沒有this指針,只能經過每次調用SLEngineItf的方法的時候手動傳入
  • SLObjectItf 用於保存建立出來的AudioPlayerObject
  • SLDataSource 輸入數據源的信息
  • SLDataSink 輸出的信息
  • numInterfaces 與下面的SLInterfaceID和SLboolean配合使用,用於標記SLInterfaceID數組和SLboolean數組的大小
  • SLInterfaceID 這裏須要傳入一個數組,指定建立的AudioPlayerObject須要包含哪些Interface
  • SLboolean 這裏也是一個數組,用來標記每一個須要包含的Interface在AudioPlayerObject不支持的狀況下,是否是須要在建立AudioPlayerObject時返回失敗。

最後的三個參數用於指定AudioPlayerObject須要包含哪些Interface,若是不包含是否是要直接建立失敗。以前也提到過,並非每一個系統上都實現了 OpenSL ES 爲 Object 定義的全部 Interface,因此在獲取 Interface 的時候須要作一些選擇和判斷,若是建立成功的話咱們就能使用AudioPlayerObject的GetInterface方法獲取到這些Interface了。ui

2. DataSouce和DataSinkthis

OpenSL ES 裏面,這兩個結構體均是做爲建立 Media Object 對象時的參數而存在的,data source 表明着輸入源的信息,即數據從哪兒來、輸入的數據參數是怎樣的;而 data sink 則表明着輸出的信息,即數據輸出到哪兒、以什麼樣的參數來輸出。spa

相關文章
相關標籤/搜索