AAudio เป็น API เสียงที่เปิดตัวใน Android 8.0 Android 8.1 รุ่นมีการปรับปรุงเพื่อลดเวลาแฝงเมื่อใช้ร่วมกับ HAL และไดรเวอร์ที่รองรับ MMAP เอกสารนี้อธิบายเกี่ยวกับ hardware abstraction layer (HAL) และการเปลี่ยนแปลงไดรเวอร์ที่จำเป็นต่อการสนับสนุนคุณลักษณะ MMAP ของ AAudio ใน Android
การสนับสนุน AAudio MMAP ต้องการ:
- การรายงานความสามารถ MMAP ของ HAL
- การใช้ฟังก์ชันใหม่ในHAL
- เลือกใช้ ioctl() แบบกำหนดเองสำหรับบัฟเฟอร์โหมด EXCLUSIVE
- ให้เส้นทางข้อมูลฮาร์ดแวร์เพิ่มเติม
- การตั้งค่าคุณสมบัติของระบบที่เปิดใช้งานคุณสมบัติ MMAP
สถาปัตยกรรม AAudio
AAudio เป็น C API แบบเนทีฟใหม่ที่ให้ทางเลือกแก่ Open SL ES ใช้รูปแบบการออกแบบของ Builder เพื่อสร้างสตรีมเสียง
AAudio ให้เส้นทางข้อมูลที่มีความหน่วงต่ำ ในโหมด EXCLUSIVE คุณลักษณะนี้อนุญาตให้โค้ดแอปพลิเคชันไคลเอ็นต์เขียนลงในบัฟเฟอร์ที่แมปหน่วยความจำโดยตรงซึ่งแชร์กับไดรเวอร์ ALSA ในโหมด SHARED บัฟเฟอร์ MMAP ถูกใช้โดยมิกเซอร์ที่ทำงานอยู่ใน AudioServer ในโหมด EXCLUSIVE เวลาแฝงจะน้อยลงอย่างมากเนื่องจากข้อมูลข้ามตัวผสม
ในโหมด EXCLUSIVE บริการร้องขอบัฟเฟอร์ MMAP จาก HAL และจัดการทรัพยากร บัฟเฟอร์ MMAP กำลังทำงานในโหมด NOIRQ ดังนั้นจึงไม่มีตัวนับการอ่าน/เขียนที่ใช้ร่วมกันเพื่อจัดการการเข้าถึงบัฟเฟอร์ ไคลเอ็นต์จะรักษาโมเดลเวลาของฮาร์ดแวร์และคาดการณ์ว่าเมื่อใดที่บัฟเฟอร์จะถูกอ่าน
ในแผนภาพด้านล่าง เราจะเห็นข้อมูลการมอดูเลตรหัสพัลส์ (PCM) ที่ไหลลงมาผ่าน MMAP FIFO ไปยังไดรเวอร์ ALSA บริการ AAudio จะร้องขอการประทับเวลาเป็นระยะๆ จากนั้นจึงส่งต่อไปยังโมเดลเวลาของไคลเอ็นต์ผ่านคิวข้อความปรมาณู

