AAudio และ MMAP

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
รูปที่ 1 การส่งข้อมูล PCM ผ่าน FIFO ไปยัง ALSA

ในโหมดแชร์ จะมีการใช้โมเดลเวลาเช่นกัน แต่จะอยู่ใน 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

สำหรับข้อมูลเพิ่มเติม โปรดดูแหล่งข้อมูลภายนอกต่อไปนี้

  1. "ที่จัดสรรหน่วยความจำของ Android ION" https://lwn.net/Articles/480055/
  2. "ภาพรวม Android ION" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "การผสานรวมที่จัดสรรหน่วยความจำ 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);