AudioRecord详解
写在前面:AudioRecord 类的主要功能是让各种应用层应用能够管理音频资源,以便它们通过此类能够录制平台的声音输入硬件所收集的声音。此功能的实现就是通过 "pulling 同步"(reading读取)AudioRecord 对象的声音数据来完成的。在录音过程中,应用所需要做的就是通过read方法去及时地获取 AudioRecord 对象的录音数据。
AudioRecord可以获取到一帧帧PCM数据,之后可以对这些数据进行处理。AudioRecord这种方式采集最为灵活,使开发者最大限度的处理采集的音频,同时它捕获到的音频是原始音频PCM格式的!
开始录音的时候,一个 AudioRecord 需要初始化一个相关联的声音buffer,这个 buffer 主要是用来保存新的声音数据。这个 buffer 的大小,我们可以在对象构造期间去指定。它表明一个 AudioRecord 对象还没有被读取(同步)声音数据前能录多长的音(即一次可以录制的声音容量)。声音数据从音频硬件中被读出,数据大小不超过整个录音数据的大小(可以分多次读出),即每次读取初始化 buffer 容量的数据。
[toc]
AudioRecord使用流程
- 配置参数(各个参数都有默认值)
- audioResource音频采集的来源:可以是麦克风声音、通话声音、系统内置声音。
- audioSampleRate音频采样率
- channelConfig声道:单声道、双声道等
- audioFormat:音频采样精度,指定采样的数据的格式和每次采样的大小,只支持8位和16位。
- buffer缓冲区大小:音频数据写入缓冲区的总数,可以通过AudioRecord.getMinBufferSize获取最小的缓冲区。获取最小的缓冲区大小,用于存放AudioRecord采集到的音频数据。
- 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小
byte data[] = new byte[recordBufSize];
- AudioRecord对象
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
- 录制相关操作
- 开始录制
startRecording()
- 停止录制
stop()
- 释放资源
release()
-
read()
的使用
- 开始录制
- 建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中的数据导入数据流
- 关闭数据流
- 停止录制
使用示例
private AudioRecord audioRecord = null; // 声明 AudioRecord 对象private int recordBufSize = 0; // 声明recoordBufffer的大小字段
recordBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, EncodingBitRate); //audioRecord能接受的最小的buffer大小
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, EncodingBitRate, recordBufSize); //创建audiorecord对象
byte data[] = new byte[recordBufSize]; //初始化一个buffer
audioRecord.startRecording(); //开始录音
//创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流。
FileOutputStream os = null;
try {
os = new FileOutputStream(filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (null != os) {
while (isRecording) {
read = audioRecord.read(data, 0, recordBufSize); // 如果读取音频数据没有出现错误,就将数据写入到文件 if (AudioRecord.ERROR_INVALID_OPERATION != read) { try {
os.write(data);
} catch (IOException e) {
e.printStackTrace();
} } }
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}}
isRecording = false; //关闭数据流
if (null != audioRecord) { //停止录制
audioRecord.stop();
audioRecord.release();
audioRecord = null;
recordingThread = null;
}
AudioRecord源码分析
构造AudioRecord对象
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)throws IllegalArgumentException { this((new AudioAttributes.Builder()) .setInternalCapturePreset(audioSource) .build(), (new AudioFormat.Builder()) .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig, true/*allow legacy configurations*/)) .setEncoding(audioFormat) .setSampleRate(sampleRateInHz) .build(), bufferSizeInBytes, AudioManager.AUDIO_SESSION_ID_GENERATE);}
该构造方法实则调用
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int sessionId)
这个构造方法:
{
…
int initResult = native_setup( new WeakReference<AudioRecord>(this), mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, session, ActivityThread.currentOpPackageName(), 0
/*nativeRecordInJavaObj*/);
if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing native AudioRecord
object."); return;
// with mState == STATE_UNINITIALIZED}mSampleRate = sampleRate[0];mSessionId = session[0];mState = STATE_INITIALIZED;
}
该构造方法实现通过调用native_setup函数进入了frameworks/base/core/jni/android_media_AudioRecord.cpp的(void *)android_media_AudioRecord_setup方法。
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
jlong nativeRecordInJavaObj)
{
…
sp<AudioRecord> lpRecorder = 0;
// create an uninitialized AudioRecord object
lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
…
}
这里在JNI创建了一个AudioRecord对象
//frameworks/av/media/libaudioclient/AudioRecord.cpp
// must be called with mLock held
status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); //获得AudioFlinger对象
IAudioFlinger::CreateRecordInput input;
IAudioFlinger::CreateRecordOutput output;
audio_session_t originalSessionId;
sp<media::IAudioRecord> record;
record = audioFlinger->createRecord(input,
output,
&status);
}
//frameworks/av/media/libaudioclient/IAudioFlinger.cpp
virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
CreateRecordOutput& output,
status_t *status)
{
Parcel data, reply;
sp<media::IAudioRecord> record;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
if (status == nullptr) {
return record;
}
input.writeToParcel(&data);
status_t lStatus = remote()->transact(CREATE_RECORD, data, &reply);
if (lStatus != NO_ERROR) {
ALOGE("createRecord transaction error %d", lStatus);
*status = DEAD_OBJECT;
return record;
}
*status = reply.readInt32();
if (*status != NO_ERROR) {
ALOGE("createRecord returned error %d", *status);
return record;
}
record = interface_cast<media::IAudioRecord>(reply.readStrongBinder());
if (record == 0) {
ALOGE("createRecord returned a NULL IAudioRecord with status OK");
*status = DEAD_OBJECT;
return record;
}
output.readFromParcel(&reply);
return record;
}
//创建IAudioFlinger接口的实例
//frameworks/av/media/libaudioclient/AudioSystem.cpp
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
{
sp<IAudioFlinger> af;
sp<AudioFlingerClient> afc;
{
Mutex::Autolock _l(gLock);
if (gAudioFlinger == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.audio_flinger"));
if (binder != 0)
break;
ALOGW("AudioFlinger not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (gAudioFlingerClient == NULL) {
gAudioFlingerClient = new AudioFlingerClient(); //创建AudioFlingerClient对象
} else {
if (gAudioErrorCallback) {
gAudioErrorCallback(NO_ERROR);
}
}
binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);
afc = gAudioFlingerClient;
// Make sure callbacks can be received by gAudioFlingerClient
ProcessState::self()->startThreadPool();
}
af = gAudioFlinger;
}
if (afc != 0) {
int64_t token = IPCThreadState::self()->clearCallingIdentity();
af->registerClient(afc);
IPCThreadState::self()->restoreCallingIdentity(token);
}
return af;
}
//通过binder进行客户端与服务端的通信
通过IAudioRecord可以调用Server服务器对象(AudioFlinger及AudioFlinger::RecordThread等)的方法并获取执行结果。
开始录制
首先要判断一下AudioRecord的状态是否已经初始化完毕了,然后再去调用AudioRecord.startRecording()。一边从 AudioRecord 中读取音频数据到缓冲区,一边将缓冲区 中数据写入到数据流。
startRecording(Java Framework) → native_start → android_media_AudioRecord_start(JNI) → lpRecorder-.start(Native)->
mAudioRecord.start(event, triggerSession).transactionError();->
virtual ::android::binder::Status start(int32_t event, int32_t triggerSession) = 0;(IAudioRecord)
//sp<media::IAudioRecord> mAudioRecord;
通过IAudioRecord调用Server服务器对象AudioFlinger的方法。
停止录制
stop → native_stop → android_media_AudioRecord_stop → lpRecorder->stop() :
释放资源
release() ->native_release()->android_media_AudioRecord_release()->lpRecord->release()
JNI中nativeToJavaStatus返回操作执行的结果
AudioFlinger
我们知道在创建应用层一个AudioRecord对象的时候,会通过JNI再到native层的AudioRecord.cpp创建一个实现了IAudioRecord的接口实例返回,录制的一系列操作最终都会是通过IAudioRecord调用服务端的AudioFligner的方法实现具体的操作。
AudioFlinger 服务启动后,其他进程可以通过 ServiceManager 来获取其代理对象 IAudioFlinger,通过 IAudioFlinger 可以向 AudioFlinger 发出各种服务请求,从而完成自己的音频业务。
AudioFlinger 响应的服务请求主要有:
- 获取硬件设备的配置信息
- 音量调节
- 静音操作
- 音频模式切换
- 音频参数设置
- 输入输出流设备管理
- 音频流管理