一、前置


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

在Android中,High Level Audio Libs是音频Java层API输入输出,属于高级API,相对来说,OpenSL ES则是比较底层级的API,属于C语言API。这里记录下用法

二、OpenSL ES引入


Android的OpenSL ES库是在NDK的platforms文件夹对应android平台相应cpu类型里面,如:

CmakeList中引入

三、开发流程


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

1、创建接口对象
2、设置混音器
3、创建播放器(录音器)
4、设置缓冲队列和回调函数
5、设置播放状态
6、启动回调函数
其中4和6是播放PCM等数据格式的音频是需要用到的。

四、播放PCM文件


4.1、 创建播放器和混音器


SLObjectItf engineObject = NULL;//用SLObjectItf声明引擎接口对象
SLEngineItf engineEngine = NULL;//声明具体的引擎对象实例void createEngine()
{SLresult result;//返回结果result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);//第一步创建引擎result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);//实现(Realize)engineObject接口对象result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);//通过engineObject的GetInterface方法初始化engineEngine
}//第一步,创建引擎createEngine();//第二步,创建混音器const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);(void)result;result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);(void)result;result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSnk = {&outputMix, NULL};

4.2、设置pcm格式的频率位数等信息并创建播放器

// 第三步,配置PCM格式信息SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};SLDataFormat_PCM pcm={SL_DATAFORMAT_PCM,//播放pcm格式的数据2,//2个声道(立体声)SL_SAMPLINGRATE_44_1,//44100hz的频率SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)SL_BYTEORDER_LITTLEENDIAN//结束标志};SLDataSource slDataSource = {&android_queue, &pcm};const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME};const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3, ids, req);//初始化播放器(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);//    得到接口后调用  获取Player接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);

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

//    注册回调缓冲区 获取缓冲队列接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);//缓冲接口回调(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);//回调函数
//喇叭没数据时候会回调这个函数,把数据写入到opensl es
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{Audio *wlAudio = (Audio *) context;if(wlAudio != NULL){int buffersize = wlAudio->resampleAudio();if(buffersize > 0){(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> buffer, buffersize);}}
}

4.4、设置播放状态并手动开始调用回调函数

pcmBufferCallBack(pcmBufferQueue, NULL);

4.5、音频重采样

FFmpeg从网络解码的音频格式和OpenSL ES设置的音频播放格式是不一样的,这里有个重采样的过程,可以看示例程序的resampleAudio方法

五、示例代码


OpenSL ES代码基本是固定的,写好基本不用修改

#include "Audio.h"Audio::Audio(Playstatus *playstatus, int sample_rate) {this->playstatus = playstatus;this->sample_rate = sample_rate;queue = new Queue(playstatus);buffer = (uint8_t *) av_malloc(sample_rate * 2 * 2);
}Audio::~Audio() {}void *decodPlay(void *data)
{Audio *wlAudio = (Audio *) data;wlAudio->initOpenSLES();pthread_exit(&wlAudio->thread_play);
}void Audio::play() {pthread_create(&thread_play, NULL, decodPlay, this);}//重采样
int Audio::resampleAudio() {while(playstatus != NULL && !playstatus->exit){avPacket = av_packet_alloc();if(queue->getAvpacket(avPacket) != 0){av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;continue;}ret = avcodec_send_packet(avCodecContext, avPacket);if(ret != 0){av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;continue;}avFrame = av_frame_alloc();ret = avcodec_receive_frame(avCodecContext, avFrame);if(ret == 0){if(avFrame->channels && avFrame->channel_layout == 0){avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);}else if(avFrame->channels == 0 && avFrame->channel_layout > 0){avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);}SwrContext *swr_ctx;swr_ctx = swr_alloc_set_opts(NULL,AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16,avFrame->sample_rate,avFrame->channel_layout,(AVSampleFormat) avFrame->format,avFrame->sample_rate,NULL, NULL);if(!swr_ctx || swr_init(swr_ctx) <0){av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;av_frame_free(&avFrame);av_free(avFrame);avFrame = NULL;swr_free(&swr_ctx);continue;}int nb = swr_convert(swr_ctx,&buffer,avFrame->nb_samples,(const uint8_t **) avFrame->data,avFrame->nb_samples);int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);data_size = nb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);if(LOG_DEBUG){LOGE("data_size is %d", data_size);}av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;av_frame_free(&avFrame);av_free(avFrame);avFrame = NULL;swr_free(&swr_ctx);break;} else{av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;av_frame_free(&avFrame);av_free(avFrame);avFrame = NULL;continue;}}return data_size;
}//喇叭没数据时候会回调这个函数,把数据写入到opensl es
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{Audio *wlAudio = (Audio *) context;if(wlAudio != NULL){int buffersize = wlAudio->resampleAudio();if(buffersize > 0){(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> buffer, buffersize);}}
}void Audio::initOpenSLES() {//第一步 创建一个引擎接口对象SLresult result;result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);//第二步,通过获取的引擎接口对象去创建混音器const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);(void)result;result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);(void)result;result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSnk = {&outputMix, 0};// 第三步,配置PCM格式信息SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};SLDataFormat_PCM pcm={SL_DATAFORMAT_PCM,//播放pcm格式的数据2,//2个声道(立体声)static_cast<SLuint32>(getCurrentSampleRateForOpensles(sample_rate)),//44100hz的频率SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)SL_BYTEORDER_LITTLEENDIAN//结束标志};SLDataSource slDataSource = {&android_queue, &pcm};const SLInterfaceID ids[1] = {SL_IID_BUFFERQUEUE};const SLboolean req[1] = {SL_BOOLEAN_TRUE};(*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 1, ids, req);//初始化播放器(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);//    得到接口后调用  获取Player接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);//    注册回调缓冲区 获取缓冲队列接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);//缓冲接口回调(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
//    获取播放状态接口(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);pcmBufferCallBack(pcmBufferQueue, this);}int Audio::getCurrentSampleRateForOpensles(int sample_rate) {int rate = 0;switch (sample_rate){case 8000:rate = SL_SAMPLINGRATE_8;break;case 11025:rate = SL_SAMPLINGRATE_11_025;break;case 12000:rate = SL_SAMPLINGRATE_12;break;case 16000:rate = SL_SAMPLINGRATE_16;break;case 22050:rate = SL_SAMPLINGRATE_22_05;break;case 24000:rate = SL_SAMPLINGRATE_24;break;case 32000:rate = SL_SAMPLINGRATE_32;break;case 44100:rate = SL_SAMPLINGRATE_44_1;break;case 48000:rate = SL_SAMPLINGRATE_48;break;case 64000:rate = SL_SAMPLINGRATE_64;break;case 88200:rate = SL_SAMPLINGRATE_88_2;break;case 96000:rate = SL_SAMPLINGRATE_96;break;case 192000:rate = SL_SAMPLINGRATE_192;break;default:rate =  SL_SAMPLINGRATE_44_1;}return rate;
}

