Android 音频低延时mmap介绍(2)

本篇介绍

本篇接着<<Android 音频低延时mmap介绍(1)>>继续介绍aaudio 的mmap机制,本篇旨在揭示mmap机制中的数据同步。

aaudio mmap介绍

故事还是获取mmap buffer开始:

java 复制代码
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中:

java 复制代码
/** @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:

java 复制代码
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 流, 接着往下看:

java 复制代码
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老皮!!!欢迎大家来找我探讨交流👀

相关推荐
氦客7 小时前
Android Compose : 传统View在Compose组件中的等价物
android·compose·jetpack·对比·传统view·等价物·compose组件
神话20097 小时前
Rust 初体验与快速上手指南
android·rust
CheungChunChiu8 小时前
Linux 内核动态打印机制详解
android·linux·服务器·前端·ubuntu
aidou13149 小时前
Android中设置Dialog和自定义布局相同高度
android·dialog·弹窗高度·getwindow
氦客9 小时前
UI编程的发展史 : 结合命令式UI和声明式UI
android·compose·声明式ui·ui编程·命令式ui·ui编程发展史·标记语言
aidou131411 小时前
Android中RecyclerView实现多级列表
android·recyclerview·多级列表·layoutmanager
青风行11 小时前
Android从入门到进阶
android
方白羽12 小时前
Android 开发中,准确判断应用处于“前台(Foreground)”还是“后台(Background)
android·app·客户端
Mart!nHu13 小时前
Android 10&15 Framework 允许设置系统时间早于编译时间
android
编程之路从0到114 小时前
ReactNative新架构之Android端TurboModule机制完全解析
android·react native·源码阅读