34.FFmpeg+OpenGLES+OpenSLES播放器实现(八.OpenSLES播放音频)
OpenSLES:(Open Sound Library for Embedded Systems)
OpenSLES是跨平台、针对嵌入式系统精心优化的硬件音频加速API。使用OpenSLES进行音频播放的好处是可以不依赖第三方。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度。
今天我们就通过OpenSLES代码来实现播放pcm格式的音频文件,后期会将相关代码整合到ffmpeg播放器上,实现音视频同步。下边是OpenSLES播放音频的简要流程(图片来自网上)
接下来进入代码阶段
首先使用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);
完整代码
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);
}

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
社会化分享插件集成分享
一.前提摘要社会化分享每个app必备的推广需求,无论是拉新,邀请,游戏奖励,等等都离不开分享的影子,下面我们介绍下社会化分享的插件;首先要说下,现在的社交App,社区App等,国内外的情况都是从以前的百花齐放到现在的几个巨头,现在能够真正存活下来的或者说用户群体大的,也就微信、QQ、微博、Facebook,所以,我们就简单的从这几个平台的社会化分享集成到app来对比下;市面上社会化分享的插件还是比较多的,比如ShareSDK(Mob),U-Share(友盟+),极光等等,不过用的比较多的现在是ShareSDK的了,毕竟友盟主打的是统计,极光主打的是推送,Mob主打的ShareSDK,不过我们还是选取ShareSDK和友盟+对比下;集成准备:需要做这几个平台的分享,或者第三方登录,必须要先去对应的第三方开发者或者开放平台申请对应的应用得到id或者key等;QQ:http://open.qq.com微信:http://open.qq.weixin.com新浪:http://open.weibo.comFacebook:https://developers.facebook.com这个论坛中...
-
下一篇
Android开发 - Retrofit 2 使用自签名的HTTPS证书进行API请求
为了确保数据传输的安全,现在越来越多的应用使用Https的方式来进行数据传输,使用https有很多有点,比如: HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。 HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。 但是即使使用HTTPS有很多有点,但是购买一个认证的HTTPS证书却价格不菲,增加了初创企业和小团队的开发成本。而且如上面所说,使用HTTPS并非绝对的安全,传输的数据并非没有办法获取到,我的之前一篇博客所使用的方法已成功截取HTTPS的数据传输。有兴趣的可以参考:使用Charles对Android App的https请求进行抓包。 为了节约成本,我们可以选择使用自签名的HTTPS。这种方式我们可以自己生成证书,不需要购买证书,但是使用这种方式的域名如果在浏览器中访问的话,会有一个不安全的标识。但是如果用在客户端上,就不会有这个问题,现在一些应用商店也要求APP上架时需要使用HTTPS的网络请求(比如AppStore),使用这种...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8编译安装MySQL8.0.19
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程