目录
播放流程和条件
Opengl SLObjectItf 对象创建的四板斧
播放pcm的流程
播放流程和条件
android自带的openSL库,可用来解码音频,也可以来播放音频,以及录音。要在jni层调用:
1、cmakeList 中target_link_libraries 内引入库:OpenSLES
2、引入头文件:
#include "SLES/OpenSLES.h"
#include "SLES/OpenSLES_Android.h"
播放流程
播放队列
只要往播放队列中压入数据,就会播放。这里采用的是回调函数,当取出buf播放完毕就会清理buf,让队列空出,这时候就push数据叫你去,从而播放。
OPengl SLObjectItf 对象创建的四板斧
一般对象创建完毕后,用到的就是 SLObjectItf的对象 和 interface接口功能对象
1、定义 SLObjectItf
Opensl机构体SLObjectItf可以定义大部分结构体,此篇文章结构体对象都是用它初始化的。结构体 SLObjectItf 类似于java的Object类,属于一个基结构体。音频的大多数结构体都继承于它。
typedef const struct SLObjectItf_ * const * SLObjectItf;
从结构以定义看出,它是一个结构以指针,因此,定义一个SLObjectItf 变量,其实是定义一个 SLObjectItf的指针。
2、创建 slCreate
因为SLObjectItf是指针对象,为了更好管理内存,会将SLObjectItf地址传入opensl中,来创建。下面为创建Engine引擎的例子:
SLObjectItf engineSL = NULL;
re = slCreateEngine(&engineSL,0,0,0,0,0);
- 1、对象空间就会在 opensl内部创建,方便利用opensl内部释放
- 2、后面参数为支持创建的接口数量,和注册接口,方便后面getInterface获得接口功能。不注册后面会后去失败。
3、实例化 Realize
SLresult (*Realize) (
SLObjectItf self,
SLboolean async
);// 第二个参数,是否等待创建好再返回
这个方法会将对象的一些参数分配好。
4、获得功能接口 GetInterface
SLresult (*GetInterface) (
SLObjectItf self,
const SLInterfaceID iid,
void * pInterface
);
参数:
SLObjectItf self 获取结构对象
const SLInterfaceID iid, 获取的结构功能id
void * pInterface 存储接口功能的对象,即接口对象。
下面为创建一个音频引擎的方法例子,大家就可以看出来:
//定义
static SLObjectItf engineSL = NULL;
SLEngineItf CreateSL(){
SLresult re;
SLEngineItf en;
// 创建
re = slCreateEngine(&engineSL,0,0,0,0,0);
if(re != SL_RESULT_SUCCESS)
return NULL;
// 实例化
re= (*engineSL)->Realize(engineSL, SL_BOOLEAN_FALSE);// 第二个参数,是否等待创建好再返回
if(re != SL_RESULT_SUCCESS) return NULL;
// 获取引擎接口,并存入en功能接口对象
re = (*engineSL)->GetInterface(engineSL,SL_IID_ENGINE, &en);
if(re != SL_RESULT_SUCCESS) return NULL;
return en;
}
播放pcm的流程
1、创建播放引擎
上面的代码已经给出了,创建音频播放引擎对象,和获得 播放引擎接口对象。这里介绍下函数
SL_API SLresult SLAPIENTRY slCreateEngine(
SLObjectItf *pEngine,
SLuint32 numOptions,
const SLEngineOption *pEngineOptions,
SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds,
const SLboolean * pInterfaceRequired
);
参数:
SLObjectItf *pEngine, 对象
SLuint32 numOptions, 选择项数量
const SLEngineOption *pEngineOptions, 具体的选择项
SLuint32 numInterfaces, 支持接口的数量
const SLInterfaceID *pInterfaceIds, 支持的接口
const SLboolean * pInterfaceRequired 接口是关闭还是打开
2、创建输出设备
SLEngineItf eng = CreateSL();
// 创建 混音器
SLObjectItf mix = NULL;
re = (*eng)->CreateOutputMix(eng,&mix,0,0,0);// 后面配置项,做混音特效
if(re != SL_RESULT_SUCCESS){
LOGE("CreateOutputMix failed");
}
//实例化 mix
re = (*mix)->Realize(mix, SL_BOOLEAN_FALSE);
if(re != SL_RESULT_SUCCESS){
LOGE("mix Realize failed");
}
// 创建一个输出接口,给播放器调用
SLDataLocator_OutputMix outMix = {SL_DATALOCATOR_OUTPUTMIX, mix};
SLDataSink audioSink= {&outMix,0};
从代码可以看出,SLObjectItf 对象获取功能接口用getInterface,而其他对象并不是,这里目的:
- 创建混音器 mix
- 创建一个混音器输出对象audioSink, 而混音的输出对象outmix,就存在audioSink中初始化的。
3、配置pcm音频格式信息
// 配置音频信息
//缓冲队列
SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE};
//音频格式配置
SLDataFormat_PCM pcm={
SL_DATAFORMAT_PCM,
2,// 声道数
SL_SAMPLINGRATE_44_1,//采用率44100
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN// 字节序,小端
};
// 构建结构体给 播放器使用
SLDataSource ds={&que, &pcm};
这里由代码可以看出,
- 创建了一个播放的音频的队列que。
- 创建配置音频pcm格式信息的对象pcm。
- 将播放队列和音频格式组件一个结构体ds,方便给播放器使用。
4、创建播放器
// 创建播放器
SLObjectItf player=NULL;
SLPlayItf playInterface;
SLAndroidSimpleBufferQueueItf pcmQue = NULL; // 播放队列
const SLInterfaceID ids[]={SL_IID_BUFFERQUEUE};// 播放队列接口参数
const SLboolean req[]={SL_BOOLEAN_TRUE};//表示接口是否开放
// 这里必须注册 SL_BOOLEAN_TRUE,SL_IID_BUFFERQUEUE 否则后面无法GetInterface到接口
//sizeof(ids)/ sizeof(SLInterfaceID) 播放队列参数个数
re = (*eng)->CreateAudioPlayer(eng, &player, &ds, &audioSink, sizeof(ids)/ sizeof(SLInterfaceID) , ids,req );
if(re != SL_RESULT_SUCCESS){
LOGE("CreateAudioPlayer failed");
} else{
LOGW("CreateAudioPlayer success");
}
//实例化 播放器
(*player)->Realize(player,SL_BOOLEAN_FALSE);
// 获得播放器接口
re = (*player)->GetInterface(player, SL_IID_PLAY,&playInterface);
if(re != SL_RESULT_SUCCESS){
LOGE("GetInterface failed");
}
re = (*player)->GetInterface(player,SL_IID_BUFFERQUEUE, &pcmQue);
if(re != SL_RESULT_SUCCESS){
LOGE("GetInterface SL_IID_BUFFERQUEUE failed");
}
创建播放器对象,从代码看主要干了:
- 创建CreateAudioPlayer播放器对象,并注册了接口:SL_IID_BUFFERQUEUE ,具体参数可以看上面四板斧中create的介绍。并且将上马的混音器输出设备对象audioSink 和 输出队列音频格式对象 ds传给播放器。
- 实例化播放器,并且获得播放器功能接口playInterface
4、设置播放回调函数,启动引擎播放。
- 启动引擎播放
// 设置回调函数,播放队列为空调用
(*pcmQue)->RegisterCallback(pcmQue, PcmCall,0);
(*playInterface)->SetPlayState(playInterface, SL_PLAYSTATE_PLAYING);
// 启动队列回调,传递一个空字符串来启动
(*pcmQue)->Enqueue(pcmQue,"",1);
启动了引擎后,就不会不停的执行回调函数,因此只需要在回调函数中压入数据即可。
- 回调函数,压入数据到播放队列
void PcmCall(SLAndroidSimpleBufferQueueItf bf, void * context){
LOGW("PcmCall");
static FILE *fp = NULL;
static char *buf = NULL;
if(!buf)
{
buf = new char[1024*1024];
}
if(!fp)
{
fp = fopen("test.pcm","rb") ;
}
if(!fp) return;
if(feof(fp)==0)// =0 表示没有到结尾
{
int len = fread(buf,1, 1024,fp);// 读取到buf,一个单位多少,读多大,fp
if(len > 0)
{
(*bf)-> Enqueue(bf, buf, len);// 发送到的bf, 发送的buf,长度
}
}
}
从代码看出,是将文件test.pcm 读取出来,然后传入到播放队列,来进行播放的。播放引擎有数据就会播放,没数据就会堵塞。
到这里完整的播放pcm的功能就完成了。