美文网首页一个Android码农的音视频学习之旅
Android音视频(五) OpenSL ES录制、播放音频

Android音视频(五) OpenSL ES录制、播放音频

作者: MzDavid | 来源:发表于2019-02-12 10:35 被阅读1次

    Android音视频(一) Camera2 API采集数据

    Android音视频(二)音频AudioRecord和AudioTrack

    Android音视频(三)FFmpeg Camera2推流直播

    Android音视频(四)MediaCodec编解码AAC

    OpenSL ES (Open Sound Library for Embedded Systems)是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。简单来说OpenSL ES是一个嵌入式跨平台免费的音频处理库。

    在Android中一般使用AudioRecord、MediaRecorder对音频进行采集,使用MediaPlayer、AudioTrack、SoundPool进行音频播放。但这些都是在Java层上的接口,如果使用FFmpeg在C/C++层做音视频处理,那么调用这几个方法就比较麻烦了,所以Android NDK也提供了一个叫做OpenSL的C语言引擎用于声音的处理,这篇博客就是简单使用OpenSL去录制、播放音频。

    开发流程

    OpenSL ES 的开发流程主要有如下6个步骤:

    1、创建接口对象

    2、设置混音器

    3、创建播放器(录音器)

    4、设置缓冲队列和回调函数

    5、设置播放状态

    6、启动回调函数

    其中第4步和第6步是OpenSL ES 播放PCM等数据格式的音频是需要用到的。

    代码实现

    定义Native方法

    
        //播放音频
        public native int play(String filePath);
    
        //停止播放音频
        public native int playStop();
        
        //录制音频
        public native int record(String filePath);
        
        //停止录制音频
        public native int stopRecod();
        
    

    录音

    参数配置

        //设置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格式的数据
                1,                                             //输出的声道数量
                SL_SAMPLINGRATE_44_1,                          //输出的采样频率,这里是44100Hz
                SL_PCMSAMPLEFORMAT_FIXED_16,                   //输出的采样格式,这里是16bit
                SL_PCMSAMPLEFORMAT_FIXED_16,                   //一般来说,跟随上一个参数
                SL_SPEAKER_FRONT_LEFT,  //双声道配置,如果单声道可以用 SL_SPEAKER_FRONT_CENTER
                SL_BYTEORDER_LITTLEENDIAN                      //PCM数据的大小端排列
        };
        SLDataSink audioSink = {
                &buffer_queue,                   //SLDataFormat_PCM配置输出
                &format_pcm                      //输出数据格式
        };
    
    

    录音流程

        //1 创建引擎
        SLEngineItf eng = CreateRecordSL();
        if (eng) {
            LOGE("CreateSL success! ");
        } else {
            LOGE("CreateSL failed! ");
        }
    
    
        //创建录制的对象,并且指定开放SL_IID_ANDROIDSIMPLEBUFFERQUEUE这个接口
        const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
        const SLboolean req[1] = {SL_BOOLEAN_TRUE};
        re = (*eng)->CreateAudioRecorder(eng,        //引擎接口
                                         &recorder_object,   //录制对象地址,用于传出对象
                                         &data_src,          //输入配置
                                         &audioSink,         //输出配置
                                         1,                  //支持的接口数量
                                         id,                 //具体的要支持的接口
                                         req                 //具体的要支持的接口是开放的还是关闭的
        );
    
        if (re != SL_RESULT_SUCCESS) {
            LOGE("CreateAudioRecorder failed!");
            return -1;
        }
    
        //实例化这个录制对象
        re = (*recorder_object)->Realize(recorder_object, SL_BOOLEAN_FALSE);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("Realize failed!");
        }
    
        //获取录制接口
        re = (*recorder_object)->GetInterface(recorder_object, SL_IID_RECORD, &recordItf);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("GetInterface1 failed!");
        }
        //获取Buffer接口
        re = (*recorder_object)->GetInterface(recorder_object, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                              &recorder_buffer_queue);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("GetInterface2 failed!");
        }
    
        //申请一块内存,注意RECORDER_FRAMES是自定义的一个宏,指的是采集的frame数量,具体还要根据你的采集格式(例如16bit)计算
        pcm_data = malloc(BUFFER_SIZE_IN_BYTES);
    
        //设置数据回调接口bqRecorderCallback,最后一个参数是可以传输自定义的上下文引用
        re = (*recorder_buffer_queue)->RegisterCallback(recorder_buffer_queue, bqRecorderCallback, 0);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("RegisterCallback failed!");
        }
        //设置录制器为录制状态 SL_RECORDSTATE_RECORDING
        re = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("SetRecordState failed!");
        }
        //在设置完录制状态后一定需要先Enqueue一次,这样的话才会开始采集回调
        re = (*recorder_buffer_queue)->Enqueue(recorder_buffer_queue, pcm_data, 8192);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("Enqueue failed!");
        }
    
    

    回调函数

    //数据回调函数
    void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
    
        fwrite(pcm_data, BUFFER_SIZE_IN_BYTES, 1, gFile);
        //取完数据,需要调用Enqueue触发下一次数据回调
        (*bq)->Enqueue(bq, pcm_data, BUFFER_SIZE_IN_BYTES);
    
    }
    

    播放

        //1 创建引擎
        SLEngineItf eng = CreateSL();
        if (eng) {
            LOGE("CreateSL success! ");
        } else {
            LOGE("CreateSL failed! ");
            return -1;
        }
    
        //2 创建混音器
        SLObjectItf mix = NULL;
        SLresult re = 0;
    
        re = (*eng)->CreateOutputMix(eng, &mix, 0, 0, 0);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("SL_RESULT_SUCCESS failed!");
            return -1;
        }
    
        re = (*mix)->Realize(mix, SL_BOOLEAN_FALSE);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("(*mix)->Realize failed!");
            return -1;
        }
    
    
        SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX, mix};
        SLDataSink audioSink = {&outmix, 0};
    
        //3 配置音频信息
        //数据定位器 就是定位要播放声音数据的存放位置,分为4种:内存位置,输入/输出设备位置,缓冲区队列位置,和midi缓冲区队列位置。
        SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};
        //音频格式
        SLDataFormat_PCM pcm = {
                SL_DATAFORMAT_PCM,
                1,//    声道数
                SL_SAMPLINGRATE_44_1,
                SL_PCMSAMPLEFORMAT_FIXED_16,
                SL_PCMSAMPLEFORMAT_FIXED_16,
                SL_SPEAKER_FRONT_LEFT,
                SL_BYTEORDER_LITTLEENDIAN //字节序,小端
        };
        SLDataSource ds = {&que, &pcm};
    
    
        //4 创建播放器
        SLObjectItf player = NULL;
        SLPlayItf iplayer = NULL;
        SLAndroidSimpleBufferQueueItf pcmQue = NULL;
        const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
        const SLboolean req[] = {SL_BOOLEAN_TRUE};
        re = (*eng)->CreateAudioPlayer(eng, &player, &ds, &audioSink,
                                       sizeof(ids) / sizeof(SLInterfaceID), ids, req);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("CreateAudioPlayer failed!");
        } else {
            LOGE("CreateAudioPlayer success!");
        }
        (*player)->Realize(player, SL_BOOLEAN_FALSE);
        //获取player接口
        re = (*player)->GetInterface(player, SL_IID_PLAY, &iplayer);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("GetInterface SL_IID_PLAY failed!");
        }
        re = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &pcmQue);
        if (re != SL_RESULT_SUCCESS) {
            LOGE("GetInterface SL_IID_BUFFERQUEUE failed!");
        }
    
        //设置回调函数,播放队列空调用
        (*pcmQue)->RegisterCallback(pcmQue, pcmCallBack, 0);
    
        //5 设置为播放状态
        (*iplayer)->SetPlayState(iplayer, SL_PLAYSTATE_PLAYING);
    
        //6 启动队列回调
        (*pcmQue)->Enqueue(pcmQue, "", 1);
    

    回调保存数据

     //回调函数
    void pcmCallBack(SLAndroidSimpleBufferQueueItf bf, void *contex) {
        static char buf[1024 * 1024] = "";
        if (feof(File) == 0) { //没到结尾
            int len = (int) fread(&buf, 1, 1024, File);
            if (len > 0) {
                // 加入队列
                (*bf)->Enqueue(bf, &buf, len);
            }
        }
    }
    

    如有问题欢迎留言,Github源码-AudioDemo-openSLActivity

    相关文章

      网友评论

        本文标题:Android音视频(五) OpenSL ES录制、播放音频

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