本篇介绍

本篇接着<<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;
}

从上面能够看到如下信息:

  1. 在数据回谐和异常回调里最好不要操作aaudio的起停接口,会有不可预期的问题。
  2. 在发动aaudio的时候也会告诉下mPlayerBase。

这时候可能会有疑问,为什么需要告诉mPlayerBase?
要回答这个问题,原因也比较直接,dumpsys audio会记载体系中所有采播的运转记载,这儿的mPlayerBase就是为了将该信息记载到audioserver中,这样dumpsys audio就能够看到aaudio的运转记载了。

重视公众号:Android老皮!!!欢迎大家来找我探讨交流