Graba audio de alta calidad y baja tasa de bits con xHE-AAC

A partir de Android 17 QPR1 (nivel de API 37.1 o Build.VERSION_CODES_FULL.CINNAMON_BUN_1), Android introduce compatibilidad de la plataforma con la codificación Extended High Efficiency Advanced Audio Coding (xHE-AAC).

Definido en ISO/IEC 23003-3 (MPEG-D USAC) y ISO/IEC 23003-4 (MPEG-D DRC), el xHE-AAC combina la codificación unificada de voz y audio con la normalización obligatoria de la sonoridad y el control integrado del rango dinámico. Si bien la decodificación de xHE-AAC se admite y exige en el Documento de definición de compatibilidad (CDD) de Android en varias versiones de la plataforma, la incorporación de un codificador de xHE-AAC (c2.android.xheaac.encoder y las implementaciones de hardware de OEM) te permite grabar mensajes de voz y audio de baja velocidad de bits, transmisiones de audio en vivo y cargas útiles de comunicación con una claridad de voz superior y un volumen constante en todos los dispositivos.

Beneficios de xHE-AAC

Por lo general, la grabación de voz tradicional en las apps de mensajería de Android usa el códec AAC-LC (AAC de baja complejidad) anterior a 28 kbps. Con tasas de bits bajas (menos de 32 kbps), el AAC-LC presenta artefactos notables de limitación de banda, calidad de voz difusa y eficiencia de compresión no óptima. Para los usuarios finales, esto genera un audio apagado y metálico en el que las consonantes son difíciles de distinguir, lo que requiere que los oyentes repitan los mensajes o se esfuercen por entender el habla en entornos ruidosos. Además, los niveles de volumen incoherentes en los clips de voz obligan a los usuarios a ajustar el volumen del dispositivo con frecuencia.

El códec xHE-AAC supera estas limitaciones aplicando algoritmos de codificación de audio más avanzados, por ejemplo, cambiando dinámicamente entre herramientas de codificación de voz dedicadas y herramientas de codificación de audio generales, a la vez que extiende el rango de frecuencia con la replicación de banda espectral mejorada (eSBR) y proporciona capacidad de codificación estéreo paramétrica con muy poca sobrecarga de codificación adicional.

El DRC de MPEG-D integrado proporciona un control de sonoridad obligatorio para xHE-AAC para reproducir contenido a un volumen constante y ofrece procesamiento de control de rango dinámico para brindar la mejor experiencia del usuario posible para escuchar en cualquier plataforma y en cualquier entorno.

Casos de uso objetivo

  • Servicios de comunicación enriquecida (RCS) y mensajería de voz: Cumple con la versión 3.1 del perfil universal RCC.71 de GSMA y versiones posteriores, que exigen xHE-AAC para la mensajería de audio de baja velocidad de bits.
  • Transmisión de audio en vivo y podcasts: Ofrece una calidad de audio resistente en conexiones de red satelital o móvil degradadas.

Disponibilidad de la plataforma (nivel de API 37.1 y versiones posteriores)

La compatibilidad con la codificación xHE-AAC de la plataforma requiere el nivel de API 37.1 (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1/SDK 3700001 o superior).

Consulta sobre la compatibilidad con el codificador xHE-AAC

No debes suponer que la codificación xHE-AAC está disponible en todos los dispositivos llamando a MediaCodec.createByCodecName(...) con "c2.android.xheaac.encoder".

  • Algunos dispositivos pueden proporcionar un codificador xHE-AAC acelerado por hardware del proveedor del chipset en lugar del codificador de software de la plataforma.
  • Las versiones anteriores del SO o las ROM personalizadas no admiten la codificación xHE-AAC.
  • Antes de codificar un adjunto para la mensajería de punto a punto (como RCS o chat), las apps también deben verificar si el cliente receptor anuncia la compatibilidad con la decodificación de USAC/xHE-AAC (por ejemplo, con el descubrimiento de capacidades de SDP profile-level-id=55 / object=42) para evitar errores de interoperabilidad en los receptores heredados.

Inspecciona las capacidades de MediaCodecList

Para inspeccionar si el dispositivo actual admite la codificación xHE-AAC, inspecciona el profileLevels de los codificadores que admiten 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 y crea una instancia del codificador xHE-AAC

Después de verificar la compatibilidad con xHE-AAC, configura tu MediaFormat con KEY_AAC_PROFILE establecido en 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;
}

Límites de codificadores de software compatibles (c2.android.xheaac.encoder)

Cuando se usa el codificador de software xHE-AAC proporcionado por el sistema (c2.android.xheaac.encoder), el componente anuncia los siguientes límites de capacidad:

  • Recuento de canales: Hasta 2 canales (mono / estéreo).
  • Tasas de muestreo: 44100 Hz y 48000 Hz
  • Rango de la tasa de bits: De 12,000 bps a 400,000 bps (de 12 kbps a 400 kbps).
  • Multiplexación de contenedores: Compatible con transmisiones elementales MP4A sin procesar y con la multiplexación de contenedores MP4/M4A de la plataforma (MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4).