音视频实践学习

  • android全平台编译ffmpeg以及x264与fdk-aac实践
  • ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器
  • android全平台编译ffmpeg合并为单个库实践
  • android-studio使用cmake编译ffmpeg实践
  • android全平台下基于ffmpeg解码MP4视频文件为YUV文件
  • android全平台编译ffmpeg支持命令行实践
  • android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
  • android平台下音频编码之编译LAME库转码PCM为MP3
  • ubuntu平台下编译vlc-android视频播放器实践
  • 图解YU12、I420、YV12、NV12、NV21、YUV420P、YUV420SP、YUV422P、YUV444P的区别
  • 图解RGB565、RGB555、RGB16、RGB24、RGB32、ARGB32等格式的区别
  • YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片
  • android全平台编译libyuv库实现YUV和RGB的转换
  • android平台下基于ffmpeg对相机采集的NV21数据编码为MP4视频文件
  • android平台下基于ffmpeg采集Camera数据编码成H.264推流到RTMP服务器
  • android平台下基于ffmpeg和ANativeWindow实现简单的视频播放器
  • android平台下基于ffmpeg实现对相机预览截图的功能(NV21数据编码为JPEG文件)
  • android平台下基于ffmpeg的swscale模块实现对YUV和RGB数据进行转换
  • android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM音频数据
  • androd平台下基于ffmpeg对AudioRecord录制的PCM音频数据编码为AAC文件
  • android平台下基于OpenSL ES实现音频录制功能

概述

我们日常在处理音频录制的时候,大部分情况下都是使用AudioRecord录制原始的PCM数据,但是音频相关的处理通常都是在native层进行的,今天笔者要记录一下在native层通过OpenSL ES来完成音频的录制。

配置权限

动态权限的申请这里不赘述

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

导入OpenSL ES库

CMake方式:CMakeList.txt中加入

target_link_libraries(native-lib OpenSLES)

NDK Build方式:在Android.mk文件添加选项

LOCAL_LDLIBS = -lOpenSLES

源代码中引入头文件

#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.h>

录制流程分析

android平台下基于OpenSL ES实现音频录制功能_第1张图片

开始录制

