AAudio คือ API เสียงที่เปิดตัวใน Android 8.0 Android 8.1 รุ่นมีการเพิ่มประสิทธิภาพเพื่อลดเวลาในการตอบสนองเมื่อใช้ร่วมกับ HAL และไดรเวอร์ที่รองรับ MMAP เอกสารนี้อธิบายกระบวนการถอดถอนฮาร์ดแวร์ เลเยอร์ (HAL) และการเปลี่ยนแปลงไดรเวอร์ที่จำเป็นเพื่อสนับสนุนฟีเจอร์ MMAP ของ AAudio ใน Android
การสนับสนุนสำหรับ AAudio MMAP ต้องมี:
- การรายงานความสามารถ MMAP ของ HAL
- การนำฟังก์ชันใหม่ๆ มาใช้ใน HAL
- (ไม่บังคับ) ใช้ ioctl() ที่กำหนดเองสำหรับบัฟเฟอร์โหมดพิเศษ
- การให้เส้นทางข้อมูลฮาร์ดแวร์เพิ่มเติม
- การตั้งค่าคุณสมบัติของระบบที่เปิดใช้ฟีเจอร์ MMAP
สถาปัตยกรรม AAudio
เสียง เป็น C API แบบเนทีฟใหม่ที่ให้บริการแทน Open SL ES โดยใช้ รูปแบบการออกแบบของเครื่องมือสร้างสตรีมเสียง
AAudio ให้เส้นทางข้อมูลที่มีเวลาในการตอบสนองต่ำ ในโหมดพิเศษ ฟีเจอร์นี้ ช่วยให้โค้ดของแอปพลิเคชันไคลเอ็นต์เขียนลงในบัฟเฟอร์ที่แมปกับหน่วยความจำได้โดยตรง ที่แชร์ให้ไดรเวอร์ ALSA ในโหมด SHARED บัฟเฟอร์ MMAP จะถูกใช้งานโดย มิกเซอร์ที่ทำงานใน AudioServer ในโหมดพิเศษ เวลาในการตอบสนองคือ น้อยลงอย่างมากเนื่องจากข้อมูลเลี่ยงผ่านมิกเซอร์
ในโหมดเฉพาะตัว บริการจะขอบัฟเฟอร์ MMAP จาก HAL และจัดการ แหล่งข้อมูล บัฟเฟอร์ MMAP กำลังทำงานในโหมด NOIRQ ดังนั้นจึงไม่มีการแชร์ ตัวนับการอ่าน/เขียนเพื่อจัดการการเข้าถึงบัฟเฟอร์ แต่ลูกค้า รักษาโมเดลช่วงเวลาของฮาร์ดแวร์และคาดการณ์เวลาที่บัฟเฟอร์ อ่านแล้ว
ในแผนภาพด้านล่าง เราจะเห็นการรับส่งข้อมูลการกรอโค้ด Pulse (PCM) ผ่าน MMAP FIFO เข้าไปในไดรเวอร์ ALSA การประทับเวลาจะแสดงเป็นระยะๆ ที่ขอโดยบริการ AAudio แล้วส่งต่อไปยังรูปแบบเวลาของลูกค้า ผ่านคิวข้อความอะตอม
![แผนภาพโฟลว์ข้อมูล PCM](https://source.android.com/static/docs/core/audio/images/pcm_data_flow.png?authuser=2&hl=th)
ในโหมดแชร์ จะมีการใช้โมเดลเวลาเช่นกัน แต่จะอยู่ใน 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);
สำหรับ HAL เสียง 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);
รายงานการรองรับ MMAP
พร็อพเพอร์ตี้ของระบบ "aaudio.mmap_policy" ควรตั้งค่าเป็น 2 (AAUDIO_POLICY_AUTO) ดังนั้น เฟรมเวิร์กเสียงจะรู้ว่า HAL เสียงรองรับโหมด MMAP (โปรดดู "การเปิดใช้เส้นทางข้อมูล AAudio MMAP" below.)
ไฟล์ 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
การประทับเวลาที่ส่งคืนไปยังโมเดลการจับเวลาจะมีตำแหน่งเฟรมและ เวลาแบบ 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
การเปลี่ยนแปลงเคอร์เนล
หากข้อบ่งชี้ไฟล์เชื่อมโยงโดยตรงกับ
/dev/snd/
เพื่อให้ใช้ได้โดยบริการ AAudio ใน
โหมด SHARED แต่ไม่สามารถส่งผ่านข้อบ่งชี้ไปยังรหัสไคลเอ็นต์สำหรับ
โหมดพิเศษ ข้อบ่งชี้ไฟล์ /dev/snd/
ก็จะมีให้เช่นกัน
ในการเข้าถึงไคลเอ็นต์ในวงกว้าง จึงถูกบล็อกโดย SELinux
เพื่อรองรับโหมดเฉพาะตัว คุณจำเป็นต้องแปลง
ตัวบ่งชี้ /dev/snd/
ไปยังไฟล์ anon_inode:dmabuf
ข้อบ่งชี้ 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
นอกจากนี้ คุณยังลบล้างค่าเหล่านี้ได้หลังจากที่อุปกรณ์เปิดเครื่องแล้ว คุณจะต้องรีสตาร์ทเซิร์ฟเวอร์เสียงเพื่อให้การเปลี่ยนแปลงมีผล ตัวอย่างเช่น หากต้องการเปิดใช้โหมดอัตโนมัติสำหรับ 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);