Gravar áudio de alta qualidade e baixa taxa de bits com xHE-AAC

A partir do Android 17 QPR1 (nível 37.1 da API ou Build.VERSION_CODES_FULL.CINNAMON_BUN_1), o Android introduz suporte da plataforma para a codificação Extended High Efficiency Advanced Audio Coding (xHE-AAC).

Definido em ISO/IEC 23003-3 (MPEG-D USAC) e ISO/IEC 23003-4 (MPEG-D DRC), o xHE-AAC combina codificação unificada de fala e áudio com normalização obrigatória de intensidade e controle dinâmico de faixa integrado. Embora a decodificação xHE-AAC seja compatível e obrigatória pelo Documento de definição de compatibilidade (CDD) do Android em várias versões da plataforma, a adição de um codificador xHE-AAC (implementações de hardware c2.android.xheaac.encoder e OEM) permite gravar mensagens de voz e áudio de baixa taxa de bits, transmissões de áudio ao vivo e payloads de comunicação com clareza de fala superior e volume consistente em todos os dispositivos.

Benefícios do xHE-AAC

A gravação de voz tradicional em apps de mensagens Android geralmente usa o AAC-LC (AAC de baixa complexidade) mais antigo a 28 kbps. Em taxas de bits baixas (<32 kbps), o AAC-LC apresenta artefatos perceptíveis de limitação de banda, qualidade de fala ruim e eficiência de compactação abaixo do ideal. Para os usuários finais, isso resulta em áudio abafado e metálico, em que as consoantes são difíceis de distinguir, exigindo que os ouvintes reproduzam as mensagens ou se esforcem para entender a fala em ambientes ruidosos. Além disso, níveis de volume inconsistentes entre os clipes de voz obrigam os usuários a ajustar o volume do dispositivo com frequência.

O xHE-AAC supera essas limitações aplicando algoritmos de codificação de áudio mais avançados, por exemplo, alternando dinamicamente entre ferramentas dedicadas de codificação de fala e de áudio geral, além de estender o intervalo de frequência com replicação de banda espectral (SBR, na sigla em inglês) aprimorada (eSBR) e oferecer capacidade de codificação estéreo paramétrica com muito pouco overhead de codificação adicional.

O DRC MPEG-D integrado oferece controle obrigatório de intensidade para o xHE-AAC reproduzir conteúdo em um volume consistente e oferece processamento de controle de faixa dinâmica para proporcionar a melhor experiência possível ao usuário em qualquer plataforma e ambiente.

Casos de uso de segmentação

  • Serviços de Comunicação Avançada (RCS) e mensagens de voz:em conformidade com o perfil universal RCC.71 da GSMA versão 3.1 e mais recente, que exige xHE-AAC para mensagens de áudio de baixa taxa de bits.
  • Streaming de áudio ao vivo e podcasts:oferece qualidade do áudio resiliente em conexões de rede móvel ou via satélite degradadas.

Disponibilidade da plataforma (nível da API 37.1 ou mais recente)

O suporte à codificação xHE-AAC da plataforma exige o nível 37.1 da API (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1 / SDK 3700001 ou mais recente).

Consultar suporte ao codificador xHE-AAC

Não suponha que a codificação xHE-AAC esteja disponível em todos os dispositivos chamando MediaCodec.createByCodecName(...) com "c2.android.xheaac.encoder".

  • Alguns dispositivos podem fornecer um codificador xHE-AAC acelerado por hardware do fornecedor do chipset em vez do codificador de software da plataforma.
  • Versões mais antigas do SO ou ROMs personalizadas não têm suporte para codificação xHE-AAC.
  • Antes de codificar um anexo para mensagens ponto a ponto (como RCS ou chat), os apps também precisam verificar se o cliente de recebimento anuncia suporte à decodificação USAC/xHE-AAC (por exemplo, usando a descoberta de recursos SDP profile-level-id=55 / object=42) para evitar falhas de interoperabilidade em receptores legados.

Inspecionar recursos do MediaCodecList

Para verificar se o dispositivo atual é compatível com a codificação xHE-AAC, inspecione o profileLevels dos codificadores compatíveis com 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;
}

Configurar e instanciar o codificador xHE-AAC

Depois de verificar o suporte a xHE-AAC, configure seu MediaFormat com KEY_AAC_PROFILE definido como MediaCodecInfo.CodecProfileLevel.AACObjectXHE (valor 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;
}

Limites de codificadores de software compatíveis (c2.android.xheaac.encoder)

Ao usar o codificador de software xHE-AAC fornecido pelo sistema (c2.android.xheaac.encoder), o componente anuncia os seguintes limites de capacidade:

  • Contagem de canais:até 2 canais (mono / estéreo).
  • Taxas de amostragem:44100 Hz, 48000 Hz.
  • Intervalo da taxa de bits:de 12,000 bps a 400,000 bps (12 kbps a 400 kbps).
  • Multiplexação de contêiner:compatível com fluxos elementares MP4A brutos e multiplexação de contêiner MP4/M4A da plataforma (MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4).