AAudio và MMAP

AAudio là API âm thanh được giới thiệu trong bản phát hành Android 8.0. Bản phát hành Android 8.1 có các cải tiến nhằm giảm độ trễ khi sử dụng cùng với HAL và trình điều khiển hỗ trợ MMAP. Tài liệu này mô tả lớp trừu tượng phần cứng (HAL) và các thay đổi trình điều khiển cần thiết để hỗ trợ tính năng MMAP của AAudio trong Android.

Hỗ trợ cho AAudio MMAP yêu cầu:

  • báo cáo khả năng MMAP của HAL
  • triển khai các chức năng mới trong HAL
  • tùy chọn triển khai ioctl() tùy chỉnh cho bộ đệm chế độ ĐỘC QUYỀN
  • cung cấp đường dẫn dữ liệu phần cứng bổ sung
  • thiết lập thuộc tính hệ thống kích hoạt tính năng MMAP

Kiến trúc âm thanh AA

AAudio là API C gốc mới cung cấp giải pháp thay thế cho Open SL ES. Nó sử dụng mẫu thiết kế Builder để tạo luồng âm thanh.

AAudio cung cấp đường dẫn dữ liệu có độ trễ thấp. Ở chế độ ĐỘC QUYỀN, tính năng này cho phép mã ứng dụng khách ghi trực tiếp vào bộ đệm được ánh xạ bộ nhớ được chia sẻ với trình điều khiển ALSA. Ở chế độ CHIA SẺ, bộ đệm MMAP được sử dụng bởi bộ trộn chạy trong Máy chủ Âm thanh. Ở chế độ ĐỘC QUYỀN, độ trễ thấp hơn đáng kể do dữ liệu bỏ qua bộ trộn.

Ở chế độ ĐỘC QUYỀN, dịch vụ yêu cầu bộ đệm MMAP từ HAL và quản lý tài nguyên. Bộ đệm MMAP đang chạy ở chế độ NOIRQ, do đó không có bộ đếm đọc/ghi chung để quản lý quyền truy cập vào bộ đệm. Thay vào đó, máy khách duy trì mô hình định thời của phần cứng và dự đoán khi nào bộ đệm sẽ được đọc.

Trong sơ đồ bên dưới, chúng ta có thể thấy dữ liệu điều chế mã xung (PCM) truyền qua MMAP FIFO vào trình điều khiển ALSA. Dấu thời gian được dịch vụ AAudio yêu cầu định kỳ và sau đó được chuyển đến mô hình thời gian của khách hàng thông qua hàng đợi tin nhắn nguyên tử.

Sơ đồ luồng dữ liệu PCM
Hình 1. Luồng dữ liệu PCM từ FIFO đến ALSA

Trong chế độ CHIA SẺ, một mô hình định giờ cũng được sử dụng nhưng nó nằm trong AAudioService.

Để thu âm, một mô hình tương tự được sử dụng nhưng dữ liệu PCM chảy theo hướng ngược lại.

thay đổi HAL

Đối với tinyALSA, hãy xem:

external/tinyalsa/include/tinyalsa/asoundlib.h
external/tinyalsa/include/tinyalsa/pcm.c
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);

Đối với HAL kế thừa, hãy xem:

hardware/libhardware/include/hardware/audio.h
hardware/qcom/audio/hal/audio_hw.c
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);

Đối với HAL âm thanh HIDL:

hardware/interfaces/audio/2.0/IStream.hal
hardware/interfaces/audio/2.0/types.hal
hardware/interfaces/audio/2.0/default/Stream.h
start() generates (Result retval);
stop() generates (Result retval) ;
createMmapBuffer(int32_t minSizeFrames)
       generates (Result retval, MmapBufferInfo info);
getMmapPosition()
       generates (Result retval, MmapPosition position);

Báo cáo hỗ trợ MMAP

Thuộc tính hệ thống "aaudio.mmap_policy" phải được đặt thành 2 (AAUDIO_POLICY_AUTO) để khung âm thanh biết rằng chế độ MMAP được HAL âm thanh hỗ trợ. (xem "Bật đường dẫn dữ liệu AAudio MMAP" bên dưới.)

Tệp audio_policy_configuration.xml cũng phải chứa cấu hình đầu ra và đầu vào cụ thể cho chế độ MMAP/NO IRQ để Trình quản lý chính sách âm thanh biết luồng nào sẽ mở khi máy khách MMAP được tạo:

<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>

Mở và đóng luồng MMAP

createMmapBuffer(int32_t minSizeFrames)
            generates (Result retval, MmapBufferInfo info);

Luồng MMAP có thể được mở và đóng bằng cách gọi các hàm Tinyalsa.

Truy vấn vị trí MMAP

Dấu thời gian được truyền trở lại Mô hình thời gian chứa vị trí khung và thời gian ĐƠN GIẢN tính bằng nano giây:

getMmapPosition()
        generates (Result retval, MmapPosition position);

HAL có thể lấy thông tin này từ trình điều khiển ALSA bằng cách gọi hàm Tinyalsa mới:

