美文网首页Android,你的皇帝来了。
34.FFmpeg+OpenGLES+OpenSLES播放器实现

34.FFmpeg+OpenGLES+OpenSLES播放器实现

作者: 任振铭 | 来源:发表于2018-10-19 15:48 被阅读77次

    项目源码
    OpenSL ES 文档

    OpenSLES:(Open Sound Library for Embedded Systems)

    OpenSLES是跨平台、针对嵌入式系统精心优化的硬件音频加速API。使用OpenSLES进行音频播放的好处是可以不依赖第三方。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度。

    今天我们就通过OpenSLES代码来实现播放pcm格式的音频文件,后期会将相关代码整合到ffmpeg播放器上,实现音视频同步。下边是OpenSLES播放音频的简要流程(图片来自网上)

    OpenSLES音频播放流程.png

    接下来进入代码阶段

    首先使用OpenSLES需要将这个库链接到我们的so中,在CMakeLists.txt脚本中添加

    target_link_libraries( 
                           ......
                           #播放音频
                           OpenSLES )
    
    实现流程

    1.创建引擎
    slCreateEngine(),第一个参数是要创建的引擎对象,是一个SLObjectItf类型,Object是一个资源的抽象集合,可以通过它获取各种资源,所有的Object在OpenSL里面我们拿到的都是一个SLObjectItf,返回值是SLresult类型,如果成功则返回SL_RESULT_SUCCESS,其他参数都传0即可,我们务求最精简的实现功能

    SLObjectItf             *pEngine,
    SLuint32                numOptions,
    const SLEngineOption    *pEngineOptions,
    SLuint32                numInterfaces,
    const SLInterfaceID     *pInterfaceIds,
    const SLboolean         * pInterfaceRequired
    

    2.引擎对象创建后实例化,创建出来之后必须先调用Realize方法做初始化。在不需要使用的时候调用Destroy方法释放资源
    (*slObjectItf)->Realize(),实例化成功则返回SL_RESULT_SUCCESS

    SLObjectItf self, //要被实例化的引擎对象本身
    SLboolean async //传SL_BOOLEAN_FALSE
    

    3.引擎实例化之后从引擎对象获取接口
    Interface则是方法的集合,例如SLRecordItf里面包含了和录音相关的方法,SLPlayItf包含了和播放相关的方法。我们功能都是通过调用Interfaces的方法去实现的,个Object里面可能包含了多个Interface,所以GetInterface方法有个SLInterfaceID参数来指定到的需要获取Object里面的那个Interface。调用这个方法会获取到SLEngineItf,SLEngineItf是OpenSL里面最重要的一个Interface,我们可以通过它去创建各种Object,例如播放器、录音器、混音器的Object,然后在用这些Object去获取各种Interface去实现各种功能

    (*slObjectItf)->GetInterface() 执行会后会获取到SLEngineItf slEngineItf对象

    SLObjectItf self,  //实例化后的引擎对象
    const SLInterfaceID iid, //SL_IID_ENGINE
    void * pInterface //输出的接口对象指针
    

    4.创建混音器
    混音器是从创建出的引擎接口中创建的
    (*slEngineItf)->CreateOutputMix(),输出的混音器同样是SLObjectItf mixObjectItf类型

    SLEngineItf self,  //引擎接口
    SLObjectItf * pMix, //输出的混音器
    SLuint32 numInterfaces,//传0
    const SLInterfaceID * pInterfaceIds,//传0
    const SLboolean * pInterfaceRequired //传0
    

    5.混音器对象创建后开始实例化,同引擎实例化
    (*mixObjectItf)->Realize()

    SLObjectItf self, //要被实例化的混音器对象本身
    SLboolean async //传SL_BOOLEAN_FALSE
    

    6.创建播放器
    (*slEngineItf)->CreateAudioPlayer(),播放器是从引擎对象创建出来的

    SLEngineItf self, //引擎对象本身
    SLObjectItf * pPlayer, //输出的播放器对象,同样是SLObjectItf类型
    SLDataSource *pAudioSrc,//数据的来源
    SLDataSink *pAudioSnk, //数据的去处,和SLDataSource是相对的
    SLuint32 numInterfaces,//与下面的SLInterfaceID和SLboolean配合使用,用于标记SLInterfaceID数组和SLboolean的大小
    const SLInterfaceID * pInterfaceIds, //这里需要传入一个数组,指定创建的播放器会包含哪些Interface
    const SLboolean * pInterfaceRequired  //这里也是一个数组,用来标记每个需要包含的Interface
    

    7.初始化播放器
    (*slPlayerItf)->Realize() 得到SLObjectItf slPlayerItf = NULL;

    SLObjectItf self, //要被实例化的播放器对象本身
    SLboolean async //传SL_BOOLEAN_FALSE
    

    8.获取播放器接口
    (*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_PLAY, &slPlayItf);
    得到播放器接口SLPlayItf slPlayItf

    SLObjectItf self,  //实例化后的播放器对象
    const SLInterfaceID iid, //SL_IID_ENGINE
    void * pInterface //输出的接口对象指针
    

    9,获取播放队列接口
    (*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_BUFFERQUEUE, &pcmQueue)

    SLObjectItf self, 
    const SLInterfaceID iid, //SL_IID_ENGINE
    void * pInterface //输出的接口对象指针
    

    10.给播放队列设置回调函数
    (*pcmQueue)->RegisterCallback(pcmQueue, CallBack, 0);
    开始播放后会不断的回调这个函数将音频数据压入队列

    void CallBack(SLAndroidSimpleBufferQueueItf bf, void *contex) {
        LOGI("CallBack ....");
        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)
                (*bf)->Enqueue(bf, buf, len);
        }
    }
    

    11.设置播放状态为播放中

    (*slPlayItf)->SetPlayState(slPlayItf, SL_PLAYSTATE_PLAYING);
    

    12.开始播放

    (*pcmQueue)->Enqueue(pcmQueue, "", 1);
    
    播放流程.png
    完整代码
    void CallBack(SLAndroidSimpleBufferQueueItf bf, void *contex) {
        LOGI("CallBack ....");
        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)
                (*bf)->Enqueue(bf, buf, len);
        }
    }
    
    /**
     * 播放音频
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_rzm_ffmpegplayer_FFmpegPlayer_playAudio(JNIEnv *env, jobject instance, jstring url_) {
        const char *url = env->GetStringUTFChars(url_, 0);
    
        /***********  1 创建引擎 获取SLEngineItf***************/
        SLObjectItf slObjectItf;
        SLEngineItf slEngineItf;
        //SLresult是unsigned int 类型
        SLresult result;
        result = slCreateEngine(&slObjectItf, 0, 0, 0, 0, 0);
        if (result != SL_RESULT_SUCCESS)
            return;
        //SLObjectItf本身是一个指针,*slObjectItf得到的是他的对象
        result = (*slObjectItf)->Realize(slObjectItf, SL_BOOLEAN_FALSE);
        if (result != SL_RESULT_SUCCESS)
            return;
        result = (*slObjectItf)->GetInterface(slObjectItf, SL_IID_ENGINE, &slEngineItf);
        if (result != SL_RESULT_SUCCESS)
            return;
        if (slEngineItf) {
            LOGI("get SLEngineItf success");
        } else {
            LOGI("get SLEngineItf failed");
        }
        /***********         1 创建引擎       ***************/
    
        /***********  2 创建混音器 ***************/
        SLObjectItf mixObjectItf = NULL;
        result = (*slEngineItf)->CreateOutputMix(slEngineItf, &mixObjectItf, 0, 0, 0);
        if (result != SL_RESULT_SUCCESS) {
            LOGE("CreateOutputMix failed");
            return;
        } else {
            LOGE("CreateOutputMix success");
        }
    
        //实例化混音器
        result = (*mixObjectItf)->Realize(mixObjectItf, SL_BOOLEAN_FALSE);
        if (result != SL_RESULT_SUCCESS) {
            LOGE("mixer init failed");
        } else {
            LOGI("mixer init success");
        }
        /***********  2 创建混音器 ***************/
    
        /***********  3 配置音频信息 ***************/
    
        SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, mixObjectItf};
        SLDataSink slDataSink = {&outputMix, 0};
    
    
        //缓冲队列
        SLDataLocator_AndroidSimpleBufferQueue queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};
        //音频格式
        SLDataFormat_PCM pcmFormat = {
                SL_DATAFORMAT_PCM,
                //声道数
                2,
                SL_SAMPLINGRATE_44_1,
                SL_PCMSAMPLEFORMAT_FIXED_16,
                SL_PCMSAMPLEFORMAT_FIXED_16,
                SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
                //字节序,小端
                SL_BYTEORDER_LITTLEENDIAN
        };
        SLDataSource slDataSource = {&queue, &pcmFormat};
        /***********  3 配置音频信息 ***************/
    
        /************* 4 创建播放器 ****************/
        const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
        const SLboolean req[] = {SL_BOOLEAN_TRUE};
        SLObjectItf slPlayerItf = NULL;
        SLPlayItf slPlayItf;
        SLAndroidSimpleBufferQueueItf pcmQueue = NULL;
        result = (*slEngineItf)->CreateAudioPlayer(slEngineItf, &slPlayerItf, &slDataSource,
                                                   &slDataSink, sizeof(ids) / sizeof(SLInterfaceID),
                                                   ids, req);
        if (result != SL_RESULT_SUCCESS) {
            LOGE("create audio player failed");
        } else {
            LOGE("create audio player success");
        }
        //初始化播放器
        result = (*slPlayerItf)->Realize(slPlayerItf, SL_BOOLEAN_FALSE);
        if (result != SL_RESULT_SUCCESS) {
            LOGE("audio player init failed");
        } else {
            LOGE("audio player init success");
        }
        //获取player接口
        result = (*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_PLAY, &slPlayItf);
        if (result != SL_RESULT_SUCCESS) {
            LOGE("player get SL_IID_PLAY failed");
        } else {
            LOGI("player get SL_IID_PLAY success");
        }
    
        //获取播放队列接口
        result = (*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_BUFFERQUEUE, &pcmQueue);
        if (result != SL_RESULT_SUCCESS) {
            LOGE("player get SL_IID_BUFFERQUEUE failed");
        } else {
            LOGI("player get SL_IID_BUFFERQUEUE success");
        }
        /************* 4 创建播放器 ****************/
    
        //设置回调函数
        (*pcmQueue)->RegisterCallback(pcmQueue, CallBack, 0);
        //设置播放状态
        (*slPlayItf)->SetPlayState(slPlayItf, SL_PLAYSTATE_PLAYING);
        //启动队列
        (*pcmQueue)->Enqueue(pcmQueue, "", 1);
    
        env->ReleaseStringUTFChars(url_, url);
    }
    
    

    相关文章

      网友评论

        本文标题:34.FFmpeg+OpenGLES+OpenSLES播放器实现

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