美文网首页
android平台下基于OpenSL ES实现音频录制功能

android平台下基于OpenSL ES实现音频录制功能

作者: Andy周 | 来源:发表于2019-01-31 23:38 被阅读19次

    概述

    我们日常在处理音频录制的时候,大部分情况下都是使用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>
    

    录制流程分析

    image

    开始录制

    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);
        }
    }
    

    小结

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

    image

    项目地址: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

    相关文章

      网友评论

          本文标题:android平台下基于OpenSL ES实现音频录制功能

          本文链接:https://www.haomeiwen.com/subject/bhhrsqtx.html