Aऑडियो और MMAP

AAudio, Android 8.0 रिलीज़ में पेश किया गया एक ऑडियो एपीआई है. Android 8.1 रिलीज़ में, इंतज़ार के समय को कम करने के लिए कई सुधार किए गए हैं. ऐसा, MMAP के साथ काम करने वाले एचएएल और ड्राइवर के साथ इस्तेमाल करने पर होता है. इस दस्तावेज़ में, हार्डवेयर एब्स्ट्रैक्शन लेयर (एचएएल) और ड्राइवर में किए गए बदलावों के बारे में बताया गया है. ये बदलाव, Android में AAudio की एमएमएपी सुविधा के साथ काम करने के लिए ज़रूरी हैं.

AAudio MMAP के साथ काम करने के लिए, इन चीज़ों की ज़रूरत होती है:

  • HAL की एमएमएपी क्षमताओं की जानकारी देना
  • HAL में नए फ़ंक्शन लागू करना
  • EXCLUSIVE मोड बफ़र के लिए, कस्टम ioctl() को लागू करना (ज़रूरी नहीं)
  • हार्डवेयर का अतिरिक्त डेटा पाथ उपलब्ध कराना
  • सिस्टम की ऐसी प्रॉपर्टी सेट करना जो एमएमएपी सुविधा को चालू करती हैं

AAudio का आर्किटेक्चर

AAudio एक नया नेटिव C API है, जो Open SL ES का विकल्प उपलब्ध कराता है. यह ऑडियो स्ट्रीम बनाने के लिए, बिल्डर डिज़ाइन पैटर्न का इस्तेमाल करता है.

AAudio, कम इंतज़ार वाला डेटा पाथ उपलब्ध कराता है. एक्सक्लूज़िव मोड में, यह सुविधा क्लाइंट ऐप्लिकेशन कोड को सीधे मेमोरी मैप किए गए बफ़र में लिखने की अनुमति देती है. यह बफ़र, ALSA ड्राइवर के साथ शेयर किया जाता है. शेयर किए गए मोड में, MMAP बफ़र का इस्तेमाल, ऑडियो सर्वर में चल रहे मिक्सर करता है. EXCLUSIVE मोड में, डेटा मिक्सर को बायपास करता है. इसलिए, इस मोड में इंतज़ार का समय काफ़ी कम होता है.

एक्सक्लूज़िव मोड में, सेवा एचएएल से एमएमएपी बफ़र का अनुरोध करती है और संसाधनों को मैनेज करती है. MMAP बफ़र, NOIRQ मोड में चल रहा है. इसलिए, बफ़र के ऐक्सेस को मैनेज करने के लिए, शेयर किए गए पढ़ने/लिखने के काउंटर नहीं हैं. इसके बजाय, क्लाइंट हार्डवेयर का टाइमिंग मॉडल बनाए रखता है और यह अनुमान लगाता है कि बफ़र कब पढ़ा जाएगा.

नीचे दिए गए डायग्राम में, हम एमएमएपी एफ़आईओ के ज़रिए ALSA ड्राइवर में, पल्स-कोड मॉड्यूलेशन (पीसीएम) डेटा को फ़्लो करते हुए देख सकते हैं. AAudio सेवा, समय-समय पर टाइमस्टैंप का अनुरोध करती है. इसके बाद, एक एटॉमिक मैसेज कतार की मदद से, क्लाइंट के टाइमिंग मॉडल को टाइमस्टैंप भेजे जाते हैं.

PCM डेटा फ़्लो डायग्राम.
पहली इमेज. FIFO के ज़रिए ALSA में PCM डेटा फ़्लो

शेयर किए गए मोड में, टाइमिंग मॉडल का भी इस्तेमाल किया जाता है. हालांकि, यह 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);

