الصوت و MMAP

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

يتطلب دعم AAudio MMAP:

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

AA هندسة الصوت

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

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

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

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

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

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

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

تغييرات HAL

لتينيالسا انظر:

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

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

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);

تغييرات Kernel

قم بتمكين وضع MMAP / NOIRQ في برنامج التشغيل.

تتم الإشارة إلى الذاكرة المشتركة باستخدام واصف ملف تم إنشاؤه بواسطة برنامج تشغيل ALSA. إذا كان واصف الملف مرتبطًا مباشرة بملف /dev/snd/ driver ، فيمكن استخدامه بواسطة خدمة AAudio في الوضع SHARED. لكن لا يمكن تمرير الواصف إلى رمز العميل للوضع الحصري. سيوفر واصف الملف /dev/snd/ وصولًا واسعًا جدًا للعميل ، لذلك تم حظره بواسطة SELinux.

لدعم الوضع EXCLUSIVE ، من الضروري تحويل /dev/snd/ descriptor إلى anon_inode:dmabuf ملف 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 مدعومًا. حاليًا ، الطريقة الوحيدة للقيام بذلك هي تمرير حجم المخزن المؤقت لـ MMAP كرقم سالب ، على سبيل المثال. -2048 بدلاً من 2048 ، إذا كان مدعومًا. يتم التخطيط لطريقة أفضل للإبلاغ عن هذا دون الحاجة إلى فتح الدفق.

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

يتطلب 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);