| <html devsite> |
| <head> |
| <title>AAudio and 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 is an audio API introduced in the Android 8.0 release. The Android 8.1 |
| release has enhancements to reduce latency when used in conjunction with a HAL |
| and driver that support MMAP. This document describes the hardware abstraction |
| layer (HAL) and driver changes needed to support AAudio's MMAP feature in |
| Android. |
| </p> |
| |
| <p> |
| Support for AAudio MMAP requires: |
| </p> |
| |
| <ul> |
| <li>reporting the MMAP capabilities of the HAL |
| <li>implementing new functions in the HAL |
| <li>optionally implementing a custom ioctl() for the EXCLUSIVE mode buffer |
| <li>providing an additional hardware data path</li> |
| <li>setting system properties that enable MMAP feature</li> |
| </ul> |
| |
| <aside class="note"><strong>Note:</strong> Enable the MMAP feature |
| only on Android O MR1 or higher.</aside> |
| |
| <h2 id="aaudio-architecture">AAudio architecture</h2> |
| |
| <p> |
| <a |
| href="https://developer.android.com/ndk/guides/audio/aaudio/aaudio.html">AAudio</a> |
| is a new native C API that provides an alternative to Open SL ES. It uses a |
| Builder design pattern to create audio streams. |
| </p> |
| |
| <p> |
| AAudio provides a low-latency data path. In EXCLUSIVE mode, the feature |
| allows client application code to write directly into a memory mapped buffer |
| that is shared with the ALSA driver. In SHARED mode, the MMAP buffer is used by |
| a mixer running in the AudioServer. In EXCLUSIVE mode, the latency is |
| significantly less because the data bypasses the mixer. |
| </p> |
| |
| <p> |
| In EXCLUSIVE mode, the service requests the MMAP buffer from the HAL and manages |
| the resources. The MMAP buffer is running in NOIRQ mode, so there are no shared |
| read/write counters to manage access to the buffer. Instead, the client |
| maintains a timing model of the hardware and predicts when the buffer will be |
| read. |
| </p> |
| |
| <p> |
| In the diagram below, we can see the Pulse-code modulation (PCM) data flowing |
| down through the MMAP FIFO into the ALSA driver. Timestamps are periodically |
| requested by the AAudio service and then passed up to the client's timing model |
| through an atomic message queue. |
| </p> |
| |
| <figure id="pcm-data-flow"> |
| <img src="/devices/audio/images/pcm_data_flow.png" |
| width="630" |
| alt="PCM data flow diagram."> |
| <figcaption><b>Figure 1.</b> PCM data flow through FIFO to ALSA</figcaption> |
| </figure> |
| |
| <p> |
| In SHARED mode, a timing model is also used, but it lives in the AAudioService. |
| </p> |
| |
| <p> |
| For audio capture, a similar model is used, but the PCM data flows in the |
| opposite direction. |
| </p> |
| |
| <h2 id="hal-changes">HAL changes</h2> |
| <p> |
| For tinyALSA see: |
| </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> |
| For the legacy HAL, see: |
| </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> |
| For HIDL audio 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">Reporting MMAP support</h3> |
| |
| <p> |
| System property "aaudio.mmap_policy" should be set to 2 (AAUDIO_POLICY_AUTO) so |
| the audio framework knows that MMAP mode is supported by the audio HAL. (see |
| "Enabling AAudio MMAP Data Path" below.) |
| </p> |
| |
| <p> |
| audio_policy_configuration.xml file must also contain an output and input |
| profile specific to MMAP/NO IRQ mode so that the Audio Policy Manager knows |
| which stream to open when MMAP clients are created: |
| </p> |
| |
| <pre |
| class="prettyprint"> |
| <mixPort name="mmap_no_irq_out" role="source" |
| flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ"> |
| <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" |
| samplingRates="48000" |
| channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> |
| </mixPort> |
| |
| <mixPort name="mmap_no_irq_in" role="sink" flags="AUDIO_INPUT_FLAG_MMAP_NOIRQ"> |
| <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" |
| samplingRates="48000" |
| channelMasks="AUDIO_CHANNEL_IN_STEREO"/> |
| </mixPort> |
| </pre> |
| |
| <h3 id="opening-and-closing-an-mmap-stream">Opening and closing an MMAP |
| stream</h3> |
| |
| <pre |
| class="prettyprint"> |
| createMmapBuffer(int32_t minSizeFrames) |
| generates (Result retval, MmapBufferInfo info); |
| </pre> |
| |
| <p> |
| The MMAP stream can be opened and closed by calling Tinyalsa functions. |
| </p> |
| |
| <h3 id="querying-mmap-position">Querying MMAP position</h3> |
| |
| <p> |
| The timestamp passed back to the Timing Model contains a frame position and a |
| MONOTONIC time in nanoseconds: |
| </p> |
| |
| <pre |
| class="prettyprint"> |
| getMmapPosition() |
| generates (Result retval, MmapPosition position); |
| </pre> |
| |
| <p> |
| The HAL can obtain this information from the ALSA driver by calling a new |
| Tinyalsa function: |
| </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">Kernel changes</h2> |
| |
| <p> |
| Enable MMAP/NOIRQ mode in the driver. |
| </p> |
| |
| <p> |
| The shared memory is referenced using a file descriptor that is generated by the |
| ALSA driver. If the file descriptor is directly associated with a |
| <code>/dev/snd/</code> driver file, then it can be used by the AAudio service in |
| SHARED mode. But the descriptor cannot be passed to the client code for |
| EXCLUSIVE mode. The <code>/dev/snd/</code> file descriptor would provide too |
| broad of access to the client, so it is blocked by SELinux. |
| </p> |
| |
| <p> |
| In order to support EXCLUSIVE mode, it is necessary to convert the |
| <code>/dev/snd/</code> descriptor to an <code>anon_inode:dmabuffer</code> file |
| descriptor. SELinux allows that file descriptor to be passed to the client. It |
| can also be used by the AAudioService. |
| </p> |
| |
| <p> |
| An <code>anon_inode:dmabuffer</code> file descriptor can be generated using the |
| Android Ion memory library. |
| </p> |
| |
| <p> |
| For additional information, see these external resources: |
| </p> |
| |
| <ol> |
| <li>"The Android ION memory allocator" <a |
| href="https://lwn.net/Articles/480055/">https://lwn.net/Articles/480055/</a> |
| <li>"Android ION overview" <a |
| href="https://wiki.linaro.org/BenjaminGaignard/ion">https://wiki.linaro.org/BenjaminGaignard/ion</a> |
| <li>"Integrating the ION memory allocator" <a |
| href="https://lwn.net/Articles/565469/">https://lwn.net/Articles/565469/</a></li> |
| </ol> |
| |
| <p> |
| The AAudio service needs to know if this <code>anon_inode:dmabuffer</code> is |
| supported. Currently, the only way to do that is to pass the size of the MMAP |
| buffer as a negative number, eg. -2048 instead of 2048, if supported. A better |
| way of reporting this without having to open the stream is planned. |
| </p> |
| |
| <h2 id="audio-subsystem-changes">Audio subsystem changes</h2> |
| |
| <p> |
| AAudio requires an additional data path at the audio front end of the audio |
| subsystem so it can operate in parallel with the original AudioFlinger path. |
| That legacy path is used for all other system sounds and application sounds. |
| This functionality could be provided by a software mixer in a DSP or a hardware |
| mixer in the SOC. |
| </p> |
| |
| <h2 id="enabling-aaudio-mmap-data-path">Enabling AAudio MMAP Data Path</h2> |
| |
| <p> |
| AAudio will use the legacy AudioFlinger data path if MMAP is not supported or |
| fails to open a stream. So AAudio will work with an audio device that does not |
| support MMAP/NOIRQ path. |
| </p> |
| |
| <p> |
| When testing MMAP support for AAudio, it is important to know whether you are |
| actually testing the MMAP data path or merely testing the legacy data path. The |
| following describes how to enable or force specific data paths, and how to query |
| the path used by a stream. |
| </p> |
| |
| <h3 id="system-properties">System properties</h3> |
| |
| <p> |
| You can set the MMAP policy through system properties: |
| </p> |
| |
| <ul> |
| <li>1 = AAUDIO_POLICY_NEVER - Only use legacy path. Do not even try to use MMAP. |
| <li>2 = AAUDIO_POLICY_AUTO - Try to use MMAP. If that fails or is not available, |
| then use legacy path. |
| <li>3 = AAUDIO_POLICY_ALWAYS - Only use MMAP path. Do not fall back to legacy |
| path.</li> |
| </ul> |
| |
| <p> |
| These may be set in the devices Makefile, like so: |
| </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> |
| You can also override these values after the device has booted. |
| You will need to restart the audioserver for the change to take effect. |
| For example, to enable AUTO mode for 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> |
| There are functions provided in |
| <code>ndk/sysroot/usr/include/aaudio/AAudioTesting.h</code> that allow you to |
| override the policy for using MMAP path: |
| </p> |
| |
| <pre |
| class="prettyprint">aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy); |
| </pre> |
| |
| <p> |
| To find out whether a stream is using MMAP path, call: |
| </p> |
| |
| <pre |
| class="prettyprint">bool AAudioStream_isMMapUsed(AAudioStream* stream); |
| </pre> |
| </body> |
| </html> |