AAudio ও MMAP

AAudio হল একটি অডিও API যা Android 8.0 রিলিজে চালু করা হয়েছে। MMAP-এর সাথে কাজ করে এমন HAL ও ড্রাইভারের সাথে ব্যবহার করা হলে, Android 8.1 রিলিজ লেটেন্সি কমানোর জন্য উন্নত করা হয়েছে। এই ডকুমেন্টে Android-এ AAudio-এর MMAP ফিচার কাজ করার জন্য প্রয়োজনীয় হার্ডওয়্যার অ্যাবস্ট্রাকশন লেয়ার (HAL) এবং ড্রাইভারের পরিবর্তন সম্পর্কে বর্ণনা করা হয়েছে।

AAudio MMAP-এর জন্য এগুলি প্রয়োজন:

  • HAL-এর MMAP কেপেবিলিটি সম্পর্কে রিপোর্ট করা
  • HAL-এ নতুন ফাংশন প্রয়োগ করা
  • ঐচ্ছিকভাবে এক্সক্লুসিভ মোড বাফারের জন্য কাস্টম ioctl() প্রয়োগ করা
  • অতিরিক্ত হার্ডওয়্যার ডেটা পাথ প্রদান করা
  • MMAP ফিচার চালু করে এমন সিস্টেম প্রপার্টি সেট করা

AAudio আর্কিটেকচার

AAudio হল একটি নতুন নেটিভ C API যা Open SL ES-এর বিকল্প প্রদান করে। এটি অডিও স্ট্রিম তৈরি করতে বিল্ডার ডিজাইন প্যাটার্ন ব্যবহার করে।

AAudio কম লেটেন্সি ডেটা পাথ প্রদান করে। এক্সক্লুসিভ মোডে, ফিচারটি ক্লায়েন্ট অ্যাপ্লিকেশন কোডকে ALSA ড্রাইভারের সাথে শেয়ার করা মেমরি ম্যাপ করা বাফারে সরাসরি লেখার অনুমতি দেয়। SHARED মোডে, MMAP বাফারটি AudioServer-এ চলমান মিক্সার দ্বারা ব্যবহৃত হয়। EXCLUSIVE মোডে, লেটেন্সি অনেক কম হয় কারণ ডেটা মিক্সারকে বাইপাস করে।

এক্সক্লুসিভ মোডে, পরিষেবাটি HAL থেকে MMAP বাফার অনুরোধ করে এবং রিসোর্স ম্যানেজ করে। MMAP বাফারটি NOIRQ মোডে চলছে, তাই বাফারে অ্যাক্সেস ম্যানেজ করার জন্য কোনও শেয়ার করা রিড/রাইট কাউন্টার নেই। পরিবর্তে, ক্লায়েন্ট হার্ডওয়্যারের একটি টাইমিং মডেল মেইনটেন করে এবং কখন বাফার রিড করা হবে তা পূর্বাভাস করে।

নিচের ডায়াগ্রামে, আমরা দেখতে পাচ্ছি যে পালস-কোড মডুলেশন (PCM) ডেটা MMAP FIFO-এর মাধ্যমে ALSA ড্রাইভারের দিকে প্রবাহিত হচ্ছে। AAudio পরিষেবা পর্যায়ক্রমে টাইমস্ট্যাম্পের অনুরোধ করে এবং তারপরে অ্যাটমিক মেসেজ কোয়ের মাধ্যমে ক্লায়েন্টের টাইমিং মডেলে পাস করে।

PCM ডেটা ফ্লো ডায়াগ্রাম।
ছবি ১. FIFO-এর মাধ্যমে ALSA-তে PCM ডেটা ফ্লো

শেয়ার করা মোডে, একটি টাইমিং মডেলও ব্যবহার করা হয়, কিন্তু এটি 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" ২ (AAUDIO_POLICY_AUTO) হিসেবে সেট করা থাকতে হবে, যাতে অডিও ফ্রেমওয়ার্ক বুঝতে পারে যে MMAP মোড অডিও HAL-এ কাজ করে। (নিচে "AAudio MMAP ডেটা পাথ চালু করা" দেখুন।)

MMAP/NO IRQ মোডের জন্য নির্দিষ্ট আউটপুট ও ইনপুট প্রোফাইল audio_policy_configuration.xml ফাইলে থাকতে হবে, যাতে MMAP ক্লায়েন্ট তৈরি হলে Audio Policy Manager কোন স্ট্রিম খুলতে হবে তা বুঝতে পারে:

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

Tinyalsa ফাংশন কল করে MMAP স্ট্রিম খোলা ও বন্ধ করা যায়।

কোয়েরি 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. "The Android ION memory allocator" https://lwn.net/Articles/480055/
  2. "Android ION ওভারভিউ" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "Integrating the ION memory allocator" https://lwn.net/Articles/565469/

HAL-এ পরিবর্তন

এই anon_inode:dmabuf কাজ করে কিনা তা AAudio পরিষেবাকে জানতে হবে। Android 10.0-এর আগে, এটি করার একমাত্র উপায় ছিল MMAP বাফারের সাইজ নেগেটিভ নম্বর হিসেবে পাস করা, যেমন -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 পাথ সাপোর্ট করে না।

AAudio-এর জন্য MMAP সাপোর্ট পরীক্ষা করার সময়, আপনি সত্যিই MMAP ডেটা পাথ পরীক্ষা করছেন নাকি শুধু লিগ্যাসি ডেটা পাথ পরীক্ষা করছেন তা জানা গুরুত্বপূর্ণ। নির্দিষ্ট ডেটা পাথ কীভাবে চালু বা বাধ্য করতে হয় এবং স্ট্রিমের ব্যবহার করা পাথ কীভাবে কোয়েরি করতে হয় তা নিচে বর্ণনা করা হয়েছে।

সিস্টেম প্রপার্টি

আপনি সিস্টেম প্রপার্টির মাধ্যমে MMAP নীতি সেট করতে পারবেন:

  • ১ = AAUDIO_POLICY_NEVER - শুধুমাত্র লিগ্যাসি পাথ ব্যবহার করুন। MMAP ব্যবহার করার চেষ্টাও করবেন না।
  • ২ = AAUDIO_POLICY_AUTO - MMAP ব্যবহার করার চেষ্টা করুন। এটি কাজ না করলে বা উপলভ্য না থাকলে, লেগেসি পাথ ব্যবহার করুন।
  • ৩ = 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

এছাড়াও, ডিভাইস বুট করার পরে আপনি এইসব ভ্যালু ওভাররাইড করতে পারবেন। পরিবর্তন কার্যকর করতে আপনাকে audioserver রিস্টার্ট করতে হবে। যেমন, MMAP-এর জন্য AUTO মোড চালু করতে:

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