本篇介绍
本篇接着<<Android 音频低延时mmap介绍(1)>>继续介绍aaudio 的mmap机制,本篇旨在揭示mmap机制中的数据同步。
aaudio mmap介绍
故事还是获取mmap buffer开端:
aaudio_result_t AAudioServiceEndpointMMAP::createMmapBuffer(
android::base::unique_fd* fileDescriptor)
{
memset(&mMmapBufferinfo, 0, sizeof(struct audio_mmap_buffer_info));
int32_t minSizeFrames = getBufferCapacity();
if (minSizeFrames <= 0) { // zero will get rejected
minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
}
status_t status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
bool isBufferShareable = mMmapBufferinfo.flags & AUDIO_MMAP_APPLICATION_SHAREABLE;
if (status != OK) {
ALOGE("%s() - createMmapBuffer() failed with status %d %s",
__func__, status, strerror(-status));
return AAUDIO_ERROR_UNAVAILABLE;
} else {
ALOGD("%s() createMmapBuffer() buffer_size = %d fr, burst_size %d fr"
", Sharable FD: %s",
__func__,
mMmapBufferinfo.buffer_size_frames,
mMmapBufferinfo.burst_size_frames,
isBufferShareable ? "Yes" : "No");
}
setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
if (!isBufferShareable) {
// Exclusive mode can only be used by the service because the FD cannot be shared.
int32_t audioServiceUid =
VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
if ((mMmapClient.attributionSource.uid != audioServiceUid) &&
getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
ALOGW("%s() - exclusive FD cannot be used by client", __func__);
return AAUDIO_ERROR_UNAVAILABLE;
}
}
// AAudio creates a copy of this FD and retains ownership of the copy.
// Assume that AudioFlinger will close the original shared_memory_fd.
fileDescriptor->reset(dup(mMmapBufferinfo.shared_memory_fd));
if (fileDescriptor->get() == -1) {
ALOGE("%s() - could not dup shared_memory_fd", __func__);
return AAUDIO_ERROR_INTERNAL;
}
// Call to HAL to make sure the transport FD was able to be closed by binder.
// This is a tricky workaround for a problem in Binder.
// TODO:[b/192048842] When that problem is fixed we may be able to remove or change this code.
struct audio_mmap_position position;
mMmapStream->getMmapPosition(&position);
mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
return AAUDIO_OK;
}
从上一篇咱们看到createMmapBuffer之后,就拿到了和驱动同享的内存地址,大小等信息,具体信息在mMmapBufferinfo中:
/** @TODO export from .hal */
typedef enum {
NONE = 0x0,
/**
* Only set this flag if applications can access the audio buffer memory
* shared with the backend (usually DSP) _without_ security issue.
*
* Setting this flag also implies that Binder will allow passing the shared memory FD
* to applications.
*
* That usually implies that the kernel will prevent any access to the
* memory surrounding the audio buffer as it could lead to a security breach.
*
* For example, a "/dev/snd/" file descriptor generally is not shareable,
* but an "anon_inode:dmabuffer" file descriptor is shareable.
* See also Linux kernel's dma_buf.
*
* This flag is required to support AAudio exclusive mode:
* See: https://source.android.com/devices/audio/aaudio
*/
AUDIO_MMAP_APPLICATION_SHAREABLE = 0x1,
} audio_mmap_buffer_flag;
/**
* Mmap buffer descriptor returned by audio_stream->create_mmap_buffer().
* note Used by streams opened in mmap mode.
*/
struct audio_mmap_buffer_info {
void* shared_memory_address; /**< base address of mmap memory buffer.
For use by local process only */
int32_t shared_memory_fd; /**< FD for mmap memory buffer */
int32_t buffer_size_frames; /**< total buffer size in frames */
int32_t burst_size_frames; /**< transfer size granularity in frames */
audio_mmap_buffer_flag flags; /**< Attributes describing the buffer. */
};
这儿看到了AUDIO_MMAP_APPLICATION_SHAREABLE符号,这个是独占形式运用的。后面咱们也能明晰看到独占和同享形式的差异。
从上面也能看到,如果内存没有AUDIO_MMAP_APPLICATION_SHAREABLE符号,那么独占形式就失败了。
接下来需要保存fd,用来通过IPC同享fd完成使用与驱动同享内存。
到了这儿同享内存,fd信息还在audioserver中,那如何让使用同享呢?又如何驱动使用读取采集数据呢?接下来咱们用start中寻找答案。
故事的开端是AAudioStream_requestStart:
AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
aaudio_stream_id_t id = audioStream->getId();
ALOGD("%s(s#%u) called --------------", __func__, id);
aaudio_result_t result = audioStream->systemStart();
ALOGD("%s(s#%u) returned %d ---------", __func__, id, result);
return result;
}
这时候是事务请求发动aaudio 流, 接着往下看:
aaudio_result_t AudioStream::systemStart() {
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
std::lock_guard<std::mutex> lock(mStreamLock);
switch (getState()) {
// Is this a good time to start?
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_PAUSING:
case AAUDIO_STREAM_STATE_PAUSED:
case AAUDIO_STREAM_STATE_STOPPING:
case AAUDIO_STREAM_STATE_STOPPED:
case AAUDIO_STREAM_STATE_FLUSHING:
case AAUDIO_STREAM_STATE_FLUSHED:
break; // Proceed with starting.
// Already started?
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
ALOGW("%s() stream was already started, state = %s", __func__,
AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
// Don't start when the stream is dead!
case AAUDIO_STREAM_STATE_DISCONNECTED:
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
default:
ALOGW("%s() stream is dead, state = %s", __func__,
AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_result_t result = requestStart_l();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
(void) mPlayerBase->startWithStatus(getDeviceId());
}
return result;
}
从上面能够看到如下信息:
- 在数据回谐和异常回调里最好不要操作aaudio的起停接口,会有不可预期的问题。
- 在发动aaudio的时候也会告诉下mPlayerBase。
这时候可能会有疑问,为什么需要告诉mPlayerBase?
要回答这个问题,原因也比较直接,dumpsys audio会记载体系中所有采播的运转记载,这儿的mPlayerBase就是为了将该信息记载到audioserver中,这样dumpsys audio就能够看到aaudio的运转记载了。
重视公众号:Android老皮!!!欢迎大家来找我探讨交流