typedef struct SLDataSink_ {
void *pLocator;
void *pFormat;
} SLDataSink;
其中,pLocator 主要有如下几种:
- SLDataLocator_Address
- SLDataLocator_BufferQueue
- SLDataLocator_IODevice
- SLDataLocator_MIDIBufferQueue
- SLDataLocator_URI
也就是说,输入源/输出源,既可以是 URL,也可以 Device,或者来自于缓冲区队列等等。
那么我们在创建AudioPlayer对象前,还需要先创建输入和输出,输入我们使用一个缓冲队列,输出则使用outputMix输出到默认声音设备。
使用步骤:
1、输入源
//输入源为缓冲队列
SLDataLocator_AndroidSimpleBufferQueue dsLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
// 设置音频格式
SLDataFormat_PCM outputFormat = { SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN
// 输入源
SLDataSource audioSource = { &dsLocator , &outputFormat };
2、输出源
outputMix对象需要通过CreateOutputMix
接口创建,并调用Realize
初始化
static SLObjectItf mix = NULL;
//创建mix
(*iengine)->CreateOutputMix(iengine, &mix, 0, NULL, NULL);
//初始化
re = (*mix)->Realize(mix, SL_BOOLEAN_FALSE);
if(re != SL_RESULT_SUCCESS )
ALOGE("mix Realize error!");
return false;
//输出源为mix
SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX, mix};
SLDataSink audioSink = {&outmix,NULL};
3、创建AudioPlayer并初始化
AudioPlayer对象需要通过CreateAudioPlayer
接口创建,并调用Realize
初始化
static SLObjectItf player = NULL;
const SLInterfaceID outputInterfaces[1] = { SL_IID_BUFFERQUEUE };
const SLboolean req[] = {SL_BOOLEAN_TRUE};
//audio player
(*iengine)->CreateAudioPlayer(iengine, &player, &audioSource, &audioSink, 1, outputInterfaces, req);
re = (*player)->Realize(player, SL_BOOLEAN_FALSE);
if(re != SL_RESULT_SUCCESS )
ALOGE("player Realize error!");
return false;
4、设置输入队列的回调函数,回调函数在数据不足时会内部调用。然后启动播放即可。
需要通过SL_IID_ANDROIDSIMPLEBUFFERQUEUE
获取队列接口,并RegisterCallback
设置回调函数。
需要通过SL_IID_PLAY
获取播放接口,并设置为播放状态,同时调用Enqueue
给队列压入一帧空数据。
需要注意的是,入队列的数据并非立刻播放,所以不能把这个数据立刻释放,否则会造成丢帧。
//获取队列接口
static SLPlayItf iplay = NULL;
static SLAndroidSimpleBufferQueueItf pcmQue = NULL;
(*player)->GetInterface(player, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pcmQue);
if(re != SL_RESULT_SUCCESS )
ALOGE("get pcmQue error!");
return false;
//设置回调函数,播放队列空调用
(*pcmQue)->RegisterCallback(pcmQue, PcmCall, this);
//获取player接口
re = (*player)->GetInterface(play, SL_IID_PLAY, &iplayer);
if(re != SL_RESULT_SUCCESS )
ALOGE("get iplayer error!");
return false;
//设置为播放状态
(*iplay)->SetPlayState(iplay, SL_PLAYSTATE_PLAYING);
//启动队列回调
(*pcmQue)->Enqueue(pcmQue, "", 1);
当播放队列数据为空时,会调用队列的回调函数,所以需要在回调函数中给队列注入音频数据
static void PcmCall(SLAndroidSimpleBufferQueueItf bf, void *contex)
if(pcmQue && (*pcmQue))
if(aFrame == NULL)
(*pcmQue)->Enqueue(pcmQue, "", 1);
//进队列后并不是直接播放,所以需要一个buf来存,不能释放掉
memcpy(buf, aFrame->data, aFrame->size);
(*pcmQue)->Enqueue(pcmQue, buf, aFrame->size);
5、播放结束后,需要调用Destroy
将各个对象释放。
opensl es参考自:https://www.jianshu.com/p/cccb59466e99
本文主要介绍Android上可以进行音频(PCM)播放的两个组件–AudioTrack/OpenSL ES的简单使用方法。
对一个音频文件(如MP3文件),如何使用FFmpeg进行解码获取到PCM,之前的文章已经有相应的说明:
https://blog.csdn.net/myvest/article/details/89254452。
那么解码后或者mic采集的PCM数据,是如何播放的呢,首先一般会对PCM数据进行重采样,也即是转换为指定的格式。重采样可以参考:https://blog.csdn.net/myvest/article/details/89442000
最后,进入本文主题,介绍A
PCM 数据播放在开发中也经常使用,例如自己编写播放器,解码之后的音频PCM数据,就可以通过OpenSL 播放,比用Java层的AudioTrack更快,延迟更低。
下面我们编写OpenSL PCM播放,播放的主要逻辑是从文件读取PCM数据然后播放,代码编写环境Eclipse。
一、 Eclipse 创建Android工程
二、布局XML 创建文件 /res/layout
//创建引擎对象,获取引擎接口
(*pEngine)->Realize(pEngine, SL_BOOLEAN_FALSE);
(*pEngine)->GetInterface(pEngine, SL_IID_ENGINE, &engineEngine);
//获取source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODE.
class PCMPlayer extends Thread {
protected AudioTrack mAudioTrack;
protected int mMiniBufferSize;
protected byte[] mBuffer;
File file;
FileInputStream in;
* @param ...
在讲解音频渲染之前,需要对音频的基础知识有所了解,所以该篇分为基础概念和AudioTrack 以及 OpenSL ES Demo 实例讲解,这样有助于更好的理解 Android 中音频渲染。
音频的基础概念涉及的知识点比较多,该篇文章的上半部分会详细的介绍,后续文章基本上都会涉及音频的开发,有了基础对于后面的内容就更容易上手了。
音频的基础知识
播放器入门到提高
快速掌握音视频开发基础知识
声音的物理性质
说到声音我相信只要听力正常的人都听见过声音,那么声音是如何产生的呢?记得初中物理课本上
```java
int sampleRate = 44100;
int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
audioTrack.play();
byte[] data = null; // 音频数据
while (true) {
int bytesWritten = audioTrack.write(data, 0, data.length);
if (bytesWritten <= 0) {
break;
audioTrack.stop();
audioTrack.release();
在上面的代码中,我们首先创建了一个 `AudioTrack` 实例,然后调用 `play()` 方法开始播放音频。然后,我们可以通过循环调用 `write()` 方法不断向 `AudioTrack` 实例中写入音频数据,直到音频数据全部播放完毕。最后,我们调用 `stop()` 方法停止播放并释放 `AudioTrack` 实例。
需要注意的是,我们在创建 `AudioTrack` 实例时需要提供的参数包括采样率、声道数、采样位数等,这些参数需要根据实际的音频数据进行设置。此外,我们还需要根据实际情况设置音频数据的读取方式,可以使用 `MODE_STATIC` 或 `MODE_STREAM` 两种模式中的任意一种。在使用 `MODE_STREAM` 模式时,我们需要在循环中不断调用 `write()` 方法向 `AudioTrack` 实例中写入数据。