Ghi lại âm thanh chất lượng cao, tốc độ bit thấp bằng xHE-AAC

Kể từ Android 17 QPR1 (cấp độ API 37.1 hoặc Build.VERSION_CODES_FULL.CINNAMON_BUN_1), Android sẽ hỗ trợ nền tảng cho hoạt động mã hoá Extended High Efficiency Advanced Audio Coding (xHE-AAC).

Được xác định trong ISO/IEC 23003-3 (MPEG-D USAC) và ISO/IEC 23003-4 (MPEG-D DRC), xHE-AAC kết hợp tính năng Mã hoá âm thanh và lời nói hợp nhất với tính năng chuẩn hoá độ lớn bắt buộc và chế độ kiểm soát dải tần động tích hợp. Mặc dù giải mã xHE-AAC đã được hỗ trợ và bắt buộc theo Tài liệu định nghĩa về khả năng tương thích (CDD) của Android trên nhiều phiên bản nền tảng, nhưng việc bổ sung bộ mã hoá xHE-AAC (c2.android.xheaac.encoder và các hoạt động triển khai phần cứng OEM) cho phép bạn ghi lại tin nhắn thoại và âm thanh có tốc độ bit thấp, luồng âm thanh trực tiếp và tải trọng giao tiếp với độ rõ ràng vượt trội của giọng nói và âm lượng nhất quán trên các thiết bị.

Lợi ích của xHE-AAC

Tính năng ghi âm giọng nói truyền thống trong các ứng dụng nhắn tin trên Android thường sử dụng AAC-LC (AAC Low Complexity) cũ ở tốc độ 28 kbps. Ở tốc độ bit thấp (<32 kbps), AAC-LC cho thấy các hiện tượng hạn chế băng tần đáng chú ý, chất lượng giọng nói mờ và hiệu quả nén không tối ưu. Đối với người dùng cuối, điều này dẫn đến âm thanh bị bóp nghẹt, chói tai, khó phân biệt các phụ âm, khiến người nghe phải phát lại tin nhắn hoặc cố gắng hiểu lời nói trong môi trường ồn ào. Ngoài ra, mức âm lượng không nhất quán giữa các đoạn giọng nói buộc người dùng phải thường xuyên điều chỉnh âm lượng của thiết bị.

xHE-AAC khắc phục những hạn chế này bằng cách áp dụng các thuật toán mã hoá âm thanh tiên tiến hơn, chẳng hạn như chuyển đổi linh hoạt giữa mã hoá giọng nói chuyên dụng và các công cụ mã hoá âm thanh chung trong khi mở rộng dải tần số bằng tính năng Sao chép dải tần số nâng cao (eSBR) và cung cấp khả năng mã hoá âm thanh nổi tham số với rất ít chi phí mã hoá bổ sung.

MPEG-D DRC tích hợp cung cấp chế độ kiểm soát âm lượng bắt buộc cho xHE-AAC để phát nội dung ở mức âm lượng nhất quán và cung cấp quy trình xử lý kiểm soát dải tương phản động để mang đến trải nghiệm tốt nhất có thể cho người dùng khi nghe trên mọi nền tảng và trong mọi môi trường.

Các trường hợp sử dụng mục tiêu

  • Dịch vụ giao tiếp đa dạng (RCS) và tin nhắn thoại: Tuân thủ Hồ sơ chung RCC.71 phiên bản 3.1 của GSMA trở lên, trong đó bắt buộc phải sử dụng xHE-AAC cho tin nhắn thoại có tốc độ bit thấp.
  • Phát trực tuyến âm thanh và podcast: Cung cấp chất lượng âm thanh ổn định qua kết nối mạng di động hoặc vệ tinh bị suy giảm.

Phạm vi cung cấp nền tảng (cấp độ API 37.1 trở lên)

Nền tảng hỗ trợ mã hoá xHE-AAC yêu cầu cấp độ API 37.1 (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1/SDK 3700001 trở lên).

Truy vấn để biết thông tin hỗ trợ bộ mã hoá xHE-AAC

Bạn không được giả định rằng mọi thiết bị đều có tính năng mã hoá xHE-AAC bằng cách gọi MediaCodec.createByCodecName(...) bằng "c2.android.xheaac.encoder".

  • Một số thiết bị có thể cung cấp bộ mã hoá xHE-AAC được tăng tốc phần cứng từ nhà cung cấp chipset thay vì bộ mã hoá phần mềm nền tảng.
  • Các bản phát hành hệ điều hành cũ hoặc ROM tuỳ chỉnh không hỗ trợ mã hoá xHE-AAC.
  • Trước khi mã hoá tệp đính kèm để nhắn tin ngang hàng (chẳng hạn như RCS hoặc cuộc trò chuyện), các ứng dụng cũng nên kiểm tra xem ứng dụng nhận có quảng cáo hỗ trợ giải mã USAC/xHE-AAC hay không (ví dụ: sử dụng tính năng khám phá khả năng SDP profile-level-id=55 / object=42) để tránh lỗi tương tác trên các thiết bị nhận cũ.

Kiểm tra các chức năng của MediaCodecList

Để kiểm tra xem thiết bị hiện tại có hỗ trợ mã hoá xHE-AAC hay không, hãy kiểm tra profileLevels của các bộ mã hoá hỗ trợ 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;
}

Định cấu hình và tạo thực thể bộ mã hoá xHE-AAC

Sau khi bạn xác minh khả năng hỗ trợ xHE-AAC, hãy định cấu hình MediaFormat với KEY_AAC_PROFILE được đặt thành MediaCodecInfo.CodecProfileLevel.AACObjectXHE (giá trị 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;
}

Giới hạn về bộ mã hoá phần mềm được hỗ trợ (c2.android.xheaac.encoder)

Khi sử dụng bộ mã hoá xHE-AAC do phần mềm do hệ thống cung cấp (c2.android.xheaac.encoder), thành phần này sẽ thông báo các giới hạn về khả năng sau:

  • Số lượng kênh: Tối đa 2 kênh (Đơn âm / Âm thanh nổi).
  • Tốc độ lấy mẫu: 44100 Hz, 48000 Hz.
  • Dải tốc độ bit: 12,000 bps đến 400,000 bps (12 kb/giây – 400 kb/giây).
  • Trộn vùng chứa: Tương thích với luồng cơ bản MP4A thô và trộn vùng chứa MP4/M4A của nền tảng (MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4).