由于FFmpeg一直都没有支持Android平台的音频输入设备API,所以无法使用FFmpeg在Android上录制音频。
Android平台音频录制API
Android上音频输入的API比较繁琐,有多套实现。有Java层的实现AudioRecord,也有native层实现OpenSLES,在 Android O之后又推出了新的native层实现AAudio,并且提供了Oboe库,对OpenSLES和AAudio进行了封装。
下面来详细的介绍下每套API的使用
AudioRecord
AudioRecord是Android平台提供的录制音频的Java层API,使用起来非常简单。通过设置采集参数就可以创建一个AudioRecord对象,调用了start方法后,就可以开始读取数据了,通过read方法将数据读取到指定的缓冲区中,调用stop后就停止采集。
创建AudioRecord
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| final int bytesPerFrame = channels * (BITS_PER_SAMPLE / 8);
final int framesPerBuffer = sampleRate / BUFFERS_PER_SECOND;
byteBuffer = ByteBuffer.allocateDirect(bytesPerFrame * framesPerBuffer); emptyBytes = new byte[byteBuffer.capacity()];
nativeCacheDirectBufferAddress(byteBuffer, nativeAudioRecord);
final int channelConfig = channelCountToConfiguration(channels);
int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);
int bufferSizeInBytes = Math.max(BUFFER_SIZE_FACTOR * minBufferSize, byteBuffer.capacity()); try { audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes); } catch (IllegalArgumentException e) { return -1; } if (audioRecord == null || audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { return -1; }
|
读取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| audioRecord.startRecording();
while (keepAlive) { int bytesRead = audioRecord.read(byteBuffer, byteBuffer.capacity()); if (bytesRead == byteBuffer.capacity()) { if (keepAlive) { nativeDataIsRecorded(bytesRead, nativeAudioRecord); } if (audioSamplesReadyCallback != null) { byte[] data = Arrays.copyOf(byteBuffer.array(), byteBuffer.capacity()); audioSamplesReadyCallback.onWebRtcAudioRecordSamplesReady( new AudioSamples(audioRecord, data)); } } else { } }
audioRecord.stop();
|
获取设备音频相关信息
通过AudioManager可以获取到当前设备默认的采样率以及最小缓冲区大小
1 2 3
| AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE); String nativeParam = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); nativeParam = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
|
AudioSource音频来源
- DEFAULT:默认为MIC
- MIC:麦克风
- VOICE_UPLINK:电话录音上行线路,系统权限不允许第三方app使用
- VOICE_DOWNLINK:电话录音下行线路,系统权限不允许第三方app使用
- VOICE_CALL:电话录音上下线路,系统权限不允许第三方app使用
- CAMCORDER:摄像头的麦克风
- VOICE_RECOGNITION:语音识别
- VOICE_COMMUNICATION:网络电话
- REMOTE_SUBMIX:传输到远程的音频混合流,默认情况下如果用该项录音,本地扬声器或者耳机的声音将会被截走,系统权限不允许第三方app使用
OpenSL-ES
OpenSL-ES是Android平台上提供的高性能音频的API,相对Java层API有着一定的低延迟性。由于是C库,所以使用起来API相对繁琐一些。
配置数据参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| SLDataFormat_PCM format;
format.formatType = SL_DATAFORMAT_PCM;
format.numChannels = static_cast<SLuint32>(channels);
switch (sample_rate) { case 8000: format.samplesPerSec = SL_SAMPLINGRATE_8; break; case 16000: format.samplesPerSec = SL_SAMPLINGRATE_16; break; case 22050: format.samplesPerSec = SL_SAMPLINGRATE_22_05; break; case 32000: format.samplesPerSec = SL_SAMPLINGRATE_32; break; case 44100: format.samplesPerSec = SL_SAMPLINGRATE_44_1; break; case 48000: format.samplesPerSec = SL_SAMPLINGRATE_48; break; case 64000: format.samplesPerSec = SL_SAMPLINGRATE_64; break; case 88200: format.samplesPerSec = SL_SAMPLINGRATE_88_2; break; case 96000: format.samplesPerSec = SL_SAMPLINGRATE_96; break; default: RTC_CHECK(false) << "Unsupported sample rate: " << sample_rate; break; }
format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
format.endianness = SL_BYTEORDER_LITTLEENDIAN; if (format.numChannels == 1) { format.channelMask = SL_SPEAKER_FRONT_CENTER; } else if (format.numChannels == 2) { format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; } else { RTC_CHECK(false) << "Unsupported number of channels: " << format.numChannels; }
|
创建引擎
在OpenSLES中,任何API接口对象都要由引擎来创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const SLEngineOption option[] = { {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}};
SLresult result = slCreateEngine(engine_object_, 1, option, 0, NULL, NULL); if (result != SL_RESULT_SUCCESS) { return nullptr; }
result = engine_object_->Realize(engine_object_, SL_BOOLEAN_FALSE); if (result != SL_RESULT_SUCCESS) { return nullptr; }
if ((*engine_object) ->GetInterface(engine_object, SL_IID_ENGINE, &engine_)) { return false; }
|
创建录制器
在OpenSLES中,录制需要通过录制器对象来进行,而数据的获取是通过回调的方式来获取,但是并非将采集的数据放在回调函数的参数中进行传递,回调函数仅仅只是一个通知的意义,真正的数据是在Enqueue函数中传入的buffer中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| SLDataLocator_IODevice mic_locator = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audio_source = {&mic_locator, NULL};
SLDataLocator_AndroidSimpleBufferQueue buffer_queue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32>(kNumOfOpenSLESBuffers)};
SLDataSink audio_sink = {&buffer_queue, &pcm_format_};
const SLInterfaceID interface_id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION}; const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
if ((*engine_)->CreateAudioRecorder( engine_, recorder_object_, &audio_source, &audio_sink, arraysize(interface_id), interface_id, interface_required)) { return false; }
SLAndroidConfigurationItf recorder_config; if ((recorder_object_->GetInterface(recorder_object_, SL_IID_ANDROIDCONFIGURATION, &recorder_config))) { return false; }
SLint32 stream_type = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; if (((*recorder_config) ->SetConfiguration(recorder_config, SL_ANDROID_KEY_RECORDING_PRESET, &stream_type, sizeof(SLint32)))) { return false; }
if ((recorder_object_->Realize(recorder_object_, SL_BOOLEAN_FALSE))) { return false; }
if ((recorder_object_->GetInterface( recorder_object_, SL_IID_RECORD, &recorder_))) { return false; }
if ((recorder_object_->GetInterface( recorder_object_.Get(), SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &simple_buffer_queue_))) { return false; }
if (((*simple_buffer_queue_) ->RegisterCallback(simple_buffer_queue_, SimpleBufferQueueCallback, this))) { return false; }
|
采集数据
每调用一次Enqueue函数都会触发一次数据回调,如果需要循环采集,则需要在回调函数中递归调用Enqueue函数。
1 2 3 4 5 6 7 8 9 10 11
|
SLresult err = (*simple_buffer_queue_) ->Enqueue( simple_buffer_queue_, reinterpret_cast<SLint8*>(audio_buffers_[buffer_index_].get()), audio_parameters_.GetBytesPerBuffer()); if (SL_RESULT_SUCCESS != err) { return false; }
|
1 2 3 4 5 6 7 8 9
| void OpenSLESRecorder::SimpleBufferQueueCallback( SLAndroidSimpleBufferQueueItf buffer_queue, void* context) { OpenSLESRecorder* stream = static_cast<OpenSLESRecorder*>(context); stream->ReadBufferQueue(); }
|