aboutsummaryrefslogtreecommitdiff
path: root/zh-cn/devices/audio/aaudio.html
blob: 06195d65693923e253658a037a6f25d4f258da20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
<html devsite><head>
    <title>AAudio 和 MMAP</title>
    <meta name="project_path" value="/_project.yaml"/>
    <meta name="book_path" value="/_book.yaml"/>
  </head>
  <body>
  <!--
      Copyright 2017 The Android Open Source Project

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
  -->

<p>
AAudio 是在 Android 8.0 版本中引入的一种音频 API。Android 8.1 版本具有增强功能,可在与支持 MMAP 的 HAL 和驱动程序结合使用时缩短延迟时间。本文档说明了需要进行哪些硬件抽象层 (HAL) 及驱动程序方面的更改才能在 Android 中支持 AAudio 的 MMAP 功能。
</p>

<p>
要支持 AAudio MMAP,须执行以下操作:</p>

<ul>
<li>报告 HAL 的 MMAP 功能</li><li>在 HAL 中实现新功能</li><li>为“专有”模式缓冲区实现自定义 ioctl()(可选)</li><li>提供一个额外的硬件数据路径</li>
<li>设置用于启用 MMAP 功能的系统属性</li>
</ul>

<aside class="note"><strong>注意:</strong>请仅在 Android O MR1 或更高版本上启用 MMAP 功能。</aside>

<h2 id="aaudio-architecture">AAudio 架构</h2>

<p>
<a href="https://developer.android.com/ndk/guides/audio/aaudio/aaudio.html">AAudio</a> 是一种新的本地 C API,可提供 Open SL ES 的替代方案。它使用“生成器”设计模式来创建音频流。
</p>

<p>
AAudio 提供了一个低延迟数据路径。在“专有”模式下,该功能可让客户端应用代码直接写入到与 ALSA 驱动程序共享的内存映射缓冲区。在“共享”模式下,MMAP 缓冲区由在 AudioServer 中运行的混音器使用。在“专有”模式下,由于数据会绕过混音器,延迟时间会明显缩短。
</p>

<p>
在“专有”模式下,服务可从 HAL 请求 MMAP 缓冲区并管理资源。MMAP 缓冲区会在 NOIRQ 模式下运行,因此没有共享的读/写计数器可用来管理对缓冲区的访问权限。相反,客户端会维护硬件的计时模型,并预测将在何时读取缓冲区。
</p>

<p>
在下图中,我们可以看到脉冲编码调制 (PCM) 数据通过 MMAP FIFO 向下流入 ALSA 驱动程序。AAudio 服务会定期请求时间戳,然后通过原子消息队列将其传递给客户端的计时模型。
</p>

<figure id="pcm-data-flow">
  <img src="/devices/audio/images/pcm_data_flow.png" width="630" alt="PCM 数据流图。"/>
  <figcaption><b>图 1.</b> PCM 数据通过 FIFO 流向 ALSA</figcaption>
</figure>

<p>
在“共享”模式下,也会用到计时模型,但它位于 AAudioService 中。
</p>

<p>
在音频捕获过程中,会用到类似的模型,但 PCM 数据会以相反方向流动。
</p>

<h2 id="hal-changes">HAL 变更</h2>
<p>
对于 tinyALSA,请参阅:</p>

<pre class="prettyprint">
external/tinyalsa/include/tinyalsa/asoundlib.h
external/tinyalsa/include/tinyalsa/pcm.c
</pre>

<pre class="prettyprint">
int pcm_start(struct pcm *pcm);
int pcm_stop(struct pcm *pcm);
int pcm_mmap_begin(struct pcm *pcm, void **areas,
           unsigned int *offset,
           unsigned int *frames);
int pcm_get_poll_fd(struct pcm *pcm);
int pcm_mmap_commit(struct pcm *pcm, unsigned int offset,
           unsigned int frames);
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr,
           struct timespec *tstamp);
</pre>

<p>
对于旧版 HAL,请参阅:</p>

