AudioRecord 录音时有两种读取模式,一种是AudioRecord.READ_BLOCKING,另外一种是AudioRecord.READ_NON_BLOCKING,但是对这两种模式的定义却比较模糊
/**
* The read mode indicating the read operation will block until all data
* requested has been read.
*/
public final static int READ_BLOCKING = 0;
/**
* The read mode indicating the read operation will return immediately after
* reading as much audio data as possible without blocking.
*/
public final static int READ_NON_BLOCKING = 1;
只说一种是会阻塞直到数据读取成功,另外一种是立刻返回尽可能多的数据而不阻塞。那么假设我选择READ_NON_BLOCKING模式,会不会导致读取到的数据长度是变化的?这两种模式究竟有什么区别?
我们跟踪安卓的源码,AudioRecord.read最终会调用到frameworks/av/media/libaudioclient/AudioRecord.cpp:
ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking)
{
if (mTransfer != TRANSFER_SYNC) {
return INVALID_OPERATION;
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
// Validation. user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("%s(%d) (buffer=%p, size=%zu (%zu)",
__func__, mPortId, buffer, userSize, userSize);
return BAD_VALUE;
}
ssize_t read = 0;
Buffer audioBuffer;
while (userSize >= mFrameSize) {
audioBuffer.frameCount = userSize / mFrameSize;
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
if (err < 0) {
if (read > 0) {
break;
}
if (err == TIMED_OUT || err == -EINTR) {
err = WOULD_BLOCK;
}
return ssize_t(err);
}
size_t bytesRead = audioBuffer.size;
memcpy(buffer, audioBuffer.i8, bytesRead);
buffer = ((char *) buffer) + bytesRead;
userSize -= bytesRead;
read += bytesRead;
releaseBuffer(&audioBuffer);
}
if (read > 0) {
mFramesRead += read / mFrameSize;
// mFramesReadTime = systemTime(SYSTEM_TIME_MONOTONIC); // not provided at this time.
}
return read;
}
可以看到,java层传过来的参数最终转换成布尔值blocking。在正常读取的时候,无论是否blocking,都会等到读取userSize之后再返回;在读取出错时,假设读取到了数据,无论是否blocking,最终也都会返回已读取的这部分数据。唯一的区别点在于obtainBuffer的传参,blocking等待时间为:ClientProxy::kForever,即INT_MAX sec,non_blocking等待时间为ClientProxy::kNonBlocking,即0。
const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/};
const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/};
需要注意的是,这个地方只是默认值,在obtainBuffer内部某些case下blocking也会被修改为non_blocking,例如
同时,在obtainBuffer内部也会进行一些状态判定,例如如果是未初始化等,无论是否blocking,都会直接返回error,只有状态正常,但是未能读取到数据时该参数才会生效
综上:
- 无论是否blocking,正常状态下都会读取到我们设置的bufferSize然后再返回;
- 无论是否blocking,在一些异常状态下都会不阻塞直接返回,例如状态错误等;
- 出错时无论是否blocking,都会返回已read的这部分size,有可能小于我们设置的bufferSize;
- 只有在状态正常但是数据未读取到时,blocking模式会进行阻塞等待,non_blocking直接返回WOULD_BLOCK的错误;
实际项目中验证发现情况4出现的概率还挺高的,会导致录音概率性有空白,推荐使用阻塞模式。