int pcm_mmap_get_hw_ptr(struct pcm* pcm,
                        unsigned int *hw_ptr,
                        struct timespec *tstamp);

Bộ mô tả tệp cho bộ nhớ dùng chung

Đường dẫn dữ liệu AAudio MMAP sử dụng vùng bộ nhớ được chia sẻ giữa phần cứng và dịch vụ âm thanh. Bộ nhớ dùng chung được tham chiếu bằng cách sử dụng bộ mô tả tệp được tạo bởi trình điều khiển ALSA.

Thay đổi hạt nhân

Nếu bộ mô tả tệp được liên kết trực tiếp với tệp trình điều khiển /dev/snd/ thì dịch vụ AAudio có thể sử dụng nó ở chế độ CHIA SẺ. Nhưng bộ mô tả không thể được chuyển tới mã máy khách cho chế độ ĐỘC QUYỀN. Bộ mô tả tệp /dev/snd/ sẽ cung cấp quyền truy cập quá rộng cho máy khách, do đó nó bị SELinux chặn.

Để hỗ trợ chế độ ĐỘC QUYỀN, cần phải chuyển đổi bộ mô tả /dev/snd/ thành bộ mô tả tệp anon_inode:dmabuf . SELinux cho phép bộ mô tả tệp đó được chuyển đến máy khách. Nó cũng có thể được AAudioService sử dụng.

Có thể tạo bộ mô tả tệp anon_inode:dmabuf bằng thư viện bộ nhớ Android Ion.

Để biết thêm thông tin, hãy xem các tài nguyên bên ngoài sau:

  1. "Bộ cấp phát bộ nhớ ION của Android" https://lwn.net/Articles/480055/
  2. "Tổng quan về Android ION" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "Tích hợp bộ cấp phát bộ nhớ ION" https://lwn.net/Articles/565469/

thay đổi HAL

Dịch vụ AAudio cần biết liệu anon_inode:dmabuf này có được hỗ trợ hay không. Trước Android 10.0, cách duy nhất để làm điều đó là chuyển kích thước của bộ đệm MMAP dưới dạng số âm, ví dụ: -2048 thay vì 2048, nếu được hỗ trợ. Trong Android 10.0 trở lên, bạn có thể đặt cờ AUDIO_MMAP_APPLICATION_SHAREABLE .

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Thay đổi hệ thống con âm thanh

AAudio yêu cầu một đường dẫn dữ liệu bổ sung ở giao diện người dùng âm thanh của hệ thống con âm thanh để nó có thể hoạt động song song với đường dẫn AudioFlinger ban đầu. Đường dẫn kế thừa đó được sử dụng cho tất cả âm thanh hệ thống và âm thanh ứng dụng khác. Chức năng này có thể được cung cấp bởi bộ trộn phần mềm trong DSP hoặc bộ trộn phần cứng trong SOC.

Kích hoạt đường dẫn dữ liệu AAudio MMAP

AAudio sẽ sử dụng đường dẫn dữ liệu AudioFlinger cũ nếu MMAP không được hỗ trợ hoặc không mở được luồng. Vì vậy AAudio sẽ hoạt động với thiết bị âm thanh không hỗ trợ đường dẫn MMAP/NOIRQ.

Khi kiểm tra khả năng hỗ trợ MMAP cho AAudio, điều quan trọng là phải biết liệu bạn đang thực sự kiểm tra đường dẫn dữ liệu MMAP hay chỉ đang kiểm tra đường dẫn dữ liệu cũ. Phần sau đây mô tả cách bật hoặc buộc các đường dẫn dữ liệu cụ thể và cách truy vấn đường dẫn được luồng sử dụng.

Thuộc tính hệ thống

Bạn có thể đặt chính sách MMAP thông qua thuộc tính hệ thống:

  • 1 = AAUDIO_POLICY_NEVER - Chỉ sử dụng đường dẫn cũ. Thậm chí đừng cố sử dụng MMAP.
  • 2 = AAUDIO_POLICY_AUTO - Hãy thử sử dụng MMAP. Nếu điều đó không thành công hoặc không có sẵn thì hãy sử dụng đường dẫn cũ.
  • 3 = AAUDIO_POLICY_ALWAYS - Chỉ sử dụng đường dẫn MMAP. Đừng quay trở lại con đường cũ.

Chúng có thể được đặt trong Makefile của thiết bị, như vậy:

# 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

Bạn cũng có thể ghi đè các giá trị này sau khi thiết bị đã khởi động. Bạn sẽ cần phải khởi động lại máy chủ âm thanh để thay đổi có hiệu lực. Ví dụ: để bật chế độ AUTO cho MMAP:

adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver

Có các hàm được cung cấp trong ndk/sysroot/usr/include/aaudio/AAudioTesting.h cho phép bạn ghi đè chính sách sử dụng đường dẫn MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Để tìm hiểu xem luồng có đang sử dụng đường dẫn MMAP hay không, hãy gọi:

bool AAudioStream_isMMapUsed(AAudioStream* stream);