精华内容
下载资源
问答
  • aaudio

    2017-07-08 01:38:00
    AAUDIO_STREAM_STATE_UNINITIALIZED ; int64_t timeoutNanos = 100 * AAUDIO_NANOS_PER_MILLISECOND ; result = AAudioStream_requestPause ( stream ); result = AAudioStream_waitForStateChange ( ...

    A音频

    A音频在AndroidØ版本引入了全新的Android C API。它是专为那些需要低延迟的高性能音频应用。应用程序通过读取和写入数据流与A音频通信。

    注: 这是A音频库的预览版本。该API可能在将来的版本后向兼容的方式发生变化。不建议在生产中使用。

    该A音频API是由设计最小的,它不执行这些功能:

    • 音频设备枚举
    • 音频端点之间自动路由
    • 文件I / O
    • 压缩音频的解码
    • 所有输入的自动演示/流在一个单独的回调。

    音频流


    A音频应用程式和Android设备上的音频输入和输出之间移动音频数据。你的应用程序通过读取和写入进出数据的音频流,通过结构AAudioStream表示。读/写呼叫可以被阻塞或非阻塞。

    流由以下定义:

    • 音频 设备,即源或汇的流中的数据。
    • 所述共享模式,其确定一个流是否具有可能另外多个流之间共享的音频装置的独占访问。
    • 格式的流中的音频数据。

    音频设备

    每个流被连接到单个音频设备。

    音频设备是硬件接口或虚拟端点充当源或汇用于数字音频数据的连续流。不要混淆的音频设备 与(一个内置的麦克风或蓝牙耳机)的Android设备运行你的应用程序(电话或观看)。

    您可以使用该AudioManager方法getDevices()来发现您可以在Android设备上的音频设备。该方法返回有关信息type的每个设备。

    每个音频设备具有Android设备上的唯一ID。您可以使用ID的音频流绑定到特定的音频设备。然而,在大多数情况下,你可以让A音频选择默认的主设备,而不是你自己指定一个。

    附连到流音频设备确定该流是否为输入或输出。甲流只能在一个方向上移动数据。当你定义一个流您还可以设置它的方向。当你打开一个流的Android检查,以确保音频设备和流方向一致。

    共享模式

    甲流有一个共享模式:

    • AAUDIO_SHARING_MODE_EXCLUSIVE意味着流具有其音频设备的独占访问; 该装置不能被任何其他音频流被使用。如果音频设备已在使用,它可能无法为流具有独占访问。独家流可能有更低的延迟,但他们也更容易获得断开。您应该尽快关闭独家流,你不再需要它们,以便其他应用程序可以访问该设备。独家流提供尽可能低的延迟。
    • AAUDIO_SHARING_MODE_SHARED允许A音频混合音频。A音频混合所有分配到同一设备的共享流。

    当你创建一个流可以明确设置共享模式。默认情况下,共享模式SHARED

    音频格式

    通过流传递的数据有通常的数字音频属性,当你定义一个流必须指定。这些措施如下:

    • 样本格式
    • 每帧样本
    • 采样率

    A音频允许这些样品格式:

    aaudio_format_tC数据类型笔记
    AAUDIO__FORMAT_PCM_I16int16_t常见的16位样本,Q0.15格式
    AAUDIO_FORMAT_PCM_FLOAT浮动-1.0到+1.0

    A音频可能对自己进行采样转换。例如,如果一个应用程序被写入FLOAT数据,但HAL使用PCM_I16,A音频会自动转换样本。转换可以在任一方向发生。如果您的应用程序处理音频输入,这是明智的验证输入格式,并准备在必要时转换数据,如下例所示:

    aaudio_format_t dataFormat = AAudioStream_getDataFormat(stream);
    //... later
    if (dataFormat == AAUDIO_FORMAT_PCM_I16) {
         convertFloatToPcm16(...)
    }

    创建音频流


    在A音频库遵循制造商的设计模式,并提供AAudioStreamBuilder。

     

    1. 创建AAudioStreamBuilder:

       

      AAudioStreamBuilder *builder;
      aaudio_result_t result = AAudio_createStreamBuilder(&builder);

       

    2. 设置在构建器中的音频流的配置,使用对应于所述流参数的助洗剂功能。这些可选设置功能:

       

      AAudioStreamBuilder_setDeviceId(builder, deviceId);
      AAudioStreamBuilder_setDirection(builder, direction);
      AAudioStreamBuilder_setSharingMode(builder, mode);
      AAudioStreamBuilder_setSampleRate(builder, sampleRate);
      AAudioStreamBuilder_setSamplesPerFrame(builder, spf);
      AAudioStreamBuilder_setFormat(builder, format);
      AAudioStreamBuilder_setBufferCapacityInFrames(builder, frames);

      注意,这些方法不报告错误,如一个未定义的常量或值超出范围。

      如果不指定设备ID,默认是主要的输出设备。如果不指定流方向上,默认是输出流。对于所有其它参数,你可以明确地设定一个值,或者让系统不指定所有参数或设置它来分配最佳值AAUDIO_UNSPECIFIED

      为了安全起见,检查音频流的状态在创建后,按照步骤4所说明的那样。

       

    3. 当AAudioStreamBuilder配置,用它来创建流:

       

      AAudioStream *stream;
      result = AAudioStreamBuilder_openStream(builder, &stream);

       

    4. 创建流后,验证其配置。如果您指定每帧的采样格式,采样率,或样品,他们将不会改变。然而,共享模式和缓存容量可能会随流的音频设备的能力,并在其上正在运行的Android设备改变(无论是否设置)。由于良好的防御性编程的问题,你在使用前应检查流的配置。有函数来获取对应于每个建设者设置流设置:

       

       

      AAudioStreamBuilder_setDeviceId()AAudioStream_getDeviceId()
      AAudioStreamBuilder_setDirection()AAudioStream_getDirection()
      AAudioStreamBuilder_setSharingMode()AAudioStream_getSharingMode()
      AAudioStreamBuilder_setSampleRate()AAudioStream_getSampleRate()
      AAudioStreamBuilder_setSamplesPerFrame()AAudioStream_getSamplesPerFrame()
      AAudioStreamBuilder_setFormat()AAudioStream_getFormat()
      AAudioStreamBuilder_setBufferCapacityInFrames()AAudioStream_getBufferCapacityInFrames()
    5. 您可以保存生成器,它在将来重复使用,使更多的流。但是,如果你不打算使用它了,应该将其删除。

       

      AAudioStreamBuilder_delete(builder);

       

     

    使用音频流


    状态转变

    一个A音频流通常是由5种稳定状态中的一种(错误状态,断开,在该部分的端部所描述的):

    • 打开
    • 入门
    • 已暂停
    • 停止

    数据只有当流处于启动状态流过流。移动状态之间的流,使用该请求的状态转变的一个功能:

    aaudio_result_t result;
    result = AAudioStream_requestStart(stream);
    result = AAudioStream_requestStop(stream);
    result = AAudioStream_requestPause(stream);
    result = AAudioStream_requestFlush(stream);

    请注意,您只能请求暂停或平齐的输出流:

    这些功能是异步的,并且状态变化不会立即发生。当您请求的状态变化,流使相应的过渡状态中的一种:

    • 开始
    • 暂停
    • 法拉盛
    • 停止
    • 闭幕

    下面的状态图显示了稳定状态为圆角矩形,而过渡状态为点的矩形。虽然它没有显示,你可以调用close()从任何状态

    A音频生命周期

    A音频不提供回调来提醒您状态的变化。一个特殊的功能, AAudioStream_waitForStateChange(stream, inputState, nextState, timeout)可以用来等待的状态变化。

    该功能不检测自身的状态变化,而不会等待一个特定的状态。它等待,直到当前的状态是不同的inputState,可以指定。

    例如,请求暂停后,流应立即进入过渡状态暂停,并到达暂停状态晚些时候-尽管也不能保证它会的。既然你不能等待暂停状态,请waitForStateChange()等待比其他暂停任何状态。以下是如何这样做了:

    aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
    aaudio_stream_state_t nextState = AAUDIO_STREAM_STATE_UNINITIALIZED;
    int64_t timeoutNanos = 100 * AAUDIO_NANOS_PER_MILLISECOND;
    result = AAudioStream_requestPause(stream);
    result = AAudioStream_waitForStateChange(stream, inputState, &nextState, timeoutNanos);

    如果流的状态不是暂停(中inputState,我们假设是在调用时的当前状态),该函数立即返回。否则,它会阻止,直到状态不再暂停或超时期满。当该函数返回,参数nextState示出了流的当前状态。

    您可以呼叫请求后开始使用同样的技术,停止或冲洗,使用相应的过渡状态作为inputState。

    读取和写入音频流

    流启动后,你可以阅读或使用该函数写它 AAudioStream_read(stream, buffer, numFrames, timeoutNanos) 和 AAudioStream_write(stream, buffer, numFrames, timeoutNanos)

    对于阻挡读或写帧指定数目的传送,设置timeoutNanos大于零。对于非阻塞调用,设置timeoutNanos为零。在这种情况下,结果是转移的帧的实际数目。

    当你读输入,您应该验证读取帧的正确数目。如果不是,缓冲区可能含有未知的数据,可能会导致音频故障。你可以垫用零缓冲,以创建一个无声辍学:

    aaudio_result_t result =
        AAudioStream_read(stream, audioData, numFrames, timeout);
    if (result < 0) {
      // Error!
    }
    if (result != numFrames) {
      // pad the buffer with zeros
      memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
          sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
    }

    您可以通过将数据写入或沉默到它开始流之前素流的缓冲区。这必须与timeoutNanos非阻塞调用设置为零来完成。

    在缓冲区中的数据必须通过返回的数据格式相匹配AAudioStream_getDataFormat()

    关闭音频流

    当您使用的是流完成后,将其关闭:

    AAudioStream_close(stream);

    断开音频流

    音频流可以在任何时候断开连接,如果这些事件之一发生:

    • 相关的音频设备不再连接。
    • 内部会发生错误。
    • 音频装置不再是主音频设备。

    当流被断开时,它具有状态“断开连接”和执行写入的任何企图()或其他函数返回AAUDIO_ERROR_DISCONNECTED。当流被切断,所有你能做的就是关闭它。

    优化性能


    您可以通过调整其内部的缓冲区,并通过使用特殊的高优先级的线程优化的音频应用程序的性能。

    调整缓冲区,以尽量减少延迟

    A音频进出它保持,一个用于每个音频设备的内部缓冲器的数据传递。

    注意:不要混淆A音频的内部缓冲区与A音频流的缓冲参数读写功能。

    该缓冲区的容量是数据的总量缓冲器可容纳。您可以拨打 AAudioStreamBuilder_setBufferCapacityInFrames() 设置的能力。该方法限制了可分配给最大值,该设备允许的容量。使用 AAudioStream_getBufferCapacityInFrames() 验证缓存的实际容量。

    一个应用程序没有使用缓冲区中的全部能力。A音频填补了缓冲高达尺寸,你可以设置。一缓冲区的大小可以不超过它的容量大,并且它往往是更小的。通过控制缓冲大小你确定需要填充它脉冲串的数量,从而控制延迟。使用方法AAudioStreamBuilder_setBufferSizeInFrames() 和AAudioStreamBuilder_getBufferSizeInFrames() 使用缓冲区大小的工作。

    当应用程序播放音频输出,直到写操作完成写入缓冲区和块。A音频从以离散的脉冲串缓冲区读取。每个突发包含音频帧的倍数数目,通常是小于正被读取的缓冲区的大小。该系统控制突发大小和速率,这些性能是通过音频设备的电路典型地支配。尽管你不能改变突发或突发速率的大小,就可以根据其包含的突发的数目设置的内部缓冲器的大小。一般情况下,你会得到最低的延迟,如果你的AAudioStream的缓冲区大小是报告的突发大小的倍数。

          A音频缓冲

    优化缓冲区大小的一种方法是先从大的缓冲区,并逐步降低,直到欠载开始,然后轻推它回来了。或者,你可以用一个小的缓冲区开始,如果产生欠载运行,增加缓冲区大小,直到输出干净再流动。

    这个过程可以发生得非常快,用户播放第一个声音可能之前。您可能需要执行初始缓冲大小首先,使用沉默,使用户不会听到任何声音的毛刺。系统性能可能随时间(例如,用户可能关闭飞行模式)而改变。由于缓冲调整增加了很少的开销,你的应用程序可以在应用程序读取或向流写入的数据,连续做。

    这里是一个缓冲优化循环的一个例子:

    int32_t previousUnderrunCount = 0;
    int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream);
    int32_t bufferSize = AAudioStream_getBufferSizeInFrames(stream);

    int32_t bufferCapacity = AAudioStream_getBufferCapacityInFrames(stream);

    while (go) {
        result = writeSomeData();
        if (result < 0) break;

        // Are we getting underruns?
        if (bufferSize < bufferCapacity) {
            int32_t underrunCount = AAudioStream_getXRunCount(stream);
            if (underrunCount > previousUnderrunCount) {
                previousUnderrunCount = underrunCount;
                // Try increasing the buffer size by one burst
                bufferSize += framesPerBurst;
                bufferSize = AAudioStream_setBufferSize(stream, bufferSize);
            }
        }
    }

    没有优势,使用这种技术来优化输入流的缓冲区大小。输入流运行尽可能快,试图缓冲数据的量保持在最低限度,然后填满时,应用程序被抢占。

    使用高优先级的回调

    如果您的应用程序读取或从一个普通的线程写入音频数据,它可能被抢占或经历定时抖动。这可能会导致音频故障。使用更大的缓冲区可能防范此类故障,但一个大的缓冲区也引入了较长的音频延迟。对于需要低延迟的应用,音频流可以使用异步回调函数来传输数据和从您的应用程序。A音频执行具有更好的性能,更高优先级的线程回调。

    回调函数的原型如下:

    typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
            AAudioStream *stream,
            void *userData,
            void *audioData,
            int32_t numFrames);

    使用流房屋登记回调:

    AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

    在最简单的情况下,流周期性地执行回调函数来获取数据以其下一突发。

    回调函数不应该进行读或调用它的流写入。如果回调属于一个输入流,您的代码应过程,是在audioData缓冲液(指定为第三个参数)提供的数据。如果回调属于输出流,你的代码应该把数据放入缓冲区。

    例如,你可以使用一个回调,不断产生这样的正弦波输出:

    aaudio_data_callback_result_t myCallback(
            AAudioStream *stream,
            void *userData,
            void *audioData,
            int32_t numFrames) {
        int64_t timeout = 0;

        // Write samples directly into the audioData array.
        generateSineWave(static_cast<float *>(audioData), numFrames);
        return AAUDIO_CALLABCK_RESULT_CONTINUE;
    }

    它可以处理使用A音频不止一个流。可以使用一个流作为主,并传递指向其他流中的用户数据。注册为主控流回调。然后在其他流使用非阻塞I / O。下面是通过一个输入流,以输出流的往返回调的一个例子。主呼叫流是输出流。输入流被包括在用户数据。

    回调做一个无阻塞从将数据放置到输出流的缓冲器中的输入流中读取:

    aaudio_data_callback_result_t myCallback(
            AAudioStream *stream,
            void *userData,
            void *audioData,
            int32_t numFrames) {
        AAudioStream *inputStream = (AAudioStream *) userData;
        int64_t timeout = 0;
        aaudio_result_t result =
            AAudioStream_read(inputStream, audioData, numFrames, timeout);

      if (result == numFrames)
          return AAUDIO_CALLABCK_RESULT_CONTINUE;
      if (result >= 0) {
          memset(static_cast<sample_type*>(userData) + result * samplesPerFrame, 0,
              sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
          return AAUDIO_CALLBACK_RESULT_CONTINUE;
      }
      return AAUDIO_CALLBACK_RESULT_STOP;
    }

    注意,在该示例中,假设在输入和输出流具有相同数量的信道,格式和采样率的。该流的格式可以不匹配的 - 只要代码正确处理翻译。

    设置性能模式

    每个AAudioStream有一个性能模式这对您的应用程序的行为有很大的影响。有三种模式:

    • AAUDIO_PERFORMANCE_MODE_NONE是默认模式。它使用的是平衡延迟和功耗节省一个基本流。
    • AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 使用较小的缓冲器和用于减少延迟优化的数据路径。
    • AAUDIO_PERFORMANCE_MODE_POWER_SAVING 使用更大的内部缓冲器和该折衷的等待时间更低的功率的数据路径。

    你可以通过调用选择性能模式setPerformanceMode() ,并通过调用发现当前模式getPerformanceMode() 

    如果低延迟比你的应用省电更重要的是,使用AAUDIO_PERFORMANCE_MODE_LOW_LATENCY。这是一个应用程序,是非常互动,如游戏或键盘合成有用。

    如果省电比你的应用程序低延迟更加重要,使用AAUDIO_PERFORMANCE_MODE_POWER_SAVING。这是典型的为回放先前生成的音乐应用,如流式音频或MIDI文件播放器。

    在当前版本A音频的,以达到您必须使用尽可能低的延迟AAUDIO_PERFORMANCE_MODE_LOW_LATENCY高优先级的回调以及性能模式。按照这个例子:

    // Create a stream builder
    AAudioStreamBuilder *streamBuilder;
    AAudio_createStreamBuilder(&streamBuilder);
    AAudioStreamBuilder_setDataCallback(streamBuilder, dataCallback, nullptr);
    AAudioStreamBuilder_setPerformanceMode(streamBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

    // Use it to create the stream
    AAudioStream *stream;
    AAudioStreamBuilder_openStream(streamBuilder, &stream);

    线程安全


    该A音频API没有完全线程安全的。你不能同时从多个线程同时调用一些的A音频功能。这是因为A音频避免使用互斥,这可能会导致线程抢占和毛刺。

    为了安全起见,不要打电话AAudioStream_waitForStateChange()或读或从两个不同的线程写入相同的流。同样,不要在读取或在另一个线程写入它在一个线程关闭流。

    返回流设置,如电话AAudioStream_getSampleRate()AAudioStream_getSamplesPerFrame(),是线程安全的。

    这些电话也是线程安全的:

    • AAudio_convert*ToText()
    • AAudio_createStreamBuilder()
    • AAudioStream_get*() 除了 AAudioStream_getTimestamp()
    注意:当流使用一个回调函数,它是安全的,从回调线程读取/写入,同时还从其所运行的线程关闭流。

    代码示例


    两个小A音频演示的应用程序都可以在我们的GitHub页面

    • Hello-Audio 产生正弦波并回放的音频。
    • Echo演示如何实现的输入/输出音频往返循环。它使用用于同步两个流的回调函数,并且执行连续缓冲器调谐。

    已知的问题


    • 音频延迟很高阻止写(),因为在AndroidØDP2释放不使用快车道。使用回调以获取更低的延迟。
    • AAUDIO_SHARING_MODE_EXCLUSIVE不支持。使用AAUDIO_SHARING_MODE_SHARED来代替。

    转载于:https://www.cnblogs.com/yangjies145/p/7135416.html

    展开全文
  • Android AAudio 架构.vsdx

    2019-10-29 10:17:15
    AAudio 数据流架构, AAudio 是在 Android 8.0 版本中引入的一种音频 API。Android 8.1 版本具有增强功能,可在与支持 MMAP 的 HAL 和驱动程序结合使用时缩短延迟时间。本文档说明了需要进行哪些硬件抽象层 (HAL) 及...
  • ...an audio engine for Android, compatible with Android 4.1 and up, using either OpenSL or AAudio (when available) as the drivers for low latency audio performance. MWEngine provides an architecture ...
  • Android AAudio源码分析 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档...

    Android 9.0 AAudio源码分析(一)


    提示:再读文章之前可以先学习一下Binder和MMAP的知识


    前言

    因为网上目前还没有关于安卓AAudio方面的原理分析,所以笔者通过研究安卓9.0源码,总结了一些偏向于底层的一些东西,希望可以帮助到大家


    一、AAudio是什么

    AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。应用通过读取数据并将数据写入流来与 AAudio 进行通信

    二、AAudio源码解析

    1.启动

    (1)系统服务启动main_audioserver.cpp

    xref: /frameworks/av/media/audioserver/main_audioserver.cpp

    // AAudioService should only be used in OC-MR1 and later.
    // And only enable the AAudioService if the system MMAP policy explicitly allows it.
    // This prevents a client from misusing AAudioService when it is not supported.
    //AAudioService只能在OC-MR1及更高版本中使用
    //并且只有在系统MMAP策略明确允许的情况下才启用AAudioService
    //这可以防止客户端在不支持AAudioService时误用它
    
    aaudio_policy_t mmapPolicy = property_get_int32(AAUDIO_PROP_MMAP_POLICY,
                                                    AAUDIO_POLICY_NEVER);
    if (mmapPolicy == AAUDIO_POLICY_AUTO || mmapPolicy == AAUDIO_POLICY_ALWAYS) {
        AAudioService::instantiate();
    }
    

    可以看到是有一个AAudioService来初始化的。

    (2)AAudioService是什么?

    先来看它都继承了什么:
    binder不用多说,BnAAudioService和AAudioServiceInterface要看一下

    xref: /frameworks/av/services/oboeservice/AAudioService.h

    class AAudioService :
        public BinderService<AAudioService>,
        public BnAAudioService,
        public aaudio::AAudioServiceInterface
    {
        friend class BinderService<AAudioService>;
    

    先来看BnAAudioService:

    从名字来看就是一个服务
    这个类写在了IAAudioService.h的头文件中
    而client的各种使用AAudio的操作,都通过层层调用及参数包装汇聚到onTransact(),再由onTransact()分发到真正的目标方法执行

    xref: /frameworks/av/media/libaaudio/src/binding/IAAudioService.h

    class BnAAudioService : public BnInterface<IAAudioService> {
    public:
        virtual status_t onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags = 0);
    
    };
    

    具体都有哪些方法呢?

    class IAAudioService : public IInterface {
    public:
    
        DECLARE_META_INTERFACE(AAudioService);
    
        // Register an object to receive audio input/output change and track notifications.
        // For a given calling pid, AAudio service disregards any registrations after the first.
        // Thus the IAAudioClient must be a singleton per process.
        virtual void registerClient(const sp<IAAudioClient>& client) = 0;
    
        /**
         * @param request info needed to create the stream
         * @param configuration contains information about the created stream
         * @return handle to the stream or a negative error
         */
        virtual aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
                                         aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
    
        virtual aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) = 0;
    
        /* Get an immutable description of the in-memory queues
        * used to communicate with the underlying HAL or Service.
        */
        virtual aaudio_result_t getStreamDescription(aaudio::aaudio_handle_t streamHandle,
                                                   aaudio::AudioEndpointParcelable &parcelable) = 0;
    
        /**
         * Start the flow of data.
         * This is asynchronous. When complete, the service will send a STARTED event.
         */
        virtual aaudio_result_t startStream(aaudio::aaudio_handle_t streamHandle) = 0;
    
        /**
         * Stop the flow of data such that start() can resume without loss of data.
         * This is asynchronous. When complete, the service will send a PAUSED event.
         */
        virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;
    
        /**
         * Stop the flow of data such that the data currently in the buffer is played.
         * This is asynchronous. When complete, the service will send a STOPPED event.
         */
        virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;
    
        /**
         *  Discard any data held by the underlying HAL or Service.
         * This is asynchronous. When complete, the service will send a FLUSHED event.
         */
        virtual aaudio_result_t flushStream(aaudio::aaudio_handle_t streamHandle) = 0;
    
        /**
         * Manage the specified thread as a low latency audio thread.
         */
        virtual aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
                                                  pid_t clientThreadId,
                                                  int64_t periodNanoseconds) = 0;
    
        virtual aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
                                                    pid_t clientThreadId) = 0;
    };
    

    这些函数能够让用户很方便的使用AAudio实现播放或者录制等等

    顺便看一下IAAudioService.cpp文件都干了啥?
    xref: /frameworks/av/media/libaaudio/src/binding/IAAudioService.cpp

    /**
     * This is used by the AAudio Client to talk to the AAudio Service.
     *
     * The order of parameters in the Parcels must match with code in AAudioService.cpp.
     */
    class BpAAudioService : public BpInterface<IAAudioService>
    {
    public:
        explicit BpAAudioService(const sp<IBinder>& impl) 
            : BpInterface<IAAudioService>(impl)
        {
        }
    
        void registerClient(const sp<IAAudioClient>& client) override
        {
            Parcel data, reply;
            data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
            data.writeStrongBinder(IInterface::asBinder(client));
            remote()->transact(REGISTER_CLIENT, data, &reply);
        }
    
        aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
                                   aaudio::AAudioStreamConfiguration &configurationOutput) override {
            Parcel data, reply;
            // send command
            data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
            // request.dump();
            request.writeToParcel(&data);
            status_t err = remote()->transact(OPEN_STREAM, data, &reply);
            if (err != NO_ERROR) {
                ALOGE("BpAAudioService::client openStream transact failed %d", err);
                return AAudioConvert_androidToAAudioResult(err);
            }
            // parse reply
            aaudio_handle_t stream;
            err = reply.readInt32(&stream);
            if (err != NO_ERROR) {
                ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
                return AAudioConvert_androidToAAudioResult(err);
            } else if (stream < 0) {
                ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream);
                return stream;
            }
            err = configurationOutput.readFromParcel(&reply);
            if (err != NO_ERROR) {
                ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
                closeStream(stream);
                return AAudioConvert_androidToAAudioResult(err);
            }
            return stream;
        }
    
    ...
    
    };
    
    // Implement an interface to the service.
    // This is here so that you don't have to link with libaaudio static library.
    IMPLEMENT_META_INTERFACE(AAudioService, "IAAudioService");
    
    // The order of parameters in the Parcels must match with code in BpAAudioService
    
    status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
                                            Parcel* reply, uint32_t flags) {
        aaudio_handle_t streamHandle;
        aaudio::AAudioStreamRequest request;
        aaudio::AAudioStreamConfiguration configuration;
        pid_t tid;
        int64_t nanoseconds;
        aaudio_result_t result;
        status_t status = NO_ERROR;
        ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);
    
        switch(code) {
            case REGISTER_CLIENT: {
                CHECK_INTERFACE(IAAudioService, data, reply);
                sp<IAAudioClient> client = interface_cast<IAAudioClient>(
                        data.readStrongBinder());
                registerClient(client);
                return NO_ERROR;
            } break;
    
            case OPEN_STREAM: {
                CHECK_INTERFACE(IAAudioService, data, reply);
                request.readFromParcel(&data);
                result = request.validate();
                if (result != AAUDIO_OK) {
                    streamHandle = result;
                } else {
                    //ALOGD("BnAAudioService::client openStream request dump --------------------");
                    //request.dump();
                    // Override the uid and pid from the client in case they are incorrect.
                    request.setUserId(IPCThreadState::self()->getCallingUid());
                    request.setProcessId(IPCThreadState::self()->getCallingPid());
                    streamHandle = openStream(request, configuration);
                    //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
                    //        streamHandle);
                }
                reply->writeInt32(streamHandle);
                configuration.writeToParcel(reply);
                return NO_ERROR;
            } break;
    
    ...
    
            default:
                // ALOGW("BnAAudioService::onTransact not handled %u", code);
                return BBinder::onTransact(code, data, reply, flags);
        }
    }
    
    } /* namespace android */
    

    其中声明了BpAAudioService类,实现了这些函数
    最终还是经过transact调到了BnAAudioService,然后根据参数分发处理
    整个过程用到了跨进程通信的方法:Binder

    再来看AAudioService的另一个继承:AAudioServiceInterface

    xref: /frameworks/av/media/libaaudio/src/binding/AAudioServiceInterface.h

    /**
     * This has the same methods as IAAudioService but without the Binder features.
     *
     * It allows us to abstract the Binder interface and use an AudioStreamInternal
     * both in the client and in the service.
     */
    

    只看注释,因为定义的方法和IAAudioService里面几乎一样,就是少了Binder
    所以也就可以单纯的把它看作一个接口,客户端和服务端都有继承AAudioServiceInterface,统一了客户端和服务端的接口

    (3)其他几个重要的类的介绍

    IAAudioClient:
    xref: /frameworks/av/media/libaaudio/src/binding/AAudioBinderClient.h

    通过绑定器与服务对话来实现AAudioServiceInterface里面的方法,就是我们上面说到的客户端
    在其内部通过从SM(ServiceManager)里面拿到AAudioService的引用,绑定之后使用

    class AAudioBinderClient : public virtual android::RefBase
            , public AAudioServiceInterface
            , public android::Singleton<AAudioBinderClient> {
    
    

    加上它的继承的一些类来看,会更加一目了然。

    AAudioBinderClient
    xref: /frameworks/av/media/libaaudio/src/binding/IAAudioClient.h

    这个类是用来service与client通信的,其中主要内容就是流 状态的变化

    // Interface (our AIDL) - client methods called by service
    class IAAudioClient : public IInterface {
    public:
    
        DECLARE_META_INTERFACE(AAudioClient);
    
        virtual void onStreamChange(aaudio::aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
    
    };
    
    class BnAAudioClient : public BnInterface<IAAudioClient> {
    public:
        virtual status_t onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags = 0);
    };
    
    } /* namespace android */
    
    

    总结:
    关于AAudioSevice,里面用了很多Binder的知识,把Binder看透了也就了解了这整个调用的流程

    Binder流程如图:
    Binder流程

    2.工作原理

    AAudioService提供了很多供我们使用它的方法,接下来就是对其中几个重要的方法进行剖析,了解其工作原理

    (1)openStream:

    先来看一张时序图:

    在这里插入图片描述

    打开流的过程如图所示,其中主要会分两种情况:
    共享模式:多个音频流可以同时访问该音频设备,该模式下音频的延迟略高于独占模式
    独占模式:只有该音频流能访问该音频设备,其它音频流拒绝访问,该模式下 音频流 性能高 , 延迟低
    注意:如果不再使用该音频设备,需要马上释放音频流,以免影响其它音频流访问该音频设备(独占模式)

    接下来来看代码:
    xref: /frameworks/av/services/oboeservice/AAudioService.cpp

    aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
                                              aaudio::AAudioStreamConfiguration &configurationOutput) {
        aaudio_result_t result = AAUDIO_OK;
        sp<AAudioServiceStreamBase> serviceStream;
        const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
        bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
        aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
    
        // Enforce limit on client processes.
        //对客户端进程实施限制
        //这里面规定每个进程都有最大音频流数
        //当超过这个最大音频流数量时,将不能继续执行函数
        pid_t pid = request.getProcessId();
        if (pid != mAudioClient.clientPid) {
            int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
            if (count >= MAX_STREAMS_PER_PROCESS) {
                ALOGE("openStream(): exceeded max streams per process %d >= %d",
                      count,  MAX_STREAMS_PER_PROCESS);
                return AAUDIO_ERROR_UNAVAILABLE;
            }
        }
        
        //传输模式检测,只允许共享模式或者独占模式
        if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
            ALOGE("openStream(): unrecognized sharing mode = %d", sharingMode);
            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
        }
    
        //独占模式的情况
        if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
            // only trust audioserver for in service indication
            //这个inService变量代表着这个流是不是被AAudioService单独打开的(没有客户端)
            bool inService = false;
            if (mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
                    mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
                inService = request.isInService();
            }
            //之后就可以new一个独占模式专用的对象来打开流
            //传递了两个参数:一个是AAudioService的指针,一个就是inService
            //在之后对流进行其他操作时会用到inService这个参数
            serviceStream = new AAudioServiceStreamMMAP(*this, inService);
            result = serviceStream->open(request);
            if (result != AAUDIO_OK) {
                // Clear it so we can possibly fall back to using a shared stream.
                ALOGW("openStream(), could not open in EXCLUSIVE mode");
                serviceStream.clear();
            }
        }
    
        // if SHARED requested or if EXCLUSIVE failed
        //接下来是共享模式打开流
        //当创建独占模式的流失败并且传输模式与请求的模式不同时,会以共享模式打开流
        if (sharingMode == AAUDIO_SHARING_MODE_SHARED
             || (serviceStream.get() == nullptr && !sharingModeMatchRequired)) {
            serviceStream =  new AAudioServiceStreamShared(*this);
            result = serviceStream->open(request);
        }
        
        //创建不成功
        if (result != AAUDIO_OK) {
            serviceStream.clear();
            ALOGE("openStream(): failed, return %d = %s",
                  result, AAudio_convertResultToText(result));
            return result;
        } else {
            //创建成功
            //生成这个AAduio流服务的句柄返回供客户端调用
            aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
            ALOGD("openStream(): handle = 0x%08X", handle);
            serviceStream->setHandle(handle);
            pid_t pid = request.getProcessId();
            //将这个创建好的音频流注册为客户端用来发送数据
            AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
            configurationOutput.copyFrom(*serviceStream);
            return handle;
        }
    }
    
    

    接下来分析独占模式下是什么情况
    xref: /frameworks/av/services/oboeservice/AAudioServiceStreamMMAP.cpp

    
    //这些对应于独占模式MMAP客户端流。
    //它独占使用一个AAudioServiceEndpointMMAP与底层设备或端口通信。
    //这个服务流的核心就是使用MMAP缓冲区
    
    //继承了AAudioServiceStreamBase
    class AAudioServiceStreamMMAP : public AAudioServiceStreamBase
    
    // Open stream on HAL and pass information about the shared memory buffer back to the client.
    //打开HAL上的流并将有关共享内存缓冲区的信息传递回客户端。
    aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {
    
        sp<AAudioServiceStreamMMAP> keep(this);
        
        //带着独占模式的参数,调用AAudioServiceStreamBase的open函数
        aaudio_result_t result = AAudioServiceStreamBase::open(request,
                                                               AAUDIO_SHARING_MODE_EXCLUSIVE);
        if (result != AAUDIO_OK) {
            return result;
        }
    
        //把这个mServiceEndpointWeak升级为强指针,看它是不是还存活
        //mServiceEndpoint变量可以由多个线程访问
        //因此,我们通过本地向智能指针升级弱指针来访问它,这是线程安全的
        sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
        if (endpoint == nullptr) {
            ALOGE("%s() has no endpoint", __func__);
            return AAUDIO_ERROR_INVALID_STATE;
        }
        
        //注册这个AAudioServiceStreamMMAP流
        //AAudioServiceEndpoint(endpoint)由AAudioServiceStreamBase的子类使用,与底层音频设备或端口通信
        //保证流的操作是线程安全的
        result = endpoint->registerStream(keep);
        if (result != AAUDIO_OK) {
            return result;
        }
    
        setState(AAUDIO_STREAM_STATE_OPEN);
    
        return AAUDIO_OK;
    }
    

    继续分析AAudioServiceStreamBase::open
    xref: /frameworks/av/services/oboeservice/AAudioServiceStreamBase.cpp

    //AAudioServiceStreamBase的每个实例都对应于一个客户机流。
    //它使用AAudioServiceEndpoint的子类与底层设备或端口通信。
    
    aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
                                                  aaudio_sharing_mode_t sharingMode) {
        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
        aaudio_result_t result = AAUDIO_OK;
    
        mMmapClient.clientUid = request.getUserId();
        mMmapClient.clientPid = request.getProcessId();
        mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
    
        // Limit scope of lock to avoid recursive lock in close().
        //限制锁的作用域以避免close()中的递归锁。
        //判断mUpMessageQueueLock是否为空,也就是说看它是否是第一次打开流
        {
            std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
            if (mUpMessageQueue != nullptr) {
                ALOGE("%s() called twice", __func__);
                return AAUDIO_ERROR_INVALID_STATE;
            }
    
            //创建共享内存
            mUpMessageQueue = new SharedRingBuffer();
            result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
                                               QUEUE_UP_CAPACITY_COMMANDS);
            if (result != AAUDIO_OK) {
                goto error;
            }
    
            // This is not protected by a lock because the stream cannot be
            // referenced until the service returns a handle to the client.
            // So only one thread can open a stream.
            //这不受锁的保护,因为在服务向客户端返回句柄之前,流不能被引用。
            //所以只有一个线程可以打开一个流。
            mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
                                                             request,
                                                             sharingMode);
            if (mServiceEndpoint == nullptr) {
                ALOGE("%s() openEndpoint() failed", __func__);
                result = AAUDIO_ERROR_UNAVAILABLE;
                goto error;
            }
            // Save a weak pointer that we will use to access the endpoint.
            //保存将用于访问端口的弱指针
            //然后这个弱指针将会在AAudioServiceStreamMMAP里面转化为强指针
            mServiceEndpointWeak = mServiceEndpoint;
    
            //获取每次写入的帧数
            mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
            copyFrom(*mServiceEndpoint);
        }
        return result;
    
    error:
        close();
        return result;
    }
    

    继续分析mEndpointManager.openEndpoint
    xref: /frameworks/av/services/oboeservice/AAudioEndpointManager.cpp

    sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
                                            const aaudio::AAudioStreamRequest &request,
                                            aaudio_sharing_mode_t sharingMode) {
        if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
            return openExclusiveEndpoint(audioService, request);
        } else {
            return openSharedEndpoint(audioService, request);
        }
    }
    

    在这里面做了个判断,依据我们传入的sharingMode来做区分
    先看独占模式,也就是openExclusiveEndpoint这个方法

    sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
            AAudioService &aaudioService,
            const aaudio::AAudioStreamRequest &request) {
    
        std::lock_guard<std::mutex> lock(mExclusiveLock);
    
        const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
    
        // Try to find an existing endpoint.
        //遍历mExclusiveStreams看是否存在当前configuration所匹配的流
        sp<AAudioServiceEndpoint> endpoint = findExclusiveEndpoint_l(configuration);
    
        // If we find an existing one then this one cannot be exclusive.
        //如果找到了,就不允许再创建了,返回空
        if (endpoint.get() != nullptr) {
            ALOGW("openExclusiveEndpoint() already in use");
            // Already open so do not allow a second stream.
            return nullptr;
        //没有匹配的流,则可以创建
        } else {
            sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP(aaudioService);
            ALOGV("openExclusiveEndpoint(), no match so try to open MMAP %p for dev %d",
                  endpointMMap.get(), configuration.getDeviceId());
            //AAudioServiceEndpointMMAP继承了AAudioServiceEndpoint
            endpoint = endpointMMap;
            
            //调用AAudioServiceEndpointMMAP的open函数
            aaudio_result_t result = endpoint->open(request);
            if (result != AAUDIO_OK) {
                ALOGE("openExclusiveEndpoint(), open failed");
                endpoint.clear();
            } else {
                //创建成功加入mExclusiveStreams
                mExclusiveStreams.push_back(endpointMMap);
                mExclusiveOpenCount++;
            }
        }
    
        if (endpoint.get() != nullptr) {
            // Increment the reference count under this lock.
            //增加此锁下的引用计数
            endpoint->setOpenCount(endpoint->getOpenCount() + 1);
        }
        return endpoint;
    }
    

    层层调用,快到头了
    AAudioServiceStreamMMAP使用AAudioServiceEndpointMMAP通过AudioFlinger访问MMAP设备
    AAudioServiceEndpointMMAP的open函数:
    xref: /frameworks/av/services/oboeservice/AAudioServiceEndpointMMAP.cpp

    aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
        aaudio_result_t result = AAUDIO_OK;
        audio_config_base_t config;
        audio_port_handle_t deviceId;
    
        //读取系统属性
        //获得系统每次出现MMAP的最小微秒
        int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
        int32_t burstMicros = 0;
    
        copyFrom(request.getConstantConfiguration());
    
        //获取这次请求的音频流方向:输出/输入
        aaudio_direction_t direction = getDirection();
    
        //转换为内部值
        const audio_content_type_t contentType =
                AAudioConvert_contentTypeToInternal(getContentType());
        // Usage only used for OUTPUT
        //输入和输出有区别
        //输出情况下source为默认值
        //输入情况下usage为默认值
        const audio_usage_t usage = (direction == AAUDIO_DIRECTION_OUTPUT)
                ? AAudioConvert_usageToInternal(getUsage())
                : AUDIO_USAGE_UNKNOWN;
        const audio_source_t source = (direction == AAUDIO_DIRECTION_INPUT)
                ? AAudioConvert_inputPresetToAudioSource(getInputPreset())
                : AUDIO_SOURCE_DEFAULT;
    
        const audio_attributes_t attributes = {
                .content_type = contentType,
                .usage = usage,
                .source = source,
                .flags = AUDIO_FLAG_LOW_LATENCY,
                .tags = ""
        };
        ALOGD("%s(%p) MMAP attributes.usage = %d, content_type = %d, source = %d",
              __func__, this, attributes.usage, attributes.content_type, attributes.source);
    
        //mMmapClient类型是AudioClient
        //在open中设置,在open和startStream中使用
        mMmapClient.clientUid = request.getUserId();
        mMmapClient.clientPid = request.getProcessId();
        mMmapClient.packageName.setTo(String16(""));
    
        mRequestedDeviceId = deviceId = getDeviceId();
    
        // Fill in config
        //获取当前流的配置信息
        aaudio_format_t aaudioFormat = getFormat();
        if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
            aaudioFormat = AAUDIO_FORMAT_PCM_I16;
        }
        config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
    
        int32_t aaudioSampleRate = getSampleRate();
        if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
            aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
        }
        config.sample_rate = aaudioSampleRate;
    
        int32_t aaudioSamplesPerFrame = getSamplesPerFrame();
    
        if (direction == AAUDIO_DIRECTION_OUTPUT) {
            config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
                                  ? AUDIO_CHANNEL_OUT_STEREO
                                  : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
            mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
    
        } else if (direction == AAUDIO_DIRECTION_INPUT) {
            config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
                                   ? AUDIO_CHANNEL_IN_STEREO
                                   : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
            mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
    
        } else {
            ALOGE("%s() invalid direction = %d", __func__, direction);
            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
        }
    
        MmapStreamInterface::stream_direction_t streamDirection =
                (direction == AAUDIO_DIRECTION_OUTPUT)
                ? MmapStreamInterface::DIRECTION_OUTPUT
                : MmapStreamInterface::DIRECTION_INPUT;
    
        aaudio_session_id_t requestedSessionId = getSessionId();
        audio_session_t sessionId = AAudioConvert_aaudioToAndroidSessionId(requestedSessionId);
    
        // Open HAL stream. Set mMmapStream
        //属性和配置信息都设置完之后,就要开始打开流了
        //这里面利用MmapStreamInterface来打开HAL层的MMAP流
        //openMmapStream在MmapStreamInterface里面定义,具体实现在AudioFlinger
        status_t status = MmapStreamInterface::openMmapStream(streamDirection,
                                                              &attributes,
                                                              &config,
                                                              mMmapClient,
                                                              &deviceId,
                                                              &sessionId,
                                                              this, // callback
                                                              mMmapStream,
                                                              &mPortHandle);
        ALOGD("%s() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
              __func__, mMmapClient.clientUid,  mMmapClient.clientPid, mPortHandle);
        if (status != OK) {
            ALOGE("%s() openMmapStream() returned status %d",  __func__, status);
            return AAUDIO_ERROR_UNAVAILABLE;
        }
    
        if (deviceId == AAUDIO_UNSPECIFIED) {
            ALOGW("%s() openMmapStream() failed to set deviceId", __func__);
        }
        setDeviceId(deviceId);
    
        if (sessionId == AUDIO_SESSION_ALLOCATE) {
            ALOGW("%s() - openMmapStream() failed to set sessionId", __func__);
        }
    
        aaudio_session_id_t actualSessionId =
                (requestedSessionId == AAUDIO_SESSION_ID_NONE)
                ? AAUDIO_SESSION_ID_NONE
                : (aaudio_session_id_t) sessionId;
        setSessionId(actualSessionId);
        ALOGD("%s() deviceId = %d, sessionId = %d", __func__, getDeviceId(), getSessionId());
    
        // Create MMAP/NOIRQ buffer.
        //创建MMAP缓存
        int32_t minSizeFrames = getBufferCapacity();
        if (minSizeFrames <= 0) { // zero will get rejected
            minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
        }
        //这个createMmapBuffer也是在AudioFlinger里面实现的
        status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
        if (status != OK) {
            ALOGE("%s() - createMmapBuffer() failed with status %d %s",
                  __func__, status, strerror(-status));
            result = AAUDIO_ERROR_UNAVAILABLE;
            goto error;
        } else {
            ALOGD("%s() createMmapBuffer() returned = %d, buffer_size = %d, burst_size %d"
                          ", Sharable FD: %s",
                  __func__, status,
                  abs(mMmapBufferinfo.buffer_size_frames),
                  mMmapBufferinfo.burst_size_frames,
                  mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
        }
    
        setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
        // The audio HAL indicates if the shared memory fd can be shared outside of audioserver
        // by returning a negative buffer size
        //音频HAL通过返回负缓冲区大小来指示共享内存fd是否可以在audioserver之外共享
        //这个可以一会分析createMmapBuffer时具体去看
        if (getBufferCapacity() < 0) {
            // Exclusive mode can be used by client or service.
            setBufferCapacity(-getBufferCapacity());
        } else {
            // Exclusive mode can only be used by the service because the FD cannot be shared.
            uid_t audioServiceUid = getuid();
            if ((mMmapClient.clientUid != audioServiceUid) &&
                getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
                // Fallback is handled by caller but indicate what is possible in case
                // this is used in the future
                //回退由调用者处理,但指出在将来使用时可能发生的情况
                setSharingMode(AAUDIO_SHARING_MODE_SHARED);
                ALOGW("%s() - exclusive FD cannot be used by client", __func__);
                result = AAUDIO_ERROR_UNAVAILABLE;
                goto error;
            }
        }
    
        // Get information about the stream and pass it back to the caller.
        //获取有关流的信息并将其传递回调用者。
        setSamplesPerFrame((direction == AAUDIO_DIRECTION_OUTPUT)
                           ? audio_channel_count_from_out_mask(config.channel_mask)
                           : audio_channel_count_from_in_mask(config.channel_mask));
    
        // AAudio creates a copy of this FD and retains ownership of the copy.
        // Assume that AudioFlinger will close the original shared_memory_fd.
        //AAudio创建此FD的副本并保留副本的所有权
        //假设AudioFlinger将关闭原始共享内存
        mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
        if (mAudioDataFileDescriptor.get() == -1) {
            ALOGE("%s() - could not dup shared_memory_fd", __func__);
            result = AAUDIO_ERROR_INTERNAL;
            goto error;
        }
        mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
        setFormat(AAudioConvert_androidToAAudioDataFormat(config.format));
        setSampleRate(config.sample_rate);
    
        // Scale up the burst size to meet the minimum equivalent in microseconds.
        // This is to avoid waking the CPU too often when the HW burst is very small
        // or at high sample rates.
        do {
            if (burstMicros > 0) {  // skip first loop
                mFramesPerBurst *= 2;
            }
            burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
        } while (burstMicros < burstMinMicros);
    
        ALOGD("%s() original burst = %d, minMicros = %d, to burst = %d\n",
              __func__, mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
    
        ALOGD("%s() actual rate = %d, channels = %d"
              ", deviceId = %d, capacity = %d\n",
              __func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
    
        return result;
    
    error:
        close();
        return result;
    }
    
    

    继续看一下在AudioFlinger上面是怎样打开流和创建缓存的
    xref: /frameworks/av/services/audioflinger/AudioFlinger.cpp

    status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t direction,
                                          const audio_attributes_t *attr,
                                          audio_config_base_t *config,
                                          const AudioClient& client,
                                          audio_port_handle_t *deviceId,
                                          audio_session_t *sessionId,
                                          const sp<MmapStreamCallback>& callback,
                                          sp<MmapStreamInterface>& interface,
                                          audio_port_handle_t *handle)
    {
    
    ......
    
        if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
            audio_config_t fullConfig = AUDIO_CONFIG_INITIALIZER;
            fullConfig.sample_rate = config->sample_rate;
            fullConfig.channel_mask = config->channel_mask;
            fullConfig.format = config->format;
            //注意AUDIO_OUTPUT_FLAG_MMAP_NOIRQ和AUDIO_OUTPUT_FLAG_DIRECT这两个flag
            ret = AudioSystem::getOutputForAttr(attr, &io,
                                                actualSessionId,
                                                &streamType, client.clientPid, client.clientUid,
                                                &fullConfig,
                                                (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
                                                        AUDIO_OUTPUT_FLAG_DIRECT),
                                                deviceId, &portId);
        } else {
            //注意AUDIO_INPUT_FLAG_MMAP_NOIRQ
            ret = AudioSystem::getInputForAttr(attr, &io,
                                                  actualSessionId,
                                                  client.clientPid,
                                                  client.clientUid,
                                                  client.packageName,
                                                  config,
                                                  AUDIO_INPUT_FLAG_MMAP_NOIRQ, deviceId, &portId);
        }
        if (ret != NO_ERROR) {
            return ret;
        }
    
        // at this stage, a MmapThread was created when openOutput() or openInput() was called by
        // audio policy manager and we can retrieve it
        //在这个阶段,当音频策略管理器调用openOutput()或openInput()时创建了一个MmapThread,我们可以检索它
        //这里可以根据这个io找到对应创建好的MMAPThread
        sp<MmapThread> thread = mMmapThreads.valueFor(io);
        if (thread != 0) {
            interface = new MmapThreadHandle(thread);
            thread->configure(attr, streamType, actualSessionId, callback, *deviceId, portId);
            *handle = portId;
            *sessionId = actualSessionId;
        } else {
            //没找到的话就释放掉资源
            if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
                AudioSystem::releaseOutput(io, streamType, actualSessionId);
            } else {
                AudioSystem::releaseInput(portId);
            }
            ret = NO_INIT;
        }
    
        ALOGV("%s done status %d portId %d", __FUNCTION__, ret, portId);
    
        return ret;
    }
    

    上文提到MMAPThread是在openOutput里面创建的
    简单来说就是getOutputForAttr这个函数在获取到句柄的同时,也会顺便打开通路
    最终调用到AudioFlinger里面的openOutput(又回来了)

    status_t AudioFlinger::openOutput(audio_module_handle_t module,
                                      audio_io_handle_t *output,
                                      audio_config_t *config,
                                      audio_devices_t *devices,
                                      const String8& address,
                                      uint32_t *latencyMs,
                                      audio_output_flags_t flags)
    {
        ALOGI("openOutput() this %p, module %d Device %#x, SamplingRate %d, Format %#08x, "
                  "Channels %#x, flags %#x",
                  this, module,
                  (devices != NULL) ? *devices : 0,
                  config->sample_rate,
                  config->format,
                  config->channel_mask,
                  flags);
    
        if (devices == NULL || *devices == AUDIO_DEVICE_NONE) {
            return BAD_VALUE;
        }
    
        Mutex::Autolock _l(mLock);
    
        sp<ThreadBase> thread = openOutput_l(module, output, config, *devices, address, flags);
        if (thread != 0) {
            //看到这个flag了---AUDIO_OUTPUT_FLAG_MMAP_NOIRQ
            if ((flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) == 0) {
                ......
                }
            } else {
                //最终拿到已经创建好的线程
                //但是是在哪创建的呢
                MmapThread *mmapThread = (MmapThread *)thread.get();
                mmapThread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
            }
            return NO_ERROR;
        }
    
        return NO_INIT;
    }
    

    MmapThread线程是在AudioFlinger里面的openOutput_l函数中创建的
    一起来看下:

    sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(audio_module_handle_t module,
                                                                audio_io_handle_t *output,
                                                                audio_config_t *config,
                                                                audio_devices_t devices,
                                                                const String8& address,
                                                                audio_output_flags_t flags)
    {
        AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices);
        if (outHwDev == NULL) {
            return 0;
        }
        
        ......
    
        AudioStreamOut *outputStream = NULL;
        status_t status = outHwDev->openOutputStream(
                &outputStream,
                *output,
                devices,
                flags,
                config,
                address.string());
    
        mHardwareStatus = AUDIO_HW_IDLE;
    
        if (status == NO_ERROR) {
            //又看到这个flag了
            //根据参数在这创建的MmapPlaybackThread
            if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
                sp<MmapPlaybackThread> thread =
                        new MmapPlaybackThread(this, *output, outHwDev, outputStream,
                                              devices, AUDIO_DEVICE_NONE, mSystemReady);
                //加入到线程组里面
                mMmapThreads.add(*output, thread);
                ALOGV("openOutput_l() created mmap playback thread: ID %d thread %p",
                      *output, thread.get());
                return thread;
            } else {
                ......
            }
        }
    
        return 0;
    }
    

    打开输出流这里面又调用到了AudioHwDevice的openOutputStream方法
    xref: /frameworks/av/services/audioflinger/AudioHwDevice.cpp

    status_t AudioHwDevice::openOutputStream(
            AudioStreamOut **ppStreamOut,
            audio_io_handle_t handle,
            audio_devices_t devices,
            audio_output_flags_t flags,
            struct audio_config *config,
            const char *address)
    {
    
        struct audio_config originalConfig = *config;
        AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
        
        //调到了hal层,继续打开通路
        status_t status = outputStream->open(handle, devices, config, address);
    
        ......
    
        *ppStreamOut = outputStream;
        return status;
    }
    
    

    整个流程大概分析了一遍
    重点记住这个flag:
    AUDIO_OUTPUT_FLAG_MMAP_NOIRQ
    AUDIO_INPUT_FLAG_MMAP_NOIRQ
    在整个打开通路的流程中起着重要的作用

    上述只分析了独占模式下的情况,共享模式和这个类似,读者可以根据时序图自行分析
    AAudioService的其他方法,将在后续章节发出


    总结

    以上对AAudioService这个服务以及它的OpenStream方法做了详细的分析

    (1)AAudio并没有走AudioTrack和AudioRecod
    (2)AAudio最终还是要通过AudioFlinger打开音频通路和创建播放线程
    (3)AAudio核心原理是通过内存映射来减小内存数据拷贝次数,增加音频传输效率,减小延迟
    (4)AAudioServiceStreamMMAP创建流之后得到了AAudioServiceEndpointMMAP类型的对象,后续对音频流的操作都是 通过操作这个对象来实现的


    觉得看完有帮助的请一键三连吧!
    您的支持将是我继续下去的动力

    展开全文
  • AAudio 音频库 简介 II . AAudio 音频流 三要素 ( 设备 | 共享模式 | 数据格式 ) III . AAudio 音频设备 IV . AAudio 音频设备获取 V . AAudio 音频设备 ID 标识 VI . AAudio 音频流方向 VII . AAudio 音频流 共享...



    I . AAudio 音频库 简介



    AAudio 音频库 简介 :

    • ① 开发级别 : AAudio 是在 Android 8.0 版本后引入的音频库 , 该音频库 需要使用 C 语言在 Native 层进行 调用 , 属于 NDK 开发范畴 ;

    • ② 音频库特点 : 该音频库是 Google 专门为高性能音频设计开发的 , 具有低延迟 , 高性能的特点 ;

    • ③ 提供的功能 : AAudio 是轻量级的音频库 , 只提供写入音频流进行发音的功能 , 不负责音频设备管理 , 文件 I / O , 音频编解码 等操作 ;



    II . AAudio 音频流 三要素 ( 设备 | 共享模式 | 数据格式 )



    1. AAudio 音频流设备 : 数据从耳机输入 , 数据 输出 到发音设备 ;

    • ① 音频输入 ( 声音来源 ) : 从话筒 , 耳机等音频输入设备中 , 采集 音频 数据 , 如果使用 AAudio 音频流读取 , 性能高 , 低延迟 ;
    • ② 音频输出 ( 声音接收 ) : 将音频流写入到 AAudio , AAudio 会以极高性能方式将音频流输出到发音设备中 ;

    从输入端获取数据 ( 话筒 -> 音频流 -> 内存 ) , 将音频数据写出到输出端 ( 内存 -> 音频流 -> 发音设备 ) ;

    2. 音频流读写数据格式 : 在应用中 , 使用 AAudioStream 结构表示音频流 , 读取 和 写出 音频流数据都使用该数据结构 ;

    3. 共享模式 : ① 阻塞方式读写 音频流数据 , ② 非阻塞方式读写 音频流数据 ;



    III . AAudio 音频设备



    AAudio 音频流与音频设备对应关系 : 每个 AAudio 音频流都需要 与一个音频设备进行关联 , 才能发挥正常作用 ;

    音频设备 :

    • ① 真实硬件音频设备 : 这里的音频设备可以是连接到 Android 手机的耳机 , 音箱 , 也可以是内置的 麦克风 话筒等 ;
    • ② 虚拟音频设备 : 音频设备还可以是用软件虚拟出来的音频设备 ;

    这里的音频设备 是 AAudio 音频流的 数据来源 ( 话筒 , 麦克风 ) , 或者数据接受者 ( 音箱 , 耳机 ) ;



    IV . AAudio 音频设备获取



    音频设备获取 :

    • ① 获取方法 : 在开发时 , 调用 AudioManagergetDevices() 方法 , 可以获取当前连接到 Android 系统上的 并且 符合指定的 flag 参数标准 的可用音频设备 ( 如将耳机插入 Android 手机 , 就会获取到耳机音频设备 ) ;
    public AudioDeviceInfo[] getDevices (int flags)
    
    • ② 返回值 : 该方法返回音频设备的类型信息 ( AudioDeviceInfo ) 数组 , AudioDeviceInfo 类封装了音频设备的一系列信息 ;


    V . AAudio 音频设备 ID 标识



    音频设备 ID :

    • ① 唯一标识 : Android 的音频设备都有一个独一无二的 ID 标识 ;
    • ② AAudio 与 音频设备绑定 : 使用该 ID 标识 , 可以将该音频设备与 AAudio 音频流进行绑定 ;
    • ③ 默认设备 : 大部分情况下 , AAudio 都有一个默认的输入 ( 麦克风 ) 和 输出 ( 外放喇叭 ) 设备 ;


    VI . AAudio 音频流方向



    AAudio 音频流方向 : AAudio 音频流 只能设置一个方向 , 输入 或者 输出 ;

    • ① 音频设备方向 :
      • a . 音频输入设备 : 如 麦克风 , 话筒等 , 其音频流是从 音频设备 -> 内存 ;
      • b . 音频输出设备 : 如 耳机 , 音箱 , 其音频流方向是从 内存 -> 音频设备 ;
    • ② 设置方向 : 在创建 AAudio 音频流时 , 需要指定该音频流的方向 ;
    • ③ 检查方向 : 在打开 AAudio 音频流时 , Android 会检查该音频流方向 与 音频设备的音频流方向是否一致 ;


    VII . AAudio 音频流 共享模式



    1. AAudio 音频流 共享模式 : 分两种 , 独占模式 ( AAUDIO_SHARING_MODE_EXCLUSIVE ) 和 混合模式 ( AAUDIO_SHARING_MODE_SHARED ) ;

    • ① 独占模式 : 该模式下 , 音频流 独占 音频设备 , 此时其它音频流无法访问该 音频设备 ;
    • ② 混合模式 : 该模式下 , 允许 AAudio 音频流 与 其它音频流 混合 , 音频设备播放多个流混合后的采样 ;

    2. 独占模式说明 :

    • ① 拒接访问 : 如果音频设备当前处于正在使用中的状态 , AAudio 音频流此时无法对其进行独占模式下的访问 ;
    • ② 延迟低 : 在独占模式下 , 音频流的延迟比较低 , 性能较高 ;
    • ③ 及时释放资源 : 如果不需要独占音频设备 , 及时将其释放 ;


    VIII . AAudio 音频格式 ( 样本格式 | 每帧样本数 | 采样率 )



    AAudio 音频格式 :

    • ① 样本格式 : 每个样本的表示方式 ;
      • a . AAUDIO_FORMAT_PCM_I16 : 数据类型是 int16_t , 表示每个样本由 2 个字节组成 ;
      • b . AAUDIO_FORMAT_PCM_FLOAT : 数据类型 float , 表示样本由浮点型数据表示 , 其取值范围 -1.0 ~ +1.0 ;
    • ② 每帧采样数 : 每帧的采样个数 ;
    • ③ 采样率 : 1秒钟 音频的 的采样个数 , 单位是 赫兹 ( Hz ) , 如 44100Hz , 代表一秒钟有 44100 个采样 ;
    • ④ 获取音频流样本格式 : 调用 AAudioStream_getDataFormat 方法 , 可以获取 AAudio 音频流格式 ; 其结果可能是 AAUDIO_FORMAT_PCM_I16 或 AAUDIO_FORMAT_PCM_FLOAT ;
    aaudio_format_t dataFormat = AAudioStream_getDataFormat(stream);
    

    如 : 一个 PCM 音频流 , 16 bit 代表其每个样本有 16 位 ( 2 字节 ) , 44100 采样率 代表每秒钟有 44100 个样本 ;



    IX . AAudio 音频样本格式转换



    AAudio 样本格式转换 :

    • ① 类型转换 : AAudio 可以根据音频设备的类型自动转换样本的类型 , 如 HAL 层使用的音频样本是 16 位的 , 如果输入 浮点型样本类型 , 会自动转换成 16bit 类型的样本 ;
    • ② 转换方向 : 输入 输出 方向 其样本格式 都可以自动转换 ;
    展开全文
  • AAudio 音频流创建流程 II . AAudio 音频流构建器 设置 通道数 AAudioStreamBuilder_setChannelCount III . AAudio 音频流构建器 设置音频格式 AAudioStreamBuilder_setFormat IV . AAudio 音频流 样本格式 V . ...



    I . AAudio 音频流创建流程



    红色标题是本博客讲解的内容 , 黑色是前几篇讲过的内容 ;

    使用 AAudio 音频库 , 首先需要导入 AAudio.h 头文件 ;

    #include <AAudio.h>
    

    创建 AAudio 音频流 , 需要先创建 AAudio 音频流构建器 , 然后在通过该构建器创建音频流 ;

        //创建构建器 , AAudio 音频流通过该构建器创建
        //声明 AAudio 音频流构建器 指针
        AAudioStreamBuilder *builder = nullptr;
        //创建 AAudio 音频流构建器 , 注意传入二维指针
        aaudio_result_t result = AAudio_createStreamBuilder(&builder);
    

    设置音频设备 ID ;

        // 设置音频流设备 ID
        AAudioStreamBuilder_setDeviceId(builder, playbackDeviceId_);
    

    设置音频流方向 ;

        // 设置音频流方向
        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
    

    设置音频设备共享模式 ;

        // 设置共享模式 , 独占模式性能更高 , 延迟更低 ; 如果 该音频设备正在被使用 , 设置失败会自动设置成 共享模式
        AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
    

    设置性能模式 ;

        // 设置性能模式
        AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
    

    设置 AAudio 音频流通道数 :

        // 设置通道个数
        AAudioStreamBuilder_setChannelCount(builder, sampleChannels_);
    

    设置 AAudio 音频流样本格式 :

        // 设置音频格式
        AAudioStreamBuilder_setFormat(builder, sampleFormat_);
    

    设置 AAudio 音频流缓冲区大小 : 这里的缓冲区是播放器的缓冲区 , 单位是帧 , 每帧的采样数就是通道数 , 单声道 每帧 1 个采样, 双声道立体声每帧 2 个采样 , 分别对应左右声道的采样 ;

        // 设置每帧的缓冲区大小 , 可以通过该设置达到尽可能低的延迟
        AAudioStream_setBufferSizeInFrames(playStream_, framesPerBurst_);
    

    下面会着重对上面的流程细节进行详细解析 ;
    每个方法的参数 , 原理 , 返回值 等细节都会讲解到 ;



    II . AAudio 音频流构建器 设置 通道数 AAudioStreamBuilder_setChannelCount



    1 . AAudio 音频流通道数设置 :

    • ① 函数原型 :
    AAUDIO_API void AAudioStreamBuilder_setChannelCount(
      AAudioStreamBuilder *builder,
      int32_t channelCount
    )
    
    • ② 函数作用 : 设置音频流的通道数 , 设置 1 代表 单声道 , 设置 2 代表 立体声 ;
    • ③ 代码示例 :
        // 设置通道个数
        AAudioStreamBuilder_setChannelCount(builder, sampleChannels_);
    

    2 . 默认处理 :

    • ① 默认值 : 如果没有调用该函数设置通道数 , 默认为 AAUDIO_UNSPECIFIED ;

    • ② 默认情况处理 : 如果通道数未指定 , 打开流时系统自动选择一个最佳通道数 , 不同设备可能有不同的通道数 ;

    3. 指定通道值情况处理 : 如果指定了通道数 , 那么打开流时会使用该通道数 ; 如果通道数与设备不匹配 , 那么 AAudio 音频流打开时会报错 ;



    III . AAudio 音频流构建器 设置音频格式 AAudioStreamBuilder_setFormat



    1 . AAudio 音频流格式设置 :

    • ① 函数原型 :
    AAUDIO_API void AAudioStreamBuilder_setFormat(
      AAudioStreamBuilder *builder,
      aaudio_format_t format
    )
    
    • ② 函数作用 : 设置 AAudio 音频流的样本数据格式 ;
    • ③ 参数 AAudioStreamBuilder *builder : AAudio 音频流构建器 ;
    • ④ 参数 aaudio_format_t format : 音频格式 , 一般是 AAUDIO_FORMAT_PCM_FLOAT ( 浮点型采样格式 ) 或 AAUDIO_FORMAT_PCM_I16 ( 每个采样 16 位 ) ;
    • ⑤ 代码示例 :
        // 设置音频格式
        AAudioStreamBuilder_setFormat(builder, sampleFormat_);
    

    2 . 默认设置 :

    • ① 默认值 : 如果没有设置音频流样本格式 , 那么会默认设置成 AAUDIO_UNSPECIFIED ;

    • ② 默认值打开流 : 默认值 AAUDIO_UNSPECIFIED 状态下 , 如果打开 AAudio 音频流 , 系统会自动选择一个最佳的样本格式 , 这个值可能由 Android 设备决定 , 每个设备的值可能都不一样 ;



    IV . AAudio 音频流 样本格式



    AAudio 音频流 样本格式 :

    • ① 样本格式定义 : aaudio_format_t 是 int32_t 类型数据 , 该枚举有四种取值 ;
    enum {
        AAUDIO_FORMAT_INVALID = -1,
        AAUDIO_FORMAT_UNSPECIFIED = 0,
    
        /**
         * This format uses the int16_t data type.
         * The maximum range of the data is -32768 to 32767.
         */
        AAUDIO_FORMAT_PCM_I16,
    
        /**
         * This format uses the float data type.
         * The nominal range of the data is [-1.0f, 1.0f).
         * Values outside that range may be clipped.
         *
         * See also 'floatData' at
         * https://developer.android.com/reference/android/media/AudioTrack#write(float[],%20int,%20int,%20int)
         */
        AAUDIO_FORMAT_PCM_FLOAT
    };
    typedef int32_t aaudio_format_t;
    
    • ② AAUDIO_FORMAT_UNSPECIFIED 格式 : 未定义格式 , 默认的格式 , 如果音频流打开系统跟自动选择合适的采样格式 ;
    • ③ AAUDIO_FORMAT_PCM_I16 格式 : 每个样本是 16 位 , 其取值范围是 -32768 ~ 32767 ;
    • ④ AAUDIO_FORMAT_PCM_FLOAT 格式 : 样本由浮点型表示 , 取值范围 -1.0 ~ +1.0 ;


    V . AAudio 音频流 设置缓冲区大小 AAudioStream_setBufferSizeInFrames



    1. 函数作用 : 在音频流播放时 , 有可能会产生阻塞 , 即 采样播放完毕 , 新采样还没到达 , 该函数可以 通过 改变 缓冲区大小阈值 , 调整 缓冲区的延迟 , 即 如果出现 阻塞 , 可以增大该缓冲区大小 ( 帧数 ) ;

    2. 结合 XRun 值使用 : 通过 AAudioStream_getXRunCount() 方法 , 可以获取 欠载 ( UnderRun ) 或 超限 ( OverRun ) 的值 , 根据该 XRun 值进行缓冲区大小的调整 , 达到为每个音频设备设置合适的延迟的目的 ;

    3. 可设置的最大值 : 通过 AAudioStream_getBufferCapacityInFrames() 函数可以获取 缓冲区可设置的最大帧数 , 设置帧数时 , 不能超过该数值 ;

    4. 查看当前缓冲区大小 : 调用 AAudioStream_getBufferSizeInFrames() 方法 , 可以查看当前的缓冲区帧数 ;

    文档中的说法是 : 获取 AAudio 音频流在不阻塞的情况下 , 可以读取 或 写入的最大帧数 , 理解不通 ;

    5. AAudioStream_setBufferSizeInFrames 函数简介 :

    • ① 函数原型 : numFrames 是设置的新的缓冲区帧数 , stream 代表 AAudio 音频流指针 ;
    AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(
      AAudioStream *stream,
      int32_t numFrames
    )
    
    • ② 代码示例 :
            //设置当前缓冲区是多少帧
            bufferSize = AAudioStream_setBufferSizeInFrames(stream, bufferSize);
    

    播放器缓冲区 : 这里的帧缓冲区指的是 AAudio 音频流的缓冲区 , 属于播放器 或 音频设备 的固有属性 ;
    采样缓冲区 : 注意与采样缓冲区进行区分 , 采样缓冲区指的是 一次性向 AAudio 音频流 读取 或 写入的 字节数 , 注意区分这两个缓冲区 ;
    电流产生 : 如果两个缓冲区设置不当 , 会造成音频卡顿 , 电流 , 刺啦 或者 啪啪 的声音 ;
    帧大小 : 这里的帧可以理解成一个样本 , 如果是单声道 , 每帧一个样本 , 如果是双声道立体声 , 每帧 2 个样本 ;



    VI . AAudio 音频流 采样率设置 AAudioStreamBuilder_setSampleRate



    推荐使用默认设置 , 不要调用该方法 设置采样率 , 获取默认的最佳采样率 , 然后根据该采样率进行采样即可 ;

    1 . AAudioStreamBuilder_setSampleRate 方法简介 :

    • ① 函数原型 : 设置 AAudio 音频流的采样率 ;
    AAUDIO_API void AAudioStreamBuilder_setSampleRate(
      AAudioStreamBuilder *builder,
      int32_t sampleRate
    )
    
    • ② sampleRate 参数 : sampleRate 参数就是采样率 , 其单位 赫兹 ( Hz ) , 一般情况下是 44100 Hz 或 48000 Hz , 代表一秒钟有 44100 或 48000 个采样 ;

    2 . 最佳实践 :

    • ① 默认值 : 默认情况下 , 如果不调用该函数设置 AAudio 播放器的采样率 , 其值为 AAUDIO_UNSPECIFIED ;
    • ② 默认行为 : 在默认状态下 , 如果打开 AAudio 音频流 , 系统会自动分配一个最佳值 ;
    • ③ 应用开发推荐 : 打开默认采样率的音频流后 , 调用 AAudioStream_getSampleRate() 可以获取当前 AAudio 音频流的采样率 , 我们可以根据该采样率进行采样 ;

    这也是我们推荐的做法 , 直接使用默认值即可 , 如果设置的值不合适 , 会造成 AAudio 音频流打开失败的后果 ;

    • ④ 出错情况 : 如果指定的采样率与音频设备不一致 , 打开音频流会失败 ;


    VII . AAudio 音频流 获取当前采样率 AAudioStream_getSampleRate



    AAudioStream_getSampleRate 简介 :

    • ① 函数原型 : 该函数用于获取当前 AAudio 音频流 采样率 ;
    AAUDIO_API int32_t AAudioStream_getSampleRate(
      AAudioStream *stream
    )
    
    • ② 代码示例 :
                // 获取音频流采样率
                sampleRate_ = AAudioStream_getSampleRate(playStream_);
    


    VIII . AAudio 音频流 每帧采样数



    设置每帧采样数 AAudioStreamBuilder_setSamplesPerFrame() :

    • ① 等同方法 : 该方法 与 AAudioStreamBuilder_setChannelCount() 方法 作用是相同的 ;
    • ② 每帧采样数 : 该值就是通道数 , 如果是单声道 , 每帧只有一个采样 , 如果是 双声道立体声 , 每帧有 2 个采样 ;
    • ③ 函数原型 :
    AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(
      AAudioStreamBuilder *builder,
      int32_t samplesPerFrame
    )
    

    获取每帧采样数 AAudioStream_getSamplesPerFrame :

    • ① 等同方法 : 该方法 与 AAudioStream_getChannelCount() 方法 作用是相同的 ;
    • ② 每帧采样数 : 该值就是通道数 , 如果是单声道 , 每帧只有一个采样 , 如果是 双声道立体声 , 每帧有 2 个采样 ;
    • ③ 函数原型 :
    AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(
      AAudioStream *stream
    )
    
    展开全文
  • AAudio 音频流创建流程 II . AAudio 音频流构建器 III . AAudio 音频流构建器 代码示例 IV . AAudio 音频流构建器创建方法 AAudio_createStreamBuilder V . AAudio 音频流 音频设备设置 VI . AAudio 音频流 音采样...
  • AAudio 音频流创建流程 II . AAudio 音频流构建器 设置音频设备 ID AAudioStreamBuilder_setDeviceId III . AAudio 音频设备 ID 获取 IV . AAudio 音频流 默认 音频设备设置 V . AAudio 音频流构建器 设置 音频流...
  • AAudio 音频流 创建 配置 使用 销毁 流程 II . AAudio 音频流 稳定状态 与 过渡状态 III . AAudio 音频流 状态改变 监听 IV . AAudio 音频流 状态改变 监听 实例 ( 暂停操作 ) V . AAudio 音频流 状态改变 监听 ...
  • AAudio进行音频采集的实现

    千次阅读 2020-01-15 15:38:30
    使用AAudio进行音频采集 前提 OpenSL ES对音频的采集方面有的不兼容,准备实现一个通过AAudio进行音频采集。我刚接触只是踩坑。 上网搜了很久,关于AAudio的资料不多。个人觉得参考NDK官网AAudio介绍和韩曙亮博主的...
  • Google官方网站: Audio Hal 适配: ...AAudio 上层应用使用说明:https://developer.android.google.cn/ndk/guides/audio/aaudio/aaudio AAudio API说明文档:https://developer.andr...
  • W/AudioStreamInternal_Client: onEventFromServer - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared E/AAudioStream: setState(1) tried to set to 9 but already DISCONNECTED E/AAudioStream: joinThread() -...
  • 内容转自:https://source.android.google.cn/devices/audio/aaudio 如有侵权,请告知删除。谢谢 ...AAudio 是 Android 8.0 版本中引入的一种音频 API。Android 8.1 版本提供了增强功能,在与支持 .
  • Android 耳返实践 OpenSL ES AAudio Oboe

    千次阅读 2019-02-27 19:39:49
    使用android系统底层的OpenSL ES或者AAudio都可以实现一个高性能的音频程序,尤其是AAudio更是简单易用,性能上,功能上都更佳,但是AAudio 是在 Android O 版本中才引入的全新 Android C API,在以前的系统版本中...
  • OpenSL ES™标准与Android Java框架中的MediaPlayer和MediaRecorderAPI提供类似的音频功能。OpenSL ES提供C语言接口和C++绑定,让您可以从使用任意一种...该封装容器在AAudio可用时对其进行调用,并在AAudio不可用时...
  • AAudio 音频流 读写操作 简介 II . AAudio 音频流 读写操作 阻塞时间设定 III . AAudio 音频流 读取 固定帧数 操作 注意点 IV . AAudio 音频流 写出音频数据 操作 注意点 V . AAudio 音频流 读取方法 AAudioStream_...
  • AAudio 音频流内部缓冲区 与 音频数据读写缓冲区 概念 II . AAudio 音频流内部缓冲区 缓冲区帧容量 BufferCapacityInFrames 与 缓冲区帧大小 BufferSizeInFrames 区分 III . AAudio 音频流内部缓冲区 缓冲区帧容量 ...
  • I . AAudio 音频流 缓冲区控制 II . AAudio 音频流 XRun ( UnderRun | OverRun ) III . AAudio 音频流 当前每次读写帧数 IV . AAudio 音频流 获取最大帧数 V . AAudio 音频流 设置缓冲区大小
  • 录音与播放示例, 以Android AAudio为例

    千次阅读 2019-09-10 21:40:02
    最近看了谷歌的codelabs介绍AAudio的示例, 写的还不错, 把主要问题介绍清楚了, 对音频这块感兴趣的可以看下. 当然实战还是推荐Oboe库. 1. 示例1, 主要讲了怎么创建一个声音合成器, 生成不同频率的正弦波. ...
  • I . 数据回调函数优先级 II . 数据回调函数 相关内容 III . 采样率 处理细节 IV . 数据回调函数 每次 采样个数 numFrames...V . 数据回调函数 缓冲区 ( AAudio 内部缓冲区 ) 调整 VI . AAudio 音频系统的线程安全性分析
  • I . AAudio 音频流 采样 缓冲 播放 的连续机制 II . AAudio 音频流 数据回调函数 函数指针类型定义 III . AAudio 音频流 数据回调函数 实现 IV . AAudio 音频流 数据回调函数 设置
  • OpenSL ES与AAudio简单介绍

    千次阅读 2019-11-19 17:38:47
    原因:在耳返功能时,如果使用AudioRecord和AudioTrack进行采集与播放会造成延迟问题,故需要涉及到openSL ES与AAudio的使用。 概况:OpenSL ES与AAudio简单理解为嵌入式跨平台免费的音频处理库,具有高性能,低...
  • Android Audio上层架构OpenSL ES、AAudio

    千次阅读 2017-11-16 13:42:31
    AAudio C - 26(8.0) android-audio-high-performance 注: android.hardware.audio.low_latency 并不一定要放到 android.hardware.audio.low_latency.xml 中,比如坚果P2就把其放到了 /etc/...
  • AAudio 1. 代码路径 1.1 source code path frameworks/av/media/libaaudio/include/aaudio/AAudio.h frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp frameworks/av/media/libaaudio/src/core/...
  • aaudio 1

    2017-07-10 00:16:00
    AAudio moves audio data between your app and the audio inputs and outputs on the device that’s running your app. Your app passes data in and out by reading from and writing to  audio streams , ...
  • Android AAudio源码分析(二) 文章目录Android AAudio源码分析(二)前言一、Start1.startStream分析总结 前言 在之前的章节中我们分析了AAudioService的启动以及openStream这个方法 本章将继续分析AAdioService...
  • <p>I opened an AAudio input stream with a data callback, and called <code>requestStart(), but the stream remained in <code>Starting</code> state and never proceeded to <code>Started</code> state. ...

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 348
精华内容 139
关键字:

aaUDIO