Merekam audio berkualitas tinggi dengan bitrate rendah menggunakan xHE-AAC

Mulai Android 17 QPR1 (level API 37.1 atau Build.VERSION_CODES_FULL.CINNAMON_BUN_1), Android memperkenalkan dukungan platform untuk encoding Extended High Efficiency Advanced Audio Coding (xHE-AAC).

Ditentukan dalam ISO/IEC 23003-3 (MPEG-D USAC) dan ISO/IEC 23003-4 (MPEG-D DRC), xHE-AAC menggabungkan Unified Speech and Audio Coding dengan normalisasi keras suara wajib dan kontrol rentang dinamis bawaan. Meskipun dekode xHE-AAC telah didukung dan diwajibkan oleh Dokumen Definisi Kompatibilitas (CDD) Android di beberapa versi platform, penambahan encoder xHE-AAC (implementasi hardware OEM dan c2.android.xheaac.encoder) memungkinkan Anda merekam pesan suara dan audio bitrate rendah, streaming audio live, dan payload komunikasi dengan kejelasan ucapan yang lebih baik dan volume yang konsisten di seluruh perangkat.

Manfaat xHE-AAC

Perekaman suara tradisional di aplikasi pesan Android biasanya menggunakan AAC-LC (AAC Low Complexity) yang lebih lama pada 28 kbps. Pada bitrate rendah (<32 kbps), AAC-LC menunjukkan artefak pembatasan band yang terlihat jelas, kualitas ucapan yang tidak jelas, dan efisiensi kompresi yang kurang optimal. Bagi pengguna akhir, hal ini menghasilkan audio yang teredam dan tipis, dengan konsonan yang sulit dibedakan, sehingga pendengar harus memutar ulang pesan atau berusaha keras untuk memahami ucapan di lingkungan yang bising. Selain itu, tingkat volume yang tidak konsisten di seluruh klip suara memaksa pengguna untuk sering menyesuaikan volume perangkat.

xHE-AAC mengatasi batasan ini dengan menerapkan algoritma coding audio yang lebih canggih, misalnya, beralih secara dinamis antara alat coding ucapan khusus dan alat coding audio umum sambil memperluas rentang frekuensi dengan Spectral Band Replication (SBR) yang ditingkatkan dan menyediakan kemampuan coding stereo parametrik dengan overhead coding tambahan yang sangat kecil.

DRC MPEG-D bawaan menyediakan kontrol keras suara wajib untuk xHE-AAC agar dapat memutar kembali konten pada volume yang konsisten dan menawarkan pemrosesan kontrol rentang dinamis untuk memberikan pengalaman pengguna terbaik saat mendengarkan di platform dan lingkungan apa pun.

Kasus penggunaan target

  • Rich Communication Services (RCS) dan pesan suara: Mematuhi GSMA RCC.71 Universal Profile versi 3.1 dan yang lebih baru, yang mewajibkan xHE-AAC untuk pesan audio bitrate rendah.
  • Streaming audio dan podcast live: Menghadirkan kualitas audio yang andal melalui koneksi jaringan seluler atau satelit yang terdegradasi.

Ketersediaan platform (level API 37.1+)

Dukungan encoding xHE-AAC platform memerlukan level API 37.1 (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1 / SDK 3700001 atau yang lebih tinggi).

Kueri untuk dukungan encoder xHE-AAC

Anda tidak boleh mengasumsikan bahwa encoding xHE-AAC tersedia di setiap perangkat dengan memanggil MediaCodec.createByCodecName(...) dengan "c2.android.xheaac.encoder".

  • Beberapa perangkat dapat menyediakan encoder xHE-AAC yang diakselerasi hardware dari vendor chipset, bukan encoder software platform.
  • Rilis OS lama atau ROM yang disesuaikan tidak memiliki dukungan encoding xHE-AAC.
  • Sebelum mengenkode lampiran untuk pengiriman pesan peer-to-peer (seperti RCS atau chat), aplikasi juga harus memeriksa apakah klien penerima mengiklankan dukungan decoding USAC/xHE-AAC (misalnya, menggunakan penemuan kemampuan SDP profile-level-id=55 / object=42) untuk mencegah kegagalan interoperabilitas pada penerima lama.

Memeriksa kemampuan MediaCodecList

Untuk memeriksa apakah perangkat saat ini mendukung encoding xHE-AAC, periksa profileLevels encoder yang mendukung MediaFormat.MIMETYPE_AUDIO_AAC:

Kotlin

import android.media.MediaCodecInfo.CodecProfileLevel
import android.media.MediaCodecList
import android.media.MediaFormat
import android.os.Build