<pre class="prettyprint">
hardware/libhardware/include/hardware/audio.h
hardware/qcom/audio/hal/audio_hw.c
</pre>

<pre class="prettyprint">
int start(const struct audio_stream_out* stream);
int stop(const struct audio_stream_out* stream);
int create_mmap_buffer(const struct audio_stream_out *stream,
                        int32_t min_size_frames,
                        struct audio_mmap_buffer_info *info);
int get_mmap_position(const struct audio_stream_out *stream,
                        struct audio_mmap_position *position);
</pre>

<p>
对于 HIDL 音频 HAL:</p>

<pre class="prettyprint">
hardware/interfaces/audio/2.0/IStream.hal
hardware/interfaces/audio/2.0/types.hal
hardware/interfaces/audio/2.0/default/Stream.h
</pre>

<pre class="prettyprint">
start() generates (Result retval);
stop() generates (Result retval) ;
createMmapBuffer(int32_t minSizeFrames)
       generates (Result retval, MmapBufferInfo info);
getMmapPosition()
       generates (Result retval, MmapPosition position);
</pre>

<h3 id="reporting-mmap-support">报告 MMAP 支持</h3>

<p>
系统属性“aaudio.mmap_policy”应设置为 2 (AAUDIO_POLICY_AUTO),以便音频框架知道音频 HAL 支持 MMAP 模式(请参阅下面的“启用 AAudio MMAP 数据路径”)。
</p>

<p>
audio_policy_configuration.xml 文件还必须包含特定于 MMAP/NO IRQ 模式的输出和输入配置文件,以便音频政策管理器得知要在创建 MMAP 客户端时打开的流:</p>

<pre class="prettyprint">
&lt;mixPort name="mmap_no_irq_out" role="source"
            flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ"&gt;
            &lt;profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                                samplingRates="48000"
                                channelMasks="AUDIO_CHANNEL_OUT_STEREO"/&gt;
&lt;/mixPort&gt;

&lt;mixPort name="mmap_no_irq_in" role="sink" flags="AUDIO_INPUT_FLAG_MMAP_NOIRQ"&gt;
            &lt;profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                                samplingRates="48000"
                                channelMasks="AUDIO_CHANNEL_IN_STEREO"/&gt;
&lt;/mixPort&gt;
</pre>

<h3 id="opening-and-closing-an-mmap-stream">打开和关闭 MMAP 流</h3>

<pre class="prettyprint">
createMmapBuffer(int32_t minSizeFrames)
            generates (Result retval, MmapBufferInfo info);
</pre>

<p>
调用 Tinyalsa 函数可以打开和关闭 MMAP 流。
</p>

<h3 id="querying-mmap-position">查询 MMAP 位置</h3>

<p>
传回到计时模型的时间戳包含帧位置和 MONOTONIC 时间(以纳秒为单位):</p>

<pre class="prettyprint">
getMmapPosition()
        generates (Result retval, MmapPosition position);
</pre>

<p>
HAL 可以通过调用新 Tinyalsa 函数从 ALSA 驱动程序中获取此信息:</p>

<pre class="prettyprint">
int pcm_mmap_get_hw_ptr(struct pcm* pcm,
                        unsigned int *hw_ptr,
                        struct timespec *tstamp);
</pre>

<h2 id="kernel-changes">内核变更</h2>

<p>
在驱动程序中启用 MMAP/NOIRQ 模式。
</p>

<p>
您可以使用由 ALSA 驱动程序生成的文件描述符引用共享内存。如果文件描述符直接与 <code>/dev/snd/</code> 驱动程序文件关联,则可用于“共享”模式下的 AAudio 服务。但是对于“专有”模式,则无法将此描述符传递给客户端代码。<code>/dev/snd/</code> 文件描述符会提供过于宽泛的客户端访问权限,因此 SELinux 会将其屏蔽。
</p>

<p>
要支持“专有”模式,需将 <code>/dev/snd/</code> 描述符转换为 <code>anon_inode:dmabuffer</code> 文件描述符。SELinux 允许将此文件描述符传递给客户端。该文件描述符也可用于 AAudioService。
</p>