लेगसी एचएएल के लिए, यह देखें:

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 ऑडियो एचएएल के लिए:

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) पर सेट किया जाना चाहिए, ताकि ऑडियो फ़्रेमवर्क को पता चले कि ऑडियो एचएएल में एमएमएपी मोड काम करता है. (नीचे, "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>

एमएमएपी स्ट्रीम खोलना और बंद करना

createMmapBuffer(int32_t minSizeFrames)
            generates (Result retval, MmapBufferInfo info);

Tinyalsa फ़ंक्शन को कॉल करके, एमएमएपी स्ट्रीम को खोला और बंद किया जा सकता है.

एमएमएपी की स्थिति के बारे में क्वेरी

टाइमिंग मॉडल को वापस भेजे गए टाइमस्टैंप में, फ़्रेम की पोज़िशन और नैनोसेकंड में MONOTONIC समय शामिल होता है:

getMmapPosition()
        generates (Result retval, MmapPosition position);

एचएएल, ALSA ड्राइवर से यह जानकारी पा सकता है. इसके लिए, वह नए Tinyalsa फ़ंक्शन को कॉल करता है:

int pcm_mmap_get_hw_ptr(struct pcm* pcm,
                        unsigned int *hw_ptr,
                        struct timespec *tstamp);

शेयर की गई मेमोरी के लिए फ़ाइल डिस्क्रिप्टर

AAudio MMAP डेटा पाथ, मेमोरी के उस हिस्से का इस्तेमाल करता है जिसे हार्डवेयर और ऑडियो सेवा के बीच शेयर किया जाता है. शेयर की गई मेमोरी का रेफ़रंस, ALSA ड्राइवर से जनरेट किए गए फ़ाइल डिस्क्रिप्टर का इस्तेमाल करके दिया जाता है.

Kernel में हुए बदलाव

अगर फ़ाइल डिस्क्रिप्टर सीधे तौर पर किसी /dev/snd/ ड्राइवर फ़ाइल से जुड़ा है, तो AAudio सेवा इसका इस्तेमाल, शेयर किए गए मोड में कर सकती है. हालांकि, एक्सक्लूज़िव मोड के लिए, क्लाइंट कोड में डिस्क्रिप्टर को पास नहीं किया जा सकता. /dev/snd/ फ़ाइल डिस्क्रिप्टर, क्लाइंट को बहुत ज़्यादा ऐक्सेस देगा. इसलिए, SELinux इसे ब्लॉक कर देता है.

एक्सक्लूज़िव मोड का इस्तेमाल करने के लिए, /dev/snd/ डिस्क्रिप्टर को anon_inode:dmabuf फ़ाइल डिस्क्रिप्टर में बदलना ज़रूरी है. SELinux, फ़ाइल डिस्क्रिप्टर को क्लाइंट को पास करने की अनुमति देता है. इसका इस्तेमाल AAudioService भी कर सकता है.

Android Ion मेमोरी लाइब्रेरी का इस्तेमाल करके, anon_inode:dmabuf फ़ाइल डिस्क्रिप्टर जनरेट किया जा सकता है.

ज़्यादा जानकारी के लिए, ये बाहरी संसाधन देखें:

  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 पाथ के साथ-साथ काम कर सके. उस लेगसी पाथ का इस्तेमाल, सिस्टम और ऐप्लिकेशन की अन्य सभी आवाज़ों के लिए किया जाता है. यह सुविधा, डीएसपी में मौजूद सॉफ़्टवेयर मिक्सर या एसओसी में मौजूद हार्डवेयर मिक्सर से मिल सकती है.

AAudio MMAP डेटा पाथ चालू करना

अगर एमएमएपी काम नहीं करता है या कोई स्ट्रीम नहीं खुल पाती है, तो AAudio, लेगसी AudioFlinger डेटा पाथ का इस्तेमाल करेगा. इसलिए, AAudio ऐसे ऑडियो डिवाइस के साथ काम करेगा जो MMAP/NOIRQ पाथ के साथ काम नहीं करता.

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 में कुछ फ़ंक्शन दिए गए हैं, जिनकी मदद से एमएमएपी पाथ का इस्तेमाल करने के लिए, नीति को बदला जा सकता है:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

यह पता लगाने के लिए कि कोई स्ट्रीम, एमएमएपी पाथ का इस्तेमाल कर रही है या नहीं, यह कॉल करें:

bool AAudioStream_isMMapUsed(AAudioStream* stream);