android open es录像,音频采集:Android基于OpenSL ES的实现

本文详细介绍了在Android系统中利用OpenSLES进行音频采集的方法,包括权限申请、OpenSLES的基本概念、如何创建引擎对象、设置IO设备、创建录制器、开始和停止录音以及释放资源。通过示例代码展示了整个音频采集流程,帮助开发者理解并实现音频采集功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

这篇文章简单介绍下移动端Android系统下利用OpenSL ES进行音频采集方法。

按照惯例先上一份源码 AudioRecordLib 。

OpenSL ES采集的核心实现在于 openslescore.cpp 这个文件。

权限申请

想要使用OpenSL ES,需要在AndroidManifest.xml的配置文件里面增加权限

OpenSL ES开发简介

什么是OpenSL ESOpenSL ES全称为Open Sound Library for Embedded Systems,即嵌入式音频加速标准。OpenSL ES是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速 API。它为嵌入式移动多媒体设备上的本地 应用程序开发者提供了标准化、高性能、低响应时间的音频功能实现方法,同时还实现了软/硬件音频性能的直接跨平台部署,不仅降低了执行难度,而且促进了高级音频市场的发展。

OpenSL ES架构原理

虽然OpenSL ES是基于C语言设计的API,但是其实基于对象和接口提供服务的,采用了面向对象的思想来开发API。

这里简单说一下OpenSL ES里面的对象和接口的概念:对象:类似于C++中类用来提供一组资源极其状态的抽象,也就是我们可以根据特定类型type(例如音频录制type)来获取一个音频录制的对象,但是对于这个对象我们并不能直接操作(换句话讲也就是我们不能直接在这个对象调用开始/结束录制的逻辑)。

接口:接口是对象提供一组特定功能方法的抽象,也就是可以从对象中获取接口(例如从录制对象中获取录制接口),然后通过接口来改变对象的状态(例如通过接口设置开始录制)以便使用对象的功能(对于就是录制功能)。

PS:对象可以有一个或者多个接口的实例,但是接口实例肯定只属于一个对象,以上就是OpenSL ES的开发理念。

引用相关库文件以及头文件

怎么导入OpenSL ES库

CMake方式:CMakeList.txt中加入#找打Android lib库里面的libOpenSLES.so的库find_library( OpenSLES-lib

OpenSLES )#链接到你的native工程的库target_link_libraries( your-native.so                       ${OpenSLES-lib}

)

NDK Build方式:在Makefile文件Android.mk添加链接选项LOCAL_LDLIBS = -lOpenSLES

引入头文件#include #include 

创建引擎对象

