OpenSL ES: 利用OpenSL ES播放一个存在于SDcard上的PCM文件
native-lib.cpp
#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()); }
代码中的一些概念补充:
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了。
2. DataSouce和DataSink
OpenSL ES 里面,这两个结构体均是作为创建 Media Object 对象时的参数而存在的,data source 代表着输入源的信息,即数据从哪儿来、输入的数据参数是怎样的;而 data sink 则代表着输出的信息,即数据输出到哪儿、以什么样的参数来输出。