<p>
可使用 Android Ion 内存库生成 <code>anon_inode:dmabuffer</code> 文件描述符。
</p>

<p>
有关更多信息,请参阅下列外部资源:</p>

<ol>
<li>“Android ION 内存分配器”<a href="https://lwn.net/Articles/480055/">https://lwn.net/Articles/480055/</a>
</li><li>“Android ION 概览”<a href="https://wiki.linaro.org/BenjaminGaignard/ion">https://wiki.linaro.org/BenjaminGaignard/ion</a>
</li><li>“集成 ION 内存分配器”<a href="https://lwn.net/Articles/565469/">https://lwn.net/Articles/565469/</a></li>
</ol>

<p>
AAudio 服务需要了解 <code>anon_inode:dmabuffer</code> 是否受支持。目前,验证是否受支持的唯一方法是将 MMAP 缓冲区大小传递为负数(例如 -2048 而非 2048;如果支持的话)。我们已经在规划不必打开流即可报告此信息的更佳方法。
</p>

<h2 id="audio-subsystem-changes">音频子系统变更</h2>

<p>
AAudio 在音频子系统的音频前端需要一个额外的数据路径,以便它可以与原始 AudioFlinger 路径并行运行。旧路径用于所有其他系统声音和应用声音。该功能可由 DSP 中的软件混音器或由 SOC 中的硬件混音器提供。</p>

<h2 id="enabling-aaudio-mmap-data-path">启用 AAudio MMAP 数据路径</h2>

<p>
如果 MMAP 不受支持或无法打开流,AAudio 将使用旧的 AudioFlinger 数据路径。因此,AAudio 将可用于不支持 MMAP/NOIRQ 路径的音频设备。
</p>

<p>
在测试 AAudio 的 MMAP 支持功能时,了解您是在实际测试 MMAP 数据路径还是仅测试旧的数据路径这一点尤为重要。下面介绍了如何启用或强制执行特定数据路径,以及如何查询流所使用的路径。
</p>

<h3 id="system-properties">系统属性</h3>

<p>
您可以通过系统属性设置 MMAP 政策:</p>

<ul>
<li>1 = AAUDIO_POLICY_NEVER - 仅使用旧的路径,甚至不要尝试使用 MMAP。</li><li>2 = AAUDIO_POLICY_AUTO - 尝试使用 MMAP。如果此操作失败或 MMAP 不可用,则使用旧路径。
</li><li>3 = AAUDIO_POLICY_ALWAYS - 仅使用 MMAP 路径。不返回到旧路径。</li>
</ul>

<p>
您可以在设备 Makefile 中设置这些项,如下所示:</p>

<pre class="prettyprint">
# Enable AAudio MMAP/NOIRQ data path.
# 2 is AAUDIO_POLICY_AUTO so it will try MMAP then fallback to Legacy path.
PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_policy=2
# Allow EXCLUSIVE then fall back to SHARED.
PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_exclusive_policy=2
</pre>

<p>
您也可以在设备启动后替换这些值。
您需要重新启动 audioserver 才能使更改生效。
例如,要为 MMAP 启用“自动”模式,请输入以下内容:
</p>

<pre class="devsite-terminal devsite-click-to-copy">
adb root
</pre>
<pre class="devsite-terminal devsite-click-to-copy">
adb shell setprop aaudio.mmap_policy 2
</pre>
<pre class="devsite-terminal devsite-click-to-copy">
adb shell killall audioserver
</pre>

<p>
<code>ndk/sysroot/usr/include/aaudio/AAudioTesting.h</code> 中提供了一些函数,可让您替换使用 MMAP 路径的政策:
</p>

<pre class="prettyprint">aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
</pre>

<p>
要了解某个流是否使用 MMAP 路径,请调用:
</p>

<pre class="prettyprint">bool AAudioStream_isMMapUsed(AAudioStream* stream);
</pre>

</body></html>