AAudio và MMAP

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

Tính năng 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
  • tuỳ chọn triển khai ioctl() tuỳ chỉnh cho vùng đệm chế độ ĐỘC QUYỀN
  • cung cấp đường dẫn dữ liệu phần cứng bổ sung
  • đặt thuộc tính hệ thống để bật tính năng MMAP

Cấu trúc AAudio

AAudio là một API C gốc mới cung cấp giải pháp thay thế cho Open SL ES. Chiến dịch này sử dụng một Mẫu thiết kế trình tạo để 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ã xử lý ứng dụng ghi trực tiếp vào vùng đệm được ánh xạ bộ nhớ được chia sẻ với trình điều khiển ALSA. Ở chế độ CHIA SẺ, vùng đệm MMAP được sử dụng bởi một bộ trộn đang chạy trong AudioServer. Ở chế độ ĐỘC QUYỀN, độ trễ là ít hơn đáng kể vì dữ liệu bỏ qua trình kết hợp.

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

Trong sơ đồ dưới đây, chúng ta có thể thấy luồng dữ liệu Điều biến mã xung (PCM) xuống thông qua MMAP FIFO vào trình điều khiển ALSA. Dấu thời gian được cung cấp định kỳ do dịch vụ AAudio yêu cầu rồi chuyển sang mô hình thời gian của ứng dụng thông qua hàng đợi thông báo ở cấp độ nguyên tử.

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

Ở chế độ CHIA SẺ, mô hình thời gian cũng được sử dụng nhưng nằm trong AAudioService.

Để ghi âm, mô hình tương tự được sử dụng, nhưng dữ liệu PCM lưu chuyển trong 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 cũ, 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 lớp trừu tượng phần cứng (HAL) cho â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 đặt thành 2 (AAUDIO_POLICY_auto) để khung âm thanh biết rằng chế độ MMAP được hỗ trợ bởi HAL âm thanh. (xem "Bật đường dẫn dữ liệu MMAP của AAudio" below.)

Tệp audio_policy_configuration.xml cũng phải chứa một dữ liệu đầu ra và đầu vào cấu hình 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 ứng dụng 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);

Bạn có thể mở và đóng luồng MMAP bằng cách gọi các hàm Tinyalsa.

Vị trí MMAP của truy vấn

Dấu thời gian được chuyển lại về Mô hình thời gian chứa vị trí khung hình và Thời gian MONOTONIC 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:

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

Chỉ số mô tả tệp cho bộ nhớ dùng chung

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

Thay đổi về kernel

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

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

Bạn có thể tạo chỉ số mô tả tệp anon_inode:dmabuf bằng cách sử dụ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. "Trình phân bổ bộ nhớ ION cho Android" https://lwn.net/Articles/480055/
  2. "Tổng quan về ION Android" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "Tích hợp trình phân bổ bộ nhớ ION" https://lwn.net/Articles/565469/

Thay đổi HAL

Dịch vụ AAudio cần biết anon_inode:dmabuf này có được hỗ trợ. Trước Android 10.0, cách duy nhất để làm điều đó là truyền kích thước của MMAP vùng đệm 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 về hệ thống âm thanh con

AAudio cần có thêm đường dẫn dữ liệu ở giao diện người dùng âm thanh của bản âm thanh hệ thống con để có thể hoạt động song song với đường dẫn AudioFlinger ban đầu. Đường dẫn cũ đó đượ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 bộ trộn phần mềm trong DSP hoặc phần cứng cung cấp trong SOC.

Bật đường dẫn dữ liệu MMAP của AAudio

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 thể mở 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, bạn cần phải biết liệu bạn thực sự kiểm thử đường dẫn dữ liệu MMAP hoặc chỉ kiểm thử đường dẫn dữ liệu cũ. Chiến lược phát hành đĩa đơn nội dung sau đây mô tả cách bật hoặc buộc các đường dẫn dữ liệu cụ thể cũng như cách truy vấn đường dẫn mà 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 các 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 – Thử sử dụng MMAP. Nếu không dùng được hoặc không dùng được cách đó, thì hãy sử dụng đường dẫn cũ.
  • 3 = AAUDIO_POLICY_ALWAYS – Chỉ sử dụng đường dẫn MMAP. Không quay lại trải nghiệm cũ đường dẫn.

Bạn có thể thiết lập các tệp này trong tệp Makefile của thiết bị, chẳng hạn như:

# 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 khởi động thiết bị. Bạn cần khởi động lại máy chủ âm thanh để thay đổi này có hiệu lực. Ví dụ: để bật chế độ TỰ ĐỘNG 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 để giú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 một luồng có đang sử dụng đường dẫn MMAP hay không, hãy gọi:

bool AAudioStream_isMMapUsed(AAudioStream* stream);