zoukankan      html  css  js  c++  java
  • 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 则代表着输出的信息,即数据输出到哪儿、以什么样的参数来输出。

  • 相关阅读:
    北极星杯 awd复现
    5,linux入门到上手-文件与文件系统的压缩,打包与备份
    4,linux入门到上手-文件与目录相关操作
    ctf中 preg_match 绕过技术 | 无字母数字的webshell
    巅峰极客 2019部分题解 writeup
    使用python爬去中国最好大学排名2016年
    python进制转换
    python应用-爬取猫眼电影top100
    3,linux入门到上手-文件权限管理与配置
    DNS、域、域名及FQDN 概念
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/11098022.html
Copyright © 2011-2022 走看看