音视频系列--OpenSL ES基础用法总结相关推荐

  1. 【Android音视频】OpenSL ES音频播放示例一

    本文将实现一个使用OpenSL ES来播放assets目录下mp3歌曲的demo(实际推荐大家使用oboe库). Android NDK之高性能音频https://developer.android. ...

  2. datagrid 重载本地数据_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/104893135,欢迎关注,点赞,评论. 前言 音视频系列: HectoorZ ...

  3. 音视频系列2:ffmpeg将H.264解码为RGB

    音视频系列2:ffmpeg将H.264解码为RGB 前言 源码 前言 喜大普奔,终于更新啦,上期说到,如何使用ffmpeg+rtmp进行拉流,不熟悉的小伙伴们,可以先看上一期.今天我们要实现的是使用f ...

  4. ffmpeg rtmp 不清晰_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/104893135,欢迎关注,点赞,评论. 前言 音视频系列: HectoorZ ...

  5. ffmpeg rtmp 花屏_音视频系列6:ffmpeg多线程拉流

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/105302384,欢迎关注,点赞,评论. 前言 本篇博客是音视频系列的续集与改 ...

  6. 音视频系列四 ffmpeg配合opensl es播放音频

    文章目录 一 解码成PCM 二 opensl es创建引擎,混音器 创建引擎 创建混音器 创建播放器,注册回调函数 一 解码成PCM 流程其实和视频差不多,参考播放视频的代码,稍作增改 先定义后面需要 ...

  7. 音视频系列1:流媒体

    1. 流媒体技术 1.1 基本需求 流媒体技术需要: 1.允许客户端在不下载完整文件的时候即可以开始播放视频: 2.允许客户端从完整内容的任何位置开始播放(不包括视频直播): 3.针对视频直播,允许客 ...

  8. 音视频系列--音频基本理论

    一.何为声音 中学物理中我们知道,声音是物体振动产生的声波.声音通过介质(空气.固体.液体)传入到人耳中,带动听小骨振动,经过一系列的神经信号传递后,被人所感知. 声音是一种波.物体振动时会使介质(如 ...

  9. 音视频系列2:基本知识

    1. 存储格式 1.1 WAV.WMV.WMA.ASF.MMS.AVI:微软全家桶 微软的东西,windows用户经常能见到. 首先是wav音频文件.WAV是微软开发的一种声音文件格式,它实际是采用R ...

最新文章

  1. .NET零基础入门06:面向对象入门
  2. windows给应用断网
  3. 【C语言】数字在排序数组中出现的次数(改动)
  4. 命令提示符_基本介绍
  5. Elasticsearch的Watcher插件
  6. python更换tkinter图标样式
  7. 设计模式--观察者模式与命令模式
  8. 宏杉科技高端存储再获认可 成功中标国家电网集采
  9. fckeditor 2.6 php,php下 FCKeditor 2.6.6的使用和配置
  10. celeste第二章_蔚蓝山Celeste全成就指南_蔚蓝山Celeste全成就获得方法_游戏堡
  11. 中兴网信发布“广义智慧城市顶层设计框架”
  12. Nacos下载与安装
  13. 苹果平替笔哪款好用?性价比最高的苹果平替笔
  14. 李宏毅机器学习 之 回归Regression(二)
  15. 剪不断,理不乱——三层架构之抽象工厂加反射
  16. 初中 昆虫记思维导图_《昆虫记》的思维导图
  17. win8运行matlab7.0,Win8.1系统中matlab7.0不兼容的解决方法
  18. #pragma warning 启用和禁用warning
  19. python数学知识_【数学知识】高一的集合知识的Python编程实现
  20. linux下挂载gpt磁盘分区,Linux下使用gpt给磁盘分区、格式化、挂载

热门文章

  1. WIN10 快捷键 个人常用
  2. 抓取dump的工具ProcDump使用
  3. ubuntu nginx安装php mysql,ubuntu下配置nginx+php+mysql详解
  4. ITEXT-小图片并排显示,大图片按行显示
  5. java计算机毕业设计基于web的公益募捐网站源码+数据库+系统+lw文档+mybatis+运行部署
  6. 启建教育:2020年一级消防工程师备考,该怎样合理分配时间?
  7. d2admin 登陆 笔记
  8. ubuntu python安装pip_在 Ubuntu 上安装 pip的方法
  9. Response request
  10. Mysql学习之表结构设计、索引、外键与数据插入