简单介绍下入口slCreateEngine()这个全局方法:SL_API SLresult SLAPIENTRY slCreateEngine(

SLObjectItf             *pEngine,           //对象地址,用于传出对象

SLuint32                numOptions,         //配置参数数量

const SLEngineOption    *pEngineOptions,    //配置参数,为枚举数组

SLuint32                numInterfaces,      //支持的接口数量

const SLInterfaceID     *pInterfaceIds,     //具体的要支持的接口,是枚举的数组

const SLboolean         *pInterfaceRequired //具体的要支持的接口是开放的还是关闭的,也是一个数组,这三个参数长度是一致的);

一个较为完整的创建过程代码示例:SLObjectItf engine_object;   //引擎对象SLEngineItf engine_engine;   //引擎接口//调用全局方法创建一个引擎对象(OpenSL ES唯一入口)slCreateEngine(&engine_object, 0, NULL, 0, NULL, NULL);//实例化这个对象(*engine_object)->Realize(engine_object, SL_BOOLEAN_FALSE);//从这个对象里面获取引擎接口(*engine_object)->GetInterface(engine_object, SL_IID_ENGINE, &engine_engine);

当然调用每一个API后要检测其返回值是否等于 SL_RESULT_SUCCESS,限于篇幅就在上面代码没有处理,后续的示例代码也是同理。

设置IO设备(麦克风) 输入输出

我们需要设置采集设备的一些输入输出配置://设置IO设备(麦克风)SLDataLocator_IODevice io_device = {

SL_DATALOCATOR_IODEVICE,         //类型 这里只能是SL_DATALOCATOR_IODEVICE

SL_IODEVICE_AUDIOINPUT,          //device类型  选择了音频输入类型

SL_DEFAULTDEVICEID_AUDIOINPUT,   //deviceID 对应的是SL_DEFAULTDEVICEID_AUDIOINPUT

NULL                             //device实例};

SLDataSource data_src = {

&io_device,                      //SLDataLocator_IODevice配置输入

NULL                             //输入格式,采集的并不需要};//设置输出buffer队列SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {

SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,    //类型 这里只能是SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE

2                                           //buffer的数量};//设置输出数据的格式SLDataFormat_PCM format_pcm = {

SL_DATAFORMAT_PCM,                             //输出PCM格式的数据

num_channels,                                  //输出的声道数量

SL_SAMPLINGRATE_44_1,                          //输出的采样频率,这里是44100Hz

SL_PCMSAMPLEFORMAT_FIXED_16,                   //输出的采样格式,这里是16bit

SL_PCMSAMPLEFORMAT_FIXED_16,                   //一般来说,跟随上一个参数

SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,  //双声道配置,如果单声道可以用 SL_SPEAKER_FRONT_CENTER

SL_BYTEORDER_LITTLEENDIAN                      //PCM数据的大小端排列};

SLDataSink audioSink = {

&buffer_queue,                   //SLDataFormat_PCM配置输出

&format_pcm                      //输出数据格式};

创建录制器

主要是创建录制对象和获取录制相关的接口:SLObjectItf recorder_object;                         //录制对象,这个对象我们从里面获取了2个接口SLRecordItf recorder_recoder;                        //录制接口SLAndroidSimpleBufferQueueItf recorder_buffer_queue; //Buffer接口//创建录制的对象,并且指定开放SL_IID_ANDROIDSIMPLEBUFFERQUEUE这个接口const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};const SLboolean req[1] = {SL_BOOLEAN_TRUE};

(*engine_engine)->CreateAudioRecorder(engine_engine,        //引擎接口

&recorder_object,   //录制对象地址,用于传出对象

&data_src,          //输入配置

&audioSink,         //输出配置

1,                  //支持的接口数量

id,                 //具体的要支持的接口

req                 //具体的要支持的接口是开放的还是关闭的

);//实例化这个录制对象(*recorder_object)->Realize(recorder_object, SL_BOOLEAN_FALSE);//获取录制接口(*recorder_object)->GetInterface(recorder_object, SL_IID_RECORD, &recorder_recoder);//获取Buffer接口(*recorder_object)->GetInterface(recorder_object, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorder_buffer_queue);

设置数据回调并且开始录制

设置开始录制状态,并通过回调函数获取录制的音频PCM数据:int8_t *pcm_data; //数据缓存区//申请一块内存,注意RECORDER_FRAMES是自定义的一个宏,指的是采集的frame数量,具体还要根据你的采集格式(例如16bit)计算pcm_data = static_cast(malloc(sizeof(int8_t) * RECORDER_FRAMES));//设置数据回调接口bqRecorderCallback,最后一个参数是可以传输自定义的上下文引用(*recorder_buffer_queue)->RegisterCallback(recorder_buffer_queue, bqRecorderCallback, this);//设置录制器为录制状态 SL_RECORDSTATE_RECORDING(*recorder_recoder)->SetRecordState(recorder_recoder, SL_RECORDSTATE_RECORDING);

/在设置完录制状态后一定需要先Enqueue一次,这样的话才会开始采集回调

(*recorder_buffer_queue)->Enqueue(recorder_buffer_queue, pcm_data, RECORDER_FRAMES);//数据回调函数static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){    //从pcm_data获取RECORDER_FRAMES长度的PCM数据

//注意这个是另外一条采集线程回调,可能需要加一个变量recodering控制退出采集

if (recodering)

{

pcm_write(pcm_data, RECORDER_FRAMES);        //取完数据,需要调用Enqueue触发下一次数据回调

(*recorder_buffer_queue)->Enqueue(recorder_buffer_queue, pcm_data, RECORDER_FRAMES);

}

}

停止录制和释放OpenSL ES资源

如果我们不需要采集了,需要调用接口停止采集并在适当的时机释放OpenSL ES相关资源。//设置录制器为停止状态 SL_RECORDSTATE_STOPPED(*recorder_recoder)->SetRecordState(recorder_recoder, SL_RECORDSTATE_STOPPED);//只需要销毁OpenSL ES对象,接口不需要做Destroy处理。(*recorder_object)->Destroy(recorder_object);

(*engine_object)->Destroy(engine_object);//释放缓存区内存free(pcm_data);

这样就完整的结束了OpenSL ES的采集业务。

播放PCM文件

Audacity这个工具可以导入pcm原始文件,并且提供了波形图查看和播放功能。

操作流程是:

文件 => 导入 => 原始数据 => 设置PCM数据格式 => 导入

具体效果图如下:

AAffA0nNPuCLAAAAAElFTkSuQmCC

p1.png

结语

作者:码农叔叔

链接:https://www.jianshu.com/p/0cb2ba3171b8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值