ในโหมด SHARED โมเดลเวลาก็ใช้เช่นกัน แต่อยู่ใน AAudioService
สำหรับการบันทึกเสียงจะใช้โมเดลที่คล้ายกัน แต่ข้อมูล PCM จะไหลไปในทิศทางตรงกันข้าม
การเปลี่ยนแปลง HAL
สำหรับ tinyALSA โปรดดูที่:
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);
สำหรับ HAL รุ่นเก่า โปรดดู:
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);
สำหรับเสียง HIDL HAL:
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);
การรายงานการสนับสนุน MMAP
คุณสมบัติของระบบ "aaudio.mmap_policy" ควรตั้งค่าเป็น 2 (AAUDIO_POLICY_AUTO) เพื่อให้กรอบงานเสียงรู้ว่าโหมด MMAP ได้รับการสนับสนุนโดย HAL เสียง (ดู "การเปิดใช้งานเส้นทางข้อมูล AAudio MMAP" ด้านล่าง)
ไฟล์ audio_policy_configuration.xml ต้องมีโปรไฟล์เอาต์พุตและอินพุตเฉพาะสำหรับโหมด MMAP/NO IRQ เพื่อให้โปรแกรมจัดการนโยบายเสียงรู้ว่าควรเปิดสตรีมใดเมื่อสร้างไคลเอ็นต์ MMAP:
<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>
การเปิดและปิดสตรีม MMAP
createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info);
สตรีม MMAP สามารถเปิดและปิดได้โดยการเรียกใช้ฟังก์ชัน Tinyalsa
สอบถามตำแหน่ง MMAP
การประทับเวลาที่ส่งกลับไปยัง Timing Model มีตำแหน่งเฟรมและเวลา MONOTONIC ในหน่วยนาโนวินาที:
getMmapPosition() generates (Result retval, MmapPosition position);
HAL สามารถรับข้อมูลนี้จากไดรเวอร์ ALSA ได้โดยการเรียกใช้ฟังก์ชัน Tinyalsa ใหม่:
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
ตัวอธิบายไฟล์สำหรับหน่วยความจำที่ใช้ร่วมกัน
เส้นทางข้อมูล AAudio MMAP ใช้ขอบเขตหน่วยความจำที่ใช้ร่วมกันระหว่างฮาร์ดแวร์และบริการเสียง มีการอ้างอิงหน่วยความจำที่ใช้ร่วมกันโดยใช้ตัวอธิบายไฟล์ที่สร้างขึ้นโดยไดรเวอร์ ALSA
การเปลี่ยนแปลงเคอร์เนล
หาก file descriptor เกี่ยวข้องโดยตรงกับไฟล์ไดรเวอร์ /dev/snd/
บริการ AAudio ก็สามารถใช้บริการนี้ได้ในโหมด SHARED แต่ตัวอธิบายไม่สามารถส่งผ่านไปยังรหัสไคลเอ็นต์สำหรับโหมด EXCLUSIVE ได้ ตัวอธิบายไฟล์ /dev/snd/
จะให้การเข้าถึงไคลเอ็นต์ได้กว้างเกินไป ดังนั้นจึงถูกบล็อกโดย SELinux
เพื่อรองรับโหมด EXCLUSIVE จำเป็นต้องแปลง descriptor /dev/snd/
เป็น anon_inode:dmabuf
file descriptor SELinux อนุญาตให้ส่งตัวอธิบายไฟล์นั้นไปยังไคลเอนต์ นอกจากนี้ยังสามารถใช้งานได้โดย AAudioService
ตัวอธิบายไฟล์ anon_inode:dmabuf
สามารถสร้างได้โดยใช้ไลบรารีหน่วยความจำ Android Ion
สำหรับข้อมูลเพิ่มเติม โปรดดูแหล่งข้อมูลภายนอกเหล่านี้:
- "ตัวจัดสรรหน่วยความจำ Android ION" https://lwn.net/Articles/480055/
- "ภาพรวม Android ION" https://wiki.linaro.org/BenjaminGaignard/ion
- "การรวมตัวจัดสรรหน่วยความจำ ION" https://lwn.net/Articles/565469/
การเปลี่ยนแปลง HAL
บริการ AAudio จำเป็นต้องทราบว่า anon_inode:dmabuf
หรือไม่ ก่อน Android 10.0 วิธีเดียวที่จะทำได้คือส่งผ่านขนาดของบัฟเฟอร์ MMAP เป็นจำนวนลบ เช่น -2048 แทนที่จะเป็น 2048 หากได้รับการสนับสนุน ใน Android 10.0 และใหม่กว่า คุณสามารถตั้งค่าสถานะ AUDIO_MMAP_APPLICATION_SHAREABLE
mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;
การเปลี่ยนแปลงระบบย่อยเสียง
AAudio ต้องการพาธข้อมูลเพิ่มเติมที่ส่วนหน้าของเสียงของระบบย่อยของเสียง เพื่อให้สามารถทำงานควบคู่ไปกับเส้นทาง AudioFlinger ดั้งเดิมได้ เส้นทางเดิมนั้นใช้สำหรับเสียงของระบบและเสียงของแอปพลิเคชันอื่นๆ ทั้งหมด ฟังก์ชันนี้สามารถจัดเตรียมได้โดยตัวผสมซอฟต์แวร์ใน DSP หรือตัวผสมฮาร์ดแวร์ใน SOC
การเปิดใช้งานเส้นทางข้อมูล AAudio MMAP
AAudio จะใช้เส้นทางข้อมูล AudioFlinger แบบเดิมหาก MMAP ไม่ได้รับการสนับสนุนหรือไม่สามารถเปิดสตรีมได้ ดังนั้น AAudio จะทำงานกับอุปกรณ์เสียงที่ไม่รองรับเส้นทาง MMAP/NOIRQ
เมื่อทดสอบการรองรับ MMAP สำหรับ AAudio สิ่งสำคัญคือต้องรู้ว่าคุณกำลังทดสอบเส้นทางข้อมูล MMAP จริงหรือเพียงทดสอบเส้นทางข้อมูลดั้งเดิม ข้อมูลต่อไปนี้อธิบายวิธีการเปิดใช้งานหรือบังคับเส้นทางข้อมูลเฉพาะ และวิธีสืบค้นเส้นทางที่ใช้โดยสตรีม
คุณสมบัติของระบบ
คุณสามารถกำหนดนโยบาย MMAP ผ่านคุณสมบัติของระบบ:
- 1 = AAUDIO_POLICY_NEVER - ใช้เส้นทางเดิมเท่านั้น อย่าแม้แต่พยายามใช้ MMAP
- 2 = AAUDIO_POLICY_AUTO - ลองใช้ MMAP หากล้มเหลวหรือไม่พร้อมใช้งาน ให้ใช้เส้นทางเดิม
- 3 = AAUDIO_POLICY_ALWAYS - ใช้เส้นทาง MMAP เท่านั้น อย่าถอยกลับไปสู่เส้นทางเดิม
สิ่งเหล่านี้อาจถูกตั้งค่าในอุปกรณ์ Makefile เช่น:
# 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
คุณยังลบล้างค่าเหล่านี้ได้หลังจากที่อุปกรณ์บูตแล้ว คุณจะต้องรีสตาร์ทเซิร์ฟเวอร์เสียงเพื่อให้การเปลี่ยนแปลงมีผล ตัวอย่างเช่น ในการเปิดใช้งานโหมด AUTO สำหรับ MMAP:
adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver
มีฟังก์ชันที่ให้ไว้ใน ndk/sysroot/usr/include/aaudio/AAudioTesting.h
ที่อนุญาตให้คุณแทนที่นโยบายสำหรับการใช้เส้นทาง MMAP:
aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
หากต้องการทราบว่าสตรีมใช้เส้นทาง MMAP หรือไม่ ให้โทร:
bool AAudioStream_isMMapUsed(AAudioStream* stream);