SL_API SLresult SLAPIENTRY slCreateEngine(    SLObjectItf             *pEngine,           //对象地址,用于传出对象    SLuint32                numOptions,         //配置参数数量    const SLEngineOption    *pEngineOptions,    //配置参数,为枚举数组    SLuint32                numInterfaces,      //支持的接口数量    const SLInterfaceID     *pInterfaceIds,     //具体的要支持的接口,是枚举的数组    const SLboolean         *pInterfaceRequired //具体的要支持的接口是开放的还是关闭的,也是一个数组,这三个参数长度是一致的);
void OpenSLESRecorder::StartRecord(const char *pcmPath, int sampleRate, int channels, int bitRate) {    //打开输出文件    pcmFile = fopen(pcmPath, "w");    recordBuffer = new RecordBuffer(RECORDER_FRAMES * 2);    //1. 调用全局方法创建一个引擎对象(OpenSL ES唯一入口)    SLresult result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);    if (SL_RESULT_SUCCESS != result) {        return;    }    //2. 实例化这个对象    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);    if (SL_RESULT_SUCCESS != result) {        return;    }    //3. 从这个对象里面获取引擎接口    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);    if (SL_RESULT_SUCCESS != result) {        return;    }    //4. 设置IO设备(麦克风)    SLDataLocator_IODevice ioDevice = {            SL_DATALOCATOR_IODEVICE,         //类型            SL_IODEVICE_AUDIOINPUT,          //device类型 选择了音频输入类型            SL_DEFAULTDEVICEID_AUDIOINPUT,   //deviceID            NULL                             //device实例    };    SLDataSource dataSource = {            &ioDevice,                      //SLDataLocator_IODevice配置输入            NULL                             //输入格式,采集的并不需要    };    //5. 设置输出buffer队列    SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {            SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,    //类型 这里只能是这个常量            2                                           //buffer的数量    };    //6. 设置输出数据的格式    SLDataFormat_PCM pcmFormat = {            SL_DATAFORMAT_PCM,                             //输出PCM格式的数据            (SLuint32) 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配置输出            &pcmFormat                      //输出数据格式    };    SLAndroidSimpleBufferQueueItf recorderBufferQueue; //Buffer接口    //7. 创建录制的对象    const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};    const SLboolean req[1] = {SL_BOOLEAN_TRUE};    result = (*engineEngine)->CreateAudioRecorder(engineEngine,        //引擎接口                                                  &recorderObject,   //录制对象地址,用于传出对象                                                  &dataSource,          //输入配置                                                  &audioSink,         //输出配置                                                  1,                  //支持的接口数量                                                  id,                 //具体的要支持的接口                                                  req                 //具体的要支持的接口是开放的还是关闭的    );    if (SL_RESULT_SUCCESS != result) {        return;    }    //8. 实例化这个录制对象    result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);    if (SL_RESULT_SUCCESS != result) {        return;    }    //9. 获取录制接口    (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecorder);    //10. 获取Buffer接口    (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,                                    &recorderBufferQueue);    finished = false;    result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer->getRecordBuffer(),                                             recorderSize);    if (SL_RESULT_SUCCESS != result) {        return;    }    result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, RecordCallback,                                                      this);    if (SL_RESULT_SUCCESS != result) {        return;    }    //11. 开始录音    (*recorderRecorder)->SetRecordState(recorderRecorder, SL_RECORDSTATE_RECORDING);}

停止录制

void OpenSLESRecorder::StopRecord() {    if (NULL != recorderRecorder) {        finished = true;    }}

回调函数

static void RecordCallback(SLAndroidSimpleBufferQueueItf bufferQueue, void *context) {    LOGI("录制大小: %d", recorderSize);    OpenSLESRecorder *recorder = (OpenSLESRecorder *) context;    if (NULL != recorder->recordBuffer) {        fwrite(recorder->recordBuffer->getNowBuffer(), 1, recorderSize, recorder->pcmFile);    }    if (recorder->finished) {        (*recorder->recorderRecorder)->SetRecordState(recorder->recorderRecorder,                                                      SL_RECORDSTATE_STOPPED);        //刷新缓冲区后,关闭流        fclose(recorder->pcmFile);        //释放内存        delete recorder->recordBuffer;        recorder->recordBuffer = NULL;        LOGI("停止录音");    } else {        (*bufferQueue)->Enqueue(bufferQueue, recorder->recordBuffer->getRecordBuffer(),                                recorderSize);    }}

小结

如果参考官方的例子,整个过程并不算复杂,笔者也是参考官方的例子进行操作实践

android平台下基于OpenSL ES实现音频录制功能_第2张图片

项目地址:audio-opensles
https://github.com/byhook/ffmpeg4android

参考

https://github.com/googlesamples/android-ndk/tree/master/native-audio
https://developer.android.com/ndk/guides/audio/opensl/getting-started

更多相关文章

  1. 自己写的一套应用管理系统(包含一套app系统,一套后台web管理系统《
  2. Android平台各类恶意软件及病毒概览
  3. 在英特尔® 架构平台上开发和优化基于 NDK 的 Android 游戏应用
  4. 跨平台编译的经验之谈--cmake编译Android之ndk-build
  5. Android数据加密之Base64编码算法
  6. 从头学Android之Android的数据存储--File

随机推荐

  1. Android4.0 battery架构和管理
  2. 重拾Android之路之Android签名及打包
  3. 配置ionic+Android开发环境
  4. Android(安卓)Wifi BT的工作流程和portin
  5. Android(安卓)Toolbar的详细使用步骤
  6. Android(安卓)RecyclerView —— 适配器
  7. Android:真机调试遇到的问题(INSTALL_FAILE
  8. android tv盒子播放器控制 监听上下左右
  9. CMake相关问题解决记录
  10. Android(安卓)7.0 Audio :通话中的音频调