AAaudio و MMAP

AAudio یک API صوتی است که در نسخه اندروید 8.0 معرفی شده است. نسخه Android 8.1 دارای پیشرفت‌هایی برای کاهش تأخیر در هنگام استفاده همراه با HAL و درایورهایی است که از MMAP پشتیبانی می‌کنند. این سند لایه انتزاعی سخت افزاری (HAL) و تغییرات درایور مورد نیاز برای پشتیبانی از ویژگی MMAP AAudio در اندروید را شرح می دهد.

پشتیبانی از AAudio MMAP به موارد زیر نیاز دارد:

  • گزارش قابلیت های MMAP HAL
  • اجرای توابع جدید در HAL
  • اجرای اختیاری یک ioctl() سفارشی برای بافر حالت EXCLUSIVE
  • ارائه یک مسیر داده سخت افزاری اضافی
  • تنظیم ویژگی های سیستم که ویژگی MMAP را فعال می کند

معماری صوتی AA

AAudio یک API بومی C جدید است که جایگزینی برای Open SL ES ارائه می‌کند. از الگوی طراحی Builder برای ایجاد جریان های صوتی استفاده می کند.

AAudio یک مسیر داده با تاخیر کم را ارائه می دهد. در حالت EXCLUSIVE، این ویژگی به کد برنامه مشتری اجازه می دهد تا مستقیماً در بافر نقشه برداری شده حافظه که با درایور ALSA به اشتراک گذاشته شده است، بنویسد. در حالت SHARED، بافر MMAP توسط یک میکسر در حال اجرا در AudioServer استفاده می شود. در حالت EXCLUSIVE، تاخیر به طور قابل توجهی کمتر است زیرا داده ها از میکسر عبور می کنند.

در حالت EXCLUSIVE، سرویس بافر MMAP را از HAL درخواست می کند و منابع را مدیریت می کند. بافر MMAP در حالت NOIRQ اجرا می شود، بنابراین شمارنده مشترک خواندن/نوشتن برای مدیریت دسترسی به بافر وجود ندارد. در عوض، مشتری یک مدل زمان‌بندی سخت‌افزار را حفظ می‌کند و زمان خواندن بافر را پیش‌بینی می‌کند.

در نمودار زیر، می‌توانیم داده‌های مدولاسیون کد پالس (PCM) را ببینیم که از طریق MMAP FIFO به درایور ALSA سرازیر می‌شوند. مهرهای زمانی به صورت دوره ای توسط سرویس AAaudio درخواست می شوند و سپس از طریق یک صف پیام اتمی به مدل زمان بندی مشتری ارسال می شوند.

نمودار جریان داده PCM.
شکل 1. جریان داده های PCM از طریق FIFO به ALSA

در حالت 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 صوتی پشتیبانی می شود. (به "فعال کردن مسیر داده MMAP AAaudio" در زیر مراجعه کنید.)

فایل 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

مُهر زمانی ارسال شده به مدل زمانبندی شامل موقعیت فریم و زمان یکنواخت بر حسب نانوثانیه است:

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 استفاده شود. اما توصیفگر نمی تواند به کد مشتری برای حالت EXCLUSIVE منتقل شود. توصیف‌گر فایل /dev/snd/ دسترسی بسیار وسیعی به کلاینت فراهم می‌کند، بنابراین توسط SELinux مسدود می‌شود.

برای پشتیبانی از حالت EXCLUSIVE، باید توصیفگر /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 تغییر می کند

سرویس AAaudio باید بداند که آیا این 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 را فعال کنید

اگر MMAP پشتیبانی نشود یا جریانی را باز نکند، AAudio از مسیر داده قدیمی AudioFlinger استفاده خواهد کرد. بنابراین 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);