美文网首页
第六节 OpenSL ES 实现播放pcm数据

第六节 OpenSL ES 实现播放pcm数据

作者: 最美下雨天 | 来源:发表于2018-12-06 14:45 被阅读7次
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png

    实现目标:播放上节生成的pcm文件

    #include <jni.h>
    #include <string>
    #include "CallBackJava.h"
    #include "Log.h"
    #include "HFFmpeg.h"
    
    extern "C"
    {
    #include <SLES/OpenSLES.h>
    #include <SLES/OpenSLES_Android.h>
    }
    
    // 引擎接口
    SLObjectItf engineObject = NULL;
    SLEngineItf engineEngine = NULL;
    
    //混音器
    SLObjectItf outputMixObject = NULL;
    SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
    SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
    
    
    //pcm
    SLObjectItf pcmPlayerObject = NULL;
    SLPlayItf pcmPlayerPlay = NULL;
    SLVolumeItf pcmPlayerVolume = NULL;
    
    //缓冲器队列接口
    SLAndroidSimpleBufferQueueItf pcmBufferQueue;
    
    FILE *pcmFile;
    void *buffer;
    uint8_t *out_buffer;
    
    
    
    /**
     * 记录一下错误,方法返回值为int,结果方法体没有写返回值,导致报错,jni查找错误难的.......
     * @param pcm
     * @return
     */
    int  getPcmData(void **pcm)
    {
        int size = 0;
        while(!feof(pcmFile))
        {
            size = fread(out_buffer, 1,44100 * 2 * 2, pcmFile);
            if(out_buffer == NULL)
            {
                LOGI("%s", "read end");
                break;
            } else{
                LOGI("%s", "reading");
            }
            *pcm = out_buffer;
            //注意这儿的break
            break;
        }
        LOGI("size is %d", size);
        return size;
    }
    
    
    void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
    {
        //assert(NULL == context);
        int size=getPcmData(&buffer);
        // for streaming playback, replace this test by logic to find and fill the next buffer
        if (NULL != buffer) {
            SLresult result;
            // enqueue another buffer
            result = (*pcmBufferQueue)->Enqueue(pcmBufferQueue, buffer, size);
            // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
            // which for this code example would indicate a programming error
        } else{
            LOGI("size null");
        }
    
    }
    
    
    
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_example_voicelib_Player_playPcm(JNIEnv *env, jobject instance, jstring path_) {
        const char *path = env->GetStringUTFChars(path_, 0);
    // TODO
        //读取pcm文件
        pcmFile = fopen(path, "r");
        if(pcmFile == NULL)
        {
            LOGI("%s", "fopen file error");
            return;
        }
        out_buffer = (uint8_t *) malloc(44100 * 2 * 2);
    
    
        SLresult result;
        //第一步------------------------------------------
        // 创建引擎对象
        slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
        (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
        (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    
    
        //第二步-------------------------------------------
        // 创建混音器
        const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
        const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
        (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
        (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
        result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);
        if (SL_RESULT_SUCCESS == result) {
             (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                    outputMixEnvironmentalReverb, &reverbSettings);
        }
        SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    
        // 第三步--------------------------------------------
        // 创建播放器
        SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
        SLDataFormat_PCM pcm={
                SL_DATAFORMAT_PCM,//播放pcm格式的数据
                2,//2个声道(立体声)
                SL_SAMPLINGRATE_44_1,//44100hz的频率
                SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
                SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
                SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)
                SL_BYTEORDER_LITTLEENDIAN//结束标志
        };
    
        SLDataSource slDataSource = {&android_queue, &pcm};
        SLDataSink audioSnk = {&outputMix, NULL};
        const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME};
        const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
    
        result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3, ids, req);
        // 初始化播放器
        (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
    
        //得到接口后调用  获取Player接口
        (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
    
        //第四步---------------------------------------
        // 创建缓冲区和回调函数
        (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
    
        //缓冲接口回调
        (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);
        //获取音量接口
        (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
    
        //第五步----------------------------------------
        // 设置播放状态
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    
    
        //第六步----------------------------------------
        // 主动调用回调函数开始工作
        pcmBufferCallBack(pcmBufferQueue, NULL);
    
    
        env->ReleaseStringUTFChars(path_, path);
    }
    

    相关文章

      网友评论

          本文标题:第六节 OpenSL ES 实现播放pcm数据

          本文链接:https://www.haomeiwen.com/subject/ftgwzftx.html