您现在的位置是:首页 > 文章详情

34.FFmpeg+OpenGLES+OpenSLES播放器实现(八.OpenSLES播放音频)

日期:2018-10-18点击:370

项目源码
OpenSL ES 文档

OpenSLES:(Open Sound Library for Embedded Systems)

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

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

img_06c112bc67c418437b9da09ac6a80b68.png
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); 
img_9359188d72721a3b0309b5964ef98e19.png
播放流程.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); } 
原文链接:https://yq.aliyun.com/articles/657298
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章