Android音視頻(一) Camera2 API採集數據java
Android音視頻(二)音頻AudioRecord和AudioTrackgit
Android音視頻(三)FFmpeg Camera2推流直播github
Android音視頻(四)MediaCodec編解碼AACbash
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去錄製、播放音頻,基於以前作的AudioDemo開發。post
OpenSL ES 的開發流程主要有以下6個步驟:性能
一、建立接口對象優化
二、設置混音器ui
三、建立播放器(錄音器)spa
四、設置緩衝隊列和回調函數
五、設置播放狀態
六、啓動回調函數
其中第4步和第6步是OpenSL ES 播放PCM等數據格式的音頻是須要用到的。
//播放音頻
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! ");
}
//2 建立錄製的對象,而且指定開放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!");
}
//3 獲取錄製接口
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);
//4 設置數據回調接口bqRecorderCallback,最後一個參數是能夠傳輸自定義的上下文引用
re = (*recorder_buffer_queue)->RegisterCallback(recorder_buffer_queue, bqRecorderCallback, 0);
if (re != SL_RESULT_SUCCESS) {
LOGE("RegisterCallback failed!");
}
//5 設置錄製器爲錄製狀態 SL_RECORDSTATE_RECORDING
re = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
if (re != SL_RESULT_SUCCESS) {
LOGE("SetRecordState failed!");
}
//6 在設置完錄製狀態後必定須要先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