/**
 * Checks if the current device supports xHE-AAC audio encoding.
 */
fun isXheAacEncodingSupported(): Boolean {
    // Verify API Level >= 37.1 (CINNAMON_BUN_1 / SDK 3700001)
    if (Build.VERSION.SDK_INT_FULL < Build.VERSION_CODES_FULL.CINNAMON_BUN_1) {
        return false
    }

    val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
    for (codecInfo in codecList.codecInfos) {
        if (!codecInfo.isEncoder) continue

        if (MediaFormat.MIMETYPE_AUDIO_AAC in codecInfo.supportedTypes) {
            val capabilities =
                codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_AUDIO_AAC)
            // Check if AACObjectXHE (42) is present in profileLevels
            val supportsXhe = capabilities.profileLevels.any { profileLevel ->
                profileLevel.profile == CodecProfileLevel.AACObjectXHE
            }
            if (supportsXhe) {
                return true
            }
        }
    }
    return false
}

Java

import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;

/**
 * Checks if the current device supports xHE-AAC audio encoding.
 */
public boolean isXheAacEncodingSupported() {
    // Verify API Level >= 37.1 (CINNAMON_BUN_1 / SDK 3700001)
    if (Build.VERSION.SDK_INT_FULL < Build.VERSION_CODES_FULL.CINNAMON_BUN_1) {
        return false;
    }

    MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
        if (!codecInfo.isEncoder()) continue;

        for (String type : codecInfo.getSupportedTypes()) {
            if (MediaFormat.MIMETYPE_AUDIO_AAC.equals(type)) {
                MediaCodecInfo.CodecCapabilities capabilities =
                        codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_AUDIO_AAC);
                for (CodecProfileLevel profileLevel : capabilities.profileLevels) {
                    if (profileLevel.profile == CodecProfileLevel.AACObjectXHE) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

Mengonfigurasi dan membuat instance encoder xHE-AAC

Setelah memverifikasi dukungan xHE-AAC, konfigurasikan MediaFormat dengan KEY_AAC_PROFILE ditetapkan ke MediaCodecInfo.CodecProfileLevel.AACObjectXHE (nilai 42).

Kotlin

fun setupXheAacEncoder(): MediaCodec? {
    if (!isXheAacEncodingSupported()) {
        return null
    }

    val mimeType = MediaFormat.MIMETYPE_AUDIO_AAC
    val sampleRate = 48000
    val channelCount = 1 // Mono voice recording
    val bitRate = 20000  // 20 kbps delivers superior speech clarity

    val format = MediaFormat.createAudioFormat(mimeType, sampleRate, channelCount).apply {
        setInteger(MediaFormat.KEY_BIT_RATE, bitRate)
        setInteger(MediaFormat.KEY_AAC_PROFILE, CodecProfileLevel.AACObjectXHE)
    }

    // Find the hardware or software encoder supporting this format and profile
    val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
    val encoderName = codecList.findEncoderForFormat(format) ?: return null
    val encoder = MediaCodec.createByCodecName(encoderName)
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
    return encoder
}

Java

public MediaCodec setupXheAacEncoder() throws IOException {
    if (!isXheAacEncodingSupported()) {
        return null;
    }

    String mimeType = MediaFormat.MIMETYPE_AUDIO_AAC;
    int sampleRate = 48000;
    int channelCount = 1; // Mono voice recording
    int bitRate = 20000;  // 20 kbps delivers superior speech clarity

    MediaFormat format = MediaFormat.createAudioFormat(mimeType, sampleRate, channelCount);
    format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, CodecProfileLevel.AACObjectXHE);

    // Find the hardware or software encoder supporting this format and profile
    MediaCodecList codecList =
            new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    String encoderName = codecList.findEncoderForFormat(format);
    if (encoderName == null) {
        return null;
    }

    MediaCodec encoder = MediaCodec.createByCodecName(encoderName);
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    return encoder;
}

Batas encoder software yang didukung (c2.android.xheaac.encoder)

Saat menggunakan encoder xHE-AAC software yang disediakan sistem (c2.android.xheaac.encoder), komponen akan mengiklankan batas kemampuan berikut:

  • Jumlah saluran: Hingga 2 saluran (Mono / Stereo).
  • Frekuensi sampel: 44100 Hz, 48000 Hz.
  • Rentang kecepatan bit: 12,000 bps hingga 400,000 bps (12 kbps – 400 kbps).
  • Penggabungan penampung: Kompatibel dengan penggabungan penampung MP4/M4A platform dan streaming elemen MP4A mentah (MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4).