الصوت و MMAP

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

يتطلب دعم AAudio MMAP ما يلي:

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

العمارة الصوتية

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

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

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

في الرسم البياني أدناه، يمكننا رؤية بيانات تعديل رمز النبض (PCM) تتدفق عبر MMAP FIFO إلى برنامج تشغيل ALSA. يتم طلب الطوابع الزمنية بشكل دوري بواسطة خدمة AAudio ثم يتم تمريرها إلى نموذج التوقيت الخاص بالعميل من خلال قائمة انتظار الرسائل الذرية.

مخطط تدفق البيانات PCM.
الشكل 1. تدفق بيانات PCM عبر FIFO إلى ALSA

في الوضع SHARED، يتم استخدام نموذج التوقيت أيضًا، ولكنه موجود في AAudioService.

لالتقاط الصوت، يتم استخدام نموذج مماثل، ولكن بيانات PCM تتدفق في الاتجاه المعاكس.

تغييرات هال

بالنسبة لـ 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 الصوتي. (راجع "تمكين مسار بيانات 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

يحتوي الطابع الزمني الذي تم تمريره مرة أخرى إلى نموذج التوقيت على موضع إطار ووقت رتيب بالنانو ثانية:

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/

تغييرات هال

تحتاج خدمة 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);