Registrare audio di alta qualità a basso bitrate con xHE-AAC

A partire da Android 17 QPR1 (livello API 37.1 o Build.VERSION_CODES_FULL.CINNAMON_BUN_1), Android introduce il supporto della piattaforma per la codifica Extended High Efficiency Advanced Audio Coding (xHE-AAC).

Definito in ISO/IEC 23003-3 (MPEG-D USAC) e ISO/IEC 23003-4 (MPEG-D DRC), xHE-AAC combina la codifica unificata di parlato e audio con la normalizzazione obbligatoria del volume e il controllo della gamma dinamica integrato. Sebbene la decodifica xHE-AAC sia stata supportata e resa obbligatoria dal Compatibility Definition Document (CDD) di Android in più versioni della piattaforma, l'aggiunta di un encoder xHE-AAC (implementazioni hardware c2.android.xheaac.encoder e OEM) consente di registrare messaggi vocali e audio a basso bitrate, stream audio live e payload di comunicazione con una chiarezza vocale superiore e un volume uniforme su tutti i dispositivi.

Vantaggi di xHE-AAC

La registrazione vocale tradizionale nelle app di messaggistica Android in genere utilizza la versione precedente AAC-LC (AAC Low Complexity) a 28 kbps. A velocità in bit basse (< 32 kbps), AAC-LC mostra artefatti di limitazione della banda evidenti, qualità del parlato sfocata ed efficienza di compressione non ottimale. Per gli utenti finali, l'audio risulta ovattato e metallico, le consonanti sono difficili da distinguere e gli ascoltatori devono riascoltare i messaggi o sforzarsi di capire il parlato in ambienti rumorosi. Inoltre, i livelli di volume incoerenti nei clip vocali costringono gli utenti a regolare frequentemente il volume del dispositivo.

xHE-AAC supera queste limitazioni applicando algoritmi di codifica audio più avanzati, ad esempio passando dinamicamente da strumenti di codifica vocale dedicati a strumenti di codifica audio generali, estendendo al contempo la gamma di frequenze con la replica della banda spettrale avanzata (eSBR) e fornendo la funzionalità di codifica stereo parametrica con un sovraccarico di codifica aggiuntivo molto ridotto.

La funzionalità MPEG-D DRC integrata fornisce il controllo obbligatorio del volume per xHE-AAC per riprodurre i contenuti a un volume costante e offre l'elaborazione del controllo della gamma dinamica per offrire la migliore esperienza utente possibile per l'ascolto su qualsiasi piattaforma e in qualsiasi ambiente.

Casi d'uso target

  • Rich Communication Services (RCS) e messaggistica vocale: sono conformi a GSMA RCC.71 Universal Profile versione 3.1 e successive, che impongono xHE-AAC per la messaggistica audio a basso bitrate.
  • Streaming audio live e podcast:offre una qualità audio resiliente su connessioni di rete mobile o satellitare degradate.

Disponibilità della piattaforma (livello API 37.1+)

Il supporto della codifica xHE-AAC della piattaforma richiede il livello API 37.1 (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1/SDK 3700001 o versioni successive).

Query per il supporto del codificatore xHE-AAC

Non devi presupporre che la codifica xHE-AAC sia disponibile su ogni dispositivo chiamando MediaCodec.createByCodecName(...) con "c2.android.xheaac.encoder".

  • Alcuni dispositivi potrebbero fornire un codificatore xHE-AAC con accelerazione hardware dal fornitore del chipset anziché dal codificatore software della piattaforma.
  • Le versioni precedenti del sistema operativo o le ROM personalizzate non supportano la codifica xHE-AAC.
  • Prima di codificare un allegato per la messaggistica peer-to-peer (ad esempio RCS o chat), le app devono anche verificare se il client di ricezione annuncia il supporto della decodifica USAC/xHE-AAC (ad esempio, utilizzando l'individuazione delle funzionalità SDP profile-level-id=55 / object=42) per evitare errori di interoperabilità sui ricevitori legacy.

Ispeziona le funzionalità di MediaCodecList

Per verificare se il dispositivo attuale supporta la codifica xHE-AAC, controlla profileLevels dei codificatori che supportano 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;
}

Configura e crea un'istanza del codificatore xHE-AAC

Dopo aver verificato il supporto di xHE-AAC, configura MediaFormat con KEY_AAC_PROFILE impostato su MediaCodecInfo.CodecProfileLevel.AACObjectXHE (valore 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;
}

Limiti dei codificatori software supportati (c2.android.xheaac.encoder)

Quando utilizzi il codificatore software xHE-AAC fornito dal sistema (c2.android.xheaac.encoder), il componente annuncia i seguenti limiti di funzionalità:

  • Numero di canali: fino a 2 canali (mono / stereo).
  • Frequenze di campionamento:44100 Hz, 48000 Hz.
  • Intervallo velocità in bit:da 12,000 bps a 400,000 bps (da 12 kbps a 400 kbps).
  • Multiplexing del contenitore:compatibile con stream elementari MP4A non elaborati e multiplexing del contenitore MP4/M4A della piattaforma (MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4).