صوت AAudio وMMAP

AAudio هي واجهة برمجة تطبيقات للصوت تم تقديمها في الإصدار 8.0 من Android. يتضمّن الإصدار 8.1 من نظام التشغيل Android تحسينات للحد من وقت الاستجابة عند استخدامه مع HAL وبرنامج تشغيل متوافقَين مع MMAP. يصف هذا المستند واجهة برمجة التطبيقات لمستوى تجريدي للأجهزة (HAL) وتغييرات برنامج التشغيل اللازمة لتفعيل ميزة MMAP في AAudio في نظام Android.

يتطلب استخدام AAudio MMAP ما يلي:

  • الإبلاغ عن إمكانات MMAP في HAL
  • تنفيذ وظائف جديدة في HAL
  • تنفيذ ioctl()‎ مخصّص اختياريًا لتخزين مؤقت الوضع الحصري
  • توفير مسار بيانات إضافي للأجهزة
  • ضبط خصائص النظام التي تفعّل ميزة MMAP

بنية AAudio

AAudio هي واجهة برمجة تطبيقات C أصلية جديدة توفّر بديلاً لواجهة Open SL ES. ويستخدم التنسيق نمط تصميم "أداة الإنشاء" لإنشاء أحداث بث صوتي.

توفّر AAudio مسار بيانات بوقت استجابة منخفض. في الوضع الحصري، تسمح الميزة لترميز تطبيق العميل بالكتابة مباشرةً في مخزن مؤقت مخطَّط للذاكرة يتمّت مشاركته مع برنامج تشغيل ALSA. في الوضع "مشترَك"، يتم استخدام مخزن MMAP من قِبل محوِّل صوت يعمل في AudioServer. في الوضع "حصري"، يكون وقت الاستجابة أقل بكثير لأنّ البيانات تتجاوز أداة المزج.

في الوضع الحصري، تطلب الخدمة مخزن MMAP من HAL وتدير الموارد. يتم تشغيل مخزن مؤقت MMAP في وضع NOIRQ، لذا لا تتوفّر عدادات قراءة/كتابة مشترَكة لإدارة الوصول إلى المخزن المؤقت. بدلاً من ذلك، يحافظ العميل على نموذج توقيت للأجهزة ويتوقّع وقت قراءة المخزن المؤقت.

في المخطّط البياني أدناه، يمكننا الاطّلاع على بيانات تعديل الرمز النبضي (PCM) التي تتدفق للأسفل من خلال ذاكرة التخزين المؤقت للقراءة والكتابة من خلال الذاكرة المشتركة (MMAP) إلى برنامج تشغيل 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);

بالنسبة إلى HIDL audio 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

يحتوي الطابع الزمني الذي تم إرساله مرة أخرى إلى نموذج "التوقيت" على موضع اللقطة ووقت 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 استخدامه في الوضع المشترَك. ولكن لا يمكن تمرير الوصف إلى رمز العميل في الوضع الحصري. سيوفّر وصف الملف /dev/snd/ إذن وصول مفرطًا إلى العميل، لذا يتم حظره بواسطة SELinux.

لتفعيل الوضع "حصري"، يجب تحويل وصف /dev/snd/ إلى وصفملف anon_inode:dmabuf. يسمح نظام SELinux بتمرير معرّف الملف إلى العميل. ويمكن أن يستخدمه أيضًا AAudioService.

يمكن إنشاء وصف ملف anon_inode:dmabuf باستخدام مكتبة ذاكرة Android Ion.

للحصول على معلومات إضافية، يُرجى الاطّلاع على هذه المراجع الخارجية:

  1. "آلية تخصيص الذاكرة ION في Android" https://lwn.net/Articles/480055/
  2. "نظرة عامة على ION في Android" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "دمج أداة تخصيص الذاكرة ION" https://lwn.net/Articles/565469/

تغييرات HAL

تحتاج خدمة AAudio إلى معرفة ما إذا كان هذا anon_inode:dmabuf متوافقًا. قبل الإصدار 10.0 من Android، كانت الطريقة الوحيدة لإجراء ذلك هي تمرير حجم ملف التخزين المؤقت لـ MMAP كهذا الرقم السالب، على سبيل المثال: -2048 بدلاً من 2048، إذا كان ذلك متاحًا في Android 10.0 والإصدارات الأحدث، يمكنك ضبط العلامة AUDIO_MMAP_APPLICATION_SHAREABLE.

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

تغييرات في النظام الفرعي للصوت

تتطلّب AAudio مسار بيانات إضافيًا في الواجهة الأمامية للصوت في المنظومة الفرعية للصوت كي تتمكّن من العمل بالتوازي مع مسار AudioFlinger الأصلي. ويُستخدَم هذا المسار القديم لجميع أصوات النظام والتطبيقات الأخرى. يمكن توفير هذه الوظيفة من خلال أداة مزج برامج في وحدة معالجة إشارات رقمية أو أداة مزج أجهزة في وحدة المعالجة المركزية.

تفعيل مسار بيانات AAudio MMAP

سيستخدم AAudio مسار بيانات AudioFlinger القديم إذا لم يكن MMAP متوافقًا أو تعذّر فتح بث. وبالتالي، سيعمل AAudio مع جهاز صوت لا يسمح بمسار MMAP/NOIRQ.

عند اختبار توافق AAudio مع MMAP، من المهم معرفة ما إذا كنت تختبر مسار بيانات MMAP فعليًا أو تختبر مسار البيانات القديم فقط. يوضّح القسم التالي كيفية تفعيل مسارات بيانات معيّنة أو فرضها، وكيفية طلب المسار المستخدَم من خلال مصدر بيانات.

خصائص النظام

يمكنك ضبط سياسة MMAP من خلال خصائص النظام:

  • ‫1 = AAUDIO_POLICY_NEVER - استخدام المسار القديم فقط لا تحاول حتى استخدام MMAP.
  • ‫2 = AAUDIO_POLICY_AUTO - جرِّب استخدام MMAP. إذا تعذّر ذلك أو لم يكن متاحًا، استخدِم المسار القديم.
  • ‫3 = AAUDIO_POLICY_ALWAYS: استخدام مسار MMAP فقط لا تعود إلى المسار القديم.

يمكن ضبط هذه الإعدادات في ملف devices 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);