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 แล้วส่งต่อไปยังรูปแบบเวลาของลูกค้า ผ่านคิวข้อความอะตอม
ในโหมดแชร์ จะมีการใช้โมเดลเวลาเช่นกัน แต่จะอยู่ใน 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);